/* Toàn bộ CSS của website. Dùng chung cho mọi trang trong pub_web/.
   Đường dẫn url() tính từ FILE NÀY — site.css nằm cùng cấp với index.html
   nên public/... vẫn đúng, không cần sửa. */

/* ============================================================
   1. FONT — Mona Sans, tự host trong public/fonts/

   Đây là VARIABLE FONT: một file lo hết mọi độ đậm 200–900 và
   mọi độ rộng 75–125%. Không cần nạp file riêng cho Bold/Black.
     font-weight: 200…900   → từ ExtraLight tới Black
     font-stretch: 75%…125% → từ Condensed tới Expanded
   ============================================================ */
@font-face {
  font-family: "Mona Sans";
  font-style: normal;
  font-weight: 200 900;
  font-stretch: 75% 125%;
  font-display: swap;
  src: url("public/fonts/mona-sans-var.woff2") format("woff2");
}
@font-face {
  font-family: "Mona Sans";
  font-style: italic;
  font-weight: 200 900;
  font-stretch: 75% 125%;
  font-display: swap;
  src: url("public/fonts/mona-sans-italic-var.woff2") format("woff2");
}
/* ============================================================
   2. DESIGN TOKEN — SỬA MÀU / FONT / BO GÓC Ở ĐÂY
   ============================================================ */
:root {
  --bg: #f4f1ea; /* nền chính */
  --surface: #e9e4d8; /* nền thẻ */
  --text: #111111; /* chữ */
  --accent: #c4522b; /* màu nhấn */

  /* Cả trang dùng chung Mona Sans. Giữ 2 biến để sau này muốn
     tách riêng font tiêu đề thì chỉ sửa 1 dòng. */
  --font-body: "Mona Sans", system-ui, sans-serif;
  --font-display: "Mona Sans", system-ui, sans-serif;

  --radius: 24px; /* bo góc lớn nhất của thang — xem bảng trong README */
  /* Khổ nội dung. Từ 1440 trở lên cho nới theo màn thay vì đứng ở 1200:
     trên màn rời 27" thì 1200 làm nội dung bị bó giữa hai dải lề rất rộng.
     max(1200px, …) để mốc 1440 không đổi so với bản đã chốt (80vw ở 1440 chỉ
     ra 1152, nhỏ hơn 1200), min(…, 1760px) để màn siêu rộng không kéo dòng
     chữ dài quá tầm đọc. */
  --maxw: 1200px;
  --pad: 1.5rem;

  /* Màu lưới cho SECTION NỀN TỐI (#181818). --ab-line vốn tính cho nền kem của
     About, đặt lên nền tối thì thành vạch trắng chói.
     Số này đo THẲNG từ ref home-journey: đường kẻ có đỉnh rgb(23,31,34) trên nền
     rgb(24,24,24) — tức chỉ nhỉnh hơn nền 7–10 mức ở kênh lam. rgba(40,210,255,.045)
     tổng hợp ra rgb(25,32,34), lệch 1–2 mức. Bản trước để .11 cho ra (38,42,45),
     sáng gần gấp đôi. */
  --tối-line: rgba(40, 210, 255, 0.045);

  /* LỀ CHUNG CỦA MỌI SECTION (trừ hero). Một token duy nhất để bốn section
     About / Selected Work / More Projects / My journey luôn thẳng cột nhau.
       ≤767  : 24 (= --pad)
       ≥768  : 76 (= --pad × 1.6 + 12)
     +12 là mức đẩy thêm đã chốt cho bản desktop. */
  --sec-pad: var(--pad);

  --muted: color-mix(in srgb, var(--text) 55%, transparent);
  --faint: color-mix(in srgb, var(--text) 12%, transparent);
}
@media (min-width: 1440px) {
  :root {
    --maxw: max(1200px, min(80vw, 1760px));
  }
}
@media (min-width: 768px) {
  :root {
    --pad: 2.5rem;
    --sec-pad: calc(var(--pad) * 1.6);
  }
}
/* +12px CHỈ cho desktop, và "desktop" ở đây tính theo BỀ RỘNG MÀN HÌNH
   (screen.width ≥ 1280), không theo bề rộng cửa sổ — xem script inline trong
   <head> của index.html. Kèm điều kiện cửa sổ ≥768 để cửa sổ thu thật hẹp vẫn về
   lề mobile.
   html.man-desktop có độ ưu tiên (0,1,1), thắng :root (0,1,0) nên không phụ thuộc
   thứ tự viết. */
/* Khi MÀN HÌNH ≥1280 dù cửa sổ hẹp hơn (panel preview của Cursor/VS Code).
   Vẫn đòi cửa sổ ≥768 để thu thật hẹp thì về lề mobile. */
@media (min-width: 768px) {
  html.man-desktop {
    --sec-pad: calc(var(--pad) * 1.6 + 12px);
  }
}
/* DESKTOP THẬT (cửa sổ ≥1280): lề 140 để nội dung KHÔNG chui xuống dưới logo-HA.
   Đo hộp của logo và nút menu ở khổ desktop: logo x 36–121, nút menu
   x (rộng−72)–(rộng−36). Lấy 140 = 121 + 19 cho logo thở; lề phải bằng lề trái nên
   cũng 140, dư so với mức 91 mà nút menu cần.
   Hero không dùng --sec-pad nên không đụng tới; băng thẻ chạy của More Projects cũng
   không, nó vẫn tràn hết bề ngang.

   PHẢI LIỆT KÊ CẢ `html.man-desktop` Ở ĐÂY. Bản trước chỉ viết `:root` trong
   @media (min-width: 1440px) và đặt sau, tưởng thế là thắng — KHÔNG: media query không
   cộng thêm chút specificity nào, mà `html.man-desktop` là (0,1,1) còn `:root` là
   (0,1,0), nên rule cũ 76 vẫn thắng bất kể thứ tự. Trên máy có màn ≥1280 (đúng máy
   người dùng) lề vẫn y nguyên 76 trong khi máy đo headless — `screen.width` chỉ 800
   nên không có class — lại ra 140. Đúng cái bẫy đã ghi ở phần Selected Work. */
@media (min-width: 1280px) {
  :root,
  html.man-desktop {
    --sec-pad: 140px;
  }
}
/* TỪ 16 INCH (1728) TRỞ LÊN: chặn KHỔ NỘI DUNG ở 1560 thay vì cho nó dàn tới sát lề.
   Màn càng rộng thì một dòng càng dài, mắt phải quét ngang cả nghìn px mới hết — ở
   1920 với lề 140 thì khổ nội dung là 1640.

   `max(144px, (100vw − 1560px) / 2)` — sàn 144 để nối liền với mức 140 ở dưới 1728,
   không thấy bước nhảy. Ra:
     1728 → lề 144, khổ 1440   (dưới 1848 thì sàn 144 thắng, khổ nở dần 1440 → 1560)
     1848 → lề 144, khổ 1560
     1920 → lề 180, khổ 1560
     2560 → lề 500, khổ 1560

   Bảng người dùng đưa ghi "1920 · lề 240 · khổ 1560" và "2560 · lề 240 · khổ 1560":
   hai cột đó không thể cùng đúng — lề 240 ở 1920 ra khổ 1440, còn lề 240 ở 2560 ra khổ
   2080 (rộng hơn cả 1920, ngược hẳn mục đích). Bám theo cột KHỔ NỘI DUNG vì đó mới là
   thứ quyết định quãng mắt phải quét. */
@media (min-width: 1728px) {
  :root,
  html.man-desktop {
    --sec-pad: max(144px, calc((100vw - 1560px) / 2));
  }
}

/* ============================================================
   3. NỀN TẢNG
   ============================================================ */
*,
*::before,
*::after {
  box-sizing: border-box;
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img,
video {
  display: block;
  max-width: 100%;
}
a {
  color: inherit;
  text-decoration: none;
}
h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
}
p {
  margin: 0;
}
ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

.container {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
}
/* Lớp chứa mọi section sau hero. Nền ĐỤC và nằm trên (z-index 1) thì mới
   trượt đè lên hero đang dính ở đỉnh; để trong suốt là hero lộ ra sau.
   Đặt nền ở đây chứ không đặt trên .section, vì #work mang thêm class
   .container nên nó hẹp hơn màn hình — nền sẽ hở hai bên. */
.scroll-over {
  position: relative;
  z-index: 1;
  background: var(--bg);
}
.section {
  padding-block: 5rem;
}
@media (min-width: 768px) {
  .section {
    padding-block: 7rem;
  }
}

.eyebrow {
  font-size: 0.75rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
}
.lead {
  font-size: 1.125rem;
  color: color-mix(in srgb, var(--text) 75%, transparent);
}
.small {
  font-size: 0.875rem;
  color: var(--muted);
}

/* Chỗ nào còn là nội dung mẫu thì gạch chân chấm — điền xong thì xoá class .todo */
.todo {
  border-bottom: 1px dotted color-mix(in srgb, var(--accent) 60%, transparent);
}

/* Khung giữ chỗ cho ảnh/video chưa có */
.ph {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  padding: 1rem;
  text-align: center;
  font-size: 0.8125rem;
  color: var(--muted);
  border: 1px dashed color-mix(in srgb, var(--text) 22%, transparent);
  border-radius: inherit;
  background: repeating-linear-gradient(
    45deg,
    transparent,
    transparent 10px,
    color-mix(in srgb, var(--text) 3%, transparent) 10px,
    color-mix(in srgb, var(--text) 3%, transparent) 20px
  );
}
.ph code {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.6875rem;
  opacity: 0.7;
}

.btn,
.btn-dark {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border-radius: 999px;
  padding: 0.625rem 1.25rem;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.25s;
}
.btn {
  border: 1px solid var(--faint);
  background: #fff;
}
.btn:hover {
  background: var(--text);
  color: var(--bg);
}
.btn-dark {
  border: 1px solid var(--text);
  background: var(--text);
  color: var(--bg);
}
.btn-dark:hover {
  opacity: 0.85;
}

/* ============================================================
   4. HERO BANNER (section 1)

   Số đo lấy từ reference/image-home/ref-desktop-home-herobanner.svg
   (khung thiết kế 1440×800). Vị trí ghi bằng % của khung đó nên
   phóng to thu nhỏ vẫn giữ đúng bố cục.

   Headline đã đo ngược ra: Mona Sans 82.17px / weight 850 /
   width 98% / tracking -0.01em / line-height 88px. Sai số so với
   file thiết kế: dưới 2.3px trên cả 3 dòng.
   ============================================================ */
.hero {
  /* Dính ở đỉnh màn hình: hero đứng yên, các section sau trượt ĐÈ LÊN
     nó. z-index 0 để mọi thứ trong hero nằm dưới lớp section (z-index 1). */
  position: sticky;
  top: 0;
  z-index: 0;
  min-height: 100svh;
  overflow: hidden;
  color: #fff;
  padding-bottom: 7.5rem;
  isolation: isolate;
  /* 1 đơn vị = 1px trong khung thiết kế. Mỗi khổ có khung riêng, nên
     chỉ cần đổi --u và vài con số là toàn bộ cụm co giãn theo. */
  /* DESKTOP ≥1440: dốc tuyến tính, chữ 72px ở 1440 → 96px ở 1920.
     Dùng dốc thay vì đổi thẳng con số ở mốc 1920, nếu không sẽ có bước
     nhảy ngay tại đúng 1920px.
     u = cỡ-chữ-muốn / --title-fs → 72/82.17 = 0.87625 và 96/82.17 = 1.16831 */
  /* cy 0.2856 · D 352 · dock+lề 109 + hở 16 = 125
     Trần theo CHIỀU CAO: khung chỉ phóng tới mức khối tiêu đề còn
     nằm trên header-bottom. Suy từ ràng buộc
       cy·H + D·u  ≤  H − (dock + lề đáy) − 16px hở
     với cy = --cluster-y, D = (đáy khối − gốc cụm)/u đo được, dock+lề =
     109px (mobile 95px). max(0.2px, …) chỉ để chặn giá trị âm ở khung
     nhìn thấp phi thực tế. */
  --u: min(
    calc(0.87625px + (100vw - 1440px) * 0.00060846),
    max(0.2px, calc((100svh * 0.7144 - 125px) / 352))
  );
  --title-fs: 82.17;
  --title-lh: 88;
  --chip-h: 49;
  --chip-fs: 18;
  --badge: 36;
  /* căn giữa cả khối chữ + 3 chip; 317 = nửa bề rộng khối (327) trừ
     khoảng cách từ gốc cụm tới mép trái khối (10), theo khung 1440 */
  --cluster-x: calc(50% - 317 * var(--u));
  --cluster-y: 28.56%;
  --i1: 0;
  --i2: 0;
  --i3: 169;
  --i4: 0;
}

/* --- ảnh nền --- */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(#4d8fc4 0%, #79a9cf 34%, #9dbfae 52%, #7f9c4a 72%, #6f8f34 100%);
}
.hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* đổi giá trị này để chọn phần ảnh giữ lại khi cắt trên màn hẹp */
  object-position: center center;
}

/* --- kính mờ dùng chung --- */
.glass {
  background: rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  border: 1px solid rgba(255, 255, 255, 0.28);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
}

/* gạch chân placeholder trên nền ảnh: đổi sang trắng mờ cho đỡ chói */
.hero .todo,
.dock .todo {
  border-bottom-color: rgba(255, 255, 255, 0.45);
}

/* --- ô chờ footage --- */
.slot {
  background: rgba(255, 255, 255, 0.16);
  border: 1px dashed rgba(255, 255, 255, 0.5);
  display: grid;
  place-items: center;
  font-size: max(9px, 0.5rem);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.75);
  text-align: center;
  overflow: hidden;
}

/* --- logo trên đỉnh --- */
.hero__logo {
  position: relative;
  z-index: 3;
  padding-top: 1.75rem;
  text-align: center;
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  /* xoá dòng chữ ảo của thẻ cha: logo là inline-block nên nằm trên
     baseline, phần chừa cho đuôi chữ bên dưới đội gap lên ~6px */
  line-height: 0;
}
.hero__logo img {
  display: inline-block;
  height: 1.75rem;
  width: auto;
}


/* --- thẻ giới thiệu góc trái trên ---
   Theo reference/image-home/ref-component-profile-card.png

   DESKTOP  avatar 40 · padding 20 · radius 24
            default: cục 1 căn giữa theo chiều dọc với avatar
            hover  : avatar lên trên, cục 1 xuống dưới (gap 24)
   TABLET+MOBILE  avatar 36 · padding 12 · gap 16 · không hover

   line-height 1.2 (không phải 1.4): với 1.4, nửa khoảng dòng ở trên và
   dưới cộng thêm ~5px nên gap 12px nhìn thành ~18px — chính là chỗ
   "hai dòng xa nhau quá". */
.hero__profile {
  position: absolute;
  top: 1.75rem; /* thẳng hàng với logo NGX.THUHA */
  left: 2.25rem;
  /* --px = lệch ngang có sẵn (tablet/mobile căn giữa dùng -50%)
     --py = quãng trôi theo cuộn. Khai báo mặc định phải nằm Ở ĐÂY, trước
     mọi media query — media query không cộng thêm độ ưu tiên, cùng độ ưu
     tiên thì rule viết sau thắng, nên để ở dưới là đè mất bản responsive. */
  --px: 0px;
  --py: -24px;
  z-index: 3;
  width: 22.5rem; /* 360px, cố định */
  min-height: 6.5rem; /* 64 (cục 1 cao nhất) + 20×2 padding */
  padding: 1.25rem; /* 20px */
  border-radius: 16px; /* 24px */
}
.hero__avatar {
  position: absolute;
  left: 1.25rem;
  /* neo cứng ở mép trên — thẻ mặc định cao đúng 40+20×2 nên đây CŨNG
     là vị trí căn giữa; khi hover thẻ cao lên thì avatar vẫn đứng yên */
  top: 1.25rem;
  width: 3.75rem; /* 60px */
  height: 3.75rem;
  border-radius: 999px;
  object-fit: cover;
}

/* cục 1 */
.hero__profile .pin {
  position: absolute;
  left: 6.25rem; /* 20 padding + 60 avatar + 16 gap */
  top: 50%;
  translate: 0 -50%;
  transition:
    left 0.45s cubic-bezier(0.4, 0, 0.2, 1),
    top 0.45s cubic-bezier(0.4, 0, 0.2, 1),
    translate 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.hero__profile .status {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.75rem; /* 12px */
  font-weight: 500;
  line-height: 1.2;
  color: #fff;
  transition: font-size 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.hero__profile .status i {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 999px;
  background: #34c75a;
  display: inline-block;
  flex: none;
  animation: blink 1.8s ease-in-out infinite;
}
@keyframes blink {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(52, 199, 90, 0.55); }
  50% { opacity: 0.45; box-shadow: 0 0 0 0.22rem rgba(52, 199, 90, 0); }
}
.hero__profile .where {
  margin-top: 0.5rem; /* gap 8px */
  font-size: 0.75rem; /* 12px */
  font-weight: 500;
  line-height: 1.2;
  color: rgba(255, 255, 255, 0.8);
  white-space: nowrap;
}
.hero__profile .who {
  margin-top: 0.5rem; /* gap 8px, không đổi khi hover */
  font-size: 1rem; /* 16px */
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

/* cục 2 + 3 */
.hero__profile .grow,
.hero__profile .more {
  display: grid;
  /* minmax(0, …) chứ không phải 0fr trần: `0fr` ngầm là minmax(auto, 0fr),
     cận dưới `auto` bám chiều cao thật của con nên hàng không co về 0. */
  grid-template-rows: minmax(0, 0fr);
  overflow: hidden;
  transition: grid-template-rows 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.hero__profile .grow > div {
  /* Avatar và cục 1 đều position:absolute nên KHÔNG chiếm chỗ trong
     luồng. Khối này phủ từ mép trên vùng đệm tới ĐÁY cục 1:
     60 (avatar) + 24 (gap) + 66.4 (cục 1 khi hover) = 150.4px. */
  height: 9.4rem;
}
.hero__profile .more__in {
  opacity: 0;
  transition: opacity 0.3s ease 0.12s;
}
.hero__profile .more p {
  margin-top: 1rem; /* gap 16px giữa các cục */
  font-size: 0.75rem; /* 12px */
  font-weight: 500;
  line-height: 1.55;
  color: #fff;
  text-align: left;
}
.hero__profile .more .tags {
  font-weight: 500;
  color: rgba(255, 255, 255, 0.65);
}

/* ---- DESKTOP ≥1440: có hover. Radius và padding GIỮ NGUYÊN ---- */
@media (min-width: 1440px) {
  .hero__profile:hover .pin {
    left: 1.25rem;
    top: 6.5rem; /* 20 padding + 60 avatar + 24 gap */
    translate: 0 0;
  }
  .hero__profile:hover .status {
    font-size: 0.875rem; /* 14px khi hover */
  }
  .hero__profile:hover .grow,
  .hero__profile:hover .more {
    grid-template-rows: minmax(0, 1fr);
  }
  .hero__profile:hover .more__in {
    opacity: 1;
  }
  .hero__profile:hover .who {
    white-space: normal;
  }
}

/* ---- TABLET + MOBILE: không hover, tên nằm trên ---- */
@media (max-width: 1439px) {
  /* Không có hover nên bỏ định vị tuyệt đối — nếu để absolute thì cục 1
     ra khỏi luồng, thẻ co lại chỉ còn bằng avatar và chữ tràn ra ngoài. */
  .hero__profile {
    display: flex;
    align-items: center;
    gap: 1rem; /* 16px giữa cục 0 và cục 1 */
    /* căn giữa ngang, và neo ĐÁY thẻ cách đỉnh cụm headline một khoảng
       cố định. --cluster-y là % chiều cao hero, nên lấy 100% trừ đi rồi
       cộng gap là ra đúng vị trí dù màn cao thấp thế nào. */
    top: auto;
    left: 50%;
    /* căn giữa bằng --px chứ không ghi thẳng `translate`, vì khai báo
       translate cho parallax nằm sau và sẽ xoá mất -50% */
    --px: -50%;
    /* trôi cùng quãng với cụm headline: ở khổ này thẻ được neo theo đỉnh
       cụm headline, hai bên trôi lệch nhau là khoảng cách đã chốt (64/56px)
       sẽ giãn ra trong lúc cuộn */
    --py: -96px;
    bottom: calc(100% - var(--cluster-y) + var(--profile-gap));
    --profile-gap: 4rem; /* 64px — tablet */
    width: max-content; /* ôm sát nội dung */
    max-width: calc(100% - 2 * var(--pad)); /* không tràn ra ngoài màn hình */
    min-height: 0;
    padding: 1rem; /* 16px */
    border-radius: 16px; /* 16px */
  }
  .hero__avatar {
    position: static;
    translate: none;
    width: 3rem; /* 48px */
    height: 3rem;
    flex: none;
  }
  .hero__profile .pin {
    position: static;
    translate: none;
    display: flex;
    flex-direction: column;
  }
  /* tên lên trên, rồi trạng thái, rồi nơi ở — dùng order chứ không dùng
     column-reverse, vì reverse sẽ hất luôn dòng nơi ở lên trên cùng */
  .hero__profile .who {
    order: -1;
    margin-top: 0;
  }
  /* gap trong cục 1 nhỏ lại còn 4px ở tablet + mobile */
  .hero__profile .status,
  .hero__profile .where {
    margin-top: 0.25rem;
  }
  .hero__profile .grow,
  .hero__profile .more {
    display: none;
  }
  /* bỏ thẻ dự án góc phải dưới — chỉ desktop mới có */
  .hero__work {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero__profile .status i {
    animation: none;
  }
}

/* ------------------------------------------------------------
   TRÔI THEO CUỘN (parallax)
   --sp chạy 0 → 1 trong lúc section 2 leo lên phủ hero (khối JS
   "HERO trôi theo cuộn" ở cuối trang cập nhật nó mỗi khung hình).
   Ảnh nền .hero__bg CỐ Ý không có ở đây — nó đứng yên thì các lớp
   trước mới thấy được là đang trôi.

   Cụm giữa trôi nhiều nhất, hai đầu trên/dưới giữ lại — đọc ra thành
   "tiêu đề rời đi còn khung ở lại".

   Chú ý: parallax dùng `translate`, còn hiệu ứng rời hero dùng
   `transform` hoặc nằm ở thẻ con/cha khác. Hai thuộc tính riêng biệt
   nên chồng được lên nhau mà mỗi cái vẫn giữ nhịp riêng — nếu dùng
   chung một thuộc tính thì cái này ghi đè cái kia.
   ------------------------------------------------------------ */
/* Hai khối này cách đỉnh màn hình đúng 28px, mà hero lại overflow:hidden
   — trôi quá 28px là bị cắt phẳng ở mép, nhìn như lỗi render. Giữ 24px.
   (--px/--py khai báo ở rule .hero__profile gốc phía trên.) */
.hero__profile {
  translate: var(--px) calc(var(--sp, 0) * var(--py));
}
.hero__logo a {
  translate: 0 calc(var(--sp, 0) * -24px);
}
/* KHÔNG đặt lên .hero__stage: bất kỳ giá trị translate nào (kể cả 0) đều
   biến thẻ đó thành khung chứa của con position:absolute. Stage cao 0px
   (con duy nhất của nó là cụm absolute), nên `top: 28.56%` của cụm sẽ
   thành 0 và cả cụm headline nhảy vọt lên đỉnh trang. */
.hero__cluster {
  translate: 0 calc(var(--sp, 0) * -96px);
}
.hero__bottom {
  translate: 0 calc(var(--sp, 0) * -32px);
}

/* --- CỤM tiêu đề + chip ---
   Chip là con của cụm này và định vị bằng --u (đơn vị của khung
   thiết kế), nên khi co kéo màn hình chúng dịch chuyển CÙNG tiêu đề
   chứ không đứng yên một chỗ. Gốc toạ độ của cụm = góc trên-trái
   khối tiêu đề, tức (400, 228.5) trong khung 1440×800. */
.hero__cluster {
  position: absolute;
  /* --fix do JS ghi vào: bù phần lệch tâm còn sót.
     Hằng số K trong --cluster-x tính theo bề rộng chip ở cỡ thiết kế; khi sàn
     chữ 9px làm chip nới ra (màn dưới ~546px) thì bề rộng khối đổi và K không
     còn đúng. JS đo bbox thật rồi bù nốt. Không có JS thì vẫn gần đúng. */
  left: calc(var(--cluster-x) + var(--fix, 0px));
  top: var(--cluster-y);
}
.hero__title {
  position: relative;
  margin: 0;
  font-family: var(--font-body);
  font-weight: 850;
  font-stretch: 98%;
  font-size: calc(var(--title-fs) * var(--u));
  line-height: calc(var(--title-lh) * var(--u));
  letter-spacing: -0.01em;
  text-transform: uppercase;
  white-space: nowrap;
}
.hero__title > span {
  display: block;
}
/* mỗi dòng có độ thụt riêng, đổi theo từng khổ màn hình */
.hero__title > span:nth-child(1) { padding-left: calc(var(--i1) * var(--u)); }
.hero__title > span:nth-child(2) { padding-left: calc(var(--i2) * var(--u)); }
.hero__title > span:nth-child(3) { padding-left: calc(var(--i3) * var(--u)); }
.hero__title > span:nth-child(4) { padding-left: calc(var(--i4) * var(--u)); }

/* ============================================================
   CHIP — số đo lấy từ public/components/component-title-chip.svg
   chip 49 cao, rx 8, bóng -4/4 · badge 36 tròn lệch (+6, -24), viền trắng 1px
   ============================================================ */
.chip {
  position: absolute;
  z-index: 4;
  display: flex;
  align-items: center;
  /* SVG xoay quanh góc trên-trái, không phải tâm — phải khớp thì
     toạ độ left/top mới rơi đúng chỗ */
  transform-origin: 0 0;
  justify-content: center; /* chữ căn giữa, lề hai bên bằng nhau */
  width: calc(var(--chip-w) * var(--u));
  height: calc(var(--chip-h) * var(--u));
  /* Lề ĐỀU hai bên để chữ luôn căn giữa chip. Đặt bằng bề rộng góc gấp
     (~17.6 đơn vị) nên mép phải chữ dừng ngay trước mảng màu đó. */
  padding-inline: calc(20 * var(--u));
  border-radius: 8px;
  background: var(--chip-bg);
  color: #212121;
  /* Sàn 9px: cả website không để chữ nào nhỏ hơn 9px. Ở iPhone X (375px)
     công thức cũ ra 6.43px, ở 320px còn 5.78px — không đọc nổi.
     CHỈ chặn cỡ chữ; chiều cao chip, padding, vị trí vẫn theo --u như đã chốt. */
  font-size: max(9px, calc(var(--chip-fs) * var(--u)));
  font-weight: 500;
  white-space: nowrap;
  box-shadow: calc(-4 * var(--u)) calc(4 * var(--u)) calc(4 * var(--u))
    rgba(0, 0, 0, 0.18);
}
.chip__badge {
  position: absolute;
  left: calc(6 * var(--u));
  top: calc(var(--badge) * var(--u) * -0.667);
  width: calc(var(--badge) * var(--u));
  height: calc(var(--badge) * var(--u));
  border-radius: 999px;
  background: var(--chip-accent);
  border: 1px solid #fff;
  display: grid;
  place-items: center;
  box-shadow: calc(-2 * var(--u)) calc(2 * var(--u)) calc(4 * var(--u))
    rgba(0, 0, 0, 0.22);
}
.chip__badge img {
  width: calc(var(--badge) * var(--u) * 0.667);
  height: calc(var(--badge) * var(--u) * 0.667);
}
/* góc gấp ở mép phải trên — path lấy nguyên từ component-title-chip.svg */
.chip__fold {
  position: absolute;
  right: 0;
  top: 0;
  width: calc(17.63 * var(--u));
  height: calc(16.14 * var(--u));
  overflow: visible;
}

/* --- BỐN MÀU của title-chip ---------------------------------------------
   Đây là toàn bộ bảng màu của component. Nói "title-chip + màu" là chọn
   một trong bốn lớp này; ngoài ra CHỈ đổi icon (.svg trong public/icons)
   và chữ bên trong. Mọi số đo khác — cao 49, bo 8, chữ 18, badge 36,
   góc gấp, bóng — dùng chung, không được sửa theo từng chỗ. */
.chip--green {
  --chip-bg: #f4f8e9;
  --chip-accent: #93ba06;
}
.chip--pink {
  --chip-bg: #f5ebf5;
  --chip-accent: #ec68fd;
}
.chip--purple {
  --chip-bg: #f2eefc;
  --chip-accent: #8168fe;
}
.chip--blue {
  --chip-bg: #e5f2f9;
  --chip-accent: #029cfb;
}

/* --- vị trí 3 chip trong hero (chỉ toạ độ + bề rộng, KHÔNG màu) --- */
.chip--ux {
  /* 871.5-400, 208-228.5 */
  left: calc(471.5 * var(--u));
  top: calc(-20.5 * var(--u));
  rotate: 8.5deg;
  --chip-w: 155;
}
.chip--motion {
  /* 818.5-400, 338.2-228.5 */
  left: calc(418.5 * var(--u));
  top: calc(109.7 * var(--u));
  rotate: 4.13deg;
  --chip-w: 226;
}
.chip--ui {
  /* 390-400, 440.2-228.5 */
  left: calc(-10 * var(--u));
  top: calc(211.7 * var(--u));
  rotate: -8.04deg;
  --chip-w: 180.2;
}

/* --- dùng chip NGOÀI hero -----------------------------------------------
   Hero khai báo --u trên chính nó, section khác không có. Đặt --u: 1px thì
   mọi số đo của component ra đúng px như đã chốt (49 / 18 / 36 / 8 / 20).
   Bề rộng tự co theo chữ, không đặt --chip-w như 3 chip hero. */
.chip--flow {
  --u: 1px;
  --chip-h: 49;
  --chip-fs: 18;
  --badge: 36;
  position: relative;
}

/* --- hàng dưới cùng --- */
.hero__bottom {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 2rem;
  z-index: 3;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
  padding-inline: var(--pad);
  pointer-events: none;
}
.hero__bottom > * {
  pointer-events: auto;
}

/* TẠM ẨN theo yêu cầu — bỏ dòng display này là hiện lại. */
.hero__tagline {
  display: none;
}
.hero__tagline {
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.35;
  max-width: 22rem;
}
.hero__tagline span {
  display: block;
}
.hero__tagline span:nth-child(2) {
  padding-left: 3.2rem;
}
.hero__tagline span:nth-child(3) {
  padding-left: 8.5rem;
}

/* --- thẻ case study, có 2 lớp xếp chồng phía sau --- */
.hero__work {
  position: relative;
  width: 21rem;
  flex: none;
}
/* --- CHỒNG 3 THẺ DỰ ÁN ---
   Số đo theo reference/image-home/ref-project-card stack.png
     ảnh 80×80 · padding thẻ 12 · ảnh↔chữ 12
     role/year 12px · tên 16px · nút 10px
     gap trong Title 2 · Title↔nút 8 · Info đệm trên-dưới 8
   → chiều cao thẻ = 80 + 12×2 = 104px, đúng bằng chiều cao ảnh.

   Ba thẻ đều position:absolute neo ĐÁY. Đóng thì hai thẻ sau hẹp dần và
   nhô lên trên; rê chuột thì cả ba giãn ra full bề rộng, xếp dọc lên
   phía trên. Container chỉ cao bằng MỘT thẻ để cụm luôn neo ở góc dưới
   phải, phần giãn ra tràn lên trên chứ không đẩy layout. */
/* Hiệu ứng xuất hiện: mờ dần + trượt lên.
   Vấn đề: trong lúc opacity < 1 trình duyệt tách cả cụm ra một lớp
   riêng, `backdrop-filter` của từng thẻ mất gốc lấy mẫu nên không làm mờ
   gì — thẻ trước mặt chỉ còn lớp trắng 14% và lộ nguyên hai thẻ sau.
   Cách xử lý:
     · hai thẻ khuất ẩn hẳn (opacity 0) trong lúc cụm đang mờ dần, chỉ
       hiện sau khi cụm đã đục hẳn → không có gì để lộ;
     · opacity chạy nhanh (0.38s) còn transform chạy chậm (0.8s), nên
       lúc lớp mờ tan hết và blur bật lại thì thẻ vẫn đang trượt —
       chuyển động che đi cái nhấp của blur. */
/* HIỆU ỨNG XUẤT HIỆN — không được dùng `opacity` ở đâu trong cụm này.
   Hễ opacity < 1 là trình duyệt tách nhóm ra một lớp riêng và
   `backdrop-filter` mất gốc lấy mẫu → kính không mờ nữa, thẻ trước mặt
   thành ô trắng nhạt lộ hai thẻ sau. Chia hai giai đoạn cũng không cứu
   được: người xem thấy đúng cái hai giai đoạn đó (một thẻ trong suốt
   rồi mới đủ ba thẻ và đúng màu).
   Cách làm ở đây: giữ opacity = 1 suốt, thay vào đó cho CHÍNH các thuộc
   tính tạo nên lớp kính chạy từ 0 lên (nền, viền, bóng, độ blur) cùng
   với nội dung. Mắt vẫn đọc ra là "mờ dần hiện lên", mà kính không bao
   giờ bị vô hiệu nên không có khoảnh khắc nào sai màu. */
.hero__work.load {
  transform: translateY(1.5rem);
  transition: transform 0.8s cubic-bezier(0.22, 1, 0.36, 1) 0.5s;
}
.hero__work.load.show {
  transform: none;
}
.hero__work.load:not(.show) .pcard {
  background: rgba(255, 255, 255, 0);
  border-color: rgba(255, 255, 255, 0);
  box-shadow: none;
  backdrop-filter: blur(0px) saturate(100%);
  -webkit-backdrop-filter: blur(0px) saturate(100%);
}
.hero__work.load:not(.show) .pcard > * {
  opacity: 0;
}
.hero__work {
  --pc-h: 6.5rem; /* 104px */
  --pc-gap: 0.75rem; /* 12px giữa 3 thẻ khi giãn ra */
  height: var(--pc-h);
  pointer-events: auto;
  /* Dòng tagline đang bị ẩn nên .hero__bottom chỉ còn một con;
     justify-content: space-between sẽ đẩy nó về mép TRÁI. */
  margin-left: auto;
  /* Trôi theo cuộn. Thẻ cha .hero__bottom đã trôi 32px, cộng thêm 64px
     ở đây là tổng 96px — bằng đúng cụm headline. Đặt riêng ở đây nên
     dòng tagline bên trái vẫn giữ nguyên 32px như cũ. */
  translate: 0 calc(var(--sp, 0) * -64px);
}
/* Khoảng hở giữa 3 thẻ lúc giãn ra không thuộc phần tử nào — rê chuột
   vào đó là mất :hover, cụm sập lại, chuột lại chạm thẻ, mở ra… giật
   liên tục. Miếng đệm này phủ trọn vùng giãn ra để giữ trạng thái mở;
   nó chỉ nhận chuột KHI ĐÃ mở, nếu không thì đứng cách cả trăm px phía
   trên cũng vô tình bật cụm. z-index -1 để nằm sau thẻ, không chắn click. */
.hero__work::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: -1;
  height: calc(3 * var(--pc-h) + 2 * var(--pc-gap));
  pointer-events: none;
}
.hero__work:hover::after,
.hero__work:focus-within::after {
  pointer-events: auto;
}
.pcard {
  position: absolute;
  left: 50%;
  bottom: 0;
  translate: -50% 0;
  width: 100%;
  height: var(--pc-h);
  display: flex;
  align-items: center;
  gap: 0.75rem; /* 12px: ảnh ↔ cụm chữ */
  padding: 0.75rem; /* 12px */
  border-radius: 16px;
  /* Kính như profile-card nhưng ĐẶC hơn: ba thẻ nằm đè nhau, để 14%
     như profile-card thì lộ nguyên thẻ phía sau xuyên qua.
     saturate hạ xuống 70%: nền là ảnh cỏ, để >100% là kính bị ám xanh. */
  background: rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(24px) saturate(45%);
  -webkit-backdrop-filter: blur(24px) saturate(45%);
  border: 1px solid rgba(255, 255, 255, 0.36);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
  color: #fff;
  /* Chữ trắng trên kính sáng chỉ đạt tương phản 2:1 (đo được) — dưới
     mức 3:1. Bóng chữ này bù lại phần nhìn; muốn đạt chuẩn thật thì
     phải đổi chữ sang màu tối hoặc hạ độ sáng của kính. */
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
  transition:
    /* translate dùng đường cong vọt quá rồi lùi lại → có độ nảy.
       width KHÔNG dùng đường cong này, vọt quá 100% là thẻ phình ra
       rộng hơn khung rồi thụt lại, nhìn như lỗi. */
    translate 0.62s cubic-bezier(0.34, 1.42, 0.64, 1),
    width 0.45s cubic-bezier(0.22, 1, 0.36, 1),
    /* bốn cái dưới đây lo phần "mờ dần hiện lên" lúc tải trang */
    background 0.55s ease-out 0.5s,
    border-color 0.55s ease-out 0.5s,
    box-shadow 0.55s ease-out 0.5s,
    backdrop-filter 0.55s ease-out 0.5s;
}
/* thẻ cuối trong DOM = thẻ trước mặt, nằm dưới cùng và luôn full cỡ */
.pcard:nth-last-child(2) {
  width: 94%;
  translate: -50% -0.4375rem; /* nhô lên 7px */
}
.pcard:nth-last-child(3) {
  width: 88%;
  translate: -50% -0.875rem; /* nhô lên 14px */
}
/* Lúc đóng, hai thẻ khuất chỉ để lộ mép kính — phải giấu NỘI DUNG của
   chúng đi. Kính 20% + blur vẫn cho thấy mờ mờ chữ nằm sau, mà nâng
   độ đục lên nữa thì mất hẳn cảm giác kính. */
.pcard:not(:last-child) > * {
  opacity: 0;
  transition: opacity 0.25s;
}
.pcard > * {
  transition: opacity 0.45s ease-out 0.65s;
}
.hero__work:hover .pcard > *,
.hero__work:focus-within .pcard > * {
  opacity: 1;
  transition: opacity 0.3s 0.12s; /* chờ thẻ trượt ra rồi mới hiện chữ */
}

/* rê chuột: cả ba full bề rộng, xếp dọc lên trên */
.hero__work:hover .pcard,
.hero__work:focus-within .pcard {
  width: 100%;
}
.hero__work:hover .pcard:nth-last-child(2),
.hero__work:focus-within .pcard:nth-last-child(2) {
  translate: -50% calc(-1 * (var(--pc-h) + var(--pc-gap)));
}
.hero__work:hover .pcard:nth-last-child(3),
.hero__work:focus-within .pcard:nth-last-child(3) {
  translate: -50% calc(-2 * (var(--pc-h) + var(--pc-gap)));
}

/* khung ảnh cố định 80×80, cắt phần thừa — ảnh bên trong phóng to
   khi rê chuột mà không tràn ra ngoài khung */
.pcard__img {
  width: 5rem; /* 80px */
  height: 5rem;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.16);
}
.pcard__img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: scale 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
/* cụm Info: Title + nút, đệm trên-dưới 8px, gap 8px */
.pcard__info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 0.5rem;
  padding-block: 0.5rem;
  height: 100%;
}
/* cụm Title: dòng role/year + tên, gap 2px */
.pcard__title {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}
.pcard__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-size: 0.75rem; /* 12px */
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
}
.pcard__name {
  font-size: 1rem; /* 16px */
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.15;
  text-transform: uppercase;
}
.pcard__cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.625rem; /* 10px */
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
}
.pcard__go {
  flex: none;
  width: 1.25rem; /* 20px — cỡ tương ứng với chữ 10px */
  height: 1.25rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.22);
  border: 1px solid rgba(255, 255, 255, 0.38);
  transition: background 0.22s;
}
.pcard__go svg {
  width: 0.75rem; /* 12px */
  height: 0.75rem;
}
/* --- rê chuột vào MỘT thẻ: ảnh phóng trong khung, tên đổi vàng,
       vòng mũi tên đổi sang nền đen --- */
.pcard:hover .pcard__img img,
.pcard:focus-visible .pcard__img img {
  scale: 1.12;
}
.pcard__name {
  transition: color 0.25s;
}
.pcard:hover .pcard__name,
.pcard:focus-visible .pcard__name {
  color: #f2eca2;
}
.pcard:hover .pcard__go,
.pcard:focus-visible .pcard__go {
  background: #111;
  border-color: #111;
  color: #fff;
}

/* ============================================================
   5. DOCK — thanh điều hướng, LUÔN NEO Ở ĐÁY MÀN HÌNH
   ============================================================ */
.dock {
  position: fixed;
  left: 50%;
  bottom: 1.25rem;
  translate: -50% 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.6rem 0.95rem;
  border-radius: 20px;
}
.dock a {
  position: relative;
  width: 4rem; /* 64px */
  height: 4rem;
  display: grid;
  place-items: center;
  /* icon trần — không nền, không viền, không đổ bóng */
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.dock a img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* hover nhấc RIÊNG ảnh icon. Nếu để transform trên cả thẻ <a> thì
     chấm xám (::after của thẻ) sẽ bị kéo lên theo. */
  transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
/* chấm xám dưới icon khi đang bấm / đang ở trang đó */
/* Chấm neo theo cạnh dưới của thanh dock (mọi icon cao bằng nhau nên
   đáy các thẻ <a> thẳng hàng), cách icon một khoảng rõ ràng — không
   dính vào icon và không nhúc nhích khi hover. */
.dock a::after {
  content: "";
  position: absolute;
  bottom: -0.5rem;
  left: 50%;
  translate: -50% 0;
  width: 0.3rem;
  height: 0.3rem;
  border-radius: 999px;
  background: rgba(70, 70, 70, 0.85);
  opacity: 0;
  transition: opacity 0.18s;
}
/* Chấm xám = đang ở section đó. Bám theo trang, không mất khi nhả chuột. */
.dock a.is-active::after {
  opacity: 1;
}
.dock a:hover img {
  transform: translateY(-0.35rem) scale(1.08);
}

/* Cả thanh nở theo icon, nhẹ hơn (1.03 so với 1.08 của icon) và trễ
   0.08s để thành một nhịp kéo theo chứ không nở cùng lúc.
   Dùng `scale` chứ không phải `transform`: thanh đang dùng
   `translate: -50% 0` để căn giữa, ghi transform là mất căn giữa. */
.dock {
  transition: scale 0.34s cubic-bezier(0.22, 1, 0.36, 1) 0.08s;
}
.dock:has(a:hover) {
  scale: 1.03;
}

/* Bấm vào icon thì nảy một nhịp: CHỈ đi lên rồi về, không phóng to
   (phóng to là việc của trạng thái hover).
   Đặt animation lên `translate` — thuộc tính riêng — để không đè mất
   `transform` của hover, và vì animation nằm ở thẻ <img> nên chấm xám
   (::after của thẻ <a>) đứng yên. */
@keyframes dock-bounce {
  0% {
    translate: 0 0;
  }
  30% {
    translate: 0 -0.9rem;
  }
  66% {
    translate: 0 0;
  }
  100% {
    translate: 0 0;
  }
}
/* Tooltip nảy cùng nhịp với icon. Phải có bộ keyframes riêng vì
   `translate` của tooltip đang gánh cả phần -50% để căn giữa ngang —
   dùng chung bộ với icon là tooltip lệch sang phải nửa bề rộng. */
@keyframes dock-bounce-tip {
  0% {
    translate: -50% 0;
  }
  30% {
    translate: -50% -0.9rem;
  }
  66% {
    translate: -50% 0;
  }
  100% {
    translate: -50% 0;
  }
}
.dock a.is-bounce img {
  animation: dock-bounce 1s cubic-bezier(0.32, 0.7, 0.4, 1);
}
.dock a.is-bounce .tip {
  animation: dock-bounce-tip 1s cubic-bezier(0.32, 0.7, 0.4, 1);
}
@media (prefers-reduced-motion: reduce) {
  .dock {
    transition: none;
  }
  .dock:has(a:hover) {
    scale: 1;
  }
  .dock a.is-bounce img,
  .dock a.is-bounce .tip {
    animation: none;
  }
}

/* ============================================================
   6. HERO — MÀN HÌNH NHỎ (0 – 809px)
   Chỉ còn những quy tắc thật sự dùng. Khối cũ ở đây tham chiếu
   .hero__center và .sticker — hai thứ đã bị thay thế khi dựng lại
   hero theo file thiết kế, và nó còn ghi đè left của profile-card
   làm thẻ không căn giữa được.
   ============================================================ */
@media (max-width: 809px) {
  .hero__tagline {
    font-size: 1.0625rem;
  }
  .hero__tagline span:nth-child(2) {
    padding-left: 2rem;
  }
  .hero__tagline span:nth-child(3) {
    padding-left: 4.5rem;
  }
  .dock a {
    width: 3.125rem; /* 50px */
    height: 3.125rem;
  }
}

/* ============================================================
   6. TIÊU ĐỀ SECTION
   ============================================================ */
.head {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}
@media (min-width: 768px) {
  .head {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
  }
}
.head h2 {
  margin-top: 1.25rem;
  max-width: 46rem;
  font-size: clamp(2rem, 5vw, 3.75rem);
  line-height: 1.08;
  text-wrap: balance;
}
.head .small {
  margin-top: 1rem;
  max-width: 34rem;
}

/* ============================================================
   7. THẺ DỰ ÁN
   ============================================================ */
.grid-2 {
  display: grid;
  gap: 1.5rem;
  margin-top: 3.5rem;
}
@media (min-width: 768px) {
  .grid-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .card--wide {
    grid-column: span 2;
  }
}
.card {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  transition: box-shadow 0.5s;
}
.card:hover {
  box-shadow: 0 24px 60px -32px rgba(17, 17, 17, 0.45);
}
.card__media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
@media (min-width: 768px) {
  .card--wide .card__media {
    aspect-ratio: 21 / 9;
  }
}
.card__num {
  position: absolute;
  top: 1.25rem;
  left: 1.25rem;
  padding: 0.25rem 0.625rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.85);
  font-family: var(--font-display);
  font-size: 0.875rem;
  line-height: 1;
  backdrop-filter: blur(4px);
}
.card__body {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.5rem;
}
.card__body h3 {
  font-size: 1.5rem;
  line-height: 1.2;
}


/* ============================================================
   9. JOURNEY
   ============================================================ */
.marquee-wrap {
  overflow: hidden;
  padding-block: 2.5rem;
}
.marquee {
  display: flex;
  width: max-content;
  gap: 2rem;
  white-space: nowrap;
  animation: marquee 22s linear infinite;
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(3rem, 9vw, 6rem);
  color: color-mix(in srgb, var(--text) 15%, transparent);
}
.marquee span span {
  font-style: normal;
}
@keyframes marquee {
  to {
    transform: translateX(-50%);
  }
}

.timeline li {
  display: grid;
  gap: 1.25rem;
  padding-block: 2rem;
  border-top: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
}
@media (min-width: 768px) {
  .timeline li {
    grid-template-columns: 3.5rem 1fr auto;
    gap: 2rem;
  }
}
.timeline__logo {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 16px;
  background: var(--surface);
  overflow: hidden;
}
.timeline__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.75rem;
}
.timeline__head h3 {
  font-size: 1.5rem;
  line-height: 1.2;
}
.tag {
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  background: var(--surface);
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--muted);
}
.timeline p {
  margin-top: 0.75rem;
  max-width: 44rem;
  color: color-mix(in srgb, var(--text) 70%, transparent);
}
.timeline__date {
  font-size: 0.875rem;
  white-space: nowrap;
  color: var(--muted);
}
@media (min-width: 768px) {
  .timeline__date {
    text-align: right;
    padding-top: 0.5rem;
  }
}

/* ============================================================
   10. FOOTER
   ============================================================ */
/* ============================================================
   FOOTER / CONTACT — dựng theo reference/home-contact/ref-home-contact.jpg
   Số đo lấy thẳng từ ref (ảnh 1440×866), xem README phần "Footer".
   ============================================================ */
.footer {
  position: relative;
  z-index: 1; /* cũng phải đè lên hero dính ở dưới */
  background: #181818;
  color: #f7f4ed;
  /* Chỗ thanh dock nổi chiếm: cao 89 + cách đáy 20. KHÔNG chừa bằng padding của
     .footer nữa — làm thế thì dải đen dưới khay dày 130 trong khi ba cạnh kia chỉ 32,
     nhìn ra ngay là lệch. Giờ khay có lề đều bốn cạnh, còn chỗ cho dock chuyển vào
     PADDING ĐÁY CỦA DẢI CUỐI: nền kem chạy xuống dưới dock, chữ và ba icon vẫn nằm
     phía trên nó. Vì hàng thẻ cam là 1fr nên phần padding này lấy từ thẻ cam chứ
     không làm section cao thêm — vẫn đúng một màn. */
  --ft-dock: 109px;
  /* Chừa thanh header nổi — MỘT công thức cho mọi khổ. Lề khay đã lấn sẵn vào vùng
     thanh nên chỉ chừa phần còn THIẾU:
       ≥1440 (lề 88–120, thanh cao 0) → 0 · 1280 (lề 88 > 60) → 0 ·
       1024 (lề 23) → 37 · 810 (lề 18) → 42 · 390 (lề 12) → 48
     Trước đây chỉ khổ ≤767 mới chừa (chừa cứng 60), nên ở tablet khay chui xuống dưới
     thanh 37–42px — đúng chỗ lệch rule người dùng báo. */
  padding-top: max(0px, calc(var(--topbar-h) - var(--ft-lề)));
  /* Lề khay, ĐỀU bốn cạnh. Là biến vì dải cuối phải trừ nó ra khi tính chỗ chừa dock. */
  --ft-lề: clamp(12px, 2.2vw, 32px);
  /* Cao trọn một màn như hero. min- chứ không phải height: khổ thấp (điện thoại nằm
     ngang) thì nội dung vẫn được phép đẩy cao hơn thay vì tràn ra ngoài. */
  min-height: 100svh;
  display: grid;
}
/* lưới nền giống các section tối khác; site.js khớp pha bằng --grid-y */
.footer::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(90deg, var(--tối-line) 0 1px, transparent 1px),
    linear-gradient(180deg, var(--tối-line) 0 1px, transparent 1px);
  background-size: var(--ab-cell) var(--ab-cell);
  background-position: 0 var(--grid-y, 0px);
}

/* ---------- khay kem ---------- */
.ft {
  position: relative;
  margin: var(--ft-lề);
  /* 20 đều ba cạnh. Ref đo ra 14 ở trên và 20–21 hai bên, nhưng nhìn thật thì mép trên
     của thẻ cam hụt hơn hai bên rõ — lấy đều 20. */
  padding: 20px 20px 0;
  border-radius: 24px;
  /* Thẻ cam ăn hết phần cao dư; dải email và dải cuối giữ nguyên chiều cao của chúng. */
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto auto;
  /* lưới mờ như ref, cùng bước ô 97 với các section tối để hai bên thẳng cột */
  background:
    linear-gradient(90deg, rgba(58, 54, 48, 0.05) 0 1px, transparent 1px) 0 0 / var(--ab-cell)
      100%,
    linear-gradient(180deg, rgba(58, 54, 48, 0.05) 0 1px, transparent 1px) 0 0 / 100%
      var(--ab-cell),
    #f7f4ed;
  color: #3a3630;
}

/* Từ 1440 lên, thanh header KHÔNG có nền (xem media query của .topbar): logo-HA và
   icon menu nổi trực tiếp trên nội dung. Section này cao trọn màn nên chúng rơi đúng
   vào mặt kem/cam của khay. Đo được logo và nút menu chiếm y 28–64 ở mọi khổ ≥1440,
   nên đẩy lề lên 72 thì cả hai nằm trọn trên nền đen — và vì lề phải ĐỀU bốn cạnh nên
   ba cạnh kia lên theo. */
@media (min-width: 1280px) {
  .footer {
    /* 88 chứ không 140 như các section khác: lề vào cả BỐN cạnh nên mỗi 1px lề ăn 2px
       chiều cao. Sàn chiều cao của phần nội dung là 604, 850 − 604 = 246 ⇒ lề tối đa
       123 là vừa khít, không còn tí dư nào. Lấy 88 thì sàn là 780 — máy 14" (viewport
       ~820–850 tuỳ có thanh bookmark hay không) còn dư 40–70px. */
    --ft-lề: 88px;
  }
}
/* Contact dùng lề riêng vì phải đều bốn cạnh — nới thêm ở khổ 16 inch trở lên.
   120 chứ không theo công thức của --sec-pad: lề này vào cả bốn cạnh nên còn phải lọt
   chiều cao. Sàn ở lề 120 là 844, thấp hơn khung nhìn của mọi máy 16" (~950+). */
@media (min-width: 1728px) {
  .footer {
    --ft-lề: 120px;
  }
}

/* ---------- thẻ cam ---------- */
.ft__card {
  position: relative;
  overflow: hidden;
  border-radius: 20px;
  /* Đệm TRÊN cố định 40, không co theo màn: bốn con ốc thụt vào 22px và cao 9px (chiếm
     y 22–31), đệm nhỏ hơn ~36 là dòng đầu tiêu đề rơi lên con ốc trên-trái. Đo bản
     trước: tablet 810 đệm 28 → tiêu đề bắt đầu đúng 28, đè lên ốc; 1024 đệm 33, sát
     rạt. Mobile và desktop vốn đã 40 — nay tablet theo cùng luật.
     Đệm DƯỚI vẫn co được vì dưới đó không có gì phải né. */
  /* Trần đệm NGANG 72 chứ không 100: từ ~1030 trở lên `7vw` chạm trần, hai cụm nội dung
     bị bó vào giữa quá nhiều. Hạ trần chỉ ảnh hưởng khổ ≥1030 — ở 1024 `7vw` đã là 71.7
     nên tablet và mobile không đổi một px nào. */
  padding: 40px clamp(24px, 7vw, 72px) clamp(28px, 3.2vw, 40px);
  display: grid;
  gap: 32px;
  /* stretch chứ không center: thẻ cao lên thì cụm chữ phải cao theo, để
     align-content: space-between của .ft__say dàn tiêu đề lên đỉnh và hàng link
     xuống đáy — đúng bố cục ref. Hình thì vẫn căn giữa. */
  align-items: stretch;
  /* Nền lấy thẳng từ ref-box-yellow-background.svg (khung 1470×730):
       gradient tuyến tính từ (0, 313) tới (1506.6, 440.7) — tức nghiêng
       atan2(127.7, 1506.6) ≈ 4.85° so với phương ngang, nên góc CSS là 90 + 4.85;
       ba chặng #003D62 → #008FE6 (70.6%) → #E3B162.
     Lưới: kẻ dọc và ngang bước 97 (đúng bước ô của cả trang), màu #119EF2 5%, dày 2 —
     đọc từ các <line> trong file: dọc ở 51.5, 148.5, … ; ngang ở 178.5, 275.5, … */
  background:
    linear-gradient(90deg, rgba(17, 158, 242, 0.05) 0 2px, transparent 2px) 50.5px 0 / 97px
      100%,
    linear-gradient(180deg, rgba(17, 158, 242, 0.05) 0 2px, transparent 2px) 0 81.5px / 100%
      97px,
    linear-gradient(94.85deg, #003d62 0%, #008fe6 70.6%, #e3b162 100%);
  color: #fff;
}
/* Hai cột chỉ khi màn RỘNG HƠN CAO. Ref là bản 1440×866, tức khổ ngang. Từ lúc
   section cao trọn màn, đặt hai cột trên màn dọc (iPad 1024×1366) làm thẻ cao tới
   1015: tiêu đề dính đỉnh, cụm link dính đáy, hình lửng lơ giữa — trông rỗng.
   Màn dọc thì xếp một cột, hình nằm dưới chữ, nội dung tự lấp đủ chiều cao. */
@media (min-width: 1000px) and (min-aspect-ratio: 1 / 1) {
  /* Xếp ngang thì cụm chữ căn GIỮA theo chiều dọc cho ngang với hình, không dàn
     space-between nữa: dàn ra thì tiêu đề dính đỉnh, hàng link dính đáy, hai thứ rời
     nhau hẳn. Khe giữa tiêu đề và cụm dưới co theo bề rộng cột. */
  /* `.ft__card .ft__say` chứ không phải `.ft__say`: rule gốc `align-content: space-between`
     nằm SAU khối media này trong file và cũng là (0,1,0) nên nó thắng — đo ra cụm chữ
     vẫn dàn hai đầu y như cũ. */
  .ft__card .ft__say {
    align-content: center;
    gap: clamp(28px, 7cqw, 72px);
  }
  .ft__card {
    /* 60 / 40: chữ bên trái, hình bên phải. `3fr 2fr` chia đúng tỉ lệ đó phần còn lại
       sau khi trừ khe. */
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: 24px;
  }
  /* Bỏ trần 490 ở bố cục hai cột — có trần thì từ ~1700 lên hình không ăn đủ 40% nữa.
     Phải viết `.ft__card .ft__art`: rule gốc `max-width: 490px` nằm SAU khối media này
     và cùng độ mạnh nên vẫn thắng — đo ra 1920 và 2560 đều kẹt đúng ở 490. */
  .ft__card .ft__art {
    max-width: none;
  }
}

.ft__say {
  /* Mốc đo cho cỡ chữ tiêu đề — xem .ft__title. Chỉ chặn trục ngang nên không ảnh
     hưởng gì tới grid dọc bên trong. */
  container-type: inline-size;
  display: grid;
  /* Ref: đỉnh chữ ở y=115, hàng link kết thúc ở y=599 trong thẻ cao 620 — tức cụm
     chữ chiếm gần trọn chiều cao, tiêu đề ở trên, cụm link dính đáy.
     `gap` ở đây chỉ là khe TỐI THIỂU: đã có space-between lo phần dàn ra, nên để nhỏ
     thôi. Trước để tới 132 và chính nó làm section không lọt một màn ở 1512×850 —
     cao tự nhiên của cụm chữ vọt lên 556 trong khi chỗ trống chỉ có 482. */
  align-content: space-between;
  gap: clamp(20px, 2vw, 28px);
  min-width: 0;
}
.ft__title {
  margin: 0;
  font-family: var(--font-display);
  /* ĐÚNG BA DÒNG ở mọi khổ. Câu này có ngắt dòng cứng, nên chỉ cần cỡ chữ đủ nhỏ để
     dòng dài nhất (33 ký tự) lọt bề rộng cột. Đo cỡ lớn nhất còn giữ 3 dòng:
       cột 302 → 19 · 516 → 33 · 621 → 39 · 643 → 41 · 734 → 47 · 850 → 54 · 1234 → 79
     Tỉ lệ ổn định ≈ 6.3% bề rộng CỘT. Không dùng vw được: cột không tỉ lệ thuận với
     màn — ở 1024 (một cột) cột rộng 796 nhưng ở 1280 (hai cột) chỉ còn 516, tức màn
     rộng hơn mà cột lại hẹp hơn. cqw bám đúng cột ở mọi bố cục.
     Trần 54px để giữ đúng thang cỡ tiêu đề của các section khác. */
  font-size: clamp(18px, 5.6cqw, 48px);
  font-weight: 500;
  line-height: 1.15; /* ref: bước dòng 62 trên cỡ 54 */
  letter-spacing: -0.01em;
}
.ft__note {
  margin: 0 0 14px;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.82);
}
/* hàng hai link: kẻ mảnh trên–dưới, một kẻ dọc ở giữa (ref: y 518 và 599, dọc x 310).
   Bề rộng cố định 505 như ref — đã thử `fit-content` cho kẻ dừng ngay sau mũi tên
   "About Me" và bỏ, nhìn không đẹp bằng. */
.ft__links {
  display: flex;
  max-width: 505px;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  border-bottom: 1px solid rgba(255, 255, 255, 0.18);
}
.ft__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 18px 29px 18px 0;
  font-size: clamp(15px, 1.25vw, 18px);
  font-weight: 600;
  color: #fff;
}
.ft__link + .ft__link {
  padding-left: 29px;
  border-left: 1px solid rgba(255, 255, 255, 0.18);
}
/* Khác .btxt của menu mở rộng một chỗ: MŨI TÊN HIỆN SẴN, chỉ gạch chân mới đợi rê
   chuột rồi chạy từ trái sang. Gạch chân đặt ở `bottom: 14px` chứ không phải 0: link
   này có đệm dọc 18px nên để 0 thì vạch rơi xuống sát đường kẻ của hàng, trông như hai
   vạch chồng nhau. */
.ft__link svg {
  width: 1.15em;
  height: 1.15em;
  overflow: visible;
  transition: translate 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.ft__link:hover svg,
.ft__link:focus-visible svg {
  translate: 4px 0;
}
.ft__link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 29px;
  bottom: 14px;
  height: 1.5px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.ft__link + .ft__link::after {
  left: 29px;
}
.ft__link:hover::after,
.ft__link:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* ---------- hình hai nửa đĩa ---------- */
/* Hình PHẢI co được theo chiều cao còn lại, không thì chính nó quyết định thẻ cao bao
   nhiêu (490 rộng ⇒ 500 cao) và section không lọt một màn ở khổ 1512×850.
   align-self: stretch + min-height: 0 để ô hình nhận đúng chiều cao hàng, còn SVG có
   viewBox nên preserveAspectRatio mặc định tự thu vừa khung và căn giữa. */
.ft__art {
  position: relative;
  align-self: stretch;
  justify-self: end;
  width: 100%;
  max-width: 490px;
  min-height: 0;
}
/* `<picture>` phải `display: contents`: nó là hộp inline bao ngoài, để mặc định thì
   `inset: 0` của <img> neo vào NÓ chứ không vào `.ft__art` (picture không position) —
   hình rơi về kích thước gốc. `contents` xoá hộp đó đi, <img> thành con trực tiếp. */
.ft__art picture {
  display: contents;
}
/* Hình đặt TUYỆT ĐỐI, không để trong luồng. `height: 100%` trong luồng vẫn không cứu
   được: lúc trình duyệt đo chiều cao tự nhiên của hàng thì chiều cao cha còn chưa
   xác định, hình rơi về kích thước gốc và lại chính nó quyết định thẻ cao bao
   nhiêu — đo được thẻ vẫn 628 và section vẫn 996 > 850. Tuyệt đối thì nó không góp
   gì vào phép đo, hàng cao bao nhiêu là do cụm chữ. */
.ft__art img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
/* ---------- email + số điện thoại ---------- */
.ft__reach {
  display: grid;
  gap: 6px 40px;
  padding: 20px 30px 24px;
}
@media (min-width: 640px) {
  .ft__reach {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.ft__lbl {
  margin: 0 0 8px;
  font-size: 15px;
  line-height: 1.2;
  color: #6b665e;
}
.ft__val {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0;
  font-size: clamp(22px, 2.1vw, 30px);
  font-weight: 500;
  line-height: 1.1;
  color: #0d9bee;
}
/* Cùng dải gradient với tiêu đề "Hi, I'm Thu Ha" của section About. */
.ft__val a {
  background: linear-gradient(90deg, #014268, #0d9bee);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.ft__val a:hover {
  text-decoration: underline;
}
/* Nút chép: dùng lại .copy của menu mở rộng, GIỮ NGUYÊN hành vi — nhãn "Copy" trượt
   ra khi rê chuột, đổi thành "Copied" kèm dấu tích khi bấm. Chỉ đổi cỡ và màu.
   Icon vẽ bằng mask + background nên đổ được gradient y như chữ. */
.copy--ft {
  height: 22px;
  color: #0d9bee;
  opacity: 0.9;
}
.copy--ft .copy__ico {
  width: 22px;
  height: 22px;
}
.copy--ft .copy__ico i {
  background: linear-gradient(90deg, #014268, #0d9bee);
}
/* `.ft__val .copy--ft:hover` chứ không phải `.copy--ft:hover`: rule `.copy:hover` của
   bản trong menu đặt màu vàng nhạt #f2eca2 (hợp nền tối), nằm SAU trong file và cùng
   độ mạnh (0,2,0) nên nó thắng — đo ra chữ "Copy" lúc rê chuột là rgb(242,236,162),
   gần như mất hút trên nền kem. */
.ft__val .copy--ft:hover {
  color: #014268;
  opacity: 1;
}

/* ---------- dải cuối ---------- */
.ft__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
  /* Chỗ chừa dock phải TRỪ ĐI LỀ: lề đáy của khay đã lấn sẵn vào vùng dock rồi. Không
     trừ thì tính trùng — ở 1512×850 nó ăn thêm 86px và section vọt lên 928 > 850, mất
     luôn cái "cao trọn một màn". */
  padding: 10px 30px calc(max(0px, var(--ft-dock) - var(--ft-lề)) + 10px);
  border-top: 1px solid #e4dfd9;
  background: #faf6ed;
  border-radius: 0 0 24px 24px;
}
/* MỘT FONT cho cả site: dòng này trước dùng mono hệ thống — chỗ mono cuối cùng còn lại.
   Dáng "nhãn nhỏ" giữ bằng weight 500 + giãn chữ, không bằng đổi font. Sửa ở đây là cả
   trang chủ, My Work và các trang chi tiết đổi theo, vì section Contact dùng chung. */
.ft__made {
  margin: 0;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  line-height: 1.5;
  color: #978f83;
}
/* Ba icon: cơ chế nở ngang lấy nguyên của .panel__social, chỉ thay lớp áo.
   File .svg vẽ cho nền TỐI (nét trắng, nền trắng 10%) nên trên nền kem phải lật màu:
   invert(1) chỉ đảo kênh màu, không đụng alpha — nét thành đen, nền tròn thành đen 10%. */
/* Hàng 5 icon chỉ dùng cho bản mobile — mặc định ẩn.
   Phải viết `.ft__reach .ft__soc--mb`: khối .panel__social nằm SAU trong file và cũng
   là (0,1,0) nên `display: flex` của nó thắng, đo được desktop vẫn hiện đủ 5 icon. */
.ft__reach .ft__soc--mb {
  display: none;
}
/* Phải viết .ft__foot .ft__soc chứ không phải .ft__soc: khối .panel__social nằm SAU
   trong file, cùng độ mạnh (0,1,0) thì nó thắng — đo được dải cuối cao 104 thay vì 70
   vì margin-top/padding-top 1rem và viền đứt của bản menu vẫn còn nguyên. */
.ft__foot .ft__soc {
  --soc: 2.25rem;
  flex: none;
  margin: 0 0 0 auto;
  padding: 0;
  border: 0;
}
.ft__foot .ft__soc a {
  /* nền đục, xem chú thích ở hàng icon bản mobile */
  background: #faf6ed;
  transition: width 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.ft__foot .ft__soc img {
  /* xem chú thích ở hàng icon bản mobile — nhạt bằng filter, không bằng opacity */
  filter: invert(1) opacity(0.72);
}
/* viền gradient của bản menu là trắng — trên nền kem thì thừa, tắt đi */
.ft__foot .ft__soc a::after {
  display: none;
}

/* Xếp một cột: hình nằm dưới cụm chữ. SVG đặt tuyệt đối nên ô phải có chiều cao
   THẬT, max-height không đủ. Điều kiện là phủ định của rule hai cột ở trên. */
@media (max-width: 999px), (max-aspect-ratio: 1 / 1) {
  /* Xếp một cột thì gradient nền chạy TỪ TRÊN XUỐNG — xanh ở trên, vàng ở dưới. Cùng
     ba chặng màu, chỉ đổi phương: ngang chỉ hợp khi hai cụm nằm cạnh nhau. */
  .ft__card {
    background:
      linear-gradient(90deg, rgba(17, 158, 242, 0.05) 0 2px, transparent 2px) 50.5px 0 /
        97px 100%,
      linear-gradient(180deg, rgba(17, 158, 242, 0.05) 0 2px, transparent 2px) 0 81.5px /
        100% 97px,
      linear-gradient(184.85deg, #003d62 0%, #008fe6 70.6%, #e3b162 100%);
  }
  /* Một cột thì KHÔNG dàn space-between nữa: cụm chữ dồn lên trên, hình lấy hết phần
     cao còn lại. Để space-between như bản hai cột thì trên iPad dọc (thẻ cao 1015) ra
     một khe trống 160px giữa tiêu đề và hàng link, rồi lại thừa 165px dưới hình. */
  .ft__card {
    grid-template-rows: auto minmax(120px, 1fr);
  }
  .ft__say {
    align-content: start;
    gap: clamp(20px, 3vw, 36px);
  }
  /* Bỏ trần 380: đo ra chính nó mới là thứ ghìm chữ H bé ở màn dọc, KHÔNG phải chiều
     cao hàng. iPad Pro 12.9 dọc lòng thẻ rộng 796 mà ô hình kẹt ở 380 — chưa tới nửa.
     Cho ô ăn trọn bề rộng lòng thẻ, hình `contain` tự căn giữa. */
  .ft__art {
    align-self: stretch;
    justify-self: center;
    max-width: none;
    height: auto;
  }
}

/* ---------- CỬA SỔ HẸP mà RẤT CAO (co kéo cửa sổ desktop về khổ mobile) ----------
   Mốc 1050px là cố ý: điện thoại thật cao nhất hiện nay cũng chỉ ~1000 CSS px
   (iPhone 16 Pro Max 956, Pixel 9 Pro XL 998), nên khối này KHÔNG bao giờ chạm tới bản
   mobile — bản mobile giữ nguyên đúng như đã chốt.

   Chỗ thừa ở đây rất lớn: 390×1600 thẻ cam cao 1330 mà nội dung chỉ ~400. Mặc định của
   grid là `align-content: normal` ⇒ `stretch`, nên các hàng `auto` NỞ RA chia nhau chỗ
   thừa (đo: hàng chữ 639, hàng hình 620) — hình bị kéo ô cao 620 rồi vẽ 302×185 ở giữa,
   thừa 436px. Ở đây cho mọi thứ ÔM nội dung và dồn lên trên: cụm chữ sát lề trên, hình
   nằm ngay dưới nó, phần dư cắt khỏi thẻ xanh và thẻ kem luôn. */
@media (max-width: 767px) and (min-height: 1050px) {
  /* Thẻ kem và thẻ xanh VẪN cao trọn màn — không co cụm lại. Chỉ đổi cách xếp BÊN
     TRONG thẻ xanh: hai hàng ôm nội dung và dồn lên lề trên, chỗ dư dồn hết xuống đáy
     thẻ xanh. (Bản trước cho cả hai thẻ ngắn lại, để hở một mảng đen tới tận dock —
     Thu Hà bảo không phải thế.) */
  .ft__card {
    align-content: start;
  }
  /* Khối ≤999 để `align-self: stretch`; không ghi đè thì ô hình vẫn bị kéo cao theo
     hàng và `aspect-ratio` mất tác dụng — hình lại lửng lơ giữa khoảng trống. */
  .ft__art {
    align-self: start;
  }
}

@media (max-width: 767px) {
  .ft {
    margin: 12px;
    padding: 10px 10px 0;
  }
  .footer {
    --ft-dock: 95px; /* dock ở khổ này cao 75 + cách đáy 20 */
    --ft-lề: 12px;
  }
  /* Điện thoại: hình nằm DƯỚI cụm chữ.
     Thẻ cam ở khổ này KHÔNG còn chút dư nào — đo: đệm 44 + cụm chữ 274 là vừa hết —
     nên mỗi px chiều cao hình lấy thêm là section cao thêm đúng 1px. 11svh ≈ 93 ở
     iPhone 390×844, vừa đủ để cả section vẫn lọt một màn. */
  /* Ô hình phải ÔM SÁT hình vẽ, và ở khổ này lấy đúng 320×320 — file h-390.png được
     vẽ cho đúng cỡ đó để chữ trong hình còn đọc được. Đổi sang lái theo BỀ RỘNG
     (`width: 100%` + trần 320) chứ không lái theo chiều cao: lòng thẻ ở iPhone 390 chỉ
     rộng 302, để `height: 320; width: auto` thì ô đòi 320 rộng và tràn ra khỏi thẻ.
     Đánh đổi: section không còn lọt đúng một màn ở điện thoại (xem ghi chú bên dưới). */
  .ft__art {
    width: 100%;
    /* `min(100%, …)` chứ không phải 320px trơn: khối ≤999 để `align-self: stretch` nên
       ô bị kéo cao theo hàng, rồi `aspect-ratio` suy ngược ra bề rộng 404 và chỉ bị
       chặn bởi max-width — đo ra ô rộng 320 trong lòng thẻ chỉ 302, thò ra 9px mỗi bên. */
    max-width: min(100%, 320px);
    /* Bắt buộc. Grid item có kích thước tối thiểu TỰ ĐỘNG bằng min-content; với
       `aspect-ratio` + `align-self: stretch` thì min-content suy từ chiều cao đã bị kéo
       (251 × 1.629 ≈ 409) và ép luôn cột grid rộng 394.75 trong lòng thẻ chỉ 302 — cả
       cụm chữ bị kéo theo, thò khỏi thẻ. `min-width: 0` gỡ đúng chỗ đó. */
    min-width: 0;
    height: auto;
    /* Đúng tỉ lệ file `h-390.png` SAU KHI cắt (640×393). Để 1/1 như trước thì ô cao
       302 mà nét vẽ chỉ 185 — thừa 117px trong suốt, chính chỗ đó làm section tràn
       khỏi một màn. Cắt nền thừa không làm chữ H nhỏ đi chút nào. */
    aspect-ratio: 640 / 393;
  }
  /* Đệm TRÊN lớn hơn hai bên: bốn con ốc nằm thụt vào 22px, mà đệm cũng 22px nên dòng
     đầu tiêu đề rơi đúng lên con ốc trên-trái — đo được cả hai cùng ở (22, 22). */
  .ft__card {
    grid-template-rows: auto auto;
    padding: 40px 22px 22px;
    gap: 8px;
  }
  .ft__title {
    line-height: 1.15;
  }
  .ft__note {
    margin-bottom: 10px;
  }
  .ft__reach {
    padding: 12px 18px 10px;
  }
  /* MOBILE: bỏ hai ô chữ email/sđt, thay bằng hàng 5 icon; hàng 3 icon ở dải cuối cũng
     nghỉ vì đã nằm trọn trong bộ 5 này. */
  .ft__cell {
    display: none;
  }
  .ft__reach {
    display: block;
  }
  .ft__reach .ft__soc--mb {
    display: flex;
    --soc: 2.75rem;
    margin: 0;
    padding: 0;
    border: 0;
    justify-content: center;
    gap: 0.625rem;
  }
  /* Nền tròn của icon phải ĐỤC. File .svg chỉ có nền trắng 10% nên lưới mờ của khay
     lộ nguyên qua. Đặt nền kem đặc lên chính thẻ <a>, và chuyển invert(1) xuống <img>
     — để trên <a> thì nền kem cũng bị lật thành đen. */
  .ft__reach .ft__soc--mb a {
    background: #f7f4ed;
  }
  /* Làm nhạt bằng FILTER chứ không bằng opacity: .panel__social giấu ảnh hover bằng
     `opacity: 0`, đặt opacity: .72 lên mọi <img> là ảnh hover cũng hiện 72% — thấy rõ
     một vệt sẫm ở mép phải mỗi icon. filter chồng lên nhau nên vẫn nhạt đúng mức mà
     không đụng vào cơ chế đổi ảnh khi rê chuột. */
  .ft__reach .ft__soc--mb img {
    filter: invert(1) opacity(0.72);
  }
  .ft__reach .ft__soc--mb a::after {
    display: none;
  }
  .ft__foot .ft__soc {
    display: none;
  }
  .ft__foot {
    justify-content: center;
  }
  .ft__foot {
    padding: 10px 18px calc(max(0px, var(--ft-dock) - var(--ft-lề)) + 10px);
  }
  .ft__foot .ft__soc {
    --soc: 2rem;
  }
  .ft__links {
    flex-direction: column;
    align-items: start;
  }
  .ft__link {
    padding: 8px 0;
    font-size: 12px;
  }
  /* Gạch chân lúc rê: `bottom: 14px` của bản gốc canh theo đệm dọc 18px; ở đây đệm chỉ
     còn 8 nên phải kéo xuống, không thì vạch cắt ngang giữa chữ. */
  .ft__link::after {
    bottom: 4px;
  }
  /* Nút thứ hai KHÔNG kéo giãn nữa. `align-self: stretch` cũ làm nó rộng trọn 302 trong
     khi chữ chỉ ~100, mà gạch chân `::after` bám mép nút ⇒ vạch dài gấp ~2.4 lần chữ.
     Cho nút ôm chữ thì `left/right: 0` ra đúng bề rộng chữ + mũi tên. */
  .ft__link + .ft__link {
    padding-left: 0;
    border-left: 0;
    border-top: 0;
    align-self: start;
  }
  /* Đường kẻ giữa hai nút vẽ bằng nền của HÀNG, không phải viền của nút — có thế mới
     chạy hết bề rộng cho khớp hai kẻ trên/dưới, trong khi nút vẫn ôm chữ.
     Hai nút cùng cỡ chữ, cùng đệm, mỗi nút một dòng nên mốc 50% là đúng tâm. */
  .ft__links {
    background-image: linear-gradient(rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.18));
    background-repeat: no-repeat;
    background-size: 100% 1px;
    background-position: 0 50%;
  }
  .ft__link::after {
    left: 0;
    right: 0;
  }
  .ft__link + .ft__link::after {
    left: 0;
  }
}

/* ============================================================
   11. HIỆU ỨNG
   ============================================================ */
.load,
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}
.load.show,
.reveal.show {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .load,
  .reveal,
  .rise {
    opacity: 1;
    transform: none;
    translate: none;
    transition: none;
  }
  /* 4 icon focus chạy vòng lặp vô hạn — phải tắt hẳn, không chỉ tạm dừng */
  .fic * {
    animation: none;
  }
  .wnote,
  .wlist {
    opacity: 1;
    translate: none;
    transition: none;
  }
  .marquee {
    animation: none;
  }
}

/* ============================================================
   12. USER CURSOR — đôi mắt bám THEO SAU con trỏ chuột

   Con trỏ hệ thống được GIỮ NGUYÊN. Đôi mắt chỉ là hình đính kèm,
   đi sau con trỏ một nhịp và đổi pose theo hướng di chuyển.
   ============================================================ */

/* Lớp chứa: phủ kín viewport, overflow hidden để mắt không tạo thanh cuộn */
.cursor-layer {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 9999;
  overflow: hidden;
  pointer-events: none;
}
.cursor-on .cursor-layer {
  display: block;
}

.cursor {
  position: absolute;
  top: 0;
  left: 0;
  width: 62px;
  height: 36px;
  opacity: 0;
  transition: opacity 0.18s;
  will-change: transform;
}
.cursor.is-visible {
  opacity: 1;
}
.cursor svg {
  display: block;
  width: 62px;
  height: 36px;
}

/* ------------------------------------------------------------
   Cursor riêng cho từng khu vực (sẽ thêm sau)
   Cách dùng: đặt data-cursor="tên" lên section hoặc element,
   JS sẽ gán tên đó vào data-variant của .cursor.

     <section data-cursor="work"> … </section>

     .cursor[data-variant="work"] svg { transform: translate(-50%,-50%) scale(1.4); }
     .cursor[data-variant="work"] .cursor__sclera { fill: var(--accent); }
   ------------------------------------------------------------ */

/* ============================================================
   7. MOTION của hero
   ============================================================ */

/* Hiệu ứng xuất hiện cho những thành phần KHÔNG dùng .load.
   Dùng animation chứ không dùng transition vì:
     · .dock đã có transition riêng cho cú nở khi hover — thêm .load là
       khai báo transition của .load ghi đè, mất luôn cú nở đó;
     · .menu / .brand-ha có rule đổi opacity ở trạng thái khác (panel mở,
       đã cuộn) — transition của .load sẽ xung đột.
   fill-mode `backwards` (không phải `forwards`/`both`): giữ trạng thái
   đầu trong lúc chờ delay, nhưng chạy xong thì KHÔNG khoá giá trị lại —
   nếu khoá thì `body.panel-open .menu { opacity: 0 }` mất tác dụng vì
   animation luôn thắng khai báo thường. */
@keyframes riseIn {
  from {
    opacity: 0;
    transform: translateY(0.75rem);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
.hero__bg {
  animation: fadeIn 0.9s ease-out backwards;
}
.menu {
  animation: riseIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.24s backwards;
}
.dock {
  animation: riseIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.78s backwards;
}
@media (prefers-reduced-motion: reduce) {
  .hero__bg,
  .menu,
  .dock,
  .brand-ha {
    animation: none;
  }
}

/* --- tiêu đề: từng dòng trồi lên lần lượt ---
   Phải ghi rõ .hero .chip, KHÔNG phải .chip: title-chip giờ còn dùng ở
   section khác, mà quy tắc này đặt opacity: 0 và mở van bằng body.is-ready.
   Để .chip trần thì chip ngoài hero cũng bị ẩn theo. */
.hero__title > span,
.hero .chip {
  opacity: 0;
  transform: translateY(0.35em);
  animation: lineUp 0.75s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(0.25s + var(--l) * 0.12s);
  /* Tạm dừng cho tới khi màn chờ tan. Không có dòng này thì cả 4 dòng tiêu đề
     và 3 chip chạy hết sau lưng màn chờ, người xem chẳng thấy gì.
     paused dừng cả phần delay, nên khi chạy lại vẫn giữ đúng thứ tự so le. */
  animation-play-state: paused;
}
body.is-ready .hero__title > span,
body.is-ready .hero .chip {
  animation-play-state: running;
}
.hero__title > span {
  display: block;
}
/* chip trồi lên nhẹ hơn vì nó nhỏ, và giữ nguyên góc nghiêng */
.hero .chip {
  transform: translateY(0.6rem);
}
@keyframes lineUp {
  to {
    opacity: 1;
    transform: none;
  }
}

/* --- ba dấu chấm chạy như đang tải --- */
.hero__title .dots {
  display: inline-block;
  opacity: 1;
  transform: none;
  animation: none;
  margin-left: 0.12em;
}
.dots i {
  font-style: normal;
  display: inline-block;
  opacity: 0.25;
  animation: dotPulse 1.4s ease-in-out infinite;
}
.dots i:nth-child(2) {
  animation-delay: 0.18s;
}
.dots i:nth-child(3) {
  animation-delay: 0.36s;
}
@keyframes dotPulse {
  0%,
  60%,
  100% {
    opacity: 0.25;
    transform: translateY(0);
  }
  30% {
    opacity: 1;
    transform: translateY(-0.12em);
  }
}

/* --- logo: từng ký tự bắt đầu bị lật ngang, rồi lật đúng lần lượt,
       ký tự cuối về đúng ở khoảng giây thứ 10 --- */
.hero__logo a {
  display: inline-block;
}
/* Logo: chữ Mona Sans, mỗi ký tự đảo qua nhiều chữ khác (và lật ngang)
   rồi lần lượt chốt về đúng NGX.THUHA trong khoảng 6 giây. */
.logo {
  display: inline-flex;
  height: 20px;
  align-items: center;
  font-size: 20px;
  line-height: 1;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #fff;
  font-feature-settings: "tnum";
}
.logo b {
  display: inline-block;
  font-weight: inherit;
  /* chỉ giữ bề rộng tối thiểu trong lúc đang đảo chữ, để không giật;
     chốt xong thì trả về bề rộng tự nhiên — nhờ vậy dấu chấm hẹp lại
     và NGX / THUHA không bị đẩy xa nhau */
  min-width: 0.6em;
  text-align: center;
  transition: transform 0.12s;
}
.logo b.done {
  min-width: 0;
}
.logo b.spin {
  transform: scaleX(-1);
}
/* --- dock: tooltip khi rê chuột --- */
.dock a {
  position: relative;
  overflow: visible;
}
.dock .tip {
  position: absolute;
  /* 12px cách icon + bù ~8px do icon nhấc lên và phóng to khi hover */
  bottom: calc(100% + 1.25rem);
  left: 50%;
  translate: -50% 0;
  padding: 0.3rem 0.6rem;
  border-radius: 4px;
  background: #fff;
  color: #111;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  text-transform: none;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
  opacity: 0;
  transform: translateY(0.3rem) scale(0.94);
  transition:
    opacity 0.2s,
    transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
/* mũi nhọn nhỏ chỉ xuống icon */
.dock .tip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  translate: -50% -1px;
  border: 5px solid transparent;
  border-top-color: #fff;
}
.dock a:hover .tip,
.dock a:focus-visible .tip {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .hero__title span,
  .logo .sl {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .dots i {
    animation: none;
    opacity: 1;
  }
}

/* ============================================================
   8. TABLET 810–1439px — khung thiết kế 810×800
   ============================================================ */
@media (max-width: 1439px) and (min-width: 810px) {
  .hero {
    /* Chữ 48px ở 810 → 72px ở 1215, từ 1215 trở lên đứng yên 72px.
       970.3 = 810 / (48/57.5): mẫu số này khiến u = 0.8348 ở đúng 810px.
       Trần 1.252174 = 72/57.5 → khớp cỡ chữ desktop ở 1440px, qua mốc
       breakpoint không nhảy cỡ.
       Phải có trần: để nguyên 100vw/810 thì ở sát 1439px khung bị kéo
       1.78×, chữ vọt lên to hơn cả bản desktop và khối tiêu đề dài
       xuống đè vào header-bottom. */
    --u: min(
      calc(100vw / 970.3),
      1.252174px,
      /* cy 0.3963 · D 244 */
      max(0.2px, calc((100svh * 0.6037 - 125px) / 244))
    );
    --title-fs: 57.5; /* cap 41.9 / 0.729 */
    --title-lh: 61;
    --chip-h: 37;
    --chip-fs: 13;
    --badge: 27;
    /* Căn giữa cả khối (chữ + 3 chip) thay vì neo theo % cố định.
       Với % cố định, khi --u chạm trần thì khối ngừng to ra còn lề trái
       vẫn nở theo màn hình → khối trôi lệch hẳn sang trái (đo ở 1439px:
       lệch 75px). K = nửa bề rộng khối (251.1) trừ khoảng cách từ gốc
       cụm tới mép trái khối (27.6), tính theo đơn vị khung thiết kế. */
    --cluster-x: calc(50% - 223.5 * var(--u));
    --cluster-y: 39.63%; /* 317.05 / 800 */
    --i1: 27.6;
    --i2: 0;
    --i3: 107;
    --i4: 40;
  }
  .chip--ux    { left: calc(359.9 * var(--u)); top: calc(-27.05 * var(--u)); --chip-w: 116; }
  .chip--motion{ left: calc(283.1 * var(--u)); top: calc(71.95 * var(--u));  --chip-w: 169; }
  .chip--ui    { left: calc(-27.6 * var(--u)); top: calc(142.85 * var(--u)); --chip-w: 135.2; }
}

/* ============================================================
   9. MOBILE 0–809px — khung thiết kế 390×800
   ============================================================ */
@media (max-width: 809px) {
  .hero {
    /* Ba đoạn, nối liền nhau tại 320 / 390 / 585:
         ≤390px : dốc riêng, chữ 28px ở 320 → 32px ở 390 (thu chậm hơn
                  tỉ lệ thuần, nếu không ở 320px chữ chỉ còn 26.3px)
         390–585: thu phóng theo tỉ lệ, chữ 32 → 48px
         ≥585px : đứng yên 48px, khớp cỡ chữ tablet ở 810px
       max() tự chọn đoạn đúng vì hai công thức cắt nhau tại đúng 390px. */
    --u: min(
      max(
        calc(0.680437px + (100vw - 320px) * 0.00138866),
        calc(100vw / 501.5)
      ),
      1.166464px,
      /* cy 0.5068 · D 184 · dock+lề 95 + hở 16 = 111 */
      max(0.2px, calc((100svh * 0.4932 - 111px) / 184))
    );
    --title-fs: 41.15; /* cap 30 / 0.729 */
    --title-lh: 46;
    --chip-h: 27.5;
    --chip-fs: 8.5;
    --badge: 20;
    /* căn giữa khối — xem giải thích ở khổ tablet.
       Đo ở 809px: cách cũ lệch trái tới 111px. */
    --cluster-x: calc(50% - 153.7 * var(--u));
    --cluster-y: 50.68%; /* 405.4 / 800 */
    --i1: 27.7;
    --i2: 0;
    --i3: 87.5;
    --i4: 25.5;
  }
  .hero__profile {
    --profile-gap: 4.5rem; /* 72px — mobile */
  }
  /* Chip xoay 8.5° nên khung bao rộng hơn bề rộng thật:
     87.1·cos + 27.5·sin ≈ 90px. Đặt sao cho lề phải của chip bằng lề
     trái của chữ "THINKING IN" (65.8px) → cụm dòng 1 căn giữa. */
  /* Sàn 9px chỉ chạm ở khổ mobile (8.5 × --u < 9 khi màn dưới ~546px).
     Chip có bề rộng CỐ ĐỊNH theo --u; chữ bị neo ở 9px thì tràn khỏi hộp và
     chui vào nếp gấp. min-width: max-content cho hộp nới đúng bằng
     badge + chữ + lề — lề vẫn đều 20 đơn vị hai bên nên chữ vẫn căn giữa và
     vẫn dừng trước nếp gấp (17.63 < 20) đúng như đã chốt. */
  .hero .chip {
    min-width: max-content;
  }
  .chip--ux    { left: calc(212 * var(--u));   top: calc(-26 * var(--u)); --chip-w: 87.1; }
  .chip--motion{ left: calc(195.9 * var(--u)); top: calc(49.6 * var(--u));  --chip-w: 127; }
  .chip--ui    { left: calc(-15.1 * var(--u)); top: calc(110.8 * var(--u)); --chip-w: 101.4; }
}

/* ============================================================
   13. MENU góc phải trên
   Vị trí đối xứng với profile-card: cách lề phải và cách đỉnh
   đúng bằng khoảng cách của profile-card với lề trái và đỉnh.
   Cỡ chữ 16px, các mục cách nhau 35px — đo từ Menu-expanded.svg
   ============================================================ */
.menu {
  position: fixed;
  top: 1.75rem; /* thẳng hàng với logo NGX.THUHA */
  right: 2.25rem;
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  color: #fff;
}
.menu__list {
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: right;
}
.menu__list li + li {
  margin-top: calc(35px - 1.2em); /* nhịp 35px giữa các mục */
}
.btxt {
  display: inline-flex;
  align-items: center;
  gap: 0.8125rem; /* 13px giữa chữ và mũi tên */
  font-size: 1rem; /* 16px */
  font-weight: 600;
  line-height: 1.2;
  color: inherit;
  position: relative;
  padding-bottom: 0.25rem;
}
/* mũi tên và gạch chân chỉ hiện khi rê chuột */
.btxt svg {
  width: 0;
  height: 1.125rem;
  opacity: 0;
  overflow: visible;
  transition:
    width 0.28s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.22s;
}
.btxt::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1.5px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.btxt:hover svg,
.btxt:focus-visible svg {
  width: 1.125rem;
  opacity: 1;
}
.btxt:hover::after,
.btxt:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* nút hamburger — chỉ dùng ở các section giữa */
.menu__toggle {
  /* absolute: để trong luồng thì nút này chiếm chỗ phía trên và đẩy
     danh sách menu xuống, khiến nó không thẳng hàng với profile-card */
  position: absolute;
  top: 0;
  right: 0;
  display: grid;
  place-items: center;
  width: 2.25rem; /* 36px */
  height: 2.25rem;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
}
.menu__toggle svg {
  width: 2.25rem; /* 36px */
  height: 2.25rem;
}

/* thu gọn: giữa trang, nền sáng nên chữ đổi sang màu mực */
.menu.is-compact {
  color: var(--text);
}
.menu.is-compact.is-open .menu__list {
  translate: 0 0;
  opacity: 1;
  pointer-events: auto;
  margin-top: 0.75rem;
}

/* ============================================================
   HEADER TRÊN CÙNG — CHỈ MOBILE (≤809px)
   Cao 60px: 12px + nút menu 36px + 12px. Logo 28px căn giữa theo chiều
   dọc của thanh. Ở section 1 thanh trong suốt, rời hero thì đổ trắng.
   ============================================================ */
:root {
  --topbar-h: 60px; /* chiều cao header mobile */
}
.topbar {
  display: none;
}
@media (max-width: 809px) {
  .topbar {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--topbar-h);
    z-index: 39; /* dưới logo và nút menu (40), trên mọi section */
    border-bottom: 1px solid transparent;
    transition:
      background 0.28s,
      border-color 0.28s;
    pointer-events: none;
  }
  /* Kính mờ: cùng công thức với thẻ liên hệ trong panel (lớp phủ bán
     trong + backdrop blur + saturate), nhưng lật sang tông SÁNG vì
     header nằm trên nền kem/ảnh trời chứ không phải nền ảnh cỏ tối. */
  body.has-scrolled .topbar {
    background: rgba(255, 255, 255, 0.42);
    backdrop-filter: blur(16px) saturate(150%);
    -webkit-backdrop-filter: blur(16px) saturate(150%);
    border-bottom-color: rgba(0, 0, 0, 0.2);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
  }
  /* panel mở thì thanh cũng phải biến mất cùng nút menu */
  body.panel-open .topbar {
    opacity: 0;
    transition: opacity 0.25s;
  }

  .menu {
    top: 0.75rem; /* 12px — căn giữa nút 36px trong thanh 60px */
    right: var(--pad);
  }
  /* chữ trắng trên ảnh nền, chữ đen khi thanh đã đổ kính */
  body.has-scrolled .menu {
    color: #111;
  }

  /* mobile dùng logo-ha ở góc trái thay cho chữ NGX.THUHA căn giữa.
     Phần .brand-ha nằm ở khối media phía dưới, sau khai báo gốc của nó —
     media query không cộng độ ưu tiên, để ở đây là bị `display: none`
     của rule gốc (viết sau) đè mất. */
  .hero__logo {
    display: none;
  }
}

/* ============================================================
   14. CHUYỂN CẢNH KHI CUỘN (chỉ desktop ≥1440)
   Rời hero: logo bay lên · profile-card trượt sang trái rồi biến mất,
   logo-ha trượt từ trái vào thế chỗ · danh sách menu trượt sang phải
   biến mất, icon hamburger trượt từ phải vào thế chỗ.
   Tới section cuối thì mọi thứ đảo ngược.
   ============================================================ */
.brand-ha {
  display: none;
  position: fixed;
  top: 1.75rem; /* cùng mốc với profile-card mà nó thay thế */
  left: 2.25rem;
  z-index: 40;
  height: 2.25rem; /* 36px, bằng chiều cao icon-menu */
  width: auto;
  line-height: 0; /* <a> là inline, bỏ khoảng thừa dưới ảnh */
  opacity: 0;
  translate: -140% 0;
  transition:
    translate 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.35s;
}

.brand-ha img {
  display: block;
  height: 100%;
  width: auto;
}
/* Bản chữ đen chồng đúng lên bản trắng (cùng viewBox 475×355 nên khít tuyệt đối).
   Mặc định TẮT ở mọi khổ; chỉ khối ≥1440 bật nó lên. */
.brand-ha__đen {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.3s;
}
.brand-ha img {
  transition: opacity 0.3s;
}
/* Đổi sang chữ ĐEN ở MỌI khổ — desktop, tablet, mobile — nhưng CHỈ section About
   (`on-light` do site.js bật, xem chú thích ở đó). KHÔNG dùng `:not(.on-dark)`:
   Selected Projects cũng ngoài danh sách nền đen nhưng nền nó là ảnh trời chạng vạng
   tông trung, chữ đen chìm — phải để trắng. */
body.is-scrolled.on-light .brand-ha img:not(.brand-ha__đen) {
  opacity: 0;
}
body.is-scrolled.on-light .brand-ha__đen {
  opacity: 1;
}

@media (min-width: 1440px) {
  .brand-ha {
    display: block;
  }
  /* Không cần position: fixed nữa — hero đã dính ở đỉnh nên ba khối này
     tự đứng yên theo nó, mà vẫn bị section sau che đúng lớp. */
  .hero__logo {
    transition:
      translate 0.5s cubic-bezier(0.22, 1, 0.36, 1),
      opacity 0.35s;
  }
  /* cụm headline đã dùng `translate` cho parallax → cú đi lên lúc rời
     hero phải đi bằng `transform`, giống profile-card */
  .hero__cluster {
    transition:
      transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
      opacity 0.35s;
  }
  /* profile-card đã dùng `translate` cho parallax, nên cú trượt sang
     trái lúc rời hero phải đi bằng `transform` để hai thứ không đè nhau */
  .hero__profile {
    transition:
      transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
      opacity 0.35s,
      border-radius 0.35s,
      padding 0.35s;
  }

  /* ---- đã rời hero ---- */
  body.is-scrolled .hero__logo {
    translate: 0 -180%;
    opacity: 0;
    pointer-events: none;
  }
  body.is-scrolled .hero__profile {
    transform: translateX(-140%);
    opacity: 0;
    pointer-events: none;
  }
  /* cả cụm headline + 3 chip đi lên rồi tan, cùng kiểu với logo.
     Cuộn ngược lên thì transition tự chạy lại theo chiều ngược. */
  body.is-scrolled .hero__cluster {
    transform: translateY(-60%);
    opacity: 0;
    pointer-events: none;
  }
  body.is-scrolled .brand-ha {
    translate: 0 0;
    opacity: 1;
  }
  /* Icon menu đi cùng logo: đen ở About, trắng ở mọi section còn lại sau khi rời hero.
     Chỉ ở ≥1440 — tablet/mobile có thanh header nền đặc phía dưới nên chữ trắng sẽ
     chìm vào chính thanh đó. */
  body.is-scrolled:not(.on-light) .menu {
    color: #fff;
  }
  body.is-scrolled.on-light .menu {
    color: #111;
  }
}

/* MOBILE: logo-ha hiện thường trực ở góc trái trong thanh header,
   không có màn chuyển cảnh trượt vào như desktop. */
@media (max-width: 809px) {
  .brand-ha {
    display: block;
    top: 1rem; /* 16px — căn giữa logo 28px trong thanh 60px */
    left: var(--pad);
    height: 1.75rem; /* 28px */
    opacity: 1;
    translate: 0 0;
    transition: none;
    animation: riseIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.12s backwards;
  }
}

/* Chữ trong title-chip về 14px ở TABLET (810–1439) — áp cho mọi chip ngoài hero
   (.chip--flow), tức About và Selected Work. Chip trong hero (.chip--ux /
   --motion / --ui) KHÔNG đổi: chúng lấy --u riêng khai báo trên .hero.
   Vẫn đổi --u chứ không đổi riêng font-size, cùng lý do như bản mobile:
   18 × 0.7778 = 14, và cao/bo/đệm/badge co theo đúng tỉ lệ (49 → 38.1,
   36 → 28, 136 → 105.8, 198 → 154). Sửa riêng font-size sẽ để chữ 14 lọt trong
   hộp giữ nguyên bề rộng, hai bên hở một khoảng lớn. */
@media (min-width: 810px) and (max-width: 1439px) {
  .chip--flow {
    --u: 0.7778px;
  }
}

/* ============================================================
   HEADER TRÊN CÙNG — TABLET (810–1439px)
   Trước đây dải này TRỐNG cả hai thứ: .brand-ha chỉ bật ở ≥1440 và ≤809,
   .topbar chỉ bật ở ≤809. Nên rời hero xong chỉ còn hamburger trơ ở góc phải,
   không có logo bên trái và không có nền.
   Mốc bật là body.is-scrolled — ĐÚNG lúc .menu nhận .is-compact (cùng điều kiện
   !inZone trong site.js), nên logo và nền xuất hiện cùng nhịp với hamburger.
   Không dùng has-scrolled (scrollY > 2) như mobile: ở tablet vẫn còn màn chuyển
   cảnh rời hero, bật ngay từ 2px sẽ lệch nhịp với menu.
   ============================================================ */
@media (min-width: 810px) and (max-width: 1439px) {
  .topbar {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--topbar-h);
    z-index: 39; /* dưới logo và nút menu (40), trên mọi section */
    border-bottom: 1px solid transparent;
    transition:
      background 0.28s,
      border-color 0.28s;
    pointer-events: none;
  }
  /* cùng công thức kính với thanh header mobile, không đặt lại số cho khác */
  body.is-scrolled .topbar {
    background: rgba(255, 255, 255, 0.42);
    backdrop-filter: blur(16px) saturate(150%);
    -webkit-backdrop-filter: blur(16px) saturate(150%);
    border-bottom-color: rgba(0, 0, 0, 0.2);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
  }
  body.panel-open .topbar {
    opacity: 0;
    transition: opacity 0.25s;
  }

  /* Logo trượt từ trái vào như bản desktop, KHÔNG hiện thường trực như mobile:
     ở tablet section 1 vẫn có logo NGX.THUHA to trong hero, hai logo cùng lúc
     là trùng lặp. */
  .brand-ha {
    display: block;
    top: 1rem; /* 16px — căn giữa logo 28px trong thanh 60px, cùng số với mobile */
    left: var(--pad);
    height: 1.75rem; /* 28px */
  }
  body.is-scrolled .brand-ha {
    translate: 0 0;
    opacity: 1;
  }

  /* Hamburger tụt vào giữa thanh khi thanh xuất hiện. Chỉ đổi ở trạng thái đã
     cuộn — trong hero menu vẫn là danh sách 4 mục và phải giữ mốc top 28 để
     thẳng hàng với logo lớn của hero. */
  .menu {
    transition:
      top 0.42s cubic-bezier(0.22, 1, 0.36, 1),
      right 0.42s cubic-bezier(0.22, 1, 0.36, 1);
  }
  body.is-scrolled .menu {
    top: 0.75rem; /* 12px — căn giữa nút 36px trong thanh 60px */
    right: var(--pad);
  }
}

/* Menu đổi hình từ TABLET trở lên (≥810): section 1 hiện đủ 4 mục,
   sang section sau thì trượt sang phải và hamburger trượt từ phải vào. */
@media (min-width: 810px) {
  .menu__list,
  .menu__toggle {
    transition:
      translate 0.5s cubic-bezier(0.22, 1, 0.36, 1),
      opacity 0.35s;
  }
  .menu__toggle {
    translate: 140% 0;
    opacity: 0;
  }
  body.is-scrolled .menu__list {
    translate: 140% 0;
    opacity: 0;
    pointer-events: none;
  }
  body.is-scrolled .menu__toggle {
    translate: 0 0;
    opacity: 1;
  }
}

/* MOBILE (<810): không bao giờ có menu mở sẵn — luôn là icon-menu.
   Bấm vào mới bung danh sách ra. */
@media (max-width: 809px) {
  .menu__list {
    display: none;
  }
  .menu.is-open .menu__list {
    display: block;
    margin-top: 0.75rem;
  }
  .menu__toggle {
    translate: 0 0;
    opacity: 1;
  }
}

/* ============================================================
   15. TRÊN 1920px — ngừng phóng to
   Từ 1440 tới 1920 vẫn scale theo bề rộng màn hình như cũ.
   Vượt 1920 thì giữ nguyên kích thước của khung 1920, và neo cụm
   headline theo một khung 1920 căn giữa màn hình — nếu không nó sẽ
   trôi dần sang phải trên màn siêu rộng.
     --u      = 1.6551px (chữ 136px)
     cluster  = 27.78% × 1920 = 533.4px tính từ mép khung
   ============================================================ */
@media (min-width: 1920px) {
  .hero {
    --u: min(
      1.16831px, /* chữ = 82.17 × 1.16831 = 96px */
      max(0.2px, calc((100svh * 0.7144 - 125px) / 352))
    );
    /* --cluster-x không cần khai báo lại: công thức căn giữa ở khổ
       desktop đã ăn theo --u nên tự đúng ở mọi bề rộng. Bản cũ neo
       theo % của 1920 làm khối lệch phải 98px. */
  }
}

/* ============================================================
   16. HAMBURGER → DẤU CỘNG khi rê chuột
   Vạch 1: hạ xuống giữa rồi xoay 90° thành vạch dọc
   Vạch 2: mờ dần về 0
   Vạch 3: đi từ dưới lên giữa, vẫn nằm ngang
   Kèm vòng tròn xám #E7E4DD hiện ra phía sau.
   ============================================================ */
.menu__toggle::before {
  content: "";
  position: absolute;
  inset: 50%;
  width: 2.75rem; /* 44px, rộng hơn icon 36px */
  height: 2.75rem;
  translate: -50% -50%;
  border-radius: 999px;
  background: #e7e4dd;
  scale: 0.4;
  opacity: 0;
  transition:
    scale 0.32s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.24s;
}
.menu__toggle svg {
  position: relative; /* nằm trên vòng tròn */
}
.menu__toggle .l1,
.menu__toggle .l2,
.menu__toggle .l3 {
  /* view-box: tính toạ độ gốc theo hệ viewBox của SVG. Mặc định là
     fill-box (khung bao của chính vạch đó) nên tâm xoay rơi sai chỗ
     và dấu cộng bị lệch. */
  transform-box: view-box;
  transform-origin: 12px 12px;
  transition:
    translate 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    rotate 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.2s;
}

.menu__toggle:hover::before,
.menu__toggle:focus-visible::before {
  scale: 1;
  opacity: 1;
}
.menu__toggle:hover,
.menu__toggle:focus-visible {
  color: #111; /* vòng tròn sáng nên nét phải đậm mới thấy */
}
/* vạch 1: xuống giữa (translate) rồi xoay đứng (rotate) */
.menu__toggle:hover .l1,
.menu__toggle:focus-visible .l1 {
  /* Phép xoay đưa vạch từ (12,7) sang (17,12), nên phải kéo ngược
     5 đơn vị theo trục X mới về đúng tâm (12,12) — không phải kéo
     xuống theo Y như trực giác. */
  translate: -5px 0;
  rotate: 90deg;
}
.menu__toggle:hover .l2,
.menu__toggle:focus-visible .l2 {
  opacity: 0;
}
/* vạch 3: từ dưới lên giữa */
.menu__toggle:hover .l3,
.menu__toggle:focus-visible .l3 {
  translate: 0 -5px;
}

@media (prefers-reduced-motion: reduce) {
  .menu__toggle::before,
  .menu__toggle .l1,
  .menu__toggle .l2,
  .menu__toggle .l3 {
    transition: none;
  }
}

/* ============================================================
   17. PANEL khi bấm icon-plus
   Rộng 478px, cách lề trên/dưới/phải 36px.
   ============================================================ */
.panel {
  position: fixed;
  top: 24px;
  right: 24px;
  bottom: 24px;
  width: 478px;
  max-width: calc(100vw - 48px);
  z-index: 70; /* trên dock (60) — trên mobile panel phủ kín màn */
  display: flex;
  flex-direction: column;
  /* viền ảnh nền lộ ra quanh ảnh bên trong */
  padding: 16px;
  border-radius: 24px;
  /* lớp vàng phủ lên trên để mẫu chữ NGXTHUHA chìm bớt — chữ nằm sẵn
     trong file .jpg nên chỉ làm nhạt được bằng cách phủ, không đổi opacity */
  background:
    linear-gradient(rgba(242, 236, 162, 0.62), rgba(242, 236, 162, 0.62)),
    #f2eca2 url("public/images/image-bg-menu-expanded-click.jpg") center / cover
      no-repeat;
  box-shadow:
    0 24px 60px rgba(20, 12, 5, 0.32),
    0 6px 18px rgba(20, 12, 5, 0.18);
  color: #fffefd;
  opacity: 0;
  translate: 2rem 0;
  transition:
    opacity 0.32s,
    translate 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}
.panel.is-shown {
  opacity: 1;
  translate: 0 0;
}
/* display:flex ở trên ghi đè mất thuộc tính hidden, khiến panel vô hình
   nhưng vẫn phủ lên trang và nuốt hết chuột. Phải trả lại display:none. */
.panel[hidden] {
  display: none;
}

/* ảnh nhỏ bên trong — mọi nội dung nằm đè lên nó */
.panel__inner {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 36px;
  border-radius: 20px;
  overflow: auto;
  background: #7fb0d6 url("public/images/image-bg-menu-expanded-click-small.jpg")
    center / cover no-repeat;
}

/* --close = đường kính nút, icon bên trong luôn bằng 2/3 đường kính */
.panel__close {
  --close: 2.5rem; /* 40px — desktop */
  position: absolute;
  top: 16px;
  right: 16px;
  width: var(--close);
  height: var(--close);
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 999px;
  background: #f2eca2; /* vòng tròn vàng */
  color: #1a1008;
  cursor: pointer;
  transition:
    scale 0.25s cubic-bezier(0.22, 1, 0.36, 1),
    background 0.25s;
}
.panel__close:hover {
  scale: 1.06;
}

/* danh mục: giống menu-expanded nhưng cỡ chữ gấp đôi */
/* 36px = đủ để cụm chữ nằm hẳn dưới nút đóng (nút cao 40, cách đỉnh 16
   → hết ở 56px; đệm khung trong 36 + 36 = 72px) */
.panel__list {
  list-style: none;
  margin: 36px 0 0;
  padding: 0;
}
/* cỡ chữ đặt ở <li> để `em` trong margin dưới đây tính đúng gốc */
.panel__list li {
  font-size: 1.75rem; /* 28px */
  line-height: 1.2;
}
.panel__list .btxt {
  font-size: inherit;
  gap: 1rem;
}
.panel__list .btxt svg {
  height: 1.75rem;
}
.panel__list .btxt:hover svg,
.panel__list .btxt:focus-visible svg {
  width: 1.75rem;
}
/* đỉnh chữ dòng này cách đỉnh chữ dòng kia 50px */
.panel__list li + li {
  margin-top: calc(50px - 1.2em - 0.25rem);
}

/* trang đang mở: chữ vàng + chấm vàng bên phải, đậm hơn các mục khác.
   Chấm dùng `background: currentColor` nên tự ăn theo màu này.
   Class .is-current do JS gắn theo URL (xem khối PANEL cuối trang). */
.panel__list li.is-current .btxt {
  color: #f2eca2;
  font-weight: 750; /* các mục khác 600 */
}
.panel__list li.is-current .btxt::after {
  display: none; /* không gạch chân trang đang đứng */
}
.cur {
  flex: none;
  width: 0.22em;
  height: 0.22em;
  /* gap của .btxt là 16px, kéo lại còn 8px cho sát chữ */
  margin-left: calc(8px - 1rem);
  border-radius: 999px;
  background: currentColor;
}

/* thẻ liên hệ — kính mờ giống profile-card.
   Nền là ảnh cỏ sáng nên phải là kính TỐI thì chữ trắng/cam mới đọc được. */
.panel__card {
  position: relative;
  margin-top: auto;
  /* kéo ra sát mép ảnh: padding của .panel__inner là 36px, còn lại 16px */
  margin-inline: -20px;
  margin-bottom: -20px;
  padding: 1.25rem;
  border-radius: 20px;
  background: rgba(20, 12, 5, 0.42);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
}

/* viền 1px chuyển sắc trắng — mép trên sáng, mép dưới mờ, cho ra cảm
   giác kính bắt sáng. Vẽ bằng gradient rồi khoét ruột bằng mask, vì
   border-color không nhận gradient. */
.glass-ring {
  position: relative;
}
.glass-ring::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(
    150deg,
    rgba(255, 255, 255, 0.85),
    rgba(255, 255, 255, 0.18) 42%,
    rgba(255, 255, 255, 0.1) 60%,
    rgba(255, 255, 255, 0.55)
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.panel__card .lbl {
  font-size: 0.875rem;
  color: rgba(242, 237, 230, 0.72);
}
.panel__card .lbl + .val {
  margin-top: 0.25rem;
}
.panel__card .val {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.875rem;
  font-size: 1.375rem;
  font-weight: 600;
  color: #fffefd;
}
.panel__card .val:last-of-type {
  margin-bottom: 0;
}
.panel__card .val a {
  color: inherit;
  transition: color 0.22s;
}
.panel__card .val a:hover,
.panel__card .val a:focus-visible {
  color: #f2eca2; /* cùng màu với nút chép khi rê chuột */
}
/* Nút chép: mặc định chỉ có icon; rê chuột thì cột thứ hai nở ra từ
   0fr → 1fr và chữ trượt từ trái sang phải hiện ra.
   Dùng grid-template-columns thay vì width vì không phải đoán trước
   chữ rộng bao nhiêu — "Copy" và "Copied" dài khác nhau. */
.copy {
  display: inline-grid;
  grid-template-columns: auto minmax(0, 0fr);
  align-items: center;
  flex: none; /* không cho co lại mất hút khi chữ dài */
  height: 1.5rem;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  opacity: 0.75;
  transition:
    opacity 0.2s,
    grid-template-columns 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
.copy:hover {
  opacity: 1;
  color: #f2eca2; /* icon lẫn chữ đều ăn theo color */
  grid-template-columns: auto minmax(0, 1fr);
}

/* hai icon xếp chồng cùng một ô nên đổi qua lại không xê dịch chữ.
   Không dùng <img> vì file .svg ghi cứng stroke="white" — phải vẽ bằng
   mask + background: currentColor thì icon mới đổi màu theo chữ. */
.copy__ico {
  display: grid;
  width: 1.125rem;
  height: 1.125rem;
}
.copy__ico i {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  background: currentColor;
  -webkit-mask: center / contain no-repeat;
  mask: center / contain no-repeat;
  transition:
    opacity 0.22s,
    scale 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.copy__ico .no {
  -webkit-mask-image: url("public/icons/icon-copy.svg");
  mask-image: url("public/icons/icon-copy.svg");
}
.copy__ico .ok {
  -webkit-mask-image: url("public/icons/icon-check-circle-one.svg");
  mask-image: url("public/icons/icon-check-circle-one.svg");
}
.copy__ico .ok {
  opacity: 0;
  scale: 0.55;
}
.copy.is-done .ok {
  opacity: 1;
  scale: 1;
}
.copy.is-done .no {
  opacity: 0;
  scale: 0.55;
}

.copy__lbl {
  overflow: hidden;
}
.copy__lbl span {
  display: block;
  padding-left: 0.375rem;
  font-size: 0.75rem; /* 12px — vừa với icon 18px */
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  translate: -6px 0;
  opacity: 0;
  transition:
    translate 0.32s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.22s;
}
.copy:hover .copy__lbl span {
  translate: 0 0;
  opacity: 1;
}

.panel__social {
  display: flex;
  /* rê chuột làm một icon nở ra tới 170px; ở thẻ hẹp (mobile chỉ còn
     254px) thì cả hàng không đủ chỗ — cho xuống dòng thay vì tràn ra
     ngoài thẻ. Lúc bình thường 5 icon luôn nằm gọn một hàng. */
  flex-wrap: wrap;
  gap: 0.75rem;
  row-gap: 0.5rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px dashed rgba(242, 237, 230, 0.25);
}
/* email + sđt dạng icon: chỉ hiện ở chế độ màn thấp bên dưới */
.panel__social .tight-only {
  display: none;
}
/* khung tròn 48px, rê chuột thì nở ra đúng bề rộng ảnh hover
   (--w đặt trên từng thẻ <a>) nên chữ trượt lộ dần ra bên phải */
/* --soc = đường kính icon. --w trên từng thẻ <a> là bề rộng ảnh hover
   ĐO Ở CỠ GỐC 48px (số không đơn vị), nên đổi --soc là bề rộng lúc rê
   chuột tự tính lại theo tỉ lệ, không phải sửa tay từng con số. */
.panel__social {
  --soc: 2.5rem; /* 40px */
}
.panel__social a {
  display: block;
  position: relative;
  width: var(--soc);
  height: var(--soc);
  overflow: hidden;
  border-radius: 999px;
  transition: width 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.panel__social a:hover {
  width: calc(var(--w) * var(--soc) / 48);
}
/* viền gradient trắng như thẻ liên hệ, bám theo bề rộng khi nở ra */
.panel__social a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(
    150deg,
    rgba(255, 255, 255, 0.9),
    rgba(255, 255, 255, 0.2) 45%,
    rgba(255, 255, 255, 0.6)
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.panel__social img {
  position: absolute;
  left: 0;
  top: 0;
  height: var(--soc);
  width: auto;
  max-width: none;
  transition: opacity 0.22s;
}
.panel__social .hv {
  opacity: 0;
}
.panel__social a:hover img:not(.hv) {
  opacity: 0;
}
.panel__social a:hover .hv {
  opacity: 1;
}

/* nút đóng: cùng cỡ 36px và cùng nét 1.5 với icon-menu.
   Hai vạch xoay riêng nên khi rê chuột chỉ cần trả vạch ngang về 0°
   và làm mờ vạch dọc là ra dấu trừ. */
.panel__close svg {
  width: calc(var(--close) * 2 / 3);
  height: calc(var(--close) * 2 / 3);
}
.panel__close .cx,
.panel__close .cy {
  transform-box: view-box;
  transform-origin: 12px 12px;
  rotate: 45deg;
  transition:
    rotate 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.22s;
}
.panel__close:hover {
  background: #fff;
}
.panel__close:hover .cx {
  rotate: 0deg;
}
.panel__close:hover .cy {
  opacity: 0;
}

/* ------------------------------------------------------------
   MÀN THẤP — panel không đủ chỗ cho hai dòng email + sđt.
   Thu hai dòng đó thành hai icon, bỏ luôn đường kẻ ngăn, còn đúng
   một hàng 5 icon. Rê chuột vẫn nở ra như cũ.
   Mốc 655px ĐO ra chứ không tính. Khung trong = màn − 80px (panel cách
   lề 24 + đệm 16); nội dung bản đầy đủ (5 mục + thẻ liên hệ) chiếm 576px
   → vừa khít ở 656px, từ 655px trở xuống phải cuộn.
   Thêm/bớt một mục danh mục là mốc này đổi ~50px — quét lại bằng script,
   đừng tính nhẩm (đã hai lần số tính lệch số đo).
   ------------------------------------------------------------ */
@media (max-height: 655px) {
  .panel__card .lbl,
  .panel__card .val {
    display: none;
  }
  .panel__social {
    gap: 0.5rem;
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }
  .panel__social .tight-only {
    display: block;
  }
  /* 44px = đúng bằng đáy nút đóng, lấy lại 12px cho màn rất thấp */
  .panel__list {
    margin-top: 44px;
  }
}

/* panel mở thì giấu hẳn menu-expanded / icon-menu ở góc phải */
body.panel-open .menu {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s;
}

/* thẻ liên hệ luôn cách mép ảnh 16px, nên margin âm phải đổi theo padding */
@media (max-width: 1439px) {
  .panel__close {
    --close: 2.25rem; /* 36px — tablet */
  }
  .panel__inner {
    padding: 28px;
  }
  .panel__card {
    margin-inline: -12px;
    margin-bottom: -12px;
  }
}
@media (max-width: 809px) {
  .panel {
    top: 1rem;
    right: 1rem;
    bottom: 1rem;
    left: 1rem;
    width: auto;
    /* max-width của bản desktop (100vw - 72px) sẽ bóp panel hẹp lại
       và lệch sang trái nếu không huỷ ở đây */
    max-width: none;
    padding: 12px;
    border-radius: 24px;
  }
  .panel__inner {
    padding: 24px;
    border-radius: 16px;
  }
  .panel__close {
    --close: 2rem; /* 32px — mobile */
  }
  .panel__list li {
    font-size: 1.5rem; /* 24px */
  }
  .panel__social {
    --soc: 2.25rem; /* 36px */
  }
  .panel__card {
    margin-inline: -8px;
    margin-bottom: -8px;
    padding: 1rem;
  }
  .panel__card .lbl {
    font-size: 0.8125rem; /* 13px */
  }
  .panel__card .val {
    font-size: 1rem; /* email 22px sẽ tràn khỏi thẻ hẹp */
    margin-bottom: 0.75rem;
  }
  .panel__list li + li {
    margin-top: calc(44px - 1.2em - 0.25rem);
  }
}




/* ============================================================
   18. PRELOAD — màn chờ

   --pu = 1px trong khung thiết kế 1440×800 nên số nào lấy từ
   public/images/preload/preload-*.svg thì dùng nguyên, không quy đổi.

   Chiều cao dấu hiệu = CAO CHỮ HOA của ThuHa = 113 (đo: Mona Sans 700 cỡ
   154px cho cao chữ hoa 113, nét dọc 25, thanh ngang 21). Nhờ vậy chữ H hình
   học ăn khớp cỡ với bốn chữ kia và không cần thu nhỏ ở bước cuối.
   ============================================================ */
.pre {
  --pu: min(calc(100vw / 1440), 1.1px);
  /* --- cỡ dấu hiệu, tính theo cao chữ hoa 113 ---
     --mk là hệ số thu nhỏ riêng cho cụm dấu hiệu (mobile dùng 0.8), mọi số đo
     của nó đi qua --mu nên đổi một chỗ là cả cụm co theo, không lệch tỉ lệ. */
  --mk: 1;
  --mu: calc(var(--pu) * var(--mk));
  --cap: calc(113 * var(--mu)); /* cao chữ hoa, = chiều cao dấu hiệu */
  --lid-w: calc(75.3 * var(--mu)); /* bầu mắt: 118.531 × 113/177.796 */
  --lid-r: calc(37.65 * var(--mu));
  --lid-gap: calc(24 * var(--mu)); /* hai bầu mắt cách nhau 37.793 → 24 */
  --stem-w: calc(25 * var(--mu)); /* nét dọc chữ H, đo từ Mona Sans */
  --stem-gap: calc(52 * var(--mu)); /* 102 (rộng mực chữ H) − 2 × 25 */
  position: fixed;
  inset: 0;
  z-index: 9000; /* trên mọi thứ, dưới con trỏ mắt (9999) */
  overflow: hidden;
  /* BẮT BUỘC: lớp kính dùng mix-blend-mode, không có isolation thì nó hoà với
     cả trang phía sau — màn chờ thành nửa trong suốt, đọc xuyên được hero. */
  isolation: isolate;
  color: #fffefd;
  /* Gradient DỌC thuần, mốc đo trên preload-edit-color.jpg: sáng dần từ đỉnh,
     đỉnh ở ~78% chiều cao rồi dịu lại ở đáy. Không có vầng sáng góc trái — đo
     trục ngang thấy phẳng, cảm giác "sáng góc" là do dải kính.
     Mốc ở đây SÁNG HƠN ẢNH MẪU ~7%: .pre__glass phủ mix-blend-mode: overlay,
     mà nền tối (< 0.5) thì overlay = 2 × nền × kính, tức nhân nền với 0.935.
     Lấy thẳng số đo của ảnh mẫu làm mốc thì render ra sẽ tối hơn mẫu. */
  background: linear-gradient(
    180deg,
    #03090a 0%,
    #040a0e 5%,
    #040d12 10%,
    #041821 25%,
    #042132 40%,
    #032c43 55%,
    #033755 70%,
    #033a5a 78%,
    #03324f 90%,
    #012337 100%
  );
  transition:
    opacity 0.55s ease,
    visibility 0.55s;
}
.pre.is-done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.pre__glass {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  mix-blend-mode: overlay;
  pointer-events: none;
}

.pre__tag {
  position: absolute;
  left: calc(64 * var(--pu));
  top: calc(66 * var(--pu));
  margin: 0;
  font-size: max(9px, calc(20 * var(--pu)));
  font-weight: 500;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.6);
}

/* ---------- ảnh project 16:9 ---------- */
.pre__shots {
  position: absolute;
  right: calc(56 * var(--pu));
  bottom: calc(112 * var(--pu));
  width: calc(252 * var(--pu));
  height: calc(142 * var(--pu));
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.pre__shots img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: center bottom;
  scale: 0;
  border-radius: 24px; /* bo lớn hơn lúc còn nhỏ → góc mềm suốt chặng phóng to */
  transition:
    scale var(--shot-ms, 0.85s) cubic-bezier(0.22, 1, 0.36, 1),
    border-radius var(--shot-ms, 0.85s) cubic-bezier(0.22, 1, 0.36, 1);
}
.pre__shots img.up {
  scale: 1;
  border-radius: 16px;
}

/* ---------- "Page is loading" + số % : NEO NGAY TRÊN Ô ẢNH ----------
   Đáy khối cách đỉnh ô ảnh 36px. Tính từ đáy màn: 112 (ô ảnh cách đáy)
   + 142 (cao ô) + 36. */
.pre__count {
  position: absolute;
  right: calc(56 * var(--pu));
  bottom: calc(290 * var(--pu));
  text-align: right;
  line-height: 1;
}
.pre__count span {
  display: block;
  font-size: max(9px, calc(16 * var(--pu)));
  font-weight: 500;
  color: rgba(255, 255, 255, 0.6);
}
.pre__count strong {
  display: block;
  margin-top: calc(20 * var(--pu));
  font-size: calc(52 * var(--pu));
  font-weight: 500;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums; /* 8% → 88% không giật ngang */
}

/* ---------- hàng chữ ThuHa, dấu hiệu nằm ngay trong hàng ---------- */
.pre__mark {
  position: absolute;
  left: calc(64 * var(--pu));
  bottom: calc(115.2 * var(--pu)); /* 800 − 684.796 */
}
.pre__word {
  display: flex;
  /* cho cú lật ngang của chữ H có chiều sâu: không có perspective thì nó chỉ
     bẹp lại thành một vạch rồi nở ra, nhìn như biến mất chứ không như lật */
  perspective: calc(900 * var(--mu));
  /* baseline: hộp dấu hiệu là inline-block rỗng nên mép đáy của nó nằm đúng
     trên đường chân chữ — khỏi phải bù trừ tay */
  align-items: baseline;
  font-size: calc(154 * var(--mu));
  font-weight: 700;
  letter-spacing: -0.022em;
  line-height: 1;
  white-space: nowrap;
}
/* Mỗi chữ là một ô grid rộng 0fr → 1fr; dùng grid chứ không dùng width vì
   không phải đo trước từng chữ rộng bao nhiêu px. */
.pre__ch {
  display: inline-grid;
  grid-template-columns: minmax(0, 0fr);
  font-style: normal;
  transition: grid-template-columns 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}
.pre__ch > span {
  display: block;
  overflow: hidden;
  /* lật quanh chân chữ khi mở ra — chỉ trượt ngang thì đơn điệu */
  transform-origin: bottom center;
  transform: rotateX(-92deg);
  opacity: 0;
  transition:
    transform 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.2s;
}
.pre__ch.on {
  grid-template-columns: minmax(0, 1fr);
}
.pre__ch.on > span {
  transform: none;
  opacity: 1;
}

/* ---------- dấu hiệu: hai bầu mắt → chữ H ----------
   Không phần tử nào hiện ra hay mất đi. Chỉ đổi width / height / radius /
   position / màu. Lòng đen chính là hai vạch mắt nhắm, rồi chính nó thành
   thanh ngang của chữ H. */
.pre__h {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--lid-gap);
  height: var(--cap);
  vertical-align: baseline;
  transform-style: preserve-3d;
  transition: gap 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
/* lật ngang một nhịp đúng lúc bốn chữ kia ùa vào (chữ H đối xứng nên lật
   180° là về đúng hình cũ), kết hợp với việc nó bị đẩy trượt sang phải */
.pre.is-word .pre__h {
  animation: preFlipH 0.55s cubic-bezier(0.34, 1.1, 0.5, 1) both;
}
@keyframes preFlipH {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(180deg);
  }
}
.pre__lid {
  position: relative;
  overflow: visible; /* thanh ngang chìa ra khỏi mí để nối hai nét dọc */
  width: var(--lid-w);
  height: 100%;
  border-radius: var(--lid-r);
  background: #fffefd;
  transition:
    width 0.55s cubic-bezier(0.22, 1, 0.36, 1),
    border-radius 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.pre__iris,
.pre__glint {
  position: absolute;
  transition:
    left 0.55s cubic-bezier(0.22, 1, 0.36, 1),
    top 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    width 0.55s cubic-bezier(0.22, 1, 0.36, 1),
    height 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    border-radius 0.5s,
    background 0.5s;
}
/* Đảo mắt liên tục cho tới lúc nhắm.
   KHÔNG có nhịp chớp mắt riêng: chính việc lòng đen dẹt xuống thành vạch ở
   chặng is-shut đã là động tác nhắm mắt. Thêm một cú chớp trước đó thành ra
   nhắm hai lần liên tiếp, nhìn vội và thừa. */
.pre__iris,
.pre__glint {
  animation: preDart 1.4s ease-in-out infinite;
}
.pre.is-shut .pre__iris,
.pre.is-shut .pre__glint {
  animation: none;
}
@keyframes preDart {
  0%,
  100% {
    translate: 0 0;
  }
  22% {
    translate: calc(7 * var(--mu)) calc(-3 * var(--mu));
  }
  48% {
    translate: calc(-7 * var(--mu)) calc(2.5 * var(--mu));
  }
  72% {
    translate: calc(4 * var(--mu)) calc(4 * var(--mu));
  }
}
/* lòng đen: ellipse 59.265 × 88.898 → 37.7 × 56.5, tâm ở 54.2% chiều cao */
.pre__iris {
  left: calc(18.8 * var(--mu));
  top: calc(32.9 * var(--mu));
  width: calc(37.7 * var(--mu));
  height: calc(56.5 * var(--mu));
  border-radius: 999px;
  background: #0c0c0c;
}
/* đốm trắng: r 7.408 → đường kính 9.4, tâm ở 41.7% chiều cao */
.pre__glint {
  left: calc(32.9 * var(--mu));
  top: calc(42.4 * var(--mu));
  width: calc(9.4 * var(--mu));
  height: calc(9.4 * var(--mu));
  border-radius: 999px;
  background: #fffefd;
}

/* --- NHẮM MẮT: lòng đen dẹt thành vạch 71×8 → 45.1×5.1, đốm trắng co về 0 --- */
.pre.is-shut .pre__iris {
  left: calc(14.6 * var(--mu));
  top: calc(54.1 * var(--mu));
  width: calc(45.1 * var(--mu));
  height: calc(5.1 * var(--mu));
  border-radius: calc(2.6 * var(--mu));
}
.pre.is-shut .pre__glint {
  left: calc(37.6 * var(--mu));
  top: calc(47.1 * var(--mu));
  width: 0;
  height: 0;
}

/* --- KHUNG CHỮ H (is-stems): mí trắng hẹp lại thành hai nét dọc | | và bo
   về ~0; hai vạch ĐEN dồn vào khoảng giữa, xếp trên–dưới thành dấu -.
   Lúc này vẫn là "mắt vừa mở ra thành khung chữ H", gạch ngang còn đen. --- */
.pre.is-stems {
  --lid-w: var(--stem-w);
  --lid-r: calc(1.5 * var(--mu));
  --lid-gap: var(--stem-gap);
}
.pre.is-stems .pre__iris {
  width: var(--stem-gap);
  height: calc(10.5 * var(--mu));
  border-radius: 0;
}
/* nửa trên của thanh ngang, chìa sang phải khỏi nét dọc bên trái */
.pre.is-stems .pre__lid:first-child .pre__iris {
  left: var(--stem-w);
  top: calc(46.1 * var(--mu));
}
/* nửa dưới, chìa sang trái khỏi nét dọc bên phải */
.pre.is-stems .pre__lid:last-child .pre__iris {
  left: calc(-1 * var(--stem-gap));
  top: calc(56.6 * var(--mu));
}
/* --- THÀNH CHỮ H (is-h): gạch ngang đen hoá trắng, chữ H hoàn chỉnh --- */
.pre.is-h .pre__iris {
  background: #fffefd;
}

/* ---------- thanh tiến trình: từng vạch tự dâng đầy từ dưới lên ---------- */
.pre__bars {
  position: absolute;
  left: calc(64 * var(--pu));
  bottom: calc(36 * var(--pu));
  height: calc(40 * var(--pu));
  display: flex;
  /* gap CỐ ĐỊNH 8 đơn vị, không dùng space-between: space-between kéo giãn
     khoảng hở khi số vạch không chia chẵn, khổ hẹp thì hở rộng gấp đôi vạch */
  gap: calc(8 * var(--pu));
}
.pre__bars i {
  position: relative;
  flex: none;
  width: calc(8 * var(--pu));
  height: 100%;
  /* 2px chứ không phải 4: vạch chỉ rộng 8 đơn vị, bo 4 là gần nửa bề ngang nên hai
     đầu thành viên thuốc. Ngoại lệ có chủ ý của thang bo góc chia hết cho 4. */
  border-radius: 2px;
  overflow: hidden;
  /* vạch chưa chạy tới: trắng 20%. Để bán trong suốt chứ không phải màu đặc,
     nên nó tự đậm nhạt theo gradient nền — trên nền đây là dải sáng nhất */
  background: rgba(255, 255, 255, 0.2);
}
.pre__bars i s {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0;
  background: #f7da4e; /* đo trên preload-edit-color.jpg */
  transition: height 0.16s linear;
}

/* ============================================================
   PRELOAD — BỐ CỤC MOBILE (≤809px)
   Khác desktop ở ba chỗ:
     · dấu hiệu chuyển lên ngay dưới dòng "UX/UI Design Portfolio", cách 16px,
       và thu nhỏ còn 0.8;
     · ô ảnh project sang góc TRÁI, khối "Page is loading" + % sang góc PHẢI,
       hai cái nằm ngang nhau;
     · cả cụm đó nằm ngay trên thanh vạch tiến trình.
   ============================================================ */
@media (max-width: 809px) {
  .pre {
    /* khung 1440 thu về 390 thì vạch chỉ còn 2px — nới đơn vị cho đọc được */
    --pu: calc(100vw / 620);
    --mk: 0.8; /* dấu hiệu nhỏ lại cho vừa bề ngang máy */
  }

  /* dòng tag cao 20 × 1.35 = 27, bắt đầu ở 66 → hết ở 93; cộng 16px là chỗ
     của dấu hiệu. Gap ghi bằng px thật vì yêu cầu là đúng 16px. */
  .pre__mark {
    top: calc(93 * var(--pu) + 16px);
    bottom: auto;
  }

  /* ô ảnh sang trái, khối % sang phải, hai cái cùng neo một đáy nên nằm
     ngang nhau; 108 = 36 (thanh vạch cách đáy) + 40 (cao vạch) + 32 hở */
  .pre__shots {
    left: calc(64 * var(--pu));
    right: auto;
    bottom: calc(108 * var(--pu));
    width: calc(196 * var(--pu));
    height: calc(110 * var(--pu));
  }
  .pre__count {
    bottom: calc(108 * var(--pu));
    right: calc(56 * var(--pu));
  }
  .pre__count strong {
    margin-top: calc(12 * var(--pu));
    font-size: calc(44 * var(--pu));
  }
  /* Vạch tiến trình ở khổ này chỉ rộng ~5px thật — bo 2 vẫn tròn quá, còn 1. */
  .pre__bars i {
    border-radius: 1px;
  }
  /* Ô ảnh ở khổ này chỉ 123×69 px thật (196×110 đơn vị × --pu 0.63), bo 16 là quá
     tròn so với thang đã chốt. Xuống 8, và bản lúc còn nhỏ 12 → 8 cho mềm góc. */
  .pre__shots {
    border-radius: 8px;
  }
  .pre__shots img {
    border-radius: 12px;
  }
  .pre__shots img.up {
    border-radius: 8px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .pre__shots img,
  .pre__iris,
  .pre__glint,
  .pre__lid,
  .pre__h,
  .pre__ch,
  .pre__bars i s {
    transition: none;
  }
}

/* chặn cuộn trong lúc màn chờ còn hiện */
body.pre-lock {
  overflow: hidden;
}

/* ============================================================
   SECTION 2 — ABOUT
   Bố cục theo reference/home-about/*.jpg, THẺ theo
   public/components/component-focus.svg (269×251).

   Lề section dùng ĐÚNG bảng đã chốt cho mọi section, không lấy theo ảnh
   mẫu: .container = max-width 1200 + padding-inline 40 (≥768) / 24, và
   padding-block 112 / 80 của .section. Ở 1440 ra khổ nội dung 1120 —
   lệch 12px so với ảnh mẫu (1132) và đó là sai số của ảnh, bỏ qua.

   Số đo lấy từ component-focus.svg (khung 269×251):
     thẻ bo 12, viền 1px rgba(0,0,0,.1), đệm 20 (+1 viền = 21 như SVG)
     ghim 25×29 tại (4, 9.13) xoay −11.84° quanh chính góc đó
     góc lật 47×49 ở mép phải-trên, ba path vector
     icon 68×68 tại (21, 29) · tiêu đề 20 · mô tả 12/17
   Số đo khác đo trên ảnh mẫu:
     headline 48, gradient ngang #014268 → #0D9BEE
     đoạn dẫn 20/28 rộng 702 · nút 104×36 bo 8, chữ 14
   ============================================================ */
:root {
  --ab-bg: #f7f4ed;
  --ab-line: #e6edf5;
  --ab-cell: 97px;
  --gap-title: 3px;
  --chip-x: -188.2px; /* chip dán lệch trái, chồng lên đầu headline như ảnh mẫu */
  --ab-g12: 72px; /* khe cục 1 → cục 2 */
  --ab-g23: 40px; /* khe cục 2 → cục 3 */
}
.about {
  position: relative;
  /* Lưới kẻ: LÁT một ô 97×97 bằng background-size, KHÔNG dùng
     repeating-linear-gradient. repeating-* vẽ một dải dài rồi lặp stop nên sai
     số khử răng cưa dồn lại: đo ở dsf 2 ra bước 193/194/195 thay vì 194 đều,
     mắt thấy ô không còn vuông. Lát ô thì hai trục dùng CÙNG một
     background-size nên ô vuông theo cấu tạo, không phụ thuộc bề rộng màn. */
  background-image:
    linear-gradient(90deg, var(--ab-line) 0 2px, transparent 2px),
    linear-gradient(180deg, var(--ab-line) 0 2px, transparent 2px);
  background-size: var(--ab-cell) var(--ab-cell);
  background-position: 50px 80px;
  background-repeat: repeat;
  background-color: var(--ab-bg);
  overflow: hidden;
}
/* Bỏ chặn --maxw của .container: About vốn bó ở 1200 nên mép nội dung ở 160 trong
   khi Work / More Projects / My journey đều ở 76 — nhìn là thấy lệch. Giờ cả bốn
   cùng dùng --sec-pad. */
#about .container {
  max-width: none;
  padding-inline: var(--sec-pad);
}
.about__wrap {
  text-align: center;
}

/* ---------- title-chip ---------- */
.chip--about {
  /* Bề rộng 136 lấy từ ảnh mẫu — vẫn đi qua --chip-w của component, không
     dùng max-content: chữ căn giữa chip nên max-content sẽ để badge đè chữ.
     translate đưa chip lệch trái 185px như ảnh mẫu (chip dán chồng lên đầu
     headline); rotate và translate là hai thuộc tính riêng nên không đè nhau. */
  --chip-w: 136;
  margin: 0 auto -2px;
  /* .chip có transform-origin: 0 0 (khớp SVG gốc), nên xoay quanh góc trên-trái
     — mép phải bị hất lên 25px. translate bù lại cho khớp ảnh mẫu. */
  translate: var(--chip-x) 17.5px;
  rotate: -10.7deg;
}

/* ---------- headline ---------- */
.about__title {
  /* width vừa chữ: background-clip: text lấy gradient theo HỘP của phần tử,
     để nguyên khối rộng 1120 thì chữ chỉ hứng đoạn giữa gradient, mất hẳn
     hai đầu đậm/nhạt */
  width: max-content;
  max-width: 100%;
  margin: var(--gap-title) auto 0;
  font-family: var(--font-display);
  font-size: 48px;
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -0.01em;
  background: linear-gradient(90deg, #014268, #0d9bee);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.about__lead {
  max-width: 702px;
  margin: 20px auto 0;
  font-size: 20px;
  line-height: 28px;
  color: #000;
}

/* ---------- 4 thẻ my focus ---------- */
.about__cards {
  /* bốn ô bằng nhau, khe đều — không ghim bề rộng thẻ, để nó co theo khổ
     nội dung đã chốt ở mọi khổ máy */
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* Ở 1440: 1120 − 4×269 = 44 chia 3 khe → 14.6, thẻ ra đúng 269 như
     component. Viết theo % để khổ nội dung nới ra thì khe nới theo, chứ không
     dồn hết phần thừa vào bề rộng thẻ. 1.3% của 1120 = 14.56. */
  gap: min(1.3%, 24px);
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: left;
}
.acard {
  position: relative;
  min-height: 251px;
  padding: 20px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 16px;
  background: #fff;
  /* góc lật phải bị bo theo góc thẻ */
  overflow: hidden;
}
.acard__pin {
  position: absolute;
  left: 3.2px;
  top: 11.2px;
  width: 25px;
  height: 29px;
  /* xoay quanh ĐÚNG góc trên-trái như trong SVG, không phải quanh tâm */
  transform-origin: 0 0;
  rotate: -11.84deg;
}
.acard__fold {
  position: absolute;
  right: -1px;
  top: -1px;
  width: 47px;
  height: 49px;
}
.acard__ic {
  display: block;
  width: 68px;
  height: 68px;
  margin-top: 8px;
}
.acard__title {
  margin: 15px 0 0;
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 700;
  line-height: 1.2;
  color: #000;
}
.acard__body {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 17px;
  color: #484848;
}

/* ---------- nút See more ---------- */
.seemore {
  /* ghim thò ra khỏi viên nút; ảnh mẫu căn giữa CẢ NHÓM nên viên nút lệch
     trái ~4.5px so với tâm khối */
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* bề rộng theo chữ: ảnh mẫu đo 104 cho "See more", lề trong 21 mỗi bên.
     Giữ lề, để bề rộng tự co thì đổi chữ không phải tính lại. */
  padding-inline: 21px;
  height: 36px;
  margin-left: -9px;
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.14);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: #000;
  text-decoration: none;
  isolation: isolate;
  /* scale là thuộc tính riêng nên không đè lên margin-left âm dùng để căn
     giữa cả nhóm nút + ghim */
  transition: scale 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.seemore:hover,
.seemore:focus-visible {
  scale: 1.06;
}
.seemore__pin {
  position: absolute;
  /* Ghim trên nút to hơn ghim trên thẻ ~1,55× — đo trên ảnh mẫu: phần đỏ
     thấy được rộng 21,5 ở nút so với 13,8 ở thẻ. Cùng một file ảnh, chỉ
     khác kích thước hiển thị. */
  right: -12.3px;
  top: -16.8px;
  width: 38.75px;
  height: 45px;
  transform-origin: 0 0;
  rotate: -11.84deg;
  /* ghim vẽ TRÊN nút: đo ảnh mẫu ở vùng chồng ra #D1311F, không phải trắng */
  z-index: 2;
}
.seemore__tx {
  position: relative;
  z-index: 1;
  transition: color 0.26s ease 0.52s;
}
/* bốn vòng màu lần lượt loang kín nút, kết ở xanh — trạng thái cuối khớp
   about-when-user-hover-button.jpg */
.seemore__ink {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
}
.seemore__ink i {
  position: absolute;
  /* tâm vòng nằm ở ĐÁY GIỮA viên nút, không phải tâm nút */
  left: 50%;
  bottom: 0;
  /* Ø = 200% BỀ RỘNG NÚT, không phải một số px cố định.
     Từ đáy giữa, điểm xa nhất là góc trên: cần Ø ≥ 2·√((W/2)² + H²). Với Ø = 2W
     thì điều kiện thành H ≤ 0.866·W — đúng với mọi nút của site (nút cao ~48).
     Bản trước ghi cứng 200px, vừa cho nút "More about ThuHa" rộng 172 nhưng
     THIẾU cho nút "Explore more of my work" rộng ~230: vòng màu không loang tới
     hai góc trên. Phần trăm thì tự đúng cho mọi bề rộng.
     margin âm 100% = một lần bề rộng nút = nửa đường kính, đưa tâm về đáy giữa
     (margin theo % luôn tính theo BỀ RỘNG khối chứa, kể cả margin-bottom). */
  width: 200%;
  aspect-ratio: 1;
  margin: 0 0 -100% -100%;
  border-radius: 50%;
  scale: 0;
  /* 0.6s / lệch 0.16s (trước 0.42s / lệch 0.11s). Bốn vòng cũ chồng nhau chỉ trong 0.75s
     nên mắt thấy màu nhảy giật; nay cả chuỗi dài ~1.08s, chuyển màu mượt hơn.
     Ba nút dùng cùng cơ chế này (.seemore__ink · .pm__cta-ink · .mwc__ink) nên phải đổi
     đồng bộ cả ba, không thì mỗi chỗ một nhịp. */
  transition: scale 0.6s cubic-bezier(0.32, 0.9, 0.36, 1);
  transition-delay: 0s; /* nhấc chuột ra thì cả bốn thu lại cùng lúc */
}
.seemore__ink i:nth-child(1) {
  background: #ea6963;
}
.seemore__ink i:nth-child(2) {
  background: #f2cc3d;
}
.seemore__ink i:nth-child(3) {
  background: #69c465;
}
.seemore__ink i:nth-child(4) {
  background: #0e9af0;
}
.seemore:hover .seemore__ink i,
.seemore:focus-visible .seemore__ink i {
  scale: 1;
}
.seemore:hover .seemore__ink i:nth-child(2),
.seemore:focus-visible .seemore__ink i:nth-child(2) {
  transition-delay: 0.16s;
}
.seemore:hover .seemore__ink i:nth-child(3),
.seemore:focus-visible .seemore__ink i:nth-child(3) {
  transition-delay: 0.32s;
}
.seemore:hover .seemore__ink i:nth-child(4),
.seemore:focus-visible .seemore__ink i:nth-child(4) {
  transition-delay: 0.48s;
}
.seemore:hover .seemore__tx,
.seemore:focus-visible .seemore__tx {
  color: #fff;
}


/* ---------- ba cục và khoảng cách giữa chúng ---------- */
.ab-g2 {
  margin-top: var(--ab-g12);
}
.ab-g3 {
  /* nút ở gần cụm thẻ hơn là gần cụm chữ */
  margin-top: var(--ab-g23);
}

/* ---------- trượt lên khi cuộn tới, trượt xuống khi cuộn lui ----------
   Đặt riêng cho About, KHÔNG dùng .reveal: .reveal bật một lần rồi thôi.
   Dùng translate (thuộc tính riêng) thay vì transform để không đè lên
   rotate/scale của phần tử nào khác. */
/* Dùng được cho MỌI section, không riêng About — section sau chỉ cần gắn
   class .rise và đặt --d là có đúng cơ chế này. Bỏ tiền tố #about cũng làm
   độ ưu tiên về (0,1,0), nên các quy tắc ghi đè chỉ cần một class là đủ. */
.rise {
  opacity: 0;
  /* quãng trượt 56px và 0.9s — bản 28px/0.62s trước đó đi ngắn và vội */
  translate: 0 56px;
  transition:
    opacity 0.9s cubic-bezier(0.22, 1, 0.36, 1),
    translate 0.9s cubic-bezier(0.22, 1, 0.36, 1);
  /* --d là thứ tự trong CỤM, không phải trong cả section: mỗi cụm kích ở một
     vị trí cuộn khác nhau, tính chung một dãy thì cụm dưới phải chờ vô ích. */
  transition-delay: calc(var(--d, 0) * 0.09s);
}
.rise.in {
  opacity: 1;
  translate: 0 0;
}

/* ---------- About: các khổ nhỏ hơn ----------
   Bốn cột chỉ vừa khi khổ nội dung còn ≥ ~960 (mỗi thẻ cần ~230 để chữ 12px
   không vỡ thành quá nhiều dòng): 4×230 + 3×14.6 ≈ 964, cộng 80 lề = 1044.
   Dưới mốc đó xuống 2 cột, rồi 1 cột. Lề vẫn theo .container đã chốt. */
@media (max-width: 1043px) {
  .about__cards {
    grid-template-columns: repeat(2, 1fr);
    gap: 20px;
  }
  .acard {
    /* 251 là chiều cao của component ở khổ 269; khi thẻ rộng ra thì chữ ít
       dòng hơn, giữ 251 sẽ hở một khoảng trống ở đáy. Hàng grid vẫn tự cào
       bằng nhau nên bỏ min-height không làm thẻ lệch cao. */
    min-height: 0;
  }
}
@media (max-width: 559px) {
  .about__cards {
    grid-template-columns: 1fr;
  }
}

/* Cỡ chữ: ảnh mẫu chỉ có bản 1440. Các mốc dưới thu theo tỉ lệ, dùng đúng
   mốc breakpoint của cả site (1440 / 810 / 390). */
@media (max-width: 1439px) {
  .about__title {
    font-size: 40px;
  }
  .about__lead {
    max-width: 620px;
    font-size: 18px;
    line-height: 26px;
  }
}
@media (max-width: 809px) {
  .about {
    /* chip về giữa: ở khổ hẹp không còn chỗ để lệch 188px sang trái mà vẫn
       nằm trong khổ nội dung */
    --chip-x: 0px;
  }
  .chip--about {
    /* về giữa thì không được kéo headline lên nữa, nếu không chip đè chữ.
       Chip xoay quanh góc trên-trái nên mép dưới hạ xuống 48px ≈ đúng chiều
       cao hộp (49) — 12px là khoảng hở thật giữa chip và headline. */
    translate: 0 0;
    margin: 0 auto 12px;
  }
  .about__title {
    font-size: 32px;
  }
  .about__lead {
    max-width: 560px;
    font-size: 14px;
    line-height: 22px;
  }
  /* Chữ trong title-chip về 12px ở mobile — áp cho MỌI chip ngoài hero
     (.chip--flow), không riêng About. Đổi --u chứ KHÔNG đổi riêng font-size:
     --u là hệ số phóng của cả component, 18 × 0.6667 = 12 nên chữ ra đúng 12
     mà cao/bo/đệm/badge vẫn giữ đúng tỉ lệ đã chốt (49 → 32.7, 36 → 24,
     136 → 90.7). Sửa riêng font-size sẽ để chữ 12 lọt trong hộp rộng 136, hai
     bên hở một khoảng trống lớn. */
  .chip--flow {
    --u: 0.6667px;
  }
  /* khe giữa ba cục hẹp lại ở khổ nhỏ, nhưng vẫn theo tỉ lệ đã tăng */
  .about {
    --ab-g12: 28px;
    --ab-g23: 28px;
  }
}
@media (max-width: 390px) {
  .about__title {
    font-size: 28px;
  }
}

/* ---------- Mobile: 4 thẻ focus thành slider ----------
   Vuốt ngón tay có sẵn nhờ overflow-x + scroll-snap; hai nút 32px và 4 chấm
   4px là cách bấm thay thế. Chấm đang xem màu #0D9CF2, còn lại cùng màu đó ở
   opacity 20%. */
.acards-nav {
  display: none;
}
@media (max-width: 809px) {
  .about__cards {
    display: flex;
    grid-template-columns: none;
    gap: 16px;
    /* PHẢI ghi overflow-y: hidden. Đặt overflow-x: auto mà để trục dọc mặc định
       thì `visible` tự bị đổi thành `auto` — khay thẻ cuộn được cả theo chiều
       dọc, ngón tay kéo dọc bị khay ăn mất thay vì cuộn trang. */
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .about__cards::-webkit-scrollbar {
    display: none;
  }
  .acard {
    flex: 0 0 100%;
    min-width: 0;
    scroll-snap-align: start;
  }
  /* Ở khổ này BỎ hiệu ứng trượt LÊN của từng thẻ. Chuyển động khi vuốt là
     trượt NGANG do chính khay cuộn lo; thêm mờ + thu nhẹ cho thẻ chưa tới lượt
     để cú trượt đọc rõ hơn (scale không đổi layout nên không lung lay
     scroll-snap).

     Quy tắc gốc .rise chỉ có một class (0,1,0) nên .acard.rise (0,2,0) ghi đè
     được. Trước đây quy tắc gốc là `#about .rise` (1,1,0) và ở đây viết
     `.acard.rise` — thua, nên translate: 0 56px và opacity: 0 vẫn còn hiệu lực:
     ba thẻ sau bị đẩy xuống 52px, khay thẻ tràn dọc đúng 52px và cuộn dọc được
     bên trong, đồng thời chúng vô hình vì bộ quan sát gỡ .in khi thẻ ra khỏi
     khung nhìn theo trục NGANG. */
  .acard.rise {
    translate: none;
    opacity: 0.45;
    /* KHÔNG scale thẻ. Vùng snap của một scroll-snap target tính theo hộp SAU
       transform, nên scale 0.965 đẩy mốc snap lệch đúng 6px (342 × 0.035 / 2):
       cuộn tới thẻ nào cũng bị kéo lệch 6px và mất một dải bên trái. Chỉ mờ
       là đủ để cú trượt ngang đọc rõ. */
    transition: opacity 0.32s ease;
  }
  .acard.rise.is-cur {
    opacity: 1;
  }
  .acards-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    margin-top: 18px;
  }
  .acn__arrow {
    /* tổng vẫn 32px: viền 1.5 + đệm 4 mỗi bên → chỗ cho glyph 21, dùng 20 */
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1.5px solid #0d9cf2;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: none;
    color: #0d9cf2;
    cursor: pointer;
  }
  /* file .svg ghi cứng stroke="white" — để <img> thì không đổi màu được, nên
     dùng mask + background: currentColor như các icon khác trong site */
  .acn__arrow::before {
    content: "";
    display: block;
    width: 20px;
    height: 20px;
    background: currentColor;
    -webkit-mask: url(public/icons/Icon-arrow-left.svg) center / 20px 20px no-repeat;
    mask: url(public/icons/Icon-arrow-left.svg) center / 20px 20px no-repeat;
  }
  .acn__arrow--next::before {
    -webkit-mask-image: url(public/icons/Icon-arrow-right.svg);
    mask-image: url(public/icons/Icon-arrow-right.svg);
  }
  .acn__dots {
    display: flex;
    align-items: center;
    gap: 6px;
  }
  .acn__dots i {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #0d9cf2;
    opacity: 0.2;
    transition: opacity 0.25s ease;
  }
  .acn__dots i.is-on {
    opacity: 1;
  }
}

/* ---------- About: màn THẤP ----------
   Bấm icon About ở dock thì cả ba cục, kể cả nút, phải nằm trên dock. Đo hộp
   nội dung cao 668 (đã tính phần title-chip tràn lên 28). Chỗ trống trên dock
   = 791 ở màn 900, 691 ở màn 800 → vừa. Xuống 768 chỉ còn 659 và 720 còn 611 →
   phải bớt nhịp dọc, nếu không nút vừa bị che vừa không chạy hiệu ứng (nó nằm
   dưới vạch kích −12% của bộ quan sát nên không bao giờ được bật). */
@media (max-height: 790px) {
  .about {
    --ab-g12: 52px;
    --ab-g23: 28px;
  }
  .about__lead {
    margin-top: 14px;
  }
}
@media (max-height: 730px) {
  .about {
    --ab-g12: 28px;
    --ab-g23: 16px;
  }
  .about__lead {
    margin-top: 8px;
  }
}
@media (max-height: 700px) {
  .about {
    --ab-g12: 16px;
    --ab-g23: 12px;
  }
  .about__lead {
    margin-top: 4px;
  }
}

/* ============================================================
   ICON 4 THẺ FOCUS — chuyển động lặp
   Vẽ lại thành SVG (trước đó là ảnh PNG, không cử động được từng phần).
   viewBox 0 0 96 96 chính là ô 68px của component, và 96 cũng là độ phân giải
   bản thu nhỏ dùng để đo artwork — nên toạ độ trong SVG BẰNG ĐÚNG toạ độ đo
   được, không qua bước quy đổi nào.

   Chỉ chạy khi thẻ đã hiện (.acard.in) — không để 4 vòng lặp quay sau lưng
   người xem suốt thời gian ở section khác.
   ============================================================ */
.fic {
  overflow: visible;
}
.fic * {
  animation-play-state: paused;
}
.acard.in .fic * {
  animation-play-state: running;
}

/* ---------- UX Design ----------
   Hai hạt chạy theo hai đường line vào giữa → hai line đổi sang xanh → rồi
   mới tới đĩa và hình người. Vòng lặp bắt đầu lại bằng cách hạt hiện dần Ở
   ĐIỂM ĐẦU: nhảy vị trí về đầu trong lúc còn opacity 0, nếu không sẽ thấy hạt
   bò ngược từ tâm ra ngoài. */
.fic__u1 {
  animation: ficU1 6s cubic-bezier(0.45, 0, 0.5, 1) infinite;
}
.fic__u2 {
  animation: ficU2 6s cubic-bezier(0.45, 0, 0.5, 1) infinite;
}
@keyframes ficU1 {
  0% {
    translate: 0 0;
    opacity: 1;
  }
  17% {
    translate: 23.1px 1px;
  }
  25% {
    translate: 27.2px 19.5px;
  }
  31% {
    translate: 28.7px 30px;
    opacity: 1;
  }
  37% {
    translate: 28.7px 30px;
    opacity: 0;
  }
  37.5%,
  92% {
    translate: 0 0;
    opacity: 0;
  }
  99%,
  100% {
    translate: 0 0;
    opacity: 1;
  }
}
@keyframes ficU2 {
  0% {
    translate: 0 0;
    opacity: 1;
  }
  17% {
    translate: -24px 1.3px;
  }
  25% {
    translate: -28.1px -11.8px;
  }
  31% {
    translate: -31px -22px;
    opacity: 1;
  }
  37% {
    translate: -31px -22px;
    opacity: 0;
  }
  37.5%,
  92% {
    translate: 0 0;
    opacity: 0;
  }
  99%,
  100% {
    translate: 0 0;
    opacity: 1;
  }
}
/* Hai đường line GIỮ NGUYÊN màu, không đổi sang xanh — bản trước có, đã bỏ. */
.fic__disc {
  animation: ficDisc 6s ease-in-out infinite;
}
.fic__ink {
  animation: ficInk 6s ease-in-out infinite;
}
@keyframes ficDisc {
  0%,
  42% {
    fill: #fdc98d;
  }
  51%,
  68% {
    fill: #0d9cf2;
  }
  80%,
  100% {
    fill: #fdc98d;
  }
}
@keyframes ficInk {
  0%,
  42% {
    fill: #704412;
  }
  51%,
  68% {
    fill: #ffffff;
  }
  80%,
  100% {
    fill: #704412;
  }
}

/* ---------- Product Design ----------
   Không phải ba khối đổi chỗ trong ba ô cố định, mà cả BỐ CỤC biến sang một
   cách chia khác: 2 vuông trái + 1 dọc phải → 1 ngang trên + 2 vuông dưới →
   1 dọc trái + 2 vuông phải → về đầu. Cả ba cách chia đều lấp kín khung
   x 15…82, y 33…83 với khe 3, nên khung và khe không đổi.
   x/y/width/height của <rect> là thuộc tính CSS animate được, khỏi cần
   transform. */
.fic__b1 {
  animation: ficPd1 9s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
.fic__b2 {
  animation: ficPd2 9s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
.fic__b3 {
  animation: ficPd3 9s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
.fic__dot {
  animation: ficPdDot 9s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
@keyframes ficPd1 {
  0%,
  22% {
    x: 15px;
    y: 33px;
    width: 26px;
    height: 21px;
    rx: 3px;
    fill: #fdc98d;
  }
  33%,
  55% {
    x: 15px;
    y: 33px;
    width: 67px;
    height: 20px;
    rx: 5px;
    fill: #f3af52;
  }
  66%,
  89% {
    x: 15px;
    y: 33px;
    width: 38px;
    height: 50px;
    rx: 6px;
    fill: #704412;
  }
  100% {
    x: 15px;
    y: 33px;
    width: 26px;
    height: 21px;
    rx: 3px;
    fill: #fdc98d;
  }
}
@keyframes ficPd2 {
  0%,
  22% {
    x: 15px;
    y: 57px;
    width: 26px;
    height: 26px;
    rx: 5px;
    fill: #704412;
  }
  33%,
  55% {
    x: 15px;
    y: 56px;
    width: 32px;
    height: 27px;
    rx: 5px;
    fill: #fdc98d;
  }
  66%,
  89% {
    x: 56px;
    y: 33px;
    width: 26px;
    height: 23px;
    rx: 4px;
    fill: #f3af52;
  }
  100% {
    x: 15px;
    y: 57px;
    width: 26px;
    height: 26px;
    rx: 5px;
    fill: #704412;
  }
}
@keyframes ficPd3 {
  0%,
  22% {
    x: 44px;
    y: 33px;
    width: 38px;
    height: 50px;
    rx: 6px;
    fill: #f3af52;
  }
  33%,
  55% {
    x: 50px;
    y: 56px;
    width: 32px;
    height: 27px;
    rx: 5px;
    fill: #704412;
  }
  66%,
  89% {
    x: 56px;
    y: 59px;
    width: 26px;
    height: 24px;
    rx: 4px;
    fill: #fdc98d;
  }
  100% {
    x: 44px;
    y: 33px;
    width: 38px;
    height: 50px;
    rx: 6px;
    fill: #f3af52;
  }
}
/* hình tròn luôn nằm trên khối LỚN NHẤT của cách chia hiện tại, bán kính co
   theo ô để không tràn */
@keyframes ficPdDot {
  0%,
  22% {
    cx: 63px;
    cy: 58px;
    r: 10px;
  }
  33%,
  55% {
    cx: 48.5px;
    cy: 43px;
    r: 7px;
  }
  66%,
  89% {
    cx: 34px;
    cy: 58px;
    r: 10px;
  }
  100% {
    cx: 63px;
    cy: 58px;
    r: 10px;
  }
}

/* ---------- Interaction & Motion ----------
   Trỏ chuột đi vào vòng → vòng đổi sang xanh → RỒI hai cung mới bung ra, cung
   ngoài chậm hơn cung trong. transform-box: view-box để transform-origin nhận
   toạ độ viewBox, đặt đúng tâm vòng (35.5, 46); mặc định origin là giữa hộp bao
   của chính path, sóng sẽ phình từ giữa nó chứ không từ vòng. */
.fic__cur {
  animation: ficCur 6.4s cubic-bezier(0.34, 1.15, 0.5, 1) infinite;
}
.fic__hub {
  animation: ficHub 6.4s ease-in-out infinite;
}
.fic__w1,
.fic__w2 {
  transform-box: view-box;
  transform-origin: 35.5px 46px;
}
/* Hai cung dùng HAI keyframe riêng, không phải một keyframe rồi lệch bằng
   animation-delay: delay dịch cả hai đầu như nhau, nên nếu cung ngoài hiện sau
   thì nó cũng tan sau — ngược với sóng thật. Ở đây tách ra để:
     hiện  : trong (32%) trước, ngoài (40%) sau — sóng lan ra ngoài;
     tan   : NGOÀI (68%) trước, trong (76%) sau — sóng thu vào tâm. */
.fic__w1 {
  animation: ficWaveIn 6.4s cubic-bezier(0.3, 1.25, 0.5, 1) infinite;
}
.fic__w2 {
  animation: ficWaveOut 6.4s cubic-bezier(0.3, 1.25, 0.5, 1) infinite;
}
@keyframes ficCur {
  0%,
  5% {
    translate: 17px 20px;
  }
  22%,
  66% {
    translate: 0 0;
  }
  84%,
  100% {
    translate: 17px 20px;
  }
}
@keyframes ficHub {
  0%,
  22% {
    fill: #704412;
  }
  32%,
  80% {
    fill: #0d9cf2;
  }
  90%,
  100% {
    fill: #704412;
  }
}
@keyframes ficWaveIn {
  0%,
  32% {
    scale: 0.12;
    opacity: 0;
  }
  46%,
  76% {
    scale: 1;
    opacity: 1;
  }
  86%,
  100% {
    scale: 0.12;
    opacity: 0;
  }
}
@keyframes ficWaveOut {
  0%,
  40% {
    scale: 0.12;
    opacity: 0;
  }
  54%,
  68% {
    scale: 1;
    opacity: 1;
  }
  78%,
  100% {
    scale: 0.12;
    opacity: 0;
  }
}

/* ---------- AI Workflow: sao quay + đổi màu, nút phập phồng ---------- */
.fic__spin {
  transform-box: view-box;
  transform-origin: 46px 50px;
  animation: ficSpin 26s linear infinite;
}
@keyframes ficSpin {
  to {
    rotate: 360deg;
  }
}
.fic__star {
  animation: ficStar 8.4s ease-in-out infinite;
}
@keyframes ficStar {
  0%,
  10% {
    fill: #fdc98d;
  }
  25%,
  35% {
    fill: #0d9cf2;
  }
  50%,
  60% {
    fill: #f8da4e;
  }
  75%,
  85% {
    fill: #0d9cf2;
  }
  100% {
    fill: #fdc98d;
  }
}
/* BẮT BUỘC transform-box: fill-box. Mặc định gốc transform của phần tử SVG là
   tâm cả viewBox, nên scale sẽ ĐẨY nút đi chỗ khác thay vì phình tại chỗ. */
.fic__n,
.fic__d {
  transform-box: fill-box;
  transform-origin: center;
}
.fic__n {
  animation: ficPulse 4.2s ease-in-out infinite;
  animation-delay: calc(var(--n) * 0.42s);
}
.fic__d {
  animation: ficBlink 4.2s ease-in-out infinite;
  animation-delay: calc(var(--n) * 0.42s);
}
@keyframes ficPulse {
  0%,
  100% {
    scale: 1;
    opacity: 0.45;
  }
  35% {
    scale: 1.22;
    opacity: 1;
  }
  70% {
    scale: 1;
    opacity: 1;
  }
}
@keyframes ficBlink {
  0%,
  100% {
    opacity: 0.25;
  }
  40% {
    opacity: 1;
  }
}

/* ============================================================
   SECTION 3 — SELECTED WORK
   Dựng theo reference/home-work/ref-home-work-1…3.png.

   Ý: cuộn từ About xuống thì khung ảnh giãn dần từ ĐÚNG khổ nội dung đã chốt
   (1120 ở 1440) ra full màn. Khác ref một điểm: ref phải cuộn thêm mới thấy
   chữ, ở đây gói gọn trong MỘT màn.

   --wp (0 → 1) do JS ghi theo vị trí cuộn, mọi số đo giãn đều nội suy theo nó.
   Điểm đầu lấy đúng bảng lề đã chốt nên section này nối liền mạch với About:
   ảnh bắt đầu rộng bằng khối nội dung của About rồi mới nở ra.
   ============================================================ */
.work {
  --wp: 0;
  --wacc: #f8da4e; /* màu vạch chạy */
  --wbar: 3px; /* độ dày vạch chạy — sàn 3, mobile 2, màn lớn 4 */
  --wpad: 112px; /* = padding-block đã chốt của section, dùng làm lề dọc lúc đầu */
  --wr: 24px; /* bo góc lúc chưa giãn */
  position: relative;
  /* Cú nở diễn ra TRƯỚC khi stage dính (xem chú thích --wp trong site.js), nên
     phần cao thêm ở đây chỉ để GIỮ trạng thái full màn. Để 100svh thì thấy full
     rồi vẫn phải cuộn thêm cả một màn mới thoát section — 300px là đủ để nhịp
     cuộn không bị hẫng mà không giam người xem lại. */
  min-height: calc(100svh + 300px);
  background: var(--ab-bg);
}
/* Lưới ô vuông nối tiếp từ About rồi nhạt dần về 0 — để hai section không bị
   cắt ngang một đường thẳng. Vẽ trên ::before chứ không phải nền của .work:
   mask đặt lên chính .work sẽ mask luôn cả khung ảnh và chữ bên trong.
   --wgrid-y do JS ghi, xem chú thích ở site.js: phải khớp PHA dọc với lưới của
   About, mà CSS thì không biết About cao bao nhiêu. */
.work::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: min(100svh, 100%);
  background-image:
    linear-gradient(90deg, var(--ab-line) 0 2px, transparent 2px),
    linear-gradient(180deg, var(--ab-line) 0 2px, transparent 2px);
  background-size: var(--ab-cell) var(--ab-cell);
  background-position: 50px var(--wgrid-y, 0px);
  background-repeat: repeat;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0.55) 42%, transparent 88%);
  mask-image: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0.55) 42%, transparent 88%);
  pointer-events: none;
  z-index: 0;
}
.work__stage {
  position: sticky;
  top: 0;
  z-index: 1; /* trên lớp lưới ::before */
  height: 100svh;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.work__frame {
  /* khổ đầu = đúng khối nội dung của .container; khổ cuối = trọn khung nhìn */
  --w0: calc(min(var(--maxw), 100vw) - 2 * var(--pad));
  --h0: calc(100svh - 2 * var(--wpad));
  position: relative;
  width: calc(var(--w0) + (100vw - var(--w0)) * var(--wp));
  height: calc(var(--h0) + (100svh - var(--h0)) * var(--wp));
  border-radius: calc(var(--wr) * (1 - var(--wp)));
  overflow: hidden;
  isolation: isolate;
  color: #fff;
}
/* ---------- ảnh thumbnail: 3 tấm xếp chồng, đổi mỗi 8s ---------- */
.work__shots {
  position: absolute;
  inset: 0;
}
.work__shots > * {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.9s ease;
}
.work__shots > .is-on {
  opacity: 1;
}
.work__shots picture {
  display: block;
  width: 100%;
  height: 100%;
}
.work__shots img {
  width: 100%;
  height: 100%;
  /* cover: khung đổi tỉ lệ trong lúc giãn (1.94 → 1.8 ở 1440×800) nên ảnh phải
     tự cắt theo, không được méo */
  object-fit: cover;
  /* NEO PHẢI, không để giữa.
     Ảnh 16:9 mà gần như mọi khổ màn đều thấp hơn 16:9, nên cover luôn cắt HAI
     BÊN — đo 10 khổ: chưa khổ nào cắt trên–dưới, còn cắt ngang thì 71px/bên ở
     1280×800 và tới 288px/bên ở 1024×900. Để giữa thì nửa số đó ăn vào mép
     phải, đúng chỗ đặt mockup điện thoại: ở 1512×982 (117px/bên) máy bị cắt dọc
     mất một dải.
     Neo phải thì toàn bộ phần cắt dồn sang trái — bên đó chỉ có nền thành phố,
     mất bớt không sao. Mép phải KHÔNG BAO GIỜ bị cắt nữa, ở mọi khổ. */
  object-position: 100% 50%;
}
.work__shots .slot {
  /* chỗ chờ ảnh: in luôn số đo thật của khung để bạn biết cần thiết kế cỡ nào.
     inset: 0 để chữ nằm giữa khung — .slot là <span>, cao theo nội dung nên
     không có dòng này nó dính lên đỉnh. */
  position: absolute;
  inset: 0;
  font-size: max(9px, 0.75rem);
  line-height: 1.6;
  letter-spacing: 0.04em;
  background: rgba(0, 0, 0, 0.35);
  border-color: rgba(255, 255, 255, 0.35);
  color: rgba(255, 255, 255, 0.9);
}
/* Không còn lớp phủ tối ở TRÊN nữa: đã bỏ hẳn .work__veil, chỉ giữ dải kính ở
   đáy. Phần đọc được của chip + headline chuyển sang bóng chữ — xem .work__title
   ngay dưới. */

/* ---------- chữ trên cùng ---------- */
.work__head {
  position: absolute;
  left: 0;
  right: 0;
  /* lề trong theo đúng bảng đã chốt; lúc giãn thì nở thêm cho khỏi sát mép.
     72 → 100 khi full màn: title đã chuyển sang căn LỀ TRÁI nên nó chui vào đúng
     cột của logo cố định .brand-ha (x 36–121, y 28–64). Badge của chip thò lên
     24px so với hộp chip, nên với top 72 thì đỉnh badge ở y=48, đè 16px vào logo.
     100 − 24 = 76, hở 12px dưới logo. */
  /* top và lề là SỐ CỐ ĐỊNH, không nội suy theo --wp nữa — cú thu nhỏ lúc khung
     còn bé giờ do `scale` bên dưới lo, một cơ chế thay vì hai cơ chế đánh nhau.
     94 = đo trên ref-content.jpg: đỉnh chữ hoa dòng 1 ở y=111 trong khung cao 810
     (mốc cũ 100 cho ra 117, lệch 6px).
     Ràng buộc "chừa cho logo" của bản trước không còn: chip giờ neo sang phải
     (x≈510), cách hẳn logo .brand-ha (x 36–121), nên top tự do theo ref. */
  /* top: 0 + PADDING-TOP 94, không phải top: 94.
     transform-origin: 0 0 lấy góc trên-trái của CHÍNH khối này, nên nếu 94px nằm
     ở `top` thì nó ở ngoài hộp và không co theo scale — đo được mốc dọc đứng
     nguyên 94 ở mọi mức nở, trong khi mốc ngang co đúng. Đưa 94 vào padding thì
     nó nằm trong hộp và co cùng. Đã mắc một lần. */
  top: 0;
  padding: 94px var(--sec-pad) 0;
  /* Cụm title + chip NỞ CÙNG KHUNG ảnh, không đứng nguyên một cỡ.
     --wsc0 là tỉ lệ bề rộng khung lúc nhỏ nhất so với khung nhìn, do site.js đo
     và ghi vào (CSS không chia được hai độ dài). 1440 → 0.778 · 1024 → 0.922 ·
     1920 → 0.758, nên không ghi cứng được.
     transform-origin: 0 0 = góc trên-trái KHUNG (khối này căng left/right theo
     khung), nhờ vậy cả lề trái 64 và mốc top 94 cũng co theo đúng tỉ lệ — cụm
     chữ giữ nguyên vị trí tương đối trong khung ở mọi mức nở. */
  transform-origin: 0 0;
  scale: calc(var(--wsc0, 1) + (1 - var(--wsc0, 1)) * var(--wp));
  /* CĂN LỀ TRÁI, không căn giữa: cùng một mốc lề với logo, với cụm description ở
     góc dưới-trái và với lưới ô vuông — ba thứ đó đều bám var(--pad). Bản trước
     căn giữa nên title là thứ duy nhất không thẳng cột với gì cả. */
  text-align: left;
}
.chip--work {
  /* PHẢI đặt --chip-w: .chip lấy bề rộng từ calc(var(--chip-w) * var(--u)),
     không có biến thì calc vô hiệu và chip giãn hết chiều ngang khối chứa.
     198 chốt bằng cách BỎ XOAY vector tâm-badge → tâm-fold, cách này không phụ
     thuộc ngưỡng nhận mép nên đo lại được y hệt:
       ref : (161, 38) → bỏ xoay 8.17° = (164.8, 14.6)
       mình: (155, 37) → bỏ xoay 8.17° = (158.7, 14.6)   ⇒ thiếu 6.1px
     Thành phần y bằng nhau (14.6) chứng minh góc xoay đã đúng, chỉ bề rộng thiếu.
     Cao 49 và badge 36 trùng đúng component (--chip-h, --badge), không sửa. */
  --chip-w: 198;
  /* margin-left: 0 chứ không auto — auto sẽ kéo chip về giữa dù .work__head đã
     text-align: left, vì .chip là block có bề rộng cố định. */
  margin: 0 0 14px;
}
/* Riêng bản desktop: chip KHÔNG nằm trong luồng mà neo tuyệt đối, lệch sang phải
   và nghiêng — theo ref-content.jpg, chip đứng phía trên dòng đầu headline chứ
   không phải trên đầu khối chữ. Đưa ra khỏi luồng để headline tự quyết định mốc
   top của khối, khỏi phải trừ chiều cao chip.
   Chỉ scope trong .work__head: bản mobile (.wmob) vẫn dùng chip trong luồng. */
.work__head .chip--work {
  position: absolute;
  margin: 0;
  /* +8.17° = NGHIÊNG THEO CHIỀU KIM ĐỒNG HỒ (ngược hướng chip ở About, −10.7°).
     .chip có transform-origin: 0 0 nên nó xoay quanh góc trên-trái, tức góc đó
     đứng yên đúng vị trí layout — nhờ vậy left/top bên dưới là toạ độ thật của
     góc trên-trái chip. */
  rotate: 8.17deg;
  /* Toạ độ suy từ TÂM BADGE, mốc chắc nhất vì badge là hình tròn.
     Ref: tâm badge (535, 42) trong khung 1440×810.
     Bản mình: với chip ở vị trí tĩnh, tâm badge lệch (+25, −2) so với góc
     trên-trái hộp layout của chip. Vậy hộp layout cần ở (510, 44):
       left = 64 (phần lề khi --wp = 1) + 446
       top  = 44 − 94 (mốc top của .work__head) = −50 */
  left: calc(var(--sec-pad) + 446px);
  /* 44 = 94 (padding-top của .work__head) − 50. Mốc `top` của con absolute tính
     theo padding-BOX của khối chứa, tức mép trên hộp .work__head — padding-top
     không đẩy mốc này xuống, nên phải cộng tay. */
  top: 44px;
}
.work__title {
  width: max-content;
  max-width: 100%;
  margin: 0;
  font-family: var(--font-display);
  font-size: 48px;
  font-weight: 700;
  /* 1.4167 = 68 / 48: ref-content.jpg đo được khoảng cách giữa hai đỉnh chữ hoa
     của các dòng đúng 68px, với cao chữ hoa 35px → font-size 48 (Mona Sans có
     cap-height ≈ 0.727em). */
  line-height: 1.4167;
  letter-spacing: -0.01em;
  /* Chữ ĐẬM MÀU, không phải trắng, và KHÔNG phải đen thuần: ref đo được
     #282828 (rgb 40,40,40). Cách đo phải lấy LÕI NÉT — điểm tối mà cả bốn lân
     cận cũng tối — chứ không lấy điểm tối nhất: một pixel tối nhất là nhiễu, lần
     đầu mình đọc ra #282627 vì thế.
     Đi cùng việc bỏ lớp phủ tối ở đỉnh khung: vùng đầu ảnh sáng nên chữ tối mới
     đọc được. Ảnh thumbnail vì thế PHẢI sáng ở góc trên-trái. */
  color: #282828;
  /* KHÔNG text-shadow. Bản trước có quầng 22px để bù việc bỏ lớp phủ tối ở đỉnh
     khung, nhưng trên thumbnail sáng nó hiện thành một đám xám dưới chữ — trông
     như một shape lạ. Giờ phần đọc được hoàn toàn dựa vào ẢNH tự tối ở vùng đầu:
     xem cảnh báo ở README, mục "Không có lớp phủ tối ở TRÊN ảnh". */
}

/* Dòng cuối headline ("part of") có GRADIENT XANH, hai dòng trên thì phẳng.
   Đo trên ref bằng cách khớp bình phương tối thiểu màu lõi nét theo x, trong
   đúng 149px bề rộng của dòng đó:
     x=74  → rgb(39,41,42)  ≈ #282828  (trùng màu hai dòng trên)
     x=222 → rgb(3,65,101)  = #034165
     dốc mỗi px: R −0.243 · G +0.158 · B +0.399
   Là <span> inline nên hộp của nó bó sát chữ, gradient tự trải đúng bề rộng dòng
   — không gặp bẫy "background-clip: text lấy gradient theo hộp phần tử" như
   headline của About (chỗ đó phải thêm width: max-content vì là block rộng 1120). */
.work__grad {
  background: linear-gradient(90deg, #282828 0%, #034165 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ---------- dải kính gradient ở đáy ----------
   TÁM BẬC, CÙNG BÁN KÍNH BLUR, OPACITY LÊN THANG. KHÔNG DÙNG MASK.

   ĐIỀU QUAN TRỌNG NHẤT ở đây, và là chỗ hai bản trước làm sai: bán kính blur nhỏ
   KHÔNG cho mép mềm. Đo được blur(1.2px) ở opacity 1 đã xoá 84% chi tiết ảnh —
   nên một lớp blur "nhẹ" trải hết dải vẫn tạo ra một đường cắt độ nét rất rõ ở
   đỉnh dải, đúng cái làm dải trông như một cục đè lên ảnh. Bước nhảy ĐỘ SÁNG thì
   mượt, nhưng mắt bắt bước nhảy ĐỘ NÉT.

   Thứ pha loãng được backdrop-filter là opacity, và nó pha đúng tuyến tính — đo
   trên nền vằn 3px với blur(8px):
     opacity 0.1 → còn 89% độ nét · 0.25 → 76% · 0.5 → 50% · 0.75 → 25% · 1 → 0.4%
   Vậy giữ nguyên bán kính, cho opacity lên thang.

   Tám lớp cùng neo đáy, cao giảm dần 100% → 12.5%, cùng blur(7px), opacity
   0.08 → 0.58. Độ nét còn lại nhân dồn qua từng lớp:
     0.92 → 0.81 → 0.67 → 0.52 → 0.38 → 0.24 → 0.13 → 0.055
   Mỗi mép chỉ giảm ~11–13% tương đối, và mép TRÊN CÙNG — chỗ dễ lộ nhất vì lớp
   phủ còn bằng 0 — chỉ giảm 8%.

   Vì sao không mask: mask là cách mượt nhất, nhưng đặt trên lớp backdrop-filter
   thì trên Chrome nó làm nhạt luôn phần blur, còn ở trình duyệt nhúng không có
   tăng tốc GPU (Simple Browser của Cursor/VS Code) thì blur mất hẳn — cùng file
   CSS mà Chrome thấy kính, Cursor không thấy.

   Vì sao lớp phủ tối phải là phần tử RIÊNG, không đặt background lên .wglass:
   background của .wglass nằm trong "nền" của từng lớp con nên bị composite lại
   mỗi lớp một lần — đo được đáy dải tụt từ 9.9 xuống 0.0 (đen kịt). */
.wglass {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  /* Cao tính bằng PX, không phải %.
     Bản trước để 52% khung: ra 468px ở 1440 và 562px ở 1920, tức trùm gần một
     phần ba section và vọt 191–285px lên phía trên cụm list — che quá nhiều ảnh.
     Cụm list bám đáy nên vị trí của nó so với đáy khung gần như KHÔNG đổi theo
     khổ máy (đo 4 khổ: đáy list cách đáy khung 130px, cao 151–158px). Vậy dải
     kính chỉ cần: 158 (list) + 130 (khe chừa dock) + ~32 (thở phía trên) ≈ 320.
     Một số px cố định do đó vừa khít ở mọi khổ, còn % thì không.
     Nhân với --wp để lúc khung còn nhỏ dải không cao quá khung. */
  height: calc(110px + 210px * var(--wp));
  /* KHÔNG background, KHÔNG filter ở đây — xem chú thích trên */
  pointer-events: none;
}
.wglass > i {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
}
/* MỘT lớp mờ duy nhất, thang 0 → 100% làm bằng MASK gradient.
   Bản trước xếp TÁM lớp backdrop-filter chồng nhau, mỗi lớp một mức opacity, vì đo
   được opacity pha loãng backdrop-filter đúng tuyến tính. Nó ra đúng thang mong muốn
   nhưng ĐẮT: mỗi lớp backdrop-filter là một bản sao vùng nền phía sau, tám lớp là
   tám bản sao mỗi khung hình. Đo lúc cuộn qua Selected Work (1512×850, DPR 2):

     | cách làm                    | khung >24ms | tốn thêm |
     |-----------------------------|-------------|----------|
     | tám lớp (bản cũ)            | 13–14 / 410 | ~560ms   |
     | còn bốn lớp                 | 13 / 413    | ~400ms   |
     | tám lớp, blur 7→3           | 14 / 409    | ~525ms   |
     | tám lớp, chiều cao cố định  | 11 / 406    | ~500ms   |
     | MỘT lớp + mask              | 5 / 441     | ~163ms   |

   Tức tiền nằm ở SỐ LỚP, không phải bán kính hay chiều cao — hạ bán kính hay bỏ
   phần cao động gần như không đổi gì.

   README cũ có ghi "đừng dùng mask trên backdrop-filter, Chrome làm mờ yếu hẳn".
   Đo lại thì KHÔNG còn đúng: so từng hàng 10px suốt cả dải, độ nét còn lại của bản
   một-lớp-mask bám sát bản tám lớp, lệch ≤ 3% (nhiễu đo). Chỉ cần bù bán kính
   7 → 9 vì không còn tám lớp cộng dồn. */
.wglass__b {
  top: 0;
  backdrop-filter: blur(9px) saturate(105%);
  -webkit-backdrop-filter: blur(9px) saturate(105%);
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0) 100%);
  mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0) 100%);
}
/* Lớp phủ tối — vẽ sau cùng nên chỉ composite một lần, và KHÔNG có backdrop-filter
   nên không bị opacity của mấy lớp trên ảnh hưởng.
   Chín chặng theo đường cong chậm trên nhanh dưới (đại khái alpha ∝ d²), không có
   đoạn phẳng: 0 ở đỉnh dải → .06 ở chỗ chữ bắt đầu → .70 ở đáy. */
.wglass__v {
  top: 0;
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.7) 0%,
    rgba(0, 0, 0, 0.6) 12%,
    rgba(0, 0, 0, 0.48) 26%,
    rgba(0, 0, 0, 0.36) 40%,
    rgba(0, 0, 0, 0.25) 54%,
    rgba(0, 0, 0, 0.155) 68%,
    rgba(0, 0, 0, 0.08) 82%,
    rgba(0, 0, 0, 0.03) 92%,
    rgba(0, 0, 0, 0) 100%
  );
}
/* Trình duyệt không đỡ backdrop-filter thì đậm lớp phủ lên, để dải vẫn ra dáng
   một dải kính chứ không phẳng lì. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wglass__v {
    background: linear-gradient(
      to top,
      rgba(0, 0, 0, 0.82) 0%,
      rgba(0, 0, 0, 0.72) 12%,
      rgba(0, 0, 0, 0.6) 26%,
      rgba(0, 0, 0, 0.46) 40%,
      rgba(0, 0, 0, 0.33) 54%,
      rgba(0, 0, 0, 0.21) 68%,
      rgba(0, 0, 0, 0.11) 82%,
      rgba(0, 0, 0, 0.04) 92%,
      rgba(0, 0, 0, 0) 100%
    );
  }
}

/* ---------- lớp chuyển tiếp xuống section "More Projects" ----------
   Tan dần về đúng #181818 — nền của section ngay dưới — nên hai section nối liền
   nhau thay vì có một đường cắt ngang.
   Cao 200: đáy cụm list cách đáy khung 130, nên ở mức đó lớp phủ mới đậm 35% —
   chữ vẫn nổi (chữ nằm TRÊN lớp này và còn có text-shadow), mà phần dưới cùng thì
   đã đặc hẳn.
   Nằm trong .work__frame nên bản mobile không có: ở ≤809 khung dính bị tắt hẳn. */
.wfade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 200px;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(24, 24, 24, 0) 0%, #181818 100%);
}

/* ---------- description ở góc dưới trái ---------- */
/* Khoảng cách đáy 110 (không phải 93) là để chừa hẳn cho dock nổi: dock cao 89
   và cách đáy màn 20, còn cụm này bị margin-bottom −11 đẩy xuống — để 93 thì hộp
   danh sách lấn 7px vào dock ở dải 810–1280. Đo lại 20 khổ máy: 0 chồng. */
/* Note và danh sách KHÔNG dùng .rise: chúng hiện theo cú nở của ảnh, do lớp
   .is-full mà JS gắn khi --wp ≥ 0.86 — xem chú thích trong site.js. */
.wnote,
.wlist {
  opacity: 0;
  translate: 0 26px;
  transition:
    opacity 0.55s cubic-bezier(0.22, 1, 0.36, 1),
    translate 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.work.is-full .wnote,
.work.is-full .wlist {
  opacity: 1;
  translate: 0 0;
}
.work.is-full .wlist {
  transition-delay: 0.08s;
}
.wnote {
  position: absolute;
  left: calc(var(--pad) + (var(--sec-pad) - var(--pad)) * var(--wp));
  bottom: calc(20px + 110px * var(--wp));
  max-width: 260px;
  margin: 0;
  /* 14px cho desktop + tablet; 16 từ 1600 (khối min-width: 1600px), 12 ở mobile
     (dùng .wmob__note, không dùng cụm này) */
  font-size: 14px;
  line-height: 19px;
  letter-spacing: 0.04em;
  /* .9 chứ không .78: ref-content.jpg đo được nét chữ #e3e3e3 (227), mức .78 chỉ
     ra #c8c9ca (200). */
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.62);
}

/* ---------- danh sách dự án: một góc bên PHẢI ----------
   Ref có danh sách trải hết bề ngang, nhưng đã chốt là thu về một góc. Trong
   mỗi hàng: TÊN bên trái, Quick Look bên phải — CTA nằm trong hàng chứ không
   phải một nút rời, nhờ vậy nó luôn đúng bên phải hàng đang xem mà không cần
   JS chỉnh vị trí. */
.wlist {
  position: absolute;
  right: calc(var(--pad) + (var(--sec-pad) - var(--pad)) * var(--wp));
  bottom: calc(20px + 110px * var(--wp));
  /* đẩy xuống đúng bằng đệm dưới của một hàng, để đáy CHỮ hàng cuối ngang với
     đáy chữ của cụm note bên trái — hai cụm cùng neo một mốc bottom */
  margin-bottom: -11px;
  width: min(460px, 46%);
}
.wlist__rows {
  margin: 0;
  padding: 0;
  list-style: none;
}
.wrow {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 11px 0;
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 700;
  line-height: 1.25;
  /* Ba tên ĐỀU trắng. Vạch chạy là thứ duy nhất chỉ ra thumbnail đang hiển
     thị — làm mờ hai hàng kia nữa là thừa và đọc khó. */
  color: #fff;
  /* Bóng SÁT NÉT (3px), không phải quầng: vẫn đủ tách chữ khỏi ảnh ở chỗ lớp phủ
     mới .06, mà không hiện thành đám xám dưới chữ như quầng 10px trước đó. */
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.62);
}
.wrow + .wrow {
  border-top: 1px solid rgba(255, 255, 255, 0.22);
}
/* Lớp kính khi rê chuột vào hàng — nền trắng 20% + blur.
   HÌNH CHỮ NHẬT ĐÚNG BẰNG ĐƯỜNG LINE: inset: 0 nên nó phủ đúng padding-box của
   hàng, tức trùng khít bề ngang của .wrow__bar và hai kẻ ngăn hàng. Không bo góc,
   không viền — chính đường line làm mép.
   Vì thế phần lùi vào của chữ + nút phải nằm ở .wrow__a (padding), KHÔNG được đặt
   padding lên .wrow: left/right của con absolute tính theo padding-box, nên làm
   thế thì cả thanh progress lẫn lớp kính cùng co lại.
   Lúc ẩn dùng opacity: 0 — opacity pha loãng đúng tuyến tính cả backdrop-filter
   (xem mục dải kính ở đáy), nên kính hiện dần chứ không bật tắt đột ngột. */
.wrow::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(10px) saturate(118%);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  opacity: 0;
  transition: opacity 0.28s ease;
  pointer-events: none;
  z-index: 0;
}
.wrow:hover::before,
.wrow:focus-within::before {
  opacity: 1;
}

/* Vùng bấm = TRỌN HÀNG. Một thẻ <a> trải hết bề ngang hàng, bọc cả tên và nút,
   nên bấm vào chữ tên hay vào khoảng trống giữa hai thứ cũng mở modal.
   min-width: 0 để .wrow__n còn cắt được bằng ellipsis. */
.wrow__a {
  /* position + z-index là BẮT BUỘC, không phải trang trí. .wrow::before là
     position: absolute nên nó vẽ ở tầng "positioned descendants", tức TRÊN nội
     dung tĩnh — để .wrow__a tĩnh thì lớp kính hover phủ lên chữ và làm nhoè
     chính cái tên dự án. Đã mắc một lần. */
  position: relative;
  z-index: 1;
  flex: 1;
  min-width: 0;
  /* lùi chữ và nút vào so với mép chữ nhật hover / đường line */
  padding-inline: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  color: inherit;
  text-decoration: none;
}
.wrow__n {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Quick Look hiện khi rê chuột vào CHÍNH hàng đó — không phụ thuộc hàng nào
   đang chạy. Dùng opacity chứ không display: none, thêm :focus-within cho
   bàn phím. */
.wrow__cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  text-decoration: none;
  opacity: 0;
  translate: -6px 0;
  pointer-events: none;
  transition:
    opacity 0.28s ease,
    translate 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.wrow:hover .wrow__cta,
.wrow:focus-within .wrow__cta {
  opacity: 1;
  translate: 0 0;
  pointer-events: auto;
}
.wrow__cta svg {
  width: 18px;
  height: 18px;
  transition: translate 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
/* mũi tên nhích khi rê chuột vào BẤT KỲ đâu trong hàng, không riêng cái nhãn —
   cả hàng là một vùng bấm thì phản hồi cũng phải theo cả hàng */
.wrow:hover .wrow__cta svg {
  translate: 5px 0;
}
/* vạch accent chạy hết trong 5s dưới hàng đang xem */
.wrow__bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--wbar) * -0.5);
  height: var(--wbar);
  border-radius: var(--wbar);
  background: var(--wacc);
  transform-origin: 0 50%;
  scale: 0 1;
}
.wrow.is-on .wrow__bar.is-run {
  animation: workProg 5s linear; /* PHẢI khớp HOLD_MS trong site.js */
}
@keyframes workProg {
  from {
    scale: 0 1;
  }
  to {
    scale: 1 1;
  }
}

/* ---------- BẢN MOBILE (≤809px): stack thẻ, không có khung dính ----------
   Theo reference/home-work/ref-home-work-mobile-1…2.png. Bố cục khác desktop
   hẳn, nên đổi cả cấu trúc chứ không co bản desktop lại: headline + note nằm
   trên nền kem, dưới là ba thẻ ảnh tỉ lệ 1:1, tên và Quick Look nằm ở đáy
   từng thẻ. */
.wmob {
  display: none;
  position: relative;
  z-index: 1; /* trên lớp lưới ::before */
}
@media (max-width: 809px) {
  .work {
    /* bỏ đường cuộn và khung dính — section này giờ chạy theo luồng bình thường */
    min-height: 0;
    /* Trên 80 dưới 80 thì MỐI NỐI với About bị cộng dồn: đo nội dung thật (đã
       tắt translate của .rise) ra 144px ở mobile và 176px ở tablet, trong khi mối
       nối work→journey chỉ 104 và 136. Chênh 40px là do cụm cuối của About kết
       thúc sớm 40px so với mép padding của nó.
       Cắt 40 ở padding-TRÊN của Work để hai mối nối bằng nhau, thay vì sửa
       padding của About (112/80 là số đã chốt trong bảng). Padding dưới giữ
       nguyên 80 nên nhịp với Journey không đổi. */
    padding-top: 40px;
    padding-bottom: 80px;
  }
  .work__stage {
    display: none;
  }
  .wmob {
    display: block;
    text-align: left;
  }
  .chip--work {
    margin: 0 0 14px;
  }
  .wmob__title {
    width: auto;
    margin: 0;
    font-family: var(--font-display);
    font-size: 32px;
    font-weight: 700;
    line-height: 1.16;
    letter-spacing: -0.01em;
    color: var(--text);
  }
  .wmob__note {
    margin: 20px 0 0;
    padding: 18px 0;
    border-top: 1px solid rgba(0, 0, 0, 0.14);
    border-bottom: 1px solid rgba(0, 0, 0, 0.14);
    font-size: max(9px, 12px);
    line-height: 18px;
    letter-spacing: 0.04em;
    color: color-mix(in srgb, var(--text) 62%, transparent);
  }
  .wmob__cards {
    display: grid;
    gap: 20px;
    margin: 24px 0 0;
    padding: 0;
    list-style: none;
  }
  .wmcard__a {
    position: relative;
    display: block;
    border-radius: 20px;
    overflow: hidden;
    color: #fff;
    text-decoration: none;
  }
  /* ảnh TỈ LỆ 1:1 — aspect-ratio giữ đúng ô vuông ở mọi bề rộng máy */
  .wmcard__img {
    display: block;
    aspect-ratio: 1;
  }
  .wmcard__img img,
  .wmcard__img .slot {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .wmcard__img .slot {
    font-size: max(9px, 11px);
    line-height: 1.6;
    background: rgba(0, 0, 0, 0.35);
    border-color: rgba(255, 255, 255, 0.35);
    color: rgba(255, 255, 255, 0.9);
  }
  /* dải tối ở đáy thẻ để tên và Quick Look luôn đọc được trên mọi ảnh */
  .wmcard__body {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: grid;
    gap: 10px;
    padding: 56px 20px 20px;
    background: linear-gradient(
      to top,
      rgba(0, 0, 0, 0.62) 0%,
      rgba(0, 0, 0, 0.3) 55%,
      rgba(0, 0, 0, 0) 100%
    );
  }
  .wmcard__name {
    font-family: var(--font-display);
    font-size: 24px;
    font-weight: 700;
    line-height: 1.2;
  }
  .wmcard__cta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-display);
    font-size: 14px;
    font-weight: 700;
  }
  .wmcard__cta svg {
    width: 18px;
    height: 18px;
  }
}
@media (max-width: 390px) {
  .wmob__title {
    font-size: 28px;
  }
}

/* Tablet, mobile và mọi thiết bị KHÔNG có hover: bỏ hẳn cơ chế hover, luôn
   hiện — và chỉ hiện MŨI TÊN, không có chữ "Quick Look" (chỗ hẹp, chữ chiếm
   mất phần tên dự án). Dùng cả mốc bề rộng lẫn (hover: none) vì có máy bảng
   báo bề rộng như laptop. */
@media (hover: none) {
  /* Không có chuột thì không có hover — cho kính bám hàng ĐANG hiển thị, để vẫn
     thấy được hàng nào ứng với thumbnail trên khung. */
  .wrow.is-on::before {
    opacity: 1;
  }
}
@media (max-width: 1439px), (hover: none) {
  .wrow__cta {
    opacity: 1;
    translate: 0 0;
    pointer-events: auto;
    gap: 0;
    font-size: 0; /* giấu chữ, chừa lại icon */
  }
  .wrow__cta svg {
    width: 22px;
    height: 22px;
  }
}

/* ---------- Selected Work: các khổ nhỏ hơn ---------- */
@media (min-width: 1600px) {
  .work {
    --wbar: 4px;
  }
  .wnote {
    font-size: 16px;
    line-height: 22px;
    max-width: 300px;
  }
}
@media (max-width: 1439px) {
  .work__title {
    font-size: 40px;
  }
  /* Chip bám theo bề rộng dòng chữ, không phải một số cố định: ở ref, chip lệch
     446px trên dòng 1 rộng 526px (font 48) → tỉ lệ 0.848. Font tụt về 40 thì dòng
     1 còn ~438px, nên lệch 438 × 0.848 ≈ 372. Để nguyên 446 thì chip trôi ra
     ngoài phần chữ. */
  .work__head .chip--work {
    left: calc(var(--sec-pad) + 372px);
  }
  /* Cụm title tụt thêm 44px: từ 810 tới 1439 có thanh header kính cao 60, mà chip
     neo cao hơn headline 50px nên badge của nó rơi vào y≈26 — nằm hẳn dưới thanh.
     28 + 110 = 138 khi full màn đưa badge về y≈70, hở 10px dưới thanh.
     Ở --wp = 0 thì khung còn thụt vào --wpad nên 28 đã nằm dưới thanh rồi.
     PHẢI đặt sau khai báo gốc của .work__head (cùng specificity 0,1,0, thắng nhau
     bằng thứ tự) — đặt trước thì không ăn. Đã mắc một lần. */
  .work__head {
    padding-top: 138px;
  }
  .work__head .chip--work {
    top: 88px; /* = 138 − 50 */
  }
  .wrow {
    font-size: 22px;
  }
}
@media (max-width: 809px) {
  .work {
    --wpad: 80px; /* = padding-block của section ở khổ này */
    --wr: 20px;
    --wbar: 2px;
  }
  .work__title {
    font-size: 32px;
  }
  .work__head {
    /* lúc full màn phải xuống dưới header nổi cao 60: badge của chip thò lên
       16 so với hộp chip, nên 88 − 16 = 72 vẫn hở 12px dưới header */
    top: calc(20px + 68px * var(--wp));
  }
  /* hai cụm dưới xếp CHỒNG: đứng cạnh nhau thì mỗi bên chưa tới 160px */
  .wnote {
    right: calc(var(--pad) + (var(--sec-pad) - var(--pad)) * var(--wp));
    max-width: none;
    bottom: calc(20px + 110px * var(--wp) + 148px);
  }
  .wlist {
    left: calc(var(--pad) + (var(--sec-pad) - var(--pad)) * var(--wp));
    width: auto;
  }
  .wrow {
    font-size: 20px;
    padding: 9px 0;
  }

}
@media (max-width: 390px) {
  .work__title {
    font-size: 28px;
  }
}

/* ============================================================
   MODAL XEM NHANH DỰ ÁN
   Dựng theo reference/modal-preview-project/ref-*-1…4.png.
   Side-sheet neo bên PHẢI, cuộn được, nút Project Detail dính đáy.

   Bề rộng: 50vw như đã chốt từ đầu, chặn trên ở 760 — trùng luôn số đo của ref
   (modal 760 trên khung 2334). Ở 1440 thì 50vw = 720, đúng nửa trang.
   Cỡ chữ theo thang của site chứ không theo ref (ref dùng headline 56 /
   section 46 / body 18): 40 / 32 / 16.
   ============================================================ */
.pm[hidden] {
  display: none;
}
.pm {
  position: fixed;
  inset: 0;
  z-index: 9500; /* trên dock (60) và panel (70), dưới màn chờ (9000)… */
  /* Bốn con số điều khiển toàn bộ khung modal, đổi ở một chỗ là cả bộ theo:
       --pm-lề    hở bốn phía · --pm-bo  bo góc tấm
       --pm-x     đường kính nút đóng · --pm-x-t  nút đóng cách mép trên
     Dải mờ đỉnh tính THẲNG từ hai số cuối nên không bao giờ lệch tâm nút X. */
  --pm-lề: 20px;
  --pm-bo: 20px;
  --pm-x: 36px;
  --pm-x-t: 18px;
}
.pm__scrim {
  position: absolute;
  inset: 0;
  background: rgba(12, 10, 8, 0.72);
  opacity: 0;
  transition: opacity 0.36s ease;
}
.pm.is-on .pm__scrim {
  opacity: 1;
}
.pm__sheet {
  position: absolute;
  top: var(--pm-lề);
  right: var(--pm-lề);
  bottom: var(--pm-lề);
  width: min(50vw, 760px);
  display: flex;
  flex-direction: column;
  border-radius: var(--pm-bo);
  background: #fff;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.32);
  overflow: hidden;
  translate: 24px 0;
  opacity: 0;
  transition:
    translate 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.32s ease;
}
.pm.is-on .pm__sheet {
  translate: 0 0;
  opacity: 1;
}
.pm__x {
  position: absolute;
  top: var(--pm-x-t);
  right: var(--pm-x-t);
  z-index: 3; /* trên cả dải mờ đỉnh */
  width: var(--pm-x);
  height: var(--pm-x);
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  /* nền XÁM có kính, cùng nhà với hai nút mũi tên của băng ảnh */
  background: rgba(17, 17, 17, 0.1);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  border: 1px solid rgba(17, 17, 17, 0.06);
  color: var(--text);
  cursor: pointer;
  transition: scale 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.pm__x:hover {
  scale: 1.06;
}
.pm__x svg {
  width: 18px;
  height: 18px;
}
/* X hoá dấu TRỪ khi rê — y hệt .panel__close của menu mở rộng: hai vạch xoay riêng,
   rê thì trả vạch ngang về 0° và làm mờ vạch dọc. Màu và nền KHÔNG đổi. */
.pm__x .cx,
.pm__x .cy {
  transform-box: view-box;
  transform-origin: 12px 12px;
  rotate: 45deg;
  transition:
    rotate 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.22s;
}
.pm__x:hover .cx {
  rotate: 0deg;
}
.pm__x:hover .cy {
  opacity: 0;
}
/* Dải mờ ở đỉnh: trắng đặc → trong suốt, cao đúng tới TÂM nút X (cách đỉnh + nửa đường
   kính). Tính bằng token nên đổi cỡ nút ở khổ nào cũng tự khớp. Chỉ hiện khi đã cuộn. */
.pm__fade {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: calc(var(--pm-x-t) + var(--pm-x) / 2);
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(180deg, #fff 0%, rgba(255, 255, 255, 0) 100%);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.pm.đã-cuộn .pm__fade {
  opacity: 1;
}
.pm__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 28px 32px 24px;
}
/* Chân modal: một dải TRẮNG ĐẶC dính đáy, có kẻ mảnh phía trên. Bản trước là gradient
   mờ dần và `margin-top: -34px` nên chữ cuộn qua vẫn lộ mờ mờ dưới nút. */
.pm__foot {
  flex: none;
  display: grid;
  place-items: center;
  padding: 14px 32px 16px;
  background: #fff;
  border-top: 1px solid rgba(0, 0, 0, 0.08);
  position: relative;
  z-index: 1;
}
.pm__cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 10px 10px 10px 22px;
  /* 12px thay cho 999px (viên thuốc) — và nhỏ dần theo khổ màn ở khối media dưới */
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.16);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: #0876b7;
  text-decoration: none;
  isolation: isolate;
  transition: scale 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.pm__cta:hover {
  scale: 1.05;
}
/* Bốn vòng màu loang kín nút rồi kết ở xanh — chép đúng cơ chế .seemore__ink của nút
   "More about ThuHa": tâm ở ĐÁY GIỮA, Ø = 200% BỀ RỘNG NÚT (số % chứ không phải px cố
   định, để đúng với mọi bề rộng chữ). */
.pm__cta-ink {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  z-index: 0;
}
.pm__cta-ink i {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 200%;
  aspect-ratio: 1;
  margin: 0 0 -100% -100%;
  border-radius: 50%;
  scale: 0;
  transition: scale 0.6s cubic-bezier(0.32, 0.9, 0.36, 1);
  transition-delay: 0s;
}
.pm__cta-ink i:nth-child(1) {
  background: #ea6963;
}
.pm__cta-ink i:nth-child(2) {
  background: #f2cc3d;
}
.pm__cta-ink i:nth-child(3) {
  background: #69c465;
}
.pm__cta-ink i:nth-child(4) {
  background: #0e9af0;
}
.pm__cta:hover .pm__cta-ink i,
.pm__cta:focus-visible .pm__cta-ink i {
  scale: 1;
}
.pm__cta:hover .pm__cta-ink i:nth-child(2),
.pm__cta:focus-visible .pm__cta-ink i:nth-child(2) {
  transition-delay: 0.16s;
}
.pm__cta:hover .pm__cta-ink i:nth-child(3),
.pm__cta:focus-visible .pm__cta-ink i:nth-child(3) {
  transition-delay: 0.32s;
}
.pm__cta:hover .pm__cta-ink i:nth-child(4),
.pm__cta:focus-visible .pm__cta-ink i:nth-child(4) {
  transition-delay: 0.48s;
}
.pm__cta-tx {
  position: relative;
  z-index: 1;
  /* HAI LỚP CHỮ chồng khít nhau, cùng glyph cùng vị trí:
       · lớp gốc  — trắng, luôn được tô;
       · ::before — bản gradient (headline "Hi, I'm Thu Ha"), nằm trên, MỜ DẦN đi.
     Chỉ `opacity` chạy transition nên chữ KHÔNG BAO GIỜ mất nét: 0→0.32s còn nguyên
     gradient (vòng màu đang loang phía sau), 0.32→0.50s gradient tan để lộ chữ trắng.

     Đã thử và BỎ hai cách khác:
       (1) tắt `background-clip` rồi đổi `color` — hai thứ đó không transition được nên
           suốt 0.32s chờ chữ ở `color: transparent` mà đã mất gradient ⇒ biến mất;
       (2) transition chính `background-image` từ gradient xanh sang gradient trắng —
           đo ra Chrome KHÔNG nội suy gradient: nhảy thẳng ngay khung thứ hai (t=5ms)
           và bỏ luôn cả `transition-delay`, nên chữ trắng nằm trên nền còn trắng. */
  color: #fff;
}
.pm__cta-tx::before {
  content: attr(data-tx);
  position: absolute;
  inset: 0;
  background-image: linear-gradient(90deg, #014268, #0d9bee);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* đợi vòng xanh loang tới rồi mới nhường chỗ, đúng nhịp .seemore__tx */
  transition: opacity 0.26s ease 0.52s;
}
.pm__cta:hover .pm__cta-tx::before,
.pm__cta:focus-visible .pm__cta-tx::before {
  opacity: 0;
}
.pm__cta-go {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: linear-gradient(90deg, #014268, #0d9bee);
  color: #fff;
  transition:
    background 0.26s ease 0.52s,
    color 0.26s ease 0.52s,
    rotate 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Rê chuột: mũi tên hất chéo LÊN 45°, vòng vàng hoá trắng, mũi tên đen hoá xanh */
.pm__cta:hover .pm__cta-go,
.pm__cta:focus-visible .pm__cta-go {
  rotate: -45deg;
  background: #fff;
  color: #0876b7;
}
.pm__cta-go svg {
  width: 16px;
  height: 16px;
}

/* ---------- thân ---------- */
.pm__eyebrow {
  margin: 0 44px 0 0;
  font-size: max(9px, 13px);
  color: color-mix(in srgb, var(--text) 62%, transparent);
}
.pm__title {
  margin: 10px 0 0;
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 700;
  line-height: 1.14;
  letter-spacing: -0.01em;
  color: var(--text);
}
.pm__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
}
.pm__tags li {
  padding: 6px 12px;
  border: 1px solid rgba(0, 0, 0, 0.18);
  border-radius: 4px;
  font-size: max(9px, 12px);
  letter-spacing: 0.02em;
  color: color-mix(in srgb, var(--text) 78%, transparent);
}
.pm__meta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px 24px;
  margin: 24px 0 0;
  padding: 22px 0 0;
  border-top: 1px solid rgba(0, 0, 0, 0.12);
}
.pm__meta dt {
  font-size: max(9px, 12px);
  letter-spacing: 0.04em;
  color: color-mix(in srgb, var(--text) 55%, transparent);
}
.pm__meta dd {
  margin: 4px 0 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
}
/* Ô Client có LINK: chữ + mũi tên chéo nhỏ, mũi tên PHẢI được chốt cỡ ở đây. Không có
   rule này thì <svg> không có width/height nở ra bằng cả ô (đo được 165px) — bản trước
   rule nằm chung một danh sách với `.cs__meta` của trang chi tiết cũ, xoá khối kia là
   mất luôn cả phần cho modal. */
.pm__meta dd a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--text) 28%, transparent);
  transition:
    color 0.2s ease,
    border-color 0.2s ease;
}
.pm__meta dd a svg {
  flex: none;
  width: 15px;
  height: 15px;
}
.pm__meta dd a:hover {
  color: #0876b7;
  border-bottom-color: currentColor;
}
.pm__meta dd small {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  line-height: 17px;
  font-weight: 400;
  color: color-mix(in srgb, var(--text) 58%, transparent);
}
/* ---------- bốn hàng số liệu của "Project results" ----------
   UI theo reference/project-orderevent/preview/ref-prj-results.png + các mức Thu Hà chốt:
   nhãn bên trái CĂN NGANG DÒNG với số bên phải, số đậm hơn nhãn, đường kẻ CHỈ nằm GIỮA
   các hàng (không có kẻ trên hàng đầu và kẻ dưới hàng cuối) và để opacity thấp. */
.pm__stats {
  margin: 18px 0 0;
  display: grid;
}
.pm__stats > div {
  display: flex;
  /* CĂN GIỮA theo chiều dọc, KHÔNG phải `baseline`: số 34px và nhãn 14px chênh nhau quá nhiều,
     căn baseline là đặt chân chữ nhãn ngang chân số nên nhãn trông tụt hẳn xuống dưới. */
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 13px 0;
}
.pm__stats > div + div {
  border-top: 1px solid rgba(0, 0, 0, 0.08);
}
.pm__stats dt {
  /* co được VÀ ngắt dòng được: có dự án lấy chữ làm giá trị (Msky Hospitality:
     "Rooms + requests + content") nên hai bên phải chia nhau bề ngang, không ai được cắt. */
  flex: 0 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: 14px;
  line-height: 1.4;
  color: color-mix(in srgb, var(--text) 62%, transparent);
}
/* CON SỐ là điểm nhấn của mục "Project results" nên nằm ngay dưới `.pm__title` trong thang chữ
   của modal, và GIẢM DẦN theo đúng bốn tầng của modal — không chốt một cỡ cho mọi khổ:
     desktop 34 · tablet (≤1023 hoặc màn vuông) 28 · ≤767 24 · ≤390 22
   Đối chiếu thang có sẵn: `.pm__title` 40/32/26/23 · `.pm__sec h3` 32/26/22/20.
   Bản trước để 22 cố định — nhỏ hơn cả h3 của chính mục đó nên số không còn ra dáng số liệu.
   Ba mức nhỏ hơn khai TRONG các khối @media của modal (xem phía dưới) để cả modal chỉ có MỘT
   thang, không rải rác nhiều nguồn. */
.pm__stats dd {
  /* KHÔNG `flex: none`: giá trị là chữ dài thì bị cắt mất chữ cuối ở 390 (đo được
     "Rooms + requests + content" đứt ở "cont"). Cho co và ngắt dòng, vẫn căn phải. */
  flex: 0 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
  text-align: right;
  margin: 0;
  font-family: var(--font-display);
  font-size: 34px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* ---------- băng ảnh ----------
   CỐ ĐỊNH CHIỀU CAO, bề rộng chạy theo tỉ lệ thật của file — ảnh vuông ra ô vuông,
   ảnh ngang ra chữ nhật ngang, ảnh dọc ra chữ nhật dọc. Vì thế KHÔNG dùng
   `aspect-ratio` + `object-fit: cover` như bản trước (bản đó ép mọi ảnh về 3/4 và cắt
   mất hai đầu). */
.pm__gal {
  position: relative;
  margin: 26px 0 0;
  --pm-gal-h: 320px;
}
.pm__gal-row {
  display: flex;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  overflow-y: hidden; /* nếu không, trục dọc tự thành auto và khay ăn mất cú kéo dọc */
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  border-radius: 12px;
}
.pm__gal-row::-webkit-scrollbar {
  display: none;
}
.pm__gal-row li {
  flex: 0 0 auto; /* bề rộng do chính ảnh quyết định */
  scroll-snap-align: center;
}
.pm__gal-row img {
  display: block;
  height: var(--pm-gal-h);
  width: auto;
  max-width: none;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.04);
}
/* Hai nút lui / tới: vòng tròn KÍNH, icon trắng, nằm giữa hai mép băng ảnh */
.pm__gal-nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 50%;
  background: rgba(20, 20, 20, 0.28);
  backdrop-filter: blur(10px) saturate(130%);
  -webkit-backdrop-filter: blur(10px) saturate(130%);
  color: #fff;
  cursor: pointer;
  transition:
    background 0.2s ease,
    scale 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.pm__gal-nav:hover {
  background: rgba(20, 20, 20, 0.44);
  scale: 1.06;
}
.pm__gal-nav--lui {
  left: 10px;
}
.pm__gal-nav--toi {
  right: 10px;
}
.pm__gal-nav svg {
  width: 18px;
  height: 18px;
}
/* Chấm: SỐ LƯỢNG bằng số ảnh (JS dựng), bọc trong một viên kính cho nổi trên mọi ảnh */
/* RULE CHUNG của băng ảnh: băng VỪA trong khổ thì KHÔNG có mũi tên và chấm — nhìn là thấy hết,
   không có gì để vuốt. JS gắn class `vừa-khổ` khi `scrollWidth <= clientWidth` và tính lại mỗi
   lần đổi khổ màn (xem `nốiMộtBăng` trong site.js). */
.pm__gal.vừa-khổ .pm__gal-nav,
.pm__gal.vừa-khổ .pm__gal-dots {
  display: none;
}

.pm__gal-dots {
  position: absolute;
  left: 50%;
  bottom: 12px;
  translate: -50% 0;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.24);
  background: rgba(20, 20, 20, 0.28);
  backdrop-filter: blur(10px) saturate(130%);
  -webkit-backdrop-filter: blur(10px) saturate(130%);
}
.pm__gal-dots i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  opacity: 0.45;
  cursor: pointer;
  transition: opacity 0.2s;
}
.pm__gal-dots i.is-on {
  opacity: 1;
}
/* Băng BỔ SUNG (mục 3) chơi kiểu khác băng bắt buộc: ảnh ăn TRỌN bề ngang modal và giữ
   nguyên tỉ lệ file — mặc định nó là thumbnail bản desktop, ép về chiều cao cố định thì
   thành một dải bé tí giữa khay. */
.pm__gal--phu .pm__gal-row {
  border-radius: 16px;
}
.pm__gal--phu .pm__gal-row li {
  flex: 0 0 100%;
}
.pm__gal--phu .pm__gal-row img {
  width: 100%;
  height: auto;
  border-radius: 16px;
}
/* Khe hẹp CHỈ dùng khi ngay dưới ảnh bổ sung là thẻ thông điệp — lúc đó không có mục
   "Project results" xen vào nên hai thứ này là một cặp. Có Project results thì nó là một
   MỤC như mọi mục khác, phải giữ đúng khe chuẩn (34 + kẻ ngăn). */
.pm__gal--phu + .pm__quote {
  margin-top: 14px;
}
/* Băng ảnh BẮT BUỘC → "Project Overview": trên băng chỉ hở 26 mà dưới băng tận 60
   (34 margin + 26 padding của .pm__sec), nhìn ra là băng ảnh dính lên trên và rơi mất
   xuống dưới. Cho đều 26/26. `:not(--phu)` để không đụng khe của mục Project results. */
.pm__gal:not(.pm__gal--phu) + .pm__sec {
  margin-top: 26px;
  padding-top: 0;
}
/* ---------- các mục chữ ---------- */
.pm__sec {
  margin-top: 34px;
  padding-top: 26px;
  border-top: 1px solid rgba(0, 0, 0, 0.12);
}
.pm__sec:first-of-type {
  border-top: 0;
}
.pm__sec h3 {
  margin: 0 0 14px;
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -0.01em;
  color: var(--text);
}
.pm__sec p {
  margin: 0 0 14px;
  font-size: 16px;
  line-height: 26px;
  color: color-mix(in srgb, var(--text) 82%, transparent);
}
.pm__roles {
  display: grid;
  gap: 22px;
  margin: 22px 0 0;
  padding: 0;
  list-style: none;
}
.pm__roles li {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 14px;
  align-items: start;
}
.pm__role-ic svg,
.pm__role-ic img {
  display: block;
  width: 44px;
  height: 44px;
  object-fit: contain;
}
.pm__roles b {
  display: block;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--text);
}
.pm__roles b + span {
  display: block;
  margin-top: 6px;
  font-size: 15px;
  line-height: 24px;
  color: color-mix(in srgb, var(--text) 78%, transparent);
}
/* Thẻ thông điệp: cùng bộ nền với thẻ Contact ở trang chủ — lưới mảnh 97px chồng lên
   dải gradient xanh → vàng. Bản trước là dải nâu. */
.pm__quote {
  margin: 34px 0 0;
  padding: 30px;
  border-radius: 16px;
  background:
    linear-gradient(90deg, rgba(17, 158, 242, 0.05) 0 2px, transparent 2px) 50.5px 0 / 97px 100%,
    linear-gradient(180deg, rgba(17, 158, 242, 0.05) 0 2px, transparent 2px) 0 81.5px / 100% 97px,
    linear-gradient(94.85deg, #003d62 0%, #008fe6 70.6%, #e3b162 100%);
  color: #fff;
}
.pm__quote p {
  margin: 0;
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 500;
  line-height: 1.35;
}
.pm__quote-note {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-top: 26px;
  padding-top: 18px;
  border-top: 1px solid rgba(255, 255, 255, 0.32);
  font-size: max(9px, 12px);
  line-height: 18px;
  letter-spacing: 0.03em;
  color: rgba(255, 255, 255, 0.86);
}
.pm__quote-note svg {
  flex: none;
  width: 18px;
  height: 18px;
}

body.pm-lock {
  overflow: hidden;
}

/* ---------- TABLET (≤1023) ----------
   VẪN LÀ MODAL, không phải trang full-bleed: hở đều bốn phía, còn bo góc, còn lớp phủ
   đen phía sau. Bản trước cho `inset: 0; border-radius: 0` nên nó thành một trang khác,
   mất hẳn cảm giác modal.

   Điều kiện có thêm `(max-aspect-ratio: 1/1)` — cùng cách với section Contact: iPad Pro
   12.9 DỌC rộng 1024, lọt nhánh desktop nên tấm modal chỉ 50vw = 512 trên màn cao 1366,
   thành một dải hẹp lửng lơ. Hễ màn CAO HƠN RỘNG thì coi như tablet. */
@media (max-width: 1023px), (max-aspect-ratio: 1 / 1) {
  .pm__stats dd {
    font-size: 28px;
  }
  .pm {
    --pm-lề: 24px;
    --pm-bo: 20px;
  }
  .pm__sheet {
    left: var(--pm-lề);
    width: auto;
    translate: 0 24px; /* vào từ DƯỚI lên, không phải từ phải sang */
  }
  .pm__scroll {
    padding: 24px 24px 20px;
  }
  .pm__title {
    font-size: 32px;
  }
  .pm__sec h3 {
    font-size: 26px;
  }
  .pm__quote {
    padding: 24px;
  }
  .pm__quote p {
    font-size: 20px;
  }
  /* Bề rộng vẫn do tỉ lệ ảnh quyết định, chỉ HẠ chiều cao cho vừa khổ hẹp.
     Đừng để `flex: 0 0 100%` như bản trước — nó ép mọi ảnh về đúng bề rộng khay,
     tức lại mất cái tỉ lệ vừa làm. */
  .pm__gal {
    --pm-gal-h: 240px;
  }
  .pm__foot {
    padding: 12px 24px 14px;
  }
  .pm__cta {
    border-radius: 10px;
    padding: 9px 9px 9px 18px;
    font-size: 13px;
  }
  .pm__cta-go {
    width: 28px;
    height: 28px;
  }
}
/* ---------- MOBILE (≤767) ---------- */
@media (max-width: 767px) {
  .pm__stats dd {
    font-size: 24px;
  }
  .pm__stats dt {
    font-size: 13px;
  }
  .pm {
    --pm-lề: 16px;
    --pm-bo: 16px;
    --pm-x: 32px;
    --pm-x-t: 14px;
  }
  .pm__scroll {
    padding: 20px 18px 16px;
  }
  .pm__eyebrow {
    margin-right: 40px;
  }
  .pm__title {
    font-size: 26px;
  }
  .pm__tags li {
    font-size: 12px;
    padding: 5px 10px;
  }
  .pm__meta {
    gap: 14px 16px;
  }
  .pm__sec {
    margin-top: 26px;
    padding-top: 20px;
  }
  .pm__sec h3 {
    font-size: 22px;
  }
  .pm__sec p {
    font-size: 15px;
    line-height: 24px;
  }
  .pm__roles li {
    grid-template-columns: 36px 1fr;
    gap: 12px;
  }
  .pm__role-ic svg,
  .pm__role-ic img {
    width: 36px;
    height: 36px;
  }
  .pm__gal {
    --pm-gal-h: 200px;
  }
  .pm__gal-nav {
    width: 32px;
    height: 32px;
  }
  .pm__gal-nav--lui {
    left: 8px;
  }
  .pm__gal-nav--toi {
    right: 8px;
  }
  .pm__quote {
    padding: 20px;
    border-radius: 12px;
  }
  .pm__quote p {
    font-size: 18px;
  }
  .pm__foot {
    padding: 10px 18px 12px;
  }
  .pm__cta {
    border-radius: 10px;
    padding: 8px 8px 8px 16px;
    font-size: 13px;
  }
  .pm__cta-go {
    width: 26px;
    height: 26px;
  }
  .pm__cta-go svg {
    width: 14px;
    height: 14px;
  }
}
/* ---------- MÁY HẸP (≤390) ---------- */
@media (max-width: 390px) {
  .pm__stats dd {
    font-size: 22px;
  }
  .pm {
    --pm-lề: 12px;
    --pm-bo: 12px;
  }
  .pm__scroll {
    padding: 18px 16px 14px;
  }
  .pm__title {
    font-size: 23px;
  }
  .pm__sec h3 {
    font-size: 20px;
  }
  .pm__gal {
    --pm-gal-h: 180px;
  }
  .pm__cta {
    border-radius: 8px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pm__sheet,
  .pm__scrim {
    transition: none;
  }
}

/* Nút "Explore more of my work" ở bản mobile — thay cho cả section More Projects.
   Chỉ hiện ở ≤809; trên đó đã có nút trong section kia rồi. */
.wmob__more {
  display: none;
}
@media (max-width: 809px) {
  .wmob__more {
    /* width: max-content — .seemore là inline-flex, ép display: flex trơn sẽ biến
       nó thành block-level và giãn hết bề ngang. Phải bó theo nội dung như nút
       "More about ThuHa", rồi mới auto-margin để căn giữa. */
    display: flex;
    width: max-content;
    max-width: 100%;
    margin: 28px auto 0;
  }
}

/* Thanh header kính vốn là tông SÁNG (nền kem / ảnh trời). Đứng trên section
   "Projects." nền đen tuyệt đối thì nó thành một mảng xám đục rất lộ. body.on-dark
   do site.js bật khi dải 60px trên cùng của màn đang nằm trong section đó. */
body.on-dark .topbar {
  background: rgba(0, 0, 0, 0.42) !important;
  border-bottom-color: rgba(255, 255, 255, 0.16) !important;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12) !important;
}
body.on-dark .menu,
body.on-dark.has-scrolled .menu,
body.on-dark.is-scrolled .menu {
  color: #fff;
}

/* ============================================================
   SECTION 3B — ALL PROJECTS
   Dựng theo reference/home-work/ref-all-project.png (ảnh 1958×1192, quy về
   1440 bằng hệ số 1440/1958 = 0.7354).

   Băng thẻ chạy phải → trái, thẻ nhỏ dần về bên phải cho ra chiều sâu.

   KHÔNG dùng perspective 3D. Đã thử và bỏ: mặt phẳng nghiêng thật thì mọi thứ
   nằm trong nó bị nén ngang đúng cos(góc) — ảnh bẹp, chữ bẹp, không tránh được.
   Thay bằng site.js tự tính vị trí + SCALE ĐỀU cho từng thẻ (xem .allp__stage).
   ============================================================ */
.allp {
  /* Cỡ thẻ chấm theo yêu cầu "một màn thấy 3 thẻ đủ + nửa thẻ thứ 4": ở 1440 ra
     bề rộng 551 · 402 · 306 · 241, thẻ 1 bắt đầu đúng x=0 và tâm thẻ 4 rơi đúng
     1440. --ap-k điều khiển nhịp thu nhỏ (càng lớn càng thu nhanh).
     LƯU Ý khi đo lại: bộ chạy ghi transform của từng thẻ mỗi frame, phải khoá nó
     trước, không thì mọi số đo là ngẫu nhiên theo pha. */
  --ap-cw: 551px; /* bề rộng thẻ ở tỉ lệ 1 (thẻ trái nhất) */
  --ap-gap: 26px;
  --ap-k: 0.00064; /* hệ số thu nhỏ theo quãng đường dọc băng */
  /* ĐỘ LẬT của từng thẻ — hình thang giống ref.
     Ref đo thẻ 2: mép trái cao 431, mép phải 358 ⇒ tỉ lệ 1.204. Với
     perspective(P) rotateY(θ) quanh mép trái thì tỉ lệ đó = 1 + w·sin(θ)/P.
     Cặp (θ, P) không duy nhất, và đây là chỗ mấu chốt: mặt phẳng nghiêng nén chữ
     theo phương ngang đúng cos(θ), nên phải chọn GÓC NHỎ rồi bù bằng P NGẮN.
       θ = 34° (bản cũ) → nén còn 0.83, chữ và ảnh bẹp rõ
       θ = 20° + P = 1108 → cùng tỉ lệ 1.204 mà chỉ nén còn 0.94 */
  --ap-flip: 20deg;
  --ap-fp: 1108px;
  position: relative;
  background: #181818;
  overflow: hidden;
  /* Section BỔ TRỢ cho Selected Work nên để thấp. */
  padding-block: 56px 64px;
  overflow: hidden; /* thẻ chạy ra ngoài hai mép thì cắt gọn */
}
/* Lưới ô vuông nối tiếp từ About / Work — CÙNG ô 97 và CÙNG pha (--grid-y do
   site.js tính), nên ô vuông ở ba section bằng nhau và các đường thẳng hàng.
   Mask cho lưới HIỆN DẦN 0 → 100% từ trên xuống: đầu section vẫn là nền phẳng của
   Selected Work, càng xuống lưới càng rõ, sang Journey thì đủ đậm. */
/* Lề DƯỚI hạ còn 24 CHỈ Ở DESKTOP: section My journey ngay dưới có khối dính căn
   giữa 100svh, tự nó đã chừa sẵn khoảng trống phía trên — cộng cả hai thì khe giữa
   hai section lên tới 118px, quá rộng; hạ xuống còn 74.
   Tablet KHÔNG áp: ở đó khối dính bị bỏ / thấp hơn nên cắt tiếp là ăn vào
   title-chip của My journey. */
@media (min-width: 1280px) {
  .allp {
    padding-bottom: 24px;
  }
}
@media (min-width: 768px) {
  html.man-desktop .allp {
    padding-bottom: 24px;
  }
}
.allp::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    linear-gradient(90deg, var(--tối-line) 0 1px, transparent 1px),
    linear-gradient(180deg, var(--tối-line) 0 1px, transparent 1px);
  background-size: var(--ab-cell) var(--ab-cell);
  background-position: 50px var(--grid-y, 0px);
  /* Lưới CHỈ hiện ở dải đáy, không trải từ đầu section: đây là đoạn bắt cầu sang
     Journey, phần trên vẫn là nền phẳng của Selected Work. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, transparent 66%, rgba(0, 0, 0, 0.5) 84%, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, transparent 66%, rgba(0, 0, 0, 0.5) 84%, #000 100%);
}
.allp__head {
  position: relative;
  z-index: 1;
  /* --sec-pad, lề chung của mọi section: phải trùng cột với Work ngay trên. Để
     var(--pad) trơn thì tiêu đề lệch trái so với "A selection of products" và cụm
     description phía trên. */
  padding-inline: var(--sec-pad);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
}
/* Ref đo cap-height 169px trên khung 1958 ⇒ 124 CSS px ⇒ font-size 171 ở 1440
   (Mona Sans cap ≈ 0.727em). Quy ra 11.9vw — đúng bằng tỉ lệ của ref
   (232/1958 = 11.85vw) nên cỡ chữ giữ nguyên tỉ lệ ở mọi khổ, chỉ chặn hai đầu. */
.allp__title {
  position: relative;
  width: max-content;
  margin: 0;
  font-family: var(--font-display);
  font-size: 36px;
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -0.01em;
  color: #fff;
}
/* (07) — nằm ngay sau chữ, sát mép trên khối tiêu đề */
.allp__count {
  position: absolute;
  left: 100%;
  top: 0;
  margin-left: 0.06em;
  font-size: max(9px, 15px);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #fbbc02; /* đo từ ref */
  vertical-align: baseline;
}
/* Pill xanh góc phải. Ref: 163×55 ⇒ 120×40 ở 1440, nền #0F8055 */
.allp__cta {
  flex: none;
  margin-top: 8px;
  display: inline-flex;
  align-items: center;
  height: 40px;
  padding-inline: 22px;
  border-radius: 999px;
  background: #0f8055;
  font-family: var(--font-display);
  font-size: max(9px, 14px);
  font-weight: 700;
  color: #fff;
  text-decoration: none;
  transition:
    scale 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    background 0.3s;
}
.allp__cta:hover {
  scale: 1.05;
  background: #129964;
}

/* ---------- khay ----------
   KHÔNG dùng perspective + rotateY nữa. Mặt phẳng nghiêng thật thì mọi thứ nằm
   trong nó đều bị nén ngang đúng cos(góc) — ảnh bẹp, chữ bẹp, không cách nào
   tránh. Thay bằng: site.js tự tính vị trí và SCALE ĐỀU cho từng thẻ.
   Scale đều là phép biến hình duy nhất KHÔNG làm méo: thẻ chỉ nhỏ đi, không xô.
   Cảm giác lùi xa vẫn còn nguyên vì (a) thẻ nhỏ dần về bên phải và (b) chúng thu
   về ĐƯỜNG GIỮA nằm ngang — đúng cách thẻ trong ref hội tụ (đo ref: tâm dọc của
   thẻ 2 là 820 ở mép trái, 846 ở mép phải, gần như một đường). */
.allp__stage {
  position: relative;
  z-index: 1;
  margin-top: 40px;
  /* cao = thẻ to nhất (16:9) + chỗ cho tên bên dưới + phần THÒ RA của cú lật.
     Từng thẻ có --ap-flip nên bốn góc nhô ra ngoài hộp 16:9; đo ở cw 551: thẻ trái
     nhất thò lên 12 và xuống 12, mà sân khấu lại overflow: hidden nên bị cắt ngang
     đúng vào ảnh — thấy rõ nhất ở thẻ ngoài cùng bên trái. Phần thò tỉ lệ với bề rộng
     thẻ (≈ 2.2%) nên cộng 4.6% × --ap-cw là đủ cho cả trên lẫn dưới ở mọi khổ.
     Vẫn phải giữ overflow: hidden — bỏ đi thì các thẻ nằm tuyệt đối ở bên phải kéo
     trang sinh thanh cuộn ngang. */
  height: calc(var(--ap-cw) / 1.778 + 58px + var(--ap-cw) * 0.052);
  overflow: hidden;
  touch-action: pan-y;
  cursor: grab;
}
.allp__stage:active {
  cursor: grabbing;
}
/* Băng ĐỨNG YÊN (ít dự án, tổng bề rộng chưa vượt màn): không còn gì để vuốt nên bỏ luôn con
   trỏ bàn tay và khoá kéo ngang. JS gắn class này — xem `xétChếĐộ()` trong site.js. */
.allp--tĩnh .allp__stage {
  cursor: default;
  touch-action: auto;
}
.allp__track {
  position: relative;
  height: 100%;
  /* KHÔNG xoay cả băng. Đã thử xoay 2D −2.5° và bỏ: băng thành xiên vẹo, khác
     hẳn ref. Độ nghiêng thuộc về TỪNG THẺ (--ap-flip), không phải cả dãy. */
}
.allp__card {
  position: absolute;
  left: 0;
  top: 50%;
  width: var(--ap-cw);
  text-decoration: none;
  color: #fff;
  /* 0 50% = mép TRÁI, giữa theo chiều dọc. Thẻ nhỏ đi thì tự thu về đường giữa
     nằm ngang — đó là thứ tạo cảm giác lùi xa, thay cho hình thang. */
  transform-origin: 0 50%;
  will-change: transform;
}
.allp__shot {
  position: relative;
  display: block;
  width: 100%;
  /* 2.085 — chọn để thẻ NHÌN TRÊN MÀN ra 16:9 sau khi đã lật.
     Cú lật làm mép trái cao hơn mép phải (tỉ lệ 1.204 như ref), nên cao trung bình
     = ch × (1 + s_xa)/2 với s_xa = 0.8306, còn bề rộng = cw × cos20° × s_xa.
     Giải cho tỉ lệ nhìn thấy = 1.778 ra cw/ch = 2.085. */
  aspect-ratio: 2.085;
  border-radius: 20px;
  overflow: hidden;
  background: #14110d;
}
.allp__shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Nhãn VAI TRÒ ở góc trên-phải thẻ. Dùng lại đúng công thức kính của thẻ hồ sơ
   trong hero (.glass) — nền trắng 14%, blur 16, viền trắng 28% — chứ không phải
   viên vàng chữ đen như ref. Chép số vào đây thay vì thêm class .glass vào markup
   vì bo góc và cỡ chữ ở đây khác. */
.allp__tag {
  position: absolute;
  top: 16px;
  right: 16px;
  padding: 8px 14px;
  border-radius: 8px;
  /* Kính TỐI, không phải trắng 14% như .glass của hero. Thẻ hồ sơ ở hero luôn nằm
     trên ảnh trời tối nên trắng 14% là đủ; ở đây thumbnail thay đổi liên tục và có
     ảnh sáng gần trắng (thẻ "Pulse") — kính trắng trên nền sáng thì chữ trắng mất
     hút. Sắc tối 34% giữ nguyên chất kính (vẫn blur, vẫn viền trắng) mà chữ đọc
     được trên mọi ảnh. Đo tương phản chữ trắng trên ba thẻ: 34% cho 3.2–3.4:1,
     hơi thấp cho chữ 15px; 48% đưa lên 4.3–4.6:1. */
  /* KHÔNG backdrop-filter ở đây, dù nhìn là kính. Mỗi thẻ chạy có một cái nhãn, cả
     băng có hơn chục thẻ và chúng dịch mỗi khung hình — mỗi nhãn là một lớp
     backdrop-filter phải lấy mẫu lại nền sau nó liên tục. Đo lúc cuộn qua khu này
     (1512×850, DPR 2): bỏ blur ở nhãn cắt được ~150ms trong tổng ~560ms thời gian
     khung dài. Đổi lại chỉ cần đậm nền thêm một chút — nhãn nằm ĐÈ LÊN chính ảnh
     của thẻ và trôi cùng nó, nên phần mờ gần như không nhìn ra. */
  background: rgba(18, 18, 18, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.28);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
  font-size: max(9px, 15px);
  font-weight: 600;
  line-height: 1;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  white-space: nowrap;
}
.allp__name {
  display: block;
  margin-top: 18px;
  font-size: max(9px, 18px);
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.92);
}

/* ---------- con trỏ "Drag" ---------- */
.cursor__pill {
  position: absolute;
  left: 0;
  top: 0;
  translate: -50% -50%;
  display: grid;
  place-items: center;
  min-width: 74px;
  height: 34px;
  padding-inline: 16px;
  border-radius: 999px;
  background: #fff;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #111;
  white-space: nowrap;
  opacity: 0;
  scale: 0.7;
  transition:
    opacity 0.18s,
    scale 0.24s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Đổi hình: giấu đôi mắt, hiện viên thuốc. Cả hai cùng dùng opacity nên đổi
   qua lại có chuyển tiếp, không nháy. */
.cursor[data-variant="drag"] svg {
  opacity: 0;
  transition: opacity 0.18s;
}
.cursor[data-variant="drag"] .cursor__pill {
  opacity: 1;
  scale: 1;
}
.cursor[data-variant="drag"].is-drag .cursor__pill {
  scale: 0.92;
}

@media (max-width: 1439px) {
  .allp__title {
    font-size: 30px;
  }
}
/* --ap-cw và --ap-gap PHẢI khai kiểu bằng @property.
   site.js đọc chúng bằng parseFloat(getComputedStyle(...).getPropertyValue(...)). Custom
   property chưa khai kiểu thì trình duyệt GIỮ NGUYÊN CHUỖI token: `max(551px, 26.1vw)`
   trả về đúng chuỗi đó, parseFloat ra NaN, rơi về số dự phòng 551 — sửa CSS mà đo lại
   thấy y hệt, không đổi một px nào. Khai <length> thì nó tính ra px thật.
   Trình duyệt không đỡ @property thì rơi về đúng hành vi cũ, không vỡ. */
@property --ap-cw {
  syntax: "<length>";
  inherits: true;
  initial-value: 551px;
}
@property --ap-gap {
  syntax: "<length>";
  inherits: true;
  initial-value: 26px;
}

/* TỪ 16 INCH TRỞ LÊN: chặn số thẻ thấy trên một màn ở 6.5.
   Vị trí các thẻ trong băng KHÔNG phụ thuộc bề rộng màn (site.js xếp từ x=0), nên đếm
   được chính xác số thẻ lọt trong khung nhìn ở mọi khổ — với --ap-cw 551:

     1440 → 3.46 · 1512 → 3.73 · 1728 → 4.61 · 1920 → 5.50
     2110 → 6.50 · 2560 → 9.40 · 3200 → 13.81

   Tức mốc 16" (1728) VỐN ĐÃ ra 4.6 thẻ, đúng mức mong muốn; chỗ vỡ là từ 2110 trở lên,
   ở 2560 đã thành 9.4 thẻ.
   Số thẻ tỉ lệ nghịch với cỡ thẻ, nên muốn giữ 6.5 thì cho thẻ TO DẦN theo màn:
   cw = 551 × W / 2110 = 0.261W. Dưới 2110 thì max() lấy 551 nên không đổi gì. */
@media (min-width: 1728px) {
  .allp {
    /* Số thẻ KHÔNG tỉ lệ nghịch đơn giản với cỡ thẻ — băng thu nhỏ theo hàm log nên
       phải quét thực nghiệm. Đo count(màn, cw):
         2110: 551→6.50 · 750→5.91 · 850→5.21
         2560: 750→7.00 · 850→6.13 · 950→5.42
         3200: 1050→8.25 · 1150→7.56
       Nội suy mốc 6.5 ra (2110, 551) và (2560, ~800) ⇒ cw = 0.553·W − 616.
       Trên 2560 thì số thẻ nhích lên quá 6.5 một chút — băng thu nhỏ theo hàm log nên
       không có hệ số tuyến tính nào giữ đúng 6.5 ở mọi khổ, và khổ >2560 hiếm gặp. */
    --ap-cw: max(551px, calc(55.3vw - 616px));
    --ap-gap: calc(var(--ap-cw) * 26 / 551);
  }
}
@media (max-width: 1023px) {
  .allp {
    padding-block: 48px 56px;
    --ap-cw: 400px;
    --ap-gap: 20px;
  }
  .allp__stage {
    margin-top: 32px;
  }
}
/* Mobile KHÔNG có section này: băng chạy nghiêng cần bề ngang mới ra dáng, ở
   390 thì chỉ thấy một thẻ rưỡi. Thay bằng đúng cái nút, đặt ngay dưới ba thẻ
   vuông của Selected Work — xem .wmob__more. */
@media (max-width: 809px) {
  .allp {
    display: none;
  }
  .allp__head {
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
  }
  .allp__title {
    line-height: 0.9;
  }
}
@media (prefers-reduced-motion: reduce) {
  /* Không tắt hẳn: thẻ được đặt bằng transform trong JS nên bỏ transform là chồng
     đống lên nhau. site.js tự về tốc độ 0, băng đứng yên và vẫn kéo được. */
  .allp__stage {
    cursor: grab;
  }
}

/* Chỉ dành cho trình đọc màn hình: có trong cây a11y nhưng không chiếm chỗ và
   không nhìn thấy. KHÔNG dùng display: none — như vậy trình đọc cũng bỏ qua. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   SECTION 4 — MY JOURNEY
   Dựng theo reference/home-journey/my-journey*.jpg (khung 1440×800).
   Nền #181818 và lưới 97px — cùng mã màu với section More Projects và cùng bước
   lưới với About, nên ba section nối nhau không có đường cắt.
   ============================================================ */
.jr {
  position: relative;
  background: #181818;
  /* Một khung nhìn + 720px quãng cuộn, KHÔNG phải 260svh.
     260svh là tỉ lệ theo chiều cao màn: trên iPad Pro dọc (1355) nó ra 3523px cho
     một cụm nội dung chỉ ~750px — phần thừa hiện ra thành mảng đen rất lớn giữa hai
     section. Cố định 900px: hai cú trồi thẻ dùng hết ~666px, còn dư ~234px làm
     QUÃNG GIỮ — thẻ cuối đã vào chỗ mà section vẫn còn dính, nên cú cuộn lỡ đà
     không lôi luôn section sau vào khung nhìn. Không phụ thuộc màn cao bao nhiêu. */
  min-height: calc(100svh + 900px);
  --jp: 0; /* tiến độ cuộn 0 → 1, site.js ghi mỗi frame */
}
/* Lưới nền: lát ô 97×97 bằng background-size, KHÔNG dùng
   repeating-linear-gradient — cách kia cộng dồn sai số khử răng cưa nên ô ra
   193/194/195 thay vì đều 97 (đã đo ở section About). */
/* Lưới đặt trên khối DÍNH, không phải trên section: yêu cầu là khi cuộn thì chỉ
   nền và chồng thẻ đi lên, còn lưới và cụm chữ bên trái đứng yên. Để trên .jr thì
   lưới cuộn theo trang.
   Cùng ô 97 và cùng pha --grid-y với About / Work / More Projects. */
.jr__stick::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    linear-gradient(90deg, var(--tối-line) 0 1px, transparent 1px),
    linear-gradient(180deg, var(--tối-line) 0 1px, transparent 1px);
  background-size: var(--ab-cell) var(--ab-cell);
  background-position: 50px var(--grid-y, 0px);
}
.jr__stick {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  align-items: center;
  overflow: hidden;
}
.jr__in {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 40px;
  width: 100%;
  /* Lề GIỐNG mọi section khác: calc(--pad * 1.6) = 64 ở ≥768, 38 ở mobile — cùng
     mốc với .work__head, .wnote và .allp__head.
     Bản đầu dùng max-width: var(--maxw) + padding: var(--pad) như .container, nên
     lề nhảy theo bề rộng màn: đo được 232 ở 1920, 160 ở 1440, 40 ở 1200 — lệch hẳn
     ba section kia (đều 64). */
  padding-inline: var(--sec-pad);
}
/* ---------- CHỈ DESKTOP: hai cụm bó vào giữa, không dạt ra hai mép ----------
   Mọi section khác căn nội dung theo đúng --sec-pad hai bên. Riêng section này thì
   không nên: cụm chữ bên trái và chồng thẻ bên phải là MỘT mạch thông tin, mà cột trái
   là 1fr nên nó phình theo màn — đo được khe giữa hai cụm 40 ở 1100 nhưng tới 1500 ở
   2560, đọc thành hai mảng rời nhau.
   Đổi sang `auto auto` (hai cụm co theo nội dung) + `space-between` + max-width cho cả
   cặp: khe lớn dần theo màn rồi DỪNG khi cặp chạm 1280, từ đó cả cặp bó vào giữa.
   Chỉ từ 1100 lên — tablet và mobile giữ nguyên bố cục một cột. */
@media (min-width: 1100px) {
  .jr__in {
    grid-template-columns: auto auto;
    justify-content: space-between;
    /* khe TỐI THIỂU; phần lớn hơn do space-between lo */
    column-gap: 32px;
    /* box-sizing: border-box nên phải cộng cả hai lề vào, không thì khổ nội dung chỉ
       còn 1280 − 152 = 1128 */
    max-width: calc(1280px + var(--sec-pad) * 2);
    margin-inline: auto;
  }
}
.jr__left {
  position: relative;
  z-index: 1;
}
.chip--jr {
  /* PHẢI đặt --chip-w: .chip lấy bề rộng từ calc(var(--chip-w) * var(--u)) */
  --chip-w: 152;
  margin: 0 0 26px;
  /* .chip có transform-origin: 0 0 nên xoay quanh góc trên-trái; translate bù lại
     cho khớp ảnh mẫu (chip dán chồng, hơi nghiêng lên như About). */
  translate: 0 6px;
  rotate: -8deg;
}
/* Thang cỡ chữ tiêu đề section, GIỐNG .about__title và .work__title:
   ≥1440 → 48 · 810–1439 → 40 · 391–809 → 32 · ≤390 → 28.
   Bản đầu để 48 đứng yên tới tận 810 rồi nhảy 32, không theo thang nào. */
.jr__title {
  margin: 0 0 30px;
  font-family: var(--font-display);
  font-size: 48px;
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -0.01em;
  color: #fff;
}

/* ---------- ô nhập kiểu "đang chọn trong Figma" ----------
   Ref đo: khung 429×100, màu viền #84CEFB, tay cầm góc là ô vuông đặc cùng màu,
   bên phải là cung xoay + một chấm tròn. */
.jr__field {
  --fig: #84cefb;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Ref đo từng cạnh:
       cạnh trái  x=146, dọc suốt y 412–512
       cạnh trên  y=418, ngang x 141→533
       cạnh dưới  y=506, ngang x 141→533
       cạnh phải  KHÔNG CÓ — ở x=526/532 chỉ có hai tay cầm, không có nét dọc nào
     ⇒ hộp 380 × 88, và cung "D" mới là thứ đóng cạnh phải.
     Bản đầu vẽ đủ bốn cạnh (và cao 100 = khoảng của TAY CẦM, không phải của hộp)
     nên nhìn khác hẳn ref. */
  width: min(380px, 100%);
  height: 88px;
  border: 2px solid var(--fig);
  border-right: 0;
  color: var(--fig);
}
.jr__word {
  font-family: var(--font-display);
  /* Ref đo cao chữ hoa 'L' = 64px ⇒ font-size 88 (Mona Sans cap ≈ 0.727em). Nhưng
     88 thì "Researching" — từ dài nhất trong vòng lặp — tràn hẳn ra ngoài hộp 380.

     Cỡ chốt theo TỈ LỆ CHỮ / HỘP đo trên ref-text.jpg, không phải theo "vừa khít":
       ref : Researching + con trỏ rộng 319 trong hộp 381 ⇒ 84%, hở hai bên ~31px
       54px: 366/380 = 96%, hở 7px — chữ R sát cạnh, xấu
       46px: 317/380 = 84%, hở 31px — trùng đúng tỉ lệ ref */
  font-size: 46px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--fig);
  white-space: nowrap;
}
/* Con trỏ nhập: gạch dưới ngắn, nhấp nháy. Là phần tử riêng chứ không phải ký tự
   "_" trong chuỗi — ghép vào chuỗi thì lúc xoá chữ nó nhảy giật theo từng nhịp. */
.jr__caret {
  /* Ref: gạch ngắn dày, nằm ở ĐƯỜNG CHÂN CHỮ chứ không giữa dòng.
     Giữ đơn vị px như bản đầu. (Có lúc đổi sang em cho "co theo cỡ chữ" — sai:
     con trỏ tụt về giữa dòng và bé đi thấy rõ. Chỉ hạ margin-top 26 → 22 cho khớp
     chân chữ mới sau khi cỡ chữ đổi 54 → 46.) */
  width: 30px;
  height: 5px;
  margin: 22px 0 0 8px;
  background: var(--fig);
  animation: jrBlink 1.06s steps(1, end) infinite;
}
@keyframes jrBlink {
  0%,
  55% {
    opacity: 1;
  }
  56%,
  100% {
    opacity: 0;
  }
}
/* Tay cầm góc: ô 11px ĐẶC màu xanh. Lấy mẫu ruột ô trên ref-text.jpg ra đúng
   rgb(132,206,251) — cùng màu với nét viền, không phải nền trắng viền xanh. */
.jr__h {
  position: absolute;
  width: 11px;
  height: 11px;
  background: var(--fig);
}
.jr__h--tl {
  left: -6px;
  top: -6px;
}
.jr__h--tr {
  right: -6px;
  top: -6px;
}
.jr__h--bl {
  left: -6px;
  bottom: -6px;
}
.jr__h--br {
  right: -6px;
  bottom: -6px;
}
/* Cung + đường chấm phải CO THEO chiều cao hộp, không được ghi cứng 100px:
   cung là nửa đường tròn bán kính = nửa chiều cao hộp, nên hộp đổi cao mà SVG giữ
   nguyên thì hai đầu cung không còn chạm hai góc — nhìn ra ngay là "vỡ".
   Đặt height theo hộp rồi để aspect-ratio suy ra width, nhờ vậy hình tròn vẫn tròn
   (nếu ép cả hai chiều thì nó méo thành elip). */
.jr__rot {
  position: absolute;
  left: 100%;
  top: 50%;
  translate: 0 -50%;
  height: calc(100% + 12px);
  width: auto;
  aspect-ratio: 52 / 100;
}

/* ---------- chồng thẻ ----------
   Số đo lấy từ public/components/Property 1=white.svg và =yellow.svg:
   thẻ 423×383, bo 16, thanh tiêu đề 44, ba đèn r=6 ở cx 42/62/82. */
/* GRID chồng một ô: cả ba thẻ nằm chung ô 1/1 nên chúng đè lên nhau, mà hàng lại
   TỰ CAO theo thẻ dài nhất và align-items mặc định kéo hai thẻ kia bằng theo. Cách
   này thay cho height: 383px cố định — ở khổ nhỏ thẻ hẹp lại, chữ xuống nhiều dòng
   hơn và sẽ tràn khỏi chiều cao cố định. */
/* Ba thẻ xếp thành HÀNG rồi kéo đè lên nhau bằng margin âm, độ đè do site.js tính
   từ chỗ trống THẬT (xem --jr-mt). Không dùng grid-area: 1/1 cố định nữa: mức đè
   phải đổi theo từng khổ máy, mà media query thì đoán sai — chiều cao cửa sổ Chrome
   trên laptop 14" thường tụt dưới mọi mốc min-height hợp lý. */
.jr__stack {
  position: relative;
  display: grid;
  grid-auto-flow: row;
  width: 423px;
  flex: none;
}
/* Lúc script đo vị trí nghỉ của ba thẻ, nó phải ép --vào về 1 rồi trả lại ngay.
   Thẻ đầu có transition nên nếu không tắt đi, cú trả lại sẽ chạy ra một cú trượt
   thật. Tắt trong lúc đo, bật lại sau khi đã ép trình duyệt tính lại bố cục. */
.jr--đo .jrcard {
  transition: none !important;
}
.jrcard + .jrcard {
  /* mặc định chồng KHÍT; site.js nới ra khi có chỗ */
  margin-top: var(--jr-mt, -100%);
}
.jrcard {
  grid-area: auto;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
  transform-origin: 50% 120%;
  /* scale RIÊNG, không nhập vào translate/rotate: hai cái kia đang do cuộn điều khiển
     từng khung hình, nhét thêm vào là hoặc phải transition chúng (thẻ sẽ trễ so với
     tay cuộn) hoặc mất luôn cú nhấc. CSS có ba thuộc tính transform độc lập nên dùng
     scale là xong. */
  scale: 1;
  /* Vị trí nghỉ của từng thẻ: xoay lệch nhau một chút cho ra chồng giấy. Thẻ đầu
     −5.14° đúng như ref. --vào là 0→1 do site.js ghi, 0 = còn nằm dưới màn. */
  --vào: 1;
  --r: -5.14deg;
  --dx: 0px;
  --dy: 0px;
  translate: calc(var(--dx) * var(--vào)) calc(var(--dy) * var(--vào) + (1 - var(--vào)) * 78vh);
  rotate: calc(var(--r) * var(--vào));
  transition:
    scale 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    filter 0.3s ease,
    box-shadow 0.3s ease;
  /* KHÔNG mờ dần theo --vào: thẻ sau trượt lên đè thẻ trước phải đục 100% ngay từ
     đầu, không thì lúc chồng lên nhau nhìn xuyên qua thấy cả hai. */
  opacity: 1;
}
/* Thẻ ĐẦU không đi theo tiến độ cuộn như hai thẻ sau — nó xuất hiện một lần khi
   section vào tầm nhìn, cùng kiểu trượt-lên với title-chip. Chuyển động do
   transition lo (site.js chỉ bật/tắt --vào), nên chỉ thẻ này có transition; hai thẻ
   kia bám tay cuộn nên phải bám sát, thêm transition là thành trễ. */
.jrcard:first-child {
  /* Phải liệt kê lại cả ba dòng của .jrcard: `transition` là shorthand, khai lại ở đây
     là ghi đè sạch danh sách bên trên. */
  transition:
    translate 0.9s cubic-bezier(0.22, 1, 0.36, 1),
    rotate 0.9s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.45s linear,
    scale 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    filter 0.3s ease,
    box-shadow 0.3s ease;
  transition-delay: 0.18s;
  /* Chưa vào khuôn thì TÀNG HÌNH hẳn. Lúc section mới nhô lên khỏi mép dưới, khung
     nhìn chỉ hứng được phần trên của khối dính: hiện ra chip "My journey" cộng NỬA
     thẻ "Building My Visual Foundation", không có dòng dẫn nào — nhìn không hiểu là
     cái gì. Nhân 3 để chỉ đúng lúc --vào = 0 là trong suốt, vừa nhấc lên một chút là
     đã đục, không thành ra một cú mờ dần dài. */
  opacity: calc(var(--vào) * 3);
}
/* Thẻ 2 (trắng) xoay NGƯỢC CHIỀU hai thẻ vàng — đo trên ref my-journey-2.jpg:
   mép trên dốc +0.0874 ⇒ +5.00°, tức thuận kim đồng hồ. Chồng thẻ trong ref so le
   trái–phải chứ không cùng nghiêng một bên. */
.jrcard:nth-child(2) {
  --r: 5deg;
  --dx: -18px;
  --dy: -22px;
}
.jrcard:nth-child(3) {
  --r: -6.4deg;
  --dx: 10px;
  --dy: -40px;
}
/* Đủ ba thẻ trên màn rồi thì trỏ vào mảng còn hở của thẻ nào là đưa thẻ đó lên trên
   cùng để đọc lại; bỏ chuột ra là về đúng thứ tự chồng ban đầu. Ô lưới của khay là
   stacking context chung nên z-index của grid item ăn ngay, không cần position.
   Class do site.js gán, KHÔNG dùng :hover — xem chú thích ở đó.

   z-index thì không nội suy được, đảo một cái là đảo dứt khoát. Nên cú đảo ấy phải có
   thứ khác đi kèm để mắt đọc ra là "thẻ vừa nhấc lên" chứ không phải "hình vừa giật":
   thẻ được nhấc thì nở nhẹ và bóng đổ dày hơn, hai thẻ còn lại thì thụt lại một chút,
   bóng mỏng đi và mờ nhẹ — thành ra có chiều sâu. Tất cả trong 0.3s. */
.jrcard--trước {
  z-index: 3;
  scale: 1.035;
  box-shadow: 0 34px 80px rgba(0, 0, 0, 0.55);
}
/* Hai thẻ còn lại chỉ mờ đi và bóng mỏng lại — TUYỆT ĐỐI không thu nhỏ. Đã thử
   scale: 0.985 và đo ra lỗi tự huỷ: thẻ thu lại thì điểm con trỏ đang đứng rơi ra
   ngoài hộp thẻ, hit-test trả về rỗng, thẻ bị hạ xuống, hộp trở lại như cũ — mà chuột
   không di nữa nên không có pointermove để phục hồi. Nhấc thì được (chỉ nở ra, hộp
   rộng thêm), thu thì không. filter không đổi hộp nên vô hại. */
.jr__stack--nhấc .jrcard:not(.jrcard--trước) {
  filter: blur(1.2px);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.32);
}
.jrcard--y {
  background: #fff5c9;
}
.jrcard--w {
  background: #fff;
}
.jrcard__bar {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding-inline: 16px;
}
.jrcard--y .jrcard__bar {
  background: #ffec97;
}
.jrcard--w .jrcard__bar {
  background: #ebebeb;
}
.jrcard__bar i {
  width: 12px;
  height: 12px;
  border-radius: 50%;
}
.jrcard__bar i:nth-child(1) {
  background: #ff5f57;
}
.jrcard__bar i:nth-child(2) {
  background: #febc2e;
}
.jrcard__bar i:nth-child(3) {
  background: #27c941;
}
.jrcard__body {
  padding: 34px 30px;
  color: #1b1b1b;
}
.jrcard__eyebrow {
  margin: 0;
  font-size: max(9px, 16px);
  color: rgba(27, 27, 27, 0.62);
}
.jrcard__role {
  margin: 4px 0 0;
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 700;
  line-height: 1.2;
}
.jrcard__time {
  margin: 8px 0 0;
  font-size: max(9px, 15px);
  color: rgba(27, 27, 27, 0.72);
}
.jrcard__text {
  margin: 26px 0 0;
  font-size: max(9px, 16px);
  line-height: 1.5;
  color: rgba(27, 27, 27, 0.86);
}

@media (max-width: 1439px) {
  .jr__title {
    font-size: 40px;
  }
}
/* Tablet + mobile: CĂN GIỮA cả cụm — title-chip, headline, ô nhập và chồng thẻ.
   Ở khổ hẹp hai cột không đứng cạnh nhau được nữa, xếp dọc mà vẫn dí trái thì cả
   khối lệch hẳn về một bên. */
/* TABLET (810–1099) VẪN GIỮ khối dính để ba thẻ trồi lên lần lượt — bỏ dính thì
   ba thẻ hiện cùng lúc, chồng đè nhau, không đọc được từng cái.
   Để cả cụm lọt trong một màn cao 768: thẻ kéo rộng hết cột (hai bên vốn thừa rất
   nhiều chỗ), chữ dàn ra ít dòng hơn nên thẻ thấp đi hẳn. */
@media (max-width: 1099px) {
  .jr__stick {
    /* chừa cho thanh header 60 ở trên (cộng badge chip thò lên ~20) và dock ở dưới:
       dock cao 89, cách đáy 20 ⇒ 109; để 84 cho thẻ chớm vào phần lề của dock chứ
       không chạm dock.
       align-items: start — căn giữa trong 100svh thì trên màn cao có một dải trống
       rất lớn phía trên cụm chữ. */
    padding: 84px 0 84px;
    align-items: start;
  }
  .jr__in {
    grid-template-columns: 1fr;
    justify-items: center;
    /* 76: khe THẬT giữa ô nhập và thẻ trên cùng nhỏ hơn con số này ~55px, vì thẻ
       thứ 3 có --dy −40 và còn xoay nên mép trên thật cao hơn mép hộp chồng thẻ.
       Cả cụm vẫn phải lọt trong 100svh trừ thanh header và dock. */
    gap: 76px;
  }
  .jr__left {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    /* chiếm trọn cột để .jr__field và .jr__stack tính % theo bề ngang nội dung
       của section, nhờ vậy lề của chúng bằng lề các section khác */
    width: 100%;
  }
  .jr__field {
    /* Bề rộng cũng phải CO THEO cỡ chữ, không giữ 380 của desktop: chữ nhỏ đi mà
       hộp giữ nguyên thì chữ lọt thỏm ở giữa. Giữ đúng tỉ lệ ~84% của ref —
       "Researching" + con trỏ ở cỡ 40 rộng 269 ⇒ hộp 320.
       Trừ 60 để cung nửa đường tròn + đường chấm (thò sang phải) không bị
       .jr__stick (overflow: hidden) cắt. */
    width: min(320px, 100% - 60px);
    margin-inline: auto;
  }
  .chip--jr {
    margin-inline: auto;
  }
  .jr__title {
    text-align: center;
  }
  .jr__stack {
    /* Chỉ rộng hơn bản gốc 423 một chút, KHÔNG kéo full cột: full ra thì thẻ dẹt
       và chữ dàn thành hai dòng rất dài, nhìn không còn ra thẻ nữa. */
    width: min(640px, 100%);
    align-self: center;
    justify-self: center;
    margin-inline: auto;
  }
  /* Thẻ rộng thì chữ ít dòng; đồng thời phải NÉN CHIỀU CAO để cả cụm lọt một màn
     768 — đó là ràng buộc chặt nhất ở khổ này. */
  .jrcard__body {
    padding: 18px 32px 22px;
  }
  .jrcard__role {
    font-size: 21px;
  }
  .jrcard__eyebrow,
  .jrcard__time {
    font-size: max(9px, 14px);
  }
  .jrcard__text {
    margin-top: 12px;
    font-size: max(9px, 14px);
    line-height: 1.45;
  }
  /* Fan dọc tightlại: --dy −22/−40 làm thẻ trên cùng vống lên, ăn vào khe với ô
     nhập (đo được khe âm 9px) và đẩy cả cụm cao thêm. */
  .jrcard:nth-child(2) {
    --dy: -14px;
  }
  .jrcard:nth-child(3) {
    --dy: -26px;
  }
  .chip--jr {
    margin-bottom: 16px;
  }
  .jr__title {
    margin-bottom: 18px;
  }
  /* Hộp, con trỏ và tay cầm phải CO THEO cỡ chữ, không giữ số của desktop.
     Tỉ lệ desktop: chữ 46 · hộp 88 · con trỏ 30×5 · lùi 22 · tay cầm 11.
     Tablet chữ 40 ⇒ nhân 0.87. */
  .jr__field {
    height: 76px;
  }
  .jr__word {
    /* BẰNG cỡ tiêu đề của khổ tablet (40px) */
    font-size: 40px;
  }
  .jr__caret {
    width: 26px;
    height: 4px;
    margin: 19px 0 0 7px;
  }
  .jr__h {
    width: 10px;
    height: 10px;
  }
  .jr__h--tl,
  .jr__h--tr {
    top: -5px;
  }
  .jr__h--bl,
  .jr__h--br {
    bottom: -5px;
  }
  .jr__h--tl,
  .jr__h--bl {
    left: -5px;
  }
  .jr__h--tr,
  .jr__h--br {
    right: -5px;
  }
}
/* Tablet MÀN THẤP (768 chẳng hạn): sau khi trừ thanh header 84 và dock 98 chỉ còn
   586px, mà cụm nội dung cần ~650. Nén thêm một nấc, chỉ ở khổ này — tablet màn cao
   (810×1080) vẫn dùng cỡ chữ lớn hơn. */
@media (max-width: 1099px) and (max-height: 820px) {
  .chip--jr {
    margin-bottom: 12px;
  }
  .jr__title {
    margin-bottom: 14px;
  }
  .jr__field {
    height: 64px;
  }
  /* KHÔNG đè `gap` ở đây. Khe 76 là số đã chốt cho khổ ≤1099; hạ xuống 60 để lấy
     chỗ thì khe THẬT giữa ô nhập và thẻ trên cùng thành −4 (đè nhau), vì thẻ thứ 3
     có --dy −26 và còn xoay nên mép trên thật cao hơn mép khay ~55px.
     Chỗ cần bù lấy từ đệm dưới của khối dính, không lấy từ khe. */
  .jrcard__body {
    padding: 14px 30px 18px;
  }
  .jrcard__role {
    font-size: 19px;
  }
  .jrcard__text {
    margin-top: 10px;
    font-size: max(9px, 13px);
    line-height: 1.4;
  }
}

@media (max-width: 809px) {
  .jr__stick {
    /* 80 trên = ĐÚNG BẰNG padding-bottom 80 của section Selected Work ngay trên,
       để mép hai section cân nhau.
       align-items: start là bắt buộc đi kèm: mặc định khối dính căn giữa nội dung
       trong 100svh nên padding-top không quyết định gì — đo được khoảng trên tới
       128px dù padding chỉ 88. */
    align-items: start;
    padding: 80px 0 110px;
  }
  .jr__title {
    font-size: 32px;
  }
  /* Chữ 28 ⇒ nhân 0.61 so với desktop. Cung nửa đường tròn tự co theo chiều cao
     hộp (height: calc(100% + 12px) + aspect-ratio), không phải chỉnh tay. */
  .jr__field {
    height: 54px;
    /* "Researching" + con trỏ ở cỡ 28 rộng 188 ⇒ hộp 224 cho ra đúng 84% */
    width: min(224px, 100% - 60px);
  }
  .jr__word {
    /* BẰNG cỡ tiêu đề của cùng khổ (28px). Trước để 37 nên chữ trong ô to hơn cả
       "With every step, I keep", kéo theo cả cái hộp to bất thường. */
    font-size: 28px;
  }
  .jr__caret {
    width: 18px;
    height: 3px;
    margin: 13px 0 0 5px;
  }
  .jr__h {
    width: 8px;
    height: 8px;
  }
  .jr__h--tl,
  .jr__h--tr {
    top: -4px;
  }
  .jr__h--bl,
  .jr__h--br {
    bottom: -4px;
  }
  .jr__h--tl,
  .jr__h--bl {
    left: -4px;
  }
  .jr__h--tr,
  .jr__h--br {
    right: -4px;
  }
  .jr__stack {
    /* trừ 20 = đúng phần thẻ nhô ra khi xoay 3° quanh tâm
       ((rộng·cos + cao·sin − rộng)/2 ≈ 10 mỗi bên), để MÉP THẤY ĐƯỢC của thẻ trùng
       lề --sec-pad chứ không thụt vào hay thò ra */
    width: calc(100% - 20px);
  }
  /* Bỏ lệch ngang ở khổ hẹp: --dx −18/+10 cộng với góc xoay làm thẻ thò hẳn 15px
     qua lề section. Chồng thẻ ở đây chỉ so le bằng góc xoay và --dy.
     PHẢI liệt kê cả nth-child: `.jrcard` (0,1,0) thua `.jrcard:nth-child(2)`
     (0,2,0) dù viết sau, nên đặt mỗi `.jrcard` là không ăn. */
  .jrcard,
  .jrcard:nth-child(2),
  .jrcard:nth-child(3) {
    --dx: 0px;
    /* Tâm xoay về GIỮA thẻ, không phải 50% 120%. Trục ở dưới đáy thẻ làm cú xoay
       đẩy cả thẻ sang ngang một đoạn 0.7·cao·sin(góc) — đo được 13.5px ở 390, đúng
       bằng phần thẻ thò qua lề. Xoay quanh tâm thì thẻ đứng yên tại chỗ. */
    transform-origin: 50% 50%;
  }
  /* xoay nhẹ hơn ở khổ hẹp: −5° trên thẻ 342 rộng làm góc trái thò ra ngoài màn */
  .jrcard {
    --r: -3deg;
  }
  .jrcard:nth-child(2) {
    --r: 3deg;
  }
  .jrcard:nth-child(3) {
    --r: -3.8deg;
  }
  .jrcard__body {
    padding: 26px 22px;
  }
  .jrcard__role {
    font-size: 20px;
  }
}
@media (max-width: 390px) {
  .jr__title {
    font-size: 28px;
  }
}
/* Máy bảng dựng dọc: thẻ hẹp lại cho cao hơn, hợp khung dọc. VẪN giữ khối dính —
   bỏ đi thì mất hiệu ứng xếp lần lượt. Khoảng đen thừa xử bằng --jr-runway ở .jr,
   không phải bằng cách bỏ hiệu ứng. */
@media (max-width: 1099px) and (min-height: 1100px) {
  .jr__stack {
    width: min(470px, 100%);
  }
  /* Chữ trong thẻ to hơn ở khung dọc: thẻ cao lên thì ba thẻ chồng một phần mới lấp
     được khoảng trống, mà chữ cũng dễ đọc hơn — thu hẹp thẻ lại không làm nó cao
     thêm bao nhiêu (đo: 470 → 300, 380 → 312). */
  .jrcard__body {
    padding: 26px 36px 30px;
  }
  .jrcard__role {
    font-size: 24px;
  }
  .jrcard__eyebrow,
  .jrcard__time {
    font-size: max(9px, 16px);
  }
  .jrcard__text {
    margin-top: 20px;
    font-size: max(9px, 16px);
    line-height: 1.55;
  }
}

/* MÀN QUÁ THẤP để chứa cả cụm trong một khung nhìn (iPhone SE 667 chẳng hạn):
   bỏ khối dính, ba thẻ hiện sẵn. Trên mốc này thì vẫn dính và thẻ trồi lên lần
   lượt như desktop/tablet — đo ở 390×844: chỗ trống 646, cụm cần 563. */
@media (max-width: 809px) and (max-height: 759px) {
  .jr {
    min-height: 0;
  }
  .jr__stick {
    position: static;
    height: auto;
    overflow: visible;
    padding: 88px 0 56px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .jr__caret {
    animation: none;
  }
}

/* ============================================================
   TRANG PHỤ — "A little update is underway" (about.html)
   Dựng theo reference/about/fixedpage.jpg (khung 1440×800):
     hình 196 · tiêu đề 44/1.2 · dòng dẫn 24
     thẻ trắng 679×198 bo 16, đệm 27/24, nhãn 14, hai hàng link 19
   Nền: #181818 + lưới 97 y hệt section My journey ở trang chủ.
   ============================================================ */
.fixp {
  position: relative;
  min-height: 100svh;
  /* Quầng xanh mờ ở đỉnh trang — public/images/about/circle-back.svg.
     File là khung 1022×407: tâm hình tròn nằm ở y = −104, tức đã CẮT SẴN phần trên,
     nên chỉ cần dán mép trên của ảnh vào mép trên section là ra đúng ref.
     Đặt trên chính `.fixp` (không phải ::before) để lưới ở ::before nằm ĐÈ LÊN quầng —
     ref cho thấy các đường kẻ vẫn hiện xuyên qua vùng sáng. */
  background: #181818 url("public/images/about/circle-back.svg") no-repeat top center;
  background-size: 1022px 407px;
  display: grid;
  place-items: center;
  /* chừa chỗ cho thanh header trên và dock dưới, không để nội dung chui xuống dưới */
  /* Không có dock ở trang phụ nên đệm dưới chỉ cần bằng đệm trên, không phải chừa
     109px cho dock như trang chủ. */
  padding: calc(var(--topbar-h, 60px) + 16px) var(--pad) 56px;
}
.fixp::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(90deg, var(--tối-line) 0 1px, transparent 1px),
    linear-gradient(180deg, var(--tối-line) 0 1px, transparent 1px);
  background-size: var(--ab-cell) var(--ab-cell);
  background-position: 50px 0;
}
.fixp__in {
  position: relative;
  z-index: 1;
  width: 100%;
  /* 860 chứ không phải 679 (bề rộng thẻ trắng): tiêu đề một dòng ở 44px cần ~760, bó
     theo thẻ thì nó gãy làm hai dòng và cả trang vượt quá một màn ở khổ 1440×800. */
  max-width: 860px;
  display: grid;
  justify-items: center;
  text-align: center;
}
.fixp__art {
  display: block;
  width: clamp(128px, 26vw, 196px);
  height: auto;
  margin-bottom: clamp(28px, 4.4vw, 54px);
}
/* Chỉ lòng đen + đốm trắng của mắt PHẢI liếc, phần trắng của mắt đứng yên.
   Biên −9 … +6: lòng đen rộng 92→116 trong bầu mắt 72→128, nên còn 19 bên trái và
   11 bên phải; lấy trong khoảng đó thì không bao giờ tràn ra ngoài bầu.
   transform-box: view-box để px ở đây là ĐƠN VỊ VIEWBOX, tự co theo cỡ hiển thị. */
.fixp__ngươi {
  transform-box: view-box;
  transform-origin: center;
  animation: fixp-liec 5.2s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
@keyframes fixp-liec {
  0%,
  16% {
    transform: translateX(0);
  }
  27%,
  43% {
    transform: translateX(-9px);
  }
  54%,
  70% {
    transform: translateX(6px);
  }
  81%,
  100% {
    transform: translateX(0);
  }
}
.fixp__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(26px, 5vw, 44px);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: #fff;
}
.fixp__sub {
  margin: clamp(12px, 1.4vw, 20px) 0 0;
  font-size: clamp(16px, 2.8vw, 24px);
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.92);
}
.fixp__sao {
  color: #ffd84d;
}
.fixp__card {
  width: 100%;
  max-width: 679px;
  margin-top: clamp(20px, 2vw, 30px);
  /* Đệm dưới 9 = 27 − 18: hàng link cuối đã tự có 18px đệm dưới, cộng thêm 26 nữa thì
     từ chữ xuống đáy thẻ là 44 trong khi từ đỉnh thẻ xuống chữ chỉ 27 — lệch hẳn.
     Với 9 thì hai đầu đều đúng 27. */
  padding: 27px 24px 9px;
  border-radius: 16px;
  background: #f6f4ed;
  color: #111;
  text-align: left;
}
.fixp__lab {
  position: relative;
  /* Khe trên/dưới MỖI đường kẻ phải bằng nhau: 18 = padding-bottom của nhãn, và cũng
     đúng 18 = padding-top của hàng link ngay dưới. Bản trước để margin 12 + padding 16
     nên trên kẻ 16 mà dưới kẻ 12+14 = 26 — nhìn ra là cách không đều. */
  margin: 0;
  padding-bottom: 18px;
  font-size: 14px;
  line-height: 1.4;
  color: rgba(17, 17, 17, 0.72);
}
/* Kẻ SUỐT bề rộng thẻ, xám nhạt — ngăn cả ba cục: nhãn / resume / projects.
   Bản đầu mình đọc ảnh ref ra thành gradient chỉ hiện nửa phải; sai, đó chỉ là chỗ nén
   JPEG làm nửa trái mờ đi. */
.fixp__lab::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(17, 17, 17, 0.12);
}
.fixp__row {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 0;
  font-size: clamp(17px, 1.32vw, 19px);
  font-weight: 600;
  color: #111;
  text-decoration: none;
  transition: color 0.22s;
}
.fixp__row + .fixp__row {
  border-top: 1px solid rgba(17, 17, 17, 0.12);
}
.fixp__row svg {
  flex: none;
  width: 22px;
  height: 22px;
  transition: translate 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.fixp__row:hover svg,
.fixp__row:focus-visible svg {
  translate: 4px 0;
}
/* Rê chuột: KHÔNG gạch chân (khác .ft__link ở section Contact), chỉ đổi màu cả chữ lẫn
   mũi tên. Mũi tên vẽ bằng `currentColor` nên chỉ cần đổi `color` của cả hàng là xong. */
.fixp__row:hover,
.fixp__row:focus-visible {
  color: #0876b7;
}
@media (max-width: 767px) {
  .fixp {
    padding: calc(var(--topbar-h, 60px) + 12px) 16px 40px;
    /* Khổ hẹp: giữ nguyên tỉ lệ quầng nhưng thu về ~1.6 lần bề rộng màn, không thì
       chỉ thấy đúng lõi sáng nhất, mất hẳn phần loang. */
    background-size: 160% auto;
  }
  .fixp__card {
    /* cùng phép tính: 20 − 15 (đệm dưới của hàng link ở khổ này) = 5 */
    padding: 20px 18px 5px;
    border-radius: 12px;
  }
  .fixp__row {
    padding: 15px 0;
  }
  .fixp__lab {
    padding-bottom: 15px;
  }

}
@media (prefers-reduced-motion: reduce) {
  .fixp__ngươi {
    animation: none;
  }
}

/* Trang phụ nền ĐEN suốt: logo và icon menu luôn trắng, thanh header không đổ nền.
   Trang chủ đổi màu theo section (`on-dark` / `on-light`), ở đây không có section nào
   nên phải nói thẳng — không thì ở mobile icon menu là #111 nằm trên nền #181818,
   coi như tàng hình. */
body.trang-phu .menu,
body.trang-phu.has-scrolled .menu,
body.trang-phu.is-scrolled .menu {
  color: #fff;
}
body.trang-phu .topbar {
  background: transparent;
  border-bottom-color: transparent;
  box-shadow: none;
}

/* ---------- MÀN CHỜ bản TRANG PHỤ ----------
   Không có ô ảnh project, nên khối "Page is loading" + % tụt xuống đúng chỗ ô ảnh cũ
   (ngay trên hàng vạch) thay vì lơ lửng giữa màn. */
.pre--phu .pre__count {
  bottom: calc(108 * var(--pu));
}
/* Tên trang thay cho hàng chữ ThuHa ghép động. Hiệu ứng lấy đúng của logo NGX.THUHA
   ở hero trang chủ: từng ký tự đảo loạn rồi CHỐT lần lượt từ trái sang.
   Bản trước cho fade in/out lặp vô hạn — nhìn ra thành nhấp nháy, đã bỏ. */
.pre--phu .pre__tt {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-display);
  font-size: calc(96 * var(--pu));
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: #fff;
  font-feature-settings: "tnum";
}
.pre--phu .pre__tt b {
  display: inline-block;
  font-weight: inherit;
  /* giữ bề rộng tối thiểu lúc đang đảo để hàng chữ không giật ngang;
     chốt xong trả về bề rộng tự nhiên — giống hệt .logo b */
  min-width: 0.6em;
  text-align: center;
  transition: transform 0.12s;
}
.pre--phu .pre__tt b.done {
  min-width: 0;
}
.pre--phu .pre__tt b.spin {
  transform: scaleX(-1);
}
/* dấu cách không đảo, và phải giữ đúng bề rộng của nó */
.pre--phu .pre__tt b.trống {
  min-width: 0.28em;
}
@media (max-width: 809px) {
  .pre--phu .pre__tt {
    font-size: calc(76 * var(--pu));
  }
}

/* ============================================================
   TRANG CHI TIẾT DỰ ÁN (project-<slug>.html)
   Dựng theo reference/project-detail/. Một bộ CSS dùng cho MỌI dự án — markup do
   site.js sinh ra từ object chi tiết, xem "TRANG CHI TIẾT DỰ ÁN" trong site.js.

   Bố cục trang, theo thứ tự cuộn:
     .pdtop   khối TỐI: ảnh dự án phủ kín làm nền cho cả banner và mục lục (ref cho thấy
              cùng một tấm ảnh chạy suốt hai phần, nên hai section nằm trong CÙNG một
              khối để không phải cắt ảnh làm hai)
     .pdchs   khối KEM: các chapter, mỗi chapter có nhiều mục con
     .pdn     khối TỐI: Next project, có dải lưới đáy để nối sang section Contact

   Thang chữ bám đúng site:
     tiêu đề chapter  = thang tiêu đề section (48/44/40/36/32) — như .mw2__h2, .jr__title
     tiêu đề mục con  = một bậc dưới, clamp(24 → 40)
     thân             = 16/1.62, màu mực 78%
     nhãn nhỏ         = mono hệ thống 12–13, cùng font với .ft__made
   ============================================================ */
body.trang-pd {
  background: #181818;
}
/* Chrome (logo + icon menu) đổi màu theo nền đang ở dưới nó, cùng cơ chế trang My Work:
   mặc định TRẮNG (banner là ảnh tối), sang vùng kem thì JS bật `pd-sáng`. */
body.trang-pd.pd-sáng .menu,
body.trang-pd.pd-sáng.has-scrolled .menu,
body.trang-pd.pd-sáng.is-scrolled .menu {
  color: #111;
}
body.trang-pd.pd-sáng .brand-ha img:not(.brand-ha__đen) {
  opacity: 0;
}
body.trang-pd.pd-sáng .brand-ha__đen {
  opacity: 1;
}

/* Token khai trên BODY chứ không trên `.pd`: thanh dock và bảng Content Navigation là
   phần tử ANH EM của <main class="pd">, khai trong `.pd` thì hai chỗ đó không thấy biến
   nào cả — biến font/màu thành giá trị không hợp lệ nên nhãn "Chapter N" rơi về font mặc
   định của <button> (đo ra Arial). */
body.trang-pd {
  /* Lề = --sec-pad, đúng token mà mọi section ở trang chủ dùng, nên trang này thẳng cột
     với Selected Work / About / Contact ở mọi khổ. */
  --pd-lề: var(--sec-pad);
  /* ---- LIGHT ↔ DARK: `--tối` (0 → 1) do site.js ghi theo cuộn ----
     Mọi màu của vùng chapter đều là color-mix giữa bản SÁNG và bản TỐI theo `--tối`, nên khi
     tới gần mục có `nềnXanh` thì cả vùng chuyển dần sang tối — mượt như bật dark mode, KHÔNG
     có mép chuyển màu chạy qua giữa màn như cách tô nền riêng cho một khối.
     Chỉ cần bốn token này đổi là gần hết CSS của trang đổi theo, vì mọi chỗ đều dùng chúng. */
  --tối: 0;
  /* CHỮ đổi màu theo một đường DỐC HƠN nền: nếu chữ và nền cùng chạy tuyến tính thì ở khoảng
     giữa cả hai đều xám, chữ chìm mất một nhịp (đo được tương phản tụt xuống 1.34).
     Với hệ số 16, chữ giữ nguyên màu tối tới đúng giữa dải rồi LẬT nhanh sang sáng trong
     khoảng 0.06 tiến độ ≈ 24px cuộn (2–3 khung hình), nên mắt không kịp thấy chỗ chìm mà vẫn
     là một phép chuyển liên tục, không phải nhảy bậc.
     Trộn trong `oklab` cho khoảng giữa không bị xám bùn như `srgb`. */
  --tối-chữ: clamp(0, (var(--tối) - 0.47) * 16, 1);
  /* Đầu TỐI của token nền cũng theo biến, để mặt phẳng phía sau khớp màu chapter đang tối. */
  --pd-kem: color-mix(in oklab, var(--pd-kem-tối, #022f48) calc(var(--tối) * 100%), #faf6ed);
  --pd-thẻ: color-mix(in oklab, rgba(255, 255, 255, 0.1) calc(var(--tối) * 100%), #f6f4ed);
  --pd-mực: color-mix(in oklab, #f2f7fa calc(var(--tối-chữ) * 100%), #181818);
  --pd-line: color-mix(in oklab, rgba(255, 255, 255, 0.16) calc(var(--tối-chữ) * 100%), rgba(24, 24, 24, 0.1));
  --pd-mờ: color-mix(in oklab, rgba(217, 233, 243, 0.7) calc(var(--tối-chữ) * 100%), #8d857a);
  /* mặt thẻ nhỏ (thẻ số, persona, số liệu, chip trạng thái) */
  --pd-mặt: color-mix(in oklab, rgba(255, 255, 255, 0.08) calc(var(--tối) * 100%), rgba(255, 255, 255, 0.6));
  --pd-viền: color-mix(in oklab, rgba(255, 255, 255, 0.14) calc(var(--tối) * 100%), rgba(24, 24, 24, 0.08));
  /* MỘT FONT cho cả trang: chỉ Mona Sans, đúng như trang My Work. Trước dùng mono hệ
     thống cho các nhãn nhỏ — nó lạc hẳn khỏi phần còn lại của site. Dáng "nhãn" giữ
     bằng weight 500 + giãn chữ, không bằng đổi font. */
  --pd-nhãn-giãn: 0.06em;
  /* ---- THANG BO GÓC: theo CỠ component, và giảm dần theo khổ màn ----
       --pd-bo-lớn  mặt lớn: thẻ mục lục · hai CTA · dải ghi chú · khối How-might-we ·
                    thẻ Next project · dock · bottom sheet · dải 4 thông tin ở banner
       --pd-bo      ô ảnh lớn (1–2 ô/hàng) · thẻ nhỏ (thẻ số, persona, số liệu, thẻ bước)
       --pd-bo-vừa  ô ảnh NHỎ (3 ô/hàng) · thumbnail Next project · hàng trong sheet
       --pd-bo-nhỏ  chip chữ (cỡ ảnh, chuỗi trạng thái)
       hình tròn thật thì dùng `50%` (nút icon, chấm) — không dùng 999.
     Bo phải theo cỡ ô: cùng 20px thì ô 1232×693 nhìn vừa, mà ô 397×223 (hàng 3 ảnh) lại
     thành tròn quá so với khung. */
  --pd-bo-lớn: 24px;
  --pd-bo: 20px;
  --pd-bo-vừa: 14px;
  --pd-bo-nhỏ: 4px;
}
@media (max-width: 1024px) {
  body.trang-pd {
    --pd-bo-lớn: 22px;
    --pd-bo: 18px;
    --pd-bo-vừa: 13px;
  }
}
@media (max-width: 767px) {
  body.trang-pd {
    --pd-bo-lớn: 18px;
    --pd-bo: 16px;
    --pd-bo-vừa: 12px;
  }
}
.pd {
  position: relative;
  background: var(--pd-kem);
  color: var(--pd-mực);
}
/* Nhãn nhỏ dùng lại ở nhiều chỗ: mono, giãn chữ, màu mờ */
.pd .pdb__eyebrow,
.pd .pdm__nhãn,
.pd .pdm__dẫn,
.pd .pdm__no,
.pd .pdch__no,
.pd .pds__no,
.pd .pdph__tag,
.pd .pdph__nhãn,
.pd .pdsố__n,
.pd .pdvai__n,
.pd .pdbước span,
.pd .pdmeta dt,
.pd .pdn__nhãn,
.pddock__no,
.pdnav__đầu p,
.pdnav__ds span {
  margin: 0;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.4;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: var(--pd-mờ);
}
@media (max-width: 767px) {
  .pd .pdb__eyebrow,
  .pd .pdm__nhãn,
  .pd .pdm__dẫn,
  .pd .pdm__no,
  .pd .pdch__no,
  .pd .pds__no,
  .pd .pdsố__n,
  .pd .pdvai__n,
  .pd .pdbước span,
  .pd .pdmeta dt,
  .pd .pdn__nhãn,
  .pddock__no,
  .pdnav__đầu p,
  .pdnav__ds span {
    font-size: 12px;
  }
}

/* ---------- KHỐI TỐI TRÊN: banner + mục lục dùng chung một tấm ảnh ---------- */
.pdtop {
  position: relative;
  isolation: isolate;
  color: #fff;
}
.pdtop__nền {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: #14100c;
}
.pdtop__nền picture {
  display: contents;
}
.pdtop__nền img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Chủ thể nên nằm ở NỬA TRÊN và giữa khung: nửa dưới bị thẻ Preface và hai CTA che. */
  object-position: 50% 35%;
}
/* Phủ tối dần xuống dưới: chữ trắng trên ảnh phải đủ tương phản mà không làm ảnh đục.
   Đậm hơn ở dưới vì thẻ mục lục và hai CTA đều là khối kính nằm ở nửa dưới. */
.pdtop__nền::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(12, 10, 8, 0.42) 0%, rgba(12, 10, 8, 0.24) 38%, rgba(12, 10, 8, 0.56) 100%);
}

/* ---------- 1. BANNER ---------- */
.pdb {
  min-height: 100svh;
  padding: calc(var(--topbar-h, 60px) + clamp(48px, 9vh, 120px)) var(--pd-lề) clamp(40px, 6vh, 80px);
  display: grid;
  align-content: center;
  justify-items: center;
  text-align: center;
  gap: clamp(16px, 2.4vh, 28px);
}
.pdtop .pdb__eyebrow {
  color: rgba(255, 255, 255, 0.72);
}
.pdb__h1 {
  margin: 0;
  max-width: 18ch;
  font-family: var(--font-display);
  /* Cỡ chữ hero: cùng cách làm với .hero__title — theo bề rộng màn, có trần để màn 27"
     không thành biển chữ. */
  font-size: clamp(40px, 9vw, 132px);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: #fdfaf6;
}
.pdb__phụ {
  margin: 0;
  max-width: 46ch;
  font-size: clamp(15px, 1.15vw, 19px);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.84);
}
.pdb__phụ span {
  display: block;
  margin-top: 10px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: rgba(255, 255, 255, 0.62);
}
/* Dải 4 thông tin — lớp KÍNH đúng công thức đã chốt (xem .wrow::before, .mwt__hàng) */
.pdb__meta {
  margin: clamp(8px, 1.6vh, 20px) 0 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  max-width: 100%;
  padding: 14px 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--pd-bo-lớn);
  /* Kính TỐI, không phải trắng mờ: ảnh banner có dự án chụp trời sáng, kính trắng 16% thì
     chữ trắng trên nó gần như mất chữ. Cùng công thức với cụm chữ trên thẻ ngang trang My
     Work (`.mwc__info`) và mũi tên đè ảnh (`rgba(20,20,20,.34)`). */
  background: rgba(20, 20, 20, 0.42);
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  backdrop-filter: blur(14px) saturate(130%);
}
.pdb__meta > div {
  padding: 0 clamp(16px, 2vw, 34px);
  text-align: center;
}
.pdb__meta > div + div {
  border-left: 1px solid rgba(255, 255, 255, 0.18);
}
.pdb__meta dt {
  margin: 0 0 4px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: rgba(255, 255, 255, 0.68);
}
.pdb__meta dd {
  margin: 0;
  font-size: clamp(13px, 1vw, 16px);
  font-weight: 500;
  line-height: 1.35;
  color: #fff;
}
/* Ô Client khi client là WEBSITE: chữ + mũi tên chéo, gạch chân mảnh cho thấy bấm được.
   Trên nền ảnh tối thì KHÔNG đổi sang xanh brand lúc rê (xanh đậm chìm vào ảnh) — thay vào
   đó gạch chân đặc lại và mũi tên hất lên, cùng kiểu với nút Project detail của modal. */
.pdb__meta dd a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 255, 255, 0.42);
  transition: border-color 0.2s ease;
}
.pdb__meta dd a svg {
  flex: none;
  width: 14px;
  height: 14px;
  transition: translate 0.2s ease;
}
.pdb__meta dd a:hover {
  border-bottom-color: #fff;
}
.pdb__meta dd a:hover svg {
  translate: 1px -1px;
}
/* ≤1023 và điện thoại: 4 cột thành 2×2, bo góc về thang bo thường */
@media (max-width: 1023px) {
  .pdb__meta {
    grid-auto-flow: row;
    grid-template-columns: 1fr 1fr;
    gap: 14px 0;
    padding: 16px 4px;
    border-radius: var(--pd-bo-lớn);
  }
  .pdb__meta > div + div {
    border-left: 0;
  }
  .pdb__meta > div:nth-child(even) {
    border-left: 1px solid rgba(255, 255, 255, 0.22);
  }
}
/* Ô ảnh bìa: nằm dưới dải thông tin, tràn rộng hơn cụm chữ như ref */
.pdb > .pdph {
  width: 100%;
  max-width: min(1040px, 100%);
  margin-top: clamp(20px, 4vh, 56px);
}

/* ---------- 2. MỤC LỤC ---------- */
.pdm {
  padding: 0 var(--pd-lề) clamp(64px, 10vh, 140px);
  display: grid;
  gap: clamp(12px, 1.4vw, 20px);
}
.pdm__thẻ {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 72px);
  padding: clamp(24px, 3.4vw, 56px);
  border-radius: var(--pd-bo-lớn);
  background: linear-gradient(160deg, #fbf8f4 0%, #f2ece3 100%);
  color: var(--pd-mực);
}
.pdm__thường {
  margin: 14px 0 0;
  font-family: var(--font-display);
  font-size: clamp(20px, 2.1vw, 32px);
  font-weight: 500;
  line-height: 1.24;
  letter-spacing: -0.01em;
}
/* Câu chốt: chữ gradient xanh — CÙNG công thức với "Hi, I'm Thu Ha" và tiêu đề My Work.
   `padding/margin-block` là để hộp nền phủ hết đuôi chữ, xem chú thích ở .mw1__tx. */
/* Dải gradient của câu nhấn đọc từ biến `--pd-nhấn-dải` để TỪNG DỰ ÁN đặt màu riêng; mặc định
   là dải xanh dương của site (giống "Hi, I'm Thu Ha"). */
/* Nhãn nhỏ trong hộp nền tối của dự án này là màu LIME `#DFF46B` (đúng ref chapter 3, và cùng
   màu nhãn của ba thẻ chapter 1). */
#pd[data-slug="tieng-viet-tv"] .pdômàu__ô.là-tối .pdômàu__nhãn {
  color: #dff46b;
}
/* ---------- Chapter 4 của Tiếng Việt TV: CHỮ BÊN TRÁI, ẢNH BÊN PHẢI ----------
   Cả cụm chữ (tiêu đề + đoạn mô tả + dãy chip) nằm ở CỘT TRÁI, ô ảnh mascot chiếm CỘT PHẢI và
   cao suốt cụm chữ. `.pds > *` mặc định trải `1 / -1`, nên phải ép mấy khối chữ về cột 1 và cho
   `.pds__tx` (đang giữ ô ảnh) trải từ hàng 1 tới hết. Ba hộp Concept/Visual cues/System use vẫn
   trải hết bề ngang nên phải trừ ra. */
#pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__no,
#pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__h,
#pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__p,
#pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pdchip {
  grid-column: 1;
}
/* Khối tối của dự án này KHÔNG ép cao trọn màn: chapter 1 chỉ có chữ + ba thẻ + ghi chú (~970px),
   trên màn cao thì `min-height: 100svh` chừa ra một khoảng trống lớn ở đáy khối — đo ở 2560×1440:
   **466px trống**, nhìn như một section rỗng trước chapter 2 (Thu Hà báo). Cho nó cao theo nội
   dung; các dự án khác vẫn giữ `100svh` vì khối tối của họ dài hơn một màn. */
#pd[data-slug="tieng-viet-tv"] .pdxanh {
  min-height: 0;
}

/* ---------- Tiếng Việt TV · CHAPTER 6: bốn thẻ trong MỘT khung có kẻ ngăn ----------
   Dựng theo reference/project-tiengviet/ref-chapter6.png: nền chapter `#101C17`, bốn thẻ nằm
   trong một khung viền mảnh, giữa các thẻ là kẻ dọc; nhãn thẻ màu lime `#DFF46B` chữ hoa; tiêu đề
   trắng; mô tả trắng 70%. Bản chung của `.pdxanh .pdsố li` là mặt TRẮNG ĐẶC — ref ở đây là mặt
   tối nên phải khai riêng. */
#pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố {
  gap: 0;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--pd-bo);
  overflow: hidden;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
#pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố li {
  border: 0;
  border-radius: 0;
  background: rgba(255, 255, 255, 0.02);
  box-shadow: none;
}
#pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố li + li {
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.14);
}
#pd[data-slug="tieng-viet-tv"] .pd .pdch[data-ch="6"] .pdsố .pdsố__n,
#pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố .pdsố__n {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: #dff46b;
}
#pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố b {
  font-size: clamp(17px, 1.4vw, 21px);
  font-weight: 500;
  color: #fff;
}
#pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố span:not(.pdsố__n) {
  color: rgba(255, 255, 255, 0.68);
}
@media (max-width: 1023px) {
  #pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  #pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố li:nth-child(3) {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14);
  }
  #pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố li:nth-child(4) {
    box-shadow:
      inset 1px 0 0 rgba(255, 255, 255, 0.14),
      inset 0 1px 0 rgba(255, 255, 255, 0.14);
  }
}
@media (max-width: 767px) {
  #pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố {
    grid-template-columns: minmax(0, 1fr);
  }
  #pd[data-slug="tieng-viet-tv"] .pdch[data-ch="6"] .pdsố li + li {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14);
  }
}

/* ---------- Tiếng Việt TV: THU KHE giữa chapter 1 (nền tối) và chapter 2 ----------
   Thu Hà: "tự nhiên có 1 khoảng trống quá nhiều" — đo ở 1512×810 khi ghi chú Evidence boundary
   nằm giữa màn: từ đáy ghi chú tới nhãn "Chapter 2" là **199px** trắng (đệm dưới của mục 88 +
   đệm dưới khối tối 45 + đệm trên đầu chapter 57). Rút cả ba lại còn tổng ~70px. KHÔNG đụng mốc
   của cổng cuộn (đáy khối ≤45% màn) nên ghi chú vẫn đọc được ở giữa màn trước khi sang chapter 2.
   Chỉ áp cho dự án này để nhịp của Event Order / Msky không đổi. */
/* Rút bớt nhưng KHÔNG sát quá (bản đầu rút còn 76px, Thu Hà thấy sát): tổng ~120px. */
#pd[data-slug="tieng-viet-tv"] .pdxanh .pds {
  padding-bottom: clamp(26px, 3.2vw, 44px);
}
#pd[data-slug="tieng-viet-tv"] .pdxanh {
  padding-bottom: clamp(24px, 3vw, 44px);
}
/* Đo lại sau khi sửa: đáy MỤC nằm cao hơn đáy dòng ghi chú 12px (ghi chú có margin âm bù), nên
   khe thật = đệm khối + đệm đầu chapter − 12. Muốn ~110px thì hai đệm này để 44 + 60. */
#pd[data-slug="tieng-viet-tv"] .pdch[data-ch="2"] .pdch__đầu {
  padding-top: clamp(40px, 4.2vw, 60px);
}

/* ---------- Chapter 4 của Tiếng Việt TV: cục chữ 2/3 · ảnh mascot 1/3 ----------
   Từ 768 lên là hai cột (TABLET cũng hai cột — Thu Hà chốt); chỉ MOBILE (≤767) mới xếp trên dưới.
   Ảnh giữ trong dòng, span qua các hàng NGẦM (`1 / span 99` — dùng `1 / -1` thì lưới không khai
   `grid-template-rows` nên `-1` quy về hàng 1, ảnh 385px nhồi hết vào hàng của "04 · Branding &
   mascot" và tiêu đề tụt xuống) và `align-self: stretch` để CHIỀU CAO ẢNH CHẠY THEO cục chữ —
   nhờ vậy ảnh không kéo giãn hàng nào, gap trong cục chữ giữ đúng nhịp. */
@media (min-width: 768px) {
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    align-content: start;
    grid-auto-rows: min-content;
  }
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__no,
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__h,
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__p,
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pdchip {
    grid-column: 1;
  }
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx {
    grid-column: 2;
    grid-row: 1 / span 99;
    align-self: stretch;
    min-height: 0;
  }
  /* Ảnh đặt TUYỆT ĐỐI trong ô: nếu để nó chạy theo dòng thì chính chiều cao tự nhiên của ảnh
     (1:1 → 385px) kéo cao ô lưới, ô cao hơn cục chữ (296) rồi tràn xuống mục dưới. Đặt tuyệt đối
     thì ô cao đúng bằng cục chữ, ảnh `contain` nên mascot vẫn nguyên tỉ lệ. */
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx {
    position: relative;
    overflow: hidden;
  }
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx .pdph {
    position: absolute;
    inset: 0;
  }
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx .pdph__ô {
    height: 100%;
    min-height: 0;
    aspect-ratio: auto;
  }
  /* Ảnh mascot KHÔNG được cắt: đặt TUYỆT ĐỐI phủ ô rồi `object-fit: contain` — hộp ảnh có kích
     thước xác định (bằng ô) nên ảnh 1:1 co vào vừa khít, cao bằng ô tức bằng cục chữ.
     `max-height: 100%` KHÔNG dùng được: chiều cao ô không "xác định" theo cách trình duyệt cần
     để rút gọn phần trăm — đo được hộp ảnh vẫn 488×488 trong ô 488×299 nên bị cắt đầu và chân. */
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx .pdph__ô {
    position: relative;
  }
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx .pdph__ô img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    aspect-ratio: auto;
    object-fit: contain;
  }
  /* GAP trong cục chữ nhỏ lại (mặc định 56px giữa các khối) — Thu Hà chốt: gap đang quá lớn. */
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > * + *:not(.pds__h):not(.pds__tx) {
    margin-top: clamp(14px, 1.4vw, 22px);
  }
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__h {
    margin-bottom: clamp(10px, 1vw, 16px);
  }
}

/* Hai mục của chapter 4 là MỘT khối liền — bỏ vạch kẻ ngăn giữa chúng. */
#pd[data-slug="tieng-viet-tv"] .pdsáng .pds + .pds {
  border-top: 0;
  padding-top: 0;
}
/* Bốn section SVG ở dưới: mỗi file đã có nền + bo góc riêng nên ô ảnh phải BỎ khung/nền/đệm. */
#pd[data-slug="tieng-viet-tv"] .pdsáng .pdph.có .pdph__ô {
  padding: 0;
  border: 0;
  background: none;
}
/* Ba hộp phía dưới vẫn trải hết bề ngang. */
#pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pdômàu {
  grid-column: 1 / -1;
}
/* Chip: nền TRẮNG (Thu Hà chốt), viền mảnh lại cho khỏi nặng. */
#pd[data-slug="tieng-viet-tv"] .pdchip li {
  border-color: rgba(24, 24, 24, 0.12);
  background: #fff;
}
@media (max-width: 767px) {
  /* MOBILE: một cột, và ảnh XUỐNG DƯỚI cụm chữ — trong DOM ô ảnh là khối đầu tiên (để nó vào cột
     phải ở khổ rộng), nên ở mobile phải đẩy nó xuống cuối bằng `order`. */
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx {
    order: 9;
  }
  /* Ảnh mascot ở mobile cũng KHÔNG được cắt: ô giữ tỉ lệ 1:1 và ảnh phủ ô bằng `contain`
     (mặc định của ô ảnh là `cover` nên đầu và chân mascot bị cắt). */
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx .pdph__ô {
    position: relative;
    aspect-ratio: 1 / 1;
  }
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx .pdph__ô img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
  }
}
@media (max-width: 767px) {
  /* Khổ hẹp: mọi thứ về một cột, ảnh xuống dưới cụm chữ. */
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds {
    grid-template-columns: minmax(0, 1fr);
  }
  #pd[data-slug="tieng-viet-tv"] .pdsáng .pds > .pds__tx {
    position: static;
    translate: none;
    width: auto;
    grid-column: 1;
  }
}
#pd[data-slug="tieng-viet-tv"] {
  /* Thanh chạy 15s của bộ lướt: xanh lá app (#12A52A) thay vì xanh dương brand site. */
  --pd-lướt-nhấn: #12a52a;
  /* Xanh lá theo brand Tiếng Việt TV: xanh rừng đậm của chapter 1 (#0F2A1F) → xanh lá của app
     (lấy từ chính ảnh bìa: vùng xanh đậm #235F20, xanh tươi #09AD1A). */
  --pd-nhấn-dải: linear-gradient(90deg, #0f2a1f 0%, #235f20 45%, #12a52a 100%);
}
.pdm__nhấn {
  margin: 0.5em 0 0;
  font-family: var(--font-display);
  font-size: clamp(20px, 2.1vw, 32px);
  font-weight: 500;
  line-height: 1.24;
  letter-spacing: -0.01em;
  background: var(--pd-nhấn-dải, linear-gradient(90deg, #014268, #0d9bee));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  padding-block: 0.18em;
  margin-block: -0.18em calc(-0.18em + 0.5em);
}
.pdm__nhãn--2 {
  margin-top: clamp(24px, 3vw, 44px);
}
.pdm__đãLàm {
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px clamp(16px, 2vw, 32px);
  font-size: clamp(14px, 1vw, 16px);
  font-weight: 600;
  line-height: 1.4;
}
.pdm__dẫn {
  padding-bottom: 14px;
  border-bottom: 1px solid var(--pd-line);
}
.pdm__ds {
  margin: 0;
  padding: 0;
  list-style: none;
}
.pdm__ds a {
  display: grid;
  grid-template-columns: clamp(84px, 8vw, 108px) minmax(0, 1fr) 22px;
  align-items: baseline;
  gap: 4px 10px;
  padding: 14px 0;
  /* Kẻ ngăn chạy hết bề ngang cột, KHÔNG bo góc và rê chuột KHÔNG tô nền — rê thì chỉ
     đổi màu chữ + icon sang #0876B7, đúng cách đã chốt ở hai CTA trang About. */
  border-bottom: 1px solid var(--pd-line);
  color: inherit;
  text-decoration: none;
  transition: color 0.22s ease;
}
.pdm__ds li:last-child a {
  border-bottom: 0;
}
.pdm__ds b {
  font-size: clamp(15px, 1.15vw, 18px);
  font-weight: 600;
  line-height: 1.32;
}
.pdm__ds small {
  grid-column: 2;
  font-size: 13px;
  line-height: 1.45;
  color: color-mix(in srgb, var(--pd-mực) 55%, transparent);
}
.pdm__ds i {
  grid-row: 1;
  grid-column: 3;
  display: grid;
  place-items: center;
  opacity: 0;
  translate: -6px 0;
  transition:
    opacity 0.22s ease,
    translate 0.22s ease;
}
.pdm__ds svg {
  width: 20px;
  height: 20px;
}
.pdm__ds a:hover,
.pdm__ds a:focus-visible {
  color: #0876b7;
  outline: 0;
}
.pdm__ds a:hover i,
.pdm__ds a:focus-visible i {
  opacity: 1;
  translate: 0 0;
}
/* Hai CTA nhảy chapter + dải ghi chú: khối KÍNH TỐI trên ảnh, cùng công thức kính */
.pdm__cta,
.pdm__note {
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--pd-bo-lớn);
  background: rgba(24, 24, 24, 0.34);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
  color: #fff;
}
.pdm__cta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  overflow: hidden;
}
.pdm__cta a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: clamp(18px, 2.2vw, 32px);
  color: inherit;
  text-decoration: none;
  transition: background-color 0.22s ease;
}
.pdm__cta a + a {
  border-left: 1px solid rgba(255, 255, 255, 0.16);
}
.pdm__cta b {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(16px, 1.5vw, 24px);
  font-weight: 500;
  line-height: 1.24;
  letter-spacing: -0.01em;
}
.pdm__cta small {
  display: block;
  margin-top: 8px;
  font-size: clamp(13px, 0.95vw, 15px);
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.7);
}
.pdm__cta i {
  flex: none;
  display: grid;
  place-items: center;
  transition: translate 0.22s ease;
}
.pdm__cta svg {
  width: 24px;
  height: 24px;
}
.pdm__cta a:hover {
  background: rgba(255, 255, 255, 0.09);
}
.pdm__cta a:hover i {
  translate: 5px 0;
}
.pdm__note {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: clamp(16px, 1.8vw, 24px) clamp(18px, 2.2vw, 32px);
  font-size: clamp(14px, 1.05vw, 17px);
  line-height: 1.45;
}
.pdm__note svg {
  flex: none;
  width: 22px;
  height: 22px;
}
/* Tablet dọc trở xuống: thẻ mục lục và hai CTA về một cột.
   Và ĐÚNG Ở ĐÂY thì ẩn luôn cột phải (câu "A quick look…" + danh sách chapter): cột đó chỉ
   có nghĩa khi còn đủ bề ngang để đứng CẠNH cụm Preface. Xếp nó xuống dưới thì thành một
   danh sách dài thứ hai ngay trên hai CTA, mà đường đi tới từng chapter đã có sẵn ở dock
   (nút more → Content Navigation) và ở hai CTA.
   Hai việc này viết TRONG CÙNG một khối để không bao giờ lệch nhau: hễ thẻ về một cột là
   cột phải tắt. */
@media (max-width: 1023px) {
  .pdm__thẻ {
    grid-template-columns: 1fr;
  }
  .pdm__phải {
    display: none;
  }
  .pdm__cta {
    grid-template-columns: 1fr;
  }
  .pdm__cta a + a {
    border-left: 0;
    border-top: 1px solid rgba(255, 255, 255, 0.16);
  }
}
@media (max-width: 767px) {
  .pdm__đãLàm {
    grid-template-columns: 1fr;
  }
}


/* ---------- 3. CÁC CHAPTER ---------- */
.pdchs {
  position: relative;
  z-index: 1;
  background: var(--pd-kem);
}
/* BANNER + MỤC LỤC phải nằm TRÊN lớp nền tối. `.pdchs` có `z-index: 1` nên nó tạo tầng riêng, và
   tấm nền tối (`.pdchs::after`, `z-index: -1`, `position: fixed`) nằm trong tầng đó → nó phủ luôn
   hai section phía trên. Trước đây không lộ vì chapter tối đầu tiên luôn cách mục lục một quãng;
   Tiếng Việt TV cho chapter 1 nền tối và bật sớm nên thẻ Preface bị lấp hẳn (đo được: điểm ảnh ở
   chỗ thẻ ra đúng màu nền tối). Nâng hai section lên z-index 2 là hết. */
.pdtop {
  position: relative;
  z-index: 2;
}
/* Thẻ Preface luôn là mặt SÁNG (gradient kem dán cứng), nên bộ chữ trong nó phải CHỐT theo mặt
   sáng — không đi theo `--pd-mực`/`--pd-line` của trang, vì lúc nền tối hai biến đó thành chữ
   sáng ⇒ chữ kem trên thẻ kem. */
.pdm__thẻ {
  --pd-mực: #181818;
  --pd-line: rgba(24, 24, 24, 0.1);
}
/* NỀN TỐI: một lớp gradient DÁN THEO MÀN (`position: fixed`) phủ cả vùng chapter, chứ không tô
   riêng cho khối chapter 2. Lý do: nếu gradient dán theo KHỐI thì màu ở đáy khối (sáng, #065c88)
   khác hẳn màu phẳng của `.pdchs` ngay bên dưới (#012132) — chỗ giáp ranh hiện ra một mối nối
   rõ rệt, đúng chỗ Thu Hà thấy "màu xanh đậm khác hẳn màu dưới ảnh".
   Dán theo màn thì mọi vùng đang tối đều là CÙNG một dải gradient, không có mối nối nào.
   `z-index: -1` để nằm trên nền phẳng của `.pdchs` mà vẫn dưới chữ. */
.pdchs::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Nền tối đọc từ biến để TỪNG CHAPTER khai màu riêng được (`nềnXanh: "#101318"` trong data);
     không khai thì vẫn là dải xanh đậm gốc. JS ghi biến này theo khối đang tối. */
  background: var(--pd-nền-tối, linear-gradient(180deg, #01202f 0%, #022f48 45%, #05496d 100%));
  opacity: var(--tối);
  transition: opacity 0.32s ease;
}

/* Đổi màu light ↔ dark: `--tối` nhảy 0 → 1 đúng lúc mục trùm kín màn, còn việc chuyển MƯỢT do
   `transition` theo THỜI GIAN lo (0.32s) — không buộc vào quãng cuộn, nên không có dải màu dài
   chạy qua giữa màn như bản trước.
   Liệt kê đích danh từng nhóm chữ, KHÔNG dùng `.pdchs *`: `transition` là shorthand, viết trùm
   như thế sẽ xoá luôn transition riêng của thẻ xoè, nút, link… */
.pdchs,
.pdchs .pdch__no,
.pdchs .pdch__h2,
.pdchs .pdch__đầu,
.pdchs .pds,
.pdchs .pds__no,
.pdchs .pds__h,
.pdchs .pds__dẫn,
.pdchs .pds__p,
.pdchs .pdsố li,
.pdchs .pdsố b,
.pdchs .pdsố span,
.pdchs .pdvai li,
.pdchs .pdvai b,
.pdchs .pdvai span,
.pdchs .pdsl > div,
.pdchs .pdsl dt,
.pdchs .pdsl dd,
.pdchs .pdmeta > div,
.pdchs .pdmeta dt,
.pdchs .pdmeta dd,
.pdchs .pdbước li,
.pdchs .pdbước span,
.pdchs .pdgạch li,
.pdchs .pdgạch b,
.pdchs .pddòng span,
.pdchs .pddòng i,
.pdchs .pdchặng__nhãn,
.pdchs .pdchặng__h,
.pdchs .pdph__ô,
.pdchs .pdph__cap,
.pdchs .pdph__nhắc,
.pdchs .pdph__cỡ,
.pdchs .pdhỏi {
  transition:
    background-color 0.32s ease,
    color 0.32s ease,
    border-color 0.32s ease;
}
.pdch {
  /* Mốc cuộn của dock: chừa thanh header nổi để tiêu đề chapter không chui xuống dưới nó */
  scroll-margin-top: calc(var(--topbar-h, 60px) + 8px);
}
.pdch__đầu {
  margin-inline: var(--pd-lề);
  /* Đệm TRÊN rút gần một nửa: bản đầu `clamp(64px, 11vh, 150px)` nở theo chiều cao màn nên ở
     màn 1440 cao ra 150, cộng với `padding-bottom: 88` của mục con phía trên là **238px trắng**
     trước nhãn "Chapter N" — mà khe giữa hai mục con thường cũng chỉ 176. Nay 7vh, trần 96:
     đầu chapter vẫn thoáng hơn mục con nhưng không còn một màn trắng.
     (Tablet ≤1024 và mobile ≤767 ghi đè hẳn `padding` ở cuối file, không đụng tới.) */
  padding: clamp(44px, 7vh, 96px) 0 clamp(36px, 5vw, 72px);
  border-bottom: 1px solid var(--pd-line);
  text-align: center;
}
.pdch__no {
  margin-bottom: 18px;
}
/* ĐÚNG thang tiêu đề section của site: 48 / 44 / 40 / 36 / 32 — xem .mw2__h2, .work__title */
.pdch__h2 {
  margin: 0 auto;
  max-width: 22ch;
  font-family: var(--font-display);
  font-size: 48px;
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -0.01em;
}
@media (max-width: 1439px) {
  .pdch__h2 {
    font-size: 44px;
  }
}
@media (max-width: 1279px) {
  .pdch__h2 {
    font-size: 40px;
  }
}
@media (max-width: 1023px) {
  .pdch__h2 {
    font-size: 36px;
  }
}
@media (max-width: 809px) {
  .pdch__h2 {
    font-size: 32px;
  }
}
@media (max-width: 480px) {
  .pdch__h2 {
    font-size: 28px;
  }
}

/* ---------- mục con ----------
   Hai cột như ref: tiêu đề bên trái, thân chữ bên phải, còn mọi khối minh hoạ (ảnh, thẻ
   số, persona…) trải hết bề ngang. Làm bằng grid chứ không bọc thêm div: chỉ cần chỉ định
   `grid-column` cho từng loại con là tự xếp đúng. */
.pds {
  margin-inline: var(--pd-lề);
  padding: clamp(40px, 6vw, 88px) 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: clamp(28px, 5vw, 96px);
  align-items: start;
}
.pds + .pds {
  border-top: 1px solid var(--pd-line);
}
.pds > * {
  grid-column: 1 / -1;
}
.pds__no {
  margin-bottom: clamp(16px, 2vw, 28px);
}
.pds__h {
  grid-column: 1;
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.5vw, 38px);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
}
.pds__tx {
  grid-column: 2;
}
/* Đoạn văn nằm RỜI (khối `p` không phải khối đầu tiên, ví dụ đoạn kết sau bảng số liệu)
   thì trải hết bề ngang, nhưng vẫn chặn độ dài dòng cho dễ đọc. */
.pds > .pds__p {
  max-width: 74ch;
}
/* MỘT cỡ duy nhất cho mọi đoạn mô tả dưới các đầu mục 01/02/03…: 15px. Trước đây câu dắt
   (`.pds__dẫn`) là 16–20 còn thân bài (`.pds__p`) là 15–16, nên mục thì chữ to mục thì chữ bé.
   Tiêu đề giữ nguyên thang của nó. */
.pds__dẫn {
  margin: 0 0 1em;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.5;
  color: color-mix(in srgb, var(--pd-mực) 62%, transparent);
}
.pds__p {
  margin: 0 0 1em;
  font-size: 15px;
  line-height: 1.62;
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}
.pds__p:last-child {
  margin-bottom: 0;
}
/* Giãn cách giữa các khối minh hoạ. TRỪ `.pds__h` và `.pds__tx`: hai cái đó nằm CÙNG
   một hàng grid (trái / phải), thêm margin-top vào cột phải là chữ tụt xuống lệch hẳn
   khỏi tiêu đề. */
.pds > * + *:not(.pds__h):not(.pds__tx) {
  margin-top: clamp(28px, 3.4vw, 56px);
}
/* Trade-off luôn dán vào khối ngay trên nó — nó là ghi chú của khối đó, không phải một
   khối ngang hàng. */
.pds > * + .pdđổi {
  margin-top: clamp(12px, 1.4vw, 18px);
}
@media (max-width: 1023px) {
  .pds {
    grid-template-columns: minmax(0, 1fr);
  }
  .pds__h,
  .pds__tx {
    grid-column: 1;
  }
  .pds__h {
    margin-bottom: clamp(16px, 2.4vw, 24px);
  }
}

/* FANFAN mục 08 là khối tối DUY NHẤT của trang và nội dung chỉ ~940px, mà `.pdxanh` mặc định
   `min-height: 100svh` → trên màn cao còn một dải đen dài gần bằng một section trước khi tới mục
   09 (đo ở 1920×1200: khối 1200 nhưng nội dung 944 → thừa 256). Cho khối bám sát nội dung, giống
   cách đã làm ở Tiếng Việt TV. Cổng cuộn vẫn chạy: nó đo theo mép khối, không cần khối cao trọn
   màn. */
/* Mục 12 (nền vàng #FFBD1B): nhãn mục đổi về ĐEN — bản chung là xám `--pd-mờ`, đặt trên nền
   vàng thì khó đọc (Thu Hà báo). Đồng thời đoạn văn và dòng trade-off TĂNG CỠ (14 → 15–17) và
   đậm màu hơn (74% → 88%) cho dễ đọc trên nền vàng. */
#pd[data-slug="fanfan"] .pdsáng .pds__no {
  color: var(--pd-mực);
}
#pd[data-slug="fanfan"] .pdsáng .pdhkẻ__p,
#pd[data-slug="fanfan"] .pdsáng .pdhkẻ__ghi {
  font-size: clamp(15px, 1.15vw, 17px);
  color: color-mix(in srgb, var(--pd-mực) 88%, transparent);
}
#pd[data-slug="fanfan"] .pdsáng .pdhkẻ__ghi b {
  font-size: 13px;
}

/* Mục 13 (nền tối): ref-chapter8-13.png vẽ câu ghi chú là CHỮ THƯỜNG mảnh, không có khung —
   bản chung của `ghiChú` là khung viền + nền trắng. Chỉ bỏ khung trong khối tối của dự án này. */
#pd[data-slug="fanfan"] .pdxanh .pdghichú {
  padding: 0;
  border: 0;
  background: transparent;
  font-style: normal;
  font-size: clamp(12px, 0.9vw, 13px);
  color: rgba(255, 255, 255, 0.62);
}
#pd[data-slug="fanfan"] .pdxanh {
  min-height: 0;
  /* Đệm đáy ngắn hơn bản chung (24–48) để dải đen sau chữ ngắn lại. */
  padding-bottom: clamp(14px, 1.6vw, 24px);
}
/* Mục nằm trong khối tối cũng bớt đệm trên/dưới: mục 08 là mục DUY NHẤT trong khối nên đệm
   88px của bản chung cộng với đệm khối thành một dải đen dài. */
#pd[data-slug="fanfan"] .pdxanh .pds {
  padding-block: clamp(28px, 3.4vw, 48px);
}

/* ---------- MỤC bố cục "ảnh bên phải" (`bố: "ảnhPhải"`, ref-chapter7-08.png) ----------
   Chữ dồn hết về CỘT TRÁI; khối khai `cộtPhải` (nằm trong `.pds__tx`) sang CỘT PHẢI và bắt đầu
   NGANG VỚI đoạn văn đầu tiên — tức hàng 3, sau `.pds__no` (hàng 1) và `.pds__h` (hàng 2).
   Dùng `3 / span 99` chứ không `3 / -1`: `-1` chỉ có nghĩa khi lưới khai sẵn số hàng, ở đây hàng
   sinh tự động nên `-1` co về đúng một hàng. */
.pds--aphai > * {
  grid-column: 1;
}
.pds--aphai > .pds__tx {
  grid-column: 2;
  grid-row: 3 / span 99;
}
@media (max-width: 1023px) {
  /* Một cột: ô ảnh xuống DƯỚI phần chữ (đọc chữ trước, xem ảnh sau) — `order` có tác dụng với
     grid item nên không cần đổi thứ tự trong markup. */
  .pds--aphai > .pds__tx {
    grid-column: 1;
    grid-row: auto;
    order: 9;
  }
}

/* ---------- ô ảnh (trống hoặc có hình) ---------- */
.pdph {
  margin: 0;
  min-width: 0;
}
.pdph__nhãn {
  margin-bottom: 10px !important;
}
.pdph__ô {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  border-radius: var(--pd-bo);
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 6px;
  padding: 20px;
  text-align: center;
  /* Nét và nền của Ô TRỐNG đi theo light↔dark: trên nền xanh đậm thì nét đen 20% coi như
     tàng hình, nên phải lật sang nét sáng cùng nhịp với `--tối`. */
  border: 1px dashed color-mix(in oklab, rgba(255, 255, 255, 0.3) calc(var(--tối) * 100%), rgba(24, 24, 24, 0.2));
  background: color-mix(in oklab, rgba(255, 255, 255, 0.05) calc(var(--tối) * 100%), rgba(24, 24, 24, 0.035));
}
.pdph.có .pdph__ô {
  border: 0;
  padding: 0;
  background: none;
  /* PHẢI tắt cả kính: ô trống trên banner có `backdrop-filter`, mà ảnh mockup là PNG nền
     trong suốt — để nguyên thì lộ ra một khối chữ nhật nhoè đúng bằng hộp ảnh. */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.pdph__ô img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pdph__tag {
  text-transform: uppercase;
  letter-spacing: 0.14em !important;
  font-size: 11px !important;
}
.pdph__cap {
  font-size: clamp(13px, 1vw, 15px);
  font-weight: 600;
  line-height: 1.4;
  color: color-mix(in srgb, var(--pd-mực) 72%, transparent);
}
.pdph__nhắc {
  font-size: 12px;
  line-height: 1.4;
  color: var(--pd-mờ);
}
/* Cỡ ảnh cần upload — in thẳng trong ô trống, một viên nhỏ cho khỏi lẫn với chú thích */
.pdph__cỡ {
  margin-top: 10px;
  padding: 5px 10px;
  border: 1px solid rgba(24, 24, 24, 0.12);
  border-radius: var(--pd-bo-nhỏ);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  line-height: 1.3;
  color: color-mix(in srgb, var(--pd-mực) 62%, transparent);
}
@media (max-width: 767px) {
  .pdph__cỡ {
    margin-top: 8px;
    padding: 4px 8px;
    font-size: 10px;
  }
}
/* Trên nền TỐI (banner) thì ô trống phải lật sang nét sáng */
.pdtop .pdph__ô {
  border-color: rgba(255, 255, 255, 0.3);
  background: rgba(20, 18, 16, 0.34);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
}
.pdtop .pdph__tag,
.pdtop .pdph__nhắc {
  color: rgba(255, 255, 255, 0.66) !important;
}
.pdtop .pdph__cỡ {
  border-color: rgba(255, 255, 255, 0.28);
  color: rgba(255, 255, 255, 0.8);
}
.pdtop .pdph__cap {
  color: rgba(255, 255, 255, 0.9);
}
.pdph-hàng {
  display: grid;
  gap: clamp(10px, 1.4vw, 20px);
}
.pdph-hàng[data-n="2"] {
  grid-template-columns: 1fr 1fr;
}
.pdph-hàng[data-n="3"] {
  grid-template-columns: 1fr 1fr 1fr;
}
/* Ô ảnh trong hàng 3 chỉ rộng ~397 ở 1512 nên dùng bậc bo NHỎ HƠN ô ảnh 1–2 cột */
.pdph-hàng[data-n="3"] .pdph__ô,
.pdph-hàng[data-n="3"] .pdphim__sân,
.pdph-hàng[data-n="3"] .pdclip__sân,
.pdph-hàng[data-n="3"] .pdvid__v {
  border-radius: var(--pd-bo-vừa);
}
@media (max-width: 767px) {
  .pdph-hàng[data-n="2"],
  .pdph-hàng[data-n="3"] {
    grid-template-columns: 1fr;
  }
}

/* ---------- thẻ đánh số ---------- */
.pdsố {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(10px, 1.4vw, 18px);
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.pdsố li {
  padding: clamp(18px, 1.8vw, 26px);
  border: 1px solid var(--pd-viền);
  border-radius: var(--pd-bo);
  background: var(--pd-mặt);
}
.pdsố__n {
  display: block;
  margin-bottom: 14px !important;
}
/* Bản ĐỔI MÀU của thẻ số (khai `màu` trong data): số và tiêu đề lấy màu đó, phần mô tả giữ
   nguyên. Đặc tả `.pd .pdsố--màu .pdsố__n` (0,3,0) để thắng rule nhãn chung `.pd .pdsố__n`
   (0,2,0) — khỏi phải dùng `!important`. */
.pd .pdsố--màu .pdsố__n,
.pdsố--màu b {
  color: var(--pd-số-màu);
}
.pdsố b {
  display: block;
  margin-bottom: 8px;
  font-size: clamp(15px, 1.15vw, 18px);
  font-weight: 600;
  line-height: 1.3;
}
/* `display: block` là BẮT BUỘC, không chỉ cho gọn: `span` để inline thì chiều cao dòng lấy
   theo cái LỚN HƠN giữa line-height của chính nó và "strut" của thẻ `li` (16px × 1.6 = 25.6) —
   đo được hai dòng cách nhau **25.59** dù khai 23.25, giãn hơn cả thân bài (24.3). Thành block
   là nó dùng đúng line-height của mình.
   1.5 = cùng nhịp với mô tả trong các thẻ khác (`.pdht__p`, `.pdbh__p`); thân bài dòng dài mới
   để 1.62. */
.pdsố span:not(.pdsố__n) {
  display: block;
  font-size: clamp(14px, 1vw, 15px);
  line-height: 1.5;
  color: color-mix(in srgb, var(--pd-mực) 72%, transparent);
}
/* Bản FILLED ĐEN của thẻ số (khai `đen: true` trong data) — dùng khi thẻ đứng ngay trên một sơ
   đồ nền đen, để hai khối đọc ra là một cụm. Nền `#000` khớp đúng màu nền của mấy ảnh sơ đồ
   (đo bằng canvas: `#000000`). Ba selector dưới phải đủ đặc tả để thắng rule chung:
   `.pd .pdsố--đen .pdsố__n` (0,3,0) > `.pd .pdsố__n` (0,2,0). */
.pdsố--đen li {
  border-color: rgba(255, 255, 255, 0.14);
  background: #000;
}
.pd .pdsố--đen .pdsố__n {
  color: rgba(255, 255, 255, 0.55);
}
.pdsố--đen b {
  color: #fff;
}
.pdsố--đen span:not(.pdsố__n) {
  color: rgba(255, 255, 255, 0.76);
}
/* ---------- BỘ LƯỚT ẢNH GẮN VỚI THẺ SỐ (chapter 5 · 08) ----------
   Phần thẻ dùng nguyên `.pdsố` nên thẻ ở đây giống hệt thẻ số thường. Thêm ba thứ:
     · thanh tiến độ ở MÉP DƯỚI thẻ, chạy hết `data-giây` giây thì sang ảnh sau;
     · thẻ đang chạy sáng 100%, thẻ chưa tới còn 80% — mờ bằng `opacity` chứ không đổi màu
       chữ: thẻ mặt trắng nên hạ opacity là cả thẻ (mặt + chữ + bóng) nhạt đều, đổi màu chữ thì
       mặt vẫn trắng chóe, đọc ra "chữ mờ" thay vì "thẻ chưa tới";
     · một nút PHỦ KÍN thẻ để bấm. Nút phủ chứ không bọc chữ vào trong `<button>`: bọc thì phải
       reset lại toàn bộ font/màu/căn lề của button (button không thừa hưởng font trang — bẫy đã
       gặp ở dock), mà markup thẻ thì đang dùng chung với `.pdsố`. */
/* MỘT KHỐI LIỀN: ba thẻ và ô ảnh dính nhau (khe 0), chỉ khối ngoài có bo góc — nên phải
   `overflow: hidden` ở đây và ép bo của từng thẻ, từng ô ảnh về 0. */
.pdlướt {
  display: grid;
  gap: 0;
  border-radius: var(--pd-bo);
  overflow: hidden;
}
.pdxanh .pdlướt__thẻ > li {
  border-radius: 0;
  box-shadow: none; /* khe 0 rồi thì đổ bóng vô nghĩa, lại bị `overflow: hidden` cắt */
}
/* Hàng thẻ cũng phải KHE 0: `.pdsố` có `gap: clamp(10px, 1.4vw, 18px)` của riêng nó, không ép
   thì ba thẻ vẫn rời nhau 18px (đo được đúng 18). */
.pdlướt__thẻ {
  gap: 0;
}
/* KẺ NGĂN giữa ba thẻ: `#FFFFFF` ở 20%, vẽ bằng `::before` tuyệt đối — KHÔNG dùng
   `inset box-shadow`: rule `li:not(.là-nay)` (0,3,1) đặc tả cao hơn `li + li` (0,2,2) nên
   `box-shadow: none` của nó đè mất (đo lần đầu ra kẻ ngăn = none). Cũng không dùng
   `border-left`: border cộng vào hộp làm ba cột lệch nhau 1px. */
.pdlướt__thẻ > li + li::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 1px;
  background: rgba(255, 255, 255, 0.2);
  pointer-events: none;
}
/* Ô ảnh trong khối cũng bỏ bo riêng: bo đã nằm ở khối ngoài.
   TỈ LỆ 8:3 (KHÔNG phải 16:9 như các ô ảnh khác): gộp ba thẻ vào cùng khối làm tổng chiều cao
   vượt một màn (đo ở 1512×900: mục cao 1242 / màn 900). 8:3 hạ ô ảnh 693 → 462, mục còn 1011.
   Đây là NGOẠI LỆ có chủ ý, chỉ áp cho ô ảnh trong `.pdlướt`. */
.pdlướt__khung .pdph__ô,
.pdlướt__khung img {
  border-radius: 0;
}
.pdlướt__khung .pdph__ô {
  aspect-ratio: 8 / 3;
}
/* Ảnh PHỦ KÍN ô, không để hở trên/dưới. `.pdph__ô` là grid có `align-content: center` nên ảnh
   là grid item: `height: 100%` không giải được (vòng tròn) → nó rơi về chiều cao theo tỉ lệ gốc.
   Ô 16:9 mà ảnh 8:3 thì hở đúng 32px trên và dưới (đo ở 390). Đặt tuyệt đối + `cover` là mọi
   lệch tỉ lệ đều thành CẮT chứ không hở — cần thiết vì hai khổ dùng hai bản ảnh khác tỉ lệ. */
.pdlướt__khung .pdph__ô img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* PHÓNG + DỊCH: chỉ ảnh của thẻ, không phóng lớp nền. Ô ảnh `overflow: hidden` nên phần vượt
     bị cắt. Dùng hai thuộc tính RIÊNG (`translate` + `scale`) chứ không `transform`: thứ tự áp
     là scale trước rồi translate, nên số px dịch là px THẬT trên màn — JS chặn biên trực tiếp
     bằng px, không phải chia lại theo mức phóng. */
  translate: var(--dx, 0px) var(--dy, 0px);
  scale: var(--zoom, 1);
  transition:
    scale 0.45s cubic-bezier(0.22, 1, 0.36, 1),
    translate 0.25s ease-out;
  /* Ảnh <img> mặc định KÉO ĐƯỢC theo kiểu drag-and-drop của trình duyệt: giữ chuột kéo là nó
     nhấc ảnh lên thành "bóng ảnh", át hẳn thao tác kéo để xem của mình. */
  -webkit-user-drag: none;
  user-select: none;
}
/* Đang kéo thì bỏ transition của `translate`, nếu không ảnh chạy trễ sau con trỏ */
.pdlướt__ảnh.đang-kéo .pdlướt__khung .pdph__ô img {
  transition: scale 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Con trỏ bàn tay CHỈ khi ảnh thật sự tràn ô (có chỗ để xem thêm) — JS gắn class `kéo-được` */
.pdlướt__ảnh.kéo-được {
  cursor: grab;
}
.pdlướt__ảnh.kéo-được.đang-kéo {
  cursor: grabbing;
}

/* ---------- HAI NÚT PHÓNG/THU ở góc dưới–phải ô ảnh ----------
   Ref `ref-icon.png` là một viên thuốc bo 999 chứa hai icon, ngăn nhau bằng một vạch dọc. Theo
   RULE của site (không dùng bo 999) tôi giữ đúng bố cục và vạch ngăn của ref nhưng bo
   `--pd-bo-vừa` (14/13). Mặt là LỚP KÍNH TỐI cùng công thức với hai CTA nhảy chapter — ảnh bên
   dưới có cả vùng sáng lẫn tối nên nút phải tự đủ tương phản. */
.pdlướt__zoom {
  position: absolute;
  z-index: 2;
  right: clamp(12px, 1.2vw, 18px);
  bottom: clamp(12px, 1.2vw, 18px);
  display: flex;
  align-items: center;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--pd-bo-vừa);
  background: rgba(12, 24, 34, 0.42);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
  overflow: hidden;
}
.pdlướt__zoom button {
  width: 40px;
  height: 36px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: #fff;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition:
    background-color 0.25s ease,
    opacity 0.25s ease;
}
.pdlướt__zoom button + button {
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.18); /* vạch ngăn như ref */
}
.pdlướt__zoom button svg {
  width: 20px;
  height: 20px;
}
.pdlướt__zoom button:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.14);
}
/* Hết đường thì MỜ và không bấm được: mức 1 thì không thu nhỏ thêm, mức 3 thì không phóng thêm.
   Vẫn giữ nút trong khung (không `display: none`) để cụm không co giãn nhảy mỗi lần bấm. */
.pdlướt__zoom button:disabled {
  opacity: 0.32;
  cursor: default;
}
/* MOBILE giữ 16:9 như cũ: ở 342px bề ngang, khung 8:3 chỉ cao 128 — mockup iPad bé quá.
   Mobile có BẢN ẢNH RIÊNG (16:9 · 2560×1440), xem `srcM` trong data. */
@media (max-width: 767px) {
  .pdlướt__khung .pdph__ô {
    aspect-ratio: 16 / 9;
  }
}
/* ---------- BỘ LƯỚT: MỘT TỈ LỆ cho mọi khổ (khối khai `mộtTỉ: true`) ----------
   Mặc định mobile đổi sang 16:9 và cần bản ảnh riêng; khai `mộtTỉ` thì giữ 8:3 ở mọi khổ — chỉ
   cần MỘT bộ ảnh, ai muốn xem kỹ thì dùng nút phóng/thu. Đặt SAU khối `@media (max-width: 767px)`
   ở trên nên thắng nó mà không cần `!important`. */
@media (max-width: 767px) {
  .pdlướt--mộtTỉ .pdlướt__khung .pdph__ô {
    aspect-ratio: 8 / 3;
  }
}

/* ---------- BỘ LƯỚT TRÊN NỀN SÁNG: GỘP THÀNH MỘT KHỐI (Tiếng Việt TV) ----------
   Event Order đặt bộ lướt trong chapter nền tối nên phần gộp khối viết cho `.pdxanh`. Ở đây khối
   nằm trên nền kem, nên phải bỏ bo + đổ bóng của từng thẻ, kẻ ngăn đổi sang mực tối, và thẻ đang
   chạy tô nền xanh `#09AD1A` chữ trắng (Thu Hà chốt 2026-08-25). */
#pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ > li {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: #fff;
}
#pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ > li + li::before {
  background: rgba(24, 24, 24, 0.1);
}
#pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ > li.là-nay {
  background: #09ad1a;
}
#pd[data-slug="tieng-viet-tv"] .pd .pdlướt__thẻ > li.là-nay .pdsố__n,
#pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ > li.là-nay .pdsố__n {
  color: rgba(255, 255, 255, 0.72);
}
#pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ > li.là-nay b {
  color: #fff;
}
#pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ > li.là-nay span:not(.pdsố__n) {
  color: rgba(255, 255, 255, 0.86);
}
/* Thanh 15s nằm TRÊN nền xanh của thẻ đang chạy → đổi sang trắng cho thấy rõ, và nền rãnh tối lại. */
#pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ > li.là-nay .pdlướt__tiến {
  background: rgba(255, 255, 255, 0.28);
}
#pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ > li.là-nay .pdlướt__tiến > i {
  background: #fff;
}

/* BỐN thẻ LUÔN nằm MỘT HÀNG từ 768 lên (Thu Hà chốt): co nhỏ dần cùng nhau, tới mức chữ phải
   xuống dòng thì chuyển sang chế độ gọn ở dưới. Bản chung dùng `auto-fit minmax(210px, 1fr)` nên
   từ 1024 xuống nó tự gãy thành 2×2 — không đúng ý ở dự án này. Mobile (≤767) giữ nguyên chế độ
   slider một thẻ như cũ. */
@media (min-width: 768px) {
  #pd[data-slug="tieng-viet-tv"] .pdlướt__thẻ {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* Khu ẢNH của bộ lướt: tấm nền TRẮNG CÓ CHẤM XÁM (cùng lưới chấm 18px với khối `khung` ở
   Msky Hospitality). Đặt trên `.pdlướt__ảnh` chứ không phải trên từng khung: nó nằm DƯỚI ảnh nên
   lúc phóng to / kéo, phần nào chưa bị ảnh che vẫn thấy đúng nền — Thu Hà chốt "khi zoom nhớ cho
   cả background". Ảnh của bốn thẻ là PNG nền trong suốt nên nền này chính là mặt của khối. */
#pd[data-slug="tieng-viet-tv"] .pdlướt__ảnh {
  background-color: #fff;
  background-image: radial-gradient(rgba(17, 19, 21, 0.13) 1.1px, transparent 1.2px);
  background-size: 18px 18px;
}
/* Ô ảnh trong khối phải TRONG SUỐT để thấy tấm nền trên: mặc định nó có nền xám nhạt của ô ảnh
   trống. */
#pd[data-slug="tieng-viet-tv"] .pdlướt__khung .pdph__ô {
  background: none;
  border: 0;
}
/* Ảnh PNG có nền trong suốt nên KHÔNG cắt-phủ (cover) mà vừa-khít (contain): cover sẽ cắt mất
   hai đầu sơ đồ. */
#pd[data-slug="tieng-viet-tv"] .pdlướt__khung .pdph__ô img {
  object-fit: contain;
}

/* ---------- CHẾ ĐỘ GỌN: chữ sắp phải xuống dòng thì ba thẻ chưa chọn chỉ còn SỐ ----------
   JS đo bề rộng chữ thật (đặt tạm `white-space: nowrap`) rồi gắn class `là-gọn` cho khối. Lúc đó
   hàng thẻ đổi sang flex: thẻ chưa chọn co về vừa con số, thẻ đang chọn ăn hết phần còn lại nên
   chữ của nó vẫn một dòng. */
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ {
  display: flex;
}
/* Thẻ ĐANG CHỌN **hug content**: rộng đúng bằng chữ của nó (một dòng), không kéo dài ra hết
   khoảng trống. Ba thẻ chỉ-còn-số thì CHIA ĐỀU phần còn lại — để chúng `auto` thì mỗi ô chỉ còn
   ~40px, nhìn như ba vạch (Thu Hà chốt 2026-08-25). */
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ > li {
  flex: 1 1 0;
  min-width: 0;
}
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ > li.là-nay {
  flex: 0 0 auto;
}
/* Chữ trong thẻ đang chọn KHÔNG được xuống dòng — có vậy bề rộng "hug" mới đúng bằng một dòng. */
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ > li.là-nay b,
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ > li.là-nay span:not(.pdsố__n) {
  white-space: nowrap;
}
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ > li:not(.là-nay) b,
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ > li:not(.là-nay) span:not(.pdsố__n) {
  display: none;
}
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ > li:not(.là-nay) .pdsố__n {
  margin-bottom: 0 !important;
}
/* Thẻ chỉ còn con số thì canh số vào GIỮA ô theo chiều dọc — chiều cao ô do thẻ đang chọn quyết
   định nên nếu để số dính mép trên thì ba ô nhìn như bị hụt. */
#pd[data-slug="tieng-viet-tv"] .pdlướt.là-gọn .pdlướt__thẻ > li:not(.là-nay) {
  display: grid;
  place-content: center;
  padding-inline: clamp(10px, 1.2vw, 18px);
}

.pdlướt__thẻ > li {
  position: relative;
  overflow: hidden; /* để thanh ở mép dưới không thò ra ngoài bo góc thẻ */
}
/* THẺ CHƯA TỚI LƯỢT: mặt KÍNH TRONG như thẻ số thường của khối nền tối, chữ theo `--pd-mực`
   (nền tối → chữ sáng). THẺ ĐANG CHẠY: mặt TRẮNG ĐẶC, chữ tối. Đối lập mặt–chữ như vậy cho
   tương phản tốt hơn hẳn cách cũ (cả ba đều trắng, thẻ chưa tới chỉ mờ 80% — trắng-mờ-trên-nền-
   tối vẫn là một khối sáng, khó nhận ra cái nào đang chạy).
   Đặc tả phải cao hơn `.pdxanh .pdsố li` (0,2,1) vì khối đó nằm SAU trong file: dùng
   `.pdxanh .pdlướt__thẻ > li:not(.là-nay)` (0,3,1) cho mặt thẻ, và thêm `.pd` cho phần chữ để
   thắng `.pd .pdxanh .pdsố__n`. */
.pdxanh .pdlướt__thẻ > li:not(.là-nay) {
  /* KHÔNG viền: khe đã 0 nên viền của hai thẻ cạnh nhau cộng với kẻ ngăn thành ba đường sát
     nhau. Ngăn cách chỉ do kẻ ngăn `li + li` lo. */
  border: 0;
  background: var(--pd-mặt);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
  box-shadow: none;
}
.pd .pdxanh .pdlướt__thẻ > li:not(.là-nay) .pdsố__n {
  color: var(--pd-mờ);
}
.pdxanh .pdlướt__thẻ > li:not(.là-nay) b {
  color: var(--pd-mực);
}
.pdxanh .pdlướt__thẻ > li:not(.là-nay) span:not(.pdsố__n) {
  color: color-mix(in srgb, var(--pd-mực) 72%, transparent);
}
/* Thanh của thẻ chưa tới: nền thanh phải sáng lên vì lúc này mặt thẻ là kính tối */
.pdxanh .pdlướt__thẻ > li:not(.là-nay) .pdlướt__tiến {
  background: color-mix(in srgb, var(--pd-mực) 18%, transparent);
}
/* Thẻ ĐANG CHẠY: chữ to hơn và đậm hơn thẻ chưa tới — mỗi bậc đúng MỘT nấc của thang chữ đã
   chốt, và giảm dần theo khổ như mọi cỡ chữ khác của trang:
     tiêu đề  18 → 21 (desktop) · 17 → 19 (tablet) · 15 → 17 (mobile), weight 600 → 700
     mô tả    15 → 16 (desktop) · 14 → 15 (tablet/mobile),             weight 400 → 500 */
.pdlướt__thẻ > li.là-nay b {
  font-size: clamp(17px, 1.4vw, 21px);
  font-weight: 700;
}
.pdlướt__thẻ > li.là-nay span:not(.pdsố__n) {
  font-size: clamp(15px, 1.06vw, 16px);
  font-weight: 500;
}
/* Thanh: thẻ đang chạy DÀY hơn (5 · 4 · 3 theo khổ), thẻ chưa tới để mảnh (3 · 3 · 2) — cùng
   thang với thanh tiến độ của dock (3 ở desktop → 2 ở mobile). */
.pdlướt__tiến {
  position: absolute;
  inset: auto 0 0 0;
  height: 3px;
  background: rgba(24, 24, 24, 0.12);
  transition: height 0.3s ease;
}
.pdlướt__thẻ > li.là-nay .pdlướt__tiến {
  height: 5px;
}
.pdlướt__tiến > i {
  display: block;
  width: 0;
  height: 100%;
  /* Màu nhấn của bộ lướt (thanh 15s + viền focus) đọc từ biến để TỪNG DỰ ÁN đặt màu brand riêng;
     mặc định là xanh brand của site. */
  background: var(--pd-lướt-nhấn, #0876b7);
  transition: width 0.12s linear; /* nhịp vẽ ~1 frame, chỉ để làm mượt bước nhảy */
}
.pdlướt__nút {
  position: absolute;
  inset: 0;
  padding: 0;
  border: 0;
  border-radius: inherit;
  background: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.pdlướt__nút:focus-visible {
  outline: 2px solid var(--pd-lướt-nhấn, #0876b7);
  outline-offset: 2px;
}
/* Ba ảnh XẾP CHỒNG một ô: cùng `grid-area` nên ô cao bằng ảnh cao nhất, không nhảy chiều cao
   lúc đổi ảnh. Chuyển ảnh mờ dần cùng nhịp với phim (ra nhanh, vào chậm hơn và trễ một nhịp)
   nên hai ảnh không cùng hiện rõ ở giữa quãng. */
.pdlướt__ảnh {
  position: relative;
  display: grid;
}
/* Lớp NỀN cố định (khai `nền` trong data): nằm dưới, KHÔNG phóng theo zoom — đổi thẻ chỉ đổi
   lớp ảnh phía trên nên nền không nháy. Chưa khai `nền` thì ô ảnh vẫn chỉ có ảnh của thẻ. */
/* PHẢI đặt TUYỆT ĐỐI: để `grid-area` như một item thường thì `height: 100%` không giải được,
   ảnh rơi về chiều cao theo tỉ lệ gốc và ĐẨY hàng grid cao lên (đo: vùng ảnh 693 thay vì 462),
   kéo theo cả biên kéo ảnh tính sai. */
.pdlướt__nền {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pdlướt__ảnh > .pdlướt__khung {
  grid-area: 1 / 1;
}
.pdlướt__khung {
  min-width: 0;
  opacity: 0;
  transition: opacity 0.42s ease-in;
}
.pdlướt__khung.là-nay {
  opacity: 1;
  transition: opacity 0.8s ease-out 0.24s;
}
@media (prefers-reduced-motion: reduce) {
  .pdlướt__khung,
  .pdlướt__khung.là-nay,
  .pdlướt__tiến > i,
  .pdlướt__thẻ > li {
    transition: none;
  }
}
@media (max-width: 1024px) {
  .pdlướt__thẻ > li.là-nay .pdlướt__tiến {
    height: 4px;
  }
}
/* Hàng lái (mũi trái · 3 dot · mũi phải) CHỈ có ở mobile — desktop/tablet dàn ba thẻ ngang nên
   bấm thẳng vào thẻ là đủ, thêm dot vào là hai cách điều khiển cho cùng một việc. */
.pdlướt__lái {
  display: none;
}
@media (max-width: 767px) {
  /* Mobile mặc định không có phóng ảnh (Thu Hà chốt cho Event Order: khung chỉ 342px, mà mobile
     có bản ảnh 16:9 riêng nên không cần phóng). */
  .pdlướt__zoom {
    display: none;
  }
  /* NGOẠI LỆ: khối khai `mộtTỉ` dùng CHUNG một bản ảnh 8:3 cho mọi khổ, nên ở mobile ảnh rất nhỏ
     — chính vì thế mới bỏ bản ảnh thứ hai. Ở đó PHẢI có nút phóng/thu (Thu Hà chốt 2026-08-26). */
  .pdlướt--mộtTỉ .pdlướt__zoom {
    display: flex;
  }
  /* Mobile chỉ hiện MỘT thẻ nên không có gì để ngăn cách — kẻ ngăn thành một vạch dọc lạc lõng
     ở mép trái thẻ 02A/02B. */
  .pdlướt__thẻ > li + li::before {
    display: none;
  }
  /* BO GÓC ở mobile phải chuyển xuống hai phần con. Lý do: hàng lái (mũi + dot) là con CUỐI của
     `.pdlướt`, nên bo + `overflow: hidden` của khối ngoài rơi vào đáy hàng lái — nó trong suốt,
     thành ra hai góc dưới của ẢNH vẫn vuông. Nay: hàng thẻ bo hai góc TRÊN, ô ảnh bo hai góc
     DƯỚI, khối ngoài thôi bo. */
  .pdlướt {
    border-radius: 0;
    overflow: visible;
  }
  .pdlướt__thẻ {
    border-radius: var(--pd-bo) var(--pd-bo) 0 0;
    overflow: hidden;
  }
  .pdlướt__ảnh {
    border-radius: 0 0 var(--pd-bo) var(--pd-bo);
    overflow: hidden;
  }
  .pdlướt__tiến {
    height: 2px;
  }
  .pdlướt__thẻ > li.là-nay .pdlướt__tiến {
    height: 3px;
  }
  /* MOBILE = SLIDER: mỗi lúc một thẻ + ảnh của nó. Ba thẻ vẫn nằm trong DOM (cùng một bộ dựng,
     cùng trạng thái `là-nay`) nhưng xếp CHỒNG một ô như vùng ảnh, thẻ nào tới lượt thì hiện.
     MẶT TRẮNG chuyển LÊN HÀNG THẺ, ba thẻ con thành trong suốt: nhờ vậy đổi nội dung chỉ có
     CHỮ mờ vào/mờ ra, còn hộp trắng (và nền ảnh) đứng yên — trước đây mỗi thẻ tự mang mặt
     trắng nên cả hộp nháy theo mỗi lần chuyển. */
  .pdlướt__thẻ {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }
  .pdxanh .pdlướt__thẻ {
    background: #fff;
  }
  .pdxanh .pdlướt__thẻ > li,
  .pdxanh .pdlướt__thẻ > li:not(.là-nay) {
    border: 0;
    background: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: none;
  }
  /* Mặt luôn TRẮNG nên chữ của MỌI thẻ (kể cả thẻ chưa tới) phải chốt màu tối — nếu để chữ sáng
     như bản desktop thì lúc mờ vào sẽ lóe một nhịp chữ trắng trên nền trắng. */
  .pd .pdxanh .pdlướt__thẻ > li:not(.là-nay) .pdsố__n {
    color: rgba(24, 24, 24, 0.48);
  }
  .pdxanh .pdlướt__thẻ > li:not(.là-nay) b {
    color: #181818;
  }
  .pdxanh .pdlướt__thẻ > li:not(.là-nay) span:not(.pdsố__n) {
    color: rgba(24, 24, 24, 0.66);
  }
  .pdxanh .pdlướt__thẻ > li:not(.là-nay) .pdlướt__tiến {
    background: rgba(24, 24, 24, 0.12);
  }
  .pdlướt__thẻ > li {
    grid-area: 1 / 1;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.42s ease-in;
  }
  .pdlướt__thẻ > li.là-nay {
    opacity: 1;
    pointer-events: auto;
    transition: opacity 0.8s ease-out 0.24s;
  }
  /* Thẻ đang hiện là thẻ DUY NHẤT nên bấm vào nó không còn nghĩa "chọn ảnh này" — chuyển ảnh
     bằng mũi, dot hoặc vuốt. Tắt nút phủ để không bấm nhầm ra một cú "chọn lại chính nó". */
  .pdlướt__nút {
    display: none;
  }
  /* ĐỒNG BỘ với bộ lướt 4 thẻ focus ở section About trang chủ (`.acards-nav`, xem
     site.css ~4250): hai mũi nằm SÁT hai bên hàng dot và cả cụm căn giữa, chứ không đẩy ra hai
     mép — cùng `justify-content: center`, cùng khe 14, cùng khe dot 6, cùng mũi 32 tròn với
     glyph 20. Khác duy nhất: ở đây là nền tối nên mũi là LỚP KÍNH và dot màu mực (gần trắng),
     còn About nền kem nên mũi viền xanh `#0D9CF2`. */
  .pdlướt__lái {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    margin-top: 18px;
  }
  /* Hai mũi: LỚP KÍNH đúng công thức đã chốt của trang (`--pd-mặt` + `--pd-viền` + blur 10
     saturate 118%) — cùng thứ kính với ô trống trên banner, hai CTA nhảy chapter và ô sơ đồ.
     Dùng token chứ không viết cứng màu trắng: token tự lật theo `--tối`, nên mũi vẫn nổi cả khi
     khối này đặt trong chapter nền sáng. */
  .pdlướt__mũi {
    flex: none;
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    padding: 0;
    border: 1px solid var(--pd-viền);
    border-radius: 50%; /* TRÒN, đúng như `.acn__arrow` của About — không phải bo vuông */
    background: var(--pd-mặt);
    -webkit-backdrop-filter: blur(10px) saturate(118%);
    backdrop-filter: blur(10px) saturate(118%);
    color: var(--pd-mực);
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    transition:
      background-color 0.25s ease,
      border-color 0.25s ease;
  }
  .pdlướt__mũi svg {
    width: 20px;
    height: 20px;
  }
  /* Nhấn: kính ĐẶC hơn một bậc, vẫn là kính — không đổi sang nền xanh đặc, vì trên nền tối một
     ô xanh đặc đọc ra như nút bị vô hiệu chứ không phải "đang nhấn". */
  .pdlướt__mũi:active {
    border-color: color-mix(in srgb, var(--pd-mực) 34%, transparent);
    background: color-mix(in srgb, var(--pd-mực) 16%, transparent);
  }
  .pdlướt__dot {
    display: flex;
    align-items: center;
    gap: 6px;
  }
  /* Dot: 8×8 TRÒN, dot đang chạy chỉ khác ở ĐỘ MỜ (0.2 → 1) — y như `.acn__dots i` của About,
     không phóng thành gạch dài. Cùng cỡ, cùng khe, cùng cách đánh dấu, chỉ khác màu vì nền tối. */
  .pdlướt__dot > button {
    width: 8px;
    height: 8px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--pd-mực);
    opacity: 0.2;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    transition: opacity 0.25s ease;
  }
  /* Dot đang chạy: mờ về 1. Màu là MỰC của trang (nền tối → gần trắng) chứ không phải xanh
     brand — `#0876B7` đặt trên nền xanh đậm của chapter 5 gần như chìm (đo: 2.63 so với 11.92). */
  .pdlướt__dot > button.là-nay {
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pdlướt__khung .pdph__ô img,
  .pdlướt__thẻ > li,
  .pdlướt__thẻ > li.là-nay,
  .pdlướt__dot > button,
  .pdlướt__mũi {
    transition: none;
  }
}

/* ---------- THẺ SỐ TRONG KHỐI NỀN TỐI: mặt TRẮNG ĐẶC ----------
   Trên nền tối, thẻ kính (`--pd-mặt` là lớp mờ) đọc ra như một ô trong suốt, chữ nổi lơ lửng.
   Trong khối `.pdxanh` thì đổi sang mặt TRẮNG ĐẶC — cùng mặt thẻ, cùng đổ bóng với `.pdbh__ô`
   của chapter 2, nên hai chapter nền tối dùng cùng một loại thẻ.
   Chữ phải CHỐT màu tối, không đi theo `--pd-mực`: mặt thẻ luôn trắng, mà `--pd-mực` lúc tối
   là chữ sáng → chữ trắng trên thẻ trắng. Đúng cái bẫy đã gặp ở `.pdbh`. */
.pdxanh .pdsố li {
  border: 0;
  background: #fff;
  box-shadow: 0 12px 26px rgba(2, 32, 48, 0.14);
}
.pd .pdxanh .pdsố__n {
  color: rgba(24, 24, 24, 0.48);
}
.pdxanh .pdsố b {
  color: #181818;
}
.pdxanh .pdsố span:not(.pdsố__n) {
  color: rgba(24, 24, 24, 0.66);
}
/* Thẻ đơn (`data-n="1"`) vẫn là "đầu mục không khung" nên KHÔNG lấy mặt trắng */
.pdxanh .pdsố[data-n="1"] li {
  background: none;
  box-shadow: none;
}
.pd .pdxanh .pdsố[data-n="1"] .pdsố__n,
.pdxanh .pdsố[data-n="1"] b {
  color: inherit;
}
.pdxanh .pdsố[data-n="1"] span:not(.pdsố__n) {
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}

/* ---------- THẺ `vai` TRONG KHỐI NỀN TỐI: MẶT KÍNH 10% ----------
   Thu Hà chốt cho chapter 1 của Tiếng Việt TV: ba thẻ để **kính trong mờ ~10%** chứ không phải
   mặt trắng đặc như thẻ số. Chữ đi theo bộ chữ SÁNG (chốt cứng, không dùng `--pd-mực` vì biến đó
   đổi theo cổng cuộn). Chỉ ảnh hưởng `vai` nằm trong `.pdxanh` — mấy chỗ `vai` trên nền kem
   (Msky OTT chapter 4, Hospitality chapter 6, TV chapter 4) không đổi. */
.pdxanh .pdvai li {
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.1);
  -webkit-backdrop-filter: blur(8px) saturate(115%);
  backdrop-filter: blur(8px) saturate(115%);
}
/* Nhãn thẻ (User need / Product need / Design challenge): CHỮ HOA và màu `#DFF46B` — Thu Hà
   chốt 2026-08-25. Chỉ trong khối nền tối nên `vai` ở các chapter nền kem không đổi. */
.pd .pdxanh .pdvai .pdvai__n {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: #dff46b;
}
.pdxanh .pdvai b {
  color: #fff;
}
.pdxanh .pdvai span:not(.pdvai__n) {
  color: rgba(255, 255, 255, 0.78);
}

/* MỘT thẻ = đầu mục quyết định (ref: "01 · A shared, state-based workflow") → bỏ khung,
   để nó đọc như một tiêu đề nhỏ chứ không phải một thẻ lẻ loi kéo ngang cả hàng. */
.pdsố[data-n="1"] {
  grid-template-columns: minmax(0, 1fr);
}
.pdsố[data-n="1"] li {
  padding: 0;
  border: 0;
  background: none;
}
.pdsố[data-n="1"] b {
  margin-bottom: 12px;
  font-family: var(--font-display);
  font-size: clamp(19px, 1.8vw, 28px);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.pdsố[data-n="1"] span:not(.pdsố__n) {
  display: block;
  max-width: 74ch;
  font-size: clamp(15px, 1.05vw, 16px);
  line-height: 1.62;
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}

/* ---------- các bước quy trình ---------- */
.pdbước {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(12px, 1.6vw, 22px);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.pdbước li {
  padding-top: 14px;
  border-top: 1px solid var(--pd-line);
  font-size: clamp(14px, 1vw, 16px);
  font-weight: 600;
  line-height: 1.4;
}
.pdbước span {
  display: block;
  margin-bottom: 8px !important;
}
.pdbước--gọn {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}
.pdbước--gọn li {
  padding-block: 12px;
  border-top: 1px solid var(--pd-line);
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.pdbước--gọn span {
  margin: 0 !important;
}

/* ---------- KHỐI NỀN XANH TRÀN MÀN (một mục con "trưng bày") ----------
   Dựng theo reference/project-orderevent/ref-03-chapter2.jpg: nền xanh đậm chuyển dần sáng
   xuống dưới, tràn hết bề ngang, cao trọn màn, chữ trắng.
   `.pdch` đã rộng hết trang nên khối này KHÔNG cần lề âm — nó tự tràn. Lề nội dung chuyển hết
   vào `padding` của khối, và `.pds` / phần đầu chapter bên trong phải bỏ `margin-inline` của
   chúng, không thì thụt hai lần (đo lần đầu: khối rộng 1792 trên màn 1512 → sinh cuộn ngang). */
/* Khối này KHÔNG tự tô nền nữa: nền do token `--pd-kem` của cả vùng chapter lo (xem
   "LIGHT ↔ DARK" ở trên). Nó chỉ còn nhiệm vụ (1) cao trọn màn như ref, (2) là mốc để
   site.js tính `--tối`. */
.pdxanh {
  position: relative;
  /* Đệm đáy ngắn: xem hết ảnh cuối là cổng trượt sang chapter sau ngay, không để một khoảng
     trống dài lửng lơ giữa hai chapter. */
  padding: 0 var(--pd-lề) clamp(24px, 3vw, 48px);
  min-height: 100svh;
  /* Việc "trượt một phát qua mép khối" do JS lo (xem `cổngXanh()` trong site.js), KHÔNG dùng
     `scroll-snap-type` của CSS: `proximity` chỉ hút khi cuộn dừng RẤT gần mép (đo được: vuốt
     một nhịp còn cách 440px thì không hút), còn `mandatory` thì kẹt luôn cả vùng nội dung dài
     bên trong khối. Trộn cả hai thì chúng đánh nhau, sinh giật. */
}
/* Lề nội dung do chính khối xanh lo → con bên trong bỏ lề riêng của chúng */
.pdxanh .pdch__đầu,
.pdxanh .pds {
  margin-inline: 0;
}
/* ---------- HÀNG CÓ SỐ (khối `hàngSố`) ----------
   Ref-outcome.png: mỗi hàng là số in đậm + một câu, giữa các hàng có kẻ mảnh, hàng cuối có kẻ
   dưới. Số nằm ở cột riêng cỡ cố định để chữ của mọi hàng thẳng nhau. */
.pdhàngsố {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--pd-line);
}
.pdhàngsố li {
  display: grid;
  grid-template-columns: clamp(48px, 5vw, 72px) minmax(0, 1fr);
  gap: clamp(8px, 1vw, 16px);
  padding: clamp(16px, 1.8vw, 26px) 0;
  border-bottom: 1px solid var(--pd-line);
}
.pdhàngsố b {
  font-family: var(--font-display);
  font-size: clamp(14px, 1.05vw, 16px);
  font-weight: 700;
  line-height: 1.5;
  color: var(--pd-mực);
}
.pdhàngsố span {
  font-size: clamp(15px, 1.2vw, 18px);
  line-height: 1.5;
  color: color-mix(in srgb, var(--pd-mực) 84%, transparent);
}
@media (max-width: 767px) {
  .pdhàngsố li {
    grid-template-columns: 34px minmax(0, 1fr);
  }
}

/* ---------- HỘP CÓ VIÊN SỐ (khối `hộpSố`) ----------
   Ref chapter 5 của Tiếng Việt TV: hộp trắng, viên số vuông nền xanh nhạt ở góc trên–trái, tiêu
   đề đậm rồi đoạn mô tả. Hai cột ở desktop/tablet, một cột ở mobile. Bo góc theo `--pd-bo` của
   site (ref bo mềm hơn nhưng rule là dùng thang bo có sẵn). */
.pdhsố {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(var(--pdhs-cột, 2), minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 24px);
}
.pdhsố[data-cột="3"] {
  --pdhs-cột: 3;
}
.pdhsố__ô {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(12px, 1.4vw, 20px);
  padding: clamp(18px, 2vw, 28px);
  border: 1px solid var(--pd-viền);
  border-radius: var(--pd-bo);
  background: #fff;
  box-shadow: 0 10px 24px rgba(24, 24, 24, 0.06);
}
.pdhsố__n {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--pd-bo-vừa);
  background: #e9f8ea;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  color: #09ad1a;
}
.pdhsố__ô b {
  display: block;
  margin-bottom: 8px;
  font-family: var(--font-display);
  font-size: clamp(16px, 1.3vw, 20px);
  font-weight: 700;
  line-height: 1.3;
  color: #181818;
}
.pdhsố__ô span:not(.pdhsố__n) {
  display: block;
  font-size: clamp(14px, 1vw, 15px);
  line-height: 1.55;
  color: rgba(24, 24, 24, 0.68);
}
/* `tối: true` — bản NỀN TỐI của bộ thẻ đánh số (FANFAN mục 07): nền #202020, viền trắng mờ, ô
   số và tiêu đề đổi từ tông xanh lá sang tông vàng brand #FFBD1B, chữ mô tả trắng 78%. */
.pdhsố.là-tối .pdhsố__ô {
  border-color: rgba(255, 255, 255, 0.1);
  background: #202020;
  box-shadow: none;
}
.pdhsố.là-tối .pdhsố__n {
  background: color-mix(in srgb, #ffbd1b 16%, transparent);
  color: #ffbd1b;
}
.pdhsố.là-tối .pdhsố__ô b {
  color: #ffbd1b;
}
.pdhsố.là-tối .pdhsố__ô span:not(.pdhsố__n) {
  color: rgba(255, 255, 255, 0.78);
}
@media (max-width: 767px) {
  .pdhsố {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- DÃY CHIP CHỮ (khối `chip`) ----------
   Ref vẽ chip bo viên thuốc; theo rule của site KHÔNG dùng bo 999 nên chip dùng `--pd-bo-vừa`.
   Viền và chữ ăn theo `--pd-mực` để chip dùng được trên cả nền kem và nền màu. */
.pdchip {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(8px, 1vw, 14px);
}
.pdchip li {
  padding: 10px clamp(14px, 1.4vw, 22px);
  border: 1px solid color-mix(in srgb, var(--pd-mực) 32%, transparent);
  border-radius: var(--pd-bo-vừa);
  font-size: clamp(13px, 1vw, 15px);
  font-weight: 600;
  line-height: 1.3;
  color: var(--pd-mực);
}

/* ---------- KHỐI NỀN SÁNG MÀU RIÊNG (mục khai `nềnSáng: "#…"`) ----------
   Khác `.pdxanh`: nền tô THẲNG trên khối (không dùng lớp dán theo màn) và bộ chữ giữ nguyên màu
   tối, vì nền sáng. Nền tràn hết bề ngang màn: `.pdchs` có đệm ngang `--pd-lề` nên khối phải kéo
   ngược ra bằng margin âm rồi tự đệm lại. */
.pdsáng {
  position: relative;
  /* KHÔNG dùng margin âm: `.pdchs` không có đệm ngang, chính các mục con mới có lề riêng — y như
     `.pdxanh`. Thêm margin âm là khối phình ra 2×lề và trang tràn ngang (đo: 1792 trên màn 1512). */
  padding: clamp(48px, 7vw, 104px) var(--pd-lề);
  background: var(--pds-nền, transparent);
}
.pdsáng .pdch__đầu,
.pdsáng .pds {
  margin-inline: 0;
}
/* Khối nền sáng nằm liền ngay dưới tên chapter (khi mục đầu của chapter khai `nềnSáng`) thì đệm
   trên do chính nó lo, tên chapter không cần đệm riêng nữa. */
.pdsáng > .pdch__đầu {
  padding-top: 0;
}
/* ---------- SƠ ĐỒ KIẾN TRÚC (ref-chapter2-04.png) ----------
   Hai phía vây quanh một tâm, dưới là vòng đời đơn. Theo rule của site, KHÔNG theo ref ở ba
   điểm: bo góc dùng `--pd-bo` (ref là viên thuốc 999), ô có nền thì là LỚP KÍNH, và chữ không
   bao giờ đè lên hình. */
.pdsđ {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  column-gap: clamp(16px, 2.4vw, 40px);
  row-gap: clamp(28px, 4vw, 56px);
}
.pdsđ__cột {
  display: grid;
  gap: clamp(10px, 1.2vw, 16px);
  min-width: 0;
}
.pdsđ__nhãn {
  margin: 0 0 4px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: var(--pd-mờ);
}
.pdsđ__cột--phải .pdsđ__nhãn {
  text-align: right;
}
.pdsđ__ds {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(10px, 1.2vw, 16px);
}
/* Ô: lớp kính, đúng công thức đã chốt (`rgba(255,255,255,.2)` + blur 10 saturate 118%), pha
   nhạt hơn một chút vì nằm trên nền xanh đậm. */
.pdsđ__ô {
  padding: clamp(14px, 1.4vw, 20px) clamp(16px, 1.6vw, 22px);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--pd-bo);
  background: rgba(255, 255, 255, 0.08);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
}
.pdsđ__ô b {
  display: block;
  font-size: clamp(15px, 1.1vw, 17px);
  font-weight: 600;
  line-height: 1.3;
  color: var(--pd-mực);
}
.pdsđ__ô span {
  display: block;
  margin-top: 4px;
  font-size: clamp(13px, 0.95vw, 14px);
  line-height: 1.45;
  color: color-mix(in oklab, var(--pd-mực) 70%, transparent);
}
/* Tâm sơ đồ: hình tròn thật (như các nút icon của site), có vầng sáng bằng radial-gradient */
.pdsđ__tâm {
  position: relative;
  width: clamp(132px, 12vw, 176px);
  aspect-ratio: 1;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 4px;
  padding: 12px;
  border-radius: 50%;
  text-align: center;
  background: radial-gradient(circle at 50% 45%, #eaf6ff 0%, #cfe9fb 58%, #b6dcf7 100%);
  box-shadow:
    0 0 0 10px rgba(140, 205, 245, 0.12),
    0 0 60px 20px rgba(120, 195, 245, 0.22);
}
.pdsđ__tâm b {
  font-family: var(--font-display);
  font-size: clamp(16px, 1.3vw, 20px);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #06344f;
}
.pdsđ__tâm span {
  font-size: 12px;
  line-height: 1.35;
  color: rgba(6, 52, 79, 0.66);
}
/* Vòng đời đơn: một dải riêng, có kẻ ngăn phía trên */
.pdsđ__vòng {
  grid-column: 1 / -1;
  padding-top: clamp(20px, 2.6vw, 36px);
  border-top: 1px solid var(--pd-line);
}
.pdsđ__chuỗi {
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(8px, 1vw, 14px);
}
.pdsđ__chuỗi li {
  padding: 12px 14px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--pd-bo);
  background: rgba(255, 255, 255, 0.06);
  text-align: center;
}
.pdsđ__chuỗi b {
  font-size: clamp(13px, 1vw, 15px);
  font-weight: 600;
  line-height: 1.3;
  color: var(--pd-mực);
}
.pdsđ__ghi {
  margin: clamp(14px, 1.6vw, 20px) 0 0;
  font-size: clamp(13px, 1vw, 15px);
  line-height: 1.5;
  color: color-mix(in oklab, var(--pd-mực) 66%, transparent);
}
@media (max-width: 1023px) {
  /* Tâm lên trên, hai phía nằm cạnh nhau bên dưới */
  .pdsđ {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    justify-items: stretch;
  }
  .pdsđ__tâm {
    grid-column: 1 / -1;
    justify-self: center;
    order: -1;
  }
  .pdsđ__cột--phải .pdsđ__nhãn {
    text-align: left;
  }
}
@media (max-width: 767px) {
  .pdsđ {
    grid-template-columns: minmax(0, 1fr);
  }
  .pdsđ__chuỗi {
    grid-template-columns: 1fr 1fr;
  }
}

/* ---------- các bước dạng THẺ TRẮNG (ref-box.png) ----------
   Nút mũi tên chéo neo góc trên phải, số bước và tiêu đề dồn xuống ĐÁY thẻ. */
/* Tiêu đề nhỏ tuỳ chọn của dãy thẻ bước — cùng thang chữ với tiêu đề của khối `cột`, nhưng
   màu chữ thường vì khối này nằm trên nền kem chứ không trong thẻ trắng. */
.pdbh__đề {
  margin: 0 0 clamp(14px, 1.6vw, 22px);
  font-family: var(--font-display);
  font-size: clamp(18px, 1.6vw, 24px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--pd-mực);
}
.pdbh {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(12px, 1.4vw, 20px);
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.pdbh__ô {
  position: relative;
  display: grid;
  align-content: end;
  gap: 6px;
  min-height: clamp(148px, 12vw, 176px);
  padding: clamp(18px, 1.6vw, 24px);
  border-radius: var(--pd-bo);
  background: #fff;
  box-shadow: 0 12px 26px rgba(2, 32, 48, 0.14);
}
.pdbh__go {
  position: absolute;
  top: clamp(14px, 1.2vw, 18px);
  right: clamp(14px, 1.2vw, 18px);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(8, 118, 183, 0.1);
  color: #0876b7;
}
.pdbh__go svg {
  width: 20px;
  height: 20px;
}
/* Mặt thẻ LUÔN trắng ở cả hai chế độ nên chữ trong thẻ phải chốt màu tối, không đi theo
   `--pd-mực` (đi theo thì lúc tối sẽ thành chữ sáng trên thẻ trắng). */
.pdbh__n {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: rgba(24, 24, 24, 0.48);
}
.pdbh__h {
  font-family: var(--font-display);
  font-size: clamp(17px, 1.4vw, 20px);
  font-weight: 600;
  line-height: 1.26;
  color: #181818;
}
.pdbh__p {
  font-size: clamp(13px, 1vw, 15px);
  line-height: 1.5;
  color: rgba(24, 24, 24, 0.66);
}
@media (max-width: 1024px) {
  .pdbh {
    grid-template-columns: 1fr 1fr;
  }
  .pdbh__go {
    width: 40px;
    height: 40px;
  }
}
/* MOBILE (chỉ mobile — tablet và desktop giữ nguyên):
     · thẻ THẤP lại: chừa chỗ cho mũi tên còn 42 thay vì 56, đệm 14 thay vì 18 → cao 121–142
       xuống còn ~94–113;
     · cỡ chữ ĐÚNG THANG MOBILE: tiêu đề 15 (bằng `.pdsố b` và thân bài), nhãn số 11 —
       trước để 17 nên nó là tiêu đề thẻ TO NHẤT trên khổ mobile, lạc thang. */
@media (max-width: 767px) {
  .pdbh {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }
  /* MOBILE đổi hẳn thế bố cục: chữ BÊN TRÁI, mũi tên BÊN PHẢI, cách nhau 16px — bỏ luôn phần
     chừa 42px phía trên nên thẻ thấp hẳn (96 → ~70). Desktop/tablet vẫn là mũi tên góc trên,
     chữ dồn đáy. */
  .pdbh__ô {
    min-height: 0;
    align-content: center;
    row-gap: 3px;
    /* Chừa 60 bên phải cho mũi tên = 30 (mũi) + 16 (khe) + 14 (đệm) → khe chữ→mũi đúng 16 */
    padding: 14px 60px 14px 14px;
  }
  .pdbh__n {
    font-size: 11px;
  }
  .pdbh__h {
    font-size: 15px;
  }
  .pdbh__p {
    font-size: 13px;
  }
  /* Mũi tên neo GÓC TRÊN–PHẢI, thẳng hàng với hộp đệm của thẻ: cách trên 14 và cách phải 14 —
     đúng bằng đệm thẻ, nên nó thẳng mép trên của nhãn số và thẳng mép phải của nội dung.
     KHÔNG căn giữa theo chiều dọc nữa. */
  .pdbh__go {
    top: 14px;
    right: 14px;
    translate: none;
    width: 30px;
    height: 30px;
  }
  .pdbh__go svg {
    width: 16px;
    height: 16px;
  }
}

/* ---------- THẺ TRẮNG CÓ DẤU TÍCH (kết luận của 07) ----------
   Cùng mặt thẻ với `.pdbh__ô` (trắng, cùng bo, cùng đổ bóng) — khác: icon là dấu tích, nằm
   GÓC TRÊN–TRÁI và ĐI TRONG DÒNG (không neo tuyệt đối), chữ theo ngay dưới icon nên không có
   khoảng trống giữa icon và nội dung. Không có số bước.
   Bố cục: BA CỘT ở desktop và tablet, chỉ mobile mới xếp dọc.
   Chữ chốt màu tối như `.pdbh` (mặt thẻ luôn trắng ở cả hai chế độ). */
.pdht {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(12px, 1.4vw, 20px);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.pdht__ô {
  display: grid;
  align-content: start;
  gap: 6px;
  padding: clamp(16px, 1.6vw, 24px);
  border-radius: var(--pd-bo);
  background: #fff;
  box-shadow: 0 12px 26px rgba(2, 32, 48, 0.14);
}
.pdht__i {
  width: 44px;
  height: 44px;
  margin-bottom: 8px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(20, 162, 233, 0.12);
}
.pdht__i img {
  width: 22px;
  height: 22px;
  display: block;
}
.pdht__h {
  font-family: var(--font-display);
  font-size: clamp(17px, 1.4vw, 20px);
  font-weight: 600;
  line-height: 1.26;
  color: #181818;
}
.pdht__p {
  font-size: clamp(13px, 1vw, 15px);
  line-height: 1.5;
  color: rgba(24, 24, 24, 0.66);
}
/* TABLET: vẫn BA CỘT (chỉ có ba thẻ, chữ ngắn) — chỉ thu icon và đệm lại. */
@media (max-width: 1024px) {
  .pdht__i {
    width: 36px;
    height: 36px;
    margin-bottom: 6px;
  }
  .pdht__i img {
    width: 19px;
    height: 19px;
  }
}
/* MOBILE: một cột, xếp từ trên xuống. */
@media (max-width: 767px) {
  .pdht {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }
  .pdht__ô {
    row-gap: 3px;
    padding: 14px;
  }
  .pdht__i {
    width: 30px;
    height: 30px;
    margin-bottom: 6px;
  }
  .pdht__i img {
    width: 17px;
    height: 17px;
  }
  .pdht__h {
    font-size: 15px;
  }
  .pdht__p {
    font-size: 13px;
  }
}

/* ---------- persona ---------- */
.pdvai {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(10px, 1.4vw, 18px);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.pdvai li {
  padding: clamp(18px, 1.8vw, 26px);
  border: 1px solid var(--pd-viền);
  border-radius: var(--pd-bo);
  background: var(--pd-mặt);
}
.pdvai__n {
  display: block;
  margin-bottom: 14px !important;
}
.pdvai b {
  display: block;
  margin-bottom: 8px;
  font-family: var(--font-display);
  font-size: clamp(16px, 1.3vw, 20px);
  font-weight: 600;
  line-height: 1.28;
}
.pdvai span:not(.pdvai__n) {
  font-size: clamp(14px, 1vw, 15px);
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 72%, transparent);
}

/* ---------- mục con CĂN GIỮA ----------
   Dùng cho mục kiểu "trưng bày" (ref-user-start): nhãn, tiêu đề, câu dẫn và khối minh hoạ
   đều căn giữa, một cột. Không đụng tới mục con hai cột mặc định. */
.pds--giữa {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  text-align: center;
}
.pds--giữa > * {
  grid-column: 1;
}
.pds--giữa .pds__h {
  max-width: 22ch;
}
.pds--giữa .pds__tx {
  max-width: 52ch;
  /* Mục hai cột thì tiêu đề và cụm chữ nằm CẠNH nhau nên không cần khe; mục căn giữa thì
     chúng xếp trên dưới, phải có khe rõ ràng chứ không dán sát. */
  margin-top: clamp(14px, 1.4vw, 22px);
}

/* Khe từ cụm chữ xuống cụm thẻ: ngắn hơn khe chung giữa các khối, vì đây là một cụm liền mạch
   (chữ dẫn rồi tới thẻ minh hoạ ngay). */
.pds > .pds__tx + .pdxếp {
  margin-top: clamp(28px, 4vw, 64px);
}

/* ---------- cụm thẻ giấy nhớ: XOÈ RA rồi CHỤM LẠI ----------
   Dựng theo reference/project-orderevent/ref-user-start.png (xoè thành vòng cung) và
   ref-user-end.png (chụm về một chồng ở giữa). `--p` do site.js ghi theo cuộn: 0 → 1.
   Mỗi thẻ nằm CHỒNG NHAU tại tâm rồi tự dạt ra bằng `translate`, nên lúc chụm không phải
   tính lại vị trí — chỉ cần `--p` về 1 là mọi thứ về tâm. */
.pdxếp {
  --pdx-w: 292px; /* bề rộng thẻ */
  /* Mỗi bậc dạt ngang = bề rộng thẻ + KHE, để lúc xoè ba thẻ TÁCH HẲN nhau và đọc trọn chữ.
     Hai bản trước gối nhau 46 rồi 10px, chữ hai thẻ rìa vẫn bị che. */
  --pdx-x: 328px;
  --pdx-cung: 16px; /* thẻ ngoài rìa tụt xuống bao nhiêu — tạo vòng cung */
  --pdx-xoay: 5deg; /* độ xoay mỗi bậc khi xoè */
  --pdx-xoay2: 2.5deg; /* độ xoay còn lại khi đã chụm, cho thấy đây là một chồng giấy */
  --p: 0;
  position: relative;
  width: 100%;
  /* `content-box` để `padding-bottom` CỘNG vào chiều cao: vừa đẩy cụm thẻ lên khỏi thanh dock
     (thẻ căn giữa trong hộp nội dung, không tính phần đệm), vừa nới khoảng cách xuống khối
     tiếp theo. Đo ở 1512: dock cao 92 + cách đáy 20, nên chừa 72 là thẻ không còn sát dock. */
  box-sizing: content-box;
  height: calc(var(--pdx-w) + var(--pdx-cung) * 2 + 24px);
  /* Đệm đáy là KHOẢNG TRỐNG dưới cụm thẻ. Cùng với `place-items: start center` (thẻ neo ở MÉP
     TRÊN hộp), nó làm cụm thẻ luôn hiện ở khoảng TRÊN–GIỮA màn chứ không nằm sát thanh dock.
     Nếu để thẻ căn giữa hộp thì đệm chỉ ăn nửa (đo: đệm 48 → thẻ cách đáy hộp 24), muốn cách
     dock 112 phải đệm tới 224 — vô lý. */
  /* Chỉ cần một quãng thở, KHÔNG cần chừa cả nửa màn nữa: cổng trượt giờ kích ngay khi cụm thẻ
     qua giữa màn (xem `canhTrùm` trong site.js) chứ không chờ mép chapter sau hé vào, nên khoảng
     trống này không còn nhiệm vụ điều phối thứ tự. */
  padding-bottom: clamp(64px, 8vh, 110px);
  display: grid;
  place-items: start center;
}
.pdxếp__thẻ {
  position: absolute;
  z-index: var(--z);
  width: var(--pdx-w);
  aspect-ratio: 1;
  display: grid;
  align-content: center;
  gap: 6px;
  padding: 22px;
  border-radius: var(--pd-bo);
  /* Giấy nhớ XANH NHẠT (không phải vàng kem như ref) — cùng họ với xanh brand #0876B7,
     pha nhạt để chữ xanh đậm trên nó vẫn rõ. Hạt mịn vẽ bằng SVG nhúng, không thêm file ảnh. */
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='0.22'/%3E%3C/svg%3E"),
    linear-gradient(155deg, #eef7fe 0%, #d9ecfb 62%, #c7e2f8 100%);
  background-blend-mode: soft-light, normal;
  box-shadow:
    0 14px 30px rgba(8, 60, 96, 0.14),
    0 1px 0 rgba(255, 255, 255, 0.7) inset;
  text-align: center;
  /* `--dx/--dy` là phần cộng thêm lúc người xem KÉO thẻ (site.js ghi vào). Mặc định 0 nên
     không kéo thì công thức y như cũ. */
  translate: calc(var(--i) * var(--pdx-x) * (1 - var(--p)) + var(--dx, 0px)) calc(var(--j) * var(--pdx-cung) * (1 - var(--p)) + var(--dy, 0px));
  rotate: calc(var(--i) * (var(--pdx-xoay) * (1 - var(--p)) + var(--pdx-xoay2) * var(--p)));
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  /* cảm ứng: cuộn dọc vẫn thuộc trang, kéo ngang thuộc thẻ */
  touch-action: pan-y;
  transition:
    translate 0.12s linear,
    rotate 0.12s linear;
}
/* Đang kéo: nhảy lên trên cùng, thẳng lại cho dễ đọc, nhấc nhẹ khỏi mặt giấy.
   KHÔNG transition `translate` để thẻ đi đúng nhịp con trỏ. */
.pdxếp__thẻ.là-kéo {
  z-index: 99;
  cursor: grabbing;
  rotate: 0deg;
  scale: 1.03;
  box-shadow: 0 22px 44px rgba(8, 60, 96, 0.22);
  transition:
    rotate 0.2s ease,
    scale 0.2s ease;
}
/* Nhả chuột: trượt về đúng chỗ cũ */
.pdxếp__thẻ.về-chỗ {
  transition:
    translate 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    rotate 0.42s cubic-bezier(0.22, 1, 0.36, 1),
    scale 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}
.pdxếp__nhãn {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: color-mix(in srgb, #0d4a6b 68%, transparent);
}
.pdxếp__h {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 600;
  line-height: 1.24;
  color: #0b3350;
}
.pdxếp__p {
  font-size: 14px;
  line-height: 1.5;
  color: color-mix(in srgb, #0b3350 76%, transparent);
}
@media (max-width: 1279px) {
  .pdxếp {
    --pdx-w: 250px;
    --pdx-x: 282px;
  }
  .pdxếp__h {
    font-size: 17px;
  }
}
@media (max-width: 1023px) {
  /* iPad DỌC (810–1023): lòng mục 682. Cụm xoè còn phình thêm vì thẻ nghiêng 5° —
     bề rộng hộp bao = 2·dạt + thẻ×1.0834 — nên phải chừa, không thì tràn khỏi mục. */
  .pdxếp {
    --pdx-w: 204px;
    --pdx-x: 222px;
    --pdx-cung: 12px;
  }
  .pdxếp__thẻ {
    padding: 18px;
  }
  .pdxếp__h {
    font-size: 16px;
  }
  .pdxếp__p {
    font-size: 13px;
  }
}
/* Dải 768–809: lòng mục chỉ còn 640 nên thẻ phải nhỏ hơn một bậc nữa */
@media (min-width: 768px) and (max-width: 809px) {
  .pdxếp {
    --pdx-w: 190px;
    --pdx-x: 206px;
  }
}

/* ĐIỆN THOẠI: 294px lòng màn không thể có ba thẻ đọc được nằm cạnh nhau (mỗi thẻ chỉ còn
   ~98px). Nên ở đây bỏ hẳn kiểu xoè–chụm, ba thẻ xếp DỌC thành một cột, đọc trọn chữ —
   đúng ưu tiên "khách phải đọc được". Hiệu ứng xoè–chụm giữ cho ≥768. */
@media (max-width: 767px) {
  .pdxếp {
    --pdx-w: 100%;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
    height: auto;
    /* mobile không có động tác xoè–chụm nên không cần chừa quãng trống */
    padding-bottom: 0;
  }
  .pdxếp__thẻ {
    position: static;
    width: 100%;
    aspect-ratio: auto;
    padding: 18px;
    translate: none;
    rotate: none;
  }
  .pdxếp__p {
    font-size: 13px;
    line-height: 1.5;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pdxếp__thẻ {
    transition: none;
  }
}

/* ---------- dải thông tin dạng định nghĩa ---------- */
.pdmeta {
  margin: 0;
  display: grid;
  gap: clamp(14px, 1.8vw, 24px) clamp(16px, 2vw, 32px);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.pdmeta > div {
  padding-top: 12px;
  border-top: 1px solid var(--pd-line);
}
.pdmeta dt {
  margin-bottom: 6px !important;
}
.pdmeta dd {
  margin: 0;
  font-size: clamp(14px, 1vw, 16px);
  font-weight: 600;
  line-height: 1.35;
}
.pdmeta dd small {
  display: block;
  margin-top: 4px;
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.4;
  color: color-mix(in srgb, var(--pd-mực) 58%, transparent);
}

/* ---------- chuỗi trạng thái ---------- */
.pddòng {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}
.pddòng span {
  padding: 8px 15px;
  border: 1px solid var(--pd-viền);
  /* cùng bo với `.pm__tags li` của modal — cùng một họ "chip chữ" */
  border-radius: var(--pd-bo-nhỏ);
  background: var(--pd-mặt);
  font-size: clamp(13px, 0.95vw, 14px);
  font-weight: 600;
  line-height: 1.3;
}
.pddòng i {
  font-style: normal;
  color: var(--pd-mờ);
}

/* ---------- hành trình mua hàng ---------- */
.pdchặng {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(20px, 3vw, 56px);
  align-items: center;
}
/* SO LE hai hành trình: hành trình 1 ảnh bên trái, hành trình 2 (`lật`) ảnh bên PHẢI.
   Phải kể tên cả `.pdphim` và `.pdclip`: ô ảnh của hành trình giờ có thể là ô ảnh tĩnh (`.pdph`),
   phim thao tác (`.pdphim`) hay clip nhiều thiết bị (`.pdclip`) — bản trước chỉ nhắm `.pdph` nên
   khi hành trình 2 đổi sang clip thì mất luôn thế so le, ảnh tụt về bên trái giống hành trình 1. */
.pdchặng.lật .pdph,
.pdchặng.lật .pdphim,
.pdchặng.lật .pdclip,
.pdchặng.lật .pdvid {
  order: 2;
}
.pdchặng__nhãn {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: var(--pd-mờ);
}
.pdchặng__h {
  margin: 0 0 0.6em;
  font-family: var(--font-display);
  font-size: clamp(19px, 1.8vw, 28px);
  font-weight: 600;
  line-height: 1.24;
  letter-spacing: -0.01em;
}
.pdchặng .pdbước {
  margin-top: clamp(14px, 1.6vw, 22px);
}
.pdchặng + .pdchặng {
  margin-top: clamp(28px, 3.4vw, 56px);
  padding-top: clamp(28px, 3.4vw, 56px);
  border-top: 1px solid var(--pd-line);
}
@media (max-width: 809px) {
  .pdchặng {
    grid-template-columns: minmax(0, 1fr);
  }
  .pdchặng.lật .pdph,
  .pdchặng.lật .pdphim,
  .pdchặng.lật .pdclip,
  .pdchặng.lật .pdvid {
    order: 0;
  }
}

/* ---------- PHIM THAO TÁC (mockup tĩnh + con trỏ chạm) ----------
   Ô ẢNH Y NHƯ MỌI Ô KHÁC: 16:9, bo `--pd-bo`, cỡ ảnh cần upload 1600×900 — chỉ khác là có 5 khung
   thay nhau và một con trỏ chạm do CSS vẽ. Không có chữ mô tả bước, không có vạch chạy (đã bỏ
   theo yêu cầu), nên khung hình sạch đúng như ô ảnh tĩnh.
   Không dùng file video: ô rộng ~593px ở 14" nên video sẽ nặng vài MB mà vẫn mờ trên retina. */
.pdphim {
  margin: 0;
  min-width: 0;
}
.pdphim__sân {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--pd-bo);
  overflow: hidden;
}
/* Ảnh nền của cảnh: cùng khổ canvas với các khung mockup nên chỉ cần phủ kín sân, các khung
   (PNG nền trong suốt) nằm bên trên. */
.pdphim__nền {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* CHUYỂN KHUNG: mềm chứ không "cứng". Hai mẹo:
     · khung cũ mờ NHANH (0.34s) còn khung mới hiện CHẬM và TRỄ 0.18s — nếu cho cả hai cùng
       0.8s thì có một nhịp cả hai đều mờ mờ, mockup nền trong suốt sẽ thành BÓNG ĐÔI;
     · khung mới còn thu nhẹ từ 1.012 về 1 nên có cảm giác "lắng xuống" thay vì bật ra.
   Tổng thời gian chuyển ≈ 1.05s (mờ), phần THU về còn chạy tiếp tới 1.4s — Thu Hà chốt kéo dài
   thêm một nhịp nữa so với bản 0.8s/0.9s. */
.pdphim__khung {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  scale: 1.016;
  transition:
    opacity 0.42s ease-in,
    scale 1.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.pdphim__khung.là-nay {
  opacity: 1;
  scale: 1;
  transition:
    opacity 0.8s ease-out 0.24s,
    scale 1.4s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Con trỏ: chấm tròn có viền sáng, bay tới điểm chạm rồi loé một vòng khi gõ */
.pdphim__trỏ {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border-radius: 50%;
  background: rgba(8, 118, 183, 0.9);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.85);
  opacity: 0;
  scale: 0.7;
  pointer-events: none;
  z-index: 2;
  transition:
    left 0.76s cubic-bezier(0.22, 1, 0.36, 1),
    top 0.76s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.24s ease,
    scale 0.24s ease;
}
.pdphim__trỏ.hiện {
  opacity: 1;
  scale: 1;
}
.pdphim__trỏ.gõ {
  scale: 0.82;
}
.pdphim__trỏ.gõ::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 2px solid rgba(8, 118, 183, 0.55);
  animation: pdphimLoé 0.6s ease-out forwards;
}
@keyframes pdphimLoé {
  from {
    scale: 0.6;
    opacity: 0.9;
  }
  to {
    scale: 2.2;
    opacity: 0;
  }
}
/* Nhãn TRÊN ô phim — dùng đúng công thức của `.pdph__nhãn` (nhãn trên ô ảnh) */
.pdphim__nhãn {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  line-height: 1.4;
  color: var(--pd-mờ);
}
@media (max-width: 767px) {
  .pdphim__nhãn {
    margin-bottom: 8px;
    font-size: 12px;
  }
}

/* Ghi chú dưới ô phim — ĐẶT NGOÀI khung, không đè lên hình (theo rule đã chốt) */
.pdphim__cap {
  margin-top: 10px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  line-height: 1.4;
  color: var(--pd-mờ);
}
@media (max-width: 767px) {
  .pdphim__cap {
    margin-top: 8px;
    font-size: 12px;
  }
}

/* ---------- THANH TIẾN ĐỘ Ở MÉP DƯỚI KHUNG ----------
   Chạy hết đúng bằng thời lượng khung đang chiếu (JS nạp `width` + `transition`), nên người
   xem biết còn bao lâu là sang khung sau. Nền thanh phải hơi sáng: ảnh trong phim có cả nền
   sáng và nền tối, thanh tối sẽ mất hút trên ảnh tối.
   QUAY LẠI ĐẦU VÒNG: thanh RÚT NGƯỢC về 0 (0.5s) thay vì chạy tiến, và khung đầu MỞ RA
   (0.984 → 1) ngược chiều các bước thường — hai dấu hiệu này cho biết vòng mới bắt đầu. */
.pdphim__tiến {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  height: 3px;
  pointer-events: none;
  display: flex;
  gap: 3px;
}
/* MỘT ĐOẠN cho MỘT khung: đoạn đã qua đầy, đoạn đang chiếu đang chạy, đoạn chưa tới rỗng —
   nên nhìn thanh là biết cả "còn bao lâu" và "khung mấy trên mấy".
   Nền đoạn phải hơi sáng: ảnh trong phim có cả nền sáng và nền tối, đoạn tối sẽ mất hút trên
   ảnh tối. */
.pdphim__tiến > span {
  flex: 1;
  min-width: 0;
  height: 100%;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.42);
  box-shadow: inset 0 0 0 100px rgba(4, 32, 48, 0.18);
}
.pdphim__tiến > span > i {
  display: block;
  width: 0;
  height: 100%;
  background: #0876b7;
}

/* Phim KHÔNG có thanh tiến độ: dấu hiệu quay lại đầu vòng là một VỆT SÁNG quét ngang. */
.pdphim__lật {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.12) 38%,
    rgba(255, 255, 255, 0.62) 50%,
    rgba(255, 255, 255, 0.12) 62%,
    transparent 100%
  );
  background-size: 240% 100%;
  background-position: -70% 0;
}
.pdphim__lật.chạy {
  animation: pdphimQuét 0.9s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes pdphimQuét {
  0% {
    opacity: 0;
    background-position: -70% 0;
  }
  22% {
    opacity: 1;
  }
  78% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    background-position: 170% 0;
  }
}

/* Khung đầu của vòng mới: đi lên từ 0.984 (mở ra) thay vì thu về, và chậm hơn một nhịp */
.pdphim__sân.quay-lại .pdphim__khung {
  scale: 0.984;
}
.pdphim__sân.quay-lại .pdphim__khung.là-nay {
  scale: 1;
  transition:
    opacity 0.95s ease-out 0.3s,
    scale 1.7s cubic-bezier(0.22, 1, 0.36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .pdphim__tiến > span > i {
    transition: none !important;
  }
  .pdphim__lật.chạy {
    animation: none;
  }
  .pdphim__sân.quay-lại .pdphim__khung,
  .pdphim__sân.quay-lại .pdphim__khung.là-nay {
    scale: 1;
    transition: none;
  }
}

/* Vòng xoay ở khung "đang xử lý" — đặt đúng tâm màn máy (máy nằm giữa canvas nên tâm sân cũng là
   tâm màn). Chỉ có ICON, không kèm chữ: ở khổ 16:9 màn máy chỉ rộng ~130px, nhồi chữ vào là
   không đọc được. */
.pdphim__tải {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  z-index: 1;
  display: grid;
  place-items: center;
  padding: 9px;
  border-radius: var(--pd-bo-vừa);
  background: rgba(255, 255, 255, 0.88);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: 0 6px 16px rgba(2, 32, 48, 0.16);
  opacity: 0;
  scale: 0.94;
  pointer-events: none;
  transition:
    opacity 0.3s ease,
    scale 0.3s ease;
}
.pdphim__sân.đang-tải .pdphim__tải {
  opacity: 1;
  scale: 1;
}
.pdphim__tải i {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2.5px solid rgba(8, 118, 183, 0.22);
  border-top-color: #0876b7;
  animation: pdphimXoay 0.8s linear infinite;
}
@keyframes pdphimXoay {
  to {
    rotate: 360deg;
  }
}
@media (max-width: 1023px) {
  .pdphim__trỏ {
    width: 15px;
    height: 15px;
    margin: -7.5px 0 0 -7.5px;
  }
  .pdphim__tải {
    padding: 7px;
  }
  .pdphim__tải i {
    width: 15px;
    height: 15px;
    border-width: 2px;
  }
}
@media (max-width: 767px) {
  .pdphim__trỏ {
    width: 13px;
    height: 13px;
    margin: -6.5px 0 0 -6.5px;
  }
  .pdphim__tiến {
    height: 2px;
    gap: 2px;
  }
  .pdphim__tải i {
    width: 13px;
    height: 13px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pdphim__khung,
  .pdphim__khung.là-nay {
    scale: 1;
    transition: none;
  }
  .pdphim__tải i {
    animation: none;
  }
}

/* ---------- VIDEO trong ô ảnh của khối "chặng" ----------
   Ô Y NHƯ MỌI Ô ẢNH KHÁC: 16:9, bo `--pd-bo`, nên hai hành trình vẫn cùng một khổ và vẫn so le.
   `object-fit: cover` để video 1280×720 phủ kín ô, không viền đen. */
.pdvid {
  margin: 0;
  min-width: 0;
}
.pdvid__v {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--pd-bo);
  background: color-mix(in oklab, var(--pd-mực) 6%, transparent);
}

/* ---------- CLIP NHIỀU THIẾT BỊ (điện thoại → iPad → TV) ----------
   Khung thiết bị và phông cảnh đều VẼ BẰNG CSS, không dùng ảnh mockup ngoài: máy này không có
   công cụ sinh ảnh, mà lấy mockup trên mạng thì vướng bản quyền. Bù lại nét ở mọi khổ và không
   thêm một byte ảnh nào.
   Ô vẫn 16:9 · bo `--pd-bo` như mọi ô ảnh khác. */
.pdclip {
  margin: 0;
  min-width: 0;
}
.pdclip__sân {
  position: relative;
  aspect-ratio: 16 / 9;
  display: grid;
  place-items: center;
  border-radius: var(--pd-bo);
  overflow: hidden;
}
/* Phông cảnh: cùng họ màu với bg05.jpg của hành trình 1 */
.pdclip__nền {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 18% 12%, rgba(255, 255, 255, 0.55), transparent 60%),
    radial-gradient(90% 70% at 82% 88%, rgba(255, 255, 255, 0.38), transparent 62%),
    linear-gradient(160deg, #dceaf6 0%, #c9e0f2 46%, #bcd8ee 100%);
}
/* Mỗi bước là một thiết bị; chỉ thiết bị của bước đang chiếu là hiện */
/* Cùng nhịp mềm với khối phim: máy cũ mờ nhanh, máy mới hiện chậm và trễ một nhịp */
.pdclip__máy {
  position: absolute;
  display: grid;
  place-items: center;
  opacity: 0;
  scale: 0.982;
  transition:
    opacity 0.42s ease-in,
    scale 1.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.pdclip__máy.là-nay {
  opacity: 1;
  scale: 1;
  transition:
    opacity 0.8s ease-out 0.24s,
    scale 1.4s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Vỏ máy: viền tối, bo góc, bên trong là ảnh chụp màn */
.pdclip__viền {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  background: #16191d;
  box-shadow:
    0 10px 26px rgba(2, 32, 48, 0.24),
    inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.pdclip__viền img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* --- điện thoại --- */
.pdclip__máy--đt {
  height: 88%;
  aspect-ratio: 9 / 19.5;
}
.pdclip__máy--đt .pdclip__viền {
  padding: 3.4%;
  border-radius: 26px;
}
.pdclip__máy--đt .pdclip__viền img {
  border-radius: 18px;
}
/* tai thỏ */
.pdclip__máy--đt .pdclip__viền::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 2.2%;
  translate: -50% 0;
  width: 26%;
  height: 1.5%;
  min-height: 4px;
  border-radius: 999px;
  background: #0d0f12;
  z-index: 2;
}
/* --- iPad --- */
.pdclip__máy--ipad {
  width: 70%;
  aspect-ratio: 3582 / 2502;
}
.pdclip__máy--ipad .pdclip__viền {
  padding: 1.9%;
  border-radius: 16px;
}
.pdclip__máy--ipad .pdclip__viền img {
  border-radius: 8px;
}
/* --- TV: viền mảnh + chân đế --- */
/* TV chốt theo CHIỀU CAO, không theo bề rộng: ô chỉ cao 334 ở 1512 mà 84% bề rộng ứng với 351
   chiều cao — TV bị cắt mất mép trên/dưới và mất luôn chân đế. Lấy 78% chiều cao thì cả máy và
   chân đế (7% nữa) đều lọt. */
.pdclip__máy--tv {
  height: 78%;
  width: auto;
  aspect-ratio: 3582 / 2502;
  margin-top: -3%;
}
.pdclip__máy--tv .pdclip__viền {
  padding: 1.1%;
  border-radius: 8px;
}
.pdclip__máy--tv .pdclip__viền img {
  border-radius: 3px;
}
.pdclip__máy--tv .pdclip__viền::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -7%;
  translate: -50% 0;
  width: 26%;
  height: 7%;
  background: #16191d;
  clip-path: polygon(18% 0, 82% 0, 100% 100%, 0 100%);
}
/* Vạch quét QR ở bước đầu */
.pdclip__quét {
  position: absolute;
  left: 6%;
  right: 6%;
  top: 18%;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, #0876b7, transparent);
  box-shadow: 0 0 12px rgba(8, 118, 183, 0.7);
  animation: pdclipQuét 1.9s ease-in-out infinite;
}
@keyframes pdclipQuét {
  0%,
  100% {
    top: 22%;
    opacity: 0.15;
  }
  50% {
    top: 70%;
    opacity: 1;
  }
}
/* Con trỏ chạm — nằm trong màn của chính thiết bị nên toạ độ % là theo màn đó */
.pdclip__trỏ {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 50%;
  background: rgba(8, 118, 183, 0.92);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.85);
  opacity: 0;
  scale: 0.7;
  z-index: 3;
  pointer-events: none;
  transition:
    left 0.74s cubic-bezier(0.22, 1, 0.36, 1),
    top 0.74s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.24s ease,
    scale 0.24s ease;
}
.pdclip__trỏ.hiện {
  opacity: 1;
  scale: 1;
}
.pdclip__trỏ.gõ {
  scale: 0.82;
}
.pdclip__trỏ.gõ::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 2px solid rgba(8, 118, 183, 0.55);
  animation: pdphimLoé 0.6s ease-out forwards;
}
@media (max-width: 1023px) {
  .pdclip__trỏ {
    width: 13px;
    height: 13px;
    margin: -6.5px 0 0 -6.5px;
  }
  .pdclip__máy--đt .pdclip__viền {
    border-radius: 18px;
  }
  .pdclip__máy--đt .pdclip__viền img {
    border-radius: 12px;
  }
}
@media (max-width: 767px) {
  .pdclip__trỏ {
    width: 11px;
    height: 11px;
    margin: -5.5px 0 0 -5.5px;
  }
  .pdclip__máy--ipad,
  .pdclip__máy--tv {
    width: 88%;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pdclip__máy {
    transition: none;
  }
  .pdclip__quét {
    animation: none;
  }
}

/* ---------- trade-off ---------- */
/* Nền và kẻ trái đọc từ biến để từng khối đổi được màu (khai `nền`/`kẻ` trong data); không khai
   thì rơi về xanh brand như bản gốc. */
.pdđổi {
  margin: 0;
  padding: 14px 18px;
  border-left: 2px solid var(--pd-đổi-kẻ, #0876b7);
  border-radius: 0 var(--pd-bo-vừa) var(--pd-bo-vừa) 0;
  background: var(--pd-đổi-nền, rgba(8, 118, 183, 0.05));
  font-size: clamp(14px, 1vw, 15px);
  /* CẢ KHỐI đậm hơn thân bài thường (400): nhãn 600, phần mô tả 500 — đây là ghi chú đánh đổi,
     cần nổi hơn đoạn văn quanh nó. */
  font-weight: 500;
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 82%, transparent);
}
.pdđổi b {
  display: block;
  margin-bottom: 6px;
  font-size: 12px;
  /* semi-bold: nhãn này là chữ nhỏ, in hoa, giãn 0.1em nên ở weight 500 nhìn mảnh quá */
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* Khai `kẻ` màu riêng thì nhãn ăn theo màu vạch kẻ — nhãn xanh cạnh vạch cam trên nền hồng
     trông như lỗi. Không khai gì thì vẫn là xanh mặc định. */
  color: var(--pd-đổi-kẻ, #0876b7);
}

/* `tối: true` — bản NỀN TỐI của khối `đổi` (ref-chapter5.png): nhãn nằm một cột bên trái, câu
   chốt chữ trắng to hơn ở cột phải, vạch kẻ bên trái dày 4px. */
.pdđổi.là-tối {
  display: grid;
  grid-template-columns: minmax(0, 190px) minmax(0, 1fr);
  gap: clamp(10px, 1.6vw, 28px);
  align-items: center;
  padding: clamp(18px, 2vw, 30px) clamp(20px, 2.4vw, 40px);
  border-left-width: 4px;
  border-radius: 0 var(--pd-bo) var(--pd-bo) 0;
  color: #fff;
}
.pdđổi.là-tối b {
  margin: 0;
}
.pdđổi.là-tối span {
  font-size: clamp(15px, 1.3vw, 20px);
  font-weight: 600;
  line-height: 1.4;
}
@media (max-width: 767px) {
  .pdđổi.là-tối {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }
}

/* ---------- KHỐI `hệMàu` — bảng màu Design System trên tấm nền tối (ref-chapter5.png) ----------
   Hàng ô grade: các ô SÁT NHAU (không khe), cả hàng bo góc nhờ `overflow: hidden` ở khay. Hai hàng
   dưới là thẻ viền mảnh, chữ ăn đúng màu token — nền tối nên màu token đọc được mà không cần ô
   màu riêng. */
.pdhệ {
  padding: clamp(16px, 1.8vw, 28px);
  border-radius: var(--pd-bo);
  background: var(--pdh-nền, #080808);
  display: grid;
  gap: clamp(10px, 1.2vw, 18px);
}
.pdhệ__grade {
  display: grid;
  grid-template-columns: repeat(var(--pdh-n, 6), minmax(0, 1fr));
  border-radius: var(--pd-bo-nhỏ);
  overflow: hidden;
}
.pdhệ__ô {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(22px, 3.4vw, 44px);
  padding: clamp(12px, 1.4vw, 20px);
  background: var(--pdh-ô, #333);
  color: #fff;
}
.pdhệ__ô b {
  font-family: var(--font-display);
  font-size: clamp(15px, 1.3vw, 20px);
  font-weight: 700;
  line-height: 1;
}
.pdhệ__ô span {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.88);
}
.pdhệ__hàng {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(var(--pdh-n, 4), minmax(0, 1fr));
  gap: clamp(8px, 1vw, 14px);
}
.pdhệ__hàng[data-n="6"],
.pdhệ__grade[data-n="6"] {
  --pdh-n: 6;
}
.pdhệ__hàng[data-n="4"] {
  --pdh-n: 4;
}
.pdhệ__hàng li {
  padding: clamp(12px, 1.3vw, 18px);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--pd-bo-nhỏ);
  font-size: clamp(13px, 0.95vw, 14px);
  line-height: 1.4;
  color: var(--pdh-chữ, #fff);
}
@media (max-width: 1023px) {
  .pdhệ__grade[data-n="6"] {
    --pdh-n: 3;
  }
  .pdhệ__hàng[data-n="4"] {
    --pdh-n: 2;
  }
}
@media (max-width: 429px) {
  .pdhệ__grade[data-n="6"] {
    --pdh-n: 2;
  }
}

/* ---------- khối "How might we" ---------- */
.pdhỏi {
  padding: clamp(24px, 3vw, 48px);
  border-radius: var(--pd-bo-lớn);
  background: var(--pd-thẻ);
}
.pdhỏi__nhãn {
  margin: 0 0 16px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: var(--pd-mờ);
}
.pdhỏi__h {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(17px, 1.4vw, 22px);
  font-weight: 500;
  line-height: 1.3;
  color: color-mix(in srgb, var(--pd-mực) 60%, transparent);
}
/* ---------- bản CÓ ẢNH NỀN của khối "How might we" ----------
   Khai `nền` trong data là bộ dựng thêm lớp này và truyền url qua `--pd-hỏi-nền`. Nền ảnh
   tối/đậm nên cả ba dòng chữ đổi sang tông sáng. Dự án không khai `nền` thì vẫn là thẻ kem
   chữ tối như cũ. */
.pdhỏi--ảnh {
  background-image: var(--pd-hỏi-nền);
  background-size: cover;
  background-position: 50% 50%;
  background-repeat: no-repeat;
}
.pdhỏi--ảnh .pdhỏi__nhãn {
  color: rgba(255, 255, 255, 0.72);
}
.pdhỏi--ảnh .pdhỏi__h {
  color: rgba(255, 255, 255, 0.78);
}
/* Câu hỏi lớn: gradient TRẮNG → XANH NHẠT (bản nền kem thì là gradient xanh đậm) */
.pdhỏi--ảnh .pdhỏi__p {
  background-image: linear-gradient(90deg, #ffffff, #a6dbfb);
}

/* ---------- BẢN NỀN MỘT MÀU + ẢNH BÊN PHẢI (Msky OTT) ----------
   Nền một màu khai trong data (`màu`), ảnh khai bằng `ảnh`. Chữ nằm BÊN TRÁI và căn giữa theo
   chiều dọc, ảnh bên phải CAO BẰNG CẢ KHỐI — nên khối bỏ `padding` bên phải và ảnh tự phủ hết
   cột của nó (`align-self: stretch` + `object-fit: cover`).
   `overflow: hidden` để ảnh không thò ra ngoài bo góc. */
.pdhỏi--màu {
  background: var(--pd-hỏi-màu, #101318);
}
.pdhỏi--màu .pdhỏi__nhãn {
  color: rgba(255, 255, 255, 0.72);
}
.pdhỏi--màu .pdhỏi__h {
  color: rgba(255, 255, 255, 0.78);
}
.pdhỏi--màu .pdhỏi__p {
  background-image: linear-gradient(90deg, #ffffff, #a6dbfb);
}
.pdhỏi--đôi {
  display: grid;
  /* Cột ảnh chỉ ~1/4 khối: cột chữ rộng ra thì câu hỏi lớn xuống còn 3 dòng ở 1512
     (0.82fr trước đây cho cột chữ 652 → câu hỏi 4 dòng). */
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.35fr);
  align-items: center;
  column-gap: clamp(24px, 3vw, 56px);
  /* ĐỆM VỀ 0 và chuyển hết sang cột chữ. Để đệm ở khối thì ảnh chỉ cao bằng HỘP NỘI DUNG
     (đo được 250 trong khối 340) — muốn ảnh cao đúng bằng khối thì cột ảnh không được có đệm
     trên/dưới nào. */
  padding: 0;
  overflow: hidden;
}
.pdhỏi--đôi .pdhỏi__chữ {
  min-width: 0;
  /* Đệm DỌC rút gọn (28 ở 1512 thay vì 45): khối này chỉ có ba dòng chữ, để đệm như thẻ nội
     dung thường thì trên/dưới hở cả 45px mỗi bên. Đệm NGANG giữ nguyên nhịp cũ. */
  padding: clamp(18px, 1.9vw, 30px) clamp(24px, 3vw, 48px);
}
/* Ô chứa ảnh: ảnh đặt TUYỆT ĐỐI trong ô nên KHÔNG còn quyết định chiều cao khối. Trước đây ảnh
   là grid item thường: ở 810 chính tỉ lệ gốc của nó (1421×1620) kéo khối cao 338 dù chữ chỉ 221.
   Nay chiều cao khối = chữ + đệm ở mọi khổ, ảnh cắt theo (`cover`). */
.pdhỏi__ô {
  position: relative;
  align-self: stretch;
  min-height: 0;
  overflow: hidden;
}
/* Câu hỏi lớn ở bản này không cần chặn 46ch: cột chữ đã hẹp hơn hẳn */
.pdhỏi--đôi .pdhỏi__p {
  max-width: none;
}
.pdhỏi__ảnh {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  display: block;
}
/* Khổ hẹp: xếp dọc — chữ trên, ảnh dưới. Giữ 16:9 cho ảnh để không chiếm cả màn. */
/* Khổ hẹp: xếp dọc — chữ trên, ảnh dưới. Lúc này đệm trả về cho KHỐI (ảnh không còn phải cao
   bằng khối nữa) và ảnh giữ 16:9 để không chiếm cả màn. */
@media (max-width: 809px) {
  .pdhỏi--đôi {
    grid-template-columns: minmax(0, 1fr);
    row-gap: clamp(18px, 3vw, 28px);
    padding: clamp(24px, 3vw, 48px);
  }
  .pdhỏi--đôi .pdhỏi__chữ {
    padding: 0;
  }
  .pdhỏi__ô {
    aspect-ratio: 16 / 9;
    border-radius: var(--pd-bo);
  }
}

/* Câu hỏi lớn: cùng gradient xanh đã chốt */
.pdhỏi__p {
  margin: 0.35em 0 0;
  /* Rộng hơn 32ch của bản đầu: khối chiếm hết bề ngang mà chữ chỉ nằm trong 60% bên trái
     thì đọc ra "co cụm một góc". 46ch cho câu hỏi dàn ra ~78% khối mà dòng vẫn trong tầm
     đọc. `text-wrap: balance` để các dòng đều nhau, không còn dòng cuối lẻ một hai chữ. */
  max-width: 46ch;
  text-wrap: balance;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.4vw, 36px);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  background: linear-gradient(90deg, #014268, #0d9bee);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  padding-block: 0.18em;
  margin-block: calc(0.35em - 0.18em) -0.18em;
}

/* ---------- HỘP CÓ ẢNH NỀN, trong hộp xếp HAI CỘT như một mục con ----------
   Nền: cùng họ với khối "How might we" bản ảnh (`.pdhỏi--ảnh`) — cùng đệm, cùng bo
   `--pd-bo-lớn`, ảnh `cover`.
   Bố cục và thang chữ bên trong: ĐÚNG như mục con thường (ví dụ "06 · Key design decisions") —
   hai cột đều nhau, khe `clamp(28, 5vw, 96)`, trái là nhãn + tiêu đề, phải là thân bài; ≤1023
   dồn về một cột. Nhãn 13/12 · tiêu đề `clamp(22, 2.5vw, 38)` · thân bài 15, y như
   `.pds__no` / `.pds__h` / `.pds__p`, chỉ đổi màu cho nền tối.
   Có LỚP PHỦ TỐI phía trên ảnh: ảnh nền sẽ còn thay (Thu Hà upload sau) nên không thể trông
   vào độ tối của riêng ảnh; lớp phủ này giữ chữ trắng luôn đọc được với mọi ảnh. */
.pdhn {
  padding: clamp(24px, 3vw, 48px);
  border-radius: var(--pd-bo-lớn);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: clamp(28px, 5vw, 96px);
  align-items: start;
  background-image: linear-gradient(
      180deg,
      rgba(2, 26, 40, 0.62),
      rgba(2, 26, 40, 0.44) 55%,
      rgba(2, 26, 40, 0.62)
    ),
    var(--pd-hn-nền);
  background-size: cover;
  background-position: 50% 50%;
  background-repeat: no-repeat;
}
/* Nhãn NẰM NGANG HẾT HỘP rồi mới tới hai cột — y như `.pds__no` ở mục con thường, nên dòng
   đầu của thân bài bên phải thẳng với TIÊU ĐỀ chứ không thẳng với nhãn. */
.pdhn__nhãn {
  grid-column: 1 / -1;
  margin: 0 0 clamp(16px, 2vw, 28px);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: var(--pd-nhãn-giãn);
  color: rgba(255, 255, 255, 0.66);
}
.pdhn__h {
  grid-column: 1;
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.5vw, 38px);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: #fff;
}
.pdhn__phải {
  grid-column: 2;
}
.pdhn__p {
  margin: 0 0 1em;
  font-size: 15px;
  line-height: 1.62;
  color: rgba(255, 255, 255, 0.84);
}
.pdhn__p:last-child {
  margin-bottom: 0;
}
@media (max-width: 1023px) {
  .pdhn {
    grid-template-columns: minmax(0, 1fr);
  }
  .pdhn__h,
  .pdhn__phải {
    grid-column: 1;
  }
  .pdhn__h {
    margin-bottom: clamp(16px, 2.4vw, 24px);
  }
}
@media (max-width: 767px) {
  .pdhn {
    padding: 20px;
  }
  .pdhn__nhãn {
    font-size: 12px;
  }
}

/* ---------- Ô ẢNH TỰ CAO THEO ẢNH (ảnh khai `tựCao`) ----------
   Không khoá 16:9: ô cao đúng theo tỉ lệ gốc của ảnh, và ảnh KHÔNG bị phóng quá cỡ gốc (tránh
   ảnh 715px rộng bị kéo lên 1232 rồi nhoè). Xuất lại ảnh thấp hơn thì ô tự thấp theo. */
.pdph--tự .pdph__ô {
  aspect-ratio: auto;
  height: auto;
  padding: 0;
  display: block;
  text-align: center;
}
/* Ảnh RỘNG HẾT khổ nội dung, cao theo tỉ lệ gốc (Thu Hà chốt: giữ 2560px rộng cho nét). Ảnh
   nguồn phải rộng ≥ 2× hộp (2464 ở 1512) mới nét; nguồn hẹp hơn hộp sẽ bị phóng lên và mềm. */
.pdph--tự .pdph__ô img {
  position: static;
  width: 100%;
  max-width: none;
  height: auto;
  margin: 0;
  object-fit: contain;
}

/* ---------- ẢNH TỰ CAO + CUỘN NGANG: hiện ở CỠ GỐC ở khổ hẹp ----------
   Ảnh dọc nhiều chữ nhỏ (sơ đồ IA, bảng so sánh nền tảng) mà co xuống 342px thì chữ nhoè hẳn:
   ảnh 1166px rộng phải thu 3.4×. Nên từ 1023 xuống: hiện đúng CỠ GỐC (1 CSS px = 1 px ảnh, nét
   nhất có thể) rồi CUỘN NGANG phần tràn. Cao vẫn theo tỉ lệ gốc nên không cắt mất phần nào. */
@media (max-width: 1023px) {
  .pdph--tự.pdph--cuộn .pdph__ô {
    height: auto;
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }
  .pdph--tự.pdph--cuộn .pdph__ô img {
    flex: none;
    width: auto;
    max-width: none;
    height: auto;
    margin: 0;
  }
}

/* ---------- BẤM ẢNH ĐỂ XEM TOÀN MÀN (ảnh khai `xem: true`) ----------
   Nút bọc ảnh: trong suốt hoàn toàn, chỉ đổi con trỏ và hiện cái kính lúp ở góc — ô ảnh trông
   y như trước, không thêm viền hay nền. `display: contents` KHÔNG dùng được: nút cần là hộp
   để đặt kính lúp theo nó, và Safari không cho `contents` trên `button`. */
.pdph__mở {
  display: block;
  position: relative;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  -webkit-appearance: none;
  appearance: none;
  border-radius: inherit;
}
.pdph--tự .pdph__mở img {
  display: block;
}
.pdph__lúp {
  position: absolute;
  right: clamp(10px, 1vw, 16px);
  bottom: clamp(10px, 1vw, 16px);
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--pd-bo-vừa);
  background: rgba(12, 24, 34, 0.42);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
  color: #fff;
  opacity: 0;
  transition:
    opacity 0.25s ease,
    transform 0.25s ease;
}
.pdph__lúp svg {
  width: 18px;
  height: 18px;
}
.pdph__mở:hover .pdph__lúp,
.pdph__mở:focus-visible .pdph__lúp {
  opacity: 1;
}
/* Trên máy cảm ứng không có hover nên luôn hiện — không thì không ai biết bấm được. */
@media (hover: none) {
  .pdph__lúp {
    opacity: 1;
  }
}

/* ---------- KHUNG XEM ẢNH TOÀN MÀN ----------
   z-index 9600: trên dock (60), panel menu (70) và bộ lướt (9500), dưới màn chờ (9000 thì thấp
   hơn — màn chờ đã tắt lúc người xem bấm được ảnh) và con trỏ mắt (9999). */
.pdxem {
  position: fixed;
  z-index: 9600;
  inset: 0;
  /* MỘT hàng MỘT cột cỡ `1fr`, KHÔNG phải `place-items: center`: với `place-items: center` thì ô
     lưới lấy cỡ theo NỘI DUNG, nên `max-height: 100%` của tấm ảnh lại đo theo chính chiều cao
     ảnh — ảnh cao 3078px mở ra là hiện gần nguyên cỡ gốc, tràn hết màn và không kéo được (Thu Hà
     báo: "ấn vào kích thước ban đầu rất to"). `1fr` cho ô một chiều cao xác định = màn hình. */
  display: grid;
  grid-template: 1fr / 1fr;
  --pdxem-lề: clamp(16px, 3vw, 56px);
  padding: var(--pdxem-lề);
  background: rgba(6, 10, 14, 0.86);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  animation: pdxem-vào 0.22s ease both;
}
.pdxem[hidden] {
  display: none;
}
@keyframes pdxem-vào {
  from {
    opacity: 0;
  }
}
.pdxem__sân {
  /* `grid-template: 1fr / 1fr` là BẮT BUỘC, không chỉ `place-items: center`: hàng ngầm của lưới
     tự co theo NỘI DUNG, nên `max-height: 100%` của ảnh đo theo chính chiều cao ảnh (vòng tròn)
     → ảnh cao 3078px mở ra vẫn 1709px, tràn khỏi sân và bị `overflow: hidden` cắt mất. Cho hàng
     `1fr` thì ô lưới có chiều cao xác định = chiều cao sân, phần trăm mới tính được. */
  display: grid;
  grid-template: 1fr / 1fr;
  place-items: center;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  touch-action: none; /* để tự lo kéo + chụm, không bị trình duyệt cuộn tranh mất */
}
/* Ảnh nền TRONG SUỐT mở trong khung xem: lót nền trắng có chấm xám (cùng tấm `khung` ở chapter
   2) — không lót thì chữ tối của sơ đồ nằm trên lớp phủ đen, không đọc được gì. */
.pdxem__sân img.là-chấm {
  padding: clamp(10px, 1.4vw, 20px);
  background-color: #fff;
  background-image: radial-gradient(rgba(17, 19, 21, 0.13) 1.1px, transparent 1.2px);
  background-size: 18px 18px;
}
.pdxem__sân img {
  max-width: 100%;
  /* Chặn theo CHIỀU CAO MÀN, không dùng `max-height: 100%`: phần trăm đó Chrome không rút gọn
     được trong lưới này (đo được ảnh cao 3078px vẫn ra 1709px trong sân cao 809), nên tính thẳng
     bằng `dvh` trừ lề của lớp phủ. `100vh` là bản lùi cho trình duyệt chưa có `dvh`. */
  max-height: calc(100vh - var(--pdxem-lề, 24px) * 2);
  max-height: calc(100dvh - var(--pdxem-lề, 24px) * 2);
  -webkit-user-drag: none;
  user-select: none;
  border-radius: var(--pd-bo);
  object-fit: contain;
  transition: scale 0.22s ease;
}
.pdxem.đang-kéo .pdxem__sân img {
  transition: none;
}
.pdxem.là-phóng .pdxem__sân {
  cursor: grab;
}
.pdxem.đang-kéo .pdxem__sân {
  cursor: grabbing;
}
.pdxem__đóng {
  position: absolute;
  top: clamp(12px, 1.6vw, 24px);
  right: clamp(12px, 1.6vw, 24px);
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--pd-bo-vừa);
  background: rgba(12, 24, 34, 0.42);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
  color: #fff;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.pdxem__đóng svg {
  width: 20px;
  height: 20px;
}
.pdxem__đóng:hover {
  background: rgba(255, 255, 255, 0.16);
}
/* Cụm phóng/thu: dùng LẠI y nguyên bộ đo của cụm zoom ở mục 08 để hai chỗ nhìn như một. */
.pdxem__zoom {
  position: absolute;
  right: clamp(12px, 1.6vw, 24px);
  bottom: clamp(12px, 1.6vw, 24px);
  display: flex;
  align-items: center;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--pd-bo-vừa);
  background: rgba(12, 24, 34, 0.42);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
  overflow: hidden;
}
.pdxem__zoom button {
  width: 44px;
  height: 40px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: #fff;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition:
    background-color 0.25s ease,
    opacity 0.25s ease;
}
.pdxem__zoom button + button {
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.18);
}
.pdxem__zoom button svg {
  width: 20px;
  height: 20px;
}
.pdxem__zoom button:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.14);
}
.pdxem__zoom button:disabled {
  opacity: 0.4;
  cursor: default;
}
/* KHOÁ CUỘN lúc đang xem ảnh: `position: fixed` + `top` âm giữ đúng chỗ đang đứng (JS đặt
   `top`), khác `overflow: hidden` là trang này còn có cổng cuộn tự trượt phía sau. */
body.đang-xem-ảnh {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden;
}

/* ---------- CHỈ MOBILE mới cuộn ngang (ảnh khai `cuộnM`) ----------
   Khác `cuộnNgang` hai điểm:
     · mốc là 767 chứ không phải 1023 — ảnh ở tablet co lại vẫn đọc được (đo dải Design process
       2560×871: 810 → 682px còn đọc, 390 → 342px thì chữ nhỏ hẳn);
     · KHÔNG hiện cỡ gốc mà THEO CHIỀU CAO CỦA Ô. Ảnh dải ngang 2560×871 nếu hiện cỡ gốc thì cao
       871px trên màn cao 844 — người xem chỉ thấy một góc đen, phải cuộn cả dọc lẫn ngang (đo
       được). Cho ô cao `min(58vh, 440px)` rồi ảnh cao 100% và rộng tự nhiên: chữ to gấp ~3.7 lần
       bản co vừa khổ, mà vẫn nằm trong một màn.
   Ảnh phải neo mép TRÁI bằng flex, không thì phần tràn bên trái không cuộn tới được — cùng cái
   bẫy đã gặp ở `cuộnNgang`. */
@media (max-width: 767px) {
  .pdph--cuộnm .pdph__ô,
  .pdph--tự.pdph--cuộnm .pdph__ô {
    aspect-ratio: auto;
    height: min(58vh, 440px);
    padding: 0;
    display: flex;
    justify-content: flex-start;
    align-items: stretch;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }
  .pdph--cuộnm .pdph__ô img,
  .pdph--tự.pdph--cuộnm .pdph__ô img {
    position: static;
    flex: none;
    width: auto;
    max-width: none;
    height: 100%;
    margin: 0;
    object-fit: contain;
  }
}

/* ---------- Ô ẢNH CUỘN NGANG Ở KHỔ HẸP (ảnh khai `cuộnNgang`) ----------
   Sơ đồ nhiều cột chữ nhỏ mà co cho vừa 342px thì chữ còn 2–3px. Nên từ 1023 xuống: GIỮ tỉ lệ
   gốc của ảnh, cho ô CAO hơn (68vh, trần 520) rồi để ảnh rộng tự nhiên và CUỘN NGANG trong ô.
   Desktop không đổi gì — vẫn 16:9 vừa khổ nội dung. */
@media (max-width: 1023px) {
  /* PHẢI neo ảnh về mép TRÁI. Ô này vốn là grid `justify-items: center`; ảnh rộng hơn ô thì bị
     căn giữa và phần tràn bên TRÁI không cuộn tới được (đo: cần cuộn 242 nhưng `scrollWidth`
     chỉ cho 121, ảnh nằm ở offsetLeft −121). Dùng flex + `justify-content: flex-start` là cách
     duy nhất chắc chắn: mọi phần tràn đều nằm bên phải nên cuộn tới được hết. */
  .pdph--cuộn .pdph__ô {
    aspect-ratio: auto;
    height: min(68vh, 520px);
    padding: 0;
    display: flex;
    justify-content: flex-start;
    align-items: stretch;
    text-align: left;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }
  .pdph--cuộn .pdph__ô img {
    flex: none;
    width: auto;
    max-width: none;
    height: 100%;
    margin: 0;
    object-fit: contain;
  }
}

/* ---------- THẺ TỐI CÓ VÒNG PHẦN TRĂM + BẢNG SO SÁNH (ref-chapter3.png) ----------
   Cả hai khối nằm trong THẺ TRẮNG như `cặp` / `mốc` / `cột`, tiêu đề cùng màu xanh lơ. */
.pdvòng,
.pdbảng {
  padding: clamp(20px, 2.4vw, 40px);
  border: 1px solid var(--pd-viền);
  border-radius: var(--pd-bo);
  background: var(--pd-mặt);
}
.pdvòng__h,
.pdbảng__h {
  margin: 0 0 clamp(10px, 1.2vw, 16px);
  font-family: var(--font-display);
  font-size: clamp(20px, 1.9vw, 28px);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: #17c0d8;
}
.pdvòng__dẫn,
.pdvòng__kết {
  margin: 0;
  font-size: 15px;
  line-height: 1.6;
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}
.pdvòng__kết {
  margin-top: clamp(18px, 2vw, 28px);
}
.pdvòng__ds {
  margin: clamp(18px, 2vw, 28px) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.6vw, 24px);
}
/* Thẻ TỐI như ref: nền #181818 của site, chữ trắng, bo `--pd-bo`. */
.pdvòng__ds > li {
  display: grid;
  justify-items: center;
  /* `align-content: start` là BẮT BUỘC: ba thẻ cao bằng nhau (hàng grid stretch) nhưng chữ dài
     khác nhau, để mặc định thì phần trống dôi ra chia cho các hàng và chữ mỗi thẻ bắt đầu ở một
     độ cao khác nhau. Neo từ trên thì khe vòng → chữ luôn bằng nhau, ba dòng chữ bắt đầu ngang
     nhau (đo được: cùng 194px từ mép trên thẻ). */
  align-content: start;
  justify-content: center;
  gap: clamp(14px, 1.6vw, 22px);
  padding: clamp(20px, 2.2vw, 34px) clamp(16px, 1.8vw, 26px);
  border-radius: var(--pd-bo);
  background: #181818;
  text-align: center;
}
/* Vòng phần trăm: SVG 100×100, nét dày 9, xoay −90° để nét bắt đầu từ đỉnh. */
.pdvòng__ô {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(112px, 11vw, 150px);
  aspect-ratio: 1;
}
.pdvòng__ô svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}
.pdvòng__nền,
.pdvòng__nét {
  fill: none;
  stroke-width: 9;
  stroke-linecap: round;
}
.pdvòng__nền {
  stroke: rgba(23, 192, 216, 0.24);
}
.pdvòng__nét {
  stroke: #17c0d8;
}
.pdvòng__ô b {
  position: relative;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.1vw, 30px);
  font-weight: 700;
  color: #fff;
}
.pdvòng__chữ {
  font-size: 15px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
}
/* ---------- BẢNG SO SÁNH ----------
   Khay riêng có `overflow-x: auto` và bảng có `min-width`: từ khổ nào mà bảng không vừa thì
   CUỘN NGANG trong khay, KHÔNG hạ cỡ chữ xuống dưới 13 — chữ 10–11px trong bảng bốn cột là
   không đọc nổi trên điện thoại. */
.pdbảng__khay {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--pd-bo-vừa);
}
.pdbảng__khay:focus-visible {
  outline: 2px solid #17c0d8;
  outline-offset: 2px;
}
.pdbảng table {
  width: 100%;
  min-width: 880px; /* dưới mức này thì cuộn ngang */
  border-collapse: collapse;
  font-size: 14px;
  line-height: 1.5;
}
.pdbảng th,
.pdbảng td {
  padding: clamp(10px, 1vw, 16px);
  border: 1px solid color-mix(in srgb, #17c0d8 34%, transparent);
  color: color-mix(in srgb, var(--pd-mực) 82%, transparent);
}
/* Ô NỘI DUNG căn LÊN TRÊN: các cột cao thấp khác nhau (Vieon 3 gạch, Msky 2 gạch), căn giữa thì
   gạch đầu dòng của các cột không thẳng hàng nhau. Riêng ô TIÊU ĐỀ (hàng đầu và cột nhãn) vẫn
   căn giữa. */
.pdbảng td {
  vertical-align: top;
}
.pdbảng th {
  vertical-align: middle;
}
/* Hàng tiêu đề và cột nhãn: nền xanh lơ NHẠT như ref, chữ đậm và tối */
.pdbảng thead th,
.pdbảng tbody th {
  background: color-mix(in srgb, #17c0d8 16%, transparent);
  color: var(--pd-mực);
  font-weight: 700;
  text-align: center;
}
.pdbảng tbody th {
  text-align: left;
  min-width: 140px;
}
/* Cột NHÃN dính lại khi cuộn ngang: bảng bốn cột ở khổ hẹp phải cuộn, mà cuộn xa thì không còn
   biết đang đọc hàng nào. Nền phải ĐẶC (không dùng `--pd-mặt` trong suốt) để chữ cột sau không
   lộ ra phía dưới. */
.pdbảng thead th.pdbảng__góc,
.pdbảng tbody th {
  position: sticky;
  left: 0;
  z-index: 1;
  background: color-mix(in srgb, #17c0d8 16%, var(--pd-kem));
}
.pdbảng thead th.pdbảng__góc {
  z-index: 2;
}
.pdbảng__góc {
  min-width: 140px;
}
.pdbảng td ul {
  margin: 0;
  padding-left: 16px;
  display: grid;
  gap: 6px;
  list-style: disc;
}
.pdbảng__giữa {
  text-align: center;
}
/* ---------- KHỐI `hộpNhấn` — MỘT tấm nền màu đặc (ref-chapter4.png) ----------
   Trong tấm: hàng đầu (nhãn đậm + một câu) → các cột cách nhau bằng kẻ mảnh, mỗi cột một tiêu đề
   đậm + một câu → dòng ghi chú cuối, cũng có kẻ mảnh phía trên. Chữ dùng mực của trang (nền vàng
   #FFBD1B là nền SÁNG nên không đổi sang bộ chữ trắng). Kẻ ngăn là mực pha 18% để nằm trên nền
   màu nào cũng thấy. */
.pdhnhấn {
  padding: clamp(20px, 2.4vw, 40px);
  border-radius: var(--pd-bo);
  background: var(--pdhn-nền, #ffbd1b);
  color: var(--pd-mực);
}
.pdhnhấn__đầu {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px clamp(14px, 1.6vw, 26px);
  font-size: 15px;
  line-height: 1.55;
}
.pdhnhấn__đầu b {
  font-weight: 700;
}
/* `to: true` — nhãn nhỏ in hoa nằm TRÊN, câu chốt in rất to bằng font display (Thu Hà chốt
   2026-08-26 cho tấm "Product principle"). */
.pdhnhấn.là-to .pdhnhấn__đầu {
  display: grid;
  gap: clamp(8px, 1vw, 14px);
}
.pdhnhấn.là-to .pdhnhấn__đầu b {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.pdhnhấn.là-to .pdhnhấn__đầu span {
  font-family: var(--font-display);
  /* Thu Hà chốt lại 2026-08-26: bản 46px to quá — hạ về thang này (28px ở 1512, 19px ở mobile),
     vẫn to hơn thân bài rõ rệt nhưng không lấn tiêu đề mục. */
  font-size: clamp(19px, 1.9vw, 28px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
}
.pdhnhấn__ds {
  margin: clamp(18px, 2vw, 30px) 0 0;
  padding: clamp(18px, 2vw, 30px) 0 0;
  border-top: 1px solid color-mix(in srgb, var(--pd-mực) 18%, transparent);
  display: grid;
  grid-template-columns: repeat(var(--pdhn-n, 3), minmax(0, 1fr));
  gap: clamp(14px, 1.8vw, 30px);
  list-style: none;
}
.pdhnhấn__ds[data-n="2"] {
  --pdhn-n: 2;
}
.pdhnhấn__ds li {
  display: grid;
  gap: 8px;
  font-size: 14px;
  line-height: 1.55;
}
.pdhnhấn__ds b {
  font-size: 15px;
  font-weight: 700;
}
.pdhnhấn__ghi {
  margin: clamp(18px, 2vw, 30px) 0 0;
  padding: clamp(14px, 1.6vw, 22px) 0 0;
  border-top: 1px solid color-mix(in srgb, var(--pd-mực) 18%, transparent);
  font-size: 13px;
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 76%, transparent);
}
@media (max-width: 1023px) {
  .pdhnhấn__ds {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- KHỐI `hàngKẻ` — mấy HÀNG NGANG cách nhau bằng vạch kẻ (ref-chapter3-03.png) ----------
   Không có mặt thẻ, không nền: chỉ vạch kẻ mảnh trên–dưới mỗi hàng như ref. Mỗi hàng hai cột —
   trái 52% (nhãn nhỏ in hoa + tiêu đề lớn + câu chuỗi bước), phải là các nguyên tắc mỗi dòng
   một câu, KHÔNG gạch đầu dòng, căn lên đỉnh hàng. */
.pdhkẻ {
  border-top: 1px solid var(--pd-viền);
}
.pdhkẻ__hàng {
  display: grid;
  grid-template-columns: minmax(0, 52%) minmax(0, 1fr);
  gap: clamp(20px, 2.4vw, 44px);
  padding: clamp(24px, 3.2vw, 52px) 0;
  border-bottom: 1px solid var(--pd-viền);
}
.pdhkẻ__nhãn {
  margin: 0 0 clamp(12px, 1.4vw, 20px);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--pd-mực) 56%, transparent);
}
.pdhkẻ__h {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.2vw, 34px);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
}
.pdhkẻ__p {
  margin: clamp(14px, 1.6vw, 22px) 0 0;
  font-size: 14px;
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 74%, transparent);
}
.pdhkẻ__p + .pdhkẻ__p {
  margin-top: 8px;
}
.pdhkẻ__ds {
  margin: 0;
  padding: 0;
  list-style: none;
}
.pdhkẻ__ds li {
  font-size: 14px;
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 74%, transparent);
}
/* Bản BA CỘT của `hàngKẻ` (ref-chapter8-12.png): số nhỏ · tiêu đề · đoạn văn + dòng trade-off. */
.pdhkẻ--ba .pdhkẻ__hàng {
  grid-template-columns: minmax(0, 60px) minmax(0, 1fr) minmax(0, 1.2fr);
}
.pdhkẻ__no {
  margin: 0;
  font-size: 12px;
  font-weight: 500;
  color: color-mix(in srgb, var(--pd-mực) 56%, transparent);
}
.pdhkẻ--ba .pdhkẻ__h {
  font-size: clamp(17px, 1.5vw, 23px);
  font-weight: 600;
}
.pdhkẻ--ba .pdhkẻ__p {
  margin: 0;
}
.pdhkẻ--ba .pdhkẻ__p + .pdhkẻ__p {
  margin-top: 8px;
}
.pdhkẻ__ghi {
  margin: clamp(12px, 1.4vw, 20px) 0 0;
  display: grid;
  grid-template-columns: minmax(0, 108px) minmax(0, 1fr);
  gap: 4px clamp(10px, 1.2vw, 20px);
  font-size: 14px;
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 74%, transparent);
}
.pdhkẻ__ghi b {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--pd-mực);
}
@media (max-width: 1023px) {
  /* Hai cột: số đứng cùng hàng với tiêu đề, đoạn văn xuống dưới trải hết bề ngang. */
  .pdhkẻ--ba .pdhkẻ__hàng {
    grid-template-columns: minmax(0, 44px) minmax(0, 1fr);
  }
  .pdhkẻ--ba .pdhkẻ__phải {
    grid-column: 2;
  }
}
@media (max-width: 767px) {
  .pdhkẻ__ghi {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Bản BA CỘT của `hàngSố` (ref-chapter8-13.png): ba cột nằm ngang, số RẤT TO màu nhấn, câu mô tả
   ở dưới. Vạch kẻ chỉ ở TRÊN cả hàng (mỗi cột có kẻ trên riêng nên khi xuống một cột vẫn còn
   vạch phân tách). */
.pdhàngsố--ba {
  border-top: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 2.4vw, 44px);
}
.pdhàngsố--ba li {
  display: block;
  padding: clamp(18px, 2vw, 30px) 0 0;
  border-top: 1px solid var(--pd-line);
  border-bottom: 0;
}
.pdhàngsố--ba b {
  display: block;
  margin-bottom: clamp(14px, 1.8vw, 26px);
  font-size: clamp(40px, 4.4vw, 72px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--pd-nhấn-số, #ffbd1b);
}
.pdhàngsố--ba span {
  font-size: clamp(13px, 1vw, 15px);
  line-height: 1.55;
}
@media (max-width: 767px) {
  .pdhàngsố--ba {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* ≤767: hai cột xếp dọc — nguyên tắc nằm dưới câu chuỗi bước, cách một khoảng nhỏ. */
@media (max-width: 767px) {
  .pdhkẻ__hàng {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(14px, 3vw, 20px);
  }
  .pdhkẻ__ds li,
  .pdhkẻ__p {
    font-size: 14px;
  }
}

/* ---------- BẢNG NỀN TỐI (khai `tối: true` trong khối `bảng`) ----------
   FANFAN chapter 2: nền #202020, tiêu đề #FFBD1B, các ô tiêu đề đổi từ tông xanh lơ #17C0D8
   sang tông vàng MỜ. Chỉ là một biến thể màu — mọi số đo (đệm, viền, `min-width` 880/760, cuộn
   ngang, cột nhãn dính) dùng chung với bảng thường. */
.pdbảng.là-tối {
  background: #202020;
  border-color: color-mix(in srgb, #ffbd1b 24%, transparent);
}
.pdbảng.là-tối .pdbảng__h {
  color: #ffbd1b;
}
.pdbảng.là-tối .pdbảng__khay:focus-visible {
  outline-color: #ffbd1b;
}
.pdbảng.là-tối th,
.pdbảng.là-tối td {
  border-color: color-mix(in srgb, #ffbd1b 26%, transparent);
  color: rgba(255, 255, 255, 0.82);
}
.pdbảng.là-tối thead th,
.pdbảng.là-tối tbody th {
  background: color-mix(in srgb, #ffbd1b 14%, transparent);
  color: #fff;
}
/* Cột nhãn dính khi cuộn ngang: nền phải ĐẶC, không thì chữ cột sau lộ ra phía dưới. */
.pdbảng.là-tối thead th.pdbảng__góc,
.pdbảng.là-tối tbody th {
  background: color-mix(in srgb, #ffbd1b 14%, #202020);
}
@media (max-width: 1023px) {
  .pdvòng__ds {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 767px) {
  .pdvòng__ds {
    grid-template-columns: minmax(0, 1fr);
  }
  .pdvòng__chữ,
  .pdvòng__dẫn,
  .pdvòng__kết {
    font-size: 14px;
  }
  .pdbảng table {
    font-size: 13px; /* SÀN cỡ chữ: dưới mức này bảng không còn đọc được */
    min-width: 760px;
  }
}

/* ---------- DÒNG THỜI GIAN CÓ CHẤM + BA CỘT CHIẾN LƯỢC (ref-chapter2-0b.jpg) ----------
   Tiêu đề của cả ba khối mới (`cặp` · `mốc` · `cột`) dùng CHUNG một kiểu: xanh lơ `#17C0D8` đo
   từ ref, cỡ theo thang tiêu đề trong khối. */
/* `mốc` và `cột` dùng CÙNG MẶT THẺ với `cặp` cho đồng bộ: nền `--pd-mặt`, viền `--pd-viền`,
   bo `--pd-bo`, có đệm trong. */
.pdmốc,
.pdcột {
  padding: clamp(20px, 2.4vw, 40px);
  border: 1px solid var(--pd-viền);
  border-radius: var(--pd-bo);
  background: var(--pd-mặt);
}
.pdmốc__h,
.pdcột__h {
  margin: 0 0 clamp(16px, 1.8vw, 26px);
  font-family: var(--font-display);
  font-size: clamp(20px, 1.9vw, 28px);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: #17c0d8;
}
/* Bản GỌN của khối `cột` (khai `gọn: true`): dòng ghi chú ở trên là chú thích nên NHỎ và mảnh
   lại; tiêu đề nhóm thì TO, ĐẬM, KHÔNG nền đen, căn TRÁI — đọc như một tiêu đề mục thật thay vì
   một cái thẻ. Bản gốc (Msky OTT) không đổi. */
.pdcột--gọn .pdcột__h {
  font-size: clamp(13px, 0.95vw, 15px);
  font-weight: 500;
  letter-spacing: 0.01em;
}
.pdcột--gọn .pdcột__đề {
  padding: 0;
  border-radius: 0;
  background: none;
  color: var(--pd-mực);
  font-size: clamp(18px, 1.7vw, 24px);
  font-weight: 700;
  text-align: left;
}

/* Bốn mốc chia đều một hàng. NÉT GẠCH ĐỨT nối các chấm vẽ bằng `::before` của từng mốc (trừ mốc
   đầu) chứ không phải một đường chạy ngang phía sau: làm một đường thì hai đầu thò ra ngoài chấm
   đầu và chấm cuối, đúng chỗ ref không có nét nào. */
.pdmốc__ds {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
}
.pdmốc__ds > li {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 10px;
  padding: 0 clamp(6px, 0.8vw, 14px);
  text-align: center;
}
.pdmốc__ds > li + li::before {
  content: "";
  position: absolute;
  top: calc(var(--pdmốc-chấm) / 2 - 1px);
  right: 50%;
  left: -50%;
  border-top: 2px dashed #f33b0a;
  z-index: 0;
}
.pdmốc__ds {
  --pdmốc-chấm: clamp(26px, 2.4vw, 34px);
}
.pdmốc__chấm {
  position: relative;
  z-index: 1;
  width: var(--pdmốc-chấm);
  height: var(--pdmốc-chấm);
  border-radius: 50%;
  background: #f33b0a;
}
.pdmốc__tên {
  font-family: var(--font-display);
  font-size: clamp(17px, 1.4vw, 21px);
  font-weight: 700;
  line-height: 1.25;
  color: var(--pd-mực);
}
/* Ô ảnh của dòng thời gian: RỘNG và THẤP (4:1), không phải 16:9 như ô ảnh thường. */
.pdmốc__ảnh .pdph__ô {
  aspect-ratio: 4 / 1;
}
@media (max-width: 767px) {
  /* Ở 342px bề ngang, 4:1 chỉ còn 85px cao — chữ trong ảnh sẽ không đọc được. Cho cao gấp đôi
     (2:1) ở mobile; ảnh `cover` nên chỉ cắt hai bên. */
  .pdmốc__ảnh .pdph__ô {
    aspect-ratio: 2 / 1;
  }
}
.pdmốc__việc {
  display: grid;
  gap: 6px;
  font-size: 15px;
  line-height: 1.45;
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}
/* Ba cột: thẻ tiêu đề NỀN TỐI (đo ref: rgb(35,35,36) ≈ #181818 của site) rồi tới danh sách. */
.pdcột__ds {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 32px);
}
/* Số nhóm thay đổi theo nội dung: một nhóm (danh sách kiểm "Future state audit") thì trải hết
   khổ và tự chia danh sách thành hai cột cho khỏi dài; hai nhóm thì hai cột.
   `[data-n]` mạnh hơn `.pdcột__ds` nên PHẢI lặp lại ở khối mobile phía dưới, không thì mobile
   vẫn hai cột. */
.pdcột__ds[data-n="1"] {
  grid-template-columns: minmax(0, 1fr);
}
.pdcột__ds[data-n="2"] {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.pdcột__ds[data-n="1"] .pdcột__ô ul {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(20px, 2.4vw, 40px);
}
.pdcột__ô {
  min-width: 0;
}
.pdcột__đề {
  display: block;
  padding: 12px 16px;
  border-radius: var(--pd-bo-vừa);
  background: #181818;
  color: #fff;
  font-family: var(--font-display);
  font-size: clamp(15px, 1.15vw, 18px);
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
}
.pdcột__ô ul {
  margin: clamp(12px, 1.4vw, 18px) 0 0;
  padding: 0 0 0 18px;
  display: grid;
  gap: clamp(8px, 0.9vw, 12px);
  list-style: disc;
}
.pdcột__ô li {
  font-size: 15px;
  line-height: 1.5;
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}
@media (max-width: 1023px) {
  /* Bốn mốc thành 2×2 và ba cột thành 1 cột ở khổ hẹp: bốn cột ở 810 chỉ còn 155px mỗi cột,
     "Information Architecture" xuống bốn dòng. */
  .pdmốc__ds {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: clamp(24px, 3vw, 36px);
  }
  .pdmốc__ds > li + li::before {
    display: none; /* xếp 2×2 thì nét nối ngang không còn đúng mạch */
  }
}
@media (max-width: 767px) {
  .pdmốc__ds {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 22px;
  }
  .pdmốc__việc,
  .pdcột__ô li {
    font-size: 14px;
  }
  .pdcột__ds,
  .pdcột__ds[data-n] {
    grid-template-columns: minmax(0, 1fr);
  }
  .pdcột__ds[data-n="1"] .pdcột__ô ul {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- NỬA ẢNH – NỬA CHỮ (ref-chapter2-02.jpg) ----------
   Hai cột đều nhau: một nửa là ảnh phủ trọn (cao bằng cả khối), nửa kia là tiêu đề + danh sách
   gạch đầu dòng có chữ dẫn in đậm. `--pdcặp-lề` là đệm của cột chữ; cột ảnh KHÔNG có đệm nên
   ảnh chạm ba mép của nửa mình.
   Ảnh đặt TUYỆT ĐỐI trong ô: nếu để làm grid item thường thì tỉ lệ gốc của nó tự quyết định
   chiều cao khối (bẫy đã gặp ở khối `hỏi` bản đôi — ảnh 1421×1620 kéo khối cao thêm 130px). */
.pdcặp {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: stretch;
  border-radius: var(--pd-bo);
  overflow: hidden;
  background: var(--pd-mặt);
  border: 1px solid var(--pd-viền);
}
.pdcặp__ô {
  position: relative;
  min-height: clamp(240px, 26vw, 380px);
  overflow: hidden;
}
.pdcặp__ô img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.pdcặp__chữ {
  min-width: 0;
  align-self: center;
  padding: clamp(20px, 2.4vw, 40px);
}
/* Tiêu đề màu XANH LƠ của ref (đo pixel: rgb(23,192,216)) — đây là màu của UI trong chapter,
   thuộc phần được đổi theo dự án; thang cỡ vẫn là thang tiêu đề trong khối (như `.pdhn__h`). */
.pdcặp__h {
  margin: 0 0 clamp(12px, 1.4vw, 18px);
  font-family: var(--font-display);
  font-size: clamp(20px, 1.9vw, 28px);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: #17c0d8;
}
.pdcặp__ds {
  margin: 0;
  padding: 0 0 0 18px;
  display: grid;
  gap: clamp(8px, 0.9vw, 12px);
  list-style: disc;
}
.pdcặp__ds li {
  font-size: 15px;
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}
.pdcặp__ds b {
  font-weight: 600;
  color: var(--pd-mực);
}
/* HAI KHỐI `cặp` LIỀN NHAU GỘP THÀNH MỘT: khe 0, chỉ khối ngoài có bo góc, mép giữa chỉ còn
   một đường kẻ. `:has(+ .pdcặp)` để biết khối nào còn khối cặp ngay sau nó — trình duyệt không
   đỡ `:has` thì rơi về hai khối bo góc riêng, vẫn đọc được. */
.pds > .pdcặp + .pdcặp {
  margin-top: 0;
  border-top: 0;
  border-radius: 0 0 var(--pd-bo) var(--pd-bo);
}
.pdcặp:has(+ .pdcặp) {
  border-radius: var(--pd-bo) var(--pd-bo) 0 0;
}
/* `lật`: ảnh sang PHẢI — đổi thứ tự hai cột, không đổi markup */
.pdcặp--lật .pdcặp__ô {
  order: 2;
}
/* Khổ hẹp: xếp dọc, ảnh 16:9 ở trên rồi tới chữ */
@media (max-width: 809px) {
  .pdcặp {
    grid-template-columns: minmax(0, 1fr);
  }
  .pdcặp__ô {
    min-height: 0;
    aspect-ratio: 16 / 9;
    order: 0 !important; /* mọi khối đều ảnh trên, chữ dưới */
  }
  .pdcặp__ds li {
    font-size: 14px;
  }
}

/* ---------- gạch đầu dòng có chữ dẫn ---------- */
.pdgạch {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(10px, 1.2vw, 16px);
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
/* `hàng: true` — mỗi gạch một HÀNG: kẻ mảnh phía trên, nhãn đậm ở cột nhỏ bên trái, câu mô tả ở
   cột còn lại (ref-chapter7-08.png). ≤767 nhãn xuống dòng trên câu mô tả. */
.pdgạch--hàng {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}
.pdgạch--hàng li {
  display: grid;
  grid-template-columns: minmax(0, 200px) minmax(0, 1fr);
  gap: 4px clamp(12px, 1.6vw, 28px);
  padding: clamp(12px, 1.4vw, 18px) 0;
}
.pdgạch--hàng li b {
  display: block;
}
@media (max-width: 767px) {
  .pdgạch--hàng li {
    grid-template-columns: minmax(0, 1fr);
  }
}
.pdgạch li {
  padding-top: 12px;
  border-top: 1px solid var(--pd-line);
  font-size: clamp(14px, 1vw, 15px);
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 72%, transparent);
}
.pdgạch b {
  color: var(--pd-mực);
}

/* ---------- BẢNG NGHIÊN CỨU: tấm nền TRẮNG có lưới CHẤM XÁM ----------
   Dựng theo reference/project-msky-hospitality/ref-chapter2.png. Thu Hà chốt: nền trắng, chấm
   tròn xám nhạt. Chấm vẽ bằng `radial-gradient` lặp 18px — không thêm file ảnh nào.
   Tấm này KHÔNG đổi lề/thang chữ chung: nó chỉ là một khối trong `khối[]` như mọi khối khác. */
.pdkhung {
  padding: clamp(20px, 2.6vw, 44px);
  border: 1px solid var(--pd-viền);
  border-radius: var(--pd-bo);
  background-color: #fff;
  background-image: radial-gradient(rgba(17, 19, 21, 0.13) 1.1px, transparent 1.2px);
  background-size: 18px 18px;
  background-position: 0 0;
}
/* Khoảng cách giữa các khối con bên trong tấm: dùng `> * + *` để không phải nhớ đặt margin cho
   từng loại khối con. */
.pdkhung > * + * {
  margin-top: clamp(14px, 1.6vw, 22px);
}
.pdkhung > .pdđềnhỏ {
  margin-top: clamp(22px, 2.6vw, 36px);
}
.pdđềto {
  margin: 0 0 clamp(16px, 2vw, 26px);
  padding-bottom: 10px;
  display: inline-block;
  border-bottom: 3px solid #17c0d8;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.4vw, 34px);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--pd-mực);
}
.pdđềnhỏ {
  margin: 0 0 clamp(10px, 1.2vw, 16px);
  font-family: var(--font-display);
  font-size: clamp(18px, 1.7vw, 24px);
  font-weight: 600;
  line-height: 1.25;
  color: #17c0d8;
}
/* Bên trong tấm: danh sách về dạng GẠCH ĐẦU DÒNG một cột (bản chung là lưới nhiều cột có vạch
   ngăn trên — dùng cho mục khác, để nguyên). Dấu chấm vẽ bằng `::before` chứ không dùng
   `list-style` để canh đúng dòng đầu khi chữ xuống dòng. */
.pdkhung .pdgạch {
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(8px, 0.9vw, 12px);
}
.pdkhung .pdgạch li {
  position: relative;
  padding: 0 0 0 18px;
  border-top: 0;
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}
.pdkhung .pdgạch li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--pd-mực) 45%, transparent);
}
.pdkhung .pdgạch i {
  display: block;
  margin-top: 2px;
  color: color-mix(in srgb, var(--pd-mực) 60%, transparent);
}
.pdkhung .pds__p {
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}
/* HAI CỘT trong tấm (Executive Intent | Target Audience + Business Type). Tỉ lệ khai trong data
   qua biến `--pdhai-tỉ`; ≤767 xếp dọc. */
.pdhai {
  display: grid;
  grid-template-columns: var(--pdhai-tỉ, 1fr 1fr);
  gap: clamp(18px, 2.4vw, 40px);
}
.pdhai > div > * + * {
  margin-top: clamp(10px, 1.2vw, 16px);
}
.pdhai > div > .pdđềnhỏ + * {
  margin-top: 0;
}
.pdhai > div > * + .pdđềnhỏ {
  margin-top: clamp(20px, 2.4vw, 32px);
}

/* ---------- DẢI TAB (khối `tab`) ----------
   Theo bộ ảnh detail/chapter5: một hàng nhãn, nhãn đang chọn màu xanh ngọc có vạch gạch dưới,
   cả hàng có một đường kẻ mảnh chạy hết bề ngang. Mô tả căn giữa, rồi tới ô ảnh 16:9.
   Ở khổ hẹp hàng nhãn CUỘN NGANG (tám nhãn không thể nhồi vào 342px mà còn đọc được). */
.pdtab__thanh {
  display: flex;
  /* Hàng nhãn CĂN GIỮA khổ nội dung (Thu Hà chốt). Ở khổ hẹp thanh cuộn ngang: lúc đó phải về
     `flex-start`, không thì phần tràn bên TRÁI không cuộn tới được — đúng cái bẫy đã gặp ở ô ảnh
     cuộn ngang. */
  justify-content: center;
  gap: clamp(14px, 1.8vw, 30px);
  border-bottom: 1px solid var(--pd-line);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.pdtab__thanh::-webkit-scrollbar {
  display: none;
}
.pdtab__nút {
  flex: none;
  position: relative;
  padding: 0 0 10px;
  border: 0;
  background: none;
  font-family: var(--font-display);
  font-size: clamp(14px, 1.15vw, 17px);
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
  color: color-mix(in srgb, var(--pd-mực) 58%, transparent);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: color 0.22s ease;
}
.pdtab__nút::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: #17c0d8;
  opacity: 0;
  transition: opacity 0.22s ease;
}
.pdtab__nút:hover {
  color: var(--pd-mực);
}
.pdtab__nút.là-nay {
  color: #17c0d8;
}
.pdtab__nút.là-nay::after {
  opacity: 1;
}
.pdtab__ô {
  margin-top: clamp(18px, 2.2vw, 32px);
}
.pdtab__p {
  margin: 0 auto clamp(16px, 2vw, 28px);
  max-width: 68ch;
  font-size: clamp(14px, 1.05vw, 16px);
  line-height: 1.6;
  text-align: center;
  color: color-mix(in srgb, var(--pd-mực) 76%, transparent);
}
@media (max-width: 1023px) {
  /* Thanh tràn khổ thì căn trái để cuộn được hết (xem ghi chú ở `.pdtab__thanh`). */
  .pdtab__thanh {
    justify-content: flex-start;
  }
}
@media (max-width: 767px) {
  .pdtab__p {
    text-align: left;
  }
}

/* ---------- HỘP NỘI DUNG TỰ KHAI MÀU NỀN (khối `ôMàu`) ----------
   Msky Hospitality chapter 3: hai vai (Hotel guest / Hotel staff) mỗi bên một hộp màu riêng —
   `#13182B` nền tối chữ sáng và `#DCECF0` nền sáng chữ tối. Màu vào qua biến `--pdo-nền` khai
   inline trong data, nên thêm hộp màu khác không phải sửa CSS. */
.pdômàu {
  display: grid;
  grid-template-columns: repeat(var(--pdo-n, 2), minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 24px);
}
.pdômàu[data-n="1"] {
  --pdo-n: 1;
}
.pdômàu[data-n="3"] {
  --pdo-n: 3;
}
.pdômàu__ô {
  padding: clamp(20px, 2.2vw, 34px);
  border-radius: var(--pd-bo);
  background: var(--pdo-nền);
}
/* `viền: true` — hộp CHỈ CÓ VIỀN, không tô nền (ref-chapter4.png của FANFAN). Khai kèm
   `nền: "transparent"`; mọi số đo đệm/bo giữ như hộp có nền. */
.pdômàu__ô.là-viền {
  border: 1px solid var(--pd-viền);
}
.pdômàu__nhãn {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--pd-mực) 62%, transparent);
}
.pdômàu__ô.là-tối .pdômàu__nhãn {
  color: rgba(255, 255, 255, 0.62);
}
.pdômàu__p {
  margin: 0;
  font-size: clamp(14px, 1vw, 15px);
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 78%, transparent);
}
.pdômàu__p + .pdômàu__p {
  margin-top: 10px;
}
.pdômàu__ô.là-tối .pdômàu__p {
  color: rgba(255, 255, 255, 0.78);
}
/* Dòng ghi chú cuối hộp (Sự đánh đổi / Cơ hội đo lường trong ref): CÓ VẠCH KẺ mảnh phía trên,
   chữ in nghiêng và nhỏ hơn đoạn mô tả — đọc ra là ghi chú, không phải thân bài. */
.pdômàu__ghi {
  margin: clamp(14px, 1.6vw, 20px) 0 0;
  padding-top: clamp(12px, 1.4vw, 16px);
  border-top: 1px solid color-mix(in srgb, var(--pd-mực) 14%, transparent);
  font-size: clamp(13px, 0.92vw, 14px);
  font-style: italic;
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 66%, transparent);
}
.pdômàu__ô.là-tối .pdômàu__ghi {
  border-top-color: rgba(255, 255, 255, 0.22);
  color: rgba(255, 255, 255, 0.68);
}
.pdômàu__h {
  margin: 0 0 clamp(12px, 1.4vw, 18px);
  font-family: var(--font-display);
  font-size: clamp(16px, 1.3vw, 20px);
  font-weight: 700;
  line-height: 1.3;
  color: var(--pd-mực);
}
.pdômàu__ô ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(8px, 1vw, 12px);
}
/* FANFAN chapter 1 · hộp "My design focus" nền #202020: Thu Hà chốt nhãn màu #FFBD1B và chữ
   trắng đặc (bản dùng chung là trắng 62% / 78%). Khoanh theo `data-slug` để hộp tối của
   Msky Hospitality và Tiếng Việt TV không đổi màu. */
#pd[data-slug="fanfan"] .pdômàu__ô.là-tối .pdômàu__nhãn {
  color: #ffbd1b;
}
/* Chapter 4: hai hộp nền #202020 có TIÊU ĐỀ trong hộp — tiêu đề mới là chữ vàng brand (Thu Hà
   chốt 2026-08-26), còn nhãn nhỏ phía trên trả về trắng mờ để trong một hộp không có hai cỡ chữ
   vàng cạnh nhau. Hộp "My design focus" ở chapter 1 chỉ có nhãn, không có tiêu đề, nên không
   trúng luật này. */
#pd[data-slug="fanfan"] .pdômàu__ô.là-tối .pdômàu__h {
  color: #ffbd1b;
}
#pd[data-slug="fanfan"] .pdômàu__ô.là-tối:has(.pdômàu__h) .pdômàu__nhãn {
  color: rgba(255, 255, 255, 0.62);
}
#pd[data-slug="fanfan"] .pdômàu__ô.là-tối .pdômàu__p {
  color: #fff;
}
.pdômàu__ô li {
  font-size: clamp(14px, 1vw, 15px);
  line-height: 1.55;
  color: color-mix(in srgb, var(--pd-mực) 74%, transparent);
}
.pdômàu__ô li b {
  color: var(--pd-mực);
}
/* Hộp nền TỐI: đổi cả bộ chữ sang sáng. Không dùng `--pd-mực` ở đây vì biến đó là mực của cả
   trang (còn dùng cho nền kem), đổi nó trong hộp là kéo theo mọi thứ lồng bên trong. */
.pdômàu__ô.là-tối .pdômàu__h {
  color: #fff;
}
.pdômàu__ô.là-tối li {
  color: rgba(255, 255, 255, 0.76);
}
.pdômàu__ô.là-tối li b {
  color: #fff;
}
@media (max-width: 767px) {
  .pdômàu {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- ĐỒ THỊ VÀNH (Quantitative Research) ----------
   Vành vẽ bằng `stroke-dasharray` trên một `<circle>` r=42 (chu vi 263.9), mỗi lát một circle
   riêng có `stroke-dashoffset` cộng dồn; cả nhóm quay −90° để lát đầu bắt đầu từ 12h — cùng
   cách đã dùng cho vòng % ở Msky OTT. */
.pdbánh {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(20px, 2.6vw, 40px);
}
.pdbánh__ô {
  margin: 0;
}
.pdbánh__đề {
  margin-bottom: clamp(12px, 1.4vw, 18px);
  font-size: clamp(14px, 1vw, 15px);
  font-weight: 600;
  line-height: 1.45;
  color: var(--pd-mực);
}
.pdbánh__thân {
  display: flex;
  align-items: center;
  gap: clamp(16px, 1.8vw, 28px);
}
.pdbánh__vẽ {
  flex: none;
  /* Khung SVG rộng 200 đơn vị (vành 120 + 40 mỗi bên cho nhãn có mũi tên) nên bề rộng hiển thị
     phải lớn hơn bản trước, không thì vành teo lại. */
  width: clamp(210px, 21vw, 290px);
  height: auto;
  color: color-mix(in srgb, var(--pd-mực) 62%, transparent); /* màu đường chỉ + số */
}
.pdbánh__nhãn text {
  font-family: var(--font-display);
  font-size: 9.5px;
  font-weight: 700;
  fill: var(--pd-mực);
}
.pdbánh__nhãn.là-nổi {
  color: #17c0d8;
}
.pdbánh__nhãn.là-nổi text {
  fill: #17c0d8;
}
.pdbánh__cg {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 8px;
  min-width: 0;
}
.pdbánh__cg li {
  display: grid;
  grid-template-columns: 10px 1fr auto;
  align-items: baseline;
  gap: 8px;
  font-size: clamp(13px, 0.92vw, 14px);
  line-height: 1.4;
  color: color-mix(in srgb, var(--pd-mực) 74%, transparent);
}
.pdbánh__cg i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  transform: translateY(1px);
}
.pdbánh__cg b {
  font-weight: 700;
  color: var(--pd-mực);
}
/* Lát được nhấn trong ref: số in màu xanh ngọc. */
.pdbánh__cg li.là-nổi b {
  color: #17c0d8;
}
@media (max-width: 1023px) {
  .pdbánh__thân {
    gap: 16px;
  }
}
@media (max-width: 767px) {
  .pdhai,
  .pdbánh {
    grid-template-columns: minmax(0, 1fr);
  }
  .pdbánh__thân {
    flex-direction: column;
    align-items: flex-start;
  }
  .pdbánh__vẽ {
    width: 150px;
  }
  .pdbánh__cg {
    width: 100%;
  }
}

/* ---------- số liệu kết quả ---------- */
.pdsl {
  margin: 0;
  display: grid;
  gap: clamp(10px, 1.4vw, 18px);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.pdsl > div {
  padding: clamp(18px, 1.8vw, 26px);
  border: 1px solid var(--pd-viền);
  border-radius: var(--pd-bo);
  background: var(--pd-mặt);
}
/* Con số tô GRADIENT XANH — cùng dải với "Hi, I'm Thu Ha" ở section About trang chủ
   (`.about__title`): `linear-gradient(90deg, #014268, #0d9bee)` + `background-clip: text`.
   `width: max-content` là BẮT BUỘC: `background-clip: text` lấy gradient theo HỘP của phần tử,
   mà `dt` là block rộng hết thẻ (~290px) nên chữ "30K" chỉ hứng đoạn ĐẦU của dải — bốn số ra
   bốn sắc lệch nhau và đều mất đầu nhạt. Cho hộp vừa chữ thì mỗi số hứng trọn dải. */
.pdsl dt {
  width: max-content;
  max-width: 100%;
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(32px, 3.4vw, 52px);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
  background: linear-gradient(90deg, #014268, #0d9bee);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* Chú thích dưới số: đậm hơn (500) và màu ĐEN ĐẶC `#000` theo yêu cầu — không đi theo
   `--pd-mực` nữa. Khối `sl` hiện chỉ dùng ở "11 · Outcome" (chapter nền sáng); nếu dự án sau
   đặt khối này vào chapter nền tối thì phải cho lại màu sáng ở đó. */
.pdsl dd {
  margin: 10px 0 0;
  font-size: clamp(13px, 1vw, 15px);
  font-weight: 500;
  line-height: 1.45;
  color: #000;
}

/* Bản NHỎ của khối `sl` (khai `nhỏ: true`): giá trị là CHỮ (End-to-end, Cross-platform…) chứ
   không phải con số, nên bỏ gradient, để cỡ nhỏ hơn và màu đen. Bản gốc (bốn con số của Msky OTT)
   không đổi. */
.pdsl--nhỏ dt {
  font-size: clamp(20px, 1.8vw, 26px);
  letter-spacing: -0.01em;
  background: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  color: #000;
}
.pdsl--nhỏ dd {
  margin-top: 8px;
  color: color-mix(in srgb, #000 72%, transparent);
}

/* ---------- CÂU TRÍCH NỔI (khối `trích`) ----------
   Chữ to, tô GRADIENT XANH — cùng dải với "Hi, I'm Thu Ha" ở section About và bốn con số của
   Msky OTT. `width: max-content` + `max-width: 100%`: `background-clip: text` lấy gradient theo
   HỘP phần tử, để hộp rộng hết thẻ thì câu ngắn chỉ hứng đoạn đầu của dải. */
.pdtrích {
  width: max-content;
  max-width: 100%;
  margin: 0;
  font-family: var(--font-display);
  /* To hơn tiêu đề mục (`.pds__h` = 22→38) nhưng KHÔNG vượt tên chapter (`.pdch__h2` = 48) —
     câu trích là điểm nhấn cuối chương, đọc trước cả tiêu đề mục quanh nó. */
  font-size: clamp(26px, 3.1vw, 46px);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
  text-wrap: balance;
  background: linear-gradient(90deg, #014268, #0d9bee);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* ---------- GHI CHÚ (khối `ghiChú`) ----------
   Khung trắng, viền xanh ngọc `#17c0d8` — cùng màu nhấn đang dùng cho tiêu đề mục trong dự án
   này; chữ nghiêng để đọc ra là ghi chú phụ, không phải thân bài. */
/* Trong khối nền TỐI: bỏ mặt trắng, chỉ còn VIỀN TRẮNG mảnh, và bo góc nhỏ lại (Thu Hà chốt
   2026-08-25 cho ghi chú "Evidence boundary" ở chapter 1 của Tiếng Việt TV). Mấy ghi chú trên nền
   kem (Hospitality, chapter 6/8 của TV) giữ nguyên mặt trắng + viền xanh ngọc. */
.pdxanh .pdghichú {
  border-color: rgba(255, 255, 255, 0.42);
  border-radius: var(--pd-bo-nhỏ);
  background: none;
  color: rgba(255, 255, 255, 0.82);
}
.pdghichú {
  margin: 0;
  padding: clamp(14px, 1.6vw, 22px) clamp(16px, 1.8vw, 26px);
  border: 1px solid #17c0d8;
  border-radius: var(--pd-bo);
  background: #fff;
  font-size: clamp(13px, 1vw, 15px);
  font-style: italic;
  line-height: 1.6;
  color: color-mix(in srgb, var(--pd-mực) 76%, transparent);
}

/* ---------- 4. NEXT PROJECT (dock tắt từ đây) ---------- */
.pdn {
  position: relative;
  z-index: 1;
  padding: clamp(56px, 9vw, 130px) var(--pd-lề) clamp(56px, 9vw, 120px);
  background: #181818;
  color: #fff;
}
/* Dải lưới đáy cao đúng 2 ô, mờ dần để nối liền vào lưới của section Contact — cùng
   ba điều kiện khớp pha đã ghi ở .mw2::after (cùng ô 97, offset ngang 0, cao bội số 97). */
.pdn::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--ab-cell, 97px) * 2);
  pointer-events: none;
  background-image:
    linear-gradient(90deg, var(--tối-line) 0 1px, transparent 1px),
    linear-gradient(180deg, var(--tối-line) 0 1px, transparent 1px);
  background-size: var(--ab-cell, 97px) var(--ab-cell, 97px);
  background-position: 0 var(--grid-y, 0px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 100%);
}
.pdn > * {
  position: relative;
  z-index: 1;
}
.pdn__nhãn {
  margin-bottom: clamp(18px, 2vw, 28px) !important;
  color: rgba(255, 255, 255, 0.6) !important;
}
.pdn__a {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(16px, 2vw, 32px);
  padding: clamp(14px, 1.4vw, 20px);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--pd-bo-lớn);
  background: rgba(255, 255, 255, 0.06);
  color: inherit;
  text-decoration: none;
  transition:
    background-color 0.24s ease,
    border-color 0.24s ease;
}
.pdn__a:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.28);
}
.pdn__shot {
  width: clamp(120px, 20vw, 280px);
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--pd-bo-vừa);
  background: rgba(255, 255, 255, 0.08);
}
.pdn__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pdn__row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: var(--pd-nhãn-giãn);
  color: rgba(255, 255, 255, 0.62);
}
.pdn__name {
  display: block;
  margin-top: 8px;
  font-family: var(--font-display);
  font-size: clamp(17px, 1.8vw, 28px);
  font-weight: 600;
  line-height: 1.22;
  letter-spacing: -0.01em;
}
.pdn__go {
  flex: none;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 50%;
  transition:
    background-color 0.24s ease,
    color 0.24s ease,
    translate 0.24s ease;
}
.pdn__go svg {
  width: 20px;
  height: 20px;
}
.pdn__a:hover .pdn__go {
  background: #fff;
  color: #181818;
  translate: 4px 0;
}
@media (max-width: 767px) {
  /* Thẻ Next project ở mobile = thẻ `.mwgọn` card view của trang My Work (khối trên).
     Thẻ ngang tắt hẳn. */
  .pdn__a {
    display: none;
  }
}

/* ---------- 5. DOCK CHAPTER ----------
   Kích thước đo THẲNG từ reference/project-detail/ (ảnh ref-dock-click-2.png là màn
   14 inch nên quy được ra px CSS: ảnh 3020px ÷ DPR 2 = 1510 ≈ 1512):

     dock         704 × 92, bo tròn hết (46)          cách đáy 20
     nút tròn     76  (= 0.83 chiều cao dock)         đệm trái 9
     icon trong   24 × 18 (= 0.31 nút)
     khe nút→chữ  30
     nhãn         mono 12   ·  tên chapter  18
     nút next/lui hình 20 (= 0.22 chiều cao dock)     đệm phải 16
     thanh tiến độ cao 3, bo 2, thụt hai đầu = nửa chiều cao dock (nằm gọn trong bo tròn)

   BỀ RỘNG CỐ ĐỊNH: mọi tên chapter đều được vẽ, tất cả nằm CHUNG MỘT Ô LƯỚI và chỉ
   chapter đang xem là hiện (`visibility`), nên ô luôn rộng bằng tên DÀI NHẤT. Cuộn qua
   các chapter dock không co kéo lấy một px. Cách này không cần JS đo chữ nên không sợ
   font tải xong mới đúng. */
/* ---------- TABLET + MOBILE: rút các khe trong section banner ----------
   CHỈ ≤1023, desktop giữ nguyên bản đã chốt.
   Hai chỗ làm banner rời rạc ở khổ nhỏ:
     · `min-height: 100svh` + `align-content: center` — nội dung thấp hơn một màn thì phần
       dôi ra CHIA ĐỀU trên/dưới, cộng thêm vào đệm đáy. Đo ở 390: khe ảnh → thẻ Preface ra
       134 trong khi đệm đáy chỉ 50. Bỏ ép cao trọn màn ở khổ nhỏ là hết dôi: ảnh nền vẫn
       phủ kín vì nó nằm trên `.pdtop` (bọc cả banner và mục lục), không phải trên `.pdb`.
     · các khe đều là `clamp(sàn, …vh, trần)` — tablet cao 1080 nên vh đẩy chúng lên gần
       trần (khe 26, meta → ảnh 68). Chốt số cứng cho khổ nhỏ.
   Mốc là 1024 chứ không phải 1023 như các khối khác: iPad NGANG rộng đúng 1024, vẫn là
   tablet nên phải nằm trong khối này. */
@media (max-width: 1024px) {
  .pdb {
    min-height: 0;
    padding-top: calc(var(--topbar-h, 60px) + 40px);
    padding-bottom: 36px;
    gap: 14px;
  }
  .pdb__meta {
    margin-top: 8px;
  }
  .pdb > .pdph {
    margin-top: 20px;
  }
}

/* ---------- TABLET: bốn thẻ thành 2×2 ----------
   `.pdsố` / `.pdbước` / `.pdsl` đều dùng `auto-fit, minmax(...)`. Ở iPad dọc khổ nội dung
   682 nên nó nhét được ĐÚNG 3 cột → bốn thẻ ra 3 trên 1 dưới, lệch hẳn. Bốn thẻ ở tầng
   tablet thì chốt 2 cột cho cân; 3 thẻ vẫn để auto-fit (682/3 = 227 > 210 nên vừa 3 cột).
   PHẢI có `min-width: 768px`: để `max-width` trơn thì mobile cũng thành 2 cột, mà mobile đã
   chốt mỗi hàng một thẻ. */
@media (min-width: 768px) and (max-width: 1024px) {
  .pdsố[data-n="4"],
  .pdbước:not(.pdbước--gọn),
  .pdsl {
    grid-template-columns: 1fr 1fr;
  }
}

/* ---------- TABLET: rút khe giữa các chapter ----------
   Chỉ 768–1024. Desktop và mobile đã chốt nên KHÔNG đụng: mobile có khối riêng ở dưới
   (viết sau nên thắng), desktop không khớp media này.
   Lý do lệch: `.pdch__đầu` lấy `clamp(64px, 11vh, 150px)` — tablet cao 1080 nên 11vh ra
   118.8, cộng đệm đáy mục con 48.6 thành khe chapter → chapter tới 167px, dài hơn cả
   desktop 14" (175 nhưng ở đó cả trang cũng to hơn). Chốt số cứng cho tầng này. */
@media (max-width: 1024px) {
  .pdch__đầu {
    padding: 64px 0 30px;
  }
  .pds {
    padding-block: 38px;
  }
}

/* ---------- MOBILE: rút đệm trên/dưới của các section ----------
   Các số ở trên đều là `clamp(sàn, …vw/vh, trần)`; ở 390 thì vw/vh rất nhỏ nên SÀN là thứ
   quyết định, mà sàn tính cho desktop nên đặt vào điện thoại là hai section cách nhau cả
   trăm px — cuộn mãi mới sang được mục sau. Hạ riêng sàn cho ≤767, desktop không đụng tới.
   Mốc lấy theo nhịp đã chốt ở các trang khác: đầu section ~48 (xem `.mw2`), mục con thì
   một bậc dưới.
   PHẢI ĐẶT SAU mọi rule của `.pdm` / `.pdch__đầu` / `.pds` / `.pdn` — media query không
   cộng thêm chút specificity nào, viết trước là bị rule gốc (cùng độ mạnh, nằm sau) đè lại.
   Đo lần đầu thấy đúng `.pdm` đổi, ba cái kia y nguyên là vì thế. */
@media (max-width: 767px) {
  .pdb {
    padding-top: calc(var(--topbar-h, 60px) + 24px);
    padding-bottom: 28px;
    gap: 12px;
  }
  .pdb > .pdph {
    margin-top: 16px;
  }
  .pdm {
    padding-bottom: 56px;
  }
  .pdch__đầu {
    padding: 52px 0 26px;
  }
  .pds {
    padding-block: 30px;
  }
  .pds > * + *:not(.pds__h):not(.pds__tx) {
    margin-top: 22px;
  }
  /* Hai khối `cặp` gộp làm một thì KHE PHẢI 0 ở mọi khổ — rule mobile ở trên nằm sau rule gộp
     trong file nên phải nhắc lại ở đây, không thì mobile hở lại 22px. */
  .pds > .pdcặp + .pdcặp {
    margin-top: 0;
  }
  .pdn {
    padding-block: 48px;
  }
}

/* Dock và bottom sheet LUÔN là mặt trắng, nên màu chữ của chúng KHÔNG được đi theo bộ token
   light↔dark của vùng chapter. `--pd-mờ` đổi theo `--tối` (khai trên body nên dock cũng thừa
   hưởng) làm nhãn "Chapter N" hoá xanh nhạt khi vào mục nền tối — chốt cứng màu ở đây.
   Viết SAU khối nhãn dùng chung nên thắng, không cần tăng độ mạnh. */
.pddock__no,
.pdnav__đầu p,
.pdnav__ds span {
  color: #8d857a;
}

/* <button> KHÔNG kế thừa font của trang — Chrome cho nó font hệ thống. Đo được: tên
   chapter trong bottom sheet ra Arial 18px trong khi cả trang là Mona Sans. Một dòng này
   phủ mọi nút của dock và của sheet. */
.pddock button,
.pdnav button {
  font-family: inherit;
}
.pddock {
  --pd-dock-h: 92px;
  /* Vòng tròn đen: ref để 0.83 chiều cao dock (76/92) — hồi dock còn là viên thuốc thì
     vừa, nhưng dock giờ bo 24 nên vòng to chỉ chừa 8px trên/dưới, nhìn nặng hẳn một góc.
     Về 0.61 (56/92) → chừa 18px, vòng thành một nút trong thanh chứ không phải cái đè
     cả thanh. Hình bên trong vẫn 26 nên viền quanh hình còn 15px. */
  --pd-dock-nút: 56px;
  /* MỘT khoảng cách cho mọi khe trong dock: lề ngoài, icon → chữ, chữ → kẻ ngăn,
     kẻ ngăn → nút. Bằng đúng khoảng hở trên/dưới của vòng tròn nên nhìn đâu cũng một
     nhịp. Trước khe icon → chữ để 30 (theo ref) trong khi lề chỉ 9–16, nên cụm chữ đọc
     ra như đang trôi giữa vòng tròn và kẻ ngăn thay vì dính vào vòng tròn. */
  --pd-dock-lề: calc((var(--pd-dock-h) - var(--pd-dock-nút)) / 2);
  /* MỘT cỡ hình cho CẢ BA nút (more · lui · tới) — trước icon more tính theo cỡ vòng tròn
     nên ra 32, to hơn hai nút kia (26) và đọc thành hai cỡ icon trong cùng một thanh. */
  --pd-dock-hình: 26px;
  --pd-dock-ô: 44px;
  --pd-dock-tên: 18px;
  position: fixed;
  /* Căn giữa bằng `inset-inline: 0` + `margin-inline: auto`, KHÔNG dùng `left: 50%`:
     với phần tử `fixed`, `left: 50%` làm khoảng trống dành cho nó chỉ còn NỬA màn, nên
     dock rộng hơn nửa màn là bị bóp lại và tên chapter bị cắt "…" — đo ở iPad dọc 810
     ra dock 405 (đúng 50%) trong khi nội dung cần 466. */
  inset-inline: 0;
  bottom: 20px;
  width: -moz-fit-content;
  width: fit-content;
  max-width: calc(100vw - 24px);
  margin-inline: auto;
  z-index: 60;
  display: flex;
  align-items: center;
  height: var(--pd-dock-h);
  /* Lề trái/phải = ĐÚNG khoảng hở trên/dưới của vòng tròn, tức (cao dock − vòng)/2.
     Viết bằng calc từ hai biến kia nên đổi cỡ dock hay cỡ vòng thì lề tự theo, không bao
     giờ lệch. Ref để 9 trái / 16 phải — nhìn ra ngay là trên dưới thoáng hơn hai bên. */
  padding-inline: var(--pd-dock-lề);
  /* Bo theo THANG CỦA SITE (--radius 24), không phải viên thuốc 999 như ref: cả site
     không có hình viên thuốc nào cỡ này. Thanh tiến độ thụt hai đầu = đúng bằng bo góc
     để nằm gọn trong đó. */
  border-radius: var(--pd-bo-lớn);
  background: #fff;
  box-shadow:
    0 12px 34px rgba(0, 0, 0, 0.16),
    0 1px 0 rgba(24, 24, 24, 0.04);
  animation: riseIn 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.78s backwards;
  transition:
    opacity 0.3s ease,
    translate 0.3s ease;
}
.pddock.là-tắt {
  opacity: 0;
  translate: 0 24px;
  pointer-events: none;
}
/* Thanh tiến độ: chạy theo MỤC CON trong chapter đang xem, sang chapter mới thì đặt lại
   (xem `canh()` trong site.js). Chapter chỉ có một mục thì vào là đầy luôn. */
.pddock__thanh {
  position: absolute;
  top: 0;
  left: var(--radius);
  right: var(--radius);
  height: 3px;
  border-radius: 2px;
  background: rgba(24, 24, 24, 0.12);
  overflow: hidden;
}
.pddock__thanh i {
  display: block;
  width: 0;
  height: 100%;
  border-radius: 2px;
  background: #181818;
  transition: width 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
.pddock__ds {
  flex: none;
  width: var(--pd-dock-nút);
  height: var(--pd-dock-nút);
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: #181818;
  color: #fff;
  cursor: pointer;
  transition:
    scale 0.22s ease,
    background-color 0.22s ease;
}
/* Icon 0.42 nút (ref là 0.31 — nhìn trên tổng thể dock 92 thì bé quá). Nút vẫn 76. */
.pddock__ds svg {
  width: var(--pd-dock-hình);
  height: var(--pd-dock-hình);
}
/* Rê chuột: nền XANH BRAND #0876B7 cho rõ — cùng màu hover đã chốt ở link trang About và
   nút Project detail của modal. Ba nút trong dock và nút X của sheet dùng chung một cách. */
.pddock__ds:hover {
  background: #0876b7;
}
.pddock__nhãn {
  display: grid;
  gap: 4px;
  min-width: 0;
  padding-inline: var(--pd-dock-lề);
}
.pddock__tên {
  display: grid;
  min-width: 200px;
}
/* Tất cả tên chapter chồng CÙNG MỘT Ô → ô rộng bằng tên dài nhất; `visibility` (không
   phải `display: none`) để tên đang ẩn vẫn tính vào bề rộng. */
.pddock__tên b {
  grid-area: 1 / 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  visibility: hidden;
  font-size: var(--pd-dock-tên);
  font-weight: 500;
  line-height: 1.3;
  color: #181818;
}
.pddock__tên b.là-nay {
  visibility: visible;
}
.pddock__nút {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  gap: 4px;
  padding-left: var(--pd-dock-lề);
}
/* Kẻ ngăn: vẽ bằng ::before để cao đúng 0.62 chiều cao dock như ref, chứ không dùng
   `border-left` — border chỉ cao bằng cái nút (0.48) nên trông như một gạch cụt. */
.pddock__nút::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  translate: 0 -50%;
  width: 1px;
  height: calc(var(--pd-dock-h) * 0.62);
  background: rgba(24, 24, 24, 0.14);
}
.pddock__nút button {
  /* Ô nút GIỮ NGUYÊN kích thước, chỉ hình bên trong to lên → phải tách thành hai biến,
     không để ô tính theo hình như trước. */
  width: var(--pd-dock-ô);
  height: var(--pd-dock-ô);
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: #181818;
  cursor: pointer;
  transition:
    background-color 0.2s ease,
    color 0.2s ease;
}
.pddock__nút button svg {
  width: var(--pd-dock-hình);
  height: var(--pd-dock-hình);
}
.pddock__nút button:hover {
  background: #0876b7;
  color: #fff;
}
/* Hết chapter ở hai đầu thì nút mờ đi — đúng như ref (ở chapter 1 nút lùi đang nhạt). */
.pddock__nút button[disabled] {
  color: rgba(24, 24, 24, 0.28);
  cursor: default;
}
.pddock__nút button[disabled]:hover {
  background: none;
}
/* Thang theo khổ màn: 14 inch trở lên giữ đúng số đo ref, nhỏ dần xuống tablet và mobile */
@media (max-width: 1279px) {
  .pddock {
    --pd-dock-h: 80px;
    --pd-dock-nút: 50px;
    --pd-dock-hình: 23px;
    --pd-dock-ô: 40px;
    --pd-dock-tên: 17px;
  }
  .pddock__tên {
    min-width: 180px;
  }
}
@media (max-width: 1023px) {
  .pddock {
    --pd-dock-h: 76px;
    --pd-dock-nút: 48px;
    --pd-dock-hình: 22px;
    --pd-dock-ô: 37px;
    --pd-dock-tên: 16px;
  }
  .pddock__tên {
    min-width: 160px;
  }
  .pddock__thanh {
    height: 2px;
  }
}
@media (max-width: 767px) {
  .pddock {
    --pd-dock-h: 68px;
    --pd-dock-nút: 44px;
    --pd-dock-hình: 20px;
    --pd-dock-ô: 35px;
    --pd-dock-tên: 15px;
    /* Điện thoại: dock dàn hết bề ngang, chỉ chừa 12 mỗi bên */
    width: auto;
    max-width: none;
    margin-inline: 12px;
  }
  /* Điện thoại: dock chiếm gần hết bề ngang nên bề rộng vẫn CỐ ĐỊNH (bằng chỗ còn lại),
     tên dài thì cắt bằng "…" — vẫn không co kéo giữa các chapter. */
  .pddock__tên {
    min-width: 0;
  }

}
@media (prefers-reduced-motion: reduce) {
  .pddock {
    animation: none;
  }
}

/* ---------- 6. BOTTOM SHEET CONTENT NAVIGATION ----------
   Nằm TRONG dock: `inset-inline: 0` nên rộng đúng bằng dock, `bottom: 0` nên mọc LÊN và
   đè kín dock. KHÔNG có lớp nền mờ — đúng ref ref-dock-click-2.png.
   Số đo từ ref (14 inch): bo 32 · thụt hàng 9.5 · hàng cao 74 · bo hàng 28 ·
   đệm trong hàng 28 · nhãn mono 12 · tên chapter 18. */
.pdnav {
  /* MỘT cột nội dung cho cả sheet: tiêu đề, mọi hàng chapter và HÌNH trong nút X đều
     thẳng cột này. `--pm-x` khai ở đây (không phải ở `.pdnav__x`) để hàng tiêu đề tính
     được lề phải theo cỡ nút. */
  --pd-sheet-pad: 28px;
  --pm-x: 36px;
  --pm-x-t: 0px;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 9px;
  border-radius: var(--pd-bo-lớn);
  background: #fff;
  box-shadow:
    0 12px 34px rgba(0, 0, 0, 0.16),
    0 1px 0 rgba(24, 24, 24, 0.04);
  opacity: 0;
  translate: 0 10px;
  pointer-events: none;
  transition:
    opacity 0.24s ease,
    translate 0.26s cubic-bezier(0.22, 1, 0.36, 1);
}
.pdnav.là-mở {
  opacity: 1;
  translate: 0 0;
  pointer-events: auto;
}
/* Hàng tiêu đề: chữ thẳng cột nội dung bên trái, còn bên phải phải trừ đi phần đệm rỗng
   trong hộp nút X — (cỡ nút − 18 của hình) / 2 — để HÌNH X thẳng cột, không phải cái hộp.
   Trước để `padding-right: 0` nên hình X chỉ cách mép sheet 18 trong khi chữ cách 37,
   nhìn ra ngay là hai đầu lệch nhau. */
.pdnav__đầu {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px calc(var(--pd-sheet-pad) - (var(--pm-x) - 18px) / 2) 12px var(--pd-sheet-pad);
}
/* Nút X DÙNG LẠI `.pm__x` của modal xem nhanh (cỡ, nền kính xám, X → dấu trừ khi rê) nên
   ở đây chỉ tắt phần định vị tuyệt đối của nó: trong sheet nó nằm trong hàng tiêu đề chứ
   không neo góc thẻ. Token cỡ `--pm-x` / `--pm-x-t` khai trong `.pm` nên phải khai lại. */
.pdnav__x {
  position: static;
}
.pdnav__ds {
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: min(56svh, 620px);
  overflow: auto;
  overscroll-behavior: contain;
}
.pdnav__ds button {
  width: 100%;
  min-height: 74px;
  display: grid;
  align-content: center;
  gap: 4px;
  padding: 10px var(--pd-sheet-pad);
  border: 0;
  border-radius: var(--pd-bo-vừa);
  background: none;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.2s ease;
}
.pdnav__ds b {
  font-size: 18px;
  font-weight: 500;
  line-height: 1.3;
  color: #181818;
}
.pdnav__ds button:hover {
  background: rgba(24, 24, 24, 0.05);
}
.pdnav__ds button.là-nay:hover {
  background: rgba(8, 118, 183, 0.11);
}
/* Chapter ĐANG XEM — theo đúng cách site đánh dấu "đang ở đây" trong menu mở rộng
   (`.panel__list li.is-current`): đổi MÀU + đậm hơn + một chấm nhỏ, KHÔNG tô khối nền.
   Bản trước tô nền kem `#F0E9DD` lấy từ ref: nền kem đó đọc ra "một ô nội dung khác màu"
   chứ không ra "mục đang chọn", mà màu cam nhạt cũng lạc khỏi bộ màu brand. */
.pdnav__ds button.là-nay {
  /* nền XANH RẤT NHẠT của brand — cùng cách pha với khối Trade-off `.pdđổi` */
  background: rgba(8, 118, 183, 0.07);
}
.pdnav__ds button.là-nay span,
.pdnav__ds button.là-nay b {
  color: #0876b7;
}
.pdnav__ds button.là-nay b {
  font-weight: 600;
}
.pdnav__ds button.là-nay b::after {
  content: "";
  display: inline-block;
  width: 0.24em;
  height: 0.24em;
  margin-left: 9px;
  vertical-align: 0.22em;
  border-radius: 50%;
  background: currentColor;
}
@media (max-width: 1279px) {
  .pdnav__ds button {
    min-height: 68px;
  }
  .pdnav__ds b {
    font-size: 17px;
  }
}
@media (max-width: 1023px) {
  .pdnav {
    --pd-sheet-pad: 22px;
  }
  .pdnav__ds button {
    min-height: 64px;
  }
  .pdnav__ds b {
    font-size: 16px;
  }
}
@media (max-width: 767px) {
  .pdnav {
    --pd-sheet-pad: 16px;
    --pm-x: 32px;
    padding: 8px;
    border-radius: var(--pd-bo);
  }
  .pdnav__đầu {
    padding-block: 8px;
  }
  .pdnav__ds button {
    min-height: 58px;
    padding-block: 9px;
  }
  .pdnav__ds b {
    font-size: 15px;
  }
}

/* ============================================================
   TRANG MY WORK (my-work.html)
   Dựng theo reference/mywork/. Hai section chồng nhau như hero ↔ About ở trang chủ:
   section 1 DÍNH đỉnh, section 2 nền đen trượt ĐÈ LÊN nó.
   Font: CHỈ Mona Sans (--font-display / --font-body). Không dùng font mono hệ thống —
   trước có dùng và nó lạc hẳn khỏi cả site.
   KHÔNG có đường kẻ ngăn nào: nền đã có lưới nên thêm kẻ là nhiễu.
   ============================================================ */
body.trang-mw {
  background: var(--ab-bg);
}
/* Section 1 nền sáng ⇒ logo bản đen. Sang section 2 nền đen thì JS bật lại bản trắng
   (xem khối "trang-mw" trong site.js). */
body.trang-mw .menu,
body.trang-mw.has-scrolled .menu,
body.trang-mw.is-scrolled .menu {
  color: #111;
}
body.trang-mw .brand-ha img:not(.brand-ha__đen) {
  opacity: 0;
}
body.trang-mw .brand-ha__đen {
  opacity: 1;
}
/* trên nền ĐEN của section 2 thì trả về logo trắng + icon menu trắng */
body.trang-mw.mw-tối .menu,
body.trang-mw.mw-tối.has-scrolled .menu,
body.trang-mw.mw-tối.is-scrolled .menu {
  color: #fff;
}
body.trang-mw.mw-tối .brand-ha img:not(.brand-ha__đen) {
  opacity: 1;
}
body.trang-mw.mw-tối .brand-ha__đen {
  opacity: 0;
}
.mw {
  /* KHỚP với lề của section Contact (`--ft-lề`) để hai section thẳng mép nhau.
     Contact khai `--ft-lề` bên trong `.footer` nên không kế thừa ra ngoài được — phải
     lặp lại đúng cái thang đó ở đây, xem `.footer` để đối chiếu:
       gốc  clamp(12px, 2.2vw, 32px) · ≥1280 → 88 · ≥1728 → 120 · ≤767 → 12 */
  --mw-lề: clamp(12px, 2.2vw, 32px);
  position: relative;
}
/* iPad (768–1279): lấy ĐÚNG `--sec-pad` của các section trang chủ. Đo trên trang chủ ở
   1024 / 810 / 768 đều ra **64px** (`calc(2.5rem * 1.6)`), trong khi thang của Contact chỉ
   cho 17–23 — lệch hẳn. Trang chủ cũng vậy: Contact có lề riêng 4 cạnh đều, các section
   khác dùng --sec-pad; ở đây làm y thế. */
@media (min-width: 768px) {
  .mw {
    --mw-lề: var(--sec-pad);
  }
}
/* Từ 1280 lên KHÔNG đặt số riêng — `--sec-pad` đã lo (140 ở 1280–1727, rồi giãn theo màn
   từ 1728 lên để khổ nội dung chốt ở 1560), giống hệt các section trang chủ. */
@media (max-width: 767px) {
  .mw {
    --mw-lề: 12px;
  }
}

/* ---------- SECTION 1 — DÍNH ĐỈNH, section 2 trượt đè lên ---------- */
.mw1 {
  position: sticky;
  top: 0;
  z-index: 0;
  min-height: 100svh;
  display: grid;
  /* 1fr trên + đáy auto: cụm tiêu đề nằm GIỮA phần còn lại, không phải giữa cả
     section — nếu giữa cả section thì cụm đáy đẩy nó lệch lên. */
  grid-template-rows: 1fr auto;
  padding: calc(var(--topbar-h, 60px) + 24px) var(--mw-lề) 40px;
  overflow: hidden;
  /* lưới kẻ 97 y hệt section About */
  background-image:
    linear-gradient(90deg, var(--ab-line) 0 2px, transparent 2px),
    linear-gradient(180deg, var(--ab-line) 0 2px, transparent 2px);
  background-size: var(--ab-cell) var(--ab-cell);
  background-position: 50px 80px;
  background-color: var(--ab-bg);
}
.mw1__giữa {
  align-self: center;
}
/* Section 2 trượt đè lên section 1 ⇒ nội dung section 1 phải ĐẨY LÊN và tan dần, y như
   `.hero__cluster` ở trang chủ. Không có phần này thì section 1 nằm im, cảm giác section 2
   chỉ là một tấm che. `translate` (thuộc tính riêng) để không đè lên transform nào khác. */
.mw1__giữa,
.mw1__đáy {
  transition:
    translate 0.6s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.45s ease;
}
/* Mốc riêng `mw-đẩy`, KHÔNG dùng `mw-tối`: `mw-tối` chỉ bật lúc dải 60px trên cùng của
   màn chạm nền đen — tới lúc đó section 2 đã che gần hết, cú đẩy diễn ra sau tấm che nên
   không ai thấy. Đó chính là lỗi "chưa bị đẩy lên". */
body.mw-đẩy .mw1__giữa {
  translate: 0 -60%;
  opacity: 0;
}
body.mw-đẩy .mw1__đáy {
  translate: 0 -40%;
  opacity: 0;
}
.mw1__h1 {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(56px, 11.5vw, 168px);
  /* 700 và −0.01em — cùng thang với headline "Hi, I'm Thu Ha" (48/700) */
  font-weight: 700;
  line-height: 0.98;
  letter-spacing: -0.01em;
}
.mw1__tx {
  /* cùng gradient với headline "Hi, I'm Thu Ha" ở section About */
  background: linear-gradient(90deg, #014268, #0d9bee);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* ĐUÔI CHỮ "y" BỊ CẮT — phải nới HỘP NỀN, không phải sửa font.
     `background-clip: text` + `color: transparent` nghĩa là chữ chỉ được tô ở phần nằm
     TRONG hộp nền; `line-height: 0.98` (< 1) làm hộp nền thấp hơn hộp chữ, đo ở 1440 ra
     chữ thò xuống dưới hộp nền đúng 36px trên cỡ 165.6 (= 0.22em) — đúng bằng chỗ đuôi
     "y" mất tích. Chữ "k" cũng thò lên trên 36px như thế.
     Nới `padding` 0.22em hai đầu để hộp nền phủ hết, rồi `margin` âm cùng lượng để chiều
     cao bố cục không đổi — nếu không cả cụm tiêu đề sẽ tụt xuống 73px. */
  padding-block: 0.22em;
  margin-block: -0.22em;
}
.mw1__nam {
  flex: none;
  margin-top: 0.42em;
  font-family: var(--font-body);
  font-size: clamp(11px, 1vw, 15px);
  font-weight: 500;
  letter-spacing: 0.08em;
  color: color-mix(in srgb, var(--text) 52%, transparent);
}
.mw1__sub {
  margin: clamp(10px, 1.4vw, 20px) 0 0;
  font-family: var(--font-display);
  font-size: clamp(20px, 2.6vw, 38px);
  font-weight: 500;
  line-height: 1.25;
  color: var(--text);
}
.mw1__đáy {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: 32px;
  margin-top: clamp(28px, 4vw, 56px);
}
.mw1__lbl {
  margin: 0 0 8px;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--text) 50%, transparent);
}
.mw1__note {
  margin: 0;
  font-size: clamp(15px, 1.25vw, 18px);
  line-height: 1.5;
  color: var(--text);
}
.mw1__ba {
  display: flex;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.mw1__ba li {
  flex: none;
  width: clamp(120px, 13vw, 192px);
}
.mw1__ba a {
  display: block;
  border-radius: 8px;
  overflow: hidden;
  transition: scale 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.mw1__ba a:hover {
  scale: 1.04;
}
.mw1__ba img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

/* ---------- SECTION 2 — NỀN ĐEN, trượt đè lên section 1 ---------- */
.mw2 {
  position: relative;
  z-index: 1;
  padding: clamp(48px, 8vw, 120px) var(--mw-lề) 0;
  background: #181818;
  color: #fff;
}
/* Thân section 2 KHÔNG có lưới. Chỉ một DẢI ĐÁY cao đúng 2 ô (194px) mờ dần
   0 → 100% để nối liền vào lưới của section Contact.
   Ba điều kiện để hai lưới KHỚP NHAU, thiếu một là lệch ngay chỗ giáp ranh:
     · cùng `background-size: var(--ab-cell)` (97);
     · cùng OFFSET NGANG = 0 — Contact dùng `0 var(--grid-y)`, không phải 50px như
       About / Work / My journey. Bản trước mình để 50px nên hai lưới lệch nhau đúng 50;
     · chiều cao dải là BỘI SỐ của 97 (194 = 2 ô) nên pha ở mép dưới dải trùng pha ở mép
       trên nó, tức trùng pha mà Contact bắt đầu.
   Trang này không có #about nên bộ khớp pha trong site.js thoát sớm, `--grid-y` của
   Contact rơi về 0 — dải này cũng để 0, khớp. */
.mw2::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--ab-cell) * 2);
  pointer-events: none;
  background-image:
    linear-gradient(90deg, var(--tối-line) 0 1px, transparent 1px),
    linear-gradient(180deg, var(--tối-line) 0 1px, transparent 1px);
  background-size: var(--ab-cell) var(--ab-cell);
  background-position: 0 var(--grid-y, 0px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 100%);
}
.mw2 > * {
  position: relative;
  z-index: 1;
}
/* KHÔNG chốt cứng bề rộng nữa. Bề rộng nội dung do `--mw-lề` (= `--sec-pad`) quyết định,
   y như mọi section ở trang chủ. Trước chốt 980 nên màn 27" nội dung bó lại giữa hai dải
   trống rất rộng, không giống trang chủ.
   Từ 1728 lên vẫn có trần 1560 — đúng bằng khổ nội dung mà `--sec-pad` chốt cho trang
   chủ; `margin-inline: auto` để khối nằm giữa khi đã chạm trần. */
.mw2 > * {
  width: 100%;
  margin-inline: auto;
}
/* Trần riêng cho dải 1280–1727: `--sec-pad` ở đây là 140 nên khổ nội dung ra 1160 ở
   1440 — thẻ to hơn mức Thu Hà chốt. Giữ 980 trong dải này, còn từ 1728 lên thì cho giãn
   tới đúng khổ 1560 của trang chủ. */
@media (min-width: 1280px) and (max-width: 1727px) {
  .mw2 > * {
    max-width: 980px;
  }
}
@media (min-width: 1728px) {
  .mw2 > * {
    max-width: 1560px;
  }
}
/* Cỡ chữ theo ĐÚNG thang tiêu đề section của site (xem `.work__title`, `.about__title`,
   `.jr__title`): ≥1440 → 48 · 810–1439 → 40 · 391–809 → 32 · ≤390 → 28, weight 700,
   line-height 1.16, letter-spacing −0.01em.
   Bản trước để `clamp(30px, 4.4vw, 64px)` weight 500 — đo ra 64px ở 1440 trong khi tiêu
   đề Selected Work / My journey chỉ 48, to hơn hẳn và lạc thang. */
.mw2__h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 48px;
  font-weight: 700;
  line-height: 1.16;
  letter-spacing: -0.01em;
  color: #fff;
}
/* Thang giảm dần theo khổ, bước nhỏ để không nhảy cục:
     ≥1440 → 48 · 1280 → 44 · 1024 → 40 · 810 → 36 · 768–809 → 32
   Từ 767 xuống thì KHÔNG dùng số cố định nữa: tiêu đề có <br> ngắt cứng nên dòng
   "Here are the stories, decisions," (31 ký tự) phải lọt MỘT dòng, không thì ra 4 dòng.
   Chia bề rộng lòng màn cho 15.5 là ra cỡ chữ lớn nhất còn giữ được ba dòng — đo ở
   767/600/500/430/390/375/360 đều đúng 3 dòng. */
@media (max-width: 1439px) {
  .mw2__h2 {
    font-size: 44px;
  }
}
@media (max-width: 1279px) {
  .mw2__h2 {
    font-size: 40px;
  }
}
@media (max-width: 1023px) {
  .mw2__h2 {
    font-size: 36px;
  }
}
@media (max-width: 809px) {
  .mw2__h2 {
    font-size: 32px;
  }
}
@media (max-width: 767px) {
  .mw2__h2 {
    font-size: min(30px, calc((100vw - 2 * var(--mw-lề)) / 15.5));
  }
}
.mw2__h2 span {
  display: block;
  color: rgba(255, 255, 255, 0.46);
}
.mw2__head {
  padding-bottom: clamp(22px, 2.6vw, 38px);
}
.mw2__khay {
  display: grid;
  gap: clamp(20px, 2.4vw, 34px);
  padding-top: clamp(10px, 1.4vw, 20px);
}
/* Hiệu ứng vào của section 2: CHỈ mờ dần, KHÔNG trượt và KHÔNG lệch nhịp.
   Bản trước dùng nguyên `.rise` (trượt 56px + delay theo --d) — với danh sách 8 dự án
   thì thẻ dưới phải chờ tới 0.63s, và cú trượt làm chiều cao nhảy, nhìn ra thành lỗi. */
.mw2 .rise {
  translate: none;
  transition: opacity 0.55s ease;
  transition-delay: 0s;
}
.mw2 .rise.in {
  translate: none;
}
.mw2__khay[hidden] {
  display: none;
}

/* --- CÁCH XEM 1: thẻ ngang 16:9, cụm chữ ĐÈ LÊN ảnh bằng lớp kính (desktop + tablet) ---
   Tỉ lệ 827/361 đo THẲNG trên ref (khung 1440) — KHÔNG phải 16:9. */
.mwc {
  position: relative;
  aspect-ratio: 827 / 361;
  border-radius: 16px;
  overflow: hidden;
  isolation: isolate;
}
.mwc__shot {
  position: absolute;
  inset: 0;
}
.mwc__shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Kính TỐI (`rgba(24,24,24,.34)`), không phải `.glass` sáng của dock: ảnh dự án có bản
   rất sáng (nền kem) — kính sáng + chữ trắng là sáng trên sáng, dòng eyebrow mất hẳn.
   ÁP SÁT ba mép phải / trên / dưới, đè lên thumbnail — ref không có khe nào.
   Bề rộng 379/827 = 45.8% cũng đo trên ref. */
.mwc__info {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 45.8%;
  display: flex;
  flex-direction: column;
  /* Khe đo trên ref (quy về khung 1440): nhãn → giá trị 18 · giữa hai cụm 27–28 ·
     headline → cụm thông tin 42. Bản trước để 10/12/4 nên cả cụm bị dồn cục. */
  gap: 16px;
  /* Dải CHUYỂN TIẾP từ ảnh sang kính. Khai bằng PX, không phải %: `padding` tính % theo bề rộng
     khối CHỨA (cả thẻ), còn gradient tính % theo bề rộng của chính khối này — hai con số 12% sẽ
     không bằng nhau, chữ lại nằm trên vùng chưa đủ đặc. */
  --mwc-mờ: clamp(40px, 4vw, 64px);
  padding: clamp(16px, 1.6vw, 24px);
  /* Chữ bắt đầu SAU khi dải chuyển đã đạt 100%, cộng thêm đúng phần đệm thường — nhờ vậy không
     có chữ nào nằm trên vùng kính còn trong. */
  padding-left: calc(var(--mwc-mờ) + clamp(16px, 1.6vw, 24px));
  color: #fff;
  overflow: auto;
  scrollbar-width: none;
}
.mwc__info::-webkit-scrollbar {
  display: none;
}
/* KÍNH nằm ở LỚP RIÊNG (::before), không đặt trên chính khối chữ. Lý do: mép trái phải chuyển
   dần từ ảnh sang kính (không còn `border-left` cứng), mà `mask` đặt trên khối chữ thì nó mờ
   luôn cả CHỮ và NÚT ở mép trái — đo lần đầu thấy chữ "Turning…" và nút Quick look bị bợt đúng
   dải chuyển. Nay `mask` chỉ ăn vào lớp kính; chữ giữ opacity 1.
   Dải chuyển 12% bề rộng khối (≈54px ở thẻ 449) — đủ ngắn để chữ vẫn nằm trọn trên kính đặc. */
.mwc__info::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(90deg, rgba(24, 24, 24, 0) 0, rgba(24, 24, 24, 0.34) var(--mwc-mờ));
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--mwc-mờ));
  mask-image: linear-gradient(90deg, transparent 0, #000 var(--mwc-mờ));
}
/* Chữ và nút nằm TRÊN lớp kính */
.mwc__info > * {
  position: relative;
  z-index: 1;
}
.mwc__eyebrow {
  margin: 0;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.82);
}
.mwc__h3 {
  margin: 0;
  font-family: var(--font-display);
  /* 22 và chặn 3 dòng: thẻ có tỉ lệ CỐ ĐỊNH nên cụm chữ chỉ có ~309px chiều cao dùng
     được. Headline Event Order dài 77 ký tự, ở 26px ăn 4 dòng ⇒ đẩy hai nút xuống dưới
     mép, đo trên ảnh chụp thì mất hẳn nút.
     `flex: none` bắt buộc: `.mwc__info` là flex column nên con CO LẠI được — đo ra hộp
     chỉ còn 2 dòng dù đã cho phép 3. */
  /* THANG theo khổ máy, không clamp theo vw:
       ≥1512 (MacBook 14" và lớn hơn) → 24
       1280–1511 (13")                → 20
       tablet (≤1023, hoặc ≤1279 màn dọc) → 18   ← ở khối TABLET phía dưới
       mobile ≤767                     → dùng `.mwgọn__name` 16, không phải rule này */
  font-size: 24px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  flex: none;
}
/* 13 inch (1440×900 là khổ MacBook Air/Pro 13") — cụm chữ chỉ còn 449px nên 24 hơi to */
@media (max-width: 1511px) {
  .mwc__h3 {
    font-size: 20px;
  }
}
/* ---------- TOOLTIP tên đầy đủ khi TIÊU ĐỀ THẺ NGANG bị cắt ----------
   Tiêu đề chặn 3 dòng nên câu dài bị "…"; JS gắn class `là-cắt` cho đúng thẻ bị cắt rồi hiện
   tooltip này khi hover/focus. Đặt `position: fixed` + `translate` do JS tính nên không bị cắt
   bởi `overflow` của thẻ và không đẩy layout. */
.mwc__h3.là-cắt {
  cursor: help;
}
.mwmẹo {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9700;
  max-width: min(420px, calc(100vw - 32px));
  padding: 10px 14px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--pd-bo-vừa, 14px);
  background: rgba(12, 24, 34, 0.92);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  backdrop-filter: blur(10px) saturate(118%);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.28);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.45;
  color: #fff;
  pointer-events: none;
}
.mwmẹo[hidden] {
  display: none;
}

.mwc__meta {
  display: grid;
  gap: 18px;
  margin: auto 0 0;
}
.mwc__đôi {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 16px;
}
/* Cùng cách: viết đúng như gõ, căn theo `.pm__meta dt` của modal */
.mwc__meta dt {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.6);
}
.mwc__meta dd {
  margin: 6px 0 0;
  font-size: 14px;
  line-height: 1.4;
  color: #fff;
}
/* NỐI KHỐI như ref: một khối trắng, hai nửa ngăn nhau bằng kẻ dọc. Chỉ khác ref hai chỗ
   — chữ ("Quick look" / "Project detail") và bo góc 12 thay cho 999. */
.mwc__nút {
  /* `flex: none` bắt buộc: `.mwc__info` là flex column nên khối nút CO LẠI được — ở 810
     đo ra nó bị bóp còn một vạch trắng mỏng, mất hẳn hai nút. */
  flex: none;
  display: flex;
  border-radius: 12px;
  overflow: hidden;
  background: #fff;
}
.mwc__b {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 10px;
  border: 0;
  background: none;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  color: #111;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s;
}
.mwc__b + .mwc__b {
  border-left: 1px solid rgba(0, 0, 0, 0.12);
}
/* Bốn vòng màu loang kín nút rồi kết ở XANH, chữ và mũi tên hoá TRẮNG — chép đúng cơ chế
   `.seemore__ink` của nút "More about ThuHa" và `.pm__cta-ink` của nút Project Detail
   trong modal: tâm vòng ở ĐÁY GIỮA, Ø = 200% BỀ RỘNG NÚT (số %, không phải px cố định),
   bốn vòng lệch nhau 0.11s, chữ đổi màu trễ 0.32s để đợi vòng xanh loang tới.
   `overflow: hidden` phải đặt trên CHÍNH nút: khối `.mwc__nút` cũng có overflow nhưng nó
   bọc cả hai nút, không cắt được vòng màu theo từng nửa. */
.mwc__b {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.mwc__ink {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 0;
}
.mwc__ink i {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 200%;
  aspect-ratio: 1;
  margin: 0 0 -100% -100%;
  border-radius: 50%;
  scale: 0;
  transition: scale 0.6s cubic-bezier(0.32, 0.9, 0.36, 1);
  transition-delay: 0s;
}
.mwc__ink i:nth-child(1) {
  background: #ea6963;
}
.mwc__ink i:nth-child(2) {
  background: #f2cc3d;
}
.mwc__ink i:nth-child(3) {
  background: #69c465;
}
.mwc__ink i:nth-child(4) {
  background: #0e9af0;
}
.mwc__b:hover .mwc__ink i,
.mwc__b:focus-visible .mwc__ink i {
  scale: 1;
}
.mwc__b:hover .mwc__ink i:nth-child(2),
.mwc__b:focus-visible .mwc__ink i:nth-child(2) {
  transition-delay: 0.16s;
}
.mwc__b:hover .mwc__ink i:nth-child(3),
.mwc__b:focus-visible .mwc__ink i:nth-child(3) {
  transition-delay: 0.32s;
}
.mwc__b:hover .mwc__ink i:nth-child(4),
.mwc__b:focus-visible .mwc__ink i:nth-child(4) {
  transition-delay: 0.48s;
}
/* Nhãn + mũi tên nằm TRÊN lớp vòng màu. Mũi tên vẽ bằng currentColor nên tự hoá trắng
   theo chữ, không cần rule riêng. */
.mwc__btx {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: color 0.26s ease 0.52s;
}
.mwc__b:hover .mwc__btx,
.mwc__b:focus-visible .mwc__btx {
  color: #fff;
}
.mwc__b svg {
  flex: none;
  width: 16px;
  height: 16px;
  transition: translate 0.26s cubic-bezier(0.22, 1, 0.36, 1);
}
.mwc__b:hover svg {
  translate: 3px 0;
}

/* --- CÁCH XEM 2: bảng liệt kê 6 cột (desktop + tablet) --- */
.mwt {
  display: grid;
  font-family: var(--font-body);
  font-size: 15px;
}
.mwt__đầu,
.mwt__hàng {
  display: grid;
  grid-template-columns: minmax(0, 2.3fr) 0.85fr 0.5fr minmax(0, 1.85fr) 0.9fr 132px;
  gap: 16px;
  align-items: center;
}
/* Nhãn cột viết ĐÚNG NHƯ GÕ — "Project / Role / Year / Responsibility / Credit /
   Detail Status", KHÔNG `text-transform: uppercase`. Căn theo `.pm__meta dt` của modal
   xem nhanh (12px, letter-spacing .04em, không in hoa, không đậm) — đó là bộ đã chốt;
   ref cũng viết chữ thường. */
.mwt__đầu {
  padding: 0 0 14px;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.5);
}
/* CẢ HÀNG là một nút: rê chuột thì cả dòng sáng lên và cột cuối đổi từ viên nhãn trạng
   thái sang "Quick View →". Bấm đâu trong dòng cũng mở modal. */
.mwt__hàng {
  width: calc(100% + 24px);
  padding: 14px 12px;
  margin-inline: -12px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
/* Lớp KÍNH khi rê — cùng công thức `.wrow::before` của section Selected Work ở trang chủ
   (`rgba(255,255,255,.2)` + `blur(10px) saturate(118%)`), KHÔNG bo góc.
   Vẽ bằng ::before đặt tuyệt đối chứ không đổi `background` của hàng: `backdrop-filter`
   trên chính hàng sẽ làm nhoè luôn chữ và ảnh BÊN TRONG nó. Vì thế các ô con phải
   `position: relative; z-index: 1` để nằm TRÊN lớp kính — chỗ này đã mắc một lần ở
   `.wrow__a`, xem chú thích bên đó. */
.mwt__hàng {
  position: relative;
  border-radius: 0;
}
.mwt__hàng::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(10px) saturate(118%);
  -webkit-backdrop-filter: blur(10px) saturate(118%);
  opacity: 0;
  transition: opacity 0.28s ease;
  pointer-events: none;
  z-index: 0;
}
.mwt__hàng:hover::before,
.mwt__hàng:focus-visible::before {
  opacity: 1;
}
.mwt__hàng > * {
  position: relative;
  z-index: 1;
}
/* Kẻ ngăn TRẮNG 20% giữa các dự án — chỉ ở dạng BẢNG CỘT (bảng này chỉ tồn tại từ 768
   trở lên; mobile dùng bản gọn nên không có kẻ nào). Không kẻ trên hàng đầu. */
.mwt__hàng + .mwt__hàng {
  border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.mwt__prj {
  display: flex;
  align-items: center;
  gap: 16px;
  min-width: 0;
}
.mwt__shot {
  flex: none;
  width: 96px;
  border-radius: 6px;
  overflow: hidden;
}
.mwt__shot img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
/* TÊN dự án ở TRÊN (16px, cùng màu với cột Role), LOẠI dự án ở DƯỚI (12px, nhạt hơn).
   Bản trước ngược lại — loại ở trên, tên ở dưới. */
.mwt__tên b {
  display: block;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.82);
}
.mwt__tên small {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.55);
}
/* Bốn cột còn lại lấy ĐÚNG cỡ chữ của dòng "project type" (12px) — cùng cỡ với hàng nhãn
   ở đầu bảng luôn, nên cả bảng chỉ còn HAI bậc chữ: tên dự án 16 và mọi thứ khác 12. */
.mwt__role,
.mwt__year,
.mwt__credit,
.mwt__resp {
  font-size: 12px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.82);
}
.mwt__resp {
  color: rgba(255, 255, 255, 0.72);
}
.mwt__st {
  justify-self: start;
}
/* Viên nhãn trạng thái — hiện ở trạng thái thường (ref `section2-listing view.png`).
   Ref là nền kem nên dùng màu đậm; ở đây nền #181818 nên lấy bản sáng hơn. */
.mwpill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.06);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  font-style: normal;
  white-space: nowrap;
}
.mwpill::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.mwpill--showcase {
  color: #63d68a;
}
.mwpill--quickview {
  color: #e6c65a;
}
.mwpill--ongoing {
  color: #f0a05a;
}
.mwpill--none {
  color: rgba(255, 255, 255, 0.5);
}
/* "Quick View →" — CHỈ hiện khi rê vào HÀNG, thay chỗ viên nhãn
   (ref `section2-listingview-hover.png`) */
.mwqv {
  display: none;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  white-space: nowrap;
  color: #fff;
}
.mwqv svg {
  width: 18px;
  height: 18px;
  transition: translate 0.26s cubic-bezier(0.22, 1, 0.36, 1);
}
.mwt__hàng:hover .mwpill {
  display: none;
}
.mwt__hàng:hover .mwqv {
  display: inline-flex;
}
.mwt__hàng:hover .mwqv svg {
  translate: 4px 0;
}

/* --- BẢN GỌN: CHỈ Ở MOBILE (≤767) ---
   Thẻ DỌC, thumbnail VUÔNG, chỉ Role · Tên dự án · Năm — cùng nội dung với thẻ dự án ở
   góc hero trang chủ. Bấm cả thẻ là mở modal xem nhanh.
   Ở desktop và tablet KHÔNG hiện: hai khổ đó giữ nguyên bản đã chốt theo ref. */
.mwgọn,
.mwgọn-khay {
  display: none;
}
@media (max-width: 767px) {
  /* tắt bản đầy đủ */
  .mwc__info,
  .mwt {
    display: none;
  }
  .mwc {
    aspect-ratio: auto;
    border-radius: 0;
    overflow: visible;
  }
  .mwc__shot {
    display: none;
  }
  .mwgọn-khay {
    display: grid;
    gap: 10px;
  }
  /* thẻ gọn */
  .mwgọn {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "shot shot"
      "txt go";
    gap: 12px;
    width: 100%;
    padding: 12px;
    border-radius: 14px;
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(16px) saturate(140%);
    -webkit-backdrop-filter: blur(16px) saturate(140%);
    border: 1px solid rgba(255, 255, 255, 0.16);
    color: #fff;
    text-align: left;
    cursor: pointer;
  }
  .mwgọn__shot {
    grid-area: shot;
    border-radius: 10px;
    overflow: hidden;
  }
  .mwgọn__shot img {
    display: block;
    width: 100%;
    aspect-ratio: 1; /* THUMBNAIL VUÔNG */
    object-fit: cover;
  }
  .mwgọn__txt {
    grid-area: txt;
    min-width: 0;
  }
  .mwgọn__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    font-family: var(--font-body);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.72);
  }
  .mwgọn__name {
    display: block;
    margin-top: 5px;
    font-family: var(--font-display);
    /* bậc cuối của thang: 24 → 20 → 18 → 16 */
    font-size: 16px;
    font-weight: 700;
    line-height: 1.25;
    color: #fff;
  }
  .mwgọn__go {
    grid-area: go;
    align-self: center;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.16);
    color: #fff;
  }
  .mwgọn__go svg {
    width: 17px;
    height: 17px;
  }
  /* CARD VIEW ở mobile: mũi tên KHÔNG đứng cạnh cụm chữ nữa mà ĐÈ LÊN ảnh 1:1, neo góc
     dưới-phải, cách hai mép 4px.
     Làm bằng cách xếp nó vào CÙNG Ô GRID với ảnh (`grid-area: shot`) rồi
     `place-self: end end` — hai phần tử cùng một ô thì chồng lên nhau, không cần
     `position: absolute`; `margin` lo luôn khoảng cách tới hai mép. `z-index: 1` để nằm
     trên ảnh.
     Nền kính TỐI (không phải trắng mờ như bản cũ): ảnh dự án có bản rất sáng, mũi tên
     trắng trên kính trắng sẽ mất hút. */
  .mw2__khay--ngang .mwgọn,
  .pdn__gọn {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "shot"
      "txt";
  }
  .mw2__khay--ngang .mwgọn__go,
  .pdn__gọn .mwgọn__go {
    grid-area: shot;
    place-self: end end;
    /* 12px hai mép — bản đầu để 4px, Thu Hà chốt lại 12 */
    margin: 12px;
    z-index: 1;
    background: rgba(20, 20, 20, 0.34);
    backdrop-filter: blur(10px) saturate(130%);
    -webkit-backdrop-filter: blur(10px) saturate(130%);
    border: 1px solid rgba(255, 255, 255, 0.24);
    color: #fff;
  }
  /* LISTING VIEW ở mobile: hàng trên nền TRẮNG có bo góc + mũi tên phải */
  .mw2__khay--bảng .mwgọn {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "txt go";
    background: #f6f4ed;
    border: 0;
    color: var(--text);
    padding: 14px 16px;
    border-radius: 12px;
  }
  .mw2__khay--bảng .mwgọn__shot {
    display: none;
  }
  .mw2__khay--bảng .mwgọn__row {
    color: color-mix(in srgb, var(--text) 55%, transparent);
  }
  .mw2__khay--bảng .mwgọn__name {
    color: var(--text);
  }
  .mw2__khay--bảng .mwgọn__go {
    background: rgba(1, 66, 104, 0.08);
    color: #014268;
  }
}

/* --- chốt cuối section (ref dock.png) --- */
.mw2__cuối {
  margin-top: clamp(30px, 3.4vw, 52px);
  padding: clamp(18px, 2vw, 28px) 0 clamp(30px, 4vw, 60px);
  text-align: center;
}
.mw2__nhắn {
  margin: 0 0 clamp(20px, 2.4vw, 34px);
  font-family: var(--font-body);
  font-size: clamp(12px, 1.05vw, 15px);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.62);
}

/* ---------- BỘ CHUYỂN CÁCH XEM — CÙNG CỠ với dock trang chủ ---------- */
/* Cỡ CŨ — bản 64px theo dock trang chủ to quá, đã trả lại. */
.mwsw {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.28);
}
.mwsw--dính {
  position: fixed;
  left: 50%;
  bottom: 24px;
  translate: -50% 0;
  z-index: 60; /* cùng tầng dock của trang chủ */
  transition:
    opacity 0.28s ease,
    translate 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.mwsw--dính.là-tắt {
  opacity: 0;
  translate: -50% 16px;
  pointer-events: none;
}
.mwsw__b {
  position: relative;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: none;
  /* tông XANH đã chốt của site (#0876B7 / #014268), không phải cam nâu */
  color: #0876b7;
  cursor: pointer;
  transition:
    background 0.2s,
    color 0.2s;
}
.mwsw__b svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}
.mwsw__b:hover {
  background: rgba(8, 118, 183, 0.12);
}
.mwsw__b.is-on {
  background: #fff;
  color: #014268;
  box-shadow: 0 2px 8px rgba(1, 66, 104, 0.18);
}
/* Tooltip — dùng lại đúng kiểu .tip của dock trang chủ */
/* Y HỆT `.dock .tip` của trang chủ: nền TRẮNG, chữ #111, cách icon 1.25rem, bo 4,
   cỡ 0.6875rem. Bản trước mình tự đặt nền #111 chữ trắng — lệch khỏi bộ đã chốt. */
.mwsw .tip {
  position: absolute;
  bottom: calc(100% + 1.25rem);
  left: 50%;
  translate: -50% 0;
  padding: 0.3rem 0.6rem;
  border-radius: 4px;
  background: #fff;
  color: #111;
  font-family: var(--font-body);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  text-transform: none;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
  opacity: 0;
  transform: translateY(0.3rem) scale(0.94);
  transition:
    opacity 0.2s,
    transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.mwsw__b:hover .tip,
.mwsw__b:focus-visible .tip {
  opacity: 1;
  transform: none;
}

/* ---------- TABLET ---------- */
/* Bố cục giữ nguyên như desktop, chỉ nới cụm chữ, bớt một dòng headline và gộp bảng
   thông tin còn hai hàng — cụm chữ ở đây chỉ 342–399px, không đủ chỗ cho ba hàng.

   Điều kiện có thêm nhánh `(max-width: 1279px) and (max-aspect-ratio: 1/1)`: iPad Pro
   12.9 DỌC rộng tới 1024 nên `max-width: 1023px` không bắt được nó — đo ra nội dung tràn
   22px ở đúng khổ đó. */
@media (max-width: 1023px), (max-width: 1279px) and (max-aspect-ratio: 1 / 1) {
  .mwc__info {
    width: 52%;
    /* Thu các khe lại: ở 810 cụm chữ chỉ còn 342px, tiêu đề 20px hai dòng làm nội dung
       tràn 24px — đo được. Bớt khe cụm chữ, khe meta và đệm nút là vừa khít. */
    gap: 12px;
  }
  /* Ở tablet cụm chữ chỉ còn 342–399px nên phải nhỏ hơn desktop VÀ bớt một dòng:
     giữ 24px thì đo được nội dung tràn 22px ở 1024 và 45px ở 810 — hai nút bị đẩy ra
     ngoài. 20px + chặn 2 dòng thì tràn về 0. */
  .mwc__h3 {
    font-size: 18px;
    -webkit-line-clamp: 2;
  }
  /* Gộp còn HAI hàng: "Project type" và "Timeline" nằm cạnh nhau, cụm Role/Client xuống
     hàng dưới chiếm cả hai cột. Ba hàng như desktop thì thừa ~35px, hai nút bị đẩy ra
     ngoài cụm chữ. */
  .mwc__meta {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px 16px;
  }
  .mwc__đôi {
    grid-column: 1 / -1;
  }
  .mwc__meta dd {
    margin-top: 4px;
  }
  .mwc__b {
    padding: 8px 8px;
    font-size: 12px;
  }
  .mwc__eyebrow {
    font-size: 12px;
  }
}
/* TABLET DỌC: cụm "A small note" LÊN TRÊN, ba thumbnail xuống DƯỚI và dàn hết bề rộng
   cột nội dung (mỗi ô `flex: 1` nên ba ô chia đều, ăn trọn từ lề trái tới lề phải).
   Điều kiện `max-aspect-ratio: 1/1` — cùng cách với section Contact: iPad DỌC rộng tới
   1024 nên không thể lọc bằng bề rộng, phải lọc bằng tỉ lệ màn. Tablet NGANG giữ nguyên
   kiểu xếp cạnh nhau. */
@media (min-width: 768px) and (max-aspect-ratio: 1 / 1) {
  .mw1__đáy {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: clamp(22px, 3vw, 40px);
  }
  .mw1__ba li {
    flex: 1;
    width: auto;
  }
}
@media (max-width: 767px) {
  .mw1__đáy {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: 22px;
  }
  .mw1__ba li {
    flex: 1;
    width: auto;
  }
  .mwsw--dính {
    bottom: 16px;
  }
  .mwsw__b {
    width: 40px;
    height: 40px;
  }
}

/* ba ảnh section 1 giờ là <button> — bỏ mọi kiểu mặc định của nút */
.mw1__ba button {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 8px;
  overflow: hidden;
  cursor: pointer;
  transition: scale 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.mw1__ba button:hover {
  scale: 1.04;
}
