/* ==========================================================================
   tokens.css — Design tokens
   Trích xuất từ reference: jmiiwedding.com/dathaaa
   Mọi giá trị màu / font / nhịp cách đều khai báo ở đây. Không hardcode
   màu hoặc font-family ở bất kỳ file nào khác.
   ========================================================================== */

/* --- Fonts ------------------------------------------------------------- */
@font-face {
  font-family: 'Hastegi';
  src: url('../fonts/hastegi.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Rift';
  src: url('../fonts/rift.ttf') format('truetype');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'BeyondPerfection';
  src: url('../fonts/beyond-perfection.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: url('../fonts/lora-regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Lora';
  src: url('../fonts/lora-semibold.ttf') format('truetype');
  font-weight: 600 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'UVNHoaTay';
  src: url('../fonts/uvn-hoa-tay.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Dancing Script — chữ viết tay NỐI NÉT, đủ dấu tiếng Việt (cmap phủ 559 mã,
   không thiếu một dấu nào của bộ chữ Việt) nên "ở, ệ" hiển thị đúng.
   MỘT khai báo duy nhất phục vụ CẢ HAI chỗ đang dùng — nút "Mở thiệp"
   (--ff-cta) và vai trò DỰ PHÒNG cho tên ở đầu thiệp (--ff-hero-name). Đừng
   thêm @font-face thứ hai cho cùng family + cùng file: trình duyệt sẽ tải lặp
   và về sau sửa một chỗ quên chỗ kia.
   File là font BIẾN THIÊN, một file chứa cả dải 400–700 nên `font-weight:
   400 700` là khai báo đúng (giống Rift ở trên) — khai báo 400 cứng sẽ khiến
   trình duyệt tự làm đậm giả khi gặp font-weight lớn hơn. File nằm cùng thư
   mục fonts nên vẫn chạy offline (file://).
   Giấy phép SIL OFL 1.1 — MIỄN PHÍ cả cho mục đích thương mại. Đây là font
   DUY NHẤT trong thư mục này có giấy phép rõ ràng; bản OFL đi kèm ở
   `../fonts/dancing-script-OFL.txt` vì OFL bắt buộc phát hành kèm giấy phép. */
@font-face {
  font-family: 'DancingScript';
  src: url('../fonts/dancing-script.ttf') format('truetype');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* Romantically — chữ ký viết tay nét mảnh, dùng cho ĐÚNG MỘT chỗ: tên cô dâu
   chú rể ở đầu thiệp (--ff-hero-name bên dưới).
   ĐÂY LÀ BẢN GỐC "Romantically" của Abo Daniel, KHÔNG phải bản iCiel Việt hoá.
   Hệ quả phải biết: bảng cmap chỉ có 256 mã (hết Latin-1), đủ cho "Quang Danh"
   và "Bích Anh" — đã kiểm từng ký tự, không thiếu glyph nào — nhưng KHÔNG có
   các dấu riêng của tiếng Việt (ế ộ ữ ằ ...). Vì vậy chuỗi dự phòng của
   --ff-hero-name để DancingScript ngay sau: trình duyệt thay glyph theo TỪNG
   KÝ TỰ, nên nếu sau này đổi tên trong data.js thành tên có dấu nặng hơn thì
   riêng ký tự đó rơi về Dancing Script chứ không thành ô vuông.
   MUỐN DÙNG BẢN iCiel VIỆT HOÁ: chép file .ttf/.otf đó đè lên
   `../fonts/romantically.otf` là xong, không phải sửa CSS.
   Giấy phép: đây là bản DEMO "Free For Personal Use" (tên family trong file
   ghi đúng như vậy) — bản thương mại phải mua. Xem `romantically-README.txt`
   cùng thư mục. Đám cưới nhà mình thì không sao; kinh doanh thì phải mua. */
@font-face {
  font-family: 'Romantically';
  src: url('../fonts/romantically.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  /* --- Màu ------------------------------------------------------------ */
  --sage: #52613e;        /* màu chính — tiêu đề, đường kẻ, nút, viền */
  --sage-soft: #7e8a6c;   /* sage nhạt — CHỈ hoạ tiết & chữ ≥ 24px (3.3:1) */
  /* BỔ SUNG SAU VÒNG AUDIT TRỢ NĂNG: --sage-soft chỉ đạt 3.31:1 trên nền ngà
     nên KHÔNG được dùng cho chữ nhỏ (ngưỡng AA là 4.5:1 dưới 18.66px), mà
     giữa --sage (6.4:1) và --sage-soft thì thang màu không có bậc nào. Đây là
     bậc còn thiếu: nhạt hơn --sage một cách thấy được nhưng vẫn 4.8:1 → dùng
     cho placeholder và nhãn phụ. Đo bằng công thức tương phản WCAG 2.1 với
     nền --ivory. Đừng làm nhạt thêm. */
  --sage-mute: #687454;   /* chữ phụ nhỏ trên nền sáng — 4.8:1, đạt AA */
  --sage-deep: #3c4830;   /* sage đậm — hover, chữ trên nền sage nhạt */
  --olive: #5a6b0d;       /* nhấn phụ — footer, link */
  --ivory: #fffaf0;       /* nền trang */
  --cream: #fff3e0;       /* nền section xen kẽ */
  --gold: #fde298;        /* hoạ tiết, đường phân cách */
  --glow: rgba(255, 200, 100, 0.35); /* lớp phủ ánh nến */
  --ink: #2b2b2b;         /* chữ nội dung tối */
  --paper: #ffffff;       /* nền thẻ */
  --on-dark: #ffffff;     /* chữ trên nền ảnh / nền sage */

  /* Đỏ sáp niêm — MÀU DUY NHẤT ngoài thang sage, CHỈ dùng cho dấu mộc trái tim
     A&D trên phong bì (.envelope-intro__stamp). Khai ở đây cho đúng luật
     "không màu literal ngoài tokens.css". */
  --wax-red: #b02b2b;      /* thân sáp */
  --wax-red-hi: #d1544f;   /* ánh sáng góc trên-trái + nét nổi (tim, chữ) */
  --wax-red-deep: #7c1d1d; /* mép tối */

  /* LÀM NHẠT DẤU SÁP SAGE Ở ĐẦU THƯ (wax-seal-sage-botanical.png).
     File PNG có màu trung bình rgb(81, 96, 54) — TRÙNG KHÍT --sage (#52613e),
     tức con dấu đậm ngang với màu chính của cả trang. Trên nền giấy phong bì
     (~#E3DFD8) nó thành điểm tối nhất của cả cụm đầu thư và hút mắt khỏi tên
     cô dâu chú rể — đúng thứ "nổi trội" cần bỏ.
     Sửa bằng filter chứ KHÔNG sửa file PNG, vì cùng file đó còn làm favicon
     và apple-touch-icon (index.html dòng 33–34) — ở kích thước 16px thì bản
     đậm mới đọc ra hình.
     saturate(0.5) hạ độ rực để sắc xanh không còn "kêu" cạnh nền ngà ấm;
     brightness(1.55) kéo màu lên; opacity(0.82) cho sắc giấy ăn xuyên qua nên
     con dấu NGẢ THEO nền chứ chỉ sáng lên suông. Ba giá trị hợp lại cho màu
     HIỆN THẬT trên giấy ~#949d82 — nhạt hơn cả --sage-soft (#7e8a6c) một bậc,
     tức đã ra khỏi vai "màu chữ" và về đúng vai hoạ tiết chìm.
     ĐÂY LÀ GIỚI HẠN: thử tiếp 0.45/1.65/0.76 (ra ~#a0a790) thì vành sáng quanh
     mép sáp và ba chùm hoa nổi bắt đầu tan vào nền, mất luôn cảm giác "khối
     sáp dập nổi" — nhạt thêm nữa là hỏng hình, không còn là hỏng màu.
     opacity() ĐẶT TRONG CHUỖI FILTER, không dùng thuộc tính opacity: keyframes
     envelope-seal-break animate thuộc tính opacity để làm dấu sáp nứt rồi tan,
     đặt opacity tĩnh ở đây sẽ bị animation ghi đè và mất hiệu ứng làm nhạt. */
  --seal-soften: saturate(0.5) brightness(1.55) opacity(0.82);

  /* --- Giấy phong bì ----------------------------------------------------
     MẶT TRƯỚC phong bì (#envelope-intro, mục 0B của sections-hero.css) KHÔNG
     có ảnh PNG nào — nó được vẽ hoàn toàn bằng CSS. Vì vậy sắc giấy phải dựng
     lại cho khớp mặt sau, nếu không giữa cú lật khách sẽ thấy tờ giấy ĐỔI MÀU
     — đúng thứ phá tan ảo giác "một tờ giấy đang xoay".

     HAI SỐ NÀY ĐO TỪ BẢN RENDER THẬT, KHÔNG PHẢI TỪ FILE PNG. Cách đo: cho
     hai mặt hiện cạnh nhau trong Chrome, chụp màn hình rồi lấy màu ở 9 điểm
     TƯƠNG ĐƯƠNG (cùng toạ độ % trong khung) trên cả hai mặt.
     ⚠ ĐO TỪ FILE PNG LÀ SAI, và bản đầu tiên đã sai đúng như vậy: chỉ đo
     envelope-ivory-front-pocket.png rồi lấy #E6E2DB → #D8D4CD. Nhưng vùng
     giấy khách NHÌN THẤY ở mặt sau phần lớn lại là envelope-ivory-open.png
     (thân + nắp), mà ảnh đó SÁNG HƠN hẳn ảnh mép trước (CATALOG.md: #EEEBE4
     chiếm 26% diện tích). Kết quả: mặt trước tối hơn mặt sau trung bình 18.7
     đơn vị sáng (≈9%) — nhìn ra ngay là "một tấm xi măng cạnh một tờ giấy".

     Mốc rendered ĐÍCH (trung bình 11 điểm, đã bỏ các điểm rơi trúng tem/chữ):
         vùng sáng ~#EAE7E1 · vùng giữa ~#E3DFD8 · mép đáy ~#DCD8D1
         độ sáng trung bình của mặt sau = 217.9 / 255

     HAI SỐ DƯỚI ĐÂY LÀ GIÁ TRỊ TRƯỚC KHI NHÂN VÂN GIẤY: mặt trước phủ vân giấy
     bằng background-blend-mode: multiply, lớp đó ăn mất 3.5% độ sáng nên phải
     bù trước. Chốt bằng ĐO LẠI chứ không bằng phép chia suông — chia đúng
     1/0.965 thì mặt trước vọt lên 225.9, sáng hơn mặt sau 8 đơn vị; hạ thêm
     2.4% nữa mới về ~220, lệch còn ~1%, mắt không phân biệt được nữa.
     (Vì sao phép chia hụt: trung bình mặt sau đã bao gồm cả những dải tối ở
     nếp gấp mà mặt trước phẳng lì không có.)
     ĐỔI HAI SỐ NÀY THÌ PHẢI ĐO LẠI, đừng suy bằng công thức.

     KHÁC với --ivory (nền trang): giấy phong bì vẫn xám và tối hơn nền một
     bậc thấy được — đó là thứ tách phong bì khỏi nền. Đừng kéo hai màu về sát
     nhau, cũng đừng đẩy xa thêm. */
  --envelope-paper: #e5e1da;       /* thân giấy (trước khi nhân vân) */
  --envelope-paper-shade: #dedbd3; /* mép đáy, phía khuất sáng */
  --envelope-paper-rgb: 227, 223, 216; /* tông trung bình đã render, để pha rgba() */

  /* Dạng "r, g, b" của các màu trên, để pha rgba(..., alpha).
     BỔ SUNG SAU KHI TÍCH HỢP: cả bốn agent section đều báo thiếu
     --sage-deep-rgb / --ink-rgb / --olive-rgb, nên mọi lớp phủ (scrim) trên
     ảnh đều phải pha từ --sage-rgb. Sage là màu SÁNG (luminance ≈ 0.10 nhưng
     kênh trung bình ≈ 90/255) nên muốn đủ tương phản cho chữ trắng thì phải
     đẩy alpha rất cao → ảnh phía dưới gần như mất hẳn. Đo thật: để chữ trắng
     13px đạt 4.5:1 trên ảnh placeholder sáng cần alpha ≈ 0.74 với --sage,
     nhưng chỉ ≈ 0.58 với --sage-deep. Vì vậy scrim nên pha từ --sage-deep-rgb. */
  --sage-rgb: 82, 97, 62;
  --sage-deep-rgb: 60, 72, 48;
  --sage-soft-rgb: 126, 138, 108;
  --sage-mute-rgb: 104, 116, 84;
  --olive-rgb: 90, 107, 13;
  --ink-rgb: 43, 43, 43;
  --ivory-rgb: 255, 250, 240;
  /* Dạng RGB của --wax-red, cho quầng sáng drop-shadow của trái tim sau tên
     người nhận (@keyframes blink-heart). Trái tim ở bảng lịch dùng quầng
     --ivory-rgb vì nó là tim TRẮNG trên nền sage; tim này là tim ĐỎ trên giấy
     ivory nên quầng ivory sẽ vô hình. */
  --wax-red-rgb: 176, 43, 43;

  /* --- Font stack ----------------------------------------------------- */
  --ff-display: 'Hastegi', 'Cormorant Garamond', Georgia, serif;
  --ff-script: 'BeyondPerfection', 'Great Vibes', cursive;
  --ff-hand: 'UVNHoaTay', 'Great Vibes', cursive;
  --ff-label: 'Rift', 'Oswald', 'Arial Narrow', sans-serif;
  --ff-body: 'Lora', Georgia, 'Times New Roman', serif;
  /* NGOẠI LỆ CÓ CHỦ Ý — token đặt tên theo VỊ TRÍ chứ không theo vai trò như
     năm token trên. Lý do: đây là yêu cầu "chỉ đúng một chỗ" — tên cô dâu chú
     rể ở #hero, ngay dưới dòng script "Save our date". Đặt tên là --ff-hand-2
     hay --ff-letter thì lần sau sẽ có người dùng lại cho chỗ khác và mất đúng
     cái tương phản đang muốn: cả thiệp là Hastegi/Lora, riêng dòng mở đầu là
     nét bút tay. Tên token nói thẳng phạm vi để khỏi trôi.
     Dự phòng rơi về DancingScript TRƯỚC (font duy nhất trong assets/fonts có
     ĐỦ dấu tiếng Việt) rồi mới UVNHoaTay và Great Vibes. Thứ tự này quan
     trọng: Romantically thiếu dấu riêng của tiếng Việt nên Dancing Script
     phải đứng ngay sau để vá theo từng ký tự — xem ghi chú @font-face ở trên
     và bảng thay thế ở README. */
  --ff-hero-name: 'Romantically', 'DancingScript', 'UVNHoaTay', 'Great Vibes', cursive;
  /* --ff-cta — CÙNG NGOẠI LỆ: token theo VỊ TRÍ cho riêng nút "Mở thiệp" (màn
     phong bì). Cùng family Dancing Script với --ff-hero-name nhưng tách token
     để giữ đúng phạm vi "chỉ một nút", không trôi xuống .btn dùng chung. */
  --ff-cta: 'DancingScript', 'UVNHoaTay', 'Great Vibes', cursive;

  /* --- Thang chữ ------------------------------------------------------
     Số đo gốc từ reference (thiết kế ở cột 420px). clamp() để scale mượt
     từ mobile 390px lên desktop mà vẫn giữ đúng tỉ lệ ở 420px.        */
  --fs-name: clamp(3.125rem, 13.1vw, 3.9rem);   /* 50→62px  tên cô dâu/chú rể */
  --fs-name-lg: clamp(4.25rem, 19.6vw, 5.75rem);/* 68→92px  số ngày "20" ở #invitation */
  /* Tên gọi ở hero (.t-name-lg). LỊCH SỬ: ban đầu token này CÙNG nhịp vw và
     CÙNG trần với --fs-name-lg, chỉ khác SÀN (56px thay vì 68px) — nay đã đi
     hẳn đường riêng, xem khối "CẢ BA SỐ GIẢM ĐÚNG MỘT NỬA" bên dưới.
     Vì sao phải tách token:
     --fs-name-lg còn dùng cho số ngày "20" ở #invitation — chuỗi 2 ký tự,
     sàn 68px không đời nào tràn. Ở hero chuỗi dài nhất là cả một tên gọi.
     Đo thật trong Chrome: "Quang Danh" ở Hastegi rộng 4.27em, tức 290px khi
     sàn 68px ăn (khung nhìn < 347px) — rộng hơn cột chữ 288px của màn 320px
     nên tên RỚT DÒNG. Sàn 56px giữ tên trên một dòng từ 272px trở lên.

     CẢ BA SỐ GIẢM ĐÚNG MỘT NỬA KHI ĐỔI SANG ROMANTICALLY. Đây KHÔNG phải
     "làm chữ nhỏ đi" mà là quy đổi đơn vị: Romantically vẽ glyph rất lớn so
     với ô em của nó, nên cùng một con số font-size cho ra chữ to gấp đôi mọi
     font khác trong thiệp. Đo advance width (thứ quyết định ngắt dòng —
     KHÔNG phải bề rộng nét mực), chuỗi "Quang Danh":
       Hastegi 4.150em + tracking 1px = 4.26em
       Dancing Script, tracking 0     = 4.678em
       Romantically,   tracking 0     = 9.370em   (gấp đôi!)
     Giữ 17.7vw thì ở khung nhìn 520px tên rộng 9.370 × 92 = 862px, gấp gần
     hai lần cột chữ 488px. Vì vậy 17.7vw → 8.85vw, trần 92 → 46px, sàn
     56 → 28px. Chữ hiện lên MẮT THƯỜNG vẫn to y như bản Dancing Script.
     HẠ TIẾP 13% THEO YÊU CẦU "nhỏ lại một chút": 8.85vw → 7.7vw, trần
     46 → 40px, sàn 28 → 24px (nhân cả ba với 40/46 để cụm tên co đúng tỉ lệ,
     không lệch nhịp giữa mobile và desktop). Đây là NÚM VẶN DUY NHẤT của cỡ
     chữ tên — muốn nhỏ/to thêm thì đổi ba số này cùng một hệ số, đừng chỉ đổi
     trần: đổi lẻ một số là mobile và desktop lệch nhau.
     Đo lại sau khi đổi (đo thật trong Chrome, cột = min(520, khung nhìn) − 32):
       320px → 231px / 288px = 80.2% cột, 1 dòng
       390px → 281px / 358px = 78.6%,     1 dòng
       480px → 346px / 448px = 77.3%,     1 dòng
       ≥520px → 375px / 488px = 76.8%,    1 dòng
     Biên an toàn giờ rộng hơn hẳn bản Hastegi gốc (~88–92%), nên còn dư chỗ
     nếu sau này tên dài hơn. */
  --fs-name-hero: clamp(1.5rem, 7.7vw, 2.5rem); /* 24→40px tên hero lớn */
  /* Tên ĐẦY ĐỦ ở khối lời mời ("NGUYỄN QUANG DANH" — 17 ký tự). Phải nhỏ hơn
     --fs-name vì tên đầy đủ dài gấp đôi tên gọi và BẮT BUỘC nằm trên 1 dòng:
     tên người bị ngắt dòng giữa họ và tên đọc rất tệ trên thiệp cưới.       */
  --fs-name-md: clamp(1.85rem, 7.6vw, 2.15rem); /* 29.6→34.4px tên đầy đủ */
  --fs-date-hero: clamp(1.35rem, 5.7vw, 1.6rem);/* 21.6→25.6px ngày nghiêng ở hero */
  --fs-script-xl: clamp(2.25rem, 9.6vw, 2.85rem); /* 36→45.6px "Dresscode" */
  --fs-script-lg: clamp(2rem, 8.5vw, 2.5rem);   /* 32→40px  "All of me loves" */
  --fs-script-md: clamp(1.65rem, 6.9vw, 2rem);  /* 26.4→32px "Countdown" */
  --fs-script-sm: clamp(0.85rem, 3vw, 0.95rem); /* 13.6→15.2px caption script */
  --fs-digit: clamp(1.75rem, 7.1vw, 2.15rem);   /* 28→34.4px dấu ":" countdown */
  /* Số đếm ngược: reference #COUNTDOWN1 là Hastegi 54px — CHỈ ba dấu ":"
     (#HEADLINE86/87/88) mới là Rift bold 29.87px. Bản trước dùng --fs-digit
     cho cả hai nên số nhỏ hơn thiết kế 45% và không còn nổi hơn chính tiêu đề
     "Countdown" (28.87px) ở ngay trên. */
  --fs-digit-lg: clamp(3rem, 12.9vw, 3.6rem);   /* 48→57.6px số countdown */
  --fs-xl: clamp(1.3rem, 5vw, 1.45rem);         /* 20.8→23.2px tiêu đề section */
  --fs-lg: clamp(1.2rem, 4.5vw, 1.35rem);       /* 19.2→21.6px tên địa điểm */
  /* --fs-md nâng từ 4vw lên 4.364vw để .t-heading rơi đúng 18.33px ở cột
     420px — đúng cỡ #HEADLINE5 (tiêu đề "Kính mời...") của reference. Trước đó
     ra 16.8px, nhỏ hơn thiết kế 8%. Token này cũng cấp cỡ chữ cho .btn, nên
     nút "GỬI LỜI NHẮN" đồng thời tiến gần cỡ 19px của reference. */
  --fs-md: clamp(1.05rem, 4.364vw, 1.2rem);     /* 18.33→20.1px tiêu đề section */
  /* Bậc BỔ SUNG giữa --fs-md và --fs-xl, rơi đúng 20px ở cột 420px.
     Lý do tồn tại: reference đặt "17:30, THỨ BẢY" (#HEADLINE14) và cặp
     giờ+nhãn timeline (#HEADLINE81/82) ở Lora 20px — to hơn hẳn tiêu đề phía
     trên. Khi cả hai dùng chung --fs-md thì giờ tổ chức tiệc — dòng THÔNG TIN
     HÀNH ĐỘNG quan trọng nhất của cả tấm thiệp — bằng y hệt tiêu đề, mất hẳn
     vai trò là câu trả lời cho "khi nào". */
  --fs-md-lg: clamp(1.15rem, 4.76vw, 1.3rem);   /* 20→20.8px giờ tiệc, timeline */
  --fs-base: 1rem;                              /* 16px  nội dung RSVP */
  --fs-sm: 0.9375rem;                           /* 15px  ngày âm, lời cảm ơn */
  --fs-xs: 0.875rem;                            /* 14px  địa chỉ, thứ trong tuần */
  --fs-2xs: 0.8125rem;                          /* 13px  tên bố mẹ, ngày trong lịch */
  --fs-3xs: 0.75rem;                            /* 12px  nút phụ nhỏ (nút "chỉ đường") */

  --lh-tight: 1.3;
  --lh-snug: 1.4;
  --lh-normal: 1.5;
  --lh-relaxed: 1.6;
  --lh-loose: 1.7;

  --ls-wide: 0.0625rem;   /* 1px  — nhãn in hoa */
  --ls-wider: 0.125rem;   /* 2px  — nhãn Rift */
  --ls-widest: 0.25rem;   /* 4px  — nhãn giãn rộng */

  /* --- Nhịp cách (thang 4px) ------------------------------------------ */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 2.5rem;
  --sp-8: 3rem;
  --sp-9: 4rem;
  --sp-10: 5rem;

  /* --- Layout ---------------------------------------------------------
     Reference thiết kế mobile-first ở cột nội dung 420px. Giữ nguyên
     làm cột nội dung tối đa; hoạ tiết được tràn rộng hơn trên desktop. */
  --w-content: 26.25rem;  /* 420px — cột nội dung, khớp reference */
  --w-stage: 32.5rem;     /* 520px — khung "sân khấu" cho hoạ tiết tràn */
  --pad-x: 1.5rem;        /* padding ngang mặc định */

  /* --- Bo góc & đổ bóng ----------------------------------------------- */
  --r-sm: 0.25rem;
  --r-md: 0.5rem;
  --r-lg: 1rem;
  --r-arch: 50% 50% 0.5rem 0.5rem / 32% 32% 0.5rem 0.5rem; /* khung vòm ảnh */
  --r-pill: 62.5rem;

  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 12px 40px rgba(var(--sage-rgb), 0.18);

  /* --- Chuyển động ----------------------------------------------------- */
  --dur-fast: 180ms;
  --dur-base: 320ms;
  --dur-slow: 720ms;
  --dur-reveal: 900ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);

  /* --- Chuỗi mở phong bì (#envelope-intro) ------------------------------
     Ba nhịp nối tiếp, xem animations.css mục 3. ĐÃ LÀM CHẬM lại để khách kịp
     đọc tên/ngày trên thẻ, và thiếp GIỮ YÊN một nhịp ở cuối (trong keyframes)
     trước khi lớp phủ nhường chỗ cho trang:
       sáp vỡ    0   → 260
       nắp giở   220 → 1120
       thiếp lên 640 → 1720  (đạt vị trí ở 70%, giữ yên 70%→100% cho dễ đọc)
     BỐN MỐC TRÊN TÍNH TỪ MỐC 0 CỦA CHUỖI MỞ, KHÔNG PHẢI TỪ LÚC BẤM NÚT: khi
     phong bì có mặt trước thì cả chuỗi bị dời muộn đi --dur-envelope-front-offset
     (xem khối ngay dưới). Nhờ tách như vậy, sáu con số ở đây vẫn đọc được độc
     lập với chuyện có lật hay không.
     PHẢI LÀ SỐ ms TRẦN, KHÔNG BỌC calc(): envelope.js đọc
     --dur-envelope-total bằng getPropertyValue() để đặt timeout dự phòng, mà
     custom property chưa đăng ký @property thì trả về nguyên chuỗi "calc(…)"
     → parseFloat ra NaN → rơi nhầm về nhánh ước lượng.
     Đổi bất kỳ số nào ở đây thì PHẢI cộng lại --dur-envelope-total. */
  --dur-envelope-seal-in: 0ms;    /* sáp là nhịp đầu ⇒ trễ 0 (xem ghi chú)  */
  --dur-envelope-seal: 260ms;     /* sáp nứt rồi tan                       */
  --dur-envelope-flap-in: 220ms;  /* nắp chờ sáp tan gần hết mới động      */
  --dur-envelope-flap: 900ms;     /* nắp quay -180deg → 0deg (chậm hơn)    */
  --dur-envelope-card-in: 640ms;  /* thiếp chờ nắp qua phương ngang        */
  /* 1300ms → 1080ms KHI THÊM MẶT TRƯỚC. Cắt đúng 220ms NHỊP ĐỨNG YÊN ở cuối,
     KHÔNG cắt tốc độ trồi: mốc 58% trong keyframes được đẩy lên 70% để thiếp
     vẫn mất đúng ~755ms để lên tới vị trí (0.58×1300 = 754 ≈ 0.70×1080 = 756),
     chỉ còn giữ yên 324ms thay vì 546ms. Lý do cắt: nhịp lật cộng thêm 760ms
     vào đầu chuỗi, để nguyên là khách phải xem 2.76 giây mỗi lần mở lại link
     từ Zalo/Messenger. Nhịp giữ yên là phần ít mất mát nhất khi phải cắt —
     nội dung trên thiếp vốn aria-hidden và lặp lại nguyên vẹn ở #hero.
     Muốn trả về nhịp đọc thong thả như cũ: đổi lại 1080 → 1300, 70% → 58%,
     34% → 28% (animations.css) và cộng lại --dur-envelope-total. */
  --dur-envelope-card: 1080ms;    /* thiếp trồi + giữ yên cuối cho dễ đọc  */
  --dur-envelope-total: 1720ms;   /* = card-in + card, mốc kết thúc SAU CÙNG */

  /* --- Nhịp LẬT phong bì: mặt trước → mặt sau ---------------------------
     Chỉ chạy khi màn phong bì có mặt trước (.envelope.has-front — envelope.js
     gắn class này khi data.js có options.envelopeFront). Không có mặt trước
     thì offset = 0 và mọi thứ chạy y hệt bản cũ.

     Toàn tuyến khi CÓ mặt trước (mốc tính từ lúc bấm nút):
       lật       0    → 900    hai mặt cùng quay -180deg, đổi mặt ở 450ms
       sáp vỡ    760  → 1020   ├─ chồng 140ms lên đuôi cú lật: lúc này mặt sau
       nắp giở   980  → 1880   │  đã hiện từ lâu (450ms) và gần như phẳng
       thiếp lên 1400 → 2480   │  (còn -6deg), bắt nhịp sớm cho liền mạch.
     ⇒ TỔNG = --dur-envelope-front-offset + --dur-envelope-total = 2480ms.
     envelope.js CỘNG hai token này để ra timeout dự phòng, nên đổi số ở đây
     là timeout tự đúng theo — không có chỗ thứ hai phải sửa tay.

     HAI BẤT BIẾN khi chỉnh offset, phá là lỗi hình ảnh chứ không phải lỗi gu:
       offset ≥ 450                       (= nửa cú lật). Nhỏ hơn thì sáp vỡ
         lúc mặt sau còn đang quay khuất — vỡ trong bóng tối, phí cả nhịp.
       offset + flap-in > --dur-envelope-flip   (760 + 220 = 980 > 900). Nhỏ
         hơn thì nắp quay rotateX TRONG LÚC mặt sau còn quay rotateY: hai phép
         chiếu chồng lên nhau trên một phần tử đang clip-path — Safari và
         WebView iOS dựng ra vệt xén chéo. */
  --dur-envelope-flip: 900ms;          /* hai mặt quay nửa vòng             */
  --dur-envelope-front-offset: 760ms;  /* mốc 0 của chuỗi mở, khi có mặt trước */

  --z-decor: 1;
  --z-content: 2;
  --z-nav: 40;
  --z-lightbox: 60;
  --z-toast: 70;
}
