:root {
  --color-bg: #f7f3ea;
  --color-dark: #14120f;
  --color-dark-2: #1c1914;
  --color-gold: #c9a668;
  --color-gold-soft: #e6d7b3;
  --color-text: #2b2620;
  --color-text-light: #f7f3ea;
  /* Referensi luxury-02 memakai Poppins (UI/heading) + Alegreya Sans (label kecil) +
     font kaligrafi berbayar "edensor-free" untuk nama mempelai — diganti Beau Rivage
     (Google Font gratis dengan karakter goresan tipis yang serupa). */
  --font-serif: "Poppins", "Segoe UI", sans-serif;
  --font-script: "Beau Rivage", cursive;
  --font-sans: "Poppins", "Segoe UI", sans-serif;
  --font-label: "Alegreya Sans", "Segoe UI", sans-serif;
  --max-w: 560px;
  /* Satu sumber overlay gelap closing — dipakai juga oleh background footer
     supaya warnanya persis sama dengan overlay slideshow. */
  --overlay-closing: linear-gradient(180deg, rgba(10,9,7,.7) 0%, rgba(10,9,7,.78) 50%, rgba(10,9,7,.88) 100%);
}

* { box-sizing: border-box; }

/* Atribut `hidden` cuma bernilai `display: none` di stylesheet BAWAAN browser,
   dan deklarasi `display` apa pun dari stylesheet ini mengalahkannya. Tanpa
   aturan ini, `.rsvp-form label { display: flex }` membuat kolom "Jumlah Tamu"
   tetap terlihat walau sudah di-`hidden` lewat JS. Dipasang sebagai aturan
   umum supaya setiap elemen ber-`hidden` berikutnya otomatis aman. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-sans);
  color: var(--color-text);
  /* Latar halaman: POLOS GELAP. Di desktop, area kiri-kanan kolom HP
     (.app-frame) memperlihatkan warna ini; di HP kolom menutup seluruh
     layar sehingga latar ini tidak pernah terlihat. */
  background: var(--color-dark);
  scroll-behavior: smooth;
  /* Pengaman tingkat dokumen untuk gerak reveal yang menyamping (geseran Our
     Story, zoom foto mempelai, kartu event miring). Klip UTAMA kini terjadi di
     .app-frame (overflow-x: hidden di tepi kolom selebar ponsel) — aturan ini
     tinggal jaring pengaman terakhir. Dipasang di html DAN body karena iOS
     Safari mengabaikannya kalau hanya di body. */
  overflow-x: hidden;
}

/* ---------- Bingkai HP portrait ----------
   Seluruh halaman dikurung dalam kolom selebar ponsel (430px) yang di-tengah
   di desktop; di HP (lebar <=430px) kolom = seluruh layar, jadi tampilan
   persis seperti sebelumnya. Nilai lebar yang sama dipakai admin.html &
   admin-qr.html (admin.css) supaya ketiga halaman konsisten.

   DUA LAPIS — containing block DIPISAH dari scroll container (perbaikan bug
   "FAB/modal hanya tampil saat section pertama di layar"):

   1. .app-frame = containing block semua elemen position:fixed (FAB audio/QR,
      menu hamburger, semua .modal, .lightbox, #cover, #preloader, .toast):
      `transform: translateZ(0)` membuatnya containing block (CSS spec:
      fixed mengacu ke ancestor TERDEKAT yang punya transform — bukan ancestor
      position:relative), dan `height: 100dvh` membuat anchor bottom:/inset:
      dihitung dari kolom setinggi viewport (bukan min-height — kalau tingginya
      mengikuti isi halaman, FAB melayang ke dasar dokumen). Ia TIDAK discroll
      (overflow: hidden): scroll tidak boleh terjadi di elemen yang juga
      containing block — kalau keduanya menyatu (transform + overflow-y:auto
      di elemen yang sama), engine browser ikut menyeret elemen fixed naik
      bersama konten, dan elemen itu "hilang" begitu discroll lewat viewport
      pertama (gejala persis: FAB/modal hanya tampil saat #opening di layar).

   2. .app-frame__scroll = scroller isi undangan (membungkus main#invitation di
      index.html): overflow-y:auto + height:100dvh, TANPA transform apa pun.
      Karena ia bukan containing block, perputaran scroll-nya tidak bisa
      menyentuh elemen fixed di luar dirinya; karena .app-frame tidak discroll,
      elemen fixed menempel ke kolom selamanya.

   Aman bagi JS: reveal.js memakai IntersectionObserver (relatif viewport, klip
   kolom tetap berlaku karena scroller membentang penuh) dan tidak ada
   window.scrollY/scrollTo di proyek, jadi tidak ada logika yang menyesuaikan. */
.app-frame {
  max-width: 430px;
  margin: 0 auto;
  position: relative;
  height: 100dvh;
  overflow: hidden;           /* klip geseran reveal — scroller ada di lapis bawah */
  overscroll-behavior: contain;
  background: var(--color-bg);  /* warna asli halaman di dalam kolom */
  transform: translateZ(0);     /* containing block fixed descendant (lihat atas) */
}
.app-frame__scroll {
  height: 100dvh;               /* scrollport = kolom setinggi viewport */
  overflow-x: hidden;           /* klip geseran reveal di tepi kolom */
  overflow-y: auto;             /* halaman scroll DI SINI, bukan di .app-frame */
  overscroll-behavior: contain;
  scroll-behavior: smooth;
  scrollbar-width: thin;               /* scrollbar ramping: kesan "frame HP" */
  scrollbar-color: var(--color-gold) transparent;
}
.app-frame__scroll::-webkit-scrollbar { width: 6px; }
.app-frame__scroll::-webkit-scrollbar-thumb { background: var(--color-gold); border-radius: 3px; }

html.no-scroll,
html.no-scroll body {
  overflow: hidden;
  height: 100%;
}
/* Saat undangan terkunci (sebelum Buka Undangan), scroller ikut terkunci —
   yang discroll adalah .app-frame__scroll, bukan body, jadi pembatasan
   dipasang di sana. (.app-frame sendiri selalu overflow:hidden.) */
html.no-scroll .app-frame__scroll { overflow: hidden; }

img { max-width: 100%; display: block; }

h1, h2, h3 { font-family: var(--font-serif); font-weight: 500; margin: 0; }

a { color: inherit; }

/* ---------- Preloader ---------- */
#preloader {
  position: fixed; inset: 0; z-index: 9999;
  background: var(--color-dark);
  display: flex; align-items: center; justify-content: center;
  transition: opacity .6s ease, visibility .6s ease;
}
#preloader.hide { opacity: 0; visibility: hidden; pointer-events: none; }
.preloader-ring {
  width: 48px; height: 48px;
  border: 2px solid var(--color-gold-soft);
  border-top-color: var(--color-gold);
  border-radius: 50%;
  animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- Audio FAB ---------- */
/* 40px (dari 48px): FAB ramping yang tetap enak di-thumb. */
.audio-fab {
  position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 500;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--color-dark); border: 1.5px solid var(--color-gold);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.audio-fab__disc {
  width: 17px; height: 17px; border-radius: 50%;
  background: radial-gradient(circle at center, var(--color-dark) 0 3px, var(--color-gold) 4px 5px, var(--color-dark) 6px 100%);
  animation: spin 3s linear infinite;
  animation-play-state: paused;
}
.audio-fab.playing .audio-fab__disc { animation-play-state: running; }

/* Tombol QR check-in — ditumpuk di KANAN-BAWAH, persis di atas audio FAB:
   bottom = jarak audio FAB dari tepi (1.1rem) + tingginya (40px) + jarak
   antar-tombol (.9rem), supaya tidak nempel. Muncul hanya untuk tamu
   perorangan (URL ber-?to=), diatur qr-checkin.js. */
.qr-fab {
  position: fixed; right: 1.1rem; bottom: calc(1.1rem + 40px + .9rem); z-index: 500;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--color-dark); border: 1.5px solid var(--color-gold);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: var(--color-gold);
}
.qr-fab svg { width: 18px; height: 18px; }

/* Tombol menu hamburger — kembaran visual FAB lain (bulat, dark + emas), tapi
   di kanan atas: kanan bawah sudah dipakai audio + QR FAB. Muncul hanya
   setelah undangan dibuka (lihat nav-menu.js). */
.nav-fab {
  position: fixed; top: 1.1rem; right: 1.1rem; z-index: 500;
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--color-dark); border: 1.5px solid var(--color-gold);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: var(--color-gold);
}
.nav-fab svg { width: 22px; height: 22px; }

/* ---------- Hero sections (cover / opening / closing) ---------- */
.section-hero {
  position: relative;
  min-height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  text-align: center;
  color: var(--color-text-light);
}
.section-hero--short { min-height: 90dvh; }

.hero-media { position: absolute; inset: 0; overflow: hidden; }
.hero-media img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* pan/zoom admin pada hero: --fx/--fy dipakai object-position, --zoom sudah
     ditangani keyframe kenburns di atas */
  object-position: var(--fx, 50%) var(--fy, 50%);
}
/* Slide bergeser dari kiri ke kanan (filmstrip) saat berganti, bukan crossfade opacity —
   foto baru masuk dari kiri, foto lama keluar ke kanan, sempat bersebelahan. */
.hero-slide {
  position: absolute; inset: 0; overflow: hidden;
  transform: translateX(-100%);
  transition: transform 3.2s cubic-bezier(.65,0,.35,1);
}
.hero-slide.active { transform: translateX(0); z-index: 1; }
.hero-slide.exiting { transform: translateX(100%); z-index: 1; }
.kenburns { animation: kenburns 9s ease-in-out infinite alternate; }
/* Zoom admin (--zoom) dimasukkan KE DALAM keyframe, bukan transform biasa di
   elemen — animasi selalu menimpa transform statis, jadi --zoom di luar
   keyframe akan diabaikan diam-diam (jebakan #5 rencana admin panel). */
@keyframes kenburns {
  from { transform: scale(var(--zoom, 1)); }
  to   { transform: scale(calc(var(--zoom, 1) * 1.15)); }
}
/* Slideshow yang sedang di luar layar (cover setelah keluar, closing sebelum
   discroll) — animasinya dibekukan, lihat hero-slideshow.js. */
.hero-media--paused .kenburns { animation-play-state: paused; }


/* Selaraskan dengan timing asli Elementor (Animate.css): durasi 1s, zoomIn dari scale(.3).
   AOS sekarang HANYA dipakai section #cover — section di bawahnya pindah ke
   mekanisme [data-reveal] (lihat bawah). */
[data-aos="zoom-in"] { transform: scale(.3) !important; }
[data-aos].aos-animate { transform: none !important; }

.hero-overlay {
  position: absolute; inset: 0;
  z-index: 2; /* di atas foto slideshow (z-index 1), di bawah teks (z-index 2, DOM belakang) */
  background: linear-gradient(180deg,
    rgba(10,9,7,.35) 0%,
    rgba(10,9,7,.08) 22%,
    rgba(10,9,7,.05) 55%,
    rgba(10,9,7,.55) 80%,
    rgba(10,9,7,.92) 100%);
}
.hero-overlay--soft {
  background: linear-gradient(180deg,
    rgba(10,9,7,.25) 0%,
    rgba(10,9,7,.05) 30%,
    rgba(10,9,7,.15) 60%,
    rgba(10,9,7,.75) 100%);
}
/* Untuk section dengan konten di tengah (opening/closing) — perlu gelap merata,
   bukan profil gelap-di-tepi/terang-di-tengah seperti cover. */
.hero-overlay--center {
  background: linear-gradient(180deg, rgba(10,9,7,.4) 0%, rgba(10,9,7,.58) 50%, rgba(10,9,7,.7) 100%);
}
/* Closing (footer): gelap transparan lebih pekat agar teks di atas slider terbaca jelas */
.hero-overlay--closing {
  background: var(--overlay-closing);
}
.hero-content, .hero-content * { text-shadow: 0 2px 14px rgba(0,0,0,.55); }

/* Cover diperpanjang: foto sama menyambung dari layar-1 (nama+tombol) ke layar-2
   (countdown), lalu gradasi jadi solid --color-dark persis di titik section
   berikutnya mulai -> transisi mulus tanpa "sambungan" terlihat. */
#cover { min-height: 0; display: block; }
.cover-screen { position: relative; min-height: 100dvh; }
.cover-screen .hero-content--split { position: absolute; inset: 0; }
.cover-countdown {
  position: relative; z-index: 2;
  text-align: center; color: #fff;
  padding: 1.5rem 1.5rem 3.5rem;
}
.cover-countdown__date {
  font-family: var(--font-sans); font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase;
  font-size: .85rem; color: #fff; margin: 0;
}
/* Dorong konten section countdown lebih ke kanan-bawah, sesuai posisi di referensi. */
.section-hero--anchor-right-bottom { align-items: flex-end; padding-bottom: 2.5rem; }
.section-hero--anchor-right-bottom .hero-content {
  width: 100%; max-width: 100%;
  padding-right: .9rem; padding-left: .9rem; padding-bottom: 0;
  text-align: right;
}

.countdown-heading { width: 100%; text-align: right; margin-bottom: .85rem; padding-right: .5rem; }
.countdown-heading .eyebrow { text-align: right; }
.countdown-heading .script-names { text-align: right; margin: .1rem 0; }

.scroll-chevron {
  display: block; margin-top: 1.25rem;
  font-size: 1.4rem; color: var(--color-gold-soft);
  animation: chevron-bounce 1.8s ease-in-out infinite;
}
@keyframes chevron-bounce { 0%,100% { transform: translateY(0); opacity:.6; } 50% { transform: translateY(6px); opacity:1; } }

.hero-overlay--cover {
  background: linear-gradient(180deg,
    rgba(10,9,7,.32) 0%,
    rgba(10,9,7,.08) 42%,
    rgba(10,9,7,.9) 100%);
}
/* Cover (screen pertama): overlay flat & tipis saja — keterbacaan teks bawah
   ditangani terpisah oleh pita .hero-content--split::after, bukan gradient ini,
   supaya tidak ada dua gradient warna yang tumpuk (penyebab "patahan"). */
.hero-overlay--flat { background: rgba(10,9,7,.22); }

.opening-thumbs { display: flex; gap: .6rem; justify-content: center; margin-bottom: 1.75rem; }
.opening-thumbs img { width: 110px; height: 140px; object-fit: cover; border-radius: 8px; }

.hero-content {
  position: relative; z-index: 2;
  max-width: var(--max-w);
  padding: 2rem 1.5rem;
}

/* Cover: judul di atas, guest+tombol di bawah, wajah mempelai tetap terlihat di tengah */
.hero-content--split {
  position: absolute; inset: 0;
  max-width: none;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  text-align: center;
  padding: 3.25rem 1.5rem 2.5rem;
}
.hero-content__top, .hero-content__bottom { max-width: var(--max-w); width: 100%; position: relative; z-index: 1; }

/*
 * RESEP "blur-fade" yang dipakai berulang di file ini untuk menaruh teks di atas foto
 * tanpa membuat wajah/subjek jadi buram dan tanpa garis patahan:
 *   1. SATU pseudo-element (::after), bukan dua gradient warna yang tumpuk (tumpukan
 *      dua gradient warna menghasilkan "patahan" di titik pertemuannya).
 *   2. background: linear-gradient(warna-solid di ujung dekat teks -> transparent).
 *   3. backdrop-filter: blur(Npx) supaya foto di baliknya ikut buram di zona teks.
 *   4. mask-image dengan arah gradient YANG SAMA seperti background di atas — ini kunci
 *      supaya blur-nya ikut menipis/fade, bukan menyala penuh dengan tepi tegas (tanpa
 *      mask, backdrop-filter selalu 100% di seluruh kotak elemen, seolah ada garis).
 *   5. Elemen dibuat sedikit lebih lebar dari kontainer (left/right negatif) untuk
 *      menghindari sisi kiri-kanan yang belum ter-blur kelihatan saat discroll miring.
 * Pakai pola ini lagi untuk section lain yang butuh teks di atas foto (event card, dsb).
 */
.hero-content--split::after {
  content: "";
  position: absolute;
  left: -10%; right: -10%; bottom: -2px;
  height: calc(46% + 2px);
  background: linear-gradient(180deg, transparent 0%, #2b2620 100%);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  mask-image: linear-gradient(180deg, transparent 0%, black 85%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, black 85%);
  pointer-events: none;
}

/* Sama seperti .hero-content--split::after, TAPI nempel ke dasar SECTION (bukan ke
   blok teks yang tingginya ikut konten) — elemen sibling sendiri, position:absolute
   relatif ke .section-hero (position:relative), bottom:0 sungguhan. */
.section-fade-bottom {
  position: absolute;
  left: -10%; right: -10%; bottom: -2px;
  height: calc(42% + 2px);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  mask-image: linear-gradient(180deg, transparent 0%, black 85%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, black 85%);
  pointer-events: none;
  z-index: 1;
}
.section-fade-bottom--dark { background: linear-gradient(180deg, transparent 0%, var(--color-dark) 100%); }
.hero-note { margin-top: .85rem; font-size: .65rem; opacity: .65; letter-spacing: .03em; }

.eyebrow, .section-eyebrow {
  font-family: var(--font-label);
  text-transform: uppercase;
  letter-spacing: .35em;
  font-size: .7rem;
  color: var(--color-gold-soft);
  margin: 0 0 .75rem;
}
.hero-content .eyebrow { color: #ffffff; font-weight: 500; }

.script-names {
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(3rem, 14vw, 5rem);
  color: #ffffff;
  margin: .25rem 0;
  line-height: 1.1;
}
.script-names--small { font-size: clamp(2rem, 8vw, 3rem); }
.amp { padding: 0 .1em; }

.hero-date {
  letter-spacing: .25em;
  font-size: .85rem;
  margin: .5rem 0 2rem;
}

.guest-label { margin: 0 0 .4rem; font-family: var(--font-sans); font-weight: 500; font-size: .8rem; letter-spacing: .02em; color: #ffffff; }
.guest-name { margin: 0 0 1.5rem; font-family: var(--font-sans); font-weight: 500; font-size: 1.25rem; color: #ffffff; }

.btn-open {
  font-family: var(--font-sans);
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: .8rem;
  font-weight: 500;
  background: transparent;
  border: 1.5px solid #ffffff;
  color: #ffffff;
  padding: .65rem 1.6rem;
  border-radius: 10px;
  cursor: pointer;
  transition: background .3s ease, color .3s ease;
  display: inline-flex; align-items: center; gap: .5rem;
}
.btn-open:hover { background: #ffffff; color: var(--color-dark); }
.btn-open .icon-envelope { width: 15px; height: 15px; flex: none; }

/* Tombol aksi utama (kirim RSVP, kirim hadiah). Sengaja terisi penuh warna emas,
   bukan sekadar bergaris seperti .btn-outline: ini satu-satunya aksi yang
   diharapkan dari tamu di section-nya, jadi harus paling menonjol.
   Emas pekat ini kontras di dua-duanya — di atas latar krem (#f7f3ea) maupun
   latar gelap (#14120f) — jadi satu kelas ini cukup untuk kedua section.
   Jangan pakai .btn-open di latar terang: teks & garisnya putih, dirancang untuk
   foto gelap di cover, dan di atas krem nyaris tidak terlihat. */
.btn-primary {
  font-family: var(--font-sans);
  letter-spacing: .1em;
  text-transform: uppercase;
  font-size: .82rem;
  font-weight: 600;
  background: var(--color-gold);
  border: 1.5px solid var(--color-gold);
  color: var(--color-dark);
  padding: .85rem 2rem;
  border-radius: 10px;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(201, 166, 104, .35);
  transition: background .3s ease, color .3s ease, border-color .3s ease, box-shadow .3s ease;
}
/* Hover memekatkan emasnya, BUKAN menggelapkannya jadi --color-dark: di section
   Tanda Kasih yang latarnya gelap, tombolnya akan lenyap menyatu dengan latar. */
.btn-primary:hover {
  background: #b8914f;
  border-color: #b8914f;
  color: var(--color-dark);
}
/* rsvp.js menonaktifkan tombol selama pengiriman — beri tanda yang jelas supaya
   tamu tidak menekan berulang kali karena mengira tidak ada yang terjadi. */
.btn-primary:disabled {
  opacity: .5;
  cursor: default;
  box-shadow: none;
}

.btn-outline {
  display: inline-block;
  font-family: var(--font-sans);
  letter-spacing: .12em;
  text-transform: uppercase;
  font-size: .75rem;
  border: 1.5px solid var(--color-gold);
  color: inherit;
  background: transparent;
  padding: .75rem 1.8rem;
  border-radius: 10px;
  cursor: pointer;
  text-decoration: none;
  transition: background .3s ease, color .3s ease;
}
.btn-outline:hover { background: var(--color-gold); color: var(--color-dark); }

/* Tombol di atas foto (cover & countdown) — putih konsisten dengan .btn-open, bukan emas. */
.btn-outline--light {
  border-color: #fff; color: #fff;
  padding: .5rem 1rem; font-size: .68rem;
  display: inline-flex; align-items: center;
  margin-bottom: 1.5rem;
}
.btn-outline--light:hover { background: #fff; color: var(--color-dark); }
.btn-outline--light svg { width: 13px; height: 13px; margin-right: .35rem; }

.btn-text {
  background: none; border: none; text-decoration: underline;
  color: var(--color-gold-soft); cursor: pointer; font-family: var(--font-sans);
  margin-top: 1rem;
}

/* ---------- Invitation lock & open transition ---------- */
#invitation.is-locked .invitation-body { display: none; }
/* Tombol ikut fade/slide keluar bersama seluruh #cover (opacity+transform) —
   jangan di-display:none mendadak, itu bikin tombol "hilang tiba-tiba" saat cover
   masih dalam proses transisi keluar. */

#cover {
  position: fixed; inset: 0; z-index: 50;
  transform: translateY(-100%);
  opacity: 0;
  transition: transform 1.8s cubic-bezier(.65,0,.35,1), opacity 1.8s ease;
}
#cover.cover-visible { transform: translateY(0); opacity: 1; }
#cover.is-exiting { transform: translateY(-100%); opacity: 0; pointer-events: none; }

#opening {
  opacity: 0;
  transform: translate(-20px, 20px);
  /* Slide-in (transform) lebih pelan dari fade opacity foto, supaya foto pertama
     benar-benar terasa "hanyut masuk" — bukan langsung nongol. Tempo ini sengaja
     tenang mengikuti nuansa golden hour lagunya.
     CATATAN: kalau durasi transform di bawah diubah, geser juga jeda
     `opening.classList.add("text-revealed")` di main.js — teks memang dirancang
     baru masuk SETELAH slide-in foto selesai. */
  transition: opacity 2.4s ease, transform 1.8s cubic-bezier(.22,.61,.36,1);
}
#opening.section-revealed { opacity: 1; transform: translate(0, 0); }

/* Teks/tombol/countdown section countdown BARU animasi masuk SETELAH foto section
   selesai (class .text-revealed ditambahkan lewat JS, bukan lewat AOS — AOS memicu
   terlalu awal karena section induknya sudah "di viewport" walau masih opacity:0). */
.text-enter { opacity: 0; transition: opacity .75s ease, transform .75s ease; }
.text-enter--down { transform: translateY(-24px); }
.text-enter--left { transform: translateX(34px); }
.text-enter--up { transform: translateY(28px); }
#opening.text-revealed .text-enter { opacity: 1; transform: translate(0, 0); }
#opening.text-revealed .script-names.text-enter { transition-delay: .1s; }
#opening.text-revealed .cover-countdown__date.text-enter { transition-delay: .2s; }
#opening.text-revealed #btn-add-calendar.text-enter { transition-delay: .3s; }
#opening.text-revealed .countdown--plain.text-enter { transition-delay: .4s; }

/* ---------- Scroll-reveal section di bawah cover ([data-reveal]) ----------
   Perluasan dari pola .text-enter di atas, tapi dipicu IntersectionObserver
   (assets/js/reveal.js) alih-alih setTimeout. Semua state tersembunyi digantung
   pada html.reveal-ready — kalau JS-nya mati, konten tetap terlihat.
   Gerakannya sengaja dibatasi opacity + transform saja supaya murni di GPU;
   tidak ada will-change agar 20 foto galeri tidak jadi 20 layer di HP. */
.reveal-ready [data-reveal] {
  opacity: 0;
  /* Tempo sengaja lambat & tidak terburu-buru, menyesuaikan nuansa golden hour
     lagunya. Per elemen bisa ditimpa lewat --reveal-dur (foto besar lebih pelan
     lagi). Easing easeOutQuad: melaju tenang lalu mengendap, bukan menyentak. */
  transition: opacity var(--reveal-dur, 1.8s) cubic-bezier(.22,.61,.36,1),
              transform var(--reveal-dur, 1.8s) cubic-bezier(.22,.61,.36,1);
  /* Jeda berurutan dalam satu grup: style="--reveal-i:N" -> N x 200ms.
     Indeks sengaja dibatasi di sisi pemanggil (i % 4) supaya daftar panjang
     tidak menumpuk delay sampai elemen terakhir keburu lewat layar. */
  transition-delay: calc(var(--reveal-i, 0) * 200ms);
}
.reveal-ready [data-reveal].is-revealed { opacity: 1; }

/* Tiap varian me-reset transform-nya SENDIRI (bukan satu aturan `transform: none`
   bersama) — supaya elemen yang sudah punya transform sendiri, seperti .event-label
   yang di-rotate -90deg, tidak kehilangan rotasinya saat direveal. Karena itu pula
   varian "fade" sengaja tidak menyentuh properti transform sama sekali. */

/* -- gerak dasar teks -- */
.reveal-ready [data-reveal="up"]                     { transform: translateY(26px); }
.reveal-ready [data-reveal="up"].is-revealed         { transform: translateY(0); }
.reveal-ready [data-reveal="down"]                   { transform: translateY(-22px); }
.reveal-ready [data-reveal="down"].is-revealed       { transform: translateY(0); }

/* -- Our Story: item masuk selang-seling dari kanan lalu kiri, seperti langkah
      bergantian menyusuri perjalanan. Section-nya diberi overflow hidden supaya
      geseran ini tidak pernah memunculkan scroll horizontal. -- */
.reveal-ready [data-reveal="slide-right"]            { transform: translateX(48px); }
.reveal-ready [data-reveal="slide-right"].is-revealed{ transform: translateX(0); }
.reveal-ready [data-reveal="slide-left"]             { transform: translateX(-48px); }
.reveal-ready [data-reveal="slide-left"].is-revealed { transform: translateX(0); }

/* -- Mempelai: foto besar MELUNCUR MASUK DARI LUAR LAYAR — mempelai wanita dari
      kanan, mempelai pria dari kiri, seolah keduanya saling mendekat.
      opacity sengaja dibiarkan 1: kalau ikut fade seperti varian lain, fotonya
      terlihat "menyala di tengah jalan" alih-alih benar-benar meluncur masuk.
      Selagi di luar layar ia sudah dikurung #couple.section (overflow: hidden),
      jadi tidak pernah terlihat maupun memicu scroll. -- */
.reveal-ready [data-reveal="enter-right"]            { opacity: 1; transform: translateX(100%) scale(1.06); }
.reveal-ready [data-reveal="enter-left"]             { opacity: 1; transform: translateX(-100%) scale(1.06); }
.reveal-ready [data-reveal="enter-right"].is-revealed,
.reveal-ready [data-reveal="enter-left"].is-revealed { transform: none; }

/* Teks identitas mempelai ikut masuk bersama fotonya, bukan menunggu luncuran
   2.8s di atas selesai — teksnya ada DI DALAM elemen yang meluncur, jadi ia ikut
   terbawa masuk sambil memudar tampil. Jeda .5s hanya memberi fotonya kesempatan
   mengisi layar lebih dulu; kalau dinaikkan mendekati 2.8s, namanya baru muncul
   setelah tamu biasanya sudah menggulir lewat. */
.reveal-ready .couple-info [data-reveal] {
  transition-delay: calc(.5s + var(--reveal-i, 0) * 200ms);
}

/* -- Kartu event: mendarat dari sedikit miring, seperti kartu undangan fisik
      yang diletakkan di meja. Akad & Resepsi miring berlawanan. --
   Nama & detail acara (.event-card__info) kini satu grup dengan kartunya
   sendiri (lihat index.html) supaya terpicu bareng, bukan trigger scroll
   terpisah yang telat karena posisinya di bawah kartu setinggi 90vh. Delay
   di bawah membuatnya tetap terasa "menyusul" kartu yang baru mendarat --
   .event-card durasinya 2.2s (lihat --reveal-dur di bawah), tapi delay-nya
   SENGAJA jauh lebih pendek dari itu: kalau didekatkan ke durasi penuh,
   namanya baru muncul setelah tamu biasanya sudah scroll lewat (pelajaran
   yang sama seperti .couple-info di atas). 140ms per --reveal-i menyamai
   satuan stagger default (event-date=1, event-place=2 sudah ditulis dengan
   asumsi itu). */
.reveal-ready .event-card__info [data-reveal] {
  transition-delay: calc(.5s + var(--reveal-i, 0) * 140ms);
}
.reveal-ready [data-reveal="tilt-left"]              { transform: translateY(34px) rotate(-1.8deg); }
.reveal-ready [data-reveal="tilt-right"]             { transform: translateY(34px) rotate(1.8deg); }
.reveal-ready [data-reveal="tilt-left"].is-revealed,
.reveal-ready [data-reveal="tilt-right"].is-revealed { transform: none; }

/* -- Galeri: tiap foto mengembang pelan dari sedikit mengecil. -- */
.reveal-ready [data-reveal="pop"]                    { transform: scale(.9); }
.reveal-ready [data-reveal="pop"].is-revealed        { transform: scale(1); }

/* -- Kartu berat (dresscode): naik dengan skala nyaris 1 — jauh lebih murah
      daripada zoom dari scale(.3) yang dipakai versi AOS dulu. -- */
.reveal-ready [data-reveal="rise"]                   { transform: translateY(34px) scale(.985); }
.reveal-ready [data-reveal="rise"].is-revealed       { transform: translateY(0) scale(1); }

/* -- Foto full-bleed (quote): mengendap dari 1.07 ke 1. -- */
.reveal-ready [data-reveal="zoom"]                   { transform: scale(1.07); }
.reveal-ready [data-reveal="zoom"].is-revealed       { transform: scale(1); }

/* Durasi per jenis konten — makin besar & makin "foto", makin pelan. */
.reveal-ready .couple-reveal[data-reveal]  { --reveal-dur: 2.8s; }
.reveal-ready #quote-picture[data-reveal]  { --reveal-dur: 2.5s; }
.reveal-ready .event-card[data-reveal]     { --reveal-dur: 2.2s; }
.reveal-ready .timeline-item[data-reveal]  { --reveal-dur: 2.1s; }
.reveal-ready .gallery-item[data-reveal]   { --reveal-dur: 1.9s; }
.reveal-ready .wfl-slider[data-reveal]     { --reveal-dur: 2.2s; }

/* Geseran horizontal Our Story & zoom foto mempelai butuh ruang keluar layar —
   kurung di section-nya masing-masing supaya tidak jadi scroll horizontal.
   Transform foto mempelai sengaja dipasang di .couple-block, BUKAN di
   .couple-slider — .couple-slider adalah root Swiper, dan menaruh scale di situ
   berisiko membuat Swiper salah mengukur lebar slide saat init. */
#love-story.section, #couple.section { overflow: hidden; }

/* ---------- Opening quote ---------- */
.arabic-quote { font-size: 1.6rem; margin: 0 0 1rem; color: var(--color-gold-soft); }
.opening-quote { font-family: var(--font-serif); font-size: 1.25rem; line-height: 1.7; font-style: italic; }
.opening-source { margin-top: .75rem; font-size: .8rem; letter-spacing: .1em; opacity: .8; }

/* ---------- Generic sections ---------- */
.section {
  padding: 5rem 1.5rem;
  text-align: center;
  background: var(--color-bg);
}
.section--dark { background: var(--color-dark); color: var(--color-text-light); }
.section--dark .section-eyebrow { color: var(--color-gold); }

.section-title {
  font-size: clamp(1.8rem, 6vw, 2.4rem);
  margin-bottom: 2.5rem;
}
.section-title--script {
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(2.6rem, 9vw, 3.4rem);
  color: var(--color-gold);
  margin-bottom: 2.5rem;
  line-height: 1;
}
.section-desc {
  max-width: 480px; margin: -1.5rem auto 2rem;
  line-height: 1.7; font-size: .95rem;
}

/* ---------- Couple ---------- */
.couple-grid {
  display: flex; flex-direction: column; align-items: center; gap: 2rem;
  max-width: 720px; margin: 0 auto;
}
@media (min-width: 720px) {
  .couple-grid { flex-direction: row; justify-content: center; align-items: center; }
}
.couple-card {
  position: relative;
  width: 100%; max-width: 340px;
  aspect-ratio: 3 / 4;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--color-gold);
}
.couple-card__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.couple-card__info {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 3rem 1.25rem 1.25rem;
  background: linear-gradient(180deg, transparent, rgba(10,9,7,.55) 45%, rgba(10,9,7,.92) 100%);
  text-align: center;
}
.couple-name { font-size: 1.4rem; color: var(--color-gold-soft); margin-bottom: .35rem; }
.couple-parents { font-size: .8rem; line-height: 1.55; opacity: .9; color: var(--color-text-light); }
.couple-divider {
  font-family: var(--font-script);
  font-size: 2.5rem; color: var(--color-gold);
}

/* ---------- Event + Countdown ---------- */
.countdown {
  display: flex; justify-content: center; gap: .75rem;
  flex-wrap: wrap; margin-bottom: 3rem;
}
.countdown__item {
  background: var(--color-dark); color: var(--color-text-light);
  border: 1px solid var(--color-gold);
  border-radius: 10px;
  width: 72px; padding: .9rem 0;
}
.countdown__item span {
  display: block; font-family: var(--font-serif); font-size: 1.7rem; color: var(--color-gold);
}
.countdown__item small { font-family: var(--font-label); font-size: .65rem; letter-spacing: .1em; text-transform: uppercase; opacity: .8; }

/* Countdown polos di atas foto (tanpa kartu gelap), dipakai di section countdown.
   Lebarnya SELALU 4/5 (80%) lebar layar di HP mana pun: tiap item flex:1 membagi
   lebarnya secara merata, jadi jarak antar item otomatis menyesuaikan layar
   (longgar di HP besar, rapat tanpa meluber di HP sempit) dan keempat item tetap
   sejajar satu baris. Grup menempel kanan (margin-left auto) mengikuti gaya
   anchor-right-bottom section countdown. */
.countdown--plain {
  width: 80%; margin-left: auto;
  justify-content: space-between; gap: .6rem; margin-bottom: 0;
}
.countdown--plain .countdown__item {
  background: none; border: none; width: auto; padding: 0; text-align: center;
  flex: 1 1 0; min-width: 0;
}
.countdown--plain .countdown__item span { color: #fff; font-size: 1.15rem; }
.countdown--plain .countdown__item small { color: #fff; opacity: .85; font-size: .6rem; }

.event-grid {
  display: grid; gap: 1.25rem;
  grid-template-columns: 1fr; max-width: 480px; margin: 0 auto 2.5rem;
}
@media (min-width: 560px) { .event-grid { grid-template-columns: 1fr 1fr; } }
.event-card {
  border: 1px solid var(--color-gold);
  border-radius: 10px; padding: 1.5rem 1rem;
}
.event-card__label { font-family: var(--font-serif); font-size: 1.3rem; margin: 0 0 .4rem; color: var(--color-text); }
.event-card__time { margin: 0; letter-spacing: .08em; font-size: .9rem; }

.venue { max-width: 420px; margin: 0 auto; }
.venue h3 { font-size: 1.3rem; margin-bottom: .5rem; }
.venue p { line-height: 1.6; font-size: .9rem; margin-bottom: 1.25rem; opacity: .85; }

/* ---------- Love story timeline ---------- */
.timeline { max-width: 480px; margin: 0 auto; display: flex; flex-direction: column; gap: 2.5rem; }
.timeline-item { text-align: left; } /* tanpa garis timeline di kiri foto */
.timeline-item__photo {
  width: 100%; aspect-ratio: 16/10; object-fit: cover;
  border-radius: 10px; border: 1px solid var(--color-gold);
  margin-bottom: 1rem;
}
.timeline-item .timeline-date { color: var(--color-gold); font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; }
.timeline-item h4 { font-family: var(--font-serif); font-size: 1.3rem; margin: .3rem 0; color: var(--color-gold-soft); }
.timeline-item p { margin: 0; font-size: .9rem; line-height: 1.6; opacity: .85; }

/* ---------- Gallery (grid 4 kolom, pola: landscape / 2 potrait / landscape / 1-4+3-4 / landscape / 2 potrait / landscape) ---------- */
.gallery-grid {
  max-width: 720px; margin: 0 auto;
  display: grid;
  /* 12 kolom, bukan 4: baris campur memakai pembagian PERTIGA (1/3 + 2/3),
     sedangkan baris potret memakai PERDUA. 12 adalah kelipatan persekutuan
     terkecil dari 2, 3, dan 4 — dengan 4 kolom, sepertiga tidak bisa dibentuk
     sama sekali. */
  grid-template-columns: repeat(12, 1fr);
  gap: .5rem;
  /* Semua baris sama tinggi = tinggi foto landscape (16:10 dari lebar kontainer ~96vw).
     min(450px, 60vw) meniru aspek 16/10: 0.96vw / 0.6vw = 1.6, dan 720/450 = 1.6. */
  grid-auto-rows: min(450px, 60vw);
}
.gallery-item { border-radius: 8px; overflow: hidden; cursor: zoom-in; }
.gallery-item img, .gallery-item picture { display: block; width: 100%; height: 100%; }
.gallery-item img { object-fit: cover; }

/* Pan/zoom admin — satu aturan untuk semua foto non-hero: object-position
   menggeser titik fokus (--fx/--fy), transform menge-zoom (--zoom). Setiap
   wadah foto di atas sudah overflow:hidden, jadi scale tidak bocor keluar.
   Hero tidak ikut di sini (kenburns menangani transform-nya sendiri). */
.wfl-slide img,
.couple-slide img,
.event-slide img,
.gallery-item img,
.timeline-item__photo,
#quote-img {
  object-position: var(--fx, 50%) var(--fy, 50%);
  transform: scale(var(--zoom, 1));
}
/* 1 foto landscape selebar grid — seukuran foto Our Story (16/10) */
.gallery-item--landscape { grid-column: 1 / -1; }
/* 2 foto potrait satu baris, tinggi menyamai foto landscape di atasnya */
.gallery-item--portrait { grid-column: span 6; }
/* baris campur: potrait 1/3 lebar + semi-landscape 2/3 lebar, tinggi sama */
.gallery-item--third { grid-column: span 4; }
.gallery-item--twothirds { grid-column: span 8; }

/* Slot video galeri (baris pertama, opsional): thumbnail dulu, iframe baru
   dibuat saat tombol putar diklik — tidak ada beban YouTube sebelum diminta. */
.gallery-video { cursor: default; }
.gallery-video__launcher {
  position: relative; display: block; width: 100%; height: 100%;
  padding: 0; border: 0; cursor: pointer; background: #000;
}
.gallery-video__launcher img { width: 100%; height: 100%; object-fit: cover; opacity: .85; }
.gallery-video__play {
  position: absolute; inset: 0; margin: auto;
  width: 64px; height: 64px; border-radius: 50%;
  background: rgba(20, 18, 15, .65); border: 2px solid var(--color-gold);
  display: flex; align-items: center; justify-content: center;
  color: var(--color-gold-soft); pointer-events: none;
}
.gallery-video__play svg { width: 26px; height: 26px; margin-left: 3px; }
.gallery-video__frame { display: block; width: 100%; height: 100%; border: 0; }

.lightbox {
  position: fixed; inset: 0; background: rgba(10,9,8,.94); z-index: 900;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none; transition: opacity .3s ease;
}
.lightbox.show { opacity: 1; pointer-events: auto; }
/* max-width 100% (bukan 92vw): lightbox kini terkurung di kolom 430px; 92vw di
   desktop bisa lebih lebar dari kolom dan foto terpotong di tepinya. */
.lightbox img { max-width: 100%; max-height: 88vh; border-radius: 6px; }
.lightbox__close {
  position: absolute; top: 1rem; right: 1.25rem;
  background: none; border: none; color: var(--color-gold-soft);
  font-size: 2.2rem; cursor: pointer; line-height: 1;
}

/* ---------- Modal (overlay + panel) ----------
   Pola yang sama dengan .lightbox: fixed inset-0, konten di tengah, panel
   gelap berbingkai emas supaya konsisten di atas section terang maupun gelap.
   Dibuka/ditutup lewat atribut `hidden` (lihat aturan [hidden] di atas). */
.modal {
  position: fixed; inset: 0; z-index: 950;
  background: rgba(10, 9, 8, .9);
  display: flex; align-items: center; justify-content: center;
  padding: 1.25rem;
}
.modal__panel {
  width: 100%; max-width: 420px; max-height: 88vh; overflow-y: auto;
  background: var(--color-dark-2); border: 1px solid var(--color-gold);
  border-radius: 14px; padding: 1.5rem;
  color: var(--color-text-light);
  animation: fadeUp .35s ease;
}
.modal__header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.25rem; }
.modal__header h3 { font-family: var(--font-serif); font-weight: 600; font-size: 1.05rem; margin: 0; letter-spacing: .03em; }
.modal__close {
  background: none; border: none; color: var(--color-gold-soft);
  font-size: 1.8rem; line-height: 1; cursor: pointer; padding: .1rem .35rem;
}
.modal__field { display: flex; flex-direction: column; gap: .4rem; margin-bottom: 1rem; text-align: left; }
.modal__field > span { font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; opacity: .75; }
.modal__field select, .modal__field textarea {
  font-family: var(--font-sans); font-size: .92rem; color: var(--color-text-light);
  background: transparent; border: 1px solid var(--color-gold); border-radius: 8px;
  padding: .6rem .7rem; resize: vertical;
}
.modal__field select option { color: var(--color-text); background: var(--color-bg); }
.modal__field select:focus, .modal__field textarea:focus { outline: none; border-color: var(--color-gold-soft); }
.modal__panel .btn-primary { width: 100%; }
.modal__hint { font-size: .7rem; opacity: .6; margin: .9rem 0 0; text-align: center; }

/* Elemen dalam modal Konfirmasi Pengiriman & Rekomendasi Kado muncul
   BERURUTAN (stagger) setiap modal dibuka. Dipakai animasi keyframes (bukan
   transition) supaya di-REPLAY ulang tiap kali modal dibuka: saat `hidden`
   (display:none) elemen keluar dari rendering, lalu saat dibuka mereka masuk
   lagi dari state awal bersama panelnya (panel memakai fadeUp milik
   .modal__panel) — pola yang sama dengan menu navigasi. Modal lain
   (QR check-in) sengaja tidak ikut. */
#gift-confirm-modal .modal__panel > *,
#gift-recs-modal .modal__panel > * {
  animation: modalItemIn .5s cubic-bezier(.22,.61,.36,1) both;
}
@keyframes modalItemIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* Jeda berurutan: anak ke-N mulai ~80ms setelah anak sebelumnya */
#gift-confirm-modal .modal__panel > *:nth-child(1) { animation-delay: .05s; }
#gift-confirm-modal .modal__panel > *:nth-child(2) { animation-delay: .13s; }
#gift-confirm-modal .modal__panel > *:nth-child(3) { animation-delay: .21s; }
#gift-confirm-modal .modal__panel > *:nth-child(4) { animation-delay: .29s; }
#gift-confirm-modal .modal__panel > *:nth-child(5) { animation-delay: .37s; }
#gift-recs-modal .modal__panel > *:nth-child(1) { animation-delay: .05s; }
#gift-recs-modal .modal__panel > *:nth-child(2) { animation-delay: .13s; }
/* Tombol aksi utama mengembang (pop) — pusat perhatian saat modal terbuka */
#gift-confirm-modal #gift-confirm-send { animation-name: modalBtnPop; }
@keyframes modalBtnPop {
  from { opacity: 0; transform: scale(.9); }
  to   { opacity: 1; transform: scale(1); }
}

/* Daftar shortcut menu navigasi — gaya premium: tanpa bingkai kotak, tiap item
   hanya dipisah garis bawah tipis emas; item muncul BERURUTAN (stagger) setiap
   modal dibuka. Animasi di-replay otomatis tiap kali modal terbuka karena saat
   `hidden` (display:none) item keluar dari rendering, lalu masuk lagi bersama
   panelnya (panel memakai animasi fadeUp milik .modal__panel). */
.nav-menu-list { display: flex; flex-direction: column; }
.nav-menu-list button {
  font-family: var(--font-sans);
  letter-spacing: .12em;
  text-transform: uppercase;
  font-size: .75rem;
  border: none;
  border-bottom: 1px solid rgba(201, 166, 104, .22);
  color: var(--color-text-light);
  background: transparent;
  padding: .95rem .2rem;
  cursor: pointer;
  text-align: left;
  animation: navItemIn .75s cubic-bezier(.22,.61,.36,1) both;
  transition: color .3s ease, border-color .3s ease, padding-left .3s ease;
}
@keyframes navItemIn {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* Jeda berurutan: item ke-N mulai ~60ms setelah item sebelumnya */
.nav-menu-list button:nth-child(1) { animation-delay: .08s; }
.nav-menu-list button:nth-child(2) { animation-delay: .14s; }
.nav-menu-list button:nth-child(3) { animation-delay: .2s; }
.nav-menu-list button:nth-child(4) { animation-delay: .26s; }
.nav-menu-list button:nth-child(5) { animation-delay: .32s; }
.nav-menu-list button:nth-child(6) { animation-delay: .38s; }
.nav-menu-list button:nth-child(7) { animation-delay: .44s; }
/* Hover: teks emas menyala + garis bawah menguat + item "maju" halus ke kanan */
.nav-menu-list button:hover {
  color: var(--color-gold-soft);
  border-bottom-color: var(--color-gold);
  padding-left: .6rem;
}

/* Kanvas QR check-in tamu — kotak putih supaya kontras QR-nya jelas saat
   dipindai petugas (QR itu di atas panel gelap). */
.qr-container {
  background: #fff; border-radius: 12px; padding: 1rem;
  display: flex; align-items: center; justify-content: center;
}
.qr-modal__canvas-wrap { max-width: 250px; margin: 0 auto; }
.qr-modal__canvas-wrap canvas { display: block; width: 100%; height: auto; }
.qr-modal__hint { font-size: .72rem; opacity: .65; margin-top: 1rem; text-align: center; }

/* ---------- Gift ---------- */
.gift-panel { max-width: 480px; margin: 2rem auto 0; opacity: 0; animation: fadeUp .4s ease forwards; }
@keyframes fadeUp { from { opacity: 0; transform: translateY(12px);} to { opacity: 1; transform: translateY(0);} }
.gift-accounts { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1.5rem; }
.gift-account {
  border: 1px solid var(--color-gold); border-radius: 10px; padding: 1rem 1.25rem;
  display: flex; justify-content: space-between; align-items: center; gap: 1rem; text-align: left;
}
.gift-account__bank { font-family: var(--font-serif); font-size: 1.1rem; color: var(--color-gold-soft); }
.gift-account__number { font-size: .95rem; letter-spacing: .05em; }
.gift-account__holder { font-size: .75rem; opacity: .75; }
.gift-copy-btn {
  border: 1px solid var(--color-gold); background: none; color: var(--color-gold-soft);
  border-radius: 999px; padding: .4rem .9rem; font-size: .7rem; cursor: pointer; white-space: nowrap;
}
.gift-address {
  border: 1px dashed var(--color-gold); border-radius: 10px; padding: 1rem 1.25rem;
  text-align: left; font-size: .88rem; line-height: 1.6;
  display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem;
}
.gift-address__text { flex: 1; min-width: 0; }
.gift-address__copy { align-self: flex-end; flex: none; }
.gift-note { font-size: .7rem; opacity: .6; margin-top: .75rem; }

/* Amplop digital di section Tanda Kasih; tombol rekomendasi kado pindah ke
   dalam #gift-panel (alur: Kirim Hadiah -> panel termasuk tombol -> modal). */
.gift-actions { display: flex; flex-direction: column; align-items: center; gap: .8rem; }
/* Dua tombol panel (Rekomendasi & Konfirmasi) sama-sama selebar panel — lebar
   seragam mengikuti kartu rekening/alamat, tidak bergantung panjang teks. */
.gift-confirm-btn,
.gift-recs-btn { display: block; width: 100%; margin: 1.1rem auto 0; }

/* Rekomendasi kado (modal): kartu SEJAJAR yang bisa DIGESER KE SAMPING
   (flex row + overflow-x:auto), bukan grid yang wrap ke bawah. Foto
   dipasangkan by-index dengan entri giftRecommendations[] oleh gift.js.
   margin/padding -1.5rem/+1.5rem meniadakan padding panel (1.5rem) supaya
   kartu menyentuh tepi modal dan kartu pertama/terakhir tidak terpotong
   scrollbar horizontal emasnya. */
.gift-recs {
  display: flex; gap: .8rem;
  overflow-x: auto;
  margin: 0 -1.5rem;
  padding: 0 1.5rem .6rem;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--color-gold) transparent;
}
.gift-recs::-webkit-scrollbar { height: 6px; }
.gift-recs::-webkit-scrollbar-thumb { background: var(--color-gold); border-radius: 3px; }
.gift-rec-card {
  flex: 0 0 auto; width: 165px;
  scroll-snap-align: start;
  border: 1px solid var(--color-gold); border-radius: 12px; overflow: hidden;
  display: flex; flex-direction: column; text-align: left;
}
.gift-rec-card__img { aspect-ratio: 1; background: var(--color-dark); }
.gift-rec-card__img img { display: block; width: 100%; height: 100%; object-fit: cover; }
.gift-rec-card__placeholder {
  display: flex; align-items: center; justify-content: center;
  font-size: 2rem; height: 100%;
}
.gift-rec-card__body { padding: .7rem .8rem .85rem; display: flex; flex-direction: column; gap: .3rem; }
.gift-rec-card__name { font-family: var(--font-serif); font-size: .9rem; color: var(--color-gold-soft); }
.gift-rec-card__price { font-size: .78rem; opacity: .8; }
.gift-rec-card__body .btn-outline {
  margin-top: .4rem; padding: .5rem 1rem; font-size: .68rem; text-align: center;
}
.gift-rec-card__nobuy { font-size: .68rem; opacity: .4; letter-spacing: .12em; text-transform: uppercase; margin-top: .4rem; }

/* Tombol platform live streaming — ikon + label sejajar */
.livestream-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: .8rem; margin-top: 1.75rem; }
.livestream-actions .btn-outline { display: inline-flex; align-items: center; gap: .45rem; }
.livestream-actions svg { width: 15px; height: 15px; }

/* ---------- RSVP ---------- */
.rsvp-form {
  max-width: 420px; margin: 0 auto 3rem;
  display: flex; flex-direction: column; gap: 1.1rem; text-align: left;
}
.rsvp-form label { display: flex; flex-direction: column; gap: .35rem; font-size: .8rem; letter-spacing: .05em; }
.rsvp-form input, .rsvp-form select, .rsvp-form textarea {
  font-family: var(--font-sans); font-size: .95rem;
  border: none; border-bottom: 1.5px solid var(--color-gold);
  background: transparent; padding: .5rem .1rem; color: inherit;
}
.rsvp-form input:focus, .rsvp-form select:focus, .rsvp-form textarea:focus { outline: none; border-color: var(--color-dark); }

/* Tombol pilihan kehadiran capsule: Hadir full-width, Tidak Hadir & Ragu setengah */
.rsvp-attendance {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .5rem;
  margin-top: .15rem;
}
.rsvp-pill {
  grid-column: span 1;
  padding: .7rem 1rem;
  border: 1px solid var(--color-gold);
  border-radius: 999px; /* capsule */
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: .9rem;
  cursor: pointer;
  transition: background .2s ease, color .2s ease;
}
.rsvp-pill--full { grid-column: 1 / -1; } /* Hadir: lebar penuh */
.rsvp-pill.is-selected {
  background: var(--color-gold);
  color: var(--color-dark);
  font-weight: 600;
}
.rsvp-status { text-align: center; font-size: .85rem; min-height: 1.2em; }

.wishes-list {
  /* overflow-x: hidden mengurung geseran masuk kartu ucapan (slide kiri/kanan)
     supaya tidak memunculkan scrollbar horizontal di dalam daftar ini. */
  max-width: 480px; margin: 0 auto; max-height: 340px; overflow-y: auto; overflow-x: hidden;
  display: flex; flex-direction: column; gap: 1rem; text-align: left;
}
.wish-card { border-bottom: 1px solid #e3dcc9; padding-bottom: .9rem; }
.wish-card__name { font-weight: 600; font-size: .9rem; }
.wish-card__status {
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-weight: 600;
  margin-left: .5rem;
  padding: .18rem .55rem;
  border-radius: 999px;
}
/* Badge warna berbeda per status kehadiran */
.wish-card__status--hadir { background: rgba(61, 132, 89, .16); color: #3d8459; }
.wish-card__status--tidak_hadir { background: rgba(176, 68, 60, .15); color: #b0443c; }
.wish-card__status--ragu { background: rgba(190, 133, 51, .18); color: #b6852f; }
.wish-card__message { font-size: .88rem; line-height: 1.55; margin: .3rem 0 0; opacity: .9; }

/* ---------- Share / QR ---------- */
.share-box { display: flex; flex-direction: column; align-items: center; gap: 1.5rem; }
.qr-container { background: #fff; padding: .75rem; border-radius: 10px; }
.share-actions { display: flex; gap: .75rem; flex-wrap: wrap; justify-content: center; }

/* ---------- Closing ---------- */
.closing-text { line-height: 1.8; font-size: 1rem; margin-bottom: 1.5rem; }

/* Footer punya background sendiri — memakai overlay gelap yang SAMA dengan
   overlay slideshow (var --overlay-closing) agar warnanya seragam, tanpa
   menampilkan foto slideshow di belakangnya. */
.site-footer {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 4.5rem;
  display: flex; align-items: center; justify-content: center;
  text-align: center; padding: 0 1rem;
  font-size: .75rem; opacity: .6;
  background: var(--overlay-closing);
  color: var(--color-text-light);
  z-index: 3; /* di atas overlay (z-index 2) dan foto slideshow (z-index 1) */
}
/* Slideshow full selayar: foto menutupi seluruh section, footer tampil
   di atasnya dengan background overlay gelap yang sama.
   100vh (bukan 100dvh) supaya di HP saat discroll penuh — bar browser sudah
   mengecil — foto tetap pas menutupi layar tanpa sisa piksel. */
#closing { min-height: 100vh; }
#closing .hero-media { inset: 0; }
/* Teks penutup ditempatkan di BAWAH section, bukan di tengah — foto slideshow
   punya wajah di tengah layar yang tidak boleh tertutup teks. .section-hero
   default flex center; #closing menimpa ke flex-end (tetap center horizontal
   via text-align di parent). */
#closing { align-items: flex-end; }
/* Teks penutup di dasar section tidak bertabrakan dengan footer di bawahnya */
#closing .hero-content { padding-bottom: 5.5rem; }

/* ---------- Toast ---------- */
.toast {
  position: fixed; left: 50%; bottom: 1.5rem; transform: translate(-50%, 20px);
  background: var(--color-dark); color: var(--color-text-light);
  padding: .65rem 1.2rem; border-radius: 999px; font-size: .8rem;
  opacity: 0; pointer-events: none; transition: opacity .3s ease, transform .3s ease;
  z-index: 1000; border: 1px solid var(--color-gold);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* Tone overlay global — menyatukan warna semua foto (beda sesi pemotretan)
   ke satu nuansa hangat konsisten, tanpa menurunkan opacity elemen mana pun.
   Dipindah dari body::after ke .app-frame::after: overlay kini hanya menutupi
   kolom undangan (area polos kiri-kanan di desktop tidak ikut di-warm-up),
   dan #preloader (z-index 9999, di dalam kolom) tampil di atasnya. */
.app-frame::after {
  content: "";
  position: fixed; inset: 0;
  background: var(--color-gold);
  mix-blend-mode: soft-light;
  opacity: .4;
  pointer-events: none;
  z-index: 9990;
}

@media (prefers-reduced-motion: reduce) {
  .kenburns, .audio-fab__disc, .preloader-ring { animation: none !important; }
  /* reveal.js sudah menandai semuanya .is-revealed saat reduced-motion; di sini
     cukup matikan transisinya supaya benar-benar tanpa gerak. Transform sengaja
     tidak dipaksa `none` — itu akan menghapus rotasi .event-label. */
  .reveal-ready [data-reveal] {
    opacity: 1 !important;
    transition: none !important;
  }
  /* Item menu navigasi: tanpa animasi stagger, langsung tampil semua */
  .nav-menu-list button {
    animation: none !important;
    opacity: 1;
  }
}

/* ---------- We Found Love (slider foto 1:1 + ayat quran kecil) ---------- */
#we-found-love.section {
  padding-top: 2.5rem;
  padding-bottom: 1.5rem; /* ruang setelah ayat — padding, bukan margin */
}
#we-found-love .section-title--script {
  margin-bottom: 1.25rem;
  font-size: clamp(2rem, 6vw, 2.6rem);
}
.wfl-slider {
  overflow: hidden;
  padding-bottom: 1.5rem; /* ruang pagination */
  /* Aliran pita harus berkecepatan RATA. Dengan easing bawaan Swiper, tiap foto
     melambat di ujung perpindahannya dan hanyutan yang seharusnya mulus jadi
     tersendat berdenyut. Variabel ini disediakan Swiper untuk menimpa timing
     function .swiper-wrapper. Berpasangan dengan autoplay delay:0 di
     we-found-love.js — keduanya harus ada, satu saja tidak cukup. */
  --swiper-wrapper-transition-timing-function: linear;
}
.wfl-slider .swiper-wrapper { align-items: center; }
.wfl-slide {
  aspect-ratio: 1 / 1;
  border-radius: 12px;
  overflow: hidden;
}
.wfl-slide picture,
.wfl-slide img { width: 100%; height: 100%; display: block; }
.wfl-slide img { object-fit: cover; }
.wfl-slider .swiper-pagination { bottom: .25rem; }
.wfl-slider .swiper-pagination-bullet {
  background: var(--color-gold);
  opacity: .35;
}
.wfl-slider .swiper-pagination-bullet-active { opacity: 1; }

/* Ayat quran font sangat kecil, di bawah slider (sesuai referensi) */
.wfl-quote { margin-top: 1rem; }
.wfl-quote .arabic-quote,
.wfl-quote .opening-quote,
.wfl-quote .opening-source {
  font-size: 10px;
  line-height: 1.8;
  margin: 0;
}
.wfl-quote .opening-quote {
  color: var(--color-text-light);
  opacity: .85;
  font-size: 10px;
}
.wfl-quote .opening-source {
  margin-top: .25rem;
  letter-spacing: .15em;
  opacity: .6;
}

/* ---------- Kedua Mempelai (slideshow besar 3/4 layar + info overlay) ---------- */
/* Full-width, tanpa rounded, rapat ke ayat quran di atasnya (tanpa heading). */
#couple.section {
  padding: 0 0 1.5rem;
}
.couple-block { margin-bottom: 0; }
.couple-block:last-child { margin-bottom: 0; }
/* Pembungkus yang meluncur masuk dari luar layar. Sengaja terpisah dari
   .couple-slider (root Swiper) supaya Swiper tidak mengukur slide dalam keadaan
   ter-transform, dan terpisah dari .couple-block supaya .couple-block bisa diam
   di tempat sebagai pemicu IntersectionObserver-nya. */
.couple-reveal { position: relative; }
.couple-slider {
  position: relative;
  height: 75vh;
  max-height: 720px;
  overflow: hidden;
}
.couple-slider .swiper-wrapper,
.couple-slider .swiper-slide { height: 100%; }
.couple-slide picture,
.couple-slide img { width: 100%; height: 100%; display: block; }
.couple-slide img { object-fit: cover; }

/* Blur-fade band (resep standar): gelap pekat di bottom, makin ke atas makin
   blur & transparan, ujung atas tidak bertekstur garis (mask arah sama). */
.couple-fade {
  position: absolute;
  left: -2px; right: -2px; bottom: -2px;
  height: 55%;
  background: linear-gradient(180deg, transparent 0%, var(--color-dark) 85%);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  mask-image: linear-gradient(180deg, transparent 0%, black 70%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, black 70%);
  pointer-events: none;
  z-index: 2;
}

.couple-info {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 2rem 1.5rem 1.25rem;
  text-align: center;
  z-index: 3;
}
.couple-info__label {
  font-family: var(--font-label);
  text-transform: uppercase;
  letter-spacing: .35em;
  font-size: .65rem;
  color: var(--color-gold);
  margin: 0 0 .35rem;
}
.couple-info__name {
  font-family: var(--font-script);
  font-size: clamp(2.2rem, 8vw, 3rem);
  line-height: 1.1;
  color: #ffffff;
  font-weight: 400;
  margin: 0 0 .25rem;
}
.couple-info__parents {
  font-size: .75rem;
  line-height: 1.6;
  color: var(--color-text-light);
  opacity: .85;
  margin: 0;
}

/* ---------- Kartu Event (portrait rounded: slider pasangan + kanvas info) ---------- */
/* Hilangkan padding kiri-kanan section supaya kartu selebar layar, dan padding bawah
   supaya foto quote langsung menyambung di bawah kartu dresscode. */
#event.section { padding-left: 0; padding-right: 0; padding-bottom: 0; }
.event-card {
  width: min(96%, 720px); /* sangat lebar, sisakan gap kecil kiri-kanan */
  margin: 0 auto 2.5rem;
  height: 90vh; /* 9/10 layar */
  background: #ffffff;
  border-radius: 24px;
  overflow: hidden; /* foto tetap terpotong mengikuti sudut rounded */
  display: flex;
  flex-direction: column;
  padding: 0; /* override padding CSS lama agar slider full-bleed ke kiri-atas */
  box-shadow: 0 24px 60px rgba(0, 0, 0, .18);
}
.event-card:last-child { margin-bottom: 0; }

/* ---------- Kartu Dresscode (kartu kecil 1/4 layar, border sama dengan event card) ---------- */
.dresscode-card {
  width: min(96%, 720px);
  margin: 0 auto 2.5rem; /* jarak normal ke foto quote di bawah */
  height: 25vh; /* 1/4 layar */
  background: #ffffff;
  border-radius: 24px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .18);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  padding: 1rem;
}
.dresscode-label {
  font-family: var(--font-label);
  font-size: clamp(1.1rem, 4.5vw, 1.6rem);
  font-weight: 700;
  letter-spacing: .35em;
  text-transform: uppercase;
  color: var(--color-gold);
  margin: 0;
}
.dresscode-text {
  font-size: .8rem;
  line-height: 1.6;
  color: var(--color-text);
  opacity: .8;
  max-width: 90%;
  margin: 0;
}
.dresscode-swatches {
  display: flex;
  gap: .65rem;
  margin-top: .35rem;
}
.dresscode-swatch {
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .2), inset 0 0 0 2px #fff;
}

/* ---------- Foto Quote (full-width 1:1, tanpa rounded, overlay golden hour gelap) ---------- */
.quote-section {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1; /* persegi: 1:1 terhadap lebar layar */
  overflow: hidden;
  background: var(--color-dark);
}
.quote-section picture,
.quote-section img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}
.quote-overlay {
  position: absolute;
  inset: 0;
  /* layer golden hour agak gelap agar teks putih tetap terbaca */
  background: linear-gradient(180deg, rgba(48, 32, 13, .55) 0%, rgba(20, 13, 5, .62) 100%);
}
.quote-text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 2rem;
  text-align: center;
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(1.15rem, 4.6vw, 1.9rem);
  line-height: 1.7;
  color: #ffffff;
  text-shadow: 0 2px 14px rgba(0, 0, 0, .55);
}

/* 4/10 atas kartu: slider foto pasangan, penuh menyentuh tepi kartu */
.event-card__slider {
  height: 40%;
  flex: none;
  overflow: hidden;
  width: 100%;
}
.event-card__slider .swiper-wrapper,
.event-card__slider .swiper-slide { height: 100%; }
.event-slide picture,
.event-slide img { width: 100%; height: 100%; display: block; }
.event-slide img { object-fit: cover; }

/* 6/10 bawah kartu: kanvas bersih info acara, 2 kolom — 1/4 label acara, 3/4 detail */
.event-card__info {
  flex: 0 0 60%; /* 6/10 dari tinggi kartu, bukan dari layar */
  display: grid;
  /* minmax(0, …) agar kolom label tidak ikut melebar saat tulisannya diperbesar —
     label tetap di 1/4, detail tetap di 3/4 tanpa bergeser */
  grid-template-columns: minmax(0, 1fr) minmax(0, 3fr);
  color: var(--color-text);
  background: #ffffff;
  padding: 1.5rem .75rem 1.5rem 0;
  min-height: 0;
}

/* Kolom 1 (1/4): label acara vertikal, center horizontal & vertikal */
.event-label {
  font-family: var(--font-label);
  font-size: clamp(1.35rem, 5vw, 2.2rem); /* label acara dibesarkan */
  min-width: 0; /* jangan paksa kolom label melebar karena teks nowrap */
  font-weight: 700;
  letter-spacing: .35em;
  text-transform: uppercase;
  color: var(--color-gold);
  white-space: nowrap;
  transform: rotate(-90deg);
  transform-origin: center;
  align-self: center;
  justify-self: center;
  margin: 0;
}

/* Kolom 2: tanggal di atas, lokasi di bawahnya (digabung) */
.event-detail {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.1rem;
  min-width: 0;
  padding-right: .5rem;
  padding-left: .9rem; /* ruang agar garis pemisah tidak menempel teks */
  position: relative;
}
/* Garis pemisah vertikal antara kolom label acara dan kolom detail */
.event-detail::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1rem;
  bottom: 1rem;
  border-left: 1px solid rgba(201, 166, 104, .4);
}
.event-date {
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .3rem;
}
.event-day { font-size: .72rem; letter-spacing: .2em; text-transform: uppercase; opacity: .7; margin: 0; }
.event-divider {
  width: 2.5rem;
  border: none;
  border-top: 1px solid var(--color-gold);
  margin: .35rem auto;
}
.event-num {
  font-family: var(--font-script);
  font-size: clamp(3rem, 12vw, 4.2rem);
  line-height: 1;
  color: var(--color-text);
  margin: 0;
}
.event-month { font-size: .72rem; letter-spacing: .18em; text-transform: uppercase; opacity: .7; margin: 0; }
/* Tahun di kartu event — dipisah dari bulan supaya bisa animasi count-up
   sendiri (data-count di index.html); sedikit lebih menonjol dari bulan. */
.event-year {
  font-size: .9rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--color-gold-soft);
  margin: .15rem 0 0;
}
.event-time { font-size: 1.05rem; font-weight: 600; color: var(--color-gold); margin: .25rem 0 0; letter-spacing: .08em; }

/* Lokasi di bawah tanggal, dipisah garis tipis */
.event-place {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(201, 166, 104, .35);
}
.event-place h3 { font-size: .95rem; margin: 0 0 .3rem; line-height: 1.3; }
.event-place p { font-size: .7rem; line-height: 1.55; opacity: .75; margin: 0 0 .9rem; }
.btn-maps {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .45rem .8rem;
  border: 1px solid var(--color-gold);
  border-radius: 10px;
  color: var(--color-text);
  font-size: .7rem;
  letter-spacing: .06em;
  text-decoration: none;
  transition: background .3s ease, color .3s ease;
}
.btn-maps svg { width: .9rem; height: .9rem; color: var(--color-gold); }
.btn-maps:hover { background: var(--color-gold); color: #ffffff; }
.btn-maps:hover svg { color: #ffffff; }
