/* =============================================================
   SECTIONS — bố cục từng khối
   Trang đi theo MỘT chuyển động màu duy nhất, bám đúng hai buổi
   chụp trong concept ảnh cưới:

     CHƯƠNG I  · phông trắng Hàn Quốc
       hero · save the date · thiệp mời · câu chuyện
       -> nền giấy trắng, vệt nắng rèm xiên, chữ than chì

     [ chuyển chương ] album nền mực, cuộn NGANG

     CHƯƠNG II · sân vườn
       chi tiết (kem) · đếm ngược (rêu đậm) · chân trang (rêu đậm)
   ============================================================= */


/* =============================================================
   1 · HERO — CHƯƠNG I
   ============================================================= */

.hero {
  --hero-pb: clamp(2rem, 5vh, 3.5rem);
  min-height: 100svh;
  display: grid; align-items: end;
  grid-template-columns: repeat(12, 1fr);
  /* Hai hàng KHAI BÁO TƯỜNG MINH. Bắt buộc, không phải cho đẹp:
     `grid-row: 1 / -1` của .hero-shot đếm theo lưới TƯỜNG MINH, mà
     lưới không khai báo hàng nào thì -1 rơi về đúng vạch 1 -> ảnh chỉ
     cao bằng hàng 1 và hụt mất một khúc ở đáy. */
  grid-template-rows: 1fr auto;
  column-gap: var(--gut);
  padding: clamp(6rem, 14vh, 9rem) var(--edge) var(--hero-pb);
  position: relative; overflow: hidden;
}

/* Vệt nắng xiên qua rèm — chữ ký ánh sáng của concept phông trắng.
   Hai điều bắt buộc, đã thử sai một lần:
     · BIÊN MỀM. Dải cạnh cứng đọc ra như lỗi render; nắng thật luôn
       có nửa tối (penumbra).
     · CHỈ PHỦ NỬA TRÁI (inset phải 38%). Đè lên ảnh là hỏng ảnh. */
.hero::before {
  content: ""; position: absolute; inset: 0 38% 0 0;
  pointer-events: none;
  background: linear-gradient(107deg,
    transparent 46%, rgba(35,40,34,.022) 52%, rgba(35,40,34,.022) 60%, transparent 66%,
    transparent 72%, rgba(35,40,34,.014) 77%, rgba(35,40,34,.014) 82%, transparent 88%);
}

/* Sống lưng: chữ là vế NẶNG ở đây (tên hai người cỡ --t-mega), ảnh
   là vế nhẹ. Trước là 6+5 chừa cột 7 trống — gần đối xứng nên hai vế
   tranh nhau; giờ 7+5 liền cột, một vế thắng rõ. Xem --sp-* trong
   tokens.css. */
.hero-txt {
  grid-column: var(--sp-major);
  position: relative; z-index: 2;
  padding-bottom: clamp(1rem, 6vh, 4rem);
}
.hero-name {
  font-family: var(--f-disp); font-weight: 400;
  font-size: var(--t-mega); line-height: .92; letter-spacing: -.03em;
}
/* Mỗi dòng tên là một khối riêng — cần thế để mở trang gọi được từng
   dòng theo nhịp. Trước đây chúng là text node trần, xuống dòng nhờ
   dấu & ở giữa là block; giờ khai báo tường minh. */
.hero-name .ln { display: block; }
/* Dấu & là chữ VIẾT TAY, xuống dòng riêng, thụt vào — nhại đúng cách
   các tấm biển viết tay trong bộ ảnh xếp chữ.
   .82em chứ không .62em như hồi dùng Allura: dấu & của Carattere gọn
   và đặc hơn, ở .62em nó teo thành một cái móc. Đây cũng là chỗ ăn
   nhũ, mà dải nhũ 7 chặng cần một diện tích nét tối thiểu mới trải
   hết được — chữ càng nhỏ nhũ càng ra một màu. */
.hero-name .amp {
  display: block; font-family: var(--f-sign); font-style: normal;
  font-size: .82em; line-height: .7;
  margin: .08em 0 .02em; padding-left: .18em;
  /* width:fit-content là ĐIỀU KIỆN để lớp .foil hoạt động: nhũ tô bằng
     background-clip:text nên dải gradient trải theo bề rộng của HỘP,
     không phải bề rộng chữ. Để hộp rộng hết cột thì dấu & chỉ lấy
     trúng đoạn giữa dải — mất hai đầu tối, hết ra nhũ. Màu dự phòng
     do .foil cấp, không đặt ở đây nữa. */
  width: fit-content;
}
.hero-meta { display: flex; align-items: center; gap: 1.1rem; margin-top: clamp(1.6rem, 4vh, 2.6rem); }
.hero-meta i { flex: 1 1 auto; height: 1px; background: var(--line); max-width: 5rem; }
.hero-meta p {
  font-size: var(--t-cap); text-transform: uppercase; letter-spacing: var(--track-cap);
  color: var(--ink-2);
}
.hero-quote {
  margin-top: 1.6rem;
  font-family: var(--f-disp); font-style: italic;
  font-size: var(--t-h3); color: var(--ink-2); max-width: 22ch;
}

/* Ảnh tràn mép phải + mép dưới.
   grid-row 1/-1 để phủ luôn hàng của .hero-scroll, margin âm để ăn
   nốt padding đáy — nếu không, ảnh dừng lửng lơ cách đáy một khoảng. */
.hero-shot {
  grid-column: var(--sp-minor); grid-row: 1 / -1; align-self: stretch;
  position: relative; overflow: hidden;
  margin-right: calc(var(--edge) * -1);
  margin-bottom: calc(var(--hero-pb) * -1);
}
.hero-shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero-shot::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 30%;
  background: linear-gradient(180deg, transparent, rgba(251,250,247,.55));
}

/* CHƯA CÓ ẢNH CƯỚI -> không để ô trống, cũng không mượn tranh vẽ.
   Dựng thẳng cái LÀM NÊN concept "Our Pure Love": bóng rèm cửa hắt
   cứng lên phông trắng. Điền hero.image thì lớp này tự biến mất. */
.hero-light { background: linear-gradient(158deg, #FFFFFF 0%, #F6F3EC 55%, #EFEBE1 100%); }
.hero-light::before {
  content: ""; position: absolute; inset: -25%;
  background: repeating-linear-gradient(101deg,
    rgba(35,40,34,.075) 0 26px, rgba(35,40,34,0) 26px 76px);
  -webkit-mask-image: linear-gradient(196deg, #000 6%, rgba(0,0,0,.35) 52%, transparent 84%);
          mask-image: linear-gradient(196deg, #000 6%, rgba(0,0,0,.35) 52%, transparent 84%);
}
.hero-light .motif {
  position: absolute; left: 50%; bottom: 8%;
  width: min(42%, 190px); height: auto;
  transform: translateX(-50%);
  color: var(--gold-lt); opacity: .5;
}

.hero-scroll {
  grid-column: var(--sp-minor-l);
  display: flex; align-items: center; gap: .7rem;
  font-size: var(--t-cap); text-transform: uppercase; letter-spacing: var(--track-cap);
  color: var(--ink-3); margin-top: 2.2rem;
}
.hero-scroll i {
  display: block; width: 46px; height: 1px; background: var(--ink-3);
  transform-origin: left; animation: sweep 2.6s ease-in-out infinite;
}
@keyframes sweep { 0%, 100% { transform: scaleX(.35); } 50% { transform: scaleX(1); } }
@media (prefers-reduced-motion: reduce) { .hero-scroll i { animation: none; } }


/* -------------------------------------------------------------
   1b · MỞ TRANG — khoảnh khắc dàn dựng DUY NHẤT của cả thiệp
   -------------------------------------------------------------
   Màn hình đầu là luận điểm của thiết kế, mà bản trước nó chỉ… hiện
   ra. Ở đây nó được MỞ, theo đúng thứ tự một tấm thiệp giấy được lấy
   ra khỏi phong bì:

     0,05s  tấm phông sáng hé ra từ mép ngoài  (rèm vừa kéo)
     0,18s  thanh điều hướng (chỉ mờ dần, không dịch chuyển)
     0,34s  nhãn Save the Date
     0,46s  "Ninh"
     0,64s  dấu &         <- chậm hơn một nhịp, đây là chữ ký
     0,80s  "Trang"
     1,06s  hai vạch kẻ đẩy ra hai bên, ngày hiện giữa
     1,16s  câu dẫn
     1,28s  câu trích
     1,44s  "cuộn xuống"

   Nhịp này giãn hơn bản đầu khoảng một phần ba. Không phải để "sang"
   — mà vì cùng lúc đổi sang đường cong êm hơn (xem --ease trong
   tokens.css): đường cong cũ bắn hết quãng đường ngay từ đầu nên
   khoảng cách 0,10s giữa hai dòng đã đủ đọc ra thứ tự; đường cong mới
   còn đang chuyển động thật ở giữa thời lượng, để nguyên 0,10s thì
   các dòng chồng lên nhau thành một mảng nhoè.

   Toàn bộ khoá sau `html.intro`, do render.js gắn và CHỈ gắn khi máy
   không bật "giảm chuyển động". Không có lớp đó thì không có
   animation nào chạy -> mọi thứ hiện đủ ngay. Hỏng JS cũng không bao
   giờ để lại một hero trắng.

   `both` là bắt buộc: nó giữ trạng thái 0% trong lúc chờ delay, nếu
   không mỗi dòng sẽ loé lên rồi mới tụt xuống để chạy lại. */

@keyframes hRise  { from { opacity: 0; transform: translateY(30px); } }
@keyframes hAmp   { from { opacity: 0; transform: translateY(14px) rotate(-5deg); } }
@keyframes hRule  { from { transform: scaleX(0); } }
@keyframes hFade  { from { opacity: 0; } }
/* Phông sáng mở từ mép NGOÀI vào trong, không phải mờ dần: rèm kéo
   ngang thì mới ra ánh sáng, mờ dần thì ra một lớp phủ. */
@keyframes hLight { from { clip-path: inset(0 0 0 100%); } }

html.intro .hero-light,
html.intro .hero-shot   { animation: hLight var(--dur-slow) var(--ease) .05s both; }
/* Thanh điều hướng cũng phải cùng đến. Để nó sẵn ở đó lúc sân khấu
   còn trống thì khung hình đầu tiên đọc ra "trang lỗi, thiếu nội
   dung" chứ không đọc ra "trang đang mở". Chỉ mờ dần, KHÔNG dịch
   chuyển: nó là giấy tiêu đề, không phải diễn viên. */
html.intro .nav           { animation: hFade 1.1s var(--ease) .18s both; }
html.intro .hero-txt .cap { animation: hRise .9s  var(--ease) .34s both; }
html.intro .hero-name .ln:first-child { animation: hRise 1s    var(--ease) .46s both; }
html.intro .hero-name .amp            { animation: hAmp  1.05s var(--ease) .64s both; }
html.intro .hero-name .ln:last-child  { animation: hRise 1s    var(--ease) .80s both; }
html.intro .hero-meta i { animation: hRule .95s var(--ease) 1.06s both; }
html.intro .hero-meta p { animation: hRise .85s var(--ease) 1.16s both; }
html.intro .hero-quote  { animation: hRise .85s var(--ease) 1.28s both; }
html.intro .hero-scroll { animation: hRise .85s var(--ease) 1.44s both; }

/* Hai vạch bung ra TỪ ngày tháng, nên gốc xoay của mỗi vạch nằm ở
   phía giáp con số. Để mặc định (giữa) thì chúng nở ra hai đầu, đọc
   ra như hai thanh loading. */
.hero-meta i:first-child { transform-origin: right; }
.hero-meta i:last-child  { transform-origin: left; }

/* Ánh sáng THỞ. Đây là một canh bạc có tính toán: cả trang còn lại
   đứng yên tuyệt đối, chỉ mỗi vệt nắng trôi rất chậm — vì trong buổi
   chụp phông trắng, thứ duy nhất chuyển động thật sự là nắng qua rèm.
   19 giây một vòng: chậm tới mức không ai "thấy nó đang chạy", chỉ
   thấy màn hình không chết. Lớp giả đã có inset -25% nên trôi thoải
   mái mà không hở mép. transform + opacity -> chạy trên GPU. */
@keyframes lightDrift {
  0%, 100% { transform: translate3d(0, 0, 0);          opacity: 1;   }
  50%      { transform: translate3d(-2.4%, 1.6%, 0);   opacity: .78; }
}
.hero-light::before { animation: lightDrift 19s ease-in-out 2s infinite; }

/* Khi đã có ảnh cưới thật thì tấm phông CSS biến mất — hero sẽ mất
   luôn chuyển động nếu không có luật này. Ken-burns 26 giây, biên độ
   nhỏ, để ảnh cũng "thở" đúng nhịp ấy. */
@keyframes heroKen { from { scale: 1.015; } to { scale: 1.075; } }
.hero-shot img { animation: heroKen 26s ease-in-out infinite alternate; }

@media (prefers-reduced-motion: reduce) {
  .hero-light::before, .hero-shot img { animation: none; }
}

@media (max-width: 56rem) {
  .hero { align-content: end; row-gap: 2rem; grid-template-rows: none; }
  /* Trên điện thoại ảnh nằm TRÊN chữ, không còn nửa trái/nửa phải
     nữa -> giữ inset 38% sẽ để lộ một đường dọc chạy suốt trang. */
  .hero::before { inset: 0; }
  .hero-txt  { grid-column: var(--sp-full); order: 2; padding-bottom: 0; }
  /* align-self:auto BẮT BUỘC phải reset — stretch của bản desktop sẽ
     kéo ảnh cao vượt aspect-ratio, hero dài ngoẵng trên điện thoại. */
  .hero-shot {
    grid-column: var(--sp-full); grid-row: auto; order: 1;
    align-self: auto; aspect-ratio: 5 / 4; min-height: 0;
    margin-inline: calc(var(--edge) * -1); margin-bottom: 0;
  }
  .hero-shot img { position: static; }
  .hero-scroll { grid-column: var(--sp-full); order: 3; margin-top: .5rem; }
}


/* =============================================================
   2 · SAVE THE DATE
   ============================================================= */

.std { background-color: var(--paper-2); text-align: center; overflow: hidden; }
/* Không đặt color ở đây: lớp .foil lo cả màu lẫn nhũ. width:fit-content
   để dải nhũ trải đúng bề ngang dòng chữ (xem .hero-name .amp). */
.std-sign { font-size: clamp(3rem, 8.4vw, 6.4rem); width: fit-content; margin-inline: auto; }
.std-date {
  font-family: var(--f-disp); font-weight: 400;
  font-size: clamp(2.8rem, 9vw, 7rem); line-height: 1;
  display: flex; align-items: baseline; justify-content: center;
  gap: clamp(.6rem, 2.4vw, 1.8rem);
  margin-top: clamp(1.2rem, 3vw, 2rem);
}
.std-date i { font-style: normal; font-size: .3em; color: var(--gold); transform: translateY(-.55em); }
.std-sub {
  margin-top: 1.5rem;
  font-size: var(--t-cap); text-transform: uppercase; letter-spacing: var(--track-cap);
  color: var(--ink-2);
}
/* Ba khung dọc — nhại đúng dải 3 ảnh trong bảng concept của studio */
.std-strip {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gut);
  margin-top: clamp(3rem, 7vw, 5rem);
}
.std-strip .shot { aspect-ratio: 3 / 4.4; }
.std-strip .shot::before { translate: 10px 10px; }
@media (max-width: 46rem) {
  .std-strip { grid-template-columns: repeat(2, 1fr); }
  .std-strip .shot:nth-child(3) { display: none; }
}


/* =============================================================
   3 · THIỆP MỜI
   ============================================================= */

.inv-head { grid-column: var(--sp-minor-l); }
.inv-body { grid-column: var(--sp-major-r); }
.inv-fam  {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--gut);
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
}
.inv-fam > div { padding-top: 1.6rem; border-top: 1px solid var(--line); }
.inv-fam .cap { margin-bottom: .8rem; }
.inv-fam h3 { font-family: var(--f-disp); font-size: var(--t-h3); font-weight: 500; margin-bottom: .5rem; }
.inv-fam p:not(.cap) { font-size: .92rem; color: var(--ink-2); line-height: 1.85; }
/* NGÀY — khối này ở cột trái, ngay dưới tiêu đề. Vạch trên là vạch
   DUY NHẤT trong khối, nên nó đọc ra "phần dưới của tấm thiệp" chứ
   không phải một đường kẻ trang trí. */
.inv-when {
  margin-top: clamp(2.4rem, 5vw, 3.6rem);
  padding-top: clamp(1.4rem, 2.6vw, 2rem);
  border-top: 1px solid var(--line);
}
.inv-when .cap { margin-bottom: .7rem; }
.inv-date {
  font-family: var(--f-disp); font-weight: 400;
  font-size: clamp(1.9rem, 3.4vw, 2.9rem);
  line-height: 1.1; letter-spacing: .02em;
}
/* Dấu chấm giữa hai con số bằng đồng thau: cùng một cử chỉ với dải
   ngày trên con dấu ở album, để hai chỗ đọc ra cùng một bàn tay. */
.inv-date i {
  font-style: normal; color: var(--gold);
  padding-inline: .34em; font-size: .55em;
  vertical-align: .18em;
}
.inv-lunar {
  margin-top: .9rem;
  font-size: var(--t-small); font-style: italic;
  color: var(--ink-3);
}@media (max-width: 56rem) {
  .inv-head, .inv-body { grid-column: var(--sp-full); }
  .inv-body { margin-top: 2.5rem; }
}


/* =============================================================
   4 · CÂU CHUYỆN — chương có số thứ tự khổng lồ, so le trái/phải
   ============================================================= */

.st-head { margin-bottom: clamp(2rem, 5vw, 3.5rem); }
.st-item { padding-block: clamp(3rem, 7vw, 6rem); align-items: center; }
.st-item + .st-item { border-top: 1px solid var(--line); }

/* grid-row:1 là BẮT BUỘC. Thuật toán tự xếp của CSS grid không bao
   giờ lùi về hàng trước: đặt ảnh ở cột 8 rồi đặt chữ ở cột 1 thì chữ
   bị đẩy xuống hàng 2 -> khối 2 và 4 vỡ thành trên/dưới.

   Ảnh nhận vế NHẸ chứ không phải vế nặng, và đây là lần thử thứ hai:
   cho ảnh 7 cột thì ở khổ 1440px nó ra 728px ngang, mà khung dọc 4/5
   nên cao 910px — một khối chuyện dài 3,6 nghìn điểm ảnh, đọc ra như
   trang bị lỗi. Luật chốt lại cho CẢ trang: vế nặng là phần ĐỌC, vế
   nhẹ là ảnh và nhãn. Đây là thiệp chữ, album mới là chỗ ảnh thắng —
   và album nằm ngoài lưới, cuộn ngang. */
.st-shot { grid-column: var(--sp-minor-l); grid-row: 1; }
.st-txt  { grid-column: var(--sp-major-r); grid-row: 1; }

/* nth-of-type chứ KHÔNG phải nth-child: thẻ tiêu đề <div> đứng trước
   sẽ làm lệch parity nếu đếm bằng nth-child, cả bốn khối đảo sai bên. */
.st-item:nth-of-type(even) .st-shot { grid-column: var(--sp-minor); }
.st-item:nth-of-type(even) .st-txt  { grid-column: var(--sp-major); }

.st-shot .shot { aspect-ratio: 4 / 5; }
/* Ô 7 cột rộng tới ~730px; thả chữ chạy tự do trong đó là dòng dài
   quá 90 ký tự, mắt lạc dòng. Vế nặng cấp KHÔNG GIAN, không phải cấp
   bề dài dòng chữ. */
.st-txt > * { max-width: 40ch; }
.st-no {
  display: block; font-family: var(--f-disp); font-weight: 400;
  font-size: clamp(3.2rem, 7vw, 6rem); line-height: .85;
  color: transparent; -webkit-text-stroke: 1px var(--gold-lt);
  margin-bottom: .6rem;
}
.st-txt .yr { font-size: var(--t-cap); letter-spacing: var(--track-cap); text-transform: uppercase; color: var(--gold); }
.st-txt h3  { font-family: var(--f-disp); font-weight: 400; font-size: clamp(1.6rem, 3.2vw, 2.4rem); margin-bottom: .9rem; }

@media (max-width: 56rem) {
  .st-shot, .st-txt,
  .st-item:nth-of-type(even) .st-shot,
  .st-item:nth-of-type(even) .st-txt { grid-column: var(--sp-full); grid-row: auto; }
  .st-txt { margin-top: 1.8rem; }
  .st-shot .shot { aspect-ratio: 4 / 3; }
}


/* =============================================================
   5 · ALBUM — CHUYỂN CHƯƠNG
   Khối DUY NHẤT trên nền mực và khối DUY NHẤT cuộn ngang. Đổi chiều
   cuộn giữa trang là cách chia hai chương mà không cần vách ngăn.
   ============================================================= */

.alb { background-color: var(--ink); color: var(--paper); padding-block: var(--sec); overflow: hidden; position: relative; }
.alb .cap  { color: var(--gold-lt); }
.alb h2    { color: var(--paper); }
.alb .lead { color: rgba(251, 250, 247, .62); }

/* VẠCH CHUYỂN CHƯƠNG — cử chỉ tốn kém nhất của trang, và là cử chỉ
   duy nhất kiểu này.
   Vấn đề nó giải: nền đổi từ giấy sang mực ngay chỗ này, nhưng khi
   cuộn qua thì người xem không đọc ra "sang chương", chỉ đọc ra "trang
   đổi màu". Một vạch đồng thau mở ra từ giữa, chạy hết bề ngang, biến
   chỗ đó thành một SỰ KIỆN — kéo màn xong mới sang cảnh sau.
   Nằm ở mép trên khối, dài hết viewport chứ không bó trong .wrap. */
.alb::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg,
    transparent, var(--gold) 22%, var(--gold-lt) 50%, var(--gold) 78%, transparent);
  transform: scaleX(0); transform-origin: center;
  transition: transform var(--dur-slow) var(--ease);
}
.alb.in::before { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  .alb::before { transform: scaleX(1); transition: none; }
}

/* CON DẤU — đặt chính giữa ngay dưới vạch, trước tiêu đề chương.
   Vạch kéo ra rồi mới lộ con dấu: đọc ra "mở niêm phong, sang chương".
   Tiêu đề bên dưới vẫn canh trái — huy hiệu canh giữa trên một đầu đề
   canh trái là cách bày của trang bìa tạp chí, không phải lỗi lệch. */
.alb-seal {
  display: grid; place-items: center;
  margin: 0 auto clamp(2.2rem, 4.5vw, 3.4rem);
}
.alb-seal .seal circle  { stroke: var(--gold-lt); }
.alb-seal .seal .s-r1   { stroke-opacity: .7; }
.alb-seal .seal .s-r2   { stroke-opacity: .4; }
.alb-seal .seal .s-mono { fill: var(--paper); }
.alb-seal .seal .s-date { fill: var(--gold-lt); fill-opacity: .9; }

.alb-head {
  width: var(--wrap); margin-inline: auto;
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 2rem; flex-wrap: wrap;
}
.alb-head .lead { margin-top: 1.2rem; }
.alb-hint {
  display: flex; align-items: center; gap: .6rem;
  font-size: var(--t-cap); text-transform: uppercase; letter-spacing: var(--track-cap);
  color: rgba(251, 250, 247, .4);
}
.alb-hint svg { stroke: currentColor; fill: none; }

.alb-rail {
  display: flex; gap: var(--gut);
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-inline: var(--edge) 40vw;      /* đuôi rộng: ảnh cuối không dính mép */
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: rgba(251, 250, 247, .3) transparent;
}
.alb-rail figure { flex: 0 0 auto; scroll-snap-align: center; }
.alb-rail .shot::before { border-color: rgba(251, 250, 247, .22); }
.alb-rail .shot--empty  { background: rgba(251, 250, 247, .045); }
.alb-rail .shot--empty::after { --c: rgba(198, 179, 146, .55); }
.alb-rail .shot--empty span { color: rgba(251, 250, 247, .45); }
.alb-rail .ph-no { color: var(--gold-lt); opacity: .85; }
.alb-rail .t-p { width: clamp(210px, 26vw, 330px); aspect-ratio: 3 / 4; }   /* dọc   */
.alb-rail .t-l { width: clamp(280px, 36vw, 460px); aspect-ratio: 4 / 3; }   /* ngang */
.alb-rail .t-b { width: clamp(300px, 42vw, 540px); aspect-ratio: 1 / 1; align-self: center; }
.alb-rail .t-p:nth-child(odd) { align-self: flex-end; }
.alb-rail::-webkit-scrollbar { height: 3px; }
.alb-rail::-webkit-scrollbar-thumb { background: rgba(251, 250, 247, .3); }
.alb-rail .shot[data-src] { cursor: zoom-in; }


/* =============================================================
   6 · CHI TIẾT — CHƯƠNG II mở ra, nền kem
   ============================================================= */

.det { background-color: var(--cream); }
.det-head { grid-column: var(--sp-full); margin-bottom: clamp(2.5rem, 6vw, 4.5rem); }

/* Lịch trình đọc như bảng giờ tàu: giờ khổng lồ bên trái, việc ở
   giữa, chủ hôn bên phải. Ba cột chốt bằng minmax(0,…) để chuỗi dài
   không tự bung cột. */
.ev {
  grid-column: var(--sp-full);
  display: grid;
  grid-template-columns: minmax(0, 3.2fr) minmax(0, 5fr) minmax(0, 4fr);
  gap: var(--gut); align-items: baseline;
  padding-block: clamp(1.8rem, 4vw, 2.8rem);
  border-top: 1px solid rgba(35, 40, 34, .16);
}
.ev:last-of-type { border-bottom: 1px solid rgba(35, 40, 34, .16); }
/* Giờ là con số CHÍNH của khối, phải to. Đừng gộp nó vào một luật
   `.ev p` chung: `.ev p` có độ ưu tiên cao hơn `.ev-time` nên sẽ đè
   mất cỡ chữ, giờ tụt xuống bằng chữ chạy. */
.ev-time { font-family: var(--f-disp); font-size: clamp(2.4rem, 6vw, 4.4rem); line-height: 1; }
.ev-time small {
  display: block; margin-top: .7rem;
  font-family: var(--f-body); font-size: var(--t-cap);
  letter-spacing: var(--track-cap); text-transform: uppercase; color: var(--gold);
}
.ev-body h3 { font-family: var(--f-disp); font-size: var(--t-h3); font-weight: 500; margin-bottom: .4rem; }
.ev-body p, .ev-host { color: var(--ink-2); font-size: .95rem; }

.det-venue {
  grid-column: var(--sp-full); margin-top: clamp(2.5rem, 6vw, 4rem);
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 2rem; flex-wrap: wrap;
}
.det-venue .cap { margin-bottom: .6rem; }
.det-venue h3 { font-family: var(--f-disp); font-size: var(--t-h3); font-weight: 500; margin-bottom: .6rem; }
.det-venue p:not(.cap) { color: var(--ink-2); }
.det-dress {
  display: flex; align-items: center; gap: .55rem; margin-top: 1.2rem;
  font-size: var(--t-cap); letter-spacing: var(--track-mic); text-transform: uppercase;
  color: var(--ink-2);
}
.det-dress i { width: 15px; height: 15px; border-radius: 50%; border: 1px solid rgba(35, 40, 34, .2); }
.det-map { grid-column: var(--sp-full); margin-top: clamp(2.5rem, 5vw, 3.5rem); }
.det-map iframe { width: 100%; height: clamp(240px, 42vw, 420px); border: 1px solid var(--line); display: block; }
@media (max-width: 56rem) { .ev { grid-template-columns: 1fr; row-gap: .9rem; } }


/* =============================================================
   7 · ĐẾM NGƯỢC — dải rêu đậm, mỏng
   ============================================================= */

.cd { background-color: var(--moss-d); color: var(--cream); padding-block: clamp(3rem, 7vw, 5rem); position: relative; }
/* MÉP BĂNG IN. Dải rêu này trước đây là một mảng màu tràn màn hình
   không có mép: bốn con số thả giữa hư không. Hai đường chỉ thụt vào
   theo lề trang biến nó thành một BĂNG được in lên trang — vẫn không
   thêm chữ, không thêm hoạ tiết, chỉ thêm giới hạn. */
.cd::before, .cd::after {
  content: ""; position: absolute; left: var(--edge); right: var(--edge);
  height: 1px; background: rgba(198, 179, 146, .22);
}
.cd::before { top: clamp(1.2rem, 2.6vw, 1.9rem); }
.cd::after  { bottom: clamp(1.2rem, 2.6vw, 1.9rem); }
.cd .cap { text-align: center; }
.cd-grid { display: grid; grid-template-columns: repeat(4, 1fr); text-align: center; }
.cd-grid > div + div { border-left: 1px solid rgba(198, 179, 146, .2); }
.cd-grid b {
  display: block; font-family: var(--f-disp); font-weight: 400;
  font-size: clamp(2.4rem, 6vw, 4.2rem); line-height: 1;
  color: var(--gold-lt);
  /* tabular-nums: bề rộng chữ số cố định, nếu không thì mỗi giây con
     số lại nhích ngang một chút, nhìn như trang bị giật. */
  font-variant-numeric: lining-nums tabular-nums;
}
.cd-grid span {
  display: block; margin-top: .7rem;
  font-size: var(--t-micro); text-transform: uppercase; letter-spacing: var(--track-mic);
  color: rgba(198, 179, 146, .6);
}
.cd-done { text-align: center; font-family: var(--f-disp); font-size: var(--t-h2); color: var(--gold-lt); }
@media (max-width: 46rem) {
  .cd-grid { grid-template-columns: repeat(2, 1fr); row-gap: 2rem; }
  .cd-grid > div:nth-child(3) { border-left: 0; }
}


/* =============================================================
   8 · HỒI ÂM
   ============================================================= */

.rsvp-head { grid-column: var(--sp-minor-l); }
.rsvp-head .lead { margin-top: 1.4rem; }
.rsvp-form { grid-column: var(--sp-major-r); }
.rsvp-note { margin-top: 1rem; font-size: var(--t-small); color: var(--gold); }
/* align-self:start là bắt buộc: ô lưới này cao bằng cả khối tiêu đề
   bên trái, mà flex mặc định kéo giãn con theo trục ngang -> hai nút
   phình thành hai ô vuông khổng lồ. */
.rsvp-alt  {
  grid-column: var(--sp-major-r); align-self: start; align-items: flex-start;
  display: flex; gap: var(--gut); flex-wrap: wrap; margin-top: 2rem;
}
/* Chưa cấu hình endpoint -> không dựng biểu mẫu, và khi đó hai nút
   đứng ở cột 6 bỏ lại hơn nửa khối trống hoác. Không có biểu mẫu thì
   không có lý do gì phải chia hai cột: kéo nút về ngay dưới đầu đề,
   thêm một đường chỉ ngăn. Có endpoint là lớp này tự biến mất. */
.rsvp--alt-only .rsvp-head { grid-column: var(--sp-major); }
.rsvp--alt-only .rsvp-alt {
  grid-column: var(--sp-major); margin-top: 2.6rem;
  padding-top: 2.4rem; border-top: 1px solid var(--line);
}
@media (max-width: 56rem) {
  .rsvp-head, .rsvp-form, .rsvp-alt { grid-column: var(--sp-full); }
  .rsvp-form { margin-top: 2.5rem; }
}


/* =============================================================
   9 · MỪNG CƯỚI + LỜI CHÚC
   ============================================================= */

.gift { background-color: var(--paper-2); }
.gift-head { grid-column: var(--sp-full); text-align: center; margin-bottom: clamp(2.5rem, 5vw, 3.5rem); }
.gift-head .lead { margin: 1.6rem auto 0; text-align: center; }
/* Trước là `3 / span 8` — mép phải đóng ở cột 10, một vạch không
   trùng với bất kỳ khối nào khác trên trang. Giờ nằm đúng nhịp full,
   còn khối lượng thị giác thì siết bằng max-width: đó là cần gạt
   khác và hợp lệ, không phải bịa thêm một nhịp cột thứ tư. */
.gift-cards {
  grid-column: var(--sp-full);
  max-width: 52rem; margin-inline: auto;
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--gut);
}
.gcard {
  background: var(--paper); border: 1px solid var(--line);
  padding: clamp(1.6rem, 3vw, 2.4rem); text-align: center;
}
.gcard .qr {
  width: 132px; height: 132px; margin: 0 auto 1.4rem;
  background: #fff; border: 1px solid var(--line);
  display: grid; place-content: center;
  font-size: var(--t-micro); letter-spacing: var(--track-mic); text-transform: uppercase;
  color: var(--ink-3);
}
.gcard .qr img { width: 100%; height: 100%; object-fit: contain; }
/* Chưa có mã QR: bỏ khung kín + nền trắng (đọc ra "ảnh vỡ") và dùng
   đúng dấu góc của ô ảnh chưa có. Có ảnh QR là lớp này biến mất, khung
   trắng quay lại — mã QR cần nền trắng thật để máy quét đọc được.
   Phải viết `.gcard .qr--empty`: `.qr--empty` trơ chỉ có độ đặc hiệu
   (0,1,0), thua `.gcard .qr` (0,2,0) ở ngay trên, nên nền trắng vẫn
   thắng và dấu góc nằm đè lên một ô trắng. */
.gcard .qr--empty {
  background: transparent; border: 0; position: relative;
  --tick-in: 0px; --t: 13px;
}
.gcard h3 { font-family: var(--f-disp); font-size: 1.15rem; font-weight: 500; margin-bottom: .3rem; }
.gcard p:not(.cap) { font-size: .85rem; color: var(--ink-2); }
.gcard .num {
  margin: .5rem 0 1.2rem; color: var(--ink);
  font-variant-numeric: lining-nums tabular-nums; letter-spacing: .1em;
}

/* Lời chúc nối liền khối mừng cưới: cùng nền, ngăn nhau bằng một
   đường kẻ chứ không phải một quãng nghỉ nữa. */
.wish { background-color: var(--paper-2); padding-top: 0; }
.wish-in { border-top: 1px solid var(--line); padding-top: clamp(3rem, 6vw, 4.5rem); }
.wish-head { text-align: center; }
/* Cùng bề rộng với .gift-cards. Hai lưới 2 cột nằm sát nhau trên
   CÙNG một nền paper-2 mà lệch bề rộng thì mắt bắt được ngay, kể cả
   khi không chỉ ra được là lệch chỗ nào. */
.wish-grid {
  max-width: 52rem; margin-inline: auto;
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--gut); margin-top: 2.5rem;
}
.wish-grid blockquote {
  position: relative;
  padding: 1.8rem 1.8rem 1.8rem 3.2rem;
  background: var(--paper); border: 1px solid var(--line);
}
.wish-grid blockquote::before {
  content: "\201C"; position: absolute; left: 1.1rem; top: .5rem;
  font-family: var(--f-disp); font-size: 3.4rem; color: var(--gold-lt);
}
.wish-grid cite {
  display: block; margin-top: 1rem; font-style: normal;
  font-size: var(--t-cap); letter-spacing: var(--track-cap); text-transform: uppercase;
  color: var(--ink-3);
}
@media (max-width: 56rem) {
  .gift-cards { grid-column: var(--sp-full); grid-template-columns: 1fr; }
  .wish-grid  { grid-template-columns: 1fr; }
}


/* =============================================================
   10 · CHÂN TRANG — CHƯƠNG II khép lại ở sân vườn
   ============================================================= */

.foot {
  background-color: var(--moss-d); color: var(--cream); text-align: center;
  /* Chừa một khoảng đáy. Trước đây chừa rất ít vì hoạ tiết là chiếc
     xe, để 0 thì bánh xe chạm hàng điểm ảnh cuối và đọc ra bị cắt.
     Con dấu là hình TRÒN nên cần nhiều khoảng thở hơn hẳn: một vòng
     tròn sát mép trang đọc ra như bị xén, dù nó không bị xén. */
  padding-block: clamp(4.5rem, 9vw, 7rem) clamp(2.6rem, 5vw, 3.6rem);
  overflow: hidden;
}
.foot .sign { font-size: clamp(2.7rem, 8.4vw, 5.2rem); color: var(--gold-lt); }
.foot p { margin: 1.6rem auto 0; max-width: 34ch; font-size: .95rem; color: rgba(239, 231, 214, .62); }
.foot .fdate {
  max-width: 22rem; margin: clamp(2.5rem, 5vw, 3.5rem) auto 0;
  padding-top: 2.2rem; border-top: 1px solid rgba(198, 179, 146, .22);
  font-size: var(--t-cap); text-transform: uppercase; letter-spacing: var(--track-cap);
  color: rgba(198, 179, 146, .85);
}
/* CON DẤU DẬP CHÌM — khung hình cuối của cả thiệp.
   Thay cho hoạ tiết xe cổ vẽ tay (đã gỡ khỏi index.html). Ba lý do,
   xếp theo sức nặng:

   · Không thêm từ vựng. Con dấu này đã đóng một lần ở album để mở
     chương II; đóng lại lần nữa ở đây là khép vòng, không phải nhắc
     lại. Chiếc xe thì là hình tả thực DUY NHẤT trên một trang thuần
     chữ + chất liệu — nó không đọc ra "xe cổ", nó đọc ra "chỗ này
     chưa có gì".
   · Nó mang tên hai người và ngày cưới, chứ không mang một vật thể
     chung chung mượn từ concept.
   · Dập chìm (blind emboss) là cách thiệp in thật xử lý hoạ tiết ở
     chân trang: không mực, chỉ hình nổi bắt sáng. Ở màn hình, tương
     đương là để nó rất mờ và không tô đặc — opacity .17, nét đồng
     thau, chữ giữa cũng chỉ là nét chứ không phải khối đặc.

   Cỡ: min(46vw, 290px). Lớn hơn con dấu ở album (tối đa 132px) vì ở
   đây nó là dấu chấm hết chứ không phải một chi tiết trong khối. */
.foot-seal {
  display: grid; place-items: center;
  margin: clamp(1.5rem, 3vw, 2.4rem) auto 0;
}
.foot-seal .seal { width: min(46vw, 290px); }
.foot-seal .seal circle   { stroke: var(--gold-lt); }
.foot-seal .seal .s-r1    { stroke-opacity: .8; }
.foot-seal .seal .s-r2    { stroke-opacity: .45; }
/* Chữ N&T ở đây KHÔNG tô đặc mà chỉ viền — đúng chỗ trũng của một vết
   dập chìm. Tô đặc thì con dấu quay lại thành một huy hiệu in mực,
   tranh sáng với dòng "Just Married" ngay phía trên. */
.foot-seal .seal .s-mono  { fill: none; stroke: var(--cream); stroke-width: .9; }
.foot-seal .seal .s-amp   { fill: var(--gold-lt); stroke: none; }
.foot-seal .seal .s-date  { fill: var(--gold-lt); }

/* Đỗ lại chứ không hiện ra. Cử chỉ chậm nhất trang (1,6s) vì nó là
   dấu chấm hết, không phải một khối nội dung. Con dấu thì KHÔNG trượt
   ngang như chiếc xe — nó ĐÓNG XUỐNG: hơi to rồi thu về, đúng động
   tác ấn một con dấu lên giấy.
   Ghi đè .rv.in nhờ độ ưu tiên .foot .foot-seal.rv.in (0,4,0). */
.foot .foot-seal.rv {
  opacity: 0; transform: scale(1.06);
  transition: opacity var(--dur-slow) ease, transform 1.6s var(--ease);
}
.foot .foot-seal.rv.in { opacity: .2; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .foot .foot-seal.rv { opacity: .2; transform: none; transition: none; }
}
