/* ============================================================================
   EASTER CELEBRATION — evergreen, cream, gold foil, sunrise amber

   The palette is taken from the footage, not chosen in the abstract:
     the card film is a deep forest-green envelope with a cream card, gold foil
     lettering, a gold wax seal and white lilies;
     the scene film is Calvary at sunrise — three crosses against an amber sky.

   So evergreen is the ground colour (where a wedding site would use navy),
   cream is the ink, gold is the foil, and amber is the accent and the glow.
   It is meant to read as a printed order of service: restrained, quiet,
   unhurried. Not festive.
   ========================================================================== */

:root{
  /* evergreen field */
  --forest-950:#030806;
  --forest-900:#07110D;
  --forest-850:#0A1712;
  --forest-800:#0E2019;
  --forest-700:#143026;
  --forest-600:#1C4234;

  /* gold foil — the luminous variant, which never washes out on dark grounds */
  --gold:#C9A24B;
  --gold-hi:#E8CC86;
  --gold-pale:#F3E2B6;
  --gold-deep:#8A6524;
  --gold-line:rgba(201,162,75,.30);
  --gold-line-soft:rgba(201,162,75,.16);

  /* sunrise */
  --amber:#D98431;
  --amber-hi:#F0A959;
  --ember:#B4501C;

  /* ink — cream and ivory, off the card in the film */
  --ink:#F4EDDF;
  --ink-soft:rgba(244,237,223,.75);
  --ink-mute:rgba(244,237,223,.52);
  --ink-faint:rgba(244,237,223,.30);

  --ease:cubic-bezier(.22,1,.36,1);
  --serif:'Cormorant Garamond',Georgia,serif;
  --sans:'Jost',-apple-system,'Segoe UI',sans-serif;

  --maxw:560px;
  --maxw-wide:640px;

  /* keep in step with films.dissolveMs in js/config.js */
  --dissolve:1600ms;
}

*,*::before,*::after{box-sizing:border-box}

/* The `hidden` attribute is applied by the UA stylesheet as `display:none`,
   which ANY author `display` declaration silently overrides. Several things
   here are flex containers that are also toggled with `hidden` — the lightbox,
   the scroll hint, the RSVP confirmation, the whole gallery section — and
   without this every one of them would stay in the layout. The lightbox is
   `position:fixed; inset:0; z-index:90`, so it would sit invisibly over the
   entire page and swallow every click on the site, "Tap To Open" included.
   This rule has to beat all of them. */
[hidden]{ display:none !important }
html,body{margin:0;padding:0}
html{-webkit-text-size-adjust:100%; scroll-behavior:auto}

body{
  background:var(--forest-900);
  color:var(--ink);
  font-family:var(--serif);
  font-weight:300;
  line-height:1.6;
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%; display:block}
button{font:inherit; color:inherit; cursor:pointer; border:0; background:none}
a{color:var(--gold-hi); text-decoration:none}

html.intro-lock, html.intro-lock body{
  overflow:hidden !important; height:100%;
  touch-action:none; overscroll-behavior:none;
}

/* ── type primitives ─────────────────────────────────────────────────────── */
.eyebrow{
  font-family:var(--sans); font-weight:200;
  font-size:clamp(.6rem,2.4vw,.68rem);
  letter-spacing:.42em; text-indent:.42em; text-transform:uppercase;
  color:var(--gold-hi); opacity:.9; margin:0;
}
.micro{
  font-family:var(--sans); font-weight:200;
  font-size:clamp(.6rem,2.3vw,.66rem);
  letter-spacing:.24em; text-indent:.24em; text-transform:uppercase;
  color:var(--ink-mute); margin:0;
}
.body{
  font-size:clamp(.95rem,3.7vw,1.02rem); line-height:1.78;
  color:var(--ink-soft); margin:0; font-weight:300;
}
.lede{
  font-size:clamp(1rem,3.9vw,1.1rem); line-height:1.75;
  color:var(--ink-soft); font-style:italic; margin:0; max-width:48ch;
}

/* The display face. Deliberately NOT a wedding script — the card film sets
   "EASTER CELEBRATION" in widely letterspaced serif capitals, and following it
   is what keeps this reading as an order of service rather than a party.
   line-height and padding still matter: background-clip:text only paints
   inside the element box, so any ink that overshoots renders transparent. */
.display{
  font-family:var(--serif); font-weight:300;
  font-size:clamp(1.55rem,6.4vw,2.15rem);
  line-height:1.25;
  letter-spacing:.2em; text-indent:.2em; text-transform:uppercase;
  padding:.16em .1em .2em;         /* ink overshoots the box on all sides */
  margin:0;
}
.display.xl{ font-size:clamp(2rem,8.6vw,3rem); letter-spacing:.24em; text-indent:.24em }

/* Foil for dark grounds: luminous end-to-end, so the sweep shifts warmth and
   never brightness — contrast cannot drop out mid-cycle.
   NO `filter` on foil text. A filter promotes the element to its own
   composited layer, which Chrome rasterises once and then rescales; on type
   that is already painted through background-clip the result is permanently
   soft letterforms. */
.foil{
  background-image:linear-gradient(100deg,
    #B98F3C 0%, #D6B166 14%, #EFD79E 30%,
    #FFF6DC 46%, #EFD79E 62%, #D0A957 80%, #B98F3C 100%);
  background-size:260% 100%;
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
  animation:foilSweep 12s ease-in-out infinite;
  text-rendering:geometricPrecision;
}
@keyframes foilSweep{
  0%,100%{background-position:0% 50%}
  50%{background-position:100% 50%}
}

/* the gold rule, with a small lozenge at its centre — stationery, not chrome */
.rule{
  display:block; width:min(230px,62%); height:1px; position:relative;
  background:linear-gradient(90deg,transparent,var(--gold-line),transparent);
  margin:.2rem 0 .3rem;
}
.rule::after{
  content:""; position:absolute; left:50%; top:50%;
  width:5px; height:5px; transform:translate(-50%,-50%) rotate(45deg);
  background:var(--gold); box-shadow:0 0 8px rgba(201,162,75,.6);
}

/* ── stages ──────────────────────────────────────────────────────────────── */
.stage{
  position:relative; width:100%;
  min-height:62svh;                /* gradient grounds may hug their content */
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center; overflow:hidden;
  padding:clamp(3.4rem,9vh,5rem) clamp(1.4rem,6vw,4rem);
}
.stage>.inner{
  max-width:var(--maxw); width:100%; position:relative; z-index:2;
  display:flex; flex-direction:column; align-items:center;
  gap:clamp(.5rem,1.5vh,.85rem);
}
.stage>.inner.wide{ max-width:var(--maxw-wide) }

/* Three-pass evergreen grounds: a cool bloom above, a warm sunrise wash from
   one lower corner, and the field itself. The amber is what stops the green
   reading as corporate. */
.bg-forest{
  background:
    radial-gradient(92% 60% at 50% 0%,   rgba(34,84,64,.55) 0%, rgba(34,84,64,0) 64%),
    radial-gradient(120% 78% at 12% 100%, rgba(217,132,49,.13) 0%, rgba(217,132,49,0) 58%),
    linear-gradient(180deg,#0A1712 0%,#0E2019 48%,#071009 100%);
}
.bg-forest-deep{
  background:
    radial-gradient(88% 56% at 50% 4%,   rgba(26,66,50,.5) 0%, rgba(26,66,50,0) 62%),
    radial-gradient(130% 84% at 88% 100%, rgba(201,162,75,.10) 0%, rgba(201,162,75,0) 60%),
    linear-gradient(180deg,#07110D 0%,#0A1913 52%,#040A07 100%);
}
.bg-forest-warm{
  background:
    radial-gradient(96% 62% at 50% 2%,   rgba(40,96,72,.44) 0%, rgba(40,96,72,0) 66%),
    radial-gradient(120% 80% at 20% 96%, rgba(217,132,49,.18) 0%, rgba(217,132,49,0) 58%),
    linear-gradient(180deg,#0B1810 0%,#123024 50%,#080F0B 100%);
}

/* a woven tooth you feel more than see — laid paper, not a texture */
.stage::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  opacity:.55;
  background-image:
    repeating-linear-gradient(90deg,rgba(255,255,255,.014) 0 1px,transparent 1px 3px),
    repeating-linear-gradient(0deg, rgba(0,0,0,.05)      0 1px,transparent 1px 3px);
}
/* feathered seam so neighbouring sections melt instead of banding */
.stage::after{
  content:""; position:absolute; left:0; right:0; bottom:0;
  height:clamp(52px,10vh,110px); z-index:0; pointer-events:none;
  background:linear-gradient(180deg,rgba(3,8,6,0),rgba(3,8,6,.55));
}

/* ── reveals ─────────────────────────────────────────────────────────────── */
.reveal{
  opacity:0; transform:translateY(18px); filter:blur(4px);
  transition:opacity 1.4s var(--ease), transform 1.7s var(--ease), filter 1.4s var(--ease);
  will-change:opacity,transform;
}
/* `filter:none`, NOT `blur(0)`. A zero-radius blur is still an active filter,
   so the element keeps its composited layer and gold foil headings stay
   permanently soft. `none` drops the layer and the type snaps back sharp. */
.reveal.in{ opacity:1; transform:none; filter:none; will-change:auto }

/* ══════════════════ 1 · OPENING ══════════════════ */
.opening-scene{
  position:relative; height:100svh; height:100dvh; overflow:hidden;
  background:#030806; cursor:pointer;
}
.opening-sticky{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; overflow:hidden }
.opening-video{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; pointer-events:none; background:#030806;
}
#sceneVideo{ z-index:1 }
/* A long, slow dissolve. The card film fades to black on its own in the last
   half second, so the two fades overlap and the hand-off reads as one
   continuous darkening into the sunrise rather than a cut. */
#cardVideo.card-video{ z-index:2; transition:opacity var(--dissolve) cubic-bezier(.42,0,.35,1) }
#cardVideo.card-video.faded{ opacity:0 }

/* Graded from the footage. The top band was deepened when the hero type was
   enlarged: the bigger title reaches down to ~19% of frame, where the brightest
   pixel is 137, and the foil's darkest stop needs the ground at 70 or below to
   hold 3:1. That works out at .49 alpha, so the stops below clear it with
   margin. Darkening there costs nothing — it is storm cloud, not subject. The scene is a bright amber band across the middle
   with dark cloud above and dark hillside below; these gradients deepen the
   two dark bands where the type sits and leave the sky alone. */
.hero-vignette{
  position:absolute; inset:0; z-index:3; pointer-events:none;
  background:
    radial-gradient(120% 78% at 50% 46%, rgba(0,0,0,0) 40%, rgba(3,8,6,.6) 100%),
    linear-gradient(180deg,
      rgba(3,8,6,.8) 0%, rgba(3,8,6,.62) 14%, rgba(3,8,6,.46) 22%, rgba(3,8,6,0) 36%,
      rgba(3,8,6,0) 56%, rgba(3,8,6,.55) 74%, rgba(3,8,6,.9) 100%);
}
/* Portrait needs a deeper floor and a deeper lid: the crosses sit higher in
   frame (33–58%) and the type has more room above and below them. */
html:not(.is-wide) .hero-vignette{
  background:
    radial-gradient(120% 70% at 50% 42%, rgba(0,0,0,0) 42%, rgba(3,8,6,.5) 100%),
    linear-gradient(180deg,
      rgba(3,8,6,.82) 0%, rgba(3,8,6,.6) 18%, rgba(3,8,6,.44) 25%, rgba(3,8,6,0) 36%,
      rgba(3,8,6,0) 55%, rgba(3,8,6,.5) 68%, rgba(3,8,6,.9) 82%, rgba(3,8,6,.98) 100%);
}

/* ── buffering gate ──────────────────────────────────────────────────────
   Both films are downloaded in full before the guest can open anything, so
   this shows what is happening meanwhile. The gate is safe to make strict
   ONLY because readiness cannot hang — see initOpening() in main.js. */
.film-loader{
  position:absolute; left:50%; bottom:clamp(128px,23vh,196px);
  transform:translateX(-50%); z-index:7;
  display:flex; flex-direction:column; align-items:center; gap:.7rem;
  pointer-events:none;                     /* never swallows a tap */
  transition:opacity .7s var(--ease), transform .7s var(--ease);
}
.film-loader.gone{ opacity:0; transform:translateX(-50%) translateY(6px) }
.loader-ring{ width:clamp(46px,11vw,58px); height:clamp(46px,11vw,58px); overflow:visible }
.lr-track{ fill:none; stroke:rgba(232,204,134,.16); stroke-width:2.4 }
/* NOTE: the -90° start is in the SVG transform attribute, never in CSS — a
   CSS transform-origin would stack on top of it and skew the arc. */
.lr-arc{
  fill:none; stroke:var(--gold-hi); stroke-width:2.4; stroke-linecap:round;
  transition:stroke-dashoffset .45s linear;
}
.loader-pct{
  font-family:var(--serif); font-weight:300; font-size:.98rem;
  letter-spacing:.06em; color:#FBF5E9;
  text-shadow:0 2px 12px rgba(0,0,0,.85);
}
.loader-label{
  font-family:var(--sans); font-weight:200;
  font-size:.56rem; letter-spacing:.34em; text-indent:.34em; text-transform:uppercase;
  color:var(--ink-mute); text-shadow:0 2px 10px rgba(0,0,0,.85);
}

/* Withheld until both films are wholly downloaded, so the prompt is never
   offered against footage that would stutter. A solid contrasting pill, not
   faint text on a busy frame. */
.tap-cue{
  position:absolute; left:50%; bottom:clamp(58px,12vh,104px);
  transform:translateX(-50%); z-index:8;
  opacity:0; pointer-events:none;
  transition:opacity .8s var(--ease);
}
.opening-scene.is-ready .tap-cue{ opacity:1; pointer-events:auto }
.film-cue{ transition:opacity .9s var(--ease), transform .9s var(--ease) }
.tap-cue .label{
  display:inline-block;
  font-family:var(--sans); font-weight:200;
  font-size:.66rem; letter-spacing:.38em; text-indent:.38em; text-transform:uppercase;
  color:#FBF3E2; padding:.85rem 1.7rem; border-radius:999px;
  background:linear-gradient(180deg,rgba(14,32,25,.88),rgba(4,10,7,.92));
  border:1px solid rgba(232,204,134,.5);
  backdrop-filter:blur(3px);
  animation:tapBreathe 3s ease-in-out infinite;
}
@keyframes tapBreathe{
  0%,100%{ box-shadow:0 12px 34px -14px rgba(0,0,0,.8), 0 0 0 6px rgba(232,204,134,.10) }
  50%    { box-shadow:0 12px 34px -14px rgba(0,0,0,.8), 0 0 0 16px rgba(232,204,134,0) }
}
.opening-scene.is-playing .film-cue{ opacity:0; transform:translateX(-50%) scale(.94) }

/* ── hero type ────────────────────────────────────────────────────────────
   MEASURED, not guessed. Mean luminance of the closing frame, 16 × 12 grid:

     portrait   rows  0–25%  →  11–41  (storm cloud)     SAFE
                rows 33–58%  →  90–170 (amber sky, crosses)  KEEP OFF
                rows 58–100% →   0–2   (foreground rock)  SAFE
     landscape  rows  0–16%  →   3–50  (cloud)           SAFE
                rows 33–58%  →  70–174 (horizon band)    KEEP OFF
                rows 58–100% →   0–26  (hillside)        SAFE

   So both orientations get the same shape: one group pinned into the dark
   band at the top, one into the dark band at the bottom, and the sky left
   completely alone. Gold foil on that amber would not read at all. */
.hero-type{
  position:absolute; inset:0; z-index:5; pointer-events:none;
  display:flex; flex-direction:column; justify-content:space-between; align-items:center;
  text-align:center; gap:clamp(.7rem,2.6vh,1.3rem);
  padding:clamp(1.4rem,4vh,2.6rem) 1.3rem clamp(2.4rem,7vh,4rem);
}
/* Opacity and transform only — no blur. See revealText() in main.js. */
.hero-type [data-r]{
  opacity:0; transform:translateY(16px);
  transition:opacity 1.15s var(--ease), transform 1.4s var(--ease);
}
.ht-group{ display:flex; flex-direction:column; align-items:center; gap:.5rem }
.ht-eyebrow{
  font-family:var(--sans); font-weight:200; margin:0;
  /* Grown from the original .55-.66rem. The crosses sit LOW in the portrait
     frame (33-58%), so the dark cloud band above them is a lot of unused
     room — this and .ht-title now fill it instead of floating in it. */
  font-size:clamp(.68rem,3.1vw,.82rem);
  letter-spacing:.38em; text-indent:.38em; text-transform:uppercase;
  color:#F2E9D6; text-shadow:0 2px 14px rgba(0,0,0,.9);
  max-width:32ch;
}
.ht-title{
  font-family:var(--serif); font-weight:300;
  /* was clamp(1.5rem,6.4vw,2.4rem) — about 30% larger now, sized to the empty
     band above the crosses rather than to caution. Re-measure the placement
     against the footage if you change this (see the note above .hero-type). */
  font-size:clamp(1.95rem,8.4vw,3rem); line-height:1.2;
  letter-spacing:.22em; text-indent:.22em; text-transform:uppercase;
  padding:.16em .12em .2em;
}
.ht-acclaim{
  font-family:var(--serif); font-weight:300;
  font-size:clamp(2.3rem,11.5vw,3.6rem); line-height:1.14;
  letter-spacing:.16em; text-indent:.16em; text-transform:uppercase;
  color:#FCF7EC; text-shadow:0 3px 24px rgba(0,0,0,.92), 0 0 42px rgba(217,132,49,.28);
}
.ht-orn{ display:block; width:120px; height:9px; position:relative }
.ht-orn::before,.ht-orn::after{
  content:""; position:absolute; top:50%; width:44px; height:1px;
  background:linear-gradient(90deg,transparent,rgba(232,204,134,.72));
}
.ht-orn::before{ left:0 }
.ht-orn::after{ right:0; transform:scaleX(-1) }
.ht-orn i{
  position:absolute; left:50%; top:50%; width:6px; height:6px;
  transform:translate(-50%,-50%) rotate(45deg);
  background:var(--gold-hi); box-shadow:0 0 10px rgba(232,204,134,.8);
}

.scroll-hint{
  position:absolute; left:50%; bottom:1.5rem; transform:translateX(-50%);
  z-index:7; display:flex; flex-direction:column; align-items:center; gap:.45rem;
  opacity:0; transition:opacity 1.2s var(--ease);
  font-family:var(--sans); font-weight:200; font-size:.56rem;
  letter-spacing:.36em; text-indent:.36em; text-transform:uppercase; color:var(--ink-mute);
}
.scroll-hint.in{ opacity:1 }
.scroll-hint i{
  width:1px; height:34px; display:block;
  background:linear-gradient(180deg,transparent,rgba(232,204,134,.85));
  animation:hintDrop 2.4s ease-in-out infinite;
}
@keyframes hintDrop{ 0%,100%{transform:scaleY(.5); opacity:.4} 50%{transform:scaleY(1); opacity:1} }

/* ══════════════════ 2 · COUNTDOWN DIALS ══════════════════ */
.dials{
  display:grid; grid-template-columns:repeat(4,1fr);
  gap:clamp(.4rem,2.4vw,1.1rem);
  width:100%; margin-top:clamp(.8rem,3vh,1.6rem);
}
.dial{ position:relative; display:flex; flex-direction:column; align-items:center; gap:.5rem }
.dial-ring{ position:relative; width:100%; aspect-ratio:1; max-width:126px }
.dial-ring svg{ width:100%; height:100%; transform:rotate(-90deg); overflow:visible }
.dial-ring .track{ fill:none; stroke:rgba(201,162,75,.14); stroke-width:1.6 }
.dial-ring .arc{
  fill:none; stroke:url(#goldArc); stroke-width:2.2; stroke-linecap:round;
  filter:drop-shadow(0 0 5px rgba(201,162,75,.5));
  transition:stroke-dashoffset .55s var(--ease);
}
.dial-ring .ticks line{ stroke:rgba(201,162,75,.3); stroke-width:1 }
.dial-ring .ticks line.major{ stroke:rgba(232,204,134,.62); stroke-width:1.4 }

/* The sweeping marker on the seconds dial. Two things are easy to get wrong.
   (1) The pivot lives in the SVG `rotate(deg 50 50)` attribute ONLY — declare
   a CSS transform-origin as well and both apply, so the hand swings outside
   the dial. (2) A full-length hand from the centre strikes through the
   numeral, so this is a short marker riding the rim instead: it orbits the
   number rather than crossing it. It travels CLOCKWISE while the number
   counts down — see the `elapsed` calculation in main.js. */
.dial-ring .hand{
  stroke:var(--gold-hi); stroke-width:2.2; stroke-linecap:round;
  transition:transform .3s var(--ease);
}
.dial-ring .pin{ fill:rgba(232,204,134,.5) }

.dial-num{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-family:var(--serif); font-weight:300; letter-spacing:.02em;
  font-size:clamp(1.5rem,6.6vw,2.05rem); line-height:1;
  color:#FCF6E8; text-shadow:0 2px 16px rgba(0,0,0,.6);
}
.dial-num.tick{ animation:numTick .5s var(--ease) }
@keyframes numTick{
  0%{ opacity:.25; transform:translateY(-5px) }
  100%{ opacity:1; transform:none }
}
.dial-label{
  font-family:var(--sans); font-weight:200;
  font-size:clamp(.5rem,2.1vw,.6rem);
  letter-spacing:.28em; text-indent:.28em; text-transform:uppercase; color:var(--ink-mute);
}

/* ══════════════════ 2b · THE RECORD ══════════════════
   Adapted from the sibling invitation's turntable, recoloured gold-on-
   evergreen. The one behavioural difference: this arrives ALREADY PLAYING,
   because the song started on the tap that opened the invitation. So the
   default rendered state is needle-down and spinning, and the gold play badge
   is retired from the start. */
.turntable{
  width:100%; display:flex; flex-direction:column; align-items:center;
  gap:clamp(.9rem,3vh,1.4rem); margin-top:clamp(.6rem,2.5vh,1.2rem);
}
/* 74vw, not 82. The tonearm PARKS at -30deg from a pivot at 90% of the
   platter, which throws its tip out beyond the platter's right edge:
   tip_x = vw/2 + 0.4P + 0.46P*sin(park). At 82vw with a -30deg park that
   landed at 380px on a 375px screen — clipped by the section's overflow:hidden.
   It only appears once a guest PAUSES, which is exactly when nobody is looking
   for it, so it survived the first pass entirely.
   74vw plus a -24deg park (see .ta-arm) clears at 320 and 375 both, with the
   stylus head's own 15px width counted in. Change either and re-measure. */
.tt-platter{
  position:relative; width:min(360px,74vw); aspect-ratio:1;
  display:flex; align-items:center; justify-content:center;
}
/* platter mat glow */
.tt-platter::before{
  content:""; position:absolute; inset:-9%; border-radius:50%;
  background:radial-gradient(circle at 50% 42%, rgba(201,162,75,.16), rgba(201,162,75,0) 66%);
  pointer-events:none;
}

.record{
  position:relative; width:100%; height:100%; border-radius:50%; padding:0;
  background:
    radial-gradient(circle at 50% 50%, #0E2019 0 17%, #060F0B 17% 100%),
    #05100C;
  box-shadow:
    0 0 0 1px rgba(201,162,75,.26),
    0 26px 60px -26px rgba(0,0,0,.95),
    inset 0 0 60px rgba(0,0,0,.7);
  transition:transform .6s var(--ease), box-shadow .6s var(--ease);
}
.record:active{ transform:scale(.985) }
.record.spinning{ animation:spin 2.6s linear infinite }
.record.spinning.slowing{ animation:spin 6.5s linear infinite }
@keyframes spin{ to{ transform:rotate(360deg) } }

/* The gold play badge exists only for the stopped state. Since the song is
   normally already running when this section is reached, it is usually never
   seen — it appears if the guest pauses, or if audio failed to start. */
.rec-play{
  position:absolute; left:50%; top:50%;
  width:26%; aspect-ratio:1; border-radius:50%;
  transform:translate(-50%,-50%); z-index:3;
  display:grid; place-items:center;
  background:radial-gradient(circle at 38% 32%, rgba(10,26,20,.94), rgba(3,9,7,.97));
  box-shadow:0 0 0 1.5px rgba(232,204,134,.75), 0 10px 26px -10px rgba(0,0,0,.95);
  transition:opacity .5s var(--ease), transform .55s var(--ease);
}
.rec-play svg{ width:42%; height:42%; overflow:visible; transform:translateX(4%) }
.rec-play .ic-play { fill:var(--gold-hi); transition:opacity .3s var(--ease) }
.rec-play .ic-pause{ fill:none; stroke:var(--gold-hi); stroke-width:2.4; stroke-linecap:round; opacity:0 }
.turntable.playing .rec-play{ opacity:0; transform:translate(-50%,-50%) scale(.72) }

.rec-halo{
  position:absolute; inset:0; border-radius:50%; pointer-events:none; z-index:0;
  box-shadow:0 0 0 0 rgba(232,204,134,.42);
  animation:recPulse 2.8s ease-out infinite;
}
.turntable.playing .rec-halo{ animation:none; box-shadow:none }
@keyframes recPulse{
  0%   { box-shadow:0 0 0 0 rgba(232,204,134,.34) }
  70%  { box-shadow:0 0 0 20px rgba(232,204,134,0) }
  100% { box-shadow:0 0 0 0 rgba(232,204,134,0) }
}
.record:hover .rec-play{ transform:translate(-50%,-50%) scale(1.06) }
/* while playing, hovering offers PAUSE rather than nothing */
.turntable.playing .record:hover .rec-play{
  opacity:1; transform:translate(-50%,-50%) scale(1);
}
.turntable.playing .record:hover .rec-play .ic-play { opacity:0 }
.turntable.playing .record:hover .rec-play .ic-pause{ opacity:1 }

.rec-grooves{
  position:absolute; inset:6%; border-radius:50%; pointer-events:none;
  background:repeating-radial-gradient(circle at 50% 50%,
    rgba(255,255,255,.045) 0 1px, rgba(0,0,0,0) 1px 4px);
  mask:radial-gradient(circle at 50% 50%, transparent 0 27%, #000 28%, #000 100%);
  -webkit-mask:radial-gradient(circle at 50% 50%, transparent 0 27%, #000 28%, #000 100%);
}
.rec-sheen{
  position:absolute; inset:0; border-radius:50%; pointer-events:none;
  background:conic-gradient(from 210deg,
    rgba(255,255,255,0) 0deg, rgba(190,230,205,.10) 34deg, rgba(255,255,255,0) 78deg,
    rgba(255,255,255,0) 190deg, rgba(232,204,134,.12) 224deg, rgba(255,255,255,0) 268deg);
}
/* the paper label — the gold wax seal lifted out of the card film, so the
   cross that seals the envelope in the opening is the same cross turning here */
.rec-label{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:38%; aspect-ratio:1; border-radius:50%; overflow:hidden;
  display:grid; place-items:center;
  background:radial-gradient(circle at 38% 30%, #F0DAA4 0%, #C9A24B 46%, #97722E 100%);
  box-shadow:0 0 0 1px rgba(255,246,220,.42), inset 0 0 18px rgba(90,60,16,.5);
}
.rec-art{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; border-radius:50%;
}
.rec-label::after{
  content:""; position:absolute; inset:0; border-radius:50%; pointer-events:none;
  box-shadow:inset 0 0 0 1px rgba(255,246,220,.5), inset 0 0 14px rgba(20,12,0,.45);
}
.rec-spindle{
  position:relative; z-index:2;
  width:15%; aspect-ratio:1; border-radius:50%;
  background:radial-gradient(circle at 40% 35%, #0A1712, #030806);
  box-shadow:inset 0 0 4px rgba(0,0,0,.9), 0 0 0 1.5px rgba(255,246,220,.55);
}

/* ── the tone arm ─────────────────────────────────────────────────────────
   The pivot post sits at 90%/6% of the platter — ON its edge, not beyond it —
   and the arm is 46% of the platter long, so hanging straight down the stylus
   lands at ~80% of the record radius: the outer groove, where a needle starts.

   Direction is easy to get backwards. On screen (y points DOWN) a POSITIVE CSS
   rotation swings the hanging arm LEFT, i.e. INWARD toward the spindle. So
   resting must be NEGATIVE (parked out beyond the rim) and playing is 0deg.
   Reversing the two makes the arm start over the record's centre and swing
   outward to play. The arm pivots at its TOP. */
.tonearm{ position:absolute; inset:0; pointer-events:none; z-index:4 }
.ta-base{
  position:absolute; left:90%; top:6%; width:10%; height:10%;
  transform:translate(-50%,-50%); border-radius:50%; z-index:2;
  background:radial-gradient(circle at 36% 30%, #EBD5A2, #A9853A 60%, #63491B);
  box-shadow:0 5px 14px -5px rgba(0,0,0,.95), 0 0 0 1px rgba(255,246,220,.28);
}
.ta-base::after{
  content:""; position:absolute; left:50%; top:50%; width:26%; height:26%;
  transform:translate(-50%,-50%); border-radius:50%;
  background:radial-gradient(circle at 40% 35%, #3B2E12, #171104);
}
.ta-arm{
  position:absolute; left:90%; top:6%;
  width:5px; height:46%;
  transform-origin:50% 0;                       /* pivot at the top */
  transform:translateX(-50%) rotate(-24deg);    /* parked out beyond the rim */
  background:linear-gradient(180deg,#F3E2B8,#C6A253 42%,#7E6127);
  border-radius:4px;
  box-shadow:0 8px 18px -10px rgba(0,0,0,.95), 0 0 0 .5px rgba(255,246,220,.18);
  transition:transform 1.15s cubic-bezier(.34,.02,.2,1);
}
.tonearm.down .ta-arm{ transform:translateX(-50%) rotate(0deg) }
/* Arriving already-playing must NOT animate the arm swinging in — that would
   replay a moment the guest never saw. `.set` puts it straight down with no
   transition; JS drops the class a frame later so later pauses still animate. */
.tonearm.set .ta-arm,
.tonearm.set .ta-head{ transition:none }

.ta-head{
  position:absolute; left:50%; bottom:-9px;
  transform:translateX(-50%) rotate(-17deg);
  width:15px; height:22px; border-radius:2px 2px 4px 4px;
  background:linear-gradient(160deg,#F5E7C2,#A9853A);
  box-shadow:0 4px 10px -4px rgba(0,0,0,.95);
  transition:transform .5s var(--ease);
}
.ta-head::after{                      /* the stylus */
  content:""; position:absolute; left:50%; bottom:-6px; transform:translateX(-50%);
  width:2px; height:7px; border-radius:0 0 2px 2px;
  background:linear-gradient(180deg,var(--gold-pale),#FFF6DC);
}
.tonearm.down .ta-head{ transform:translateX(-50%) rotate(-17deg) translateY(3px) }

.tt-track{ margin:0; display:flex; flex-direction:column; align-items:center; gap:.16rem }
.tt-track b{
  font-family:var(--serif); font-weight:400; letter-spacing:.03em;
  font-size:clamp(1.15rem,4.6vw,1.35rem); color:#FBF5E9;
}
.tt-track em{
  font-family:var(--sans); font-weight:200; font-style:normal;
  font-size:clamp(.55rem,2.2vw,.62rem);
  letter-spacing:.3em; text-indent:.3em; text-transform:uppercase; color:var(--gold-hi);
}

.tt-cue{
  font-family:var(--sans); font-weight:300;
  font-size:.63rem; letter-spacing:.32em; text-indent:.32em; text-transform:uppercase;
  color:#FBF3E2;
  padding:.78rem 1.6rem; border-radius:999px;
  background:linear-gradient(180deg,rgba(20,48,38,.92),rgba(5,14,10,.95));
  border:1px solid rgba(232,204,134,.5);
  box-shadow:0 12px 30px -16px rgba(0,0,0,.9);
  transition:color .6s var(--ease), border-color .6s var(--ease), background .6s var(--ease);
  animation:cueBreathe 3s ease-in-out infinite;
}
@keyframes cueBreathe{
  0%,100%{ box-shadow:0 12px 30px -16px rgba(0,0,0,.9), 0 0 0 5px rgba(232,204,134,.10) }
  50%    { box-shadow:0 12px 30px -16px rgba(0,0,0,.9), 0 0 0 13px rgba(232,204,134,0) }
}
/* while playing the pill stands down to a quiet label — it is a transport
   control at that point, not a call to action */
.turntable.playing .tt-cue{
  color:var(--gold-hi); animation:none;
  background:transparent; border-color:transparent; box-shadow:none;
}

/* ── floating transport ─────────────────────────────────────────────────── */
.mini-player{
  position:fixed; left:clamp(.8rem,3vw,1.4rem); bottom:clamp(1.1rem,3.5vh,1.7rem);
  z-index:55; display:flex; align-items:center; gap:.65rem;
  padding:.5rem .95rem .5rem .5rem; border-radius:999px;
  background:linear-gradient(150deg,rgba(20,48,38,.9),rgba(5,14,10,.94));
  border:1px solid var(--gold-line);
  box-shadow:0 16px 36px -20px rgba(0,0,0,.95);
  backdrop-filter:blur(8px);
  opacity:0; transform:translateY(14px); pointer-events:none;
  transition:opacity .8s var(--ease), transform .8s var(--ease);
}
.mini-player.in{ opacity:1; transform:none; pointer-events:auto }
.mini-disc{
  position:relative; width:30px; height:30px; border-radius:50%; flex:0 0 auto;
  background:radial-gradient(circle at 50% 50%, #143026 0 22%, #07110D 22% 100%);
  box-shadow:0 0 0 1px rgba(201,162,75,.35);
  display:grid; place-items:center;
}
.mini-disc.spin{ animation:spin 2.6s linear infinite }
.mini-disc i{
  width:9px; height:9px; border-radius:50%; display:block;
  background:radial-gradient(circle at 38% 32%, #F0DAA4, #C9A24B 60%, #97722E);
}
.mini-meta{ display:flex; flex-direction:column; align-items:flex-start; gap:.1rem; min-width:0 }
.mini-meta b{
  font-family:var(--serif); font-weight:300; font-size:.78rem; color:#FBF5E9;
  letter-spacing:.02em; max-width:38vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.mini-meta em{
  font-family:var(--sans); font-weight:200; font-style:normal; font-size:.45rem;
  letter-spacing:.24em; text-indent:.24em; text-transform:uppercase; color:var(--gold);
}

/* ══════════════════ 3 · HOLY WEEK ══════════════════
   Eight framed photographs, alternating side to side.

   These were full-bleed backgrounds with the type laid over them at first.
   That failed for two compounding reasons, both worth recording because they
   are easy to walk back into:

     1. To keep cream text at 4.5:1 over the BRIGHTEST pixel of an arbitrary
        stock photograph, the scrim needs roughly .6 alpha across the whole
        copy column. At that strength the photograph is no longer a
        photograph — every one of them went to flat mud, and the sunset in
        the Good Friday frame lost its glow entirely.
     2. Full-bleed means the image must fill whatever box the viewport gives
        it, so a 3:2 landscape composition gets centre-cropped into a
        375×812 phone frame. The chalice in the Last Supper photograph was
        sliced clean in half.

   Framing them solves both without a trade: no scrim at all, the full tonal
   range survives, the 3:2 crop matches how the pictures were actually shot,
   and the text sits on the evergreen ground where contrast is already solved.
   Reserve full-bleed for footage that was art-directed with empty space in
   it — which the opening films were, and stock photography is not. */
.hw-list{
  width:100%; display:flex; flex-direction:column;
  gap:clamp(2.6rem,8vh,4.4rem);
  margin:clamp(1.6rem,5vh,2.6rem) 0 clamp(1.4rem,4vh,2rem);
}
.hw-outro{ text-align:center; max-width:46ch }

.station{
  position:relative; display:flex; flex-direction:column;
  gap:clamp(1rem,3vh,1.5rem); text-align:left;
  opacity:0; transform:translateY(26px);
  transition:opacity 1.2s var(--ease), transform 1.45s var(--ease);
}
.station.in{ opacity:1; transform:none }

/* The frame: a gold keyline and a deep drop, so the photograph reads as a
   plate tipped onto the page rather than a browser image. */
.st-photo{
  position:relative; margin:0; overflow:hidden; border-radius:3px;
  aspect-ratio:3/2; background:var(--forest-850);
  box-shadow:
    0 0 0 1px var(--gold-line),
    0 34px 60px -38px rgba(0,0,0,.95),
    0 6px 18px -12px rgba(0,0,0,.7);
}
.st-photo img{
  width:100%; height:100%; object-fit:cover; display:block;
  /* A slow settle, inside the frame's overflow:hidden — the photograph is
     never blurred or dimmed, only moved. */
  transform:scale(1.05);
  transition:transform 8s cubic-bezier(.33,0,.2,1), opacity 1.4s var(--ease);
  opacity:0;
}
.station.in .st-photo img{ transform:scale(1); opacity:1 }
/* an engraved inner keyline, matching the event card */
.st-photo::after{
  content:""; position:absolute; inset:7px; border-radius:2px;
  border:1px solid rgba(255,246,220,.14); pointer-events:none;
}
/* the faintest warm lift from the bottom edge, so the plate sits INTO the
   evergreen ground instead of on top of it */
.st-photo::before{
  content:""; position:absolute; left:0; right:0; bottom:0; height:38%;
  z-index:1; pointer-events:none;
  background:linear-gradient(180deg,rgba(4,12,9,0),rgba(4,12,9,.28));
}

.st-copy{ display:flex; flex-direction:column; gap:.5rem; min-width:0 }
.st-index{ display:flex; align-items:center; gap:.8rem }
.st-no{
  font-family:var(--serif); font-weight:300; line-height:1;
  font-size:clamp(1.25rem,4.6vw,1.6rem); letter-spacing:.14em; text-indent:.14em;
  background-image:linear-gradient(100deg,#B98F3C,#EFD79E 46%,#C9A24B);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
  padding:.1em .1em .16em;
}
.st-rule{ display:block; width:clamp(28px,7vw,54px); height:1px; background:linear-gradient(90deg,var(--gold-line),transparent) }
.st-day{
  font-family:var(--sans); font-weight:200; margin:0;
  font-size:clamp(.53rem,2.2vw,.6rem);
  letter-spacing:.32em; text-indent:.32em; text-transform:uppercase; color:var(--gold-hi);
}
.st-title{
  margin:0; font-family:var(--serif); font-weight:300;
  font-size:clamp(1.35rem,5.6vw,1.85rem); line-height:1.18;
  letter-spacing:.02em; color:#FBF5E9;
}
/* Scripture carries the section, so it gets the weight — an indented
   quotation with a gold stem, the way an order of service sets it. */
.st-verse{
  margin:.35rem 0 .15rem; padding:.1rem 0 .1rem 1rem;
  border-left:1px solid var(--gold-line);
  font-family:var(--serif); font-style:italic; font-weight:300;
  font-size:clamp(.96rem,3.8vw,1.1rem); line-height:1.62; color:var(--ink);
}
.st-cite{
  display:block; margin-top:.5rem; font-style:normal;
  font-family:var(--sans); font-weight:200; font-size:.53rem;
  letter-spacing:.26em; text-indent:.26em; text-transform:uppercase; color:var(--gold-hi);
}
.st-text{
  margin:.25rem 0 0; font-size:clamp(.85rem,3.3vw,.92rem); line-height:1.76;
  color:var(--ink-mute);
}

/* gold connector between stations */
.hw-link{
  align-self:center; display:flex; flex-direction:column; align-items:center; gap:.2rem;
  opacity:0; transition:opacity 1s var(--ease) .2s;
}
.hw-link.in{ opacity:1 }
.hw-link i{
  display:block; width:1px; height:clamp(24px,5vh,38px);
  background:linear-gradient(180deg,rgba(201,162,75,0),rgba(201,162,75,.6));
}
.hw-link svg{ width:11px; height:9px; display:block }
.hw-link svg path{ fill:none; stroke:var(--gold); stroke-width:1.4; stroke-linecap:round; stroke-linejoin:round }

/* ══════════════════ shared card surface ══════════════════ */
.event-card,.detail-card,.rsvp-card{
  position:relative;
  background:linear-gradient(168deg, rgba(24,60,46,.6), rgba(7,16,12,.74));
  border:1px solid var(--gold-line);
  border-radius:4px;
  box-shadow:
    0 1px 0 rgba(255,246,220,.07) inset,
    0 30px 60px -40px rgba(0,0,0,.95),
    0 4px 14px -10px rgba(0,0,0,.6);
  backdrop-filter:blur(2px);
}
/* engraved inner keyline */
.event-card::after,.detail-card::after,.rsvp-card::after{
  content:""; position:absolute; inset:6px; border-radius:2px;
  border:1px solid var(--gold-line-soft); pointer-events:none;
}
/* corner ticks — stationery detail */
.ec-corner{ position:absolute; width:13px; height:13px; pointer-events:none; z-index:3 }
.ec-corner::before,.ec-corner::after{ content:""; position:absolute; background:var(--gold); opacity:.62 }
.ec-corner::before{ width:100%; height:1px }
.ec-corner::after { width:1px; height:100% }
.ec-corner.tl{ left:12px; top:12px }
.ec-corner.tr{ right:12px; top:12px } .ec-corner.tr::after{ right:0 }
.ec-corner.bl{ left:12px; bottom:12px } .ec-corner.bl::before{ bottom:0 }
.ec-corner.br{ right:12px; bottom:12px } .ec-corner.br::before{ bottom:0 } .ec-corner.br::after{ right:0 }

/* ══════════════════ 4 · ORDER OF SERVICE ══════════════════ */
.event-card{
  width:100%; margin-top:clamp(1rem,3.5vh,1.6rem);
  padding:clamp(1.7rem,6vw,2.6rem) clamp(1.2rem,5vw,2.2rem);
  text-align:left;
}
.ec-head{ display:flex; flex-direction:column; align-items:center; gap:.5rem; margin-bottom:clamp(1.1rem,4vh,1.7rem) }
.ec-mono{
  font-family:var(--serif); font-size:clamp(1.5rem,6vw,1.9rem); line-height:1.2;
  padding:.1em .12em .16em;
  background-image:linear-gradient(100deg,#B98F3C,#EFD79E 46%,#C9A24B);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
}
.ec-line{ display:block; width:74%; height:1px; background:linear-gradient(90deg,transparent,var(--gold-line),transparent) }
.ec-date{
  font-family:var(--sans); font-weight:200; margin:0;
  font-size:clamp(.56rem,2.3vw,.63rem);
  letter-spacing:.3em; text-indent:.3em; text-transform:uppercase; color:var(--ink-mute);
}
.ec-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column }
.ec-item{
  display:grid; grid-template-columns:auto 22px 1fr; align-items:start;
  gap:0 clamp(.5rem,2.5vw,.9rem);
  padding:clamp(.6rem,2.2vh,.85rem) 0;
  opacity:0; transform:translateY(12px);
  transition:opacity 1s var(--ease), transform 1.15s var(--ease);
}
.ec-item.in{ opacity:1; transform:none }
.ec-time{
  font-family:var(--serif); font-weight:400; font-size:clamp(.95rem,3.8vw,1.08rem);
  color:var(--gold-hi); line-height:1.35; letter-spacing:.03em; white-space:nowrap;
  padding-top:.06rem;
}
.ec-node{ position:relative; height:100%; display:flex; justify-content:center }
.ec-node i{
  width:6px; height:6px; margin-top:.52rem; transform:rotate(45deg);
  background:var(--gold); box-shadow:0 0 8px rgba(201,162,75,.55); z-index:2;
}
.ec-node::before{
  content:""; position:absolute; left:50%; top:0; bottom:0; width:1px;
  transform:translateX(-50%);
  background:linear-gradient(180deg,rgba(201,162,75,.28),rgba(201,162,75,.28));
}
.ec-item:first-child .ec-node::before{ top:.55rem }
.ec-item:last-child  .ec-node::before{ bottom:calc(100% - .95rem) }
.ec-body h3{
  margin:0; font-family:var(--serif); font-weight:400;
  font-size:clamp(1rem,4vw,1.14rem); line-height:1.3; color:#FBF5E9; letter-spacing:.02em;
}
.ec-body p{
  margin:.16rem 0 0; font-size:clamp(.8rem,3.1vw,.87rem);
  line-height:1.62; color:var(--ink-mute);
}
.ec-foot{ margin-top:clamp(1rem,3.5vh,1.5rem); padding-top:1rem; border-top:1px solid var(--gold-line-soft); text-align:center }
.ec-foot p{
  margin:0; font-family:var(--sans); font-weight:200;
  font-size:clamp(.56rem,2.3vw,.62rem);
  letter-spacing:.22em; text-indent:.22em; text-transform:uppercase; color:var(--ink-mute);
  line-height:2;
}

/* ══════════════════ 5 · WHERE & WHEN ══════════════════ */
.venue-line{ display:flex; flex-direction:column; align-items:center; gap:.25rem; margin-top:.4rem }
.detail-cards{
  width:100%; display:grid; grid-template-columns:1fr;
  gap:clamp(.9rem,3vw,1.2rem); margin-top:clamp(1rem,3.5vh,1.5rem);
}
.detail-card{
  padding:clamp(1.5rem,5.5vw,2rem) clamp(1.2rem,5vw,1.7rem);
  display:flex; flex-direction:column; align-items:center; gap:.4rem; text-align:center;
  opacity:0; transform:translateY(18px);
  transition:opacity 1.2s var(--ease), transform 1.4s var(--ease);
}
.detail-card.in{ opacity:1; transform:none }
.dc-icon{ width:28px; height:28px; margin-bottom:.2rem }
.dc-icon svg{ width:100%; height:100%; fill:none; stroke:var(--gold); stroke-width:1.15; stroke-linecap:round; stroke-linejoin:round }
.dc-label{
  font-family:var(--sans); font-weight:200; margin:0;
  font-size:clamp(.55rem,2.3vw,.62rem);
  letter-spacing:.34em; text-indent:.34em; text-transform:uppercase; color:var(--gold-hi);
}
.dc-venue{
  font-family:var(--serif); font-weight:300; margin:.1rem 0 0;
  font-size:clamp(1.25rem,5.2vw,1.55rem); line-height:1.24; color:#FBF5E9; letter-spacing:.02em;
}
.dc-address{ margin:0; font-size:clamp(.84rem,3.3vw,.92rem); line-height:1.6; color:var(--ink-soft) }
.dc-time{
  font-family:var(--serif); font-style:italic; margin:.3rem 0 0;
  font-size:clamp(1.25rem,5vw,1.5rem); color:var(--gold-hi); line-height:1.2;
}
.dc-note{
  margin:.35rem 0 0; font-size:clamp(.8rem,3.1vw,.87rem);
  line-height:1.62; color:var(--ink-mute);
}
.dc-dir{
  margin-top:.6rem; font-family:var(--sans); font-weight:200;
  font-size:.58rem; letter-spacing:.26em; text-indent:.26em; text-transform:uppercase;
  color:var(--gold-hi); border-bottom:1px solid var(--gold-line); padding-bottom:.22rem;
}

.map-frame{
  position:relative; width:100%; margin-top:clamp(1rem,3.5vh,1.5rem);
  aspect-ratio:16/11; border-radius:4px; overflow:hidden;
  border:1px solid var(--gold-line);
  box-shadow:0 30px 60px -42px rgba(0,0,0,.95);
}
/* Google's embed ships only a light tile set, so it is inverted into a night
   map. invert + hue-rotate alone leaves it muddy khaki; pulling saturation
   back and lifting contrast afterwards is what turns it evergreen. */
.map-frame iframe{
  width:100%; height:100%; border:0;
  filter:invert(.93) hue-rotate(120deg) saturate(.45) brightness(.95) contrast(1.08);
}
.map-veil{
  position:absolute; inset:0; pointer-events:none;
  mix-blend-mode:multiply;
  background:linear-gradient(180deg,rgba(24,70,52,.5),rgba(14,40,30,.34) 40%,rgba(24,70,52,.5));
  box-shadow:inset 0 0 70px rgba(3,10,7,.7);
}

/* Gold button. The ink is deep evergreen rather than brown-black, and the
   gold stays in the rich champagne band — a paler fill washes thin uppercase
   lettering out against it. At #08150F on #E9D093 this clears 9:1. */
.btn-gold{
  position:relative; margin-top:clamp(1.1rem,4vh,1.7rem);
  padding:1.05rem 2.2rem; border-radius:999px;
  font-family:var(--sans); font-weight:400;
  font-size:.66rem; letter-spacing:.3em; text-indent:.3em; text-transform:uppercase;
  color:#08150F;
  background:linear-gradient(100deg,#B68A35 0%,#D4AD5E 30%,#E9D093 50%,#D2AC5C 70%,#B68A35 100%);
  background-size:200% 100%;
  box-shadow:0 16px 36px -20px rgba(0,0,0,.9), 0 0 0 1px rgba(255,246,220,.34);
  transition:background-position .9s var(--ease), transform .5s var(--ease), box-shadow .5s var(--ease);
  overflow:hidden;
}
.btn-gold:hover{ background-position:100% 50%; transform:translateY(-1px) }
.btn-gold:active{ transform:translateY(0) scale(.985) }
.btn-gold:focus-visible{ outline:2px solid var(--gold-hi); outline-offset:3px }

.btn-ghost{
  position:relative; margin-top:clamp(1.1rem,4vh,1.7rem);
  padding:1.05rem 2.1rem; border-radius:999px;
  font-family:var(--sans); font-weight:300;
  font-size:.63rem; letter-spacing:.28em; text-indent:.28em; text-transform:uppercase;
  color:var(--gold-hi); text-decoration:none;
  border:1px solid var(--gold-line);
  background:linear-gradient(180deg,rgba(20,48,38,.6),rgba(6,14,10,.72));
  transition:border-color .5s var(--ease), background .5s var(--ease), transform .5s var(--ease);
}
.btn-ghost:hover{ border-color:var(--gold-hi); transform:translateY(-1px) }
.btn-ghost:focus-visible{ outline:2px solid var(--gold-hi); outline-offset:3px }

.cal-btn{ display:inline-flex; align-items:center; justify-content:center; min-width:238px; min-height:47px }
.cal-btn .cal-ring{
  position:absolute; inset:-3px; width:calc(100% + 6px); height:calc(100% + 6px);
  opacity:0; pointer-events:none; overflow:visible;
}
.cal-btn .cal-ring circle{
  fill:none; stroke:var(--gold-hi); stroke-width:2.4; stroke-linecap:round;
  stroke-dasharray:296; stroke-dashoffset:296; transform-origin:50% 50%; transform:rotate(-90deg);
}
.cal-btn .cal-tick{
  position:absolute; left:50%; top:50%; width:22px; height:22px;
  transform:translate(-50%,-50%) scale(.7); opacity:0; pointer-events:none;
}
.cal-btn .cal-tick path{
  fill:none; stroke:#08150F; stroke-width:2.6; stroke-linecap:round; stroke-linejoin:round;
  stroke-dasharray:26; stroke-dashoffset:26;
}
.cal-btn span{ transition:opacity .4s var(--ease), transform .5s var(--ease) }
.cal-btn.saving .cal-ring{ opacity:1 }
.cal-btn.saving .cal-ring circle{ animation:ringDraw 1s var(--ease) forwards }
@keyframes ringDraw{ to{ stroke-dashoffset:0 } }
.cal-btn.saving span{ opacity:0; transform:translateY(-6px) }
.cal-btn.saved .cal-tick{ opacity:1; transform:translate(-50%,-50%) scale(1) }
.cal-btn.saved .cal-tick path{ animation:tickDraw .55s var(--ease) forwards }
@keyframes tickDraw{ to{ stroke-dashoffset:0 } }
.cal-btn.done span{ opacity:1; transform:none }
.cal-btn.done .cal-tick{ opacity:0; transform:translate(-50%,-50%) scale(.6) }
.cal-btn.done .cal-ring{ opacity:0 }

/* ══════════════════ 7 · RESERVE YOUR SEAT ══════════════════ */
.rsvp-card{
  width:100%; margin-top:clamp(1rem,3.5vh,1.6rem);
  padding:clamp(1.8rem,6vw,2.4rem) clamp(1.2rem,5vw,1.9rem);
  display:flex; flex-direction:column; gap:clamp(.95rem,3vh,1.2rem); text-align:left;
}
.field{ position:relative }
.field input,.field textarea,.field select{
  width:100%; padding:1.1rem .1rem .45rem; border:0; border-bottom:1px solid var(--gold-line);
  background:transparent; color:var(--ink);
  font-family:var(--serif); font-weight:300; font-size:1rem; line-height:1.4;
  border-radius:0; outline:none; resize:none;
  transition:border-color .5s var(--ease);
}
.field select{ appearance:none; -webkit-appearance:none; cursor:pointer }
.field select option{ background:#0B1810; color:var(--ink) }
.field-select::after{
  content:""; position:absolute; right:.3rem; bottom:1rem; width:7px; height:7px;
  border-right:1px solid var(--gold); border-bottom:1px solid var(--gold);
  transform:rotate(45deg); pointer-events:none;
}
.field label{
  position:absolute; left:.1rem; top:1.05rem; pointer-events:none;
  font-family:var(--sans); font-weight:200; font-size:.78rem;
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-mute);
  transition:transform .45s var(--ease), font-size .45s var(--ease), color .45s var(--ease);
  transform-origin:left top;
}
.field input:focus~label,.field textarea:focus~label,
.field input:not(:placeholder-shown)~label,.field textarea:not(:placeholder-shown)~label,
.field select~label{
  transform:translateY(-1.05rem) scale(.78); color:var(--gold-hi);
}
.field input:focus,.field textarea:focus,.field select:focus{ border-bottom-color:var(--gold-hi) }
.field.invalid input,.field.invalid textarea{ border-bottom-color:#C86A6A }

/* the "bringing someone new" checkbox — the answer the welcome team needs */
.check{ display:flex; align-items:flex-start; gap:.7rem; cursor:pointer; padding:.2rem 0 }
.check input{ position:absolute; opacity:0; width:1px; height:1px }
.check-box{
  flex:0 0 auto; width:20px; height:20px; margin-top:.1rem; border-radius:3px;
  border:1px solid var(--gold-line); display:grid; place-items:center;
  background:rgba(6,14,10,.5);
  transition:border-color .4s var(--ease), background .4s var(--ease);
}
.check-box svg{ width:13px; height:13px; opacity:0; transition:opacity .3s var(--ease) }
.check-box svg path{ fill:none; stroke:#08150F; stroke-width:2.6; stroke-linecap:round; stroke-linejoin:round }
.check input:checked~.check-box{
  background:linear-gradient(100deg,#B98F3C,#E9D29A 46%,#D6B166); border-color:var(--gold-hi);
}
.check input:checked~.check-box svg{ opacity:1 }
.check input:focus-visible~.check-box{ outline:2px solid var(--gold-hi); outline-offset:2px }
.check-label{
  font-size:clamp(.85rem,3.3vw,.92rem); line-height:1.55; color:var(--ink-soft);
}

/* Two short fields side by side on anything wider than a small phone — a
   connect card with ten stacked rows reads as a tax return. */
.field-row{ display:grid; grid-template-columns:1fr; gap:clamp(.95rem,3vh,1.2rem) }
@media (min-width:520px){
  .field-row{ grid-template-columns:1fr 1fr; gap:1.1rem }
  /* When the service question is hidden (a single service) the remaining
     field must not sit in a half-width column on its own. main.js adds
     .one-up rather than using :has(), so this holds on older phones too. */
  .field-row.one-up{ grid-template-columns:1fr }
}

/* Multi-select interests, as chips rather than a column of checkboxes:
   five stacked rows of tick-boxes is what makes a form feel long. */
.chips{ border:0; margin:0; padding:0; min-width:0 }
.chips legend{
  padding:0 0 .6rem; font-family:var(--sans); font-weight:200;
  font-size:.58rem; letter-spacing:.3em; text-indent:.3em; text-transform:uppercase;
  color:var(--gold-hi);
}
.chip-row{ display:flex; flex-wrap:wrap; gap:.45rem }
.chip{ position:relative; display:inline-flex }
.chip input{ position:absolute; opacity:0; width:1px; height:1px }
.chip span{
  display:inline-block; padding:.55rem 1rem; border-radius:999px;
  border:1px solid var(--gold-line-soft);
  background:rgba(6,16,11,.45);
  font-family:var(--sans); font-weight:200; font-size:.63rem;
  letter-spacing:.1em; color:var(--ink-soft); cursor:pointer;
  transition:border-color .4s var(--ease), background .4s var(--ease), color .4s var(--ease);
}
.chip span:hover{ border-color:var(--gold-line); color:var(--ink) }
.chip input:checked ~ span{
  border-color:var(--gold-hi); color:#08150F;
  background:linear-gradient(100deg,#C79E4C,#E9D29A 46%,#D6B166);
  font-weight:400;
}
.chip input:focus-visible ~ span{ outline:2px solid var(--gold-hi); outline-offset:2px }

.rsvp-privacy{
  text-align:center; text-transform:none; letter-spacing:.06em; text-indent:0;
  font-size:.62rem; line-height:1.8; color:var(--ink-faint); max-width:44ch;
  margin-left:auto; margin-right:auto;
}
.rsvp-contact-note{ color:var(--gold-hi); font-style:italic }

.rsvp-card .btn-gold{ margin-top:.3rem; width:100% }
.rsvp-card .micro{ text-align:center }
.rsvp-error{
  margin:0; text-align:center; color:#E39B9B;
  font-family:var(--sans); font-weight:200; font-size:.6rem;
  letter-spacing:.2em; text-transform:uppercase;
}

.rsvp-done{
  width:100%; margin-top:clamp(1rem,3.5vh,1.6rem);
  display:flex; flex-direction:column; align-items:center; gap:.6rem;
  padding:clamp(2rem,7vw,2.8rem) 1.2rem;
}
.done-seal{
  width:56px; height:56px; border-radius:50%; display:grid; place-items:center;
  border:1px solid var(--gold-line);
  background:radial-gradient(circle at 40% 34%, rgba(232,204,134,.24), rgba(201,162,75,.05));
  margin-bottom:.5rem;
}
.done-seal svg{ width:24px; height:24px }
.done-seal svg path{
  fill:none; stroke:var(--gold-hi); stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round;
  stroke-dasharray:26; stroke-dashoffset:26; animation:tickDraw .8s var(--ease) .25s forwards;
}
.rsvp-done .display{ font-size:clamp(1.4rem,5.6vw,1.9rem) }
.demo-note{
  max-width:44ch; line-height:1.9; color:var(--ink-faint);
  letter-spacing:.16em; text-indent:.16em;
}

/* ══════════════════ 7 · INVITE SOMEONE ══════════════════
   Photographic ground — a church at golden hour — because this is the section
   asking somebody to walk through a door. Image background, so the section is
   full-height (see the sizing rule at Holy Week). */
.share-scene{
  min-height:100svh; justify-content:center;
  background:var(--forest-950); overflow:hidden;
}
.share-img{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:50% 55%; z-index:0;
}
/* This one IS a background, because the section was asked for that way — so
   the photograph was chosen to suit it rather than fought with a scrim. It is
   a SILHOUETTE against a soft dusk sky: the subject is already dark, and the
   bright part is a smooth gradient, which is the one thing that can be
   darkened without turning to mud. (The first attempt used a grey stone church
   filling the frame; no scrim setting made that both legible and good.)

   The strength is measured, not chosen. The brightest pixel anywhere under the
   copy block is 161, not 255, so cream text needs only .34 alpha to clear
   4.5:1 — and the earlier .82 was making a warm sunset look like a power cut.
   These stops land around .40–.55 there: enough margin, and the sky still
   glows. If you swap the photograph, re-measure; do not reuse these numbers. */
.share-scrim{
  position:absolute; inset:0; z-index:1; pointer-events:none;
  background:
    radial-gradient(105% 68% at 50% 46%, rgba(3,8,6,.18) 0%, rgba(3,8,6,.58) 100%),
    linear-gradient(180deg, rgba(3,8,6,.5) 0%, rgba(3,8,6,.2) 30%, rgba(3,8,6,.3) 62%, rgba(3,8,6,.66) 100%);
}
.share-scene .lede{ text-shadow:0 2px 14px rgba(0,0,0,.9) }

.share-row{
  width:100%; display:flex; flex-wrap:wrap; justify-content:center;
  gap:clamp(.6rem,2.5vw,.9rem);
}
.btn-share{
  display:inline-flex; align-items:center; justify-content:center; gap:.6rem;
  padding-left:1.6rem; padding-right:1.9rem; min-height:47px;
}
.btn-share .sh-ic{ width:17px; height:17px; flex:0 0 auto; text-indent:0 }
.btn-share .sh-ic svg{ width:100%; height:100%; fill:none; stroke:currentColor;
  stroke-width:1.4; stroke-linecap:round; stroke-linejoin:round }
.btn-gold.btn-share .sh-ic svg{ stroke:#08150F }
/* the ghost buttons sit on a photograph here, so they need a real ground */
.share-scene .btn-ghost{ background:linear-gradient(180deg,rgba(9,24,18,.82),rgba(3,10,7,.88)); backdrop-filter:blur(3px) }
.share-url{
  margin-top:clamp(.9rem,3vh,1.3rem); word-break:break-all;
  text-indent:0; letter-spacing:.1em; color:rgba(244,237,223,.42);
  text-shadow:0 2px 10px rgba(0,0,0,.9);
}

/* ══════════════════ 9b · GALLERY ══════════════════
   Self-hiding: main.js leaves the whole section `hidden` while
   gallery.images is empty in config, so an invitation with no photographs has
   no empty rail on it. */
.gallery-stage{ padding-left:0; padding-right:0; min-height:auto }
.gallery-stage>.inner{ padding:0 clamp(1.4rem,6vw,4rem) }

.gallery-rail{
  position:relative; z-index:2; width:100%;
  margin-top:clamp(1.4rem,5vh,2.4rem);
  overflow-x:auto; overflow-y:hidden;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
  cursor:grab;
  overscroll-behavior-x:contain;         /* never hand a flick to the page */
}
.gallery-rail::-webkit-scrollbar{ display:none }
.gallery-rail.dragging{ cursor:grabbing }
.gallery-rail.dragging .gl-shot{ pointer-events:none }

.gallery-track{
  display:flex; align-items:center;
  gap:clamp(.6rem,2vw,1.1rem);
  padding:0 clamp(1.4rem,6vw,4rem);
  width:max-content;
}
.gl-shot{
  position:relative; flex:0 0 auto; overflow:hidden; border-radius:3px;
  height:clamp(230px,42vh,420px);
  background:var(--forest-850);
  box-shadow:0 0 0 1px var(--gold-line-soft), 0 24px 44px -34px rgba(0,0,0,.95);
  opacity:0; transform:translateX(38px);
  transition:opacity 1.1s var(--ease), transform 1.4s var(--ease), box-shadow .55s var(--ease);
  border:0; padding:0; margin:0; cursor:pointer;
}
.gl-shot.in{ opacity:1; transform:none }
.gl-shot img{ height:100%; width:auto; display:block; filter:saturate(.96) }
.gl-shot::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(180deg,rgba(4,12,9,.12),rgba(4,12,9,0) 36%,rgba(4,12,9,.3));
  transition:opacity .5s var(--ease);
}
.gl-shot:hover{ box-shadow:0 0 0 1px var(--gold-line), 0 26px 50px -30px rgba(0,0,0,.95) }
.gl-shot:hover::after{ opacity:.4 }

.rail-fade{
  position:absolute; top:0; bottom:0; width:clamp(28px,7vw,90px);
  pointer-events:none; z-index:3;
}
.rail-fade.left { left:0;  background:linear-gradient(90deg,#07110D,rgba(7,17,13,0)) }
.rail-fade.right{ right:0; background:linear-gradient(270deg,#07110D,rgba(7,17,13,0)) }

.gallery-hint,.gallery-note{
  position:relative; z-index:2; text-align:center;
  padding:0 clamp(1.4rem,6vw,4rem);
}
.gallery-hint{ margin-top:clamp(1rem,3.5vh,1.5rem); color:var(--gold-hi); opacity:.75 }
.gallery-note{ margin-top:.55rem }

/* ── lightbox ─────────────────────────────────────────────────────────── */
.lightbox{
  position:fixed; inset:0; z-index:90;
  display:flex; align-items:center; justify-content:center;
  background:rgba(3,8,6,.94);
  backdrop-filter:blur(10px);
  opacity:0; transition:opacity .45s var(--ease);
}
.lightbox.in{ opacity:1 }
.lb-stage{ margin:0; max-width:88vw; max-height:82svh; display:flex }
.lb-stage img{
  max-width:88vw; max-height:82svh; width:auto; height:auto; object-fit:contain;
  border-radius:3px; box-shadow:0 0 0 1px var(--gold-line), 0 40px 90px -40px #000;
  transform:scale(.97); opacity:0;
  transition:transform .6s var(--ease), opacity .5s var(--ease);
}
.lightbox.in .lb-stage img{ transform:none; opacity:1 }
.lb-close{
  position:absolute; top:clamp(.8rem,3vh,1.8rem); right:clamp(.8rem,3vw,1.8rem);
  width:44px; height:44px; border-radius:50%; z-index:2;
  font-size:1.7rem; line-height:1; color:var(--ink);
  border:1px solid var(--gold-line); background:rgba(10,23,18,.7);
}
.lb-nav{
  position:absolute; top:50%; transform:translateY(-50%); z-index:2;
  width:48px; height:48px; border-radius:50%;
  font-size:2rem; line-height:1; color:var(--gold-hi);
  border:1px solid var(--gold-line); background:rgba(10,23,18,.7);
  transition:background .4s var(--ease), border-color .4s var(--ease);
}
.lb-nav:hover{ background:rgba(20,48,38,.9); border-color:var(--gold-hi) }
.lb-nav.prev{ left:clamp(.5rem,2.5vw,2rem) }
.lb-nav.next{ right:clamp(.5rem,2.5vw,2rem) }
.lb-count{
  position:absolute; bottom:clamp(1rem,4vh,2.2rem); left:50%; transform:translateX(-50%);
  margin:0; font-family:var(--sans); font-weight:200; font-size:.6rem;
  letter-spacing:.3em; text-indent:.3em; color:var(--ink-mute);
}

/* ══════════════════ 10 · FINALE ══════════════════ */
.finale-scene{
  position:relative; width:100%; min-height:100svh;
  display:flex; align-items:flex-end; justify-content:center; overflow:hidden;
  background:#030806;
}
.finale-img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  object-position:50% 38%;
}
/* The closing frame is a bright amber sky over a dark hillside, so the scrim
   only has to deepen the lower third — the type never leaves it. */
.finale-scrim{
  position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(120% 76% at 50% 30%, rgba(3,8,6,0) 32%, rgba(3,8,6,.5) 100%),
    linear-gradient(180deg, rgba(3,8,6,.55) 0%, rgba(3,8,6,.08) 24%, rgba(3,8,6,.74) 62%, rgba(3,8,6,.97) 100%);
}
.finale-type{
  position:relative; z-index:2; width:100%; max-width:var(--maxw);
  display:flex; flex-direction:column; align-items:center; gap:.55rem;
  text-align:center; padding:0 1.4rem clamp(3.4rem,11vh,5.6rem);
}
.fin-sub{
  margin:0; font-family:var(--serif); font-style:italic;
  font-size:clamp(1.05rem,4.3vw,1.25rem); color:var(--ink-soft);
}
.fin-verse{
  margin:.3rem 0 .2rem; padding:0; max-width:40ch;
  display:flex; flex-direction:column; align-items:center; gap:.45rem;
}
.fin-verse p{
  margin:0; font-family:var(--serif); font-style:italic; font-weight:300;
  font-size:clamp(.95rem,3.8vw,1.05rem); line-height:1.7; color:var(--ink);
  text-shadow:0 2px 16px rgba(0,0,0,.8);
}
.fin-verse cite{
  font-style:normal; font-family:var(--sans); font-weight:200; font-size:.53rem;
  letter-spacing:.28em; text-indent:.28em; text-transform:uppercase; color:var(--gold-hi);
}

/* ══════════════════ chrome ══════════════════ */
.progress-rail{
  position:fixed; left:0; right:0; bottom:0; height:2px; z-index:60;
  background:rgba(255,255,255,.045); pointer-events:none;
}
.progress-rail i{
  display:block; height:100%; width:0;
  background:linear-gradient(90deg,#8A6524,#C9A24B 40%,#F3E2B6 70%,#C9A24B);
  box-shadow:0 0 12px rgba(201,162,75,.7);
}

/* ══════════════════ desktop tier ══════════════════ */
@media (min-width:900px){
  :root{ --maxw:700px; --maxw-wide:1020px }
  .stage{ min-height:78svh; padding:clamp(5rem,11vh,7.5rem) 2.5rem }

  .display{ font-size:clamp(1.8rem,3.2vw,2.5rem) }
  .display.xl{ font-size:clamp(2.6rem,4.4vw,3.6rem) }

  .tt-platter{ width:420px }
  .mini-meta b{ max-width:180px }

  .dials{ gap:clamp(1rem,2.6vw,1.8rem) }
  .dial-ring{ max-width:150px }
  .dial-num{ font-size:2.5rem }

  /* Holy Week: photograph and copy side by side, swapping sides each station
     so the eye crosses the page rather than running down one edge. */
  .station{ flex-direction:row; align-items:center; gap:clamp(1.8rem,4vw,3rem) }
  .station.side-right{ flex-direction:row-reverse }
  .station.side-right .st-copy{ text-align:right }
  .station.side-right .st-index{ flex-direction:row-reverse }
  .station.side-right .st-rule{ background:linear-gradient(270deg,var(--gold-line),transparent) }
  .station.side-right .st-verse{
    padding:.1rem 1rem .1rem 0; border-left:0; border-right:1px solid var(--gold-line);
  }
  .st-photo{ flex:0 0 52%; max-width:52% }
  .st-copy{ flex:1 }
  .station.side-left { transform:translate(-3%, 26px) }
  .station.side-right{ transform:translate(3%, 26px) }
  .station.in{ transform:none }

  .event-card{ max-width:720px }
  .ec-item{ grid-template-columns:auto 26px 1fr; padding:.95rem 0 }

  .detail-cards{ grid-template-columns:1fr 1fr }
  .map-frame{ aspect-ratio:21/8 }


  .rsvp-card{ max-width:620px }
  .gl-shot{ height:clamp(300px,52vh,480px) }
  .finale-type{ max-width:760px }
}
@media (min-width:1400px){
  :root{ --maxw:780px; --maxw-wide:1120px }
}

/* ══════════════════ LANDSCAPE OPENING ══════════════════
   Same two-band shape as portrait, tightened. The bright horizon runs 33–58%
   of the frame, so the groups have less dark to sit in and the type comes
   down a size rather than creeping onto the sky. */
@media (min-aspect-ratio: 1/1){
  .hero-type{ padding:clamp(1.2rem,4vh,2.6rem) clamp(1.6rem,5vw,4.5rem) clamp(2rem,7vh,3.6rem) }
  .ht-title{ font-size:clamp(1.75rem,3.5vw,2.9rem) }
  .ht-acclaim{ font-size:clamp(2.2rem,5.6vw,3.6rem) }
  .ht-group{ max-width:min(760px,80vw) }
}

/* landscape phones — short and wide, so tighten before anything clips */
@media (max-height:520px) and (orientation:landscape){
  .hero-type{ padding:.9rem 1.4rem 1rem }
  .ht-title{ font-size:1.1rem }
  .ht-acclaim{ font-size:1.7rem }
  .ht-orn{ display:none }
  .film-loader{ bottom:auto; top:50%; transform:translate(-50%,-50%) }
  .film-loader.gone{ transform:translate(-50%,-50%) translateY(6px) }
  .tap-cue{ bottom:1.1rem }
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important }
  .reveal{ opacity:1; transform:none; filter:none }
}
