/* =====================================================================
   Thiệp cưới Kim Tuyến & Linh Đan
   Design canvas: 390 × 844 CSS px. Every px value inside .stage and
   .cover-comp is a design-canvas px; the whole canvas is scaled:
     --s  = column width / 390   (letter sections, set by JS)
     --sc = min(width/390, height/844)  (cover, always fully visible)
   Colours: red #A71732 · dark red #760115 · field #880E23
            cream text #F7EFE6 · timeline #1E1A1B
   ===================================================================== */

:root {
  --s: 1;
  --sc: 1;
  --colw: calc(390px * var(--s));
  --red: #A71732;
  --red-dark: #760115;
  --field: #880E23;
  --cream: #F7EFE6;
}

/* ---------- Page shell ---------- */
html {
  background: #3a0610;
  scrollbar-gutter: stable;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
html.locked { overflow: hidden; }
body {
  margin: 0;
  font-family: 'Open Sans', Arial, sans-serif;
  color: var(--red);
  -webkit-tap-highlight-color: transparent;
}
[hidden] { display: none !important; }
a { color: var(--red); }
a:hover { color: var(--red-dark); }

/* The phone-width column. Its box has the scaled size; .stage inside is
   the unscaled 390-px canvas scaled with transform. */
.page {
  position: relative;
  z-index: 0;                 /* own stacking context, below the cover */
  display: block;
  width: var(--colw);
  height: calc(2819px * var(--s));   /* 693 + 693 + 740 + 693 */
  margin: 0 auto;
  overflow: hidden;
  background: #F6F2EA;
  box-shadow: 0 0 60px rgba(0, 0, 0, .45);
  outline: none;
}
.stage {
  position: absolute;
  left: 0;
  top: 0;
  width: 390px;
  transform: scale(var(--s));
  transform-origin: 0 0;
}
.sec { position: relative; width: 390px; }
.sec-env   { z-index: 1; height: 693px; perspective: 1000px; perspective-origin: 195px 250px; }
.sec-card  { z-index: 2; height: 693px; overflow: hidden; background-color: var(--red); perspective: 1100px; }
.sec-tl    { z-index: 1; height: 740px; background: linear-gradient(180deg, var(--red) 0, var(--red) 506px, var(--red-dark) 506px); perspective: 1100px; }
.sec-thanks{ z-index: 1; height: 693px; perspective: 1100px; }

/* While the cover is closed the letter is completely behind it, so:
   - hide it (no visual change; avoids layout-shift reports if the viewport is
     resized during load, e.g. by Lighthouse's device emulation),
   - skip rendering S3–S5 (sizes are fixed, so layout is unchanged). This keeps
     their lazy images from competing with the cover on load. */
.js .page.st:not(.st-open) { visibility: hidden; }
/* The envelope (S2) also waits until the page has loaded (main.js adds .warm),
   so its images don't compete with the cover for bandwidth. */
.js .st:not(.st-open):not(.warm) .sec-env { content-visibility: hidden; }
.st:not(.st-open) .sec-card,
.st:not(.st-open) .sec-tl,
.st:not(.st-open) .sec-thanks { content-visibility: hidden; }

.bg-paper {
  background-color: #F6F2EA;
  background-size: cover;
  background-position: center;
}
.warm .bg-paper, .st-open .bg-paper, html:not(.js) .bg-paper { background-image: url(../assets/paper.webp); }

/* ---------- Photo slots ---------- */
.photo { position: absolute; }
.photo > img { position: absolute; }
.photo-shape { left: 0; top: 0; width: 100%; height: 100%; }
.photo-label { width: 60.2px; height: 20.8px; }
.photo-img {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: hidden;
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.photo-pic { display: block; width: 100%; height: 100%; object-fit: cover; }
.photo-rot { position: absolute; left: 50%; top: 50%; overflow: hidden; }
.photo-img picture { display: contents; } /* the <img> inside sizes itself as before */
.has-photo .photo-label { display: none; }
.frame { position: absolute; left: 0; top: 0; filter: drop-shadow(0 8px 10px rgba(60, 40, 30, .2)); }

/* ---------- Keyframes (from the prototype) ---------- */
@keyframes write { from { opacity: 1; clip-path: inset(-20% 100% -20% 0); } to { opacity: 1; clip-path: inset(-20% -3% -20% 0); } }
@keyframes fadeUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.04); } }
@keyframes glow { 0%, 100% { filter: drop-shadow(0 8px 10px rgba(40,0,8,.45)) brightness(1); } 50% { filter: drop-shadow(0 8px 12px rgba(40,0,8,.5)) brightness(1.1); } }
@keyframes land { 0% { opacity: 0; transform: translateY(46px) scale(.94); } 70% { opacity: 1; transform: translateY(-3px) scale(1.005); } 100% { opacity: 1; transform: none; } }
@keyframes slideUp { 0% { opacity: 0; transform: translateY(100px); } 12% { opacity: 1; } 72% { transform: translateY(-8px); } 100% { opacity: 1; transform: none; } }
@keyframes sweepR { 0% { opacity: 0; transform: translate(70px,-34px) rotate(22deg) scale(.7); } 60% { opacity: 1; } 80% { transform: translate(-4px,2px) rotate(-2deg) scale(1.02); } 100% { opacity: 1; transform: none; } }
@keyframes sweepL { 0% { opacity: 0; transform: translate(-60px,-20px) rotate(-20deg) scale(.7); } 60% { opacity: 1; } 80% { transform: translate(3px,1px) rotate(2deg) scale(1.02); } 100% { opacity: 1; transform: none; } }
@keyframes riseIn { 0% { opacity: 0; transform: translateY(60px) rotate(8deg) scale(.8); } 70% { opacity: 1; transform: translateY(-4px) rotate(-1deg) scale(1.02); } 100% { opacity: 1; transform: none; } }
@keyframes popSpin { 0% { opacity: 0; transform: scale(.2) rotate(-40deg); } 65% { opacity: 1; transform: scale(1.08) rotate(4deg); } 100% { opacity: 1; transform: none; } }
@keyframes stamp { 0% { opacity: 0; transform: scale(2.3); } 60% { opacity: 1; transform: scale(.9); } 80% { transform: scale(1.05); } 100% { opacity: 1; transform: none; } }
@keyframes dropTilt { 0% { opacity: 0; transform: translateY(-50px) rotate(-9deg) scale(1.05); } 70% { opacity: 1; transform: translateY(3px) rotate(1deg); } 100% { opacity: 1; transform: none; } }
@keyframes pop { 0% { opacity: 0; transform: scale(.3); } 70% { opacity: 1; transform: scale(1.12); } 100% { opacity: 1; transform: none; } }
@keyframes cardIn { 0% { opacity: 0; transform: translateY(80px) rotateX(28deg) scale(.96); } 100% { opacity: 1; transform: none; } }
@keyframes growY { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes growX { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes sway { 0%, 100% { transform: rotate(-1.6deg); } 50% { transform: rotate(1.8deg); } }
@keyframes bob { 0%, 100% { transform: translateY(0) rotate(-1.5deg); } 50% { transform: translateY(-4px) rotate(2deg); } }
@keyframes beat { 0%, 100% { transform: scale(1); } 15% { transform: scale(1.18); } 30% { transform: scale(1); } 45% { transform: scale(1.12); } 60% { transform: scale(1); } }
@keyframes float3d { 0%, 100% { transform: rotateX(0deg) rotateY(0deg); } 25% { transform: rotateX(3deg) rotateY(-4deg); } 50% { transform: rotateX(-2deg) rotateY(3deg); } 75% { transform: rotateX(2deg) rotateY(4deg); } }
@keyframes fallL { 0% { opacity: 0; transform: translate(0,0) rotate(0deg); } 10% { opacity: .95; } 85% { opacity: .8; } 100% { opacity: 0; transform: translate(-50px,300px) rotate(560deg); } }
@keyframes fallR { 0% { opacity: 0; transform: translate(0,0) rotate(0deg); } 10% { opacity: .95; } 85% { opacity: .8; } 100% { opacity: 0; transform: translate(34px,320px) rotate(-480deg); } }
@keyframes modalIn { from { opacity: 0; transform: translateY(24px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes cardBob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
@keyframes blobShake { 0%, 55%, 100% { transform: rotate(0deg) scale(1); } 5% { transform: rotate(-6deg) scale(1.06); } 12% { transform: rotate(5.5deg) scale(1.06); } 19% { transform: rotate(-4.5deg) scale(1.04); } 26% { transform: rotate(3.5deg) scale(1.03); } 33% { transform: rotate(-2deg) scale(1.01); } 40% { transform: rotate(1deg) scale(1); } }
@keyframes iconShake { 0%, 55%, 100% { transform: translateY(0) rotate(0deg); } 5% { transform: translateY(-6px) rotate(-16deg); } 12% { transform: translateY(-9px) rotate(14deg); } 19% { transform: translateY(-5px) rotate(-12deg); } 26% { transform: translateY(-3px) rotate(9deg); } 33% { transform: translateY(-1px) rotate(-5deg); } 40% { transform: translateY(0) rotate(2deg); } }

/* =====================================================================
   COVER
   ===================================================================== */
.cover {
  position: fixed;
  z-index: 10;                /* above the letter (z-index 0) */
  top: 0; bottom: 0; left: 0; right: 0;
  width: var(--colw);
  margin: 0 auto;
  overflow: hidden;
  /* fills any space around the scaled composition with the same paper */
  background: #9E1A32 url(../assets/cover-bg.webp) center / cover no-repeat;
  background-image: image-set(url(../assets/cover-bg.avif) type("image/avif"), url(../assets/cover-bg.webp) type("image/webp"));
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity .8s ease 1.05s, transform 1.6s ease .2s, visibility 0s linear 0s;
  cursor: pointer;            /* the whole cover opens the invitation */
}
.cover.is-open {
  opacity: 0;
  visibility: hidden;
  transform: scale(1.04);
  transition: opacity .8s ease 1.05s, transform 1.6s ease .2s, visibility 0s linear 1.9s;
}
.cover-comp {
  position: absolute;
  left: 50%; top: 50%;
  width: 390px; height: 844px;
  margin: -422px 0 0 -195px;
  transform: scale(var(--sc));
  perspective: 1200px;
  perspective-origin: 195px 300px;
}
.cv-bg {
  /* cover-bg: the envelope layer from the Photoshop file. AVIF (61 KB) is almost identical
     to the lossless original; browsers without AVIF get the WebP. */
  background-image: url(../assets/cover-bg.webp);
  background-image: image-set(url(../assets/cover-bg.avif) type("image/avif"), url(../assets/cover-bg.webp) type("image/webp"));
  background-size: 474.75px 844px;
  background-position: -42.375px 0;
  background-repeat: no-repeat;
}
.cv-inside {
  position: absolute; left: 0; top: 0; width: 390px; height: 406px;
  clip-path: polygon(0px 0px, 390px 0px, 390px 276px, 195px 406px, 0px 272px);
  background: linear-gradient(180deg, rgba(40,0,8,.55) 0%, rgba(40,0,8,.25) 100%);
  opacity: 0;
  transition: opacity .6s ease .3s;
}
.cover.is-open .cv-inside { opacity: 1; }
.cv-flap {
  position: absolute; left: 0; top: 0; width: 390px; height: 406px;
  transform-origin: 50% 0;
  transform-style: preserve-3d;
  transform: rotateX(0deg);
  transition: transform 1.1s cubic-bezier(.5,0,.25,1) .2s;
}
.cover.is-open .cv-flap { transform: rotateX(-172deg); }
.cv-face {
  position: absolute; left: 0; top: 0; width: 390px; height: 406px;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
.cv-front {
  clip-path: polygon(0px 0px, 390px 0px, 390px 276px, 195px 406px, 0px 272px);
  filter: drop-shadow(0 4px 6px rgba(40,0,8,.4));
}
.cv-back {
  transform: rotateX(180deg);
  clip-path: polygon(0px 406px, 390px 406px, 390px 130px, 195px 0px, 0px 134px);
  background: linear-gradient(0deg, #8E0F27 0%, #6E0A1C 100%);
}
.cv-txt {
  position: absolute; left: 0; top: 0; width: 390px; height: 844px;
  pointer-events: none;
  transition: opacity .35s ease;
}
.cover.is-open .cv-txt { opacity: 0; }
.cv-sealbtn {
  position: absolute; left: 123.8px; top: 329.7px; width: 142.4px; height: 153px;
  padding: 0; border: none; background: transparent; cursor: pointer;
  border-radius: 50%;
  transition: transform .5s cubic-bezier(.3,1.4,.5,1), opacity .45s ease;
}
.cv-sealbtn:focus-visible { outline: 3px solid #F4D8A8; outline-offset: 4px; }
.cover.is-open .cv-sealbtn { transform: scale(1.3); opacity: 0; }
.cv-ot { animation: write 1.5s .3s cubic-bezier(.45,0,.2,1) both; }
.cv-nm { animation: write 1.8s 1s cubic-bezier(.45,0,.2,1) both; }
.cv-wd { animation: fadeUp 1s 2.4s ease both; }
.cv-breathe { display: block; animation: breathe 3.6s 1.2s ease-in-out infinite; }
.cv-glow { animation: glow 3.6s 1.2s ease-in-out infinite; }

/* =====================================================================
   S2 — envelope (plays when .page gets .st-open)
   ===================================================================== */
.s2-title { position: absolute; left: 86.2px; top: 35.5px; width: 217.1px; height: 28.2px; margin: 0; }
.s2-title img { display: block; width: 217.1px; height: 28.2px; }
.a-fa, .a-fb { position: absolute; }
.env-shadow {
  position: absolute; left: 48px; top: 410px; width: 300px; height: 40px; border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(90,10,25,.3) 0%, rgba(90,10,25,.1) 45%, rgba(90,10,25,0) 70%);
}
.full { position: absolute; left: 0; top: 0; width: 390px; height: 693px; }
.a-card { pointer-events: none; }

/* 3D stage. Keep these wrappers free of opacity/filter/overflow (they flatten 3D). */
.d3 { position: absolute; left: 0; top: 0; width: 390px; height: 693px; transform-style: preserve-3d; }
.tilt { transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); transition: transform .35s ease-out; }
.ly { position: absolute; left: 0; top: 0; width: 390px; height: 693px; transform-origin: 195px 250px; pointer-events: none; }
.ly > * { pointer-events: auto; }

/* Depth cues while the envelope floats / tilts.
   main.js sets --tx / --ty on #s2 (unitless: the tilt rotateX / rotateY in degrees).
   The idle float3d sway is mirrored by keyframes with the same timing, so light and
   shadow also move during the automatic sway. Only transform/translate are animated. */
.ly > .sh, .ly > .sheen { pointer-events: none; }
.sh { position: absolute; }

/* Ground shadow: on the paper, outside the 3D stage, shifts against the tilt */
.gsh { position: absolute; left: 0; top: 0; width: 390px; height: 693px; pointer-events: none; }
.gsh-move { position: absolute; inset: 0; translate: calc(var(--ty, 0) * -2.5px) calc(var(--tx, 0) * 2.5px); transition: translate .35s ease-out; }
.st-open .gsh-move { animation: gshFloat 12s 4.2s ease-in-out infinite; }
@keyframes gshFloat { 0%, 100% { transform: none; } 25% { transform: translate(10px, 7.5px); } 50% { transform: translate(-7.5px, -5px); } 75% { transform: translate(-10px, 5px); } }

/* Sheen: a soft band of light that slides across the paper as the angle changes */
.sheen { position: absolute; overflow: hidden; pointer-events: none;
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; }
.sheen-env  { -webkit-mask-image: url(../assets/envelope.webp); mask-image: url(../assets/envelope.webp); }
.sheen-card { -webkit-mask-image: url(../assets/paper-v.webp); mask-image: url(../assets/paper-v.webp); }
.sheen > i {
  position: absolute; left: -60%; top: -60%; width: 220%; height: 220%;
  background:
    linear-gradient(115deg, rgba(255,248,236,0) 36%, rgba(255,248,236,.14) 41%, rgba(255,248,236,.26) 43.5%, rgba(255,248,236,.14) 46%, rgba(255,248,236,0) 51%),
    linear-gradient(115deg, rgba(255,255,255,.10) 30%, rgba(255,255,255,0) 45%, rgba(40,0,10,0) 55%, rgba(40,0,10,.12) 70%);
  translate: calc(var(--ty, 0) * 9px) calc(var(--tx, 0) * -9px);
  transition: translate .35s ease-out;
}
.sheen-card > i { opacity: .8; }
.st-open .sheen > i { animation: sheenFloat 12s 4.2s ease-in-out infinite; }
@keyframes sheenFloat { 0%, 100% { transform: none; } 25% { transform: translate(-36px, -27px); } 50% { transform: translate(27px, 18px); } 75% { transform: translate(36px, -18px); } }

.st .a { opacity: 0; }
.st-open .float3d { animation: float3d 12s 4.2s ease-in-out infinite; }
.st-open .a-title { animation: write 1.6s .9s cubic-bezier(.45,0,.2,1) both; }
.st-open .a-env, .st-open .a-sheen { animation: land 1s .8s cubic-bezier(.2,.7,.2,1) both; }
.st-open .a-front { animation: land 1s .8s cubic-bezier(.2,.7,.2,1) both; }
.st-open .bob-card { animation: cardBob 4.5s 3s ease-in-out infinite; }
.st-open .a-card { animation: slideUp 1.4s 1.3s cubic-bezier(.25,.7,.25,1) both; }
.st-open .a-bm, .st-open .a-bm-sh { transform-origin: 30% 90%; animation: sweepR 1.4s 2s cubic-bezier(.25,.7,.25,1) both; }
.st-open .a-bl, .st-open .a-bl-sh { transform-origin: 60% 30%; animation: sweepL 1.3s 2.2s cubic-bezier(.25,.7,.25,1) both; }
.st-open .a-br, .st-open .a-br-sh { transform-origin: 40% 30%; animation: riseIn 1.2s 2.4s cubic-bezier(.25,.7,.25,1) both; }
.st-open .a-date, .st-open .a-date-sh { animation: popSpin .9s 2.6s cubic-bezier(.3,.7,.3,1) both; }
.st-open .a-seal { animation: stamp .7s 3s cubic-bezier(.3,.7,.3,1) both; }
.st-open .a-names { animation: write 1.4s 3.1s cubic-bezier(.45,0,.2,1) both; }
.st-open .a-fa { animation: dropTilt 1.1s 3.2s cubic-bezier(.25,.7,.25,1) both; }
.st-open .a-fb { animation: dropTilt 1.1s 3.45s cubic-bezier(.25,.7,.25,1) both; }
.st-open .a-rings { animation: pop .8s 3.7s cubic-bezier(.3,.7,.3,1) both; }
.st-open .sw-bm { transform-origin: 30% 92%; animation: sway 6s 3.6s ease-in-out infinite; }
.st-open .sw-bl { transform-origin: 55% 20%; animation: sway 7s 3.6s ease-in-out infinite reverse; }
.st-open .sw-br { transform-origin: 35% 25%; animation: sway 5.4s 3.8s ease-in-out infinite; }
.st-open .fl-date { animation: bob 6s 3.6s ease-in-out infinite; }
.pt { opacity: 0; transform-box: fill-box; transform-origin: center; }
.st-open .pt1 { animation: fallL 8s 4.4s linear infinite; }
.st-open .pt2 { animation: fallR 9.5s 7s linear infinite; }
.st-open .pt3 { animation: fallL 10.5s 9.6s linear infinite; }
.st-open .pt4 { animation: fallR 8.8s 12.2s linear infinite; }

/* Falling petals over S2–S5: one .petals overlay per pair of sections (top/height inline,
   generated by tools/petals.js).
   Outer .pf falls and drifts; the inner svg flips in 3D so it catches the light.
   Paused while off screen (main.js toggles .on). */
.petals { position: absolute; left: 0; width: 390px; z-index: 3; overflow: hidden; pointer-events: none; display: none; }
.st-open .petals { display: block; }
.pf { position: absolute; width: calc(16px * var(--z, 1)); height: calc(19px * var(--z, 1)); opacity: 0; perspective: 40px; }
.pf svg { display: block; width: 100%; height: 100%; fill: var(--c, #A31B30); filter: drop-shadow(0 1px 1px rgba(60,0,12,.25)); }
.st-open .pf { animation: pfFall var(--d, 11s) var(--dl, 5s) linear infinite; }
.st-open .pf svg { animation: pfFlip var(--fd, 2.6s) var(--dl, 5s) ease-in-out infinite; }
.petals:not(.on) .pf, .petals:not(.on) .pf svg { animation-play-state: paused; }
@keyframes pfFall {
  0%   { opacity: 0; transform: translate(0, 0) rotate(0deg); }
  8%   { opacity: var(--o, .9); }
  30%  { transform: translate(calc(var(--dx) * .9), calc(var(--fy) * .3)) rotate(calc(var(--r) * .3)); }
  60%  { transform: translate(calc(var(--dx) * -.2), calc(var(--fy) * .6)) rotate(calc(var(--r) * .6)); }
  88%  { opacity: var(--o, .9); }
  100% { opacity: 0; transform: translate(var(--dx), var(--fy)) rotate(var(--r)); }
}
@keyframes pfFlip {
  0%, 100% { transform: rotateX(-35deg) rotateY(20deg); }
  50%      { transform: rotateX(45deg) rotateY(-30deg); }
}

/* =====================================================================
   S3 — invitation card
   ===================================================================== */
.families { position: absolute; left: 37px; top: 71px; width: 288px; display: flex; }
.families > div { flex: 1; }
.fam-k { margin: 0; font-size: 10px; line-height: 12px; font-weight: 400; }
.fam-n { margin: 0; font-size: 12.3px; line-height: 17.4px; font-weight: 700; white-space: nowrap; }
.divider { position: absolute; left: 59.4px; width: 237.5px; height: 1.3px; background-color: rgba(167,23,50,.35); }
.hall {
  display: inline-flex; align-items: center; justify-content: center;
  width: 57.2px; height: 15.7px; border-radius: 8px;
  background-color: var(--red); color: #FFFFFF;
  font-size: 10px; font-weight: 800; letter-spacing: .04em;
}

/* =====================================================================
   S4 — timeline + RSVP
   ===================================================================== */
.tl-t { position: absolute; margin: 0; font-family: 'Noto Serif', Georgia, serif; font-weight: 700; font-size: 10.4px; line-height: 1; color: var(--red); white-space: nowrap; }
.tl-d { position: absolute; margin: 0; font-family: 'Noto Serif', Georgia, serif; font-style: italic; font-size: 6.7px; line-height: 8.7px; color: var(--red); white-space: nowrap; }
.tl-k { color: #2A2224; }

.rsvp { position: absolute; left: 0; top: 550px; width: 390px; height: 190px; margin: 0; color: var(--cream); }
.rs-intro {
  position: absolute; left: 30px; right: 30px; top: 7.7px; margin: 0;
  text-align: center; font-size: 8.6px; line-height: 11.9px; font-weight: 700; letter-spacing: .02em;
}
.rs-grid {
  position: absolute; left: 23px; top: 37.2px; width: 350.1px;
  display: grid; grid-template-columns: 132.6px 217.5px; row-gap: 1px;
}
.rs-lab { font-size: 10.2px; line-height: 14.7px; font-weight: 600; white-space: nowrap; color: var(--cream); }
.rs-box { position: relative; box-sizing: border-box; background-color: var(--field); }
.rs-box:has(:focus-visible) { outline: 2px solid #F4D8A8; outline-offset: 1px; }
.rs-in {
  display: block; box-sizing: border-box; width: 100%; height: 100%; margin: 0;
  border: none; outline: none; border-radius: 0; background: transparent; color: var(--cream);
  font-family: 'Open Sans', Arial, sans-serif; font-weight: 600; font-size: 10.5px; line-height: normal;
  padding: 0 8px 0 24px;
}
.rs-in::placeholder { color: rgba(247,239,230,.4); opacity: 1; }
.rs-opt { display: flex; align-items: center; gap: 5.9px; font-size: 10.5px; line-height: 14.7px; font-weight: 600; white-space: nowrap; cursor: pointer; }
.rd {
  appearance: none; -webkit-appearance: none; flex: none;
  width: 14.7px; height: 14.7px; margin: 0; border-radius: 50%;
  background: #EDE9DD; border: 1px solid rgba(167,23,50,.5); cursor: pointer;
}
.rd:checked { background: var(--red); box-shadow: inset 0 0 0 3px #EDE9DD; }
.rd:focus-visible { outline: 2px solid #F4D8A8; outline-offset: 2px; }
.rs-caret {
  position: absolute; left: 3.4px; top: 2.4px; width: 14.7px; height: 14.7px; border-radius: 50%;
  background-color: #EDE9DD; display: flex; align-items: center; justify-content: center; pointer-events: none;
}
.rs-val {
  position: absolute; left: 24px; top: 0; bottom: 0; display: flex; align-items: center;
  font-size: 10.5px; font-weight: 600; white-space: nowrap; color: var(--cream); pointer-events: none;
}
.rs-val.ph { color: rgba(247,239,230,.4); }
/* 16px font on the hidden select stops iOS from zooming in on focus */
.rs-ovl { position: absolute; left: 0; top: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; font-size: 16px; }
.rs-ovl option { color: #3A2A28; }
.rs-actions { position: absolute; left: 0; right: 0; top: 134px; text-align: center; }
.rs-btn { font-family: 'Open Sans', Arial, sans-serif; cursor: pointer; }
.rs-btn:hover { filter: brightness(1.05); }
.rs-btn:focus-visible, .gift-btn:focus-visible { outline: 2px solid #F4D8A8; outline-offset: 2px; }
.rs-btn:disabled { cursor: progress; opacity: .8; }
.rs-submit {
  min-height: 34px; padding: 0 26px; border: none; border-radius: 17px;
  background-color: #F4EEE3; color: var(--red);
  font-size: 12px; font-weight: 800; letter-spacing: .04em;
  box-shadow: 0 6px 14px rgba(0,0,0,.25);
}
.rs-status { margin: 0; font-size: 12px; font-weight: 700; color: var(--cream); }
.rsvp.is-sent .rs-submit { display: none; }
.rsvp.is-sent .rs-status { margin-top: 6px; }
.rs-status.is-error { margin-top: 6px; font-size: 10.5px; color: #F4D8A8; }

/* =====================================================================
   S5 — photos, gift, thank you
   ===================================================================== */
.thanks-text { position: absolute; left: 33.8px; top: 81.9px; width: 288px; font-size: 7.9px; line-height: 10.5px; font-weight: 700; color: var(--red); }
.gift-btn {
  position: relative; display: block; width: 199.3px; height: 191.1px;
  padding: 0; border: none; background: transparent; cursor: pointer; color: #FFFFFF;
  font-family: 'Open Sans', Arial, sans-serif;
}
.gift-label { position: absolute; left: 0; right: 0; top: 43px; font-size: 14.5px; line-height: 18px; font-weight: 700; text-align: center; }

/* =====================================================================
   Scroll reveals: hidden by default, play when the section is .seen
   ===================================================================== */
.rv { opacity: 0; }
.seen .rv-card { animation: cardIn 1.2s cubic-bezier(.2,.7,.2,1) both; }
.seen .rv-date { animation: popSpin .9s .5s cubic-bezier(.3,.7,.3,1) both; }
.seen .rv-seal { animation: stamp .7s .9s cubic-bezier(.3,.7,.3,1) both; }
.seen .rv-t { animation: fadeUp .8s ease both; }
.seen .rv-write { animation: write 1.4s ease both; }
.seen .rv-line { opacity: 1; transform-origin: 50% 0; animation: growY 1.8s .5s ease-out both; }
.seen .rv-br { opacity: 1; transform-origin: 0 50%; animation: growX .5s ease-out both; }
.seen .rv-bl { opacity: 1; transform-origin: 100% 50%; animation: growX .5s ease-out both; }
.seen .rv-icon { animation: pop .7s cubic-bezier(.3,.7,.3,1) both; }
.seen .rv-frame { animation: dropTilt 1.1s cubic-bezier(.25,.7,.25,1) both; }
.seen .bt-heart { animation: beat 1.6s 3s ease-in-out infinite; }

/* Gift: its own observer adds .gift-on — no delay before the pop */
.gift-on .gift-in { animation: popSpin .8s cubic-bezier(.3,.7,.3,1) both; }
.gift-on .gift-shake { transform-origin: 50% 55%; animation: blobShake 2s .8s ease-in-out infinite; }
.gift-on .gift-icon-shake { transform-origin: 50% 85%; animation: iconShake 2s .8s ease-in-out infinite; }

/* Delay helpers — MUST stay after every rule that uses the `animation` shorthand */
.seen .d1 { animation-delay: .7s; }
.seen .d2 { animation-delay: .85s; }
.seen .d3t { animation-delay: 1s; }
.seen .d4 { animation-delay: 1.15s; }
.seen .d5 { animation-delay: 1.3s; }
.seen .d6 { animation-delay: 1.45s; }
.seen .d7 { animation-delay: 1.6s; }
.seen .d8 { animation-delay: 1.75s; }
.seen .d9 { animation-delay: 1.9s; }
.seen .d10 { animation-delay: 2.05s; }
.seen .d11 { animation-delay: 2.2s; }
.seen .d12 { animation-delay: 2.35s; }

/* =====================================================================
   Floating reseal button + gift dialog
   ===================================================================== */
.col-fixed {
  position: fixed; z-index: 30; top: 0; left: 0; right: 0;
  width: var(--colw); height: 0; margin: 0 auto;
}
.fab-stack {
  position: absolute; right: 12px; top: max(12px, env(safe-area-inset-top));
  display: flex; flex-direction: column; gap: 10px;
}
.fab {
  position: relative;
  width: 44px; height: 44px; padding: 0; border: none; border-radius: 50%;
  background-color: rgba(167,23,50,.9);
  box-shadow: 0 6px 14px rgba(40,0,8,.35), inset 0 0 0 1.5px rgba(244,216,168,.6); /* thin gold ring: visible on the red cover too */
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.fab[hidden] { display: none; }
.fab:focus-visible { outline: 2px solid #F4D8A8; outline-offset: 2px; }
.fab:active { transform: scale(.94); }

/* Music button: speaker with waves while playing (plus a soft pulse ring), crossed out when off */
.music-btn svg { display: block; }
.music-btn .ic-on, .music-btn[aria-pressed="false"] .ic-off { display: block; }
.music-btn .ic-off, .music-btn[aria-pressed="false"] .ic-on { display: none; }
.music-btn[aria-pressed="false"] { background-color: rgba(122,15,32,.82); }
.music-btn::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px solid rgba(244,216,168,.75); opacity: 0; pointer-events: none;
}
.music-btn[aria-pressed="true"]::after { animation: musicPulse 2.4s ease-out infinite; }
@keyframes musicPulse { 0% { opacity: .8; transform: scale(1); } 100% { opacity: 0; transform: scale(1.45); } }

.gift-dialog {
  width: 390px; max-width: none; max-height: none;
  box-sizing: border-box; padding: 20px; margin: auto;
  border: 0; background: transparent; overflow: visible;
  transform: scale(var(--s));
  font-family: 'Open Sans', Arial, sans-serif;
}
.gift-dialog::backdrop { background-color: rgba(40,0,10,.55); }
.modal-card {
  width: 100%; max-width: 340px; margin: 0 auto; box-sizing: border-box;
  padding: 22px 18px 18px; border-radius: 16px;
  background-color: #F7F2EA; box-shadow: 0 24px 50px rgba(0,0,0,.35);
  color: var(--red); text-align: center;
}
.gift-dialog[open] .modal-in { animation: modalIn .45s cubic-bezier(.2,.7,.2,1) both; }
.modal-card h2 { margin: 0; font-size: 18px; line-height: 24px; font-weight: 800; }
.modal-sub { margin: 6px 0 16px; font-size: 11px; line-height: 15px; color: #5A3A3E; }
.bank-cols { display: flex; gap: 10px; }
.bank { flex: 1; padding: 12px 8px; border: 1px solid rgba(167,23,50,.3); border-radius: 12px; background-color: #FFFFFF; }
.bank-h { margin: 0 0 8px; font-size: 12px; font-weight: 800; }
.qr {
  width: 84px; height: 84px; margin: 0 auto 8px; box-sizing: border-box;
  border: 1px dashed rgba(167,23,50,.5); border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; color: #7A4A50;
}
img.qr { display: block; width: 100%; max-width: 124px; height: auto; aspect-ratio: 1; border: 0; border-radius: 6px; object-fit: contain; }
.bank-info { margin: 0; font-size: 10px; line-height: 15px; color: #3A2A28; overflow-wrap: anywhere; }
.bank-info > span { display: block; }
.bank-name { font-weight: 700; color: var(--red); }
.bank-no { font-size: 13px; line-height: 19px; font-weight: 800; letter-spacing: .02em; font-variant-numeric: tabular-nums; user-select: all; -webkit-user-select: all; }
.bank-holder { font-size: 9.5px; letter-spacing: .03em; }
.copy-btn {
  position: relative; margin-top: 8px; min-height: 30px; padding: 0 12px; border: 1px solid rgba(167,23,50,.45); border-radius: 15px;
  background: #FFFFFF; color: var(--red); font: inherit; font-size: 10.5px; font-weight: 700; cursor: pointer;
}
.copy-btn::before { content: ""; position: absolute; inset: -7px -4px; } /* 44-px tall touch target */
.copy-btn.is-done { background: var(--red); border-color: var(--red); color: #FFFFFF; }
.copy-btn:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
.modal-close {
  margin-top: 16px; min-height: 44px; padding: 0 30px; border: none; border-radius: 22px;
  background-color: var(--red); color: var(--cream); font-size: 13px; font-weight: 700;
}

/* =====================================================================
   Reduced motion: no animation, everything in its final state
   ===================================================================== */
@media (prefers-reduced-motion: reduce) {
  .cv-ot, .cv-nm, .cv-wd, .cv-breathe, .cv-glow,
  .st-open .float3d, .st-open .bob-card,
  .st-open .sw-bm, .st-open .sw-bl, .st-open .sw-br, .st-open .fl-date,
  .st-open .pt1, .st-open .pt2, .st-open .pt3, .st-open .pt4,
  .st-open .pf, .st-open .pf svg,
  .st-open .gsh-move, .st-open .sheen > i,
  .music-btn[aria-pressed="true"]::after,
  .seen .bt-heart, .gift-on .gift-shake, .gift-on .gift-icon-shake,
  .gift-dialog[open] .modal-in { animation: none; }
  .st-open .a, .rv, .gift-in, .gift-on .gift-in { animation: none; opacity: 1; clip-path: none; }
  .cover, .cover.is-open, .cv-flap, .cv-inside, .cv-txt, .cv-sealbtn, .tilt { transition: none; }
  .petals, .st-open .petals { display: none; }
}
