/* Author: Claude (Anthropic AI), written 2026-10-06. */
/* Save-the-date reveal landing. Brick-dominant, Fraunces, candlelit.
   Scoped under body.landing so it never touches the Dash/DMC pages. */

/* border-box everywhere on the landing so padding is included in sizes — without
   it, .stage's min-height:100svh + vertical padding overflows the viewport and
   forces a scrollbar. Scoped to body.landing (this sheet is auto-loaded on Dash
   pages too, which must not be touched). */
body.landing,
body.landing *,
body.landing *::before,
body.landing *::after { box-sizing: border-box; }

body.landing {
  /* --- palette: raw stone field, deep draped-cloth colour, candlelit gilt. ---
     Stone is the cold neutral ground; colour enters only as garment/velvet
     accents (wine, sapphire); gold reads as gilt on stone; light is candle. */
  --stone-pale: #8f8578;   /* lit limestone */
  --stone: #5f584e;        /* mid stone */
  --stone-deep: #332f2a;   /* shadowed stone */
  --stone-black: #1c1917;  /* deepest */
  --wine: #6d1f2b;         /* bordeaux velvet (primary garment/drape) */
  --sapphire: #1c3a63;     /* deep blue cloth / dusk */
  --gold: #c9a24b;         /* gilt */
  --candle: #ffcf9e;       /* warm candle highlight */
  --amber: #ffb169;        /* candle glow */
  --parchment: #efe7d6;    /* bone / parchment text */

  margin: 0;
  min-height: 100svh;
  background: var(--stone-black);
  color: var(--parchment);
  font-family: "Fraunces", Georgia, serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Single-screen landing: never scroll. The content is absolutely positioned
     within a 100svh stage, so there's nothing to scroll to — locking overflow
     (on both axes, before and after reveal) is the robust way to kill the bar on
     phone and desktop alike. svh (not dvh) keeps it stable as mobile browser
     chrome shows/hides. */
  overflow: hidden;
}

/* ---------- the rich ground ---------- */
.stage {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: max(6svh, 2rem) 1.5rem;
  /* raw stone, candle pool rising from below; grain adds the stone's tooth */
  background:
    radial-gradient(130% 100% at 50% 120%, var(--stone) 0%, var(--stone-deep) 52%, var(--stone-black) 100%);
  isolation: isolate;
  overflow: hidden; /* clip the drifting glow/grain so it can't inflate scroll */
}

/* animated mesh-gradient background (WebGL2, shader-bg.js). Sits above the
   stage's stone gradient (the no-WebGL / reduced-motion fallback) and below the
   candle pools, grain, frame and content. Fades in on reveal via .on. */
.bg-shader {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 1.1s ease .1s;
}
.bg-shader.on { opacity: 1; }

/* candle pools — emission, not fill. Drift + flicker. */
.stage::before {
  content: "";
  position: absolute; inset: -12%;
  pointer-events: none; z-index: 1;
  background:
    radial-gradient(260px 320px at 50% 96%, rgba(255,177,105,.34), transparent 70%),
    radial-gradient(200px 260px at 26% 90%, rgba(255,207,158,.22), transparent 72%),
    radial-gradient(220px 280px at 76% 94%, rgba(228,98,31,.20), transparent 72%);
  filter: blur(8px);
  animation: drift 15s ease-in-out infinite alternate, flicker 3.4s ease-in-out infinite;
}
/* film grain — cheapest "this cost money" cue; also kills gradient banding */
.stage::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none; z-index: 2; opacity: .45;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

@keyframes drift {
  from { transform: translate3d(-1.5%, 1%, 0) scale(1.02); }
  to   { transform: translate3d(2%, -1.5%, 0) scale(1.05); }
}
@keyframes flicker {
  0%,100% { opacity: .9; } 25% { opacity: 1; } 42% { opacity: .84; }
  63% { opacity: .97; } 80% { opacity: .9; }
}

/* ---------- thin gold hairline frame (engraved-invitation nod) ---------- */
.frame {
  position: absolute; inset: 14px; z-index: 3;
  border: 1px solid rgba(217,164,65,.55);
  border-radius: 3px;
  pointer-events: none;
}
.frame::before {
  content: ""; position: absolute; inset: 4px;
  border: 1px solid rgba(217,164,65,.22); border-radius: 2px;
}

/* ---------- centred composition ----------
   Names are pinned dead-centre of the viewport in BOTH the spree and the
   landing (see .spree-names) so the handoff is a morph, not a jump. The
   eyebrow and footer float to the top/bottom edges and never shift the names. */
.content {
  position: absolute; inset: 0; z-index: 3; text-align: center;
  /* starts hidden; settles in once the spree releases (see .revealed .content) */
  opacity: 0; transform: translateY(14px) scale(.985);
}

.eyebrow {
  position: absolute; top: max(6svh, 2rem); left: 0; right: 0; margin: 0;
  font-family: "Inter", system-ui, sans-serif;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .34em;
  font-size: clamp(.6rem, 2.6vw, .72rem);
  color: var(--gold);
}

.footer {
  position: absolute; bottom: max(5svh, 1.6rem); left: 0; right: 0;
}

/* The wordmark. Identical metrics in both the spree overlay and the landing so
   the handoff morphs rather than jumps — the shared rule is what keeps them in
   lockstep; only the fill differs (gilt foil here, knockout white on .spree-names). */
.names, .spree-names {
  position: absolute; inset: 0; margin: 0;
  display: grid; place-items: center;
  font-family: "Fraunces", Georgia, serif;
  font-weight: 500;
  line-height: .82;
  letter-spacing: -.01em;
  font-size: clamp(3rem, 16vw, 6.5rem);
}
.names .amp, .spree-names .amp { font-style: italic; font-weight: 400; }

.names {
  /* gilt-leaf foil swept across the glyphs (gilded inscription on stone) */
  background: linear-gradient(100deg,
    #7a5e22 0%, var(--gold) 20%, #efd79a 42%, #fff7e4 50%,
    #efd79a 58%, var(--gold) 80%, #7a5e22 100%);
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: sweep 7s ease-in-out infinite;
}
@keyframes sweep {
  0%,100% { background-position: 0% 0; } 50% { background-position: 100% 0; }
}

/* per-guest greeting (shown only for a named guest); placement refined with the
   reveal choreography later */
.greeting {
  font-family: "Inter", system-ui, sans-serif;
  text-transform: uppercase;
  letter-spacing: .24em;
  font-size: clamp(.6rem, 2.6vw, .72rem);
  color: var(--candle);
  margin: 0 0 .8rem;
}

.marrying {
  font-style: italic;
  font-size: clamp(1rem, 4.6vw, 1.4rem);
  color: var(--parchment);
  opacity: .85;
  margin: 0 0 1rem;
}

.date {
  font-family: "Inter", system-ui, sans-serif;
  text-transform: uppercase;
  letter-spacing: .3em;
  font-size: clamp(.78rem, 3.4vw, .95rem);
  color: var(--candle);
  margin: 0 0 .3rem;
}
.location {
  font-family: "Inter", system-ui, sans-serif;
  text-transform: uppercase;
  letter-spacing: .24em;
  font-size: clamp(.58rem, 2.6vw, .68rem);
  color: rgba(242,228,207,.6);
  margin: 0 0 1.4rem;
}

/* ---------- countdown ---------- */
.countdown {
  display: flex; justify-content: center; gap: clamp(.9rem, 5vw, 1.8rem);
  margin: 0;
}
.cd-unit { display: flex; flex-direction: column; align-items: center; }
.cd-num {
  font-weight: 500; font-variant-numeric: tabular-nums;
  font-size: clamp(1.7rem, 8vw, 2.6rem); line-height: 1;
  color: var(--gold);
}
.cd-lab {
  font-family: "Inter", system-ui, sans-serif;
  text-transform: uppercase; letter-spacing: .22em;
  font-size: clamp(.5rem, 2.2vw, .6rem);
  color: rgba(242,228,207,.55); margin-top: .5rem;
}

/* ---------- add to calendar ----------
   A gilt-hairline plate echoing the frame; a light stone tint keeps the label
   legible over the brighter parts of the colour field. */
.add-cal {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; /* thumb-sized tap target */
  margin-top: 1.4rem;
  /* trailing letter-spacing makes the label look off-centre; trim it on the right */
  padding: 0 calc(1.4rem - .26em) 0 1.4rem;
  font-family: "Inter", system-ui, sans-serif;
  font-weight: 500; text-transform: uppercase;
  letter-spacing: .26em;
  font-size: clamp(.58rem, 2.6vw, .68rem);
  color: var(--candle);
  text-decoration: none;
  background: rgba(28,25,23,.28);
  border: 1px solid rgba(217,164,65,.55);
  border-radius: 2px;
  transition: background .25s ease, border-color .25s ease;
}
.add-cal:hover, .add-cal:active {
  background: rgba(28,25,23,.45);
  border-color: rgba(217,164,65,.9);
}
.add-cal:focus-visible {
  outline: 1px solid rgba(217,164,65,.7);
  outline-offset: 3px;
}

/* ---------- flash-spree overlay ---------- */
.spree {
  position: fixed; inset: 0; z-index: 10;
  background: var(--stone-black);
}
.spree-frame {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
  opacity: 0;
}
.spree-frame.on { opacity: 1; }

/* Spree exit, leaving the spree's stone-black ground for the tap gate. Normally
   the vignette closes over the held frame + wordmark and landing.js cuts them
   away beneath it, unseen (.spree-closed). Without the vignette (no WebGL2) they
   fade out instead (.spree-fading). The wordmark returns, restyled, on reveal. */
body.landing.spree-closed .spree-frame,
body.landing.spree-closed .spree-names { visibility: hidden; }
body.landing.spree-fading .spree-frame.on,
body.landing.spree-fading .spree-names {
  opacity: 0;
  transition: opacity .8s ease;
}

/* the wordmark over the spree — inverts against every frame (knockout look).
   Shared metrics live in the .names, .spree-names rule above. */
.spree-names {
  z-index: 2; padding: 0 1.5rem; text-align: center;
  color: #fff; mix-blend-mode: difference;
  pointer-events: none;
}
body.landing.revealed .spree {
  opacity: 0; transform: scale(1.04);
  transition: opacity .7s ease, transform 1.1s ease;
  pointer-events: none;
}

/* ---------- "tap to reveal" prompt ----------
   A full-screen button (above the spree) so a tap/click anywhere reveals; the
   label floats in the lower third. Keeps keyboard focus + a screen-reader label. */
.tap-prompt {
  position: fixed; inset: 0; z-index: 15;
  display: flex; align-items: flex-end; justify-content: center;
  padding-bottom: 22vh;
  margin: 0; border: 0; background: transparent; cursor: pointer;
  font-family: "Inter", system-ui, sans-serif;
  font-weight: 500; text-transform: uppercase;
  letter-spacing: .3em;
  font-size: clamp(.64rem, 2.8vw, .78rem);
  color: var(--candle);
  opacity: 0; pointer-events: none;
  transition: opacity .6s ease;
}
body.landing.awaiting-tap .tap-prompt {
  opacity: 1; pointer-events: auto;
  animation: tap-pulse 2.2s ease-in-out infinite;
}
.tap-prompt:focus-visible {
  outline: 1px solid rgba(217,164,65,.7);
  outline-offset: -8px;
  border-radius: 2px;
}
@keyframes tap-pulse { 0%,100% { opacity: .55; } 50% { opacity: 1; } }

/* ---------- spotlight vignette (top canvas of the sandwich) ----------
   A WebGL canvas (vignette.js) darkening everything below, with a transparent hole
   at the tap point that grows until the scene is revealed. Above everything (incl.
   the fading spree) so the spotlight appears right at the tap; pointer-events: none
   so clicks/mousemove pass through to the tap gate and the colour shader. JS toggles
   display while the one-shot animation runs. */
.vignette {
  position: fixed; inset: 0; z-index: 20;
  width: 100%; height: 100%;
  display: none; pointer-events: none;
}

/* content settles in once the spree releases */
body.landing.revealed .content {
  opacity: 1; transform: none;
  transition: opacity 1s ease .15s, transform 1.2s cubic-bezier(.16,.84,.3,1) .15s;
}

/* ---------- respect reduced motion: no spree, no drift, static reveal ---------- */
@media (prefers-reduced-motion: reduce) {
  .stage::before { animation: none; }
  .names { animation: none; }
  .spree { display: none; }
  .tap-prompt, .vignette { display: none; }
  .content { opacity: 1; transform: none; }
}
