/* ==============================================================
   YOURI & NIKKESHA - WEDDING INVITATION . STYLESHEET
   All styling for the site lives in this one file.

   * COLOURS - the palette is at the very top (":root"):
      ivory / sage green #8CAE9F / gold. Change any colour of the
      whole site there, in one place.

   Sections: [C0] palette . [C0b] layout (grid/utilities)
     [C1] envelope . [C2] site base . [C2b] utilities
     [C3] RSVP button . [C4] hero . [C5] shared . [C6] story
     [C7] events . [C8] venue . [C9] gallery . [C10] rsvp
     [C11] footer . [C12] timeline
============================================================== */
/* ===== [C0] PALETTE - change any colour of the whole site here ===== */
:root {
  /* === PALETTE - Ivory (main) . Sage green (detail) . Gold (secondary detail) ===
     Change any colour of the whole site here in one place. */
  --ivory:#f8f4e9; --ivory2:#efe7d3; --ivory3:#e2d8bd; --cream:#fbf8f2;
  --sage:#8CAE9F; --sage-lt:#B4CBC1; --sage-dk:#496E5E; --sage-xdk:#22352D;
  --gold:#c8a84f; --gold-lt:#e6cd84; --gold-dk:#9c7a2c;
  --text:#2a1e0a; --text-lt:#5a4a2a;
}
/* === ENVELOPE PAPER - your floral lace paper, embedded (used on every face of the envelope) === */
:root { --paper-band:url("../assets/paper-band.webp"); }

:root { --paper:url("../assets/paper-lace.webp"); }
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
:root{ --font-scale:1; }   /* global text-size dial (set from js/settings.js) */

/* -- Wax seal sizes: desktop / phone ------------------------------
   js/settings.js pushes two values per seal (--seal-scale for wide
   screens, --seal-scale-m for phones). These two lines pick the one
   that applies; every rule below then reads only the "-now" name, so
   the choice is made once, here. Leaving the phone value out simply
   falls back to the desktop one.                                    */
:root{
  --seal-scale-now: var(--seal-scale, 1);
  --fab-scale-now : var(--fab-scale,  1);
  /* Where the floating RSVP seal sits, and how far its label stands off it.
     The fallbacks are what the old clamp() resolved to on a wide screen. */
  --fab-side-now  : var(--fab-side,   28px);
  --fab-bottom-now: var(--fab-bottom, 28px);
  --fab-gap-now   : var(--fab-gap,    12px);
}
@media(max-width:640px){
  :root{
    --seal-scale-now: var(--seal-scale-m, var(--seal-scale, 1));
    --fab-scale-now : var(--fab-scale-m,  var(--fab-scale,  1));
    /* and what it resolved to on a phone */
    --fab-side-now  : var(--fab-side-m,   var(--fab-side,   14px));
    --fab-bottom-now: var(--fab-bottom-m, var(--fab-bottom, 14px));
    --fab-gap-now   : var(--fab-gap-m,    var(--fab-gap,    12px));
  }
}
html{scroll-behavior:smooth;font-size:calc(100% * var(--font-scale, 1));}
body{font-family:'Cormorant Garamond',serif;background:#0F1613;color:var(--text);overflow-x:hidden;}
body.env-open{overflow:hidden;height:100vh;}

/* ===== [C0b] LAYOUT - small replacement for the handful of Bootstrap
   grid/utility classes this page actually used (container, row, col-*,
   gutters, alignment). Bootstrap itself has been removed: it added a
   ~230KB stylesheet + JS bundle for about a dozen classes, almost all
   of it unused. This does the same job in ~30 lines. ===== */
/* CONTENT WIDTH AND SIDE MARGINS - set from SETTINGS.contentLayout.
   --content-pad is the breathing space between the text and the edge of
   the screen. --content-max caps how wide the content column ever gets:
   every step below is wrapped in min(), so lowering it narrows the whole
   site at once while narrow screens keep their own smaller width. */
.container{
  width:100%;
  padding-left:var(--content-pad,12px);
  padding-right:var(--content-pad,12px);
  margin-left:auto;margin-right:auto;
}
@media(min-width:576px) {.container{max-width:min(540px,  var(--content-max,100%));}}
@media(min-width:768px) {.container{max-width:min(720px,  var(--content-max,100%));}}
@media(min-width:992px) {.container{max-width:min(960px,  var(--content-max,100%));}}
@media(min-width:1200px){.container{max-width:min(1140px, var(--content-max,100%));}}
@media(min-width:1400px){.container{max-width:min(1320px, var(--content-max,100%));}}
@media(max-width:991px){ .container{padding-left:var(--content-pad-tablet,var(--content-pad,12px));
                                    padding-right:var(--content-pad-tablet,var(--content-pad,12px));} }
@media(max-width:575px){ .container{padding-left:var(--content-pad-phone,var(--content-pad,12px));
                                    padding-right:var(--content-pad-phone,var(--content-pad,12px));} }
.row{
  --gx: .75rem; /* half-gutter, both sides - 1.5rem gutter by default */
  display:flex;
  flex-wrap:wrap;
  margin-left:calc(var(--gx) * -1);
  margin-right:calc(var(--gx) * -1);
  row-gap:0;
}
.row.g-5{ --gx:1.5rem; row-gap:3rem; }
.col-12,.col-md-8,.col-md-10,.col-lg-4,.col-lg-5,.col-lg-7,.col-lg-8{
  padding-left:var(--gx);
  padding-right:var(--gx);
  flex:0 0 auto;
  width:100%;
}
@media(min-width:768px){
  .col-md-8{width:66.66667%;}
  .col-md-10{width:83.33333%;}
}
@media(min-width:992px){
  .col-lg-4{width:33.33333%;}
  .col-lg-5{width:41.66667%;}
  .col-lg-7{width:58.33333%;}
  .col-lg-8{width:66.66667%;}
  .order-lg-1{order:1;}
  .order-lg-2{order:2;}
}
.align-items-center{align-items:center;}
.align-items-start{align-items:start;}
.justify-content-center{justify-content:center;}
.text-center{text-align:center;}
.position-relative{position:relative;}
.mb-5{margin-bottom:3rem;}

/* ===== [C1] ENVELOPE OVERLAY - two lace panels + wax seal ===== */
/* ===============================
   ENVELOPE SCREEN
=============================== */
#envScreen {
  position:fixed;inset:0;z-index:500;
  background:transparent; /* the live website sits right behind the panels */
  cursor:default;
}
#envScreen.gone{display:none;}

/* The envelope box - fills the entire screen (folds and flap are
   percentage-based, so the geometry adapts to any screen shape) */
.env-box {
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
}

/* === THE ENVELOPE = TWO PAPER PANELS over the live website.
   The left half is an envelope fold with a triangle point that
   lies over the right half - the wax seal rests on the point.
   On open, both halves slide apart and the site is unveiled
   from the middle outward. === */
.env-panel {
  position:absolute;top:0;bottom:0;
  background-color:#eee2d2;
  background-size:auto, cover;
  transition:transform 1.9s cubic-bezier(.65,.05,.2,1) .45s;
  will-change:transform;
}
/* Right half - concave inward triangle that receives the fold's point.
   Its notch runs parallel to the fold's edges (2% hidden overlap so the
   seal is airtight until it opens). */
.env-right {
  right:0;width:60%;
  z-index:5;
  background-image:
    linear-gradient(240deg, rgba(255,255,250,.18) 0%, rgba(255,255,250,0) 50%, rgba(90,74,36,.28) 100%),
    linear-gradient(to left, transparent 60%, rgba(70,55,25,.14)),
    var(--paper);
  background-position:center, center, right center;
  box-shadow:inset 0 0 90px rgba(70,55,25,.12);
  clip-path:polygon(0 0, 100% 0, 100% 100%, 0 100%, 40% 50%);
  /* plain border line on the notch edge + layered shadow */
  filter:
    drop-shadow(-1.8px 0 0 rgba(120,98,55,.55))
    drop-shadow(-4px 0 5px rgba(45,34,12,calc(.38 * var(--edge-cast,1))))
    drop-shadow(-12px 0 20px rgba(45,34,12,calc(.36 * var(--edge-cast,1))))
    drop-shadow(-28px 0 50px rgba(30,22,8,calc(.26 * var(--edge-cast,1))));
}
/* cast shadow painted onto this panel along the fold's edge -
   the dark seam between the two papers that sells the overlap */
.fold-cast-shadow{position:absolute;inset:0;width:100%;height:100%;z-index:3;pointer-events:none;}
.fold-cast-shadow .cast-a{stroke:rgba(40,30,10,calc(.5  * var(--edge-cast,1)));}
.fold-cast-shadow .cast-b{stroke:rgba(40,30,10,calc(.34 * var(--edge-cast,1)));}
.fold-cast-shadow .cast-c{stroke:rgba(30,22,8,calc(.24 * var(--edge-cast,1)));}

/* matching plain paper band along the concave notch */
.env-right::after {
  content:'';position:absolute;inset:0;z-index:2;
  background-color:#eee1d2;
  background-image:
    linear-gradient(240deg, rgba(255,253,245,.2) 0%, transparent 55%, rgba(110,90,50,.09) 100%),
    var(--paper-band);
  background-size:auto, 151px 112px;
  background-repeat:no-repeat, repeat;
  background-position:center, 63px 41px;
  clip-path:polygon(0 0, 40% 50%, 0 100%,
    calc(4.4% * var(--edge-band-width,1)) 100%,
    calc(40% + 4% * var(--edge-band-width,1)) 50%,
    calc(4.4% * var(--edge-band-width,1)) 0);
  filter:
    drop-shadow(-1px 0 0 rgba(255,255,252,calc(.8 * var(--edge-rim,1))))
    drop-shadow(2.5px 0 1px rgba(85,64,30,calc(.65 * var(--edge-groove,1))))
    drop-shadow(2.5px 0 0 rgba(255,255,250,calc(.85 * var(--edge-groove,1))))
    drop-shadow(1.5px 0 2px rgba(85,64,30,calc(.3 * var(--edge-groove,1))));
}
/* Left half - the fold: straight edge at centre, triangle point
   reaching over the right half (point tip at 58% of the screen) */
.env-left {
  left:-4%;width:70%; /* 4% bleed past the screen edge for the over-slide */
  z-index:6;
  background-image:
    linear-gradient(120deg, rgba(255,255,250,.36) 0%, rgba(255,255,250,.03) 55%, rgba(90,74,36,.16) 100%),
    var(--paper);
  background-position:center, left center;
  clip-path:polygon(0 0, 65.7% 0, 100% 50%, 65.7% 100%, 0 100%);
  /* plain border line + heavy layered shadow: the fold visibly
     floats OVER the paper beneath it */
  filter:
    drop-shadow(1.8px 0 0 rgba(120,98,55,.55))
    drop-shadow(5px 0 6px rgba(40,30,10,calc(.52 * var(--edge-cast,1))))
    drop-shadow(14px 0 24px rgba(40,30,10,calc(.46 * var(--edge-cast,1))))
    drop-shadow(34px 0 62px rgba(25,18,6,calc(.34 * var(--edge-cast,1))));
}
/* curvature shading - the paper darkens as it turns toward the edge */
.env-left::after {
  content:'';position:absolute;top:0;bottom:0;right:0;width:40%;z-index:1;
  background:linear-gradient(to left,
    rgba(50,38,12,.42) 0%, rgba(55,42,14,.22) 22%,
    rgba(55,42,14,.08) 50%, transparent 78%);
  clip-path:polygon(0 0, 13.7% 0, 100% 50%, 13.7% 100%, 0 100%);
}
/* plain paper band edging the fold - smooth ivory trim against the
   textured lace, like the border strip on a real lined envelope */
.env-left::before {
  content:'';position:absolute;inset:0;z-index:2;
  /* felt-marked paper, pre-tinted to the lace's exact colours - seamless tile */
  background-color:#eee1d2;
  background-image:
    linear-gradient(120deg, rgba(255,253,246,.34) 0%, rgba(255,253,246,.06) 55%, rgba(110,90,50,.1) 100%),
    var(--paper-band);
  background-size:auto, 151px 112px;
  background-repeat:no-repeat, repeat;
  clip-path:polygon(65.7% 0, 100% 50%, 65.7% 100%,
    calc(65.7% - 4.2% * var(--edge-band-width,1)) 100%,
    calc(100% - 4.1% * var(--edge-band-width,1)) 50%,
    calc(65.7% - 4.2% * var(--edge-band-width,1)) 0);
  /* raised strip: lit rim on the outer edge, then a groove (dark
     hairline) and a ridge-light where the band meets the lace -
     the band visibly stands proud of the paper */
  filter:
    drop-shadow(1px 0 0 rgba(255,255,252,calc(.8 * var(--edge-rim,1))))
    drop-shadow(-2.5px 0 1px rgba(85,64,30,calc(.65 * var(--edge-groove,1))))
    drop-shadow(-2.5px 0 0 rgba(255,255,250,calc(.85 * var(--edge-groove,1))))
    drop-shadow(-1.5px 0 2px rgba(85,64,30,calc(.3 * var(--edge-groove,1))));
}

/* === AUTO-OPEN - the fold first glides a touch OVER the paper
   (like a flap being slid free), then both halves sweep apart === */
#envScreen.open .env-left {
  animation:foldSlide 2.1s cubic-bezier(.65,.05,.2,1) .45s forwards;
}
#envScreen.open .env-right { transform:translateX(112%); transition-delay:.75s; }
@keyframes foldSlide {
  0%   { transform:translateX(0); }
  16%  { transform:translateX(calc(2.6% * var(--edge-overslide,1))); }   /* the little slide over the paper */
  100% { transform:translateX(-118%); }
}

/* Wax seal - sits at flap fold point (centre of envelope) */
.env-seal {
  position:absolute;
  /* rests on the fold's point */
  left:66%;top:50%;
  transform:translate(-50%,-50%);
  width:calc(clamp(84px,14vw,150px) * var(--seal-scale-now,1));
  height:calc(clamp(84px,14vw,150px) * var(--seal-scale-now,1));
  z-index:9;

  /* NO transition on transform here, deliberately.
     There used to be one - "rides away with the fold" - but the seal
     does not ride away by transition at all: the open is driven by the
     sealSlide KEYFRAMES below, which ignore it entirely. So the only
     thing that transition could ever do was cause the bug it looked
     innocent enough to hide.

     translate(-50%,-50%) resolves against the seal's OWN width, and
     that width comes from clamp(84px,14vw,150px) x --seal-scale. If
     anything settles a moment after the first paint - the viewport
     width firming up, a slow stylesheet, the phone's address bar - the
     resolved transform changes, and a transition on transform turns
     that correction into a 1.9-second glide across the screen. The
     seal appeared to drift into position on a cold load.

     Nothing here transitions now, so the seal is simply drawn where it
     belongs, in the first frame. It also explains why this never showed
     up in a layout-shift measurement: transform changes are not layout
     shifts, so every CLS check reported a perfect zero. */
}
/* The seal is shown at full size straight away - identical on a fresh
   load and from cache. A gentle fade-in plays if supported, but the
   seal's real, resting state is fully visible: it never depends on an
   animation finishing, so it can't get "stuck" invisible. */
.env-seal img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:contain;display:block;
  filter:drop-shadow(0 5px 9px rgba(10,14,6,.5)) drop-shadow(0 1px 2px rgba(10,14,6,.4));
  opacity:1;
  animation:sealFadeIn .5s ease;
}
@keyframes sealFadeIn{
  from{ opacity:0; }
  to  { opacity:1; }
}
/* Monogram stamped over the seal - scales with the seal automatically */
.seal-mono-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;transition:opacity .45s ease;display:none;}
/* the engraved monogram belongs to the built-in gold seal - it only
   appears when the custom seal is missing and the gold one steps in */
.env-seal.seal-fallback .seal-mono-svg{display:block;}
/* the seal stays whole and rides the fold - same little over-slide,
   same sweep, same distance (118% of the 66vw panel), as one piece */
#envScreen.open .env-seal {
  animation:sealSlide 2.1s cubic-bezier(.65,.05,.2,1) .45s forwards;
}
@keyframes sealSlide {
  0%   { transform:translate(-50%, -50%); }
  16%  { transform:translate(calc(-50% + 1.82vw * var(--edge-overslide,1)), -50%); }
  100% { transform:translate(calc(-50% - 82.6vw), -50%); }
}

/* ===== [C2] SITE BASE - website phase, typography, layout ===== */
/* ===============================
   WEBSITE PHASE
=============================== */
#websitePhase {
  display:none;
  flex-direction:column;
  background:#faf7f0;
  opacity:0;
  transition:opacity .8s ease;
  z-index:300;
}
#websitePhase.visible{display:flex;opacity:1;}

/* ===== [C2b] UTILITIES - lace background tile ===== */
.lace-bg{
  background-color:var(--cream);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80'%3E%3Ccircle cx='40' cy='40' r='30' fill='none' stroke='rgba(170,135,55,0.08)' stroke-width='1'/%3E%3Ccircle cx='40' cy='40' r='18' fill='none' stroke='rgba(170,135,55,0.06)' stroke-width='.7' stroke-dasharray='2 2'/%3E%3Ccircle cx='40' cy='40' r='2' fill='rgba(170,135,55,0.09)'/%3E%3C/svg%3E");
  background-size:80px 80px;
}


/* ===== [C3] FLOATING RSVP BUTTON - the travelling wax seal ===== */
/* === FLOATING RSVP SEAL BUTTON ===
   A wax-seal button that floats in the bottom corner while you scroll -
   it appears after the hero and hides itself on the RSVP section. */
#rsvpFab {
  position:fixed;
  right:var(--fab-side-now,28px);
  bottom:var(--fab-bottom-now,28px);
  z-index:250;
  width:calc(clamp(76px,15vw,100px) * var(--fab-scale-now,1));
  height:calc(clamp(76px,15vw,100px) * var(--fab-scale-now,1));
  display:flex;align-items:center;justify-content:center;
  text-decoration:none;
  filter:drop-shadow(0 8px 18px rgba(0,0,0,.35)) drop-shadow(0 2px 5px rgba(0,0,0,.3));
  opacity:0;visibility:hidden;
  transform:translateY(18px) scale(.88);
  transition:opacity .45s ease, transform .45s ease, visibility 0s .45s;
}
#rsvpFab.show {
  opacity:1;visibility:visible;
  transform:translateY(0) scale(1);
  transition:opacity .45s ease, transform .45s ease, visibility 0s;
}
#rsvpFab.show:hover { transform:translateY(-2px) scale(1.06); }
#rsvpFab img { position:absolute;inset:0;width:100%;height:100%;object-fit:contain;pointer-events:none; }
/* pulsing gold ring to draw the eye */
#rsvpFab::after {
  content:'';position:absolute;inset:4%;
  border-radius:50%;
  border:1.5px solid rgba(200,168,79,.75);
  opacity:0;
  pointer-events:none;
}
#rsvpFab.show::after { animation:fabPulse 2.6s ease-out infinite; }
@keyframes fabPulse {
  0%   { transform:scale(.82); opacity:.9; }
  70%  { transform:scale(1.28); opacity:0; }
  100% { transform:scale(1.28); opacity:0; }
}
/* little note that greets the first appearance, then slips away */
.fab-note {
  position:absolute;
  right:calc(100% + var(--fab-gap-now,12px));
  top:50%;transform:translateY(-50%) translateX(6px);
  background:rgba(30,44,38,.94);
  border:1px solid rgba(200,168,79,.45);
  color:var(--gold-lt);
  font-family:'Cinzel',serif;font-size:.52rem;letter-spacing:.22em;text-transform:uppercase;
  padding:9px 14px;white-space:nowrap;
  opacity:0;
  transition:opacity .5s ease, transform .5s ease;
  pointer-events:none;
}
#rsvpFab.note-visible .fab-note { opacity:1; transform:translateY(-50%) translateX(0); }

/* THE NOTE ABOVE THE SEAL INSTEAD OF BESIDE IT.
   Switched on by SETTINGS.fabNotePlace in js/settings.js, which puts
   .fabnote-above or .fabnote-left on <html> and keeps it in step as the
   screen changes. Not a media query: the value is a setting now, and a
   media query cannot read one.

   Why a phone wants it: beside the seal the note runs leftwards from a
   button already near the right edge, and white-space:nowrap means it
   cannot give way - so a longer line ("Open your personal link to
   reply") ran off the side of the display and was cut in half. Above,
   it has the whole width and may wrap.

   Its RIGHT edge lines up with the seal's, not its centre. Centred on
   the seal, a wide note hangs off the screen - the seal is only
   --fab-side-now from the edge, so half a 204px pill does not fit
   beside it. Anchored right, it grows leftwards into the space that is
   actually there and can never pass the edge it started from. */
html.fabnote-above .fab-note{
  left:auto; right:0;
  top:auto; bottom:calc(100% + var(--fab-gap-now,12px));
  transform:translateY(6px);
  white-space:normal;
  width:max-content;                 /* one line unless the max-width bites */
  max-width:min(78vw, 260px);
  text-align:center;
  line-height:1.5;
}
html.fabnote-above #rsvpFab.note-visible .fab-note{ transform:translateY(0); }

/* THE LABEL MAY NOT RUN OFF THE SCREEN.
   Beside the seal (fabNotePlace 'left') the note grows leftwards from a
   button that is already only --fab-side-now from the right edge, and
   white-space:nowrap means it cannot give way. On a 375px phone the
   English wording measured 204px past the left edge, and the Dutch
   "Open uw persoonlijke link om te reageren" is longer still - the label
   was simply cut in half.

   This does not overrule the setting: 'left' stays 'left'. It only caps
   the width at the space that actually exists and lets a line that will
   not fit wrap, instead of disappearing. The percentage resolves against
   #rsvpFab, the note's containing block, so the sum is exactly
   "the screen, less the seal, less the gap, less the margin".
   Nothing changes for any label that already fits. */
@media(max-width:640px){
  html.fabnote-left .fab-note{
    white-space:normal;
    text-align:right;
    line-height:1.5;
    max-width:calc(100vw - 100% - var(--fab-gap-now,12px) - var(--fab-side-now,14px) - 8px);
  }
}
@media(max-width:480px){ .fab-note{font-size:.48rem;letter-spacing:.16em;} }

/* ===== [C4] HERO - first screen: video + names ===== */
#wsHero{min-height:100vh;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;background:var(--sage-xdk);}
/* Phones held upright: the hero fills the screen and the video covers it
   edge-to-edge - no panels above or below.

   dvh, NOT svh. The three units differ only on a phone:
     svh  the SMALLEST the screen ever is - address bar showing
     lvh  the LARGEST - address bar hidden
     dvh  whatever it is RIGHT NOW, following the bar as it moves

   svh looks safe and is not: it is the height with the browser's bars
   showing, so the moment Safari retracts its address bar the visible
   area grows and the hero does not - leaving a band of the next section
   below the video, which is the whole "it does not fill the screen"
   complaint. dvh keeps up with the bar in both directions.

   The plain vh line first is the fallback for anything that has never
   heard of dvh; it is what this rule used to be. */
@media (orientation:portrait) and (max-width:820px){
  #wsHero{min-height:100vh;  height:100vh;}
  #wsHero{min-height:100dvh; height:100dvh;}
}
.hero-photo{position:absolute;inset:0;background:linear-gradient(to bottom,rgba(26,38,33,.35) 0%,rgba(26,38,33,.1) 40%,rgba(26,38,33,.55) 85%,rgba(26,38,33,.85) 100%),linear-gradient(135deg,#4F7565,#8CAE9F 40%,#22352D);background-size:cover;background-position:center;}
.hero-photo img{width:100%;height:100%;object-fit:cover;object-position:center top;display:block;}
/* hero background video (activated automatically when PHOTOS.heroVideo loads) */
.hero-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .6s ease;} /* invisible via opacity, NOT display:none - iOS refuses to load display:none videos */
/* soft dark veil over the video so the names stay readable */
.hero-veil{position:absolute;inset:0;display:none;background:linear-gradient(to bottom,rgba(26,38,33,.42) 0%,rgba(26,38,33,.16) 40%,rgba(26,38,33,.55) 85%,rgba(26,38,33,.82) 100%);}
.ph-ov{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:rgba(201,168,76,.28);font-family:'Cinzel',serif;font-size:.56rem;letter-spacing:.3em;pointer-events:none;}
/* NOTE: a radial "pool of shade" behind the names was tried here and
   removed - it read as a grey smudge floating over the footage. If the
   hero text ever needs more contrast, the honest options are a stronger
   text-shadow (below, invisible as a shape) or turning .hero-veil back
   on for the whole frame. Not a soft blob in the middle. */
.hero-content{position:relative;z-index:2;text-align:center;padding:0 20px;
  /* The text hovers high above the footage on a heavy black shadow.
     Every layer below is thrown DOWNWARD, which lifts the words off the
     video but leaves their top edges bare - and that is the edge a bright
     frame eats into. The first line is new: a tight halo with no offset
     at all, hugging each letter on every side. It has no shape of its
     own and cannot be seen as an area; it only stops the strokes
     dissolving when something pale passes behind them. */
  text-shadow:
    0 0 2px rgba(0,0,0,.72),
    0 2px 3px rgba(0,0,0,.85),
    0 5px 10px rgba(0,0,0,.75),
    0 12px 24px rgba(0,0,0,.6),
    0 24px 55px rgba(0,0,0,.5);
  /* the names fade in as the video appears behind the parting envelope */
  opacity:0;transform:translateY(16px);
  transition:opacity 1.7s ease, transform 1.7s ease;}
.hero-content.fade-in{opacity:1;transform:none;}
@keyframes fadeUp{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:translateY(0)}}
.hero-lbl{font-family:'Cinzel',serif;font-size:clamp(.62rem,1.9vw,.8rem);letter-spacing:.5em;color:var(--gold-lt);text-transform:uppercase;margin-bottom:24px;opacity:.95;animation:fadeUp 1.2s ease .2s both;}
.hero-lbl2{font-family:'Cinzel',serif;font-size:clamp(.6rem,1.8vw,.76rem);letter-spacing:.42em;color:var(--gold-lt);text-transform:uppercase;margin-top:6px;animation:fadeUp 1.2s ease .7s both;}
.hero-names{font-family:'Cormorant Garamond',serif;font-style:italic;font-weight:300;font-size:clamp(3rem,10vw,7.5rem);color:var(--ivory);line-height:1;animation:fadeUp 1.2s ease .5s both;}
.hero-amp{display:inline-block;color:var(--gold);font-size:.65em;margin:0 .2em;vertical-align:middle;}
.hero-date{font-family:'Cinzel',serif;font-size:clamp(.65rem,2vw,.9rem);letter-spacing:.4em;color:var(--gold-lt);margin-top:28px;animation:fadeUp 1.2s ease .8s both;}
.hero-div{filter:drop-shadow(0 1px 2px rgba(8,12,9,.5)) drop-shadow(0 3px 10px rgba(8,12,9,.35));display:flex;align-items:center;justify-content:center;gap:16px;margin:28px auto;width:min(320px,80vw);animation:fadeUp 1.2s ease 1s both;}
.hero-div-line{flex:1;height:1px;background:linear-gradient(to right,transparent,rgba(201,168,76,.5));}
.hero-div-line.r{background:linear-gradient(to left,transparent,rgba(201,168,76,.5));}
.hero-div-gem{width:6px;height:6px;background:var(--gold);transform:rotate(45deg);}
/* was .7 - the faintest line on the page, over moving footage. .84 keeps
   it clearly secondary to the names without making it a squint. */
.hero-loc{font-style:italic;font-size:clamp(.9rem,2.5vw,1.1rem);color:rgba(240,228,196,.84);animation:fadeUp 1.2s ease 1.1s both;}
/* The scroll cue is deliberately built to match the envelope's "tap the
   seal to open" hint (.env-hint): same pill, same border, same blur, same
   lettering, so the two read as a pair - one invites you in, the other
   invites you on. It also fixes the old problem of pale gold text sitting
   directly on video, which needed three stacked text-shadows to stay
   readable; the pill does that job properly. */
.scroll-hint{
  position:absolute;
  left:50%;bottom:40px;
  transform:translateX(-50%);
  z-index:2;
  display:flex;
  align-items:center;
  gap:.6em;
  font-family:'Cinzel',serif;
  font-size:clamp(.72rem,2vw,.82rem);
  letter-spacing:.22em;
  text-transform:uppercase;
  color:#F5EEDD;
  background:rgba(20,28,22,.55);
  border:1px solid rgba(201,169,97,.55);
  padding:.7em 1.3em;
  border-radius:999px;
  box-shadow:0 6px 22px rgba(0,0,0,.35);
  backdrop-filter:blur(3px);
  -webkit-backdrop-filter:blur(3px);
  white-space:nowrap;
  pointer-events:none;
  animation:floatHint 2.5s ease-in-out infinite;
}
/* The old 40px hairline becomes a downward arrow sitting in the same icon
   slot the envelope hint uses for its tapping finger - same size, same
   stroke weight, same warm gold. */
.scroll-hint .arr{
  width:1.15em;height:1.15em;
  flex:0 0 auto;
  background:no-repeat center/contain;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23E8D9B0' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14'/%3E%3Cpath d='M19 12l-7 7-7-7'/%3E%3C/svg%3E");
}
@keyframes floatHint{0%,100%{transform:translateX(-50%) translateY(0)}50%{transform:translateX(-50%) translateY(8px)}}

/* ===== [C5] SHARED SECTION STYLES - labels, titles, dividers, cards ===== */
section{position:relative;}
.sec-label{font-family:'Cinzel',serif;font-size:clamp(.48rem,1.4vw,.58rem);letter-spacing:.45em;color:var(--sage);text-transform:uppercase;display:block;margin-bottom:14px;}
.sec-title{font-family:'Cormorant Garamond',serif;font-style:italic;font-weight:300;font-size:clamp(2rem,5vw,3.5rem);color:var(--sage-dk);line-height:1.1;}
.sec-div{display:flex;align-items:center;gap:12px;margin:20px 0 36px;}
.sec-div .line{flex:1;height:1px;background:linear-gradient(to right,var(--sage),transparent);max-width:120px;}
.sec-div .gem{width:5px;height:5px;background:var(--gold);transform:rotate(45deg);}
.sec-div .line2{flex:0;width:40px;height:1px;background:linear-gradient(to right,rgba(201,168,76,.4),transparent);}
.gold-line{width:100%;height:1px;background:linear-gradient(to right,transparent,rgba(201,168,76,.4),transparent);}
.reveal{opacity:0;transform:translateY(28px);transition:opacity .8s ease,transform .8s ease;}
.reveal.visible{opacity:1;transform:translateY(0);}
.rd1{transition-delay:.1s}.rd2{transition-delay:.2s}
.gold-card{border:1px solid rgba(140,174,159,.5);background:var(--ivory);position:relative;padding:36px;}
.gold-card::before,.gold-card::after{content:'';position:absolute;width:28px;height:28px;border-color:var(--gold);border-style:solid;opacity:.5;}
.gold-card::before{top:8px;left:8px;border-width:1px 0 0 1px;}
.gold-card::after{bottom:8px;right:8px;border-width:0 1px 1px 0;}

/* ===== [C6] OUR STORY - photo grid + text ===== */
#wsStory{padding:100px 0 80px;background:var(--cream);}
/* The --story-* variables are set from SETTINGS.storyLayout in
   js/settings.js. The values after the commas are the fallbacks, i.e.
   the layout as originally designed, used if settings.js is missing.

   grid-auto-rows, NOT grid-template-rows: the old version named an
   exact number of rows, so a photo past the end got no height at all.
   With five photos on a phone that meant photo 5 was 0px tall and
   simply invisible. Auto rows size however many photos there are, so
   adding a sixth to photos.story now just works. */
/* gap is SETTINGS.photoGap - shared with the gallery and reception grids */
.story-grid{
  display:grid;
  grid-template-columns:repeat(var(--story-cols,3),1fr);
  grid-auto-rows:var(--story-row,300px);
  gap:var(--photo-gap,12px);
  /* BLOCK WIDTH - how wide the whole photo block is, as a percentage of
     the space it sits in. The margin keeps it centred at any value, and
     because it goes negative above 100% the block can be made WIDER than
     the text beside it and still stay centred. */
  width:var(--story-width,100%);
  margin-left:calc((100% - var(--story-width,100%)) / 2);
}
@media(max-width:991px){
  .story-grid{
    grid-template-columns:repeat(var(--story-cols-tablet,2),1fr);
    grid-auto-rows:var(--story-row-tablet,220px);
  }
}
@media(max-width:575px){
  .story-grid{
    grid-template-columns:repeat(var(--story-cols-phone,1),1fr);
    grid-auto-rows:var(--story-row-phone,240px);
  }
}
.story-photo{position:relative;overflow:hidden;background:var(--ivory2);}
/* The first photo is the tall one - that is the section's signature.
   Its height is (span x --story-row) + (span - 1) x --photo-gap, so it
   lines up exactly with the shorter photos beside it. */
.story-photo:first-child{grid-row:span var(--story-big-span,2);background:linear-gradient(135deg,#8CAE9F,#4F7565);}
/* Below 991px the columns get narrow and a double-height first photo
   turns into an awkward tower, so it goes back to normal size. */
@media(max-width:991px){.story-photo:first-child{grid-row:span 1;}}
.story-photo img{width:100%;height:100%;object-fit:var(--story-fit,cover);transition:transform .7s ease;display:block;}
.story-photo:hover img{transform:scale(1.04);}
.story-photo .ph-lbl{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;color:rgba(201,168,76,.3);font-family:'Cinzel',serif;font-size:.55rem;letter-spacing:.3em;gap:8px;pointer-events:none;}
.story-body{font-size:clamp(1rem,2vw,1.15rem);color:var(--text-lt);line-height:1.9;font-weight:300;font-style:italic;max-width:480px;}

/* ===== [C7] THE DAY - events section ===== */
#wsEvents{padding:90px 0;background:var(--sage-xdk);position:relative;overflow:hidden;}
#wsEvents::before{content:'';position:absolute;inset:0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Ccircle cx='50' cy='50' r='40' fill='none' stroke='rgba(201,168,76,0.05)' stroke-width='1'/%3E%3Ccircle cx='50' cy='50' r='3' fill='rgba(201,168,76,0.05)'/%3E%3C/svg%3E");background-size:100px 100px;pointer-events:none;}
#wsEvents .sec-title{color:var(--ivory);}
#wsEvents .sec-label{color:var(--gold-lt);}
#wsEvents .sec-div .line{background:linear-gradient(to right,var(--gold-lt),transparent);}
#wsEvents .sec-div .gem{background:var(--gold-lt);}
/* ===== [C8] VENUE - photos, details, map ===== */
#wsVenue{padding:90px 0;background:var(--cream);}
.venue-loc-addr{font-size:.92rem;color:var(--text-lt);line-height:1.6;font-style:italic;}
.venue-loc-time{font-family:'Cinzel',serif;font-size:.62rem;letter-spacing:.22em;color:var(--gold-dk);margin-top:14px;padding-top:12px;border-top:1px solid rgba(140,174,159,.35);display:inline-block;}
/* the two location blocks: kicker headers with flanking lines */
.venue-block-kicker{display:flex;align-items:center;justify-content:center;gap:18px;font-family:'Cinzel',serif;font-size:clamp(.82rem,2vw,1.05rem);letter-spacing:.45em;color:var(--sage);text-transform:uppercase;margin-bottom:20px;}
.venue-block-kicker::before,.venue-block-kicker::after{content:'';height:1px;flex:1;max-width:150px;}
.venue-block-kicker::before{background:linear-gradient(to right,transparent,var(--sage));}
.venue-block-kicker::after{background:linear-gradient(to left,transparent,var(--sage));}
.venue-block-gap{margin-top:52px;}
/* The reception's photo grid: 2 across, so four photos stack 2 on top of 2.
   (Same on phones, hence no separate small-screen rule any more.) */
/* gap is SETTINGS.photoGap - shared with the story and gallery grids.
   margin-top uses it too, so the space below the big reception photo
   always matches the space between the four below it. */
.venue-gallery{
  display:grid;
  grid-template-columns:repeat(var(--venue-cols,2),1fr);
  gap:var(--photo-gap,12px);
  margin-top:var(--photo-gap,12px);
  /* see the note on .story-grid - same idea, its own dial. The left
     margin has to carry the centring AND nothing else, so the top
     margin above stays on its own line. */
  width:var(--venue-width,100%);
  margin-left:calc((100% - var(--venue-width,100%)) / 2);
}
/* Width comes from --venue-cols above (how many share a row); height is one
   plain number per screen size, from SETTINGS.venueLayout.height. Same shape
   as the gallery's rowHeight, and deliberately NOT a clamp() - with a clamp
   the middle term wins at most screen widths, so raising the number appeared
   to do nothing. */
.venue-gi{
  height:var(--venue-h,260px);
  overflow:hidden;position:relative;background:var(--ivory3);
}
@media(max-width:767px){ .venue-gi{ height:var(--venue-h-tablet,200px); } }
@media(max-width:480px){ .venue-gi{ height:var(--venue-h-phone,160px); } }
.venue-gi img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .6s ease;}
.venue-gi:hover img{transform:scale(1.06);}
.map-box{height:280px;position:relative;overflow:hidden;border:1px solid rgba(140,174,159,.35);}
/* maps in the stacked venue section - a comfortable tall aspect */
.map-box.venue-map-tall{height:clamp(280px,42vw,420px);}
.venue-hero-lbl-solo{
  font-family:'Cormorant Garamond',serif;font-style:italic;
  font-size:clamp(1.4rem,3.4vw,2.1rem);color:var(--sage-dk);
  font-weight:300;margin:2px 0 12px;
}
.gt-card{padding:36px 32px;}
.map-box iframe{width:100%;height:100%;border:none;}
.map-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;color:rgba(201,168,76,.4);font-family:'Cinzel',serif;font-size:.58rem;letter-spacing:.3em;background:linear-gradient(135deg,#4F7565,#8CAE9F);text-align:center;padding:20px;}

/* ===== [C9] GALLERY - masonry (built from SETTINGS.photos.gallery) ===== */
#wsGallery{padding:90px 0;background:var(--ivory2);}
/* uniform grid: every cell the same width, fixed row height, photos
   cropped to fill - flows left-to-right with zero holes, whatever the
   mix of portrait and landscape shots. Every 4th photo spans 2 rows
   for rhythm; grid-auto-flow:dense fills any gap that would create. */
/* The gallery grid is driven by SETTINGS.galleryLayout in js/settings.js,
   which sets the --gal-* variables. The values after the commas are the
   fallbacks, so the grid still looks right if settings.js ever fails to
   load. grid-auto-flow:dense lets short photos backfill the gaps that the
   double-height ones leave behind. */
.gallery-masonry{
  display:grid;
  grid-template-columns:repeat(var(--gal-cols,3),1fr);
  grid-auto-rows:var(--gal-row,240px);
  gap:var(--photo-gap,12px);
  /* see the note on .story-grid - same idea, its own dial */
  width:var(--gal-width,100%);
  margin-left:calc((100% - var(--gal-width,100%)) / 2);
  grid-auto-flow:dense;
}
@media(max-width:767px){
  .gallery-masonry{
    grid-template-columns:repeat(var(--gal-cols-tablet,2),1fr);
    grid-auto-rows:var(--gal-row-tablet,200px);
  }
}
@media(max-width:480px){
  .gallery-masonry{
    grid-template-columns:repeat(var(--gal-cols-phone,1),1fr);
    grid-auto-rows:var(--gal-row-phone,230px);
  }
}
.gal-item{position:relative;overflow:hidden;background:var(--ivory3);}
/* A tall photo is a whole number of rows, so it always lines up with the
   short ones beside it. Its real height works out as
   (span x --gal-row) + (span - 1) x --photo-gap. */
.gal-item.tall{grid-row:span var(--gal-tall-span,2);}
.gal-item img{width:100%;height:100%;object-fit:var(--gal-fit,cover);display:block;transition:transform .6s ease;}

/* ---- the photos held back until the guest asks --------------------
   display:none, not opacity:0 - a hidden lazy image is never fetched,
   so the extra photos cost a first-time visitor nothing at all. */
.gal-item.gal-hidden{ display:none; }

/* ---- "View all photos" / "Show fewer photos" ----------------------
   ONE button that toggles - the gallery must not be a one-way door.
   Same shape as .btn-gold on the RSVP form, but this sits on the pale
   ivory gallery background, so the gold has to be the darker one to
   stay readable. */
.gallery-more{
  display:block;
  margin:calc(var(--photo-gap,12px) + 22px) auto 0;
  background:none;
  border:1px solid rgba(201,168,76,.55);
  color:var(--sage-dk);
  font-family:'Cinzel',serif;
  font-size:.62rem;
  letter-spacing:.34em;
  text-transform:uppercase;
  padding:15px 42px;
  cursor:pointer;
  position:relative;
  overflow:hidden;
  transition:color .3s ease, border-color .3s ease, opacity .3s ease, transform .3s ease;
}
.gallery-more::before{
  content:'';position:absolute;inset:0;
  background:rgba(201,168,76,.14);
  transform:scaleX(0);transform-origin:left;
  transition:transform .35s ease;
}
.gallery-more:hover::before{ transform:scaleX(1); }
.gallery-more:hover{ color:var(--gold-dk,#8a6f28); border-color:var(--gold); }
.gallery-more:focus-visible{ outline:2px solid var(--gold); outline-offset:3px; }
@media(max-width:575px){
  .gallery-more{ padding:14px 28px; letter-spacing:.24em; }
}
.gal-item:hover img{transform:scale(1.04);}
.gal-ph{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:rgba(170,130,50,.28);font-family:'Cinzel',serif;font-size:.5rem;letter-spacing:.3em;gap:8px;}

/* ===== [C9b] QUESTIONS & ANSWERS ==================================
   Built by main.js from the "faq" block in the content files, so the
   number of rows is not known here. Everything is drawn from the
   vocabulary already on the page - the gold hairline, Cinzel for the
   question, italic Cormorant for the answer - so the section reads as
   part of the invitation rather than as a support page.

   --cream, the same background Our Story renders. Deliberately NOT the
   .lace-bg class: the story and the venue both carry that class and
   neither shows the lace, because their own #id{background:...} shorthand
   outranks it and blanks the image out. Matching the story means matching
   what it LOOKS like, so this is the plain colour they end up with. */
#wsFaq{padding:90px 0;background:var(--cream);}
.faq-note{
  font-style:italic;color:var(--text-lt);
  font-size:1rem;line-height:1.8;max-width:520px;margin:0 auto;
}

/* The list is a .gold-card (see index.html) - the ivory panel with gold
   corner brackets that the venue's "Getting There" blocks use. Only its
   padding is changed here: a card's 36px all round would push the first
   question's own padding into a double margin. The rows supply their
   own vertical rhythm, so the card gives horizontal room only. */
.faq-list{
  padding:6px clamp(20px,3.5vw,38px);
}

/* <details>, not a JavaScript accordion: the browser already knows how
   to open, close, take focus and announce this to a screen reader. */
.faq-item{border-bottom:1px solid rgba(140,174,159,.32);}
.faq-item:last-child{border-bottom:none;}

.faq-q{
  display:flex;align-items:center;gap:20px;justify-content:space-between;
  padding:24px 0;cursor:pointer;
  font-family:'Cormorant Garamond',serif;
  font-size:clamp(1.1rem,2.2vw,1.34rem);
  color:var(--sage-dk);line-height:1.45;
  transition:color .25s ease;
  /* the default disclosure triangle goes; .faq-mark replaces it in the
     site's own gold. Two rules because Safari needs its own. */
  list-style:none;
}
.faq-q::-webkit-details-marker{display:none;}
.faq-q:hover{color:var(--gold-dk);}
.faq-item[open] .faq-q{color:var(--gold-dk);padding-bottom:14px;}
.faq-q:focus-visible{outline:2px solid var(--gold);outline-offset:3px;}
.faq-q-text{flex:1 1 auto;}

/* The marker is a hairline gold ring around a + that turns into a x -
   the same ring the timeline dots are drawn with, at a quarter of the
   size, so the two sections share a shape as well as a palette. */
.faq-mark{
  flex:0 0 auto;position:relative;
  width:26px;height:26px;
  border:1px solid rgba(201,168,76,.45);
  border-radius:50%;
  transition:transform .35s ease, background-color .25s ease, border-color .25s ease;
}
.faq-mark::before,.faq-mark::after{
  content:'';position:absolute;top:50%;left:50%;
  width:10px;height:1px;margin:-.5px 0 0 -5px;
  background:var(--gold);
  transition:background-color .25s ease;
}
.faq-mark::after{transform:rotate(90deg);}
.faq-q:hover .faq-mark{border-color:var(--gold);background:rgba(201,168,76,.09);}
.faq-item[open] .faq-mark{
  transform:rotate(135deg);
  border-color:var(--gold);
  background:rgba(201,168,76,.14);
}

/* The answer is set in from the left, so it sits clear of the question's
   own line and reads as belonging under it rather than beside it. The
   hairline is the same gradient rule the section dividers use. */
.faq-a{
  padding:0 0 26px;
  margin-left:2px;
  padding-left:16px;
  border-left:1px solid rgba(201,168,76,.35);
  /* was max-width:62ch - a reading-measure cap. On a laptop that stopped
     the answer at 467px inside a 658px row, so it ended two-thirds of the
     way across while the question above it ran the full width, which reads
     as a broken box rather than as a comfortable measure. The card is
     already capped (col-lg-8 inside a 1140px container), so the line
     length is held there instead. */
  font-size:clamp(.98rem,1.9vw,1.08rem);
  font-style:italic;font-weight:300;
  color:var(--text-lt);line-height:1.85;
}

@media (prefers-reduced-motion: reduce){
  .faq-mark,.faq-mark::before,.faq-mark::after{transition:none;}
}
@media(max-width:575px){
  .faq-list{padding:2px 18px;}
  .faq-q{gap:14px;padding:20px 0;font-size:1.06rem;}
  .faq-item[open] .faq-q{padding-bottom:12px;}
  .faq-mark{width:23px;height:23px;}
  .faq-a{padding-bottom:22px;padding-left:13px;}
}

/* ---- [button:Label|url] written into any text ---------------------
   Same shape and lettering as the gallery's "View all photos" button,
   so a button written into a FAQ answer looks like it belongs to the
   site rather than to the browser.

   Two shapes, chosen in the text itself:
     [button:Label|url]          on its own line, text flows above/below
     [button:Label|url|inline]   inside the sentence, on the text baseline
   Separated is the default: these land at the end of a sentence, and a
   full-size gold box mid-paragraph reads as an accident. It was
   inline-block with no break before, so a "block" button still sat in
   the flow beside the words - display:block + width:fit-content is what
   actually gives it its own line without stretching the border edge to
   edge. */
.rich-btn{
  display:block;
  width:fit-content;
  margin:18px 0 4px;
  background:none;
  border:1px solid rgba(201,168,76,.55);
  color:var(--gold-dk);
  font-family:'Cinzel',serif;
  font-size:.6rem;
  letter-spacing:.3em;
  text-transform:uppercase;
  text-decoration:none;
  font-style:normal;
  /* the extra left padding pays back the trailing letter-spacing after
     the last letter, which otherwise pushes the word off-centre */
  padding:13px 30px 13px calc(30px + .3em);
  transition:color .3s ease, border-color .3s ease, background-color .3s ease;
}
.rich-btn-inline{
  display:inline-block;
  width:auto;
  margin:0 .2em;
  padding:5px 14px 5px calc(14px + .2em);
  font-size:.55rem;
  letter-spacing:.2em;
  vertical-align:baseline;
}
.rich-btn:hover{ color:var(--gold-dk); border-color:var(--gold); background:rgba(201,168,76,.12); }
.rich-btn:focus-visible{ outline:2px solid var(--gold); outline-offset:3px; }
/* the two dark panels - pale gold on ivory is unreadable there */
#wsRsvp .rich-btn,
#wsZoom .rich-btn,
#wsEvents .rich-btn{ color:var(--gold-lt); border-color:var(--gold); }
#wsRsvp .rich-btn:hover,
#wsZoom .rich-btn:hover,
#wsEvents .rich-btn:hover{ color:var(--ivory); background:rgba(201,168,76,.18); }
@media(max-width:575px){
  .rich-btn{ padding:12px 22px 12px calc(22px + .22em); letter-spacing:.22em; }
  .rich-btn-inline{ padding:5px 12px 5px calc(12px + .18em); letter-spacing:.18em; }
}

/* ===== [C10] RSVP - form + success/error states ===== */
/* #wsZoom (zoom.html only) rides along on every one of these rules. It
   is the same dark panel in the same place in the page, and .btn-gold
   is drawn for a dark background - pale gold on ivory is unreadable.
   Listing both selectors is what keeps them from drifting apart. */
#wsRsvp,
#wsZoom{padding:90px 0 100px;background:var(--sage-xdk);position:relative;overflow:hidden;}
#wsRsvp::before,
#wsZoom::before{content:'';position:absolute;inset:0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Ccircle cx='50' cy='50' r='40' fill='none' stroke='rgba(201,168,76,0.04)' stroke-width='1'/%3E%3Ccircle cx='50' cy='50' r='2' fill='rgba(201,168,76,0.05)'/%3E%3C/svg%3E");background-size:100px 100px;pointer-events:none;}
#wsRsvp .sec-title,
#wsZoom .sec-title{color:var(--ivory);}
#wsRsvp .sec-label,
#wsZoom .sec-label{color:var(--gold-lt);}
#wsRsvp .sec-div .line,
#wsZoom .sec-div .line{background:linear-gradient(to right,var(--gold-lt),transparent);}
#wsRsvp .sec-div .gem,
#wsZoom .sec-div .gem{background:var(--gold-lt);}

/* ---- the "join us online" panel (zoom.html) ------------------
   Centred rather than the RSVP's two columns: there is no form to
   sit beside, and a single short block reads better down the middle
   of the page than pushed to one side of an empty half. */
#wsZoom .sec-div{justify-content:center;}
.zoom-intro{font-size:clamp(.95rem,2vw,1.1rem);color:rgba(220,210,185,.75);font-style:italic;line-height:1.8;max-width:560px;margin:0 auto 20px;}
.zoom-when{font-family:'Cinzel',serif;font-size:clamp(.6rem,1.7vw,.72rem);letter-spacing:.16em;line-height:2;color:var(--gold-lt);max-width:560px;margin:0 auto 40px;}
.zoom-card{background:rgba(245,240,224,.05);border-color:rgba(201,168,76,.22);max-width:520px;margin:0 auto;padding:clamp(28px,5vw,44px);text-align:center;}
/* .btn-gold is an inline-flex built for a <button>; as a link it needs
   the underline taken off and centring of its own. */
.zoom-btn{text-decoration:none;justify-content:center;}
.zoom-meta{margin-top:28px;display:flex;flex-direction:column;gap:12px;}
/* Each row is label on the left, value on the right, with a hairline
   between them - the same engraved pairing the venue card uses. */
.zoom-row{display:flex;align-items:baseline;justify-content:space-between;gap:16px;padding-top:12px;border-top:1px solid rgba(201,168,76,.14);}
.zoom-row:first-child{border-top:none;padding-top:0;}
.zoom-key{font-family:'Cinzel',serif;font-size:.52rem;letter-spacing:.3em;text-transform:uppercase;color:var(--gold-lt);opacity:.85;flex:0 0 auto;}
/* The value is a code people copy or type in. Tabular figures keep the
   digits from jittering, and user-select makes it easy to copy. */
.zoom-val{font-family:'Cormorant Garamond',serif;font-size:1.15rem;color:var(--ivory);font-variant-numeric:tabular-nums;letter-spacing:.06em;text-align:right;word-break:break-word;-webkit-user-select:all;user-select:all;}
.zoom-note{font-size:.9rem;color:rgba(220,210,185,.45);font-style:italic;line-height:1.8;max-width:520px;margin:32px auto 0;}
@media(max-width:520px){
  /* Side by side, a long meeting ID squeezes the label to nothing.
     Stacked, both get the full width. */
  .zoom-row{flex-direction:column;gap:6px;}
  .zoom-val{text-align:left;}
}
.rsvp-intro{font-size:clamp(.95rem,2vw,1.1rem);color:rgba(220,210,185,.75);font-style:italic;line-height:1.8;margin-bottom:32px;max-width:520px;}
.rsvp-form-wrap{background:rgba(245,240,224,.05);border:1px solid rgba(201,168,76,.22);padding:clamp(24px,5vw,52px);position:relative;}
.rsvp-form-wrap::before{content:'';position:absolute;top:10px;left:10px;right:10px;bottom:10px;border:1px solid rgba(201,168,76,.09);pointer-events:none;}
.f-group{margin-bottom:26px;}
.f-label{display:block;font-family:'Cinzel',serif;font-size:.52rem;letter-spacing:.3em;color:var(--gold-lt);text-transform:uppercase;margin-bottom:10px;}
.f-input{width:100%;background:rgba(245,240,224,.06);border:none;border-bottom:1px solid rgba(201,168,76,.35);padding:12px 0;color:var(--ivory);font-family:'Cormorant Garamond',serif;font-size:1rem;font-style:italic;outline:none;transition:border-color .3s;-webkit-appearance:none;}
.f-input::placeholder{color:rgba(220,210,185,.3);}
.f-input:focus{border-bottom-color:var(--gold);}
.guest-ctr{display:flex;align-items:center;gap:16px;padding:8px 0;}
.ctr-btn{width:32px;height:32px;border:1px solid rgba(201,168,76,.4);background:none;color:var(--gold-lt);font-size:1.1rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;}
.ctr-btn:hover{background:rgba(201,168,76,.1);border-color:var(--gold);}
.ctr-num{font-family:'Cinzel',serif;font-size:1.1rem;color:var(--ivory);min-width:28px;text-align:center;}
.btn-gold{display:inline-flex;align-items:center;gap:12px;background:none;border:1px solid var(--gold);color:var(--gold-lt);font-family:'Cinzel',serif;font-size:.6rem;letter-spacing:.4em;text-transform:uppercase;padding:16px 48px;cursor:pointer;transition:all .3s;position:relative;overflow:hidden;}
.btn-gold::before{content:'';position:absolute;inset:0;background:rgba(201,168,76,.12);transform:scaleX(0);transform-origin:left;transition:transform .35s ease;}
.btn-gold:hover::before{transform:scaleX(1);}
.btn-gold:hover{color:var(--gold);border-color:var(--gold-lt);}
.btn-gold:disabled{opacity:.5;cursor:not-allowed;}
#formSuccess{display:none;padding:20px 28px;margin-top:20px;border:1px solid rgba(180,203,193,.4);color:var(--sage-lt);background:rgba(180,203,193,.06);font-style:italic;line-height:1.7;}
#formError{display:none;padding:20px 28px;margin-top:20px;border:1px solid rgba(180,80,60,.4);color:#e09080;background:rgba(180,80,60,.06);font-style:italic;line-height:1.7;}
.spin{display:inline-block;width:16px;height:16px;border:1.5px solid rgba(201,168,76,.3);border-top-color:var(--gold);border-radius:50%;animation:spinAnim .7s linear infinite;}
@keyframes spinAnim{to{transform:rotate(360deg)}}

/* ===== [C11] FOOTER ===== */
#wsFoot{background:#101815;padding:60px 0 40px;border-top:1px solid rgba(201,168,76,.15);text-align:center;}
.foot-mono{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:clamp(2rem,5vw,3.2rem);color:var(--gold);opacity:.7;font-weight:300;}
.foot-date{font-family:'Cinzel',serif;font-size:.58rem;letter-spacing:.5em;color:rgba(201,168,76,.4);margin-top:14px;}
.foot-div{width:60px;height:1px;background:rgba(201,168,76,.25);margin:24px auto;}
.foot-note{font-size:.82rem;color:rgba(220,210,185,.22);font-style:italic;}

/* ===== [C12] INFOGRAPHIC TIMELINE - inside THE DAY ===== */
/* ===============================
   INFOGRAPHIC TIMELINE
=============================== */
.itl-wrap { position:relative; padding:0 0 8px; }

.itl-row {
  display:grid;
  grid-template-columns: 1fr 64px 1fr;
  align-items:flex-start;
  min-height:110px;
}

/* - spine column (centre) - */
.itl-spine-col {
  display:flex;
  flex-direction:column;
  align-items:center;
  position:relative;
}

.itl-dot {
  width:52px; height:52px;
  border-radius:50%;
  background:var(--ivory);
  border:2px solid rgba(180,203,193,.65);
  display:flex; align-items:center; justify-content:center;
  flex-shrink:0;
  position:relative;
  z-index:2;
  box-shadow:0 0 0 6px rgba(201,168,76,.08);
}
.itl-dot-gold {
  background:var(--gold);
  border-color:var(--gold-lt);
  box-shadow:0 0 0 6px rgba(201,168,76,.2), 0 4px 16px rgba(201,168,76,.3);
}

/* -- the icon inside the dot, when it comes from a file --------------
   SETTINGS.timelineIcons names an .svg per row and main.js swaps it in
   for the one written into index.html. The file supplies the SHAPE
   only, as a mask: the colour still comes from the palette here, so a
   replacement icon inherits the design instead of overriding it - which
   an <img> could not do, since an image cannot read a CSS variable.

   28x28 is the size the inline drawings are authored at, so a swapped
   icon lands in exactly the same place at exactly the same size. */
.itl-icon{
  width:28px; height:28px;
  display:block;
  background-color:var(--sage-xdk);
  -webkit-mask:var(--itl-icon) no-repeat center / contain;
          mask:var(--itl-icon) no-repeat center / contain;
}
/* the last dot is gold, and its drawing is the pale one on top of it */
.itl-dot-gold .itl-icon{ background-color:#fff; }

.itl-line {
  width:2px;
  flex:1;
  min-height:40px;
  background:linear-gradient(to bottom, rgba(180,203,193,.45), rgba(180,203,193,.15));
  margin-top:0;
}

/* - time column - */
.itl-time-col {
  display:flex;
  align-items:flex-start;
  justify-content:flex-end;
  padding-top:12px;
  padding-right:16px;
}
.itl-time-right {
  justify-content:flex-start;
  padding-right:0;
  padding-left:16px;
}
.itl-time {
  font-family:'Cinzel', serif;
  font-size:clamp(.75rem, 2vw, 1rem);
  font-weight:600;
  color:var(--gold-lt);
  letter-spacing:.05em;
  white-space:nowrap;
}

/* - content column - */
.itl-content-col {
  padding-top:10px;
  padding-left:16px;
}
.itl-content-left {
  padding-left:0;
  padding-right:16px;
  text-align:right;
}
.itl-event {
  font-family:'Cormorant Garamond', serif;
  font-style:italic;
  font-weight:300;
  font-size:clamp(1.1rem, 2.5vw, 1.45rem);
  color:var(--ivory);
  line-height:1.2;
  margin-bottom:5px;
}
.itl-detail {
  font-size:clamp(.78rem, 1.6vw, .9rem);
  color:rgba(220,210,185,.6);
  line-height:1.6;
  font-style:italic;
}

/* Mobile: collapse to single column */
@media(max-width:575px){
  /* CENTRED on a phone. It used to be a 40px spine hard against the left
     edge with the text beside it, which left the whole section looking
     off-balance next to every other block on the page - those are all
     centred. Here each entry becomes one centred column: the dot, a short
     stem under it, then the time and text centred beneath.

     The long connecting rail is deliberately NOT kept. Centred text with a
     full-height line down the middle would put the line straight through
     the words. The stem under each dot reads as the same idea without the
     collision. On a wide screen nothing changes - the rail is still there,
     with the entries alternating left and right of it. */
  .itl-row {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
    justify-items: center;
    min-height: 0;
  }
  /* both side columns of times go - the time is drawn under the dot instead */
  .itl-row .itl-time-col { display:none; }

  .itl-row .itl-spine-col,
  .itl-row.itl-left .itl-spine-col   { order:1; grid-column:1; }
  .itl-row .itl-content-col,
  .itl-row.itl-left .itl-content-col,
  .itl-row.itl-left .itl-content-col.itl-content-left {
    order:2; grid-column:1;
    text-align:center;
    padding:10px 8px 30px;
  }
  .itl-dot { width:40px; height:40px; }
  /* the stem: a short line under each dot, instead of a rail through the text */
  .itl-line { min-height:0; height:16px; flex:0 0 16px; }
  /* Show time inline inside content for mobile.
     On a phone the real .itl-time element is hidden (above) and the time
     is drawn HERE instead, as a pseudo-element. Nothing in JavaScript can
     set a font-size on a pseudo-element, which is why the per-line size
     for day.timeline.*.time appeared to do nothing on phones. The size
     travels as a plain multiplier instead, set by applyTextSizes(). */
  .itl-event::before {
    display:block;
    font-family:'Cinzel',serif;
    font-size:calc(.65rem * var(--tl-time-scale,1));
    font-style:normal;
    color:var(--gold);
    letter-spacing:.08em;
    margin-bottom:3px;
    content:attr(data-time);
  }
}
@keyframes itlSlideIn {
  from { opacity:0; transform:translateY(22px); }
  to   { opacity:1; transform:translateY(0); }
}
/* Items start hidden; .itl-playing parent triggers the animation */
.itl-wrap .itl-ani {
  opacity: 0;
}
.itl-wrap.itl-playing .itl-ani {
  animation: itlSlideIn .7s ease forwards;
}

/* ===== envelope tap hint ===== */
.env-hint{
  position:absolute;
  left:50%;bottom:10vh;
  transform:translateX(-50%);
  z-index:10;
  display:flex;
  align-items:center;
  gap:.6em;
  font-family:'Cinzel',serif;
  font-size:clamp(.8rem,3vw,1rem);
  letter-spacing:.22em;
  text-transform:uppercase;
  color:#F5EEDD;
  background:rgba(20,28,22,.55);
  border:1px solid rgba(201,169,97,.55);
  padding:.7em 1.3em;
  border-radius:999px;
  box-shadow:0 6px 22px rgba(0,0,0,.35);
  backdrop-filter:blur(3px);
  -webkit-backdrop-filter:blur(3px);
  white-space:nowrap;
  max-width:calc(100vw - 24px);
  pointer-events:none;
  /* HIDDEN UNTIL THE WORDING IS FINAL.
     index.html paints the ENGLISH hint and main.js swaps in the guest's
     language a moment later. In Dutch that pill is 94px wider, and since
     it is centred it grew 47px out of each side in full view. So it only
     fades in once main.js has put the real text in - it adds
     .texts-ready to <html> the instant it has.

     A fixed delay was not enough on its own: on slow mobile data main.js
     can easily arrive later than any delay worth waiting, and the jump
     would be back. What is left here IS a fixed delay, but a long one -
     purely a safety net, so that a browser which somehow never gets that
     class still shows the hint after 3 seconds rather than hiding it for
     ever. In practice the class always wins. */
  opacity:1;
  animation:envHintIn .5s ease 3s backwards;
  transition:opacity .4s ease;
}
html.texts-ready .env-hint{
  animation:envHintIn  .5s ease .15s backwards,
            envHintBob 1.8s ease-in-out .65s infinite;
}
@keyframes envHintIn{
  from{ opacity:0; transform:translateX(-50%) translateY(8px); }
  to  { opacity:1; transform:translateX(-50%) translateY(0); }
}
.env-hint::before{
  content:"";
  width:1.15em;height:1.15em;
  flex:0 0 auto;
  background:no-repeat center/contain;
  /* finger-tap icon in warm gold */
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23E8D9B0' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 11V6a1.5 1.5 0 0 1 3 0v5'/%3E%3Cpath d='M12 11V4.5a1.5 1.5 0 0 1 3 0V11'/%3E%3Cpath d='M15 11V6.5a1.5 1.5 0 0 1 3 0V13c0 4-2.5 7-6 7s-6-2.5-6-6.5c0-1 .5-1.8 1.4-2.2L9 11'/%3E%3C/svg%3E");
}
/* The fade-out target only. Freezing the pill in place is done in JS
   (openEnvelope), not here, because it has to pin the OPACITY too:
   animation-play-state:paused on its own held a half-finished entry
   animation - opacity included - and the hint stayed on screen. A bare
   "animation:none" is no good either; it snapped the pill back to its
   resting position mid-bob, up to 7px, so it twitched as it faded. */
#envScreen.open .env-hint{opacity:0;}
#envScreen{cursor:pointer;}
#envScreen:focus-visible{outline:2px solid rgba(201,169,97,.6);outline-offset:-6px;}
@keyframes envHintBob{
  0%,100%{transform:translateX(-50%) translateY(0);}
  50%{transform:translateX(-50%) translateY(-7px);}
}
/* On a narrow phone, let the hint WRAP instead of hanging off the edges.
   The Dutch wording is 29 characters to English's 20, and at 320px wide
   (an iPhone SE) the pill measured 354px - 17px of it clipped off each
   side, with the text cut in half. white-space only breaks a line when
   it has to, so English still sits on one line here; Dutch takes two. */
@media(max-width:430px){
  .env-hint{
    /* NOT flex here. The text is an anonymous flex item, and once it is
       allowed to wrap flex shrinks it to its longest WORD - the pill
       collapsed to 160px and four lines. As a plain block with
       width:max-content it takes its natural one-line width, and only
       the max-width above forces the break. */
    display:block;
    width:max-content;
    white-space:normal;
    text-align:center;
    letter-spacing:.16em;
    line-height:1.55;
  }
  .env-hint::before{
    display:inline-block;
    vertical-align:-.22em;
    margin-right:.5em;
  }
}

@media (prefers-reduced-motion: reduce){
  /* both hints bob; neither should for a guest who asked for less motion.
     The fade-in stays - it is what hides the language swap, and it is a
     dissolve rather than movement. */
  html.texts-ready .env-hint{animation:envHintIn .5s ease .15s backwards;}
  .scroll-hint{animation:none;}
}

/* ===== language switch (EN | NL) ===== */
/* ---- the top-right control strip ---------------------------------
   Sound and language sit in one flex row. Done this way rather than
   giving the sound button its own fixed "right: 78px" so that the two
   can never overlap: the language pill's width depends on its font and
   wording, and any guess made here would eventually be wrong. */
#topControls{
  position:fixed;
  top:calc(env(safe-area-inset-top, 0px) + 14px);
  right:16px;
  z-index:60;
  display:flex;
  align-items:center;
  gap:8px;
}
/* inside the strip the language pill is laid out by the flex row, so it
   gives up the fixed position it uses when it is on its own */
#topControls #langSwitch{ position:static; top:auto; right:auto; }

/* ---- sound on/off -------------------------------------------------
   Same pill, same gold, so the two read as one control. Only put on
   the page when a sound file is actually configured. */
#soundToggle{
  position:relative;
  display:flex;align-items:center;justify-content:center;
  width:34px;height:34px;
  padding:0;
  background:rgba(20,28,22,.5);
  border:1px solid rgba(201,169,97,.4);
  border-radius:999px;
  backdrop-filter:blur(4px);
  -webkit-backdrop-filter:blur(4px);
  color:var(--gold-lt);
  cursor:pointer;
  transition:color .25s ease, border-color .25s ease, opacity .25s ease;
}
#soundToggle:hover{ color:var(--gold); border-color:rgba(201,169,97,.7); }
#soundToggle.is-muted{ color:rgba(230,205,132,.45); }
#soundToggle:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }
/* fingertip-sized without making the pill itself bigger */
#soundToggle::after{ content:''; position:absolute; inset:-6px; }

#langSwitch{
  position:fixed;
  top:calc(env(safe-area-inset-top, 0px) + 14px);
  right:16px;
  z-index:60;
  display:flex;
  align-items:center;
  gap:.35em;
  padding:.3em .7em;
  background:rgba(20,28,22,.5);
  border:1px solid rgba(201,169,97,.4);
  border-radius:999px;
  backdrop-filter:blur(4px);
  -webkit-backdrop-filter:blur(4px);
  font-family:'Cinzel',serif;
}
#langSwitch button{
  background:none;
  border:none;
  cursor:pointer;
  font-family:inherit;
  font-size:.72rem;
  letter-spacing:.12em;
  color:rgba(220,210,185,.55);
  padding:.15em .25em;
  transition:color .25s ease;
}
#langSwitch button:hover{ color:rgba(220,210,185,.85); }
#langSwitch button.lang-active{ color:var(--gold-lt); }
#langSwitch .lang-sep{ color:rgba(201,169,97,.4); font-size:.7rem; }
#langSwitch button:focus-visible{ outline:1px solid rgba(201,169,97,.6); outline-offset:2px; border-radius:3px; }

/* ===== combined venue "Getting There" panel (address + travel) ===== */
.gt-addr{ text-align:center; margin-bottom:6px; }
.gt-addr .venue-loc-addr{
  font-family:'Cormorant Garamond',serif; font-style:italic;
  font-size:clamp(1.1rem,2.6vw,1.4rem); color:var(--text-lt); line-height:1.6;
}
.gt-addr .venue-loc-time{
  font-family:'Cinzel',serif; font-size:.62rem; letter-spacing:.3em;
  color:var(--gold); margin-top:10px;
}
/* The two times, in bold. Cinzel at .62rem with wide letter-spacing
   reads very light next to the address above it, so both are turned up.
   How far is SETTINGS.venueTimeWeight in js/settings.js - one dial each,
   so either can be leaned on without touching the other.

   Selected by content path rather than a new class, so nothing in
   index.html has to change. The number after the comma is the fallback,
   i.e. what you get if settings.js ever fails to load. */
.gt-addr [data-c="venue.ceremony.time"] { font-weight:var(--venue-time-w-ceremony, 600); }
.gt-addr [data-c="venue.reception.time"]{ font-weight:var(--venue-time-w-reception, 600); }
.gt-divider{
  height:1px; margin:22px auto 26px;
  background:linear-gradient(90deg, transparent, rgba(201,169,97,.5), transparent);
}

/* ===== RSVP personal greeting + gate ===== */
.rsvp-greeting{
  font-family:'Cormorant Garamond',serif;
  font-size:clamp(1.15rem,2.6vw,1.5rem);
  color:var(--ivory);font-style:italic;line-height:1.5;
  margin-bottom:26px;padding-bottom:22px;
  border-bottom:1px solid rgba(201,168,76,.2);
}
.rsvp-greeting .rg-seats{
  display:block;margin-top:8px;
  font-family:'Cinzel',serif;font-style:normal;
  font-size:.62rem;letter-spacing:.24em;color:var(--gold-lt);
}
.rsvp-gate{
  padding:26px 24px;border:1px solid rgba(201,168,76,.25);
  text-align:center;color:var(--ivory);
}
.rsvp-gate .rg-title{
  font-family:'Cinzel',serif;font-size:.6rem;letter-spacing:.3em;
  color:var(--gold);margin-bottom:12px;
}
.rsvp-gate .rg-body{
  font-family:'Cormorant Garamond',serif;font-style:italic;
  font-size:1.1rem;line-height:1.6;color:rgba(220,210,185,.85);
}
/* The address, wherever {email} was written in the wording. This used to
   be an inline style="" on a link built in JS; as a class it is one more
   thing the Content-Security-Policy does not have to make room for. */
.rg-mail{
  color:var(--gold-lt);
  text-decoration:none;
  border-bottom:1px solid rgba(201,168,76,.35);
  font-style:normal;
  white-space:nowrap;
}
.rg-mail:hover{ color:var(--gold); border-bottom-color:var(--gold); }

/* ===== RSVP per-person checkboxes ===== */
.people-list{display:flex;flex-direction:column;gap:2px;}
.person-row{
  display:flex;align-items:center;gap:14px;
  padding:12px 14px;cursor:pointer;
  border:1px solid transparent;border-radius:4px;
  transition:background .18s ease,border-color .18s ease;
}
.person-row:hover{background:rgba(140,174,159,.08);}
.person-row.is-on{border-color:rgba(201,168,76,.35);background:rgba(201,168,76,.06);}
.person-row.person-all{
  border-bottom:1px solid rgba(201,168,76,.22);
  border-radius:4px 4px 0 0;margin-bottom:4px;
}
.person-all .person-name{font-family:'Cinzel',serif;font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;}
.person-box{
  flex:0 0 auto;width:22px;height:22px;border-radius:4px;
  border:1.5px solid rgba(201,168,76,.55);background:transparent;
  position:relative;transition:background .18s ease,border-color .18s ease;
}
.person-row.is-on .person-box{background:var(--gold);border-color:var(--gold);}
.person-box svg{position:absolute;inset:0;width:100%;height:100%;opacity:0;transform:scale(.6);transition:opacity .18s ease,transform .18s ease;}
.person-row.is-on .person-box svg{opacity:1;transform:scale(1);}
.person-name{
  font-family:'Cormorant Garamond',serif;font-size:1.18rem;
  color:var(--ivory);line-height:1.2;
}
.person-row input[type="checkbox"]{position:absolute;opacity:0;width:0;height:0;pointer-events:none;}

/* plus-one (a member row whose name is "(placeholder)").
   It works like any other person: tick the box first, and only then does
   the name field appear. The box itself is locked until the person who is
   bringing them is ticked - a plus one can never come on their own. */
.person-plusone{flex-wrap:wrap;}
/* the tickable part: box + "Plus one". The name field sits OUTSIDE this
   label on purpose - inside it, clicking the field would toggle the box. */
.plusone-head{
  display:flex;align-items:center;gap:14px;
  flex:1 1 auto;cursor:pointer;
}
/* locked = the person bringing them has not been ticked yet */
.person-plusone.is-locked{opacity:.4;}
.person-plusone.is-locked .plusone-head{cursor:not-allowed;}
.person-plusone.is-locked:hover{background:transparent;}
.plusone-input{
  display:none;                 /* revealed by .is-on below */
  flex:1 1 100%;min-width:0;width:100%;margin-top:10px;background:transparent;
  border:1px solid rgba(201,168,76,.4);border-radius:4px;
  font-family:'Cormorant Garamond',serif;font-size:1.18rem;font-style:italic;
  color:var(--ivory);padding:10px 14px;outline:none;
}
.person-plusone.is-on .plusone-input{display:block;}
.plusone-input::placeholder{color:rgba(220,210,185,.5);font-style:italic;}
.plusone-input:focus{border-color:var(--gold);background:rgba(201,168,76,.05);}

/* ===== full-screen typed intro ("Our Journey starts here...") ===== */
#introOverlay{
  /* Anchored to the TOP of the page, sized to one screen height - so it
     sits exactly over the hero video and is HARD-LOCKED to the video's
     centre, not the screen's. It scrolls together with the video, so the
     "Our Journey" line stays dead-centre on the footage even if a guest
     scrolls before it fades (it never jumps to the middle of the screen). */
  position:absolute;top:0;left:0;right:0;
  height:100vh;height:100svh;z-index:200;
  display:none;                       /* switched to flex by JS when it runs */
  align-items:center;justify-content:center;
  /* transparent - the hero video plays behind it. A soft radial scrim
     keeps the text readable over any frame. */
  background:radial-gradient(ellipse at center, rgba(18,26,20,.55) 0%, rgba(18,26,20,.35) 45%, rgba(18,26,20,.15) 100%);
  padding:32px;text-align:center;
  opacity:1;transition:opacity 1s ease;
  pointer-events:none;                /* never blocks taps on the site */
}
#introOverlay.intro-show{display:flex;}
#introOverlay.intro-hide{opacity:0;}
.intro-inner{
  display:inline-flex;align-items:baseline;
  max-width:90%;
}
#introText{
  font-family:'Cormorant Garamond',serif;font-style:italic;
  font-size:clamp(1.6rem,5.5vw,3rem);color:var(--ivory);
  line-height:1.3;white-space:pre-wrap;
  text-shadow:0 2px 18px rgba(0,0,0,.6), 0 1px 3px rgba(0,0,0,.5);
}
.intro-caret{
  display:inline-block;width:2px;height:1em;margin-left:4px;
  background:var(--gold-lt, #d8b45c);
  transform:translateY(.12em);
  animation:introBlink .8s step-end infinite;
}
.intro-caret.caret-done{animation:none;opacity:0;transition:opacity .4s ease;}
@keyframes introBlink{50%{opacity:0;}}
@media (prefers-reduced-motion: reduce){
  .intro-caret{animation:none;}
}

/* ===== RSVP decline + single-seat choices ===== */
.people-head{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  flex-wrap:wrap;margin-bottom:6px;
  border-bottom:1px solid rgba(201,168,76,.22);padding-bottom:6px;
}
.people-head .person-row.person-all{border-bottom:none;margin-bottom:0;flex:1;min-width:160px;}
.decline-btn{
  flex:0 0 auto;background:transparent;cursor:pointer;
  border:1px solid rgba(201,168,76,.4);border-radius:999px;
  color:rgba(220,210,185,.8);
  font-family:'Cinzel',serif;font-size:.56rem;letter-spacing:.14em;text-transform:uppercase;
  padding:9px 16px;transition:all .2s ease;
}
.decline-btn:hover{border-color:var(--gold);color:var(--ivory);}
.decline-btn.is-on{background:rgba(150,60,50,.5);border-color:#c98a80;color:#f2ddd8;}
/* single-seat: two big choices */
.single-choice{display:flex;flex-direction:column;gap:12px;}
.choice-btn{
  width:100%;cursor:pointer;text-align:center;
  background:transparent;border:1px solid rgba(201,168,76,.4);border-radius:6px;
  color:var(--ivory);font-family:'Cormorant Garamond',serif;font-size:1.2rem;
  padding:16px 18px;transition:all .2s ease;
}
.choice-btn:hover{border-color:var(--gold);}
.choice-yes.is-on{background:rgba(201,168,76,.18);border-color:var(--gold);color:var(--gold-lt);}
.choice-no.is-on{background:rgba(150,60,50,.4);border-color:#c98a80;color:#f2ddd8;}


/* ==============================================================
   [C13] PHONES - corrections for small screens and iOS.
   Kept LAST so these win over the rules above. Nothing here
   changes the desktop design; every rule is either inside a
   phone-width media query or is a no-op on a desktop browser.
============================================================== */

/* -- 1. stop the page scrolling sideways ------------------------
   .container gives 12px of side padding, but .row.g-5 pulls itself
   24px WIDER on each side, so the row hung 12px off the screen.
   It looked fine only because body{overflow-x:hidden} was clipping
   it - the page was really 388px wide inside a 375px screen.
   Below 992px every column is already full width, so the horizontal
   half-gutter has nothing left to space apart. Dropping it to match
   the container leaves the text in exactly the same place (12px in
   from the edge) and the row stops overhanging.
   row-gap:3rem is set separately and is untouched.               */
@media(max-width:991px){
  .row.g-5{ --gx:.75rem; }
}

/* -- 2. iOS: 100vh is TALLER than the screen --------------------
   On iPhone, 100vh means "the viewport with the address bar hidden",
   so a 100vh box always overflows what you can actually see.
   Written twice so a browser that has never heard of the new unit
   simply keeps the old value.

   The ENVELOPE keeps svh - the small, bar-visible height. It is a
   scroll lock on a screen nobody scrolls, and locking to the smallest
   height is what guarantees it can never overflow.

   The HERO uses dvh - the height right now, following the address bar
   as it comes and goes. At svh it stayed the bar-visible height even
   after Safari had retracted the bar, and the extra strip of screen
   showed the next section under the video. See the note by #wsHero. */
body.env-open{ height:100vh; height:100svh; }
#wsHero{ min-height:100vh; min-height:100dvh; }

/* -- 3. the floating RSVP seal ----------------------------------
   HISTORY, so nobody re-adds the old fix: fabScale is tuned for a
   desktop screen, and on a 375px phone that same one number rendered
   a 134px seal - over a THIRD of the screen width, sitting on top of
   the content. It used to be capped at 23vw here. That cap has been
   removed because fabScale now has its OWN phone value in
   js/settings.js (fabScale.mobile), so the size is set deliberately
   rather than clamped behind the setting's back. A cap would make
   that new knob look broken - type a bigger number, nothing happens.
   If the seal is too big on a phone, lower fabScale.mobile.        */
/* lift it clear of the iPhone home-indicator bar (0 on every other device).
   The home-indicator inset is ADDED to your setting rather than replacing
   it, so "20px from the bottom" still means 20px of visible gap on an
   iPhone instead of 20px measured from behind the bar. */
#rsvpFab{ bottom:calc(var(--fab-bottom-now,28px) + env(safe-area-inset-bottom, 0px)); }

/* -- 4. fingertip-sized tap targets -----------------------------
   The EN|NL buttons were 25x18px and the email link 176x14px - well
   under the ~44px a fingertip needs. An invisible pseudo-element
   grows the TOUCH area without moving a single pixel on screen, so
   the switch and the link still look exactly as designed.         */
@media(max-width:991px){
  #langSwitch button{ position:relative; }
  #langSwitch button::after{
    content:''; position:absolute; top:50%; left:50%;
    transform:translate(-50%,-50%);
    width:38px; height:44px;          /* 38px = just short of touching its neighbour */
  }
  .rsvp-intro a{ position:relative; display:inline-block; }
  .rsvp-intro a::after{
    content:''; position:absolute; left:0; right:0; top:50%;
    height:44px; transform:translateY(-50%);
  }
}

/* -- 5. the smallest labels ------------------------------------
   The engraved Cinzel labels are letter-spaced, which makes them
   read larger than their size suggests - but 7.7px is below what
   anyone should have to squint at on a phone. Only the ones a guest
   actually needs to READ are nudged up; the hero wording and the
   decorative placeholder captions are left exactly as designed.
   To undo, delete this block.                                    */
@media(max-width:575px){
  .sec-label     { font-size:.58rem; }   /* "Our Story", "The Location"  */
  /* two classes, to outrank the .gt-addr .venue-loc-time rule above */
  .venue-loc-time,
  .gt-addr .venue-loc-time{ font-size:.74rem; }   /* the ceremony / arrival times */
  .foot-date     { font-size:.66rem; }
  .rsvp-intro a  { font-size:.74rem; }   /* the contact email address    */
}

/* -- 6. small iOS/Android manners -------------------------------
   text-size-adjust: stops iOS inflating text of its own accord when
   the phone is turned to landscape.
   tap-highlight: replaces the default grey flash on tap with a faint
   gold one, so tapping the envelope feels like part of the design.  */
html{ -webkit-text-size-adjust:100%; text-size-adjust:100%; }
#envScreen, #rsvpFab, a, button, .person-row, .plusone-head, .choice-btn{
  -webkit-tap-highlight-color:rgba(201,168,76,.18);
}
