/*
 * Player tamu Undangin. HTML-first: tanpa JS semua bagian tampil sebagai
 * dokumen biasa. Kelas `enhanced` di <html> dipasang skrip setelah berhasil
 * jalan; hanya di bawahnya sampul menjadi layar penuh dan kartu memudar masuk.
 */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  background: var(--page);
}

body {
  margin: 0;
  color: var(--ink);
  background: var(--page);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  overflow-wrap: anywhere;
}

img {
  max-width: 100%;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.2;
  margin: 0 0 0.5em;
}

p {
  margin: 0 0 0.75em;
}

a {
  color: var(--accent);
}

.visually-hidden,
.person__role {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.js-only {
  display: none !important;
}

.enhanced .js-only {
  display: revert !important;
}

.enhanced .js-only[hidden] {
  display: none !important;
}

.skip-link {
  position: absolute;
  left: 8px;
  top: -60px;
  z-index: 100;
  padding: 8px 12px;
  background: var(--card-solid);
  color: var(--ink);
}

.skip-link:focus {
  top: 8px;
}

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- Kolom undangan ---------- */

main {
  position: relative;
  max-width: 600px;
  margin: 0 auto;
  overflow: clip;
  background: #000;
}

.scene {
  position: relative;
  min-height: 100svh;
}

/* Latar menempel selama bagiannya di-scroll, lalu ikut tergulung pergi. */
.stage {
  position: sticky;
  top: 0;
  height: 100lvh;
  margin-bottom: -100lvh;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
}

/* Kanvas 2:3 menutupi layar (cover); HP hanya melihat ±64% lebar tengah. */
.canvas {
  position: absolute;
  left: 50%;
  top: 50%;
  width: max(100%, 66.667lvh);
  aspect-ratio: 2 / 3;
  transform: translate(-50%, -50%);
}

.canvas > picture,
.canvas > .layer {
  position: absolute;
  inset: 0;
}

.canvas picture img,
.layer img {
  display: block;
  width: 100%;
  height: 100%;
}

.layer {
  /* z-index diisi skrip per lapisan (10, 12, 14, …); foto s2 tepat di belakang bingkai. */
  will-change: auto;
}

.scene.is-active .layer {
  will-change: transform;
}

/* `.layer` (scroll) > `.layer__fly` (menembus bingkai) > `.layer__dolly` (kamera) > picture (idle). */
.layer__fly,
.layer__dolly,
.layer__dolly > picture {
  position: absolute;
  inset: 0;
  display: block;
}

.layer img {
  position: absolute;
}

.klasik--front {
  z-index: 2;
}

/* Pendar lampu (Sinematik penuh); ukuran relatif lebar kanvas. */
.glow {
  position: absolute;
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  pointer-events: none;
  mix-blend-mode: screen;
  background: radial-gradient(
    circle,
    rgba(255, 228, 165, 0.8) 0%,
    rgba(255, 200, 120, 0.32) 30%,
    rgba(255, 180, 100, 0) 65%
  );
}

/* Langit Penutup meredup ke malam seiring scroll. */
.dusk {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(18, 22, 58, 0.62) 0%,
    rgba(48, 30, 70, 0.32) 45%,
    rgba(48, 30, 70, 0) 75%
  );
}

/* Penutup menapak: hanya di Sinematik (Klasik tidak punya adegan tanah). */
.penutup-rig {
  display: none;
}

.stages-fixed .penutup-rig {
  display: block;
}

/* Rig seukuran kanvas berisi foto pasangan; di Klasik berada di antara back & front. */
.photo-rig {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.photo {
  position: absolute;
  overflow: hidden;
  background: #d9d2c5;
}

.photo__img,
.photo__img img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * Foto yang dipotong pasangan (`Photo.crop`, berkas asli utuh): hanya bagian potongan yang menutupi kotak,
 * seperti `object-fit: cover` atas potongannya. Ukuran kotak dibaca lewat container query; browser lama
 * jatuh ke `object-fit: cover` biasa dengan `object-position` di titik fokus dalam potongan (gaya inline).
 */
@supports (container-type: size) {
  .photo__img--crop {
    container-type: size;
    position: relative;
    overflow: hidden;
  }

  .photo__img.photo__img--crop img {
    --bw: max(100cqw, 100cqh * var(--car));
    --bh: calc(var(--bw) / var(--car));
    position: absolute;
    max-width: none;
    max-height: none;
    width: calc(var(--bw) / var(--cw));
    height: calc(var(--bh) / var(--ch));
    left: calc((100cqw - var(--bw)) * var(--fx) - var(--bw) * var(--cx) / var(--cw));
    top: calc((100cqh - var(--bh)) * var(--fy) - var(--bh) * var(--cy) / var(--ch));
    object-fit: fill;
  }
}

/* Foto tambahan mempelai: bertumpuk di jendela yang sama; Sinematik menggilirnya saat disorot. */
.photo__img--more {
  position: absolute;
  inset: 0;
  opacity: 0;
}

/*
 * Foto tanpa latar (tanpa bingkai): utuh, menapak di dasar kotaknya, dengan
 * bayangan lembut supaya tampak berdiri di dalam adegan.
 */
.photo--cutout {
  overflow: visible;
  background: none;
}

.photo__img--whole img {
  object-fit: contain;
  object-position: 50% 100%;
  /* Foto setengah badan terpotong datar di bawah: tepinya dipudarkan supaya tidak tampak melayang. */
  -webkit-mask-image: linear-gradient(to bottom, #000 84%, transparent);
  mask-image: linear-gradient(to bottom, #000 84%, transparent);
}

/* Seluruh badan (kaki terlihat, berdiri di tanah): tidak ada potongan datar yang perlu dipudarkan. */
.photo__img--whole.photo__img--ground img {
  -webkit-mask-image: none;
  mask-image: none;
}

/*
 * Foto tanpa latar yang diselaraskan dengan cahaya adegan (`render/grade.ts`; `--m` = foto sendiri
 * sebagai topeng). Ukuran bayangan mengikuti tinggi kotak foto (`cqh`), jadi kotaknya jadi kontainer.
 * ::before = bayangan di belakang kaki, hanya foto seluruh badan (`--ground`, `Photo.body === 'utuh'`;
 * kontak + jatuh ke sisi yang berlawanan dengan cahaya);
 * ::after = tarikan warna adegan di atas foto, hanya di dalam siluet. Tanpa `filter`: aman di HP.
 */
.photo--cutout,
.framed--cutout .framed__photo {
  container-type: size;
}

.photo__img--graded:not(.photo__img--more) {
  position: relative;
  isolation: isolate;
}

.photo__img--ground::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(
      ellipse 17cqh 2.4cqh at var(--f-x, 50%) calc(var(--f-y, 100%) - 1.1cqh),
      rgba(20, 12, 6, 0.55),
      rgba(20, 12, 6, 0) 72%
    ),
    radial-gradient(
      ellipse 30cqh 2.2cqh at calc(var(--f-x, 50%) + var(--g-cast, 6%)) calc(var(--f-y, 100%) - 1.1cqh),
      rgba(20, 12, 6, 0.3),
      rgba(20, 12, 6, 0) 75%
    );
}

.photo__img--graded::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(var(--g-dir, to right), var(--g-bright), var(--g-avg) 85%);
  mix-blend-mode: soft-light;
  opacity: 0.5;
  -webkit-mask: var(--m) 50% 100% / contain no-repeat;
  mask: var(--m) 50% 100% / contain no-repeat;
}

/* Saat lapisan Sinematik siap, komposit Klasik disembunyikan. */
.stage.has-layers > .canvas > .klasik {
  visibility: hidden;
}

/*
 * Gaya Sinematik: bila lapisan terlambat (jaringan lambat), komposit Klasik tampil dulu
 * (jeda.ts `HOLD_MAX`); lapisan yang menyusul masuk lewat crossfade, tanpa letupan.
 */
html[data-gaya] .stage.has-layers > .canvas > .klasik {
  opacity: 0;
  /* Hanya saat lapisan masuk (keadaan tujuan); panggung yang baru tampil langsung menampilkan kompositnya. */
  transition:
    opacity 0.45s ease,
    visibility 0s linear 0.45s;
}

.scene__content {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  min-height: 100svh;
  padding: max(24px, calc(var(--zone-top, 10) * 1svh)) 16px max(96px, calc(var(--zone-bottom, 10) * 1svh));
}

.scene--mempelai .scene__content {
  justify-content: flex-start;
}

/* ---------- Momen adegan: satu layar adegan bersih sebelum kartu ---------- */

.scene--beat .scene__content {
  justify-content: flex-start;
  padding-top: 0;
}

.beat {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  min-height: 100svh;
  padding-top: max(24px, calc((var(--zone-top, 40) + var(--zone-h, 20) / 2) * 1svh - 2.4rem));
}

/*
 * Klasik (juga Sinematik yang jatuh ke tampilan Klasik): judul tetap di zona teks adegan, tetapi
 * kartunya langsung menyusul di bawahnya, bukan selayar kemudian (judul jelas milik kartunya).
 * Momen pembuka (nama mempelai) tetap selayar penuh.
 */
html:not(.stages-fixed) .scene--beat:not(.scene--pembuka) .beat {
  min-height: 0;
}

/* Klasik: judul bagian rapat dengan kartu/isi pertamanya (celah 10px, bukan 16px). */
html:not(.stages-fixed) .scene__content > .section-title + *,
html:not(.stages-fixed) .scene--beat:not(.scene--pembuka) .beat + * {
  margin-top: -6px;
}

.beat .section-title {
  font-size: 2.1rem;
  padding: 6px 30px;
}

/* Scrim nama pembuka: inti pekat cukup lebar sampai "Undangan Pernikahan" & tanggal di tepinya. */
.beat__names {
  margin-top: -2.5rem;
  padding: 40px 48px;
  text-align: center;
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--card-solid) 90%, transparent) 55%,
    color-mix(in srgb, var(--card-solid) 62%, transparent) 76%,
    color-mix(in srgb, var(--card-solid) 22%, transparent) 92%,
    transparent
  );
}

.enhanced .beat__names {
  opacity: 0;
  transition: opacity 1.2s ease;
}

.enhanced.is-opened .beat__names {
  opacity: 1;
}

.beat__names .couple-title {
  font-size: clamp(2.3rem, 11vw, 3.6rem);
  margin: 0.1em 0;
}

/*
 * "Undangan Pernikahan" & tanggal: huruf kecil/tipis yang mudah tenggelam di adegan ramai, jadi
 * lebih tebal dan diberi pendar warna kartu (gelap di preset gelap, terang di preset terang).
 */
.beat__names .eyebrow,
.beat__date {
  color: var(--ink);
  font-weight: 600;
  text-shadow:
    0 0 3px var(--card-solid),
    0 0 10px var(--card-solid),
    0 0 18px color-mix(in srgb, var(--card-solid) 70%, transparent);
}

.beat__date {
  margin: 0;
}

.enhanced .beat__line {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 1s ease,
    transform 1s ease;
}

.enhanced.is-opened .beat__line {
  opacity: 1;
  transform: none;
}

.enhanced.is-opened .beat__couple {
  transition-delay: 0.35s;
}

.enhanced.is-opened .beat__date {
  transition-delay: 0.7s;
}

/* Lambang & garis ornamen hanya untuk urutan judul Sinematik. */
.beat__emblem,
.beat__divider {
  display: none;
}

.beat__emblem img,
.beat__divider img {
  display: block;
  width: 100%;
  height: auto;
}

/*
 * Sinematik: semua panggung menjadi fixed di belakang konten supaya adegan
 * bisa berganti dengan transisi (skrip mengatur opacity, skala, visibility).
 * Panggung yang jauh `display: none` agar gambarnya belum diunduh.
 */
.stages-fixed main > .scene:not(.cover) > .stage {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  max-width: 600px;
  height: 100lvh;
  margin: 0 auto;
  visibility: hidden;
}

.stages-fixed main > .scene:not(.cover) > .stage:not(.is-near) {
  display: none;
}

/* Jangan tampilkan panggung (mis. foto pasangan saja) sebelum latarnya siap. */
.stages-fixed main > .scene:not(.cover) > .stage:not(.is-ready) {
  visibility: hidden !important;
}

/* ---------- Sinematik: pembuka seperti film ---------- */

/* Bar hitam atas-bawah selama pembuka; di bawah dock & kontrol supaya tetap bisa diketuk. */
.letterbox {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 28;
  width: min(100vw, 600px);
  height: max(40px, 6.5svh);
  margin: 0 auto;
  background: #050505;
  transform: scaleY(0);
  pointer-events: none;
}

.letterbox--top {
  top: 0;
  transform-origin: 50% 0;
}

.letterbox--bottom {
  bottom: 0;
  transform-origin: 50% 100%;
}

/* Lapisan warna (redup & cahaya hangat pembuka, cahaya tiap bagian, redup penutup): di atas
   kanvas, di bawah konten. Wadah tanpa z-index supaya blend "screen" tetap mengenai adegan. */
.grades,
.grade {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

html[data-level='penuh'] .grade--warm {
  mix-blend-mode: screen;
}

/* HP: blend "screen" memaksa menggambar ulang seluruh adegan di bawahnya tiap frame; alfa biasa cukup. */
@media (hover: none) {
  html[data-level='penuh'] .grade--warm,
  html[data-level='penuh'] .glow {
    mix-blend-mode: normal;
  }
}

.glows {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Urutan judul digerakkan skrip, jadi tanpa transisi CSS supaya tidak saling tarik. */
.stages-fixed .beat__names,
.stages-fixed .beat__line {
  transition: none;
}

/* Judul pembuka di bagian atas layar (langit/dinding), bukan di atas gazebo/tangga;
   jaraknya per tema (`--title-top` dari direction.ts). */
.stages-fixed .scene--pembuka .beat {
  padding-top: max(24px, var(--title-top, 17svh));
}

.stages-fixed .beat__names {
  margin-top: 0;
  padding: 44px clamp(18px, 7vw, 52px);
  text-shadow: 0 0 14px color-mix(in srgb, var(--card-solid) 85%, transparent);
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--card-solid) 78%, transparent) 45%,
    color-mix(in srgb, var(--card-solid) 48%, transparent) 80%,
    transparent
  );
}

.stages-fixed .beat__emblem {
  display: block;
  width: 44px;
  margin: 0 auto 10px;
}

.stages-fixed .beat__divider {
  display: block;
  width: min(190px, 56vw);
  margin: 8px auto 10px;
}

.stages-fixed .beat__names .eyebrow {
  white-space: nowrap;
}

/* Kata nama tetap inline (spasi tak terputus tetap berlaku); selama urutan judul naik lewat
   `top`. Sesudahnya kembali statis supaya kilau (background-clip: text) mencakup semua kata. */
.stages-fixed:not(.titles-done) .beat__word {
  position: relative;
}

.stages-fixed .beat__names .couple-title {
  text-shadow: 0 0 18px color-mix(in srgb, var(--card-solid) 80%, transparent);
}

/* Kilau nama berhenti selama urutan judul (kata-katanya bergerak sendiri), lalu jalan lagi. */
.stages-fixed:not(.titles-done) .beat__names .couple-title {
  background: none;
  color: var(--accent);
  animation: none;
}

/* Judul bagian ala judul film: tanpa pil, scrim lembut, garis ornamen di bawahnya. */
.stages-fixed .section-title {
  padding: 18px 40px 16px;
  border-radius: 0;
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--card-solid) 80%, transparent) 45%,
    transparent
  );
  text-shadow: 0 0 16px color-mix(in srgb, var(--card-solid) 70%, transparent);
}

.stages-fixed .section-title::after {
  content: var(--title-divider-content, none);
  display: block;
  width: min(170px, 60vw);
  height: 22px;
  margin: 8px auto 0;
  background: var(--title-divider) center / contain no-repeat;
}

.stages-fixed .beat .section-title {
  font-size: clamp(2.1rem, 9.5vw, 2.8rem);
  padding: 26px 48px 22px;
}

/* Muncul memudar sambil mengecil ke ukurannya (transform saja, tata letak tidak bergeser). */
.enhanced.stages-fixed .scene:not(.cover) .section-title {
  transform: scale(1.08);
  transition:
    opacity 1.1s ease,
    transform 1.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.enhanced.stages-fixed .scene.is-seen .section-title {
  transform: none;
}

/* ---------- Sinematik: adegan terarah (Mempelai, Kisah) ---------- */

/* Kartu Mempelai & daftar Kisah jadi rangkaian adegan setinggi layar. */
.stages-fixed .scene--directed .couple,
.stages-fixed .scene--directed .story {
  display: block;
  width: 100%;
  max-width: none;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

.stages-fixed .scene--directed .couple > .corner {
  display: none;
}

.stages-fixed .scene--directed .shot,
.stages-fixed .scene--directed .story > li {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  /* Lebih panjang bila ada beberapa foto yang bergiliran (`--shot-len`, dari render). */
  min-height: calc(100svh * var(--shot-len, 1));
}

/* Keterangan bawah (lower third): menempel di atas dock selama adegannya. */
.stages-fixed .scene--directed .shot > *,
.stages-fixed .scene--directed .story__chapter {
  position: sticky;
  bottom: calc(74px + env(safe-area-inset-bottom));
  width: auto;
  margin: 0 -16px;
  padding: 76px 28px 14px;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  text-align: center;
  background: linear-gradient(
    to bottom,
    transparent,
    color-mix(in srgb, var(--card-solid) 80%, transparent) 40%,
    color-mix(in srgb, var(--card-solid) 94%, transparent)
  );
}

/* Teater: lambrekin menutupi ±56px teratas layar; kartu keterangan menapak mulai di bawahnya. */
html[data-gaya='teater'] {
  --kartu-atas: 64px;
}

/* Foto menapak (`.photo--stand`): kaki berdiri di tanah adegan, yang letaknya di separuh bawah layar, jadi
   keterangan pindah ke ATAS (kamera menurunkan pasangan: `coupleCams`). Kartu tidak pernah menimpa kaki. */
.stages-fixed .scene--directed:has(.photo--stand) .shot {
  justify-content: flex-start;
}

.stages-fixed .scene--directed:has(.photo--stand) .shot > *,
/* Gaya Buku/Lukisan/Teater menulis `margin` singkatan di aturan yang lebih spesifik: tanpa selektor ini
   `margin-top` negatif di bawah kalah, kartu tertinggal di 30% layar dan menutup kepala pasangan. */
html[data-gaya].stages-fixed .scene--directed:has(.photo--stand) .shot > * {
  top: calc(env(safe-area-inset-top) + var(--kartu-atas, 20px));
  bottom: auto;
  /* Kamera tiba saat atas adegan di 0,3 tinggi layar (`SHOT_ARRIVE`): kartu sudah di puncak layar saat itu. */
  margin-top: calc(-30svh + var(--kartu-atas, 20px));
}

html:not([data-gaya='buku'], [data-gaya='lukisan'], [data-gaya='teater']).stages-fixed
  .scene--directed:has(.photo--stand)
  .shot
  > * {
  padding: 14px 28px 70px;
  background: linear-gradient(
    to top,
    transparent,
    color-mix(in srgb, var(--card-solid) 80%, transparent) 40%,
    color-mix(in srgb, var(--card-solid) 94%, transparent)
  );
}

/*
 * Penutup menapak (`.penutup-rig`, usulan §8): pasangan berdiri di tanah adegan di separuh bawah layar, jadi
 * judul + kartu penutup pindah ke ATAS, satu blok rapat (tanpa layar kosong di depannya), memakai
 * `--kartu-atas` seperti Mempelai/Kisah. Pasangan naik dari tanah setelah blok itu tiba (cinematic.ts), jadi
 * kartu yang masuk tidak menyapu wajah.
 */
.stages-fixed #penutup:has(.penutup-rig) .beat {
  min-height: 0;
  padding-top: calc(env(safe-area-inset-top) + var(--kartu-atas, 20px) - 14px);
}

/* Kartu pucat (scrim) harus menutup seluruh teks judul: tanpa ruang di sekitar judul, halo mengecil dan teks
   berwarna tema jatuh ke langit pucat (kontras kurang). Ruangnya dikembalikan lewat margin negatif. */
.stages-fixed #penutup:has(.penutup-rig) .beat .section-title {
  padding: 18px 44px 14px;
  margin-bottom: -10px;
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--card-solid) 92%, transparent) 55%,
    transparent
  );
}

.stages-fixed #penutup:has(.penutup-rig) .beat + .card {
  margin-top: 4px;
}

/*
 * Kisah menapak: foto seluruh badan babak (`.story__photo--stand`) berdiri di tanah adegan lewat rig kanvas
 * (`.story-rig`, hanya di adegan terarah); ubinnya di kartu tak terlihat. Babak itu (`.story__chapter--stand`)
 * memindah keterangan ke atas seperti Mempelai; foto berbingkai di babak yang sama tampil di bawah keterangan
 * (satu foto sekali tampil: foto menapak disapu turun dulu).
 */
.story-rig {
  display: none;
}

.stages-fixed .scene--directed .story-rig {
  display: block;
}

.stages-fixed .scene--directed .story__photo--stand {
  visibility: hidden;
  pointer-events: none;
}

.stages-fixed .scene--directed .story > li:has(.story__chapter--stand:not(.story__chapter--long)) {
  justify-content: flex-start;
}

.stages-fixed .scene--directed .story__chapter--stand:not(.story__chapter--long),
html[data-gaya].stages-fixed .scene--directed .story__chapter--stand:not(.story__chapter--long) {
  top: calc(env(safe-area-inset-top) + var(--kartu-atas, 20px));
  bottom: auto;
  margin-top: calc(-30svh + var(--kartu-atas, 20px));
}

.stages-fixed .scene--directed .story__chapter--stand:not(.story__chapter--long) .story__photos {
  top: calc(100% + 8px);
  bottom: auto;
}

html:not([data-gaya='buku'], [data-gaya='lukisan'], [data-gaya='teater']).stages-fixed
  .scene--directed
  .story__chapter--stand:not(.story__chapter--long) {
  padding: 14px 28px 70px;
  background: linear-gradient(
    to top,
    transparent,
    color-mix(in srgb, var(--card-solid) 80%, transparent) 40%,
    color-mix(in srgb, var(--card-solid) 94%, transparent)
  );
}

.stages-fixed .scene--directed .person {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.stages-fixed .scene--directed .person__nickname {
  font-size: clamp(2.4rem, 11vw, 3rem);
  line-height: 1.05;
}

/* Peran (mempelai wanita/pria) tampil sebagai label kecil di atas nama. */
.stages-fixed .scene--directed .person__role {
  position: static !important;
  order: -1;
  width: auto;
  height: auto;
  margin: 0 0 4px;
  overflow: visible;
  clip-path: none;
  white-space: normal;
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.stages-fixed .scene--directed .couple__amp {
  padding-top: 64px;
  font-size: 4.4rem;
  font-style: italic;
  line-height: 1;
}

.stages-fixed .scene--directed .couple__amp::after {
  content: attr(data-names);
  display: block;
  margin-top: 10px;
  font: 600 0.8rem/1.4 var(--font-body);
  font-style: normal;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Foto tambahan mempelai sudah bergiliran di bingkai adegan: deretan fotonya tidak perlu. */
.stages-fixed .scene--directed .thumbs:not(.thumbs--keep),
.stages-fixed .scene--directed .thumbs--keep > li:not(.thumbs__keep) {
  display: none;
}

/* Foto babak Kisah: satu foto berbingkai mengambang di atas keterangan, berganti mengikuti gulir. */
.stages-fixed .scene--directed .story__photos {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /*
   * Cerita panjang = kartu tinggi: foto mengecil (`--chapter-h`, dari cinematic.ts) supaya bingkai
   * fotonya tetap utuh di bawah bilah atas, tidak terdorong keluar layar.
   */
  width: min(
    60vw,
    240px,
    calc(36svh * 2 / 3),
    max(96px, calc((100svh - var(--chapter-h, 0px) - 74px - 84px) * 2 / 3))
  );
  margin: 0;
  transform: translateX(-50%);
}

.stages-fixed .scene--directed .story__photo {
  grid-area: 1 / 1;
  opacity: 0;
  transition: opacity 0.6s ease;
}

.stages-fixed .scene--directed .story__photo.is-shown {
  opacity: 1;
}

/*
 * Cerita panjang (cinematic.ts `story__chapter--long`): kartu terlalu tinggi untuk foto di atasnya,
 * jadi adegannya dua langkah. Langkah foto (`is-foto`): teks keluar → kertas kartu hilang → foto
 * besar masuk di bawah bilah atas. Langkah cerita: foto keluar → kertas & teks masuk. Tidak pernah
 * tumpang tindih (09 §10).
 */
html.stages-fixed .scene--directed .story > li > .story__chapter--long {
  transition:
    background-color 0s linear 0.3s,
    background-image 0s linear 0.3s,
    box-shadow 0s linear 0.3s;
  transition-behavior: allow-discrete;
}

html.stages-fixed .scene--directed .story > li > .story__chapter--long > :not(.story__photos) {
  transition: opacity 0.3s ease 0.3s;
}

html.stages-fixed .scene--directed .story > li > .story__chapter--long .story__photos {
  top: calc(84px - 100svh + 74px + var(--chapter-h, 0px));
  bottom: auto;
  row-gap: 8px;
  /* Sisakan tempat keterangan singkat di bawah foto (`.story__ringkas`, ±96 px). */
  width: min(70vw, 300px, calc((100svh - 74px - 84px - 24px - 96px) * 2 / 3));
  opacity: 0;
  transition: opacity 0.3s ease;
}

/*
 * Keterangan singkat langkah foto (cinematic.ts `addStoryBrief`): waktu + judul babak di secarik
 * kartu kecil tepat di bawah foto; ikut masuk & keluar bersama fotonya. Cerita lengkapnya baru
 * masuk di langkah berikutnya (foto keluar → cerita masuk).
 */
.story__ringkas {
  display: none;
}

html.stages-fixed .scene--directed .story__chapter--long .story__ringkas {
  display: block;
  grid-area: 2 / 1;
  justify-self: center;
  max-width: 100%;
  margin: 0;
  padding: 8px 18px 9px;
  border-radius: 6px;
  text-align: center;
  background: color-mix(in srgb, var(--card-solid) 94%, transparent);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.22);
}

.story__ringkas .eyebrow {
  margin: 0 0 2px;
  font-size: 0.72rem;
}

.story__ringkas-judul {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.2rem;
  line-height: 1.2;
  color: var(--ink);
}

html.stages-fixed .scene--directed .story > li > .story__chapter--long.is-foto {
  background-color: transparent;
  background-image: none;
  box-shadow: none;
}

html.stages-fixed .scene--directed .story > li > .story__chapter--long.is-foto > :not(.story__photos) {
  opacity: 0;
  transition-delay: 0s;
}

html.stages-fixed .scene--directed .story > li > .story__chapter--long.is-foto .story__photos {
  opacity: 1;
  transition-delay: 0.3s;
}

/* Pola B (bingkai di samping, sisi bergantian) dan C (tanggal jangkar) bab Kisah; usulan §5.2. */
.stages-fixed
  .scene--directed
  .story
  > li[data-pola='b'][data-sisi='kiri']
  .story__photos:not(:has(.framed--cutout)) {
  left: 34%;
}

.stages-fixed
  .scene--directed
  .story
  > li[data-pola='b'][data-sisi='kanan']
  .story__photos:not(:has(.framed--cutout)) {
  left: 66%;
}

.stages-fixed .scene--directed .story > li[data-pola='c'] .story__chapter .eyebrow {
  margin-bottom: 4px;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.32em;
  color: var(--accent, var(--ink));
}

.stages-fixed .scene--directed .story > li[data-pola='c'] .story__chapter h3 {
  font-size: 1.05rem;
}

/*
 * Ada foto tanpa latar di babak ini: foto lama hilang dulu, baru foto berikutnya
 * masuk (bukan memudar silang), supaya foto transparan tidak tampak bertumpuk.
 */
.stages-fixed .scene--directed .story__photos:has(.framed--cutout) > .story__photo {
  transition-duration: 0.3s;
}

.stages-fixed .scene--directed .story__photos:has(.framed--cutout) > .story__photo.is-shown {
  transition-delay: 0.3s;
}

/* ---------- Sinematik: layar penutup (gerbang menutup, judul akhir, "Putar ulang") ---------- */

/* Layar penutup jadi layar terakhir halaman: di akhir gulir kartunya tepat di tengah. */
.has-finale > .scene__content {
  padding-bottom: 0;
}

.finale {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 100svh;
}

/*
 * Judul akhir bisa jatuh di atas latar gelap & ramai (ukiran kayu, langit malam, peta malam):
 * scrim berwarna kartu yang pekat sampai ke ujung nama, plus halo rapat di tiap huruf supaya
 * ujung huruf yang keluar dari scrim tetap terbaca.
 */
.finale__card {
  padding: 48px clamp(20px, 8vw, 56px);
  text-align: center;
  opacity: 0;
  visibility: hidden;
  text-shadow:
    0 0 2px color-mix(in srgb, var(--card-solid) 90%, transparent),
    0 0 8px color-mix(in srgb, var(--card-solid) 85%, transparent),
    0 0 20px color-mix(in srgb, var(--card-solid) 70%, transparent);
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--card-solid) 88%, transparent) 55%,
    color-mix(in srgb, var(--card-solid) 64%, transparent) 80%,
    color-mix(in srgb, var(--card-solid) 28%, transparent) 93%,
    transparent
  );
}

.finale__eyebrow {
  margin: 0 0 6px;
  font-size: 0.8rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.finale__names {
  margin: 0.1em 0;
  font-family: var(--font-display);
  font-size: clamp(2.3rem, 10.5vw, 3.3rem);
  line-height: 1.1;
  color: var(--accent);
}

.finale__date {
  margin: 0 0 18px;
  font-weight: 600;
  color: var(--ink);
}

.finale__replay {
  gap: 6px;
  background: var(--card-solid);
  text-shadow: none;
}

/* "Putar ulang": layar menggelap sebentar sebelum kembali ke awal. */
.veil {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: #000;
  opacity: 0;
  pointer-events: none;
}

.section-title {
  font-size: 1.7rem;
  color: var(--accent);
  text-align: center;
  margin: 0;
  padding: 4px 22px;
  border-radius: 999px;
  background: var(--card-bg);
}

/* ---------- Kartu info ---------- */

.card {
  position: relative;
  width: min(100%, 30rem);
  padding: 28px 22px;
  border-radius: 14px;
  background: var(--card-bg);
  color: var(--ink);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
  text-align: center;
  border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
}

.card h3 {
  font-size: 1.35rem;
  color: var(--accent);
}

.corner {
  position: absolute;
  width: 56px;
  height: auto;
  pointer-events: none;
  opacity: 0.9;
}

.corner--tl {
  left: -6px;
  top: -6px;
}

.corner--tr {
  right: -6px;
  top: -6px;
  transform: scaleX(-1);
}

.corner--bl {
  left: -6px;
  bottom: -6px;
  transform: scaleY(-1);
}

.corner--br {
  right: -6px;
  bottom: -6px;
  transform: scale(-1, -1);
}

.divider {
  display: block;
  width: min(70%, 260px);
  height: auto;
  margin: 12px auto;
  border: 0;
}

hr.divider {
  border-top: 1px solid var(--line);
}

.eyebrow {
  font-size: 0.85rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 0.4em;
}

.couple-title {
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 11vw, 3.2rem);
  font-weight: 400;
  color: var(--accent);
  margin: 0.2em 0;
}

.couple-title--small {
  font-size: 2rem;
}

.amp {
  font-style: italic;
}

/*
 * Nama pasangan (page.tsx `CoupleNames`), satu format di semua judul nama: baris 1 "A", baris 2
 * "& B" — juga untuk nama pendek yang muat satu baris. Nama yang sangat panjang tetap boleh patah.
 */
.nama-pasangan__1,
.nama-pasangan__2 {
  display: block;
  line-height: 1.08;
}

/* Kilau yang lewat di nama mempelai (dekoratif; warna dasar tetap accent). */
@supports (background-clip: text) or (-webkit-background-clip: text) {
  html[data-motion='on'] .cover .couple-title,
  html[data-motion='on'] .beat__names .couple-title {
    background: linear-gradient(
        110deg,
        var(--accent) 42%,
        color-mix(in srgb, var(--accent) 40%, #fff) 50%,
        var(--accent) 58%
      )
      0 0 / 260% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: shimmer 6s ease-in-out infinite;
  }
}

@keyframes shimmer {
  0%,
  35% {
    background-position: 100% 0;
  }
  70%,
  100% {
    background-position: 0% 0;
  }
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 10px 20px;
  border-radius: 999px;
  border: 1.5px solid var(--accent);
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.button--primary {
  background: var(--accent);
  color: var(--on-accent);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin-top: 12px;
}

/* ---------- Sampul ---------- */

/*
 * Kartu mulai ±30% layar supaya gerbang terlihat. Jarak atas itu (::before)
 * yang lebih dulu menyusut bila kartu tidak muat, jadi baris terbawah tidak
 * terdorong keluar layar di HP pendek / saat toolbar browser tampil.
 */
.cover__content {
  justify-content: flex-start;
  padding-top: calc(16px + env(safe-area-inset-top));
  padding-bottom: calc(max(20px, 4svh) + env(safe-area-inset-bottom));
}

.cover__content::before {
  content: '';
  flex: 0 1 calc(30svh - 32px);
}

.card--cover {
  flex-shrink: 0;
  padding-top: 20px;
}

.enhanced .cover__content {
  height: 100svh;
  min-height: 0;
}

@media (max-height: 700px) {
  .card--cover {
    padding: 16px 20px 10px;
  }

  .card--cover .eyebrow {
    margin-bottom: 0.2em;
  }

  .card--cover .monogram {
    width: 84px;
    margin-bottom: 0;
  }

  .card--cover .monogram__text {
    font-size: 1.25rem;
  }

  .card--cover .couple-title {
    font-size: clamp(2rem, 10vw, 2.6rem);
    line-height: 1.1;
  }

  .card--cover p {
    margin-bottom: 0.5em;
  }

  .card--cover .cover__skip {
    margin-top: 2px;
  }
}

.monogram {
  position: relative;
  width: 110px;
  margin: 0 auto 4px;
}

.monogram__frame img {
  display: block;
  width: 100%;
  height: auto;
}

/* Kotak setinggi huruf (bukan selebar bingkai), supaya isian muat bergerak di hurufnya. */
.monogram__text {
  position: absolute;
  top: 50%;
  right: 0;
  left: 0;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  line-height: 1.05;
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--accent);
}

/* Isi monogram pilihan pasangan: nama panggilan bertumpuk (mengecil menurut nama terpanjang), atau "&". */
.monogram.monogram--nama .monogram__text {
  flex-direction: column;
  gap: 0;
  font-size: min(1.05rem, calc(5.6rem / var(--nama-len, 6)));
  line-height: 1.05;
}

.monogram.monogram--nama .monogram__text .amp {
  font-size: 0.8em;
}

.monogram.monogram--amp .monogram__text {
  font-size: 2.3rem;
}

/* Logo unggahan pasangan: menggantikan bingkai & isi monogram di sampul, dan tampil di penutup. */
.logo-nama {
  display: block;
}

.logo-nama img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 150px;
  object-fit: contain;
}

.monogram.monogram--logo {
  width: 130px;
}

.closing .logo-nama {
  width: min(140px, 45%);
  margin: 6px auto 10px;
}

/*
 * Unduhan awal (JS, main.ts): inisial asli menjadi garis tipis, salinannya
 * (.monogram__isi) terisi dari bawah sesuai --muat (0..1). Tanpa JS: utuh.
 */
.monogram__isi {
  clip-path: inset(calc((1 - var(--muat, 1)) * 100%) 0 0 0);
  transition: clip-path 0.45s ease-out;
}

.is-loading .monogram__text:not(.monogram__isi) {
  color: transparent;
  -webkit-text-stroke: 1px var(--accent);
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
  html[data-motion='on'].is-loading .monogram__isi {
    background: linear-gradient(
        110deg,
        var(--accent) 40%,
        color-mix(in srgb, var(--accent) 35%, #fff) 50%,
        var(--accent) 60%
      )
      0 0 / 260% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: shimmer 2.4s ease-in-out infinite;
  }
}

html[data-motion='on'].is-loaded .cover .monogram {
  animation: monogram-siap 1.1s ease-out;
}

@keyframes monogram-siap {
  40% {
    transform: scale(1.06);
    filter: drop-shadow(0 0 12px color-mix(in srgb, var(--accent) 55%, transparent));
  }
}

/*
 * Tirai muat: "Buka Undangan" diketuk sebelum semua gambar terunduh. Selebar
 * sampul; di atas sampul (40), di bawah tombol kontrol (45) supaya musik yang
 * sudah mulai tetap bisa dimatikan.
 */
.tirai {
  position: fixed;
  inset: 0;
  z-index: 44;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  max-width: 600px;
  margin: 0 auto;
  padding: 24px;
  text-align: center;
  background: color-mix(in srgb, var(--card-solid) 90%, transparent);
  outline: none;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .tirai {
    background: color-mix(in srgb, var(--card-solid) 60%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(1.15);
    backdrop-filter: blur(18px) saturate(1.15);
  }
}

.tirai .monogram {
  width: min(190px, 50vw);
  margin: 0;
}

.tirai .monogram__text {
  font-size: min(2.75rem, 11.5vw);
}

.tirai__teks {
  margin: 0;
  color: var(--ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* `.button` = inline-flex, yang mengalahkan atribut hidden. */
.tirai__lewati[hidden] {
  display: none;
}

.cover__progress {
  font-variant-numeric: tabular-nums;
}

.cover__date {
  font-weight: 600;
}

.cover__to {
  color: var(--ink-muted);
}

.guest-name {
  display: inline-block;
  max-width: 100%;
  color: var(--ink);
  font-size: 1.15rem;
}

.cover__skip {
  display: inline-block;
  margin-top: 10px;
  padding: 8px;
  color: var(--ink-muted);
  font-size: 0.9rem;
  text-underline-offset: 3px;
}

.enhanced .cover {
  position: fixed;
  inset: 0;
  z-index: 40;
  max-width: 600px;
  margin: 0 auto;
  /* Jaring pengaman: layar yang sangat pendek (HP landscape) tetap bisa menggulir kartunya. */
  overflow: hidden auto;
  overscroll-behavior: contain;
}

.enhanced .cover .stage {
  position: absolute;
  inset: 0;
  height: auto;
  margin: 0;
}

.enhanced.is-opened .cover {
  display: none;
}

.enhanced.cover-open body {
  overflow: hidden;
}

/* ---------- Isi bagian ---------- */

.greeting {
  font-size: 1.1rem;
}

.quote {
  display: grid;
  gap: 6px;
  margin: 12px 0 0;
  font-style: italic;
  color: var(--ink-muted);
}

/* Ayat: tegak, kanan-ke-kiri, lebih besar, berjarak dari terjemahannya. */
.quote__arab {
  margin-bottom: 6px;
  font-family: 'Amiri', 'Scheherazade New', 'Noto Naskh Arabic', 'Traditional Arabic', serif;
  font-size: 1.35em;
  font-style: normal;
  line-height: 1.9;
}

.quote__source {
  font-size: 0.9em;
  font-style: normal;
  letter-spacing: 0.02em;
}

.couple {
  display: grid;
  gap: 8px;
}

.person__nickname {
  font-family: var(--font-display);
  font-size: 2rem;
  color: var(--accent);
  margin: 0;
}

.person__name {
  font-weight: 600;
  margin-bottom: 0.2em;
}

.person__parents {
  color: var(--ink-muted);
  font-size: 0.95rem;
}

.couple__amp {
  font-family: var(--font-display);
  font-size: 1.8rem;
  color: var(--accent);
  margin: 0;
}

/* Logo nama unggahan pasangan menggantikan "&" di antara kedua mempelai. */
.couple__amp--logo .logo-nama {
  width: min(120px, 40%);
  margin: 4px auto;
}

.stages-fixed .scene--directed .couple__amp--logo .logo-nama {
  width: min(180px, 52vw);
}

.story {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 16px;
  width: min(100%, 30rem);
}

.story .card {
  width: 100%;
}

/* Foto babak: 1 foto di tengah, 2–3 foto berjajar. */
.story__photos {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
  margin: 0 auto 16px;
  padding: 0;
}

.story__photos[data-count='1'] {
  grid-template-columns: minmax(0, 1fr);
  width: min(100%, 13rem);
}

.story__photos[data-count='2'] {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  width: min(100%, 20rem);
}

/* Foto mempelai yang tidak tampil di bingkai adegan. */
.thumbs {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 12px 0 4px;
  padding: 0;
}

.thumb {
  display: block;
  width: 64px;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border: 2px solid var(--line);
  border-radius: 8px;
  background: #d9d2c5;
}

.countdown {
  color: var(--ink);
  font-weight: 600;
  text-align: center;
  margin: 0;
  padding: 6px 16px;
  border-radius: 999px;
  background: var(--card-bg);
}

.events {
  display: grid;
  gap: 18px;
  width: min(100%, 30rem);
}

.events .card {
  width: 100%;
}

.event__date {
  font-weight: 600;
  margin-bottom: 0.1em;
}

.event__venue {
  font-weight: 600;
  margin-bottom: 0.1em;
}

.event__address {
  color: var(--ink-muted);
}

.event__plan {
  margin: 12px 0 4px;
}

.event__plan summary {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
}

.event__plan img {
  display: block;
  width: 100%;
  height: auto;
  margin: 4px 0 6px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
}

.calendar-all .button {
  background: var(--card-solid);
}

/*
 * Galeri: baris-baris dari `ritmeGaleri` (`render/page.tsx`, `@undangin/schema`), bukan grid seragam.
 * Foto utama berbingkai tema; sisanya ubin tanpa ornamen (garis tipis `--line`) dalam kelompok tiga (satu besar,
 * dua kecil), pasangan, jeda lanskap selebar kolom, dan baris campur.
 */
.gallery {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 12px;
  width: min(100%, 30rem);
}

.gallery__row {
  display: grid;
  gap: 10px;
  margin: 0;
}

.gallery__hint {
  margin: 2px 0 0;
  text-align: center;
  font-size: 0.8rem;
  color: var(--ink-muted);
}

.gtile {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 3px;
  box-shadow: 0 0 0 1px var(--line);
  background: color-mix(in srgb, var(--line) 35%, transparent);
}

.gtile .photo__img,
.gtile img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Foto tanpa latar: ubin bercahaya lembut; seluruh badan berdiri di dasar ubin dengan bayangan kontak. */
.gtile--cutout {
  background: radial-gradient(
    ellipse 90% 70% at 50% 35%,
    color-mix(in srgb, var(--card-solid) 85%, transparent),
    color-mix(in srgb, var(--line) 55%, transparent)
  );
}

.gtile--cutout img {
  object-position: 50% 12%;
}

.gtile--utuh img {
  object-fit: contain;
  object-position: 50% 100%;
}

.gtile--utuh::before {
  content: '';
  position: absolute;
  left: 22%;
  right: 22%;
  bottom: 1.5%;
  height: 5%;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(20, 10, 6, 0.4), transparent 70%);
}

.gallery__row--utama {
  gap: 8px;
}

/* Foto tanpa latar sebagai foto utama: duduk di jendela bingkai, di atas latar bercahaya. */
/* `position: absolute` tetap (jendela dipasang inline lewat left/top/width/height); `relative` menggeser jendela. */
.gallery__row--utama .framed[data-cutout] .framed__photo {
  position: absolute;
  overflow: hidden;
  background: color-mix(in srgb, var(--line) 55%, var(--card-solid));
}

.gallery__row--utama .framed[data-cutout] .framed__photo > picture,
.gallery__row--utama .framed[data-cutout] .framed__photo > .photo__img {
  position: relative;
  z-index: 1;
}

.gallery__row--utama .framed[data-cutout] img {
  object-fit: contain;
  object-position: 50% 100%;
}

/* Judul "Galeri" tidak memakan satu layar sendiri: isinya mulai tepat di bawahnya. */
.stages-fixed #galeri .beat {
  min-height: auto;
  padding-top: max(24px, 9svh);
}

.gallery__row--tiga {
  grid-template-columns: 1.7fr 1fr;
  grid-template-rows: auto auto;
}

.gallery__row--tiga > .gtile:first-child {
  grid-row: 1 / span 2;
}

.gallery__row--tiga > .gtile:nth-child(n + 2) {
  aspect-ratio: 3 / 4;
}

.gallery__row--tiga.gallery__row--flip {
  grid-template-columns: 1fr 1.7fr;
}

.gallery__row--tiga.gallery__row--flip > .gtile:first-child {
  grid-column: 2;
}

.gallery__row--tiga.gallery__row--flip > .gtile:nth-child(2) {
  grid-column: 1;
  grid-row: 1;
}

.gallery__row--tiga.gallery__row--flip > .gtile:nth-child(3) {
  grid-column: 1;
  grid-row: 2;
}

.gallery__row--dua {
  grid-template-columns: 1fr 1fr;
  align-items: start;
}

.gallery__row--dua > .gtile {
  aspect-ratio: 3 / 4;
}

.gallery__row--dua > .gtile:nth-child(2) {
  margin-top: 22px;
}

.gallery__row--dua.gallery__row--flip > .gtile:nth-child(2) {
  margin-top: 0;
}

.gallery__row--dua.gallery__row--flip > .gtile:first-child {
  margin-top: 22px;
}

.gallery__row--lebar > .gtile {
  aspect-ratio: 3 / 2;
}

.gallery__row--campur {
  grid-template-columns: 1fr 2.25fr;
  aspect-ratio: 2.4 / 1;
}

.gallery__row--campur.gallery__row--flip {
  grid-template-columns: 2.25fr 1fr;
}

.gallery__row--campur.gallery__row--flip > .gtile:first-child {
  order: 2;
}

.gallery__row--tunggal > .gtile {
  width: 66%;
  margin-inline: auto;
  aspect-ratio: 3 / 4;
}

@media (min-width: 768px) {
  .gallery,
  .videos {
    width: min(100%, 44rem);
  }
}

.framed {
  position: relative;
  display: block;
  aspect-ratio: var(--ratio, 2 / 3);
}

/*
 * Video Galeri, di pembuka galeri, tanpa bingkai ornamen: lanskap pertama selebar layar (maks 56rem, judul di atas
 * gradasi), klip tegak sepertiga lebar dengan judul di sampingnya, lanskap berikutnya selebar kolom.
 */
.videos {
  list-style: none;
  display: grid;
  gap: 14px;
  width: min(100%, 30rem);
  margin: 0 0 20px;
  padding: 0;
}

.video {
  margin: 0;
}

.vtile {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  background: #1c1714;
  box-shadow: 0 0 0 1px var(--line);
}

.vtile .photo__img,
.vtile img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.video--hero {
  width: min(100vw, 56rem);
  margin-inline: calc(50% - min(100vw, 56rem) / 2);
}

.video__caption {
  position: absolute;
  inset: auto 0 0;
  padding: 36px 18px 14px;
  background: linear-gradient(to top, rgba(10, 6, 4, 0.78), transparent);
  color: #fff;
  font-size: 1.15rem;
  font-weight: 600;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
}

.video--portrait {
  display: flex;
  align-items: center;
  gap: 16px;
}

.video--portrait .vtile {
  flex: 0 0 38%;
  aspect-ratio: 3 / 4.2;
}

.video--portrait .video__title {
  margin: 0;
  padding: 0;
  background: none;
  text-align: left;
  font-size: 1.1rem;
}

.video__play {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 64px;
  height: 64px;
  margin: -32px 0 0 -32px;
  border: 2px solid rgba(255, 255, 255, 0.85);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
}

.video__play::after {
  content: '';
  position: absolute;
  left: 23px;
  top: 17px;
  border-style: solid;
  border-width: 13px 0 13px 21px;
  border-color: transparent transparent transparent #fff;
}

.video__title {
  display: table;
  margin: 8px auto 0;
  padding: 4px 14px;
  border-radius: 999px;
  background: var(--card-bg);
  color: var(--ink);
  font-weight: 600;
  text-align: center;
}

.framed__photo {
  position: absolute;
  overflow: hidden;
}

.framed__photo img,
.framed__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.framed__frame {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/*
 * Foto mendatar di grid Galeri & Kisah (`orient.ts`): bingkai lanskap menggantikan bingkai potret,
 * jadi foto tidak dipotong jadi 2:3. Bingkai lanskap disembunyikan sampai dibutuhkan.
 */
.framed__frame--land {
  display: none;
}

.framed--land {
  aspect-ratio: var(--ratio-land);
  align-self: center;
}

.framed--land .framed__frame {
  display: none;
}

.framed--land .framed__frame--land {
  display: block;
}

.framed--land .framed__photo {
  left: var(--land-x) !important;
  top: var(--land-y) !important;
  width: var(--land-w) !important;
  height: var(--land-h) !important;
}

/* Galeri & Kisah tanpa bingkai: foto utuh di atas latar bagian. */
.framed--cutout .framed__photo {
  overflow: visible;
}

.framed--cutout .framed__photo img {
  object-fit: contain;
  object-position: 50% 100%;
}

.rsvp {
  text-align: left;
}

.rsvp__form {
  display: grid;
  gap: 14px;
}

/*
 * Formulir mengikuti kartu tema: label kecil berhuruf kapital renggang berwarna aksen (seperti
 * eyebrow & "N ucapan terbaru"), isian bernuansa warna kartu dengan tinta tema, bukan kotak putih.
 */
.rsvp__form label:not(.choice) {
  display: grid;
  gap: 6px;
}

.rsvp__form label:not(.choice),
.rsvp__form legend {
  padding: 0;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}

.rsvp__form legend {
  margin-bottom: 2px;
}

.rsvp__form .choice {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  color: var(--ink);
}

.rsvp__form input[type='radio'] {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--accent);
}

.rsvp__form input:not([type='radio']),
.rsvp__form select,
.rsvp__form textarea {
  width: 100%;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  border-radius: 10px;
  font: inherit;
  /* 16px: iOS tidak memperbesar halaman saat isian disentuh. */
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink);
  background: color-mix(in srgb, var(--card-solid) 55%, #fff);
}

.rsvp__form :is(input, select, textarea):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}

/* Preset gelap: isian sedikit lebih terang dari kartu; kontrol bawaan (panah, daftar pilihan) gelap. */
html[data-kartu='gelap'] .rsvp__form {
  color-scheme: dark;
}

html[data-kartu='gelap'] .rsvp__form input:not([type='radio']),
html[data-kartu='gelap'] .rsvp__form select,
html[data-kartu='gelap'] .rsvp__form textarea {
  background: color-mix(in srgb, var(--card-solid) 84%, var(--ink));
}

.rsvp__form button[type='submit'] {
  justify-self: center;
  min-width: 11rem;
}

.rsvp__form fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

.hint {
  font-size: 0.92rem;
  color: var(--ink-muted);
  margin: 0;
}

.hint--note {
  font-style: italic;
}

.rsvp__status:empty {
  display: none;
}

/*
 * Film & Lukisan, preset berlawanan dengan preset bawaan tema (`data-kartu-lawan`, mis. Jati Tua di
 * Pendopo Jawa): scrim lembut gelap di adegan terang (atau sebaliknya) tampak seperti noda, jadi
 * judul bagian berkartu jadi papan senada kartu di bawahnya. Buku & Teater punya judul sendiri.
 */
html[data-kartu-lawan].stages-fixed:is(:not([data-gaya]), [data-gaya='lukisan'])
  :is(.scene--acara, .scene--rsvp, .scene--hadiah)
  .section-title {
  padding: 14px 28px 12px;
  border-radius: 14px;
  border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  background: var(--card-bg);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
  text-shadow: none;
}

/* Ucapan & Doa: kartu kecil per ucapan (inisial, nama, waktu, pesan bergaya kutipan). */
.wishes {
  text-align: left;
}

.wishes h3 {
  text-align: center;
}

.wishes__count {
  margin: -4px 0 16px;
  text-align: center;
  font-size: 0.8rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.wishes__count:empty {
  display: none;
}

.wishes__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

.wish {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  padding: 14px 14px 14px 12px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--card-solid) 70%, transparent);
  border: 1px solid color-mix(in srgb, var(--line) 45%, transparent);
}

.wish__avatar {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 16%, var(--card-solid));
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  font: 600 0.95rem/1 var(--font-display);
  letter-spacing: 0.02em;
}

.wish__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  margin: 0 0 4px;
}

.wish__name {
  color: var(--ink);
  font-weight: 600;
  overflow-wrap: anywhere;
}

.wish__time {
  font-size: 0.78rem;
  color: var(--ink-muted);
  white-space: nowrap;
}

.wish__message {
  position: relative;
  margin: 0;
  padding-left: 14px;
  color: var(--ink-muted);
  font-size: 0.95em;
  font-style: italic;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

/* Garis kutipan tipis berwarna aksen di kiri pesan. */
.wish__message::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.2em;
  bottom: 0.2em;
  width: 2px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--accent) 55%, transparent);
}

.gift__account {
  padding: 12px 0;
  border-top: 1px solid color-mix(in srgb, var(--line) 50%, transparent);
}

.gift__bank {
  font-weight: 600;
  margin: 0;
}

.gift__number {
  font-size: 1.3rem;
  letter-spacing: 0.06em;
  margin: 0;
}

.gift__holder {
  color: var(--ink-muted);
}

/* ======================================================================
 * Komposisi bagian info: Acara (Agenda), RSVP (Balasan), Hadiah (Amplop).
 * docs/usulan-komposisi-acara-rsvp-hadiah.md. Dasar kartu (`.card`) tetap
 * dari atas dan kulit gaya (Buku/Lukisan/Teater); blok ini hanya mengatur
 * tata letak, hierarki, dan jarak, disandarkan pada `[data-komposisi]`
 * (preset `kartu` = tampilan lama, tanpa aturan di bawah). Tanpa gambar
 * baru; warna dan font dari tema. Info acara tetap HTML (tanpa JS).
 * ====================================================================== */

[data-komposisi] .event__name,
[data-komposisi] .event__venue,
[data-komposisi] .event__address,
[data-komposisi] .gift__number,
[data-komposisi] .gift__holder,
[data-komposisi] .wish__message {
  overflow-wrap: anywhere;
}

/* ---------- Agenda (Acara) ---------- */

[data-komposisi='agenda'] {
  --rel: 5.6rem;
}

[data-komposisi='agenda'] .card.agenda {
  width: min(100%, 34rem);
  padding: 22px 16px 18px;
  text-align: left;
}

/* Padding samping menghindari sudut ornamen panel. */
[data-komposisi='agenda'] .agenda .countdown {
  margin: 0 0 14px;
  padding: 2px 30px 12px;
  border-radius: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  background: none;
  color: var(--accent);
  font-size: 0.95rem;
  text-align: center;
}

[data-komposisi='agenda'] .agenda .countdown:empty {
  display: none;
}

[data-komposisi='agenda'] .agenda .events {
  list-style: none;
  display: block;
  width: auto;
  margin: 0;
  padding: 0;
}

[data-komposisi='agenda'] .agenda__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

[data-komposisi='agenda'] .agenda__day + .agenda__day {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
}

/* Penanda hari: numeral besar di lajur jam, tanggal panjang (teks nyata) di sampingnya. */
[data-komposisi='agenda'] .agenda__dayhead {
  display: grid;
  grid-template-columns: var(--rel) minmax(0, 1fr);
  column-gap: 12px;
  align-items: center;
  margin-bottom: 8px;
}

[data-komposisi='agenda'] .agenda__num {
  font-family: var(--font-display);
  font-size: 3rem;
  font-weight: 600;
  line-height: 1;
  color: var(--accent);
  font-variant-numeric: lining-nums tabular-nums;
}

[data-komposisi='agenda'] .agenda__date {
  margin: 0;
  font-weight: 600;
  color: var(--ink);
}

/* Rel jam di kiri, isi acara di kanan. */
[data-komposisi='agenda'] .event {
  display: grid;
  grid-template-columns: var(--rel) minmax(0, 1fr);
  column-gap: 12px;
  align-items: start;
  padding: 12px 0;
}

[data-komposisi='agenda'] .event + .event {
  border-top: 1px solid color-mix(in srgb, var(--line) 45%, transparent);
}

[data-komposisi='agenda'] .event__time {
  margin: 0.2em 0 0;
  color: var(--accent);
  font-size: 0.94rem;
  font-weight: 600;
  line-height: 1.35;
  font-variant-numeric: tabular-nums;
  text-wrap: balance;
}

[data-komposisi='agenda'] .event__body {
  min-width: 0;
}

[data-komposisi='agenda'] .event__name {
  margin: 0 0 0.25em;
  font-size: 1.3rem;
  color: var(--ink);
}

[data-komposisi='agenda'] .event__venue {
  margin: 0;
  font-weight: 600;
}

[data-komposisi='agenda'] .event__address {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.95rem;
}

[data-komposisi='agenda'] .event__plan {
  margin: 6px 0 0;
}

[data-komposisi='agenda'] .event .actions {
  justify-content: flex-start;
  align-items: center;
  gap: 4px 16px;
  margin-top: 10px;
}

[data-komposisi='agenda'] .event .button--primary {
  min-height: 44px;
  padding: 8px 18px;
}

/* Google Calendar jadi tautan teks bergaris bawah (tinggi sentuh 44 px). */
[data-komposisi='agenda'] .event .event__cal {
  min-height: 44px;
  padding: 0 2px;
  color: var(--accent);
  font-weight: 600;
  text-underline-offset: 3px;
}

/* Teks panjang: jam pindah di atas nama (tanpa rel) supaya kolom isi tidak menyempit. */
[data-komposisi='agenda'] .event.k-susun {
  grid-template-columns: minmax(0, 1fr);
}

[data-komposisi='agenda'] .event.k-susun .event__time {
  margin: 0 0 4px;
}

[data-komposisi='agenda'] .calendar-all {
  margin: 6px 0 0;
  padding: 14px 30px 4px;
  border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  text-align: center;
}

/* Satu acara: numeral dan nama diperbesar. */
[data-komposisi='agenda'].k-tunggal .agenda__num {
  font-size: 3.6rem;
}

[data-komposisi='agenda'].k-tunggal .event__name {
  font-size: 1.55rem;
}

/* Empat acara atau lebih: jarak rapat (semua informasi tetap tampil). */
[data-komposisi='agenda'].k-ringkas .agenda__day + .agenda__day {
  margin-top: 12px;
  padding-top: 12px;
}

[data-komposisi='agenda'].k-ringkas .agenda__num {
  font-size: 2.5rem;
}

[data-komposisi='agenda'].k-ringkas .event {
  padding: 8px 0;
}

[data-komposisi='agenda'].k-ringkas .event__name {
  font-size: 1.2rem;
}

/* Lajur jam lebih sempit supaya Buka Peta dan Google Calendar muat sebaris. */
[data-komposisi='agenda'].k-ringkas {
  --rel: 4.7rem;
}

[data-komposisi='agenda'].k-ringkas .event__time {
  font-size: 0.88rem;
}

[data-komposisi='agenda'].k-ringkas .event .actions {
  gap: 0 10px;
  margin-top: 4px;
}

[data-komposisi='agenda'].k-ringkas .event .button--primary {
  min-height: 40px;
  padding: 4px 14px;
  font-size: 0.92rem;
}

[data-komposisi='agenda'].k-ringkas .event .event__cal {
  min-height: 44px;
  font-size: 0.9rem;
}

@media (min-width: 640px) {
  [data-komposisi='agenda'],
  [data-komposisi='agenda'].k-ringkas {
    --rel: 6.5rem;
  }

  [data-komposisi='agenda'] .card.agenda {
    padding: 26px 22px 20px;
  }

  [data-komposisi='agenda'] .agenda__num {
    font-size: 3.4rem;
  }
}

/* Judul "A C A R A" (Film & Klasik): huruf kapital renggang, tanpa pil; gaya lain memakai judulnya sendiri. */
html:not([data-gaya]) [data-komposisi='agenda'] .section-title {
  padding: 16px 40px 8px;
  border-radius: 0;
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--card-solid) 80%, transparent) 45%,
    transparent
  );
  font-size: 1.3rem;
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  text-transform: uppercase;
  text-shadow: 0 0 16px color-mix(in srgb, var(--card-solid) 70%, transparent);
}

/* Latar tenang: adegan di belakang Acara (Film) diredam scrim warna kartu; Klasik memakai komposit diam. */
html.stages-fixed:not([data-gaya]) [data-komposisi='agenda'] .stage::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 3;
  background: color-mix(in srgb, var(--card-solid) var(--scrim-acara, 55%), transparent);
  pointer-events: none;
}

/* ---------- Balasan (RSVP) ---------- */

[data-komposisi='balasan'] .card.rsvp {
  width: min(100%, 26rem);
  padding: 24px 20px 20px;
}

[data-komposisi='balasan'] .card.rsvp--tutup {
  text-align: center;
}

[data-komposisi='balasan'] .rsvp__deadline {
  margin: 0 0 18px;
}

[data-komposisi='balasan'] .rsvp__form {
  gap: 18px;
}

[data-komposisi='balasan'] .rsvp__choices {
  display: grid;
  gap: 2px;
}

/* Jumlah orang sebaris: label di kiri, pilihan di kanan. */
[data-komposisi='balasan'] .rsvp__pax {
  grid-template-columns: minmax(0, 1fr) 6rem;
  align-items: center;
  gap: 12px;
}

[data-komposisi='balasan'] .rsvp__form textarea {
  min-height: 7rem;
  resize: vertical;
}

[data-komposisi='balasan'] .rsvp__form button[type='submit'] {
  width: 100%;
  min-height: 48px;
}

[data-komposisi='balasan'] .rsvp__form .captcha {
  justify-self: center;
}

[data-komposisi='balasan'] .rsvp__form .button--link {
  justify-self: center;
}

/* Kehadiran = tiga segmen sama lebar (markup tetap tiga radio; tanpa :has tampil seperti radio biasa). */
@supports selector(:has(*)) {
  [data-komposisi='balasan'] .rsvp__choices {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0;
    overflow: hidden;
    border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
    border-radius: 12px;
    background: color-mix(in srgb, var(--card-solid) 55%, #fff);
  }

  /* Preset gelap: segmen sedikit lebih terang dari kartu (sama dengan isian lain). */
  html[data-kartu='gelap'] [data-komposisi='balasan'] .rsvp__choices {
    background: color-mix(in srgb, var(--card-solid) 84%, var(--ink));
  }

  [data-komposisi='balasan'] .rsvp__form .choice {
    position: relative;
    justify-content: center;
    min-height: 48px;
    padding: 4px 6px;
    font-size: 0.95rem;
    line-height: 1.15;
    text-align: center;
    text-transform: none;
    letter-spacing: normal;
    cursor: pointer;
  }

  [data-komposisi='balasan'] .rsvp__form .choice + .choice {
    border-left: 1px solid color-mix(in srgb, var(--line) 55%, transparent);
  }

  /* Radio asli tetap ada (fokus, pembaca layar, validasi) tetapi tidak terlihat; seluruh segmen = target ketuk. */
  [data-komposisi='balasan'] .rsvp__form .choice input[type='radio'] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }

  [data-komposisi='balasan'] .rsvp__form .choice:has(input:checked) {
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
  }

  [data-komposisi='balasan'] .rsvp__form .choice:has(input:focus-visible) {
    outline: 2px solid var(--ink);
    outline-offset: -4px;
  }
}

/* Ucapan & Doa: daftar terbuka di bawah lembar (bukan kartu kedua); tiap ucapan satu petak. */
[data-komposisi='balasan'] .wishes {
  width: min(100%, 26rem);
  margin-top: 6px;
}

[data-komposisi='balasan'] .wishes__head {
  margin: 0 auto 12px;
  padding: 12px 28px 10px;
  text-align: center;
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--card-solid) 80%, transparent) 50%,
    transparent
  );
}

[data-komposisi='balasan'] .wishes h3 {
  margin: 0;
  font-size: 1.35rem;
  color: var(--accent);
}

[data-komposisi='balasan'] .wishes__count {
  margin: 2px 0 0;
}

[data-komposisi='balasan'] .wish {
  background: color-mix(in srgb, var(--card-solid) 90%, transparent);
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.1);
}

/* ---------- Amplop (Hadiah) ---------- */

[data-komposisi='amplop'] .card.gift {
  width: min(100%, 28rem);
  padding: 22px 16px 16px;
  text-align: left;
}

[data-komposisi='amplop'] .gift__intro {
  margin: 0 0 16px;
  padding: 0 4px;
  color: var(--ink-muted);
  font-size: 0.95rem;
  text-align: center;
}

[data-komposisi='amplop'] .gift__accounts {
  display: grid;
  gap: 10px;
}

[data-komposisi='amplop'] .gift__account {
  padding: 12px 14px;
  border: 1px solid color-mix(in srgb, var(--line) 50%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--card-solid) 70%, transparent);
}

[data-komposisi='amplop'] .gift__accounts + .gift__account {
  margin-top: 10px;
}

[data-komposisi='amplop'] .gift__bank {
  margin: 0;
  color: var(--accent);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Tombol Salin sebaris dengan nomor; nomor panjang menurunkannya ke baris sendiri selebar petak. */
[data-komposisi='amplop'] .gift__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px 12px;
  margin: 2px 0 4px;
}

[data-komposisi='amplop'] .gift__number {
  flex: 1 1 8rem;
  min-width: 0;
  margin: 0;
  font-size: 1.15rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  /* Tanpa JS tombol Salin tersembunyi: satu ketukan memilih seluruh nomor. */
  user-select: all;
}

[data-komposisi='amplop'] .gift__row .button {
  flex: 0 0 auto;
  min-height: 44px;
  padding: 6px 16px;
  font-size: 0.95rem;
}

/* Nomor panjang: boleh turun baris, tombol Salin di baris sendiri selebar petak. */
[data-komposisi='amplop'] .gift__row--panjang .gift__number {
  flex-basis: 100%;
  white-space: normal;
}

[data-komposisi='amplop'] .gift__row--panjang .button {
  flex-basis: 100%;
}

[data-komposisi='amplop'] .gift__holder {
  margin: 0;
  font-size: 0.95rem;
}

[data-komposisi='amplop'] .gift__address {
  margin: 4px 0 8px;
  color: var(--ink);
  font-size: 1rem;
}

[data-komposisi='amplop'] .gift__account--alamat .button {
  min-height: 44px;
  padding: 6px 16px;
  font-size: 0.95rem;
}

@media (min-width: 640px) {
  [data-komposisi='amplop'].k-banyak .gift__accounts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  [data-komposisi='amplop'].k-banyak .gift__account:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  [data-komposisi='amplop'].k-banyak .gift__row .button {
    flex-basis: 100%;
  }
}

.credits,
.made-with {
  width: min(100%, 30rem);
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--card-bg);
  color: var(--ink);
  font-size: 0.9rem;
}

.made-with {
  text-align: center;
}

/* ---------- Jelajahi (tema peta): peta utuh + garis rute + tombol tiap bagian ---------- */

/* Tanpa panggung adegan: warna halaman, peta utuh di kolom (juga tanpa JS & Klasik). */
.scene--jelajah {
  background: var(--page);
  color: var(--ink);
}

.scene--jelajah .scene__content {
  padding: max(24px, 5svh) 16px max(96px, 12svh);
}

.map {
  width: 100%;
}

.map__canvas {
  position: relative;
  width: 100%;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  border-radius: 14px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}

.map__img,
.map__img img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.map__route {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

/* `pathLength="1"`: panjang putus-putus relatif panjang rute. */
.map__route-line {
  fill: none;
  stroke: #fffaf0;
  stroke-width: 7;
  stroke-linecap: round;
  stroke-dasharray: 0.006 0.009;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55));
}

.map__pins {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

/*
 * Titik tanpa ukuran di posisi tempat. Titik bulat tepat di posisi itu; label
 * di kanan/kiri/atas/bawahnya (dipilih server supaya tidak saling tumpang).
 */
.map__pin,
.map__pin a {
  position: absolute;
  width: 0;
  height: 0;
}

.map__pin a {
  left: 0;
  top: 0;
  color: var(--ink);
  text-decoration: none;
}

.map__dot {
  position: absolute;
  left: -12px;
  top: -12px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow:
    inset 0 0 0 6px var(--card-solid),
    0 2px 6px rgba(0, 0, 0, 0.35);
}

.map__label {
  position: absolute;
  padding: 8px 12px;
  border-radius: 999px;
  background: var(--card-solid);
  font: 600 0.8rem/1 var(--font-body);
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

.map__pin--right .map__label {
  left: 16px;
  top: 0;
  transform: translateY(-50%);
}

.map__pin--left .map__label {
  right: 16px;
  top: 0;
  transform: translateY(-50%);
}

.map__pin--up .map__label {
  left: 0;
  bottom: 16px;
  transform: translateX(-50%);
}

.map__pin--down .map__label {
  left: 0;
  top: 16px;
  transform: translateX(-50%);
}

/* Area ketuk titik ±44 px tanpa memperbesar tampilan (labelnya sendiri sudah ±30 px). */
.map__dot::before {
  content: '';
  position: absolute;
  inset: -10px;
}

.map__pin a:focus-visible {
  outline: none;
}

.map__pin a:focus-visible .map__label {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.map__hint {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.9rem;
  text-align: center;
}

/* ---------- Sinematik tema peta: peta dunia menjadi layar kamera (world.ts) ---------- */

/* Jelajahi ±2 layar: seluruh peta tampil selama tamu menggulirnya; judul & petunjuk menempel. */
.world-on .scene--jelajah {
  background: none;
}

.world-on .scene--jelajah .scene__content {
  justify-content: space-between;
  min-height: 190svh;
  padding: calc(env(safe-area-inset-top) + 16px) 16px calc(88px + env(safe-area-inset-bottom));
}

.world-on .scene--jelajah .section-title,
.world-on .map__hint {
  position: sticky;
  z-index: 1;
}

.world-on .scene--jelajah .section-title {
  top: calc(env(safe-area-inset-top) + 16px);
}

.world-on .scene--jelajah .section-title,
.world-on .scene--jelajah .map__hint {
  transition: opacity 0.35s ease;
}

/* `html.` : harus menang atas `.enhanced .scene.is-seen .section-title` (ditulis lebih bawah),
   kalau tidak judul menempel di atas Mempelai sesudah kamera meninggalkan peta. */
html.world-on .scene--jelajah.is-leaving .section-title,
html.world-on .scene--jelajah.is-leaving .map__hint {
  opacity: 0;
}

.world-on .map__hint {
  bottom: calc(88px + env(safe-area-inset-bottom));
  padding: 6px 14px;
  border-radius: 999px;
  background: rgba(5, 12, 18, 0.6);
  color: #f4efe6;
}

/* Lapisan tetap di atas semua panggung adegan, di bawah konten bagian berikutnya. */
.world-on .map {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  max-width: 600px;
  height: 100lvh;
  margin: 0 auto;
  overflow: hidden;
  background: #07121a;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.world-on .map.is-overview {
  pointer-events: auto;
}

/* Peta contain di tengah layar; skrip menggeser & memperbesarnya sebagai kamera. */
.world-on .map__canvas {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(100%, 66.667lvh);
  border-radius: 0;
  box-shadow: none;
  will-change: transform;
}

.map__night {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
}

/* Garis putus-putus tergambar lewat mask; garis tegas = rute yang sudah dilalui. */
.map__route-mask {
  fill: none;
  stroke: #fff;
  stroke-width: 40;
  stroke-dasharray: 1 1;
  stroke-dashoffset: 1;
}

.map__route-done {
  fill: none;
  stroke: #ffe3a6;
  stroke-width: 6;
  stroke-linecap: round;
  stroke-dasharray: 1 1;
  stroke-dashoffset: 1;
  filter: drop-shadow(0 0 3px rgba(255, 214, 140, 0.85));
}

.world-on .map__pin {
  transition: none;
}

/* ---------- Elemen tetap ---------- */

/* Dock menempel di bawah layar: scrollIntoView/fokus papan ketik tidak boleh menyisakan elemen di balik dock. */
html {
  scroll-padding-bottom: 84px;
}

.dock {
  position: fixed;
  left: 50%;
  bottom: max(10px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 2px;
  max-width: calc(100vw - 16px);
  padding: 4px;
  border-radius: 999px;
  background: var(--card-solid);
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.25);
}

.dock a,
.dock button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 44px;
  padding: 0 12px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

@media (max-width: 380px) {
  .dock__step-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .dock a,
  .dock button {
    padding: 0 9px;
  }
}

/* Satu baris ringkas di pojok kanan atas (bukan tumpukan tiga pil): menutupi lebih sedikit sampul. */
.controls {
  position: fixed;
  top: max(10px, env(safe-area-inset-top));
  right: max(10px, calc(50vw - 300px + 10px));
  z-index: 45;
  flex-direction: row;
  align-items: center;
  gap: 6px;
}

/* Tamu sedang membaca (gulir turun, main.ts): tombol & label pratinjau tidak menutup teks. */
.controls,
.banner {
  transition:
    opacity 0.3s ease,
    translate 0.3s ease;
}

.is-reading .controls:not(:focus-within) {
  opacity: 0;
  translate: 24px 0;
  pointer-events: none;
}

.is-reading .banner {
  opacity: 0;
}

.enhanced .controls.js-only {
  display: flex !important;
}

/* Tombol berlabel (ikon + kata pendek yang terbaca), bukan ikon bulat tanpa teks. Kata pendek
   disembunyikan dari pembaca layar; nama tombol = `.control__label` (berisi kata yang sama). */
.control {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 44px;
  height: 44px;
  padding: 0 11px 0 9px;
  border: 0;
  border-radius: 22px;
  background: var(--card-solid);
  color: var(--ink);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
  font-size: 1rem;
  cursor: pointer;
}

.control__icon {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 1.25em;
  line-height: 1;
}

.control__text {
  font-family: var(--font-body);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}

.control[aria-pressed='true'] {
  background: var(--accent);
  color: var(--on-accent);
}

/* Musik mati: ikon not dicoret, bukan tanda silang (mirip tombol tutup). */
.control[data-mute][aria-pressed='true'] .control__icon::after {
  content: '';
  position: absolute;
  width: 26px;
  height: 2px;
  background: currentColor;
  transform: rotate(-45deg);
}

.control__label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

html[data-mode='klasik'] [data-motion-toggle],
html[data-mode='klasik'] [data-saver-toggle] {
  display: none !important;
}

.progress {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  height: 3px;
  z-index: 35;
  pointer-events: none;
}

.progress__bar {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(var(--progress, 0));
  transform-origin: left;
}

/* Pita kecil di pojok kiri atas (pratinjau, demo): tidak menutup judul sampul di tengah. */
.banner {
  position: fixed;
  left: max(10px, calc(50vw - 300px + 10px));
  top: max(10px, env(safe-area-inset-top));
  z-index: 50;
  margin: 0;
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 0.8rem;
  pointer-events: none;
}

/* Layar sempit: pita turun ke bawah baris kontrol supaya tidak menutup tombol Musik (pratinjau & demo saja;
   halaman terbit tanpa pita). */
@media (max-width: 440px) {
  .banner {
    top: calc(max(10px, env(safe-area-inset-top)) + 52px);
  }
}

.watermark {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  pointer-events: none;
  font-size: 18vw;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.18);
  transform: rotate(-24deg);
}

.particles {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: min(100vw, 600px);
  height: 100lvh;
  z-index: 1;
  pointer-events: none;
}

/* ---------- Muncul pelan (hanya setelah JS jalan) ---------- */

.enhanced .scene:not(.cover) .card,
.enhanced .scene:not(.cover) .section-title {
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity 0.7s ease,
    transform 0.7s ease;
}

.enhanced .scene.is-seen .card,
.enhanced .scene.is-seen .section-title {
  opacity: 1;
  transform: none;
}

html[data-motion='off'] .scene .card,
html[data-motion='off'] .scene .section-title,
html[data-motion='off'] .beat__line {
  transform: none !important;
}

/* ---------- Lightbox ---------- */

.lightbox {
  max-width: 96vw;
  max-height: 92vh;
  padding: 0;
  border: 0;
  background: transparent;
}

.lightbox::backdrop {
  background: rgba(0, 0, 0, 0.85);
}

.lightbox img {
  display: block;
  max-width: 96vw;
  max-height: 84vh;
  margin: 0 auto;
}

/* Foto yang dipotong: kotak seukuran potongan (rasio `--car`) sebesar mungkin di layar. */
.lightbox__crop {
  display: block;
  width: min(96vw, 84vh * var(--car));
  height: auto;
  aspect-ratio: var(--car);
  margin: 0 auto;
}

.lightbox__crop img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.lightbox > button {
  display: block;
  margin: 10px auto 0;
}

.lightbox__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 10px;
  color: #fff;
}

.lightbox__arrow {
  width: 44px;
  height: 44px;
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
}

.lightbox__count {
  min-width: 4.5ch;
  text-align: center;
  font-size: 0.9rem;
}

.lightbox video,
.lightbox iframe {
  display: block;
  width: min(96vw, 960px);
  max-height: 80vh;
  margin: 0 auto;
  border: 0;
  background: #000;
}

.lightbox iframe {
  height: auto;
  aspect-ratio: 16 / 9;
}

.lightbox video.is-portrait {
  width: auto;
  max-width: 96vw;
}

/* ---------- Panel debug (?debug=1) ---------- */

.debug {
  position: fixed;
  left: 8px;
  top: 8px;
  z-index: 90;
  max-width: 260px;
  padding: 8px 10px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.8);
  color: #9f9;
  font:
    12px/1.4 ui-monospace,
    monospace;
}

.debug button {
  font: inherit;
  margin: 2px 2px 0 0;
}

/* ---------- Desktop: kolom di tengah ---------- */

@media (min-width: 640px) {
  body {
    background:
      radial-gradient(circle at 50% 20%, color-mix(in srgb, var(--page) 70%, #fff) 0, var(--page) 70%) fixed,
      var(--page);
  }

  main {
    box-shadow: 0 0 60px rgba(0, 0, 0, 0.35);
  }
}

/* RSVP undangan terbit */
.captcha {
  min-height: 65px;
}
.button--link {
  background: none;
  border: 0;
  text-decoration: underline;
  color: var(--ink-muted);
  padding: 4px 0;
  min-height: 0;
}
.wishes[hidden],
.button--link[hidden] {
  display: none;
}

/* ======================================================================
 * Gaya Buku (Sinematik, demo & pratinjau `?gaya=buku`): docs/wireframe/gaya.md.
 * `html[data-gaya='buku']` dipasang main.ts selama mesin Sinematik aktif;
 * Klasik / animasi mati = dokumen biasa tanpa aturan di bawah ini.
 * ====================================================================== */

/* Muka buku sampul: hanya punya kotak di gaya Buku. */
.cover__book {
  display: contents;
}

html[data-gaya='buku'] {
  --foil: linear-gradient(176deg, #fff6ea 0%, #f3d9b8 38%, #d7a878 52%, #f1d6b4 70%, #fff1de 100%);
  --kertas-buku: color-mix(in srgb, var(--card-solid) 96%, #fff);
  --tinta-sampul: #fbeedd;
  --emas: #efd2a4;
  /* Kain sampul & papan buku: warna aksen preset. */
  --kain-buku: var(--accent);
}

/* ---------- Sampul: buku tertutup di meja (adegan pembuka diburamkan) ---------- */

html[data-gaya='buku'] .cover .poster img {
  filter: blur(12px) brightness(0.48) saturate(0.85);
  transform: scale(1.08);
}

html[data-gaya='buku'][data-level='ringan'] .cover .poster img {
  filter: brightness(0.42);
  transform: none;
}

html[data-gaya='buku'] .cover__content {
  justify-content: center;
}

html[data-gaya='buku'] .cover__content::before {
  flex-basis: 0;
}

html[data-gaya='buku'] .card--cover {
  width: 100%;
  padding: 0 16px;
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--tinta-sampul);
}

html[data-gaya='buku'] .card--cover > .corner {
  display: none;
}

/* Kain bersampul: punggung di kiri, tenunan, kilap lembut, garis timbul. Ukuran isi dalam --u (1/100 lebar buku), BUKAN cqw: di Safari satuan container query
   bisa terhitung 0 sebelum wadahnya punya ukuran → nama/tanggal tak terlihat sampai buku dibuka. */
html[data-gaya='buku'] .cover__book,
.buku-sampul {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* 1/100 lebar buku sampul (rumus yang sama dengan `width`). */
  --u: calc(min(70vw, calc((100svh - 250px) * 2 / 3), 340px) / 100);
  width: min(70vw, calc((100svh - 250px) * 2 / 3), 340px);
  aspect-ratio: 2 / 3;
  margin: 0 auto 18px;
  padding: calc(var(--u) * 8) calc(var(--u) * 8) calc(var(--u) * 8) calc(var(--u) * 14);
  border-radius: 2px 7px 7px 2px;
  color: var(--tinta-sampul);
  text-align: center;
  background-color: var(--kain-buku);
  background-image:
    linear-gradient(
      90deg,
      rgba(20, 8, 6, 0.42) 0,
      rgba(20, 8, 6, 0.12) 4.2%,
      rgba(255, 236, 220, 0.16) 5.6%,
      rgba(20, 8, 6, 0.22) 6.5%,
      rgba(20, 8, 6, 0.04) 8.2%,
      transparent 11%
    ),
    radial-gradient(140% 90% at 30% 18%, rgba(255, 240, 225, 0.16), transparent 60%),
    radial-gradient(120% 100% at 80% 110%, rgba(20, 8, 6, 0.3), transparent 60%),
    repeating-linear-gradient(0deg, rgba(255, 245, 235, 0.045) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(20, 8, 6, 0.055) 0 1px, transparent 1px 3px),
    var(--tekstur-kartu, none);
  background-size:
    auto,
    auto,
    auto,
    auto,
    auto,
    200px 200px;
  background-blend-mode: normal, normal, normal, normal, normal, multiply;
  box-shadow:
    inset 0 0 0 1px rgba(255, 235, 215, 0.08),
    inset 0 0 50px rgba(20, 8, 6, 0.32),
    0 22px 44px rgba(0, 0, 0, 0.45),
    0 5px 12px rgba(0, 0, 0, 0.3);
}

/* Garis timbul ganda di tepi dalam sampul. */
html[data-gaya='buku'] .cover__book::before,
html[data-gaya='buku'] .cover__book::after,
.buku-sampul::before,
.buku-sampul::after {
  content: '';
  position: absolute;
  inset: 5.5% 5.5% 5.5% 13%;
  border: 1px solid rgba(20, 8, 6, 0.32);
  box-shadow:
    0 1px 0 rgba(255, 236, 220, 0.14),
    inset 0 1px 0 rgba(255, 236, 220, 0.14);
  border-radius: 2px;
  pointer-events: none;
}

html[data-gaya='buku'] .cover__book::after,
.buku-sampul::after {
  inset: 7.2% 7.2% 7.2% 14.7%;
  border-color: rgba(20, 8, 6, 0.2);
}

html[data-gaya='buku'] .cover__book .eyebrow {
  margin: 0;
  font: 700 calc(var(--u) * 3.1)/1.3 var(--font-body);
  letter-spacing: 0.3em;
  color: rgba(251, 238, 221, 0.86);
}

html[data-gaya='buku'] .cover__book .monogram {
  width: calc(var(--u) * 46);
  margin: calc(var(--u) * 6) auto calc(var(--u) * 2);
}

html[data-gaya='buku'] .cover__book .monogram__frame img {
  filter: drop-shadow(0 1px 1px rgba(20, 8, 6, 0.35));
}

/*
 * Foil: warna emas padat + bayangan timbul. Bukan `background-clip: text` + `filter`:
 * Safari/iPhone tidak menggambar teksnya sama sekali (nama & inisial kosong sampai buku
 * dibuka), terutama di dalam flex seperti inisial monogram.
 */
html[data-gaya='buku'] .cover__book .monogram__text,
html[data-gaya='buku'] .cover__book h1.couple-title,
html[data-gaya='buku'] .cover__book .monogram__isi {
  background: none;
  color: var(--emas);
  -webkit-text-fill-color: var(--emas);
  -webkit-text-stroke: 0;
  text-shadow:
    0 1px 0 rgba(20, 8, 6, 0.5),
    0 -1px 0 rgba(255, 246, 234, 0.22);
  animation: none;
}

html[data-gaya='buku'] .cover__book .monogram__text {
  font-size: calc(var(--u) * 10.5);
}

/* Selama memuat: inisial garis tipis, salinannya terisi emas dari bawah (seperti Film). */
html[data-gaya='buku'].is-loading .cover__book .monogram__text:not(.monogram__isi) {
  color: transparent;
  -webkit-text-fill-color: transparent;
  -webkit-text-stroke: 1px rgba(251, 238, 221, 0.7);
  text-shadow: none;
}

html[data-gaya='buku'] .cover__book h1.couple-title {
  margin: calc(var(--u) * 3) 0 0;
  font-size: calc(var(--u) * 12.4);
  line-height: 1.04;
}

html[data-gaya='buku'] .cover__book .cover__date {
  margin: calc(var(--u) * 4) 0 0;
  font: 600 calc(var(--u) * 4.1)/1.3 var(--font-display);
  letter-spacing: 0.14em;
  color: rgba(251, 238, 221, 0.92);
}

html[data-gaya='buku'] .card--cover .cover__to,
html[data-gaya='buku'] .card--cover .cover__progress,
html[data-gaya='buku'] .card--cover .cover__skip {
  color: rgba(255, 244, 230, 0.84);
  text-shadow: 0 1px 8px rgba(20, 10, 6, 0.75);
}

html[data-gaya='buku'] .card--cover .guest-name {
  color: #fff6ea;
}

/* Buku di lapisan `.buku` sudah menggantikan buku sampul tepat di tempatnya. */
.cover.is-membuka .stage,
.cover.is-membuka .cover__book {
  visibility: hidden;
}

/* ---------- Ruang gulir perpindahan bagian (tingginya dari skrip) ---------- */

.buku-jeda {
  display: block;
  pointer-events: none;
}

/* ---------- Lapisan buku: meja, kamera, buku terbuka ---------- */

.buku {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 0;
  max-width: 600px;
  height: 100lvh;
  margin: 0 auto;
  overflow: hidden;
  pointer-events: none;
  visibility: hidden;
  background: #24140f;
}

.buku.is-tampil,
.buku.is-siap {
  visibility: visible;
}

.buku__meja {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.buku__meja-lapis {
  position: absolute;
  will-change: opacity;
}

/* Komposit adegan yang digambar ke kanvas kecil (buku.ts `tableArt`): sudah buram & redup, tanpa `filter`. */
.buku-meja-gambar {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.buku__meja::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(130% 95% at 50% 46%, rgba(40, 20, 14, 0.12), rgba(24, 12, 8, 0.72));
}

html[data-level='ringan'] .buku__meja::after {
  background: radial-gradient(130% 95% at 50% 46%, rgba(30, 16, 10, 0.52), rgba(20, 10, 6, 0.86));
}

.buku__kamera,
.buku__dunia,
.buku__badan {
  position: absolute;
  inset: 0;
}

.buku__kamera {
  transform-origin: 0 0;
  will-change: transform;
}

.buku__badan {
  transform-style: preserve-3d;
}

.buku__papan,
.buku__tumpuk,
.buku__halaman,
.buku__daun {
  position: absolute;
}

/* Papan sampul di bawah kedua halaman (kain). */
.buku__papan {
  border-radius: 4px;
  background-color: var(--kain-buku);
  background-image:
    repeating-linear-gradient(0deg, rgba(255, 245, 235, 0.04) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(20, 8, 6, 0.05) 0 1px, transparent 1px 3px),
    var(--tekstur-kartu, none);
  background-size:
    auto,
    auto,
    200px 200px;
  background-blend-mode: normal, normal, multiply;
  box-shadow:
    inset 0 0 30px rgba(20, 8, 6, 0.35),
    0 30px 60px rgba(0, 0, 0, 0.5),
    0 8px 18px rgba(0, 0, 0, 0.35);
}

/* Tumpukan halaman: tepi bergaris yang menyembul di bawah & sisi luar, setebal halaman yang tersisa. */
.buku__tumpuk {
  border-radius: 3px;
  background:
    linear-gradient(to bottom, rgba(62, 44, 42, 0.05), rgba(62, 44, 42, 0.28)),
    repeating-linear-gradient(90deg, #e9dac6 0 1px, #fbf4ea 1px 2.6px);
}

.buku__tumpuk--kiri {
  transform: translate(calc(-1 * var(--tebal-kiri, 2px)), var(--tebal-kiri, 2px));
}

.buku__tumpuk--kanan {
  transform: translate(var(--tebal-kanan, 2px), var(--tebal-kanan, 2px));
}

.buku__halaman,
.buku__daun {
  container-type: inline-size;
  overflow: hidden;
  background: var(--kertas-buku);
}

.buku__halaman--kiri,
.buku__daun--balik {
  border-radius: 4px 0 0 4px;
}

.buku__halaman--kanan,
.buku__daun {
  border-radius: 0 4px 4px 0;
}

.buku__daun {
  transform-origin: 0 50%;
}

.buku__daun--balik {
  transform-origin: 100% 50%;
}

/* Lengkung halaman di dekat punggung buku. */
.buku__halaman::after,
.buku__daun::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: linear-gradient(to right, rgba(60, 38, 28, 0.36), rgba(60, 38, 28, 0.08) 9%, transparent 18%);
}

.buku__halaman--kiri::after,
.buku__daun--balik::after {
  background: linear-gradient(to left, rgba(60, 38, 28, 0.36), rgba(60, 38, 28, 0.08) 9%, transparent 18%);
}

.buku__redup {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: #1e0f0a;
  opacity: 0;
  pointer-events: none;
}

/* Keadaan buku: tertutup (sampul di atas halaman kanan) & tertutup di kiri (sampul belakang). */
.buku[data-keadaan='tertutup'] .buku__papan,
.buku[data-keadaan='tertutup'] .buku__tumpuk--kiri,
.buku[data-keadaan='tertutup'] .buku__halaman--kiri,
.buku[data-keadaan='tertutup-kiri'] .buku__papan,
.buku[data-keadaan='tertutup-kiri'] .buku__tumpuk--kanan,
.buku[data-keadaan='tertutup-kiri'] .buku__halaman--kanan {
  visibility: hidden;
}

/* Muka sampul di daun buku: mengisi daun, tanpa bayangan lepas (buku sendiri yang berbayang). */
html[data-gaya='buku'] .buku__daun > .buku-sampul {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  border-radius: inherit;
  box-shadow:
    inset 0 0 0 1px rgba(255, 235, 215, 0.08),
    inset 0 0 50px rgba(20, 8, 6, 0.32);
}

/* Sampul belakang: punggung di kanan (buku tertutup tergeletak terbalik). */
.buku-sampul--belakang {
  transform: scaleX(-1);
}

/* Halaman bab (kiri) & halaman bergambar (kanan): kertas buku. */
.buku-bab,
.buku-gambar {
  position: absolute;
  inset: 0;
  background-color: var(--kertas-buku);
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--kertas-buku) 55%, transparent),
      color-mix(in srgb, var(--kertas-buku) 55%, transparent)
    ),
    var(--tekstur-kartu, none);
  background-size:
    auto,
    220px 220px;
}

.buku-bab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 12cqw 10cqw;
  text-align: center;
  color: var(--ink);
}

.buku-bab p {
  margin: 0;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);
}

.buku-bab__ikon {
  width: 15cqw;
  aspect-ratio: 148 / 192;
  margin-bottom: 7cqw;
  background: var(--ikon-ornamen, none) center / contain no-repeat;
  opacity: 0.85;
}

.buku-bab__nomor {
  font: 700 4.4cqw/1.2 var(--font-body);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.buku-bab .buku-bab__judul {
  margin-top: 3cqw;
  font: 600 12cqw/1.08 var(--font-display);
  color: var(--accent);
}

.buku-bab__pembatas {
  width: 64%;
  height: 10cqw;
  margin: 4cqw 0 0;
  background: var(--pembatas-1, none) center / contain no-repeat;
}

.buku-bab .buku-bab__hal {
  position: absolute;
  bottom: 7cqw;
  left: 0;
  right: 0;
  font: italic 400 4.2cqw/1 var(--font-display);
  color: var(--ink-muted);
}

/* Gambar adegan rata bertepi kertas. */
.buku-gambar__bingkai {
  position: absolute;
  inset: 7%;
  overflow: hidden;
  box-shadow:
    0 0 0 1px rgba(60, 40, 30, 0.22),
    0 2px 6px rgba(60, 40, 30, 0.22);
}

.buku-adegan {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.buku-adegan > img {
  position: absolute;
  display: block;
  max-width: none;
}

/* Gambar halaman = salinan hidup panggung adegannya (buku.ts `Mirror`), diperkecil ke kotak gambar. */
.buku-panggung {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
}

/*
 * Di dalam buku, tekstur kertas/kain dipasang sebagai <img> (buku.ts `texture`): tekstur latar CSS
 * digambar ulang tiap frame selama kamera buku zoom (±6× lebih mahal).
 */
.buku {
  --tekstur-kartu: none;
}

.buku__papan,
.buku-bab,
.buku-gambar,
.buku .buku-sampul {
  isolation: isolate;
}

.buku-tekstur {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  border-radius: inherit;
  pointer-events: none;
}

/* Kertas: tekstur di bawah lapisan warna kertas 55% (sama dengan gradasi di atas tekstur). */
.buku-tekstur--kertas {
  opacity: 0.45;
}

/* Kain sampul: tekstur dikalikan dengan warna aksen. */
.buku-tekstur--kain {
  mix-blend-mode: multiply;
}

.buku__kelopak {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.buku__kelopak img {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  will-change: transform, opacity;
}

/* ---------- Isi di dalam buku: kertas buku ---------- */

html[data-gaya='buku'] .scene:not(.cover) .card {
  border: 0;
  border-radius: 3px;
  background-color: color-mix(in srgb, var(--kertas-buku) 95%, transparent);
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--kertas-buku) 55%, transparent),
      color-mix(in srgb, var(--kertas-buku) 55%, transparent)
    ),
    var(--tekstur-kartu, none);
  background-size:
    auto,
    220px 220px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    0 14px 34px rgba(30, 18, 14, 0.3),
    0 3px 8px rgba(30, 18, 14, 0.18);
}

html[data-gaya='buku'].stages-fixed .section-title {
  padding: 14px 30px 12px;
  border-radius: 2px;
  background-color: var(--kertas-buku);
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--kertas-buku) 55%, transparent),
      color-mix(in srgb, var(--kertas-buku) 55%, transparent)
    ),
    var(--tekstur-kartu, none);
  background-size:
    auto,
    220px 220px;
  box-shadow:
    0 12px 28px rgba(30, 18, 14, 0.32),
    0 2px 6px rgba(30, 18, 14, 0.2);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);
}

/* Keterangan adegan terarah: secarik kertas yang menempel di bawah. */
html[data-gaya='buku'].stages-fixed .scene--directed .shot > *,
html[data-gaya='buku'].stages-fixed .scene--directed .story__chapter {
  margin: 0 10px;
  padding: 22px 24px 16px;
  border-radius: 3px;
  background-color: color-mix(in srgb, var(--kertas-buku) 96%, transparent);
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--kertas-buku) 55%, transparent),
      color-mix(in srgb, var(--kertas-buku) 55%, transparent)
    ),
    var(--tekstur-kartu, none);
  background-size:
    auto,
    220px 220px;
  box-shadow:
    0 14px 30px rgba(30, 18, 14, 0.34),
    0 3px 8px rgba(30, 18, 14, 0.2);
}

/* Layar akhir: ruang gulir kamera keluar & buku menutup, lalu judul di layar terakhir. */
html[data-gaya='buku'] .finale {
  display: grid;
  grid-template-rows: 150lvh 100svh;
  align-items: center;
  justify-items: center;
  min-height: 0;
}

/*
 * Judul tercetak foil di sampul belakang: selebar bagian dalam bingkai foil (sampul belakang
 * dicerminkan: punggung & tepi bingkai yang lebar di kanan, jadi isinya sedikit ke kiri).
 * Lebar buku = rumus yang sama dengan `.buku-sampul`.
 */
html[data-gaya='buku'] .finale__card {
  --lebar-buku: min(70vw, calc((100svh - 250px) * 2 / 3), 340px);
  width: calc(var(--lebar-buku) * 0.72);
  max-width: calc(var(--lebar-buku) * 0.72);
  translate: calc(var(--lebar-buku) * -0.04) 0;
  padding: 24px 0;
  background: none;
  text-shadow: 0 1px 0 rgba(20, 8, 6, 0.45);
}

html[data-gaya='buku'] .finale__eyebrow,
html[data-gaya='buku'] .finale__date {
  color: rgba(251, 238, 221, 0.9);
}

html[data-gaya='buku'] .finale__names {
  /* Nama panjang turun ke baris kedua, tidak melewati bingkai. */
  font-size: clamp(1.6rem, calc(var(--lebar-buku) * 0.11), 2.3rem);
  text-wrap: balance;
  overflow-wrap: anywhere;
  color: var(--emas);
  -webkit-text-fill-color: var(--emas);
  text-shadow:
    0 1px 0 rgba(20, 8, 6, 0.5),
    0 -1px 0 rgba(255, 246, 234, 0.22);
}

html[data-gaya='buku'] .finale__replay {
  border-color: transparent;
  background: var(--kertas-buku);
  color: var(--accent);
  box-shadow: 0 8px 18px rgba(20, 8, 6, 0.35);
}

/* ======================================================================
 * Gaya Lukisan Hidup (Sinematik, `?gaya=lukisan`): docs/wireframe/gaya.md.
 * Lukisan rata di atas kertas krem: sketsa pensil → diwarnai lapis demi lapis.
 * ====================================================================== */

/*
 * Kertas ikut warna kartu (bukan latar halaman): tulisan memakai tinta kartu, jadi preset berkartu
 * terang di halaman gelap (mis. Gading Terang di Ballroom Malam) tetap terbaca.
 */
html[data-gaya='lukisan'] {
  --kertas-lukis: color-mix(in srgb, var(--card-solid) 78%, #fffaf3);
  --tinta-pensil: #4a3a32;
}

/* Kertas: latar panggung, sampul, kertas penutup, dan siluet kertas tiap lapisan. */
html[data-gaya='lukisan'].stages-fixed main > .scene:not(.cover) > .stage,
html[data-gaya='lukisan'] .cover,
.lukisan-tirai__kertas,
.lukisan-kertas {
  background-color: var(--kertas-lukis);
  background-image: var(--tekstur-kartu, none);
  background-size: 260px 260px;
  background-blend-mode: multiply;
}

/* Siluet kertas tiap lapisan: menutup garis lapisan di belakangnya selama tahap sketsa. */
.lukisan-kertas {
  position: absolute;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.lukisan-sketsa {
  position: absolute;
  display: block;
  max-width: none;
  pointer-events: none;
}

/*
 * Kertas penutup (lukisan.ts `curtain`): mencuci lukisan lama / membuka sketsa baru. Hanya
 * transform & opacity yang berubah tiap frame (topeng kuasnya tetap), jadi tidak ada yang
 * digambar ulang selama perpindahan.
 */
/*
 * Topeng kuas dipanaskan (lukisan.ts `warmBrushes`): 1 px nyaris transparan, tetap digambar supaya
 * ketiga topeng sudah diunduh & didekode sebelum sapuan pertama.
 */
.lukisan-kuas-hangat {
  position: fixed;
  left: 0;
  top: 0;
  width: 1px;
  height: 1px;
  background: #000;
  opacity: 0.01;
  pointer-events: none;
}

.lukisan-tirai {
  position: absolute;
  left: 0;
  top: 0;
  visibility: hidden;
  pointer-events: none;
}

.lukisan-tirai__sapu,
.lukisan-tirai__kertas {
  position: absolute;
  inset: 0;
}

/* Warna tiap lapisan: sapuan bertopeng kuas tetap (bergeser) > isi (diam di tempat) > gambar. */
.lukisan-sapu,
.lukisan-isi,
.lukisan-isi > picture {
  position: absolute;
  inset: 0;
  display: block;
}

/*
 * Selama perpindahan & pembuka: kertas penutup digerakkan kompositor saja (sapuan warna tiap
 * lapisan dipromosikan oleh skrip hanya selama sapuannya berjalan).
 */
.stage.is-pindah .lukisan-tirai,
.stage.is-pindah .lukisan-tirai__kertas {
  will-change: transform, opacity;
}

html[data-gaya='lukisan'] .stage.is-pindah > .grades,
html[data-gaya='lukisan'] .stage.is-pindah .glows,
html[data-gaya='lukisan'] .stage.is-pindah .photo__img img,
.stage.is-pindah .lukisan-sketsa-foto {
  will-change: opacity;
}

.lukisan-jeda {
  display: block;
  pointer-events: none;
}

/* Foto pasangan: sketsa di bawah, foto berwarna memudar masuk di atasnya (`--warna` dari skrip). */
.gaya-lukisan .photo > .photo__img:not(.photo__img--more) {
  position: relative;
}

.gaya-lukisan .photo-rig .photo__img img {
  opacity: var(--warna, 1);
}

.lukisan-sketsa-foto {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  opacity: calc(1 - var(--warna, 1) * 0.6);
}

/* Foto tidak bisa disketsa (foto beda domain tanpa izin baca): cetakan sepia lalu diwarnai. */
.gaya-lukisan .lukisan-foto--sepia .photo__img img {
  opacity: 1;
  filter: sepia(calc(1 - var(--warna, 1))) saturate(var(--warna, 1))
    contrast(calc(0.8 + var(--warna, 1) * 0.2));
}

/* ---------- Sampul: kertas kosong, monogram tersketsa, nama ditulis tangan ---------- */

html[data-gaya='lukisan'] .cover .stage {
  visibility: hidden;
}

html[data-gaya='lukisan'] .card--cover {
  border: 0;
  background: none;
  box-shadow: none;
}

html[data-gaya='lukisan'] .card--cover > .corner {
  display: none;
}

/* Bingkai monogram: garis pensil sepia selama undangan dimuat, lalu diwarnai. */
html[data-gaya='lukisan'] .cover .monogram__frame img {
  filter: grayscale(1) sepia(0.45) contrast(1.35) brightness(1.08);
  opacity: 0.85;
  transition:
    filter 1.4s ease,
    opacity 1.4s ease;
}

html[data-gaya='lukisan'].is-loaded .cover .monogram__frame img {
  filter: none;
  opacity: 1;
}

/* Nama: garis tinta tipis dulu, lalu terisi warna (seperti ditulis lalu ditebalkan). */
html[data-gaya='lukisan'] .cover h1.couple-title {
  font-style: italic;
  background: none;
  animation: none;
  color: transparent;
  -webkit-text-fill-color: transparent;
  -webkit-text-stroke: 0.7px var(--tinta-pensil);
  transition:
    color 1.2s ease,
    -webkit-text-fill-color 1.2s ease;
}

html[data-gaya='lukisan'].is-loaded .cover h1.couple-title,
html[data-gaya='lukisan']:not(.is-loading) .cover h1.couple-title {
  color: var(--accent);
  -webkit-text-fill-color: var(--accent);
  -webkit-text-stroke: 0;
}

html[data-gaya='lukisan'] .card--cover .eyebrow,
html[data-gaya='lukisan'] .card--cover .cover__date {
  letter-spacing: 0.18em;
}

/* ---------- Isi: kertas kanvas, judul ditulis tangan bergaris bawah ---------- */

html[data-gaya='lukisan'] .scene:not(.cover) .card {
  border: 0;
  border-radius: 2px;
  background-color: color-mix(in srgb, var(--kertas-lukis) 94%, transparent);
  background-image: var(--tekstur-kartu, none);
  background-size: 260px 260px;
  background-blend-mode: multiply;
  box-shadow:
    0 10px 26px rgba(40, 26, 20, 0.2),
    0 2px 6px rgba(40, 26, 20, 0.14);
}

html[data-gaya='lukisan'].stages-fixed .section-title {
  font-style: italic;
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--kertas-lukis) 86%, transparent) 50%,
    transparent
  );
  text-shadow: 0 0 14px color-mix(in srgb, var(--kertas-lukis) 80%, transparent);
}

html[data-gaya='lukisan'].stages-fixed .section-title::after {
  content: '';
  height: 18px;
  background: var(--pembatas-1, var(--title-divider, none)) center / contain no-repeat;
}

/* Keterangan adegan terarah: sapuan cat krem di belakang tulisan. */
html[data-gaya='lukisan'].stages-fixed .scene--directed .shot > *,
html[data-gaya='lukisan'].stages-fixed .scene--directed .story__chapter {
  background: radial-gradient(
    ellipse 75% 70% at 50% 62%,
    color-mix(in srgb, var(--kertas-lukis) 94%, transparent) 55%,
    color-mix(in srgb, var(--kertas-lukis) 55%, transparent) 78%,
    transparent
  );
}

html[data-gaya='lukisan'].stages-fixed .scene--directed .person__nickname {
  font-style: italic;
}

/* Layar akhir: nama sebagai tanda tangan. */
html[data-gaya='lukisan'] .finale__card {
  text-shadow:
    0 0 2px color-mix(in srgb, var(--kertas-lukis) 90%, transparent),
    0 0 8px color-mix(in srgb, var(--kertas-lukis) 85%, transparent),
    0 0 20px color-mix(in srgb, var(--kertas-lukis) 70%, transparent);
  background: radial-gradient(
    ellipse closest-side,
    color-mix(in srgb, var(--kertas-lukis) 90%, transparent) 55%,
    color-mix(in srgb, var(--kertas-lukis) 66%, transparent) 80%,
    color-mix(in srgb, var(--kertas-lukis) 28%, transparent) 93%,
    transparent
  );
}

html[data-gaya='lukisan'] .finale__names {
  font-style: italic;
}

/* ======================================================================
 * Gaya Teater Kertas (Sinematik, `?gaya=teater`): docs/wireframe/gaya.md.
 * Gedung panggung tetap membingkai undangan; adegan = set flat kertas.
 * ====================================================================== */

html[data-gaya='teater'] {
  --beludru: color-mix(in srgb, var(--accent) 50%, #1c070a);
  --beludru-terang: color-mix(in srgb, var(--accent) 78%, #2a0d10);
  --kuningan: linear-gradient(180deg, #f8e2b8 0%, #d9ad6c 45%, #b98a4a 55%, #f1d39c 100%);
}

.teater-jeda {
  display: block;
  pointer-events: none;
}

/* Selama ganti set: flat & foto pasangan yang meluncur digerakkan kompositor (tanpa gambar ulang). */
html[data-gaya='teater'] .stage.is-pindah .layer__fly,
html[data-gaya='teater'] .stage.is-pindah > .canvas > .photo-rig {
  will-change: transform;
}

/* Dinding belakang panggung (tampak saat set berganti): beludru tema yang digelapkan. */
html[data-gaya='teater'].stages-fixed main > .scene:not(.cover) > .stage {
  background: radial-gradient(
    ellipse 80% 70% at 50% 60%,
    color-mix(in srgb, var(--beludru) 55%, #000),
    color-mix(in srgb, var(--beludru) 22%, #000) 70%
  );
}

/* Pergantian Calm: set baru turun di depan set lama, jadi tanpa dinding belakang sendiri. */
html[data-gaya='teater'].stages-fixed main > .scene:not(.cover) > .stage.teater-tumpuk {
  background: none;
}

/* Gedung: di atas sampul & isi (bingkai), di bawah kontrol; tidak bisa diketuk. */
.teater-gedung {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 41;
  max-width: 600px;
  height: 100lvh;
  margin: 0 auto;
  pointer-events: none;
}

/* Lampu meredup selama set berganti (opacity dari skrip). */
.teater-redup {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 60% at 50% 55%, rgba(10, 4, 4, 0.15), rgba(10, 4, 4, 0.75));
  opacity: 0;
  will-change: opacity;
}

/* Tirai beludru terikat: lebar di atas, menyempit ke bawah. */
.teater-tirai {
  position: absolute;
  top: 0;
  bottom: 0;
  width: clamp(18px, 7%, 44px);
  background:
    repeating-linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.32) 0 3px,
      rgba(255, 220, 200, 0.08) 3px 7px,
      rgba(0, 0, 0, 0.18) 7px 11px
    ),
    linear-gradient(180deg, var(--beludru-terang), var(--beludru) 60%, #0d0304);
  filter: drop-shadow(0 0 10px rgba(0, 0, 0, 0.55));
}

.teater-tirai--kiri {
  left: 0;
  clip-path: polygon(0 0, 100% 0, 100% 18%, 62% 44%, 42% 70%, 34% 100%, 0 100%);
}

.teater-tirai--kanan {
  right: 0;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 66% 100%, 58% 70%, 38% 44%, 0 18%);
}

/* Lambrekin bergelombang dengan lambang monogram di tengah. */
.teater-lambrekin {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: max(46px, 7svh);
  background:
    repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.25) 0 4px, rgba(255, 220, 200, 0.07) 4px 9px),
    linear-gradient(180deg, #120405, var(--beludru-terang) 70%, var(--beludru));
  -webkit-mask: radial-gradient(22px 12px at 50% 100%, transparent 98%, #000) 50% 0 / 44px 100%;
  mask: radial-gradient(22px 12px at 50% 100%, transparent 98%, #000) 50% 0 / 44px 100%;
  border-bottom: 2px solid #c9995a;
}

.teater-lambrekin .teater-lambang {
  position: absolute;
  left: 50%;
  top: 4px;
  width: max(40px, 6svh);
  margin: 0;
  transform: translateX(-50%);
}

.teater-lambang .monogram__text {
  font-size: max(0.7rem, 1.1svh);
  color: #f6dfb4;
}

/* Lambang tetap terisi sejak awal (tidak ikut garis tipis indikator muat sampul, lalu terisi). */
html .teater-lambrekin .teater-lambang .monogram__text {
  color: #f6dfb4;
  -webkit-text-fill-color: #f6dfb4;
  -webkit-text-stroke: 0;
  background: none;
  animation: none;
}

/* Lambang dari logo unggahan pasangan: utuh di dalam lambrekin (tidak terpotong tepi bawahnya). */
.teater-lambrekin .teater-lambang.monogram--logo {
  top: 3px;
  width: max(56px, 9svh);
}

.teater-lambang .logo-nama img {
  max-height: calc(max(46px, 7svh) - 12px);
  margin: 0 auto;
  width: auto;
  max-width: 100%;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5));
}

/* Isi monogram nama (dua baris) dikecilkan supaya muat di lambang. */
.teater-lambang.monogram--nama .monogram__text {
  font-size: min(max(0.5rem, 0.8svh), calc(3.6rem / var(--nama-len, 6)));
}

/*
 * Papan judul babak: plakat kuningan bertali ke loteng, di tengah panggung (di depan tirai
 * utama). Hanya tampil selama tirai bekerja: turun saat tirai tertutup, tergantung selama
 * tirai terbuka, lalu ditarik naik sampai hilang (posisi dari skrip).
 */
.teater-papan {
  position: absolute;
  left: 50%;
  top: 44%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: min(62vw, 250px);
  max-width: 86%;
  padding: 12px 30px 13px;
  border-radius: 4px;
  transform: translate(-50%, -50%);
  transform-origin: 50% 0;
  background: var(--kuningan);
  box-shadow:
    0 10px 22px rgba(0, 0, 0, 0.45),
    inset 0 0 0 2px rgba(90, 55, 20, 0.4),
    inset 0 0 0 5px rgba(248, 226, 184, 0.35);
  color: #3b2410;
  text-align: center;
  text-shadow: 0 1px 0 rgba(255, 242, 205, 0.55);
  visibility: hidden;
  will-change: transform;
}

/* Dua tali ke loteng (ikut bergerak bersama papan). */
.teater-papan::before {
  content: '';
  position: absolute;
  bottom: calc(100% - 4px);
  left: 20%;
  right: 20%;
  height: 110lvh;
  background:
    repeating-linear-gradient(180deg, #7a5a3a 0 3px, #4a3220 3px 6px) left / 2px 100% no-repeat,
    repeating-linear-gradient(180deg, #7a5a3a 0 3px, #4a3220 3px 6px) right / 2px 100% no-repeat;
  pointer-events: none;
}

.teater-papan__nomor {
  font: 700 0.66rem/1.3 var(--font-body);
  letter-spacing: 0.32em;
  text-transform: uppercase;
}

.teater-papan__judul {
  font: 600 clamp(1.5rem, 7.4vw, 2.1rem) / 1.12 var(--font-display);
  text-wrap: balance;
}

/*
 * Tirai utama: dua panel beludru menutup/membuka di depan panggung. Di bawah tirai samping,
 * lambrekin & papan nama sampul (gedung z 41, sampul z 40), di bawah dock (z 30), di atas isi.
 * Terbuka = panel menyempit ke tepi luar, terkumpul di balik tirai samping.
 */
.teater-layar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 25;
  max-width: 600px;
  height: 100lvh;
  margin: 0 auto;
  overflow: hidden;
  pointer-events: none;
  visibility: hidden;
}

.teater-layar__panel {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 51%;
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.35), transparent 18%, transparent 82%, rgba(0, 0, 0, 0.45)),
    repeating-linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.34) 0 6px,
      rgba(255, 220, 200, 0.1) 6px 14px,
      rgba(0, 0, 0, 0.16) 14px 22px
    ),
    linear-gradient(180deg, var(--beludru-terang), var(--beludru) 55%, #140405);
  box-shadow: 0 0 24px rgba(0, 0, 0, 0.55);
  will-change: transform;
}

.teater-layar__panel--kiri {
  left: 0;
  transform-origin: 0 50%;
}

.teater-layar__panel--kanan {
  right: 0;
  transform-origin: 100% 50%;
}

/* Sampul: tirai tertutup di belakang papan nama (bukan poster adegan). */
html.gaya-teater .cover .stage {
  visibility: hidden;
}

/* Lampu kaki: pendar hangat sepanjang tepi bawah panggung. */
.teater-lampu {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 10px;
  background:
    radial-gradient(circle at 50% 100%, rgba(255, 214, 150, 0.9), transparent 70%) 0 100% / 36px 10px repeat-x,
    linear-gradient(180deg, transparent, #1a0b06);
}

/* ---------- Sampul: pintu panggung tertutup, papan nama bertali ---------- */

html[data-gaya='teater'] .card--cover,
html[data-gaya='teater'] .finale__card,
html[data-gaya='teater'].stages-fixed .section-title {
  position: relative;
  border: 0;
  border-radius: 4px;
  background-color: var(--card-solid);
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--card-solid) 60%, transparent),
      color-mix(in srgb, var(--card-solid) 60%, transparent)
    ),
    var(--tekstur-kartu, none);
  background-size:
    auto,
    220px 220px;
  box-shadow:
    inset 0 0 0 3px color-mix(in srgb, var(--line) 70%, transparent),
    0 16px 30px rgba(10, 4, 4, 0.45);
}

/* Tali papan ke loteng. */
html[data-gaya='teater'] .card--cover::before,
html[data-gaya='teater'] .finale__card::before,
html[data-gaya='teater'].stages-fixed .section-title::before {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 18%;
  right: 18%;
  height: 100vh;
  border-left: 1.5px solid rgba(60, 40, 30, 0.75);
  border-right: 1.5px solid rgba(60, 40, 30, 0.75);
  pointer-events: none;
}

html[data-gaya='teater'] .card--cover > .corner {
  display: none;
}

html[data-gaya='teater'].stages-fixed .section-title {
  padding: 12px 28px 10px;
}

/* Papan judul turun dari loteng saat bagiannya datang. */
html[data-gaya='teater'].enhanced.stages-fixed .scene:not(.cover) .section-title {
  transform: translateY(-36px);
  transition:
    opacity 0.9s ease,
    transform 1s cubic-bezier(0.34, 1.56, 0.64, 1);
}

html[data-gaya='teater'].enhanced.stages-fixed .scene.is-seen .section-title {
  transform: none;
}

/* Judul bagian yang sudah tampil di papan judul babak tidak tampil lagi (tetap terbaca pembaca layar). */
html[data-gaya='teater'].stages-fixed .teater-judul-papan .section-title {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Isi: kartu "buku acara" (kertas program bertepi). */
html[data-gaya='teater'] .scene:not(.cover) .card {
  border: 0;
  border-radius: 3px;
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--line) 75%, transparent),
    inset 0 0 0 6px var(--card-solid),
    inset 0 0 0 7px color-mix(in srgb, var(--line) 45%, transparent),
    0 14px 28px rgba(10, 4, 4, 0.35);
}

/* Keterangan adegan terarah: papan kecil bertali. */
html[data-gaya='teater'].stages-fixed .scene--directed .shot > *,
html[data-gaya='teater'].stages-fixed .scene--directed .story__chapter {
  margin: 0 22px;
  padding: 20px 22px 14px;
  border-radius: 4px;
  background: var(--card-solid);
  box-shadow:
    inset 0 0 0 3px color-mix(in srgb, var(--line) 70%, transparent),
    0 14px 28px rgba(10, 4, 4, 0.4);
}

html[data-gaya='teater'] .finale__card {
  max-width: min(80vw, 320px);
  padding: 28px 20px;
}

/* Keterangan singkat di bawah foto babak (cerita panjang) memakai kulit keterangan gayanya. */
html[data-gaya='buku'].stages-fixed .scene--directed .story__chapter--long .story__ringkas {
  border-radius: 3px;
  background-color: color-mix(in srgb, var(--kertas-buku) 96%, transparent);
  box-shadow: 0 8px 18px rgba(30, 18, 14, 0.3);
}

html[data-gaya='lukisan'].stages-fixed .scene--directed .story__chapter--long .story__ringkas {
  border-radius: 40% 46% 42% 48% / 60% 52% 58% 50%;
  background: color-mix(in srgb, var(--kertas-lukis) 94%, transparent);
  box-shadow: none;
}

html[data-gaya='teater'].stages-fixed .scene--directed .story__chapter--long .story__ringkas {
  border-radius: 4px;
  background: var(--card-solid);
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--line) 70%, transparent),
    0 10px 20px rgba(10, 4, 4, 0.4);
}

/* ---------- Penyempurnaan G4 ---------- */

/* Buku Cerita: foto babak Kisah & foto Galeri ditempel dengan sudut foto (kertas gelap). */
html[data-gaya='buku'] .story__photo .framed:not(.framed--cutout) .framed__photo::after,
html[data-gaya='buku'] .gtile:not(.gtile--cutout)::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  --sudut: color-mix(in srgb, var(--ink) 82%, #000);
  background:
    linear-gradient(135deg, var(--sudut) 0 50%, transparent 50%) top left / 26px 26px no-repeat,
    linear-gradient(225deg, var(--sudut) 0 50%, transparent 50%) top right / 26px 26px no-repeat,
    linear-gradient(45deg, var(--sudut) 0 50%, transparent 50%) bottom left / 26px 26px no-repeat,
    linear-gradient(315deg, var(--sudut) 0 50%, transparent 50%) bottom right / 26px 26px no-repeat;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
}

html[data-gaya='buku'] .story__photo .framed:not(.framed--cutout) .framed__photo::after {
  background-size: 18px 18px;
}

/* Lukisan Hidup: foto babak muncul sebagai cetakan pensil, lalu diwarnai. */
html[data-gaya='lukisan'] .story__photo img {
  transition: filter 1.3s ease 0.35s;
}

html[data-gaya='lukisan'] .story__photo:not(.is-shown) img {
  filter: grayscale(1) sepia(0.45) contrast(1.4) brightness(1.1);
  transition: none;
}

/* ---------- Isi adegan terarah: wadahnya bukan kartu (semua gaya) ---------- */

/*
 * Mempelai & Kisah terarah: `.couple` / `.story` (berkelas `.card`) jadi rangkaian adegan
 * setinggi beberapa layar. Kulit kartu gaya (kertas Buku/Lukisan, garis program Teater)
 * tidak boleh mengenainya, kalau tidak seluruh adegan tertutup kertas polos.
 */
html[data-gaya].stages-fixed .scene--directed .couple,
html[data-gaya].stages-fixed .scene--directed .story {
  border: 0;
  background: none;
  box-shadow: none;
}

/* ---------- Buku Cerita: judul bab terbang ke tengah adegan ---------- */

/* Judul yang ikut tergulir disembunyikan: judulnya sudah tampil terbang dari halaman buku. */
html[data-gaya='buku'].stages-fixed .buku-judul-terbang .section-title {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.buku-terbang {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 18px 34px 16px;
  pointer-events: none;
  visibility: hidden;
  will-change: transform, opacity;
  text-align: center;
}

/* Papan kertas di belakang judul (muncul selama terbang) supaya terbaca di atas adegan. */
.buku-terbang__latar {
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 3px;
  background-color: var(--kertas-buku, var(--card-solid));
  background-image: var(--tekstur-kartu, none);
  background-size: 220px 220px;
  background-blend-mode: multiply;
  box-shadow:
    0 14px 34px rgba(30, 18, 14, 0.38),
    0 3px 8px rgba(30, 18, 14, 0.22),
    inset 0 0 0 1px color-mix(in srgb, var(--line) 55%, transparent);
  opacity: 0;
}

.buku-terbang__nomor {
  margin: 0 0 4px;
  font: 700 0.72rem/1.2 var(--font-body);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-muted);
  opacity: 0;
}

.buku-terbang__judul {
  margin: 0;
  font: 600 clamp(2.4rem, 11vw, 3.4rem) / 1.08 var(--font-display);
  color: var(--accent);
  white-space: nowrap;
}

/* Salinan sampul di daun buku: 1/100 lebar halaman (diisi skrip sebagai --halaman). */
html[data-gaya='buku'] .buku .cover__book,
.buku .buku-sampul {
  --u: calc(var(--halaman, 200px) / 100);
}

/* ======================================================================
 * Aset gambar gaya per tema (`docs/eksplorasi-3d/13-aset-gaya.md`): berlaku hanya bila tema
 * punya asetnya (`<html data-aset-gaya="buku teater lukisan">`, variabel `--gaya-<gaya>-<nama>`
 * dari render). Tema tanpa aset tetap memakai gambar CSS di atas.
 * ====================================================================== */

/* ---------- Buku Cerita ---------- */

/* Kertas halaman, kartu, judul & keterangan: tekstur kertas buku tema. */
html[data-gaya='buku'][data-aset-gaya~='buku'] {
  --tekstur-kartu: var(--gaya-buku-kertas-halaman);
}

/* Kain sampul: tenunan linen tema diwarnai aksen (overlay: warna aksen tetap, serat terlihat). */
html[data-gaya='buku'][data-aset-gaya~='buku'] .cover__book,
html[data-aset-gaya~='buku'] .buku-sampul {
  background-image:
    linear-gradient(
      90deg,
      rgba(20, 8, 6, 0.42) 0,
      rgba(20, 8, 6, 0.12) 4.2%,
      rgba(255, 236, 220, 0.16) 5.6%,
      rgba(20, 8, 6, 0.22) 6.5%,
      rgba(20, 8, 6, 0.04) 8.2%,
      transparent 11%
    ),
    radial-gradient(140% 90% at 30% 18%, rgba(255, 240, 225, 0.14), transparent 60%),
    radial-gradient(120% 100% at 80% 110%, rgba(20, 8, 6, 0.3), transparent 60%), var(--gaya-buku-kain-sampul);
  background-size:
    auto,
    auto,
    auto,
    240px 240px;
  background-blend-mode: normal, normal, normal, overlay;
}

/* Bingkai emas timbul di sampul (menggantikan garis timbul CSS). */
html[data-gaya='buku'][data-aset-gaya~='buku'] .cover__book::before,
html[data-aset-gaya~='buku'] .buku-sampul::before {
  inset: 4.5% 4.5% 4.5% 12%;
  border: 0;
  box-shadow: none;
  background: var(--gaya-buku-bingkai-foil) center / 100% 100% no-repeat;
  filter: drop-shadow(0 1px 1px rgba(20, 8, 6, 0.4));
}

html[data-gaya='buku'][data-aset-gaya~='buku'] .cover__book::after,
html[data-aset-gaya~='buku'] .buku-sampul::after {
  display: none;
}

/* Tekstur di buku (img): kain berserat lewat overlay, kertas lebih nyata. */
html[data-aset-gaya~='buku'] .buku-tekstur--kain {
  mix-blend-mode: overlay;
}

html[data-aset-gaya~='buku'] .buku-tekstur--kertas {
  opacity: 0.8;
}

/* Sudut foto tempel: gambar kertas sudut di keempat sudut. */
html[data-gaya='buku'][data-aset-gaya~='buku']
  .story__photo
  .framed:not(.framed--cutout)
  .framed__photo::after,
html[data-gaya='buku'][data-aset-gaya~='buku'] .gtile:not(.gtile--cutout)::after {
  background:
    var(--gaya-buku-sudut-foto) top left / 28px 28px no-repeat,
    var(--gaya-buku-sudut-foto-kanan-atas) top right / 28px 28px no-repeat,
    var(--gaya-buku-sudut-foto-kiri-bawah) bottom left / 28px 28px no-repeat,
    var(--gaya-buku-sudut-foto-kanan-bawah) bottom right / 28px 28px no-repeat;
}

html[data-gaya='buku'][data-aset-gaya~='buku']
  .story__photo
  .framed:not(.framed--cutout)
  .framed__photo::after {
  background-size: 20px 20px;
}

/* ---------- Teater Kertas ---------- */

/* Tirai samping: beludru terikat (gambar), kanan = cermin kiri. Lebih lebar supaya ikatannya terlihat. */
html[data-gaya='teater'][data-aset-gaya~='teater'] .teater-tirai {
  width: clamp(34px, 12%, 72px);
  clip-path: none;
  background: var(--gaya-teater-tirai-kiri) right center / auto 100% no-repeat;
}

html[data-gaya='teater'][data-aset-gaya~='teater'] .teater-tirai--kanan {
  transform: scaleX(-1);
}

/* Tirai utama: kain beludru berlipat diulang mendatar, mengecil ke tepi saat terbuka. */
html[data-gaya='teater'][data-aset-gaya~='teater'] .teater-layar__panel {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.3), transparent 16%, transparent 84%, rgba(0, 0, 0, 0.4)),
    var(--gaya-teater-tirai-panel) left top / auto 100% repeat-x,
    #3a1418;
}

/* Lambrekin: beludru berumbai (gambar diulang mendatar). */
html[data-gaya='teater'][data-aset-gaya~='teater'] .teater-lambrekin {
  border: 0;
  -webkit-mask: none;
  mask: none;
  background: var(--gaya-teater-lambrekin) left top / auto 100% repeat-x;
  filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.45));
}

/* Papan judul babak: plakat kuningan tema (gambar); tulisan seperti terukir. */
html[data-gaya='teater'][data-aset-gaya~='teater'] .teater-papan {
  padding: 16px 40px 17px;
  border-radius: 0;
  background: var(--gaya-teater-plakat) center / 100% 100% no-repeat;
  box-shadow: none;
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.45));
}

/* Tepi panggung + lampu kaki (gambar diulang mendatar). */
html[data-gaya='teater'][data-aset-gaya~='teater'] .teater-lampu {
  height: 30px;
  background: var(--gaya-teater-lantai-panggung) left bottom / auto 100% repeat-x;
}

/* ---------- Lukisan Hidup ---------- */

/* Kertas cat air tema untuk kanvas, kertas yang dicuci, siluet kertas & kartu. */
html[data-gaya='lukisan'][data-aset-gaya~='lukisan'] {
  --tekstur-kartu: var(--gaya-lukisan-kertas-cat-air);
}

/* ---------- Gaya Sinematik di preset gelap (`<html data-kartu="gelap">`) ---------- */

/*
 * Kertas ikut warna kartu preset (mis. navy Ballroom Malam) dengan tinta terang: tekstur kertas
 * tema yang terang tidak lagi ditumpuk biasa (hasilnya abu-abu kusam), tetapi lembut (soft-light),
 * jadi tinggal seratnya.
 */
html[data-kartu='gelap'] .buku-bab,
html[data-kartu='gelap'] .buku-gambar,
html[data-kartu='gelap'][data-gaya='buku'] .scene:not(.cover) .card,
html[data-kartu='gelap'][data-gaya='buku'].stages-fixed .section-title,
html[data-kartu='gelap'][data-gaya='buku'].stages-fixed .scene--directed .shot > *,
html[data-kartu='gelap'][data-gaya='buku'].stages-fixed .scene--directed .story__chapter {
  background-blend-mode: normal, soft-light;
}

/*
 * Teater, papan gelap (sampul, layar akhir, judul bagian): tanpa `background-blend-mode`. Di
 * sebagian HP soft-light tidak selalu diterapkan saat papan dirasterisasi utuh (papan abu-abu
 * kusam), sedangkan kotak yang digambar ulang (kilau nama, monogram berpendar) diterapkan (gelap):
 * papan tampak berkedip di sekitar monogram & nama. Serat kertas cukup lewat lapisan kartu 93%.
 */
html[data-kartu='gelap'][data-gaya='teater'] .card--cover,
html[data-kartu='gelap'][data-gaya='teater'] .finale__card,
html[data-kartu='gelap'][data-gaya='teater'].stages-fixed .section-title {
  background-image:
    linear-gradient(
      color-mix(in srgb, var(--card-solid) 93%, transparent),
      color-mix(in srgb, var(--card-solid) 93%, transparent)
    ),
    var(--tekstur-kartu, none);
  background-blend-mode: normal;
}

html[data-kartu='gelap'] .buku-tekstur--kertas {
  opacity: 0.7;
  mix-blend-mode: soft-light;
}

/*
 * Buku: aksen preset gelap biasanya emas (u2, j1, u4) → kain emas menenggelamkan bingkai & tulisan
 * foil. Kain mengikuti kartu gelap, sedikit diberi aksen (mis. navy berkilau emas).
 */
html[data-kartu='gelap'][data-gaya='buku'] {
  --kain-buku: color-mix(in srgb, var(--card-solid) 82%, var(--accent));
}

/* Teater: dinding belakang panggung (tampak saat set berganti) ikut warna kartu, bukan marun u1. */
html[data-kartu='gelap'][data-gaya='teater'].stages-fixed main > .scene:not(.cover) > .stage {
  background: radial-gradient(
    ellipse 80% 70% at 50% 60%,
    color-mix(in srgb, var(--card-solid) 88%, #fff),
    color-mix(in srgb, var(--card-solid) 55%, #000) 70%
  );
}

/* Lukisan: kertas gelap, garis sketsa & tinta pensil terang (kapur di kertas gelap). */
html[data-kartu='gelap'][data-gaya='lukisan'] {
  --kertas-lukis: color-mix(in srgb, var(--card-solid) 94%, #fffaf3);
  --tinta-pensil: #efe4d2;
}

html[data-kartu='gelap'][data-gaya='lukisan'] .lukisan-sketsa,
html[data-kartu='gelap'][data-gaya='lukisan'] .lukisan-sketsa-foto {
  filter: brightness(0) invert(0.9);
}

/* ---------- Galeri: ubin foto tanpa latar di atas potongan adegan tema; klip tegak + judul sepasang ---------- */

.gtile--cutout {
  background: color-mix(in srgb, var(--line) 55%, var(--card-solid));
}

.gtile__latar {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.gtile__latar::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, color-mix(in srgb, var(--card-solid) 38%, transparent), transparent 45%),
    color-mix(in srgb, var(--card-solid) 14%, transparent);
}

.gtile__latar .gtile__latar-gambar,
.gtile__latar img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--bx, 50%) 70%;
  transform: scale(1.35);
  filter: saturate(0.92);
}

.gtile--cutout > .photo__img,
.gtile--cutout > picture {
  z-index: 1;
}

.video--portrait {
  gap: 0;
  align-items: stretch;
  overflow: hidden;
  border-radius: 3px;
  background: var(--card-solid);
  box-shadow: 0 0 0 1px var(--line);
}

.video--portrait .vtile {
  flex: 0 0 34%;
  box-shadow: none;
}

.video--portrait .video__title {
  display: flex;
  flex: 1;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  margin: 0;
  padding: 12px 16px;
  font-family: var(--font-display, inherit);
  font-size: 1.3rem;
  line-height: 1.2;
  text-align: left;
  color: var(--ink);
  border-left: 3px solid var(--accent);
  border-radius: 0;
  background: none;
  box-shadow: none;
}

.video--portrait .video__title::after {
  content: 'Ketuk untuk memutar';
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--ink-muted);
}

/* Galeri: jarak seragam. Isi tidak dipaksa setinggi layar; ujung bawah tidak menyisakan ruang kosong besar. */
.stages-fixed #galeri .scene__content {
  min-height: auto;
  justify-content: flex-start;
  gap: 14px;
  padding-bottom: max(48px, 7svh);
}

.stages-fixed #galeri .videos {
  margin-bottom: 0;
}

.stages-fixed #galeri .gallery__row--utama {
  margin-top: -22px;
}

/* ---------- Galeri per gaya: kulit di atas ritme yang sama (hanya Sinematik; Klasik tetap ubin dasar) ---------- */

.gallery__plakat {
  display: none;
}

.gallery {
  counter-reset: baris;
}

.gallery__row:not(.gallery__row--utama) {
  counter-increment: baris;
}

/* Film: jeda lanskap = sebingkai pita film selebar layar (lubang sprocket, nomor bingkai). */
html:not([data-gaya]):not([data-level='klasik']) .gallery__row--lebar {
  position: relative;
  width: min(100vw, 56rem);
  margin-inline: calc(50% - min(100vw, 56rem) / 2);
  padding: 26px clamp(14px, 6vw, 40px) 30px;
  background: #19120e;
  box-shadow: 0 6px 18px rgba(10, 5, 2, 0.35);
}

html:not([data-gaya]):not([data-level='klasik']) .gallery__row--lebar::before,
html:not([data-gaya]):not([data-level='klasik']) .gallery__row--lebar::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 9px;
  background: repeating-linear-gradient(90deg, transparent 0 7px, #efe3d0 7px 17px, transparent 17px 26px);
  -webkit-mask: linear-gradient(#000, #000);
  opacity: 0.9;
}

html:not([data-gaya]):not([data-level='klasik']) .gallery__row--lebar::before {
  top: 9px;
}

html:not([data-gaya]):not([data-level='klasik']) .gallery__row--lebar::after {
  bottom: 12px;
}

html:not([data-gaya]):not([data-level='klasik']) .gallery__row--lebar > .gtile {
  border-radius: 1px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
}

html:not([data-gaya]):not([data-level='klasik']) .gallery__row--lebar > .gtile::after {
  content: counter(baris, decimal-leading-zero) 'A';
  position: absolute;
  right: 8px;
  bottom: 6px;
  z-index: 3;
  font:
    600 0.68rem/1 ui-monospace,
    monospace;
  letter-spacing: 0.1em;
  color: #f0b45a;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}

/* Buku Cerita: tiap kelompok = satu halaman album; foto ditempel berbingkai putih, miring kecil, nomor halaman. */
html[data-gaya='buku'] .gallery__row:not(.gallery__row--utama) {
  position: relative;
  padding: 16px 14px 30px;
  background: var(--kertas-buku, var(--card-solid));
  box-shadow:
    0 0 0 1px var(--line),
    0 6px 16px rgba(40, 20, 8, 0.18);
  border-radius: 2px 6px 6px 2px;
}

html[data-gaya='buku'] .gallery__row:not(.gallery__row--utama)::after {
  content: counter(baris);
  position: absolute;
  left: 0;
  right: 0;
  bottom: 8px;
  text-align: center;
  font: italic 0.8rem/1 var(--font-display);
  color: var(--ink-muted);
}

html[data-gaya='buku'] .gtile {
  box-shadow:
    0 0 0 4px #fffdf8,
    0 2px 7px 4px rgba(40, 20, 8, 0.2);
  margin: 4px;
}

html[data-gaya='buku'] .gtile:nth-child(odd) {
  transform: rotate(-1.4deg);
}

html[data-gaya='buku'] .gtile:nth-child(even) {
  transform: rotate(1.6deg);
}

html[data-gaya='buku'] .gallery__row--tunggal > .gtile {
  width: 62%;
}

/* Lukisan Hidup: dinding galeri; karya berbingkai emas dengan passe-partout; plakat di bawah karya utama. */
html[data-gaya='lukisan'] .gtile {
  border-radius: 1px;
  background-color: var(--kertas-lukis);
  box-shadow:
    0 0 0 5px var(--kertas-lukis),
    0 0 0 8px #c9a35c,
    0 0 0 9px #7c5d27,
    0 6px 16px 6px rgba(30, 18, 8, 0.28);
  margin: 9px;
}

html[data-gaya='lukisan'] .gallery__row--lebar > .gtile {
  margin-inline: 9px;
}

html[data-gaya='lukisan'] .gallery__row--utama {
  gap: 2px;
}

html[data-gaya='lukisan'] .gallery__plakat {
  display: grid;
  gap: 2px;
  justify-items: center;
  width: min(72%, 15rem);
  margin: -6px auto 4px;
  padding: 8px 14px;
  background: linear-gradient(180deg, #e9d7ae, #d3b77b);
  border: 1px solid #7c5d27;
  border-radius: 2px;
  box-shadow: 0 2px 5px rgba(30, 18, 8, 0.3);
  color: #3a2a12;
  text-align: center;
}

html[data-gaya='lukisan'] .gallery__plakat b {
  font: 600 0.95rem/1.2 var(--font-display);
}

html[data-gaya='lukisan'] .gallery__plakat span {
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* Teater Kertas: papan karton bertepi putih berdiri di lantai panggung. */
html[data-gaya='teater'] .gallery__row:not(.gallery__row--utama) {
  position: relative;
  padding-bottom: 20px;
}

html[data-gaya='teater'] .gallery__row:not(.gallery__row--utama)::after {
  content: '';
  position: absolute;
  left: -8px;
  right: -8px;
  bottom: 0;
  height: 14px;
  background: linear-gradient(180deg, #5b3a22, #3b2314);
  border-top: 2px solid #8a5d3a;
  border-radius: 2px;
  box-shadow: 0 4px 8px rgba(10, 4, 2, 0.35);
}

html[data-gaya='teater'] .gtile {
  border-radius: 2px;
  box-shadow:
    0 0 0 5px #f6efe0,
    0 0 0 6px rgba(60, 30, 20, 0.4),
    0 10px 0 -2px rgba(20, 8, 4, 0.3);
  margin: 5px;
}
