/* ============================================================
   Athulya & Isuru — animated wedding invitation
   Flow:  sealed envelope → lift wax seal → flap opens →
          card slides out, envelope leaves → gate-fold card
          → open left → open right → reverse
   ============================================================ */

/* ---------- tokens ---------- */
:root{
  --ink:#171310;
  --ink-2:#241c15;
  --gold:#d8b25c;
  --gold-soft:#e8cf95;
  --paper:#e5ddc9;

  --f-serif:'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --f-sans:'Jost', 'Helvetica Neue', Arial, sans-serif;

  /* set by JS */
  --cardH:640px;
  --cardW:465px;
  --dw:232px;          /* door width  = cardW / 2 */
  --envW:520px;
  --fit:1;
  --tx:0px;
  --ty:0px;
  --cy:0px;
  --rot:0deg;
  --envH:370px;

  --ratio-card:0.7264;  /* card  w / h */
  --ratio-env:1.417;    /* envelope w / h */
  --flap:55.6%;         /* flap depth as % of envelope height */

  --ease:cubic-bezier(.22,.9,.24,1);
  --ease-soft:cubic-bezier(.4,0,.2,1);
  --t-door:1150ms;
}

*,*::before,*::after{box-sizing:border-box}

html,body{height:100%}
body{
  margin:0;
  background:var(--ink);
  color:#e9e0cf;
  font-family:var(--f-serif);
  overflow:hidden;
  -webkit-font-smoothing:antialiased;
  overscroll-behavior:none;
  touch-action:manipulation;
}

/* ============================================================
   CUSTOM CURSOR
   A gold ring that trails the pointer with a little weight, and a
   dot that stays exact.  Only on real pointers; the native cursor
   comes straight back on touch or if the pointer ever leaves.
   ============================================================ */
.cursor{
  position:fixed;left:0;top:0;z-index:300;
  pointer-events:none;
  opacity:0;transition:opacity .3s ease;
}
html.cur-on .cursor{opacity:1}
html.cur-on,html.cur-on *{cursor:none !important}

.cursor__ring,.cursor__dot{
  position:absolute;left:0;top:0;
  border-radius:50%;
  transform:translate(-50%,-50%);
  will-change:width,height,transform;
}
.cursor__ring{
  width:30px;height:30px;
  border:1px solid rgba(236,214,158,.85);
  box-shadow:
    0 0 0 1px rgba(38,25,8,.30),
    0 0 14px rgba(216,178,92,.32),
    inset 0 0 8px rgba(216,178,92,.18);
  transition:width .32s var(--ease), height .32s var(--ease),
             border-color .32s ease, background-color .32s ease, opacity .3s ease;
}
.cursor__dot{
  width:5px;height:5px;
  background:rgba(240,222,172,.95);
  box-shadow:0 0 0 1px rgba(38,25,8,.35), 0 0 8px rgba(216,178,92,.55);
  transition:width .25s var(--ease), height .25s var(--ease), opacity .25s ease;
}

/* over anything you can act on */
.cursor.is-hot .cursor__ring{
  width:52px;height:52px;
  border-color:rgba(246,230,184,.95);
  background:rgba(216,178,92,.10);
}
.cursor.is-hot .cursor__dot{width:3px;height:3px}

/* while the wax seal is being pulled */
.cursor.is-hold .cursor__ring{width:22px;height:22px;background:rgba(216,178,92,.2)}
.cursor.is-hold .cursor__dot{width:7px;height:7px}

@media (prefers-reduced-motion:reduce){
  .cursor__ring,.cursor__dot{transition:none}
}

/* ---------- ambience ---------- */
#dust{position:fixed;inset:0;width:100%;height:100%;z-index:0;pointer-events:none}
.vignette{
  position:fixed;inset:0;z-index:1;pointer-events:none;
  background:
    radial-gradient(120% 90% at 50% 42%, rgba(214,170,96,.14) 0%, rgba(214,170,96,0) 55%),
    radial-gradient(100% 100% at 50% 50%, rgba(0,0,0,0) 38%, rgba(0,0,0,.62) 100%);
}
body::before{
  content:'';position:fixed;inset:0;z-index:0;
  background:
    radial-gradient(80% 60% at 50% 30%, #2a211a 0%, #1b1511 45%, #120e0b 100%);
}

/* ---------- preloader ---------- */
.boot{
  position:fixed;inset:0;z-index:200;display:grid;place-content:center;gap:26px;
  background:#120e0b;transition:opacity .7s ease,visibility .7s;
}
.boot.is-done{opacity:0;visibility:hidden}
.boot__mark{
  width:96px;height:96px;margin:0 auto;
  background:linear-gradient(120deg,#8a6620,#f6e6a8 45%,#a9822c 70%,#f2dfa4);
  -webkit-mask:url('../img/monogram.png') center/contain no-repeat;
          mask:url('../img/monogram.png') center/contain no-repeat;
  animation:breathe 2.6s var(--ease-soft) infinite;
}
@keyframes breathe{0%,100%{opacity:.45;transform:scale(.97)}50%{opacity:1;transform:scale(1.03)}}
.boot__bar{width:180px;height:1px;background:rgba(216,178,92,.2);overflow:hidden}
.boot__bar i{display:block;height:100%;width:0;background:var(--gold);transition:width .3s linear}

/* ---------- scene ---------- */
.scene{
  position:relative;z-index:2;
  height:100svh;height:100dvh;height:var(--appH,100dvh);
  display:grid;place-items:center;
  perspective:2400px;
}

/* ============================================================
   STAGE 1 — ENVELOPE
   Two independent layers sitting either side of the card, so the
   card really does come out from inside: flap behind, body in front.
   Both layers hold an identically-placed .env-frame, so they stay
   glued together until the envelope leaves.
   ============================================================ */
.env-layer{
  position:absolute;inset:0;
  display:grid;place-content:center;
  pointer-events:none;
  transition:opacity .9s var(--ease-soft), transform 1s var(--ease);
  will-change:transform,opacity;
}
/* While it is closed the flap lies ON TOP of the body, as it does on a real
   envelope.  Once it has swung past edge-on it drops behind, so the card can
   be drawn out in front of it.  The swap happens while the flap is standing
   clear above the envelope, so it is invisible. */
/* Perspective is what makes the flap read as tipping over rather than being
   squashed flat — without it rotateX is just a vertical scale. */
.env-layer--flap{z-index:22;perspective:1150px}
.env-layer--body{z-index:20}
.env-layer--cavity{z-index:13}
.scene[data-stage="flying"] .env-layer--flap,
.scene[data-stage="card"] .env-layer--flap{z-index:12}
.env-layer .seal{pointer-events:auto}

.env-frame{
  position:relative;
  width:var(--envW);
  aspect-ratio:var(--ratio-env);
  animation:float 7s ease-in-out infinite;
}
.env-layer--flap .env-frame{transform-style:preserve-3d}
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-9px)}}
/* hold still from the moment it is opened, so the mouth stays put */
.scene:not([data-stage="sealed"]) .env-frame{animation-play-state:paused}

/* The top edge is cut away in a shallow bow — that gap IS the mouth, and the
   card behind it shows through, so you watch it rise out of the opening.
   (clip-path would eat a box-shadow, hence drop-shadow filters instead.) */
.env-back{
  position:absolute;inset:0;
  border-radius:5px;
  background:#c9bdac url('../img/paper-env.jpg') center/cover;
  box-shadow:
    0 2px 1px rgba(255,255,255,.22) inset,
    0 -2px 4px rgba(0,0,0,.16) inset;
  /* Sealed, the top edge is a straight line: the flap is glued down and there
     is nothing to see into.  The mouth only gapes once it is being opened, and
     it does so smoothly because both polygons carry the same nine points.
     (Cutting the notch while still sealed left two little holes at the top
     corners, where the flap's sloping edge no longer covered the gap.) */
  clip-path:polygon(0% 0%, 8% 0%, 20% 0%, 33% 0%, 42% 0%, 50% 0%, 58% 0%, 67% 0%, 80% 0%, 92% 0%, 100% 0%,
                    100% 100%, 0% 100%);
  transition:clip-path 900ms var(--ease-soft) 120ms;
  filter:drop-shadow(0 16px 26px rgba(0,0,0,.72)) drop-shadow(0 4px 9px rgba(0,0,0,.5));
}

/* what you see through the mouth when the card is not filling it */
.env-cavity{
  position:absolute;left:0;right:0;top:0;height:30%;
  background:linear-gradient(180deg,#33261a 0%,#1d1409 45%,#0f0a05 100%);
  opacity:0;transition:opacity .5s .15s ease;pointer-events:none;
}
/* the shadow the mouth throws across whatever is in it — painted under the
   body panel, so it only ever darkens the card showing through the gap */
.env-lip{
  position:absolute;left:0;right:0;top:-8%;height:32%;
  background:linear-gradient(180deg,
    rgba(9,6,3,0) 0%, rgba(9,6,3,.30) 20%, rgba(9,6,3,.62) 33%,
    rgba(9,6,3,.80) 44%, rgba(9,6,3,.42) 66%, rgba(9,6,3,0) 100%);
  opacity:0;transition:opacity .5s .15s ease;pointer-events:none;
}
.scene:not([data-stage="sealed"]) .env-back{
  clip-path:polygon(0% 0%, 8% 1.5%, 20% 5.4%, 33% 9.6%, 42% 11.4%, 50% 12%, 58% 11.4%, 67% 9.6%, 80% 5.4%, 92% 1.5%, 100% 0%,
                    100% 100%, 0% 100%);
}
.scene:not([data-stage="sealed"]) .env-cavity,
.scene:not([data-stage="sealed"]) .env-lip{opacity:1}
.env-folds{position:absolute;inset:0;width:100%;height:100%;border-radius:5px}
.env-flap{
  position:absolute;left:0;right:0;top:0;
  height:var(--flap);
  transform-origin:top center;
  /* no translateZ: with perspective on, any z offset magnifies the flap and it
     ends up a few px wider than the envelope, showing as a step at the corners.
     Layer order already puts it in front. */
  transform:rotateX(0deg);
  transform-style:preserve-3d;
  transition:transform 900ms var(--ease);
  will-change:transform;
}
.env-flap__face{
  position:absolute;inset:0;
  backface-visibility:hidden;-webkit-backface-visibility:hidden;
  background:#c9bdac url('../img/paper-env.jpg') center/cover;
}
.env-flap__face--out{
  clip-path:polygon(0 0,100% 0,50% 100%);
  filter:brightness(1.06);
  box-shadow:0 10px 18px -12px rgba(0,0,0,.6);
}
.env-flap__face--in{
  transform:rotateX(180deg);
  clip-path:polygon(0 100%,100% 100%,50% 0);
  filter:brightness(.94);
}
/* darker towards the fold, catching a little light at the tip */
.env-flap__face--in::after{
  content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,
    rgba(255,250,240,.10) 0%, rgba(38,26,14,.10) 34%,
    rgba(30,20,10,.30) 72%, rgba(26,17,8,.42) 100%);
}
.env-flap__face--out::after{
  content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(255,255,255,.16),rgba(0,0,0,0) 40%,rgba(0,0,0,.10));
}

/* gold monogram on the flap — mirrors the photograph */
.foil--mono{
  position:absolute;left:52%;top:42%;
  width:25%;aspect-ratio:600/603;
  transform:translate(-50%,-50%);
  -webkit-mask-image:url('../img/monogram.png');mask-image:url('../img/monogram.png');
}

/* ---------- wax seal ---------- */
.seal{
  position:absolute;left:50%;top:100%;
  width:19.5%;aspect-ratio:432/404;
  transform:translate(-50%,-50%) translateZ(3px);
  backface-visibility:hidden;-webkit-backface-visibility:hidden;
  padding:0;border:0;background:none;cursor:grab;
  touch-action:none;-webkit-tap-highlight-color:transparent;
  filter:drop-shadow(0 6px 10px rgba(0,0,0,.55));
  animation:sealBob 3.4s ease-in-out infinite;
  transition:filter .3s ease;
  z-index:5;
}
.scene:not([data-stage="sealed"]) .seal{animation:none;pointer-events:none}
.seal img{width:100%;height:100%;display:block;user-select:none;pointer-events:none}
.seal:active{cursor:grabbing}
.seal:focus-visible{outline:2px solid var(--gold);outline-offset:8px;border-radius:50%}
@keyframes sealBob{0%,100%{filter:drop-shadow(0 6px 10px rgba(0,0,0,.55))}
                   50%{filter:drop-shadow(0 10px 16px rgba(0,0,0,.6)) brightness(1.06)}}
.seal.is-dragging{animation:none;transition:none}

.crumbs{position:absolute;left:50%;top:var(--flap);width:0;height:0}
.crumb{
  position:absolute;width:6px;height:6px;border-radius:50% 40% 55% 45%;
  background:linear-gradient(140deg,#c89a4e,#7b5620);
  will-change:transform,opacity;
}

/* ---------- hints ---------- */
.hint{
  margin:0;font-family:var(--f-sans);font-weight:300;
  font-size:clamp(11px,1.1vw,13px);letter-spacing:.34em;text-transform:uppercase;
  color:rgba(232,207,149,.72);
  display:flex;align-items:center;gap:.9em;
  transition:opacity .5s ease;
}
.hint__arrow{
  width:1px;height:20px;background:linear-gradient(180deg,rgba(216,178,92,0),var(--gold));
  animation:up 1.8s ease-in-out infinite;
}
@keyframes up{0%,100%{transform:translateY(4px);opacity:.35}50%{transform:translateY(-4px);opacity:1}}
.hint--seal{
  position:absolute;left:50%;
  top:calc(50% + var(--envH) / 2 + 34px);
  transform:translateX(-50%);
  z-index:22;
}
.hint--card{
  position:absolute;left:50%;transform:translateX(-50%);
  bottom:calc(env(safe-area-inset-bottom,0px) + 78px);
  z-index:40;opacity:0;pointer-events:none;justify-content:center;text-align:center;
}

/* ============================================================
   STAGE 2 — GATE-FOLD CARD
   ============================================================ */
.card-wrap{
  position:absolute;inset:0;z-index:16;
  display:grid;place-content:center;
  pointer-events:none;
  opacity:0;visibility:hidden;
}
.scene[data-stage="flying"] .card-wrap,
.scene[data-stage="card"] .card-wrap{opacity:1;visibility:visible}

/* the slide out of the envelope — and the perspective the doors turn in */
.card-lift{
  perspective:1600px;
  transform:translateY(calc(var(--ty) + var(--cy)));
  transition:transform 700ms var(--ease);
  will-change:transform;
}
/* turning upright, scaling, and re-centring as the doors open */
.card-stage{
  transform-style:preserve-3d;
  transform:translateX(var(--tx)) scale(var(--fit)) rotate(var(--rot));
  transition:transform var(--t-door) var(--ease);
  will-change:transform;
}
.scene[data-stage="card"] .card{pointer-events:auto}

.card{
  position:relative;
  width:var(--cardW);height:var(--cardH);
  transform-style:preserve-3d;
  cursor:pointer;pointer-events:none;
  -webkit-tap-highlight-color:transparent;
}
.card:focus{outline:none}
.card:focus-visible{outline:2px solid var(--gold);outline-offset:14px}

/* --- centre panel: the invitation --- */
.panel--centre{
  position:absolute;inset:0;
  border-radius:3px;
  background:#e4dac1 url('../img/paper.jpg') center/cover;
  box-shadow:
    0 30px 60px -26px rgba(0,0,0,.85),
    0 0 0 1px rgba(90,70,45,.25),
    inset 14px 0 22px -16px rgba(60,44,26,.55),
    inset -14px 0 22px -16px rgba(60,44,26,.55);
  overflow:hidden;
}
.foil--invite{
  position:absolute;inset:3.2% 3.6%;
  -webkit-mask-image:url('../img/invite.png');mask-image:url('../img/invite.png');
}

/* --- the addressee, written in ink on the printed card ---
   The two lines the artwork no longer carries. Everything else on this panel
   is gold foil struck through a mask; this is the one thing a real card has
   in pen, so it stays brown ink and is deliberately not foiled.

   --to-top is the only value that needs tuning: raise or lower it until the
   name sits in the gap retype-invite.py opened up. Do it once, on any guest,
   and all six inherit it.                                                   */
/* --- the addressee block ---------------------------------------------
   A reproduction of what the printed card actually does. The card prints
   "To", then "Mr. / Mrs. / Miss." above a rule, then "You / You Both /
   Your Family", all in gold foil. Whoever fills the card in writes the
   name on the rule in pen and strikes out the options that do not apply.

   So: printed parts in gold, pen marks in ink, struck options ruled
   through. --to-top is the only value that ever needs nudging.        */
.panel--centre{
  --to-top:68.7%;
  --to-size:.0632;        /* the printed line size, measured off invite.png */
  --to-gold:#7b5a14;
  --to-name:#6a4a0d;
  --to-rule:#9c7a2c;
}

.invite__to{
  position:absolute; left:8%; right:8%; top:var(--to-top);
  margin:0; z-index:3; text-align:center; pointer-events:none;
  font-family:var(--f-sans);          /* same face as the printed paragraph */
  letter-spacing:.01em; color:var(--to-gold);
  font-size:calc(var(--dw) * var(--to-size));
}
.to-label{ display:block; font-size:.60em; line-height:1; }

.to-line{
  display:flex; align-items:baseline; justify-content:center;
  margin-top:.1em; line-height:1;
}
/* struck options stay gold and stay readable, just clearly ruled out */
.to-scope s{
  color:var(--to-gold); opacity:.8;
  text-decoration-color:var(--to-rule);
  text-decoration-thickness:.07em;
}

/* The name is set at the same size and face as "Request the honour" above it,
   so it reads as part of the card rather than as something added to it. Only
   the weight, the deeper gold and the rule under it mark it as filled in. */
.to-name{
  font-weight:500;
  font-size:1em; line-height:1.02; white-space:nowrap;
  color:var(--to-name);
  text-shadow:0 .5px 0 rgba(255,249,229,.5), 0 -.5px .6px rgba(58,40,10,.26);
  border-bottom:.04em dotted var(--to-rule);
  padding:0 .3em .02em;
}

.to-scope{
  display:block; margin-top:.1em;
  font-size:.62em; line-height:1; white-space:nowrap;
}

/* --- the two doors --- */
.door{
  position:absolute;top:0;height:100%;width:50%;
  transform-style:preserve-3d;
  transition:transform var(--t-door) var(--ease), visibility 0s;
  will-change:transform;
}
.door--l{left:0;transform-origin:0% 50%;transform:translateZ(3px) rotateY(0deg)}
.door--r{right:0;transform-origin:100% 50%;transform:translateZ(3px) rotateY(0deg)}

.card[data-open="1"] .door--l,
.card[data-open="2"] .door--l{transform:translateZ(3px) rotateY(-178.6deg)}
.card[data-open="2"] .door--r{transform:translateZ(3px) rotateY(178.6deg)}

.door__face{
  position:absolute;inset:0;
  backface-visibility:hidden;-webkit-backface-visibility:hidden;
  overflow:hidden;
  background-size:cover;background-position:center;
  box-shadow:0 24px 46px -24px rgba(0,0,0,.8);
}
.door--l .door__face--front{background-image:url('../img/card-left.jpg');border-radius:3px 0 0 3px}
.door--r .door__face--front{background-image:url('../img/card-right.jpg');border-radius:0 3px 3px 0}
.door__face--back{
  transform:rotateY(180deg);
  background:#e4dac1 url('../img/paper.jpg') center/cover;
  background-position:80% center;
}
.door--l .door__face--back{border-radius:0 3px 3px 0}
.door--r .door__face--back{border-radius:3px 0 0 3px}

/* light turning across the doors */
.shade{
  position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,rgba(0,0,0,.5),rgba(0,0,0,0) 60%);
  opacity:0;transition:opacity var(--t-door) var(--ease);
}
.door--r .shade{background:linear-gradient(270deg,rgba(0,0,0,.5),rgba(0,0,0,0) 60%)}
.door__face--back .shade{opacity:.72}
.card[data-open="1"] .door--l .door__face--front .shade,
.card[data-open="2"] .door--l .door__face--front .shade{opacity:.6}
.card[data-open="2"] .door--r .door__face--front .shade{opacity:.6}
.card[data-open="1"] .door--l .door__face--back .shade,
.card[data-open="2"] .door--l .door__face--back .shade{opacity:0}
.card[data-open="2"] .door--r .door__face--back .shade{opacity:0}

/* ---------- inner faces ---------- */
.inner{
  position:absolute;inset:0;
  display:flex;flex-direction:column;align-items:center;
  padding:calc(var(--dw)*.075) calc(var(--dw)*.07);
  text-align:center;color:#2c2317;
  text-shadow:0 1px 0 rgba(255,253,246,.5);
}
.foil--sprig{
  width:80%;aspect-ratio:660/96;flex:none;
  -webkit-mask-image:url('../img/sprig.png');mask-image:url('../img/sprig.png');
}
.foil--sprig-b{transform:scaleY(-1)}

/* --- left inner : monogram, date, the count, the place ---
   Two hairlines cut the leaf into three, and the slack is shared by two auto
   margins so it never pools into one hole between the promise and the venue. */
.inner--l{justify-content:flex-start;gap:calc(var(--dw)*.046)}
.foil--mono-sm{
  width:40%;aspect-ratio:600/603;flex:none;
  -webkit-mask-image:url('../img/monogram.png');mask-image:url('../img/monogram.png');
}
.rule{
  width:32%;height:1px;flex:none;
  background:linear-gradient(90deg,transparent,rgba(122,90,32,.85),transparent);
}
.rule--wide{width:60%;margin-top:auto}

/* The leaf-wide text-shadow is a paper emboss for ink.  On text whose colour
   is a clipped gradient it paints a near-white ghost under every stroke and
   the gold washes straight out - so the foiled text opts out of it. */
.count__n,.count__l{text-shadow:none}

/* the day count is the only number on this leaf in foil - at that size the
   gold holds against the paper, which it would not at caption size */
.count{
  display:flex;flex-direction:column;align-items:center;
  gap:calc(var(--dw)*.026);margin-top:auto;
}
.count > span{
  display:flex;flex-direction:column;align-items:center;
  gap:calc(var(--dw)*.022);
}
.count__n{
  font-size:calc(var(--dw)*.19);line-height:.95;font-weight:500;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
  /* struck in exactly the foil the monogram and the sprig use, sheen and all */
  background:linear-gradient(112deg,
     #5d4210 0%, #8c6820 9%, #c39a3c 20%, #e9d089 29%,
     #b78c2f 38%, #7d5a18 48%, #bd9333 60%, #edd695 70%,
     #ab7f26 82%, #6d4d13 92%, #967021 100%);
  background-size:260% 260%;
  animation:sheen 11s ease-in-out infinite;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  /* No emboss here.  The monogram is a raised acrylic piece, so the lit-edge
     and drop-shadow belong on it; these numerals are foil printed flat into
     the paper, and the 3-D reading fought the rest of the leaf. */
  filter:saturate(1.06) contrast(1.06);
}
.count__l{
  font-family:var(--f-sans);font-size:calc(var(--dw)*.033);font-weight:500;
  letter-spacing:.4em;text-transform:uppercase;text-indent:.4em;font-style:normal;
  background:linear-gradient(112deg,#4e3608 0%,#7d5a18 28%,#8f6a22 50%,#7d5a18 72%,#563c0b 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
/* on the day itself the count stops rather than running into negatives */
.inner--l.is-here .count > span + span{display:none}
.inner--l.is-here .count__l{display:none}
.inner--l.is-here .count__n{font-size:calc(var(--dw)*.15);line-height:1.1}

.inner__line{
  margin:0;font-size:calc(var(--dw)*.072);line-height:1.5;
  font-style:italic;color:#5b4a30;letter-spacing:.01em;
}

/* The venue used to sit here.  It is already printed across the middle of the
   invitation, so the leaf gives the space to the two of them instead. */
.couple{
  margin:calc(var(--dw)*.022) 0 0;
  width:100%;display:flex;justify-content:center;
}
/* sized by height, not width, so any shape of drawing still clears the sprig */
.couple img{
  display:block;width:auto;height:auto;
  max-height:calc(var(--dw)*.52);max-width:92%;
  /* the emboss the monogram and the sprig carry: a lit edge on top, a dark one
     under it, then the shadow the piece casts on the paper.  Lighter here than
     on the flat foils, because this drawing brings its own shading. */
  filter:
    drop-shadow(0 .5px 0 rgba(255,252,238,.55))
    drop-shadow(0 -.5px .5px rgba(62,42,10,.5))
    drop-shadow(0 calc(var(--dw)*.007) calc(var(--dw)*.014) rgba(44,30,8,.42))
    saturate(1.06) contrast(1.06);
  /* and the same eleven-second breath the foil runs, so it lives with them */
  animation:coupleSheen 11s ease-in-out infinite;
}
@keyframes coupleSheen{
  0%,100%{filter:
    drop-shadow(0 .5px 0 rgba(255,252,238,.55))
    drop-shadow(0 -.5px .5px rgba(62,42,10,.5))
    drop-shadow(0 calc(var(--dw)*.007) calc(var(--dw)*.014) rgba(44,30,8,.42))
    saturate(1.06) contrast(1.06) brightness(1)}
  50%    {filter:
    drop-shadow(0 .5px 0 rgba(255,252,238,.7))
    drop-shadow(0 -.5px .5px rgba(62,42,10,.5))
    drop-shadow(0 calc(var(--dw)*.007) calc(var(--dw)*.014) rgba(44,30,8,.42))
    saturate(1.17) contrast(1.08) brightness(1.05)}
}
/* the leaf is proportionally shorter on a phone, so the drawing gives way first */
@media (max-width:700px){ .couple img{max-height:calc(var(--dw)*.42)} }
@media (prefers-reduced-motion:reduce){ .couple img{animation:none} }

.inner--l .foil--sprig{margin-top:calc(var(--dw)*.02)}

/* --- right inner : story, QR, RSVP --- */
.inner--r{justify-content:flex-start;gap:0}
.inner--r .foil--sprig{width:78%}
.inner__h{
  margin:calc(var(--dw)*.055) 0 0;font-size:calc(var(--dw)*.092);font-weight:600;
  letter-spacing:.015em;color:#241c12;white-space:nowrap;
}
.inner__sub{
  margin:calc(var(--dw)*.075) 0 0;font-size:calc(var(--dw)*.079);line-height:1.4;
  font-weight:500;color:#332818;
}
.qr{
  display:block;width:58%;aspect-ratio:1;flex:none;
  margin:calc(var(--dw)*.055) 0 0;
  background:#fff;padding:calc(var(--dw)*.014);
  box-shadow:0 2px 7px rgba(40,28,12,.22);
  border:0;border-radius:1px;
}
.qr img{width:100%;height:100%;display:block;object-fit:contain}
.inner__bless{
  margin:calc(var(--dw)*.06) 0 0;font-size:calc(var(--dw)*.077);line-height:1.42;
  font-weight:600;color:#241c12;
}
.inner__rsvp{
  margin:calc(var(--dw)*.065) 0 0;font-size:calc(var(--dw)*.094);font-weight:700;
  letter-spacing:.07em;color:#241c12;
}
.inner__tel{
  margin:calc(var(--dw)*.004) 0 0;font-size:calc(var(--dw)*.083);font-weight:600;
  color:#241c12;letter-spacing:.005em;white-space:nowrap;
}
.inner__tel a{color:inherit;text-decoration:none}
.inner__tel span{opacity:.75}
.inner--r .foil--sprig-b{margin-top:auto}

/* ---------- gold foil ---------- */
.foil{
  display:block;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;
  background:linear-gradient(112deg,
     #5d4210 0%, #8c6820 9%, #c39a3c 20%, #e9d089 29%,
     #b78c2f 38%, #7d5a18 48%, #bd9333 60%, #edd695 70%,
     #ab7f26 82%, #6d4d13 92%, #967021 100%);
  background-size:260% 260%;
  animation:sheen 11s ease-in-out infinite;
  filter:
    drop-shadow(0 .5px 0 rgba(255,252,238,.6))
    drop-shadow(0 -.6px .6px rgba(62,42,10,.75))
    drop-shadow(0 1.6px 1.6px rgba(44,30,8,.62))
    saturate(1.06) contrast(1.06);
}
@keyframes sheen{0%,100%{background-position:8% 50%}50%{background-position:92% 50%}}

/* the invitation carries fine text, so its foil runs a deeper, higher-contrast
   antique gold than the decorative monogram / sprig foils */
.foil--invite{
  background:linear-gradient(112deg,
     #3f2b06 0%, #6a4a0f 10%, #96701d 22%, #c39a3c 30%,
     #7c5813 40%, #5a3d09 50%, #936c1c 62%,
     #bd943a 72%, #815c15 84%, #4a330a 94%, #6d4d11 100%);
  background-size:260% 260%;
  filter:
    drop-shadow(0 .5px 0 rgba(255,250,232,.55))
    drop-shadow(0 -.6px .6px rgba(44,29,5,.9))
    drop-shadow(0 1.4px 1.4px rgba(34,22,4,.75));
}

/* ============================================================
   CHROME
   ============================================================ */
.chrome{
  position:absolute;left:50%;transform:translateX(-50%);
  bottom:calc(env(safe-area-inset-bottom,0px) + 20px);
  z-index:40;display:flex;flex-direction:column;align-items:center;gap:14px;
  opacity:0;pointer-events:none;transition:opacity .6s ease;
}
.chrome__row{display:flex;align-items:center;gap:18px}
.nav{
  width:42px;height:42px;display:grid;place-items:center;
  border:1px solid rgba(216,178,92,.28);border-radius:50%;
  background:rgba(28,21,15,.55);color:var(--gold-soft);cursor:pointer;
  transition:background .25s,border-color .25s,transform .25s,opacity .25s;
  backdrop-filter:blur(5px);
}
.nav svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.nav:hover{background:rgba(216,178,92,.16);border-color:rgba(216,178,92,.55)}
.nav:active{transform:scale(.93)}
.nav[disabled]{opacity:.25;pointer-events:none}
.steps{display:flex;gap:9px}
.steps i{
  width:7px;height:7px;border-radius:50%;
  background:rgba(216,178,92,.28);transition:background .35s,transform .35s;
}
.steps i.on{background:var(--gold);transform:scale(1.25)}

.read-pill{
  display:none;align-items:center;gap:.7em;white-space:nowrap;
  padding:1.05em 1.35em;border-radius:100px;cursor:pointer;
  border:1px solid rgba(216,178,92,.42);
  background:rgba(216,178,92,.1);color:#f2e2b8;
  font-family:var(--f-sans);font-size:11.5px;font-weight:400;
  letter-spacing:.22em;text-transform:uppercase;
  backdrop-filter:blur(6px);
  transition:background .25s,border-color .25s,transform .2s;
}
.read-pill svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}
.read-pill:hover{background:rgba(216,178,92,.2);border-color:rgba(216,178,92,.75)}
.read-pill:active{transform:scale(.97)}
.scene[data-open="2"] .read-pill{display:inline-flex;animation:fadeUp .55s .35s both var(--ease)}
@keyframes fadeUp{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}

.extras{
  position:absolute;left:50%;transform:translateX(-50%);white-space:nowrap;max-width:96vw;
  top:calc(env(safe-area-inset-top,0px) + 20px);
  z-index:40;display:flex;gap:14px;align-items:center;
  font-family:var(--f-sans);font-size:11px;letter-spacing:.24em;text-transform:uppercase;
  color:rgba(232,207,149,.5);
  opacity:0;pointer-events:none;transition:opacity .6s ease;
}
.extras a{color:inherit;text-decoration:none;border-bottom:1px solid rgba(216,178,92,.25);padding-bottom:2px}
.extras a:hover{color:var(--gold-soft);border-color:var(--gold)}

.tools{
  position:absolute;z-index:41;transition:opacity .5s ease;
  right:calc(env(safe-area-inset-right,0px) + 16px);
  bottom:calc(env(safe-area-inset-bottom,0px) + 16px);
  display:flex;gap:10px;
}
.tool{
  width:36px;height:36px;display:grid;place-items:center;
  border:1px solid rgba(216,178,92,.2);border-radius:50%;
  background:rgba(28,21,15,.4);color:rgba(232,207,149,.65);cursor:pointer;
  transition:color .25s,border-color .25s,opacity .4s;
}
.tool svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round}
/* 36px is right for a mouse and too small for a thumb, and screen width is a
   poor proxy for which one you have - a tablet is wide AND touched.  Ask the
   pointer instead. */
@media (pointer:coarse){
  .tool{width:44px;height:44px}
  .tool svg{width:19px;height:19px}
  .read-pill{padding:1.15em 1.4em}
}
.tool:hover{color:var(--gold);border-color:rgba(216,178,92,.5)}
.tool .mt{opacity:0}
.tool[aria-pressed="false"] .wv{opacity:0}
.tool[aria-pressed="false"] .mt{opacity:1}
#resetBtn{opacity:0;pointer-events:none;transition:opacity .5s ease}

/* ---------- stage visibility ---------- */
.scene[data-stage="card"] .chrome{opacity:1;pointer-events:auto}
.scene[data-stage="card"] .hint--card{opacity:1}
.scene[data-open="2"] .hint--card{display:none}
.scene[data-stage="card"] .extras{opacity:1;pointer-events:auto}
.scene[data-stage="card"] #resetBtn{opacity:1;pointer-events:auto}
.scene:not([data-stage="sealed"]) .hint--seal{opacity:0}

/* ============================================================
   READING THE INVITATION
   No overlay and no second copy of the artwork: the two open
   sides simply keep turning until they are edge-on and gone,
   and the middle panel scales up into the space they leave.
   ============================================================ */
.dim{
  position:absolute;inset:0;z-index:14;pointer-events:none;
  background:radial-gradient(125% 95% at 50% 45%, rgba(11,8,4,.62), rgba(6,4,2,.96));
  opacity:0;transition:opacity .8s var(--ease-soft);
}
.scene[data-read="1"] .dim{opacity:1;pointer-events:auto}

/* Exactly 270deg is edge-on, so they vanish by geometry — no opacity fade.
   (opacity below 1 forces transform-style:flat, which breaks
   backface-visibility and makes the card FRONT show instead of the inner
   panel.  That was the smeared fern in the middle of the fold.) */
.scene[data-read="1"] .card[data-open="2"] .door--l{transform:translateZ(3px) rotateY(-270deg)}
.scene[data-read="1"] .card[data-open="2"] .door--r{transform:translateZ(3px) rotateY(270deg)}
.scene[data-read="1"] .door{
  visibility:hidden;
  transition:transform var(--t-door) var(--ease), visibility 0s linear var(--t-door);
}
.scene[data-read="1"] .panel--centre{
  box-shadow:
    0 44px 90px -34px rgba(0,0,0,.92),
    0 0 0 1px rgba(120,95,60,.35),
    inset 0 0 60px -30px rgba(60,44,26,.5);
}
.scene[data-read="1"] .card{cursor:default}
/* the small upward nudge the open spread gets on phones is not wanted here */
.scene[data-stage="card"][data-read="1"]{--cy:0px}
.scene[data-read="1"] .chrome,
.scene[data-read="1"] .extras,
.scene[data-read="1"] .hint--card,
.scene[data-read="1"] .tools{opacity:0;pointer-events:none}

.read-close{
  position:absolute;z-index:45;
  top:calc(env(safe-area-inset-top,0px) + 16px);
  right:calc(env(safe-area-inset-right,0px) + 16px);
  width:44px;height:44px;display:grid;place-items:center;
  border:1px solid rgba(216,178,92,.32);border-radius:50%;
  background:rgba(28,21,15,.62);color:var(--gold-soft);cursor:pointer;
  opacity:0;pointer-events:none;transform:scale(.88);
  transition:opacity .4s ease, transform .4s var(--ease), border-color .25s;
  backdrop-filter:blur(5px);
}
.read-close svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round}
.read-close:hover{border-color:rgba(216,178,92,.7)}
.scene[data-read="1"] .read-close{
  opacity:1;pointer-events:auto;transform:none;
  transition-delay:.5s,.5s,0s;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width:560px){
  .chrome{gap:12px}
  .chrome__row{gap:14px}
  .hint{letter-spacing:.22em;font-size:10.5px}
  .extras{gap:10px;font-size:9.5px;letter-spacing:.15em}
  .read-pill{font-size:10.5px;letter-spacing:.16em;padding:1.15em 1.2em}
  .scene[data-stage="card"]{--cy:-4vh}
  .tools{bottom:calc(env(safe-area-inset-bottom,0px) + 10px);right:10px;gap:8px}
  /* 40px, not 32: a finger pad is about 9mm, and these are the only two
     controls that are always on screen. */
  .tool{width:40px;height:40px}
}

/* On the narrowest phones the centred button stack grows wide enough to reach
   the corner buttons.  Moving those up puts them on the card, so instead both
   sides give a little width and they pass each other. */
@media (max-width:365px){
  .chrome__row{gap:12px}
  /* stacked, so 40px of width instead of 88 and the arrows pass them cleanly */
  .tools{right:8px;gap:8px;flex-direction:column}
}

/* ============================================================
   A PHONE HELD SIDEWAYS
   About 320px of height for everything.  layout() already gives the
   card a smaller share; here the furniture around it goes compact so
   the two meet in the middle instead of overlapping.
   ============================================================ */
html.is-short .chrome{
  flex-direction:row;gap:14px;
  bottom:calc(env(safe-area-inset-bottom,0px) + 8px);
}
html.is-short .chrome__row{gap:12px}
html.is-short .hint--card{display:none}
html.is-short .extras{top:calc(env(safe-area-inset-top,0px) + 6px);font-size:9px;gap:8px}
html.is-short .read-pill{font-size:10px;padding:1.25em 1.1em}
html.is-short .tools{
  bottom:calc(env(safe-area-inset-bottom,0px) + 8px);right:8px;gap:6px;
}
html.is-short .tool{width:40px;height:40px}
html.is-short .tool svg{width:18px;height:18px}
html.is-short .read-close{top:calc(env(safe-area-inset-top,0px) + 8px);width:40px;height:40px}
/* set by layout() when the card's lower edge would reach the hint */
html.is-tight .hint--card{display:none}
@media (max-height:520px){
  .hint--card{display:none}
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  :root{--t-door:1ms}
  .env-frame,.seal,.boot__mark,.hint__arrow,.foil{animation:none}
  .scene[data-open="2"] .read-pill{animation:none}
  .env-flap{transition-duration:1ms}
  .card-lift,.card-stage,.env-layer{transition-duration:1ms}
  #dust{display:none}
}
