/* ==========================================================================
   sections-hero.css
   --------------------------------------------------------------------------
   PHẠM VI FILE NÀY
     - #envelope-intro (.envelope-intro) — màn phong bì mở ra trước khi vào thiệp
     - S1  #hero       (.hero)           — reference SECTION1, 420.7 × 692.7,
                                           14 lớp: cổng hoa, phong bì, 2 khung
                                           polaroid, tên, ngày nghiêng 5°
     - S2  #divider    (.divider)        — reference SECTION2, 420 × 252.6,
                                           dải kem + 1 bông nhỏ tràn từ trên
     - S3  #invitation (.invitation)     — reference SECTION3, 420 × 925.7,
                                           lời mời + nhà trai/nhà gái + địa
                                           điểm + khối ngày + ngày âm lịch
     - .music-toggle, .skip-link         — "chrome" toàn trang thuộc file này

   BÀN GIAO: comment stub gốc chỉ ghi #envelope-intro / #hero / #divider cho
   file này và ghi #invitation cho sections-story.css. Nhưng agent story đã
   viết sections-story.css CHỈ với #quote + #story và ghi rõ "#invitation hiện
   chưa có CSS ở bất kỳ file nào". Nhiệm vụ của agent này bao gồm S1–S3, nên
   #invitation (+ .families__*) được viết Ở ĐÂY. Không có luật nào trùng lặp
   giữa hai file — đã đối chiếu.

   NGUYÊN TẮC ĐÃ TUÂN THỦ
   1. Không literal màu / font-family — chỉ dùng biến của tokens.css.
   2. Không định nghĩa lại utility của base.css; chỉ tinh chỉnh từng thuộc
      tính trên phần tử BEM của chính section này.
   3. Mobile-first (gốc = 390px), rồi 768px và 1024px.
   4. Mọi selector đều neo dưới .envelope-intro / .hero / .divider /
      .invitation / .families / .music-toggle → không rò rỉ ra ngoài.
   5. KHÔNG đặt `transform` cố định lên phần tử mang .reveal — animations.css
      có `.reveal.is-visible { transform: none }` sẽ xoá mất. Xem ghi chú ở
      .hero__date (dùng thuộc tính `rotate`, không dùng `transform`) và
      .hero__portrait (xoay ở phần tử CON của .reveal).
   6. Hoạ tiết .decor đặt cỡ + vị trí bằng % nên tự co giãn; phần tràn ra
      ngoài đã bị .section (overflow-x: clip) chặn → không bao giờ cuộn ngang.
   ========================================================================== */

/* ==========================================================================
   0. MÀN PHONG BÌ  (#envelope-intro)
   --------------------------------------------------------------------------
   Lớp phủ toàn màn hình, nằm TRÊN mọi thứ (--z-toast, theo đúng ghi chú của
   hợp đồng DOM). Mang [hidden] sẵn trong HTML → envelope.js mới bỏ ra.

   PHONG BÌ CÓ HAI MẶT và KHỞI ĐẦU Ở MẶT TRƯỚC (mặt ghi người nhận). Bấm
   "Mở thiệp" mới lần lượt: LẬT sang mặt sau → sáp vỡ → nắp giở lên → thiếp
   trồi ra (animations.css mục 3.0 rồi mục 3). Mặt sau chính là trạng thái
   GẤP KÍN, dán sáp của bản trước — không đổi một pixel nào.

   Xếp lớp — HAI CÂY RIÊNG BIỆT, không tranh nhau: mỗi .envelope__face tự tạo
   một ngữ cảnh xếp lớp (mặt sau nhờ `perspective`, mặt trước nhờ `z-index`),
   nên z-index bên trong mặt này không bao giờ với sang mặt kia được.

     .envelope__face--back      ngữ cảnh riêng; các con xếp với --z-decor = 1:
       .envelope-intro__back    1    thân phong bì — ảnh -open đã cắt tam giác
       .envelope-intro__card    4    thiếp; opacity 0 lúc đầu vì đang NẰM TRONG
       .envelope-intro__pocket  5    mép trước, che nửa dưới tấm thiếp
       .envelope-intro__flap  6 → 2  nắp; 6 khi còn úp, 2 sau khi đã dựng lên
       .envelope-intro__script  7    chữ trên mặt nắp (khối tuỳ chọn, xoá được)
       .envelope-intro__seal    8    dấu sáp, trên cùng
     .envelope__face--front     9    ngữ cảnh riêng, xem mục 0B:
       .envelope-front__sprig   0    nhánh lan mờ, dưới cùng
       .envelope-front__from    1    người gửi
       .envelope-front__to      1    người nhận
       .envelope-front__postage 2    tem + dấu bưu điện

   ⚠ DẤU BƯU ĐIỆN ĐỎ nay nằm ở MẶT TRƯỚC, không còn ở mặt sau như bản cũ:
   dấu bưu điện đóng lên TEM, mà tem thì ở mặt ghi địa chỉ.

   HAI HẰNG SỐ DƯỚI ĐÂY ĐO BẰNG ALPHA TRÊN ẢNH THẬT, không ước lượng:
     envelope-ivory-open.png (650×710) — đáy tam giác nắp ở y = 301
       ⇒ bản lề = 301/710 = 42.39% chiều cao HỘP ẢNH.
     envelope-ivory-front-pocket.png (871×550) đặt bottom:0 rộng 100% ⇒ cao
       0.6315W; khung cao 1.0769W ⇒ mép trên mép trước ở 41.36% KHUNG. Hộp ảnh
       -open cao 0.985 × 710/650 = 99.92% khung ⇒ 41.36% khung = 41.39% hộp ảnh.

   Vì sao .__back cắt ở 41.39% (mép trên mép trước) chứ không cắt ở 42.39%
   (bản lề): mép trước có một khoét chữ V TRONG SUỐT ở giữa — đo được cột giữa
   ảnh chỉ đục từ y = 233/550, tức khoét kéo dài từ 41.4% xuống 66.2% khung.
   Cắt ở bản lề sẽ để hở dải 41.36%→42.36% (~3px) nhìn thấu ra nền trang ngay
   giữa mép trên phong bì lúc đang đóng. Cắt ở mép trước thì dải đó được chính
   giấy ivory của thân phong bì lấp kín.
   ========================================================================== */
.envelope-intro {
  position: fixed;
  inset: 0;
  z-index: var(--z-toast);

  /* Bề rộng phong bì. Tách thành biến vì .envelope-intro__envelope dùng nó HAI
     lần: inline-size, và margin bù khoảng trống của nắp (xem @media
     (min-height: 640px) bên dưới).

     BA TRẦN, cái nào nhỏ nhất thắng — bản trước chỉ có trần theo bề NGANG nên
     trên máy tính phong bì bị ghim ở 23rem (368px) dù màn rộng 1440px:
       88vw   bề ngang — vừa hết vùng trong padding ngang của lớp phủ
       54svh  CHIỀU CAO, thường là trần thật trên máy tính: cụm hiển thị cao
              1.0769 × W (khung) + gap + nút ≈ 1.08W + 120px, mà lúc mở tấm
              thiếp còn trồi lên sát mép trên khung.

              ĐÂY LÀ MỨC SÁT TRẦN, ĐỪNG TĂNG THÊM. Đo thật (render trạng thái
              MỞ, đếm pixel từ đỉnh viewport tới đỉnh tấm thiếp):
                        360×640  390×780  1024×768  1366×768  1440×900
                50svh        23       46        41        40        57
                54svh         8       22        19        17        29
                58svh        12        8      CẮT       CẮT        26
              58svh cắt cụt đỉnh thiếp trên màn cao 768px. 54svh còn 8px dư ở
              360×640 — chọn có chủ ý để phong bì to nhất có thể, đổi lại biên
              an toàn rất mỏng: khách để cỡ chữ mặc định lớn hơn (nút cao thêm)
              là có thể cụt. Muốn chắc chắn thì lùi về 50svh.
       34rem  chặn trên cho màn rất lớn = 544px, đúng 1.5× trần cũ (23rem =
              368px). Quá cỡ này phong bì trông như poster.

     Dùng svh (khung nhìn NHỎ NHẤT, lúc thanh địa chỉ đang hiện) chứ không phải
     vh: trên iOS Safari và WebView Zalo/Messenger, vh tính theo khung nhìn LỚN
     (thanh địa chỉ đã ẩn), nên 50vh có thể vượt quá nửa vùng thật sự nhìn thấy
     và làm cụt phong bì ngay khi vừa vào trang. Khối @supports ngay dưới nâng
     cấp cho trình duyệt có svh; trình duyệt cũ giữ vh. */
  --envelope-w: min(88vw, 54vh, 34rem);

  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--sp-7);
  padding: var(--sp-6) var(--pad-x);

  /* Nền phải ĐẶC để che hoàn toàn trang phía sau — cùng công thức vân giấy
     như <body> trong base.css (ảnh vân giấy không có alpha nên phải multiply). */
  background-color: var(--ivory);
  background-image: url('../img/paper-texture-white.png');
  background-repeat: repeat;
  background-size: 375px auto;
  background-position: center top;
  background-blend-mode: multiply;

  overflow: hidden;
}

/* Nâng cấp trần chiều cao lên svh khi trình duyệt hỗ trợ. Tách khối riêng vì
   @supports không lồng được vào trong luật ở CSS phẳng. Trình duyệt cũ bỏ qua
   cả khối và giữ nguyên 54vh ở trên — an toàn, chỉ là kém chính xác hơn trên
   iOS lúc thanh địa chỉ đang hiện.
   ĐỔI CON SỐ Ở ĐÂY THÌ PHẢI ĐỔI CẢ 54vh Ở KHỐI GỐC, nếu không trình duyệt có
   svh và trình duyệt không có svh sẽ ra hai cỡ phong bì khác nhau. */
@supports (height: 1svh) {
  .envelope-intro {
    --envelope-w: min(88vw, 54svh, 34rem);
  }
}

/* Trạng thái hiện, do envelope.js bật/tắt (CLASS_ACTIVE = 'is-active').
   Trước đây class này được gắn rồi gỡ mà KHÔNG có luật CSS nào tương ứng, nên
   hiệu ứng vào là no-op và comment trong JS mô tả một hành vi không tồn tại.

   CỐ Ý không viết `.envelope-intro { opacity: 0 }` như đề xuất của reviewer:
   overlay này là `position: fixed; inset: 0`, nên nếu JS không kịp gắn
   .is-active thì nó tàng hình NHƯNG VẪN CHẶN mọi cú bấm — khách nhìn thấy
   thiệp mà không bấm được gì, kiểu hỏng tệ hơn hẳn. Vì vậy trạng thái mặc định
   để nguyên đục, còn .is-active chỉ thêm animation vào; JS chết thì tệ nhất là
   mất hiệu ứng fade, không bao giờ khoá trang. */
.envelope-intro.is-active {
  animation: envelope-intro-in var(--dur-base) var(--ease-out) both;
}

@keyframes envelope-intro-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) and (max-width: 0px) { /* guard tắt theo yêu cầu: thiệp LUÔN chạy animation. Xoá ' and (max-width: 0px)' để bật lại. */
  .envelope-intro.is-active {
    animation: none;
  }
}

/* .envelope (animations.css) đã lo position: relative + perspective. */
.envelope-intro__envelope {
  inline-size: var(--envelope-w);
  /* 650 / 700 ⇒ cao ≈ 1.077 lần rộng, đúng tỉ lệ mặt sau phong bì. */
  aspect-ratio: 650 / 700;
}

/* 41.36% phía trên khung là chỗ TRỐNG giữ sẵn cho nắp lúc dựng lên. Khi phong
   bì còn gấp kín thì chỗ trống đó đẩy cả cụm (phong bì + nút "Mở thiệp") tụt
   xuống — đo trong Chrome ở 390×780: tâm phần NHÌN THẤY rơi ở y=460 thay vì
   390, lệch 70px, đọc ra như lỗi canh lề.
   Bù bằng ĐÚNG CẢ khoảng trống, không phải một nửa: .envelope-intro là grid
   `place-content: center`, nên margin âm M vừa làm co chiều cao cụm vừa được
   phép canh giữa chia lại — khung chỉ dịch lên M/2. Muốn tâm phần nhìn thấy
   về đúng giữa thì M phải bằng cả khoảng trống.
     hệ số = 41.36% × (700 / 650) = 0.4454
   (Đặt 0.2227 sẽ chỉ kéo lên 1/4 khoảng trống — đo thật: còn lệch 36px.)
   Lúc mở, khung nằm cao hơn tâm nửa khoảng trống, nhưng trạng thái mở chỉ sống
   1,3 giây rồi lớp phủ biến mất.

   Khoá theo CHIỀU CAO màn hình, không phải chiều rộng: dưới 640px (điện thoại
   xoay ngang) kéo thêm lên sẽ đẩy đỉnh tấm thiếp lúc trồi ra vượt khỏi
   .envelope-intro (overflow: hidden) và bị cắt cụt. Thà lệch tâm còn hơn mất
   nội dung. */
@media (min-height: 640px) {
  .envelope-intro__envelope {
    margin-block-start: calc(var(--envelope-w) * -0.4454);
  }
}

/* --- HAI MẶT CỦA PHONG BÌ ------------------------------------------------
   .envelope__face--back  bọc trọn 6 lớp mặt sau, trùng khít khung phong bì
                          (inset: 0) nên MỌI toạ độ % của 6 lớp đó giữ nguyên
                          không phải tính lại một con số nào.
   .envelope__face--front mặt trước, chỉ chiếm đúng phần thân phong bì nhìn
                          thấy được (xem khối toạ độ ở mục 0B).
   Chuyện quay / ẩn hiện do animations.css mục 3.0 lo; ở đây chỉ có hình học.

   ⚠ perspective LẶP LẠI ở mặt sau là BẮT BUỘC, không phải thừa: `perspective`
   chỉ có tác dụng với CON TRỰC TIẾP. Trước khi có mặt lật, .envelope__flap là
   con trực tiếp của .envelope nên hưởng perspective của nó; giờ nắp nằm trong
   mặt sau, nếu mặt sau không tự khai perspective thì cú giở nắp mất sạch chiều
   sâu (rotateX chỉ còn là phép co dẹt theo phương đứng, nhìn như nắp bị bóp).
   Vì hộp mặt sau TRÙNG KHÍT hộp .envelope nên perspective-origin cũng trùng ⇒
   hình ảnh cú giở nắp giống hệt bản chưa có mặt lật, không lệch một pixel.

   perspective còn khiến mặt sau thành stacking context riêng — đúng điều mong
   muốn: z-index 1…8 của 6 lớp bên trong được phân xử trong phạm vi mặt sau,
   không rò ra tranh chấp với mặt trước. */
.envelope__face--back {
  position: absolute;
  inset: 0;
  perspective: var(--envelope-perspective, 900px);
}

/* Thân phong bì. Cắt bỏ tam giác nắp phía trên vì nắp đã là phần tử riêng và
   phải quay được — để nguyên thì lúc đóng sẽ thấy MỘT nắp dựng lên (ảnh nền)
   và MỘT nắp úp xuống (nắp quay) cùng lúc. */
.envelope-intro__back {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0.75%;
  inline-size: 98.5%;
  z-index: var(--z-decor);
  clip-path: inset(41.39% 0 0 0);
}

/* Nắp. CÙNG ảnh, CÙNG toạ độ, CÙNG bề rộng với .__back — nhờ vậy khi dựng lên
   0deg nó trùng khít vào đúng chỗ tam giác vừa bị cắt, không có đường nối.
   clip-path và transform-origin tính theo HỘP CỦA CHÍNH ẢNH (42.39%), không
   phải theo khung; 57.61% = 100 − 42.39.
   animations.css đọc 4 biến dưới đây — xem @keyframes envelope-flap-open. */
.envelope-intro__flap {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0.75%;
  inline-size: 98.5%;
  clip-path: inset(0 0 57.61% 0);

  --envelope-hinge: 42.39%;
  --envelope-flap-closed: -180deg;
  --envelope-flap-z-shut: calc(var(--z-decor) + 5);
  --envelope-flap-z-open: calc(var(--z-decor) + 1);
  z-index: var(--envelope-flap-z-shut);
}

/* Thiếp nằm trong lòng phong bì: nửa dưới bị mép trước che, khi mở thì trồi
   lên (--envelope-lift: -38% của animations.css).
   CẢNH BÁO của animations.css: .envelope__card khởi đầu opacity 0 → nội dung
   ở đây chỉ là bản nhắc lại (aria-hidden), tên/ngày thật nằm ở #hero. */
.envelope-intro__card {
  position: absolute;
  inset-block-start: 14%;
  inset-inline: 9%;
  z-index: calc(var(--z-decor) + 3);

  padding: var(--sp-4) var(--sp-3);
  background-color: var(--ivory);
  border: 1px solid rgba(var(--sage-rgb), 0.18);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-md);
  text-align: center;
}

/* .t-name = --fs-name (50–62px) quá lớn cho tấm thiếp 19rem → hạ về --fs-md
   (clamp 16.8→19.2px) cho cân đối: TÊN GỌI "QUANG DANH" / "BÍCH ANH" rộng ~48%
   lòng thẻ, lề hai bên thoáng đều; hai tên xuống dòng riêng, "&" ở giữa.
   Vẫn là token, không phải literal. Chỉ tác động màn phong bì; tên ở hero /
   lời mời / câu chuyện giữ nguyên. */
.envelope-intro__names {
  font-size: var(--fs-md);
  line-height: var(--lh-tight);
}

.envelope-intro__amp {
  display: block;
  font-size: 0.85em;
  color: var(--sage-soft);
}

.envelope-intro__date {
  margin-block-start: var(--sp-3);
  font-size: var(--fs-xs);
}

/* Mép trước phong bì — che nửa dưới tấm thiếp. */
.envelope-intro__pocket {
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  inline-size: 100%;
  z-index: calc(var(--z-decor) + 4);
  pointer-events: none;
}

/* KHỐI TUỲ CHỌN — chữ script trên mặt nắp, để phong bì lúc gấp kín không phải
   một hình chữ nhật ivory trơn không chữ nào.
   Muốn bỏ: xoá <img class="envelope-intro__script"> trong index.html, khối này,
   và hai luật .envelope__script trong animations.css (mục 3 + mục 7.3).
   Nằm gọn giữa mặt nắp (nắp úp phủ 42.36%→84.72% khung) và không chạm dấu sáp:
   55% + cao 0.62W × 140/867 = 9.3% khung ⇒ đáy ở 64.3%, sáp bắt đầu ở 76.44%. */
.envelope-intro__script {
  position: absolute;
  inset-block-start: 55%;
  inset-inline-start: 19%;
  inline-size: 62%;
  z-index: calc(var(--z-decor) + 6);
  pointer-events: none;
}

/* Dấu sáp — dán ĐÚNG MŨI NẮP lúc gấp kín, như niêm một lá thư thật (sáp bắc
   cầu giữa mũi nắp và thân phong bì).
   Mũi nắp: bản lề ở 42.36% khung, nắp cao 42.36% khung ⇒ mũi ở 84.72%.
   Dấu sáp 164×190 đặt rộng 15.4%W ⇒ cao 0.1784W = 16.57% khung, nửa = 8.28%
   ⇒ inset-block-start = 84.72 − 8.28 = 76.44%.
   Ngang: (100 − 15.4) / 2 = 42.3%, canh giữa thật vì mũi nắp nằm đúng trục
   giữa. (Bản cũ để 62.2% / 42.9% cho khớp dấu sáp ở #hero — nhưng ở hero phong
   bì đã mở nên dấu sáp nằm trên mép trước, khác vai hoàn toàn.) */
.envelope-intro__seal {
  position: absolute;
  inset-block-start: 76.44%;
  inset-inline-start: 42.3%;
  inline-size: 15.4%;
  z-index: calc(var(--z-decor) + 7);
  pointer-events: none;
  filter: var(--seal-soften);
}

/* ==========================================================================
   DẤU MỘC BƯU ĐIỆN ĐỎ (vành chữ cong + trái tim giữa A & D) — vẽ HOÀN TOÀN
   bằng CSS. Khối này chỉ tả HÌNH DÁNG con dấu; chỗ ĐẶT nó là việc của
   .envelope-front__postmark ở mục 0B (nó đóng lên con tem ở mặt trước).

   LỊCH SỬ: bản trước dán con dấu này ở góc trên-phải MẶT SAU phong bì và cho
   nó mờ đi cùng nhịp với dấu sáp khi mở thư. Cả hai đều đã bỏ:
     · sai lệ — dấu bưu điện bao giờ cũng đóng lên TEM, mà tem thì ở mặt trước;
     · thừa — mặt trước lật đi ở nửa đầu cú lật, con dấu biến mất theo cả mặt,
       không cần thêm một animation mờ dần cho riêng nó nữa.
   ========================================================================== */
.envelope-intro__stamp {
  position: absolute;
  z-index: calc(var(--z-decor) + 8);

  /* Kích thước + MỌI chi tiết bên trong neo theo --envelope-w (mốc dùng chung
     với phong bì) nên khi zoom / đổi khung nhìn, con dấu co ĐÚNG cùng nhịp với
     phong bì — hết "loạn" do trộn rem với vw. */
  --stamp-size: calc(var(--envelope-w) * 0.16);
  inline-size: var(--stamp-size);
  aspect-ratio: 1;
  border-radius: 50%;

  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.06em;

  /* DẤU MỘC MỰC ĐỎ: ruột TRONG SUỐT (in trên giấy), viền tròn MẢNH + vòng
     trong NÉT ĐỨT. Viền vẽ bằng ::before/::after để không thành flex item. */
  background: transparent;
  color: var(--wax-red);
  opacity: 0.92;

  transform: rotate(-8deg);
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;

  /* font-size = 26% cỡ con dấu → A/D + trái tim + độ dày viền (em) tự co theo
     --stamp-size. Đổi cỡ con dấu chỉ cần sửa hệ số ở --stamp-size. */
  font-family: var(--ff-display);
  font-size: calc(var(--stamp-size) * 0.26);
  line-height: 1;
}

/* Viền NGOÀI — mảnh. */
.envelope-intro__stamp::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 0.09em solid var(--wax-red);
}

/* Chữ cong quanh vành (ngày cưới) — dùng SVG vì CSS thuần không uốn chữ tròn.
   font-size theo đơn vị viewBox (0 0 100 100) nên tự co theo cỡ con dấu. */
.envelope-intro__stamp-svg {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  overflow: visible;
  fill: var(--wax-red);
}
.envelope-intro__stamp-rim {
  font-family: var(--ff-label);
  font-size: 8px;
  letter-spacing: 0.2px;
  text-anchor: middle;
  text-transform: uppercase;
  fill: var(--wax-red);
}

/* Vòng TRONG — NÉT ĐỨT (position: absolute nên không thành flex item). */
.envelope-intro__stamp::after {
  content: "";
  position: absolute;
  inset: 20%;
  border-radius: 50%;
  border: 0.07em dashed var(--wax-red);
}

.envelope-intro__stamp-letter {
  font-weight: 400;
}

/* Trái tim: hai "cánh" bo tròn đỉnh, xoay ±45° chụm mũi ở đáy. currentColor =
   màu chữ nên tim và A/D cùng sắc nổi. */
.envelope-intro__stamp-heart {
  position: relative;
  flex: 0 0 auto;
  inline-size: 0.74em;
  block-size: 0.66em;
}
.envelope-intro__stamp-heart::before,
.envelope-intro__stamp-heart::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inline-size: 0.37em;
  block-size: 0.59em;
  background: currentColor;
  border-radius: 0.37em 0.37em 0 0;
}
.envelope-intro__stamp-heart::before {
  inset-inline-start: 0.37em;
  transform: rotate(-45deg);
  transform-origin: 0 100%;
}
.envelope-intro__stamp-heart::after {
  inset-inline-start: 0;
  transform: rotate(45deg);
  transform-origin: 100% 100%;
}

/* Nút "Mở thiệp" — chữ script Dancing Script (--ff-cta) cho nét thiệp cưới bay
   bổng, ĐỒNG BỘ với tên cô dâu chú rể ở hero (cùng Dancing Script).
   Ghi đè .btn (Lora in hoa; cùng độ đặc hiệu 0,1,0 nhưng file này nạp SAU
   base.css nên thắng). Script bắt buộc: giữ hoa/thường (text-transform:none)
   và KHÔNG giãn chữ (letter-spacing rời làm đứt nét nối chữ Dancing Script).
   THU NHỎ cho cân với phong bì min(78vw,19rem): bỏ min-block-size 3.25rem để
   trả về 2.75rem (=44px vùng chạm) của .btn, giảm min-inline 12→9rem và cỡ chữ
   --fs-script-md (~32px) → --fs-xl (~23px). Dancing Script cao/gọn hơn nên ở
   ~23px trong viên thuốc 44px không bị cắt nét thả (g, p, ệ).
   CHỈ nút này đổi; các nút khác của thiệp vẫn Lora in hoa. */
.envelope-intro__cta {
  /* PHẢI CÓ, không phải trang trí: giữa cú lật, mặt phong bì nghiêng trong
     không gian 3D nên mép dưới của nó CHIẾU XUỐNG thấp hơn hẳn lúc nằm phẳng
     và tràn qua chỗ cái nút. Đo trên màn 1440×900: từ t=355ms tới t=570ms mép
     phong bì đè lên nút, nặng nhất ở t=475ms — đè 45px trên bề rộng 127px của
     một cái nút chỉ cao 55px. Nút là phần tử em trong cùng ngữ cảnh xếp lớp,
     mà không có z-index thì phần tử có transform (mặt phong bì) được vẽ SAU
     ⇒ nút bị che gần một nửa đúng lúc khách vừa bấm.
     (Trên điện thoại 390px chỉ đè 1.9–3.5px nên rất dễ lọt lưới khi chỉ test
     bằng chế độ giả lập điện thoại — đây là lỗi CHỈ thấy ở màn rộng.)
     position: relative là bắt buộc để z-index có tác dụng. */
  position: relative;
  z-index: 1;

  /* Co theo --envelope-w như con dấu → cả cụm phong bì + nút + con dấu là MỘT
     khối responsive: zoom / đổi khung nhìn thì nút không lệch tỉ lệ với phong
     bì (trước dùng rem + --fs-xl vw nên lệch nhịp). */
  min-inline-size: calc(var(--envelope-w) * 0.5);
  padding-block: 0.5em;
  padding-inline: 1.2em;
  font-family: var(--ff-cta);
  font-size: calc(var(--envelope-w) * 0.05);
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  line-height: 1.2;
}

/* ==========================================================================
   0B. MẶT TRƯỚC PHONG BÌ  (.envelope-front)
   --------------------------------------------------------------------------
   Đây là thứ khách nhìn thấy ĐẦU TIÊN khi mở thiệp: mặt ghi người nhận của một
   lá thư vừa đến tay. Bấm "Mở thiệp" mới lật sang mặt sau (nắp + sáp) rồi mở.

   BỐ CỤC CHÉO — hai cụm nặng nằm chéo nhau, hai cụm nhẹ đỡ hai góc còn lại,
   đúng cách xếp một phong bì thật:
       trên-trái   người GỬI      (tên cô dâu chú rể + ngày cưới, chữ khắc)
       trên-phải   TEM + DẤU      (cụm nặng nhất, hút mắt vào trước)
       dưới-trái   người NHẬN     (nhãn nhỏ + tên viết tay + nét kẻ)
       dưới-phải   nhánh lan mờ   (đối trọng thị giác với cụm tem)
   Hai kiểu chữ CỐ Ý tương phản nhau và mỗi kiểu nói một vai:
       --ff-display (Hastegi, in hoa)  = phần NHÀ IN in sẵn  → người gửi
       --ff-hand (UVN Hoa Tay)         = phần VIẾT TAY bằng bút → người nhận
   Đừng đảo hai vai này; đảo xong là mặt trước hết ra dáng thư.

   HÌNH HỌC — bốn con số inset dưới đây ĐO TỪ ĐIỂM ẢNH THẬT, không ước lượng.
   Mặt trước phải trùng khít cái bóng giấy của phong bì lúc gấp kín, nếu không
   cú lật sẽ thấy tờ giấy đổi kích thước giữa chừng.
     · envelope-ivory-front-pocket.png (871×550) đặt bottom:0 rộng 100% khung
       ⇒ hộp ảnh cao 0.63146W, khung cao 1.07692W ⇒ mép trên hộp ở 41.365% khung.
     · Trong hộp ảnh đó, vùng GIẤY (điểm ảnh vừa đục vừa sáng, đo bằng
       System.Drawing) nằm ở x 0.23%→98.51%, y 0.36%→97.82% — phần còn lại là
       bóng đổ mềm bên ngoài mép giấy.
     ⇒ quy về % KHUNG (nhân hệ số cao 0.58636 cho hai số theo phương đứng):
         trên  41.365 + 0.36×0.58636 = 41.58%
         dưới  (100 − 97.82) × 0.58636 = 1.28%
         trái  0.23%      phải  1.49%
   Bóng đổ của ảnh PNG được dựng lại bằng box-shadow (ảnh có bóng vẽ sẵn, CSS
   thì không) — hai lớp, một lớp toả rộng một lớp sát chân giấy.

   MỌI KÍCH THƯỚC BÊN TRONG neo theo `font-size` của chính mặt trước, mà
   font-size lại neo theo --envelope-w ⇒ cả mặt trước co giãn như MỘT khối,
   giống hệt cách .envelope-intro__cta và --stamp-size đang làm. Muốn phóng to
   / thu nhỏ toàn bộ chữ trên mặt trước: đổi ĐÚNG một hệ số 0.047 bên dưới.
   Đo thật (computed style trong Chrome, không phải tính tay):
       544px (trần 34rem, màn lớn)        → 25.6px
       440px (khung nhìn 500px)           → 20.7px
       281.6px (88vw ở điện thoại 320px)  → 13.2px  ← XẤU NHẤT
   TRẦN NÀO THẮNG: --envelope-w = min(88vw, 54svh, 34rem). Với mọi điện thoại
   phổ thông (tỉ lệ rộng/cao < 0.61) thì 88vw luôn nhỏ hơn 54svh ⇒ trường hợp
   xấu nhất tới từ màn HẸP (320px), không phải màn thấp. 54svh chỉ thắng ở
   những khổ bè ngang bất thường (vd cửa sổ 500×568 → 306.7px, 14.4px).
   Ở khổ 320px, ba dòng BẮT BUỘC đọc được là 9.5px (tên người gửi) / 7.4px
   (ngày) / 19.9px (tên người nhận), và hai nhãn vi mô ("KÍNH GỬI", "ĐÀ NẴNG")
   rơi xuống ~7.4px — CỐ Ý chấp nhận, đúng cỡ chữ nhỏ của văn phòng phẩm thật.
   Bố cục ở khổ đó đã kiểm: không rớt dòng, không tràn, không bị xén.
   ĐỪNG bọc clamp() để đặt sàn cho hệ số này: chữ có sàn mà khung giấy thì
   không, ở khung 307px là tên người gửi tràn khỏi cột 52% ngay lập tức.
   ========================================================================== */
.envelope-front {
  position: absolute;
  inset-block-start: 41.58%;
  inset-block-end: 1.28%;
  inset-inline-start: 0.23%;
  inset-inline-end: 1.49%;
  /* Trên MỌI lớp của mặt sau (cao nhất ở đó là dấu sáp, +7). */
  z-index: calc(var(--z-decor) + 9);

  /* Cắt mọi thứ ở mép giấy: nhánh lan cố tình tràn ra góc, mà hoạ tiết in trên
     giấy thì không thể thò ra ngoài tờ giấy được. */
  overflow: hidden;
  border-radius: calc(var(--envelope-w) * 0.006);

  /* GIẤY — ba lớp chồng lên nhau, đọc từ dưới lên:
       3. dốc sáng gần như thẳng đứng (172deg): thân giấy giữ nguyên sắc tới
          hơn nửa chiều cao rồi mới tối dần về mép đáy. Điểm dừng 58% là số
          ĐO, không phải ước: mặt sau vẫn còn sáng #E0DDD6 ở 80% chiều cao,
          dốc tuyến tính suốt từ 0→100% làm nửa dưới mặt trước tối hơn mặt sau
          tới 12–14 đơn vị sáng.
       2. VŨNG SÁNG toả từ trên-trái — lớp duy nhất KHÔNG suy ra từ ảnh gốc,
          nhưng là lớp quyết định. Giấy thật bao giờ cũng có một vùng bắt sáng;
          thiếu nó thì dù hai đầu dốc khớp số, tổng thể vẫn đọc ra "hình chữ
          nhật CSS" chứ không ra tờ giấy. Pha bằng --ivory (trắng ẤM), không
          phải trắng trung tính — trắng trung tính kéo giấy ngả xanh lạnh.
       1. vân giấy dùng chung của cả trang. Ảnh vân KHÔNG có alpha nên bắt
          buộc multiply — y hệt công thức của <body> trong base.css. Chính lớp
          này ăn mất 3.5% độ sáng, và hai token màu đã bù sẵn (xem tokens.css).
     Muốn giấy sáng/tối hơn: chỉnh alpha 0.36 ở lớp 2 TRƯỚC, đừng động vào hai
     token màu — chúng là số đo đã hiệu chỉnh, không phải nút vặn. */
  background-color: var(--envelope-paper);
  background-image:
    url('../img/paper-texture-white.png'),
    radial-gradient(
      104% 76% at 34% 34%,
      rgba(var(--ivory-rgb), 0.36) 0%,
      rgba(var(--ivory-rgb), 0) 70%
    ),
    linear-gradient(
      172deg,
      var(--envelope-paper) 0%,
      var(--envelope-paper) 58%,
      var(--envelope-paper-shade) 100%
    );
  background-size: 375px auto, 100% 100%, 100% 100%;
  background-repeat: repeat, no-repeat, no-repeat;
  background-blend-mode: multiply, normal, normal;

  box-shadow:
    0 calc(var(--envelope-w) * 0.011) calc(var(--envelope-w) * 0.03) rgba(var(--ink-rgb), 0.16),
    0 calc(var(--envelope-w) * 0.002) calc(var(--envelope-w) * 0.006) rgba(var(--ink-rgb), 0.09);

  font-size: calc(var(--envelope-w) * 0.047);
  color: var(--sage);
}

/* --- Nhánh lan góc dưới-phải --------------------------------------------
   Đối trọng cho cụm tem ở góc trên-phải. Cố tình tràn quá mép rồi để
   overflow:hidden của mặt trước xén — hoạ tiết bị cắt ở mép giấy trông như in
   tràn lề, còn hoạ tiết nằm gọn bên trong trông như dán sticker.
   opacity thấp + z-index 0: nó là VÂN NỀN, không được tranh chữ. */
.envelope-front__sprig {
  position: absolute;
  inset-block-end: -8%;
  inset-inline-end: -5%;
  inline-size: 27%;
  z-index: 0;
  opacity: 0.42;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

/* --- Người gửi, góc trên-trái -------------------------------------------
   Vạch ngắn phía trên (::before) là quy ước của khối địa chỉ người gửi trên
   văn phòng phẩm: một nét cắt nhỏ báo "đây là đầu khối", đỡ phải viết chữ
   "Từ:" cho một mặt giấy vốn đã kín chữ. */
.envelope-front__from {
  position: absolute;
  inset-block-start: 8%;
  inset-inline-start: 7.5%;
  inline-size: 52%;
  z-index: 1;
}

.envelope-front__from::before {
  content: '';
  display: block;
  inline-size: 1.7em;
  block-size: 1px;
  margin-block-end: 0.55em;
  background-color: var(--sage-soft);
}

.envelope-front__from-names {
  display: block;
  font-family: var(--ff-display);
  font-size: 0.72em;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--sage);
}

/* "&" lùi một bậc màu để hai cái tên nổi lên trước, giống cách
   .envelope-intro__amp đang làm trên tấm thiếp bên trong. */
.envelope-front__amp {
  margin-inline: 0.22em;
  color: var(--sage-mute);
}

/* ⚠ MÀU: --sage chứ KHÔNG PHẢI --sage-mute, dù đây đúng là "chữ phụ nhỏ" —
   vai trò mà tokens.css dành cho --sage-mute. Lý do: bậc màu đó được đo là
   4.8:1 TRÊN NỀN --ivory, còn nền ở đây là GIẤY PHONG BÌ (~#E3DFD8, tối hơn
   ivory một bậc) nên tụt xuống 3.76:1 — dưới ngưỡng AA 4.5:1. Cùng chữ đó tô
   --sage cho 5.02:1, đạt.
   ⇒ TRÊN MẶT TRƯỚC, PHÂN CẤP THỊ GIÁC LÀM BẰNG CỠ CHỮ VÀ KIỂU CHỮ, KHÔNG LÀM
   BẰNG ĐỘ ĐẬM NHẠT CỦA MÀU. --sage-mute chỉ còn được dùng ở hai chỗ đã kiểm:
   dấu "&" (ký hiệu trang trí, cỡ lớn) và chữ trên con tem (nền --paper trắng,
   ở đó --sage-mute vẫn cho 4.99:1). */
.envelope-front__from-date {
  display: block;
  margin-block-start: 0.42em;
  font-family: var(--ff-body);
  font-size: 0.56em;
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-wide);
  color: var(--sage);
}

/* --- Cụm bưu chính, góc trên-phải ---------------------------------------
   position: absolute nên bản thân nó là khối chứa cho con dấu bên trong —
   nhờ vậy con dấu neo theo MÉP CON TEM chứ không theo mép tờ giấy, đổi cỡ tem
   thì con dấu tự đi theo. */
.envelope-front__postage {
  position: absolute;
  inset-block-start: 6.5%;
  inset-inline-end: 6.5%;
  z-index: 2;
}

/* CON TEM. Răng cưa vẽ bằng 4 lớp radial-gradient màu GIẤY phủ đè lên bốn mép:
   mỗi lớp lát một hàng nửa hình tròn cắn vào mép tem, phần còn lại trong suốt.
   Vì sao KHÔNG dùng mask + mask-composite (cách "sạch" hơn): mask-composite
   chưa chắc chạy trong WebView cũ của Zalo/Messenger — mà đó lại đúng là nơi
   phần lớn khách sẽ mở thiệp. Hỏng mask thì con tem thành hình chữ nhật đặc
   không răng; hỏng nền thì cùng lắm lệch màu vài phần trăm.
   Màu răng lấy --envelope-paper-shade chứ KHÔNG phải --envelope-paper: hai
   token kia là giá trị TRƯỚC khi nhân vân giấy, còn răng cưa vẽ trên chính
   con tem nên không đi qua lớp vân — dùng đầu sáng sẽ hơi trắng hơn nền quanh
   nó. Bản tối hơn một bậc rơi đúng vào tông giấy ĐÃ RENDER ở vùng con tem
   (~#E6E2DB), lệch dưới 2% và không thấy được ở răng đường kính ~5px. */
.envelope-front__stamp {
  --stamp-w: calc(var(--envelope-w) * 0.195);
  --perf-step: calc(var(--stamp-w) * 0.072); /* bước răng ≈ 6.2px @440 */
  --perf-r: calc(var(--perf-step) * 0.42);

  display: block;
  position: relative;
  inline-size: var(--stamp-w);
  padding: calc(var(--stamp-w) * 0.085);

  background-color: var(--paper);
  background-image:
    radial-gradient(circle var(--perf-r) at 50% 0, var(--envelope-paper-shade) 97%, transparent 100%),
    radial-gradient(circle var(--perf-r) at 50% 100%, var(--envelope-paper-shade) 97%, transparent 100%),
    radial-gradient(circle var(--perf-r) at 0 50%, var(--envelope-paper-shade) 97%, transparent 100%),
    radial-gradient(circle var(--perf-r) at 100% 50%, var(--envelope-paper-shade) 97%, transparent 100%);
  background-size:
    var(--perf-step) var(--perf-step),
    var(--perf-step) var(--perf-step),
    var(--perf-step) var(--perf-step),
    var(--perf-step) var(--perf-step);
  background-position: 0 0, 0 100%, 0 0, 100% 0;
  background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;

  /* Nghiêng nhẹ như một con tem dán vội bằng tay. Ngược chiều với con dấu
     (-8deg) để hai vật không song song — song song trông như in sẵn. */
  transform: rotate(2.4deg);
  box-shadow: 0 1px 2px rgba(var(--ink-rgb), 0.10);
  user-select: none;
  -webkit-user-select: none;
}

/* Khung ảnh trong tem: cửa sổ overflow:hidden để còn phóng to ảnh bên trong.
   4/5 là tỉ lệ tem chân dung kinh điển. */
.envelope-front__stamp-window {
  display: block;
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 5;
  background-color: var(--cream);
  border: 1px solid rgba(var(--sage-rgb), 0.22);
}

/* Ảnh cưới trên tem. PHẢI phóng to: khung tem chỉ ~60px trên điện thoại, để
   nguyên khung hình gốc thì hai người chỉ còn vài chục pixel, nhìn ra một vệt
   màu chứ không ra ảnh cưới. scale + transform-origin neo vào vùng MẶT (đo
   trên gallery-02.jpg: hai gương mặt nằm quanh y ≈ 30% chiều cao ảnh).
   Đổi ảnh khác trong data.js thì kiểm lại đúng hai số này. */
.envelope-front__stamp-photo {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: 50% 30%;
  transform: scale(1.85);
  transform-origin: 50% 30%;
  /* Hạ độ bão hoà một nhịp: ảnh chụp ngoài trời nhiều xanh lá tươi, để nguyên
     thì con tem nhảy hẳn ra khỏi bảng màu sage/kem của cả thiệp. */
  filter: saturate(0.82);
}

.envelope-front__stamp-place {
  display: block;
  margin-block-start: calc(var(--stamp-w) * 0.06);
  font-family: var(--ff-label);
  font-size: calc(var(--stamp-w) * 0.135);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  text-align: center;
  color: var(--sage-mute);
}

/* DẤU BƯU ĐIỆN đóng CHỚM lên góc dưới-trái con tem. Dấu PHẢI chạm tem, đó là
   quy tắc của bưu điện thật (đóng dấu để tem không dùng lại được) và cũng là
   thứ khiến cụm này đọc ra "đã gửi đi" chứ không phải "sắp gửi".
   Hai số âm tính theo --stamp-size nên đổi cỡ con dấu là chỗ đè tự giữ nguyên
   tỉ lệ.
   ĐÃ ĐẨY RA NGOÀI so với bản đầu (-0.3 / -0.52): lúc đó con dấu trùm gần nửa
   con tem và che mất chữ "ĐÀ NẴNG" ở đáy tem — đo trên bản render thật. Bây
   giờ nó chỉ liếm qua ~1/4 bề ngang tem, phần còn lại rơi xuống khoảng trống
   dưới tem, tiện thể lấp luôn mảng trống giữa-phải của mặt giấy. */
.envelope-front__postmark {
  inset-block-start: auto;
  inset-inline-end: auto;
  inset-block-end: calc(var(--stamp-size) * -0.5);
  inset-inline-start: calc(var(--stamp-size) * -0.76);
}

/* --- Người nhận, góc dưới-trái ------------------------------------------
   Nét kẻ (::after) là dòng kẻ để viết tên khách. Nó chạy hết bề ngang khối —
   dài hơn cái tên — nên đọc ra là "dòng kẻ có sẵn, tên được viết lên trên",
   chứ không phải "gạch chân cho đẹp". */
.envelope-front__to {
  position: absolute;
  inset-block-start: 54%;
  inset-inline-start: 7.5%;
  inline-size: 79%;
  z-index: 1;
}

.envelope-front__to-label {
  display: block;
  font-family: var(--ff-label);
  font-size: 0.56em;
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  /* --sage, không phải --sage-mute — xem ghi chú tương phản ở
     .envelope-front__from-date. Nhãn này là chữ nhỏ nhất của mặt giấy nên
     càng không được dùng bậc màu chỉ đạt 3.76:1 trên nền giấy phong bì. */
  color: var(--sage);
}

/* Hàng chứa tên người nhận + trái tim. PHẢI có lớp bọc này: trái tim là phần
   tử anh em của .__to-name chứ không phải con của nó (data-bind đặt chữ bằng
   textContent, con nào cũng bị xoá), mà .__to-name lại cần `display: inline`
   để trái tim bám ngay sau chữ cuối cùng của dòng cuối. Khoảng cách với nhãn
   "Kính gửi" nay do hàng này giữ, không còn ở .__to-name. */
.envelope-front__to-line {
  display: block;
  margin-block-start: 0.16em;
  /* Chữ ký tay hay có nét thả xuống (g, y, ệ) — cho thêm chỗ ở dưới để nét
     thả không chạm vào dòng kẻ. Chỗ này TRƯỚC ĐÂY nằm ở .__to-name, nhưng
     padding dọc trên phần tử `inline` không nong hộp dòng ra, nên nó phải
     chuyển lên hàng bọc mới còn tác dụng. */
  padding-block-end: 0.06em;
}

.envelope-front__to-name {
  /* inline, KHÔNG phải block — xem ghi chú ở .envelope-front__to-line. */
  display: inline;
  font-family: var(--ff-hand);
  /* --to-name-scale do guest.js đặt qua data-css: 1 cho tên thường, 0.82 khi
     chuỗi "danh xưng + tên" vượt 20 ký tự. Mặc định 1 nên khi guest.js không
     có mặt (gỡ thẻ script) cỡ chữ trở về đúng 1.5em như trước. */
  font-size: calc(1.5em * var(--to-name-scale, 1));
  line-height: 1.15;
  color: var(--sage);
}

/* Trái tim đỏ sau tên người nhận — chỉ có mặt khi link mang ?to=…, tức nó là
   dấu hiệu "thiệp này gửi riêng cho bạn". Thiệp dán công khai không có tim.

   HÌNH TRÁI TIM dùng lại nguyên si của dấu bưu điện (.envelope-intro__stamp-
   heart): cùng hai pseudo-element bo tròn xoay 45°, cùng ăn `currentColor`.
   Không vẽ lại — mặt phong bì chỉ nên có MỘT hình trái tim, hai bản vẽ khác
   nhau là hai lần phải sửa khi đổi kiểu.

   Nhịp nháy do .blink-heart (animations.css §6) lo, không đặt ở đây. */
.envelope-front__to-heart {
  position: relative;
  display: inline-block;
  margin-inline-start: 0.22em;
  /* Cỡ đo theo em của CHÍNH nó (kế thừa cỡ chữ tay đã nhân --to-name-scale),
     nên tên dài co lại thì trái tim co theo, không bao giờ to hơn chữ. */
  inline-size: 0.62em;
  block-size: 0.56em;
  /* --wax-red-hi chứ không phải --wax-red: tim chỉ ~13px trên giấy ivory, bậc
     đậm hơn nhìn ra vết mực loang. Cùng họ đỏ với sáp niêm và dấu bưu điện
     nên mặt phong bì vẫn chỉ có đúng MỘT sắc đỏ. */
  color: var(--wax-red-hi);
  /* Nhích xuống cho tâm trái tim ngang tầm thân chữ, không treo lên ngang
     nét bút cao của chữ ký tay. */
  vertical-align: 0.08em;
}

.envelope-front__to-heart::before,
.envelope-front__to-heart::after {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inline-size: 0.31em;
  block-size: 0.5em;
  background: currentColor;
  border-radius: 0.31em 0.31em 0 0;
}

.envelope-front__to-heart::before {
  inset-inline-start: 0.31em;
  transform: rotate(-45deg);
  transform-origin: 0 100%;
}

.envelope-front__to-heart::after {
  inset-inline-start: 0;
  transform: rotate(45deg);
  transform-origin: 100% 100%;
}

.envelope-front__to::after {
  content: '';
  display: block;
  block-size: 1px;
  margin-block-start: 0.3em;
  background-color: rgba(var(--sage-soft-rgb), 0.6);
}

/* ==========================================================================
   1. NÚT NHẠC NỀN  (.music-toggle)
   music.file = null trong data.js ⇒ render.js xoá hẳn nút. Vẫn style sẵn để
   khi bật nhạc không phải sửa CSS.
   ========================================================================== */
.music-toggle {
  position: fixed;
  inset-block-end: var(--sp-4);
  inset-inline-end: var(--sp-4);
  z-index: var(--z-nav);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.75rem; /* 44px — vùng chạm tối thiểu */
  block-size: 2.75rem;

  border: 1px solid rgba(var(--sage-rgb), 0.3);
  border-radius: var(--r-pill);
  background-color: var(--ivory);
  color: var(--sage);
  box-shadow: var(--shadow-sm);

  transition:
    background-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.music-toggle:hover {
  background-color: var(--sage);
  color: var(--ivory);
  /* reference: #music-toggle:hover { transform: translateY(-3px) } */
  transform: translateY(-3px);
}

.music-toggle__icon {
  /* <symbol id="icon-music-on"> không đặt fill → phải tô theo màu chữ. */
  fill: currentColor;
}

/* .skip-link không cần luật riêng: .visually-hidden-focusable trong base.css
   đã lo cả trạng thái ẩn và viên thuốc nền --sage khi được focus. */

/* ==========================================================================
   2. S1 — #hero  (reference SECTION1: 420.736 × 692.7)
   --------------------------------------------------------------------------
   TOẠ ĐỘ GỐC (px, cột 420.736) → tất cả đều đo lại theo "khung sân khấu"
   SCENE = dải y 178.9 → 681.9 (cao 503) vì đó là vùng chứa toàn bộ cụm phong
   bì + polaroid + hoa. Vậy SCENE có tỉ lệ 420.736 / 503.

     lớp                     ref top/left/rộng        → % trong SCENE
     IMAGE14 "Save our date" 46.6 / 41.3 / 318.2      → (nằm trên SCENE)
     IMAGE5  phong bì (sau)  189.8 / 42.1 / 337.5     → 2.2%  / 10%   / 80.2%
     GROUP3  polaroid trái   206.7 / 61.3 / 224.8     → 5.5%  / 14.6% / 53.4%
     GROUP2  polaroid phải   178.9 / 141.7 / 223.0    → 0%    / 33.7% / 53%
     IMAGE9  phong bì (trước)345.1 / 39.6 / 343.0     → 33%   / 9.4%  / 81.5%
     IMAGE70 dấu sáp         418.9 / 186.5 / 52.7     → 47.7% / 44.3% / 12.5%
     IMAGE10 bó hoa phải     235.2 / 298.5 / 127.0    → 11.2% / 71%   / 30.2%
     IMAGE11 bó hoa trái     454.0 / 0.1 / 131.9      → 54.7% / 0%    / 31.3%
     IMAGE12 nhánh lan       270.6 / 19.5 / 93.2      → 18.2% / 4.6%  / 22.2%
     IMAGE8  tú cầu nhỏ      363.1 / 256.4 / 86.1     → 36.6% / 60.9% / 20.5%
     HEADLINE4 ngày 25.10    391.0 / 163.1 / 135      → rotate(5deg)

   KỸ THUẬT BỐ CỤC — vì sao dùng grid 3 hàng:
   reference là section cao cứng 692.7px với MỌI thứ absolute và KHÔNG có tên
   cô dâu chú rể dạng chữ (tên chỉ có ở màn phong bì). DOM của ta thêm <h1>
   Hastegi 68–92px, không thể nhét vào khoảng trống 12% phía trên của
   reference. Nên hero được chia 3 hàng:
       "names"  <h1> + ngày (dòng chữ "Save our date" phủ lên đỉnh hàng này)
       "scene"  cụm phong bì + polaroid + hoa
       "cue"    gợi ý cuộn
   Các lớp .decor được gán `grid-area: scene`: theo spec CSS Grid, phần tử
   absolute là con của grid container VÀ có vị trí grid xác định thì khối chứa
   của nó CHÍNH LÀ ô grid đó. Nhờ vậy mọi % top/left của hoạ tiết neo vào đúng
   dải SCENE, không bị lệch khi <h1> đổi số dòng. Chiều cao dải SCENE do
   .hero__photos (aspect-ratio) quyết định nên luôn tỉ lệ với bề rộng.
   ========================================================================== */
.hero {
  /* Khối đầu trang: bớt padding trên để cụm chữ nằm cao trong khung nhìn. */
  padding-block: var(--sp-6) var(--sp-7);
}

.hero__stage {
  /* .stage đã có position: relative (khối chứa của .decor) + max 520px. */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    'names'
    'scene'
    'cue';
  /* Chặn tràn ngang thêm một lớp nữa ngoài .section. */
  overflow-x: clip;
}

/* --- Dòng chữ script "Save our date" ----------------------------------- */
/* Phủ lên đỉnh hàng "names"; .hero__content chừa chỗ bằng padding-block-start
   (padding % luôn tính theo BỀ RỘNG khối chứa → tỉ lệ với stage, không phụ
   thuộc chiều cao chữ). Mang .glow của animations.css. */
.hero__script {
  grid-area: names;
  inset-block-start: 0;
  inset-inline-start: 12%;
  inline-size: 76%;
  z-index: var(--z-decor);
}

/* --- Tên cô dâu chú rể + ngày ------------------------------------------- */
.hero__content {
  grid-area: names;
  /* .content của base.css để z-index --z-content (2); ở hero phải nằm trên
     TẤT CẢ 6 tầng hoạ tiết bên dưới. */
  z-index: calc(var(--z-content) + 5);
  /* 76% / (867/140) ≈ 12.3% là chiều cao dòng script → 18% cho cả khoảng thở. */
  padding-block-start: 18%;
  text-align: center;

  /* CỘT CHỮ CỦA HERO RỘNG BẰNG .stage, KHÔNG PHẢI --w-content.
     .content của base.css khoá 420px + đệm 24px mỗi bên ⇒ chỉ còn 372px cho
     chữ. Đo thật trong Chrome ở 1440px: "Quang Danh" ở Hastegi 92px rộng
     393px > 372px nên <h1> rớt thành "Quang" / "Danh" — tên chú rể bị bẻ đôi
     ngay dòng đầu tấm thiệp. Lỗi bám từ 445px trở lên (chỗ 19.6vw chạm trần
     92px trong khi cột chữ đã khoá ở 420px), tức TOÀN BỘ tablet + desktop.
     Hero chỉ có <h1> và dòng ngày, không có đoạn văn nào cần cột hẹp 420px để
     dễ đọc, nên cho nó dùng trọn 520px của .stage — đúng bằng dải mà dòng
     script "Save our date" (76% stage ≈ 395px) đang chiếm, nên hai dòng chữ
     ăn khớp nhau hơn hẳn. Đệm ngang hạ về --sp-4: chỉ để chữ không dính mép
     màn ở khổ hẹp, 16px là đủ cho chữ display. Sau khi sửa: 488px cho chữ ở
     desktop (dư 24%), 358px ở 390px, 288px ở 320px. */
  max-inline-size: none;
  padding-inline: var(--sp-4);
}

.hero__names {
  /* CHỖ DUY NHẤT DÙNG ROMANTICALLY. Ghi đè .t-name-lg (Hastegi) của base.css
     — cùng độ đặc hiệu 0,1,0 nhưng file này nạp SAU nên thắng, đúng cơ chế
     .envelope-intro__cta đang dùng. Đừng đổ --ff-hero-name xuống .t-name-lg:
     utility đó còn dùng cho chỗ khác, và cả thiệp chỉ nên có MỘT dòng nét bút.
     Đổi font ở đây BẮT BUỘC kéo theo --fs-name-hero (17.7vw → 8.85vw, trần
     92 → 46px) vì Romantically vẽ glyph gấp đôi ô em của nó — bảng đo từng bề
     rộng máy ghi ở tokens.css, đừng trả token về số cũ mà không đọc chỗ đó. */
  font-family: var(--ff-hero-name);
  /* Romantically là chữ ký NỐI NÉT (u–a–n–g trong "Quang" dính liền).
     --ls-wide của .t-name-lg giãn 1px sẽ cắt đứt các nét nối — cùng lý do đã
     ghi ở .envelope-intro__cta. */
  letter-spacing: normal;
  /* LÀM ĐẬM NÉT — KHÔNG dùng font-weight được, và đây là lý do:
     Romantically CHỈ CÓ MỘT nét (file @font-face khai báo font-weight: 400,
     không có bản Bold). Đặt font-weight: 600/700 thì trình duyệt tự "béo"
     glyph ra — bold giả. Đã đo lượng mực trên ảnh chụp thật: bold giả cho
     +63% mực, quá tay so với yêu cầu "đậm hơn tý", lại còn mỗi trình duyệt
     béo một kiểu nên máy khách nhìn khác máy mình. Chrome cũng gộp 600 và
     700 thành CÙNG một mức, không chỉnh nhỏ hơn được.
     -webkit-text-stroke viền đều quanh nét, chỉnh được tới từng phần trăm em
     và cho ra kết quả GIỐNG NHAU trên Chrome/Edge/Safari/Firefox. Đo lượng
     mực trên ảnh chụp thật, ở cỡ 40px, so với nét gốc:
       0.015em  +30%
       0.02em   +41%   ← đang dùng
       0.025em  +52%
       0.03em   +62%   (≈ đúng mức bold giả, nhưng chỉnh được)
       0.04em   +82%
     Chọn 0.02em (≈0.8px ở cỡ 40px): đậm rõ, đứng vững cạnh dòng script
     "Save our date", mà vẫn còn nét thanh–đậm của chữ ký. Trước đó dùng
     0.015em — đúng nghĩa "đậm hơn tý" nhưng còn mảnh.
     Vì sao ĐƠN VỊ em chứ không px: cỡ chữ chạy 24→40px theo bề rộng máy;
     viền cố định 0.8px sẽ làm chữ trên điện thoại trông đậm hơn hẳn trên máy
     tính. Theo em thì tỉ lệ nét/thân giữ nguyên ở mọi khổ màn.
     Thuộc tính này CHỈ TÔ, không đổi advance width → không phải chỉnh lại
     --fs-name-hero. Trình duyệt nào không hiểu thì chữ về lại nét mảnh gốc,
     không vỡ layout.
     Muốn đậm nữa thì tăng từng nấc 0.005em rồi NHÌN, đừng quay lại
     font-weight. Đã dựng thử tới 0.04em: bụng chữ ('a', 'g', 'D') vẫn chưa
     bít, nhưng từ khoảng 0.03em trở lên nét thanh và nét đậm bắt đầu bằng
     nhau — chữ ký mất dáng, thành nét đều. Đó mới là trần thật, không phải
     chuyện bít mắt chữ. */
  -webkit-text-stroke: 0.02em currentColor;
  /* 2.8 — CHỌN BẰNG MẮT, KHÔNG BẰNG CÔNG THỨC, và đây là chỗ dễ chỉnh sai
     nhất của font này. Hộp dòng tự nhiên của Romantically là 4.311em (hhea
     5248/−3581 trên upem 2048) vì nó chừa chỗ cho những nét móc rất dài: nét
     mực của "Quang Danh" cao 2.028em trên baseline và buông 1.354em xuống
     dưới. Muốn hai khung mực RỜI HẲN nhau thì phải đẩy line-height lên ~3.7 —
     lúc đó cụm tên rã thành hai khối tách rời, hỏng hẳn dáng chữ ký. Ngược
     lại, phép đo bằng khung bao chuỗi báo "đè nhau ~25px" ở 2.8 là quá chặt:
     các nét móc lồng vào nhau ở những vị trí NGANG khác nhau nên không thật
     sự chạm. Cả hai phép tính đều vô dụng ở đây — phải nhìn.
     Đã dựng thật rồi chụp so, VÀ PHẢI SO TRONG HERO ĐẦY ĐỦ (có dòng script
     "Save our date" đè phía trên + dòng ngày phía dưới) chứ không phải cụm
     tên đứng một mình — đo rời cho kết quả sai:
       1.6 → 'B' của "Bích" quấn vào vòng 'Q' của "Quang", rối
       2.0 → còn chạm nhẹ
       2.4 → đứng một mình thì đẹp, nhưng đặt vào hero thì 'B' vẫn vướng vòng
             'Q' và dấu "&" bị kẹp cứng giữa hai nét lớn
       2.8 → 'B' thoát hẳn vòng 'Q', "&" có chỗ thở, vẫn là MỘT cụm  ← chọn
       3.2 → hai tên bắt đầu rời nhau, đẩy cụm ảnh polaroid xuống quá sâu
     Ở 46px cụm tên cao ~284px, xấp xỉ bản Dancing Script (266px) nên dải
     "names" của grid không xô lệch. */
  line-height: 2.8;
  /* Sàn thấp hơn --fs-name-lg — xem ghi chú --fs-name-hero ở tokens.css. */
  font-size: var(--fs-name-hero);
  margin-block-end: var(--sp-5);
  text-wrap: balance;
}

/* Mỗi tên một dòng (reference SECTION3 cũng xếp dọc như vậy). Nếu một tên
   vẫn dài hơn cột 420px thì nó tự ngắt theo TỪ — base.css chỉ break-word khi
   một từ đơn không vừa, nên không bao giờ cắt giữa chữ. */
.hero__name {
  display: block;
}

.hero__amp {
  display: block;
  font-size: 0.4em;
  line-height: 1;
  color: var(--sage-soft);
  margin-block: var(--sp-1);
}

/* NGÀY 25.10.2025 — CHI TIẾT KÝ HIỆU CỦA REFERENCE: nghiêng đúng 5°.
   Phải dùng thuộc tính `rotate` chứ KHÔNG dùng `transform: rotate(5deg)`:
   phần tử này mang .reveal, và animations.css có
   `.reveal.is-visible { transform: none }` sẽ xoá sạch transform ngay khi
   hiện ra. `rotate` là thuộc tính riêng (CSS Transforms L2), hợp thành với
   transform của .reveal nên góc nghiêng còn nguyên ở mọi trạng thái, kể cả
   trong @media (prefers-reduced-motion) và khi in.
   fit-content + margin auto để chỉ xoay đúng khối chữ, không xoay cả dòng. */
.hero__date {
  inline-size: fit-content;
  margin-inline: auto;
  rotate: 5deg;
}

/* --- Dải SCENE: hai khung polaroid ------------------------------------- */
/* aspect-ratio 420.736 / 503 của reference chính là thứ ĐỊNH chiều cao hàng
   "scene"; mọi lớp .decor khác neo vào cùng ô grid này. */
.hero__photos {
  grid-area: scene;
  position: relative;
  z-index: calc(var(--z-decor) + 2);
  align-self: start;
  aspect-ratio: 421 / 503;
}

/* Hai khung mang .reveal.reveal--rotate-left/right ⇒ KHÔNG đặt transform ở
   đây. overflow: clip để góc ảnh đã xoay không lú ra ngoài tấm giấy
   (ảnh PNG khung đã gồm cả bóng đổ nên không mất gì khi clip).

   isolation: isolate là BẮT BUỘC, không phải trang trí.
   Hai <figure> này chồng nhau 16% bề ngang (trái 8%–58%, phải 42%–92%).
   Trước đây cả hai đều `position: absolute; z-index: auto` ⇒ KHÔNG tạo
   stacking context, nên 4 phần tử con tranh nhau xếp lớp TRỰC TIẾP trong
   .hero__photos và thứ tự vẽ ra thành:
       ảnh chú rể → ảnh cô dâu → KHUNG chú rể → khung cô dâu
   (`.hero__polaroid-frame` có z-index: 1, hai ảnh chân dung z-index auto).
   Tức KHUNG GIẤY TRẮNG CỦA CHÚ RỂ vẽ đè lên ẢNH CÔ DÂU: đã quan sát ở 1440px
   và 390px — nẹp trắng của khung trái cắt dọc ngay qua mặt cô dâu, và mép
   trong của nó trông như một khung thứ hai lồng trong khung phải.
   `overflow: clip` KHÔNG cứu được: nó chỉ cắt con của chính mình, không đổi
   thứ tự vẽ. isolation: isolate cho mỗi <figure> một stacking context riêng
   ⇒ z-index của khung chỉ còn so với ảnh CÙNG figure, còn hai figure so nhau
   theo thứ tự DOM (chú rể trước, cô dâu sau) — đúng thứ tự lớp của reference
   (GROUP3 vẽ trước GROUP2). */
.hero__polaroid {
  position: absolute;
  overflow: clip;
  isolation: isolate;
}

/* BỐ CỤC HAI KHUNG — CỐ Ý LỆCH KHỎI TOẠ ĐỘ REFERENCE.
   Reference: GROUP3 (trái) 14.6% / 53.4% và GROUP2 (phải) 33.7% / 53%, tức hai
   khung chồng nhau 30.3% — hơn MỘT NỬA khung trái. Reference chỉ là ô giấy
   trống nên chồng bao nhiêu cũng không sao. Với ảnh thật thì hỏng hai việc:

   1. LỆCH HẲN VỀ PHÍA CÔ DÂU. Khung cô dâu vẽ sau nên hiện trọn 53% bề ngang,
      còn khung chú rể chỉ còn thấy 23.1% (43% của chính nó) — ảnh cô dâu to
      gấp 2,3 lần ảnh chú rể. Đọc ra là "thiệp của cô dâu", không phải của đôi.
   2. CẮT MẶT CHÚ RỂ. groom.jpg tỉ lệ 0.75 hẹp hơn ô giấy (176/182 ≈ 0.967) nên
      object-fit: cover chỉ cắt theo TRỤC DỌC — toàn bộ bề ngang ảnh hiện ra, và
      trong ảnh anh đứng lệch trái, đầu chiếm x 10–47%. Mép giấy ĐỤC của khung
      cô dâu (33.7% + ~5.5% viền PNG) rơi đúng x ≈ 39.5% của ảnh, cắt dọc qua
      nửa ngoài mắt kính, đuôi lông mày và thái dương. KHÔNG sửa được bằng
      object-position: trục X của nó vô hiệu khi cover chỉ cắt dọc.

   BỐ CỤC MỚI — hai khung BẰNG NHAU (50%):
       chú rể  11%–61%  ·  cô dâu 42%–92%
       chồng nhau 19% ⇒ chú rể còn thấy 31/50 = 62% khung của mình
       bề ngang thấy được: 31% (chú rể) / 50% (cô dâu) — tỉ lệ 0.62 thay vì 0.44
   Chồng ~1/3 bề ngang một tấm vẫn đủ để đọc ra "hai tấm ảnh xếp chồng", nhưng
   không còn nuốt mất tấm dưới. Hạ 53.4%→50% là cái giá phải trả để giãn ra mà
   hai tấm vẫn nằm gọn trong miệng phong bì (mặt ~6% mỗi bên); mặt người nhỏ đi
   6%, đổi lại cả hai đều hiện đủ.

   CHÚ RỂ ĐÃ DỊCH PHẢI 8% → 11% (yêu cầu chỉnh cho hài hoà). Hệ quả phải biết,
   vì mọi con số dưới đây đều đi theo:
     · chồng nhau 16% → 19%; phần thấy được của chú rể 68% → 62% khung
     · tâm cụm ảnh 50% → 51.5%. Tâm phong bì (dấu sáp) ở 50.6%, nên cụm ảnh từ
       chỗ lệch TRÁI 0.6% thành lệch PHẢI 0.9% so với dấu sáp — đổi bên nhưng
       độ lệch tương đương, vẫn coi như trùng tâm.
     · lề trái 8% → 11%, lề phải giữ 8%: hai lề KHÔNG còn bằng nhau. Đây là chủ
       ý, không phải lỗi — đừng "sửa lại cho cân" nếu chưa hỏi.
   NÚM VẶN DUY NHẤT là inset-inline-start của .hero__polaroid--groom. Muốn dịch
   thêm thì xem trần an toàn ngay dưới.

   BIÊN AN TOÀN đã đo thật trong Chrome:
     · mép giấy cô dâu ở 44.8% vs mép phải mặt chú rể ở 34.6% → hở 10.2% bề
       rộng stage (≈53px ở 520px). Trước khi dịch là 13%; trước cả bố cục này
       thì ÂM 8%, tức mép giấy cắt ngang mặt.
       ⇒ TRẦN CỦA NÚM VẶN: dịch tối đa tới ~14% thì hở về 0 và mép giấy cô dâu
       bắt đầu chạm mặt chú rể. Quá 11% nên đo lại bằng mắt trước khi chốt.
     · mặt cô dâu ở 69.3%; bó hoa phải chỉ thật sự ĐỤC từ 83.5% ở đúng tầm cao
       đó (đo alpha bouquet-calla-lily-tall.png: ở 8% chiều cao PNG, pixel đục
       đầu tiên nằm ở 41.5% bề ngang PNG) → hở 14%. Ở ≥1024px bó hoa lùi về
       74% ⇒ mép đục 86.5%, còn hở nhiều hơn.
   Lệch dọc giữ nguyên tinh thần reference nhưng thu lại (5.5%→3%): khung cô dâu
   vốn đã nằm TRÊN theo thứ tự vẽ, cho nó cao hơn nhiều nữa là cộng dồn ưu thế. */
.hero__polaroid--groom {
  inset-block-start: 3%;
  inset-inline-start: 11%;
  inline-size: 50%;
}

.hero__polaroid--bride {
  inset-block-start: 0;
  inset-inline-start: 42%;
  inline-size: 50%;
}

/* Khung PNG ĐÃ nghiêng sẵn trong pixel (−6° / +5°) và có ô trong SUỐT ALPHA →
   tuyệt đối không xoay lại; nó đứng trong luồng nên quyết định chiều cao
   <figure>, và position: relative để vẽ ĐÈ LÊN ảnh chân dung phía dưới
   (cả hai đều positioned ⇒ ai sau trong DOM thì nằm trên). */
.hero__polaroid-frame {
  position: relative;
  z-index: 1;
  inline-size: 100%;
  block-size: auto;
}

/* Ảnh chân dung lọt đúng ô giấy của khung. Dùng aspect-ratio + object-fit
   thay cho chiều cao cứng nên không bao giờ bóp méo ảnh. */
.hero__portrait {
  position: absolute;
  object-fit: cover;
  object-position: center 22%;
}

/* Xoay ẢNH cho khớp độ nghiêng của khung. Đặt ở phần tử CON của .reveal nên
   không bị `.reveal.is-visible { transform: none }` xoá.
   reference: BOX2 (khung trái) rotate(-6deg) · BOX1 (khung phải) rotate(4deg). */
.hero__polaroid--groom .hero__portrait {
  inset-block-start: 9.2%;
  inset-inline-start: 10.4%;
  inline-size: 78.2%;
  aspect-ratio: 176 / 182;
  transform: rotate(-6deg);
}

.hero__polaroid--bride .hero__portrait {
  inset-block-start: 8.8%;
  inset-inline-start: 10.3%;
  inline-size: 80.6%;
  aspect-ratio: 180 / 182;
  transform: rotate(4deg);
}

/* --- Dải SCENE: phong bì + dấu sáp ------------------------------------- */
.hero__envelope-back {
  grid-area: scene;
  inset-block-start: 2.2%;
  inset-inline-start: 10%;
  inline-size: 80.2%;
  z-index: calc(var(--z-decor) + 1);
}

.hero__envelope-front {
  grid-area: scene;
  inset-block-start: 33%;
  inset-inline-start: 9.4%;
  inline-size: 81.5%;
  z-index: calc(var(--z-decor) + 3);
}

/* Dấu sáp ở #hero (thư đã mở) — làm nhạt CÙNG MỘT MỨC với dấu sáp trên phong
   bì đang gấp kín. Hai chỗ này khách thấy cách nhau đúng một cú lật, lệch tông
   là đọc ra ngay thành "hai con dấu khác nhau". */
.hero__seal {
  grid-area: scene;
  inset-block-start: 47.7%;
  inset-inline-start: 44.3%;
  inline-size: 12.5%;
  z-index: calc(var(--z-decor) + 4);
  filter: var(--seal-soften);
}

/* --- Dải SCENE: cổng hoa ------------------------------------------------ */
/* CỐ Ý không đặt z-index cho .hero__flora: position: relative + z-index auto
   thì nó KHÔNG tạo stacking context, nhờ vậy 4 lớp hoa bên trong vẫn so
   z-index trực tiếp với phong bì/polaroid và tái tạo được đúng thứ tự lớp của
   reference (nhánh lan + tú cầu nằm SAU phong bì, hai bó hoa nằm TRƯỚC).
   Bọc trong .hero__flora còn để animations.css lệch pha .drift theo
   :nth-child(2..4). */
.hero__flora {
  grid-area: scene;
  position: relative;
  align-self: stretch;
  pointer-events: none;
}

/* DỜI XUỐNG 11.2% → 18% để bó hoa bớt ăn vào ảnh cô dâu (vẫn CHỦ Ý chồng lên
   một phần — cụm hoa phải dính vào mép ảnh mới ra "cổng hoa", rời hẳn ra là
   thành hai vật thể lạc nhau).

   Vì sao 6.8% giải quyết được nhiều như vậy: mép ĐỤC bên trái của
   bouquet-calla-lily-tall.png không đều theo chiều cao — đo alpha (ngưỡng 32)
   trên chính file PNG 359×838:

       tầm cao PNG   2%    4%    6%    8%   12%   17.5%   25%   30%
       mép đục trái  76.9% 71.0% 48.5% 41.2% 35.7% 41.5%  30.6%  9.2%

   Tức phần cắn sâu sang trái nằm ở dải 25–30% chiều cao PNG trở xuống (cụm cẩm
   tú cầu + đuôi amaranthus), còn 4% trên cùng chỉ là mấy cuống loa kèn mảnh.
   Ở 390px, bó hoa cao 275px hiển thị: dời 32px là đẩy đúng cái dải cắn sâu đó
   từ y 461 xuống 493, trong khi mép dưới ảnh cô dâu ở 528 ⇒ phần ảnh bị hoa
   chồng lên giảm từ 67px còn 35px, khoảng một nửa.

   BIÊN AN TOÀN CỦA MẶT CÔ DÂU CHỈ RỘNG RA, không hẹp lại: ghi chú ở
   .hero__polaroid--bride tính mép đục 41.2% tại tầm 8% PNG. Dời bó hoa xuống
   thì ở đúng cao độ mặt cô dâu ta rơi vào tầm 2–4% PNG, nơi mép đục lùi về
   71–77% ⇒ hở thêm chứ không bớt.

   MÉP DƯỚI vẫn trong khung: 15% + 59% (chiều cao bó hoa so với .hero__flora)
   = 77% < 100%, đo thật ở 390px cho mép dưới y=700 trong khi .hero__flora kết
   thúc ở 809 và nút "cuộn xuống" bắt đầu ở 833. Đừng đẩy quá ~24%: từ đó đuôi
   amaranthus bắt đầu lấn xuống hàng nút cuộn. */
.hero__bouquet-right {
  inset-block-start: 15%;
  inset-inline-start: 71%;
  inline-size: 30.2%;
  z-index: calc(var(--z-decor) + 5);
}

.hero__bouquet-left {
  inset-block-start: 54.7%;
  inset-inline-start: 0;
  inline-size: 31.3%;
  z-index: calc(var(--z-decor) + 5);
}

/* Hai lớp này nằm SAU phong bì → z-index thấp nhất. */
.hero__orchid {
  inset-block-start: 18.2%;
  inset-inline-start: 4.6%;
  inline-size: 22.2%;
  z-index: var(--z-decor);
}

.hero__hydrangea {
  inset-block-start: 36.6%;
  inset-inline-start: 60.9%;
  inline-size: 20.5%;
  z-index: var(--z-decor);
}

/* --- Gợi ý cuộn xuống -------------------------------------------------- */
/* Không có trong reference — phần thêm để hero có điểm dẫn xuống #invitation. */
.hero__scroll-cue {
  grid-area: cue;
  position: relative; /* cần cho z-index */
  z-index: calc(var(--z-content) + 5);
  justify-self: center;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.75rem; /* 44px */
  block-size: 2.75rem;
  margin-block-start: var(--sp-5);

  border: 1px solid rgba(var(--sage-rgb), 0.28);
  border-radius: var(--r-pill);
  color: var(--sage);

  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.hero__scroll-cue:hover {
  background-color: rgba(var(--sage-rgb), 0.08);
  border-color: var(--sage);
}

/* <symbol id="icon-chevron-down"> vẽ bằng stroke="currentColor" → chỉ cần
   màu chữ; mang .pulse-soft của animations.css. */
.hero__chevron {
  fill: none;
}

/* ==========================================================================
   3. S2 — #divider  (reference SECTION2: 420 × 252.6)
   --------------------------------------------------------------------------
   Reference: BOX4 phủ kín 420×252.6 + IMAGE13 (bông nhỏ) ở top −96.3 /
   left 263 / 51.9×55 — tức bông nằm HOÀN TOÀN phía trên section và bị cắt.
   Ta giữ đúng ý đồ đó nhưng cho bông chớm vào trong: nửa trên bị mép section
   cắt, nửa dưới lộ ra như đang "mọc" vào dải kem.
   Section này aria-hidden, thuần trang trí, cố ý không có <h2>.
   ========================================================================== */
.divider {
  /* MỘT NGUỒN SỰ THẬT cho padding của dải. .divider__bloom phải neo theo đúng
     con số này (xem khối đó); trước đây cả hai cùng viết --sp-9 nên chúng khớp
     nhau chỉ do TRÙNG HỢP — sửa padding mà quên bông là bông lệch khỏi mép
     cắt. Giờ đổi một chỗ là cả hai đi theo. */
  --divider-pad: var(--sp-6);

  background-color: var(--cream);
  /* 64px → 32px. Dải này thuần trang trí (aria-hidden, nội dung chỉ là một
     vạch vàng ở giữa) nhưng ở 64px nó cao 224px, và cộng với padding trên của
     #invitation thì từ vạch vàng xuống dòng "Kính mời…" là gần 180px trống
     liên tục. Hạ còn 32px ⇒ dải cao 160px, vẫn đủ làm nhịp nghỉ giữa hero và
     khối lời mời mà không cắt rời hai phần. */
  padding-block: var(--divider-pad);
  /* Ghi đè overflow-x: clip của .section thành clip CẢ HAI TRỤC để cắt phần
     bông tràn lên phía trên. `clip` không tạo scroll container. */
  overflow: clip;
}

.divider__stage {
  display: grid;
  align-content: center;
  /* Giữ tỉ lệ "dải nghỉ" của reference (253px) mà không cần chiều cao cứng:
     96px thân + 2×64px padding của .divider ≈ 224px. */
  min-block-size: calc(var(--sp-9) + var(--sp-6));
}

/* −(padding trên của .divider + --sp-4) ⇒ bông luôn vượt lên khỏi mép section
   đúng 16px và bị `overflow: clip` cắt như reference, BẤT KỂ padding bằng bao
   nhiêu. Phải dùng --divider-pad chứ không viết lại --sp-9: xem ghi chú ở
   .divider. */
.divider__bloom {
  inset-block-start: calc((var(--divider-pad) + var(--sp-4)) * -1);
  inset-inline-end: 12%;
  inline-size: 16%;
}

.divider__inner {
  /* .content đã lo max-inline-size / padding-inline / z-index. */
  padding-block: 0;
}

/* .rule của base.css có margin-block --sp-5; ở đây khoảng thở đã do
   .divider__stage lo nên bỏ margin dọc đi. */
.divider__rule {
  margin-block: 0;
  inline-size: min(100%, 17rem);
}

/* ==========================================================================
   4. S3 — #invitation  (reference SECTION3: 420 × 925.7)
   --------------------------------------------------------------------------
   TOẠ ĐỘ GỐC (px, cột 420) → %:
     IMAGE16 nhánh lan đôi (viền trên-phải) top 230.2 = 24.9% · phải 0 · w 36%
     IMAGE22 nhánh lan đơn (viền dưới-trái) top 826.0 = 89.2% · trái 0 · w 29.3%
   Thứ tự khối của reference là: nhà trai/nhà gái → "Kính mời…" → tên → khối
   ngày → địa điểm. DOM (hợp đồng, không đổi) đảo lại: "Kính mời…" → gia đình
   → tên → địa điểm → chỉ đường → khối ngày → âm lịch. Giữ nguyên thứ tự DOM,
   chỉ lấy lại ĐÚNG các chi tiết tạo hình của reference:
     · nhà trai / nhà gái = 2 cột ~203px cạnh nhau  (GROUP11 / GROUP12)
     · hai tên xếp DỌC, mỗi tên một dòng            (HEADLINE10 / HEADLINE11)
     · khối ngày là 3 CỘT: "tháng 10" | 25 | "năm 2025", với "25" Hastegi
       82.28px (= --fs-name-lg) áp đảo ở giữa       (GROUP14 | HEADLINE15 | GROUP15)
     · "tháng 10" và "năm 2025" có nét kẻ 1px màu sage TRÊN và DƯỚI
       (LINE1/LINE2 và LINE3/LINE4, rộng 96/117 ≈ 82% ô)
   Toàn bộ chữ của section: sage trên nền ngà (thừa hưởng color của <body>).
   ========================================================================== */
.invitation {
  /* PADDING TRÊN NHỎ HƠN PADDING DƯỚI — cố ý, không phải sơ suất.
     Phía TRÊN section này là #divider: một dải cream cao 224px mà nội dung chỉ
     là một vạch vàng nhỏ ở giữa, tức bản thân nó đã là một khoảng nghỉ lớn.
     Cộng thêm 64px padding trên nữa thì từ vạch vàng xuống dòng "Kính mời…"
     là 176px trống liên tục — dòng tiêu đề bị đẩy rời hẳn khỏi mạch đọc.
     Phía DƯỚI thì ngược lại: section kết thúc bằng khối địa điểm + nút "chỉ
     đường" đầy tới sát đáy, và .invitation__sprig-bottom còn được đẩy xuống
     dải padding này (inset-block-end: -2%) — nên 64px dưới phải giữ nguyên.
     Đổi số trên thì kiểm lại nhánh hoa đáy không thò sang #quote. */
  padding-block: var(--sp-6) var(--sp-9);
}

.invitation__stage {
  overflow-x: clip;
}

/* --- Hai nhánh lan làm viền -------------------------------------------- */
/* Nằm sau nội dung (--z-decor < --z-content). Ở mobile chúng lọt vào sau cột
   chữ; ở ≥520px thì .stage rộng hơn .content nên chúng ló ra hai bên.
   opacity 0.9: vì DOM của ta xếp "Kính mời…" TRƯỚC khối gia đình (reference
   làm ngược lại) nên khoảng trống 122px mà reference dành cho nhánh lan không
   còn — nhánh lan buộc phải nằm sau chữ, hạ nhẹ độ đục cho khối chữ nổi lên.
   Cùng quy ước với .story__spray-* của sections-story.css. */
/* 20.5% / 30% — TINH LẠI CHO BỐ CỤC HAI TIỆC.
   Lịch sử: 18% → nhánh lan nằm sau dòng địa chỉ 13px của khối gia đình, chữ
   nhỏ bị nền hoa làm rối. Rồi 22.5%/36% khi section còn MỘT tiệc (cao ~1105px
   ở 390px) — lúc đó nó rơi đúng khoảng trống dưới khối gia đình.
   Từ khi có hai tiệc, section cao 1596px ở 390px nên CÙNG MỘT SỐ % rơi xuống
   thấp hơn hẳn: đo thật ở 390px thấy bông lan đè ngang 85px lên "NGUYỄN THỊ
   BÍCH ANH" — hoa nằm ngay trên chữ "ANH". Tên cô dâu bị hoa che là lỗi, không
   phải nét trang trí.
   20.5% + thu 36%→30% (cao 70px thay vì 85px) cho nhánh lan lọt TRỌN vào khe
   giữa hai tên: đã đo lại, KHÔNG chạm bất kỳ dòng chữ nào ở 390px.
   Ở mobile cột chữ rộng hết màn nên đây là khe duy nhất còn chỗ — mọi vị trí
   khác đều đè lên một dòng chữ nào đó (đã dò từ 4% tới 33%). */
.invitation__sprig-top {
  inset-block-start: 20.5%;
  inset-inline-end: 0;
  inline-size: 30%;
  opacity: 0.9;
}

/* Hai hoạ tiết GIỮA — phần thêm cho bố cục hai tiệc, KHÔNG có trong reference
   (reference chỉ có một tiệc nên dải này không tồn tại).
   Toạ độ dọc neo vào khối số ngày của từng tiệc: 40% rơi vào "13 / 09 / 26"
   của tiệc nhà gái, 69% rơi vào "20 / 09 / 26" của tiệc nhà trai. Đó là hai
   dải trống nhất của section — chỉ có ba con số cỡ 68–92px nằm giữa cột, hai
   mép còn dư ~100px mỗi bên ở khổ 390px.
   CẢNH BÁO BẢO TRÌ: hai số % này tính theo CHIỀU CAO CỦA CẢ SECTION, nên thêm
   / bớt một khối tiệc là phải chỉnh lại. Nếu sau này chỉ còn một tiệc thì bỏ
   hẳn hai ảnh này đi — dải trống mà chúng lấp cũng không còn. */
.invitation__sprig-mid-left {
  inset-block-start: 40%;
  inset-inline-start: 0;
  /* PNG này bị cắt sẵn một nửa bông ở MÉP TRÁI (xem CATALOG.md) — cố ý để tràn
     viền, nên phải dán sát left: 0, đẩy vào trong là lộ chỗ cắt. */
  inline-size: 26%;
  opacity: 0.85;
}

.invitation__sprig-mid-right {
  /* Hạ dần: 69% → 72% → 77%. Mỗi lần đều vì cùng một lý do — cụm tú cầu rơi
     ngang DÒNG SCRIPT tên lễ ("Lễ Thành Hôn") và chen vào đuôi chữ. Dòng đó là
     chữ script cỡ 26–32px, chuỗi càng dài thì càng vươn sát mép phải: đổi
     "Lễ Tân Hôn" (199px) → "Lễ Thành Hôn" (245px) là chạm thêm 18px ở 320px.
     77% đặt cụm hoa vào khe giữa khối số "20 / 09 / 26" và dòng "CHỦ NHẬT" —
     dải trống nhất của khối tiệc, và là dải KHÔNG đổi độ rộng theo độ dài tên
     lễ. Dò cả băng 72–80% ở 320px: chỉ 76–78% là sạch hoàn toàn. */
  inset-block-start: 77%;
  inset-inline-end: 0;
  inline-size: 20%;
  opacity: 0.85;
}

.invitation__sprig-bottom {
  /* −2%, KHÔNG phải 3% như reference (reference cách đáy 31/925.7 ≈ 3.4%).
     Reference kết thúc section bằng dòng ngày âm lịch, dưới nó là khoảng
     trống — nên hoa nằm ở 3% là gọn trong góc. Bố cục hai tiệc kết thúc bằng
     KHỐI ĐỊA ĐIỂM + NÚT "CHỈ ĐƯỜNG", đầy tới sát đáy stage; ở 3% bông hoa đè
     ngang 89px lên dòng địa chỉ 13px của nhà trai — đo thật ở 390px sau khi
     điền mapsUrl. Chữ 13px không chịu được nền hoa (cùng lý do đã ghi ở
     .invitation__sprig-top).
     Số âm đẩy hoa xuống dải padding-block của .invitation (64px ở mobile,
     80px từ 768px): −2% ≈ 26px dưới đáy stage — hết đè chữ mà vẫn nằm trọn
     trong section, không thò sang #quote. */
  inset-block-end: -2%;
  inset-inline-start: 0;
  inline-size: 29.3%;
  opacity: 0.9;
}

/* --- Nhịp dọc của cả khối ---------------------------------------------- */
/* Đây là phần dày đặc nội dung nhất của trang: một nhịp cơ sở --sp-5, rồi
   nới rộng ở đúng 4 chỗ cần "thở" để khối không bị chật. */
.invitation__inner {
  text-align: center;
}

.invitation__inner > * + * {
  margin-block-start: var(--sp-5);
}

.invitation__heading {
  text-wrap: balance;
}

/* --- Nhà trai / nhà gái ------------------------------------------------ */
/* reference: 2 cột 203px + 206px trong khung 398.5 ⇒ luôn cạnh nhau.
   minmax(0, 1fr) để tên bố mẹ dài không làm phình cột. Ở 360px mỗi cột còn
   ~150px, tên dài nhất ("Bà. Trần Thị Bích Phương") tự xuống 2 dòng — vẫn
   cạnh nhau, không phải xếp dọc. */
/* column-gap co theo màn hình: ở 360px mỗi cột giành thêm được vài px, đủ để
   tên bố mẹ nằm trọn một dòng (xem ghi chú ở .families__parent). */
.families__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(var(--sp-2), 4vw, var(--sp-4));
  row-gap: var(--sp-5);
  margin-block-start: var(--sp-6);
}

.families__col {
  text-align: center;
}

.families__side {
  margin-block-end: var(--sp-2);
}

.families__parents {
  display: grid;
  row-gap: var(--sp-1);
}

/* Tên bố mẹ phải gọn trong MỘT dòng — tên người bị bẻ đôi giữa họ và tên đọc
   rất tệ trên thiệp cưới, y như lý do .invitation__name tồn tại.
   Số đo thật (Chrome): "Ông. Nguyễn Quang Lãnh" cần ~180px ở --fs-sm (15px),
   nhưng lưới hai cột chỉ cấp 148.8px ở 360px, 163.2px ở 390px và 170px ở
   ≥768px — nên ở CẢ desktop nó cũng rớt hai dòng, không riêng màn hẹp.
   Cỡ chữ co theo bề rộng và TRẦN hạ xuống 14px (thay vì --fs-sm 15px) để mọi
   khổ đều dư chỗ. Vẫn lớn hơn .families__address (13px, sage nhạt) nên thứ bậc
   thị giác "tên bố mẹ > địa chỉ nhà" không bị san phẳng.
   text-wrap: balance giữ lại làm lối thoát: tên dài bất thường vẫn xuống dòng
   cân đối chứ không bao giờ bị cắt. */
.families__parent {
  font-size: clamp(0.765rem, 3.4vw, 0.875rem);
  line-height: var(--lh-snug);
  text-wrap: balance;
}

/* Địa chỉ nhà hai bên — nhỏ hơn tên bố mẹ một bậc, sage nhạt, để không cạnh
   tranh với tên. Cột chỉ ~150px ở 360px nên địa chỉ dài chắc chắn xuống dòng:
   text-wrap: pretty để tránh dòng cuối trơ một từ. */
.families__address {
  margin-block-start: var(--sp-2);
  font-size: var(--fs-2xs);
  color: var(--sage-soft);
  line-height: var(--lh-snug);
  text-wrap: pretty;
}

/* --- Tên cô dâu chú rể + thứ bậc -------------------------------------- */
/* Khối này là <div> (không phải <p> mang .t-name như bản đầu) vì mỗi bên giờ
   có HAI dòng: thứ bậc nhỏ ở trên, tên đầy đủ lớn ở dưới. Cỡ chữ do
   .invitation__name tự khai, không thừa hưởng từ container nữa. */
.invitation__couple {
  margin-block-start: var(--sp-7);
  text-align: center;
}

.invitation__party {
  display: grid;
  row-gap: var(--sp-2);
  justify-items: center;
}

/* Thứ bậc: nhỏ, in hoa, sage nhạt — cố ý KHÔNG cạnh tranh với tên. */
.invitation__rank {
  color: var(--sage-soft);
}

/* Tên đầy đủ NÊN nằm trên một dòng — --fs-name-md được tính cho chuỗi ~17–20
   ký tự trong cột 420px, và hai tên của thiệp này vừa đúng một dòng.

   TRƯỚC ĐÂY chỗ này đặt `white-space: nowrap` kèm ghi chú "tên dài hơn thì chữ
   tự co theo clamp". GHI CHÚ ĐÓ SAI, và cái sai này nguy hiểm: clamp() co theo
   BỀ RỘNG MÀN HÌNH, không hề co theo độ dài chuỗi. Nên tên dài không co chút
   nào — nó tràn ra ngoài rồi bị .stage (overflow: hidden) CẮT CỤT trong im
   lặng. Đo thật với tên 40 ký tự: mất 260–289px chữ cuối ở cả 360/390/768px,
   và dòng thứ bậc bị đẩy lệch hẳn khỏi tâm. README lại mời người khác dùng lại
   thiệp cho đám cưới của họ, nên đây là cái bẫy nằm chờ.

   Nay bỏ nowrap: tên vừa cột thì vẫn một dòng y như cũ, tên quá dài thì XUỐNG
   DÒNG chứ không bao giờ mất chữ. Mất một dòng còn hơn mất cái tên. */
.invitation__name {
  display: block;
  font-size: var(--fs-name-md);
  /* Nếu buộc phải hai dòng thì chia đều, đỡ trơ một chữ ở dòng dưới. */
  text-wrap: balance;
  overflow-wrap: break-word;
}

.invitation__amp {
  font-size: var(--fs-md);
  line-height: 1;
  color: var(--sage-soft);
  margin-block: var(--sp-3);
}

.invitation__rule {
  margin-block: var(--sp-6) 0;
}

/* ==========================================================================
   4b. HAI KHỐI TIỆC  (.events / .event — template #tpl-event)
   --------------------------------------------------------------------------
   Đám cưới này có HAI tiệc ở hai nơi hai ngày (nhà gái 13.9 tại nhà hàng, nhà
   trai 20.9 tại tư gia) và thiệp gửi cho khách của cả hai họ. Trước đây phần
   này là MỘT bộ .invitation__where + .invitation__when + .invitation__lunar
   gắn cứng vào `venue` và `date`; nay là một danh sách render từ mảng
   `events`, mỗi phần tử tự đủ thông tin.

   VÌ SAO XẾP DỌC, KHÔNG PHẢI HAI CỘT: cột chữ khoá ở --w-content 420px, chia
   đôi còn ~186px mỗi bên — không đủ cho "NHÀ HÀNG PHÚC PALACE" (t-venue in
   hoa) lẫn dòng địa chỉ. Xếp dọc thì mỗi khối được trọn bề rộng.

   VÌ SAO KHỐI NGÀY NẰM NGANG: bố cục cũ xếp DỌC ba số (20 trên / 09 giữa / 26
   dưới) cao ~200px. Nhân đôi thành hai khối là +400px chỉ để in bốn con số.
   Nằm ngang giữ nguyên chữ số ngày cỡ --fs-name-lg (nét nhấn lớn nhất của
   reference) mà chỉ tốn ~110px mỗi khối. Đo bề rộng ở trần 92px: "13 / 09 /
   26" ≈ 252px, vẫn dư trong cột chữ 372px của desktop.
   ========================================================================== */
.invitation .events {
  display: grid;
  row-gap: var(--sp-7);
  /* Thắng .invitation__inner > * + * (--sp-5): khối này mở một chương mới của
     thiệp nên cần khoảng thở lớn hơn nhịp cơ sở. */
  margin-block-start: var(--sp-7);
}

/* Vạch ngăn giữa hai tiệc: khách phải thấy ngay đây là HAI sự kiện tách bạch
   chứ không phải một khối thông tin dài.

   Dựng bằng ::before/::after thay vì thêm phần tử vào template, vì template
   dùng chung cho CẢ HAI tiệc — thêm <img> vào đó thì tiệc đầu cũng mọc một
   vạch ngăn ở trên đầu. `.event + .event` chỉ khớp khối thứ hai trở đi.

   ::before là đường kẻ CÓ KHE Ở GIỮA (linear-gradient chứ không phải border):
   bông lan là PNG có nền trong suốt, kẻ liền một mạch sẽ lộ qua các kẽ cánh
   hoa. Cắt khe 38–62% rồi đặt bông vào đúng khe đó thì không có gì chạy sau
   hoa cả. */
.event + .event {
  position: relative;
  padding-block-start: var(--sp-7);
}

.event + .event::before {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline: 12%;
  block-size: 1px;
  background-image: linear-gradient(
    to right,
    rgba(var(--sage-rgb), 0.22) 0 38%,
    transparent 38% 62%,
    rgba(var(--sage-rgb), 0.22) 62% 100%
  );
}

/* Bông lan đơn ngồi giữa khe của đường kẻ. Đây là hoạ tiết DUY NHẤT của dự án
   nạp bằng CSS thay vì <img class="decor"> — bắt buộc, vì nó phải neo vào ranh
   giới giữa hai phần tử của một danh sách render động, thứ mà HTML tĩnh không
   trỏ tới được. */
.event + .event::after {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 50%;
  translate: -50% -50%;
  inline-size: 3.25rem;
  aspect-ratio: 230 / 261;
  background-image: url('../img/orchid-bloom-single.png');
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  pointer-events: none;
}

/* "NHÀ GÁI" / "NHÀ TRAI" — nhãn định hướng, cố ý nhỏ và nhạt hơn tên lễ. */
.event__side {
  color: var(--sage-soft);
}

.event__ceremony {
  margin-block-start: var(--sp-1);
  color: var(--sage);
}

/* --- Khối ngày: 13 / 09 / 26 nằm ngang --------------------------------- */
/* align-items: baseline để "13" (68–92px) và "09"/"26" (29.6–34.4px) đứng
   chung một đường chân chữ — canh giữa theo chiều cao sẽ làm hai số nhỏ trôi
   lên lưng chừng số lớn. Nét kẻ sage trên/dưới giữ motif đường kẻ của
   reference. */
.event__datestack {
  position: relative;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--sp-2);
  margin-block-start: var(--sp-4);
  padding-block: var(--sp-4);
  font-family: var(--ff-display);
  line-height: 1;
  letter-spacing: var(--ls-wide);
}

.event__datestack::before,
.event__datestack::after {
  content: '';
  position: absolute;
  inset-inline: 22%;
  block-size: 1px;
  background-color: var(--sage);
}

.event__datestack::before {
  inset-block-start: 0;
}

.event__datestack::after {
  inset-block-end: 0;
}

/* Ngày là điểm nhấn lớn nhất của khối (reference dùng Hastegi 82.28px cho số
   ngày = đúng --fs-name-lg). Tháng và năm nhỏ hơn một bậc để mắt đọc theo
   thứ tự ngày → tháng → năm. */
.event__dd {
  font-size: var(--fs-name-lg);
}

.event__mm,
.event__yy {
  font-size: var(--fs-name-md);
  color: var(--sage-soft);
}

/* Dấu "/" là nhịp phân cách giữa ba số.
   CỐ Ý KHÔNG dùng --gold: gold (#FDE298) trên nền cream (#FFFAF0) gần như
   không có tương phản — đã quan sát ở 390px, dấu "/" biến thành hạt bụi.
   --sage-soft đọc rõ mà vẫn dưới hạng ba con số. */
.event__sep {
  font-size: var(--fs-lg);
  color: var(--sage-soft);
  line-height: var(--lh-tight);
}

.event__weekday {
  margin-block-start: var(--sp-3);
  color: var(--sage-soft);
}

.event__lunar {
  margin-block-start: var(--sp-2);
}

/* --- Hai mốc giờ: nhãn | giá trị --------------------------------------- */
/* <dl> hai cột, cả cụm canh giữa cột chữ. Kiểu nhãn–giá trị thay cho một dòng
   chạy ngang ("Đón khách 17 giờ 00 · Khai tiệc 18 giờ 00") vì dòng chạy ngang
   rớt hai dòng ở khổ ≤390px và điểm ngắt rơi vào giữa cụm giờ. Lưới thì không
   bao giờ ngắt, và giờ của hai tiệc thẳng cột với nhau khi cuộn qua.

   align-items: baseline SỬA LỖI LỆCH ĐÃ THẤY: nhãn là 14px (--fs-xs, dòng cao
   19,6px) còn giờ là 18,6px (--fs-md-lg, dòng cao 26px). Mặc định `normal`
   kéo giãn cả hai ô cho bằng chiều cao hàng (26px) rồi đặt chữ ở MÉP TRÊN hộp
   của mình, nên chân chữ "Đón khách" nằm cao hơn chân chữ "17 GIỜ 00" ~5px —
   đo thật ở 390px. baseline ghim hai chữ vào cùng một đường chân.

   text-align: start cho nhãn (KHÔNG phải end): canh phải làm mép TRÁI của
   "Đón khách" và "Khai tiệc" so le nhau vì hai chuỗi khác độ dài. Canh trái
   thì cả hai cột đều thẳng mép trái, cả khối vẫn nằm giữa nhờ justify-content.

   margin: 0 là BẮT BUỘC — UA stylesheet cho <dl> margin dọc 1em và <dd>
   margin-inline-start 40px, cả hai đều phá canh giữa. */
.event__times {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: center;
  align-items: baseline;
  column-gap: var(--sp-4);
  row-gap: var(--sp-2);
  margin-block: var(--sp-5) 0;
}

.event__time-label {
  margin: 0;
  color: var(--sage-soft);
  text-align: start;
}

/* Reference #HEADLINE14 là Lora 20px, KHÔNG phải cỡ của .t-heading (18.33px).
   Đây là dòng khách cần đọc được đầu tiên, nên nó phải nhô lên khỏi tiêu đề
   quanh nó — xem ghi chú --fs-md-lg trong tokens.css. */
.event__time-value {
  margin: 0;
  font-size: var(--fs-md-lg);
  text-align: start;
}

/* --- Địa điểm ---------------------------------------------------------- */
.event__venue-label {
  margin-block-start: var(--sp-5);
}

.event__venue-name {
  margin-block-start: var(--sp-2);
  text-wrap: balance;
}

.event__address {
  margin-block-start: var(--sp-2);
  text-wrap: pretty;
}

/* --- Nút "chỉ đường" --------------------------------------------------- */
/* .btn.btn--ghost của base.css đã lo viên thuốc viền sage + đổ nền khi hover;
   .btn svg đã tô icon theo currentColor. Mang .pulse-soft (reference cho
   GROUP21 chạy pulse vô hạn). Mỗi tiệc có nút riêng, tiệc nào chưa có link
   thì data-if xoá nút của riêng tiệc đó. */
.event__maps-wrap {
  margin-block-start: var(--sp-4);
}

.event__maps {
  /* Ghim bên TRÁI chữ (reference). THU GỌN THÊM cho hợp layout: chữ --fs-3xs,
     viên thuốc thấp + hẹp. GIỮ nhịp pulse-soft (class .pulse-soft trên <a>) —
     KHÔNG ghi đè animation nữa. Nút nhỏ nên biên độ scale 1.05 cũng dịu, không
     còn cộm như bản to. */
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3);
  min-block-size: 2rem;
  font-size: var(--fs-3xs);
  letter-spacing: var(--ls-wide);
}

.event__pin {
  /* Ghim cao hơn chữ một chút, đúng dáng của reference (IMAGE21 15.7×22). */
  inline-size: 0.85em;
}

/* ==========================================================================
   5. ≥768px — nhịp dọc thoáng hơn, hoạ tiết bắt đầu ló ra hai bên cột chữ
   (từ 520px trở lên .stage đã đạt --w-stage nên phần "bleed" là chênh lệch
   giữa --w-stage 520px và --w-content 420px).
   ========================================================================== */
@media (min-width: 768px) {
  .envelope-intro {
    gap: var(--sp-8);
    /* KHÔNG ghi đè --envelope-w ở đây nữa. Bản trước đặt min(58vw, 23rem) và
       chính cái trần 23rem đó làm phong bì bé tí trên máy tính: màn 1440px thì
       58vw = 835px nhưng vẫn bị ghim ở 368px. Công thức 3 trần ở khối gốc đã
       tự lo mọi khổ màn — thêm luật ở đây chỉ tạo nguồn sự thật thứ hai. */
  }

  .hero {
    padding-block: var(--sp-8) var(--sp-9);
  }

  .hero__content {
    /* Cột chữ đã đạt 420px trong khi stage là 520px ⇒ dòng script rộng hơn
       cột chữ, cần ít khoảng đệm tương đối hơn. */
    padding-block-start: 16%;
  }

  /* 80px → 40px, cùng lý do đã ghi ở rule gốc của .divider: dải này gần như
     trống nên padding lớn chỉ đẩy "Kính mời…" rời khỏi mạch đọc. Vẫn để rộng
     hơn mobile (40 so với 32) vì màn rộng thì cần nhịp nghỉ dài hơn một chút.
     ĐẶT QUA --divider-pad, KHÔNG viết padding-block trực tiếp: .divider__bloom
     neo theo biến này, ghi đè padding thẳng là bông lệch khỏi mép cắt. */
  .divider {
    --divider-pad: var(--sp-7);
  }

  /* inset-block-start ĐÃ BỎ khỏi đây: nó tự bám --divider-pad qua rule gốc.
     Giữ lại thì thành hai nguồn sự thật, và đó đúng là cái bẫy đã gài sẵn —
     bản trước lặp lại var(--sp-10) ở cả hai chỗ nên chúng chỉ khớp nhau nhờ
     trùng số. */
  .divider__bloom {
    inline-size: 15%;
    inset-inline-end: 10%;
  }

  /* Giữ đúng thế bất đối xứng của rule gốc: trên hẹp (dưới dải cream đã là
     khoảng nghỉ rồi), dưới rộng (còn phải chứa .invitation__sprig-bottom). */
  .invitation {
    padding-block: var(--sp-7) var(--sp-10);
  }

  /* TRƯỚC ĐÂY là var(--sp-6) (32px) cho "thoáng". Nhưng cột chữ vẫn khoá ở
     420px, nên nới khe = bóp cột: mỗi cột chỉ còn 170px và tên bố mẹ rớt hai
     dòng ngay trên desktop. Khoảng thở không đáng đổi bằng một cái tên bị bẻ
     đôi — giữ var(--sp-4) để mỗi cột được 178px. */
  .families__grid {
    column-gap: var(--sp-4);
  }

  /* Khối ngày nằm ngang: ≥768px chữ số chạm trần clamp nên cụm "13 / 09 / 26"
     rộng ra, hai nét kẻ phải lùi vào ít hơn để vẫn ôm trọn cụm số; nhịp dọc
     giãn thêm một bậc. */
  .event__datestack {
    padding-block: var(--sp-5);
  }

  .event__datestack::before,
  .event__datestack::after {
    inset-inline: 18%;
  }

  /* Hai tiệc cách nhau thoáng hơn khi màn rộng. */
  .invitation .events {
    row-gap: var(--sp-8);
  }

  .event + .event {
    padding-block-start: var(--sp-8);
  }

  /* Cột chữ vẫn 420px nhưng .stage đã 520px ⇒ ba hoạ tiết bên lùi hẳn ra máng
     50px hai bên, không còn nằm sau chữ nữa. */
  .invitation__sprig-mid-left {
    inline-size: 22%;
    inset-inline-start: -4%;
  }

  .invitation__sprig-mid-right {
    inline-size: 17%;
    inset-inline-end: -3%;
  }

  /* Nhánh lan trên: RULE NÀY TRƯỚC ĐÂY NẰM Ở KHỐI ≥1024px, dù chính comment
     của nó mở đầu bằng "≥768px cột chữ vẫn 420px nhưng .stage đã là 520px" —
     tức nó được viết cho tình huống bắt đầu từ 768px mà lại đặt nhầm chỗ. Hậu
     quả đo được ở đúng 768px: nhánh lan đè ngang 57px lên "NGUYỄN THỊ BÍCH
     ANH". Chuyển lên đây cho khớp với điều kiện thật.
     −12% (thay vì −10%): −10% còn chạm 4px ở 768px, −12% thì hết hẳn mà vẫn
     còn 94px hoa lộ ra trong máng. */
  .invitation__sprig-top {
    inline-size: 30%;
    inset-inline-end: -12%;
  }

  /* Nhánh lan dưới: cùng lý do và cùng lịch sử đặt nhầm khối như .sprig-top.
     Đo ở đúng 768px khi chưa có rule này: đè ngang 63px lên dòng địa chỉ của
     tiệc nhà trai. Bộ −6% / 32% / −4% cho kết quả sạch ở CẢ 768px lẫn 1440px
     (hết đè, và mép dưới còn cách đáy section 11–19px). */
  .invitation__sprig-bottom {
    inline-size: 32%;
    inset-inline-start: -6%;
    inset-block-end: -4%;
  }
}

/* ==========================================================================
   6. ≥1024px — hoạ tiết tràn sâu nhất ra ngoài .stage, cột chữ vẫn 420px
   ========================================================================== */
@media (min-width: 1024px) {
  .hero {
    padding-block: var(--sp-9) var(--sp-10);
  }

  /* Nới cổng hoa ra hai bên cho khung ảnh rộng hơn (không đổi kích cỡ để cụm
     hoa không lấn xuống hàng "cue"). */
  .hero__bouquet-right {
    inset-inline-start: 74%;
  }

  .hero__bouquet-left {
    inset-inline-start: -4%;
  }

  /* Khối ≥768px đã đặt --divider-pad: var(--sp-7) và giá trị đó vẫn đúng ở
     đây, nên rule này KHÔNG cần lặp lại nữa. Trước đây nó ghi lại đúng cùng
     một con số (--sp-10) như khối 768px — thừa, và nguy hiểm vì nếu chỉ sửa
     một trong hai thì mốc 1024px lặng lẽ nhảy về giá trị cũ. */

  /* .invitation__sprig-top KHÔNG còn override ở đây: điều kiện của nó là
     ".stage rộng hơn cột chữ", đúng từ 768px chứ không phải 1024px, nên rule
     đã chuyển lên khối ≥768px phía trên. Giữ lại nguyên do lịch sử để không ai
     đưa ngược xuống: 39%/-6% của bản đầu đưa mép trái nhánh lan vào tận x=348
     (cột chữ kết thúc ở 470) → đè lên đuôi "NGUYỄN QUANG DANH", quan sát ở
     1440px. */

  /* .invitation__sprig-bottom cũng đã chuyển lên khối ≥768px, cùng lý do với
     .sprig-top: điều kiện là ".stage rộng hơn cột chữ", đúng từ 768px.
     Ghi lại số đo để đừng ai chỉnh mò: ở bộ −6%/32%, mép PHẢI của nhánh lan
     thò sâu vào cột chữ nên phải hạ inset-block-end tới −4% mới thoát dòng địa
     chỉ 13px. ĐỪNG hạ tới −6%: khi đó hoa thò ra ngoài section, sang #quote. */
}

/* ==========================================================================
   7. TÔN TRỌNG prefers-reduced-motion
   File này chỉ có 2 chuyển động riêng: transition khi hover ở .music-toggle
   và ở .hero__scroll-cue. Tắt hẳn transition và bỏ phép nhấc 3px.
   LƯU Ý: `rotate: 5deg` ở .hero__date KHÔNG bị tắt — đó là chi tiết TẠO HÌNH
   tĩnh của reference, không phải chuyển động. Không phần tử nào của S1–S3 bị
   bỏ lại ở opacity 0 (animations.css đã mở hết .reveal trong media này).
   ========================================================================== */
@media (prefers-reduced-motion: reduce) and (max-width: 0px) { /* guard tắt theo yêu cầu: thiệp LUÔN chạy animation. Xoá ' and (max-width: 0px)' để bật lại. */
  .music-toggle,
  .hero__scroll-cue {
    transition: none;
  }

  .music-toggle:hover {
    transform: none;
  }
}
