/* ============================================================
   墨讀 · Mặc Độc — landing
   Nền tối là mặt bàn. Vật SÁNG duy nhất trên trang là chính sản
   phẩm: tờ giấy demo và ảnh chụp app. Không thẻ trang trí nào
   sáng. Nhờ vậy tương phản mang thông tin — sáng là sản phẩm,
   tối là mọi thứ còn lại — chứ không phải để cho đẹp.
   ============================================================ */

/* ---------- chữ: TỰ HOST, đã cắt ----------
   Trước đây nạp từ Google Fonts và đo được ~810KB: tám tệp con Noto Serif SC
   để vẽ đúng 42 chữ Hán, cộng Newsreader đầy đủ. Nay ~149KB, và quan trọng
   không kém — mất luôn một tệp CSS bên thứ ba đang CHẶN VẼ TRANG cùng hai
   lượt preconnect. Tái tạo bằng `scripts/cat-font.sh`; giấy phép OFL của cả
   hai bộ chữ nằm ở `/font/OFL.txt`.
   `font-display: swap` giữ nguyên: chữ phải đọc được ngay, kể cả khi font
   chưa về. Đó cũng là lý do stack dự phòng phải là serif thật (Georgia),
   không phải `serif` chung chung. */
@font-face {
  font-family: "Newsreader";
  /* Còn nguyên trục opsz — mặt chữ này vẽ cả tiêu đề 83px lẫn thân bài 17px,
     nên `font-optical-sizing: auto` ở body có thứ để làm việc. */
  src: url("/font/newsreader-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Newsreader";
  /* opsz ghim ở 16: mặt này chỉ dùng cho vài cụm in đậm cỡ ~17px. */
  src: url("/font/newsreader-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Newsreader";
  /* Cắt hẹp về ASCII + nguyên âm có dấu thanh pinyin: bản nghiêng dùng ở ĐÚNG
     .kn-pinyin. Có test khoá điều đó — dùng nghiêng cho chữ tiếng Việt sẽ rơi
     về Georgia nghiêng ngay giữa câu. */
  src: url("/font/newsreader-italic-400.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Noto Serif SC";
  /* 42 chữ, giữ trục wght: 400 thân bài · 600 con dấu · 700 chữ hiệu. */
  src: url("/font/noto-serif-sc.woff2") format("woff2");
  font-weight: 200 900; font-style: normal; font-display: swap;
}

:root {
  /* mực & giấy — sắc ấm, lấy từ chính token của app rồi đảo vai */
  --muc:        #14120d;   /* nền: mực loãng trên mặt bàn tối */
  --muc-cao:    #1f1b14;   /* mặt nổi hiếm khi dùng */
  --giay:       #efe9dc;   /* CHÍNH LÀ màu nền của app, nay là màu chữ */
  --giay-mo:    #c9c1ae;
  --giay-nhat:  #948b77;
  --chi:        #35301f;   /* kẻ chỉ mảnh */

  /* son — dùng ở ĐÚNG ba chỗ: con dấu, từ đang chạm, nút gửi */
  --son:        #e0654f;   /* trên nền tối */
  --son-dam:    #b23529;   /* ruột con dấu, y hệt app */
  --ngoc:       #86a890;
  --vang:       #c9a86a;

  /* "Noto Serif SC" PHẢI có trong ngăn xếp này, không chỉ ở --serif-cn.
     Newsreader không có glyph Hán, nên một chữ Hán lẫn trong câu tiếng Việt
     rơi xuống font hệ thống — đo bằng CDP: "Nội dung 财经 trên YouTube" và chữ
     hiệu 墨讀 ở CHÂN TRANG cả ba trang đang vẽ bằng Noto Sans CJK JP, một mặt
     chữ KHÔNG CHÂN và của tiếng Nhật. Kết quả: cùng một trang có hai chữ hiệu
     墨讀 trông khác nhau, đầu trang một kiểu chân trang một kiểu.
     Trình duyệt chọn font theo TỪNG KÝ TỰ, nên đặt sau Newsreader là đúng:
     chữ Latin vẫn Newsreader, chỉ chữ Hán mới rơi sang đây. */
  --serif:    "Newsreader", "Noto Serif SC", Georgia, "Times New Roman", serif;
  --serif-cn: "Noto Serif SC", "Songti SC", serif;

  /* Thang chữ theo Elements of Typographic Style — tỉ lệ quãng năm (1.5)
     ở đầu thang, thu dần về 1.25 ở thân bài. */
  --t-display: clamp(2.6rem, 8.5vw, 5.2rem);
  --t-lon:     clamp(1.9rem, 4.6vw, 2.9rem);
  --t-vua:     clamp(1.25rem, 2.4vw, 1.5rem);
  --t-than:    clamp(1.0rem, 1.6vw, 1.125rem);
  --t-nho:     0.875rem;

  --long:      clamp(20px, 5.5vw, 32px);   /* lề trong khung */
  --khoang:    clamp(64px, 12vw, 128px);   /* khoảng giữa các mục */
  --rong:      680px;                       /* dưới 80 ký tự mỗi dòng */
  --khung-max: 1080px;                      /* bề rộng khung ngoài cùng */
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { scrollbar-gutter: stable; -webkit-text-size-adjust: 100%; }

body {
  background: var(--muc);
  color: var(--giay);
  font-family: var(--serif);
  font-size: var(--t-than);
  line-height: 1.65;          /* serif cần thoáng hơn sans */
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Vân giấy rất mờ + hai vệt sáng lệch tâm, để nền không phẳng lì như một
     khối màu. Cùng thủ pháp với app, đảo sang tông tối. */
  background-image:
    radial-gradient(ellipse 80% 50% at 15% 0%, rgba(224, 101, 79, 0.07), transparent 60%),
    radial-gradient(ellipse 70% 40% at 90% 30%, rgba(134, 168, 144, 0.05), transparent 60%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.045'/%3E%3C/svg%3E");
  background-attachment: fixed;
}

/* MÀU LINK GỐC. Trước đây KHÔNG có luật `a` nào ở mức gốc — chỉ có `.chan a`,
   `.dong-y a`, `.quay-lai`. Nên một liên kết nằm trong <p class="than"> rơi về
   màu mặc định của trình duyệt: xanh dương đậm trên nền #14120d, tương phản
   khoảng 1,3:1. Chỗ nó xảy ra là địa chỉ thư liên hệ trong mục "Ai làm cái
   này" — tức là đúng dòng người ta cần đọc được nhất để quyết định có tin hay
   không. Đặt ở gốc chứ không vá riêng `.than a`: lỗi này không thuộc về một
   lớp nào, nó thuộc về việc thiếu một mặc định. */
a { color: var(--son); text-underline-offset: 3px; }

/* Một glyph Hán đứng cạnh chữ Latin thổi phồng hộp dòng nó nằm trong (~1.45x ở
   cùng font-size), và font dự phòng còn đổi hộp dòng mà không đổi nét — nên
   cùng một trang lệch một kiểu lúc font chưa nạp, lệch kiểu khác sau khi nạp.
   `line-height: 1` chốt hộp dòng, ô vuông cố định ở nơi gọi mới là thứ căn với
   hàng xóm. Lý do đầy đủ nằm trong frontend/src/styles.css của app.
   CỐ Ý KHÔNG có font-size ở đây và CỐ Ý KHÔNG nudge bằng transform. */
.cjk-mark {
  display: inline-grid;
  place-items: center;
  align-self: center;
  font-family: var(--serif-cn);
  line-height: 1;
}

/* LỀ NGANG CỦA CẢ TRANG NẰM Ở ĐÂY, và chỉ ở đây.
   Mọi lớp dùng CHUNG phần tử với .khung (.hero, .muc, .form-khoi, .chan…) PHẢI
   dùng `margin-block` / `padding-block`, KHÔNG BAO GIỜ dùng shorthand `margin`
   hay `padding`: shorthand đặt lại cả bốn cạnh, nên nó xoá sạch trục ngang mà
   .khung vừa đặt — cùng độ đặc hiệu, đứng sau nên thắng, không có cảnh báo nào.
   Đã xảy ra HAI lần: `padding` làm cả trang dính mép trái ở điện thoại, rồi
   `margin` trong .form-khoi và .chan xoá mất `margin-inline: auto` nên biểu mẫu
   và chân trang văng sang sát mép trái ở màn rộng — mà ở điện thoại thì không
   thấy, vì ở đó `auto` vốn đã bằng 0.
   Có test tĩnh khoá điều này: xem `noi-dung.test.js`. */
.khung { width: 100%; max-width: var(--khung-max); margin-inline: auto; padding-inline: var(--long); }

/* KHÔNG có lớp "cột chữ hẹp" nào ở đây, và đó là điểm chính.
   Bản đầu dựng trang theo mô hình một cột 680px — bố cục của BÀI BÁO. Trên màn
   1194px nó để trống 464px bên phải suốt cả trang; ở 1440 và 1920 còn tệ hơn.
   Kéo cột đó sang trái cho thẳng hàng chỉ làm chỗ trống lộ ra rõ hơn, không
   chữa được gì — đó là vá triệu chứng.
   Thay bằng: mỗi mục có bố cục riêng dùng hết bề ngang, xếp chồng thành một
   cột ở điện thoại. Bề rộng đọc được vẫn giữ, nhưng giữ Ở TỪNG CỘT
   (`.than { max-width: 62ch }`) chứ không bằng cách bóp cả mục. */
.doi { display: grid; gap: clamp(30px, 4.5vw, 60px); }
@media (min-width: 900px) {
  .doi { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
  /* Cột trái nhỉnh hơn khi nó mang lập luận còn cột phải mang bằng chứng. */
  .doi.lech { grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr); }
}

/* ---------- masthead ---------- */
.dau-trang {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding-block: 26px 18px;
  border-bottom: 1px solid var(--chi);
}
.hieu { display: flex; align-items: center; gap: 14px; }
.hieu .glyph {
  font-family: var(--serif-cn); font-weight: 700; font-size: 30px;
  line-height: 1; letter-spacing: 3px; color: var(--giay);
}
.hieu .ten { font-size: var(--t-nho); color: var(--giay-nhat); letter-spacing: 0.06em; }

/* Con dấu thật: ô vuông son ĐẶC, chữ màu giấy khoét ngược ra — đúng cách một
   con dấu 阳文 hoạt động. Viền rỗng ruột là cách vẽ của giao diện, không phải
   của con dấu. */
.dau {
  width: 34px; height: 34px; border-radius: 5px;
  background: var(--son-dam); color: var(--giay);
  font-size: 19px; font-weight: 600;
  transform: rotate(-5deg);
  box-shadow: 0 1px 0 rgba(0,0,0,0.35);
}

/* ---------- hero ---------- */
/* Ở điện thoại: một cột, lời hứa rồi tới sản phẩm.
   Từ 1000px: HAI CỘT — lời hứa trái, tờ giấy demo phải. Không phải để lấp chỗ
   trống: nó đặt lời hứa và bằng chứng cạnh nhau trong cùng một tầm mắt, và nó
   bó dòng chữ Hán về bề rộng đọc được. Một cột ở 1440px cho dòng chữ Hán dài
   gần 1000px — quá dài để đọc, mà nửa phải màn hình thì bỏ không. */
.hero { padding-block: clamp(48px, 11vw, 104px) 0; }

@media (min-width: 1000px) {
  .hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr);
    gap: clamp(40px, 5vw, 72px);
    /* `center` khoá chiều dọc hai cột vào nhau: tờ giấy bên phải cao thấp theo
       từ đang chạm, nên mỗi lần chạm sang từ khác thì TIÊU ĐỀ VÀ NÚT bên trái
       cũng bị đẩy — đo được 25px ở 1194px. Người ta chạm để đọc nghĩa, không
       phải để xem trang nhún nhảy. `start` cắt sợi dây đó. */
    align-items: start;
  }
  .hero .to-giay { margin-top: 0; }
  /* Bỏ trần 15ch: cột lưới đã bó bề rộng đọc được rồi, nên giữ thêm trần chỉ
     làm tiêu đề vỡ thành năm dòng ngắn trong một cột rộng gấp rưỡi nó. */
  /* --t-display neo theo `vw`, đúng khi h1 chiếm cả bề ngang. Từ 1000px nó chỉ
     còn nằm trong một cột rộng ~500px, nên tính theo cả màn hình là tính sai
     vật chứa: đo ở 1194px được h1 cao 529px trong khung 834px, đẩy câu phụ đề
     xuống y=770–882 — BỊ CẮT bởi mép dưới màn hình, cùng với chỗ đăng ký.
     Cỡ chữ ở đây neo theo cột, không theo màn hình. */
  .hero .dinh-vi { max-width: none; font-size: clamp(2.9rem, 4.6vw, 4.1rem); }
}

.dinh-vi {
  font-size: var(--t-display);
  line-height: 1.06;
  letter-spacing: -0.022em;
  font-weight: 400;
  text-wrap: balance;
  max-width: 15ch;
}

/* Nét bút loang dưới câu định vị. Đây là MỘT khoảnh khắc chuyển động duy nhất
   không do người dùng kích hoạt trên cả trang — mọi mục khác hiện thẳng, không
   trượt-mờ. Hiệu ứng trượt-mờ rải đều từng mục là dấu hiệu trang máy sinh, và
   nó cũng làm người đọc phải đợi nội dung. */
.net-but { display: block; width: min(420px, 78%); height: 14px; margin: 18px 0 0; overflow: visible; }
.net-but path {
  fill: none; stroke: var(--son); stroke-width: 3.5; stroke-linecap: round;
  stroke-dasharray: 300; stroke-dashoffset: 300;
  animation: ve-net 1.1s cubic-bezier(0.25, 0.6, 0.3, 1) 0.35s forwards;
}
@keyframes ve-net { to { stroke-dashoffset: 0; } }

.phu-de {
  margin: 26px 0 0; max-width: 46ch;
  font-size: var(--t-vua); line-height: 1.55; color: var(--giay-mo);
}

.hero-cta { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px; margin: 30px 0 0; }
.hero-cta .nut { margin: 0; }
.hero-cta-phu { font-size: var(--t-nho); color: var(--giay-nhat); max-width: 30ch; }

/* Điểm dừng thứ hai, ngay sau loạt ảnh sản phẩm. */
.giua-cta { margin: 40px 0 0; }
.giua-cta .nut { margin: 0; }

/* ---------- tờ giấy demo: vật sáng đầu tiên trên trang ---------- */
.to-giay {
  position: relative;
  margin: clamp(34px, 7vw, 56px) 0 0;
  background: var(--giay);
  color: #211e18;
  border-radius: 3px;
  padding: clamp(26px, 5vw, 40px);
  /* Bóng nhiều lớp: một lớp sát để tách khỏi nền, một lớp xa và mềm để tờ giấy
     có vẻ NẰM TRÊN mặt bàn chứ không dán vào nền. */
  box-shadow:
    0 1px 2px rgba(0,0,0,0.4),
    0 12px 28px rgba(0,0,0,0.45),
    0 40px 80px rgba(0,0,0,0.35);
}
/* CỐ Ý KHÔNG có lớp gradient giả "mép giấy vênh" ở đây. Đã thử: một
   ::before `position:absolute; inset:0` vẽ ĐÈ LÊN chữ, vì con của .to-giay
   không được định vị. Lớp trắng 50% ở góc trên làm dòng chữ Hán đầu tiên nhạt
   hẳn so với dòng cuối — tụt tương phản đúng chỗ quan trọng nhất của cả trang,
   để đổi lấy một hiệu ứng không ai nhận ra. Muốn thêm lại thì phải đặt nó
   dưới chữ (z-index âm) và đo tương phản sau khi đặt, chứ không đặt bằng mắt.
*/

.doan-demo {
  font-family: var(--serif-cn);
  font-size: clamp(1.25rem, 4.6vw, 1.6rem);
  /* 2.25 chứ không phải 2.15: mỗi <button class="hz"> lấy chiều cao từ hộp
     dòng này, và 20px × 2.15 = 43px — dưới chuẩn chạm 44px của iOS ở MỌI bề
     rộng điện thoại. Đây là ô bấm CHÍNH của cả trang, thứ duy nhất trên trang
     cho người ta thử sản phẩm. Nới bằng line-height chứ không bằng padding:
     padding sẽ kéo nền của từ đang chọn tràn sang dòng trên và dòng dưới. */
  line-height: 2.25;
  letter-spacing: 0.01em;
  color: #211e18;
}
/* Chữ Hán cấm bắt đầu một dòng bằng dấu đóng câu. Mỗi token ở đây là một
   <button> (inline-block) nên trình duyệt coi ranh giới giữa nút và dấu câu
   là chỗ ngắt dòng hợp lệ và bỏ qua luật đó — kết quả là dấu 。 rơi xuống một
   dòng riêng. demo.js gói mỗi nút cùng dấu câu theo sau vào .cum. */
.cum { white-space: nowrap; }

/* "Hán-Việt" KHÔNG được ngắt dòng ở dấu gạch nối. Nó là thuật ngữ trung tâm của
   cả luận điểm bán hàng, và trình duyệt coi gạch nối là chỗ ngắt hợp lệ nên nó
   vỡ thành "Hán-" / "Việt" ngay ở tiêu đề — đã xảy ra hai lần trên cùng một
   màn hình 1440px. */
.lien { white-space: nowrap; }

.hz {
  cursor: pointer; border: 0; background: none; padding: 0 1px;
  font: inherit; color: inherit; border-radius: 3px;
  transition: color 0.15s, background 0.15s;
}
.hz:hover { color: var(--son-dam); background: rgba(178,53,41,0.08); }
.hz.dang-chon { color: var(--son-dam); background: rgba(178,53,41,0.14); }
.hz:focus-visible { outline: 2px solid var(--son-dam); outline-offset: 2px; }

.goi-y {
  display: flex; align-items: center; gap: 9px;
  margin: 20px 0 0; font-size: var(--t-nho); color: #8b8270;
}
.goi-y .cham { width: 7px; height: 7px; border-radius: 50%; background: var(--son-dam); flex: none; }

/* Khung nghĩa — cùng thứ tự tầng với WordSheet của app: chữ, pinyin,
   Hán-Việt, nghĩa Việt, nghĩa Anh. */
.khung-nghia {
  margin: 22px 0 0; padding: 22px 0 0;
  border-top: 1px solid #d8cfba;
  /* CHỖ NÀY ĐƯỢC ĐẶT TRƯỚC, không co theo nội dung. Mỗi từ cho một khung nghĩa
     cao thấp khác nhau (đo trên 18 từ × 10 bề rộng: 206–266px), nên không đặt
     trước thì tờ giấy nhảy mỗi lần chạm sang từ khác — và ở bố cục hai cột nó
     kéo theo cả tiêu đề lẫn nút bên trái.
     327px là TRẦN ĐO ĐƯỢC trên 18 từ × 10 bề rộng, không phải số ước lượng —
     trần rơi vào 表现 ở bề rộng điện thoại, từ có 10 nghĩa tiếng Anh. Đo lại
     bằng scripts/do-khung-nghia.mjs sau MỖI lần sinh lại demo-data.json hoặc
     đổi cỡ chữ trong khung nghĩa; đổi cỡ chữ mà quên đo lại thì tờ giấy nhảy
     trở lại y như cũ.
     HAI giá trị chứ không một: 327px là trần ở bề rộng điện thoại, nơi nghĩa
     Anh dài nhất vỡ thành bốn dòng. Từ 1000px tờ giấy rộng hơn nên trần chỉ
     còn 311px; dùng chung một số thì màn rộng phải chịu thêm 16px giấy trắng
     mà chẳng đổi lấy gì. */
  min-height: 327px;
}
@media (min-width: 1000px) {
  .khung-nghia { min-height: 311px; }
}
.khung-nghia[hidden] { display: none; }
/* Từ đây xuống là BẢN SAO số đo của WordSheet trong app
   (frontend/src/styles.css, các luật `.sheet .*`). Không phải "lấy cảm hứng":
   demo này tồn tại để người xem biết trước app trông thế nào, nên mỗi con số
   lệch đi là một lời hứa sai. Sáu chỗ từng lệch, nay đã khớp:
   chữ đậm 600 · pinyin 22px · Hán-Việt 19px · nhãn VIẾT HOA giãn 0.2em ·
   nghĩa Việt 22px có vạch ngăn phía trên · nghĩa Anh in nghiêng 16px. */
.kn-tu {
  font-family: var(--serif-cn); font-size: clamp(2rem, 7vw, 2.6rem);
  /* app: `.sheet .word` dùng font-weight 600 — không có nó thì chữ Hán ở đây
     mảnh hơn hẳn trong app, và đó là thứ đập vào mắt đầu tiên. */
  font-weight: 600; line-height: 1.1; color: #211e18;
}
.kn-pinyin { margin-top: 14px; font-size: 1.375rem; color: #4c473c; font-style: italic; }
.kn-hv { margin-top: 3px; font-size: 1.1875rem; color: var(--son-dam); }

/* Nhãn: app dùng 11px, giãn chữ 0.2em, VIẾT HOA. Landing từng để "Hán-Việt"
   chữ thường giãn 0.02em — cùng chữ nhưng đọc ra là hai giao diện khác nhau. */
.kn-hv .nhan, .kn-en .nhan {
  display: inline-block; margin-right: 9px; font-size: 11px;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: #8b8270; font-style: normal; vertical-align: middle;
}

/* Vạch ngăn Ở TRONG khung nghĩa, đúng chỗ app đặt (`.sheet .meanings`): nó
   tách phần NHẬN MẶT CHỮ (chữ, pinyin, Hán-Việt) khỏi phần NGHĨA. Vạch phía
   trên cùng của .khung-nghia là chuyện khác — nó tách đoạn văn khỏi khung. */
.kn-vi {
  margin-top: 20px; padding-top: 18px; border-top: 1px solid #d8cfba;
  font-size: 1.375rem; line-height: 1.4; color: #211e18;
}
/* app: in nghiêng, 16px. CỐ Ý lệch app đúng một chỗ — màu. App dùng
   --ink-faint #8b8270, trên nền giấy chỉ đạt 3,14:1; landing là trang công
   khai nên giữ #4c473c (7,63:1, đạt AA). Khác biệt gần như không thấy được,
   còn cái mất thì thấy được. */
.kn-en { margin-top: 9px; font-size: 1rem; font-style: italic; color: #4c473c; }

/* ---------- mục chung ---------- */
.muc { padding-block: var(--khoang) 0; }
.tieu-de {
  font-size: var(--t-lon); font-weight: 400; line-height: 1.15;
  letter-spacing: -0.015em; text-wrap: balance; max-width: 20ch;
}
/* Tiêu đề cỡ nhỏ, dùng cho mục nằm trong một cột của lưới đôi — cỡ lớn ở đó
   sẽ vỡ thành bốn dòng và nuốt mất cột. */
.tieu-de.nho { font-size: clamp(1.5rem, 2.6vw, 1.9rem); max-width: 24ch; }

.than { margin: 20px 0 0; max-width: 62ch; color: var(--giay-mo); }
.than + .than { margin-top: 14px; }
.than strong { color: var(--giay); font-weight: 500; }

/* ---------- ba cặp từ: luận điểm Hán-Việt ---------- */
/* margin-top chỉ cần khi bảng nằm DƯỚI đoạn văn (điện thoại, một cột). Trong
   lưới đôi nó là cột riêng, nên phải bằng 0 để mép trên thẳng hàng với tiêu đề
   cột bên cạnh. */
.cap-tu { margin-block: 34px 0; border-top: 1px solid var(--chi); }
@media (min-width: 900px) { .doi > .cap-tu { margin-block: 0; } }
.cap {
  display: flex; align-items: baseline; gap: clamp(16px, 4vw, 32px);
  padding: 18px 2px; border-bottom: 1px solid var(--chi);
}
.cap .han {
  font-family: var(--serif-cn); font-size: clamp(1.9rem, 6vw, 2.4rem);
  line-height: 1.15; color: var(--giay); flex: none; min-width: 2.6em;
}
.cap .viet { font-size: var(--t-vua); color: var(--son); }
.cap .nghia { margin-left: auto; font-size: var(--t-nho); color: var(--giay-nhat); text-align: right; }

/* ---------- vòng lặp + ảnh chụp ---------- */
.anh-loat {
  display: grid; gap: clamp(20px, 4vw, 30px);
  grid-template-columns: 1fr;
  margin: 38px 0 0;
}
@media (min-width: 760px) { .anh-loat { grid-template-columns: repeat(3, 1fr); } }

.anh figure { margin: 0; height: 100%; display: flex; flex-direction: column; }
.anh img {
  display: block; width: 100%; height: auto; border-radius: 4px;
  background: var(--giay);
  box-shadow: 0 2px 6px rgba(0,0,0,0.4), 0 18px 44px rgba(0,0,0,0.42);
}
.anh figcaption { margin: 14px 0 0; font-size: var(--t-nho); color: var(--giay-nhat); }
@media (min-width: 760px) { .anh figcaption { margin-top: auto; padding-top: 14px; } }
.anh figcaption b { display: block; color: var(--giay); font-weight: 500; margin-bottom: 2px; }

/* ---------- mốc thời gian, trong cột trái của biểu mẫu ---------- */
.moc { margin-block: 30px 0; padding-block: 22px 0; border-top: 1px solid var(--chi); }
.moc-nhan { font-size: var(--t-nho); color: var(--giay-nhat); letter-spacing: 0.04em; }
.moc-gia { margin-top: 4px; font-size: var(--t-lon); line-height: 1.1; color: var(--son); }
.moc-phu { margin-top: 16px; font-size: var(--t-nho); line-height: 1.55; color: var(--giay-mo); max-width: 40ch; }

/* ---------- biểu mẫu ---------- */
.form-khoi {
  margin-block: var(--khoang) 0; padding-block: clamp(30px, 6vw, 46px) 0;
  /* CHỈ có vạch trên. Vạch dưới đã bị bỏ: .chan ngay sau đó cũng mở bằng một
     vạch, nên hai vạch nằm cách nhau một dải trống lớn và dải đó đọc ra là
     thiếu nội dung. Một khối cần một vạch mở, không cần cả vạch đóng khi khối
     kế tiếp đã tự mở. */
  border-top: 1px solid var(--chi);
}
/* Ba câu trấn an đứng cạnh ô nhập, không phải dưới nó — đây là lúc người ta
   phân vân có nên đưa email không, nên câu trả lời phải nằm trong tầm mắt.
   Cả ba đều là sự thật kiểm được ở /rieng-tu; có test buộc chúng khớp nhau. */
.yen-tam { margin: 26px 0 0; padding-block: 18px 0; border-top: 1px solid var(--chi); list-style: none; }

/* Ở lưới đôi, cột trái (lời mời + ba câu trấn an) ngắn hơn cột phải (cả biểu
   mẫu) khoảng 630px, để lại một mảng trống lớn dưới đáy. Đẩy ba câu trấn an
   xuống cuối cột vừa lấp mảng đó, vừa đưa chúng về ngang tầm ô ĐỒNG Ý — chỗ
   người ta phân vân nhất, và cũng là ô mà bấm trượt thì mất luôn một email. */
@media (min-width: 900px) {
  .form-khoi .doi { align-items: stretch; }
  .form-khoi .doi > div:first-child { display: flex; flex-direction: column; }
  .form-khoi .yen-tam { margin-top: auto; }
}
.yen-tam li { font-size: var(--t-nho); color: var(--giay-nhat); line-height: 1.5; padding-left: 20px; position: relative; }
.yen-tam li + li { margin-top: 9px; }
.yen-tam li::before { content: "·"; position: absolute; left: 6px; color: var(--son); font-size: 1.3em; line-height: 1.05; }

.o { display: block; margin: 22px 0 0; }
.o > .nhan { display: block; margin-bottom: 8px; font-size: var(--t-nho); color: var(--giay-mo); }

input[type="email"] {
  width: 100%; max-width: 420px; padding: 13px 15px;
  font-family: var(--serif); font-size: 1.0625rem;
  color: var(--giay); background: rgba(239,233,220,0.05);
  border: 1px solid var(--chi); border-radius: 4px;
}
input[type="email"]::placeholder { color: var(--giay-nhat); }
input[type="email"]:focus-visible { outline: 2px solid var(--son); outline-offset: 1px; border-color: transparent; }

fieldset { border: 0; margin: 26px 0 0; }
legend { font-size: var(--t-nho); color: var(--giay-mo); margin-bottom: 4px; }
.tuy-chon { color: var(--giay-nhat); }
.ghi-chu { font-size: var(--t-nho); color: var(--giay-nhat); margin-bottom: 12px; max-width: 52ch; }

/* padding-block 11px cho ra 45px ở bề rộng điện thoại (16px × 1.45 + 22).
   Với 9px nó ra 41px — dưới chuẩn chạm 44px của iOS ở MỌI bề rộng điện thoại,
   và chỉ vừa đủ 44px ở đúng màn ~1194px, nên đo ở một bề rộng là không phát
   hiện ra. Đây là năm ô bấm chính của cả trang. */
.chon {
  display: flex; align-items: flex-start; gap: 11px;
  padding-block: 11px; cursor: pointer;
}
.chon input { margin-top: 6px; accent-color: var(--son-dam); flex: none; width: 17px; height: 17px; }
.chon span { line-height: 1.45; }

/* padding-block 10px đẩy vùng bấm lên 48px. Đo được 43px khi chưa có nó —
   dưới chuẩn chạm 44px của iOS, mà đây lại là ô BẮT BUỘC về pháp lý: bấm trượt
   ô này là người dùng gửi hỏng rồi bỏ đi, và ta mất một email vì một pixel. */
.dong-y {
  display: flex; align-items: flex-start; gap: 11px;
  margin-block: 26px 0; padding-block: 10px; cursor: pointer;
}
.dong-y input { margin-top: 5px; accent-color: var(--son-dam); flex: none; width: 17px; height: 17px; }
.dong-y span { font-size: var(--t-nho); line-height: 1.55; color: var(--giay-mo); max-width: 56ch; }
.dong-y a { color: var(--giay); text-underline-offset: 3px; }

.nut {
  display: inline-block; text-decoration: none; text-align: center;
  margin: 28px 0 0; padding: 14px 30px;
  font-family: var(--serif); font-size: 1.0625rem; letter-spacing: 0.01em;
  /* <button> KHÔNG thừa kế line-height của body — nó dùng `normal`. Đo được:
     thẻ <a> cao 58px (line-height 28,05px), thẻ <button> cao 47px, cùng một
     lớp .nut. Đặt thẳng ở đây thì hai loại thẻ mới cao bằng nhau. */
  line-height: 1.2;
  color: var(--giay); background: var(--son-dam);
  /* 4px, GIỐNG ô nhập email ngay phía trên nó.
     Trước đây là 999px — viên nhộng tròn hoàn toàn — và đó là thứ DUY NHẤT bo
     tròn trên cả trang: tờ giấy demo 3px, từ chạm được 3px, ảnh 4px, ô nhập
     4px, con dấu 5px. Một viên nhộng nằm sát dưới một ô chữ nhật góc sắc thì
     đọc ra là hai thứ mượn từ hai bộ giao diện khác nhau. Kéo nút rộng hết bề
     ngang ở điện thoại còn làm nó tệ hơn: hai đầu tròn thành hai nửa đường
     tròn bán kính 29px, tỉ lệ rộng/cao 5,7 — hình đó đọc ra là dải băng chứ
     không phải nút bấm. */
  border: 1px solid var(--son-dam); border-radius: 4px;
  cursor: pointer; transition: transform 0.16s, filter 0.16s;
}
.nut:hover { filter: brightness(1.1); }
.nut:active { transform: scale(0.98); }
.nut:focus-visible { outline: 2px solid var(--son); outline-offset: 3px; }

/* Ở bước cuối của một biểu mẫu dài, nút phải là thứ khó bấm trượt nhất trên
   màn hình. Đo được 249px = 64% bề ngang ở 390px — đủ rộng để bấm, nhưng
   không phải thứ ngón cái tìm thấy mà không cần nhìn. */
@media (max-width: 759px) {
  .nut { width: 100%; }
}

/* Honeypot: ẩn khỏi mắt VÀ khỏi trình đọc màn hình, nhưng vẫn nằm trong form.
   `display:none` bị một số bot bỏ qua có chủ ý, nên dùng cách đẩy ra ngoài. */
.bay { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Ẩn mặc định, hiện khi fragment của URL trỏ đúng vào id. Function chuyển
   hướng về `/#loi-<mã>` sau một lượt gửi hỏng, nên câu lỗi hiện ra mà không
   cần một dòng JS nào — và trình duyệt tự cuộn tới đúng chỗ. */
.loi {
  display: none;
  margin: 20px 0 0; padding: 12px 15px;
  border-left: 3px solid var(--son); background: rgba(224,101,79,0.09);
  color: var(--giay); font-size: var(--t-nho); line-height: 1.5;
  scroll-margin-top: 24px;
}
.loi:target { display: block; }

/* ---------- chân trang ---------- */
.chan {
  margin-block: clamp(40px, 8vw, 72px) 0; padding-block: 30px 44px;
  border-top: 1px solid var(--chi);
  font-size: var(--t-nho); color: var(--giay-nhat);
}
.chan a { color: var(--giay-mo); text-underline-offset: 3px; }
/* Link ĐỨNG MỘT MÌNH trong một đoạn là một ô bấm thật, không phải chữ trong
   câu — đo được 14px, dưới xa chuẩn chạm 44px. Link nằm giữa câu (CC-CEDICT)
   thì không đụng tới: WCAG miễn trừ đúng trường hợp đó, và nới nó ra sẽ phá
   nhịp dòng chữ. */
.chan p > a:only-child { display: inline-block; padding-block: 11px; }
.chan p + p { margin-top: 10px; }
.chan-luoi { display: grid; gap: 18px; }
@media (min-width: 760px) {
  .chan-luoi { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 48px; }
}

/* ---------- trang phụ ---------- */
.trang-phu { padding-block: clamp(40px, 9vw, 76px) 0; }
.trang-phu h1 { font-size: var(--t-lon); font-weight: 400; line-height: 1.2; letter-spacing: -0.015em; }
.trang-phu h2 { margin: 34px 0 10px; font-size: var(--t-vua); font-weight: 500; }
.trang-phu p, .trang-phu li { color: var(--giay-mo); max-width: 62ch; }
.trang-phu p + p { margin-top: 12px; }
.trang-phu ul { margin: 10px 0 0 1.1em; }
.trang-phu li { margin-bottom: 7px; }
.quay-lai {
  display: inline-block; margin-top: 34px; padding-block: 9px;
  color: var(--giay); text-underline-offset: 4px;
}

/* ---------- thanh neo đáy (điện thoại) ---------- */
/* Mặc định KHÔNG có thanh. Nó chỉ bật ở trình duyệt vừa hiện được vừa GIẤU
   được nó khi biểu mẫu đã vào tầm mắt — vì một thanh cố định không giấu được
   sẽ nằm đè lên đúng cái nút gửi ở cuối trang, tức là tự bắn vào chân.
   Nơi không hỗ trợ thì không có thanh, và hai CTA tĩnh (màn đầu + giữa trang)
   vẫn làm xong việc. Đây là nâng cấp, không phải điều kiện. */
.thanh-neo { display: none; }

@supports (timeline-scope: --form-hien) {
  @media (max-width: 899px) and (prefers-reduced-motion: no-preference) {
    /* CẢ HAI tên mốc phải nằm trong scope. Thiếu một cái thì mốc đó không
       phân giải được, hoạt ảnh gắn với nó im lặng không chạy, và thanh neo
       hiện ngay từ đầu trang — không có cảnh báo nào. Đã xảy ra. */
    body { timeline-scope: --form-hien, --hero-roi; }
    #dangky { view-timeline-name: --form-hien; }

    .hero-cta { view-timeline-name: --hero-roi; }

    .thanh-neo {
      display: block; position: fixed; inset: auto 0 0 0; z-index: 20;
      padding: 10px var(--long) calc(10px + env(safe-area-inset-bottom));
      background: rgba(20, 18, 13, 0.92);
      backdrop-filter: blur(8px);
      border-top: 1px solid var(--chi);

      /* Nằm ngoài màn hình cho tới khi được gọi. Vị trí mặc định phải là ẨN:
         nếu một trong hai hoạt ảnh dưới đây không chạy, thứ còn lại là một
         thanh không bao giờ hiện — phiền, nhưng vô hại. Mặc định HIỆN thì
         hỏng ngược lại: một thanh không bao giờ tắt, đè lên nút gửi. */
      transform: translateY(110%);

      /* HAI mốc, không phải một, vì thanh này có hai câu hỏi khác nhau:
           lên  — khi CTA ở màn đầu đã trôi khỏi màn hình (trước đó nó chỉ là
                  bản sao của một nút đang nhìn thấy, hai nút đỏ y hệt nhau
                  trong cùng một màn);
           xuống — khi biểu mẫu chớm vào tầm mắt (từ đó trở đi nó chỉ còn là
                  vật cản nằm trên chính nút gửi).
         Cả hai cùng lái `transform`, nên khung hình khuyết của hoạt ảnh sau
         lấy giá trị từ hoạt ảnh trước — chúng nối nhau chứ không giành nhau. */
      animation: neo-len linear both, neo-xuong linear both;
      animation-timeline: --hero-roi, --form-hien;
      animation-range: exit 0% exit 100%, cover 0% cover 20%;
    }
    .thanh-neo .nut { margin: 0; width: 100%; }

    /* CTA giữa trang bị ẩn Ở ĐÂY, và chỉ ở đây. Nó nằm đúng vùng cuộn mà
       thanh neo đang hiện, nên hai nút đỏ y hệt nhau xếp chồng lên nhau cách
       nhau một sợi chỉ — trông như trang bị hỏng. Ẩn nó BÊN TRONG khối
       @supports là có chủ ý: trình duyệt nào không dựng được thanh neo thì
       vẫn giữ CTA giữa trang, không mất điểm dừng nào. */
    .giua-cta { display: none; }
  }
}
@keyframes neo-len   { to { transform: translateY(0); } }
@keyframes neo-xuong { to { transform: translateY(110%); } }

/* ---------- tôn trọng người tắt hiệu ứng ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important; scroll-behavior: auto !important;
  }
  /* Nét bút phải HIỆN SẴN, không phải ẩn mãi mãi — không hiệu ứng nào được là
     điều kiện để nhìn thấy nội dung. */
  .net-but path { stroke-dashoffset: 0; }
}
