/* Chupp / Smith wedding — shared styles.
   Palette values come from ../../brand/PALETTE.md. Do not introduce colors
   outside that table (dark ramps of pine are the one sanctioned extension).

   Architecture references, per Christian's direction 2026-08-07 (session 10):
   - Header/hero: a full-bleed engagement photo (hands + ring + bible,
     assets/photos/hero.jpg) behind a fixed translucent nav — uppercase serif
     links either side of a "C & C" italic monogram, thin gold rule with a
     leaf glyph, italic subline, gold-outline button, "scroll to explore".
     The separate "Enter Site" gate page is retired entirely — index.html
     IS the homepage now.
   - Site-wide motion below the fold: flyhyer.com (full-bleed color-block
     sections, oversized headlines, smooth scroll, parallax imagery, dashed
     traveling-leaf path).
   Motion lives in motion.js (GSAP + Lenis); everything here must look right
   with JS off. */
:root{
  --pine:#174A4A;        /* Deep Pine Teal — primary text/buttons */
  --pine-deep:#0E3232;   /* darker pine ramp — hovers, dark-block depth */
  --sage:#9EAA9C;        /* Soft Eucalyptus Sage — logo disc, accents */
  --eucalyptus:#73806C;  /* Muted Eucalyptus — secondary accents */
  --ivory:#F4EFE5;       /* Soft Ivory — page background */
  --cream:#E8DDC9;       /* Cream — cards, borders */
  --stone:#A68D73;       /* Warm Stone — muted labels (large text only) */
  --beige:#D8C3A5;       /* Champagne Beige — the site's "gold" */
  --accent:#9E7958;      /* Groom's tie tone — italic accents, links */
  --cognac:#70452D;      /* Cognac Brown */

  /* Pulled directly from Christian's Canva source file (2026-08-08).
     Nav in Canva uses "Libra Serif Modern", a Canva-exclusive font with no
     free web-embeddable version — substituted with the same Cormorant
     Garamond family (tracked uppercase) rather than a mismatched stand-in. */
  --font-serif:'Cormorant Garamond',Georgia,serif;
  --font-script:'Alex Brush',cursive;
  --font-sans:'Jost',system-ui,sans-serif;

  --radius-card:18px;
  --header-h:88px;
  --z-nav:50;
  --z-mobile-nav:52;
  --z-trail:55;
  --z-toggle:60;
}
@media(max-width:640px){:root{--header-h:68px}}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
html.lenis{scroll-behavior:auto}
body{
  font-family:var(--font-sans);
  color:var(--pine);background:var(--ivory);
  line-height:1.7;font-size:17px;
  overflow-x:hidden;
  padding-top:var(--header-h);
}
/* The homepage's hero photo fills the full viewport behind the fixed nav —
   no reserved top padding there, the header floats on top of the image. */
body.hero-page{padding-top:0}
/* Ground truth pulled directly from Christian's Canva source file
   (2026-08-08): headline is Cormorant Garamond, plain uppercase, uniform
   cap height — NOT small-caps. Small-caps was a misread of the reference
   screenshot; corrected here. */
h1,h2,h3{
  font-family:var(--font-serif);font-weight:400;line-height:1.12;text-wrap:balance;
  text-transform:uppercase;letter-spacing:.03em;
}
img{max-width:100%;display:block}
a{color:var(--accent);text-decoration:none}
/* Every :hover rule in this file is gated behind (hover:hover) — without
   it, touch devices (iOS Safari especially) eat the first tap on any link
   to "trigger" the hover state and need a second tap to actually navigate.
   No hover-capable pointer, no hover CSS, no double-tap requirement. */
@media(hover:hover){a:hover{text-decoration:underline}}
p{max-width:68ch}
.wrap{max-width:1160px;margin:0 auto;padding:0 clamp(20px,4vw,48px)}
.center{text-align:center}
.center p{margin-left:auto;margin-right:auto}

/* Display type — the voice of the site. Italic spans carry the accent color. */
.display{
  font-family:var(--font-serif);
  font-size:clamp(42px,7.2vw,88px);
  line-height:1.06;letter-spacing:-.01em;
  font-weight:400;text-wrap:balance;
}
.display em,.section-title em{font-style:italic;font-size:1.08em;color:var(--accent)}
.section-title{
  font-size:clamp(32px,4.6vw,58px);
  line-height:1.1;letter-spacing:-.005em;margin-bottom:20px;
}
.lede{
  font-size:clamp(18px,2.2vw,22px);line-height:1.55;color:var(--cognac); /* was --eucalyptus, ~3.1:1 on cream, failed AA -- same fix as .fine-print/.msg-note/.goal-bar-label */
}
.on-dark .lede{color:var(--cream)}

/* ---------- nav — fixed translucent header, per Christian's reference ----------
   Uppercase serif link groups flank a "C & C" italic monogram. Always fixed
   and always has its dark scrim, so it reads correctly both floating over
   the hero photo and sitting on the plain ivory body of every inner page —
   one component, no page-specific cases. */
header.site-header{
  position:fixed;top:0;left:0;right:0;z-index:var(--z-nav);
  height:var(--header-h);
  background:rgba(14,50,50,.34);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border-bottom:1px solid rgba(244,239,229,.14);
}
.nav{
  height:100%;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  gap:12px;padding:0 clamp(16px,3vw,40px);max-width:1320px;margin:0 auto;
}
.nav-group{display:flex;align-items:center;gap:clamp(2px,1.4vw,10px)}
.nav-group.nav-right{justify-content:flex-end}
.nav-links{display:flex;align-items:center;gap:clamp(2px,1.4vw,10px)}
.nav-links a{
  font-family:var(--font-serif);color:var(--cream);
  font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;
  padding:8px 11px;border-bottom:1px solid transparent;
  transition:color .2s,border-color .2s;white-space:nowrap;
}
@media(hover:hover){.nav-links a:hover{color:var(--ivory);text-decoration:none;border-color:rgba(244,239,229,.5)}}
.nav-links a.active{color:var(--ivory);border-color:var(--beige)}
.nav-mono{
  grid-column:2;font-family:var(--font-serif);font-style:normal;
  font-size:clamp(26px,4vw,32px);letter-spacing:.06em;color:var(--ivory);
  white-space:nowrap;
}
.nav-mono em{font-style:normal;font-family:var(--font-script);color:var(--beige);margin:0 .1em;font-size:1.3em}
@media(hover:hover){.nav-mono:hover{text-decoration:none;opacity:.85}}

/* mobile: side link groups collapse into a hamburger + full-screen sheet */
.nav-toggle{
  display:none;grid-column:3;justify-self:end;
  width:44px;height:44px;flex-direction:column;justify-content:center;align-items:flex-end;gap:6px;
  background:none;border:none;cursor:pointer;padding:0; /* box grown to a real 44x44 tap target; bars below stay the original visual size */
}
.nav-toggle span{display:block;height:1px;width:22px;background:var(--ivory)}
.mobile-sheet{
  position:fixed;inset:0;z-index:var(--z-mobile-nav);
  background:var(--pine-deep);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:26px;
  opacity:0;pointer-events:none;transition:opacity .3s ease;
}
.mobile-sheet.open{opacity:1;pointer-events:auto}
.mobile-sheet-close{
  position:absolute;top:12px;right:clamp(10px,2.4vw,34px);
  width:44px;height:44px;background:none;border:none;cursor:pointer;
  display:flex;align-items:center;justify-content:center; /* box grown to 44x44, glyph size below unchanged */
}
.mobile-sheet-close span{
  position:absolute;width:22px;height:1px;background:var(--ivory);
}
.mobile-sheet-close span:first-child{transform:rotate(45deg)}
.mobile-sheet-close span:last-child{transform:rotate(-45deg)}
.mobile-sheet a{
  font-family:var(--font-serif);color:var(--ivory);font-size:22px;
  letter-spacing:.1em;text-transform:uppercase;
}
.mobile-sheet a.active{color:var(--beige)}
@media(max-width:860px){
  /* Symmetric 1fr/auto/1fr, not auto/1fr/auto -- the first column has
     nothing in it now that nav-left is hidden (see below), so an "auto"
     track there collapses to 0 width with no counterweight to the
     hamburger's column on the right, visibly pushing the monogram left of
     true center. Two equal 1fr tracks stay equal regardless of which side
     has content in it. */
  .nav{grid-template-columns:1fr auto 1fr}
  /* Both side groups must be display:none outright, not just their links —
     an empty-but-present .nav-right div still occupies a grid cell, which
     collided with .nav-toggle's explicit grid-column:3 and silently bumped
     the button into an implicit second row below the header. */
  .nav-group.nav-left,.nav-group.nav-right{display:none}
  .nav-mono{grid-column:2;justify-self:center}
  .nav-toggle{display:flex}
}

/* greeting banner — shown only when a household code is resolved. Sits
   just under the fixed header as a slim toast, doesn't push layout. */
.greeting{
  position:fixed;top:var(--header-h);left:0;right:0;z-index:calc(var(--z-nav) - 1);
  background:var(--sage);color:#fff;text-align:center;
  padding:12px 24px;font-family:var(--font-serif);
  font-size:clamp(17px,2.4vw,22px);letter-spacing:.02em;
  box-shadow:0 8px 20px -10px rgba(14,50,50,.4);
}
.greeting[hidden]{display:none}

/* buttons */
.btn{
  display:inline-block;padding:14px 30px;border-radius:999px;
  font-size:15px;letter-spacing:.04em;cursor:pointer;
  border:1px solid transparent;transition:background .2s,color .2s,transform .2s;
  font-family:var(--font-sans);font-weight:500;
}
.btn-primary{background:var(--pine);color:var(--ivory)}
.btn-ghost{border-color:var(--pine);color:var(--pine);background:transparent}
.btn:disabled{opacity:.5;cursor:not-allowed}
.on-dark .btn-primary{background:var(--ivory);color:var(--pine)}
.on-dark .btn-ghost{border-color:var(--cream);color:var(--cream)}

/* pill CTA — gridless-style: full pill, dark, drop shadow, flowing arrow.
   Arrow choreography adapted from 21st.dev's Flow Button (xubohuah): two
   arrows (one entering from off-left, one resting at the right) swap sides
   on hover so the CTA reads as "sliding forward," plus a pill->rounded-rect
   radius morph. No circle-reveal here (unlike .hero-cta) -- this button is
   already solid-filled at rest, nothing to reveal. */
.btn-pill{
  position:relative;overflow:hidden;
  display:inline-flex;align-items:center;gap:10px;
  padding:17px 36px;border-radius:999px;
  background:var(--pine);color:var(--ivory);
  font-family:var(--font-sans);font-weight:500;font-size:16px;letter-spacing:.02em;
  box-shadow:0 12px 28px -10px rgba(14,50,50,.5);
  transition:background .25s,transform .25s,box-shadow .25s,border-radius .6s cubic-bezier(.23,1,.32,1);
}
.btn-pill-text{position:relative;transition:transform .8s cubic-bezier(0,0,.2,1)}
/* Fixed-size flex slot for each arrow -- transform/opacity animate inside
   it, the slot's own width never changes, so the button's layout box stays
   stable through the hover transition (no reflow/thrash). */
.btn-pill-arrow{width:15px;height:15px;flex:none;transition:transform .8s cubic-bezier(.25,1,.5,1),opacity .8s cubic-bezier(.25,1,.5,1)}
.btn-pill-arrow-in{transform:translateX(-24px);opacity:0}
.btn-pill-arrow-out{transform:translateX(0);opacity:1}
.btn-pill:active{transform:scale(.97)}

/* Hover-only lift/color states, all buttons — see the (hover:hover) note
   near the top of this file. */
@media(hover:hover){
  .btn-primary:hover{background:var(--pine-deep);transform:translateY(-1px);text-decoration:none}
  .btn-ghost:hover{background:var(--pine);color:var(--ivory);text-decoration:none}
  .on-dark .btn-primary:hover{background:var(--cream)}
  .on-dark .btn-ghost:hover{background:var(--cream);color:var(--pine)}
  .btn-pill:hover{
    background:var(--pine-deep);transform:translateY(-2px);
    box-shadow:0 18px 34px -10px rgba(14,50,50,.55);
    border-radius:12px;
    text-decoration:none;
  }
  .btn-pill:hover .btn-pill-text{transform:translateX(4px)}
  .btn-pill:hover .btn-pill-arrow-in{transform:translateX(0);opacity:1}
  .btn-pill:hover .btn-pill-arrow-out{transform:translateX(24px);opacity:0}
}

/* ---------- Section blocks — the flyhyer architecture ---------- */
section{padding:clamp(72px,11vw,140px) 0}
.block-dark{background:var(--pine);color:var(--ivory)}
.block-dark h1,.block-dark h2,.block-dark h3{color:var(--ivory)}
.block-sage{background:var(--sage);color:var(--pine-deep)}
.block-cream{background:var(--cream)}
.block-accent{background:var(--accent);color:var(--ivory)}
.block-accent h2,.block-accent h3{color:var(--ivory)}
.block-flush{padding:0}

/* Full-bleed parallax photo band */
.photo-band{
  position:relative;overflow:hidden;height:clamp(340px,58vh,560px);
}
.photo-band img{
  position:absolute;inset:-12% 0;width:100%;height:124%;
  object-fit:cover;will-change:transform;
}
.photo-band .photo-band-caption{
  position:absolute;left:0;right:0;bottom:0;z-index:1;
  padding:28px clamp(20px,4vw,48px);
  background:linear-gradient(transparent,rgba(14,50,50,.55));
  color:var(--ivory);font-family:var(--font-serif);
  font-size:clamp(20px,3vw,30px);font-style:italic;
}

/* Full-bleed hover-fan story gallery — flush tiles, one widens on hover */
.story-gallery{
  display:flex;height:clamp(320px,52vh,520px);overflow:hidden;
}
.story-tile{
  position:relative;flex:1 1 0;min-width:0;overflow:hidden;
  transition:flex-grow .5s cubic-bezier(.4,0,.2,1);
}
.story-tile img{
  width:100%;height:100%;object-fit:cover;display:block;
}
.story-tile::after{
  content:'';position:absolute;inset:0;
  background:rgba(14,50,50,.35);opacity:0;
  transition:opacity .4s ease;
}
@media(hover:hover){
  .story-tile:hover{flex-grow:4}
  .story-tile:hover::after{opacity:1}
}
/* Below the hover breakpoint the fan effect is unreachable (no hover to
   trigger it), so 6 flush equal-width tiles just read as ~60px photo
   strips -- unrecognizable on a phone, which is most guests. Swap to a
   swipeable snap-scroll row instead, same flush/full-bleed language as
   the map-list mobile pattern above. */
@media(max-width:640px){
  .story-gallery{overflow-x:auto;-webkit-overflow-scrolling:touch;scroll-snap-type:x mandatory}
  .story-tile{flex:none;width:78vw;scroll-snap-align:start}
}

/* Editorial info rows — replaces the identical-card grid */
.info-rows{border-top:1px solid var(--cream)}
.on-dark .info-rows{border-color:rgba(244,239,229,.2)}
.info-row{
  display:grid;grid-template-columns:minmax(140px,240px) 1fr;
  gap:18px 40px;padding:30px 0;border-bottom:1px solid var(--cream);
  align-items:baseline;
}
.on-dark .info-row{border-color:rgba(244,239,229,.2)}
.info-row h3{font-size:clamp(22px,2.6vw,30px)}
.info-row .info-k{
  font-size:14px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--eucalyptus);font-weight:500;
}
.on-dark .info-row .info-k{color:var(--sage)}
@media(max-width:640px){.info-row{grid-template-columns:1fr;gap:6px}}

/* Fine print — small disclaimer-weight note, not a full info-row */
.fine-print{
  /* --eucalyptus on --cream measures ~3.1:1, fails WCAG AA (4.5:1) for
     body text -- --cognac (already an approved, previously-unused PALETTE.md
     token) measures ~6:1 here. Same muted-secondary intent, actually readable. */
  font-size:13px;line-height:1.6;color:var(--cognac);
  margin-top:18px;max-width:60ch;
}
.on-dark .fine-print{color:rgba(244,239,229,.75)}

/* Big stat / countdown */
.big-number{
  font-family:var(--font-serif);
  font-size:clamp(72px,16vw,190px);line-height:1;letter-spacing:-.02em;
}

/* timeline — a real ordered sequence, numbers earn their place here */
.timeline{list-style:none;margin-top:8px}
.timeline li{
  display:grid;grid-template-columns:110px 1fr;gap:10px 34px;
  padding:26px 0;border-bottom:1px solid rgba(244,239,229,.2);
}
.timeline .t-time{
  font-family:var(--font-serif);font-size:clamp(22px,2.8vw,32px);
  font-style:italic;color:var(--sage);white-space:nowrap;
}
.timeline .t-what{font-size:clamp(17px,2vw,20px);align-self:center}
.timeline .t-what small{display:block;color:var(--cream);opacity:.8;font-size:14px;margin-top:2px}
@media(max-width:560px){.timeline li{grid-template-columns:1fr;gap:2px}}

/* Honeymoon Fund goal bar -- HeroUI's currency meter (21st.dev) for the
   visual idea (label, formatted output, track/fill), hand-built here in
   plain markup. Fill's width lives as an inline style in the HTML (the
   real, final value, visible with JS off); motion.js's goalMeter() is the
   only place 0 ever appears, and only at runtime.
   Shows a percentage only, never a dollar figure -- the $5,000 goal that
   100% maps to is a private internal number, not shown to guests. Giving
   above the goal is allowed: the label can read past 100% (e.g. "114%"),
   but the fill's own width always caps at 100% since it can't visually
   overflow its track -- see the comment above each .goal-bar-fill in the
   HTML for how to hand-update both together. */
.goal-bar-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px;margin-bottom:10px}
.goal-bar-label{font-family:var(--font-serif);font-size:15px;letter-spacing:.08em;text-transform:uppercase;color:var(--cognac)} /* same contrast fix as .fine-print */
.goal-bar-output{font-family:var(--font-serif);font-size:20px;color:var(--pine)}
.goal-bar-track{height:10px;border-radius:999px;background:var(--cream);border:1px solid var(--stone);overflow:hidden;position:relative}
.goal-bar-fill{height:100%;border-radius:999px;background:var(--beige)}
.goal-bar-fill.is-over{background:var(--accent)}
.goal-bar-bonus{position:absolute;top:0;bottom:0;left:74.07%;right:0;background:repeating-linear-gradient(135deg,transparent 0 5px,rgba(0,0,0,.05) 5px 10px)} /* the extra 35% past the goal */
.goal-marker{position:absolute;top:0;height:10px;width:2px;background:var(--cognac);transform:translateX(-50%);z-index:2}
.goal-marker-label{position:absolute;top:14px;left:50%;transform:translateX(-50%);font-family:var(--font-sans);font-size:10.5px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--cognac);white-space:nowrap}

/* Milestone markers along the track -- structural idea (dotted line, ring
   markers, label above) borrowed from a generic timeline/roadmap slide
   layout, rebuilt on this site's own light palette (never that dark-navy
   corporate look, it'd clash with everything else here). Each marker is
   hand-authored at a fixed left% and carries "is-reached" as a plain class,
   same as .goal-bar-fill.is-over -- correct with JS off / reduced-motion on,
   no computed state. Green is the one deliberate exception to PALETTE.md's
   locked 10 colors: a completion checkmark reads as "done" universally and
   doesn't work in pine/beige/stone, flagged rather than silently added. */
.goal-bar-track-wrap{position:relative;margin-top:46px}
.goal-milestone{position:absolute;bottom:100%;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;padding-bottom:5px}
.goal-milestone-label{font-family:var(--font-sans);font-size:10.5px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--eucalyptus);white-space:nowrap;margin-bottom:4px}
.goal-milestone-line{width:1px;flex:1;min-height:12px;background:var(--stone)}
.goal-milestone-dot{position:absolute;top:100%;left:50%;transform:translate(-50%,-50%);width:18px;height:18px;border-radius:50%;background:var(--ivory);border:2px solid var(--stone);display:flex;align-items:center;justify-content:center;transition:background .3s,border-color .3s}
.goal-milestone-dot svg{width:10px;height:10px;color:var(--ivory);opacity:0;transition:opacity .3s}
.goal-milestone.is-reached .goal-milestone-dot{background:#2E7D4F;border-color:#2E7D4F}
.goal-milestone.is-reached .goal-milestone-dot svg{opacity:1}
@media(max-width:480px){.goal-milestone-label{font-size:9px}}

/* Travel map (travel.html) -- Leaflet + OpenStreetMap tiles, no API key, no
   paid service (matches the site's cost-cap rule). Structural idea (map +
   a synced side list, click a pin for an info card) borrowed from a generic
   map-widget product demo page, rebuilt here on the free open-source stack
   instead of that paid widget. Requires JS like the Cici chat widget does --
   the .map-fallback noscript list underneath is what guests without JS or
   with reduced-motion-adjacent script blockers actually see; it duplicates
   the same three addresses so nobody loses the information. */
.map-wrap{display:flex;gap:0;border:1px solid var(--cream);border-radius:var(--radius-card);overflow:hidden;background:#fff}
.map-list{flex:0 0 260px;border-right:1px solid var(--cream);padding:10px;max-height:420px;overflow-y:auto}
.map-list-item{display:block;width:100%;text-align:left;background:none;border:0;border-radius:12px;padding:14px 14px;cursor:pointer;font-family:inherit}
.map-list-item+.map-list-item{margin-top:2px}
.map-list-item:hover{background:var(--ivory)}
.map-list-item.is-active{background:var(--ivory)}
.map-list-item-head{display:flex;align-items:center;gap:10px}
.map-list-pin{flex:none;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff}
.map-list-pin svg{width:11px;height:11px}
.map-list-item-name{font-family:var(--font-serif);font-size:17px;color:var(--pine)}
.map-list-item-addr{font-size:12.5px;color:var(--eucalyptus);margin-top:4px;margin-left:30px}
.map-list-item-link{font-size:12px;color:var(--accent);margin-top:6px;margin-left:30px;display:inline-block}
#travelMap{flex:1;min-height:420px}
.map-fallback{display:none}
/* Hovering a sidebar row highlights its pin: brought above the others
   (z-index via marker.setZIndexOffset in JS) plus a ring + slight scale on
   the inner circle -- not the outer .leaflet-marker-icon element itself,
   since Leaflet manages that one's transform for positioning and a CSS
   transform there would fight it. */
.leaflet-marker-icon.map-marker-highlight{z-index:1000!important}
.leaflet-marker-icon.map-marker-highlight>div{
  transform:scale(1.3);
  box-shadow:0 0 0 4px rgba(244,239,229,.95),0 4px 10px rgba(0,0,0,.4);
}
@media(max-width:720px){
  .map-wrap{flex-direction:column}
  .map-list{flex:none;border-right:0;border-bottom:1px solid var(--cream);display:flex;gap:6px;overflow-x:auto;padding:10px}
  .map-list-item{flex:none;width:auto;white-space:nowrap}
  .map-list-item-addr,.map-list-item-link{display:none}
  #travelMap{min-height:320px}
}

/* Local recommendations list (travel.html) -- reuses .card, no new pattern.
   Content is Christian's own picks, hand-authored per item; keep the
   coming-soon block until a given area's list arrives rather than
   inventing names. Four fixed categories (restaurant/bar/outdoor/area),
   same icon-badge-on-a-colored-circle language as the map's pins above,
   for visual consistency down the page. All four colors are existing
   PALETTE.md tokens -- no new-color exception needed here. */
.rec-legend{display:flex;flex-wrap:wrap;gap:18px;margin-top:18px}
.rec-legend-item{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--eucalyptus)}
.rec-legend-dot{width:16px;height:16px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff}
.rec-legend-dot svg{width:9px;height:9px}
.rec-area+.rec-area{margin-top:44px}
.rec-area-title{font-family:var(--font-serif);font-size:clamp(22px,3vw,28px);font-style:italic;color:var(--pine)}
.rec-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;margin-top:20px}
.rec-card{background:#fff;border:1px solid var(--cream);border-radius:var(--radius-card);padding:18px 20px}
.rec-card-head{display:flex;align-items:center;gap:10px}
.rec-card-icon{flex:none;width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff}
.rec-card-icon svg{width:14px;height:14px}
.rec-card h3{font-family:var(--font-serif);font-size:18px;font-weight:500}
.rec-card-link{margin-top:10px;margin-left:36px;font-size:13px;color:var(--accent);display:inline-block}

/* cards + coming soon (kept for RSVP panel + honest placeholders) */
.card{background:#fff;border:1px solid var(--cream);border-radius:var(--radius-card);padding:32px}
.coming-soon{
  background:transparent;border:1px dashed var(--stone);
  border-radius:var(--radius-card);padding:34px 28px;text-align:center;color:var(--cognac);
  font-size:15px;letter-spacing:.04em;
} /* was --eucalyptus, ~3.1:1 on cream/ivory, fails WCAG AA -- same fix already
     applied to .lede/.fine-print/.msg-note/.goal-bar-label, missed here */
.on-dark .coming-soon{color:var(--cream);border-color:rgba(244,239,229,.4)}

/* forms */
label{display:block;font-size:14px;letter-spacing:.04em;margin-bottom:6px}
input[type=text],input[type=email],textarea{
  width:100%;padding:13px 14px;border:1px solid var(--stone);
  border-radius:10px;font-family:inherit;font-size:16px;background:#fff;color:var(--pine);
}
textarea{resize:vertical;min-height:90px}
input[type=text]:focus,input[type=email]:focus,textarea:focus{outline:2px solid var(--sage);outline-offset:1px}
.code-input{
  text-transform:uppercase;letter-spacing:.5em;font-size:24px;
  text-align:center;max-width:280px;margin:0 auto;
}
.msg{padding:13px 16px;border-radius:10px;font-size:15px;margin:16px 0}
.msg-error{background:#F6E3E3;color:#7A2E2E;border:1px solid #E0B9B9}
.msg-ok{background:#E4EDE4;color:#2E5233;border:1px solid #B9D0BC}
.msg-note{background:var(--cream);color:var(--cognac);border:1px solid var(--stone)} /* same contrast fix as .fine-print */
.msg[hidden]{display:none}
.link-btn{
  background:none;border:none;padding:0;cursor:pointer;
  font-family:var(--font-sans);font-size:14px;color:var(--accent);
  text-decoration:underline;text-underline-offset:3px;
}

/* RSVP attendance choice -- fieldset/legend is the native radiogroup
   (screen readers announce the legend as the group name; same-name radios
   already get arrow-key navigation between them natively, same behavior
   shadcn's radio-group wraps in Radix). Real <input type=radio> stays in
   the DOM and focusable -- only visually hidden -- so keyboard/AT behavior
   is untouched; the label's ::before dot + :has() reflect checked state
   in the site's own pill language instead of the bare OS control. */
.rsvp-row{border:0;margin:0;padding:14px 0;border-bottom:1px solid var(--cream)}
.rsvp-row legend{
  padding:0;margin-bottom:10px;width:100%;
  font-family:var(--font-serif);font-weight:500;font-size:17px;color:var(--pine);
}
.rsvp-choices{display:flex;gap:10px;flex-wrap:wrap}
.rsvp-choice{
  position:relative;display:inline-flex;align-items:center;gap:8px;
  padding:9px 18px 9px 16px;border-radius:999px;border:1px solid var(--stone);
  font-size:14px;letter-spacing:.02em;cursor:pointer;color:var(--pine);
  transition:background .2s,border-color .2s,color .2s;
}
.rsvp-choice input{
  position:absolute;width:1px;height:1px;padding:0;margin:0;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}
.rsvp-choice:has(input:checked){background:var(--pine);border-color:var(--pine);color:var(--ivory)}
.rsvp-choice:has(input:focus-visible){outline:2px solid var(--sage);outline-offset:2px}
@media(hover:hover){.rsvp-choice:hover{border-color:var(--pine)}}

/* footer — a real closing block, not an afterthought */
footer.site-footer{
  background:var(--pine);color:var(--cream);
  padding:clamp(56px,8vw,96px) clamp(20px,4vw,48px) 40px;
  text-align:center;
}
footer.site-footer .footer-line{
  font-family:var(--font-serif);font-size:clamp(30px,5vw,56px);
  color:var(--ivory);line-height:1.15;margin-bottom:18px;text-wrap:balance;
}
footer.site-footer .footer-line em{font-style:italic;color:var(--beige)}
footer.site-footer small{font-size:14px;letter-spacing:.06em;color:var(--sage);display:block;margin-top:26px}
footer.site-footer small a{color:var(--beige);text-decoration:underline;text-underline-offset:3px}

/* companion layer — cursor trail (site-wide), sound toggle */
#companion-trail{position:fixed;inset:0;z-index:var(--z-trail);pointer-events:none}
#companion-sound{position:fixed;left:20px;bottom:20px;z-index:var(--z-toggle);display:flex;align-items:center;gap:8px}
#companion-sound-toggle{
  width:44px;height:44px;border-radius:50%;flex:none;
  border:1px solid var(--pine);background:var(--ivory);color:var(--pine);
  font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;
  transition:background .2s,color .2s;
}
#companion-sound-toggle.on{background:var(--pine);color:var(--ivory)}
@media(hover:hover){#companion-sound-toggle:hover{background:var(--sage);color:var(--ivory)}}
/* Pause/skip pair, hover-revealed -- gated to real hover devices, same
   convention as every other :hover rule on this site (touch devices keep
   the toggle's existing tap-to-play/pause, just without this extra panel). */
#companion-sound-controls{
  display:flex;gap:6px;opacity:0;pointer-events:none;
  transform:translateX(-6px);transition:opacity .18s ease,transform .18s ease;
}
@media(hover:hover){
  #companion-sound:hover #companion-sound-controls,
  #companion-sound:focus-within #companion-sound-controls{
    opacity:1;pointer-events:auto;transform:translateX(0);
  }
}
.companion-sound-btn{
  width:34px;height:34px;border-radius:50%;flex:none;
  border:1px solid var(--pine);background:var(--ivory);color:var(--pine);
  font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center;
  transition:background .2s,color .2s;
}
@media(hover:hover){.companion-sound-btn:hover{background:var(--sage);color:var(--ivory)}}
@media(max-width:560px){
  #companion-sound{left:12px;bottom:12px}
  /* #companion-sound-toggle no longer shrinks here -- stays 44x44, the
     primary touch target should never get smaller on the breakpoint
     where touch is guaranteed */
  .companion-sound-btn{width:32px;height:32px}
}

/* ---------- hero collage — the homepage header ----------
   Pinned ~300vh zoom-parallax collage (ported from wedding V2's
   ZoomParallax.tsx/Hero.tsx onto this site's own GSAP+ScrollTrigger+Lenis
   stack in motion.js — ScrollTrigger scrub, no second animation setup).
   7 photos scale 1x up to 4-9x as the section scrolls; the headline/date/
   CTA overlay and a centered scrim fade in once the collage reads as one
   full-bleed photo, then the section unpins cleanly. */
.hero-collage{position:relative;height:300vh}
.hero-collage-sticky{
  position:sticky;top:0;height:100svh;min-height:640px;overflow:hidden;
  display:flex;align-items:center;justify-content:center;text-align:center;
  color:var(--ivory);
}
.hero-collage-tile{
  position:absolute;top:0;left:0;width:100%;height:100%;
  display:flex;align-items:center;justify-content:center;will-change:transform;
}
.hero-collage-tile-inner{position:relative;width:25vmin;height:25vh;overflow:hidden}
.hero-collage-tile-inner img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
}
.hero-collage-tile.tile-0{z-index:1}
/* Per-tile layout, matching ZoomParallax.tsx's collage geometry 1:1 --
   widths/left-offsets in vmin rather than vw (2026-08-23 fix). On a wide,
   short desktop window vw runs far ahead of vh, so vw-based widths spread
   the mosaic apart horizontally while tile heights (vh) stay put, reading
   as a sparse cluster with a lot of bare page background around/between
   photos. vmin = min(vw,vh) tracks whichever axis is smaller: on a tall
   mobile viewport that's still vw (unchanged from before), but on a wide
   desktop viewport it's vh, so the mosaic scales down to match tile height
   instead of stretching -- same tight collage on every aspect ratio.
   Heights/top-offsets stay vh; only the axis that broke needed the unit
   swap. */
.tile-1 .hero-collage-tile-inner{top:-30vh;left:5vmin;height:30vh;width:35vmin}
.tile-2 .hero-collage-tile-inner{top:-10vh;left:-25vmin;height:45vh;width:20vmin}
/* Default object-position (50% 50%) crops on the geometric center of the
   photo, not the couple in it -- both of these are tall/off-center source
   shots where that leaves mostly sand or sky in frame depending on
   viewport aspect. Pinned to where the actual subject sits instead. */
.tile-0 .hero-collage-tile-inner img{object-position:68% 38%}
.tile-2 .hero-collage-tile-inner img{object-position:47% 40%}
.tile-3 .hero-collage-tile-inner{left:27.5vmin;height:25vh;width:25vmin}
.tile-4 .hero-collage-tile-inner{top:27.5vh;left:5vmin;height:25vh;width:20vmin}
.tile-5 .hero-collage-tile-inner{top:27.5vh;left:-22.5vmin;height:25vh;width:30vmin}
.tile-6 .hero-collage-tile-inner{top:22.5vh;left:25vmin;height:15vh;width:15vmin}
/* Both start fully visible in plain HTML/CSS -- opacity:0 lives only in the
   gsap.set() call in motion.js's heroCollage(), which only ever runs if JS
   loaded, GSAP/ScrollTrigger loaded from the CDN, and reduced-motion is off.
   Same rule every other headline on this site follows via [data-lines] +
   gsap.from(): visible by default, JS animates FROM hidden only when it
   actually runs. A static opacity:0 here would strand reduced-motion guests
   (motion.js's boot() bails before heroCollage() runs) and anyone hitting a
   slow/blocked CDN script with an invisible headline and RSVP button. */
.hero-scrim{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  background:
    radial-gradient(ellipse at center,rgba(14,50,50,.55) 0%,rgba(14,50,50,.15) 55%,transparent 75%),
    linear-gradient(180deg,rgba(14,50,50,.8) 0%,rgba(14,50,50,.5) 45%,rgba(14,50,50,.15) 100%);
}
/* max-width was 1080px -- at desktop widths the one-line nowrap headline
   (up to 96px font) genuinely measures ~1200px wide, wider than that box.
   text-align:center does NOT symmetrically center an overflowing nowrap
   line; it anchors flush to the box's left edge and pushes all overflow
   right, which body's overflow-x:hidden then clips -- reads as the
   headline being shoved off-center to the right. 1300px gives the widest
   case (96px font) a real margin instead of shrinking the type. */
.hero-content{position:relative;z-index:3;padding:calc(var(--header-h) + 20px) 20px 0;max-width:1300px}
/* The global p{max-width:68ch} rule (body-text readability) was clamping
   these three short display lines narrower than .hero-content's own width,
   and with no margin:auto to self-center a shrunk block, they sat flush-
   left against the box's left edge instead of centered under the headline
   -- text-align:center only centers text WITHIN a box, not the box itself.
   These aren't body prose; the readability cap was never meant for them. */
.hero-content p{max-width:none}
.hero-content,.hero-content *{text-shadow:0 2px 18px rgba(14,50,50,.45)}
.hero-kicker{
  font-size:13px;letter-spacing:.36em;text-transform:uppercase;
  color:var(--cream);opacity:.92;margin-bottom:20px;
}
.hero-content h1{
  font-size:clamp(42px,7.4vw,96px);line-height:1;letter-spacing:.01em;
  white-space:nowrap;
}
.hero-content h1 em{font-style:normal;font-family:var(--font-script);font-size:1.45em;color:var(--beige);margin:0 .06em;vertical-align:-.08em}
@media(max-width:760px){.hero-content h1{white-space:normal}}
.hero-rule{display:flex;align-items:center;justify-content:center;gap:16px;margin:28px auto 22px;max-width:320px}
.hero-rule-line{flex:1;height:1px;background:rgba(244,239,229,.4)}
.hero-leaf{width:32px;height:16px;flex:none}
.hero-sub{
  font-family:var(--font-serif);font-style:italic;
  font-size:clamp(17px,2vw,21px);color:var(--cream);margin-bottom:16px;
}
.hero-meta{
  font-size:13px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--cream);opacity:.9;margin-bottom:38px;
}
/* Outline pill -> circle-reveal fill, adapted from 21st.dev's Flow Button
   (xubohuah): a small beige disc expands from the button's center to fill
   it on hover instead of a flat background-color swap, same two-arrow
   choreography as .btn-pill. Kept this button's own established squared
   (2px) corner identity rather than importing the reference's pill->12px
   radius morph -- that shape-shift fits a pill CTA, not this one. */
.hero-cta{
  position:relative;overflow:hidden;
  display:inline-flex;align-items:center;gap:12px;
  padding:16px 38px;border:1px solid var(--beige);border-radius:2px;
  color:var(--ivory);font-size:12.5px;letter-spacing:.2em;text-transform:uppercase;
  transition:border-color .5s,color .5s;
}
.hero-cta-text{position:relative;z-index:2;transition:transform .8s cubic-bezier(0,0,.2,1)}
.hero-cta-circle{
  position:absolute;top:50%;left:50%;
  width:16px;height:16px;margin:-8px 0 0 -8px;border-radius:50%;
  background:var(--beige);opacity:0;z-index:1;
  transition:transform .8s cubic-bezier(.19,1,.22,1),opacity .8s cubic-bezier(.19,1,.22,1);
  transform:scale(0);
}
.hero-cta-arrow{position:relative;z-index:2;width:15px;height:15px;flex:none;transition:transform .8s cubic-bezier(.25,1,.5,1),opacity .8s cubic-bezier(.25,1,.5,1)}
.hero-cta-arrow-in{transform:translateX(-26px);opacity:0}
.hero-cta-arrow-out{transform:translateX(0);opacity:1}
.hero-cta:active{transform:scale(.97)}
@media(hover:hover){
  .hero-cta:hover{border-color:transparent;color:var(--pine-deep);text-decoration:none}
  .hero-cta:hover .hero-cta-circle{transform:scale(24);opacity:1}
  .hero-cta:hover .hero-cta-text{transform:translateX(10px)}
  .hero-cta:hover .hero-cta-arrow-in{transform:translateX(0);opacity:1}
  .hero-cta:hover .hero-cta-arrow-out{transform:translateX(26px);opacity:0}
}
.hero-scroll{
  position:absolute;left:50%;bottom:36px;transform:translateX(-50%);z-index:1;
  display:flex;flex-direction:column;align-items:center;gap:10px;
  font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;
  color:var(--cream);opacity:.85;
}
.hero-scroll .line{width:1px;height:42px;background:linear-gradient(var(--cream),transparent);animation:heroScrollLine 1.9s ease-in-out infinite}
@keyframes heroScrollLine{
  0%{opacity:.15;transform:scaleY(.35);transform-origin:top}
  50%{opacity:1;transform:scaleY(1);transform-origin:top}
  100%{opacity:.15;transform:scaleY(.35);transform-origin:bottom}
}

/* ---------- wedding chat widget ----------
   Same architecture and layout as Sand Dollar Surplus Recovery's Shelly
   widget (website/site/index.html), recolored onto this site's own tokens
   instead of duplicating a second visual language. Rendered site-wide by
   assets/shared.js's renderChat(), not per-page like Shelly's. */
.wdw-chat{position:fixed;right:22px;bottom:22px;z-index:70;font-family:var(--font-sans)}
.wdw-bub{
  display:flex;align-items:center;gap:10px;
  background:var(--pine);color:var(--ivory);border:none;border-radius:28px;
  padding:12px 20px 12px 12px;cursor:pointer;
  box-shadow:0 8px 28px rgba(14,50,50,.28);font-size:15px;font-weight:500;
}
.wdw-bub img{width:30px;height:30px}
.wdw-teaser{
  position:absolute;right:0;bottom:62px;background:#fff;color:var(--pine);
  border:1px solid var(--beige);padding:12px 16px;border-radius:12px;width:230px;
  font-size:14px;line-height:1.5;box-shadow:0 10px 30px rgba(14,50,50,.18);display:none;
}
.wdw-teaser.on{display:block}
.wdw-teaser .x{
  position:absolute;top:0;right:0;cursor:pointer;color:var(--stone);font-size:15px;
  width:32px;height:32px;display:flex;align-items:center;justify-content:center;
} /* real hit box, not just the bare glyph -- 32px is the practical ceiling in a 230px bubble, clears WCAG 2.2 AA's 24px minimum */
.wdw-win{
  position:absolute;right:0;bottom:0;width:min(360px,calc(100vw - 44px));
  height:520px;max-height:calc(100vh - 120px);
  background:#fff;border:1px solid var(--beige);border-radius:14px;
  box-shadow:0 18px 60px rgba(14,50,50,.30);display:none;flex-direction:column;overflow:hidden;
}
.wdw-win.on{display:flex}
.wdw-head{position:relative;background:var(--pine);color:var(--ivory);padding:14px 16px}
.wdw-head .t{font-family:var(--font-serif);font-size:18px;display:flex;align-items:center;gap:9px}
.wdw-head .t img{width:22px;height:22px}
.wdw-head .x{position:absolute;top:12px;right:14px;cursor:pointer;font-size:20px;color:var(--cream);background:none;border:none}
.wdw-log{flex:1;overflow-y:auto;padding:16px 14px;background:var(--cream);display:flex;flex-direction:column;gap:10px}
.wdw-m{max-width:86%;padding:10px 14px;border-radius:12px;font-size:14.5px;line-height:1.55;white-space:pre-line}
.wdw-m.bot{background:#fff;border:1px solid var(--beige);align-self:flex-start;border-bottom-left-radius:4px;color:var(--pine)}
.wdw-m.me{background:var(--pine);color:var(--ivory);align-self:flex-end;border-bottom-right-radius:4px}
.wdw-chips{display:flex;flex-wrap:wrap;gap:6px;align-self:flex-start}
.wdw-chips button{background:#fff;border:1px solid var(--stone);color:var(--pine);border-radius:16px;padding:6px 12px;font-size:13px;font-family:var(--font-sans);cursor:pointer}
@media(hover:hover){.wdw-chips button:hover{background:var(--pine);color:var(--ivory);border-color:var(--pine)}}
.wdw-in{display:flex;border-top:1px solid var(--beige);background:#fff}
.wdw-in input{flex:1;border:none;padding:14px;font-size:15px;font-family:var(--font-sans);background:transparent;outline:none;color:var(--pine)}
.wdw-in button{border:none;background:var(--pine);color:var(--ivory);padding:0 18px;cursor:pointer;font-size:15px;font-family:var(--font-sans)}
.wdw-in input:disabled,.wdw-in button:disabled{opacity:.5;cursor:default}
.wdw-note{font-size:11px;color:var(--stone);text-align:center;padding:5px 10px 7px;background:#fff}
@media(max-width:760px){.wdw-chat{right:16px;bottom:16px}}
/* Below 640px, the fixed 360x520 floating card read as a desktop panel
   squeezed onto a phone -- barely narrower than the viewport but still
   anchored bottom-right with dead space around it, and tall enough to
   crowd the visible page. Bottom-sheet instead: full width, near-full
   height, flat against all three lower edges, matching the site's own
   .mobile-sheet full-width pattern already used for nav. */
@media(max-width:640px){
  .wdw-win{
    position:fixed;left:0;right:0;bottom:var(--wdw-kb-offset,0px);width:100%;
    height:min(82vh,640px);max-height:88vh;
    border-radius:18px 18px 0 0;border-width:1px 0 0 0;
  }
}

@media(prefers-reduced-motion:reduce){
  .hero-scroll .line{animation:none}
  html{scroll-behavior:auto}
  .btn-pill,.btn-pill-text,.btn-pill-arrow,
  .hero-cta,.hero-cta-text,.hero-cta-circle,.hero-cta-arrow{transition:none}
}
