/* ══ حَفل زِفاف حَكيم و رشَا ════════════════════════════════════════════
   Palette and metrics come straight from the Canva designs:
   #443927 ink · #996240 / #a88f4c / #dca05b gold · #f0ddc7 / #ffe5c7 cream
   Each page is three real layers (bg / art / text) extracted from Canva,
   so they can parallax and animate independently.
   Overlay widgets are positioned as exact % of the 941×1672 artboard.

   Two kinds of motion run at once:
     • scroll parallax — JS writes the `translate` property
     • ambient drift   — CSS keyframes own `transform`
   They are separate properties, so they compose instead of overwriting one
   another. `.no-indep-transform` (set by app.js on engines without the
   independent properties) keeps the parallax and drops the drift.
   ══════════════════════════════════════════════════════════════════════ */

:root {
  color-scheme: light;
  --ink:      #443927;
  --gold:     #996240;
  --gold-lt:  #c69769;
  --gold-pale:#dca05b;
  --cream:    #f0ddc7;
  --cream-lt: #ffe5c7;
  --sand:     #efcba2;
  --paper:    #f1e7d8;
  --ar: 'Amiri', 'Noto Naskh Arabic', 'Times New Roman', serif;
  --la: 'Cormorant Garamond', 'Amiri', Georgia, serif;
  --ruqaa: 'Aref Ruqaa', 'Amiri', serif;
  --shell: min(100vw, 560px);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: #2f2719;
  color: var(--ink);
  font-family: var(--ar);
  -webkit-text-size-adjust: 100%;
  overscroll-behavior-y: none;
}
body.locked { overflow: hidden; }

img { display: block; max-width: 100%; }
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.i { width: 1.05em; height: 1.05em; flex: none; vertical-align: -0.16em; }

/* ── progress ─────────────────────────────────────────────────────── */
.progress {
  position: fixed; inset: 0 0 auto 0; height: 3px; z-index: 40;
  background: rgba(68, 57, 39, .14);
  opacity: 0; transition: opacity .5s ease;
}
body.opened .progress { opacity: 1; }
.progress span {
  display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, var(--gold-pale), var(--gold));
  transition: width .15s linear;
}

/* ── cover / envelope ─────────────────────────────────────────────── */
/* One sequence, driven entirely by `.is-opening`, with each piece on its own
   delay rather than transitions racing each other. The flap steps behind the
   envelope halfway through its fold — z-index cannot tween, but it can step,
   and that is exactly what a real flap does as it passes the paper. */

.cover {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 5vh;
  background:
    radial-gradient(130% 90% at 50% 34%, #7a6746 0%, #4e412c 46%, #322920 100%);
  transition: opacity 1s cubic-bezier(.4, 0, .2, 1), transform 1.1s cubic-bezier(.32, 0, .24, 1), visibility 1.1s;
}

/* warm light behind the envelope, breathing */
.cover__glow {
  position: absolute; width: 96vw; height: 96vw; max-width: 560px; max-height: 560px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(226, 168, 96, .34), rgba(180, 120, 60, .10) 46%, transparent 70%);
  filter: blur(10px);
  animation: coverGlow 7s ease-in-out infinite;
}
@keyframes coverGlow {
  0%, 100% { transform: scale(1);    opacity: .8 }
  50%      { transform: scale(1.11); opacity: 1 }
}

/* dust drifting through the light — two layers of tiny dots on long loops */
.cover__motes {
  position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image:
    radial-gradient(1.6px 1.6px at 18% 24%, rgba(255,232,196,.9), transparent 60%),
    radial-gradient(1.2px 1.2px at 71% 16%, rgba(255,232,196,.7), transparent 60%),
    radial-gradient(1.8px 1.8px at 39% 71%, rgba(255,232,196,.8), transparent 60%),
    radial-gradient(1.2px 1.2px at 86% 62%, rgba(255,232,196,.6), transparent 60%),
    radial-gradient(1.5px 1.5px at 58% 43%, rgba(255,232,196,.75), transparent 60%),
    radial-gradient(1.1px 1.1px at 8% 57%, rgba(255,232,196,.6), transparent 60%);
  animation: motes 26s linear infinite;
}
@keyframes motes {
  from { transform: translate3d(0, 0, 0) }
  to   { transform: translate3d(-4%, -9%, 0) }
}

.cover__greeting {
  position: relative; margin: 0; padding: 0 9vw;
  font-family: var(--ruqaa);
  font-size: clamp(19px, 5.4vw, 28px); line-height: 1.7;
  color: var(--cream-lt); text-align: center;
  text-shadow: 0 2px 14px rgba(40, 26, 10, .55);
  animation: rise 1s .2s both;
}

/* Amiri here, not Ruqaa: Ruqaa is a display face and at hint size its strokes
   thin out until the word stops reading as a word. */
.cover__hint {
  position: relative; margin: 0;
  font-family: var(--ar);
  font-size: clamp(15px, 4.1vw, 19px);
  color: rgba(255, 233, 205, .8);
  letter-spacing: .02em;
  text-shadow: 0 1px 10px rgba(40, 26, 10, .5);
  animation: rise 1s .5s both, hintPulse 3.4s 1.6s ease-in-out infinite;
}
@keyframes rise { from { opacity: 0; transform: translateY(16px) } to { opacity: 1; transform: none } }
@keyframes hintPulse { 0%, 100% { opacity: .52 } 50% { opacity: 1 } }

/* ── the envelope ─────────────────────────────────────────────────── */
.envelope {
  position: relative; border: 0; background: none; padding: 0; cursor: pointer;
  width: min(74vw, 330px); aspect-ratio: 1.45 / 1;
  perspective: 1200px;
  -webkit-tap-highlight-color: transparent;
  animation: rise 1.1s both, envFloat 9s 1.1s ease-in-out infinite;
}
@keyframes envFloat {
  0%, 100% { transform: translateY(0)      rotate(-0.4deg) }
  50%      { transform: translateY(-9px)   rotate( 0.4deg) }
}
.envelope:focus-visible { outline: 2px solid var(--gold-pale); outline-offset: 14px; border-radius: 10px; }

.env { position: absolute; inset: 0; display: block; transform-style: preserve-3d; }

/* shared paper: a warm cream with a faint woven grain */
.env__back, .env__front, .env__flap, .env__flap-liner {
  position: absolute; display: block;
  background-color: #f3e6d2;
  background-image:
    repeating-linear-gradient(90deg, rgba(150,110,70,.045) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(0deg,  rgba(150,110,70,.035) 0 1px, transparent 1px 3px);
}

.env__back {
  inset: 0; border-radius: 7px; z-index: 1;
  background-image:
    linear-gradient(168deg, #f7ecdb, #e9d6ba),
    repeating-linear-gradient(90deg, rgba(150,110,70,.05) 0 1px, transparent 1px 3px);
  box-shadow: 0 30px 60px -12px rgba(28, 18, 8, .62), 0 4px 14px rgba(28, 18, 8, .3);
}
/* a hairline gold rule set in from the edge, the way a good envelope is blind-ruled */
.env__back::after {
  content: ''; position: absolute; inset: 4.5%;
  border: 1px solid rgba(196, 150, 101, .42);
  border-radius: 4px;
}

/* the front pocket: a V, drawn as one shape rather than two overlapping
   triangles, so there is no seam down the middle */
.env__front {
  inset: 0; border-radius: 7px; z-index: 3;
  background-image: linear-gradient(158deg, #efdfc6, #e3cdad 58%, #dcc3a0);
  clip-path: polygon(0 100%, 0 14%, 50% 63%, 100% 14%, 100% 100%);
  box-shadow: inset 0 1px 0 rgba(255, 250, 240, .5);
}
/* a hairline of gold where the two front edges meet */
.env__front::after {
  content: ''; position: absolute; inset: 0;
  clip-path: polygon(0 13.4%, 50% 62.4%, 100% 13.4%, 100% 15%, 50% 64%, 0 15%);
  background: linear-gradient(90deg, rgba(196,150,101,.55), rgba(224,186,136,.8), rgba(196,150,101,.55));
}

/* the flap, hinged at the top */
.env__flap {
  left: 0; right: 0; top: 0; height: 64%;
  z-index: 4;
  transform-origin: top center;
  transform: rotateX(0deg);
  transform-style: preserve-3d;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  background-image: linear-gradient(174deg, #faf0e0, #ead8bc 70%, #e2cdac);
  border-radius: 7px 7px 0 0;
  filter: drop-shadow(0 6px 8px rgba(40, 26, 10, .22));
}
/* its underside — the coloured liner you only see once it folds back */
.env__flap-liner {
  position: absolute; inset: 0;
  transform: rotateX(180deg);
  backface-visibility: hidden;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  background-image:
    linear-gradient(174deg, #8d5f3c, #6f4728 65%, #5d3a1f),
    repeating-linear-gradient(90deg, rgba(0,0,0,.05) 0 1px, transparent 1px 3px);
  border-radius: 7px 7px 0 0;
}

/* ── the wax seal ─────────────────────────────────────────────────── */
.env__seal {
  position: absolute; left: 50%; top: 54%; width: 27%; aspect-ratio: 1; z-index: 6;
  transform: translate(-50%, -50%);
  /* wax is poured, not stamped round — the radii are deliberately uneven */
  border-radius: 48% 52% 51% 49% / 50% 48% 52% 50%;
  background:
    radial-gradient(circle at 34% 28%, #e0a765, #a8663c 52%, #7a4322 82%, #5e3218);
  box-shadow:
    0 4px 12px rgba(30, 16, 6, .5),
    inset 0 0 0 2.5px rgba(255, 226, 180, .22),
    inset 0 -3px 8px rgba(70, 34, 12, .55),
    inset 0 3px 7px rgba(255, 210, 150, .3);
  display: grid; place-items: center;
}
.env__seal-mono {
  font-family: var(--ruqaa);
  font-size: clamp(19px, 5.9vw, 27px);
  line-height: 1;
  color: #ffeecf;
  text-shadow: 0 1px 3px rgba(60, 28, 10, .75);
  /* the two initials sit side by side as a monogram rather than joining into
     a word, which is what they would do if they touched */
  display: flex; align-items: center; gap: .26em;
  transform: translateY(-2%);
}
.env__seal-mono i { font-style: normal; font-size: .95em; }

/* ── the card inside ──────────────────────────────────────────────── */
.env__card {
  left: 6.5%; right: 6.5%; top: 5%; height: 88%;
  position: absolute; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8%;
  background: linear-gradient(180deg, #fffcf6, #f8eedd);
  border: 1px solid rgba(198, 151, 105, .5);
  border-radius: 4px;
  box-shadow: 0 -2px 10px rgba(40, 26, 10, .12);
  opacity: 0;
}
.env__card-rule {
  width: 42%; height: 1px;
  background: linear-gradient(90deg, transparent, var(--gold-lt), transparent);
}
.env__card-mono {
  font-family: var(--ruqaa);
  font-size: clamp(15px, 4.4vw, 20px);
  color: var(--gold);
  display: flex; align-items: center; gap: .45em;
  white-space: nowrap;
}
.env__card-mono i { font-style: normal; color: var(--gold-pale); font-size: .9em; }

/* ── opening ──────────────────────────────────────────────────────── */
/* the seal gives first */
.cover.is-opening .env__seal { animation: sealBreak .55s cubic-bezier(.4, 0, .6, 1) forwards; }
@keyframes sealBreak {
  0%   { transform: translate(-50%, -50%) scale(1)    rotate(0deg);    opacity: 1 }
  35%  { transform: translate(-50%, -54%) scale(1.09) rotate(-5deg);   opacity: 1 }
  100% { transform: translate(-50%, -34%) scale(.42)  rotate(-38deg);  opacity: 0 }
}

/* then the flap folds back, stepping behind the envelope as it passes */
.cover.is-opening .env__flap { animation: flapOpen .95s .3s cubic-bezier(.45, .05, .25, 1) forwards; }
@keyframes flapOpen {
  0%   { transform: rotateX(0deg);    z-index: 4; filter: drop-shadow(0 6px 8px rgba(40,26,10,.22)) }
  49%  {                              z-index: 4 }
  50%  {                              z-index: 0 }
  100% { transform: rotateX(-174deg); z-index: 0; filter: drop-shadow(0 -10px 14px rgba(40,26,10,.28)) }
}

/* the card rises, and comes forward so it clears the pocket */
.cover.is-opening .env__card { animation: cardOut 1s .75s cubic-bezier(.22, 1, .3, 1) forwards; }
@keyframes cardOut {
  0%   { opacity: 0; transform: translate3d(0, 0, 0) }
  18%  { opacity: 1 }
  100% { opacity: 1; transform: translate3d(0, -62%, 60px) }
}

.cover.is-opening .cover__hint,
.cover.is-opening .cover__greeting { animation: fadeOut .5s ease forwards; }
@keyframes fadeOut { to { opacity: 0; transform: translateY(-10px) } }

/* and the whole thing lifts away */
.cover.is-gone {
  opacity: 0; visibility: hidden;
  transform: scale(1.16) translateY(-3%);
  pointer-events: none;
}

/* ── the deck ─────────────────────────────────────────────────────── */
.deck {
  width: var(--shell); margin-inline: auto;
  background: var(--paper);
  box-shadow: 0 0 60px rgba(0, 0, 0, .5);
  opacity: 0; transition: opacity .8s ease .1s;
}
body.opened .deck { opacity: 1; }

/* ── a page = three stacked layers ────────────────────────────────── */
.page {
  position: relative; display: block;
  container-type: inline-size;
  aspect-ratio: var(--w) / var(--h);
  overflow: hidden;
  background: var(--paper);
  isolation: isolate;
}

/* the 24px blurred preview from lqip.css: a page is never blank, even when
   the guest scrolls faster than the network can hand over the real layers */
.page::before {
  content: ''; position: absolute; inset: -5%; z-index: 0;
  background-image: var(--lqip); background-size: cover; background-position: center;
  filter: blur(16px) saturate(1.08);
}
/* Once the real layers cover it, the blur is invisible but still costs a
   full-page GPU filter on every page, forever. Take it out of the tree. */
.page.loaded::before { display: none; }

.lyr {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  backface-visibility: hidden;
}
/* Promote only the two layers that actually move, and only while the page is
   on screen. A composited layer costs width x height x dpr x 4 bytes, so on a
   3x phone each full-page layer is ~9 MB of GPU memory — promoting all three
   on several pages at once is enough for iOS to give up on accelerating any
   of it, which is how the animations end up frozen on a phone but fine on a
   laptop. The background never animates now, so it has no business here. */
.page.live .lyr--art,
.page.live .lyr--text { will-change: transform; }

/* No scale on any layer. Every Canva page is framed by a cream paper border,
   and scaling an image that exactly fills the page crops that frame away. */
.lyr--bg   { z-index: 1; }
.lyr--art  { z-index: 3; }
.lyr--text { z-index: 4; }

/* entrance: each layer arrives on its own beat, but only once its own bytes
   have landed — `.ready` is added by app.js on load */
/* Opacity only. This used to blur the art in from 6px, but transitioning a
   filter on a full-page image is the one thing here that iOS will quietly
   refuse to finish — and when it stalls the page sits behind a permanent
   blur until you reload. Not worth the flourish. */
.page .lyr--bg   { opacity: 0; transition: opacity 1.1s ease; }
.page .lyr--art  { opacity: 0; transition: opacity .9s ease .18s; }
.page .lyr--text { opacity: 0; transition: opacity .9s ease .42s; }
.page.in .lyr--bg.ready   { opacity: 1; }
.page.in .lyr--art.ready  { opacity: 1; }
.page.in .lyr--text.ready { opacity: 1; }

/* The couple portrait gets a touch more presence — but a drop-shadow on a
   941x1671 image is an expensive filter, and on a phone it is both invisible
   and the sort of thing that pushes iOS past its GPU budget. Desktop only. */
@media (min-width: 620px) {
  .page.in .lyr--art.lyr--hero.ready { filter: drop-shadow(0 12px 26px rgba(30, 22, 12, .34)); }
}

/* a gold halo blooms around the names once, when the page arrives.
   (brightness() would just wash the dark brown out — a shadow reads as gold.) */
.page.in .lyr--shimmer.ready { animation: shimmer 2.6s ease-out .55s 1 both; }
@keyframes shimmer {
  0%   { filter: drop-shadow(0 0 0    rgba(220, 160, 91, 0)) }
  40%  { filter: drop-shadow(0 0 12px rgba(220, 160, 91, .85)) }
  100% { filter: drop-shadow(0 0 0    rgba(220, 160, 91, 0)) }
}

/* ── ambient drift: the scene keeps breathing while you sit on a page ─ */
/* The background holds still so its paper border stays pixel-exact; the
   transparent layers above it carry the motion. */
.page.live .lyr--art  { animation: artSway 26s ease-in-out infinite; }
.page.live .lyr--text { animation: textFloat 17s ease-in-out infinite; }

/* stagger the pages so two never sway on the same beat */
.page[data-page="2"] .lyr--art { animation-delay: -4s }
.page[data-page="3"] .lyr--art { animation-delay: -9s }
.page[data-page="4"] .lyr--art { animation-delay: -14s }
.page[data-page="5"] .lyr--art { animation-delay: -19s }
.page[data-page="6"] .lyr--art { animation-delay: -23s }

/* translate only — a scale here would crop the border back off */
@keyframes artSway {
  0%, 100% { transform: translate3d( 0.30%,  0.00%, 0) rotate(-0.09deg) }
  50%      { transform: translate3d(-0.30%, -0.38%, 0) rotate( 0.09deg) }
}
@keyframes textFloat {
  0%, 100% { transform: translate3d(0,  0.00%, 0) }
  50%      { transform: translate3d(0, -0.42%, 0) }
}

/* JS owns `transform` on these engines, so the drift has to sit this one out */
.no-indep-transform .page.live .lyr { animation: none !important; }

/* ── the palm fronds on page 01 ───────────────────────────────────── */
/* Split out of p1-art by scripts/make-frond.py so they can move on their own.
   The origin sits where they attach at the top-right corner, so the tips
   travel and the join does not. */
.frond {
  position: absolute; z-index: 3;
  left: 69.926%; top: 0;
  width: 30.074%; height: 21.903%;
  object-fit: fill;
  transform-origin: 95% 3%;
  opacity: 0; transition: opacity .9s ease .18s;
  pointer-events: none;
}
.page.in .frond.ready { opacity: 1; }
.page.live .frond { animation: frondSway 7.5s ease-in-out infinite; will-change: transform; }

/* uneven on purpose — a frond in a breeze does not keep time */
@keyframes frondSway {
  0%   { transform: rotate(-0.85deg) }
  34%  { transform: rotate( 0.55deg) }
  58%  { transform: rotate(-0.25deg) }
  79%  { transform: rotate( 0.80deg) }
  100% { transform: rotate(-0.85deg) }
}

/* ── sun rays on page 01 ──────────────────────────────────────────── */
/* The fan is centred on the sun but lives inside a box that stops at the
   horizon, so the rays only ever reach up into the sky — clipping is far
   safer here than trying to intersect two masks, which Safari only learned
   recently. */
.rays {
  position: absolute; left: 0; right: 0; top: 0;
  height: var(--horizon);
  overflow: hidden; pointer-events: none; z-index: 2;
  mix-blend-mode: screen;
  opacity: 0;
}
.page.in .rays { opacity: 1; transition: opacity 2.4s ease 1s; }

.rays i {
  position: absolute; left: var(--x); top: 100%;
  width: 190%; aspect-ratio: 1;
  /* nothing right at the sun, nothing at the far end */
  -webkit-mask-image: radial-gradient(closest-side, transparent 9%, #000 32%, rgba(0,0,0,.55) 58%, transparent 76%);
          mask-image: radial-gradient(closest-side, transparent 9%, #000 32%, rgba(0,0,0,.55) 58%, transparent 76%);
  transform: translate(-50%, -50%);
  will-change: transform, opacity;
}

/* Three fans, all with a 24deg repeat but different ray shapes, offsets and
   speeds. Because they turn at different rates they slide across one another,
   so a ray brightens as two line up and thins out again as they part — rays
   appear and fade without any single one of them ever blinking. */
.rays i:nth-child(1) {
  background: repeating-conic-gradient(from 0deg at 50% 50%,
    rgba(255, 219, 165, 0)   0deg,
    rgba(255, 219, 165, .34) 1.8deg,
    rgba(255, 219, 165, 0)   5.2deg,
    rgba(255, 219, 165, 0)   24deg);
  animation: raySpin 34s linear infinite, rayFade 23s ease-in-out infinite;
}
.rays i:nth-child(2) {
  background: repeating-conic-gradient(from 9deg at 50% 50%,
    rgba(255, 231, 188, 0)   0deg,
    rgba(255, 231, 188, .22) 1deg,
    rgba(255, 231, 188, 0)   3.4deg,
    rgba(255, 231, 188, 0)   12deg,
    rgba(255, 231, 188, .16) 13.2deg,
    rgba(255, 231, 188, 0)   15.6deg,
    rgba(255, 231, 188, 0)   24deg);
  animation: raySpin 52s linear infinite, rayFade 17s ease-in-out infinite -7s;
}
.rays i:nth-child(3) {
  background: repeating-conic-gradient(from 15deg at 50% 50%,
    rgba(255, 208, 138, 0)   0deg,
    rgba(255, 208, 138, .30) 2.8deg,
    rgba(255, 208, 138, 0)   7.4deg,
    rgba(255, 208, 138, 0)   24deg);
  animation: raySpin 77s linear infinite, rayFade 31s ease-in-out infinite -13s;
}

/* Each fan repeats every 24deg, so turning by exactly that much lands the
   pattern back on itself. A full 360 would not divide evenly into 24 the way
   the eye expects and the loop point would show. */
@keyframes raySpin {
  from { transform: translate(-50%, -50%) rotate(0deg) }
  to   { transform: translate(-50%, -50%) rotate(24deg) }
}
@keyframes rayFade {
  0%, 100% { opacity: .28 }
  50%      { opacity: 1 }
}

/* ── the sea on page 01 ───────────────────────────────────────────── */
/* Two bands of light streaks crossing in opposite directions. Neither reads
   as anything on its own; together they read as water. */
.waves {
  position: absolute; left: 0; right: 0; z-index: 2;
  top: var(--top); height: var(--h);
  overflow: hidden; pointer-events: none; mix-blend-mode: screen;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 68%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 68%, transparent);
}
.waves__band {
  position: absolute; inset: 0 auto 0 0;
  width: 200%;
  background-repeat: repeat-x;
  background-size: 50% 100%;
  will-change: transform;
}
.waves__band--far  { background-image: url(layers/waves-far.webp);  opacity: .40; animation: waveDrift 46s linear infinite; }
.waves__band--near { background-image: url(layers/waves-near.webp); opacity: .30; animation: waveDrift 29s linear infinite reverse; }
@keyframes waveDrift { from { transform: translateX(0) } to { transform: translateX(-50%) } }

/* ── drifting clouds over the sky pages ───────────────────────────── */
.sky {
  position: absolute; left: 0; right: 0; z-index: 2;
  top: var(--top); height: var(--h);
  overflow: hidden; pointer-events: none;
  /* hold full strength across most of the band — fading it from 20% to 70%
     left so little of the cloud visible that it read as nothing at all */
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 80%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 80%, transparent);
}
.sky__band {
  position: absolute; inset: 0 auto 0 0;
  width: 200%;
  /* the sprite tiles horizontally, and one tile is exactly the sky's width —
     so sliding by 50% of the band lands on the next tile and never seams */
  background-repeat: repeat-x;
  background-size: 50% 100%;
  will-change: transform;
}
/* Slow enough that you never catch them moving, strong enough that the sky
   is plainly different a minute later. */
.sky__band--far  { background-image: url(layers/clouds-far.webp);  opacity: .78; animation: cloudDrift 115s linear infinite; }
.sky__band--near { background-image: url(layers/clouds-near.webp); opacity: .58; animation: cloudDrift 74s linear infinite; }
@keyframes cloudDrift { from { transform: translateX(0) } to { transform: translateX(-50%) } }

/* `screen` over an already-bright sunset barely registers — the cloud has
   nothing left to lighten. Soft-light keeps them reading as cloud against
   the paint instead of washing out. The night sky is dark, so screen works
   there and stays subtle. */
.sky--dusk  { mix-blend-mode: soft-light; opacity: .95; }
.sky--night { mix-blend-mode: screen; opacity: .5; }
.sky--night .sky__band--far  { animation-duration: 150s; opacity: .42; }
.sky--night .sky__band--near { animation-duration: 98s;  opacity: .30; animation-direction: reverse; }

/* ── stars on the closing page ────────────────────────────────────── */
.stars {
  position: absolute; left: 0; right: 0; z-index: 2;
  top: var(--top); height: var(--h);
  pointer-events: none;
}
.star {
  position: absolute; border-radius: 50%;
  background: #fff8e8; box-shadow: 0 0 4px rgba(255, 244, 214, .9);
  opacity: 0;
  animation: twinkle var(--dur, 4s) ease-in-out var(--delay, 0s) infinite;
}
@keyframes twinkle {
  0%, 100% { opacity: .07; transform: scale(.65) }
  50%      { opacity: var(--peak, .7); transform: scale(1) }
}

/* ── a sweep of light across the water ────────────────────────────── */
.sheen {
  position: absolute; left: 0; right: 0; z-index: 2;
  top: var(--top); height: var(--h);
  overflow: hidden; pointer-events: none; mix-blend-mode: screen;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 45%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 45%, transparent);
}
.sheen::after {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 55%;
  background: linear-gradient(100deg, transparent, rgba(255, 228, 176, .28) 45%, transparent);
  animation: sheenSweep 11s ease-in-out infinite;
}
@keyframes sheenSweep {
  0%        { transform: translateX(-90%) }
  60%, 100% { transform: translateX(240%) }
}

/* ── lamps, sun, moon ─────────────────────────────────────────────── */
.glow {
  position: absolute; z-index: 2;
  left: var(--x); top: var(--y);
  width: var(--r); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 226, 170, .85), rgba(255, 196, 120, .35) 42%, transparent 70%);
  mix-blend-mode: screen;
  opacity: 0; pointer-events: none;
}
.page.in .glow { animation: lamp 5.5s ease-in-out infinite 1s; }
.glow--sun  { background: radial-gradient(circle, rgba(255, 214, 150, .9), rgba(255, 160, 80, .32) 45%, transparent 70%); }
.glow--moon { background: radial-gradient(circle, rgba(226, 232, 255, .8), rgba(180, 200, 255, .25) 45%, transparent 70%); }
@keyframes lamp {
  0%, 100% { opacity: .5;  transform: translate(-50%, -50%) scale(1) }
  50%      { opacity: .95; transform: translate(-50%, -50%) scale(1.12) }
}

/* ── lanterns: a flame, not a pulsing disc ────────────────────────── */
/* Two parts, because that is what a real flame throws: a small hot core that
   dances, and a wide warm wash on the wall around it. They flicker on
   different clocks and neither is regular, which is what keeps it from
   reading as a blinking light. */
.lamp {
  position: absolute; z-index: 2;
  left: var(--x); top: var(--y);
  width: var(--r); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  mix-blend-mode: screen;
  pointer-events: none;
  opacity: 0;
}
.page.in .lamp { opacity: 1; transition: opacity 1.8s ease .5s; }

.lamp::before,
.lamp::after { content: ''; position: absolute; left: 50%; top: 50%; border-radius: 50%; }

/* the light it throws into the room */
.lamp::before {
  width: 100%; height: 100%; margin: -50% 0 0 -50%;
  background: radial-gradient(circle,
      rgba(255, 198, 116, .72) 0%,
      rgba(255, 162, 70, .32) 34%,
      rgba(255, 140, 50, .11) 58%,
      transparent 75%);
  animation: lampHalo 6.1s ease-in-out infinite;
}

/* the flame */
.lamp::after {
  width: 30%; height: 30%; margin: -15% 0 0 -15%;
  background: radial-gradient(circle,
      rgba(255, 249, 229, .95) 0%,
      rgba(255, 216, 145, .68) 40%,
      rgba(255, 172, 84, .24) 70%,
      transparent 86%);
  animation: lampCore 2.3s ease-in-out infinite;
}

/* no two lanterns in a room ever flicker together */
.lamp:nth-of-type(2)::before { animation-duration: 7.4s; animation-delay: -2.9s }
.lamp:nth-of-type(2)::after  { animation-duration: 3.1s; animation-delay: -1.3s }
.lamp:nth-of-type(3)::before { animation-duration: 5.3s; animation-delay: -4.1s }
.lamp:nth-of-type(3)::after  { animation-duration: 2.7s; animation-delay: -0.7s }
.lamp:nth-of-type(4)::before { animation-duration: 8.2s; animation-delay: -1.6s }
.lamp:nth-of-type(4)::after  { animation-duration: 1.9s; animation-delay: -2.2s }

@keyframes lampHalo {
  0%   { opacity: .70; transform: scale(1.00) }
  17%  { opacity: .95; transform: scale(1.06) }
  29%  { opacity: .64; transform: scale(0.975) }
  46%  { opacity: 1;   transform: scale(1.09) }
  61%  { opacity: .77; transform: scale(1.02) }
  74%  { opacity: .92; transform: scale(1.05) }
  88%  { opacity: .68; transform: scale(0.99) }
  100% { opacity: .70; transform: scale(1.00) }
}
@keyframes lampCore {
  0%   { opacity: .84; transform: translate(0, 0) scale(1) }
  21%  { opacity: 1;   transform: translate(2%, -3%) scale(1.12) }
  38%  { opacity: .78; transform: translate(-1.5%, 1.5%) scale(.95) }
  55%  { opacity: .98; transform: translate(1.5%, -1.5%) scale(1.08) }
  72%  { opacity: .81; transform: translate(-2%, .5%) scale(.97) }
  100% { opacity: .84; transform: translate(0, 0) scale(1) }
}

/* generic reveal for the non-image sections */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .9s ease, transform .9s cubic-bezier(.22, 1, .36, 1); }
.reveal.in { opacity: 1; transform: none; }

/* ── overlay: the map button on page 04 ───────────────────────────── */
.hot {
  position: absolute; z-index: 6;
  display: grid; place-items: center;
  text-decoration: none; color: var(--sand);
  font-family: var(--ar); line-height: 1;
  border-radius: 999px;
  background: var(--gold);
  transition: transform .18s ease, filter .18s ease;
  -webkit-tap-highlight-color: transparent;
}
.hot--map {
  left: 38.62%; width: 39.09%;
  top: 73.41%; height: 3.59%;
  font-size: 3.67cqw;
}
.hot::after {
  content: ''; position: absolute; inset: -6%;
  border-radius: inherit; border: 1px solid rgba(220, 160, 91, .55);
  opacity: 0; transform: scale(.94);
}
.page.in .hot::after { animation: halo 3.2s ease-out infinite 1.5s; }
@keyframes halo {
  0%   { opacity: .9; transform: scale(.94) }
  70%  { opacity: 0;  transform: scale(1.16) }
  100% { opacity: 0;  transform: scale(1.16) }
}
.hot:active { transform: scale(.97); filter: brightness(1.06); }

/* ── overlay: RSVP on page 05 ─────────────────────────────────────── */
.rsvp { position: absolute; inset: 0; z-index: 6; margin: 0; }
.rsvp[hidden], .rsvp2[hidden] { display: none; }

.rsvp__names,
.rsvp__opt,
.rsvp__submit {
  position: absolute;
  font-family: var(--ar); color: var(--ink);
  border: 0.32cqw solid var(--gold-lt);
  background: var(--cream);
  font-size: 4.68cqw; line-height: 1.4;
  text-align: center;
  -webkit-tap-highlight-color: transparent;
  -webkit-appearance: none; appearance: none;
}

.rsvp__names {
  left: 19.10%; width: 61.49%;
  top: 38.50%; height: 12.22%;
  border-radius: 3.4cqw;
  /* top pad centres the single-line placeholder inside the 12.22% tall box */
  padding: 6.6cqw 3cqw 2cqw;
  resize: none; overflow-y: auto;
  transition: box-shadow .2s ease, border-color .2s ease;
}
.rsvp__names::placeholder { color: var(--ink); opacity: .41; }
.rsvp__names:focus {
  outline: 0; border-color: var(--gold);
  box-shadow: 0 0 0 0.5cqw rgba(153, 98, 64, .16);
}

.rsvp__choice { display: contents; }

.rsvp__opt {
  top: 58.63%; height: 6.29%; width: 29.86%;
  border-radius: 2.2cqw;
  display: grid; place-items: center; cursor: pointer;
  /* Amiri sets «أعتذر عن الحضور» wider than the original Canva face, so it
     wrapped to two lines and overflowed the fixed artboard height. */
  white-space: nowrap;
  font-size: 4.02cqw;
  transition: background-color .22s ease, color .22s ease, transform .18s ease, box-shadow .22s ease;
}
#optNo  { left: 19.10%; }
#optYes { left: 50.72%; }
.rsvp__opt:active { transform: scale(.98); }
.rsvp__opt[aria-checked="true"] {
  background: var(--gold); color: var(--cream-lt); border-color: var(--gold);
  box-shadow: 0 0.6cqw 1.6cqw rgba(68, 57, 39, .22);
}

.rsvp__submit {
  left: 19.10%; width: 61.49%;
  top: 68.41%; height: 6.29%;
  border-radius: 999px;
  background: var(--ink); border-color: var(--ink); color: var(--cream);
  cursor: pointer;
  transition: transform .18s ease, filter .18s ease, opacity .2s ease;
}
.rsvp__submit:active { transform: scale(.985); filter: brightness(1.12); }
.rsvp__submit[disabled] { opacity: .6; cursor: progress; }

.rsvp__msg {
  position: absolute; left: 19.10%; width: 61.49%; top: 75.8%;
  margin: 0; text-align: center; font-size: 3.4cqw; min-height: 4cqw;
  color: #8a3b2a;
}

/* ── step two: head count, phone, anything we should know ─────────── */
/* It replaces the printed card rather than sitting under it, so the page
   keeps its composition and the caterer still gets a number. */
.rsvp2 {
  position: absolute; z-index: 7;
  left: 13%; width: 74%;
  /* height follows the content now that it is just the head count — a fixed
     box would leave the panel mostly empty */
  top: 38%;
  display: flex; flex-direction: column; gap: 2.6cqw;
  background: var(--cream);
  border: 0.32cqw solid var(--gold-lt); border-radius: 3.4cqw;
  padding: 5cqw 4.5cqw 4cqw;
  box-shadow: 0 1.4cqw 4cqw rgba(48, 38, 22, .2);
  animation: rise .5s both;
}
.rsvp2__title {
  margin: 0 0 .4cqw; text-align: center;
  font-size: 5.2cqw; color: var(--ink);
}

.field { display: flex; flex-direction: column; gap: 1.4cqw; }
.field__label { font-size: 3.6cqw; color: var(--ink); opacity: .86; }

.field--count { flex-direction: row; align-items: center; justify-content: space-between; gap: 2cqw; }

.stepper {
  display: inline-flex; align-items: center; gap: 1cqw;
  /* − on the left, + on the right, as on a number line — the digits are
     Latin here too, so LTR is the readable order in both languages */
  direction: ltr;
  background: #fffaf1; border: 0.28cqw solid var(--gold-lt);
  border-radius: 999px; padding: 0.8cqw;
}
.stepper__btn {
  width: 8cqw; height: 8cqw; flex: none;
  display: grid; place-items: center; cursor: pointer;
  font-family: var(--la); font-size: 5cqw; line-height: 1;
  color: var(--cream-lt); background: var(--gold);
  border: 0; border-radius: 50%;
  -webkit-tap-highlight-color: transparent;
  transition: transform .15s ease, filter .15s ease;
}
.stepper__btn:active { transform: scale(.92); filter: brightness(1.1); }
.stepper__val {
  width: 11cqw; border: 0; background: none; text-align: center;
  font-family: var(--la); font-size: 5.4cqw; font-weight: 500; color: var(--gold);
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield; appearance: textfield;
}
.stepper__val::-webkit-outer-spin-button,
.stepper__val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper__val:focus { outline: 0; }

.rsvp2__submit {
  margin-top: 1cqw;
  font-family: var(--ar); font-size: 4.4cqw; line-height: 1.4;
  color: var(--cream); background: var(--ink);
  border: 0; border-radius: 999px; padding: 2.6cqw;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: transform .18s ease, filter .18s ease, opacity .2s ease;
}
.rsvp2__submit:active { transform: scale(.985); filter: brightness(1.12); }
.rsvp2__submit[disabled] { opacity: .6; cursor: progress; }

.rsvp__msg--step2 { position: static; width: auto; min-height: 0; font-size: 3.4cqw; }

.rsvp2__back {
  font-family: var(--ar); font-size: 3.4cqw;
  background: none; border: 0; color: var(--ink); opacity: .6;
  cursor: pointer; padding: .4cqw;
}

.thanks {
  position: absolute; z-index: 7;
  left: 19.10%; width: 61.49%;
  top: 38.50%; min-height: 36%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2.4cqw; text-align: center;
  background: var(--cream);
  border: 0.32cqw solid var(--gold-lt); border-radius: 3.4cqw;
  padding: 4cqw 3cqw;
  animation: rise .7s both;
}
.thanks[hidden] { display: none; }
.thanks__line { margin: 0; font-size: 6.4cqw; color: var(--ink); }
.thanks__sub  { margin: 0; font-size: 4.2cqw; color: var(--ink); opacity: .75; line-height: 1.5; }

/* ── countdown strip ──────────────────────────────────────────────── */
.strip { background: var(--paper); padding: clamp(28px, 8vw, 54px) clamp(18px, 6vw, 34px); }
.strip__inner { display: flex; flex-direction: column; align-items: center; gap: clamp(14px, 4vw, 22px); }
.strip__title { margin: 0; font-size: clamp(19px, 5.4vw, 26px); font-weight: 400; color: var(--ink); }
.strip__actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }

.rule { display: block; width: 46%; height: 1px; position: relative;
        background: linear-gradient(90deg, transparent, var(--gold-lt), transparent); }
.rule::after {
  content: '❖'; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  background: var(--paper); padding: 0 8px; color: var(--gold-lt); font-size: 10px;
}

.count {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(6px, 2.4vw, 14px); width: 100%; max-width: 420px;
  /* days first, reading left to right — the page is RTL, which would
     otherwise put the seconds on the left and the days on the right */
  direction: ltr;
}
.count__cell {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: clamp(8px, 2.6vw, 14px) 2px;
  background: var(--cream); border: 1px solid var(--gold-lt); border-radius: 12px;
}
.count__cell b {
  font-family: var(--la); font-weight: 500; color: var(--gold);
  font-size: clamp(24px, 8vw, 38px); line-height: 1; font-variant-numeric: tabular-nums;
}
.count__cell span { font-size: clamp(11px, 3.2vw, 14px); color: var(--ink); opacity: .72; }
.count__done { grid-column: 1 / -1; margin: 0; text-align: center; }

.ghost-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5em;
  font-family: var(--ar); font-size: clamp(14px, 4vw, 17px);
  color: var(--ink); background: transparent;
  border: 1px solid var(--gold-lt); border-radius: 999px;
  padding: .62em 1.5em; cursor: pointer; text-decoration: none;
  transition: background-color .2s ease, transform .18s ease;
  -webkit-tap-highlight-color: transparent;
}
.ghost-btn:active { transform: scale(.97); }
.ghost-btn:hover { background: var(--cream); }
.ghost-btn--sm { border-color: var(--gold); color: var(--gold); font-size: 3.6cqw; padding: .5em 1.4em; }

/* ── outro ────────────────────────────────────────────────────────── */
.outro {
  background: var(--paper);
  padding: clamp(30px, 9vw, 60px) clamp(18px, 6vw, 34px) calc(clamp(30px, 9vw, 60px) + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; align-items: center; gap: clamp(14px, 4vw, 22px);
  text-align: center;
}
.outro__names { margin: 0; font-size: clamp(28px, 9vw, 42px); color: var(--ink); line-height: 1.3; }
.outro__names i { font-style: normal; color: var(--gold-pale); }
.outro__meta { margin: 0; font-size: clamp(14px, 4vw, 17px); color: var(--ink); opacity: .78; line-height: 1.9; }
.outro__actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.outro__tag { margin: 0; font-size: clamp(13px, 3.8vw, 16px); color: var(--gold); letter-spacing: .02em; }

/* only ever seen with scripting off */
.nojs { display: none; background: var(--cream); padding: clamp(24px, 7vw, 40px) clamp(18px, 6vw, 34px);
        text-align: center; font-size: clamp(14px, 4vw, 17px); line-height: 1.9; border-top: 1px solid var(--gold-lt); }
.nojs a { color: var(--gold); }
.nojs__contacts { margin: .6em 0 1.2em; }

/* ── ambience ─────────────────────────────────────────────────────── */
.sound {
  position: fixed; inset-block-start: calc(12px + env(safe-area-inset-top));
  z-index: 45; height: 40px; border-radius: 999px;
  display: grid; place-items: center; cursor: pointer;
  color: var(--ink); background: rgba(241, 231, 216, .85);
  border: 1px solid var(--gold-lt); backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .6s ease .6s, transform .2s ease;
  -webkit-tap-highlight-color: transparent;
}
.sound { inset-inline-start: 12px; width: 40px; overflow: hidden; }
/* the class sets display:grid, which would otherwise beat the UA [hidden] rule */
.sound[hidden] { display: none; }
body.opened .sound { opacity: 1; }
.sound:active { transform: scale(.94); }

/* ── the music button: five dancing bars ──────────────────────────── */
.eq {
  display: flex; align-items: flex-end; justify-content: center;
  gap: 2px; height: 17px;
}
.eq i {
  display: block; width: 2.5px; height: 100%;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--gold-pale), var(--gold));
  transform-origin: bottom center;
  transform: scaleY(.18);
  transition: transform .45s cubic-bezier(.22, 1, .36, 1);
}

/* playing: each bar runs at its own tempo, so they never move as a block */
.sound[aria-pressed="true"] .eq i { animation: eq 1s ease-in-out infinite alternate; }
.sound[aria-pressed="true"] .eq i:nth-child(1) { animation-duration: .82s; animation-delay: -.42s }
.sound[aria-pressed="true"] .eq i:nth-child(2) { animation-duration: .57s; animation-delay: -.18s }
.sound[aria-pressed="true"] .eq i:nth-child(3) { animation-duration: .95s; animation-delay: -.64s }
.sound[aria-pressed="true"] .eq i:nth-child(4) { animation-duration: .68s; animation-delay: -.31s }
.sound[aria-pressed="true"] .eq i:nth-child(5) { animation-duration: .88s; animation-delay: -.07s }

@keyframes eq {
  from { transform: scaleY(.20) }
  to   { transform: scaleY(1) }
}

/* muted: the bars settle, and a hairline strikes through them */
.sound[aria-pressed="false"] .eq i { animation: none; transform: scaleY(.18); opacity: .5; }
.sound[aria-pressed="false"]::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 24px; height: 1.5px; border-radius: 2px;
  background: var(--gold);
  transform: translate(-50%, -50%) rotate(-45deg);
  opacity: .75;
}

.scroll-hint {
  position: fixed; inset-block-end: calc(16px + env(safe-area-inset-bottom)); left: 50%;
  z-index: 45; width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center; color: var(--cream-lt);
  background: rgba(68, 57, 39, .55); border: 1px solid rgba(220, 160, 91, .5);
  backdrop-filter: blur(4px); text-decoration: none;
  opacity: 0; pointer-events: none; transition: opacity .5s ease;
  animation: bob 2.4s ease-in-out infinite;
}
body.opened .scroll-hint.show { opacity: 1; pointer-events: auto; }
@keyframes bob { 0%, 100% { transform: translate(-50%, 0) } 50% { transform: translate(-50%, 6px) } }

/* ── motion & print ───────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  .page .lyr { opacity: 1 !important; filter: none !important; }
  .glow { opacity: .55; }
  .sky, .stars, .sheen, .waves, .rays { display: none; }
  .frond { animation: none !important; }
  .lamp::before, .lamp::after { animation: none !important; }
  .sound[aria-pressed="true"] .eq i { transform: scaleY(.62) !important; }
  .eq i:nth-child(2) { transform: scaleY(.9) !important }
  .eq i:nth-child(4) { transform: scaleY(.78) !important }
}

@media print {
  .cover, .sound, .scroll-hint, .progress, .ghost-btn,
  .sky, .stars, .sheen, .waves, .rays, .rsvp2 { display: none !important; }
  .deck { opacity: 1; box-shadow: none; width: 100%; }
  .reveal, .page .lyr { opacity: 1 !important; transform: none !important; filter: none !important; }
  .page::before { display: none; }
}
