/* ===========================================================================
   Jeet × Bhavini — 1–2 December 2026
   Shell: pale grey-ivory, mogra garlands, brass. Events: one accent each.
   =========================================================================== */

:root {
  /* ---- ground & ink ---- */
  --paper:        #F2F1ED;
  --paper-warm:   #EDE9E0;
  --ink:          #33322E;
  --ink-soft:     #6B6862;
  --ink-faint:    #9C988F;
  --hairline:     #D8D4CA;

  /* ---- accents ---- */
  --rose:         #C08478;
  --brass:        #A8894F;
  --brass-light:  #C9B27C;
  --sage:         #6E7F63;
  --charcoal:     #3A3A38;
  --terracotta:   #B4674A;
  --magenta:      #A63A62;

  /* ---- closing half (save-the-date + countdown) ----
     A DEEPER REGISTER OF THE SAME PAPER, not a dark panel. The oxblood that
     sat here read as a different site once the cards went to dusty rose and
     antique gold - it was the only saturated dark thing left on the page.
     This is the last leaf of the kankotri, so it carries the same ivory a
     shade lower, with brass doing the work the bright gold used to. Change
     these and the whole closing section follows. */
  --dusk:        #E8E1D2;
  --dusk-deep:   #DFD5C1;
  --dusk-ink:    #3A332A;
  --dusk-soft:   #6A6157;
  --dusk-faint:  #645B51;
  --dusk-accent: #8E3A5D;   /* the cards' berry plum, for headings */
  --gold:        #9A7A40;   /* brass for rules, icons and fills */
  --gold-ink:    #74592B;   /* the same brass, dark enough to be read as text */

  /* ---- type ---- */
  --serif:  "Cormorant Garamond", "Iowan Old Style", Palatino, Georgia, serif;
  /* Jost has no Gujarati glyphs, so Noto sits in the stack behind it to
     catch stray Gujarati in otherwise-Latin runs (e.g. the Hastamelap
     label) rather than letting the OS pick. */
  --sans:   "Jost", "Noto Serif Gujarati", "Avenir Next", "Segoe UI", system-ui, sans-serif;
  --gujarati: "Noto Serif Gujarati", "Shruti", "Nirmala UI", var(--serif);
  /* Rasa is a display Gujarati serif — heavier and more calligraphic than
     Noto, which is what the reference cards use for their event titles.
     Great Vibes carries the English name beneath it. */
  --gu-display: "Rasa", "Noto Serif Gujarati", "Shruti", "Nirmala UI", var(--serif);
  --script: "Great Vibes", "Apple Chancery", "Segoe Script", cursive;
  /* Card lettering.

     Gujarati: Mogra, chosen from the ONLY six decorative Gujarati faces on
     Google Fonts (Mogra, Shrikhand, Kumar One, Kumar One Outline, Farsan,
     Baloo Bhai 2) - brush-drawn, with the largest flowing curves of the set.
     None of Kalam / Tiro Devanagari Hindi / Yatra One / Modak / Khand carry
     Gujarati glyphs at all, and Tiro Gujarati is not on Google Fonts.

     English: the reference's long sweeping swashes are a connected script,
     not a serif, so Great Vibes matches it far better than Playfair. */
  --gu-card: "Rasa", "Noto Serif Gujarati", "Shruti", "Nirmala UI", var(--serif);
  --card-en: "Great Vibes", "Playfair Display", "Apple Chancery", cursive;

  /* ---- language ----
     In Gujarati the body copy has to be set in a face that carries the script.
     Cormorant does not, so --serif and --sans are re-pointed at the Gujarati
     stack for the whole document. Rasa keeps the same warm serif colour as
     Cormorant, so the page does not change character when it changes language.
     Both stacks keep their Latin fallbacks: dates, times and brand names stay
     in Latin even on a Gujarati page. */

  /* ---- rhythm ---- */
  --gutter: clamp(1.25rem, 5vw, 3.5rem);
  --section-y: clamp(4.5rem, 12vw, 9rem);
  --measure: 34rem;
}

html[data-lang="gu"] {
  --serif: "Rasa", "Noto Serif Gujarati", "Shruti", "Nirmala UI", Georgia, serif;
  --sans:  "Noto Serif Gujarati", "Shruti", "Nirmala UI", "Jost", system-ui, sans-serif;
}
/* Gujarati carries matras above and below the line and needs the headroom. */
html[data-lang="gu"] body { line-height: 1.65; }
html[data-lang="gu"] .section-title,
html[data-lang="gu"] .countdown__title { line-height: 1.4; }

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.65;
  overflow-x: hidden;
  /* Paper grain. The tile is made seamless by scripts/make_seamless.py — the
     raw generated texture has mismatched edges and a vignette, which showed up
     as a grid of lines across every page. Size matches the tile 1:1. */
  background-image: url("assets/generated/paper_grain.png");
  background-size: 640px;
  background-blend-mode: multiply;
}

img { max-width: 100%; display: block; }
[hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   Type primitives
   -------------------------------------------------------------------------- */

.eyebrow {
  font-size: clamp(.62rem, 1.6vw, .72rem);
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--rose);
  margin: 0 0 1.1em;
  font-weight: 500;
}

.display {
  font-family: var(--serif);
  font-weight: 300;
  line-height: 1.02;
  letter-spacing: -.005em;
  margin: 0;
}

.gu {
  font-family: var(--gujarati);
  font-weight: 500;
  line-height: 1.5;   /* Gujarati needs headroom for matras */
}

.section-title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2rem, 6vw, 3.1rem);
  line-height: 1.1;
  margin: 0;
}

.lede {
  max-width: var(--measure);
  margin-inline: auto;
  color: var(--ink-soft);
  font-size: clamp(.95rem, 2.4vw, 1.02rem);
}

/* ornamental rule ------------------------------------------------------- */
.rule {
  display: block;
  width: min(320px, 62%);
  margin: 1.6rem auto;
  color: var(--brass);
}
.rule svg { width: 100%; height: auto; display: block; }

/* --------------------------------------------------------------------------
   Layout
   -------------------------------------------------------------------------- */

.section {
  position: relative;
  padding: var(--section-y) var(--gutter);
}
.section--center { text-align: center; }

.wrap { max-width: 68rem; margin-inline: auto; }
.wrap--narrow { max-width: 40rem; margin-inline: auto; }

/* --------------------------------------------------------------------------
   Intro screen
   The strike lands at 56% of a 3.6s timeline; the elephant and the bell share
   that duration so the swing reads as a consequence of the reach.
   -------------------------------------------------------------------------- */

.intro {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--paper);
  display: grid;
  grid-template-rows: 1fr auto;
  overflow: hidden;
  transition: opacity .9s ease, visibility .9s;
}
body.intro-open { overflow: hidden; }

.intro.is-done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.intro__scene {
  position: relative;
  overflow: hidden;
  min-height: 0;
  container-type: size;           /* 100cqh/100cqw = the scene box */
}

.intro__backdrop {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 67%;  /* puts a floor at the scene's bottom edge */
  animation: introFade 1.2s ease both;
}

/* Bell + elephant.

   The backdrop is a 576x1024 painting cropped with `cover`, so how much of it
   a viewport shows varies enormously - a phone sees almost all of it, a wide
   desktop sees a ~27% tall band. Two coordinate systems therefore exist and
   they do not scale together:

     scene   the visible box. The elephant lives here: its feet sit on a
             ground line at 92% of the scene, and `object-position: center 67%`
             is chosen so the painting puts a floor under that line at every
             size - the pavilion deck on wide screens, the courtyard tiles on
             phones.
     stage   the painting as actually rendered, `max(100cqh, 177.78cqw)` tall
             and offset by the object-position. The bell's hook lives here: it
             is the little brass pendant painted under the pavilion's eave, at
             49.7% across and 38.3% down the image.

   The chain has one end in each, so it is not part of the bell image - it is a
   tiled strip whose length is whatever the two ends ask for. When the eave is
   off the top of the crop the hook goes with it and the chain simply runs out
   of frame, which is the same moment the roof stops being visible, so it never
   reads as hanging from nothing.

   Measured off the trimmed elephant PNG:

     trunk tip   24.6% across (mean of its rest and contact poses), ~0% down
     back feet   78% across, 100% down   (the strike pivots here)

   The elephant is capped at 58cqw so its rump cannot run off a narrow screen;
   on a phone that, not the height, is what sets its size. */
.intro__pair {
  position: absolute;
  left: 50%;
  bottom: 8%;                     /* the ground line */
  height: 40%;                    /* fallback: no container query units */
  height: min(46cqh, 58cqw);      /* = the elephant's height */
  aspect-ratio: 800 / 742;
  translate: -24.6% 0;            /* puts the trunk tip under the hook */
}

/* contact shadow, spanning the four feet (27%-95% of the elephant's width) */
.intro__pair::before {
  content: "";
  position: absolute;
  left: 22%;
  width: 78%;
  bottom: -2.5%;
  height: 9%;
  background: radial-gradient(50% 50% at 50% 50%,
              rgba(78,62,48,.30), rgba(78,62,48,.11) 55%, rgba(78,62,48,0) 76%);
  animation: introFade 1.1s ease .5s both;
}

/* The chain. Its top is the painted hook, converted from stage to scene and
   then to this box:  hook - pairTop  =  (67cqh - .287*stage) - (92cqh - He).
   Its bottom is the clapper, just clear of the trunk.

   The bell is 41.8% of the elephant's height tall and hangs at the bottom of
   this box, so the box has to be longer than that or the bell swallows the
   hook. It always is, except in a narrow band of scene aspect ratios around
   0.76-0.82 where it falls short by about 1cqh - the `min()` is the floor that
   covers it, at the cost of the chain starting a hair above the hook there. */
.intro__rig {
  position: absolute;
  left: 24.6%;
  width: 2.85%;
  margin-left: -1.425%;
  top: -55%;                      /* fallback: no container query units */
  top: min(calc(min(46cqh, 58cqw) - 25cqh - .287 * max(100cqh, 177.7778cqw)), -50%);
  bottom: 103%;
  background: url(assets/generated/intro_chain.png?v=20260913a) repeat-y 50% 100% / 100% auto;
  animation: introFade .8s ease .3s both;
}

/* The bell hangs off the bottom of the chain and rocks about the ring it hangs
   by, which is where a tapped bell actually articulates - the chain barely
   moves. Rocking about its own ring also keeps the swing proportional to the
   bell at every viewport, which rotating the whole chain would not. */
.intro__bell {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 947%;                    /* 27% of the elephant's width */
  max-width: none;                /* the global img rule would clamp it to the
                                     chain's width, which is 1/9th of this */
  height: auto;
  translate: -50.3% 0;            /* the ring, not the rim, sits on the chain */
  transform-origin: 50.3% 13%;    /* the ring */
  animation: bellRock 3.6s cubic-bezier(.33,.7,.4,1) .3s both;
}

.intro__ele {
  position: absolute;
  inset: 0;
  transform-origin: 78% 100%;     /* pivots on its back feet */
  animation: introRise 1s cubic-bezier(.22,.61,.36,1) .2s both,
             elephantRing 3.6s cubic-bezier(.33,.7,.4,1) .3s both;
}
.intro__elebody { width: 100%; height: 100%; display: block; }

/* The trunk is lifted off the elephant into its own layer and replaced by 11
   warped poses on a sprite sheet - a twirl about the joint plus a lift, both
   weighted to nothing at the joint so the seam never opens. The poses are a
   designed sequence, not a slider: frame 0 rest, 4 wound left, 6 the strike,
   7 the follow-through right. The tip covers 7.8% of the elephant's width
   between 4 and 7, which is what makes the swing read as a swing. Held on
   step-end, so it looks drawn rather than tweened. */
.intro__trunk {
  position: absolute;
  left: 1.25%;
  top: -7.547%;
  width: 33.75%;
  height: 37.197%;
  background: url(assets/generated/intro_trunk_sheet.png?v=20260913a) no-repeat 50% 0 / 1100% 100%;
  animation: trunkCurl 3.6s step-end .3s both;
}

.intro__copy {
  text-align: center;
  padding: clamp(1.2rem, 4vh, 2.4rem) var(--gutter) clamp(2rem, 6vh, 3.4rem);
  background: var(--paper);
  position: relative;
  z-index: 2;
}
.intro__copy .eyebrow { animation: introFade .9s ease 1.9s both; margin-bottom: .7em; }
.intro__names {
  font-size: clamp(2.2rem, 9vw, 3.6rem);
  animation: introFade 1s ease 2.1s both;
}
.intro__names em { font-style: italic; color: var(--rose); font-size: .58em; }
.intro__gu {
  font-size: clamp(.9rem, 3vw, 1.1rem);
  color: var(--ink-soft);
  margin: .5rem 0 0;
  animation: introFade 1s ease 2.3s both;
}

.intro__skip {
  position: absolute;
  /* clears the language toggle, which is pinned to the same corner */
  top: clamp(.8rem, 2vh, 1.4rem);
  left: clamp(.9rem, 3vw, 1.6rem);
  z-index: 3;
  border: 1px solid var(--hairline);
  background: rgba(255,255,255,.7);
  backdrop-filter: blur(4px);
  color: var(--ink-soft);
  font: inherit;
  font-size: .64rem;
  letter-spacing: .24em;
  text-transform: uppercase;
  padding: .55rem 1.1rem;
  cursor: pointer;
  animation: introFade .6s ease 1.2s both;
}
.intro__skip:hover { color: var(--ink); border-color: var(--brass); }

@keyframes introFade { from { opacity: 0 } to { opacity: 1 } }
@keyframes introRise {
  from { opacity: 0; translate: 0 5%; }
  to   { opacity: 1; translate: 0 0; }
}

/* The reach. Rotating clockwise about the back feet is what lifts the front
   of the elephant, so the trunk rises; the translate adds the rest. Both are
   relative, so the reach scales with the elephant. Contact is at 56%, which
   is exactly where the bell starts to move - the bell must never lead. */
@keyframes elephantRing {
  0%, 44%   { transform: translateY(0) rotate(0deg); }
  56%       { transform: translateY(-.6%) rotate(1.2deg); }
  68%       { transform: translateY(0) rotate(0deg); }
  76%       { transform: translateY(0) rotate(.5deg); }
  86%, 100% { transform: translateY(0) rotate(0deg); }
}

/* Frame index x 10%. Idle sway, wind left, sweep right through the bell at
   56%, follow through, settle. */
@keyframes trunkCurl {
  0%   { background-position-x: 0%; }    /* rest */
  9%   { background-position-x: 10%; }
  18%  { background-position-x: 20%; }
  26%  { background-position-x: 0%; }
  34%  { background-position-x: 30%; }
  44%  { background-position-x: 40%; }   /* wound left */
  50%  { background-position-x: 50%; }
  56%  { background-position-x: 60%; }   /* the strike */
  61%  { background-position-x: 70%; }   /* follow through, right of the bell */
  66%  { background-position-x: 80%; }
  71%  { background-position-x: 90%; }
  77%  { background-position-x: 100%; }
  84%  { background-position-x: 10%; }
  91%  { background-position-x: 20%; }
  100% { background-position-x: 0%; }
}

/* Struck at 56% and pushed to its right, then damping out. */
@keyframes bellRock {
  0%, 56%  { transform: rotate(0deg); }
  61%      { transform: rotate(9deg); }
  67%      { transform: rotate(-6.5deg); }
  73%      { transform: rotate(4.4deg); }
  79%      { transform: rotate(-2.8deg); }
  85%      { transform: rotate(1.7deg); }
  91%      { transform: rotate(-.9deg); }
  100%     { transform: rotate(0deg); }
}

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  /* Content sits high, with the garden filling the space beneath it — the
     arrangement on the reel's names screen — rather than centred. */
  place-items: start center;
  padding: clamp(4.5rem, 13vh, 8rem) var(--gutter) clamp(4rem, 10vh, 6rem);
  overflow: hidden;
  isolation: isolate;
}

/* faint palace skyline along the base */
.hero::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 74%;
  /* the reel's second screen: a watercolour garden with blue domes and a
     fountain, held back so the names stay dominant */
  background: url("assets/generated/hero_garden.png") bottom center / cover no-repeat;
  opacity: .42;
  z-index: -2;
  pointer-events: none;
  /* fade it out upward so it sits behind the type instead of competing */
  -webkit-mask-image: linear-gradient(transparent, #000 42%);
          mask-image: linear-gradient(transparent, #000 42%);
}

.hero__inner { position: relative; z-index: 2; text-align: center; }
/* A soft scrim of the paper colour, so the names, date and venue stay legible
   over the garden's fountain and domes without hiding them. */
.hero__inner::before {
  content: "";
  position: absolute;
  inset: -10% -14%;
  z-index: -1;
  background: radial-gradient(ellipse at center,
    rgba(242,241,237,.94) 0%,
    rgba(242,241,237,.86) 38%,
    rgba(242,241,237,.55) 62%,
    transparent 78%);
  pointer-events: none;
}

.hero__names {
  font-size: clamp(3.4rem, 15vw, 7.5rem);
  color: var(--ink);
}
.hero__amp {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1.3rem, 4vw, 2rem);
  color: var(--rose);
  margin: .12em 0;
  font-weight: 400;
}
.hero__gu {
  font-size: clamp(1rem, 3.4vw, 1.4rem);
  color: var(--ink-soft);
  margin: .9rem 0 0;
  letter-spacing: .04em;
}
.hero__meta {
  margin: 1.9rem 0 0;
  font-size: clamp(.78rem, 2.2vw, .9rem);
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.hero__meta span { display: block; }
.hero__meta span + span { margin-top: .5em; }

/* --- hero furniture: garlands, palm, brass ------------------------------- */
.deco {
  position: absolute;
  pointer-events: none;
  user-select: none;
  z-index: 1;
}
.deco img { width: 100%; height: auto; }

/* Garlands hang from the top edge. They are sized by HEIGHT, not width:
   the source PNGs are tall with transparent margins, so constraining the
   width leaves a stub of empty padding on screen. */
.garland { width: auto; }
.garland img { height: 100%; width: auto; }

.deco--garland-a { top: -1%; left: 3%;   height: clamp(260px, 56vh, 580px); }
.deco--garland-b { top: -1%; left: 21%;  height: clamp(185px, 40vh, 415px); opacity: .92; }
.deco--garland-c { top: -1%; right: 4%;  height: clamp(235px, 50vh, 520px); }
.deco--garland-d { top: -1%; right: 22%; height: clamp(165px, 35vh, 360px); opacity: .88; }

/* palm from the lower corners */
.deco--palm-l { bottom: -4%; left: -7%;  width: clamp(200px, 44vw, 450px); }
.deco--palm-r { bottom: -6%; right: -8%; width: clamp(190px, 42vw, 430px); }

/* brass props sit on the baseline */
.deco--kalash   { bottom: 0; left: clamp(4px, 4vw, 64px);  width: clamp(104px, 24vw, 225px); }
.deco--elephant { bottom: 0; right: clamp(4px, 5vw, 82px); width: clamp(112px, 25vw, 235px); }
.deco--lotus    { bottom: 0; right: clamp(120px, 30vw, 330px); width: clamp(80px, 17vw, 160px); }

/* --- birds ------------------------------------------------------------- */
.bird {
  position: absolute;
  z-index: 3;
  pointer-events: none;
  /* size and aspect are per-bird: the two frame pairs were cropped to
     different shared boxes (647x599 and 480x543) */
}
/* Three wing positions sliced from one sprite sheet, cropped to a shared box
   so only the wings move. The cycle is a ping-pong — folded, spread, raised,
   spread — which reads as a wingbeat. Each frame is a stacked image whose
   opacity is stepped on for its quarter of the cycle. */
.bird img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: auto;
  opacity: 0;
}
.bird .wf1 { animation: wfFolded  var(--beat) steps(1, end) infinite; }
.bird .wf2 { animation: wfSpread  var(--beat) steps(1, end) infinite; }
.bird .wf3 { animation: wfRaised  var(--beat) steps(1, end) infinite; }

@keyframes wfFolded {          /* quarter 1 */
  0%, 24.99%   { opacity: 1; }
  25%, 100%    { opacity: 0; }
}
@keyframes wfSpread {          /* quarters 2 and 4 — passed through twice */
  0%, 24.99%   { opacity: 0; }
  25%, 49.99%  { opacity: 1; }
  50%, 74.99%  { opacity: 0; }
  75%, 100%    { opacity: 1; }
}
@keyframes wfRaised {          /* quarter 3 */
  0%, 49.99%   { opacity: 0; }
  50%, 74.99%  { opacity: 1; }
  75%, 100%    { opacity: 0; }
}
/* the sheet birds face right; flip the right-hand one so they face inward */
.bird--b img { transform: scaleX(-1); }

/* The birds hold their position and only beat their wings — they hover beside
   the names rather than crossing the screen. */
/* Anchored to the text column, not the viewport edge: the calc tracks the
   centre so they flank the names on a wide screen, and the clamp floor keeps
   them on-screen on a narrow one. */
.bird--a {
  top: 25%;
  left: clamp(1.5rem, calc(50% - 19rem), 50%);
  width: clamp(92px, 21vw, 205px);
  aspect-ratio: 232 / 158;
  --beat: .95s;
  rotate: -6deg;
}
.bird--b {
  top: 44%;
  right: clamp(1.5rem, calc(50% - 18rem), 50%);
  width: clamp(78px, 18vw, 178px);
  aspect-ratio: 205 / 196;
  --beat: 1.15s;
  rotate: 5deg;
}

@media (max-width: 560px) {
  /* kept clear of the viewport edges so wing tips and tails are not clipped */
  .bird--a { top: 24%; left: 5%;  width: 94px; }
  .bird--b { top: 45%; right: 4%; width: 80px; }
}

@media (max-width: 560px) {
  /* Thin the arrangement out rather than shrinking it into mush, and pull the
     remaining strands right to the edges so they clear the headline. */
  .deco--garland-b, .deco--garland-d, .deco--lotus { display: none; }
  .deco--garland-a { left: 0;  height: 30vh; }
  .deco--garland-c { right: 0; height: 26vh; }
  .hero__inner { padding-top: 4vh; }
}

/* scroll cue -------------------------------------------------------------- */
.scroll-cue {
  position: absolute;
  bottom: clamp(1rem, 3vh, 2rem);
  left: 50%;
  translate: -50% 0;
  z-index: 3;
  font-size: .6rem;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--ink-faint);
  display: grid;
  justify-items: center;
  gap: .55rem;
}
.scroll-cue::after {
  content: "";
  width: 1px;
  height: 2.4rem;
  background: linear-gradient(var(--brass), transparent);
  animation: cue 2.4s ease-in-out infinite;
}
@keyframes cue {
  0%, 100% { transform: scaleY(.35); transform-origin: top; opacity: .4; }
  50%      { transform: scaleY(1);   transform-origin: top; opacity: 1; }
}

/* --------------------------------------------------------------------------
   Invitation panel
   -------------------------------------------------------------------------- */

.invite { position: relative; }
.invite__card {
  position: relative;
  max-width: 33rem;
  margin-inline: auto;
  padding: clamp(2.5rem, 8vw, 4.5rem) clamp(1.5rem, 6vw, 3.25rem);
  background: rgba(255,255,255,.42);
  border: 1px solid var(--hairline);
  outline: 1px solid rgba(168,137,79,.25);
  outline-offset: 6px;
  text-align: center;
}
.invite__ganesha { width: clamp(58px, 14vw, 84px); margin: 0 auto 1.6rem; }
.invite__blessing {
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink-soft);
  font-size: 1.02rem;
  margin: 0 0 .4rem;
}
.invite__lead {
  color: var(--ink-soft);
  font-size: .93rem;
  max-width: 24rem;
  margin: 0 auto 2.2rem;
}
.invite__name {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2.1rem, 8vw, 3rem);
  line-height: 1.05;
  margin: 0;
}
.invite__gu { font-size: clamp(.9rem, 3vw, 1.05rem); color: var(--ink-soft); margin: .25rem 0 0; }
.invite__parents {
  font-size: .8rem;
  color: var(--ink-faint);
  margin: .7rem auto 0;
  max-width: 22rem;
  line-height: 1.55;
}
.invite__weds {
  font-family: var(--serif);
  font-style: italic;
  color: var(--rose);
  font-size: 1.15rem;
  margin: 1.5rem 0;
}
.invite__when {
  margin-top: 2.4rem;
  font-size: .82rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.invite .deco--corner-tl,
.invite .deco--corner-br { width: clamp(90px, 22vw, 190px); opacity: .8; }
.invite .deco--corner-tl { top: 2%;  left: 0; }
.invite .deco--corner-br { bottom: 2%; right: 0; scale: -1 1; }

/* --------------------------------------------------------------------------
   Events — five individual invitation cards

   Matched to the reference card set. Each card is its own element, rendered
   from its own entry in content.js, independently editable and clickable —
   deliberately not one composite image.

   Reference details that drive the rules below:
     - no printed border of any kind; the card is just the sheet
     - the vernacular title is huge, nearly the card's width, in the event
       colour; the English sits under it in a WARM ORANGE script, offset right
     - faint sepia architecture ghosted behind the copy
     - a big numeral, a vertical rule, then month over time
     - venue set small and centred beneath
     - a large illustration across the foot
   -------------------------------------------------------------------------- */

.events { background: linear-gradient(180deg, transparent, rgba(237,233,224,.5) 42%, transparent); }

/* Per-card ink. `--ink-c` is the title/numeral colour, `--ink-en` the English
   script — warm orange on every card in the reference, which is what ties the
   set together. */
.inv[data-ink="sage"]       { --ink-c: #8E3A5D; --ink-en: #6B3A24; }
.inv[data-ink="indigo"]     { --ink-c: #B0552E; --ink-en: #6B3A24; }
.inv[data-ink="gold"]       { --ink-c: #A0752B; --ink-en: #6B3A24; }
.inv[data-ink="rose"]       { --ink-c: #D4417B; --ink-en: #6B3A24; }
.inv[data-ink="terracotta"] { --ink-c: #9A5B43; --ink-en: #6B3A24; }

.events__list {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2rem, 6vw, 3rem);
  justify-items: center;
  margin-top: clamp(2.5rem, 7vw, 4rem);
}

/* ---- the hanger --------------------------------------------------------
   The card hangs from a rod with beaded strings below it and sways, the way a
   punkah does. The whole assembly turns together from a point above the rod's
   finial, so the rod stays the still edge and the tassels swing widest. */
.hang {
  position: relative;
  width: 100%;
  max-width: 21rem;
  --tassel-drop: clamp(2.5rem, 12vw, 4.5rem);
  padding-bottom: var(--tassel-drop);            /* room for the tassels */
  transform-origin: 50% -6%;
}
/* It swings in when it comes into view and settles, then breathes. Two
   animations on one property: the second only starts once the first is done,
   so the swing hands over to the idle sway without a jump. */
.hang.is-in {
  animation: swingIn 1.5s cubic-bezier(.28,.66,.32,1) both,
             sway 7s ease-in-out calc(1.5s + var(--sway-delay, 0s)) infinite;
}
@keyframes swingIn {
  0%   { rotate: -9deg;   }
  32%  { rotate:  5.2deg; }
  55%  { rotate: -2.8deg; }
  74%  { rotate:  1.5deg; }
  88%  { rotate: -0.7deg; }
  100% { rotate: -1.15deg; }   /* lands where the sway begins */
}
@keyframes sway {
  0%, 100% { rotate: -1.15deg; }
  50%      { rotate:  1.15deg; }
}
@media (prefers-reduced-motion: reduce) { .hang.is-in { animation: none; } }

.hang__rod {
  position: absolute;
  top: -1.5%;
  bottom: calc(var(--tassel-drop) - 1.5%);
  width: clamp(5px, 1.7cqw, 8px);
  border-radius: 999px;
  /* turned brass: a highlight just off centre, shadow down both edges */
  background: linear-gradient(90deg,
    #8A6828 0%, #C7A257 26%, #F0DDAE 46%, #DCC183 58%, #A98240 78%, #7A5B22 100%);
  box-shadow: 0 1px 3px rgba(61,52,40,.32);
  z-index: 2;
  pointer-events: none;
}
/* a small ball finial at each end */
.hang__rod::before,
.hang__rod::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 2.2em;
  height: 2.2em;
  translate: -50% 0;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #F6E7BC, #D3B06A 52%, #8A6828);
  box-shadow: 0 1px 3px rgba(61,52,40,.34);
}
.hang__rod::before { top: 0; translate: -50% -62%; }
.hang__rod::after  { bottom: 0; translate: -50% 62%; }

.hang[data-side="l"] .hang__rod { left:  clamp(-10px, -2cqw, -4px); }
.hang[data-side="r"] .hang__rod { right: clamp(-10px, -2cqw, -4px); }

.hang__tassels {
  position: absolute;
  left: 0; right: 0;
  top: calc(100% - var(--tassel-drop));
  display: flex;
  justify-content: center;
  gap: clamp(1.6rem, 9vw, 3rem);
  pointer-events: none;
}
.hang__tassel {
  width: clamp(13px, 4vw, 20px);
  height: auto;
  opacity: .92;
}
/* the middle string hangs longest, as a real set does */
.hang__tassel:nth-child(2) { width: clamp(16px, 5vw, 25px); }

/* ---- the card ---------------------------------------------------------- */
.inv {
  position: relative;
  width: 100%;
  aspect-ratio: 2 / 3;
  border-radius: clamp(.5rem, 3cqw, 1rem);
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* centred in the space above the floral border, rather than stacked from
     the top and leaving a hole between the button and the flowers */
  justify-content: center;
  text-align: center;
  padding: clamp(1.5rem, 8cqw, 2.4rem) clamp(1.1rem, 6cqw, 1.8rem)
           clamp(4.5rem, 24cqw, 6.5rem);
  overflow: hidden;
  background: #EFE7D2 center / cover no-repeat;
  box-shadow: 0 1px 1px rgba(61,52,40,.12),
              0 14px 30px -18px rgba(61,52,40,.5);
  transition: transform .45s cubic-bezier(.22,.61,.36,1),
              box-shadow .45s cubic-bezier(.22,.61,.36,1);
}
.inv[data-paper="a"] { background-image: url("assets/generated/card_paper_a.png"); }
.inv[data-paper="b"] { background-image: url("assets/generated/card_paper_b.png"); }
.inv[data-paper="c"] { background-image: url("assets/generated/card_paper_c.png"); }

/* Faded architecture across the whole card, held back behind the copy. */
.inv::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--wash) center / cover no-repeat;
  opacity: .10;
  mix-blend-mode: multiply;
  z-index: -1;
  pointer-events: none;
}
.inv[data-paper="a"] { --wash: url("assets/generated/card_wash_a.png"); }
.inv[data-paper="b"] { --wash: url("assets/generated/card_wash_b.png"); }
.inv[data-paper="c"] { --wash: url("assets/generated/card_wash_c.png"); }

.inv > * { position: relative; z-index: 1; }
.inv:hover { transform: translateY(-5px);
  box-shadow: 0 2px 3px rgba(61,52,40,.12), 0 22px 38px -18px rgba(61,52,40,.55); }

/* ---- the frame --------------------------------------------------------- */
/* Two keylines a few pixels apart, the outer heavier, with a flourish laid
   over each corner - the way an engraved card is ruled. */
.inv__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
}
.inv__frame path { fill: none; vector-effect: non-scaling-stroke; }
.inv__frame-o { stroke: color-mix(in srgb, var(--ink-c) 55%, transparent); stroke-width: 1.6; }
.inv__frame-i { stroke: color-mix(in srgb, var(--ink-c) 32%, transparent); stroke-width: 1; }

.inv__corner {
  position: absolute;
  width: clamp(38px, 20cqw, 74px);
  height: auto;
  opacity: .85;
  z-index: 0;
  pointer-events: none;
}
.inv__corner--tl { top: clamp(.5rem, 2.6cqw, .85rem); left:  clamp(.5rem, 2.6cqw, .85rem); }
.inv__corner--tr { top: clamp(.5rem, 2.6cqw, .85rem); right: clamp(.5rem, 2.6cqw, .85rem); scale: -1 1; }
.inv__corner--br { bottom: clamp(.5rem, 2.6cqw, .85rem); right: clamp(.5rem, 2.6cqw, .85rem); scale: -1 -1; }
.inv__corner--bl { bottom: clamp(.5rem, 2.6cqw, .85rem); left:  clamp(.5rem, 2.6cqw, .85rem); scale: 1 -1; }

/* ---- the face ---------------------------------------------------------- */
.inv__crest {
  width: auto;
  max-width: clamp(70px, 34cqw, 120px);
  max-height: clamp(52px, 26cqw, 92px);
  object-fit: contain;
  height: auto;
  margin: 0 0 clamp(.5rem, 3cqw, .9rem);
}

.inv__title { width: 100%; }
.inv__gu {
  font-family: var(--gu-card);
  font-weight: 600;
  font-size: clamp(1.5rem, 11cqw, 2.3rem);
  line-height: 1.15;
  color: var(--ink-c);
  margin: 0;
  letter-spacing: -.01em;
}
.inv__en {
  font-family: var(--card-en);
  font-size: clamp(1.15rem, 8cqw, 1.7rem);
  line-height: 1.1;
  color: var(--ink-en);
  margin: .1em 0 0;
}

.inv__date {
  font-family: var(--serif);
  font-size: clamp(.68rem, 4cqw, .82rem);
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-c);
  margin: clamp(.7rem, 4cqw, 1.1rem) 0 0;
  font-variant-numeric: lining-nums tabular-nums;
}

.inv__times {
  list-style: none;
  margin: clamp(.45rem, 2.6cqw, .75rem) 0 0;
  padding: 0;
  display: grid;
  gap: .2rem;
  font-family: var(--serif);
  font-size: clamp(.78rem, 4.4cqw, .95rem);
  color: #4A4238;
}
.inv__times li { display: flex; justify-content: center; gap: .5em; }
.inv__times b { font-weight: 600; font-variant-numeric: tabular-nums; }

.inv__venue {
  font-family: var(--serif);
  margin: clamp(.6rem, 3.4cqw, 1rem) 0 0;
  font-size: clamp(.82rem, 4.6cqw, 1rem);
  line-height: 1.45;
  color: #3A332A;
  white-space: pre-line;
}
.inv__note {
  font-family: var(--serif);
  font-style: italic;
  margin: clamp(.35rem, 2cqw, .6rem) 0 0;
  font-size: clamp(.72rem, 4cqw, .88rem);
  line-height: 1.45;
  color: #6A6157;
  max-width: 22em;
}

.inv__maps {
  margin: clamp(.7rem, 4cqw, 1.1rem) 0 0;
  padding: .5em 1.3em;
  border: 1px solid color-mix(in srgb, var(--ink-c) 45%, transparent);
  font-family: var(--sans);
  font-size: clamp(.6rem, 3.4cqw, .7rem);
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-c);
  text-decoration: none;
  transition: background .2s, border-color .2s;
}
.inv__maps:hover {
  background: color-mix(in srgb, var(--ink-c) 10%, transparent);
  border-color: var(--ink-c);
}

/* The card's own subject, then the floral border across the foot. */
.inv__ill {
  margin: auto auto 0;
  padding-top: clamp(.5rem, 3cqw, .9rem);
  width: auto;
  max-width: clamp(96px, 46cqw, 160px);
  max-height: 22%;
  object-fit: contain;
  filter: saturate(.9) contrast(.97);
}
.inv__spray {
  position: absolute;
  left: 50%;
  translate: -50% 0;
  bottom: clamp(.9rem, 5cqw, 1.6rem);
  width: clamp(130px, 62cqw, 220px);
  height: auto;
  z-index: 0;
  opacity: .95;
  pointer-events: none;
}

/* ---- tablet up --------------------------------------------------------- */
@media (min-width: 40rem) {
  .events__list {
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: clamp(1.4rem, 3.5vw, 2.2rem);
    max-width: 54rem;
    margin-inline: auto;
  }
  .hang { max-width: none; }
}

/* Gujarati must not be tracked; these two are set in caps for Latin. */
html[data-lang="gu"] .inv__date,
html[data-lang="gu"] .inv__maps { letter-spacing: .02em; text-transform: none; }

/* A handful of prints tipped onto the paper.

   Laid out in columns rather than at absolute positions: an absolute scatter
   is fixed to one viewport width and falls apart at every other. */
.scatter {
  columns: 2;
  column-gap: clamp(.5rem, 2vw, 1.1rem);
  max-width: 58rem;
  margin: clamp(1.4rem, 5vw, 2.2rem) auto 0;
  text-align: left;
}
@media (min-width: 760px) { .scatter { columns: 3; } }

.print {
  position: relative;
  break-inside: avoid;
  width: var(--w, 100%);
  margin: 0 0 clamp(.7rem, 2.5vw, 1.3rem) var(--dx, 0);
  /* uneven padding and corners - a cut print is never quite square */
  padding: clamp(.28rem, 1.1vw, .5rem) clamp(.3rem, 1.2vw, .55rem)
           clamp(.85rem, 2.8vw, 1.45rem);
  border-radius: 2px 3px 2px 4px;
  background: #F7F2E4;              /* aged stock, not white */
  box-shadow: 0 1px 2px rgba(61,52,40,.18),
              0 12px 22px -14px rgba(61,52,40,.5);
  rotate: var(--rot, 0deg);
  transition: rotate .45s cubic-bezier(.22,.61,.36,1),
              box-shadow .45s ease;
}
.print img { display: block; width: 100%; height: auto; border-radius: 1px; }

/* The scorch. Four corner burns of different sizes and strengths, over one
   all-round edge darkening, multiplied into both the mount and the picture -
   an old print browns at the edges right through. */
.print::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  mix-blend-mode: multiply;
  background:
    radial-gradient(58% 44% at  2%   0%, rgba(124,78,34,.40), transparent 72%),
    radial-gradient(44% 56% at 100%  16%, rgba(124,78,34,.27), transparent 74%),
    radial-gradient(68% 40% at  88% 100%, rgba(124,78,34,.36), transparent 72%),
    radial-gradient(40% 52% at   0%  84%, rgba(124,78,34,.24), transparent 74%),
    radial-gradient(114% 110% at 50% 50%, transparent 56%, rgba(108,66,26,.30) 100%);
}

.print:hover, .print:focus-within {
  rotate: 0deg;
  z-index: 2;
  box-shadow: 0 2px 4px rgba(61,52,40,.2),
              0 22px 36px -16px rgba(61,52,40,.6);
}

.gallery__caption {
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink-faint);
  margin: 0 0 .2rem;          /* sits above the prints now, not below */
}

/* --------------------------------------------------------------------------
   Closing half — save the date + countdown
   -------------------------------------------------------------------------- */

.closing {
  position: relative;
  background: var(--dusk);
  color: var(--dusk-ink);
  isolation: isolate;
  overflow: hidden;
}
.closing::before {
  content: "";
  position: absolute;
  inset: 0;
  /* The invitation cards' own architecture wash, reused so the closing reads
     as the back of the same card rather than a new surface. */
  background: url("assets/generated/card_wash_b.png") center / cover no-repeat;
  opacity: .085;
  mix-blend-mode: multiply;
  z-index: -2;
}
.closing::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 80% at 50% 10%, transparent 40%, var(--dusk-deep) 100%);
  z-index: -1;
}
/* soften the seam where the paper meets the night */
.closing__seam {
  position: absolute;
  inset: 0 0 auto 0;
  height: 4.5rem;
  /* Two papers a shade apart need a hand's width of transition, not a full
     screen of it. The long ramp belonged to the dark panel. */
  background: linear-gradient(
    var(--paper) 0%,
    rgba(242,241,237,.55) 45%,
    transparent 100%);
  z-index: 1;
  pointer-events: none;
}

/* The families list now sits on the closing's paper, which is a shade deeper
   than the one these greys were picked against - --ink-soft fell to 4.27:1
   there. These are relatives' names on a wedding invitation; they get the
   closing's own body ink instead. */
.closing .pairs li,
.closing .awaiting li,
.closing .awaiting--solo,
.closing .awaiting--kids { color: var(--dusk-soft); }
.closing .hosts__heading { color: var(--dusk-ink); }
.closing .hosts__sub { color: var(--dusk-faint); }

.closing .eyebrow { color: var(--gold-ink); }
.closing .rule { color: var(--gold); }

.savedate { text-align: center; padding-top: clamp(6rem, 16vw, 10rem); }

/* save the date -----------------------------------------------------------
   A leaf torn from the wedding month, the way a printed save-the-date card
   sets it: the month in the script face, the days in a plain serif grid, and
   the two days the couple is asking for marked. */
.cal {
  margin: 0 auto;
  max-width: 22rem;
  /* No card: no ground, no keyline, no shadow. The calendar is printed ON the
     closing's paper, which is the only surface this half of the site has. */
  padding: 0 clamp(1.2rem, 4vw, 1.8rem);
}
.cal__month {
  font-family: var(--script);
  font-size: clamp(2.2rem, 10vw, 3rem);
  line-height: 1;
  color: var(--dusk-ink);
  margin: 0 0 clamp(1rem, 4vw, 1.4rem);
  text-align: center;
}
/* Rasa, not Great Vibes: the script face carries no Gujarati. */
html[data-lang="gu"] .cal__month {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(1.7rem, 7.5vw, 2.2rem);
}

.cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: .3rem .1rem;
  align-items: center;
  justify-items: center;
}
.cal__head {
  font-family: var(--serif);
  font-size: .78rem;
  letter-spacing: .1em;
  color: var(--dusk-soft);
  padding-bottom: .4rem;
  border-bottom: 1px solid rgba(58,51,42,.14);
  width: 100%;
  text-align: center;
}
html[data-lang="gu"] .cal__head { letter-spacing: 0; font-size: .72rem; }

.cal__day {
  font-family: var(--serif);
  font-size: clamp(.88rem, 3.6vw, 1rem);
  color: var(--dusk-ink);
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
  width: 1.9em; height: 1.9em;
  display: grid; place-items: center;
}
/* The days being asked for. A filled disc rather than the reference's heart:
   two adjacent days read as a pair of hearts, which is a lot of heart. */
.cal__day--on {
  background: var(--dusk-accent);
  color: #FBF6EE;
  border-radius: 50%;
  font-weight: 500;
}

.cal__motif {
  display: block;
  width: 100%;
  max-width: 15rem;
  margin: clamp(.6rem, 3vw, 1rem) auto 0;
  filter: saturate(.92) contrast(.97);
}

/* countdown ------------------------------------------------------------- */
.countdown { text-align: center; padding-bottom: clamp(4rem, 10vw, 7rem); }
.countdown__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.8rem, 7vw, 3rem);
  margin: 0;
  color: var(--dusk-ink);
}
.countdown__title em {
  display: block;
  font-style: italic;
  color: var(--dusk-accent);
  font-size: 1.25em;
}


.countdown__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(.3rem, 1.6vw, .9rem);
  max-width: 40rem;
  margin: clamp(1.5rem, 5vw, 2.6rem) auto 0;
}
.locket { position: relative; }
.locket img { width: 100%; }
.locket__num {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  gap: .1rem;
  padding-bottom: 18%;   /* the pearls hang below the oval */
}
.locket__num b {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.7rem, 7.6vw, 3rem);
  line-height: 1;
  color: #7A2E48;
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
}
.locket__num span {
  font-size: clamp(.55rem, 2.1vw, .8rem);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(106,97,87,.85);
  margin-top: .15em;
}

.signoff { margin-top: clamp(2.5rem, 7vw, 4rem); }
.signoff__names {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.7rem, 7vw, 2.7rem);
  color: var(--dusk-ink);
  margin: 0;
}
.signoff__names em { font-style: italic; color: var(--gold); font-size: .62em; }
.signoff__date {
  font-size: .72rem; letter-spacing: .28em; text-transform: uppercase;
  color: var(--gold-ink); margin: .9rem 0 0;
}
.signoff__line {
  font-family: var(--serif); font-style: italic;
  color: var(--dusk-soft); margin: .7rem 0 0;
}

/* compliments ----------------------------------------------------------- */
.compliments {
  padding: clamp(3rem, 8vw, 4.5rem) var(--gutter);
  text-align: center;
  background: var(--dusk-deep);
  color: var(--dusk-soft);
  border-top: 1px solid rgba(58,51,42,.12);
}
.compliments__heading {
  font-size: .66rem; letter-spacing: .3em; text-transform: uppercase;
  color: var(--gold-ink); margin: 0 0 2rem;
}
.compliments__list {
  display: grid; gap: 1.8rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  max-width: 40rem; margin-inline: auto;
}
.compliments__name {
  font-family: var(--serif); font-size: 1.3rem; color: var(--dusk-ink); margin: 0;
}
.compliments__city {
  font-size: .74rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--dusk-faint); margin: .35rem 0 0;
}

.footer {
  padding: 2rem var(--gutter) 2.6rem;
  text-align: center;
  background: var(--dusk-deep);
  color: var(--dusk-faint);
  font-size: .7rem;
  letter-spacing: .14em;
}

/* --------------------------------------------------------------------------
   Floating controls
   -------------------------------------------------------------------------- */

.controls {
  position: fixed;
  right: clamp(.6rem, 2vw, 1.2rem);
  bottom: clamp(.9rem, 3vh, 1.6rem);
  z-index: 40;
  display: grid;
  gap: .6rem;
}
.ctrl {
  width: 2.7rem; height: 2.7rem;
  border-radius: 50%;
  border: 1px solid var(--hairline);
  background: rgba(255,255,255,.88);
  backdrop-filter: blur(6px);
  color: var(--brass);
  cursor: pointer;
  display: grid; place-items: center;
  transition: background .2s, color .2s, border-color .2s, scale .15s;
}
.ctrl:hover { scale: 1.06; }
.ctrl:active { scale: .96; }
.ctrl svg { width: 1.05rem; height: 1.05rem; }
.ctrl[aria-pressed="true"] { background: var(--brass); color: #fff; border-color: var(--brass); }
/* invert over the night sections */
body.is-closing .ctrl {
  background: rgba(255,253,247,.9);
  border-color: rgba(58,51,42,.18);
  color: var(--gold);
}
body.is-closing .ctrl[aria-pressed="true"] { background: var(--gold); color: #FBF7EE; }

/* --------------------------------------------------------------------------
   Scroll reveal
   -------------------------------------------------------------------------- */

.reveal {
  opacity: 0;
  translate: 0 24px;
  transition: opacity .7s cubic-bezier(.22,.61,.36,1),
              translate .7s cubic-bezier(.22,.61,.36,1);
  transition-delay: var(--d, 0ms);
  will-change: opacity, translate;
}
.reveal.is-in { opacity: 1; translate: 0 0; }

/* Garlands drop in from above rather than rising. */
.deco--garland-a, .deco--garland-b,
.deco--garland-c, .deco--garland-d {
  transform-origin: top center;
}
.reveal--drop { opacity: 0; translate: 0 -40px; }
.reveal--drop.is-in { opacity: 1; translate: 0 0; }

/* --------------------------------------------------------------------------
   Motion off
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal, .reveal--drop {
    opacity: 1 !important;
    translate: none !important;
    transition: none !important;
  }
  .scroll-cue::after { animation: none; }
  .gallery__slides img { transition: none; }
  * { scroll-behavior: auto !important; }
}

/* ---- language toggle ---------------------------------------------------
   Fixed top right, above the intro overlay so the language can be changed
   before the invitation has even opened. */
.topbar {
  position: fixed;
  top: clamp(.6rem, 2vh, 1.1rem);
  right: clamp(.6rem, 2vw, 1.2rem);
  z-index: 120;                      /* .intro is 100, .controls 40 */
}
.lang {
  appearance: none;
  cursor: pointer;
  font-family: var(--sans);
  font-size: .78rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding: .5rem .85rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: rgba(255,255,255,.86);
  backdrop-filter: blur(6px);
  box-shadow: 0 1px 2px rgba(61,52,40,.08), 0 8px 20px -14px rgba(61,52,40,.5);
  transition: background .25s ease, color .25s ease, border-color .25s ease;
}
/* the Gujarati label is a script, not a set of capitals */
.lang--gu {
  font-family: var(--gujarati);
  font-size: .92rem;
  letter-spacing: 0;
  text-transform: none;
  padding: .34rem .85rem .44rem;
}
.lang:hover, .lang:focus-visible {
  background: #fff;
  color: var(--ink);
  border-color: color-mix(in srgb, var(--ink) 28%, transparent);
}
.lang:focus-visible { outline: 1px solid var(--brass); outline-offset: 3px; }

@media (max-width: 480px) {
  .lang { font-size: .72rem; padding: .42rem .7rem; }
  .lang--gu { font-size: .85rem; }
}

/* ---- Gujarati typography ------------------------------------------------
   Gujarati must not be letter-spaced. The tracking these small-caps labels use
   for Latin pulls a matra away from the consonant it belongs to, which reads
   as a spelling error rather than a style. `text-transform: uppercase` does
   nothing to the script and mangles any Latin word standing beside it, so both
   come off together. `.lang` is deliberately not in this list: its Gujarati
   state has its own rule, and its English state must stay in caps. */
html[data-lang="gu"] .eyebrow,
html[data-lang="gu"] .intro__skip,
html[data-lang="gu"] .hero__meta,
html[data-lang="gu"] .scroll-cue,
html[data-lang="gu"] .invite__when,
html[data-lang="gu"] .locket__num span,
html[data-lang="gu"] .signoff__date,
html[data-lang="gu"] .compliments__heading,
html[data-lang="gu"] .compliments__city,
html[data-lang="gu"] .hosts__heading {
  letter-spacing: .015em;
  text-transform: none;
}
/* Those labels lean on tracking for their weight; give it back as size. */
html[data-lang="gu"] .eyebrow,
html[data-lang="gu"] .scroll-cue,
html[data-lang="gu"] .compliments__heading { font-size: .88rem; }
html[data-lang="gu"] .locket__num span { font-size: .78rem; }
