/* ==================================================================
   Marathi — "Wada" · T-MW
   A SKIN of Lantern Palace (T-LP), taken by way of Ram Sita (T-RS). Same
   renderer, same class names, same asset keys — only the tokens, the type, the
   edge paths, the hero band numbers and the scratch card's geometry change.

   The room modifier classes keep their Lantern Palace names on purpose:
   main.js maps `room--teal → paper-teal` etc., so renaming them would be a
   change to the engine. ONE class is inherited from Goa — `room--palm`, with a
   matching line in main.js's paintPaper map — because this skin has a seventh
   ground (gold, for "before you come") that the six inherited slots could not
   carry.

   WHICH TILE SITS IN WHICH SLOT IS NOT A FREE CHOICE. Each room multiplies its
   paper tile over its own ground at 24% with mix-blend-mode: multiply, so a
   ground that disagrees with its tile in HUE goes muddy. The source of truth is
   `../assets/manifest.json` → `skin.rooms`, which is DERIVED at build time by
   manifest.py: it samples each tile's median hue, compares it against the
   ground below, and refuses to write a manifest where any pair has drifted more
   than 40 degrees. Do not re-derive the mapping from key names, and do not
   "fix" a mismatch here — fix it where it is measured.

   Measured off the tiles on disk, agreeing with skin.rooms:

       paper-teal      = paper-ivory.png      #EBE4D9  ivory
       paper-pink      = paper-zendu.png      #DA7E1C  marigold
       paper-marigold  = paper-green.png      #356045  deep green
       paper-indigo    = paper-saffron.png    #F2BD53  saffron
       paper-cream     = paper-ivory-alt.png  #F3EDE4  ivory, lightened
       paper-palm      = paper-gold.png       #E6A041  gold

   THE NAMING TRAP FIRED HERE. The client delivered a file literally called
   `paper-marigold.png` — and `paper-marigold` is an ENGINE KEY, the one main.js
   hands the ROUTE room, which in this skin is deep GREEN. A key resolves under
   its OWN NAME FIRST and only then its `as:` aliases, so that file would have
   been claimed by the route room and painted orange over green whatever the
   alias said. It is renamed `paper-zendu.png` on disk. Goa lost a round to the
   same rule and Shiv Parvati lost a second one; this is the third, and the
   first that was caught before it shipped.

   So the slots are:

     .room--teal      ivory       invite      The Wada Doorway
     .room--pink      marigold    functions   Four Days in Pune
     .room--marigold  deep green  route       The road to Pune
     .room--indigo    saffron     story       How It Began
     .room--cream     ivory-lit   RSVP        Say You Will Come
     .room--palm      gold        know        Before You Come
     .room--night     night green countdown   The Muhurat, Counted

   THREE LIGHT SATURATED GROUNDS, against Ram Sita's one. Marigold, saffron and
   gold are all LIGHT: ivory ink on them measures 1.7:1, 2.0:1 and 2.0:1 and
   fails outright, and white is barely better. All three take the kumkum-brown
   ink #33170F (8.3:1, 7.0:1, 6.9:1) and all three are OUT of the dark-room
   button selectors. Only .room--marigold (deep green, 8.1:1) and .room--night
   (11.9:1) are dark rooms here.
   ================================================================== */

:root {
  /* the brief's six, verbatim */
  --marigold-o:#F2A93B;   /* marigold orange — the FUNCTIONS ground */
  --saffron:   #E39A2C;   /* the STORY ground */
  --green:     #1F4D33;   /* deep green — the ROUTE ground */
  --gold-leaf: #C9A24A;   /* the KNOW ground, and the METAL for rules and the monogram.
                             NOT called --gold: Lantern Palace already uses that name for the
                             ROUTE room's ground, and declaring it twice in :root silently
                             painted the gold `know` room green — the second declaration wins
                             and nothing warns. Caught by screenshotting the room. */
  --ivory:     #F3EAD3;
  --kumkum:    #B3261E;   /* the red. An ACCENT — 3.3:1 on marigold, so never body ink. */
  /* three derived values the six do not cover */
  --ivory-lit: #FDF7EC;   /* the RSVP ground: ivory, one step up, so invite and RSVP
                             read as two rooms and not one long one */
  --night:     #14301F;   /* deep green after dark — gate, footer, countdown. Green and
                             not a maroon: the countdown room takes a SAFFRON tile at 24%
                             multiply, and this skin's dark is the nauvari's bottle green,
                             not a north-Indian maroon. */
  --green-lo:  #143122;

  /* the rooms — LP slot names, Marathi values, each matched to its paper tile */
  --teal:      var(--ivory);        /* paper-teal tile is ivory */
  --teal-deep: #E4DBC9;
  --pink:      var(--marigold-o);   /* paper-pink tile is marigold */
  --pink-deep: #D9902A;
  /* --gold is LP's name for the ROUTE room's ground, and it is the one token
     whose LP name is actively misleading here: the route room is deep GREEN.
     The metal and the `know` ground live on --gold-leaf above; --zari is the
     same colour again under LP's own accent name. */
  --gold:      var(--green);
  --gold-deep: #173B27;
  --zari:      #C9A24A;             /* the warm metal, where LP had gold */
  --zari-deep: #9C7B31;
  --indigo:    var(--saffron);      /* the story room */
  --indigo-lo: var(--night);
  --cream:     var(--ivory-lit);
  --cream-deep:#F0E7D6;
  --rose:      var(--kumkum);

  /* Ink measured, not assumed. #33170F is a deep kumkum-brown — wet kumkum on
     teak — and it is what the three LIGHT saturated grounds take: 13.8:1 on
     ivory, 8.3:1 on marigold, 7.0:1 on saffron, 6.9:1 on gold, 15.5:1 on
     ivory-lit. Ivory ink is 8.1:1 on the green route room and 11.9:1 on night.
     Ivory on marigold/saffron/gold is 1.7 / 2.0 / 2.0 — it must never be used
     there, which is what the contrast repair below is for. */
  --ink:        #33170F;
  --ink-soft:   #6B4433;
  --on-dark:    #F3EAD3;
  --on-dark-soft:#E0D4B8;

  /* Cormorant Garamond for display. Marcellus for caps and body: a
     Trajan-derived Roman with real stone-carved proportion — the same
     letterform the wada's own stone lintel is cut in. Noto Serif Devanagari is
     used by exactly ONE rule (.mr, the Marathi half of the greeting). */
  --serif:  "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --body:   "Marcellus", Georgia, "Times New Roman", serif;
  --caps:   "Marcellus", Georgia, serif;
  --deva:   "Noto Serif Devanagari", "Nirmala UI", serif;

  --wrap: 1080px;
  --edge-h: clamp(46px, 6vw, 108px);
  --pad-y: clamp(72px, 11vh, 150px);
  --pad-x: clamp(20px, 5vw, 56px);

  /* Gate geometry — the box the two door-leaf plates are drawn into, as a % of
     the gate plate. Both leaves were cut onto ONE shared 678x888 canvas at
     (1073,481) of the 2752x1536 plate, so they take the same rect and cannot
     drift apart, and each canvas edge IS its hinge.
     These are defaults: main.js overrides them from ../assets/manifest.json's
     `door_rig.rect_norm` when it is present, so a repainted gate needs no edit
     here. From rect_norm x0 .3899 x1 .6363 y0 .3132 y1 .8913. */
  --gate-door-left:  38.99%;
  --gate-door-top:   31.32%;
  --gate-door-w:     24.64%;
  --gate-door-h:     57.81%;
  --gate-hinge-l:    0%;
  --gate-hinge-r:    100%;
}

/* The phone gate is a different painting with a different door rect, so it gets
   its own defaults. main.js overwrites all four from assets/manifest.json's
   `door_rig.mobile` once that fetch lands; these keep the first painted frame
   correct in the meantime. From rect_norm x0 .2201 x1 .7793 y0 .4775 y1 .9320.
   y0 is .4775 and NOT the top of the leaves: a second marigold garland is strung
   across the doors on this plate, so the rect starts BELOW it and the garland
   (and the sliver of teak behind it) stay on the frame plate — see doors.py. */
@media (max-width: 759px) {
  :root {
    --gate-door-left: 22.01%;
    --gate-door-top:  47.75%;
    --gate-door-w:    55.92%;
    --gate-door-h:    45.45%;
  }
}

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

html { scroll-behavior: auto; }

body {
  margin: 0;
  background: var(--indigo-lo);
  color: var(--ink);
  font-family: var(--body);
  font-size: clamp(16px, 1.05vw + 12px, 19px);
  /* Marcellus is a Roman with a modest x-height and long extenders, so it takes
     a touch more leading than Josefin Sans did */
  line-height: 1.72;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }

a { color: inherit; }

.skip {
  position: fixed; top: -100px; left: 16px; z-index: 90;
  background: var(--cream); color: var(--ink);
  padding: 10px 18px; border-radius: 999px; text-decoration: none;
  font-family: var(--caps); font-size: 14px;
}
.skip:focus { top: 16px; }

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

/* T-GS: T-TM's bilingual block (.ta / .en / .bi and the two Tamil faces) is
   gone. This skin is English with ONE Marathi line — the second half of the
   household greeting. It gets its own face and its own line, and nothing else
   on the page loads Devanagari. Marcellus has no Devanagari coverage at all, so
   without this rule the line renders in the browser's fallback and the letter
   height jumps mid-paragraph. */
.mr {
  display: block;
  font-family: var(--deva);
  font-size: .82em;
  letter-spacing: 0;             /* Devanagari must never be tracked out */
  line-height: 1.9;              /* the shirorekha needs the room */
  text-transform: none;          /* .gate-to is uppercase; this would mangle it */
  margin-top: .35em;
  opacity: .9;
}

/* ------------------------------------------------------------------
   type
   ------------------------------------------------------------------ */
.eyebrow {
  font-family: var(--caps);
  font-size: clamp(11px, 1.1vw, 14px);
  letter-spacing: .26em;
  text-transform: uppercase;
  margin: 0 0 .9em;
  opacity: .78;
}
.room-title {
  font-family: var(--caps);
  font-weight: 400;
  font-size: clamp(30px, 5.4vw, 62px);
  letter-spacing: .1em;
  text-transform: uppercase;
  line-height: 1.12;
  margin: 0 0 .35em;
}
.room-sub {
  font-size: clamp(16px, 1.7vw, 21px);
  font-style: italic;
  opacity: .82;
  margin: 0 auto;
  max-width: 46ch;
}
.center { text-align: center; }
.wrap {
  position: relative; z-index: 3;
  width: min(var(--wrap), 100% - var(--pad-x) * 2);
  margin-inline: auto;
}
.wrap--narrow { width: min(720px, 100% - var(--pad-x) * 2); }
.room-head { margin-bottom: clamp(36px, 6vw, 72px); }

.rule {
  width: 130px; height: 1px; margin: clamp(22px,3vw,34px) auto;
  background: currentColor; opacity: .32;
}

/* ------------------------------------------------------------------
   buttons
   ------------------------------------------------------------------ */
.btn {
  display: inline-block;
  font-family: var(--caps);
  font-size: clamp(12px, 1.2vw, 15px);
  letter-spacing: .18em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 14px 30px;
  border-radius: 999px;
  border: 1.5px solid currentColor;
  background: var(--ink);
  color: var(--cream);
  border-color: var(--ink);
  cursor: pointer;
  transition: transform .25s ease, background .25s ease, color .25s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn--ghost { background: transparent; color: inherit; border-color: currentColor; }
.cta-row {
  display: flex; flex-wrap: wrap; gap: 14px;
  justify-content: center; align-items: center;
  margin-top: clamp(26px, 4vw, 42px);
}

.mobcta {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%) translateY(140%);
  z-index: 60;
  background: var(--rose); color: var(--cream);
  font-family: var(--caps); letter-spacing: .2em; text-transform: uppercase;
  font-size: 13px; padding: 14px 40px; border-radius: 999px; text-decoration: none;
  box-shadow: 0 10px 30px rgba(30,14,20,.35);
  transition: transform .5s cubic-bezier(.2,.8,.2,1);
}
.mobcta.is-in { transform: translateX(-50%) translateY(0); }
.mobcta.is-hid { transform: translateX(-50%) translateY(140%); } /* the real buttons are on screen — main.js */
@media (min-width: 760px) { .mobcta { display: none; } }

/* ==================================================================
   1 + 2. THE JOURNEY — sky, push-through, doors
   ================================================================== */
.journey {
  position: relative;
  height: 340vh;               /* JS shortens this when motion is reduced */
  background: #3A2430;   /* pre-dawn behind the mandapam */
}
.stage {
  position: sticky; top: 0;
  height: 100vh; height: 100svh;
  overflow: hidden;
  perspective: 1500px;
  transform-style: preserve-3d;
}

.sky, .courtyard {
  position: absolute; inset: -6% -6% -6% -6%;
  background-size: cover; background-position: center bottom;
  will-change: opacity, transform;
}
.sky--dusk  { background-color: #E8A46A; }   /* sunset over the Arabian Sea */
.sky--night { background-color: #0E2E36; opacity: 0; }
/* the painted night stars are large; holding the plate under full opacity keeps
   the dusk warmth showing through and stops them reading as confetti */
.courtyard  { opacity: 0; transform: scale(1.25); background-position: center; }

.lyr {
  position: absolute; left: 50%; bottom: -2%;
  width: 128%; transform: translateX(-50%);
  transform-origin: 50% 100%;
  will-change: transform;
}
.lyr img { width: 100%; }

/* the cloud plane: the slowest thing in the hero, slower than palace-back */
.lyr--cloud {
  bottom: auto; top: 0; height: 74%;
  width: 118%;
  transform-origin: 50% 60%;
}
/* T-GS: half T-TM's size and two thirds its opacity, and both pushed off the
   centre line. The Goan cloud plate is a single big cumulus rather than a thin
   wisp, so at 30% width it sat directly behind the names as a pale blob that
   read as fog on the lens — it is the thing that looked like a broken scrim in
   the first pass, not the scrim. */
.cloud { position: absolute; width: auto; will-change: transform; }
.cloud--a { top: 3%;  left: -11%; width: 20%; opacity: .22; }
.cloud--b { top: 15%; right: -9%; left: auto; width: 15%; opacity: .18; }
/* These four numbers are the whole desktop hero, and they are not free.
   `.lyr` sets a percentage WIDTH and bottom-aligns; `.lyr img` is width:100%.
   So a band's drawn height is (lyr width in px) x band_over_width, and the
   plate's own HEIGHT never enters it (assets/manifest.json → hero_layers).
   palace-mid measures 0.425 here against tamil-mandapam's 0.287 — its palms run
   most of the plate — so at T-TM's `bottom: -3%` it stood 636px tall on a
   900px viewport and buried palace-back completely. Pushed down to -16% and
   with back lifted to 14%, the sea horizon and the church land in the gap
   between mid's compound wall and its palm canopy. Both stay past 100% so their
   own left and right cut edges fall off-screen. Composited at 1440x900. */
/* T-MW: both bands arrived as REAL RGBA with a feathered top and painted
   ground running to the plate's own bottom edge, so neither needed the haze
   ramp or the bottom crop Shiv Parvati's chroma-keyed bands did. Measured,
   palace-mid is 0.563 of its plate width and palace-back is 0.283.
   `.lyr--mid` is 124% and NOT Ram Sita's 104%, and the reason is a hard edge
   rather than a proportion: palace-mid's two teak PILLARS run to the plate's
   own top row, so its top edge is opaque where they are and its own cut showed
   as a ruled line across the upper left of the hero at 104%. At 124% both
   pillars fall off-screen (their inner edges are ~7.5% in from each side) and
   the band's remaining top edge is fully transparent, so there is no line to
   see. Pushed to -18% to keep the marigold rail low; back lifts to 16% so the
   Sahyadri valley and its fort clear the rail. Composited at 1440x900 with
   hero.py, then checked on the real page. */
.lyr--back { bottom: 16%; width: 106%; opacity: .9; }
.lyr--mid  { bottom: -18%; width: 124%; }

/* the gate sits nearest the camera and is what you fly through. It is
   deliberately bottom-heavy: the names live in the open sky above it. */
.gate {
  position: absolute; left: 50%; bottom: -4%;
  /* 50%, not Goa's 46%: palace-mid's teak pillars and marigold rail are big
     near-camera masses here, and at 46% the doorway was smaller than the band
     flanking it, which inverts the depth.
     82vh and not Goa's 70vh, and this was caught by shooting the real page
     rather than by the composite: at 1440x900 `min(70vh, 50%)` resolves to the
     70vh arm (630px) and NOT to 50% (720px), so the live gate came out 90px
     smaller than the frame hero.py had signed off — hero.py models the
     percentage arm only. 82vh moves the clamp onto the 50% arm at this
     viewport, which is what was actually composited. */
  width: min(82vh, 50%);
  aspect-ratio: 2752 / 1536;
  transform: translateX(-50%);
  /* The camera flies into the DOORWAY, not the middle of the plate, so the
     opening stays put on screen while everything else expands past the edges.
     This is the centre of the door rect measured by doors.py and published in
     assets/manifest.json — 51.3% / 60.2%. The doors are RECTANGULAR here, under
     a flat carved stone lintel, and they stop at y1 = 0.8913 because they stand
     on the stone stair. Scale is 9.3, NOT Ram Sita's 13.4: the doorway is 24.6%
     of this plate's width against 17.1% there, so the camera has LESS far to
     travel before it is through and copying the bigger number overshoots.
     main.js reads the number from the manifest. */
  transform-origin: var(--gate-origin, 51.3% 60.2%);
  will-change: transform, opacity;
}
.gate-plate { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; object-position: bottom; }
.gate-plate--open, .gate-plate--frame { opacity: 0; }

/* Lantern Palace's gate plate was keyed — transparent outside the gateway
   silhouette — so it sat on the sky with no edge. Both Tamil plates are OPAQUE
   and full-bleed (manifest: alpha false), so at the small end of the fly-through
   they read as a painted rectangle pasted onto the sky. Feather the edges: the
   mandapam genuinely continues past frame, so a soft fall-off reads as aerial
   distance where a hard cut reads as a mistake. The mask scales with the plate,
   so by the time the camera is through the arch the feather is off-screen.
   T-RS keeps Goa's DEEP top fade: this plate's own sky is painted the same
   peach sunset as sky-dusk, so the step is gentler than Goa's orange-on-teal
   was, but the plate is a hard rectangle either way. 22% desktop / 18% phone. */
.gate-plate {
  /* Every plate, not just --shut: main.js shows `.gate-plate--frame` whenever
     both door leaves exist (which they do), and although that plate has the
     arch punched transparent, its OUTER boundary is still the opaque full
     rectangle — so the cut shows on whichever plate is on screen.
     Both axes. The top edge could stay crisp while the gate sat against open
     sky, but palace-mid is a full-width band now, so the gate is an opaque
     painting laid over another opaque painting of the same building — a hard
     cut there reads as a panel pasted into the facade. Feathered on all three
     free edges it reads as depth instead. Bottom stays hard; it is the ground. */
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0, #000 11%, #000 89%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 22%, #000 100%);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(90deg, transparent 0, #000 11%, #000 89%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 22%, #000 100%);
          mask-composite: intersect;
}
@media (max-width: 759px) {
  .gate-plate {
    -webkit-mask-image:
      linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%),
      linear-gradient(180deg, transparent 0, #000 18%, #000 100%);
    -webkit-mask-composite: source-in;
            mask-image:
      linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%),
      linear-gradient(180deg, transparent 0, #000 18%, #000 100%);
            mask-composite: intersect;
  }
}

.arch-view, .doors {
  position: absolute;
  top: var(--gate-door-top);
  left: var(--gate-door-left);
  width: var(--gate-door-w);
  height: var(--gate-door-h);
  transform-style: preserve-3d;
  /* the .gate ancestor is scaled, which cuts off .stage's perspective — so the
     doors carry their own. Keep it generous or the swing reads as a shear. */
  perspective: 1100px;
}
.door {
  position: absolute; inset: 0;      /* both leaves share the box; each paints its half */
  backface-visibility: hidden;
  will-change: transform;
}
.arch-view {
  background-size: cover;
  background-position: center bottom;
  background-color: #2B1218;
  opacity: 0;
}
.door img { width: 100%; height: 100%; object-fit: fill; }
.door--l { transform-origin: var(--gate-hinge-l) center; }
.door--r { transform-origin: var(--gate-hinge-r) center; }

.drift {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 4;
}

/* hero type */
.hero {
  position: absolute; inset: 0; z-index: 6;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  /* sits above the skyline, not on it */
  padding: 0 var(--pad-x) clamp(140px, 42vh, 420px);
  color: var(--on-dark);
  text-shadow: 0 2px 26px rgba(27,13,18,.55), 0 1px 3px rgba(27,13,18,.4);
  will-change: opacity, transform;
}
.hero-plate {
  position: relative;
  padding: clamp(26px, 5vh, 60px) clamp(24px, 5vw, 80px);
}
/* T-GS: an ELLIPSE, not `closest-side`. The inherited circle was sized off the
   box's short side, so on a 1440-wide hero it read as a distinct grey disc
   floating in a bright sunset sky — very visible, where over T-TM's dark
   mandapam it was invisible. Widened to the shape of the type block, dropped
   in peak alpha, and tinted to the skin's own NIGHT GREEN. Ram Sita's tint was
   a cold blue-teal, which over that skin's dark plates was invisible and over
   this one's warm dusk valley read as a grey haze with a visible edge — caught
   by looking at the shipped hero, not by any number. Same hue family as the
   sky it sits on, so it darkens without tinting. */
.hero-plate::before {
  content: ""; position: absolute; inset: -10% -6%; z-index: -1;
  background: radial-gradient(ellipse 66% 72% at 50% 46%,
    rgba(20,48,31,.34), rgba(20,48,31,.16) 52%, rgba(20,48,31,0) 84%);
}

.hero-eyebrow {
  text-shadow: 0 1px 14px rgba(10,40,48,.6);
  font-family: var(--caps);
  font-size: clamp(10px, 1.1vw, 13px);
  letter-spacing: .4em; text-transform: uppercase;
  margin: 0 0 clamp(14px, 2.4vw, 26px); opacity: .85;
}
.hero-names {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(46px, 10.5vw, 132px);
  line-height: .96;
  letter-spacing: .01em;
  margin: 0;
  /* carries most of the legibility now that the scrim behind is light — a
     tight, dark halo reads as evening air rather than as a panel */
  text-shadow: 0 2px 26px rgba(10,40,48,.55), 0 1px 3px rgba(10,40,48,.35);
  display: flex; flex-direction: column; align-items: center; gap: .04em;
}
.hero-weds {
  font-family: var(--caps);
  font-style: normal;
  font-size: clamp(13px, 1.9vw, 22px);
  letter-spacing: .5em; text-transform: lowercase;
  opacity: .82;
  padding-left: .5em;
}
.hero-date {
  font-family: var(--caps);
  font-size: clamp(13px, 1.6vw, 19px);
  letter-spacing: .22em; text-transform: uppercase;
  margin: clamp(20px, 3vw, 34px) 0 6px;
}
.hero-venue { margin: 0; font-style: italic; opacity: .86; font-size: clamp(15px,1.5vw,19px); }
/* "Sri Meenakshi Kalyana Mandapam · Mylapore, Chennai" is 50 characters and
   wraps to two lines at 390px. The eyebrow directly above already carries
   மயிலாப்பூர் · CHENNAI, so the phone drops the duplicate rather than shrinking
   the type to fit. */
@media (max-width: 759px) { .hero-city { display: none; } }
.hero-hint {
  position: absolute; bottom: clamp(24px, 5vh, 54px); left: 0; right: 0;
  margin: 0;
  font-family: var(--caps);
  font-size: clamp(10px, 1vw, 12px);
  letter-spacing: .3em; text-transform: uppercase;
  opacity: .95;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
}
/* the hint lands over the gate, so it carries its own scrim */
.hint-pill {
  background: rgba(27,13,18,.42);
  border: 1px solid rgba(247,242,230,.22);
  border-radius: 999px;
  padding: 9px 20px;
  backdrop-filter: blur(3px);
}
/* ------------------------------------------------------------------
   9:16 — the phone hero
   ------------------------------------------------------------------
   `.lyr` sizes these bands by WIDTH and bottom-aligns them, so the only ratio
   that matters is band-height over plate-WIDTH; plate height never enters it.
   At 9:16 the same "bottom 40% of the frame" is 1.8x taller relative to width
   than at 16:9, which is how the inherited `.lyr--mid { width: 160% }` ended up
   filling the whole phone viewport with facade and burying the dawn sky.
   The 9:16 bands have since been repainted low (palace-mid-9x16 band/width went
   1.79 → 0.374), and these two pairs are the asset side's A/B at 390x844
   (manifest.json → hero_layers.recommended_css_mobile).
   ------------------------------------------------------------------ */
@media (max-width: 759px) {
  /* palace-mid-9x16's band_over_width is 1.053 — the band is TALLER than the
     plate is wide — so on a 390px phone it draws 460px of palms and shack. It
     is left at the bottom and taken past the gate's own width instead: at 112%
     its palms flank the villa on both sides, which is the only place they can
     be seen at all. palace-back is lifted to 40% so the church and the sea
     horizon clear the gate's gable. Composited at 390x844. */
  /* palace-mid-9x16 measures 1.033 — TALLER THAN ITS OWN PLATE IS WIDE — so at
     Goa's `bottom: 0` it stood about 400px on an 844px screen and buried the
     back band and the gate together. Dropped to -20% and taken to 134% so its
     pillars and marigold rail flank the doorway at the bottom corners instead
     of stacking above it; back lifted to 30% so the Sahyadri valley clears the
     gate's arch. Dropped again from -20% to -30% after shooting the real page:
     at -20% the band's own floor left an orange block with a hard top edge in
     each bottom corner beside the gate, and only the rail should be showing
     there. Composited at 390x844, then checked live. */
  .lyr--back { width: 130%; bottom: 30%; opacity: .85; }
  .lyr--mid  { width: 134%; bottom: -30%; }

  /* The phone gate is a SEPARATE PORTRAIT PAINTING (palace-gate-9x16, 941x1672),
     not a crop of the 16:9 one — a 9:16 cover-crop of the wide doorway keeps the
     doors and loses both samai lamps, both carved niches, the marigold toran
     and the whole stone stair, i.e. every Marathi recogniser except the door,
     at the exact moment the gate is the whole screen.
     Because the plate is portrait, the BOX has to be portrait too: the door
     rect is a percentage of this box, and a 16:9 box would letterbox the
     portrait plate inside it and slide the doors off the opening.
     86vw: the doorway here is 55.9% of its own plate width — by far the widest
     door any skin has cut — so the gate has to be large on screen or the leaves
     are all you can see of it. At 86vw the mid band's pillars still read at both
     bottom corners. */
  .gate {
    width: 86vw;
    aspect-ratio: 1536 / 2752;
    /* centre of door_rig.mobile's rect, not the desktop rect. main.js overwrites
       --gate-origin from the manifest once it loads; this is the default. */
    --gate-origin: 50.0% 70.5%;
  }
}

.hint-arrow {
  width: 1px; height: 34px; background: currentColor;
  transform-origin: top;
  animation: hint 2.1s ease-in-out infinite;
}
@keyframes hint {
  0%, 100% { transform: scaleY(.35); opacity: .35; }
  50%      { transform: scaleY(1);   opacity: .95; }
}

/* ==================================================================
   ROOMS — painted paper, curved edges between them
   ================================================================== */
.room {
  position: relative;
  padding: calc(var(--pad-y) + var(--edge-h) * .55) 0 var(--pad-y);
  isolation: isolate;
}
.room::before {                       /* the paper grain */
  content: ""; position: absolute; inset: 0; z-index: 0;
  background-image: var(--paper-tex, none);
  background-size: 300px 300px;
  opacity: .24;
  mix-blend-mode: multiply;
  pointer-events: none;
}
.room > .wrap, .room > .drive { position: relative; z-index: 3; }

/* the curved wipe: the room's own paper bulges up over the section above it */
.edge {
  position: absolute; top: calc(-1 * var(--edge-h) + 1px); left: 0;
  width: 100%; height: var(--edge-h);
  z-index: 2; display: block;
  fill: var(--edge-fill, currentColor);
}

/* The four rooms of the BRIEF, plus RSVP and the countdown. Each ground is the
   colour of the paper tile main.js will multiply over it — see the map at the
   top of this file. Swapping a ground without swapping the tile makes mud. */
/* THE CONTRAST SPLIT, measured. FIVE of the seven rooms are LIGHT grounds and
   take the kumkum-brown ink #33170F: ivory 13.8:1, marigold 8.3:1, saffron
   7.0:1, gold 6.9:1, ivory-lit 15.5:1. TWO are dark and take ivory: the deep
   green route room 8.1:1 and the night countdown 11.9:1.
   The three SATURATED light grounds are the trap — ivory on marigold is 1.7:1,
   on saffron 2.0:1 and on gold 2.0:1, and white is only 2.0 / 2.4 / 2.4. Ram
   Sita hit this once (its saffron `know` room); this skin hits it three times,
   so all three are kept out of the dark-room selectors below. */
.room--teal     { background: var(--ivory);      color: var(--ink);     --edge-fill: var(--ivory); }      /* The Wada Doorway */
.room--pink     { background: var(--marigold-o); color: var(--ink);     --edge-fill: var(--marigold-o); } /* Four Days in Pune */
.room--marigold { background: var(--green);      color: var(--on-dark); --edge-fill: var(--green); }      /* The road to Pune */
.room--indigo   { background: var(--saffron);    color: var(--ink);     --edge-fill: var(--saffron); }    /* How It Began */
.room--cream    { background: var(--ivory-lit);  color: var(--ink);     --edge-fill: var(--ivory-lit); }
/* the one class inherited from Goa's engine edit. main.js has
   `room--palm → paper-palm` beside it; without that line the ground paints but
   the paper grain does not. Here it is GOLD #C9A24A — a LIGHT ground, like Ram
   Sita's saffron and unlike Goa's palm green, so it takes the ink and stays out
   of both dark-room selectors below. */
.room--palm     { background: var(--gold-leaf);  color: var(--ink);     --edge-fill: var(--gold-leaf); }  /* Before You Come */
.room--night    { background: var(--indigo-lo);  color: var(--on-dark); --edge-fill: var(--indigo-lo); }

/* TWO of the seven rooms are dark grounds, so they take light buttons — the
   deep green route room and the night countdown. `room--pink`, `room--indigo`
   and `room--palm` are deliberately NOT in this list; see the split above. */
.room--night .btn, .room--marigold .btn {
  background: var(--ivory); color: var(--ink); border-color: var(--ivory);
}
.room--night .btn--ghost, .room--marigold .btn--ghost {
  background: transparent; color: var(--on-dark); border-color: rgba(243,234,211,.6);
}
/* hairline rules: ivory on the green, the gold metal on the light rooms */
.room--marigold .rule { background: var(--ivory); opacity: .55; }
.room--teal .rule, .room--indigo .rule { background: var(--zari); opacity: .7; }
/* kumkum is too light to be body ink (3.3:1 on marigold) but it is exactly
   right as the ampersand's accent on the two palest rooms */
.room--indigo .amp, .room--teal .amp { color: var(--kumkum); }

/* petals drift BEHIND the copy inside a paper room — in the sky stage they are
   in front of the palace, but type is never something you read through. */
.room > .drift { z-index: 1; }

/* painted stars for the night rooms */
.stars {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background-repeat: repeat;
  background-size: 520px 520px;
  opacity: .85;
  animation: twinkle 7s ease-in-out infinite;
}
@keyframes twinkle {
  0%, 100% { opacity: .58; }
  50%      { opacity: .95; }
}

/* ------------------------------------------------------------------
   3. INVITE
   ------------------------------------------------------------------ */
.greet {
  font-style: italic; font-size: clamp(17px, 2vw, 23px);
  margin: 0 0 clamp(28px, 4vw, 46px); opacity: .9;
}
.hosts { margin: 0 0 clamp(30px, 4.5vw, 54px); display: grid; gap: 18px; }
.host-l { font-family: var(--caps); font-size: clamp(17px, 2.2vw, 25px); letter-spacing: .05em; margin: 0; }
.host-s { margin: 4px 0 0; font-style: italic; font-size: clamp(14px,1.4vw,16px); opacity: .72; }
.lede { font-style: italic; font-size: clamp(16px,1.7vw,20px); opacity: .85; margin: 0 0 .5em; }
.couple {
  font-weight: 300;
  font-size: clamp(48px, 11vw, 122px);
  line-height: 1; margin: .1em 0 0;
  display: flex; flex-direction: column; align-items: center;
}
.couple .amp {
  font-family: var(--caps); font-style: normal;
  font-size: clamp(20px, 3vw, 36px); opacity: .7; margin: .12em 0;
}
.couple-date {
  font-family: var(--caps); font-size: clamp(20px, 3.4vw, 40px);
  letter-spacing: .22em; margin: 0;
}
.couple-meta { margin: 10px 0 0; font-style: italic; opacity: .82; }
/* T-GP addition: the tagline under the names. */
.tagline {
  margin: clamp(12px,1.8vw,20px) 0 0;
  font-family: var(--caps); letter-spacing: .16em; text-transform: uppercase;
  font-size: clamp(11px,1.2vw,14px); opacity: .72;
}

/* ------------------------------------------------------------------
   4. FUNCTIONS
   ------------------------------------------------------------------ */
.funcs { display: grid; gap: clamp(28px, 4.5vw, 56px); }
.func {
  display: grid;
  grid-template-columns: minmax(0, 270px) minmax(0, 1fr);
  gap: clamp(20px, 3.5vw, 48px);
  align-items: center;
  background: rgba(255,252,246,.92);
  border: 1px solid rgba(74,44,6,.22);
  border-radius: clamp(30px, 5vw, 64px);
  box-shadow: 0 18px 40px rgba(74,44,6,.12);
  padding: clamp(24px, 3.2vw, 42px) clamp(26px, 3.6vw, 48px);
  will-change: transform, opacity;
}
.func:nth-child(even) { direction: rtl; }
.func:nth-child(even) > * { direction: ltr; }
.func-art { margin: 0; }
/* the pieces behind these cards are not one family — a kalash, a nath, a
   dhol-tasha pair, a puja thali, a pearl mundavalya, a pheta — so cap the
   height and let each one sit inside the box at its own aspect */
.func-art img { width: 100%; max-height: clamp(180px, 26vh, 300px); object-fit: contain; }
/* T-GP addition: a real photograph, when the record has one (see
   renderEvents() in main.js) — a photo needs cover + a fixed box, not the
   illustrated piece's contain-and-shrink. */
.func-art img.func-photo {
  /* Capped like the illustrated piece it stands in for. `max-height: none`
     let a photograph fill the whole column at 4:3, so a card that had been a
     300px lantern became a 450px photograph and the page went from a set of
     cards to a stack of posters — the "giant image" a couple sees the moment
     they upload one. The shape stays 4:3; only the size is disciplined, and
     `cover` means a portrait photo from a phone crops rather than stretches. */
  max-height: clamp(200px, 30vh, 340px); aspect-ratio: 4 / 3; object-fit: cover;
  width: 100%; margin-inline: auto; border-radius: 22px;
}
.func-title {
  font-family: var(--caps); font-weight: 400;
  font-size: clamp(26px, 4vw, 44px); letter-spacing: .06em;
  margin: 0 0 .18em;
}
.func-meta { font-family: var(--caps); letter-spacing: .12em; text-transform: uppercase;
  font-size: clamp(11px,1.2vw,13px); margin: 0 0 .55em; opacity: .8; }
.func-window { margin: 0 0 .5em; font-style: italic; opacity: .9; }
.func-venue { margin: 0 0 .35em; }
.func-venue a { text-decoration-thickness: 1px; text-underline-offset: 3px; }
.func-body { margin: 0 0 .5em; }
.func-dress, .func-avoid { margin: 0; font-size: .92em; opacity: .78; }
.func-acts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
.mini {
  font-family: var(--caps); font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  padding: 9px 18px; border-radius: 999px;
  border: 1px solid currentColor; background: none; color: inherit;
  cursor: pointer; text-decoration: none; opacity: .85;
}
.mini:hover { opacity: 1; }

@media (max-width: 720px) {
  .func { grid-template-columns: 1fr; text-align: center; border-radius: 34px; }
  .func:nth-child(even) { direction: ltr; }
  .func-art { max-width: 190px; margin-inline: auto; }
  .func-acts { justify-content: center; }
}

/* ------------------------------------------------------------------
   5. ROUTE — the drive
   ------------------------------------------------------------------ */
/* The ScrollTrigger runs `start: top top` to `end: bottom bottom`, so the whole
   crossing happens in (height - 100vh) of scroll — 20vh at T-TM's 120vh, which
   is 180px on a 900px screen. The boat crossed in about a fifth of one flick of
   the wheel and every screenshot stop missed it. 168vh gives it ~610px, which
   is roughly one scroll gesture. It cannot leave a blank field the way T-TM
   warned: `end: bottom bottom` keeps the stage pinned for the whole range. */
.drive { height: 168vh; margin-bottom: clamp(30px, 6vw, 80px); }
.drive-stage {
  position: sticky; top: 0;
  height: 100vh; height: 100svh;
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
/* The band is the RED-EARTH ROAD out of the Deccan, and it is the one bright
   mass in the room. Goa's problem was a teal band on a teal room; here the room
   ground is deep green, so the band goes the other way — the murrum road up to
   the fort, lit by the same dusk the plates are painted in. It is what makes
   the section read as a road at all. Warmer and redder than Ram Sita's marble
   ghat: this is laterite, not stone. */
.drive-band {
  position: absolute; left: 0; right: 0; bottom: 0; height: 34%;
  background: linear-gradient(#E8B96B, #B06A2C);
}
/* the lip along the top of the path, so the band carries something even at
   scroll positions between deer poses */
.drive-band::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 5px;
  background: linear-gradient(rgba(255,246,222,.85), rgba(255,246,222,0));
  opacity: .9;
}
/* Goa broke this into short faint strokes because a steady dash on water reads
   as tarmac. This IS a road, so the rhythm comes back — but as CART RUTS rather
   than as a painted centre line: two long green hairlines wide-set in the dust,
   which is what a bullock cart actually leaves. */
.drive-band::after {
  content: ""; position: absolute; left: 0; right: 0; top: 14%; height: 2px;
  background: repeating-linear-gradient(90deg,
    rgba(31,77,51,.24) 0 74px, rgba(31,77,51,0) 74px 116px);
  box-shadow: 0 22px 0 rgba(31,77,51,.16);
}
/* the car and (optionally) the couple sitting on it travel as one rig */
.drive-rig {
  position: absolute;
  /* the cart RIDES on the road — its wheels sit low inside the band rather than
     on the lip of it. The band is 34% of the stage and this plate draws about
     235px on a 900px one; at anything above ~12% the road's top edge cuts
     across the bullocks' shoulders and the whole rig reads as half-buried.
     Checked at both viewports. */
  bottom: 7%;
  left: 0;
  width: clamp(280px, 44vw, 680px);
  will-change: transform;
  z-index: 2;
}
/* CONTENT SWAP: the `car` key holds the BULLOCK CART — two garlanded bullocks,
   a cart, and the couple up on the boards. The most Maharashtrian thing on the
   vehicle sheet and the only one of the four that reads as a procession. `couple.onCar` stays NULL because this
   plate ALREADY CONTAINS the pair — they are up on the boards behind the
   bullocks — so a second couple bolted on top would be two of them.
   Bound by HEIGHT, like Goa's and unlike Lantern Palace's car. 26vh, a shade
   over Ram Sita's 22vh: that plate was one small fawn and this one is a full
   cart-and-pair that has to stay legible as two bullocks, a cart and two
   people. object-position is BOTTOM, so the wheels land on the road. */
.drive-car {
  width: auto; max-width: 100%;
  height: clamp(140px, 26vh, 330px);
  object-fit: contain; object-position: bottom center;
  filter: drop-shadow(0 14px 18px rgba(20,48,31,.34));
}
.drive-couple {
  position: absolute; left: 0; bottom: 34%;
  width: 100%;
}
/* pinned to the top of the stage rather than centred: the car crosses the
   middle band, and a centred heading sits right under its wheels */
.drive-head { position: absolute; top: 11%; left: 0; right: 0; z-index: 3; margin: 0; padding: 0 var(--pad-x); }

/* ------------------------------------------------------------------
   5b. venue + travel
   ------------------------------------------------------------------ */
.venue-card {
  background: rgba(247,242,230,.10);
  border: 1px solid rgba(201,162,74,.55);
  border-radius: 999px / 140px;
  padding: clamp(34px,5vw,64px) clamp(24px,4vw,56px);
  margin-bottom: clamp(34px, 5vw, 66px);
}
.venue-name { font-family: var(--caps); font-size: clamp(24px,3.6vw,42px); margin: 0 0 .2em; letter-spacing: .04em; }
.venue-city { font-family: var(--caps); letter-spacing: .28em; font-size: clamp(11px,1.2vw,13px); margin: 0; opacity: .75; }

.rolls { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: clamp(18px,2.6vw,32px); }
.roll {
  background: rgba(247,242,230,.09);
  border: 1px solid rgba(201,162,74,.42);
  border-radius: 26px; padding: clamp(20px,2.6vw,30px);
  will-change: transform, opacity;
}
.roll-h { font-family: var(--caps); letter-spacing: .14em; text-transform: uppercase;
  font-size: clamp(12px,1.3vw,14px); margin: 0 0 .6em; }
.roll-b { margin: 0; font-size: .95em; }

.hotels { display: grid; gap: 16px; margin-top: clamp(22px,3vw,36px); }
.hotel {
  background: rgba(247,242,230,.09);
  border: 1px solid rgba(201,162,74,.42);
  border-radius: 26px; padding: clamp(20px,2.6vw,30px);
  display: grid; grid-template-columns: minmax(0,1.2fr) minmax(0,1fr); gap: 10px 26px;
  will-change: transform, opacity;
}
.hotel-n { font-family: var(--caps); font-size: clamp(17px,2vw,22px); margin: 0 0 .25em; letter-spacing: .04em; }
.hotel-m, .hotel-p { margin: 0; font-size: .93em; opacity: .85; }
.hotel-acts { display: flex; flex-wrap: wrap; gap: 10px; align-self: end; }
@media (max-width: 640px) { .hotel { grid-template-columns: 1fr; } }

.baraat {
  margin-top: clamp(34px,5vw,60px);
  display: grid; grid-template-columns: minmax(0, 240px) minmax(0,1fr);
  gap: clamp(18px,3vw,40px); align-items: center;
}
.baraat-art { width: 100%; }
.baraat-line { margin: 0; font-size: clamp(16px,1.7vw,20px); }
@media (max-width: 640px) { .baraat { grid-template-columns: 1fr; text-align: center; }
  .baraat-art { max-width: 200px; margin-inline: auto; } }

/* ------------------------------------------------------------------
   6. STORY
   ------------------------------------------------------------------ */
.beats { display: grid; gap: clamp(40px, 7vw, 96px); }
.beat {
  display: grid; grid-template-columns: minmax(0, 0.95fr) minmax(0, 1fr);
  gap: clamp(22px, 4vw, 54px); align-items: center;
  will-change: transform, opacity;
}
.beat:nth-child(even) .beat-fig { order: 2; }
.beat-fig {
  margin: 0; position: relative;
  border-radius: 50% 50% 14px 14px / 34% 34% 14px 14px;
  overflow: hidden;
  border: 1px solid rgba(247,242,230,.28);
  background: rgba(247,242,230,.08);
  aspect-ratio: 4 / 5;
  max-width: 380px;
  width: 100%;
  justify-self: center;
}
.beat-fig img { width: 100%; height: 100%; object-fit: cover; }
.beat-fig .miss {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: var(--caps); letter-spacing: .2em; text-transform: uppercase;
  font-size: 12px; opacity: .6;
}
.beat-no { font-family: var(--caps); font-size: clamp(32px,5vw,58px); opacity: .35; margin: 0; line-height: 1; }
.beat-kicker { font-family: var(--caps); letter-spacing: .2em; text-transform: uppercase;
  font-size: clamp(11px,1.2vw,13px); margin: .8em 0 .5em; opacity: .8; }
.beat-title { font-weight: 300; font-size: clamp(24px,3.4vw,40px); line-height: 1.16; margin: 0 0 .4em; }
.beat-body { margin: 0; opacity: .86; }
@media (max-width: 720px) {
  .beat { grid-template-columns: 1fr; }
  .beat:nth-child(even) .beat-fig { order: 0; }
  .beat-fig { aspect-ratio: 3 / 2; border-radius: 44% 44% 12px 12px / 22% 22% 12px 12px; }
}

/* ------------------------------------------------------------------
   6b. GALLERY — T-GP addition. A mosaic of the record's own photographs
   (C.gallery), portrait-friendly by default (a landscape shot still reads
   fine at 3:4, cropped by its own `focal`). See renderGallery() in main.js.
   ------------------------------------------------------------------ */
.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: clamp(10px, 1.6vw, 18px); }
.gal-shot {
  position: relative; margin: 0; overflow: hidden;
  border-radius: 22px; aspect-ratio: 3 / 4;
  border: 1px solid rgba(38,56,58,.18);
  background: rgba(255,253,249,.4);
  will-change: transform, opacity;
}
.gal-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.gal-cap {
  position: absolute; left: 0; right: 0; bottom: 0; margin: 0;
  padding: .9em .9em .6em; font-size: .78em; color: var(--on-dark);
  background: linear-gradient(to top, rgba(42,15,26,.78), transparent);
}

/* ------------------------------------------------------------------
   6c. THE SCRATCH CARD — haldi paste over the client's painted card.

   T-MW REBUILDS THIS ROOM. Every skin before it painted the whole card in
   code and let the canvas fill it. Here the card is a real plate —
   media/scratch-card.webp, the chibi couple holding an ornate heart frame —
   and the only rubbable surface is the flat matte-gold HEART inside it.

   `python3 ../_system/goldpanel.py assets/scratch-card.png` measures that
   heart at bbox_norm [0.2895, 0.3589, 0.7081, 0.6715] of a square plate, i.e.
   9.86 % of the frame. The four numbers on .scr-heart below are that bbox and
   NOTHING ELSE reads it: the muhurat type and the canvas are both children of
   .scr-heart, so they track the art at every width and a repainted card is a
   re-measure plus a four-line edit. The same numbers are published in
   ../assets/manifest.json under `scratch_card.reveal_bbox_norm`.

       left  = x0                        = 28.95%
       top   = y0                        = 35.89%
       width = x1 - x0 = .7081 - .2895   = 41.86%
       height= y1 - y0 = .6715 - .3589   = 31.26%

   THE CARD IS BIG ON PURPOSE. At Ram Sita's 460px max-width the heart is only
   193x144 CSS px and a fingertip covers a third of it. 620px puts it at
   260x194, and on a phone the card is allowed to bleed past the narrow wrap.

   Palette: room--palm (gold), for the reason in index.html.
   ------------------------------------------------------------------ */
.scratch-card {
  position: relative; margin: 0 auto; width: 100%; max-width: 620px;
  /* the plate is square (1254x1254) and .scr-heart is positioned in percent of
     it, so the box has to keep the plate's ratio or the heart slides */
  aspect-ratio: 1 / 1;
  container-type: inline-size;
}
.scr-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }

/* the reveal area — goldpanel.py's bbox, and the only place it appears */
.scr-heart {
  position: absolute;
  left: 28.95%; top: 35.89%; width: 41.86%; height: 31.26%;
}
.scratch-card .under {
  position: absolute; inset: 0; display: grid; align-content: center;
  gap: .2rem; padding: 0 6%; color: #3B1F04; text-align: center;
  /* the revealed type sits on the card's OWN flat gold, not on a room ground,
     so it takes its own ink rather than --ink: measured off the plate, the
     heart is about #C9A44B and #3B1F04 is 8.0:1 on it. */
}
/* type inside the heart is sized in cqw — a percentage of the CARD, not of the
   viewport — so it stays inside the heart at every width. The heart is 41.86%
   of the card, so 1cqw here is about 2.4% of the heart's own width. */
.scr-lbl {
  font-family: var(--caps); font-size: 1.6cqw; letter-spacing: .26em;
  text-transform: uppercase; margin: 0; opacity: .92;
}
.scr-date {
  font-family: var(--serif); font-size: 5.4cqw; line-height: 1;
  margin: .18em 0 0; white-space: nowrap;
}
.scr-date i { font-style: normal; font-size: .42em; margin: 0 .16em; opacity: .75; }
.scr-sub { margin: .28em 0 0; font-size: 2cqw; line-height: 1.3; }
/* the venue moved OUT of the heart — there is no room for a fourth line in a
   260x194 box, and it truncated at both viewports on the first pass */
.scr-place {
  margin: 1rem auto 0; font-size: .95rem; opacity: .8; text-align: center;
  max-width: 34ch;
}
.scr-clip { position: absolute; width: 0; height: 0; }
#scratchFoil {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  touch-action: none; cursor: grab; transition: opacity .8s ease;
  /* the measured bbox is a RECTANGLE and the gold inside it is a HEART, so the
     canvas is clipped to the heart or it paints a gold rectangle over the
     couple's frame. objectBoundingBox units, so it scales with the element. */
  clip-path: url(#heartClip);
}
#scratchFoil.is-gone { opacity: 0; pointer-events: none; }
.scr-prompt {
  position: absolute; left: 0; right: 0; bottom: 4%; pointer-events: none;
  font-family: var(--caps); font-size: .6rem; letter-spacing: .26em;
  text-transform: uppercase; color: var(--ink); text-align: center;
  transition: opacity .4s ease;
}
.scr-prompt .shell { color: var(--kumkum); }
.scratch-card.is-scratched .scr-prompt { opacity: 0; }
.scr-reveal { margin-top: 1.1rem; }
.scr-reveal.is-gone { display: none; }
@media (max-width: 759px) {
  /* let the card out of .wrap--narrow: the heart is 41.86% of it, and on a
     360px wrap that is 150px across — about three fingertips. */
  .scratch-card { width: 112%; margin-left: -6%; }
}
@media (prefers-reduced-motion: reduce) {
  #scratchFoil, .scr-prompt { transition: none; }
}

/* ------------------------------------------------------------------
   7. RSVP
   ------------------------------------------------------------------ */
.ring-art { width: clamp(96px, 13vw, 168px); margin: 0 auto clamp(10px,2vw,22px); }
.form { display: grid; gap: clamp(18px, 2.6vw, 28px); }
.grp-h {
  display: block; font-family: var(--caps); letter-spacing: .2em; text-transform: uppercase;
  font-size: clamp(11px,1.2vw,13px); margin-bottom: 14px; opacity: .8;
}
.field { display: grid; gap: 7px; }
.field label { font-size: .88em; opacity: .8; }
input, select, textarea {
  font: inherit; font-size: 1em;
  color: var(--ink);
  background: rgba(255,255,255,.72);
  border: 1px solid rgba(34,32,58,.24);
  border-radius: 14px;
  padding: 13px 15px;
  width: 100%;
}
textarea { resize: vertical; }
.toggles { display: flex; flex-wrap: wrap; gap: 10px; }
.toggle { position: relative; }
.toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
/* direct child only: the labels carry bilingual <span class="ta">/<span class="en">
   runs inside them, and a descendant selector drew a pill around each one */
.toggle > span {
  display: inline-block;
  font-family: var(--caps); font-size: clamp(12px,1.2vw,14px);
  letter-spacing: .1em;
  padding: 11px 22px; border-radius: 999px;
  border: 1px solid rgba(34,32,58,.3);
  cursor: pointer; transition: background .2s, color .2s, border-color .2s;
}
.toggle input:checked + span { background: var(--green); color: var(--on-dark); border-color: var(--green); }
.toggle input:focus-visible + span { outline: 3px solid var(--zari); outline-offset: 2px; }
.guests { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; }
.guest {
  border: 1px solid rgba(34,32,58,.16);
  border-radius: 22px; padding: clamp(14px,1.8vw,20px);
  background: rgba(255,255,255,.34);
}
.guest-h { font-family: var(--caps); letter-spacing: .16em; text-transform: uppercase;
  font-size: 11px; margin: 0 0 12px; opacity: .75; }
.guest-row { display: grid; grid-template-columns: minmax(0,1.3fr) minmax(0,1fr); gap: 14px; }
@media (max-width: 560px) { .guest-row { grid-template-columns: 1fr; } }
.rsvp-note { font-size: .88em; opacity: .72; margin: 0; }
.rsvp-err { color: #8E1F19; opacity: 1; }
.rsvp-submit { justify-self: start; }
.rsvp-done { padding: clamp(30px,5vw,60px) 0; }
.rsvp-done-h { font-family: var(--caps); font-size: clamp(24px,3.4vw,38px); letter-spacing: .12em;
  text-transform: uppercase; margin: 0 0 .4em; }

/* ------------------------------------------------------------------
   8. THINGS TO KNOW
   ------------------------------------------------------------------ */
.knows { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: clamp(18px,2.6vw,30px); }
.know {
  border: 1px solid rgba(247,242,230,.3);
  border-radius: 28px; padding: clamp(22px,2.8vw,32px);
  background: rgba(247,242,230,.08);
  will-change: transform, opacity;
}
/* Every room this skin puts a .know card in is LIGHT — Ram Sita's saffron and
   this skin's gold, marigold and ivory — so `room--palm` joins the ink-edged
   list rather than keeping the pale card the countdown wants. The border is the
   kumkum-brown ink at low alpha, not Goa's slate. */
.room--pink .know, .room--teal .know, .room--cream .know,
.room--indigo .know, .room--palm .know {
  border-color: rgba(51,23,15,.26); background: rgba(255,253,249,.52);
}
.know-h { font-family: var(--caps); letter-spacing: .16em; text-transform: uppercase;
  font-size: clamp(12px,1.3vw,14px); margin: 0 0 .6em; }
.know-b { margin: 0; font-size: .95em; opacity: .9; }
/* T-GP addition: the wishes wall reuses this same card (see renderWishes()
   in main.js) — "How you know them", inline after the guest's name. */
.wish-rel { font-style: italic; text-transform: none; letter-spacing: 0; opacity: .75; font-size: .92em; }
.wish-empty { text-align: center; opacity: .78; font-style: italic; margin: 0 0 clamp(24px,3.5vw,40px); }

/* Reused by #evening (the timeline) and #gifts (shagun + registry) — same
   card, new content, so nothing else needed to add those two rooms. */
.know code { font-size: .9em; opacity: .85; }
.gift-qr { display: block; width: 140px; height: 140px; margin: 0 auto .8em; border-radius: 12px; }
.copy {
  font: inherit; font-size: .85em; letter-spacing: .04em; text-transform: uppercase;
  border: 1px solid currentColor; border-radius: 999px; padding: .2em .8em;
  background: transparent; color: inherit; cursor: pointer;
}

/* ------------------------------------------------------------------
   8b. COUNTDOWN
   ------------------------------------------------------------------ */
.count {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(8px, 1.6vw, 20px);
  margin: clamp(26px,4vw,46px) 0 clamp(20px,3vw,30px);
}
.count-cell {
  border: 1px solid rgba(247,242,230,.28);
  border-radius: 20px; padding: clamp(12px,2vw,22px) 6px;
  background: rgba(247,242,230,.06);
}
.count-n { font-family: var(--caps); font-size: clamp(26px, 5.6vw, 54px); line-height: 1; }
.count-l { font-family: var(--caps); letter-spacing: .2em; text-transform: uppercase;
  font-size: clamp(9px,1vw,11px); opacity: .7; margin-top: 8px; }

/* ------------------------------------------------------------------
   footer
   ------------------------------------------------------------------ */
.site-footer {
  position: relative;
  color: var(--on-dark);
  background: var(--indigo-lo);
  padding: clamp(90px, 16vh, 200px) 0 clamp(50px, 8vh, 90px);
  overflow: hidden;
}
.footer-sky {
  position: absolute; inset: 0;
  background-size: cover; background-position: center bottom;
  opacity: .85;
}
/* The Goan night plate has a lit villa and its reflection sitting exactly where
   the hashtag and the footer line go, and white italic over lamplight is
   unreadable. One scrim, centre-weighted so the palms at the edges stay
   visible. */
.footer-sky::after {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 74% 56% at 50% 46%, rgba(27,13,18,.74) 0%, rgba(27,13,18,.5) 60%, rgba(27,13,18,.12) 100%),
    linear-gradient(180deg, rgba(27,13,18,.5) 0%, rgba(27,13,18,.12) 38%, rgba(27,13,18,.55) 100%);
}
.site-footer .wrap { position: relative; z-index: 2; }
.hashtag {
  font-family: var(--caps); font-size: clamp(22px,4vw,46px);
  letter-spacing: .16em; margin: 0 0 .3em;
}
.footer-line { font-style: italic; opacity: .82; margin: 0 0 clamp(30px,5vw,54px); }
.fams { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px,1fr)); gap: clamp(18px,3vw,40px); }
.fam-s { font-family: var(--caps); letter-spacing: .2em; text-transform: uppercase;
  font-size: 11px; opacity: .68; margin: 0 0 .5em; }
.fam-n { font-family: var(--caps); font-size: clamp(16px,1.9vw,21px); margin: 0 0 .3em; }
.fam-l { margin: 0 0 .5em; font-style: italic; font-size: .9em; opacity: .72; }
.fam-t { margin: 0; }
.footer-fine { margin: clamp(34px,5vw,60px) 0 0; font-size: .84em; opacity: .6; }

/* ------------------------------------------------------------------
   no-JS twin
   ------------------------------------------------------------------ */
.fallback { display: none; background: var(--cream); color: var(--ink); padding: 40px 24px; }
.fallback h1 { font-weight: 300; font-size: 44px; margin: 0 0 10px; }
.fallback h2 { font-family: var(--caps); font-size: 20px; letter-spacing: .1em; text-transform: uppercase; }

/* ------------------------------------------------------------------
   reduced motion — a composed still, not a broken scroller
   ------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .journey { height: auto; }
  .stage { position: relative; height: 92vh; }
  /* the courtyard only makes sense as the thing you arrive at — with no
     journey to travel, it would just black out the dusk sky. */
  .courtyard { display: none; }
  .gate { width: min(58vh, 44%); }
  .drive { height: auto; margin-bottom: 0; }
  .drive-stage { position: relative; height: 56vh; }
  .drive-car { transform: translateX(0); left: 8%; }
  .hint-arrow { animation: none; opacity: .6; }
  .stars { animation: none; opacity: .8; }
  .drift { display: none; }
  .mobcta { transition: none; }
  * { scroll-behavior: auto !important; }
}


/* ==================================================================
   0. THE OPEN MOMENT — the envelope
   ==================================================================
   Geometry is the manifest's `envelope_rig`, in canvas fractions:
     canvas 1200x1681 · pocket V at .229/.437/.224 of canvas height
     flap art in the top .350, hinge center top, opens rotateX(-170deg)
     card 0.80 of canvas width, closed top .30, travels up ~.70 to withdraw
   Everything below is those numbers and nothing else, so a re-cut rig only
   needs these constants changed.
   ================================================================== */
:root {
  /* unitless percentages of CANVAS height, so they can be composed in calc().
     A plain `top: calc(75% + 30%)` on the card resolves both against the clip
     box, not the canvas, which put the card below the clip and made it
     invisible — hence the explicit re-basing in .env-card below. */
  --env-clip-up: 75;        /* headroom above the canvas for the card to rise into */
  --env-card-top: 30;       /* manifest envelope_rig.card.closed_top */
  --env-card-travel: -87%;  /* of the CARD's own height, = .70 canvas height */
}

.env {
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center;
  background: radial-gradient(circle at 50% 44%, #FBF3E4, #E7D6B8 70%, #D8C29C);
  cursor: pointer;
  perspective: 1800px;
  transition: opacity .7s ease, transform .7s cubic-bezier(.4,0,.2,1);
}
body.env-out .env { opacity: 0; transform: translateY(6vh) scale(1.06); pointer-events: none; }
body.env-gone .env { display: none; }
/* the page does not scroll until the envelope is open */
body.pre-open { overflow: hidden; height: 100%; }

.env-stage {
  position: relative;
  width: min(430px, 74vw);
  aspect-ratio: 1200 / 1681;
  transform-style: preserve-3d;
}

/* every plate is the full canvas, so one rule places all of them */
.env-l {
  position: absolute; inset: 0;
  background-size: 100% 100%; background-repeat: no-repeat;
}
/* fallback tint if a plate is missing — a plain cream envelope, never a blank */
.env-l--back  { z-index: 1; background-color: #EFE0C2; border-radius: 8px; }
.env-l--front { z-index: 3; }

.env-cardclip {
  position: absolute; left: 0; right: 0;
  top: calc(-1% * var(--env-clip-up)); bottom: 0;
  overflow: hidden;
  z-index: 2;
}
.env-card {
  position: absolute; left: 10%; width: 80%;
  aspect-ratio: 1200 / 1694;
  /* the clip box is (100 + clip-up)% of canvas height, so a canvas-relative
     position has to be re-based onto it */
  top: calc((var(--env-clip-up) + var(--env-card-top)) / (100 + var(--env-clip-up)) * 100%);
  background: var(--cream);
  transition: transform .95s cubic-bezier(.3,0,.2,1) .2s;
}
body.is-opening .env-card { transform: translateY(var(--env-card-travel)); }
.env-card-art { border-radius: 4px; }
.env-card-txt {
  /* clear of the card's printed paisley border */
  position: absolute; left: 20%; right: 20%; top: 26%;
  text-align: center; color: var(--ink);
}
.env-note {
  font-family: var(--caps); letter-spacing: .26em; text-transform: uppercase;
  font-size: clamp(8px, 1.5vw, 12px); margin: 0 0 .8em; opacity: .7;
}
.env-names {
  font-size: clamp(19px, 4.4vw, 34px); font-weight: 300; margin: 0;
  display: flex; align-items: center; justify-content: center; gap: .28em;
  flex-wrap: wrap;
}
.env-names em { font-family: var(--caps); font-style: normal; font-size: .55em; opacity: .65; }
.env-date {
  font-family: var(--caps); letter-spacing: .18em;
  font-size: clamp(10px, 1.9vw, 14px); margin: .8em 0 0; opacity: .8;
}

/* the greeting is printed on the pocket panel, below the V */
.env-to {
  position: absolute; left: 8%; right: 8%; top: 74%; z-index: 4;
  margin: 0; text-align: center; font-style: italic;
  font-size: clamp(13px, 2.6vw, 19px);
  color: var(--ink); opacity: .85;
}

.env-flap {
  position: absolute; inset: 0; z-index: 5;
  transform-origin: center top;
  transform-style: preserve-3d;
  transition: transform .9s cubic-bezier(.5,0,.2,1);
}
/* the seal is composited into the flap plate, so it lifts with the flap —
   no separate seal element to keep in sync */
body.is-opening .env-flap { transform: rotateX(-170deg); }

.env-cta {
  position: absolute; bottom: clamp(24px, 7vh, 70px); left: 0; right: 0;
  margin: 0; text-align: center;
  font-family: var(--caps); letter-spacing: .3em; text-transform: uppercase;
  font-size: clamp(10px, 1.2vw, 13px); color: var(--ink); opacity: .62;
  animation: hint 2.4s ease-in-out infinite;
}

/* ==================================================================
   0. THE OPEN MOMENT — the gate (replaces the envelope, 2026-09-02)
   ==================================================================
   Full-bleed painted night palace, dimmed to indigo, gold monogram, two
   hairlines, the greeting, one outlined button. Lanterns drift behind the
   type. Reuses .env's fixed/transition/open-state rules above.
   ================================================================== */
:root { --gate-gold: #E9A93E; }   /* SAFFRON, not the zari gold: the gate plate is a
                                     Sahyadri fort at dusk and the brass reads muddy on
                                     it, while the bhagwa saffron is already the one
                                     colour on that hillside. */

.env.opengate {
  background: var(--indigo-lo);
  display: grid; place-items: center;
  overflow: hidden;
  cursor: pointer;
}
.gate-bg {
  position: absolute; inset: -4%;
  background: url("media/palace-night.jpg") center 62% / cover no-repeat;
  animation: gate-drift 26s ease-in-out infinite alternate;
  will-change: transform;
}
@media (max-width: 760px) {
  .gate-bg { background-image: url("media/palace-night-9x16.jpg"); background-position: center 58%; }
  /* the palace sits right behind the type on a phone — hold it darker there */
  .gate-shade {
    background:
      radial-gradient(ellipse 70% 46% at 50% 50%, rgba(27,13,18,.62) 0%, rgba(27,13,18,.7) 55%, rgba(27,13,18,.86) 100%),
      linear-gradient(180deg, rgba(27,13,18,.55) 0%, rgba(27,13,18,.25) 40%, rgba(27,13,18,.72) 100%);
  }
}
@keyframes gate-drift {
  from { transform: scale(1) translateY(0); }
  to   { transform: scale(1.07) translateY(-1.5%); }
}
.gate-shade {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse at 50% 42%, rgba(27,13,18,.18) 0%, rgba(27,13,18,.62) 60%, rgba(27,13,18,.86) 100%),
    linear-gradient(180deg, rgba(27,13,18,.55) 0%, rgba(27,13,18,.18) 40%, rgba(27,13,18,.7) 100%);
}

.gate-lanterns { position: absolute; inset: 0; pointer-events: none; }
.gl {
  position: absolute; top: -20vh; width: clamp(34px, 4.2vw, 64px);
  opacity: 0; filter: drop-shadow(0 0 14px rgba(217,178,94,.5));
  animation: gl-rise linear infinite;
}
.gl-1 { left: 8%;  animation-duration: 21s; animation-delay: -3s;  }
.gl-2 { left: 22%; animation-duration: 26s; animation-delay: -11s; width: clamp(26px, 3vw, 46px); }
.gl-3 { left: 36%; animation-duration: 23s; animation-delay: -17s; }
.gl-4 { left: 64%; animation-duration: 25s; animation-delay: -7s;  width: clamp(28px, 3.4vw, 50px); }
.gl-5 { left: 78%; animation-duration: 20s; animation-delay: -14s; }
.gl-6 { left: 90%; animation-duration: 28s; animation-delay: -1s;  width: clamp(24px, 2.8vw, 42px); }
/* THIS ONE RISES, and it is the only layer on the page that does. The six gate
   sprites are two zendu heads, their mirrors and two samai lamps drifting up
   the night plate — sparks off a fort, not weather. The two in-page drift
   layers still FALL (main.js `dir: 1`); a marigold that floats upward over a
   room reads as a bug, but one rising past a lit fort at night reads as an
   ember. */
@keyframes gl-rise {
  0%   { transform: translate3d(0, 24vh, 0) rotate(-2deg); opacity: 0; }
  8%   { opacity: .9; }
  50%  { transform: translate3d(1.6vw, -34vh, 0) rotate(2deg); }
  92%  { opacity: .8; }
  100% { transform: translate3d(-1vw, -96vh, 0) rotate(-1deg); opacity: 0; }
}

.gate-body {
  position: relative; z-index: 2;
  display: flex; flex-direction: column; align-items: center;
  text-align: center; color: var(--on-dark);
  padding: 0 var(--pad-x);
  max-width: 34rem;
  animation: gate-in 1.4s cubic-bezier(.2,.7,.2,1) both;
}
@keyframes gate-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* The monogram over the gate: the two initials cut in SAFFRON, the way a wada's
   own name is cut into its stone lintel. T-TM set ஸ்ரீ here in the Tamil face;
   this skin has no equivalent opening word, so the initials carry the slot. */
.gate-sri {
  margin: 0 0 .55rem;
  font-family: var(--serif);
  font-size: clamp(34px, 4.6vw, 58px);
  font-weight: 300;
  line-height: 1;
  color: var(--on-dark);
  letter-spacing: .04em;
  text-shadow: 0 0 30px rgba(243,234,211,.28);
}
.gate-sri em { font-style: italic; font-size: .62em; opacity: .74; padding: 0 .12em; }
.gate-mono {
  margin: 0 0 .9rem;
  font-family: var(--caps); letter-spacing: .32em; color: var(--gate-gold);
  font-size: clamp(15px, 1.7vw, 20px);
  display: flex; align-items: center; gap: .55em;
}
.gate-mono i { font-style: normal; font-size: .5em; opacity: .85; transform: translateY(-.05em); }
.gate-rule {
  display: block; width: 3.4rem; height: 1px;
  background: linear-gradient(90deg, transparent, var(--gate-gold), transparent);
  margin: .2rem 0 1.3rem; opacity: .85;
}
.gate-to {
  margin: 0 0 1.6rem; font-family: var(--serif); font-style: italic;
  font-size: clamp(16px, 2vw, 21px); color: var(--on-dark-soft);
}
.gate-note {
  margin: 0 0 .5rem; font-family: var(--caps); letter-spacing: .3em; text-transform: uppercase;
  font-size: clamp(9px, 1vw, 11px); color: var(--gate-gold); opacity: .9;
}
.gate-names {
  margin: 0; font-family: var(--serif); font-weight: 300;
  /* 1.14, not T-TM's 1.02. Cormorant's descenders are long and the two names
     stack, so at 1.02 the y of a descender ran into the date line under it on a
     390px screen. The shadow is for the same reason the hero has one: the lit
     bastion of the night fort sits directly behind these two words. */
  line-height: 1.14;
  font-size: clamp(40px, 7.2vw, 92px); letter-spacing: .01em;
  text-shadow: 0 2px 24px rgba(6,20,26,.7), 0 1px 4px rgba(6,20,26,.5);
  display: flex; flex-direction: column; align-items: center;
}
.gate-names em {
  font-style: italic; font-weight: 300; color: var(--gate-gold);
  font-size: .42em; line-height: 1.2; margin: .1em 0;
}
.gate-date {
  margin: 1.5rem 0 0; font-family: var(--caps); letter-spacing: .34em;
  font-size: clamp(11px, 1.3vw, 15px); color: var(--on-dark);
}
.gate-date + .gate-rule { margin-top: 1.4rem; }
.gate-cta { margin: 0; }
.gate-cta span {
  display: inline-block; padding: .95em 2.4em;
  border: 1px solid var(--gate-gold); border-radius: 999px;
  font-family: var(--caps); letter-spacing: .28em; text-transform: uppercase;
  font-size: clamp(10px, 1.05vw, 12px); color: var(--gate-gold);
  background: rgba(27,13,18,.25); backdrop-filter: blur(2px);
  transition: background .35s ease, color .35s ease, transform .35s ease;
  animation: gate-pulse 3.2s ease-in-out infinite;
}
.env.opengate:hover .gate-cta span, .env.opengate:focus-visible .gate-cta span {
  background: var(--gate-gold); color: var(--indigo-lo); transform: translateY(-1px);
}
@keyframes gate-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(217,178,94,.0); }
  50%      { box-shadow: 0 0 0 9px rgba(217,178,94,.12); }
}
.gate-place {
  margin: 2rem 0 0; font-family: var(--caps); letter-spacing: .36em;
  font-size: clamp(9px, .95vw, 11px); color: var(--on-dark-soft); opacity: .7;
}
.env.opengate:focus-visible { outline: none; }

@media (prefers-reduced-motion: reduce) {
  .gate-bg, .gl, .gate-body, .gate-cta span { animation: none; }
  .gl { opacity: .8; top: 20%; }
}

/* ==================================================================
   THE MUSIC TOGGLE
   ================================================================== */
.audio-btn {
  position: fixed; top: 16px; right: 16px; z-index: 70;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 9px 15px;
  border-radius: 999px;
  border: 1px solid rgba(247,242,230,.5);
  background: rgba(33,16,22,.4);
  backdrop-filter: blur(6px);
  color: var(--on-dark);
  font-family: var(--caps); letter-spacing: .16em; text-transform: uppercase;
  font-size: 11px; cursor: pointer;
  opacity: 0; transform: translateY(-10px);
  transition: opacity .5s ease .4s, transform .5s ease .4s, background .25s ease;
}
body.is-open .audio-btn { opacity: 1; transform: none; }
/* `display: inline-flex` above beats the UA rule for [hidden], so the toggle
   has to be told again — otherwise a site with no track shows a dead control */
.audio-btn[hidden] { display: none !important; }
.audio-btn:hover { background: rgba(33,16,22,.6); }
.audio-ico { display: inline-flex; align-items: flex-end; gap: 2px; height: 13px; }
.audio-ico i {
  width: 2px; background: currentColor; border-radius: 1px;
  height: 5px; transform-origin: bottom;
}
.audio-btn[aria-pressed="true"] .audio-ico i { animation: eq .9s ease-in-out infinite; }
.audio-btn[aria-pressed="true"] .audio-ico i:nth-child(2) { animation-delay: .18s; }
.audio-btn[aria-pressed="true"] .audio-ico i:nth-child(3) { animation-delay: .36s; }
.audio-btn[aria-pressed="false"] .audio-ico i { height: 4px; opacity: .55; }
@keyframes eq { 0%,100% { height: 4px; } 50% { height: 13px; } }

/* ==================================================================
   ORNAMENT LAYER — it frames, it does not shout
   ================================================================== */
.orn-edge {
  position: absolute; left: 0; right: 0; height: clamp(28px, 4vw, 56px);
  z-index: 2; pointer-events: none;
  background-repeat: repeat-x;
  background-size: auto 100%;
  opacity: .3;
}
.orn-edge--top { top: calc(var(--edge-h) * .5); }
.orn-edge--bot { bottom: clamp(10px, 2vw, 26px); transform: scaleY(-1); }
.room--cream .orn-edge, .room--pink .orn-edge,
.room--teal .orn-edge, .room--indigo .orn-edge { opacity: .5; }

/* The arch plate is 1400x1747 and its interior opening runs x 16.1-83.3%,
   y 16.2-90.4% (measured off the alpha channel). The cusps narrow it toward
   the top, so the type block is inset further than the raw opening. */
.name-arch {
  position: relative;
  width: min(560px, 100%);
  margin: clamp(18px, 3vw, 34px) auto 0;
  aspect-ratio: 1400 / 1747;
  display: grid; place-items: center;
}
.name-arch-frame {
  position: absolute; inset: 0;
  background-size: contain; background-repeat: no-repeat; background-position: center;
  pointer-events: none;
}
.name-arch-in {
  position: relative;
  /* the cusps make the top of the opening narrower than its 16-83% span, and
     the decorated head reads as "the frame" — so the block sits a little low
     and a little wide, which fills the plain lower half of the opening */
  width: 70%;
  margin-top: 9%;
}
/* no frame plate: the type keeps its own rhythm rather than a squashed box */
.name-arch:not(:has(.name-arch-frame[style*="url"])) { aspect-ratio: auto; }
.name-arch:not(:has(.name-arch-frame[style*="url"])) .name-arch-in { width: 100%; margin-top: 0; }

.name-arch .couple { font-size: clamp(36px, 8vw, 84px); }
.name-arch .couple-date { font-size: clamp(15px, 2.4vw, 28px); letter-spacing: .16em; }
.name-arch .couple-meta { font-size: clamp(12px, 1.4vw, 16px); }
.name-arch .tagline { font-size: clamp(10px, 1.1vw, 13px); }
.name-arch .rule { margin: clamp(12px,2vw,22px) auto; width: 84px; }

.orn-mandala {
  position: absolute; left: 50%; top: 46%;
  width: min(760px, 108%); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background-size: contain; background-repeat: no-repeat; background-position: center;
  opacity: .07; z-index: 1; pointer-events: none;
}

/* The garland spans the full width; only its lower swag is shown.
   Two earlier attempts were both wrong: sizing by width alone dropped the
   catenary through the heading, and capping the height shrank it to a small
   centred necklace. The fix is to draw it full width and clip the top off —
   the band is the window, --garland-hide is how much of the plate rides above
   it. Clipping happens on the band, NOT on the room: the room has to stay
   overflow-visible or its curved top edge gets cut off. */
.orn-garland-band {
  position: absolute; top: calc(var(--edge-h) * .3); left: 0; right: 0;
  height: clamp(88px, 12vh, 168px);
  overflow: hidden;
  z-index: 2; pointer-events: none;
}
/* T-TM ran its swag full-bleed and cropped it from the bottom, because its
   2048x796 thoranam was a dense curtain whose hanging depth varied between
   regenerations. This plate is the opposite object: one 459x273 marigold rope
   whose ends leave the plate at both TOP corners. Stretched to 1440 and then
   cropped vertically the only thing left inside the band would be the two
   gold finials, so it is CENTRED and CONTAINED at its own aspect instead,
   which is how one garland is actually hung over a doorway. */
.orn-garland {
  position: absolute; left: 50%;
  width: min(760px, 74%); height: auto;
  max-height: clamp(88px, 12vh, 168px);
  object-fit: contain; object-position: top center;
  transform: translateX(-50%);
  opacity: .95;
}
/* the swag hangs into the room, so its room needs headroom for the heading */
#functions { padding-top: calc(var(--pad-y) + var(--edge-h) * .55 + clamp(84px, 12vh, 168px)); }
#functions .orn-edge--top { display: none; }   /* the garland IS the top edge here */

/* orn-garland is the MARIGOLD ROPE (459x273): its rope leaves the plate at both
   TOP corners, so it takes a HEIGHT cap and hangs from the top rather than a
   width that would stretch it into the room.
   orn-toran here is the GOLD ZARI STRIP, which is a flat band and not a hang —
   so it takes its natural aspect, capped, and sits as a rule above the RSVP.
   Both were re-cut seamless along X by seamx.py. */
.orn-toran {
  display: block; width: min(560px, 100%);
  max-height: clamp(26px, 4vh, 46px);
  object-fit: cover; object-position: center;
  margin: 0 auto clamp(14px, 2.4vw, 28px);
  opacity: .9;
}

/* four mirrored corners on a card, from one file */
.orn-corner {
  /* SMALLER AND FAINTER than Goa's. Goa left `orn-corner` unpainted, so these
     numbers were only ever tuned against Lantern Palace's flat paisley. This
     skin's corner is a paithani peacock-and-paisley cluster with dark green in
     it, and at 68px / .34 on the gold `know` cards it sat ON the body copy —
     four of them per card, one at each corner. Caught by looking at the room,
     not by a contrast number: it never failed a ratio, it just made the text
     hard to read. */
  position: absolute; width: clamp(26px, 3.2vw, 44px); aspect-ratio: 1;
  background-size: contain; background-repeat: no-repeat;
  opacity: .20; pointer-events: none;
}
.orn-corner--tl { top: 10px; left: 10px; }
.orn-corner--tr { top: 10px; right: 10px; transform: scaleX(-1); }
.orn-corner--bl { bottom: 10px; left: 10px; transform: scaleY(-1); }
.orn-corner--br { bottom: 10px; right: 10px; transform: scale(-1); }
.func, .know, .roll, .venue-card { position: relative; }

/* ==================================================================
   COUPLE SLOTS — a real photo, a commissioned illustration, or a
   deliberately abstract paper silhouette. Never a generated face.
   ================================================================== */
.portrait {
  position: relative;
  width: min(340px, 76%);
  margin: clamp(30px, 5vw, 56px) auto 0;
  aspect-ratio: 620 / 780;
}
.portrait--wide { width: min(380px, 82%); margin-top: clamp(40px, 6vw, 76px); }
.portrait-img {
  /* the opening of this skin's frame plate, measured from its alpha (top,
     right, bottom, left, plus half a percent inside the ring). The 7%/10%
     inset this rule shipped with drew the picture larger than the ring, so
     it showed outside the frame. */
  position: absolute; inset: 22.5% 21.5% 27.5% 21.5%;
  border-radius: 50%;
  overflow: hidden;
  background: rgba(247,242,230,.14);
  background-size: cover; background-position: center;
}
.room--cream .portrait-img { background-color: rgba(34,32,58,.05); }
.portrait-frame { position: absolute; inset: 0; width: 100%; height: 100%; }
.portrait[data-empty="1"] .portrait-img {
  background-size: contain; background-repeat: no-repeat; background-position: bottom center;
  opacity: .9;
}
/* the caption lives here, not baked into the SVG, so it takes the room's ink
   and stays legible on teal, indigo or cream alike */
.portrait[data-empty="1"]::after {
  content: "A photo of the two of you goes here";
  position: absolute; left: 0; right: 0; bottom: -1.9em;
  text-align: center; font-style: italic;
  font-size: clamp(12px, 1.2vw, 14px); opacity: .5;
}

@media (prefers-reduced-motion: reduce) {
  .env, .env-flap, .env-card { transition: none; }
  .env-cta { animation: none; }
  .audio-btn { transition: none; }
  .audio-btn[aria-pressed="true"] .audio-ico i { animation: none; height: 9px; }
}

/* ==================================================================
   ROOM ATMOSPHERE — the round-4 pieces
   ==================================================================
   Each one is dressing: low in the stack, never over live text, and every
   key is `optional` so a room ends up plainer rather than wrong.
   ================================================================== */
.piece { position: absolute; z-index: 1; pointer-events: none; }

/* the paithani peacock corner is a CORNER piece — its paisley tail sweeps down
   and left — so it is anchored to the invite room's right edge and leans into
   the copy the way it would off the pallu it was cut from */
.piece--peacock {
  right: -4%; bottom: clamp(12px, 3vw, 40px);
  width: clamp(140px, 19vw, 300px);
  opacity: .85;
}

/* T-TM hung an oonjal swing from the story room's top edge. There is nothing
   in this skin that hangs, so that element is removed from index.html and its
   rules with it — a single bhagwa flag stands in the same room instead. */

.piece--jharokha {
  left: -6%; top: clamp(40px, 8vw, 140px);
  width: clamp(130px, 17vw, 270px);
  opacity: .5;
}

/* Sits along the top of the night room. Capped by HEIGHT, not width: `piece-diya-row`
   holds the MANGO-LEAF TORAN here, and a full-width draw made it a quarter of
   the viewport. Height-capped and centred it reads as a toran strung across the
   room, which is where a toran belongs — the flicker below is kept because the
   marigolds on it catch the light the same way a lamp does. */
.piece--diyas {
  left: 50%; top: 0;
  transform: translateX(-50%);
  height: clamp(52px, 8vh, 108px); width: auto; max-width: 118%;
  opacity: .95;
  animation: diya-flicker 4.5s ease-in-out infinite;
}
@keyframes diya-flicker {
  0%, 100% { opacity: .82; }
  38%      { opacity: 1; }
  61%      { opacity: .88; }
}

/* The river strip. The boat was first put on the cart's band travelling the
   other way, and the two crossed in the middle of the screen with the boat
   behind the cart — so it gets its own field instead. */
.lake {
  position: relative;
  height: clamp(70px, 11vw, 130px);
  margin: clamp(26px, 4vw, 52px) 0 clamp(6px, 1vw, 14px);
  overflow: hidden;
  border-radius: 999px;
  /* the river past the end of the murrum road. It sits ON the deep-green room
     ground, so it is only a shade or two off it — a strip of moving water read
     against the fields behind it, not a separate blue object. */
  background: linear-gradient(#2C7A63, #12433A);
  opacity: .62;
}
/* piece-boat is the couple in a garlanded river boat, and it FLOATS — so it
   sits in the middle of the strip rather than standing on the bank. Wider than
   Ram Sita's cushioned padukas: this plate is a boat WITH two people in it and
   at 12vw the faces were the size of the type. */
.piece--boat {
  left: 32%; bottom: 12%;
  width: clamp(110px, 15vw, 210px);
  opacity: .95;
}

@media (max-width: 759px) {
  .piece--jharokha { opacity: .34; width: clamp(110px, 30vw, 160px); }
  .piece--peacock  { right: -8%; width: clamp(120px, 34vw, 200px); }
}
@media (prefers-reduced-motion: reduce) {
  .piece--diyas { animation: none; opacity: .92; }
}

/* ==================================================================
   ?plain=1 — the painted scene with none of the site's furniture,
   so the video pipeline can shoot the live hero cleanly.
   ================================================================== */
body.is-plain .hero,
body.is-plain .hero-hint,
body.is-plain .audio-btn,
body.is-plain .mobcta,
body.is-plain .skip,
body.is-plain .env { display: none !important; }
body.is-plain { overflow: hidden; }

/* ==================================================================
   0b. THE OPENING FILM
   ==================================================================
   Full-bleed, plays once, and hands to the interactive hero on `ended`.
   The type is DOM rather than burnt into the clip — same reason the RSVP is
   a form and not a picture of one.
   ================================================================== */
.intro {
  position: fixed; inset: 0; z-index: 95;
  background: #0D1030;
  opacity: 0;
  transition: opacity .6s ease;
}
.intro[hidden] { display: none !important; }
.intro.is-in { opacity: 1; }
/* on handoff the film fades and the live hero is already behind it, seeked to
   the progress the last frame was rendered at */
.intro.is-out { opacity: 0; transition: opacity .65s ease; pointer-events: none; }

.intro-vid {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  background: #0D1030;
}
/* keeps cream type legible over whatever the clip is doing underneath */
.intro-scrim {
  position: absolute; inset: 0;
  background:
    /* T-GS: the pool is pushed DOWN to 62% and the bottom stop is deepened from
       .46 to .62. This clip is a bright daylit beach, and the couple stand full
       height in the 16:9 cut, so the title lands on a white dress in white
       serif — at T-TM's values "Rohan" was barely there. Tinted to the night
       sea, not to maroon. */
    radial-gradient(ellipse 66% 40% at 50% 62%, rgba(10,40,48,.46), rgba(10,40,48,0) 72%),
    linear-gradient(rgba(10,40,48,.26), rgba(10,40,48,0) 26%, rgba(10,40,48,0) 46%, rgba(10,40,48,.62));
}

.intro-type {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  text-align: center;
  padding: 0 var(--pad-x);
  color: var(--on-dark);
  text-shadow: 0 2px 26px rgba(27,13,18,.6), 0 1px 3px rgba(27,13,18,.45);
}
.intro-line {
  grid-area: 1 / 1;              /* every line stacks on the same cell */
  /* A grid item centred with place-items sizes to max-content, so a long line
     runs straight off the viewport instead of wrapping — the bilingual date +
     venue did exactly that on a 390px phone. */
  max-width: 100%;
  opacity: 0;
  will-change: transform, opacity;
}
.intro-line--greeting { font-style: italic; font-size: clamp(17px, 2.6vw, 30px); }
.intro-line--title {
  font-weight: 300; font-size: clamp(40px, 8.4vw, 118px); line-height: 1.02;
  display: flex; flex-direction: column; align-items: center; gap: .04em;
}
/* the joiner is a plain ampersand — set in the display italic, small, so the
   two names stay the loud things on the line */
.intro-line--title em {
  font-family: var(--serif); font-style: italic;
  font-size: clamp(15px, 2vw, 30px); letter-spacing: normal; text-transform: none;
  opacity: .8; padding-left: 0;
}
/* Josefin Sans caps want the tracking back that T-TM had to cut for Tamil. */
.intro-line--detail {
  font-family: var(--caps); letter-spacing: .17em; text-transform: uppercase;
  font-size: clamp(16px, 2.1vw, 28px); line-height: 1.9;
}
.intro-line--tag {
  font-family: var(--caps); letter-spacing: .18em;
  font-size: clamp(22px, 4.4vw, 52px);
}

/* ==================================================================
   0c. THE INVITE SCENE — config.intro.mode "scene"
   ==================================================================
   Ganesha, then the couple, then the words. Everything here is positioned in
   percentages of the scene box and sized in vmin, so the same choreography
   plays at 1440x900 and at 390x844 without a second set of timings — only the
   backdrop swaps to its 9:16 twin. GSAP owns opacity/transform; this file owns
   placement and the two ambient loops (flame flicker, heart pulse).
   ================================================================== */
/* CENTRING NOTE for the four elements GSAP animates (.isc-kolam, .isc-ganesha,
   .isc-diyas, .isc-heart): they carry NO transform here. GSAP writes the whole
   transform property when it animates scale or y, which silently drops a
   translate(-50%,-50%) written in CSS and shunts the element half its own size
   down and right. Their centring is set once in playIntroScene() with GSAP's
   own xPercent/yPercent so it survives every tween. .isc-couple is not animated
   directly, so it keeps its CSS transform. */
.iscene { position: absolute; inset: 0; overflow: hidden; }
.iscene[hidden] { display: none !important; }

.isc-backdrop {
  position: absolute; inset: 0;
  background: var(--night) center / cover no-repeat;
  opacity: 0;
}
/* The gold kolam grid is baked into the plate, so it cannot be dimmed on its
   own — a flat wash of the plate's own ground colour pulls the pattern ~30%
   back toward it without shifting the hue or darkening the figures, which sit
   above this layer. */
.isc-backdrop::after {
  content: ""; position: absolute; inset: 0;
  background: rgba(110,27,43,.30);
}

/* The kolam draws itself left-to-right: a clip-path wipe, which works on a PNG
   as well as on an SVG and needs no path length to be known.
   The delivered plate is a square MEDALLION, not the wide strip this was first
   built against, so it sits BEHIND Ganesha as the floor he arrives onto rather
   than as a band under him — at strip proportions it overflowed the viewport
   bottom by 70-odd pixels. */
/* SIZING NOTE. These three are sized by HEIGHT, not width, because the brief
   specifies them in vh and a width in vmin only yields the intended vh on a
   landscape viewport — on a phone vmin is the WIDTH, so the same rule shrank
   Ganesha to 28vh where 44 was asked for. Heights are set directly; widths
   follow the plate's own aspect. */
.isc-kolam {
  position: absolute; left: 50%; top: 42%;
  height: 65vh; width: auto;       /* 1.25x Ganesha, behind him */
  z-index: 0; opacity: .5;
  clip-path: inset(0 100% 0 0);
}
.isc-kolam img { height: 100%; width: auto; display: block; }

.isc-diyas {
  /* bottom-anchored, not centred on a percentage: at 90% the pair hung 5px
     past the viewport floor */
  position: absolute; left: 50%; bottom: 3%; top: auto;
  width: min(30vmin, 300px);       /* centred by GSAP — see CENTRING NOTE */
  opacity: 0;
}

.isc-ganesha {
  /* Sized from the delivered sprites, not guessed: murti 1204x1604 drawn at
     78% of this width, lotus 1200x758 overlapped by 14%, so the stack is about
     1.53x whatever width is set here. 300px therefore composes ~460px tall and
     leaves the blessing a clear band beneath it. */
  position: absolute; left: 50%; top: 42%;
  height: 52vh; width: auto;       /* centred by GSAP — see CENTRING NOTE */
  z-index: 2;
  opacity: 0;
}
.isc-murti { height: 100%; width: auto; display: block; position: relative; z-index: 2; }

.isc-sparks { position: absolute; inset: 0; pointer-events: none; }
.isc-sparks img {
  position: absolute; width: min(5.5vmin, 54px);
  opacity: 0; will-change: transform, opacity;
}
/* a ring around where Ganesha lands */
/* a ring around Ganesha's landing point (50%, 38%) — scattered across the whole
   frame they read as background sparkle rather than as his arrival */
.isc-sparks img:nth-child(1) { left: 33%; top: 26%; }
.isc-sparks img:nth-child(2) { left: 65%; top: 25%; }
.isc-sparks img:nth-child(3) { left: 28%; top: 44%; }
.isc-sparks img:nth-child(4) { left: 70%; top: 43%; }
.isc-sparks img:nth-child(5) { left: 42%; top: 17%; }
.isc-sparks img:nth-child(6) { left: 58%; top: 18%; }
.isc-sparks img:nth-child(7) { left: 36%; top: 56%; }
.isc-sparks img:nth-child(8) { left: 64%; top: 55%; }

.isc-couple {
  /* Sits in the TOP two thirds — the words own the bottom third. Width is set
     from the delivered sprite (1427x1688, so 1.18x as tall as it is wide): at
     380px the pair composes ~450px and clears the title's cap height. */
  position: absolute; left: 50%; top: 42%;
  height: 58vh; width: auto;
  transform: translate(-50%, -50%);
}
/* the cream pool the bride stands against — see the note in index.html */
.isc-lift {
  position: absolute; left: 50%; top: 52%;
  height: 74vh; aspect-ratio: 1 / 1; width: auto;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(247,242,230,.38), rgba(247,242,230,.17) 46%, rgba(247,242,230,0) 74%);
  pointer-events: none; opacity: 0;
}
.isc-heart {
  position: absolute; left: 50%; top: 50%;
  height: 48vh; width: auto;       /* centred by GSAP — see CENTRING NOTE */
  opacity: 0;
}
.isc-pair { position: relative; height: 100%; width: auto; display: block; opacity: 0; }

.isc-petals { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }

.isc-blessing {
  position: absolute; left: 0; right: 0; top: 74%;
  z-index: 4;                      /* over the lotus, never behind it */
  margin: 0; padding: 0 var(--pad-x);
  text-align: center; color: var(--zari);
  font-family: var(--serif);
  font-size: clamp(17px, 2.6vw, 30px);
  opacity: 0;
  text-shadow: 0 2px 20px rgba(27,13,18,.7);
}

/* the scene lays the type lower than the video path did — the couple occupies
   the middle of the frame where the film had sky */
/* The words sit in a band above the bottom edge, not against it. On a phone the
   layout viewport is taller than the captured/visible frame, and a line resting
   on the last few percent of the height falls off the bottom — the bilingual
   date line, which wraps to two rows, did exactly that. */
/* Anchored to a fixed line rather than to the bottom edge: every line's TOP
   sits at 74vh, ~4vh under the figures centred at 42%, so the text reads as
   belonging to them instead of stranded at the foot of the frame. */
.intro.is-scene .intro-type {
  align-items: start; justify-items: center;
  padding-top: 74vh; padding-bottom: 0;
}
/* Video mode too: the film composes its figures in the upper ~65% and leaves
   the lower third clear, so the live lines sit there instead of dead centre
   (2026-09-02: figures were being kept tiny to dodge centred text). */
.intro:not(.is-scene):not(.is-still) .intro-type {
  align-items: start; justify-items: center;
  padding-top: 68vh; padding-bottom: 0;
}
.intro:not(.is-scene):not(.is-still) .intro-line--title { font-size: min(9vh, 8.4vw, 110px); }
@media (max-width: 760px) {
  .intro:not(.is-scene):not(.is-still) .intro-type { padding-top: 66vh; }
}
/* The title is the only line tall enough to fall off the bottom from 74vh: two
   118px rows plus the joiner is 30vh, which needs 104. Capped against the
   HEIGHT as well as the width so it always fits the 26vh the band leaves it. */
.intro.is-scene .intro-line--title { font-size: min(9.4vh, 8.4vw, 118px); }
.intro.is-scene .intro-line--tag   { font-size: min(5.2vh, 4.4vw, 52px); }
.intro.is-scene .intro-scrim {
  background:
    radial-gradient(ellipse 70% 40% at 50% 78%, rgba(27,13,18,.62), rgba(27,13,18,0) 72%),
    linear-gradient(rgba(27,13,18,.28), rgba(27,13,18,0) 30%, rgba(27,13,18,0) 54%, rgba(27,13,18,.5));
}

@media (max-width: 759px) {
  /* Heights, not widths — see the SIZING NOTE above. The numbers are ~13%
     larger than the vh targets they hit: on a phone `vh` resolves against the
     DEVICE height while the layout viewport is taller, so a literal 44vh
     rendered as 39% of the frame the guest actually sees. These are calibrated
     against the measured result, not the nominal unit. */
  .isc-ganesha { height: 50vh; width: auto; top: 42%; }   /* ≈ 44% of frame */
  .isc-couple  { height: 54vh; width: auto; top: 42%; }   /* ≈ 48% of frame */
  .isc-kolam   { height: 62vh; width: auto; top: 42%; }   /* 1.25x Ganesha   */
  .isc-heart   { height: 45vh; width: auto; }
  .isc-lift    { height: 68vh; }
  .isc-diyas   { width: min(44vmin, 175px); bottom: 4%; }
  .isc-blessing { top: 74%; }
  /* calibrated the same way as the heights above: a literal 74vh lands at 66%
     of the visible frame, which is exactly where the couple's feet end */
  .intro.is-scene .intro-type { padding-top: 79vh; }
}

/* prefers-reduced-motion: the scene is a held still, not a sequence */
@media (prefers-reduced-motion: reduce) {
  .iscene .isc-diyas, .iscene .isc-sparks { display: none; }
  .isc-kolam { clip-path: none; }
}

.intro-skip {
  position: absolute; right: clamp(14px, 2.4vw, 30px); bottom: clamp(14px, 2.4vw, 30px);
  z-index: 3;
  padding: 9px 20px; border-radius: 999px;
  border: 1px solid rgba(247,242,230,.45);
  background: rgba(33,16,22,.4);
  backdrop-filter: blur(6px);
  color: var(--on-dark);
  font-family: var(--caps); letter-spacing: .2em; text-transform: uppercase;
  font-size: 11px; cursor: pointer;
}
.intro-skip:hover { background: rgba(33,16,22,.62); }

/* reduced motion gets the last frame and the lines stacked, no video */
.intro.is-still {
  background-size: cover; background-position: center; background-repeat: no-repeat;
}
.intro.is-still .intro-vid,
.intro.is-still .intro-skip { display: none; }
.intro.is-still .intro-type {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: clamp(12px, 2.6vh, 26px);
}
.intro.is-still .intro-line { grid-area: auto; opacity: 1; transform: none; }

/* Words the record supplies in Hindi or Marathi. This skin's own faces have
   no Devanagari, so those glyphs would fall back to whatever the phone has.
   main.js marks any leaf whose text is Devanagari with .dv after render. */
.dv{font-family:"Tiro Devanagari Hindi",Georgia,serif!important;letter-spacing:0!important;text-transform:none!important}
html[lang="mr"] .dv{font-family:"Tiro Devanagari Marathi",Georgia,serif!important}

/* A form told to hide must hide: `display` here outranks the browser's own
   [hidden] rule, so the submitted form used to sit beside its own thank-you. */
.form[hidden],form[hidden]{display:none!important}


/* Couple-uploaded photographs — the venue card and the hotel list. Both are
   optional and both are hidden until a file exists, so a wedding with no
   photographs looks exactly as this design did before they were possible. */
.venue-photo{display:block;width:100%;max-width:520px;aspect-ratio:16/9;object-fit:cover;
  margin:0 auto 18px;border-radius:6px}
/* `display:block` above beats the UA rule for [hidden] (same trap as .audio-btn),
   so an empty slot painted a blank 16:9 box above "The venue". */
.venue-photo[hidden]{display:none!important}
.hotel-photo{width:84px;height:84px;object-fit:cover;border-radius:6px;flex:none;margin-right:14px}
@media(max-width:560px){.hotel-photo{width:62px;height:62px;margin-right:10px}}

/* Scroll reveals — a class and a transition (see the reveals in main.js).
   Elements get .rv from JS only, so without JS nothing is ever hidden. */
.rv { opacity: 0; transform: translateY(46px); transition: opacity .9s cubic-bezier(.2,.7,.2,1), transform .9s cubic-bezier(.2,.7,.2,1); }
.rv-l { transform: translateX(-70px); }
.rv-r { transform: translateX(70px); }
.rv.is-shown { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .rv { opacity: 1; transform: none; transition: none; } }

/* The corner piece of the invite room (data-img="piece-peacock") sits in the
   room's bottom-right; on a phone that corner is where the RSVP row ends up,
   and the piece was drawn over "Add to calendar". A clear band under the
   buttons keeps them apart. (Measured at 430px on every skin.) */
@media (max-width: 759px) { #invite .wrap { padding-bottom: clamp(150px, 40vw, 200px); } }
