:root {
  --cream: #efe6d6; /* dropdown background */
  --ink: #2e2a24; /* warm charcoal, for text sitting on the cream */

  /* Cormorant Garamond carries the display type — a high-contrast old-style
     face that reads as heritage without costume. Jost handles everything
     small: geometric, quiet, and legible at the sizes Cormorant goes weak. */
  --serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --sans: "Jost", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* --- Type scale ---
     One fluid base, and every step is a fixed multiple of it. That is the
     whole point: because the steps are multiples rather than separate
     clamps, the ratio between any two sizes is identical at 380px and at
     1600px. Nine independent clamp()s cannot do that — each grows at its
     own rate, so the relationships drift as the window changes. */
  --text-base: clamp(1rem, 0.93rem + 0.34vw, 1.2rem);

  --text-2xs: calc(var(--text-base) * 0.63);
  --text-xs: calc(var(--text-base) * 0.79);
  --text-sm: calc(var(--text-base) * 0.89);
  --text-md: calc(var(--text-base) * 1.26);
  --text-lg: calc(var(--text-base) * 1.59);
  --text-xl: calc(var(--text-base) * 2);
  --text-2xl: calc(var(--text-base) * 2.52);

  /* Display type is the one justified exception: it has to compress harder
     on a phone than body copy does, so it gets its own root. The hero
     headline and the page titles both derive from this, which keeps them
     related to each other even though they sit on different pages. */
  --display: clamp(2.4rem, 1.15rem + 5.4vw, 5.2rem);
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  background: #000;
  font-family: var(--sans);
}

html {
  scroll-behavior: smooth;
}

/* Anchors the absolutely positioned slides.
   Fixed, not relative: it stays pinned to the viewport while the content
   panel below slides up over it. Being fixed takes it out of the document
   flow, which is why .siteContent needs margin-top: 100vh to clear it. */
.baseContainer {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

/* --- Sub-page hero (a single still, rather than the home page slideshow) --- */
/* Fixed for the same reason as .baseContainer: the article scrolls up over it. */
.pageHero {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

.pageHeroImage {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Darkens the photo so white text stays readable whatever the image
   underneath happens to be. Bottom-weighted so the scroll cue clears too. */
.pageHeroScrim {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0.15) 45%, rgba(0, 0, 0, 0.5));
}

.pageHeroTitle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  padding: 0 24px;

  color: #fff;
  font-family: var(--serif);
  font-size: var(--display);
  font-weight: 300;
  letter-spacing: 0.22em;
  text-indent: 0.22em; /* cancels the trailing letter-space, as elsewhere */
  text-transform: uppercase;
  text-align: center;
  text-shadow: 0 2px 40px rgba(0, 0, 0, 0.5);
}

/* --- Home hero text --- */
/* Bottom-weighted, because the text sits low. The photographs vary a lot in
   brightness and the type has to hold up over all of them. */
.heroScrim {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.72), rgba(0, 0, 0, 0.12) 52%, rgba(0, 0, 0, 0.3));
}

.heroOverlay {
  position: absolute;
  left: clamp(20px, 6vw, 88px);
  bottom: clamp(96px, 17vh, 180px);
  z-index: 5;
  /* Wide enough that the <br> in each headline is the only break that
     happens. Too narrow and the text wraps again on its own, breaking
     phrases at arbitrary points. */
  max-width: min(40em, 86vw);

  color: #fff;
  text-shadow: 0 2px 26px rgba(0, 0, 0, 0.45);
  pointer-events: none; /* never intercepts a click meant for the page */

  /* THE one fluid value in this block. Every size and gap below is expressed
     in em against it, so the whole composition scales as a single unit.
     Sizing each part with its own vw clamp is what broke the proportions
     before: the headline grew far faster than the text under it, so their
     relationship changed at every width instead of holding. */
  font-size: clamp(0.95rem, 0.55rem + 1.15vw, 1.32rem);
}

.heroEyebrow {
  font-size: 0.62em;
  letter-spacing: 0.34em;
  text-indent: 0.34em; /* cancels the trailing letter-space */
  text-transform: uppercase;
  opacity: 0.82;
  margin-bottom: 1.9em;
}

/* Captions are stacked and crossfaded on the same 1000ms as the slides.
   Every caption occupies the SAME grid cell, which stacks them without
   taking them out of flow — so the container measures itself against the
   tallest one automatically. The previous version positioned them
   absolutely against a min-height guess, and any caption that wrapped to an
   extra line overflowed upward into the eyebrow above it. */
.heroCaptions {
  display: grid;
}

.heroCaption {
  grid-area: 1 / 1;
  align-self: end; /* short captions sit against the marks, not float above */
  opacity: 0;
  transition: opacity 1000ms ease-in-out;
}

.heroCaption.isVisible {
  opacity: 1;
}

/* Cormorant at light weight, set large. The restraint is what reads as
   expensive here — a heavier face would shout over the photograph. 3.15em
   against the block's root keeps it in fixed ratio to the line beneath. */
.heroHeadline {
  font-family: var(--serif);
  font-size: 3.15em;
  font-weight: 300;
  line-height: 1.02;
  letter-spacing: 0.005em;
}

.heroLede {
  margin-top: 0.75em;
  max-width: 42ch;
  font-size: 0.95em;
  font-weight: 300;
  line-height: 1.7;
  opacity: 0.84;
}

.heroMarks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7em 1.5em;
  margin-top: 2em;
  list-style: none;
}

.heroMarks li {
  position: relative;
  font-size: 0.6em;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  opacity: 0.9;
}

/* Hairline between the three words. On the ::before of each item after the
   first, so it never dangles after the last one or on a wrapped row. */
.heroMarks li + li::before {
  content: "";
  position: absolute;
  left: -1.25em; /* half the column gap, in this item's own em */
  top: 50%;
  width: 1px;
  height: 11px;
  transform: translateY(-50%);
  background-color: rgba(255, 255, 255, 0.45);
}

/* Rises in once, in reading order, after the first slide has settled */
@keyframes heroRise {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.heroOverlay > * {
  opacity: 0;
  animation: heroRise 1.1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

.heroOverlay > *:nth-child(1) { animation-delay: 0.35s; }
.heroOverlay > *:nth-child(2) { animation-delay: 0.50s; }
.heroOverlay > *:nth-child(3) { animation-delay: 0.65s; }

/* --- Scroll cue at the foot of the hero --- */
.scrollCue {
  position: absolute;
  bottom: 34px;
  left: 50%;
  z-index: 6;

  color: #fff;
  font-size: var(--text-2xs);
  letter-spacing: 0.35em;
  text-indent: 0.35em; /* offsets the trailing letter-space, as on .villaName */
  text-transform: uppercase;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);

  opacity: 0.75;
  pointer-events: none;
  transition: opacity 0.5s ease;

  animation: scrollCueDrift 2.4s ease-in-out infinite;
}

/* Thin tail under the word, fading downwards */
.scrollCue::after {
  content: "";
  display: block;
  width: 1px;
  height: 30px;
  margin: 10px auto 0;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0));
}

/* Its job is done the moment the page moves */
body.hasScrolled .scrollCue {
  opacity: 0;
}

/* The -50% has to be repeated in every frame: the animation replaces the
   base transform outright rather than adding to it, so leaving it out
   would snap the cue back to the left edge as soon as it ran. */
@keyframes scrollCueDrift {
  0%,
  100% {
    transform: translate(-50%, 0);
  }
  50% {
    transform: translate(-50%, 8px);
  }
}

/* --- Content panel that pulls up over the hero --- */
.siteContent {
  position: relative;
  z-index: 5; /* over the hero, under the navbar (30) */
  margin-top: 100vh; /* starts one full screen down, so the hero shows first */

  background-color: var(--cream);
  color: var(--ink);
  border-radius: 32px 32px 0 0; /* only the top corners; the bottom never lifts */

  /* Casts onto the hero so the panel reads as a separate sheet rising
     over it rather than a flat colour change. */
  box-shadow: 0 -24px 60px rgba(0, 0, 0, 0.35);
}

.contentSection {
  /* Clears the fixed navbar when a menu link jumps here, otherwise the
     heading lands underneath it. */
  scroll-margin-top: 90px;
  padding: 90px clamp(24px, 6vw, 80px);
  max-width: 1100px;
  margin: 0 auto;
}

.contentSection h2 {
  font-family: var(--serif);
  font-size: var(--text-2xl);
  font-weight: 300;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 20px;
}

.contentSection p {
  font-size: var(--text-base);
  line-height: 1.7;
  max-width: 60ch;
}

.sectionLink {
  display: inline-block;
  margin-top: 0.4em;
  color: inherit;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  border-bottom: 1px solid rgba(46, 42, 36, 0.45);
  padding-bottom: 4px;
  transition: opacity 0.3s ease;
}

.sectionLink:hover {
  opacity: 0.55;
}

/* --- Guest book --- */
.guestBookIntro,
.guestBookOutro {
  max-width: 720px;
  margin: 0 auto;
  padding: 0 clamp(24px, 6vw, 40px);
  color: var(--ink);
}

.guestBookIntro {
  padding-top: clamp(60px, 9vw, 120px);
}

.guestBookOutro {
  padding-bottom: 120px;
  text-align: center;
}

/* Columns rather than grid: the entries differ a lot in length, and columns
   let each take only the height it needs. A grid row would stretch every
   card to match its tallest neighbour, leaving big gaps under short quotes. */
.guestBook {
  column-count: 3;
  column-gap: clamp(18px, 2.4vw, 30px);

  max-width: 1240px;
  margin: 0 auto;
  padding: clamp(40px, 5vw, 70px) clamp(20px, 5vw, 60px);
  color: var(--ink);
}

.testimonial {
  /* Without this a quote can be split down the middle and continue at the
     top of the next column, separated from its own attribution. */
  break-inside: avoid;
  -webkit-column-break-inside: avoid;

  margin: 0 0 clamp(18px, 2.4vw, 30px);
  padding: clamp(26px, 3vw, 36px);
  background-color: rgba(46, 42, 36, 0.05);
  border-radius: 18px;
}

.testimonial p {
  font-family: var(--serif);
  font-size: var(--text-md);
  font-style: italic;
  line-height: 1.5;
}

/* Typographic quotes come from CSS so the marks stay out of the text
   itself — the wording then reads exactly as the guest wrote it. */
.testimonial p::before {
  content: "\201C";
}

.testimonial p::after {
  content: "\201D";
}

.testimonial cite {
  display: block;
  margin-top: 1.25em;
  font-style: normal;
}

.testimonialName {
  display: block;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.testimonialFrom {
  display: block;
  margin-top: 0.4em;
  font-size: var(--text-xs);
  opacity: 0.6;
}

@media (max-width: 1024px) {
  .guestBook {
    column-count: 2;
  }
}

@media (max-width: 640px) {
  .guestBook {
    column-count: 1;
  }
}

/* --- Map embed --- */
/* The iframe carries no intrinsic ratio, so the wrapper supplies one and
   the frame fills it — otherwise it collapses to a default 150px tall. */
.mapFrame {
  position: relative;
  aspect-ratio: 4 / 3;
  margin: 2.4em 0 1em;
  border-radius: 18px;
  overflow: hidden; /* clips the map's own square corners */
  background-color: rgba(46, 42, 36, 0.08);
}

.mapFrame iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.article .mapNote {
  font-size: var(--text-xs);
  opacity: 0.62;
  margin-bottom: 2.4em;
}

/* --- Label/value pairs --- */
.infoList {
  margin: 1.8em 0 2.6em;
}

.infoRow {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 20px;
  justify-content: space-between;
  padding: 0.95em 0;
  border-bottom: 1px solid rgba(46, 42, 36, 0.16);
}

.infoRow:first-child {
  border-top: 1px solid rgba(46, 42, 36, 0.16);
}

.infoRow dt {
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.7;
}

.infoRow dd {
  font-size: var(--text-sm);
}

/* --- Primary action --- */
.ctaRow {
  margin: 2.2em 0 0.8em;
}

.ctaButton {
  display: inline-block;
  padding: 1.05em 2.4em;

  background-color: var(--ink);
  color: var(--cream);
  border-radius: 999px;

  font-size: var(--text-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;

  transition: opacity 0.3s ease, transform 0.3s ease;
}

.ctaButton:hover {
  opacity: 0.85;
  transform: translateY(-2px);
}

.ctaButton:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

.article .ctaNote {
  font-size: var(--text-xs);
  opacity: 0.6;
  margin-bottom: 2.6em;
}

/* --- Gallery --- */
.galleryGrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: clamp(18px, 2.5vw, 32px);
  max-width: 1300px;
  margin: 0 auto;
  padding: clamp(60px, 8vw, 110px) clamp(20px, 5vw, 60px) 120px;
}

.galleryTile {
  position: relative;
}

/* One box holding the whole group. Fixed ratio so the stacked photos share
   a footprint and the grid never reflows as they swap. */
.galleryStack {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: 20px;
  overflow: hidden; /* clips every layer to the rounded corners */
  background-color: rgba(46, 42, 36, 0.08); /* shows through before load */
}

.galleryLayer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1000ms ease-in-out;
}

.galleryLayer.isVisible {
  opacity: 1;
}

/* Sits over the foot of the stack, so it needs its own scrim to stay
   readable against whichever photo is showing. */
.galleryTileTitle {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  padding: 42px 22px 20px;

  color: #fff;
  font-size: var(--text-sm);
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.5);

  background: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
  border-radius: 0 0 20px 20px;
  pointer-events: none;
}

/* Transparent hit area over the whole tile. A real <button> rather than a
   click handler on the div, so it is focusable and reachable by keyboard;
   it sits outside the stack because a heading can't live inside a button. */
.galleryTrigger {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: transparent;
  border: none;
  border-radius: 20px;
  cursor: zoom-in;
}

.galleryTrigger:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

/* --- Enlarged viewer --- */
.lightbox {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  z-index: 60; /* above the navbar (30) and its dropdown */

  display: flex;
  align-items: center;
  justify-content: center;

  background-color: rgba(12, 10, 8, 0.94);

  /* Same reason as the nav panel: display can't transition, and visibility
     keeps the buttons out of the tab order while it's shut. */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s ease, visibility 0.35s ease;
}

.lightbox.isOpen {
  opacity: 1;
  visibility: visible;
}

/* The constant frame the eye settles on. Its size is fixed by the viewport,
   never by the photo, so stepping between a wide shot and a tall one leaves
   the frame — and therefore the arrows — exactly where they were. */
.lightboxStage {
  position: relative;
  width: min(88vw, 1100px);
  height: min(76vh, 780px);

  display: flex;
  align-items: center;
  justify-content: center;

  /* A faint plate behind the photo, so an image smaller than the stage
     reads as deliberately placed rather than as a loading gap. */
  background-color: rgba(255, 255, 255, 0.05);
  border-radius: 16px;
  overflow: hidden;
}

.lightboxImage {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain; /* contain, not cover — never crop in the viewer */
  border-radius: 12px;
}

.lightboxNav,
.lightboxClose {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.1);
  border: none;
  border-radius: 50%;
  color: #fff;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

/* Absolute against the viewport rather than laid out beside the image.
   As flex siblings they shifted inward or outward with every photo's width;
   pinned, they stay in one place so they can be found without looking. */
.lightboxNav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;

  width: clamp(42px, 6vw, 56px);
  height: clamp(42px, 6vw, 56px);
  font-size: 2rem;
  line-height: 1;
  padding-bottom: 6px; /* optically centres the chevron glyph */
}

.lightboxPrev {
  left: clamp(10px, 3vw, 44px);
}

.lightboxNext {
  right: clamp(10px, 3vw, 44px);
}

.lightboxClose {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 44px;
  height: 44px;
  font-size: 1.75rem;
  line-height: 1;
  padding-bottom: 4px;
}

.lightboxNav:hover,
.lightboxClose:hover {
  background: rgba(255, 255, 255, 0.24);
}

.lightboxNav:focus-visible,
.lightboxClose:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

.lightboxCaption {
  position: absolute;
  left: 0;
  bottom: 24px;
  width: 100%;
  text-align: center;
  color: rgba(255, 255, 255, 0.75);
  font-size: var(--text-2xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  pointer-events: none;
}

/* Stops the gallery behind the viewer from scrolling */
body.lightboxOpen {
  overflow: hidden;
}

@media (max-width: 768px) {
  /* The pinned arrows overlap the stage at this width, so pull it in to
     keep them clear of the photo. */
  .lightboxStage {
    width: 78vw;
    height: 68vh;
  }
}

/* --- Long-form article --- */
.article {
  max-width: 720px; /* ~65-75 characters a line, which is the comfortable range */
  margin: 0 auto;
  padding: clamp(60px, 9vw, 120px) clamp(24px, 6vw, 40px) 120px;
}

.article p {
  font-size: var(--text-base);
  line-height: 1.85;
  margin-bottom: 1.5em;
}

.article .articleLede {
  font-family: var(--serif);
  font-size: var(--text-lg);
  font-style: italic;
  line-height: 1.55;
  opacity: 0.9;
}

.article .articleMeta {
  font-size: var(--text-xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.6;
  margin-bottom: 3em;
  padding-bottom: 2em;
  border-bottom: 1px solid rgba(46, 42, 36, 0.18);
}

.article h2 {
  font-family: var(--serif);
  font-size: var(--text-xl);
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 2.4em 0 1em;
}

.article blockquote {
  margin: 2.5em 0;
  padding-left: clamp(20px, 4vw, 34px);
  border-left: 2px solid rgba(46, 42, 36, 0.35);

  font-family: var(--serif);
  font-size: var(--text-lg);
  font-style: italic;
  line-height: 1.6;
}

/* The one bolded line in the source, given room to stand on its own */
.article .articleCallout {
  font-weight: 600;
  font-size: var(--text-md);
  line-height: 1.6;
  margin: 2.2em 0;
  padding: 1.4em 1.6em;
  border-radius: 14px;
  background-color: rgba(46, 42, 36, 0.06);
}

.articleFigure {
  margin: 2.6em 0;
}

.articleFigure img {
  display: block;
  width: 100%;
  border-radius: 18px; /* matches the dropdown previews */
}

/* --- Rotating article figures --- */
/* A fixed-ratio box so the two stacked layers share one footprint and the
   article never reflows when a taller or shorter photo swaps in. */
.figureFrame {
  position: relative;
  aspect-ratio: 3 / 2;
  border-radius: 18px;
  overflow: hidden; /* clips both layers to the rounded corners */
}

.figureLayer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 18px;
  opacity: 0;
  transition: opacity 0.9s ease;
}

.figureLayer.isVisible {
  opacity: 1;
}

.articleFigure figcaption {
  margin-top: 0.9em;
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  opacity: 0.62;
}

/* Two side by side, collapsing to stacked on narrow screens */
.articleFigureGrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 18px;
  margin: 2.6em 0;
}

.articleFigureGrid .articleFigure {
  margin: 0;
}

.article .articleMark {
  margin: 2.6em 0 1.6em;
  text-align: center;
  font-size: var(--text-md);
  opacity: 0.55;
}

.article .articleFooterNote {
  font-style: italic;
  font-size: var(--text-sm);
  text-align: center;
  opacity: 0.65;
  padding-top: 2em;
  border-top: 1px solid rgba(46, 42, 36, 0.18);
}

/* Every slide is pinned to the same box, so they stack on top of
   each other instead of flowing one after another. */
.slide {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;

  opacity: 0;
  transition: opacity 1000ms ease-in-out;
}

/* The one class JS toggles. Fading opacity 0 <-> 1 gives the
   crossfade: the outgoing slide fades out while the next fades in. */
.slide.isVisible {
  opacity: 1;
}

/* --- Navbar --- */
/* Fixed so it stays put while the overlay is open and floats over the
   hero rather than being pushed below it by the 100vh slideshow. */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 30;

  display: flex;
  align-items: center;
  justify-content: flex-start; /* hamburger pinned to the left corner */

  /* 12px less than it looks: the button's own 12px padding makes up the
     rest, so the icon still lands 24px/40px from the corner. */
  padding: 12px 28px;
}

/* --- Villa name in the navbar --- */
/* Absolutely centred rather than laid out by the flexbox, so it sits in the
   true middle of the bar instead of being pushed off-centre by the
   hamburger's width on the left. */
.villaName {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2; /* stays readable above the overlay */

  color: #fff;
  font-family: var(--serif);
  font-size: var(--text-md);
  font-weight: 300;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  white-space: nowrap;

  /* letter-spacing also adds a gap after the final letter, which drags the
     text visually left of centre. Indenting by the same amount cancels it. */
  text-indent: 0.3em;

  /* Keeps it legible over the brighter photos */
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.6);

  /* The h1 is a wide invisible box across the middle of the bar, so it
     stays click-through; only the anchor inside takes clicks back. */
  pointer-events: none;
  transition: color 0.4s ease;
}

.villaName a {
  pointer-events: auto;
  color: inherit; /* follows the h1, including the flip to ink when open */
  text-decoration: none;
  transition: opacity 0.3s ease;
}

.villaName a:hover {
  opacity: 0.65;
}

/* The name sits above both the overlay and the content panel, so it has to
   darken whenever cream is behind it. The shadow exists to lift white text
   off the photos and only muddies it on cream. */
body.menuOpen .villaName,
body.scrolled .villaName {
  color: var(--ink);
  text-shadow: none;
}

/* --- Hamburger button --- */
/* Always visible now, at every screen width */
.menu-toggle {
  position: relative;
  z-index: 2; /* above the overlay, so it doubles as the close button */
  display: flex;
  flex-direction: column;
  /* centre + gap rather than space-between: the bar stack keeps its 21px
     height inside a 30px box, so the negative space stays even all round
     and the hover highlight comes out square (54x54) instead of oblong. */
  justify-content: center;
  gap: 6px;

  /* content-box so the padding grows the hover target outwards. Under the
     global border-box reset the padding would eat into the box instead,
     shrinking the bar area and throwing off the X alignment below. */
  box-sizing: content-box;
  width: 30px;
  height: 30px;
  padding: 12px;

  background: transparent;
  border: none;
  border-radius: 10px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.menu-toggle:hover {
  background-color: rgba(255, 255, 255, 0.18);
}

/* On cream a white wash is invisible — flip the highlight to a dark tint,
   whether the cream is the open overlay or the scrolled content panel. */
.menu-toggle.active:hover,
body.scrolled .menu-toggle:hover {
  background-color: rgba(46, 42, 36, 0.12);
}

.menu-toggle:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

/* Style the three individual horizontal bars */
.menu-toggle span {
  width: 100%;
  height: 3px;
  background-color: #fff;
  border-radius: 2px;
  transition: transform 0.3s ease, opacity 0.3s ease, background-color 0.3s ease;
}

/* White bars would vanish against cream — both when the overlay opens and
   once the content panel has scrolled up behind the navbar. */
.menu-toggle.active span,
body.scrolled .menu-toggle span {
  background-color: var(--ink);
}

/* Collapse the bars into an X while the menu is open. The 21px stack is
   centred in the 30px box, putting the bar centres 9px apart, so 9px
   still moves the outer two onto the middle one. */
.menu-toggle.active span:nth-child(1) {
  transform: translateY(9px) rotate(45deg);
}

.menu-toggle.active span:nth-child(2) {
  opacity: 0;
}

.menu-toggle.active span:nth-child(3) {
  transform: translateY(-9px) rotate(-45deg);
}

/* --- Full-screen menu overlay --- */
/* Hidden with opacity + visibility rather than display:none, because
   display can't be transitioned — this way it fades in and out.
   visibility (not just opacity) keeps it out of the tab order. */
.nav-panel {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100dvh; /* accounts for mobile browser chrome; ignored if unsupported */
  z-index: 1;

  display: flex;
  /* Reversed rather than reordering the markup: the links stay first in
     the source, so they're still what shows up first if the CSS ever
     fails to load. The images are alt="" decoration either way. */
  flex-direction: row-reverse;
  align-items: center;
  justify-content: center;
  gap: clamp(32px, 6vw, 90px);

  background-color: var(--cream);

  /* Parked one full height above the viewport and slid down into place,
     rather than faded in. transform is compositor-driven, so the slide
     stays smooth without repainting the panel each frame. */
  transform: translateY(-100%);
  visibility: hidden;

  /* This rule governs CLOSING (it applies once .active is removed), so it
     stays brisk — a slow retreat just feels unresponsive.
     visibility is discrete: it flips at the far end of the transition, so
     the panel stays visible for the whole slide and only becomes
     untabbable once it has fully left. */
  transition: transform 0.55s cubic-bezier(0.4, 0, 0.2, 1), visibility 0.55s;
}

/* Opening is deliberately much slower. The transition declared on the
   .active state is the one that runs on the way in, which is what lets the
   two directions have different timings from the same property. */
.nav-panel.active {
  transform: translateY(0);
  visibility: visible;
  transition: transform 1.5s cubic-bezier(0.16, 1, 0.3, 1), visibility 1.5s;
}

.nav-links {
  display: flex;
  flex-direction: column;
  gap: 8px;
  list-style: none;
}

.nav-links a {
  display: block;
  padding: 10px 0;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--text-xl);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: opacity 0.3s ease;
}

.nav-links a:hover {
  opacity: 0.5;
}

/* --- Hover preview --- */
.nav-preview {
  position: relative;
  width: min(38vw, 460px);
  aspect-ratio: 4 / 3;
  border-radius: 18px;
  overflow: hidden; /* clips the stacked images to the rounded corners */
}

/* Same stacking trick as the hero slideshow: all six sit in one box and
   only opacity changes, so swapping is a crossfade with no reflow. */
.previewImage {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 18px;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.previewImage.isVisible {
  opacity: 1;
}

/* Links drift up as the overlay fades in. The stagger runs top to
   bottom so they arrive in reading order. */
.nav-links li {
  transform: translateY(20px);
  opacity: 0;
  transition: transform 0.6s ease, opacity 0.6s ease;
}

/* .active now lives on the panel, since it wraps both the links and
   the preview and is the element that fades in. */
.nav-panel.active li {
  transform: translateY(0);
  opacity: 1;
}

/* Held back so the links settle in behind the sliding panel rather than
   travelling with it — these start around the middle of the 1.5s slide
   and finish just after it lands. */
/* Increment tightened from 0.10s to 0.07s now the menu has eight items —
   at the old spacing the last one would not have started until the panel
   had already finished sliding. Written out to ten so adding another link
   needs no change here; the surplus rules simply match nothing. */
.nav-panel.active li:nth-child(1) { transition-delay: 0.65s; }
.nav-panel.active li:nth-child(2) { transition-delay: 0.72s; }
.nav-panel.active li:nth-child(3) { transition-delay: 0.79s; }
.nav-panel.active li:nth-child(4) { transition-delay: 0.86s; }
.nav-panel.active li:nth-child(5) { transition-delay: 0.93s; }
.nav-panel.active li:nth-child(6) { transition-delay: 1.00s; }
.nav-panel.active li:nth-child(7) { transition-delay: 1.07s; }
.nav-panel.active li:nth-child(8) { transition-delay: 1.14s; }
.nav-panel.active li:nth-child(9) { transition-delay: 1.21s; }
.nav-panel.active li:nth-child(10) { transition-delay: 1.28s; }

/* Stops the page behind the overlay from scrolling while it's open */
body.menuOpen {
  overflow: hidden;
}

/* --- Mobile Responsive Styles (Screens under 768px) --- */
@media (max-width: 768px) {
  .navbar {
    padding: 8px 8px; /* + the button's 12px = 20px from the corner */
  }

  /* Touch screens have no hover, so the preview can't be driven and the
     links need the full width. Drop it and centre them instead. */
  .nav-preview {
    display: none;
  }

  .nav-panel {
    gap: 0;
  }

  .nav-links {
    text-align: center;
  }

  .nav-links a {
    padding: 10px 20px;
  }

  /* Only the anchor moves. The block's internal spacing is all in em, so it
     has already scaled down with the root size — overriding it in px here
     would reintroduce exactly the mismatch this system removes. */
  .heroOverlay {
    bottom: clamp(78px, 13vh, 120px);
  }

  /* The one deliberate ratio change in the sheet. A 3.15x headline is right
     on a wide screen but overpowers a phone — and left alone it would end up
     LARGER than the page titles on the sub-pages, since --display compresses
     harder down here. Pulling it to 2.35x restores that hierarchy. */
  .heroHeadline {
    font-size: 2.35em;
  }
}

@media (prefers-reduced-motion: reduce) {
  .slide {
    transition-duration: 1ms;
  }

  .nav-panel,
  .nav-links li,
  .previewImage,
  .menu-toggle span {
    transition-duration: 1ms;
    transition-delay: 0ms;
  }

  .nav-links li {
    transform: none;
  }

  .scrollCue {
    animation: none;
    transform: translateX(-50%); /* restores the centring the keyframes carried */
  }

  /* Show the hero text outright rather than rising it in */
  .heroOverlay > * {
    animation: none;
    opacity: 1;
  }
}

/* ============================================================
   SITE DESIGN LAYER
   Folded in from the former trial.css. It loads after the base rules
   above and deliberately overrides a few of them — the page background,
   the navbar's justification — so those live here rather than being
   edited in place, and the override is visible rather than hidden.
   ============================================================ */

:root {
  /* The one token the skeleton adds. Every full-bleed band uses it, so
     they all share a margin with the page edge. */
  --gutter: clamp(24px, 6vw, 80px);

  /* The colour that wipes across a button on hover. A warm clay, drawn
     from the same family as the cream and the ink rather than introduced
     as a new hue. */
  --accent: #8c6a45;
}

/* Page goes white; the cream is kept for the dropdown panel only, where
   it still does its job as a contrast surface. */
body {
  background: #fff;
}

.siteContent {
  background-color: #fff;
}

/* The other deliberate override of style.css on this page: the navbar
   was flex-start for a lone hamburger, and now has to hold a booking
   action at the far end too. The villa name is absolutely centred, so it
   is unaffected either way. */
.navbar {
  justify-content: space-between;
  gap: 12px;
}

/* ============================================================
   FROM CASTELFALFI
   Four patterns, adapted: a persistent booking action in the header,
   alternating split sections (their rhythm relies on text BESIDE imagery,
   not only over it), experience cards carrying a tag and a description,
   and a footer grouped Stay / Discover / Connect.
   ============================================================ */

/* --- Persistent header booking action ---
   Their "Book Now" never leaves the header. Ours sits on the right, so
   the bar reads hamburger / name / book. */
.navBook {
  position: relative;
  /* 3, not 2: the villa name, the hamburger and the dropdown panel all sit
     at 2 or below inside this bar. At an equal z-index the winner is
     whichever comes last in the markup, which is fragile. 3 settles it. */
  z-index: 3;

  padding: 0.9em 1.9em;
  border: none;
  border-radius: 6px;

  /* 0.70 is not a taste value, it is the floor. White text over this grey
     scrim clears 4.5:1 against a pure-white backdrop at 0.70 and fails
     below it — so at this alpha the label stays readable over ANY of the
     thirteen slides, including the blown-out sand and sunset frames.
     Anything lighter looked cleaner and went unreadable on two of them. */
  background-color: rgba(58, 54, 49, 0.7);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
  backdrop-filter: blur(12px) saturate(150%);
  color: #fff;

  font-size: var(--text-xs);
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;

  /* Lighter than when the button was bare: the box now does most of the
     separating, and this only sharpens the letters on top of it. */
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.45);

  transition: background-color 0.3s ease, color 0.3s ease;
}

.navBook:hover {
  background-color: rgba(58, 54, 49, 0.86);
}

/* Without backdrop-filter the scrim is working alone, so it has to be
   heavier to hold white text over a bright photograph. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .navBook {
    background-color: rgba(58, 54, 49, 0.78);
  }
}

/* ---- Navbar tone ----
   .isOnDark is set per control by trial.js from what is directly beneath
   that control, so the hamburger, the villa name and the book button can
   each be in a different state at the same moment.

   The leading `body` is load-bearing, not decoration: style.css writes
   these as `body.scrolled .villaName`, which is (0,2,1). A bare
   `.villaName.isOnDark` is (0,2,0) and would LOSE. Prefixing matches the
   specificity, and this sheet loading second breaks the tie our way. */

/* Over a photograph */
body .navBook.isOnDark {
  background-color: rgba(58, 54, 49, 0.7);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
  backdrop-filter: blur(12px) saturate(150%);
  color: #fff;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.45);
}

body .navBook.isOnDark:hover {
  background-color: rgba(58, 54, 49, 0.86);
}

body .villaName.isOnDark {
  color: #fff;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.6);
}

body .menu-toggle.isOnDark span {
  background-color: #fff;
}

body .menu-toggle.isOnDark:hover {
  background-color: rgba(255, 255, 255, 0.18);
}

/* Over the white page. The blur is switched off here: there is nothing
   behind to sample, and it only washes the tint out. */
body .navBook:not(.isOnDark) {
  background-color: rgba(46, 42, 36, 0.09);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  color: var(--ink);
  text-shadow: none;
}

body .navBook:not(.isOnDark):hover {
  background-color: rgba(46, 42, 36, 0.2);
}

body .villaName:not(.isOnDark) {
  color: var(--ink);
  text-shadow: none;
}

body .menu-toggle:not(.isOnDark) span {
  background-color: var(--ink);
}

/* Menu open beats the probe: the cream panel is what is behind the bar
   now, whatever the page underneath it happens to be. */
body.menuOpen .navBook {
  background-color: rgba(46, 42, 36, 0.09);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  color: var(--ink);
  text-shadow: none;
}

body.menuOpen .villaName {
  color: var(--ink);
  text-shadow: none;
}

body.menuOpen .menu-toggle span {
  background-color: var(--ink);
}

/* The trial nav runs to eleven items; style.css only writes delays to ten,
   so an eleventh link would arrive with no delay while the rest staggered. */
.nav-panel.active li:nth-child(11) { transition-delay: 1.35s; }
.nav-panel.active li:nth-child(12) { transition-delay: 1.42s; }

/* --- Alternating split sections ---
   Castelfalfi alternates image-beside-text rather than always stacking
   text over a photograph. Every band on this page was an overlay, which
   flattens the rhythm; this gives the page a second beat. */
.splitSection {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(24px, 4vw, 56px);
  align-items: center;
  max-width: 1200px;
  margin: 0 auto 90px;
  padding: 0 var(--gutter);
}

.splitMedia img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  border-radius: 20px;
}

.splitBody .bodyText {
  margin-bottom: 1.8em;
}

@media (min-width: 900px) {
  .splitSection {
    grid-template-columns: 1fr 1fr;
  }

  /* order flips the picture to the right on alternate bands, without the
     markup having to change */
  .splitSection--reverse .splitMedia {
    order: 2;
  }
}

/* --- Experience cards ---
   Their grid gives each card a category tag, a title, a description and
   a link. Ours previously had a caption and nothing else. */
.experienceGrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
  gap: clamp(18px, 2.4vw, 30px);
  margin: clamp(28px, 4vw, 48px) 0 0;
}

.experienceCard {
  display: flex;
  flex-direction: column;
}

.experienceCard img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  border-radius: 20px;
  margin-bottom: 1.1em;
}

.experienceTag {
  font-size: var(--text-2xs);
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  opacity: 0.55;
  margin-bottom: 0.7em;
}

.experienceCard h3 {
  font-family: var(--serif);
  font-size: var(--text-lg);
  font-weight: 300;
  letter-spacing: 0.06em;
  margin-bottom: 0.45em;
}

.experienceCard p {
  font-size: var(--text-sm);
  line-height: 1.7;
  opacity: 0.78;
  margin-bottom: 1.1em;
}

/* pushes the link to the card's foot so every link in a row lines up,
   however uneven the descriptions above them are */
.experienceCard .sectionLink {
  margin-top: auto;
  align-self: flex-start;
}

/* --- Footer social + legal --- */
.footerSocial {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 0.6em;
  list-style: none;
}

.footerLegal {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  align-items: center;
  justify-content: space-between;
  margin-top: clamp(26px, 3vw, 36px);
  padding-top: clamp(18px, 2.4vw, 26px);
  border-top: 1px solid rgba(46, 42, 36, 0.18);
}

.footerLegal ul {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  list-style: none;
}

.footerLegal,
.footerLegal a {
  font-size: var(--text-2xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.55;
}

/* Hidden on screen, still read aloud. display:none would strip it from
   the accessibility tree and leave the input unnamed. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Shared type roles --- */
.sectionTitle {
  font-family: var(--serif);
  font-size: var(--text-2xl);
  font-weight: 300;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 20px;
}

.eyebrow {
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-indent: 0.16em; /* cancels the trailing letter-space */
  text-transform: uppercase;
  opacity: 0.7;
  margin-bottom: 1.2em;
}

.eyebrow--light {
  opacity: 0.82;
}

.bodyText {
  font-size: var(--text-base);
  line-height: 1.7;
  max-width: 60ch;
}

/* --- Buttons: frosted at rest, colour card wipes in on hover ---
   Two layers doing different jobs. The button itself is translucent and
   blurred — that is the resting "liquid" state. The ::after is an opaque
   colour panel parked off to the right that slides across on hover, so
   the blur is what you see BEFORE the animation and the solid colour is
   what you see after.

   isolation: isolate is what makes this work without extra markup — it
   gives the button its own stacking context, so the z-index:-1 panel
   settles above the button's background but still behind its text. With
   no isolation the panel would drop behind the button entirely and never
   be seen. */
.ctaButton {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-radius: 6px;
  padding: 1.05em 2.2em;

  /* the liquid state */
  background-color: rgba(46, 42, 36, 0.55);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  border: none;
  color: #fff;

  transition: color 0.45s ease;
}

.ctaButton::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-color: var(--accent);
  /* 101%, not 100%: at fractional widths a 100% offset can leave a
     hairline of colour showing along the right edge at rest. */
  transform: translateX(101%);
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

.ctaButton:hover::after {
  transform: translateX(0);
}

/* style.css lifts and fades .ctaButton on hover; the wipe should be the
   only thing that happens, so both are cancelled here. */
.ctaButton:hover {
  opacity: 1;
  transform: none;
}

/* Over a photograph the blur has real texture to work with, so it is
   lighter and the border brighter. The label flips to ink as the cream
   panel arrives under it. */
.ctaButton--onImage {
  background-color: rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  backdrop-filter: blur(18px) saturate(180%);
  border: none;
  color: #fff;
}

.ctaButton--onImage::after {
  background-color: var(--cream);
}

.ctaButton--onImage:hover {
  color: var(--ink);
}

/* --- Welcome intro ---
   Centred type on bare cream, above the card. The reference opens this
   way: a quiet statement with air around it, so the photograph beneath
   arrives as a change of register rather than more of the same. */
.welcomeIntro {
  max-width: 640px;
  margin: 0 auto;
  padding: clamp(70px, 10vw, 130px) var(--gutter) clamp(34px, 5vw, 60px);
  text-align: center;
  color: var(--ink);
}

.welcomeIntroTitle {
  font-family: var(--serif);
  font-size: var(--text-2xl);
  font-weight: 300;
  line-height: 1.15;
  margin-bottom: 0.7em;
}

.welcomeIntroLede {
  font-size: var(--text-sm);
  line-height: 1.8;
  opacity: 0.75;
}

/* --- Welcome card ---
   The content sits IN FLOW at the bottom of a flex box rather than being
   absolutely positioned. That is what stops a long heading overflowing:
   an absolute block can't push the card taller, so it would spill. */
.squareRoundCard {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: clamp(400px, 68vh, 660px);
  margin: 0 var(--gutter) 90px;
  border-radius: 20px;
  overflow: hidden;
  background-color: var(--ink);
}

.squareRoundCard .image-container {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.squareRoundCard .image-container img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Weighted to the lower-left where the text sits, rather than veiling
   the whole photograph. */
.squareRoundCard .image-container::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.28) 46%, rgba(0, 0, 0, 0) 76%),
    linear-gradient(to right, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0) 58%);
}

/* --- Video in the welcome card ---
   The source is soft and noisy, so rather than fight that it is pushed
   toward something deliberately filmic: a little desaturation, a warm
   cast, lifted blacks, and grain over the top. A low-quality video that
   looks like film reads as a choice; one left raw reads as a mistake. */
.cardVideo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: saturate(0.86) sepia(0.14) contrast(1.06) brightness(0.97);
}

/* Grain. Same inline-SVG turbulence as the villa tour — no request, and
   it is what actually hides softness: fine noise gives the eye
   high-frequency detail where the footage has none. */
.image-container--film::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.19;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.6'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

/* The existing ::after carries the text gradient; this adds the corner
   fall-off a lens gives, which also draws the eye off the soft edges. */
.image-container--film {
  background: var(--ink);
}

.image-container--film::after {
  background:
    radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 45%, rgba(0, 0, 0, 0.45) 100%),
    linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.28) 46%, rgba(0, 0, 0, 0) 76%),
    linear-gradient(to right, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0) 58%);
  z-index: 1;
}

@media (prefers-reduced-motion: reduce) {
  /* trial.js also declines to start it; this covers the case where it is
     already running when the preference changes. */
  .cardVideo {
    animation-play-state: paused;
  }
}

/* --- Video controls ---
   Same transparent-blur treatment as the header book button and the tour's
   icon buttons, so the page has one button language rather than three.
   Above the ::before grain and ::after gradient, which both sit at z 1. */
.videoControls {
  position: absolute;
  top: clamp(16px, 2.4vw, 26px);
  right: clamp(16px, 2.4vw, 26px);
  z-index: 2;
  display: flex;
  gap: 10px;
}

/* One appearance, in every state. The buttons previously changed surface
   depending on play and mute — which read well but meant four visual
   states across two controls, and any new state would have had to pick a
   side. Constant white; only the glyph inside ever changes. */
.mediaBtn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: none;
  border-radius: 999px;

  background-color: rgba(255, 255, 255, 0.94);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  backdrop-filter: blur(14px) saturate(150%);
  color: var(--ink);
  cursor: pointer;
  /* White on a pale frame needs an edge of its own */
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.28);
  transition: background-color 0.3s ease, transform 0.3s ease;
}

.mediaBtn:hover {
  background-color: #fff;
  transform: translateY(-1px);
}

.mediaBtn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

.mediaBtn .ico {
  width: 19px;
  height: 19px;
  /* No drop-shadow: the glyph is dark on white, and a dark shadow under a
     dark glyph only muddies it. The button's own shadow does the lifting. */
}

/* One glyph at a time, from the same attribute the label uses */
#welcomePlay[aria-pressed="false"] .ico--pause,
#welcomePlay[aria-pressed="true"] .ico--play,
#welcomeMute[aria-pressed="true"] .ico--heard,
#welcomeMute[aria-pressed="false"] .ico--muted {
  display: none;
}

.card-content {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 46em; /* holds the copy to the left, as in the reference */
  padding: clamp(30px, 5vw, 68px);
  color: #fff;
}

/* Larger and set in the serif: on the card this is the page's second
   voice, not a label. */
.card-content .sectionTitle {
  font-size: calc(var(--text-2xl) * 1.12);
  line-height: 1.08;
  letter-spacing: 0.01em;
  text-transform: none; /* sentence case, like the reference */
}

.card-content .bodyText {
  max-width: 52ch;
  font-size: var(--text-sm);
  font-weight: 300;
  opacity: 0.88;
  margin-bottom: 1.8em;
}

/* --- Tile rule ---
   One motif repeated as a hairline band, marking the seam between
   sections the way a tile course marks a wall. */
.tileRule {
  height: 34px;
  margin: 0 var(--gutter);
  background-repeat: repeat-x;
  background-position: center;
  background-size: auto 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 34 34'%3E%3Cg fill='none' stroke='%232e2a24' stroke-width='1.1'%3E%3Cpath d='M17 3.5c4 5.6 7.9 9.5 13.5 13.5C24.9 21 21 24.9 17 30.5 13 24.9 9.1 21 3.5 17 9.1 13 13 9.1 17 3.5Z'/%3E%3Ccircle cx='17' cy='17' r='2.6'/%3E%3C/g%3E%3C/svg%3E");
  opacity: 0.3;
}

/* --- Paired rooms, with the pictures moving inside fixed frames --- */
.twoImagesContainer {
  /* the one dial: how far each picture travels inside its frame */
  --parallax-shift: clamp(30px, 5vw, 70px);
  display: flex;
  gap: 0;
  width: 100%;
  padding: 0 var(--gutter) 90px;
}

.imageCard {
  position: relative;
  flex: 1;
  min-width: 0;
  aspect-ratio: 4 / 5; /* the frame: fixed proportions, never moves */
  overflow: hidden;
}

.imageCard:first-child {
  border-radius: 20px 0 0 20px;
}

.imageCard:last-child {
  border-radius: 0 20px 20px 0;
}

/* Taller than the frame by twice the travel and pulled up by one, so it
   sits centred at rest. That surplus is what slides into view — without
   it the edges would expose gaps at the extremes. */
.twoImages {
  position: absolute;
  left: 0;
  width: 100%;
  top: calc(-1 * var(--parallax-shift));
  height: calc(100% + (2 * var(--parallax-shift)));
  object-fit: cover;
  display: block;
  will-change: transform;
}

/* Sibling of the image, not of the moving layer, so the text is
   completely unaffected by the animation. */
.pair-overlay {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  padding: clamp(24px, 3.4vw, 44px);
  color: #fff;
  background: linear-gradient(to top,
      rgba(0, 0, 0, 0.82) 0%,
      rgba(0, 0, 0, 0.3) 48%,
      rgba(0, 0, 0, 0) 100%);
}

.pair-overlay h3 {
  font-family: var(--serif);
  font-size: var(--text-xl);
  font-weight: 300;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 0.5em;
}

.pair-overlay p {
  max-width: 34ch;
  margin-bottom: 1.4em;
  font-size: var(--text-sm);
  font-weight: 300;
  line-height: 1.7;
  opacity: 0.88;
}

/* --- Cuisine ---
   Rebuilt to the reference: a white card holding a serif headline, a
   carousel on the left and a short column of copy on the right. */
.cuisinePanel {
  max-width: 1240px;
  margin: 0 auto 90px;
  padding: clamp(34px, 5vw, 70px) clamp(24px, 4vw, 60px);
  border-radius: 24px;
  background-color: #fff;
  /* Two layers: a tight one to seat the card's edge, and a very wide soft
     one for the ambient spread. A single large blur alone reads as haze
     rather than as a card lifted off the page. */
  box-shadow:
    0 4px 14px rgba(46, 42, 36, 0.05),
    0 40px 110px rgba(46, 42, 36, 0.16);
  color: var(--ink);
}

/* Sentence case and large, as in the reference — an uppercase tracked
   label would read as a caption at this size, not a statement. */
.cuisineTitle {
  font-family: var(--serif);
  font-size: calc(var(--text-2xl) * 1.12);
  font-weight: 300;
  line-height: 1.1;
  margin-bottom: clamp(26px, 3.4vw, 44px);
}

.cuisineLayout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(26px, 3.4vw, 52px);
  align-items: start;
}

@media (min-width: 900px) {
  .cuisineLayout {
    grid-template-columns: 1.15fr 1fr;
  }
}

.cuisineCarousel {
  position: relative;
  border-radius: 18px;
  overflow: hidden; /* clips the sliding track to the rounded frame */
}

/* One row of full-width slides; JS shifts it by whole multiples of 100%. */
.cuisineTrack {
  display: flex;
  transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.cuisineSlide {
  flex: 0 0 100%;
  min-width: 0; /* without this the flex items refuse to shrink to 100% */
}

/* 13:10 — the wider side is 30% longer than the shorter one, giving a
   landscape rectangle like the reference rather than the portrait 4:5
   this started as. */
.cuisineSlide img {
  width: 100%;
  aspect-ratio: 13 / 10;
  object-fit: cover;
  display: block;
}

.cuisineArrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;

  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding-bottom: 2px; /* optically centres the chevron glyph */

  border: none;
  border-radius: 999px;
  background-color: rgba(20, 17, 14, 0.5);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: #fff;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.cuisineArrow:hover {
  background-color: rgba(20, 17, 14, 0.78);
}

.cuisineArrow--prev {
  left: 12px;
}

.cuisineArrow--next {
  right: 12px;
}

.cuisineDots {
  position: absolute;
  left: 0;
  bottom: 14px;
  z-index: 2;
  display: flex;
  justify-content: center;
  gap: 7px;
  width: 100%;
}

.cuisineDots button {
  width: 7px;
  height: 7px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background-color: rgba(255, 255, 255, 0.45);
  cursor: pointer;
  transition: background-color 0.3s ease, transform 0.3s ease;
}

.cuisineDots button[aria-current="true"] {
  background-color: #fff;
  transform: scale(1.25);
}

.cuisineAside .bodyText {
  margin-bottom: 1.9em;
}

/* --- Full-bleed band --- */
.findPeaceGoaSection {
  position: relative;
  display: flex;
  align-items: center;
  /* +30% on all three terms, so the band grows by the same proportion at
     every width rather than only in the middle of its range. */
  min-height: clamp(468px, 78vh, 754px);
  margin: 0 var(--gutter) 90px;
  border-radius: 20px;
  overflow: hidden;
  background-color: var(--ink);
}

/* The wrapper breaks this child selector even with display:contents —
   matching is structural, and display does not change the tree. */
.findPeaceGoaSection > img,
.findPeaceGoaSection > picture > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.findPeaceGoaSection::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to right,
      rgba(0, 0, 0, 0.7) 0%,
      rgba(0, 0, 0, 0.22) 60%,
      rgba(0, 0, 0, 0) 100%);
}

.findPeaceContent {
  position: relative;
  z-index: 1;
  padding: clamp(30px, 5vw, 68px);
  max-width: 48ch;
  color: #fff;
}

.findPeaceContent .bodyText {
  font-weight: 300;
  opacity: 0.88;
  margin-bottom: 1.8em;
}

/* --- Activities ---
   auto-fill, so dropping in another .activityCard needs no CSS change. */
.activitiesGrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr));
  gap: clamp(14px, 2vw, 24px);
  margin: clamp(28px, 4vw, 48px) 0;
}

.activityCard {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: 20px;
  overflow: hidden;
  background-color: var(--ink);
}

.activityCard img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.6s cubic-bezier(0.25, 1, 0.5, 1);
}

.activityCard:hover img {
  transform: scale(1.05);
}

.activityCard figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: clamp(16px, 2vw, 24px);
  color: #fff;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  text-transform: uppercase;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.82) 0%, rgba(0, 0, 0, 0) 100%);
}

/* --- Explore strip --- */
.explore {
  padding-bottom: 90px;
}

.exploreHead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 var(--gutter) clamp(20px, 3vw, 32px);
}

.exploreHead .sectionTitle {
  margin-bottom: 0;
  font-size: var(--text-xl);
}

.exploreNav {
  display: flex;
  gap: 8px;
}

.exploreNav button {
  width: 46px;
  height: 46px;
  border: none;
  border-radius: 999px;
  background-color: rgba(46, 42, 36, 0.08);
  color: var(--ink);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background-color 0.3s ease, color 0.3s ease;
}

.exploreNav button:hover {
  background-color: var(--ink);
  color: var(--cream);
}

.exploreStrip {
  display: flex;
  gap: 6px;
  padding: 0 var(--gutter);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.exploreStrip::-webkit-scrollbar {
  display: none;
}

.exploreStrip figure {
  flex: 0 0 clamp(220px, 30vw, 380px);
  scroll-snap-align: start;
}

.exploreStrip img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  display: block;
  border-radius: 20px;
}

.exploreStrip figcaption {
  padding-top: 12px;
  font-size: var(--text-xs);
  letter-spacing: 0.16em;
  text-indent: 0.16em;
  text-transform: uppercase;
  opacity: 0.62;
}

/* --- Newsletter --- */
.squareWhiteRoundCard {
  max-width: 1100px;
  margin: 0 auto 90px;
  padding: clamp(44px, 7vw, 90px) var(--gutter);
  border-radius: 20px;
  /* White, like the cuisine panel. On a white page the shadow is now the
     only thing separating the card from the background — which is exactly
     why it needed one before the tint could come off. */
  background-color: #fff;
  text-align: center;

  /* Same two-layer construction as the cuisine panel, so the two lifted
     cards on the page read as the same object at the same height: a tight
     layer to seat the edge, a wide soft one for the ambient spread. */
  box-shadow:
    0 4px 14px rgba(46, 42, 36, 0.05),
    0 30px 80px rgba(46, 42, 36, 0.13);
}

.squareWhiteRoundCard .bodyText {
  margin: 0 auto 2em;
}

.subscribeForm {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  max-width: 480px;
  margin: 0 auto;
}

.subscribeForm input {
  flex: 1 1 220px;
  padding: 1.05em 1.4em;
  border: 1px solid rgba(46, 42, 36, 0.28);
  border-radius: 999px;
  background: transparent;
  font-family: var(--sans);
  font-size: var(--text-sm);
  color: var(--ink);
}

.subscribeForm input::placeholder {
  color: rgba(46, 42, 36, 0.45);
}

/* --- Footer --- */
.footer {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 var(--gutter) clamp(34px, 5vw, 56px);
}

.footerGrid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(28px, 4vw, 48px);
}

.footerBrand .sectionTitle {
  font-size: var(--text-lg);
}

.footerCol p,
.footerCol li {
  font-size: var(--text-sm);
  line-height: 1.8;
  opacity: 0.78;
}

.footerCol ul {
  list-style: none;
}

.footerCol a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(46, 42, 36, 0.3);
  transition: opacity 0.3s ease;
}

.footerCol a:hover {
  opacity: 0.55;
}

/* Brand chips: a square mark and a wide wordmark read as one row only
   because the chip, not the mark, sets the shape. */
.platformRow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(14px, 2vw, 24px);
  margin-top: clamp(40px, 5vw, 64px);
  padding-top: clamp(22px, 3vw, 30px);
  border-top: 1px solid rgba(46, 42, 36, 0.18);
}

.platformRow .eyebrow {
  margin-bottom: 0;
}

.platformList {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  list-style: none;
}

.platformList a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 46px;
  padding: 0 20px;
  border: 1px solid rgba(46, 42, 36, 0.12);
  border-radius: 999px;
  background-color: #fff;
  color: var(--ink);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.platformList a:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(46, 42, 36, 0.16);
}

.footerBase {
  margin-top: clamp(26px, 3vw, 36px);
  font-size: var(--text-2xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.55;
}

@media (min-width: 820px) {
  .footerGrid {
    grid-template-columns: 1.6fr 1fr 1fr 1fr;
    align-items: start;
  }
}

@media (max-width: 700px) {
  .twoImagesContainer {
    --parallax-shift: 22px;
  }

  .imageCard {
    aspect-ratio: 3 / 4;
  }

  .pair-overlay p {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* Only the travel distance. The skeleton also carried a blanket
     *,*::before,*::after duration override, which is deliberately NOT
     ported — it would flatten the tuned reduced-motion rules already in
     style.css rather than adding to them. */
  .twoImagesContainer {
    --parallax-shift: 0px;
  }

  .cuisineTile img,
  .activityCard img {
    transition: none;
  }
}

/* =====================================================================
   Newsletter: the subscribe page, and the prompt that leads people to it.

   The section on the content pages no longer takes an address. Asking for
   one under a line of copy gives nobody room to say what will actually
   arrive, and an address handed over blind is the kind that never opens
   anything. The button now leads here, where the promise is set out first.
   ===================================================================== */

/* --- the promise, before the form ---------------------------------- */

.subscribeIntro {
  max-width: 820px;
  margin: 0 auto;
  padding: clamp(56px, 9vw, 110px) var(--gutter) clamp(28px, 4vw, 48px);
  text-align: center;
}

.promiseList {
  list-style: none;
  margin: clamp(36px, 5vw, 60px) 0 0;
  padding: 0;
  display: grid;
  gap: clamp(28px, 4vw, 44px);
  text-align: left;
}
@media (min-width: 860px) {
  .promiseList {
    grid-template-columns: repeat(3, 1fr);
  }
}

.promiseMark {
  display: block;
  font-family: var(--sans);
  font-size: var(--text-2xs);
  letter-spacing: 0.22em;
  color: var(--accent);
  margin-bottom: 0.9em;
}
.promiseList h3 {
  font-family: var(--serif);
  font-size: var(--text-md);
  font-weight: 400;
  margin: 0 0 0.5em;
}
.promiseList p {
  font-size: var(--text-sm);
  line-height: 1.7;
  opacity: 0.78;
  margin: 0;
}

/* --- the form card ------------------------------------------------- */

.subscribeCard {
  max-width: 760px;
  margin: 0 auto clamp(64px, 9vw, 120px);
  padding: clamp(34px, 5.5vw, 64px) clamp(26px, 5vw, 58px);
  background-color: #fff;
  border-radius: 20px;

  /* Same two-layer shadow as the cards on the home page, so this reads as
     the same object at the same height rather than a new kind of panel. */
  box-shadow:
    0 4px 14px rgba(46, 42, 36, 0.05),
    0 30px 80px rgba(46, 42, 36, 0.13);
}
.subscribeCard .sectionTitle,
.subscribeCard .bodyText {
  text-align: center;
}
.subscribeCard .bodyText {
  max-width: 46ch;
  margin: 0 auto 2.4em;
}

.subscribeThanks {
  text-align: center;
}

/* --- fields --------------------------------------------------------- */

.fieldRow {
  display: grid;
  gap: clamp(18px, 3vw, 26px);
}
@media (min-width: 620px) {
  .fieldRow {
    grid-template-columns: 1fr 1fr;
  }
}

.field {
  display: block;
  margin-bottom: clamp(18px, 3vw, 26px);
  border: none;
  padding: 0;
}
.field > label,
.field > legend {
  display: block;
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.72;
  margin-bottom: 0.7em;
  padding: 0;
}

.field input[type="text"],
.field input[type="email"],
.field textarea {
  width: 100%;
  padding: 0.95em 1.1em;
  font-family: var(--sans);
  font-size: var(--text-base);
  color: var(--ink);
  background-color: #fcfaf6;
  border: 1px solid rgba(46, 42, 36, 0.16);
  border-radius: 8px;
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.field textarea {
  resize: vertical;
  min-height: 6em;
  line-height: 1.6;
}
.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--accent);
  /* A ring rather than an outline, so the rounded corner is respected. */
  box-shadow: 0 0 0 3px rgba(140, 106, 69, 0.16);
}
.field input::placeholder,
.field textarea::placeholder {
  color: rgba(46, 42, 36, 0.36);
}

.req {
  font-size: var(--text-2xs);
  letter-spacing: 0.14em;
  color: var(--accent);
  margin-left: 0.5em;
}
.fieldHint {
  font-size: var(--text-xs);
  opacity: 0.6;
  margin: 0.6em 0 0;
}

/* --- checkboxes ------------------------------------------------------ */

.fieldChoices {
  margin-top: clamp(26px, 4vw, 36px);
}

.choice {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.9em;
  align-items: start;
  padding: 0.9em 0;
  cursor: pointer;
}
.choice input[type="checkbox"] {
  width: 1.15em;
  height: 1.15em;
  margin-top: 0.15em;
  accent-color: var(--accent);
  cursor: pointer;
}
.choice strong {
  display: block;
  font-weight: 400;
  font-size: var(--text-base);
}
.choice em {
  display: block;
  font-style: normal;
  font-size: var(--text-sm);
  opacity: 0.66;
  margin-top: 0.15em;
}

.choiceConsent {
  margin: clamp(18px, 3vw, 26px) 0 clamp(26px, 4vw, 34px);
  padding: 1.1em 1.2em;
  background-color: #fcfaf6;
  border-radius: 10px;
}

.subscribeFormFull .ctaButton {
  display: block;
  width: 100%;
  border: none;
  cursor: pointer;
  font-family: var(--sans);
}

.formFoot {
  font-size: var(--text-xs);
  line-height: 1.7;
  opacity: 0.62;
  text-align: center;
  margin: 1.8em 0 0;
}

/* A field only a robot fills in. Hidden from everyone else — display:none
   rather than the visually-hidden class, which is still reachable by a
   screen reader and would read as a stray unlabelled input. */
.honeypot {
  display: none;
}

/* --- the prompt: a centred modal ----------------------------------
   Half photograph, half message. It interrupts, so it has to earn it:
   shown once, remembered for 30 days when dismissed, and never again
   once somebody has actually subscribed. */

.newsletterPop {
  position: fixed;
  inset: 0;
  /* Above the chat panel (41) and the navbar (30) — a modal that something
     else can sit on top of is not a modal. */
  z-index: 60;

  display: grid;
  place-items: center;
  padding: var(--gutter);

  background-color: rgba(24, 21, 17, 0.62);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);

  opacity: 0;
  transition: opacity 0.5s ease;
}
.newsletterPop[hidden] {
  display: none;
}
.newsletterPop.isVisible {
  opacity: 1;
}

.newsletterPopCard {
  position: relative;
  width: min(940px, 100%);
  /* Tall, as asked — but capped so it never outgrows a short laptop screen
     and strands the button below the fold. */
  height: min(86vh, 660px);

  display: grid;
  grid-template-columns: 1fr 1fr; /* half image, half message */

  overflow: hidden;
  border-radius: 18px;
  background-color: var(--cream);
  color: var(--ink);
  box-shadow: 0 40px 120px rgba(0, 0, 0, 0.45);

  transform: translateY(18px) scale(0.985);
  transition: transform 0.55s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.newsletterPop.isVisible .newsletterPopCard {
  transform: none;
}

/* min-height:0 so the image column may shrink inside the grid rather than
   forcing the card taller than its own height cap. */
.newsletterPopFigure {
  position: relative;
  min-height: 0;
  background-color: #d9cdb8;
}
.newsletterPopFigure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.newsletterPopBody {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(28px, 4vw, 56px);
  overflow-y: auto;
}
.newsletterPopBody .eyebrow {
  margin-bottom: 1em;
}
.newsletterPopTitle {
  font-family: var(--serif);
  font-size: var(--text-xl);
  font-weight: 300;
  line-height: 1.12;
  margin: 0 0 0.55em;
}
.newsletterPopText {
  font-size: var(--text-sm);
  line-height: 1.75;
  opacity: 0.8;
  margin: 0 0 1.4em;
}
.newsletterPopList {
  list-style: none;
  margin: 0 0 2em;
  padding: 0;
  font-size: var(--text-sm);
  line-height: 1.6;
}
.newsletterPopList li {
  padding-left: 1.5em;
  position: relative;
  margin-bottom: 0.55em;
}
.newsletterPopList li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background-color: var(--accent);
}

.newsletterPopActions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1.4em;
}
.newsletterPopLater {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--sans);
  font-size: var(--text-xs);
  color: inherit;
  opacity: 0.6;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.newsletterPopLater:hover {
  opacity: 0.95;
}

.newsletterPopClose {
  position: absolute;
  top: 12px;
  right: 14px;
  z-index: 2;
  width: 38px;
  height: 38px;
  background-color: rgba(255, 255, 255, 0.82);
  border: none;
  border-radius: 50%;
  font-size: 22px;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
  transition: background-color 0.25s ease;
}
.newsletterPopClose:hover {
  background-color: #fff;
}

/* Stacked on a phone: the photograph takes the top third and the message
   the rest, because two columns at 380px gives neither enough room. */
@media (max-width: 720px) {
  .newsletterPopCard {
    grid-template-columns: 1fr;
    grid-template-rows: 34% 1fr;
    height: min(88vh, 640px);
  }
}

/* The scroll lock, so the page behind cannot be moved while this is open. */
body.newsletterOpen {
  overflow: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .newsletterPop,
  .newsletterPopCard {
    transition: none;
    transform: none;
  }
}

/* A mistyped address is silent and total: the form succeeds, the
   confirmation goes nowhere, and the person waits for a letter that cannot
   arrive. This warns in the accent rather than in red — it is a suggestion,
   not a rejection, and a real address that trips a rule must still go
   through. */
.fieldWarn {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6em;
  font-size: var(--text-xs);
  line-height: 1.6;
  color: var(--accent);
  margin: 0.6em 0 0;
}
.fieldWarn[hidden] {
  display: none;
}
.fieldWarnFix {
  font-family: var(--sans);
  font-size: var(--text-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cream);
  background-color: var(--accent);
  border: none;
  border-radius: 999px;
  padding: 0.5em 1.1em;
  cursor: pointer;
  transition: opacity 0.25s ease;
}
.fieldWarnFix:hover {
  opacity: 0.85;
}

/* The page somebody lands on after subscribing, and again after confirming.
   Split like the newsletter modal — photograph one side, words the other —
   so the two read as the same family. */
.subscribeThanks {
  display: grid;
  gap: clamp(24px, 4vw, 44px);
  text-align: left;
  padding: 0;
  overflow: hidden;
}
@media (min-width: 760px) {
  .subscribeThanks {
    grid-template-columns: 5fr 7fr;
    align-items: stretch;
  }
}

.thanksFigure {
  min-height: 220px;
  background-color: #d9cdb8;
}
.thanksFigure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.thanksBody {
  padding: clamp(28px, 4vw, 52px);
  padding-left: clamp(28px, 4vw, 20px);
}
@media (min-width: 760px) {
  .thanksBody { padding-left: 0; }
}
.subscribeThanks .sectionTitle,
.subscribeThanks .bodyText {
  text-align: left;
  margin-left: 0;
}
.subscribeThanks .bodyText { max-width: 42ch; }

.thanksHints {
  list-style: none;
  margin: 0 0 2em;
  padding: 0;
  font-size: var(--text-sm);
  line-height: 1.65;
  opacity: 0.78;
}
.thanksHints li {
  position: relative;
  padding-left: 1.4em;
  margin-bottom: 0.5em;
}
.thanksHints li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--accent);
}
.thanksHints[hidden] { display: none; }

.thanksActions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.4em;
}
/* The secondary way onward. A link, not a second button — two buttons of
   equal weight make somebody choose instead of act. */
.thanksQuiet {
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: inherit;
  opacity: 0.6;
  text-underline-offset: 3px;
}
.thanksQuiet:hover { opacity: 0.95; }

/* <picture> must not exist as far as layout is concerned.
 *
 * Wrapping an <img> in <picture> inserts an element between the image and
 * its parent. <picture> is display:inline by default, so an image that was
 * a grid item, a flex child, or absolutely positioned against that parent
 * silently stops being any of those — it collapses to inline size and lands
 * in the wrong place. That is what shrank and displaced images across the
 * site after the AVIF conversion.
 *
 * display:contents removes the wrapper box from the layout tree while
 * keeping its children, so the <img> is laid out by the parent exactly as
 * it was before the wrapper existed. */
picture {
  display: contents;
}
