/* ============================================================
   Lis & Chris — shell styles
   Design tokens pulled from the Figma frames (Home, Cómo Llegar).
   Fonts: Kepler Std → Cormorant Garamond, Objektiv Mk1 → Archivo
   (free stand-ins; swap the two --font-* vars when the licensed
   fonts are available). Chris is fluent in CSS — everything here is
   plain, editable, and token-driven.

   Build & style conventions (heading rules, callouts, colour roles, the
   specificity gotchas below, etc.) live in /CLAUDE.md — keep it in sync.
   ============================================================ */

:root {
  /* palette */
  --cream:        #fbf6f1;  /* page background, light text on dark */
  --cream-hi:     #fffcf8;  /* hero name text */
  --ink:          #1e1e1e;  /* body text */
  --brown:        #8b685d;  /* eyebrows, rules */
  --brown-soft:   #bc9a8f;  /* faint botanical linework */
  --green:        #216d54;  /* links, accents, RSVP */
  --green-btn:    #2c6250;  /* primary button fill */
  /* Not a brand colour on purpose: red appears ONLY for required markers and
     validation errors, so it never competes with anything else on the page. */
  --red:          #a1332b;

  /* type — Adobe Fonts kit fia6jgi (Kepler Std optical sizes + Objektiv Mk1).
     Objektiv Mk1 ships only Light (300) + Medium (500) in this kit; the
     browser resolves 400/700 to the real 500 face (no synthetic bold). */
  --font-display: "kepler-std-display", Georgia, "Times New Roman", serif;          /* hero names */
  --font-heading: "kepler-std-semicondensed-dis", Georgia, "Times New Roman", serif; /* big titles */
  --font-subhead: "kepler-std-subhead", Georgia, "Times New Roman", serif;          /* h3 / accordion */
  --font-serif:   var(--font-heading);  /* generic serif alias */
  --font-sans:    "objektiv-mk1", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Weights — only faces that actually exist in kit fia6jgi are used, so
     nothing relies on the browser's weight-matching fallback. Real faces:
     Objektiv Mk1 = 300 + 500;  Kepler (display/heading/subhead) = 400 + 700. */
  --fw-light:   300;  /* Objektiv Light — paragraph body copy + captions */
  --fw-body:    400;  /* default body / UI weight */
  --fw-strong:  600;  /* Objektiv Medium — sans emphasis, labels, inline links.
                         NOTE: the kit has no Objektiv 700; add Bold to the
                         Adobe project and bump this to 700 for a true sans bold. */
  --fw-heading: 400;  /* Kepler Regular — display + headings (their default) */
  --fw-bold:    700;  /* Kepler Bold — bold serif headings */

  /* Optical-centering fallback shift (in em) for browsers without
     text-box-trim (Firefox today). Tune if text still sits high/low. */
  --optical-fallback-shift: 0.07em;

  /* layout */
  --wrap: 1372px;
  --maxw: 1700px;

  /* Nav metrics. The bar is `padding-y + logo + padding-y`, and the logo is the
     tallest thing in it, so its height is fully derivable — anything needing to
     clear the nav uses --nav-h instead of a measured constant. Change the logo
     size or the padding and every hero follows automatically. */
  --nav-pad-y: 26px;
  --nav-logo:  78px;
  --nav-h: calc(var(--nav-logo) + 2 * var(--nav-pad-y));
  /* Breathing room between the nav and whatever sits under it. */
  --nav-clearance: 20px;

  /* Gap between one form control and the next field's label. Used by the
     stacked fields AND by the card interiors, so a person card and the
     Logistica section space their questions identically. */
  --rsvp-gap: clamp(32px, 3.2vw, 42px);
  /* Space on EITHER side of a section's rule, so the line sits centred
     between the previous group's content and its own heading. */
  --rsvp-section-gap: clamp(38px, 5.5vw, 56px);
  --gutter: clamp(20px, 5vw, 70px);
  /* distance from the viewport edge to the centered container's content edge;
     used to full-bleed left-side images to the viewport (negative margin). */
  --bleed-left: calc((100vw - min(100vw, var(--maxw))) / 2 + var(--gutter));
}

* { box-sizing: border-box; }

/* overflow-x: clip (not hidden) — hidden turns html/body into scroll
   containers, which silently breaks every position:sticky descendant (e.g. the
   Qué Hacer map). clip prevents horizontal overflow without that side effect. */
html, body { margin: 0; overflow-x: clip; }

/* Reserve the scrollbar gutter permanently so locking scroll for the overlay
   doesn't change the layout width. Without this the page (and the fixed nav's
   toggle button) shifts by the scrollbar width when the menu opens, breaking
   the "open and close in the exact same spot" interaction. JS only pads
   manually as a fallback where this property is unsupported. */
html { scrollbar-gutter: stable; }

/* In-page anchor links (e.g. "guayabera" in Para Ellos jumping down to the La
   Guayabera section) — smooth scroll, opting out under reduced-motion since
   this is pure navigation convenience, not a designed effect. scroll-margin-top
   keeps the jumped-to section clear of the fixed/stuck nav bar on landing. */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}
[id] { scroll-margin-top: 100px; }

body {
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-sans);
  font-weight: var(--fw-light);
  /* Body-copy fluid scale: all body text, links, buttons and captions grow
     from a 16px floor to their own max; the 1.5625vw slope (16px / 1024px)
     puts every one of them at its 16px minimum at a 1024px viewport. */
  font-size: clamp(16px, 1.5625vw, 18px);
  line-height: 1.8;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

/* Explicit emphasis weight so <strong>/<b> render a real face instead of the
   UA default of 700, which the sans has no face for. */
strong, b { font-weight: var(--fw-strong); }

/* ---- Language visibility (plan §4): one language at a time ---- */
html[data-lang="es"] [data-lang="en"] { display: none !important; }
html[data-lang="en"] [data-lang="es"] { display: none !important; }

/* ============================================================
   Global nav bar — overlays the hero in white (plan §9)
   ============================================================ */
.nav {
  position: absolute;
  top: 0; left: 0; right: 0;
  /* Same max-width + centering as .page-body / .home-sections so the nav's
     edges line up with the body content below it. */
  margin: 0 auto;
  z-index: 30;
  /* Grid with equal side columns so the centre logo stays perfectly centred no
     matter how wide the language toggle or Menú label gets (flex space-between
     shifted it when the side text lengths changed). */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 20px;
  padding: var(--nav-pad-y) var(--gutter);
  color: #fff;
}

.nav__lang,
.nav__menu {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: none;
  border: 0;
  padding: 6px;
  color: inherit;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: var(--fw-light);
  letter-spacing: 1px;
  text-transform: uppercase;
  cursor: pointer;
}
.nav__lang { justify-self: start; }
.nav__menu { justify-self: end; }
.nav__globe { flex: none; opacity: .9; }

/* Diamond crest logo (Figma nav). Two rotated squares + the L&C monogram:
   - inner diamond (.nav__crest): frosted-glass panel — real backdrop-blur of
     the hero behind it, a faint white radial wash, and a 50%-white edge.
   - outer diamond (.nav__crest::before): a thin low-opacity outline, no blur.
   - monogram (.nav__monogram): the extracted SVG, upright and centered. */
.nav__logo {
  position: relative;
  justify-self: center;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--nav-logo);
  height: var(--nav-logo);
  color: inherit;
  text-decoration: none;
}
.nav__crest {
  position: absolute;
  inset: 13px;
  transform: rotate(45deg);
  border: 1px solid rgba(255, 255, 255, .5);
  background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, .16), rgba(255, 255, 255, .02));
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.nav__crest::before {
  content: "";
  position: absolute;
  inset: -11px;
  border: 1px solid rgba(255, 255, 255, .16);
}
.nav__monogram {
  position: relative;
  z-index: 1;
  width: 44px;
  aspect-ratio: 98.9467 / 92.1228;   /* monogram.svg viewBox ratio */
  background-color: currentColor;     /* letters follow the nav's colour */
  -webkit-mask: url("/assets/img/monogram.svg") center / contain no-repeat;
          mask: url("/assets/img/monogram.svg") center / contain no-repeat;
}

.nav__burger {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 18px;
}
.nav__burger i {
  height: 2px;
  width: 100%;
  background: currentColor;
  display: block;
  transition: transform .25s ease, opacity .2s ease;
}

/* Menú label swap: "Menú/Menu" and "Cerrar/Close" share one grid cell so the
   button width (and therefore the burger's position) never shifts between the
   open and closed states — the click spot stays put. */
/* Right-align the label so the visible text's right edge (nearest the burger)
   is constant — otherwise the reserved max-width of Menú/Cerrar vs Menu/Close
   left a different trailing gap before the icon per language. */
.nav__menu-label { display: inline-grid; justify-items: end; }
.nav__menu-text {
  grid-area: 1 / 1;
  transition: opacity .2s ease;
}
.nav__menu-text--close { opacity: 0; visibility: hidden; }
.nav__menu[aria-expanded="true"] .nav__menu-text--open { opacity: 0; visibility: hidden; }
.nav__menu[aria-expanded="true"] .nav__menu-text--close { opacity: 1; visibility: visible; }

/* Burger morphs into an X while the overlay is open. */
.nav__menu[aria-expanded="true"] .nav__burger i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav__menu[aria-expanded="true"] .nav__burger i:nth-child(2) { opacity: 0; }
.nav__menu[aria-expanded="true"] .nav__burger i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* ---- Sticky / compact nav ----
   At the top of the page the nav overlays the hero in white (the base rules
   above). Once scrolled away from the top, JS adds .is-stuck: the bar becomes
   a shorter, fixed cream strip with ink text that slides down into place. */
.nav {
  transition: padding .25s ease, background-color .25s ease,
              box-shadow .25s ease, color .25s ease;
}
.nav.is-stuck {
  position: fixed;
  padding-top: 12px;
  padding-bottom: 12px;
  background: var(--cream);
  color: var(--green);        /* menu items + monogram read green over the cream bar */
  box-shadow: 0 6px 20px rgba(30, 30, 30, .09);
}

/* Shrink + recolour the crest logo so it reads over the cream bar. */
.nav.is-stuck .nav__logo { width: 52px; height: 52px; }
.nav.is-stuck .nav__crest {
  inset: 9px;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-color: rgba(30, 30, 30, .55);   /* ink outline */
}
.nav.is-stuck .nav__crest::before { inset: -7px; border-color: rgba(30, 30, 30, .22); }
/* Monogram + crest read black in the stuck bar, even though the labels are green. */
.nav.is-stuck .nav__monogram { width: 30px; background-color: var(--ink); }

/* ---- Nav while the overlay is open ----
   The toggle button doubles as the close control, so lift the nav above the
   overlay and colour it as ink over the cream panel. The other nav items are
   hidden so only the toggle (now an X) shows, exactly where "Menú" was. */
.nav.is-menu-open {
  z-index: 110;              /* above .menu (z-index 100) */
  color: var(--ink);
  background: none;
  box-shadow: none;
}
.nav.is-menu-open .nav__lang,
.nav.is-menu-open .nav__logo { visibility: hidden; }

/* Small phones (≤430px): trim the nav text + crest logo so the bar fits at 375px. */
/* Bring the nav closer to the top of the viewport on mobile (also shrinks
   --nav-h, so the hero's top clearance follows automatically). */
@media (max-width: 760px) {
  :root { --nav-pad-y: 16px; }
}
@media (max-width: 430px) {
  .nav__lang,
  .nav__menu { font-size: 13px; letter-spacing: .5px; gap: 7px; }
  /* Drives --nav-h too, so hero clearances follow automatically. */
  :root { --nav-logo: 62px; }
  .nav__crest { inset: 10px; }
  .nav__crest::before { inset: -9px; }
  .nav__monogram { width: 34px; }
}

/* ============================================================
   Page hero + title (both templates)
   ============================================================ */
.page-hero {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  color: var(--cream);
}
/* Hero photo on its own oversized, parallaxed layer (mirrors .home-hero__bg).
   The group's homepage photo is set per page via --hero-photo on this element
   (group.njk); standalone/FAQ heroes have no photo and fall back to the tonal
   gradient. The -14% inset gives the scroll parallax headroom so a translate
   never exposes an edge. Driven by parallax.js via [data-parallax]. */
.page-hero__bg {
  position: absolute;
  inset: -14% 0;
  z-index: 0;
  background-image:
    var(--hero-photo, none),
    linear-gradient(135deg, #2c4a3a 0%, #1e3528 45%, #3a2b26 100%);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  will-change: transform;
}
/* Scrim: a slight flat wash over the whole hero for overall title legibility,
   plus a stronger top-down black→transparent gradient behind the nav and a
   bottom wash for the title/tabs. Sits above the photo, below the content.
   Applied to every non-home hero. */
.page-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(rgba(0,0,0,.18), rgba(0,0,0,.18)),
    linear-gradient(180deg, rgba(0,0,0,.75) 0%, rgba(0,0,0,0) 40%),
    linear-gradient(0deg, rgba(0,0,0,.45) 0%, rgba(0,0,0,0) 42%);
}
.page-hero__bar {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--nav-h) + var(--nav-clearance)) var(--gutter) 0;
  text-align: center;
}
.page-hero__title {
  margin: 0;
  font-family: var(--font-serif);
  font-weight: var(--fw-heading);
  line-height: .9;
  font-size: clamp(3.25rem, 11vw, 8rem);
  color: var(--cream);
}

/* Optional intro line inside the hero, under the title (standalone.njk renders
   it when a page sets `heroLead`). The bar is a centred flex row by default, so
   a second child would land BESIDE the title — switch to a column only when a
   lead is present, leaving title-only heroes untouched. */
.page-hero__bar:has(.page-hero__lead) { flex-direction: column; }
.page-hero__lead {
  margin: clamp(14px, 2vw, 22px) auto 0;
  max-width: 620px;
  font-family: var(--font-sans);
  font-weight: var(--fw-light);
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.6;
  color: var(--cream);
  /* The hero scrim is strongest at the edges; a touch of shadow keeps the
     smaller text legible over the brighter middle of a photo. */
  text-shadow: 0 1px 12px rgba(0, 0, 0, .45);
}
.page-hero__lead strong { font-weight: var(--fw-strong); }

/* standalone: no tabs row below, so the hero is shorter by roughly the tabs'
   own rendered height (measured ~94px) than the group hero, and the title
   sits vertically centered between the nav and the cream body below (instead
   of anchored to the bottom like the group hero, which anchors against the
   tabs). */
.page-hero--standalone {
  min-height: calc(46vh - 94px);
  justify-content: center;
  /* The title sits ~15px higher than dead-centre: the logo diamond makes the nav
     *container* taller than the visual nav-items row, and the eye references that
     row — so a slightly higher title reads as centred. (nav→title = title→body − 15px.) */
  padding-top: calc(var(--nav-h) + var(--gutter) - 15px);
  padding-bottom: var(--gutter);
}
.page-hero--standalone .page-hero__bar { padding: 0 var(--gutter); }

/* group: title sits above the sibling tab row */
.page-hero--group .page-hero__bar { padding-bottom: 56px; }
.page-hero--group { min-height: 44vh; }

/* Phones: both heroes ate too much of the screen before any content showed.
   Measured at 390x844: group hero 371px (44% of the viewport), standalone
   294px (35%).

   Two levers, and which one binds flips as you shrink. At the desktop values
   `min-height` dominates (group content totals ~253px, under the 44vh floor),
   so trimming padding does nothing. Once min-height comes down, the padding
   becomes the binding constraint.

   Nav clearance is NOT set here: the base rules derive it from --nav-h, which
   tracks the logo size across the 430px breakpoint on its own. Hardcoding it
   per breakpoint is what this used to do, and it was wrong between 431px and
   760px — the nav is 130px there, but the constant assumed 114px and left the
   title 2px under it. */
@media (max-width: 760px) {
  .page-hero--group { min-height: 30vh; }
  /* Tabs sit closer to the title now that there's less room above them. */
  .page-hero--group .page-hero__bar { padding-bottom: 36px; }

  .page-hero--standalone {
    min-height: 0;  /* height comes from the derived padding + content below */
    /* Title ~15px higher than centre (see the base rule): 20px above, 35px below. */
    padding-top: calc(var(--nav-h) + 20px);
    padding-bottom: 35px;
  }
}

/* ============================================================
   Group sibling tabs (plan §9)
   Equal-width, active = cream fill bleeding into the page body,
   inactive = translucent border over the hero.
   ============================================================ */
.tabs {
  position: relative;
  z-index: 2;
  display: flex;
  width: min(1700px, 100%);
  margin: 0 auto;
  @media (min-width:1024px){
  padding: 0 var(--gutter);
  }
}
/* Inactive tabs get a tinted, blurred backdrop so the white label stays legible
   over any hero photo (accessibility); the active tab is a solid cream fill. */
.tab {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  text-wrap: balance;
  justify-content: center;
  min-width: 0;
  padding: 28px 20px;
  border: 1px solid rgba(255, 255, 255, .4);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(3px);
  color: var(--cream);
  text-decoration: none;
  text-align: center;
  font-size: 20px;
  font-weight: var(--fw-body);
  transition: background-color .2s ease, color .2s ease;
}
.tab + .tab { border-left: 0; }
.tab:hover { background: rgba(255, 255, 255, 0.064); }
.tab.is-active {
  background: var(--cream);
  color: var(--ink);
  font-weight: var(--fw-strong);
  border-color: var(--cream);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.tab__text { display: inline-block; }

/* Mobile tabs rule (plan §9): equal thirds, text wraps up to TWO lines
   (hard cap), all tabs share a common height, no horizontal scroll,
   never a second row. flex:1 + line-clamp:2 delivers exactly this. */
@media (max-width: 760px) {
  .tab {
    padding: 16px 16px;
    font-size: 15px;
    line-height: 1.4;
  }
  .tab__text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

/* ============================================================
   Page footer (plan §9) — the sibling tabs repeat over a photo band,
   above a closing line pointing to FAQ / contact. Full-bleed.
   ============================================================ */
.site-footer {
  position: relative;
  color: var(--cream);
  /* Same photo as the page's hero (set via --hero-photo in site-footer.njk:
     group pages → that group's homepage photo, home → the hero photo), darkened
     so the tabs + closing line stay legible. Falls back to the tonal gradient on
     pages with no hero photo (e.g. FAQ). */
  background-image:
    linear-gradient(rgba(0, 0, 0, .55), rgba(0, 0, 0, .55)),
    var(--hero-photo, none),
    linear-gradient(135deg, #2c4a3a 0%, #1e3528 45%, #3a2b26 100%);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
/* Tabs sit flush at the top of the band (they carry their own padding). The
   active tab's cream fill reads as a solid block over the photo; inactive tabs
   keep their translucent outline + cream text — same rules as the hero tabs. */
.tabs--footer { position: relative; z-index: 1; }

.site-footer__outro {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: clamp(56px, 8vw, 104px) var(--gutter) clamp(64px, 9vw, 120px);
  text-align: center;
}
.site-footer__outro p {
  margin: 0;
  font-family: var(--font-subhead);
  font-weight: var(--fw-heading);
  font-size: clamp(1.5rem, 3.4vw, 2.5rem);
  line-height: 1.2;
  color: var(--cream);
  text-wrap: balance;
}
/* Links are cream + dotted-underline here (green would sink into the dark
   photo); solid underline on hover. */
.site-footer__outro a {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-style: dotted;
  text-underline-offset: 0.18em;
  transition: text-decoration-color .15s ease;
}
.site-footer__outro a:hover { text-decoration-style: solid; }

/* ============================================================
   Page body — shared content styles
   ============================================================ */
.page-body {
  max-width: 1700px;
  margin: 0 auto;
  padding: clamp(48px, 8vw, 110px) var(--gutter);

  @media (max-width:900px) {
  padding:  var(--gutter);
    
  }
}
.page-body > * + * { margin-top: 1.4em; }

/* Eyebrow + home-card meta share one uppercase-label treatment. */
.eyebrow,
.home-card__meta {
  margin: 0;
  text-wrap: balance;
  color: var(--brown);
  font-size: clamp(14px, 1.3672vw, 20px);
  letter-spacing: 3px;
  text-transform: uppercase;
  font-weight: var(--fw-body);
}
/* Inside .page-body, `.page-body p` (class+element) out-specifies the lone
   `.eyebrow` class and was overriding its size, weight and line-height. Restate
   the three it clobbers at higher specificity so any eyebrow keeps its look. */
.page-body .eyebrow {
  font-size: clamp(14px, 1.3672vw, 20px);
  font-weight: var(--fw-body);
  line-height: 1.4;
}
.page-body h2 {
  margin: 0;
  font-family: var(--font-serif);
  font-weight: var(--fw-heading);
  line-height: .9;
  font-size: clamp(2.5rem, 6vw, 5rem);
  color: var(--ink);
}
/* Non-interactive by default (Bueno Saber, Horario's schedule beats) — black
   like body headings. Clickable h3s (e.g. a future FAQ-style trigger) should
   override back to var(--green) to read as interactive. */
.page-body h3 {
  margin: 0;
  font-family: var(--font-subhead);
  font-weight: var(--fw-heading);
  font-size: clamp(1.6rem, 3vw, 2.5rem);
  line-height: 1.2;
  color: var(--ink);
}
/* h3 hugs the paragraph that explains it — same tight-pairing idea as
   .eyebrow + * below, just a touch looser since body copy is denser. */
.page-body h3 + p,
.content-row > .content-row__body > h3 + p { margin-top: 0.3em; }

/* A body with no figure before it loses the figure's top edge as an alignment
   cue — this restores it. (The hide-mobile variant of this rule lives in the
   ≤900px block, since that's the only width where the figure is hidden.) */
.content-row:not(:has(div.content-figure:first-child)) .content-row__body {
  padding-top: 20px;
}
.page-body p {  font-weight: var(--fw-light); font-size: clamp(16px, 1.5625vw, 20px); line-height: 1.8; }
/* Body bullet lists (inner pages, e.g. El Lugar) — hanging diamond markers
   (diamond.svg, brand mark echoed from the nav crest), same type scale +
   rhythm as body paragraphs. list-style:none + an ::before image is used
   instead of ::marker so the marker's size/position is consistent across
   browsers (::marker image support is inconsistent). */
.page-body ul,
.content-row__body ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
.page-body li,
.content-row__body li {
  position: relative;
  padding-left: 1.4em;
  font-weight: var(--fw-light);
  font-size: clamp(16px, 1.5625vw, 20px);
  line-height: 1.7;
}
.page-body li::before,
.content-row__body li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .6em;
  width: 8px;
  height: 8px;
  background: url("/assets/img/diamond.svg") center / contain no-repeat;
}
.page-body li + li,
.content-row__body li + li { margin-top: .5em; }
.page-body a:not(.btn) {
  color: var(--green);
  font-weight: var(--fw-strong);
  text-decoration: underline;
  text-decoration-line: underline;
text-decoration-style: dotted;
text-decoration-skip-ink: auto;
text-decoration-thickness: 8.5%; /* 1.87px */
text-underline-offset: 18%; /* 3.96px */
}
/* Links over a dark background (hero, dark sections): same dotted-underline
   affordance as body links, but white instead of green for contrast. */
.link-on-dark {
  color: var(--cream);
  text-decoration: underline;
  text-decoration-line: underline;
text-decoration-style: dotted;
text-decoration-skip-ink: auto;
text-decoration-thickness: 8.5%; /* 1.87px */
text-underline-offset: 18%; /* 3.96px */
}
.link-on-dark:hover { color: var(--cream-hi); text-decoration-style: solid; }
/* Guarantee the light styling wins even inside .page-body (which would
   otherwise green these via .page-body a:not(.btn) below — figure captions
   sit on dark photos and need to stay white). */
.page-body a.link-on-dark {
  color: var(--cream);
  text-decoration-line: underline;
  text-decoration-style: dotted;
  text-decoration-skip-ink: auto;
  text-decoration-thickness: 8.5%;
  text-underline-offset: 18%;
}
.page-body a.link-on-dark:hover { color: var(--cream-hi); text-decoration-style: solid; }
/* Hover for underlined links: the dotted underline firms to solid; links on
   light backgrounds darken to ink. (link-on-dark stays light — it sits on dark.) */
.page-body a:hover,
.home-card__links a:hover,
.home-card__link:hover {
  color: var(--ink);
  text-decoration-style: solid;
}
.page-body .lead { margin-top:1em }
.placeholder-note {
  color: var(--brown);
  font-style: italic;
  font-size: 16px;
}

/* ============================================================
   Callout — a boxed aside on FINALIZED content (an address, a tip, a
   warning), as opposed to .placeholder-note which flags content that isn't
   ready yet. Icon is a thin-stroke line icon (matches the site's other line
   icons — accordion chevron, search) tinted via mask so it always matches
   --brown regardless of the source SVG's own colours. Add a modifier for the
   icon: .callout--info / .callout--warning / .callout--pin (add more the same
   way — just define --callout-icon on a new modifier).
   ============================================================ */
.callout {
  display: flex;
  align-items: flex-start;
  gap: clamp(14px, 2vw, 22px);
  padding:clamp(20px, 5vw, 30px);
  background: rgba(188, 154, 143, 0.13)
  ;
}
.callout::before {
  content: "";
  flex: none;
  width: 26px;
  height: 26px;
  margin-top: .15em;
  background-color: var(--brown);
  -webkit-mask: var(--callout-icon) center / contain no-repeat;
          mask: var(--callout-icon) center / contain no-repeat;
}
.callout p {
  margin: 0;
  font-weight: var(--fw-light);
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.7;
  color: var(--ink);
}
.callout p + p { margin-top: .6em; }
/* A callout carrying a button (the Kahal discount) stacks into a single column
   on mobile so the button isn't squeezed beside the text in the flex row. */
@media (max-width: 760px) {
  .callout:has(.btn) { flex-direction: column; }
}

/* Info — helpful tip on confirmed content (e.g. "book early, it's high season"). */
.callout--info {
  --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9.25'/%3E%3Cline x1='12' y1='11' x2='12' y2='16.5'/%3E%3Ccircle cx='12' cy='7.6' r='0.9' fill='%23000' stroke='none'/%3E%3C/svg%3E");
}
/* Warning — a caution the reader should not miss. */
.callout--warning {
  --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5 21.5 20 2.5 20Z'/%3E%3Cline x1='12' y1='9.5' x2='12' y2='14.5'/%3E%3Ccircle cx='12' cy='17.3' r='0.9' fill='%23000' stroke='none'/%3E%3C/svg%3E");
}
/* Pin — a physical address or location. */
.callout--pin {
  --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21S5 13.42 5 8.5a7 7 0 0 1 14 0C19 13.42 12 21 12 21Z'/%3E%3Ccircle cx='12' cy='8.5' r='2.4'/%3E%3C/svg%3E");
}

/* ============================================================
   Schedule time-table (Horario, Figma horario frame) — each row pairs a big
   green time + am/pm on the left with the event name + description on the
   right, divided by thin brown rules. Sits inside .content-row__body, so it
   picks up the normal 1em block spacing above/below.
   ============================================================ */
/* One outer grid: the first (auto) track sizes to the widest time across all
   rows, so every event name lines up. Each row is a subgrid spanning both
   tracks, carrying its own top/bottom rule. */
.schedule {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: clamp(18px, 3vw, 50px);
  border-top: 1px solid rgba(188, 154, 143, .66);
}
.schedule__row {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  padding: clamp(22px, 3vw, 40px) 0;
  border-bottom: 1px solid rgba(188, 154, 143, .66);
}
/* Big display time + a small uppercase am/pm sitting on its baseline. The
   .page-body prefix keeps this (a <p>) ahead of the .page-body p body-size
   rule on specificity. */
.page-body .schedule__time {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--font-heading);
  font-weight: var(--fw-heading);
  line-height: .82;
  font-size: clamp(2.8rem, 7vw, 5.5rem);
  letter-spacing: -0.01em;
  color: var(--green);
  white-space: nowrap;
}
.schedule__ampm {
  font-family: var(--font-sans);
  font-weight: var(--fw-body);
  font-size: clamp(12px, 1.1vw, 18px);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--ink);
}
/* Event name reuses the h3 treatment (ink, subhead); the description hugs it. */
.schedule__event { margin: 0; }
.content-row > .content-row__body .schedule__desc { margin-top: .3em; }
.page-body .schedule__desc {
  font-weight: var(--fw-light);
  font-size: clamp(16px, 1.5625vw, 20px);
  line-height: 1.7;
  color: var(--ink);
  text-wrap: balance;
}
/* A callout attached to a schedule beat (e.g. the no-phones note on the
   ceremony) sits just below its description, inside the event column. */
.schedule__callout { margin-top: clamp(14px, 2vw, 20px); }

@media (max-width: 560px) {
  /* Stack time above the event on very narrow screens so neither column is
     starved — the time stays big and legible. */
  .schedule { grid-template-columns: 1fr; }
  .schedule__row { row-gap: 4px; align-items: start; }
}
.hero-photo-slot {
  display: flex;
  align-items: flex-end;
  min-height: 320px;
  padding: 24px;
  color: var(--cream);
  font-style: italic;
  background: linear-gradient(135deg, #3a5245, #26382e);
}

/* ============================================================
   Content rows (image + text) — inner-page and homepage variants.
   From the Figma content-row components. Image is ALWAYS on the left
   (the <figure> is first in the DOM); stacks image-on-top below 900px.
   ============================================================ */
.content-row {
  display: flex;
  gap: clamp(32px, 5vw, 70px);
  row-gap: 0;
  align-items: stretch;
}
.content-row + .content-row { margin-top: clamp(56px, 9vw, 130px); }

.content-row__body {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 40px 0;
}
.content-row > .content-row__body > * + .callout{
  margin-top: 1em;

  &:not(:last-child){
    margin-bottom: 1em
  }
}
@media (max-width: 760px) {
  .content-row > .content-row__body > * + .callout { margin-top: 1em; }
}
/* When a callout is the last thing in the body, drop the body's bottom padding
   so the boxed callout sits flush with the row's bottom edge (and, on desktop,
   lines up with the figure's bottom) instead of floating above a 40px gap. */
.content-row__body:has(> .callout:last-child) { padding-bottom: 0; }
/* General gap between body blocks (paragraphs, accordion, button). Margins, not
   a flex `gap`, so tight label/title/subtitle clusters can opt out below. The
   `.content-row` prefix lifts specificity above `.page-body p/h2` on inner
   pages (which otherwise zero these margins). */
.content-row__body > * { margin: 0; }
.content-row > .content-row__body > * + * { margin-top: 1em; }
.content-row > .content-row__body > h3 { margin-top: 1em;
  border-top: 1px solid #bc9a8fa8;
  padding-top: 1em;}
/* An eyebrow and its title, or a title and the subtitle beneath it, read as one
   unit — sit them close instead of on the full paragraph gap (Figma ~25px). */
.content-row > .content-row__body > .eyebrow + * {
  margin-top: 0.2em;
}

.content-row > .content-row__body > .home-card__title + .home-card__meta {
  margin-top: 1em;
}

/* Whole-image link (homepage cards): people tend to tap a photo expecting it
   to go somewhere, so the figure links to that card's first page. display:
   contents drops the <a> out of the box model entirely so .content-figure —
   its only child — remains the actual flex item, unaffected by the wrapper. */
.content-figure-link { display: contents; }

/* Figure: photo with a bottom-right caption + legibility gradient. Drop an
   <img> inside; with no image it shows a tonal placeholder. */
.content-figure {
  position: relative;
  flex: none;
  margin: 0;
  /* base width + left bleed: the image touches the viewport's left edge
     while the 1700px container stays centered. */
  width: clamp(40vw, calc(400px + var(--bleed-left)), calc(100% + var(--bleed-left)));
  margin-left: calc(-1 * var(--bleed-left));
  align-self: stretch;
  min-height: 440px;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  padding: 30px;
  
  background: linear-gradient(135deg, #3a5245, #26382e);
  @media (max-width:1000px){
    padding: 20px;
    
  }
}
.content-figure > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Parallax images get vertical headroom (taller than the frame, pulled up so
   they sit centered at rest) so the scroll-driven translateY in parallax.js
   can slide them without ever revealing the frame's top/bottom edge. */
.content-figure > img[data-parallax] {
  height: 148%;
  top: -24%;
  will-change: transform;
}
.content-figure::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(153deg, rgba(0, 0, 0, 0) 68%, rgba(0, 0, 0, .7));
  @media (max-width: 800px){
      background: linear-gradient(153deg, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, .7));

  }
  pointer-events: none;
}
.content-figure__caption {
  position: relative;
  z-index: 1;
  margin: 0;  color: var(--cream);
  font-style: italic;
  font-weight: var(--fw-light);
  font-size: clamp(14px, 1.5625vw, 18px);
  line-height: 1.35;
  text-align: right;
  max-width: 70%;
  text-wrap: balance;
}

/* Map figure (El Lugar + Cómo Llegar): holds our custom Leaflet/MapLibre map,
   which fills the slot via height:100%. On desktop the figure stretches to the
   row so the map matches the body height; a definite MOBILE height is set in the
   ≤900px block below — once the row stacks the figure's height is only
   min-height (indefinite), which would otherwise collapse the map to 0. */
/* All maps in a content-figure (El Lugar venue, Cómo Llegar airport) carry the
   same thin brown rule as the Qué Hacer map, so every map on the site reads as
   framed. The map div inside is position:absolute inset:0, so it fills the
   padding box and the border frames it. */
.content-figure--map { padding: 0; background: #dfd8cc; border: 1px solid rgba(139, 104, 93, .28); }
.content-figure--map::after { display: none; }
.content-map__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Exception to the image-always-left rule (e.g. "Tu Contacto" on Cómo Llegar):
   reverse the row so the figure sits on the right and bleeds to the right edge.
   Desktop only — below 900px the stacked layout (image on top) is unchanged. */
@media (min-width: 901px) {
  .content-row--image-right { flex-direction: row-reverse; }
  .content-row--image-right .content-figure {
    margin-left: 0;
    margin-right: calc(-1 * var(--bleed-left));
  }
}

/* Homepage variant: larger gap, fixed-tall image, body reads as a link card */
.content-row--home { align-items: flex-end; gap: clamp(40px, 5vw, 100px); row-gap: 20px}
.content-row--home .content-figure {
  width: clamp(40vw, calc(400px + var(--bleed-left)), calc(100% + var(--bleed-left)));
  margin-left: calc(-1 * var(--bleed-left));
  height: clamp(400px, 40vw, 600px);
  align-self: auto;
  min-height: 0;
}
.content-row--home .content-row__body {
  max-width: 480px;
  padding: 0 0 20px;
}
/* Home cards run a slightly tighter block gap than the inner pages. */
.content-row--home > .content-row__body > * + * { margin-top: clamp(20px, 2.4vw, 34px); }
.home-card__title {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: var(--fw-heading);
  line-height: .82;
  font-size: clamp(4rem, 8.5vw, 8rem);
  color: var(--ink);
}
/* Title line break: a block box stacks the two words on two lines on desktop
   (e.g. "La" / "Boda"); on mobile it collapses to inline so the enclosed space
   renders and the title reads on one line ("La Boda"). */
.home-card__title-break { display: block; }
@media (max-width: 900px) {
  .home-card__title-break { display: inline; }
}
.home-card__blurb { margin: 0; font-weight: var(--fw-light); font-size: clamp(16px, 1.5625vw, 19px); line-height: 1.8; }
.home-card__links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* Nav-style link list (homepage cards) — no diamond marker, unlike the body
   bullet lists (Bueno Saber, Calzado Cómodo); spacing comes from the flex gap
   above only — cancel the body-list li+li margin so it doesn't stack with the
   gap into an oversized space between items (closer to the original mockup). */
.home-card__links li { padding-left: 0; }
.home-card__links li::before { content: none; }
.home-card__links li + li { margin-top: 0; }
.home-card__links a,
.home-card__link {
  color: var(--green);
  font-weight: var(--fw-strong);
  font-size: clamp(16px, 1.5625vw, 20px);
  text-decoration: underline;
  text-decoration-line: underline;
text-decoration-style: dotted;
text-decoration-skip-ink: auto;
text-decoration-thickness: 8.5%; /* 1.87px */
text-underline-offset: 18%; /* 3.96px */
}
/* Single-link card variant (e.g. Únete Después): a lone link doesn't need the
   list semantics — .home-card__link is a plain <a>, not a <ul><li>. */
.home-card__link { display: inline-block; }

.page-outro {
  margin-top: clamp(48px, 7vw, 90px);
  max-width: 780px;
  font-weight: var(--fw-light);
  font-size: clamp(16px, 1.5625vw, 20px);
}

/* ============================================================
   Reveal-on-scroll — content fades + rises slightly as it enters view
   (reveal.js toggles .is-visible). The hidden start state is gated behind the
   `.js` class (set before paint) so content is ALWAYS visible without JS, and
   is switched off on small screens + reduced motion so a fast mobile scroll can
   never land on a not-yet-revealed (blank) block. reveal--d1/d2 stagger.
   ============================================================ */
.reveal {
  transition: opacity .9s ease, transform .9s cubic-bezier(.2, .7, .2, 1);
  backface-visibility: hidden;
}
/* End on translate3d(0,0,0) rather than `none` so the element keeps its own
   compositing layer after the animation — dropping the layer re-rasterizes
   large text and causes a 1px jitter (most visible on the "Lis & Chris" title). */
.js .reveal { opacity: 0; transform: translate3d(0, 50px, 0); }
.js .reveal.is-visible { opacity: 1; transform: translate3d(0, 0, 0); }
.reveal--d1 { transition-delay: .15s; }
.reveal--d2 { transition-delay: .3s; }

@media (max-width: 768px) {
  .js .reveal { opacity: 1; transform: none; }
  .reveal { transition: none; }
}

@media (max-width: 900px) {
  .content-row,
  .content-row--home { flex-direction: column; align-items: stretch; }
  /* stacked: image on top, full-bleed to both viewport edges */
  .content-figure {
    width: 100%;
    margin-left: 0;
    min-height: 250px;
  }
  .content-row--home .content-figure { height: auto; aspect-ratio: 3 / 2; }
  /* Map figures need a definite height once stacked — min-height alone is
     indefinite and would collapse the height:100% map inside to 0. */
  .content-figure--map { height: clamp(360px, 52vh, 560px); min-height: 0; }
  /* Opt a figure out of the mobile stack entirely (e.g. Horario) so readers
     reach that section's content without scrolling past a tall photo first. */
  .content-figure--hide-mobile { display: none; }
  /* With that figure gone the body has no figure edge to align to, so it takes
     the same top padding as a figure-less row. Previously this selector was
     misspelled `.content-row__b20px` (the declaration's "20px" had been pasted
     into the class name), so it matched nothing and this never applied. Scoped
     to ≤900px on purpose — above that the figure is visible and the body should
     still align to its top edge. */
  .content-row:has(div.content-figure--hide-mobile:first-child) .content-row__body {
    padding-top: 20px;
  }
}

/* ============================================================
   Botanical leaf texture (home) — monstera line-art blended with brown,
   multiplied over the cream so only faint brown linework shows. Purely
   decorative; sits behind content along the right edge.
   ============================================================ */
.home-sections { position: relative; }
.home-sections > .content-row { position: relative; z-index: 1; }
.leaf-texture {
  position: absolute;
  top: 0;
  z-index: 0;
  width: clamp(50px, 15vw, 220px);
  height: 100%;
  pointer-events: none;
  background: url("/assets/img/leaf.png");
  background-size: contain;
  opacity: .3;
  border-left: 1px solid var(--brown);

  @media (max-width:900px) {
      background-size: 200%;

  }
}
.leaf-texture--right { right: calc(-1 * var(--bleed-left)); }
/* Left-edge mirror (home FAQ): flipped so the fronds fan inward and the brown
   rule lands on the inner (right) side. */
.leaf-texture--left { left: calc(-1 * var(--bleed-left)); transform: scaleX(-1); }

/* Gallery countdown backdrop — a large monstera that bleeds off the left edge
   and expands DOWN behind the 202 countdown (Figma home frame). Much bigger and
   taller than the thin edge strips above: no border rule, zoomed-in fronds. */
.leaf-texture--gallery {
 background: url("/assets/img/leaf-large.svg") top left;
    background-size: auto;
  background-size: 100%;
  opacity: .3;
  border-left: none;
  position: static;
  grid-column: 1 / 3;
  grid-row: 2 / 5;
  width: calc(100% + var(--bleed-left));
  margin-left: calc(-1 * var(--bleed-left));
  z-index: -1;
  border: 1px solid var(--brown);
}
@media (max-width: 900px) {
  .leaf-texture--gallery { width: 90%; background-size: 150% auto; opacity: .4; }
}

/* ============================================================
   FAQ section shell (home + the standalone FAQ page share this exact
   markup/width setup — the same left-leaf decoration, the same narrow inner
   column with its big left gutter for the leaf, and the same title treatment).
   The large left padding on .home-faq__inner gives the botanical strip its own
   column so it never overlaps the questions, with just the gutter on the right.
   ============================================================ */
.home-faq {
  position: relative;
  max-width: 1700px;
  margin: 0 auto;
}
.home-faq__inner {
  position: relative;
  z-index: 1;
  max-width: 1300px;
  padding: clamp(64px, 11vw, 150px) var(--gutter) clamp(64px, 11vw, 150px) clamp(60px, 15vw, 250px);
  margin: 0 auto;
}

.page-body .home-faq__inner {
padding-top: clamp(20px, 6vw, 150px);
}
/* Standalone FAQ page: .page-body (standalone.njk) already carries its own
   top padding, so .home-faq__inner's top padding would double up into a bare
   gap of leaf texture with nothing else in it. Only the homepage (where
   .home-faq sits directly in <main>, no .page-body wrapper) keeps the full
   top padding. */
.page-body:has(.home-faq) { padding: 0; }
/* The leaf texture is top:0 relative to .home-faq, which sits below
   .page-body's own top padding — pull it up by that same amount so it runs
   from the hero to the heading with no seam. */
.home-faq__title {
  margin: 10px 0 clamp(28px, 4vw, 56px);
  font-family: var(--font-heading);
  font-weight: var(--fw-heading);
  line-height: .82;
  font-size: clamp(4rem, 8.5vw, 8rem);  /* match the home card titles (El Viaje, La Boda) */
  text-wrap: balance;
  color: var(--ink);
}

/* ============================================================
   Accordion (FAQ + Cómo Llegar stages) — from the Figma component
   ============================================================ */
/* Separator rules are brown at 60% opacity. */
.accordion { border-top: 1px solid rgba(139, 104, 93, .6); }
.accordion__item { border-bottom: 1px solid rgba(139, 104, 93, .6); }
.accordion__trigger {
  display: flex;
  align-items: center;
  gap: 20px;
  width: 100%;
  padding: 40px 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  color: var(--green);
  font-family: var(--font-subhead);
  font-weight: var(--fw-heading);
  font-size: clamp(1.5rem, 3vw, 2.5rem);
  line-height: 1.2;
  transition: color .15s ease, padding-bottom .25s ease;
}
/* Open: tighten the gap between the question and its answer to match the
   Figma component (~22px), animated in step with the panel. */
.accordion__trigger[aria-expanded="true"] { padding-bottom: 0; }
.accordion__trigger > span:first-child { flex: 1; }
.accordion__icon {
  flex: none;
  width: 34px;
  transition: transform .25s ease, stroke .15s ease;
  stroke: var(--green);
}
.accordion__trigger[aria-expanded="true"] .accordion__icon { transform: scaleY(-1); }

/* FAQ only: hovering a question darkens it (and its chevron) to ink. */
.accordion--faq .accordion__trigger:hover { color: var(--ink); }
.accordion--faq .accordion__trigger:hover .accordion__icon { stroke: var(--ink); }

/* Quick open animation via grid-template-rows 0fr -> 1fr, matching the menu
   overlay groups. The inner wrapper clips content while the row collapses. */
.accordion__panel {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows .25s ease, visibility .25s;
  font-weight: var(--fw-light);
  font-size: clamp(16px, 1.5625vw, 20px);
  color: var(--ink);
}
.accordion__trigger[aria-expanded="true"] + .accordion__panel {
  grid-template-rows: 1fr;
  visibility: visible;
}
/* Clipper holds no padding of its own (padding on the clipped box wouldn't
   collapse to 0 when the row closes); the bottom gap is a spacer block that
   sits inside and collapses with the content. */
.accordion__panel-inner { overflow: hidden; }
.accordion__panel-inner > p + p { margin-top: 1em; }
.accordion__panel-inner::after { content: ""; display: block; height: 40px; }

/* Mobile: tighten the space between the clickable question and its rules. */
@media (max-width: 760px) {
  .accordion__trigger { padding: 24px 0; }
  .accordion__panel-inner::after { height: 28px; }
}

/* ---- FAQ live search (home + FAQ page) ---- */
/* Search field on the left, "if you can't find it…" helper on the right
   (Figma); stacks on mobile. */
.faq-search-row {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 48px);
  margin-bottom: clamp(28px, 3.5vw, 48px);
}
.faq-search {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  background: #fff;
  border: 1px solid var(--ink);
  padding: clamp(14px, 1.5vw, 20px) clamp(18px, 2vw, 30px);
}
.faq-help {
 flex: 1 1 auto;
  max-width: 30ch;
  margin: 0;
  font-style: italic;
  font-weight: var(--fw-light);
  font-size: clamp(15px, 1.4vw, 20px);
  line-height: 1.8;
  color: var(--ink);
  text-wrap: balance;

}
.faq-help a {
  color: var(--green);
  font-weight: var(--fw-strong);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 0.18em;
}
.faq-help a:hover { color: var(--ink); text-decoration-style: solid; }
@media (max-width: 760px) {
  .faq-search-row { flex-direction: column; align-items: stretch; gap: 14px; }
  .faq-help { max-width: none; }
}

/* Page references inside answers link out, styled like the site's body links. */
.accordion__panel a {
  color: var(--green);
  font-weight: var(--fw-strong);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 0.18em;
}
.accordion__panel a:hover { color: var(--ink); text-decoration-style: solid; }
.faq-search:focus-within { border-color: var(--green); }
.faq-search__icon {
  flex: none;
  width: clamp(20px, 1.9vw, 27px);
  height: auto;
  color: var(--ink);
}
.faq-search:focus-within .faq-search__icon { color: var(--green); }
.faq-search__input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: 0;
  background: none;
  color: var(--ink);
  font-family: var(--font-sans);
  font-weight: var(--fw-light);
  font-size: clamp(16px, 1.5625vw, 23px);
  line-height: 1.3;
}
.faq-search__input:focus { outline: none; }
.faq-search__input::placeholder { color: var(--ink); opacity: .5; }

/* Matched term highlight — a warm tint that reads over both green questions
   and ink answers. */
.faq-mark {
  background: rgba(139, 104, 93, .3);
  color: inherit;
  border-radius: 2px;
  padding: 0 .05em;
}

.faq-empty {
  margin: clamp(28px, 4vw, 48px) 0 0;
  font-weight: var(--fw-light);
  font-size: clamp(16px, 1.5625vw, 20px);
  color: var(--ink);
}
.faq-empty a {
  color: var(--green);
  font-weight: var(--fw-strong);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 0.18em;
}

/* ============================================================
   Buttons
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Natural width — sizes to its label. Without this, a .btn used as a flex
     item (e.g. inside .content-row__body) gets blockified and stretches full-
     width. .btn--rsvp opts back into full width. */
  width: fit-content;
  padding: 18px 30px;
  background: var(--green-btn);
  color: var(--cream);
  font-family: var(--font-sans);
  font-weight: var(--fw-light);
  font-size: clamp(16px, 1.5625vw, 19px);
  text-decoration: none;
  border: 0;
  cursor: pointer;
  transition: filter .2s ease;
}
.btn:hover { filter: brightness(1.08); }
.btn--rsvp { background: var(--green); width: 100%; }

/* Keep a button's label on one line (e.g. the Kahal booking CTA, which wraps
   to three lines in a narrow content-row column). */
.btn--nowrap { white-space: nowrap; }

/* WhatsApp contact button: the glyph sits left of the label. currentColor
   inherits the button's cream text so the icon matches. */
.btn--whatsapp { gap: 12px; }
.btn--whatsapp .btn__icon {
  flex: none;
  width: 20px;
  height: 20px;
}
/* A .btn used as a link inside prose keeps its button look — white text, no
   underline — overriding the green underlined `.page-body a` styling. */
.page-body a.btn,
.page-body a.btn:hover {
  color: var(--cream);
  text-decoration: none;
}

/* ============================================================
   Optical vertical centering (buttons + nav text vs. icons)
   ------------------------------------------------------------
   Flexbox centers the line box, but the font's ascent/descent leading is
   asymmetric around the glyphs, so text lands slightly high. Trimming the
   box to cap-height (top) and the alphabetic baseline (bottom) makes flex
   center the actual glyphs — real centering, not lopsided padding. Same
   trim Figma applies to this design.
   Support: Chromium 133+ / Safari 18.2. Older browsers + Firefox ignore it
   and fall back to today's flex centering — no regression, just less exact.
   Note: padding is now measured from the glyph edges, so buttons sit a touch
   shorter; nudge --btn padding if you want the previous height.
   ============================================================ */
.btn > span,
.nav__lang > span[data-lang],
.nav__menu-text > span[data-lang] {
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}

/* Fallback for browsers without text-box-trim (Firefox): nudge the glyphs
   down by the leading asymmetry so they sit optically centered. translateY
   keeps the box size intact (unlike padding). Auto-disabled the moment the
   browser gains text-box-trim support. */
@supports not (text-box-trim: trim-both) {
  .btn > span,
  .nav__lang > span[data-lang],
  .nav__menu-text > span[data-lang] {
    display: inline-block;
    transform: translateY(var(--optical-fallback-shift));
  }
}

/* ============================================================
   Menu overlay (plan §9) — right-hand drawer with expand/collapse groups.
   Groups toggle open in place (single-open, current group auto-expanded),
   consistent with the accordion elsewhere. Mobile (<800px): full-width.
   Desktop (>=800px): 33vw panel over a dimmed scrim.
   ============================================================ */
.menu {
  position: fixed;
  inset: 0;
  z-index: 100;
  visibility: hidden;
  background: rgba(20, 16, 14, 0);
  transition: background .4s ease, visibility 0s linear .4s;
}
.menu.is-open {
  visibility: visible;
  background: rgba(20, 16, 14, .45);
  transition: background .4s ease;
}
.menu__panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 100%;                 /* mobile: full-width */
  display: flex;
  flex-direction: column;
  padding: clamp(64px, 11vh, 116px) clamp(28px, 6vw, 56px) 44px;
  background: var(--cream);
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform .4s ease;
}
.menu.is-open .menu__panel { transform: none; }

.menu__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.menu__group { display: flex; flex-direction: column; }

.menu__row {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 10px 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  text-decoration: none;
  color: var(--ink);
  font-family: var(--font-serif);
  font-weight: var(--fw-heading);
  line-height: 1.05;
  font-size: clamp(1.9rem, 8vw, 2.6rem);
  transition: color .15s ease;
}
.menu__row:hover { color: var(--green); }
.menu__ext { flex: none; width: 24px; color: var(--brown); }

/* group toggle chevron — points down, flips up when the group is open */
.menu__chev {
  flex: none;
  width: 22px;
  color: var(--brown);
  transition: transform .25s ease;
}
.menu__row--group[aria-expanded="true"] .menu__chev { transform: scaleY(-1); }

/* collapsible children — animated height via grid-template-rows 0fr -> 1fr;
   visibility toggle keeps collapsed links out of the tab order. */
.menu__sub {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows .25s ease, visibility .25s;
}
.menu__row--group[aria-expanded="true"] + .menu__sub {
  grid-template-rows: 1fr;
  visibility: visible;
}
.menu__sub-inner { overflow: hidden; }
.menu__sublist {
  list-style: none;
  margin: 0;
  padding: 4px 0 14px 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.menu__subrow {
  display: block;
  padding: 7px 0;
  text-decoration: none;
  color: var(--ink);
  font-family: var(--font-sans);
  font-weight: var(--fw-body);
  font-size: clamp(1rem, 4.5vw, 1.15rem);
  transition: color .15s ease;
  padding-left: .7em;
}
.menu__subrow:hover { color: var(--green); }
/* Current page = ink (never green). Green is reserved for link affordance,
   not "active" state; a weight bump alone marks the current page here. */
.menu__subrow.is-current { color: var(--ink); font-weight: var(--fw-strong); font-style: italic;
  color: var(--brown);}

.menu__cta { margin-top: auto; padding-top: 30px; }

@media (min-width: 800px) {
  .menu__panel { width: 33vw; }
  .menu__row { font-size: clamp(1.8rem, 2.4vw, 2.3rem); }
  .menu__subrow { font-size: clamp(1rem, 1.3vw, 1.15rem); }
}

/* ============================================================
   RSVP form (Confirmación de Asistencia)
   ------------------------------------------------------------
   Selectors are scoped under `.page-body` on purpose: `.page-body p` and
   friends are class+element selectors that beat a lone component class
   regardless of source order (the specificity trap in CLAUDE.md).
   ============================================================ */
/* Intro, form and the closing callout share one centered column so the page
   reads as a single block rather than a centered form under full-width prose.
   Intro copy is centered; the form's own labels and inputs stay left-aligned,
   which is what actually keeps a form scannable. */
.page-body .rsvp-intro {
  max-width: 640px;
  margin-inline: auto;
  text-align: center;
}
.page-body .rsvp-intro .lead { margin-top: .6em; }

/* This page is a form, so it gets less runway above it than the FAQ / Mesa de
   Regalos pages that share the .home-faq shell. Scoped with :has so those two
   keep their generous opening. */
.page-body:has(.rsvp-form) .home-faq__inner {
  padding-top: clamp(16px, 3vw, 52px);
}

.page-body .rsvp-form {
  margin-top: clamp(20px, 2.6vw, 30px);
  /* Comfortable measure — long inputs are hard to scan on a wide screen. */
  max-width: 640px;
  margin-inline: auto;
}
/* The page sits inside the .home-faq shell, so these blocks are children of
   .home-faq__inner rather than .page-body and never see its `> * + *` rule.
   The gap below the form is therefore explicit. */
.page-body .rsvp-form + .callout {
  max-width: 640px;
  margin-inline: auto;
  margin-top: clamp(32px, 5vw, 48px);
}

.page-body .rsvp-field + .rsvp-field,
.page-body .rsvp-field + .rsvp-conditional,
.page-body .rsvp-conditional + .rsvp-field {
  margin-top: var(--rsvp-gap);
}

/* Fieldsets carry a UA border/padding — strip it so they match a plain field. */
.page-body .rsvp-fieldset {
  border: 0;
  padding: 0;
  margin-inline: 0;
  min-width: 0;
}

/* Sized to match the input text it labels, not smaller: a label set below the
   value the guest types reads as a caption for the field rather than as the
   question. Light weight too — with ~11 of these stacked, medium weight made
   the column look like a list of headings. Emphasis is carried by the red
   required marker and the section rules instead. */
.page-body .rsvp-label {
  display: block;
  font-family: var(--font-sans);
  /* 500 literally: the kit ships Objektiv 300 + 500 only, so this is the real
     Medium face rather than a weight the browser has to resolve. */
  font-weight: 500;
  font-size: clamp(15px, 1.4vw, 17px);
  letter-spacing: .2px;
  color: var(--ink);
  margin-bottom: 5px;
  padding: 0;  /* legend inherits label styles; kill its UA padding */
}

.page-body .rsvp-optional {
  font-weight: var(--fw-light);
  font-style: italic;
  color: var(--brown);
}

.page-body .rsvp-input {
  display: block;
  width: 100%;
  padding: 14px 16px;
  /* Explicit, because `normal` resolves to about 2.1x for Objektiv, which made
     every input ~11px taller than the radio rows beside it. Padding was never
     the problem: with a sane line-height the original 14px lands an input at
     53px, matching a radio row exactly. Textareas override this to 1.6 in the
     autogrow rule, where the mirror has to match. */
  line-height: 1.45;
  font-family: var(--font-sans);
  font-weight: var(--fw-light);
  font-size: clamp(15px, 1.4vw, 17px);
  color: var(--ink);
  background: var(--cream-hi);
  border: 1px solid rgba(139, 104, 93, .45);  /* --brown, softened */
  border-radius: 0;                            /* square, like .btn */
  transition: border-color .2s ease, box-shadow .2s ease;
}
.page-body .rsvp-input:focus {
  outline: none;
  border-color: var(--green);
  box-shadow: 0 0 0 2px rgba(33, 109, 84, .18);
}
/* Auto-growing textarea (CSS-Tricks' "replicated value" trick).

   The wrapper is a single-cell grid. A hidden ::after mirrors the typed text
   via attr(); being in normal flow, IT sets the row height, and the textarea —
   stacked in the same cell — stretches to match. The browser does the sizing,
   so there's no scrollHeight measuring or height math per keystroke.

   The mirror and the textarea MUST share identical box metrics (font, padding,
   border width, line-height) or the two wrap at different points and the box
   ends up short or tall. Change one, change both. */
.page-body .rsvp-grow { display: grid; }

.page-body .rsvp-grow > textarea,
.page-body .rsvp-grow::after {
  grid-area: 1 / 1 / 2 / 2;   /* same cell — stacked, not side by side */
  padding: 14px 16px;         /* keep in sync with .rsvp-input */
  font-family: var(--font-sans);
  font-weight: var(--fw-light);
  font-size: clamp(15px, 1.4vw, 17px);
  line-height: 1.6;
  white-space: pre-wrap;      /* mirror honors newlines like the textarea */
  overflow-wrap: break-word;  /* and breaks long words at the same place */
}

.page-body .rsvp-grow::after {
  content: attr(data-replicated-value) " ";
  /* The trailing space matters: without it a final newline collapses and the
     box doesn't grow for the line the caret just moved to. */
  visibility: hidden;
  /* Transparent, but the 1px still counts toward the border-box height so the
     mirror matches .rsvp-input's real border. */
  border: 1px solid transparent;
}

/* Grid handles the height now — a manual resize handle would fight it, and no
   scrollbar appears because the box is always tall enough.

   Gated behind `.js` deliberately: the wrapper ships in the markup, so without
   this gate a JS-off visitor would get a textarea that neither auto-grows NOR
   resizes by hand — strictly worse than a plain one. No JS, no lockout. */
.js .page-body .rsvp-grow > textarea {
  resize: none;
  overflow: hidden;
}

/* Default (and the JS-off fallback): the normal manual handle. */
.page-body .rsvp-textarea { resize: vertical; }

/* Single-line inputs get an explicit height rather than relying on
   line-height. Browsers apply `line-height: normal !important` from their UA
   sheet to text inputs (but NOT to <select> or <textarea>), so the 1.45 above
   silently does nothing on an <input> in some builds: the select would sit at
   53px while the text field beside it stayed 64px. Height is computed the same
   way the others resolve, 1.45em of line box plus 28px padding and 2px border,
   so it tracks the font size instead of being a magic number. */
.page-body input.rsvp-input { height: calc(1.45em + 30px); }

/* line-height is set explicitly: .rsvp-help is a <p>, and `.page-body p` was
   winning with 1.8, which is body-copy leading on 13px hint text. */
.page-body .rsvp-help {
  margin-top: 8px;
  font-size: clamp(13px, 1.15vw, 14px);
  font-weight: var(--fw-light);
  line-height: 1.4;
  color: var(--brown);
}

/* Radio groups sit in a bordered box matching .rsvp-input, so a set of options
   reads as a field like every other one, and is clearly separated from its
   label above. Rows are divided by the same faint rule. */
.page-body .rsvp-choices {
  border: 1px solid rgba(139, 104, 93, .45);
  background: var(--cream-hi);
}
.page-body .rsvp-choice + .rsvp-choice {
  border-top: 1px solid rgba(139, 104, 93, .28);
}
/* The whole row turns red when its group fails validation, matching the way a
   text input does. */
.page-body .rsvp-choices:has(input[aria-invalid="true"]) {
  border-color: var(--red);
  background: #fdf6f5;
}

/* Radio rows — generous hit area, since most guests answer on a phone. */
.page-body .rsvp-choice {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 16px;   /* matches .rsvp-input's 14px/16px box */
  font-family: var(--font-sans);
  font-weight: var(--fw-light);
  font-size: clamp(15px, 1.4vw, 17px);
  color: var(--ink);
  cursor: pointer;
  transition: background-color .15s ease;
}
.page-body .rsvp-choice:hover { background: rgba(139, 104, 93, .07); }
/* Selected row gets a wash so the choice is legible at a glance, not only from
   the 20px dot. */
.page-body .rsvp-choice:has(input:checked) { background: rgba(33, 109, 84, .07); }

.page-body .rsvp-choice input[type="radio"] {
  width: 20px;
  height: 20px;
  accent-color: var(--green);
  flex: none;
  cursor: pointer;
}

.page-body .rsvp-form .btn--rsvp { margin-top: clamp(30px, 4.5vw, 42px); }

/* The party/logistics block, revealed once the guest says they're attending.
   It ships visible and rsvp.js hides it on load, so a no-JS visitor still sees
   every field. NOTE: the explicit `display` below means the `hidden` attribute
   stops working on its own (low-specificity UA rule) — hence the explicit
   `[hidden] { display: none }`. Same gotcha as the nav count badges. */
.page-body .rsvp-conditional {
  display: block;
  margin-top: var(--rsvp-gap);
}
.page-body .rsvp-conditional[hidden] { display: none; }

/* Honeypot — off-screen rather than `display:none`, which some bots skip. */
.page-body .rsvp-form__hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- RSVP: section titles, paired fields, kid rows ------------------------ */

/* Section titles inside the form. These are NOT direct children of .page-body,
   so `.page-body > * + *` never reaches them — spacing is explicit here. */
/* Group titles use the .page-body h2 treatment (these are <h3> elements, so the
   serif family and line-height are restated here rather than inherited). They
   name the sections of the form the way an h2 names a section of a page, and at
   the smaller size they read as just another label in the stack. Field labels
   and help text deliberately stay at their own smaller scale — raising those
   too made the radio options the smallest text in their own group. */
.page-body .rsvp-section {
  /* Equal above and below the rule: the gap under the previous group and the
     gap down to this heading were 56px and 26px, so the line sat close to its
     own heading and looked like it belonged to the group above. */
  margin-top: var(--rsvp-section-gap);
  padding-top: var(--rsvp-section-gap);
  border-top: 1px solid rgba(139, 104, 93, .3);  /* --brown, faint rule */
  font-family: var(--font-serif);
  font-weight: var(--fw-heading);
  line-height: .9;
  /* Held in a variable so the number circle can size itself off the title
     rather than carrying its own clamp that has to be kept in sync. */
  --rsvp-title-size: clamp(2.5rem, 6vw, 5rem);
  font-size: var(--rsvp-title-size);

  /* Numbered so a guest can find their place again after looking away. */
  counter-increment: rsvp-section;
  display: flex;
  gap: clamp(12px, 1.5vw, 20px);
}

/* A CSS counter, not numbers in the markup: an element with display:none does
   not increment counters, so when "No podré ir" hides the two attending-only
   sections, the remaining ones renumber themselves 1-2-3 with no gaps. Hard
   numbers would leave a guest looking at 1, 2, 5. */
.page-body .rsvp-form { counter-reset: rsvp-section; }

.page-body .rsvp-section::before {
  content: counter(rsvp-section);
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Matches the height of the title beside it. .72 is roughly the cap height
     of the serif at this size, so the circle reads as tall as the letters
     rather than as tall as the line box (which includes leading). Tune this
     one multiplier to make the circle larger or smaller. */
  width: calc(var(--rsvp-title-size) * .72);
  height: calc(var(--rsvp-title-size) * .72);
  border-radius: 50%;
  background: var(--green);
  color: var(--cream);
  font-family: var(--font-sans);
  font-weight: var(--fw-strong);
  font-size: calc(var(--rsvp-title-size) * .3);
  line-height: 1;
  /* Same optical-centering problem as the buttons and nav text: flex centres
     the line BOX, but the font's leading is asymmetric around the glyphs, so
     the digit sits high in the circle. Trim to cap-height / alphabetic
     baseline so flex centres the actual numeral. */
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}
/* Firefox and older browsers ignore text-box-trim; nudge the glyph down by the
   same leading asymmetry the buttons use. */
@supports not (text-box-trim: trim-both) {
  .page-body .rsvp-section::before {
    padding-top: var(--optical-fallback-shift);
  }
}
/* The first section keeps its rule like every other one, so "Tu Asistencia"
   reads as one of the group headings rather than as a page title. Only the
   margin above it is trimmed, since it follows the short required-fields note
   rather than a block of fields. */
.page-body .rsvp-form > .rsvp-section:first-of-type {
  margin-top: clamp(22px, 3vw, 30px);
}
/* First field after a title hugs it, matching the h3 + p pairing elsewhere. */
.page-body .rsvp-section + .rsvp-field,
.page-body .rsvp-section + .rsvp-row { margin-top: clamp(16px, 2vw, 20px); }

/* Two fields on one line (first/last name, song/artist). Stacks below 480px —
   half-width inputs get cramped on a small phone and last names run long. */
/* Axes are set separately on purpose. When this row stacks, the vertical gap
   is the same "control above, label below" relationship as any two stacked
   fields, so it takes --rsvp-gap. The horizontal gap between two side-by-side
   fields is a different relationship, and --rsvp-gap's 32-42px would read as a
   gutter rather than a pair. */
.page-body .rsvp-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 16px;
  row-gap: var(--rsvp-gap);
}

@media (max-width: 480px) {
  .page-body .rsvp-row { grid-template-columns: 1fr; }
}

/* Help text sits between a label and its field, so it belongs to the label:
   pulled up tight against it (the label's own 10px bottom margin is mostly
   cancelled here), with the larger gap kept BELOW so the pair reads as one
   unit and the input is what's separated from it. */
.page-body .rsvp-help--above { margin-top: -7px; margin-bottom: 14px; }
/* That negative pull is calibrated to a label's 10px bottom margin. After a
   section heading there's no such margin to cancel, and the heading's .9
   line-height means the pull drags the text up into the title. */
.page-body .rsvp-section + .rsvp-help--above {
  margin-top: clamp(10px, 1.4vw, 15px);
}

.page-body .rsvp-kid + .rsvp-kid { margin-top: 12px; }
/* Explicit `display: grid` above would defeat the `hidden` attribute (the UA
   `[hidden]` rule is low specificity) — restore it. Same gotcha as
   .rsvp-conditional and the nav count badges. */
.page-body .rsvp-kid[hidden] { display: none; }

/* Secondary action — green because it's interactive, but text-weight so it
   never competes with the primary Enviar button. */
.page-body .rsvp-add {
  display: inline-block;
  margin-top: 14px;
  padding: 8px 0;
  background: none;
  border: 0;
  font-family: var(--font-sans);
  font-weight: var(--fw-strong);
  font-size: clamp(14px, 1.3vw, 15px);
  color: var(--green);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.page-body .rsvp-add:hover { filter: brightness(1.15); }
.page-body .rsvp-add[hidden] { display: none; }

/* --- RSVP: usability pass (touch targets, iOS zoom, validation) ----------- */

/* iOS Safari auto-zooms the page whenever a focused input renders under 16px,
   then leaves the guest zoomed in and scrolled sideways. The clamp floor was
   15px, which meant EVERY field did this at EVERY phone width. 16px minimum
   below the desktop breakpoint. */
@media (max-width: 900px) {
  .page-body .rsvp-input,
  .page-body .rsvp-grow > textarea,
  .page-body .rsvp-grow::after {
    font-size: 16px;
  }
  /* Labels track the inputs up to 16px too. The clamp bottoms out at 15px on a
     phone, which would leave every label a pixel smaller than the value under
     it — the thing that made them read as captions rather than questions. */
  .page-body .rsvp-label { font-size: 16px; }
}

/* Required marker. Decorative: the asterisk is aria-hidden and the control's
   own `required` is what assistive tech announces. Red ties the asterisk to
   the error colour, so the marker and the message a guest gets for skipping
   that field read as the same signal. */
.page-body .rsvp-req {
  color: var(--red);
  margin-left: .35em;
  font-weight: var(--fw-light);
}
.page-body .rsvp-req-note {
  margin-bottom: clamp(20px, 3vw, 28px);
  font-size: clamp(13px, 1.15vw, 14px);
  font-weight: var(--fw-light);
  color: var(--red);
}

/* Validation. Red is used ONLY here — it isn't a brand colour, which is the
   point: an error should not look like anything else on the page. */
.page-body .rsvp-error {
  margin-top: 8px;
  font-family: var(--font-sans);
  font-weight: var(--fw-strong);
  font-size: clamp(13px, 1.2vw, 14.5px);
  line-height: 1.5;
  color: var(--red);
}
.page-body .rsvp-error[hidden] { display: none; }

.page-body .rsvp-input[aria-invalid="true"] {
  border-color: var(--red);
  background: #fdf6f5;
}
.page-body .rsvp-input[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 2px rgba(161, 51, 43, .18);
}

/* "+ Agregar otro niño" measured 35px tall — under the ~44px touch minimum
   (Apple HIG; NN/g's ~1cm). Padding, not height, so the text stays put. */
.page-body .rsvp-add {
  min-height: 44px;
  padding: 12px 0;
}

/* Visually hidden but available to screen readers — used by the status region
   that announces the conditional reveal and the submit state. */
.page-body .rsvp-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* A disabled submit reads as "in flight", not as broken. */
.page-body .btn--rsvp[disabled] {
  opacity: .55;
  cursor: default;
}

/* --- RSVP: one card per invited person (per-family pages) ---------------- */
/* Each guest is a single bordered card: name once as the header, attendance
   and entrée inside it. Before this, a person produced two separate bordered
   groups and their name appeared in both legends, so a family of five read as
   ten headings and ten boxes. The card border is now the only frame, and the
   groups inside are flattened into rows of it. */
.page-body .rsvp-person {
  border: 1px solid rgba(139, 104, 93, .45);
  /* Page-tone card so the inset boxes inside it (which are --cream-hi, like
     every other input) read as sitting ON the card rather than merging with
     it. When the card was also --cream-hi only their borders separated them. */
  background: var(--cream);
}
.page-body .rsvp-person[hidden] { display: none; }
.page-body .rsvp-person + .rsvp-person { margin-top: clamp(14px, 1.8vw, 20px); }
/* First card clears the intro line above the list. */
.page-body .rsvp-person { margin-top: clamp(14px, 1.8vw, 20px); }

.page-body .rsvp-person__name {
  margin: 0;
  padding: 10px 16px 12px;
  /* .page-body h3's treatment: the serif subhead face, so a person's name
     reads as a heading rather than as another form label. Lower floor than
     h3's own 1.6rem, though: these repeat once per person, and on a narrow
     screen a five-person family became a stack of heavy bars. */
  font-family: var(--font-subhead);
  font-weight: var(--fw-heading);
  font-size: clamp(1.3rem, 3vw, 1.5rem);
  line-height: 1.2;
  /* Filled green bar so the person's name reads as the card's header rather
     than as another row of the form. A fill (not green text) also keeps clear
     of the site's rule that green type means a link. */
  background: var(--green);
  color: var(--cream);
}
/* The asterisk is brown-on-cream elsewhere; on the green bar it needs to be
   light or it disappears. */
.page-body .rsvp-person__name .rsvp-req { color: var(--cream); opacity: .85; }

/* Groups inside a card are inset and keep their own boxes, so each question
   reads as its own control sitting on the card. */
.page-body .rsvp-person .rsvp-field {
  margin-top: 0;
  /* Bottom padding included: when a person is marked as not attending the
     entrée block below is hidden, and without it the attendance box would run
     flush into the bottom edge of the card. */
  padding: 14px 16px;
}
.page-body .rsvp-person .rsvp-error { margin: 8px 0 0; }

/* Entrée block sits below the attendance rows, separated by the same rule the
   rows use, with a label matching the attendance one above. */
.page-body .rsvp-person__plate {
  /* Same gap as between any two stacked fields, so a card spaces its two
     questions exactly like Logistica does. The field above already contributes
     its 14px bottom padding, so only the remainder is added here. Against ~5px
     between the label and its own select, proximity still does the grouping. */
  padding: calc(var(--rsvp-gap) - 14px) 16px 16px;
}
.page-body .rsvp-person__plate[hidden] { display: none; }
/* Dish picker. A select rather than a row of radios per person: the question
   repeats for every guest, so the rows were what made a five-person page long.
   appearance:none for a chevron matching the site's thin-stroke icons; the
   arrow is an inline SVG so there's no extra request. */
.page-body .rsvp-select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  padding-right: 42px;
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238b685d' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 18px 18px;
  cursor: pointer;
}
/* Until a dish is picked the placeholder reads as placeholder, not as an
   answer already given. */
.page-body .rsvp-select:has(option[value=""]:checked) { color: var(--brown); }

/* Screen-reader-only legend: the visible card header carries the name, but a
   fieldset still needs an accessible name announced with the group. */
.page-body .rsvp-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Below 480px the RSVP pages drop the botanical strip. The form needs the full
   width more than it needs the decoration: every card, radio row and select
   inside it was being squeezed by the leaf's lane. Scoped to --rsvp so the FAQ,
   Mesa de Regalos and homepage keep theirs, since those are prose and can
   afford the inset. */
@media (max-width: 480px) {
  .home-faq--rsvp .leaf-texture--left { display: none; }
  /* The inner column's big left padding exists solely to give the leaf its own
     lane; with the leaf gone it would just be dead space. */
  .home-faq--rsvp .home-faq__inner { padding-left: var(--gutter); }
}

/* Below 420px the inner pages drop the strip too, for the same reason as the
   RSVP form above: at that width the leaf's lane costs more than it gives.
   `.page-body .home-faq` is what separates them from the homepage, whose
   .home-faq sits directly in <main> with no .page-body wrapper (see the
   padding note by .home-faq__inner). The homepage keeps its leaf at every
   width. */
@media (max-width: 420px) {
  .page-body .home-faq .leaf-texture--left { display: none; }
  .page-body .home-faq .home-faq__inner { padding-left: var(--gutter); }
}

/* The "ya recibimos tu confirmación" banner is the first thing in the form on a
   revisit, so it needs to clear the required-fields note that follows it. */
.page-body .rsvp-already { margin-bottom: var(--rsvp-gap); }
/* It's a .callout, whose `display: flex` overrides the low-specificity
   `[hidden] { display: none }` UA rule — so without this the banner shows on
   every visit (blank date) instead of only after a real submit. Same gotcha as
   the rsvp-conditional / rsvp-person / rsvp-error guards above. */
.page-body .rsvp-already[hidden] { display: none; }

/* ============================================================
   Dress-code block (Qué Usar) — a centred eyebrow + title over a full-width
   banner photo, then three columns of guidance (Atuendo / Calzado / Clima).
   Scoped under .page-body throughout so it clears the .page-body p/h2/h3
   specificity traps rather than relying on source order.
   ============================================================ */
.page-body .dress-block + .dress-block { margin-top: clamp(56px, 9vw, 130px); }
/* The content-row below (La Guayabera) keeps its own top margin; add matching
   space above it after a dress-block. */
.page-body .dress-block + .content-row { margin-top: clamp(56px, 9vw, 130px); }

.page-body .dress-block__head {
  text-align: center;
  margin-bottom: clamp(24px, 3.5vw, 44px);
}
.page-body .dress-block__head h2 { margin: 0.25em 0 0; }

/* Banner photo. These are wide group shots (~2.2:1), so the image keeps its
   natural ratio at every width — cropping to a shorter banner would cut people
   off the ends. No parallax for the same reason: a full-width auto-height image
   can't translate without exposing an edge. */
.page-body .dress-block__figure { margin: 0; overflow: hidden; }
.page-body .dress-block__figure img {
  display: block;
  width: 100%;
  height: auto;
}

.page-body .dress-block__cols {
  margin-top: clamp(28px, 4vw, 52px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: start;
}
.page-body .dress-col > * { margin: 0; }
.page-body .dress-col > * + * { margin-top: 0.4em; }

/* A callout below the columns (the guayabera note on Para Ellos) spans the
   full width beneath them. */
.page-body .dress-block__cols + .callout { margin-top: clamp(28px, 4vw, 52px); }

/* Stack the columns on phones. Two columns first, then one, so a tablet keeps
   the row rather than dropping straight to a tall single column. */
@media (max-width: 860px) {
  .page-body .dress-block__cols { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  .page-body .dress-block__cols {
    grid-template-columns: 1fr;
    gap: clamp(24px, 6vw, 34px);
    /* Tighter than the desktop head→columns gap: the title sits directly above
       its lead paragraph here, so it should read as attached. */
    margin-top: clamp(14px, 2.5vw, 22px);
  }
  /* Atuendo's h3 is hidden on mobile, so its paragraph is the lead — drop the
     0.4em it inherits from following the hidden h3, keeping it under the title. */
  .page-body .dress-col:first-child h3 + p { margin-top: 0; }

  /* Single column: photo leads, then the eyebrow + title, then the columns —
     so the title introduces the guidance right above it rather than sitting
     over the photo. flex + order does the reorder without touching the markup
     (which keeps the desktop order for source/reading). */
  .page-body .dress-block { display: flex; flex-direction: column; }
  .page-body .dress-block__figure { order: -1; }
  /* Left-aligned now that they stack, with a gap above (from the photo) and the
     head→columns gap left to the columns' own margin-top. */
  .page-body .dress-block__head {
    text-align: left;
    margin: clamp(24px, 5vw, 32px) 0 0;
  }

  /* Stacked, the columns read as a list, so each h3 takes the same top rule as
     El Lugar's Bueno Saber, separating Calzado / Clima. */
  .page-body .dress-col h3 {
    border-top: 1px solid #bc9a8fa8;
    padding-top: .5em;
  }
  /* "Atuendo" / "Outfit" is dropped on mobile: its paragraph is the formal-
     casual lead and reads fine straight under the title, without a label. */
  .page-body .dress-col:first-child h3 { display: none; }
}

/* On mobile the dress-block head sits right under the tabs, hugging them more
   than other La Boda pages, whose first content-row body carries top padding
   the dress-block has none of. Give the first block that same gap. */
@media (max-width: 900px) {
  .page-body [data-lang] > .dress-block:first-child { padding-top: 20px; }
}
