/* Jawa Cover — direct template, native HTML/CSS built straight from
   cover.psd (1080x1920). Whole scene authored at native PSD pixel
   coordinates and scaled as one unit to cover the viewport (same math
   as object-fit:cover for a single image) -- no runtime PSD/layer
   engine, no per-property unit conversion. Cover section only. */
*{box-sizing:border-box;}
html,body{margin:0;padding:0;background:#000;height:100%;}
body{font-family:'EB Garamond',serif;-webkit-font-smoothing:antialiased;}
img{max-width:none;}

.jw-app{
  position:relative;
  max-width:480px;
  min-height:100dvh;
  margin:0 auto;
  overflow:hidden;
  background:#f3e7cf;
}

.jw-cover{
  position:relative;
  width:100%;
  height:100dvh;
  overflow:hidden;
  background:#f3e7cf;
}

/* Scaled as one unit by script.js using cover-fit, same proven mobile
   mechanic as Betawi GOLD: the cover section is the real viewport and the
   native 1080x1920 PSD scene is centered/cropped like one image. */
.jw-cover-scene{
  position:absolute;
  top:50%;
  left:50%;
  width:1080px;
  height:1920px;
  transform-origin:center center;
}

.jw-l{position:absolute;}

.jw-photo-frame{
  border-radius:50%;
  overflow:hidden;
  background:#e7d9bd;
  border:4px solid rgba(255,255,255,.96);
}
.jw-photo-img{
  display:none;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:50% 50%;
}

.jw-text{
  color:#57321a;
  text-align:center;
  font-family:'Cormorant Garamond',serif;
}

.jw-eyebrow{
  font-size:30px;
  font-weight:600;
  letter-spacing:7px;
  text-transform:uppercase;
  white-space:nowrap;
}

.jw-names{
  font-size:69px;
  font-weight:700;
  letter-spacing:2px;
  line-height:.95;
}

.jw-guestblock{
  text-align:center;
}
.jw-guestlabel{
  font-family:'Quicksand',sans-serif;
  font-size:42px;
  font-weight:600;
  line-height:1;
  color:#1a1410;
}
.jw-guestname{
  font-size:64px;
  font-weight:600;
  letter-spacing:1px;
  line-height:1;
  margin-top:18px;
  color:#000;
}

.jw-open-btn{
  position:absolute;
  display:flex;
  align-items:center;
  justify-content:center;
  gap:10px;
  appearance:none;
  -webkit-appearance:none;
  background:#644235;
  color:#fff;
  border-radius:9999px;
  border:none;
  font-family:'Quicksand',sans-serif;
  font-weight:700;
  font-size:24px;
  letter-spacing:1px;
  text-align:center;
  text-transform:none;
  cursor:pointer;
  padding:0;
}
.jw-open-btn svg{width:20px;height:20px;flex:none;}
.jw-open-btn:active{transform:scale(.98);}

/* ================= Hero ================= (additive only -- nothing above
   this line, including every Cover rule, was changed). Built from Hero.psd
   (1080x1920): same native-coordinate scene, scaled as one unit with the
   same cover-fit mechanic as the Cover. Hidden until Buka Undangan, then
   it replaces the Cover in the document. Static -- no motion. */
.jw-hero{
  display:none;
  position:relative;
  width:100%;
  height:100dvh;
  overflow:hidden;
  background:#f3e7cf;
}
.jw-hero-scene{
  position:absolute;
  top:50%;
  left:50%;
  width:1080px;
  height:1920px;
  transform-origin:center center;
}
.jw-app.is-hero-active .jw-cover{display:none;}
.jw-app.is-hero-active .jw-hero{display:block;}

/* Hero birds: the owner-approved Betawi Hero bird POC (c8d06f3, .ua-bird-flight),
   same values, Jawa-scoped names. A direct child of the Hero SECTION (not the
   scaled scene: the POC's vw/dvh/% flight is written in screen px, and inside
   the 0.44x scene it shrank and ended off-screen left), so it is clipped by the
   Hero, never on the Cover, gone before Ayat, and starts when the Hero is
   displayed. GIF = wing flaps, CSS = flight path. */
.jw-bird-flight{
  position:absolute;
  inset:0;
  z-index:28;
  overflow:visible;
  pointer-events:none;
}
.jw-bird-flight img{
  position:absolute;
  left:-45%;
  top:43%;
  width:clamp(220px, 72%, 320px);
  height:auto;
  pointer-events:none;
  user-select:none;
  transform-origin:50% 50%;
  will-change:transform, opacity;
  animation:jwBirdFlight 12800ms 650ms linear infinite both;
}
@keyframes jwBirdFlight{
  0%{opacity:0;transform:translate3d(0, 0, 0) scale(.62) rotate(-11deg);}
  8%{opacity:.88;}
  43%{opacity:.92;transform:translate3d(calc(min(100vw, 480px) * .55), -14dvh, 0) scale(.9) rotate(-7deg);}
  74%{opacity:.8;transform:translate3d(calc(min(100vw, 480px) * .98), -28dvh, 0) scale(.72) rotate(-3deg);}
  100%{opacity:0;transform:translate3d(calc(min(100vw, 480px) * 1.35), -39dvh, 0) scale(.54) rotate(1deg);}
}
/* second flock (owner GIF, 700x194, 7.2s loop): the GIF carries its own
   left-to-right flight, so it is only placed across the Hero sky, above the
   palms; same overlay rules as the first bird */
.jw-bird-flock{
  position:absolute;
  left:0;
  top:30%;
  width:100%;
  z-index:28;
  pointer-events:none;
}
.jw-bird-flock img{display:block;width:100%;height:auto;pointer-events:none;user-select:none;}
.jw-nomo .jw-bird-flight,.jw-nomo .jw-bird-flock{display:none;}
@media (prefers-reduced-motion:reduce){.jw-bird-flight,.jw-bird-flock{display:none;}}

/* PSD text layers: colour #a4815d sampled from the layer pixels. Initials
   + "&" are Cochin (faux-bold) in the PSD -- a system font that cannot ship
   on the web -> Gelasio 600, chosen by measured glyph overlap/proportion
   against the PSD's own E/S/& pixels (the widest-fitting serif of ~35
   candidates; Cormorant Garamond came out ~25% too narrow). Sizes make the
   cap height equal the PSD's (E = 247px) and the "&" ink height 43px.
   Date is TamilSangamMN 57.5px (Latin digits, Arial-like) -> Arimo.
   Every box is line-height:1 with an explicit height so the baseline lands
   where the PSD's does; each box is a fixed-width column centred on its PSD
   ink centre so any single initial centres itself. */
.jw-hero-initial,
.jw-hero-amp,
.jw-hero-date{
  color:#a4815d;
  text-align:center;
  white-space:nowrap;
  line-height:1;
}
/* Owner request 2026-09-24: the heavy Gelasio 600 monogram is replaced by
   Cinzel (thinner, more elegant). Cap height 0.716em: 350px -> ~251px (PSD E =
   247); baselines stay on the PSD's (top letter 437, bottom letter 631) and
   both letters keep their PSD ink centres (501 / 574); "&" keeps its 43px
   ink height and centre (655,360). */
.jw-hero-initial{
  font-family:'Cinzel','Cormorant Garamond',Georgia,serif;
  font-weight:500;
  font-size:350px;
  width:400px;
  height:350px;
  text-transform:uppercase;
}
.jw-hero-init-top{left:301px;top:156px;}
.jw-hero-init-bottom{left:374px;top:350px;}
.jw-hero-amp{
  font-family:'Cinzel','Cormorant Garamond',Georgia,serif;
  font-weight:500;
  font-size:59.3px;
  width:120px;
  height:59.3px;
  left:592px;
  top:333px;
}
.jw-hero-date{
  font-family:'Arimo','Helvetica Neue',Arial,sans-serif;
  font-weight:400;
  font-size:57.5px;
  letter-spacing:22.9px;
  padding-left:27.9px;
  width:900px;
  height:57.5px;
  left:90px;
  top:664.7px;
}

/* ================= Ayat ================= (additive only -- nothing above
   this line, including every Cover and Hero rule, was changed). Built from
   ayat.psd (1080x1797). Unlike Cover/Hero it is not a full-screen gate: it
   scrolls in after Hero, so its scene is scaled to the section's WIDTH
   only (script.js sets transform + section height), never cropped. */
.jw-ayat{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#82613e;
}
.jw-ayat-scene{
  position:absolute;
  top:0;
  left:0;
  width:1080px;
  height:1797px;
  transform-origin:0 0;
}
.jw-app.is-hero-active .jw-ayat{display:block;}

/* Photo aperture = PSD "Layer 2" mask: top corners r=60, white base (the
   PSD's own white shows as a 3px hairline left of the photo, which starts
   at x=50). The reference photo is never shipped; foto_ayat fills it. The
   photo's vertical composition follows the PSD: its top 150px are cropped
   (~27% of the overflow) so heads sit in the upper third. */
.jw-ayat-photo{
  border-radius:60px 60px 0 0;
  overflow:hidden;
  background:#fff;
}
.jw-ayat-photo img{
  display:none;
  position:absolute;
  left:3px;
  top:0;
  width:978px;
  height:100%;
  object-fit:cover;
  object-position:50% 27%;
}

/* Baked PSD text -> live text, fonts fitted by measured ink widths:
   initials Georgia-Bold-class 64.4px (Gelasio 700, ink 172px = PSD 172),
   verse Georgia-Italic-class 36px / line-height 1.6 (Gelasio italic; all
   4 PSD lines within 0.4%), source bold sans 27.2px (Inter 700). Each box
   is centred on the PSD's own text centre axis; the verse column is 600px
   wide, which reproduces the PSD's 4-line breaks. */
.jw-ayat-initials{
  font-family:'Gelasio',Georgia,serif;
  font-weight:700;
  font-size:64.4px;
  line-height:1;
  color:#1c2f65;
  text-align:center;
  white-space:nowrap;
}
.jw-ayat-verse{
  display:flex;
  align-items:center;
  justify-content:center;
  font-family:'Gelasio',Georgia,serif;
  font-style:italic;
  font-weight:400;
  font-size:36px;
  line-height:1.6;
  color:#000;
  text-align:center;
}
.jw-ayat-verse-text{width:100%;}
.jw-ayat-source{
  font-family:'Inter','Helvetica Neue',Arial,sans-serif;
  font-weight:700;
  font-size:27.2px;
  line-height:1;
  color:#000;
  text-align:center;
  white-space:nowrap;
}

/* ================= Pasangan ================= (additive only -- nothing above
   this line, including every Cover, Hero and Ayat rule, was changed). Built
   from pasangan.psd (1080x3370). Scrolls in after Ayat, so like Ayat its
   scene is scaled to the section's WIDTH only (script.js). */
.jw-pasangan{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#f5efe3;
}
.jw-pasangan-scene{
  position:absolute;
  top:0;
  left:0;
  width:1080px;
  height:3370px;
  transform-origin:0 0;
}
.jw-app.is-hero-active .jw-pasangan{display:block;}

/* Portrait apertures = the PSD's own ellipses (Layer 28 / copy: 391x468,
   solid #795437 base). The reference portraits are never shipped;
   foto_wanita / foto_pria fill them. Composition follows the PSD: the
   portrait's top ~27% overflow is cropped so faces sit in the upper part. */
.jw-pas-aperture{
  border-radius:50%;
  overflow:hidden;
  background:#795437;
}
.jw-pas-aperture img{
  display:none;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:50% 27%;
}

/* Baked PSD text -> live text (fonts fitted by measured ink widths):
   title Georgia-BoldItalic-class (Gelasio italic 600, 51.8px, #65211b),
   blessing Inter 500 36px / 56px lines (#513e33), full names Cormorant
   Garamond 600 46.8px caps (#793c14 bride, #875230 groom -- the PSD's two
   names are genuinely different browns), parents Poppins 600 20.5px.
   Every box is line-height-explicit so the baseline lands on the PSD's. */
.jw-pas-title,
.jw-pas-name{
  text-align:center;
  white-space:nowrap;
  line-height:1;
}
.jw-pas-title{
  font-family:'Gelasio',Georgia,serif;
  font-style:italic;
  font-weight:600;
  font-size:51.8px;
  color:#65211b;
}
.jw-pas-intro{
  display:flex;
  align-items:center;
  justify-content:center;
  font-family:'Inter','Helvetica Neue',Arial,sans-serif;
  font-weight:500;
  font-size:36px;
  line-height:1.5556;
  color:#513e33;
  text-align:center;
}
.jw-pas-intro-text{width:100%;}
.jw-pas-name{
  font-family:'Cormorant Garamond',serif;
  font-weight:600;
  font-size:46.8px;
  text-transform:uppercase;
}
.jw-pas-name-w{color:#793c14;}
.jw-pas-name-m{color:#875230;}
.jw-pas-parents{
  font-family:'Poppins','Helvetica Neue',Arial,sans-serif;
  font-weight:600;
  font-size:20.5px;
  line-height:1.32;
  color:#000;
  text-align:center;
}

/* Instagram pill (Layer 35 / copy): 317x64, #7c4420, white glyph + label
   (Poppins 500 25px); hidden entirely when no handle is set (script.js). */
.jw-pas-ig{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:24px;
  padding-bottom:6px;
  border-radius:32px;
  background:#7c4420;
  color:#fff;
  font-family:'Poppins','Helvetica Neue',Arial,sans-serif;
  font-weight:500;
  font-size:25px;
  line-height:1;
  text-decoration:none;
}
.jw-pas-ig svg{width:25px;height:25px;flex:none;}

/* ================= Acara ================= (additive only -- nothing above
   this line, including every Cover, Hero, Ayat and Pasangan rule, was
   changed). Built from acara.psd (1080x2859); scrolls in after Pasangan, so
   like Ayat/Pasangan the scene is scaled to the section's WIDTH only. */
.jw-acara{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#f6f0e3;
}
.jw-acara-scene{
  position:absolute;
  top:0;
  left:0;
  width:1080px;
  height:2859px;
  transform-origin:0 0;
}
.jw-app.is-hero-active .jw-acara{display:block;}

/* Live type layers (PSD: Big Caslon / Cochin / Didot, none web-safe):
   title EB Garamond 600 81px, date/time + address Cormorant Garamond 600
   44.4px/45px with lining numerals (the PSD's Cochin figures are lining;
   sizes fitted to the three PSD line widths within 1.6%), venue + countdown label Playfair Display 600 (Didot).
   White #fff on the arch; card 1 title #5e0000, card 2 title #771325 (the
   PSD's two reds). Boxes are line-height-explicit so baselines land on the
   PSD's; every box is a wide column centred on the PSD's own text axis. */
.jw-ac-title,
.jw-ac-dt,
.jw-ac-venue,
.jw-ac-cdlabel{
  text-align:center;
  white-space:nowrap;
}
.jw-ac-title{
  font-family:'EB Garamond',Georgia,serif;
  font-weight:600;
  font-size:81px;
  line-height:1;
  color:#5e0000;
}
.jw-ac-title[data-card="2"]{color:#771325;}
.jw-ac-dt{
  font-family:'Cormorant Garamond',serif;
  font-weight:600;
  font-size:44.4px;
  line-height:48.76px;
  font-variant-numeric:lining-nums;
  color:#fff;
}
.jw-ac-l1,.jw-ac-l2{display:inline-block;}
.jw-ac-venue,
.jw-ac-cdlabel{
  font-family:'Playfair Display','Didot',Georgia,serif;
  font-weight:600;
  line-height:1;
  color:#fff;
}
.jw-ac-venue{font-size:42.1px;}
.jw-ac-cdlabel{font-size:41px;}
.jw-ac-addr{
  font-family:'Cormorant Garamond',serif;
  font-weight:600;
  font-size:45px;
  line-height:1.08;
  font-variant-numeric:lining-nums;
  color:#fff;
  text-align:center;
}
.jw-ac-addr-text{width:100%;}
.jw-ac-maps img,
#jwAcSave img{display:block;width:100%;height:100%;}

/* Countdown boxes: the PSD's four #771325 rounded boxes are the static
   image; digits + captions are live text on top (white). */
.jw-ac-cd{
  color:#fff;
  text-align:center;
}
.jw-ac-num{
  position:absolute;
  left:-3px;
  width:100%;
  top:14px;
  font-family:'Cormorant Garamond',serif;
  font-weight:600;
  font-size:53.5px;
  line-height:1;
}
.jw-ac-cap{
  position:absolute;
  left:-3px;
  width:100%;
  top:82px;
  font-family:'EB Garamond',Georgia,serif;
  font-weight:600;
  font-size:20px;
  line-height:1;
  text-transform:uppercase;
}

/* ================= Galeri ================= (additive only -- nothing above
   this line, including every Cover, Hero, Ayat, Pasangan and Acara rule, was
   changed). Built from Galeri.psd (1080x1920); scrolls in after Acara, so
   like Ayat/Pasangan/Acara its scene is scaled to the section's WIDTH only. */
.jw-galeri{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#82613e;
}
.jw-galeri-scene{
  position:absolute;
  top:0;
  left:0;
  width:1080px;
  height:1920px;
  transform-origin:0 0;
}
.jw-app.is-hero-active .jw-galeri{display:block;}

/* Heading copy: PSD Didot (faux bold) -> Playfair Display 600 (already
   loaded for Acara): "Our Moments" 103px, "GALERI" 55px (+.05em tracking
   to reach the PSD's 209px ink width at the PSD's cap height); the
   Avenir-Light subtitle -> Inter 500 34.3px on 44.5px lines. All white,
   centred on the PSD's own text axis (538.5 / 536.85). */
.jw-gal-galeri,
.jw-gal-title,
.jw-gal-sub{
  color:#fff;
  text-align:center;
  white-space:nowrap;
}
.jw-gal-galeri,
.jw-gal-title{
  font-family:'Playfair Display','Didot',Georgia,serif;
  font-weight:600;
  line-height:1;
}
.jw-gal-galeri{font-size:55px;letter-spacing:.05em;padding-left:.05em;}
.jw-gal-title{font-size:103px;}
.jw-gal-sub{
  font-family:'Inter','Helvetica Neue',Arial,sans-serif;
  font-weight:500;
  font-size:34.3px;
  line-height:44.5px;
}

/* Photo rows: native horizontal scroll-snap strips (the existing UA gallery
   carousel mechanic), two 460px slides visible; slot geometry from the PSD's
   baked photo layer. No motion, no lightbox. */
.jw-gal-row{overflow:hidden;}
.jw-gal-track{
  display:flex;
  gap:17px;
  width:100%;
  height:100%;
  /* hidden, not auto: the strip is scrolled by script.js only. A native
     scroller nested in the transform-scaled scene lets iOS Safari claim the
     horizontal touch for its own (non-working) overflow scrolling and cancel
     the drag -- the phone-only "cannot swipe" bug. scrollLeft/snap/dots are
     unchanged. */
  overflow-x:hidden;
  overflow-y:hidden;
  scroll-snap-type:x mandatory;
  scrollbar-width:none;
  touch-action:pan-y;
}
.jw-gal-track::-webkit-scrollbar{display:none;}
.jw-gal-slide{
  flex:0 0 460px;
  height:100%;
  scroll-snap-align:start;
  border-radius:28px;
  overflow:hidden;
  background:rgba(255,255,255,.06);
}
.jw-gal-slide[hidden]{display:none;}
.jw-gal-slide img{
  display:block;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:50% 35%;
  -webkit-user-drag:none;
  user-select:none;
}
.jw-gal-dots{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:9.5px;
}
.jw-gal-dots span{
  display:block;
  flex:0 0 auto;
  width:10px;
  height:10px;
  border-radius:5px;
  background:#868b98;
}
.jw-gal-dots span.is-active{width:29px;background:#f7f1e8;}

/* ---- Galeri interaction (no visual change to the closed gallery) ----
   The strip's horizontal drag is driven by script.js (touch-action:pan-y
   above hands vertical panning to the page and everything horizontal to
   the script, identically in every browser engine). Slides are tappable.
   The lightbox is a body-level overlay, so it is untouched by the scaled
   scene and invisible until opened. */
.jw-gal-slide{cursor:zoom-in;-webkit-tap-highlight-color:transparent;}
.jw-gal-slide:focus-visible{outline:3px solid #f7f1e8;outline-offset:-3px;}
.jw-lb{
  position:fixed;
  top:0;left:0;right:0;bottom:0;
  z-index:2147483000;
  background:rgba(8,6,4,.96);
  overflow:hidden;
  touch-action:none;
  overscroll-behavior:none;
  -webkit-user-select:none;user-select:none;
  -webkit-tap-highlight-color:transparent;
}
.jw-lb[hidden]{display:none;}
.jw-lb-img{
  position:absolute;
  left:50%;top:50%;
  max-width:none;
  transform-origin:50% 50%;
  will-change:transform;
  -webkit-user-drag:none;
  pointer-events:none;
}
.jw-lb-btn{
  position:absolute;
  width:48px;height:48px;
  border:0;border-radius:24px;
  background:rgba(0,0,0,.55);
  color:#fff;
  font:400 30px/48px 'Inter',Arial,sans-serif;
  text-align:center;
  padding:0;
  cursor:pointer;
  touch-action:manipulation;
}
.jw-lb-x{top:calc(env(safe-area-inset-top,0px) + 12px);right:calc(env(safe-area-inset-right,0px) + 12px);}
.jw-lb-prev,.jw-lb-next{top:50%;margin-top:-24px;}
.jw-lb-prev{left:calc(env(safe-area-inset-left,0px) + 8px);}
.jw-lb-next{right:calc(env(safe-area-inset-right,0px) + 8px);}
.jw-lb-btn[hidden]{display:none;}
/* Touch devices navigate by swipe; the arrows are for mouse/keyboard users
   (and would otherwise sit under the edge where swipes start). */
@media (hover:none) and (pointer:coarse){.jw-lb-prev,.jw-lb-next{display:none;}}
.jw-lb-count{
  position:absolute;
  left:0;right:0;
  bottom:calc(env(safe-area-inset-bottom,0px) + 14px);
  text-align:center;
  color:#fff;
  font:500 15px/1 'Inter',Arial,sans-serif;
  letter-spacing:.06em;
  text-shadow:0 1px 3px rgba(0,0,0,.7);
  pointer-events:none;
}
.jw-lb-count[hidden]{display:none;}

/* ================= Love Story ================= (additive only -- nothing above
   this line, including every Cover..Galeri rule and the Galeri interaction
   rules, was changed). Built from "love story.psd" (1080x2364); scrolls in
   after Galeri, scene scaled to the section's WIDTH only. */
.jw-lovestory{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#ccb7a5;
}
.jw-lovestory-scene{
  position:absolute;
  top:0;
  left:0;
  width:1080px;
  height:2364px;
  transform-origin:0 0;
}
.jw-app.is-hero-active .jw-lovestory{display:block;}

/* Headings (PSD live type layers): "PERJALANAN KAMI" Cochin faux bold ->
   Cormorant Garamond 600 36.8px caps + 18.43px tracking (ink 588px = PSD);
   "Love Story" AdobeClean -> Inter 500 107px + 6.9px tracking (ink 598 x 101
   = PSD). Trailing tracking is cancelled with padding-left so the ink is
   centred on the PSD's own axis (542 / 540). */
.jw-ls-kicker,
.jw-ls-title{
  text-align:center;
  white-space:nowrap;
  line-height:1;
}
.jw-ls-kicker{
  font-family:'Cormorant Garamond',serif;
  font-weight:600;
  font-size:36.8px;
  letter-spacing:18.43px;
  padding-left:18.43px;
  color:#000;
}
.jw-ls-title{
  font-family:'Inter','Helvetica Neue',Arial,sans-serif;
  font-weight:500;
  font-size:107px;
  letter-spacing:6.9px;
  padding-left:6.9px;
  color:#72050a;
}

/* Timeline strip (static art) + card. A card block is 859 wide from the
   strip's left edge; the strip is the PSD's own line + ring/dot. */
.jw-ls-card{overflow:visible;}
.jw-ls-strip{
  position:absolute;
  left:0;top:0;
  width:68px;height:327px;
  max-width:none;
  object-fit:none;
  object-position:0 0;
}
.jw-ls-box{
  position:absolute;
  left:90px;top:3px;
  width:764px;height:282px;
  border-radius:30px;
  background:linear-gradient(180deg,#fff 0%,#fefefd 10%,#fdfcf9 25%,#fbf8f3 50%,#f9f5ee 75%,#f8f3ea 90%,#f7f1e8 100%);
  overflow:hidden;
}
/* Live text: the PSD's block (year, then a 67px-pitch serif body) sits
   centred in the card (+6.4px bias = the PSD's own offset); year = Arial
   Bold Italic -> Arimo 700 italic 38px #2d251d, body = Cochin -> Cormorant
   Garamond 600 47.4px / 67px #362e26 (all 8 PSD story lines fit one size
   within 0.6%). Body wraps at 700px, which reproduces the PSD's breaks. */
.jw-ls-text{
  position:absolute;
  left:0;right:0;top:0;bottom:0;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  padding:6.4px 32px 0;
  text-align:center;
}
.jw-ls-year{
  font-family:'Arimo','Helvetica Neue',Arial,sans-serif;
  font-style:italic;
  font-weight:700;
  font-size:38px;
  line-height:1;
  color:#2d251d;
  white-space:nowrap;
  margin-bottom:22.3px;
}
.jw-ls-body{
  width:700px;
  max-width:100%;
  font-family:'Cormorant Garamond',serif;
  font-weight:600;
  font-size:47.4px;
  line-height:67px;
  color:#362e26;
  overflow-wrap:anywhere;
}

/* ================= Hadiah ================= (additive only -- nothing above
   this line, including every Cover..Love Story rule and the Galeri
   interaction rules, was changed). Built from Hadiah.psd (1882x3693); scrolls
   in after Love Story, scene scaled to the section's WIDTH only (script.js
   also publishes --s = scale for the phone-readable text floors). */
.jw-hadiah{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#82613e;
}
.jw-hadiah-scene{
  position:absolute;
  top:0;
  left:0;
  width:1882px;
  height:3693px;
  transform-origin:0 0;
  --s:.2;
}
.jw-app.is-hero-active .jw-hadiah{display:block;}

/* Description: PSD Poppins Regular 60.2px + 2.17px tracking (fits all 3 PSD
   lines to <1px), 90px lines, #f3efed, centred on the PSD axis 940. */
.jw-hd-desc{
  display:flex;
  align-items:flex-start;
  justify-content:center;
  font-family:'Poppins','Helvetica Neue',Arial,sans-serif;
  font-weight:400;
  font-size:60.2px;
  letter-spacing:2.17px;
  line-height:90px;
  color:#f3efed;
  text-align:center;
}
.jw-hd-desc-text{width:100%;padding-left:2.17px;}

/* Card: the PSD card layer is a 1200x818 rounded rect (r=40) inset 2px in its
   1205x822 bbox, filled with a 160deg gradient (flat #f8f8f3 to ~50%, then
   darkening to #dfe0e0) -- measured by fitting the layer's own pixels. */
.jw-hd-card{
  color:#340e06;
  font-family:'Poppins','Helvetica Neue',Arial,sans-serif;
}
.jw-hd-card::before{
  content:"";
  position:absolute;
  left:2px;top:2px;
  width:1200px;height:818px;
  border-radius:40px;
  background:linear-gradient(160deg,#f7f8f3 0%,#f8f8f3 10%,#f8f8f3 40%,#f8f8f2 50%,#f3f3ef 60%,#eeeeeb 70%,#e9e9e7 80%,#e4e4e4 90%,#dfe0e0 100%);
}
.jw-hd-card > *{position:absolute;}
.jw-hd-card[hidden]{display:none;}
/* Text sizes are the PSD's, with a floor (10-11px on screen, via --s) so a
   phone can read them: title 51.4 / holder 46.6 / date 47.7 / labels 41.4 /
   digits 55.1 + 12.4px tracking. Poppins 500/600 come from the Pasangan
   link, 400 from the Hadiah link. */
.jw-hd-title{
  left:81px;top:117px;width:700px;
  font-weight:600;
  font-size:max(51.4px,calc(11px / var(--s)));
  line-height:1.2;
  color:#330902;
  overflow-wrap:anywhere;
}
.jw-hd-logo{left:826px;top:92px;width:294px;height:86px;display:flex;align-items:center;justify-content:flex-end;}
.jw-hd-logo img{display:block;max-width:100%;max-height:100%;object-fit:contain;object-position:right center;}
.jw-hd-logo-t{
  font-weight:600;
  font-size:max(46px,calc(11px / var(--s)));
  line-height:1.1;
  color:#330902;
  text-align:right;
  overflow-wrap:break-word;
}
.jw-hd-lab{
  font-weight:400;
  font-size:max(41.4px,calc(10px / var(--s)));
  line-height:1;
  color:#38221c;
  white-space:nowrap;
}
.jw-hd-numlab{left:83px;top:349px;}
.jw-hd-num{
  left:80px;top:416px;width:700px;
  font-weight:600;
  font-size:55.1px;
  letter-spacing:12.4px;
  line-height:1.15;
  color:#350a04;
}
.jw-hd-atl{left:83px;top:611px;}
.jw-hd-holder{
  left:85px;top:668px;width:715px;
  font-weight:600;
  font-size:max(46.6px,calc(10.5px / var(--s)));
  line-height:1.2;
  color:#340e06;
  overflow-wrap:anywhere;
}
.jw-hd-pl{right:85px;top:611px;text-align:right;}
.jw-hd-date{
  right:83px;top:666px;width:260px;
  font-weight:600;
  font-size:max(47.7px,calc(10.5px / var(--s)));
  line-height:1.2;
  color:#331409;
  text-align:right;
  white-space:nowrap;
}
.jw-hd-pl[hidden],.jw-hd-date[hidden],.jw-hd-atl[hidden],.jw-hd-holder[hidden]{display:none;}

/* SALIN pill (PSD: 312x106 at card 810,386, #998143, white glyph + label).
   Icon and label are positioned inside the fixed pill so the "TERSALIN"
   feedback swap never shifts the layout. */
.jw-hd-copy{
  left:810px;top:386px;width:312px;height:106px;
  border:0;padding:0;margin:0;
  border-radius:53px;
  background:linear-gradient(180deg,#9a8446 0%,#9c8846 50%,#9a8041 100%);
  color:#fff;
  font-family:'Poppins','Helvetica Neue',Arial,sans-serif;
  font-weight:500;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
}
.jw-hd-copy svg{position:absolute;left:62px;top:32px;width:32px;height:36px;}
.jw-hd-copy-t{
  position:absolute;
  left:157px;top:31px;
  font-size:35.8px;
  line-height:1;
  white-space:nowrap;
}
.jw-hd-copy.is-done{background:#7f7a3a;}

/* ================= Live Streaming ================= (additive only -- nothing
   above this line, including every Cover..Hadiah rule, was changed). Built
   from "Live Streaming.psd" (1080x1480); sits between Acara and Galeri, scene
   scaled to the section's WIDTH only. */
.jw-live{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#f3ecdd;
}
.jw-live-scene{
  position:absolute;
  top:0;
  left:0;
  width:1080px;
  height:1480px;
  transform-origin:0 0;
}
.jw-app.is-hero-active .jw-live{display:block;}

/* Title: Didot faux bold -> Playfair Display 600 62.9px (ink 430x61 = PSD
   428x61), #84382e, baseline 290.4. Description: AdobeClean -> Inter 500
   32.3px on 42px lines, white, centred on the PSD's own axis 557.5; a 600px
   column reproduces the PSD's three line breaks. */
.jw-lv-title{
  font-family:'Playfair Display','Didot',Georgia,serif;
  font-weight:600;
  font-size:62.9px;
  line-height:1;
  color:#84382e;
  text-align:center;
  white-space:nowrap;
}
.jw-lv-desc{
  font-family:'Inter','Helvetica Neue',Arial,sans-serif;
  font-weight:500;
  font-size:32.3px;
  line-height:42px;
  color:#fff;
  text-align:center;
}
/* CTA (PSD: white pill 453x115 + ~3px gold rim, dark-red glyph and serif
   label centred with a 32px gap). Label = EB Garamond 500 39.7px. */
.jw-lv-btn{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:32px;
  box-sizing:border-box;
  padding:0 36px;
  border-radius:60.5px;
  border:3px solid #c9a752;
  background:#fff;
  color:#742128;
  text-decoration:none;
  -webkit-tap-highlight-color:transparent;
}
.jw-lv-btn::after{content:"";position:absolute;left:-12px;right:-12px;top:-14px;bottom:-14px;}
.jw-lv-ico{flex:none;width:41px;height:42px;}
.jw-lv-btn-t{
  font-family:'EB Garamond',Georgia,serif;
  font-weight:500;
  font-size:39.7px;
  line-height:1;
  color:#782830;
  white-space:nowrap;
}


/* ================= Video ================= (additive only -- nothing above
   this line was changed). Built from Video.psd (1882x2658); scrolls in after
   Galeri, scene scaled to the section's WIDTH only (script.js also publishes
   --s = scale for the phone-readable description floor). */
.jw-video{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#82613e;
}
.jw-video-scene{
  position:absolute;
  top:0;
  left:0;
  width:1882px;
  height:2658px;
  transform-origin:0 0;
  --s:.2;
}
.jw-app.is-hero-active .jw-video{display:block;}

/* Aperture = the PSD screenshot's rectangle (1878x1413 at 0,898). */
.jw-vd-frame{background:#000;overflow:hidden;}
.jw-vd-poster{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  margin:0;
  padding:0;
  border:0;
  background:#000;
  cursor:pointer;
  display:flex;
  align-items:center;
  justify-content:center;
  -webkit-appearance:none;
  appearance:none;
  -webkit-tap-highlight-color:transparent;
}
.jw-vd-poster:focus-visible{outline:6px solid #fff;outline-offset:-10px;}
.jw-vd-thumb{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;}
.jw-vd-thumb[hidden]{display:none;}
/* PSD round control: 210px disc, its centre on the aperture centre. */
.jw-vd-btn{
  position:relative;
  display:flex;
  align-items:center;
  justify-content:center;
  width:210px;
  height:210px;
  border-radius:50%;
  background:rgba(80,72,62,.5);
}
.jw-vd-btn svg{width:96px;height:100px;margin-left:9px;}
.jw-vd-poster:active .jw-vd-btn{background:rgba(60,54,46,.62);}
.jw-vd-player{position:absolute;border:0;background:#000;display:block;}
iframe.jw-vd-player{left:0;top:178px;width:100%;height:1056px;}
video.jw-vd-player{left:0;top:0;width:100%;height:100%;object-fit:contain;}
/* the PSD flower band sits above the frame; never blocks the player's controls */
.jw-vd-flowers{pointer-events:none;}

/* Texts: Andale Mono faux bold -> Roboto Mono 400 73px + 16.8px tracking (ink
   580x54 = PSD 580x54), #7a0606. Cochin faux bold -> EB Garamond 500 178px +
   18.7px tracking (Our Video, ink 885x128 = PSD) and EB Garamond 600 53px +
   13.5px tracking (description, two spaces kept as in the PSD), white. The
   trailing tracking is offset with an equal left padding so each line
   centres on the PSD axis. */
.jw-vd-kicker,.jw-vd-title,.jw-vd-desc{
  display:flex;
  align-items:center;
  justify-content:center;
  text-align:center;
  box-sizing:border-box;
  line-height:1;
  white-space:pre;
}
.jw-vd-kicker{
  font-family:'Roboto Mono',ui-monospace,Menlo,Consolas,monospace;
  font-weight:400;
  font-size:73px;
  letter-spacing:16.8px;
  padding-left:16.8px;
  color:#7a0606;
}
.jw-vd-title{
  font-family:'EB Garamond','Cochin',Georgia,serif;
  font-weight:500;
  font-size:178px;
  letter-spacing:18.7px;
  padding-left:18.7px;
  color:#fff;
}
.jw-vd-desc{
  font-family:'EB Garamond','Cochin',Georgia,serif;
  font-weight:600;
  font-size:max(53px,calc(11px / var(--s)));
  letter-spacing:.258em;
  padding-left:.258em;
  color:#fff;
}

/* ================= RSVP ================= (additive only -- nothing above
   this line was changed). Built from RSCV.psd (1882x4542, the RSVP design);
   scrolls in after Love Story, scene scaled to the section's WIDTH only
   (script.js also publishes --s = scale for the phone-readable minimums and
   --dy = how far the lower band + button + cards move up when the Wishes
   field is off). Every rule is scoped to .jw-rsvp -- no global form style. */
.jw-rsvp{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#eadfcf;
}
.jw-rsvp-scene{
  position:absolute;
  top:0;
  left:0;
  width:1882px;
  height:4542px;
  transform-origin:0 0;
  --s:.2;
  --dy:0px;
}
.jw-app.is-hero-active .jw-rsvp{display:block;}

/* lower art band (overlaps the upper one by 200px and fades in over it, so
   moving it up leaves no seam) */
.jw-rs-bgbot{
  left:0;
  top:calc(3250px + var(--dy));
  width:1882px;
  height:1292px;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 200px);
  mask-image:linear-gradient(to bottom,transparent 0,#000 200px);
}

/* Description (canonical deskripsi_rsvp): Arial -> Arimo 400 63.5px on 98px
   lines, black, centred on the PSD axis 940. */
.jw-rs-desc{
  left:240px;
  top:1190px;
  width:1400px;
  height:196px;
  display:flex;
  align-items:flex-start;
  justify-content:center;
  text-align:center;
  font-family:'Arimo','Helvetica Neue',Arial,sans-serif;
  font-weight:400;
  font-size:64px;
  line-height:98px;
  color:#000;
  overflow:hidden;
  overflow-wrap:anywhere;
}

/* Labels: EB Garamond 700 54px, #701c22. */
.jw-rs-label{
  left:80px;
  width:900px;
  height:54px;
  font-family:'EB Garamond','Cochin',Georgia,serif;
  font-weight:700;
  font-size:max(54px,calc(11px / var(--s)));
  line-height:1;
  color:#701c22;
  white-space:nowrap;
}

/* Controls: 1734x192/200/336 white rounded boxes with the PSD's soft edge.
   Text is EB Garamond 400 66px; the computed size is never below 66px (and
   never below 16 effective px), so iOS Safari does not zoom on focus. */
.jw-rs-ctl{
  left:82px;
  width:1734px;
  height:192px;
  box-sizing:border-box;
  margin:0;
  padding:0 57px;
  border:0;
  border-radius:38px;
  background:#fff;
  box-shadow:0 0 0 4px rgba(224,205,176,.55),0 8px 14px rgba(150,112,72,.10);
  font-family:'EB Garamond','Cochin',Georgia,serif;
  font-weight:400;
  font-size:max(66px,calc(16px / var(--s)));
  line-height:1.2;
  color:#2f221d;
  outline:0;
  -webkit-appearance:none;
  appearance:none;
}
.jw-rs-ctl::placeholder{color:#7f7f7f;opacity:1;}
.jw-rs-ctl:focus-visible{box-shadow:0 0 0 6px rgba(112,28,34,.55);}
.jw-rs-select{
  height:200px;
  padding-left:73px;
  padding-right:120px;
  text-overflow:ellipsis;
  cursor:pointer;
  background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='34' height='20' viewBox='0 0 34 20'%3E%3Cpath d='M3 3l14 13L31 3' fill='none' stroke='%232f221d' stroke-width='5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 14px center / 34px 20px;
}
.jw-rs-ctl[hidden],.jw-rs-label[hidden]{display:none;}
.jw-rs-select:disabled{opacity:.55;cursor:not-allowed;}
.jw-rs-area{
  height:336px;
  padding:53px 57px 0;
  resize:none;
  display:block;
  line-height:1.3;
}

/* Submit: 1743x192 #701b22 pill, Cormorant Garamond 600 54px + 7.4px tracking. */
.jw-rs-submit{
  left:77px;
  top:calc(3077px + var(--dy));
  width:1743px;
  height:192px;
  box-sizing:border-box;
  margin:0;
  padding:0 0 0 1px;
  border:0;
  border-radius:44px;
  background:#701b22;
  box-shadow:0 8px 12px rgba(120,80,50,.16);
  color:#fff;
  font-family:'Cormorant Garamond','Cochin',Georgia,serif;
  font-weight:600;
  font-size:max(54px,calc(12px / var(--s)));
  letter-spacing:7.6px;
  text-transform:uppercase;
  line-height:1;
  cursor:pointer;
  -webkit-appearance:none;
  appearance:none;
  -webkit-tap-highlight-color:transparent;
}
.jw-rs-submit:disabled{opacity:.7;cursor:default;}
/* invisible hit-area growth (the pill is only ~37px tall at 360 wide) */
.jw-rs-submit::after{content:"";position:absolute;left:0;right:0;top:-34px;bottom:-34px;}
.jw-rs-submit:focus-visible{outline:6px solid #fff;outline-offset:-14px;}
.jw-rs-status{
  left:82px;
  top:calc(3288px + var(--dy));
  width:1734px;
  height:100px;
  margin:0;
  display:flex;
  align-items:center;
  justify-content:center;
  text-align:center;
  font-family:'EB Garamond','Cochin',Georgia,serif;
  font-weight:500;
  font-size:max(50px,calc(12px / var(--s)));
  line-height:1.15;
  color:#701b22;
}
.jw-rs-status.is-ok{color:#3f5a2b;}
.jw-rs-status:empty{visibility:hidden;}

/* Wish cards: two fixed PSD slots (white 1717x315 + 6px #e3d0a3 rim, r~44).
   Name = Cormorant Garamond italic 600 69px #6f1920, one line; message =
   EB Garamond 400 59.5px #564d41, at most two lines. */
.jw-rs-card{
  left:76px;
  width:1729px;
  height:328px;
  box-sizing:border-box;
  padding:0 69px;
  border:6px solid #e3d0a3;
  border-radius:50px;
  background:#fff;
  display:flex;
  flex-direction:column;
  justify-content:center;
  overflow:hidden;
}
.jw-rs-card[hidden]{display:none;}
#jwRsCard1{top:calc(3400px + var(--dy));}
#jwRsCard2{top:calc(3775px + var(--dy));}
.jw-rs-card-name{
  font-family:'Cormorant Garamond','Cochin',Georgia,serif;
  font-style:italic;
  font-weight:600;
  font-size:max(69px,calc(12px / var(--s)));
  line-height:1.15;
  color:#6f1920;
  position:relative;
  top:-5px;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.jw-rs-card-msg{
  margin:24px 0 0;
  font-family:'EB Garamond','Cochin',Georgia,serif;
  font-weight:400;
  font-size:max(59.5px,calc(12px / var(--s)));
  line-height:1.25;
  color:#564d41;
  display:-webkit-box;
  -webkit-line-clamp:2;
  -webkit-box-orient:vertical;
  overflow:hidden;
  overflow-wrap:anywhere;
}

/* ================= Closing ================= (additive only -- nothing above
   this line was changed). Built from Clousing.psd (1882x3971), the LAST
   section after Hadiah; scene scaled to the section's WIDTH only (script.js
   publishes --s for the phone-readable minimums and sets the section height,
   shorter when there is no photo). The document ends with this panel. */
.jw-closing{
  display:none;
  position:relative;
  width:100%;
  overflow:hidden;
  background:#440e14;
}
.jw-closing-scene{
  position:absolute;
  top:0;
  left:0;
  width:1882px;
  height:3971px;
  transform-origin:0 0;
  --s:.2;
}
.jw-app.is-hero-active .jw-closing{display:block;}

/* Photo aperture = the PSD's 1882x3300 block; the veil is the PSD gradient
   fill (#440e14, 31% at the top rising to 100% at y=3250) that then becomes
   the solid bottom panel down to 3971. */
.jw-cl-photo{
  left:0;
  top:0;
  width:1882px;
  height:3300px;
  overflow:hidden;
  background:#440e14;
}
.jw-cl-photo[hidden],.jw-cl-veil[hidden]{display:none;}
.jw-cl-photo img{
  display:block;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:50% 28%;
}
.jw-cl-veil{
  left:0;
  top:0;
  width:1882px;
  height:3971px;
  background:linear-gradient(to bottom,
    rgba(68,14,20,.306) 0,rgba(68,14,20,.349) 250px,rgba(68,14,20,.396) 500px,
    rgba(68,14,20,.451) 750px,rgba(68,14,20,.506) 1000px,rgba(68,14,20,.565) 1250px,
    rgba(68,14,20,.627) 1500px,rgba(68,14,20,.69) 1750px,rgba(68,14,20,.753) 2000px,
    rgba(68,14,20,.816) 2250px,rgba(68,14,20,.875) 2500px,rgba(68,14,20,.933) 2750px,
    rgba(68,14,20,.984) 3000px,#440e14 3250px,#440e14 3971px);
}
.jw-cl-body{position:absolute;left:0;top:0;width:1882px;height:3971px;}

/* Texts (centre axis 934 like the PSD): kata = Arial -> Arimo 62px on 112px
   lines #eae7da; "We are blessed," Arimo 55px #f9ecba; names = Times italic
   -> Tinos Italic 132px #f0eadf, one line (script.js shrinks a long pair). */
.jw-cl-kata,.jw-cl-blessed,.jw-cl-names{
  margin:0;
  text-align:center;
  overflow-wrap:anywhere;
}
.jw-cl-kata{
  left:376px;
  top:2604px;
  width:1120px;
  height:250px;
  overflow:hidden;
  font-family:'Arimo','Helvetica Neue',Arial,sans-serif;
  font-weight:400;
  font-size:62.5px;
  line-height:112px;
  color:#eae7da;
}
.jw-cl-blessed{
  left:284px;
  top:2917px;
  width:1300px;
  height:60px;
  font-family:'Arimo','Helvetica Neue',Arial,sans-serif;
  font-weight:400;
  font-size:max(55px,calc(11px / var(--s)));
  line-height:60px;
  color:#f9ecba;
  white-space:nowrap;
}
.jw-cl-names{
  left:88px;
  top:3006px;
  width:1700px;
  height:160px;
  display:flex;
  align-items:center;
  justify-content:center;
  font-family:'Tinos','Times New Roman',Times,serif;
  font-style:italic;
  font-weight:400;
  font-size:133px;
  line-height:1;
  color:#f0eadf;
  white-space:nowrap;
  text-align:center;
}
.jw-cl-names:empty{display:none;}

/* Footer art: badge 178x178, circles 324x136 (two 136px links), music disc 184x183 */
.jw-cl-logo{left:848px;top:3488px;width:178px;height:178px;display:block;}
.jw-cl-logo img{display:block;width:100%;height:100%;}
.jw-cl-soc{left:774px;top:3732px;width:324px;height:136px;display:block;pointer-events:none;}
.jw-cl-soc-a{top:3732px;width:136px;height:136px;display:block;}
.jw-cl-logo::after,.jw-cl-soc-a::after{content:"";position:absolute;left:-20px;right:-20px;top:-20px;bottom:-20px;}
.jw-cl-music{
  left:1607px;
  top:3707px;
  width:184px;
  height:183px;
  margin:0;
  padding:0;
  border:0;
  border-radius:50%;
  background:transparent;
  cursor:pointer;
  -webkit-appearance:none;
  appearance:none;
  -webkit-tap-highlight-color:transparent;
}
.jw-cl-music[hidden]{display:none;}
.jw-cl-music img{display:block;width:100%;height:100%;pointer-events:none;}
.jw-cl-music.is-on{background:rgba(249,236,186,.22);}
.jw-cl-music:focus-visible,.jw-cl-logo:focus-visible,.jw-cl-soc-a:focus-visible{outline:6px solid #f9ecba;outline-offset:6px;}

/* ================= Motion (Cover, Hero; Ayat is script-driven) =================
   Fase B of the Betawi-character motion pass. Rules that make it fail-VISIBLE:
   - nothing is hidden by default: no opacity:0 / visibility rule anywhere;
   - Cover/Hero use CSS-only entrances with animation-fill-mode:backwards and
     keyframes that carry only a `from` state (the end state is the element's
     own baseline style), so after the timeline every element rests exactly
     where dcd39b1 put it, whatever JS does;
   - only opacity + the independent translate/scale/rotate properties are
     animated, so the scenes' own `transform` (cover-fit / width-fit scale) is
     never touched; no will-change, no filter, no layout property.
   Softened after owner review: small deltas (scale .92-.95, 8-10px text lift,
   28px foliage rise), tight 0-1000ms stagger, cubic-bezier(.22,.61,.36,1).
   ?motion=0 (html.jw-nomo) and prefers-reduced-motion switch all of it off. */
[data-mo="fade"],[data-mo="up"],[data-mo="scale"],[data-mo="rise"],[data-mo="growY"],[data-mo="growX"],[data-mo="growXY"],[data-mo="bg"]{
  animation-duration:var(--mo-dur,700ms);
  animation-delay:var(--mo-d,0ms);
  animation-timing-function:ease;
  animation-fill-mode:both;
}
[data-mo="fade"]{animation-name:jwMoFade;}
[data-mo="up"]{animation-name:jwMoUp;}
[data-mo="scale"]{animation-name:jwMoScale;}
[data-mo="rise"]{animation-name:jwMoRise;animation-timing-function:cubic-bezier(.22,.61,.36,1);}
[data-mo="growY"]{animation-name:jwMoGrowY;animation-timing-function:cubic-bezier(.22,.61,.36,1);}
[data-mo="growX"]{animation-name:jwMoGrowX;animation-timing-function:cubic-bezier(.22,.61,.36,1);}
[data-mo="growXY"]{animation-name:jwMoGrowXY;animation-timing-function:cubic-bezier(.22,.61,.36,1);}
/* background: soft fade + slow zoom-settle (1.15 -> 1). Finite, scale only, so
   the resting crop is the baseline's; two disjoint properties in one rule. */
[data-mo="bg"]{
  animation-name:jwMoFade,jwMoSettle;
  animation-duration:var(--mo-dur,900ms),var(--mo-settle,6000ms);
  animation-delay:var(--mo-d,0ms),var(--mo-d,0ms);
  animation-timing-function:ease,cubic-bezier(.22,.61,.36,1);
  animation-fill-mode:both,both;
}
@keyframes jwMoFade{from{opacity:0;}}
@keyframes jwMoUp{from{opacity:0;translate:0 var(--mo-ty,30px);}}
@keyframes jwMoRise{from{opacity:0;translate:0 var(--mo-ty,28px);}}
@keyframes jwMoScale{from{opacity:0;scale:var(--mo-s,.9);}}
@keyframes jwMoGrowY{from{opacity:0;scale:1 var(--mo-sy,.3);}}
@keyframes jwMoGrowX{from{opacity:0;scale:var(--mo-sx,.3) 1;}}
@keyframes jwMoGrowXY{from{opacity:0;scale:var(--mo-s,.3);}}
@keyframes jwMoSettle{from{scale:1.05;}}

/* Idle (Cover + Hero): ten light ornaments -- janur x2, coconut x2, jaw14 foliage
   x2, side flower x2, corner buput x2 -- rotate only, pivot = the ornament's own
   root (the same transform-origin as its entrance), +-0.7..1.4deg, 9.8-12.6s per
   cycle. Every element differs in amplitude, direction, duration and start
   offset (--mo-i-off), so they never move in lockstep. The cycle starts and ends
   at 0deg and begins only after the element's own entrance has finished, so
   entrance -> rest -> sway is seamless. Text, initials, names, date, photo, the
   button and the house/background never idle. script.js pauses them
   (--mo-idle-state) while the section is far from the viewport or the tab is
   hidden. */
[data-mo="growY"][data-mo-idle]{animation-name:jwMoGrowY,jwMoSway;}
[data-mo="growXY"][data-mo-idle]{animation-name:jwMoGrowXY,jwMoSway;}
[data-mo="rise"][data-mo-idle]{animation-name:jwMoRise,jwMoSway;}
[data-mo-idle]{
  animation-duration:var(--mo-dur),var(--mo-i-dur,10000ms);
  animation-delay:var(--mo-d),calc(var(--mo-d) + var(--mo-dur) + var(--mo-i-off,0ms));
  animation-timing-function:cubic-bezier(.22,.61,.36,1),ease-in-out;
  animation-iteration-count:1,infinite;
  animation-fill-mode:both,none;
  animation-play-state:running,var(--mo-idle-state,running);
}
@keyframes jwMoSway{
  0%,100%{rotate:0deg;}
  25%{rotate:var(--mo-i-deg,1deg);}
  75%{rotate:calc(var(--mo-i-deg,1deg) * -1);}
}

/* Hero: every visual asset stays alive (owner review of 56da0f5). Entrances are ~1.7x
   slower (durations, not empty delays; the stagger still overlaps), sways are larger
   (+-1.4..2.8deg, pivot at each asset's root) and the scene itself breathes:
     bg      one-shot fade + 1.05 settle (10.2s), then an endless 17s alternate drift
             (transform: translate <=6px + scale <=1.022 together, so the moved image
             always still covers the scene -- no edge can show);
     mist    slow 15.5s translate float;   house  19s scale breathing (1.008) from its base;
     bottom flower band 14s horizontal float;   every other ornament a rotate sway.
   Text (initials, "&", date) only enters, then rests (Betawi's Hero text never idles).
   All of it compositor-only (opacity/translate/scale/rotate/transform), CSS-only,
   starts from the identity state, pauses via --mo-idle-state, and every entrance
   keyframe is `from`-only so the resting state is the baseline. */
.jw-hero [data-mo="growY"][data-mo-idle="breathe"]{animation-name:jwMoGrowY,jwMoBreathe;}
.jw-hero [data-mo="growY"][data-mo-idle="float"]{animation-name:jwMoGrowY,jwMoFloat;}
.jw-hero [data-mo="fade"][data-mo-idle="float"]{animation-name:jwMoFade,jwMoFloat;}
.jw-hero [data-mo="bg"][data-mo-idle="drift"]{
  animation-name:jwMoFade,jwMoSettle,jwMoDrift;
  animation-duration:var(--mo-dur,900ms),var(--mo-settle,6000ms),var(--mo-i-dur,17000ms);
  animation-delay:var(--mo-d,0ms),var(--mo-d,0ms),var(--mo-i-off,0ms);
  animation-timing-function:ease,cubic-bezier(.22,.61,.36,1),ease-in-out;
  animation-iteration-count:1,1,infinite;
  animation-fill-mode:both,both,none;
  animation-play-state:running,var(--mo-idle-state,running),var(--mo-idle-state,running);
}
@keyframes jwMoFloat{
  0%,100%{translate:0 0;}
  50%{translate:var(--mo-f-x,0px) var(--mo-f-y,0px);}
}
@keyframes jwMoBreathe{
  0%,100%{transform:scale(1);}
  50%{transform:scale(var(--mo-b,1.008));}
}
@keyframes jwMoDrift{
  0%,100%{transform:translate3d(0,0,0) scale(1);}
  50%{transform:translate3d(var(--mo-f-x,0px),var(--mo-f-y,0px),0) scale(var(--mo-b,1.022));}
}

/* Cover exit (Betawi: 0.8s opacity + -3% translate on the Cover box, never on
   its scene). script.js swaps Cover for Hero when it ends (or after 900ms). */
.jw-cover{
  transition:opacity .8s cubic-bezier(.22,.61,.36,1),translate .8s cubic-bezier(.22,.61,.36,1);
}
.jw-app.is-open .jw-cover{
  opacity:0;
  translate:0 -3%;
  pointer-events:none;
}

@media (prefers-reduced-motion:reduce){
  [data-mo],[data-mo-idle]{animation:none !important;}
  .jw-cover{transition:none !important;}
}
.jw-nomo [data-mo],.jw-nomo [data-mo-idle]{animation:none !important;}
.jw-nomo .jw-cover{transition:none !important;}

/* ================= Betawi lifecycle parity (Cover -> Hero, long page, Ayat replay) =================
   The long page is laid out under the locked Cover (script.js locks the root
   scroll until Hero is activated), as in Betawi, so nothing heavy happens at the
   hand-off; Hero stays display:none until then. The Open button is not hidden
   separately: the whole Cover fades/lifts together (the .jw-cover exit above). */
.jw-ayat,.jw-pasangan,.jw-acara,.jw-galeri,.jw-lovestory,.jw-hadiah,.jw-live,.jw-video,.jw-rsvp,.jw-closing{display:block;}

/* Ayat replay (Betawi: animation-fill-mode both + play-state paused until the
   replay observer plays each asset; distances are scene px = CSS px x --bwk,
   set per section by script.js). Only the panel-less assets carry data-mo. */
.jw-ayat{--bwk:3;}
@keyframes jwAyatPhoto{
  from{opacity:0;translate:0 calc(-60px * var(--bwk));scale:1.04;}
  to{opacity:1;translate:0 0;scale:1;}
}
@keyframes jwAyatUp{
  from{opacity:0;translate:0 calc(36px * var(--bwk));}
  to{opacity:1;translate:0 0;}
}
@keyframes jwAyatPanel{
  from{opacity:0;translate:0 calc(60px * var(--bwk));scale:.95;}
  to{opacity:1;translate:0 0;scale:1;}
}
.jw-ayat [data-mo="ayat-photo"],.jw-ayat [data-mo="ayat-up"],.jw-ayat [data-mo="ayat-panel"]{
  animation-duration:1300ms;
  animation-delay:var(--mo-d,0ms);
  animation-timing-function:cubic-bezier(.22,.61,.36,1);
  animation-fill-mode:both;
  animation-play-state:paused;
}
.jw-ayat [data-mo="ayat-photo"]{animation-name:jwAyatPhoto;}
.jw-ayat [data-mo="ayat-up"]{animation-name:jwAyatUp;}
.jw-ayat [data-mo="ayat-panel"]{animation-name:jwAyatPanel;}
/* Betawi: still plays normally if the replay step cannot run */
.jw-ayat.is-ayat-active [data-mo^="ayat-"]{animation-play-state:running;}
.jw-ayat.jw-js-replay.is-ayat-active [data-mo^="ayat-"]{animation-play-state:paused;}

/* ================= Living motion: Pasangan -> Closing =================
   Lifecycle = Betawi's (unchanged): every [data-mo] asset of a .jw-rp section
   keeps persistent CSS animations (fill-mode both) paused at their first
   keyframe; script.js plays them when they enter the viewport and resets them
   once they are 12% outside, so every re-entry replays.
   One entrance keyframe for everything (jwRpIn): the element starts offset by
   --rp-x/--rp-y (CSS px, converted to scene px with --bwk) and scaled by
   --rp-s (or --rp-sx/--rp-sy) around its own transform-origin, and settles on
   its PSD position -- so a bottom asset rises from below, a top asset descends,
   a side asset slides in from its side and a corner asset grows out of its
   corner. The resting state is always the identity: the design never moves.
   rp-in   = text / photos / cards / panels / controls (entrance only).
   rp-deco = the independent PSD art: a softer overshoot curve and, when
             data-mo-idle="live", an endless idle in the same animation list,
             starting where the entrance ends. The idle (jwRpLive) is an
             organic push-and-settle (peak at 30%, partial return at 60%) of
             translate + rotate about the asset's own attachment point; each
             asset has its own amplitude (--i-x/--i-y scene px, --i-r),
             duration and offset, so nothing moves in lockstep. Amplitudes were
             checked so no cut PSD edge ever shows at the canvas border.
   Only opacity + the independent translate/scale/rotate properties are
   animated (the scene's own transform is never touched); nothing owns
   pointer/touch events. */
.jw-rp{--bwk:3;}
@keyframes jwRpIn{
  from{
    opacity:var(--rp-o,0);
    translate:calc(var(--rp-x,0px) * var(--bwk,3)) calc(var(--rp-y,0px) * var(--bwk,3));
    scale:var(--rp-sx,var(--rp-s,1)) var(--rp-sy,var(--rp-s,1));
  }
  to{opacity:1;translate:0 0;scale:1 1;}
}
@keyframes jwRpLive{
  0%,100%{translate:0 0;rotate:0deg;}
  30%{translate:var(--i-x,0px) var(--i-y,0px);rotate:var(--i-r,0deg);}
  60%{translate:calc(var(--i-x,0px) * .6) calc(var(--i-y,0px) * .6);rotate:calc(var(--i-r,0deg) * .4);}
}
.jw-rp [data-mo="rp-in"],.jw-rp [data-mo="rp-deco"]{
  animation-name:jwRpIn;
  animation-duration:var(--mo-dur,1200ms);
  animation-delay:var(--mo-d,0ms);
  animation-timing-function:cubic-bezier(.22,.61,.36,1);
  animation-fill-mode:both;
  animation-play-state:paused;
}
.jw-rp [data-mo="rp-deco"]{animation-timing-function:cubic-bezier(.25,.85,.3,1.15);}
.jw-rp [data-mo="rp-in"][data-mo-idle],.jw-rp [data-mo="rp-deco"][data-mo-idle]{
  animation-name:jwRpIn,jwRpLive;
  animation-duration:var(--mo-dur,1200ms),var(--mo-i-dur,12000ms);
  animation-delay:var(--mo-d,0ms),calc(var(--mo-d,0ms) + var(--mo-dur,1200ms) + var(--mo-i-off,0ms));
  animation-timing-function:cubic-bezier(.22,.61,.36,1),ease-in-out;
  animation-iteration-count:1,infinite;
  animation-fill-mode:both,none;
  animation-play-state:paused,paused;
}
.jw-rp [data-mo="rp-deco"][data-mo-idle]{animation-timing-function:cubic-bezier(.25,.85,.3,1.15),ease-in-out;}
/* restored PSD art is pure decoration: never a tap target */
.jw-deco{pointer-events:none;}

/* offscreen paint release: see script.js "Offscreen paint release" */
.jw-far{visibility:hidden;}

/* Idle pause for far sections (replaces the visibility:hidden "farhide": no repaint when a section comes back). */
.jw-idle-off [data-mo-idle]{--mo-idle-state:paused;}
.jw-idle-off .jw-bird-flight{visibility:hidden;}
