/* ============================================================
   MOTION LAYER
   ------------------------------------------------------------
   Styles that exist only to support the animation system. No
   layout, colour or typography of the finished invitation is
   touched here.
   ============================================================ */

/* ---- parallax hook -----------------------------------------------------
   The temple and the couple are centred with a percentage translate. GSAP
   would resolve that to pixels and freeze the centring at one viewport
   width, so the scroll offset rides in through a variable instead and CSS
   keeps recomposing the transform itself. Default 0px: with the motion
   layer absent, these render exactly as they always did. */

.events__temple {
  transform: translateX(-50%) translateY(var(--sp-par, 0px));
}

.events__couple {
  transform: translateX(-48%) translateY(var(--sp-par, 0px));
}

/* ---- RSVP blessing lines ------------------------------------------------
   Two spans in place of a <br>, so the blessing can be revealed a line at
   a time. Block display reproduces the line break exactly. */

.venue__rsvp-line {
  display: block;
}

/* ---- hero: names written toward each other --------------------------------
   hero-animations.js splits each name into letters before the sequence
   starts. The name itself then stops animating and stays visible, and its
   letters carry the reveal instead: Aarav writes left to right, Ananya
   right to left, so the two names close in on "weds".

   `body` in the selector lifts these to the same specificity as the
   original body.is-idle / body.is-revealing rules; loading later wins. */

body .hero__name.hero__name--split {
  opacity: 1;
  animation: none;
}

.hero__char {
  display: inline-block;
}

body.is-idle .hero__char {
  opacity: 0;
}

body.is-revealing .hero__char {
  animation: heroCharInk 0.95s var(--ease-ink) calc(var(--d, 1.6s) + var(--i, 0) * 65ms) both;
}

.hero__name--groom { --d: 1.55s; }
.hero__name--bride { --d: 2s; }

/* "weds" now sits between the two writings rather than inside them. */
body.is-revealing .hero__joiner {
  animation-delay: 1.95s;
}

@keyframes heroCharInk {
  from {
    opacity: 0;
    transform: translateY(0.28em) rotate(-5deg);
    filter: blur(5px);
  }
  60% {
    filter: blur(0);
  }
  to {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

/* ---- hero: scroll exit --------------------------------------------------
   The artwork grows from its base as the hero scrolls away, which carries
   it a few percent past both side edges. Clip that sideways only — the
   hero has nothing that should overflow vertically, and `clip` (unlike
   `hidden`) does not turn the section into a scroll container. */

.hero {
  overflow-x: clip;
}

/* ---- invitation: scroll owns the crossing --------------------------------
   Set by invitation-animations.js. Two classes plus the element match the
   specificity of the .is-in crossing rules, and this sheet loads last. */

.invitation.invitation--scrubbed .invitation__hand {
  animation: none;
}

/* ---- invitation: bells that feel the scroll -------------------------------
   `rotate` composes with the transform the CSS sway already animates, around
   the same transform-origin, so the sway keeps running underneath. */

.invitation__bell {
  rotate: var(--swing, 0deg);
}

/* ---- invitation: the flare where the fingertips meet ---------------------
   Centred on the gap between the two tips, using the same variables that
   place the hands, so it sits on the touch at every screen size. */

.invitation__spark {
  position: absolute;
  z-index: 3;
  left: calc((var(--tip-parvati) + var(--tip-shiva)) / 2);
  top: calc(0.1515 * var(--parvati-w));
  width: calc(64 * var(--u));
  height: calc(64 * var(--u));
  translate: -50% -50%;
  pointer-events: none;
}

.invitation__spark-core,
.invitation__spark-ring {
  position: absolute;
  border-radius: 50%;
  opacity: 0;
  will-change: transform, opacity;
}

.invitation__spark-core {
  inset: 18%;
  background: radial-gradient(
    circle,
    #fffaf0 0%,
    rgba(255, 238, 186, 0.95) 20%,
    rgba(222, 178, 72, 0.6) 46%,
    rgba(222, 178, 72, 0) 72%
  );
}

.invitation__spark-ring {
  inset: 30%;
  border: 1.5px solid rgba(189, 151, 54, 0.8);
}

/* ---- text for assistive tech only -----------------------------------------
   Used where an animation splits a word into visual pieces (the rolling
   year digits), so it is still read once, whole. */

.sp-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- story: the copy, word by word ----------------------------------------
   story-animations.js wraps each word. The paragraph stops animating — but
   stays hidden by `.story--live .rv:not(.is-in)` until it is read — and its
   words rise instead, carrying the paragraph's own --rv-delay. */

.story--live .story__copy.story__copy--split.is-in {
  animation: none;
}

.story__word {
  display: inline-block;
}

.story--live .story__copy--split.is-in .story__word {
  animation: storyWord 0.7s var(--story-ease) calc(var(--rv-delay, 0s) + var(--i, 0) * 16ms) both;
}

@keyframes storyWord {
  from {
    opacity: 0;
    transform: translateY(0.6em);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ---- story: chapters on a gold thread -------------------------------------
   A hairline runs from the first chapter to the last, just beneath the
   years, with a diamond under each. It draws itself as the guest reads, and
   each chapter develops when the thread reaches it. */

.story__thread,
.story__node {
  position: absolute;
  pointer-events: none;
}

.story__thread {
  height: 1px;
  background: linear-gradient(90deg,
    rgba(189, 151, 54, 0) 0%,
    rgba(189, 151, 54, 0.85) 6%,
    rgba(189, 151, 54, 0.85) 94%,
    rgba(189, 151, 54, 0) 100%);
  transform-origin: 0 50%;
}

.story__node {
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  background: #bd9736;
  rotate: 45deg;
  box-shadow: 0 0 0 3px rgba(248, 240, 221, 0.9);
}

/* The year rolls in digit by digit, like a departure board. */
.story__digit {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}

.story__digit > span {
  display: inline-block;
}

/* ---- blessing: the aarti ------------------------------------------------
   blessing-animations.js adds story--aarti and conducts the whole reveal.

   The deities are masked by three layers, added together:
     the lamp     a soft circle wherever the aarti light is (--lx --ly --ls)
     the sanctum  a widening glow of accumulated light (--reach)
     presence     38% everywhere, so the deities are always there, never in the dark
   The mask is removed once the reveal completes. */

.story__shrine {
  position: relative;
  z-index: 1;
}

/* The mask is on the whole shrine — the deities, both Nandi and both kolams — so the
   lamp and the sanctum's light reveal all of it, not the deities alone. Positions are
   in px of the shrine, written by blessing-animations.js:
     --lx --ly --ls   the lamp: where it is, and how far its light reaches
     --rx --ry        the sanctum's centre (the deities)
     --rw --rh        how far the sanctum's gathered light has spread */
.story.story--aarti .story__shrine {
  --lx: 50%;
  --ly: 110%;
  --ls: 1px;
  --rx: 50%;
  --ry: 55%;
  --rw: 1px;
  --rh: 1px;
  -webkit-mask-image:
    radial-gradient(circle var(--ls) at var(--lx) var(--ly), #000 30%, transparent 100%),
    radial-gradient(var(--rw) var(--rh) at var(--rx) var(--ry), #000 55%, transparent 100%),
    linear-gradient(rgba(0, 0, 0, 0.38), rgba(0, 0, 0, 0.38));
  mask-image:
    radial-gradient(circle var(--ls) at var(--lx) var(--ly), #000 30%, transparent 100%),
    radial-gradient(var(--rw) var(--rh) at var(--rx) var(--ry), #000 55%, transparent 100%),
    linear-gradient(rgba(0, 0, 0, 0.38), rgba(0, 0, 0, 0.38));
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.story.story--aarti .story__shrine.is-revealed {
  -webkit-mask-image: none;
  mask-image: none;
}

/* Inside the aarti every piece of the shrine is simply there, lit only by the lamp:
   none of their own entrances (the rise, the kolam's circular draw, Nandi's sweep)
   and none of them held back — the shrine's mask is the whole reveal. */
.story.story--aarti .story__shrine > .rv,
.story.story--aarti .story__shrine > .rv.is-in {
  opacity: 1;
  animation: none;
  -webkit-mask-image: none;
  mask-image: none;
}

/* The sanctum is dim until the lamp is lit. Behind the shrine only. */
.story__dim {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(ellipse 70% 80% at 50% 55%, rgba(3, 12, 28, 0.42), rgba(3, 12, 28, 0.78));
}

/* The halo of light behind the deities, and the lamp itself. Both are
   children of the devotional field rather than the shrine: the shrine is its
   own stacking context, and `screen` inside it would have nothing blue to
   brighten. --k scales them with the drawing (1 at a laptop's 389px). */
.story__prabha,
.story__light {
  position: absolute;
  left: 0;
  top: 0;
  border-radius: 50%;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  will-change: transform, opacity;
}

.story__prabha {
  z-index: 0;
  background: radial-gradient(closest-side,
    rgba(255, 226, 160, 0.6),
    rgba(255, 196, 96, 0.24) 45%,
    rgba(255, 196, 96, 0) 100%);
}

.story__light {
  z-index: 2;
  width: calc(var(--k, 1) * 10px);
  height: calc(var(--k, 1) * 10px);
  margin: calc(var(--k, 1) * -5px) 0 0 calc(var(--k, 1) * -5px);
  background: radial-gradient(circle, #fffdf3 0%, #ffe9b0 45%, rgba(255, 200, 110, 0) 100%);
  box-shadow:
    0 0 calc(var(--k, 1) * 14px) calc(var(--k, 1) * 5px) rgba(255, 232, 180, 0.85),
    0 0 calc(var(--k, 1) * 48px) calc(var(--k, 1) * 18px) rgba(255, 186, 84, 0.42),
    0 0 calc(var(--k, 1) * 130px) calc(var(--k, 1) * 60px) rgba(255, 170, 60, 0.16);
}

/* ---- flowers on the air (flowing-flowers.js) -----------------------------
   One layer per section, clipped to it and never in the way of a tap. It floats
   ABOVE the section's artwork, as the Wada's does: at the bottom of the stack most
   of them drifted behind the hands, the invitation and the shrine, so the air was
   thick in one section and empty in the next. They keep to the margins, clear of
   the words. The flowers get their own compositor layers only while their section
   is near the screen. */
.sp-flowers {
  position: absolute;
  inset: 0;
  z-index: 20;
  overflow: hidden;
  pointer-events: none;
}

.sp-flowers__f {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  height: auto;
  transform-origin: 50% 50%;
  /* a whisper of shadow, so a flower lies on the paper rather than printed into it */
  filter: drop-shadow(0 1px 1.5px rgba(96, 52, 14, 0.28));
}

.sp-flowers.is-live .sp-flowers__f {
  will-change: transform;
}

/* ---- reduced motion -----------------------------------------------------
   The JS modules already bail out, but the ambient CSS loops that predate
   this layer are stopped here too: bells, petals and the damru. Entrance
   transitions are left to the existing per-section reduced-motion rules,
   which already resolve to a plain fade. */

@media (prefers-reduced-motion: reduce) {
  .invitation__bell,
  .events__bell,
  .events__petal,
  .invitation__damru {
    animation: none !important;
  }

  .events__temple,
  .events__couple {
    --sp-par: 0px;
  }
}
