/* ============================================================================
   ANNUAL THANKSGIVING SERVICE — near-black, antique gold, amber, parchment

   The palette is taken from the footage, not chosen in the abstract:
     the card film is a NEAR-BLACK envelope in charcoal-brown paper with
     ANTIQUE GOLD foil lettering, a gold wax seal bearing a cross and wheat,
     and gold foliage pressed into the corners;
     the scene film is a congregation with hands raised, lit from behind in
     warm golden light.

   So near-black is the ground colour (where the Easter template used
   evergreen and a wedding site would use navy), parchment is the ink, antique
   gold is the foil, and amber/honey is the accent and the glow.

   Richer and darker than the Easter template. It should read as gold leaf on
   black stationery — grateful and dignified, not festive.
   ========================================================================== */

:root{
  /* near-black and charcoal-brown field — the envelope stock */
  --night-950:#050403;
  --night-900:#0A0806;
  --night-850:#100C08;
  --night-800:#16100B;
  --night-700:#211710;
  --night-600:#2E2015;

  /* antique gold foil — the luminous variant, which never washes out on dark
     grounds. Carried over from the reference implementation unchanged. */
  --gold:#C8A253;
  --gold-hi:#E7C77E;
  --gold-pale:#F2E0B4;
  --gold-deep:#8A6524;
  --gold-line:rgba(200,162,83,.32);
  --gold-line-soft:rgba(200,162,83,.17);

  /* warm amber and honey — the light behind the congregation */
  --amber:#D98A2B;
  --honey:#E8A94A;
  --ember:#A8511A;

  /* autumn: the harvest end of the same fire. These are what carry the colour
     in the section grounds — the near-black is still the ground, but it is lit
     from the edges by copper and rust rather than left as a flat void. */
  --copper:#C86A25;
  --rust:#8A3B12;
  --russet:#5E2A12;

  /* ink — cream and parchment, off the card in the film */
  /* These alphas are set against the BRIGHTEST patch of the warmest section —
     rgb(110,72,25), right under the copper crown — because that is where the
     page is hardest to read, and it is measured rather than eyeballed:
       --ink       6.6 : 1
       --ink-soft  5.2 : 1   (body copy, clears the 4.5 target)
       --ink-mute  3.9 : 1   (captions and notes, clears the 3.0 target)
     Lifting the grounds without lifting these would have quietly dropped body
     copy to 3.5 : 1. */
  --ink:#F2E7D3;
  --ink-soft:rgba(242,231,211,.84);
  --ink-mute:rgba(242,231,211,.66);
  --ink-faint:rgba(242,231,211,.42);

  --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:1200ms;
}

*,*::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 scroll
   hint, both form confirmations, the thanksgiving wall, the members-only cell
   group row — and without this every one of them would stay in the layout,
   holding space and intercepting pointer events while invisible. Any such
   element that is also `position:fixed; inset:0` would sit over the whole
   page and swallow every click on the site, "Tap To Open" included. Cheap
   rule, whole class of bug. */
[hidden]{ display:none !important }
html,body{margin:0;padding:0}
html{-webkit-text-size-adjust:100%; scroll-behavior:auto}

body{
  background:#0D0906;
  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
   "ANNUAL THANKSGIVING SERVICE" 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.5rem,6.2vw,2.1rem);
  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,
    #B58936 0%, #D3AC5F 14%, #EDD498 30%,
    #FFF4D6 46%, #EDD498 62%, #CDA451 80%, #B58936 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(200,162,83,.6);
}

/* a scripture set apart from the running copy */
.verse-block{
  margin:.5rem 0 .2rem; padding:0; max-width:44ch;
  display:flex; flex-direction:column; align-items:center; gap:.5rem;
}
.verse-block p{
  margin:0; font-family:var(--serif); font-style:italic; font-weight:300;
  font-size:clamp(1rem,4vw,1.1rem); line-height:1.72; color:var(--ink);
}
.verse-block 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);
}

/* ── 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) }

/* FOUR-pass grounds. The earlier version used two washes at low alpha over a
   near-black field, and it read as brown-grey: technically warm, visibly
   flat. These keep exactly the same stock — near-black paper, brown ink — but
   light it properly, from three directions:

     1. a copper crown spilling in from above the section
     2. a rust wash rising out of one lower corner
     3. a honey counter-glow from the opposite side, so the middle of the
        section is never the darkest part of it
     4. the field itself, whose browns have been lifted a step (#16100B →
        #221709) so the paper has a colour of its own rather than being black
        that happens to be sitting under something warm

   The three variants are deliberately different temperatures — copper, gold,
   full amber — so scrolling reads as moving through changing light instead of
   down one long brown page. */
.bg-night{
  background:
    radial-gradient(78% 52% at 50% -6%,  rgba(200,106,37,.42) 0%, rgba(200,106,37,0) 62%),
    radial-gradient(66% 48% at 6% 98%,   rgba(138,59,18,.40)  0%, rgba(138,59,18,0)  66%),
    radial-gradient(58% 44% at 97% 32%,  rgba(232,169,74,.18) 0%, rgba(232,169,74,0) 70%),
    linear-gradient(180deg,#17100A 0%,#201608 46%,#0C0805 100%);
}
.bg-night-deep{
  background:
    radial-gradient(74% 50% at 50% -4%,  rgba(150,80,26,.44)  0%, rgba(150,80,26,0)  60%),
    radial-gradient(72% 52% at 94% 100%, rgba(200,162,83,.21) 0%, rgba(200,162,83,0) 64%),
    radial-gradient(56% 42% at 4% 26%,   rgba(138,59,18,.28)  0%, rgba(138,59,18,0)  68%),
    linear-gradient(180deg,#110C07 0%,#191108 52%,#080604 100%);
}
.bg-night-warm{
  background:
    radial-gradient(86% 58% at 50% -2%,  rgba(217,138,43,.44) 0%, rgba(217,138,43,0) 64%),
    radial-gradient(78% 56% at 14% 100%, rgba(168,81,26,.42)  0%, rgba(168,81,26,0)  62%),
    radial-gradient(60% 46% at 96% 42%,  rgba(232,169,74,.17) 0%, rgba(232,169,74,0) 70%),
    linear-gradient(180deg,#1B1208 0%,#281908 48%,#0F0A05 100%);
}

/* a woven tooth you feel more than see — laid paper, not a texture. The warp
   is tinted amber rather than white, so the weave picks up the section's own
   light instead of dusting grey over it. */
.stage::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  opacity:.6;
  background-image:
    repeating-linear-gradient(90deg,rgba(255,206,138,.026) 0 1px,transparent 1px 3px),
    repeating-linear-gradient(0deg, rgba(0,0,0,.07)        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(13,9,6,0),rgba(13,9,6,.72));
}

/* ── section ornaments: the cross and wheat from the wax seal ─────────────
   The card film closes on a gold wax seal bearing a cross over two ears of
   wheat. These are that seal, drawn as line art and bled off the corners of
   each section, so the motif that opens the invitation keeps recurring
   quietly underneath it.

   Line art, not filled shapes: at this opacity a filled cross turns into a
   smudge, while a 1px stroke stays legible and reads as engraving. They sit
   at z-index 0, behind .inner (z-index 2), and .stage has overflow:hidden, so
   bleeding them past the edge crops them cleanly instead of widening the
   page. */
.stage-orn{
  position:absolute; z-index:0; pointer-events:none;
  width:clamp(104px,19vw,196px);
  color:var(--gold);
  opacity:0; transform:translateY(10px);
  transition:opacity 2.4s var(--ease), transform 2.4s var(--ease);
}
.stage-orn.in{ opacity:.2; transform:none }
.stage-orn svg{ width:100%; height:auto; display:block; overflow:visible }
.stage-orn .ln{
  fill:none; stroke:currentColor; stroke-width:1.15;
  stroke-linecap:round; stroke-linejoin:round;
}
/* the glory behind the crossing — finer and fainter than the cross itself, or
   it competes with it */
.stage-orn .ray{ fill:none; stroke:currentColor; stroke-width:.75; opacity:.45 }

.stage-orn.tl{ top:clamp(-1.4rem,-2vh,0rem);    left:clamp(-2.2rem,-4vw,-.6rem) }
.stage-orn.br{ bottom:clamp(-1.4rem,-2vh,0rem); right:clamp(-2.2rem,-4vw,-.6rem);
               transform:translateY(10px) scaleX(-1) }
.stage-orn.br.in{ transform:scaleX(-1) }

/* A single large watermark behind the two sections that carry the most
   weight. Much fainter than the corner pieces and centred, so it reads as
   paper stock rather than decoration. */
.stage-mark{
  position:absolute; z-index:0; pointer-events:none;
  left:50%; top:50%; transform:translate(-50%,-50%);
  width:min(74%,520px); color:var(--copper);
  opacity:0; transition:opacity 3s var(--ease);
}
.stage-mark.in{ opacity:.085 }
.stage-mark svg{ width:100%; height:auto; display:block }
.stage-mark .ln{ fill:none; stroke:currentColor; stroke-width:.7; stroke-linecap:round }

@media (max-width:560px){
  .stage-orn{ width:clamp(88px,26vw,130px) }
  .stage-orn.in{ opacity:.16 }
  .stage-mark.in{ opacity:.07 }
}

/* ── 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:#050403; 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:#050403;
}
#sceneVideo{ z-index:1 }
/* 1.2s rather than the 1.6s the reference uses, and that is deliberate. The
   landscape card film CUTS TO BLACK for its final 0.375s; a longer dissolve
   would still be running when it did, dragging a half-opaque black plate
   across the congregation. main.js also parks the card on its last lit frame
   (films.hold.landscape.card) so the black tail is never on screen at all. */
#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 HELD frame of the congregation film, and the grading is the
   opposite of what you would expect — see the luminance table above the hero
   markup in index.html. The scene is a blazing backlit sunburst across the
   TOP third (mean luma 135–180) and dark heads and shoulders across the
   BOTTOM half (18–40).

   So this vignette does almost nothing at the top — the sunburst is the shot,
   and veiling it would be vandalism — and does the real work at the foot,
   deepening the already-dark crowd into a ground the gold can sit on. */
.hero-vignette{
  position:absolute; inset:0; z-index:3; pointer-events:none;
  background:
    radial-gradient(125% 80% at 50% 12%, rgba(0,0,0,0) 38%, rgba(5,4,3,.55) 100%),
    linear-gradient(180deg,
      rgba(5,4,3,.34) 0%, rgba(5,4,3,.10) 12%, rgba(5,4,3,0) 30%,
      rgba(5,4,3,.16) 48%, rgba(5,4,3,.62) 68%, rgba(5,4,3,.9) 86%, rgba(5,4,3,.97) 100%);
}
/* Portrait keeps light much further down the frame (mean luma is still ~55 at
   two-thirds height), so the floor has to start earlier and come on harder. */
html:not(.is-wide) .hero-vignette{
  background:
    radial-gradient(125% 72% at 50% 10%, rgba(0,0,0,0) 40%, rgba(5,4,3,.48) 100%),
    linear-gradient(180deg,
      rgba(5,4,3,.38) 0%, rgba(5,4,3,.12) 14%, rgba(5,4,3,0) 28%,
      rgba(5,4,3,.24) 44%, rgba(5,4,3,.6) 60%, rgba(5,4,3,.88) 78%, rgba(5,4,3,.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(231,199,126,.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:#FAF2E2;
  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 — the card artwork behind it is near-black with
   gold on it, so a plain gold label would disappear into the foil. */
.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:#FAF0DC; padding:.85rem 1.7rem; border-radius:999px;
  background:linear-gradient(180deg,rgba(33,23,16,.9),rgba(5,4,3,.94));
  border:1px solid rgba(231,199,126,.52);
  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,.9), 0 0 0 6px rgba(231,199,126,.10) }
  50%    { box-shadow:0 12px 34px -14px rgba(0,0,0,.9), 0 0 0 16px rgba(231,199,126,0) }
}
.opening-scene.is-playing .film-cue{ opacity:0; transform:translateX(-50%) scale(.94) }

/* ── hero type ────────────────────────────────────────────────────────────
   ANCHORED TO THE FOOT OF THE FRAME, as one group. The reference
   implementation splits its type into a top group and a bottom group with
   space-between, because its footage is dark at both ends. This footage is
   not: the top third is the brightest part of the shot. A group up there
   would be unreadable whatever colour it was, so there isn't one. */
.hero-type{
  position:absolute; inset:0; z-index:5; pointer-events:none;
  display:flex; flex-direction:column; justify-content:flex-end; align-items:center;
  text-align:center;
  padding:clamp(1.4rem,4vh,2.6rem) 1.3rem clamp(2.6rem,8vh,4.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:.55rem }
/* "You Are Invited To Our" and the date line beneath the title.
   These were set at .55–.66rem, which is 9–10.5px — genuinely too small to
   read on a phone held at arm's length, and lost entirely on a large desktop
   where everything around them scales up. Nearly half again as large now, and
   the tracking comes down a touch: at .4em a longer line was being stretched
   so wide it broke into an awkward second row. */
.ht-eyebrow{
  font-family:var(--sans); font-weight:300; margin:0;
  font-size:clamp(.92rem,3.9vw,1.05rem);
  letter-spacing:.3em; text-indent:.3em; text-transform:uppercase;
  color:#F4EAD6; text-shadow:0 2px 14px rgba(0,0,0,.95), 0 0 30px rgba(0,0,0,.7);
  max-width:36ch; line-height:1.55;
}
.ht-sub{ color:var(--ink-soft); font-size:clamp(.82rem,3.4vw,.94rem); letter-spacing:.26em; text-indent:.26em }
.ht-title{
  font-family:var(--serif); font-weight:300;
  font-size:clamp(1.28rem,5.6vw,2.1rem); line-height:1.26;
  letter-spacing:.24em; text-indent:.24em; text-transform:uppercase;
  padding:.16em .12em .2em;
  max-width:15ch;
  /* The foil is painted through background-clip, so it cannot carry a
     text-shadow of its own — a drop-shadow filter would soften the type
     permanently (gotcha 5). The vignette floor above is what gives it its
     ground instead. */
}
.ht-acclaim{
  font-family:var(--serif); font-weight:300;
  font-size:clamp(2.2rem,11vw,3.4rem); line-height:1.16;
  letter-spacing:.18em; text-indent:.18em; text-transform:uppercase;
  color:#FDF8ED; text-shadow:0 3px 24px rgba(0,0,0,.95), 0 0 46px rgba(217,138,43,.3);
}
.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(231,199,126,.75));
}
.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(231,199,126,.85);
}

.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(231,199,126,.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 }
/* NO CSS rotation here. This is the exact trap the note on .hand warns about,
   and it was live: the arc already carries `rotate(-90 50 50)` as an SVG
   attribute, the tick marks are drawn with twelve o'clock at the top, and the
   hand's own `rotate(deg 50 50)` is measured from there. Adding a further
   -90deg in CSS rotated the whole face on top of all that, so the dial started
   at NINE o'clock and the seconds marker pointed nowhere meaningful. */
.dial-ring svg{ width:100%; height:100%; overflow:visible }
.dial-ring .track{ fill:none; stroke:rgba(200,162,83,.15); 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(200,162,83,.55));
  transition:stroke-dashoffset .55s var(--ease);
}
.dial-ring .ticks line{ stroke:rgba(200,162,83,.3); stroke-width:1 }
.dial-ring .ticks line.major{ stroke:rgba(231,199,126,.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. */
/* THE PIVOT LIVES IN THE SVG `transform` ATTRIBUTE, AND NOWHERE ELSE.

   That is not a style preference, it is the fix for a bug this dial actually
   had. `rotate(deg 50 50)` as an attribute is exact and works on its own. Add
   a CSS `transition:transform` and the browser routes the attribute through
   the CSS transform machinery, where `transform-origin` applies — and the
   origin does not resolve to the centre of a 100x100 viewBox. The rotation
   was computed about the wrong point, came out as near-identity, and the
   marker sat frozen at twelve o'clock while the number counted down beside
   it. Setting it as a CSS transform instead did not help either; the two
   systems simply must not both be in play.

   So: no transition, no transform-origin, no transform-box. The marker ticks
   once per second, which is what a quartz second hand does anyway. */
.dial-ring .hand{
  stroke:var(--gold-hi); stroke-width:2.2; stroke-linecap:round;
}
.dial-ring .pin{ fill:rgba(231,199,126,.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:#FBF4E4; 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);
}

/* ══════════════════ 3 · A YEAR OF FAITHFULNESS ══════════════════
   Alternating left / right, joined by gold connectors — the same rhythm the
   reference implementation uses for its story, carrying scripture instead of
   photographs. No photographs have been supplied, so each entry renders an
   engraved numeral plate under a sheaf of wheat. Give an entry an `img` in
   config and the plate is replaced by the photograph with no other change. */
.year-list{
  width:100%; display:flex; flex-direction:column;
  gap:clamp(1.5rem,5vh,2.4rem); margin-top:clamp(1.2rem,4vh,2rem);
}
.year-item{
  position:relative; display:flex; align-items:center;
  gap:clamp(.9rem,4vw,1.6rem); text-align:left; opacity:0;
  transition:opacity 1.1s var(--ease), transform 1.3s var(--ease);
}
.year-item.from-left { transform:translateX(-9%) }
.year-item.from-right{ transform:translateX(9%); flex-direction:row-reverse; text-align:right }
.year-item.in{ opacity:1; transform:none }

.year-plate{
  flex:0 0 34%; max-width:34%; aspect-ratio:4/5; position:relative; overflow:hidden;
  border-radius:3px;
  display:grid; place-items:center;
  background:
    radial-gradient(120% 90% at 50% 18%, rgba(217,138,43,.2), rgba(217,138,43,0) 62%),
    linear-gradient(168deg,rgba(46,32,21,.82),rgba(6,5,4,.9));
  box-shadow:0 0 0 1px var(--gold-line-soft), 0 20px 40px -26px rgba(0,0,0,.98);
}
.year-plate::after{
  content:""; position:absolute; inset:7px; border-radius:2px;
  border:1px solid var(--gold-line-soft); pointer-events:none;
}
/* Stock photography arrives in whatever grade the photographer chose, and a
   bright frame punches a hole in a near-black page. These three adjustments
   pull every plate into the same warm, low-key register as the footage:
   desaturate slightly, lift contrast, and drop the brightness. */
.year-plate img{
  width:100%; height:100%; object-fit:cover;
  filter:saturate(.88) contrast(1.06) brightness(.8) sepia(.16);
}
/* A warm scrim over the photograph, under the gold keyline (which is ::after).
   Deepest at the foot, where the plate meets the copy. */
.year-plate.has-photo::before{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:
    radial-gradient(120% 90% at 50% 20%, rgba(217,138,43,.14), rgba(217,138,43,0) 62%),
    linear-gradient(180deg, rgba(8,6,4,.1) 0%, rgba(8,6,4,.28) 62%, rgba(8,6,4,.62) 100%);
}
.yp-mark{ display:flex; flex-direction:column; align-items:center; gap:.5rem; padding:1rem }
.yp-wheat{ width:clamp(20px,5vw,26px); height:auto }
.yp-wheat path{ fill:none; stroke:var(--gold); stroke-width:1.2; stroke-linecap:round; stroke-linejoin:round }
.yp-no{
  font-family:var(--serif); font-weight:300; line-height:1;
  font-size:clamp(1.5rem,6vw,2.1rem); letter-spacing:.16em; text-indent:.16em;
  background-image:linear-gradient(100deg,#B58936,#EDD498 46%,#C8A253);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
}
.yp-theme{
  font-family:var(--sans); font-weight:200; font-size:.5rem;
  letter-spacing:.26em; text-indent:.26em; text-transform:uppercase;
  color:var(--ink-mute); text-align:center;
}

.year-copy{ flex:1; min-width:0; display:flex; flex-direction:column; gap:.42rem }
.year-theme{
  font-family:var(--sans); font-weight:200; font-size:.56rem;
  letter-spacing:.3em; text-indent:.3em; text-transform:uppercase; color:var(--gold);
}
.year-title{
  font-family:var(--serif); font-weight:300; margin:0;
  font-size:clamp(1.12rem,4.6vw,1.4rem); line-height:1.24;
  color:#FAF2E0; letter-spacing:.03em;
}
/* The verse is the point of the section, so it gets the weight: an indented
   quotation with a gold stem, the way an order of service sets scripture. */
.year-verse{
  margin:.15rem 0 .1rem; padding:.15rem 0 .15rem .9rem;
  border-left:1px solid var(--gold-line);
  font-family:var(--serif); font-style:italic; font-weight:300;
  font-size:clamp(.92rem,3.6vw,1.02rem); line-height:1.66; color:var(--ink);
}
.year-item.from-right .year-verse{
  padding:.15rem .9rem .15rem 0;
  border-left:0; border-right:1px solid var(--gold-line);
}
.year-cite{
  display:block; margin-top:.35rem; font-style:normal;
  font-family:var(--sans); font-weight:200; font-size:.53rem;
  letter-spacing:.24em; text-indent:.24em; text-transform:uppercase; color:var(--gold-hi);
}
.year-text{
  font-size:clamp(.82rem,3.2vw,.89rem); line-height:1.72;
  color:var(--ink-mute); margin:0;
}

/* gold connector between entries */
.year-arrow{
  align-self:center; display:flex; flex-direction:column; align-items:center; gap:.18rem;
  opacity:0; transition:opacity 1s var(--ease) .2s;
}
.year-arrow.in{ opacity:1 }
.year-arrow i{
  display:block; width:1px; height:clamp(20px,4vh,30px);
  background:linear-gradient(180deg,rgba(200,162,83,0),rgba(200,162,83,.6));
}
.year-arrow svg{ width:11px; height:9px; display:block }
.year-arrow svg path{ fill:none; stroke:var(--gold); stroke-width:1.4; stroke-linecap:round; stroke-linejoin:round }

/* ══════════════════ shared card surface ══════════════════ */
.event-card,.detail-card,.gift-card,.rsvp-card,.thanks-note{
  position:relative;
  background:linear-gradient(168deg, rgba(46,32,21,.68), rgba(8,6,4,.8));
  border:1px solid var(--gold-line);
  border-radius:4px;
  box-shadow:
    0 1px 0 rgba(255,244,214,.07) inset,
    0 30px 60px -40px rgba(0,0,0,.98),
    0 4px 14px -10px rgba(0,0,0,.7);
  backdrop-filter:blur(2px);
}
/* engraved inner keyline */
.event-card::after,.detail-card::after,.gift-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, echoing the foil corners on the envelope */
.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,#B58936,#EDD498 46%,#C8A253);
  -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(200,162,83,.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(200,162,83,.28),rgba(200,162,83,.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:#FAF2E0; 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:#FAF2E0; 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,.98);
}
/* Google's embed ships only a light tile set, so it is inverted into a night
   map. invert + hue-rotate alone leaves it cold blue; dropping saturation and
   lifting contrast is what turns it into warm sepia rather than teal. */
.map-frame iframe{
  width:100%; height:100%; border:0;
  filter:invert(.93) hue-rotate(178deg) saturate(.4) sepia(.28) brightness(.92) contrast(1.1);
}
.map-veil{
  position:absolute; inset:0; pointer-events:none;
  mix-blend-mode:multiply;
  background:linear-gradient(180deg,rgba(74,48,22,.5),rgba(38,26,14,.32) 40%,rgba(74,48,22,.5));
  box-shadow:inset 0 0 70px rgba(6,4,3,.75);
}

/* Gold button. The ink is near-black rather than pure black, and the gold
   stays in the rich champagne band — a paler fill washes thin uppercase
   lettering out against it. At #0E0A06 on #E8CE90 this clears 10: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:#0E0A06;
  background:linear-gradient(100deg,#B48733 0%,#D3AB5C 30%,#E8CE90 50%,#D1AA5A 70%,#B48733 100%);
  background-size:200% 100%;
  box-shadow:0 16px 36px -20px rgba(0,0,0,.95), 0 0 0 1px rgba(255,244,214,.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(46,32,21,.7),rgba(8,6,4,.8));
  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:#0E0A06; 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 }

/* ══════════════════ 6 · GIVE THANKS / 7 · RSVP — shared form ══════════════════ */
.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:#120D09; 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 }

/* live character count on the thanksgiving box */
.field-count{
  position:absolute; right:.1rem; bottom:-1.1rem;
  font-family:var(--sans); font-weight:200; font-size:.53rem;
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-faint);
  transition:color .4s var(--ease);
}
.field-count.near{ color:var(--honey) }
.thanks-card .field:first-of-type{ margin-bottom:.5rem }

/* the checkboxes — "bringing someone new", "read this out", "keep my name off" */
.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(8,6,4,.55);
  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:#0E0A06; stroke-width:2.6; stroke-linecap:round; stroke-linejoin:round }
.check input:checked~.check-box{
  background:linear-gradient(100deg,#B58936,#E8CE90 46%,#D3AC5F); 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);
}

/* The rows are injected into this wrapper by buildRSVP(), so the gap that
   used to sit on .rsvp-card has to live here or every field collapses onto
   the next. */
.rsvp-fields{
  display:flex; flex-direction:column;
  gap:clamp(.95rem,3vh,1.2rem);
}
.rsvp-card .btn-gold{ margin-top:.3rem; width:100% }
.rsvp-privacy{
  text-align:center; color:var(--ink-faint);
  letter-spacing:.14em; text-indent:.14em; line-height:1.9; max-width:42ch;
  margin-left:auto; margin-right:auto;
}
.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(231,199,126,.26), rgba(200,162,83,.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;
}
/* the wheat seal draws over a longer path than a tick, so it needs its own
   dash length or the ear of wheat only ever half-appears */
.wheat-seal svg path{ stroke-dasharray:34; stroke-dashoffset:34 }
.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;
}

/* the wall of submitted thanksgivings — hidden entirely while thanks.wall is
   empty in config, so a fresh deploy has no empty frame on it */
.thanks-wall{
  width:100%; margin-top:clamp(1.4rem,5vh,2.2rem);
  display:grid; grid-template-columns:1fr; gap:clamp(.6rem,2.2vw,.9rem);
}
.thanks-note{
  padding:clamp(1rem,4vw,1.3rem) clamp(1rem,4vw,1.3rem);
  text-align:left; display:flex; flex-direction:column; gap:.5rem;
  opacity:0; transform:translateY(14px);
  transition:opacity 1.1s var(--ease), transform 1.3s var(--ease);
}
.thanks-note.in{ opacity:1; transform:none }
.thanks-note p{
  margin:0; font-family:var(--serif); font-style:italic; font-weight:300;
  font-size:clamp(.92rem,3.6vw,1rem); line-height:1.7; color:var(--ink);
}
.thanks-note cite{
  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);
}

/* ══════════════════ 8 · THANKSGIVING OFFERING ══════════════════ */
.gift-card{
  width:100%; margin-top:clamp(1rem,3.5vh,1.5rem);
  padding:clamp(1.7rem,6vw,2.2rem) clamp(1.2rem,5vw,1.8rem); text-align:left;
}
/* the card unseals rather than appearing — account details are private until
   someone actually asks for them */
.gift-card.sealed{ opacity:0; transform:translateY(-8px) }
.gift-card.unsealed{
  opacity:1; transform:none;
  transition:opacity .7s var(--ease), transform .8s var(--ease);
}
.gift-kicker{
  margin:0 0 1rem; text-align:center;
  font-family:var(--sans); font-weight:200; font-size:.56rem;
  letter-spacing:.34em; text-indent:.34em; text-transform:uppercase; color:var(--gold-hi);
}
.gift-rows{ margin:0; display:flex; flex-direction:column; gap:.1rem }
.gift-row{
  display:flex; align-items:baseline; justify-content:space-between; gap:1rem;
  padding:.62rem 0; border-bottom:1px solid var(--gold-line-soft);
}
.gift-row:last-child{ border-bottom:0 }
.gift-row dt{
  font-family:var(--sans); font-weight:200; font-size:.55rem;
  letter-spacing:.24em; text-indent:.24em; text-transform:uppercase;
  color:var(--ink-mute); flex:0 0 auto;
}
.gift-row dd{
  margin:0; font-family:var(--serif); font-weight:300;
  font-size:clamp(.92rem,3.6vw,1rem); color:#FAF2E0;
  text-align:right; letter-spacing:.03em; word-break:break-word;
}
.gift-warn{
  margin:1rem 0 0; padding-top:.9rem; border-top:1px solid var(--gold-line-soft);
  font-family:var(--sans); font-weight:200; font-size:.58rem; line-height:1.9;
  letter-spacing:.14em; text-transform:uppercase; color:#E7B27E; text-align:center;
}

/* ══════════════════ 9 · INVITE SOMEONE ══════════════════ */
.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:#0E0A06 }
.share-url{
  margin-top:clamp(.9rem,3vh,1.3rem); word-break:break-all;
  text-indent:0; letter-spacing:.1em; color:var(--ink-faint);
}

/* ══════════════════ 10 · FINALE ══════════════════ */
.finale-scene{
  position:relative; width:100%; min-height:100svh;
  display:flex; align-items:flex-end; justify-content:center; overflow:hidden;
  background:#050403;
}
.finale-img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  object-position:50% 30%;
}
/* Same measurement as the opening: the frame is blazing across the top and
   dark across the bottom, so the scrim leaves the sunburst alone and buries
   the foot of the frame, which is where every word sits. */
.finale-scrim{
  position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(125% 78% at 50% 14%, rgba(5,4,3,0) 34%, rgba(5,4,3,.5) 100%),
    linear-gradient(180deg, rgba(5,4,3,.34) 0%, rgba(5,4,3,.06) 20%, rgba(5,4,3,.3) 46%, rgba(5,4,3,.82) 68%, rgba(5,4,3,.98) 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:42ch;
  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,.85);
}
.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);
}

/* One quiet line acknowledging the photographers. Deliberately small and
   low-contrast — it is a courtesy, not a piece of the invitation. */
.photo-credit{
  margin:0; padding:1.8rem 1.4rem 2.6rem;
  text-align:center; background:#050403;
  font-family:var(--sans); font-weight:200;
  font-size:.53rem; letter-spacing:.24em; text-indent:.24em; text-transform:uppercase;
  color:rgba(242,231,211,.22); line-height:2;
}

/* ══════════════════ THE RECORD ══════════════════
   A vinyl in the bottom corner. It only appears once the guest has scrolled
   off the opening — nothing should compete with "Tap To Open" on that first
   screen — and it stays parked there afterwards so the music is always one
   tap away from being stopped.

   Sizing note: the disc is 54px and the whole pill has a 44px minimum touch
   target, which is the smallest a control should ever be on a phone. */
.record{
  position:fixed; z-index:70;
  left:clamp(.9rem,3vw,1.8rem); bottom:clamp(.9rem,3vh,1.8rem);
  display:flex; align-items:center; gap:.72rem;
  padding:.4rem .95rem .4rem .4rem; border-radius:999px;
  background:linear-gradient(150deg,rgba(46,32,21,.9),rgba(8,6,4,.92));
  border:1px solid var(--gold-line);
  box-shadow:0 18px 40px -22px rgba(0,0,0,.98), 0 0 0 1px rgba(255,244,214,.05) inset;
  backdrop-filter:blur(6px);
  opacity:0; transform:translateY(14px) scale(.96);
  transition:opacity .9s var(--ease), transform 1s var(--ease);
  pointer-events:none;
  max-width:min(76vw,300px);
}
.record.in{ opacity:1; transform:none; pointer-events:auto }

/* ── the disc ─────────────────────────────────────────────────────────────
   Grooves are a repeating-conic gradient rather than a stack of rings: one
   paint, no extra elements, and it reads as vinyl at this size. */
.record-disc{
  position:relative; flex:0 0 auto;
  width:54px; height:54px; border-radius:50%;
  display:grid; place-items:center; padding:0;
  background:
    repeating-conic-gradient(from 0deg,
      rgba(255,244,214,.05) 0deg 3deg, rgba(0,0,0,0) 3deg 6deg),
    radial-gradient(circle at 34% 30%, rgba(255,244,214,.14), rgba(255,244,214,0) 46%),
    radial-gradient(circle, #241a12 0 34%, #120c08 34% 100%);
  box-shadow:0 0 0 1px rgba(200,162,83,.35), 0 6px 16px -8px rgba(0,0,0,.9);
  animation:spin 4.5s linear infinite;
  animation-play-state:paused;
}
.record.playing .record-disc{ animation-play-state:running }
@keyframes spin{ to{ transform:rotate(360deg) } }

/* the paper label at the centre of the disc */
.rd-label{
  position:absolute; width:56%; height:56%; border-radius:50%;
  background:linear-gradient(150deg,#B58936,#8A6524 60%,#6B4C19);
  box-shadow:0 0 0 1px rgba(255,244,214,.18) inset;
}
.rd-spindle{
  position:absolute; width:5px; height:5px; border-radius:50%;
  background:#0D0906; box-shadow:0 0 0 1px rgba(0,0,0,.6);
}

/* The pause/play glyph rides ON the label. It must NOT spin with the disc, or
   it tumbles — so it is a sibling of the animated background rather than a
   child, and carries the counter-rotation of nothing at all. */
.rd-icon{
  position:relative; z-index:2; width:19px; height:19px;
  animation:spinBack 4.5s linear infinite; animation-play-state:paused;
}
.record.playing .rd-icon{ animation-play-state:running }
@keyframes spinBack{ to{ transform:rotate(-360deg) } }
.rd-icon path{
  fill:none; stroke:#100A05; stroke-width:2.1; stroke-linecap:round; stroke-linejoin:round;
}
.rd-icon .rd-play path{ fill:#100A05; stroke-width:1.4 }
.rd-icon .rd-play{ display:none }
.rd-icon .rd-pause{ display:block }
.record.paused .rd-icon .rd-play{ display:block }
.record.paused .rd-icon .rd-pause{ display:none }

.record-disc:focus-visible{ outline:2px solid var(--gold-hi); outline-offset:3px }

/* ── the label copy ──────────────────────────────────────────────────────── */
.record-meta{ min-width:0; display:flex; flex-direction:column; gap:.12rem }
.rd-title{
  margin:0; font-family:var(--serif); font-weight:400;
  font-size:.94rem; line-height:1.2; color:#FAF2E0; letter-spacing:.02em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.rd-artist{
  margin:0; font-family:var(--sans); font-weight:200;
  font-size:.54rem; letter-spacing:.24em; text-indent:.24em; text-transform:uppercase;
  color:var(--ink-mute); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* On a narrow phone the pill would crowd the scroll-progress rail and the
   thumb's natural resting place, so it drops the wordmark and becomes just
   the disc. The title is still on the button's accessible name. */
@media (max-width:430px){
  .record{ padding:.36rem; gap:0 }
  .record-meta{ display:none }
}

@media (prefers-reduced-motion:reduce){
  .record-disc, .rd-icon{ animation:none !important }
}

/* ══════════════════ 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,#C8A253 40%,#F2E0B4 70%,#C8A253);
  box-shadow:0 0 12px rgba(200,162,83,.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) }

  .dials{ gap:clamp(1rem,2.6vw,1.8rem) }
  .dial-ring{ max-width:150px }
  .dial-num{ font-size:2.5rem }

  .year-list{ gap:clamp(2rem,6vh,3.2rem) }
  .year-plate{ flex:0 0 30%; max-width:30%; aspect-ratio:1 }
  .year-text{ font-size:.92rem; line-height:1.76 }
  .year-title{ font-size:1.45rem }
  .year-verse{ font-size:1.06rem }

  .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 }
  .gift-card{ max-width:620px }
  .thanks-wall{ grid-template-columns:1fr 1fr }
  .finale-type{ max-width:760px }
}
@media (min-width:1400px){
  :root{ --maxw:780px; --maxw-wide:1120px }
}

/* ══════════════════ LANDSCAPE OPENING ══════════════════
   The type stays at the foot in both orientations — the difference is only
   that a wide frame gives the words more room across and less down, so they
   come in a size and tighten their stack rather than climbing toward the
   sunburst. */
@media (min-aspect-ratio: 1/1){
  .hero-type{ padding:clamp(1.2rem,4vh,2.6rem) clamp(1.6rem,5vw,4.5rem) clamp(2.2rem,7vh,3.8rem) }
  .ht-title{ font-size:clamp(1.15rem,2.4vw,1.95rem); max-width:28ch }
  .ht-acclaim{ font-size:clamp(2rem,5.2vw,3.2rem) }
  .ht-group{ max-width:min(820px,84vw); gap:.45rem }
}

/* 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-eyebrow{ font-size:.72rem; letter-spacing:.24em; text-indent:.24em }
  .ht-sub{ font-size:.66rem }
  .ht-title{ font-size:1.05rem }
  .ht-acclaim{ font-size:1.6rem }
  .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 }
}
