/* ==========================================================================
   Villa Jasmine — Platanidia, South Pelion
   Palette drawn from the property itself: pebble whites, Pelion stone greys,
   deep Aegean teal, and the bougainvillea on its walls.
   Type: Marcellus (display) · Jost (body)
   ========================================================================== */

:root {
  /* Colour */
  --pebble: #F6F5F2;        /* base — pebble in sunlight */
  --pebble-deep: #ECEAE4;   /* tinted sections */
  --stone: #8C8A83;         /* mid stone grey */
  --stone-soft: #D8D5CE;    /* light stone, borders */
  --ink: #232E2C;           /* deep sea-slate text */
  --aegean: #2E5A5E;        /* deep sea teal — primary */
  --aegean-deep: #1E2B29;   /* dusk section background */
  --olive: #7A7E5F;         /* olive leaf */
  --bougainvillea: #A84A6F; /* the flower on the walls — used sparingly */
  --gold: #C9AF6A;          /* jasmine heart / star */
  --pmap-sea: #DAE2DD;      /* the drawn map's water, top stop — mirrors the
                               `pmap-sea` gradient's first stop in index.html.
                               Change one, change the other. (It no longer has
                               a second job dissolving the Google map's foot —
                               see "Architecture: the location section".) */

  /* Type */
  --font-display: "Marcellus", "Times New Roman", serif;
  --font-body: "Jost", "Avenir Next", "Helvetica Neue", sans-serif;

  /* Rhythm */
  --space-section: clamp(5rem, 12vw, 9rem);
  --wrap: 72rem;
  --wrap-wide: 76rem;
  --wrap-narrow: 46rem;

  /* Pebble shapes — sea-worn, each slightly different */
  --pebble-a: 58% 42% 55% 45% / 48% 55% 45% 52%;
  --pebble-b: 45% 55% 48% 52% / 55% 44% 56% 45%;
  --pebble-c: 52% 48% 58% 42% / 45% 52% 48% 55%;
  --pebble-d: 48% 52% 44% 56% / 52% 48% 56% 44%;

  /* Motion — one vocabulary for every animated thing on the page */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);          /* the house curve */
  --ease-drift: cubic-bezier(0.25, 0.46, 0.45, 0.94);  /* softer, for values re-set every frame */
  --dur-reveal: 1.05s;
  --dur-drift: 0.45s;
  --stagger-step: 90ms;
  --reveal-rise: 2.4rem;
}

/* ==========================================================================
   Reset & base
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 5rem;
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 1.0625rem;
  line-height: 1.7;
  color: var(--ink);
  background: var(--pebble);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.15;
  margin: 0;
}

p { margin: 0; }

a { color: var(--aegean); }

:focus-visible {
  outline: 2px solid var(--aegean);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--aegean); color: var(--pebble); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0s !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }
}

/* ==========================================================================
   Layout primitives
   ========================================================================== */

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 5vw, 2.5rem);
}

.wrap--narrow { max-width: var(--wrap-narrow); }

.section { padding-block: var(--space-section); }

.section--tint { background: var(--pebble-deep); }

.section--dusk {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background:
    radial-gradient(90rem 40rem at 50% 120%, rgba(46, 90, 94, 0.55), transparent 65%),
    var(--aegean-deep);
  color: var(--pebble);
}

/* Sunset over the same sea the copy is talking about */
.book__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.42;
}

.section--dusk::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(30, 43, 41, 0.9), rgba(30, 43, 41, 0.74) 45%, rgba(30, 43, 41, 0.93));
}

/* The section that lifts over the full-bleed hero above it, like a card set
   down on it. It carries its own background, since the panel shows through
   the overlap otherwise. */
.rise {
  position: relative;
  z-index: 10;
  border-radius: clamp(20px, 3vw, 34px) clamp(20px, 3vw, 34px) 0 0;
  box-shadow: 0 -40px 80px -40px rgba(30, 43, 41, 0.55);
  background: var(--pebble);
}

.rise.section--tint { background: var(--pebble-deep); }

.rise--welcome { margin-top: -12vh; }

.eyebrow {
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--bougainvillea);
  margin-bottom: 1.25rem;
}

.eyebrow--dusk { color: var(--gold); }

.title {
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  letter-spacing: 0.01em;
  margin-bottom: 1.5rem;
  text-wrap: balance;
}

.prose {
  max-width: 42rem;
  color: color-mix(in srgb, var(--ink) 88%, var(--stone));
}

.prose p + p { margin-top: 1.1em; }

.prose em { font-style: italic; }

.prose--dusk { color: color-mix(in srgb, var(--pebble) 85%, var(--aegean-deep)); }

/* Scroll reveal — hidden state only when JS is running.
   Direction and distance come from custom properties, so one transition rule
   serves every variant. The defaults are the plain upward rise this started as. */
html.js .reveal {
  opacity: 0;
  transform: translate3d(var(--reveal-x, 0), var(--reveal-y, var(--reveal-rise)), 0)
             scale(var(--reveal-s, 1));
  transition: opacity var(--dur-reveal) var(--ease-out),
              transform var(--dur-reveal) var(--ease-out);
}

html.js .reveal.in {
  opacity: 1;
  transform: none;
}

/* Variants — set the vars, inherit the transition */
html.js .reveal--left  { --reveal-x: -2.4rem; --reveal-y: 0; }
html.js .reveal--right { --reveal-x:  2.4rem; --reveal-y: 0; }
html.js .reveal--fade  { --reveal-y: 0; }
html.js .reveal--image { --reveal-y: 3rem; --reveal-s: 1.03; }

/* A full-bleed frame already spans the viewport, so scaling it up widens the
   document and puts a horizontal scrollbar on the page for as long as it sits
   unrevealed below the fold. It rises; it does not grow. */
html.js .band.reveal--image { --reveal-s: 1; }

/* A stagger group does not move itself; its direct children rise in sequence.
   main.js writes --i on each child once, at startup. These rules must stay
   after the base ones — same specificity, so the later wins. Groups must not
   nest: the selector is `> *` and nested delays would compound unpredictably. */
html.js .reveal--stagger {
  opacity: 1;
  transform: none;
}

html.js .reveal--stagger > * {
  opacity: 0;
  transform: translate3d(0, var(--reveal-rise), 0);
  transition: opacity var(--dur-reveal) var(--ease-out) calc(var(--i, 0) * var(--stagger-step)),
              transform var(--dur-reveal) var(--ease-out) calc(var(--i, 0) * var(--stagger-step));
}

html.js .reveal--stagger.in > * {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html.js .reveal,
  html.js .reveal--stagger > * {
    opacity: 1;
    transform: none;
    transition-delay: 0s !important;
  }
}

/* The icon sprite lives in the DOM but is never painted. Hidden by size
   rather than display:none, which has a history of breaking <use>. */
.svg-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* ==========================================================================
   Scroll drift — the photographs move slower than the page
   One custom property, --drift (px), is the only thing main.js writes. The
   frame stays put; the picture inside it moves. Layering a transition on top
   of a value that is re-set every frame is what makes it read as a slow drag
   rather than a 1:1 twitch.

   The overscan that hides the frame edge is done with scale(), not width:
   the global `img { max-width: 100% }` would clamp a 110% width straight
   back to 100% and the drift would expose an edge on the first scroll.
   ========================================================================== */

/* A text column drifting against the picture beside it */
.drift {
  transform: translate3d(0, var(--drift, 0px), 0);
  transition: transform var(--dur-drift) var(--ease-drift);
}

/* .photo is already position:relative + overflow:hidden, so it is the
   parallax window as it stands. scale(1.16) buys 8% of the frame's height
   of headroom at each edge, and |--drift| never exceeds the element's own
   data-drift, so that number must stay under 8% of the frame's *shortest*
   height above 720px or the drift will expose an edge. The tight one is
   .band: clamp(20rem, 46vw, 34rem) bottoms out near 336px at 721px wide,
   which is why it drifts 22px and not the 28px its desktop height allows. */
.photo--drift img {
  transform: translate3d(0, var(--drift, 0px), 0) scale(1.16);
  transition: transform var(--dur-drift) var(--ease-drift);
}

/* The dusk sunset is a bare <img>, not inside a .photo frame */
.book__bg {
  transform: translate3d(0, var(--drift, 0px), 0) scale(1.14);
  transition: transform var(--dur-drift) var(--ease-drift);
}

/* will-change is granted only while an element is actually on screen —
   holding a compositor layer for every photograph on the page is how you
   get the browser to start ignoring the hint altogether. */
.drift.is-drifting,
.photo--drift.is-drifting img,
.book__bg.is-drifting { will-change: transform; }

/* A rounded frame can leak a transformed child's corners on Safari */
.photo--drift { isolation: isolate; }

/* Phones: off. A collapsing address bar changes innerHeight mid-scroll,
   which shifts the ratio under every drifting element. */
@media (max-width: 720px) {
  .drift,
  .photo--drift img,
  .book__bg { transform: none; transition: none; }
}

@media (prefers-reduced-motion: reduce) {
  .drift,
  .photo--drift img,
  .book__bg { transform: none; }
}

/* ==========================================================================
   Photo placeholders — dignified until the real photographs arrive.
   When an image file exists it simply covers the placeholder.
   ========================================================================== */

.photo {
  margin: 0;
  position: relative;
  overflow: hidden;
  background:
    linear-gradient(160deg, #B9C4C0 0%, #9FB0AD 38%, #7E9793 62%, #5F7F7C 100%);
}

.photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.photo.is-empty img { display: none; }

.photo.is-empty::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(60% 45% at 70% 20%, rgba(246, 245, 242, 0.35), transparent 70%),
    radial-gradient(80% 60% at 20% 90%, rgba(30, 43, 41, 0.25), transparent 70%);
}

.photo.is-empty::after {
  content: attr(data-label);
  position: absolute;
  inset: auto 0 0 0;
  padding: 0.6rem 1rem;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(246, 245, 242, 0.85);
  background: linear-gradient(transparent, rgba(30, 43, 41, 0.45));
  text-align: center;
}

/* ==========================================================================
   Navigation
   ========================================================================== */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 40;
  transition: background 0.4s ease, box-shadow 0.4s ease;
  color: var(--pebble);
}

.nav.is-solid {
  background: color-mix(in srgb, var(--pebble) 92%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 1px 0 rgba(35, 46, 44, 0.08);
  color: var(--ink);
}

.nav__inner {
  max-width: var(--wrap);
  margin-inline: auto;
  padding: 1rem clamp(1.25rem, 5vw, 2.5rem);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
}

.nav__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-display);
  font-size: 1.25rem;
  letter-spacing: 0.06em;
  text-decoration: none;
  color: inherit;
}

.nav__flower { width: 1.35rem; height: 1.35rem; }
.nav__flower-heart { fill: var(--gold); }

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(1.2rem, 3vw, 2.2rem);
}

.nav__links a {
  font-size: 0.875rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: inherit;
  opacity: 0.9;
  transition: opacity 0.25s ease;
}

.nav__links a:hover { opacity: 1; }

/* A clean pill, per the Claude Design file — not one of the pebble blobs.
   Plain `inline` (an <a>'s default) draws a separate box per line if the text
   ever wraps, each with its own padding and only the outer two corners
   rounded — the pill then reads as off-centre because it no longer is one
   shape. `inline-flex` keeps it a single box no matter how the text breaks. */
.nav__links a.nav__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  padding: 0.5rem 1.1rem;
  border: 1px solid currentColor;
  border-radius: 999px;
  opacity: 1;
  transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}

.nav__links a.nav__cta:hover {
  background: var(--aegean);
  border-color: var(--aegean);
  color: var(--pebble);
}

.nav__toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  width: 2.6rem;
  height: 2.6rem;
  padding: 0.6rem;
  background: none;
  border: 0;
  cursor: pointer;
  color: inherit;
}

.nav__toggle span {
  display: block;
  height: 1.5px;
  background: currentColor;
  transition: transform 0.35s ease, opacity 0.35s ease;
}

.nav.is-open .nav__toggle span:first-child { transform: translateY(4.25px) rotate(45deg); }
.nav.is-open .nav__toggle span:last-child { transform: translateY(-4.25px) rotate(-45deg); }

@media (max-width: 720px) {
  .nav__toggle { display: flex; }

  .nav__links {
    position: fixed;
    inset: 0;
    flex-direction: column;
    justify-content: center;
    gap: 2.2rem;
    background: var(--aegean-deep);
    color: var(--pebble);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.35s ease;
  }

  .nav.is-open .nav__links {
    opacity: 1;
    pointer-events: auto;
  }

  .nav__links a { font-size: 1.05rem; }

  .nav__brand, .nav__toggle { position: relative; z-index: 2; }

  /* The solid bar's backdrop-filter makes the nav a containing block for its
     fixed children, which would trap the menu inside the header. Drop the bar
     while the menu is open — the full-screen panel is the background now. */
  .nav.is-open,
  .nav.is-open.is-solid {
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
    color: var(--pebble);
  }
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.95rem 2rem;
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  border-radius: 999px;
  transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease, transform 0.3s ease;
}

.btn:hover { transform: translateY(-1px); }

.btn__icon { width: 1.15rem; height: 1.15rem; flex: none; }

.btn--whatsapp {
  background: var(--gold);
  color: var(--aegean-deep);
  padding: 1.1rem 2.6rem;
}

.btn--whatsapp:hover { background: #D8C07E; }

/* ==========================================================================
   Hero
   ========================================================================== */

/* The wrapper is taller than the screen; the hero pins inside it, so the
   scroll distance (height - 100vh) is the runway for the drift: the longer it
   is, the more scrolling the hero takes before it lets go. */
.hero-wrap {
  position: relative;
  height: 140vh;
  background: var(--aegean-deep);
}

html.capture .hero-wrap { height: 100vh; }

.hero {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  color: var(--pebble);
}

/* The stack the scroll zoom is applied to. updateJourney() owns its inline
   transform, which is why the crossfade below lives on the frames inside it
   and never on this box. */
.hero__frames {
  position: absolute;
  inset: 0;
  transform-origin: 50% 60%;
  will-change: transform;
}

/* One photograph of the cycle. Only .is-on moves between them; the fade and
   the slow settle inwards are both here.

   Overscan is scale(), never width — the global `img { max-width: 100% }`
   would clamp a wider frame straight back and leave an edge showing.

   The settle runs far longer than the hold so it never arrives and stops; a
   resting frame sits at 1.05 and drifts in towards 1 for as long as it shows. */
.hero__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.05);
  z-index: 0;
  transition: opacity 1800ms ease, transform 11s ease-out;
}

/* The incoming frame rides above the one it replaces, so the crossfade is a
   dissolve on top of a solid photograph rather than a dip to the dark ground
   beneath the stack. */
.hero__frame.is-on {
  opacity: 1;
  transform: scale(1);
  z-index: 2;
}

/* Held opaque underneath until the arriving frame has fully covered it */
.hero__frame.is-under { z-index: 1; }

.hero__veil {
  position: absolute;
  inset: 0;
  opacity: 0.42;
  background: linear-gradient(185deg, rgba(30, 43, 41, 0.55) 0%, rgba(30, 43, 41, 0.15) 42%, rgba(30, 43, 41, 0.78) 100%);
}

/* Darkens the side the words sit on, so they stay legible over the stone */
.hero__side {
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, rgba(30, 43, 41, 0.72) 0%, rgba(30, 43, 41, 0.42) 32%, rgba(30, 43, 41, 0) 62%);
}

/* The exit curtain. The pinned hero darkens as the .rise section below climbs
   over it, so the outgoing photograph reads as being covered rather than
   merely scrolled past. --exit runs 0 -> 1 and is written by main.js; every
   consumer of it is CSS, which is what keeps this clear of the inline
   transforms updateJourney() owns on .hero__content. */
.hero { --exit: 0; }

.panel-veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--aegean-deep);
  opacity: calc(var(--exit) * 0.62);
}

.hero__content {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  justify-items: start;
  max-width: var(--wrap-wide);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 5vw, 2.5rem);
  will-change: transform;
}

.hero__eyebrow {
  margin-bottom: 1.1rem;
  font-size: 0.8125rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: rgba(246, 245, 242, 0.82);
}

.hero__title {
  font-size: clamp(3rem, 9vw, 7rem);
  line-height: 0.98;
  letter-spacing: 0.005em;
}

.hero__line {
  margin-top: 1.4rem;
  max-width: 30rem;
  font-size: clamp(1.05rem, 1.6vw, 1.25rem);
  color: rgba(246, 245, 242, 0.9);
}

.hero__cue {
  position: absolute;
  left: 50%;
  bottom: 2.2rem;
  transform: translateX(-50%);
  display: grid;
  justify-items: center;
  gap: 0.7rem;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(246, 245, 242, 0.75);
}

.hero__cue span {
  display: block;
  width: 1px;
  height: 3.4rem;
  background: linear-gradient(180deg, rgba(246, 245, 242, 0.75), rgba(246, 245, 242, 0));
}

/* Page-load sequence */
html.js .hero__eyebrow,
html.js .hero__title,
html.js .hero__line {
  opacity: 0;
  transform: translateY(1rem);
  animation: rise 1.1s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

html.js .hero__title { animation-delay: 0.15s; }
html.js .hero__line { animation-delay: 0.35s; }

@keyframes rise {
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-wrap { height: 100vh; }

  html.js .hero__eyebrow,
  html.js .hero__title,
  html.js .hero__line { opacity: 1; transform: none; animation: none; }

  .panel-veil { opacity: 0; }
}

@media (max-width: 720px) {
  .hero-wrap { height: 125vh; }
}

/* ==========================================================================
   Around the villa — three rows, one photograph each
   ========================================================================== */

/* Each row is the page split in two — photograph on one side, words on the
   other, alternating with every row — inset from the true viewport edge by
   --around-pad rather than run out to it. A full-bleed photograph is a
   deliberate statement, and the hero is the only place on the page that
   makes it; here it would just read as the browser cropping the picture. The
   section sits outside .wrap because that inset is its own value, not
   .wrap's. The ground stays --pebble, the colour #welcome ends on, so the
   section still joins its neighbours with no edge at all. */
.around {
  background: var(--pebble);
  /* `clip` rather than `hidden` — it must not become a scroll container. A
     figure rising into place under .reveal is translated, not scaled, so this
     is only insurance against a subpixel overshoot at the viewport edge. */
  overflow-x: clip;
}

.around__row {
  --around-pad: clamp(1.5rem, 5vw, 5rem);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: stretch;
  column-gap: clamp(1.5rem, 5vw, 4rem);
  padding-inline: var(--around-pad);
}

.around__row + .around__row { margin-top: clamp(3rem, 8vw, 6rem); }

/* The row's height is whichever is taller, the photograph's own 4:3 crop or
   the words beside it — a short paragraph and a wide column let the photo
   decide; a long one at a narrow column makes the text win, and the photo is
   simply cropped a little more (see the stretch note below). Measured across
   the two-column range (961px–1920px+), the shortest a row stands is ~427px,
   8% of which is ~34px — the ceiling `data-drift` has to stay under. It
   drifts 24, comfortably inside it.

   Both stretches are spelled out. A grid item with an aspect-ratio treats the
   default `normal` alignment as `start`, not `stretch`, so with only the ratio
   set it took its *width* from a row that a long paragraph had made taller
   than 4:3 — and overflowed into the words beside it. Held to its track on
   both axes, a taller row simply crops the photograph a little more instead. */
.around__fig {
  width: 100%;
  justify-self: stretch;
  align-self: stretch;
  aspect-ratio: 4 / 3;
  min-height: 20rem;
}

/* Words on the left, photograph on the right. `order` rather than a second
   grid so the markup keeps the photograph first in every row, which is the
   order it stacks in on a phone. */
.around__row--flip .around__fig { order: 2; }

/* The block sits centred in its column's empty space, both axes — flex
   centres it vertically against the row height and horizontally against the
   column width, so it reads as filling the gap beside the photograph rather
   than hugging either edge of it. The paragraph text itself stays
   left-aligned within that centred block: ragged, centred line-ends make
   even short, plain sentences read as a formal announcement rather than a
   plain description. */
.around__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding-block: clamp(1.5rem, 4vw, 3rem);
}

.around__copy { max-width: 32rem; }

/* A size below the page's section titles — this heading introduces one of
   three short chapters inside a section, not the section itself, and at full
   .title size all three read as separate proclamations rather than a set. */
.around__title {
  font-size: clamp(1.6rem, 3.4vw, 2.35rem);
}

/* One column below 960px, photograph first — the breakpoint math is
   unchanged, see the note it used to carry here: a half-width column of copy
   above it runs ~320px of text under a four-line heading, legible but no
   longer airy. --around-pad drops to match .wrap's own side padding, and the
   photograph cancels it with an equal negative margin to bleed back out to
   the true edge: one full-width photo per row is the same edge-to-edge
   treatment the rest of the site's photography uses on a phone (the strip,
   the hero). Only the words need the inset here. */
@media (max-width: 960px) {
  .around__row {
    grid-template-columns: minmax(0, 1fr);
    --around-pad: clamp(1.25rem, 5vw, 2.5rem);
    row-gap: clamp(1rem, 4vw, 1.6rem);
    column-gap: 0;
  }

  .around__row + .around__row { margin-top: clamp(3rem, 10vw, 5rem); }

  .around__row--flip .around__fig { order: 0; }
  .around__text { align-items: flex-start; }

  .around__fig {
    min-height: 0;
    /* The base rule sets width: 100%, which resolves against the grid
       track (not the margin box), so the negative margins below would
       only shift the photo left without growing it — full bleed on one
       edge, a gap on the other. Widening by twice the pad it gives up
       is what actually reaches both true edges. */
    width: calc(100% + (2 * var(--around-pad)));
    margin-inline: calc(-1 * var(--around-pad));
  }

  .around__text { padding-block: clamp(1.8rem, 6vw, 3rem) 0; }
}

/* ==========================================================================
   Story — text beside the jasmine
   ========================================================================== */

.story {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  gap: clamp(2rem, 6vw, 4.5rem);
  align-items: center;
}

.story__photo {
  aspect-ratio: 4 / 5;
  border-radius: 12px;
}

/* The photograph keeps its 4:5 portrait frame at every width. It is a portrait
   (2:3) shot, and the old 4:3 landscape frame on a phone cut away most of it. */
@media (max-width: 860px) {
  .story { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Full-width photographic bands
   Currently unused — the location section (its one instance) was rebuilt as
   a pinned shore panel. Kept as a primitive for a future full-bleed photo
   with a caption; markup is `<figure class="photo band ..."><img data-photo><figcaption class="band__caption">`.
   ========================================================================== */

.band {
  position: relative;
  margin-block: clamp(3rem, 7vw, 5rem);
  height: clamp(20rem, 46vw, 34rem);
}

.band__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 3rem 1.5rem 1.4rem;
  text-align: center;
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 2.2vw, 1.4rem);
  letter-spacing: 0.03em;
  color: var(--pebble);
  background: linear-gradient(transparent, rgba(30, 43, 41, 0.65));
}

/* The label sits above the caption on an empty placeholder */
.band.is-empty::after { bottom: auto; top: 0; }

/* ==========================================================================
   Stats — a divided line under the intro, not cards
   ========================================================================== */

.stats {
  list-style: none;
  margin: clamp(3rem, 7vw, 5rem) 0 0;
  padding: clamp(1.8rem, 4vw, 2.6rem) 0 0;
  border-top: 1px solid var(--stone-soft);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 1.6rem;
}

.stat {
  display: grid;
  gap: 0.2rem;
}

.stat strong {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.9rem, 3.5vw, 2.6rem);
  line-height: 1;
}

.stat strong i {
  font-style: normal;
  font-size: 0.5em;
}

.stat span {
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--stone);
}

/* ==========================================================================
   The Villa
   ========================================================================== */

.villa__intro { max-width: 46rem; }

/* --- The photograph strip -------------------------------------------------
   A row of columns — some one tall photograph, some a stacked pair — dragged
   sideways rather than scrolled. It is full-bleed, so it sits outside .wrap.

   The lens: a column stands at its full height in the middle of the screen and
   falls away to half that at either edge. JS writes two numbers per column per
   frame, --k (0.5…1) and its inverse --ik; every transform lives here.

   Height is never animated. The column is squashed with scaleY(--k), which
   would squash the photograph with it, so the photograph is counter-scaled by
   --ik: the two cancel out and the image renders at its natural proportions
   inside a frame that is shorter than it is. What moves is the crop, not the
   picture — and it is all one composited transform, so no layout runs while
   the strip is being dragged.
   ------------------------------------------------------------------------ */

.gal {
  --gal-w: clamp(11.5rem, 21vw, 22rem);   /* one column */
  --gal-h: calc(var(--gal-w) * 1.62);     /* the tallest a column ever stands */
  --gal-gap: clamp(0.5rem, 1.1vw, 1rem);
  --gal-r: 10px;
  margin-top: clamp(2.4rem, 5vw, 3.4rem);
  position: relative;   /* the drag cue is placed against this box, not .gal__pan
                           — see the cue's own note below */
}

.gal__pan {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  padding-block: clamp(1.6rem, 3vw, 2.6rem);
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

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

.gal.is-dragging .gal__pan { cursor: grabbing; }

/* --- The drag cue.
   A ring trailing the pointer with the word beside it, in place of the
   native cursor. main.js builds it on entry, removes it on leave, and writes
   only its translate; everything it looks like is here.

   It hangs off .gal rather than .gal__pan on purpose: the pan is the scroll
   container, so a box positioned inside it would be laid out in the scrolled
   content and travel with the strip — sliding off the pointer by however far
   the strip had been dragged. .gal does not scroll.

   The native cursor is hidden only while the cue is actually up (.has-cue),
   never unconditionally: where the cue is not built — no fine pointer,
   reduced motion, capture — grab/grabbing above is still the whole answer,
   and a bare `cursor: none` would leave those visitors with no cursor at all
   over the strip. */
.gal.has-cue .gal__pan,
.gal.has-cue .gal__tile { cursor: none; }

/* The cue's own origin is the pointer itself: main.js translates this box to
   the pointer's position and the two children are placed around that one
   point, each with its own offset. Nothing here is laid out relative to
   anything else, so the word cannot be shifted by the ring's box — an
   earlier flex row had exactly that problem. */
.gal__cue {
  --cue-r: 0.5rem;         /* the ring's radius; every offset below is in terms of it */
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  z-index: 3;
  pointer-events: none;    /* it must never be the thing under the pointer */
  will-change: transform;
  animation: gal-cue-in 0.32s var(--ease-out) both;
}

/* Centred on the pointer. Both the ring and the word are pale on a dark
   ground, so the ground is the cue's own: a tinted fill dark enough that a
   sunlit photograph underneath cannot wash the white border or the word out.
   Tested against the four the strip actually runs over — olive, aegean, the
   pale stone of a bright interior, and near-black. */
.gal__cue-ring {
  position: absolute;
  top: calc(var(--cue-r) * -1);
  left: calc(var(--cue-r) * -1);
  width: calc(var(--cue-r) * 2);
  height: calc(var(--cue-r) * 2);
  border: 1px solid rgba(246, 245, 242, 0.9);
  border-radius: 50%;
  background: rgba(24, 35, 33, 0.42);
  box-shadow: 0 2px 14px rgba(24, 35, 33, 0.35);
  transition: transform var(--dur-drift) var(--ease-out),
              background-color var(--dur-drift) var(--ease-out);
}

/* Above and to the right, clear of the ring's edge on both axes. No
   backdrop-filter anywhere in the cue, and none may be added: this box moves
   every frame over the photographs, and a filtered surface would have to be
   re-composited against them each time — the cost the motion layer's note
   about expensive backdrops is about. The text-shadow does the same job of
   holding the word legible over a bright photograph for nothing. */
.gal__cue-word {
  position: absolute;
  left: calc(var(--cue-r) * 0.78);
  bottom: calc(var(--cue-r) * 0.78);
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--pebble);
  /* Two shadows, not one: the tight one is the contrast that keeps the word
     off a pale photograph, the wide one is what stops it looking pasted on a
     dark one. */
  text-shadow: 0 0 3px rgba(24, 35, 33, 0.9),
               0 1px 2px rgba(24, 35, 33, 0.95),
               0 2px 12px rgba(24, 35, 33, 0.65);
  white-space: nowrap;
}

/* Pressed: the ring closes a little, the way a hand does */
.gal.is-dragging .gal__cue-ring {
  transform: scale(0.82);
  background: rgba(24, 35, 33, 0.58);
}

@keyframes gal-cue-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.gal__rail {
  display: flex;
  align-items: center;
  gap: var(--gal-gap);
  width: max-content;
  padding-inline: clamp(1.25rem, 5vw, 2.5rem);
}

.gal__col {
  --k: 1;
  --ik: 1;
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--gal-gap);
  width: var(--gal-w);
  height: var(--gal-h);
  transform: scaleY(var(--k));
  transform-origin: 50% 50%;
}

/* Every column is the same height and centred on the same axis — a stacked
   pair included, so the bell peaks the same whichever kind of column happens
   to be in the middle of the screen. The lens is the only thing that varies
   a column's height; anything else nudging them up or down reads as noise
   over the curve rather than as variety. */

.gal__tile {
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  display: block;
  appearance: none;
  padding: 0;
  border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* The vertical radius is stretched by --ik so the squash returns it to
     --gal-r. Two-value slash syntax: horizontal / vertical. */
  border-radius: var(--gal-r) / calc(var(--gal-r) * var(--ik));
}

.gal__tile img {
  transform: scaleY(var(--ik));
  transform-origin: 50% 50%;
  pointer-events: none;
  -webkit-user-drag: none;
}

/* A veil the photograph shrugs off when it is pointed at — only where there
   is something to point with. On a touch screen the same rule would simply
   dim every photograph, for good. */
@media (hover: hover) {
  .gal__tile::after {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(30, 43, 41, 0.14);
    transition: opacity 0.5s var(--ease-out);
  }

  .gal__tile:hover::after,
  .gal__tile:focus-visible::after { opacity: 0; }
}

.gal__tile:focus-visible {
  outline: 2px solid var(--aegean);
  outline-offset: 3px;
}

/* A compositor layer for every column and every photograph is worth having
   while the strip is on screen and worth giving back the moment it is not —
   the same bargain the drifting photographs make. */
.gal.is-live .gal__col,
.gal.is-live .gal__tile img { will-change: transform; }

@media (prefers-reduced-motion: reduce) {
  .gal__col,
  .gal__tile img { transform: none; }
}

/* --- The way into the album.
   Deliberately quiet: a small spaced-out line under the strip, centred, that
   only underlines when reached for. The strip is the gallery; this is for the
   visitor who wants every room. */
.gal__more {
  display: flex;
  justify-content: center;
  margin-top: clamp(0.4rem, 1.2vw, 0.9rem);
  padding-inline: 1.25rem;
}

.gal__all {
  appearance: none;
  padding: 0.5rem 0.2rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--stone);
  text-decoration: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.5em;
  cursor: pointer;
  transition: color 0.3s ease, text-decoration-color 0.3s ease;
}

.gal__all:hover,
.gal__all:focus-visible {
  color: var(--aegean);
  text-decoration-color: currentColor;
}

.gal__all:focus-visible {
  outline: 2px solid var(--aegean);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ==========================================================================
   Album — every photograph, room by room
   ========================================================================== */

/* display: none must survive here, for the same reason as the lightbox's */
.album[hidden] { display: none; }

/* A sheet of the page colour over the page, scrolling on its own. It sits
   under the lightbox (z-index 60), which opens over it from any tile. */
.album {
  position: fixed;
  inset: 0;
  z-index: 55;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--pebble);
  opacity: 0;
  transition: opacity 0.4s var(--ease-out);
}

.album.is-open { opacity: 1; }

.album__bar {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: clamp(0.9rem, 2.4vw, 1.4rem) clamp(1.25rem, 5vw, 2.5rem);
  background: var(--pebble);
  border-bottom: 1px solid var(--stone-soft);
}

.album__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.25rem, 2.6vw, 1.7rem);
  color: var(--ink);
}

.album__close {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.7rem;
  height: 2.7rem;
  padding: 0 0 0.2rem;
  border-radius: 999px;
  border: 1px solid var(--stone-soft);
  background: transparent;
  color: var(--ink);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease;
}

.album__close:hover {
  background: var(--aegean);
  border-color: var(--aegean);
  color: var(--pebble);
}

.album__body {
  max-width: var(--wrap-wide);
  margin-inline: auto;
  padding: clamp(1.6rem, 4vw, 2.8rem) clamp(1.25rem, 5vw, 2.5rem) clamp(3rem, 8vw, 5rem);
}

.album__group + .album__group { margin-top: clamp(2.2rem, 5vw, 3.4rem); }

.album__head {
  margin-bottom: 1rem;
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--bougainvillea);
}

.album__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
  gap: clamp(0.5rem, 1.1vw, 0.9rem);
}

/* Every tile the same 4:3 crop, so the grid reads as a contact sheet; the
   lightbox shows each photograph whole. */
.album__tile {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  appearance: none;
  padding: 0;
  border: 0;
  border-radius: 10px;
  cursor: zoom-in;
  -webkit-tap-highlight-color: transparent;
}

.album__tile img { transition: transform 0.8s var(--ease-out); }

@media (hover: hover) {
  .album__tile:hover img { transform: scale(1.04); }
}

.album__tile:focus-visible {
  outline: 2px solid var(--aegean);
  outline-offset: 3px;
}

@media (max-width: 560px) {
  .album__grid { grid-template-columns: repeat(2, 1fr); }
}

/* ==========================================================================
   Lightbox — one photograph, full size
   ========================================================================== */

/* display: none must survive here. A panel's own display rule outranks the
   browser default for [hidden], which would leave the overlay on the page. */
.lightbox[hidden] { display: none; }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
  /* Opaque, and lit the way the dusk section is — nothing of the page
     behind should read through a photograph shown on its own. */
  background:
    radial-gradient(80rem 50rem at 50% 35%, rgba(46, 90, 94, 0.4), transparent 70%),
    var(--aegean-deep);
  opacity: 0;
  transition: opacity 0.4s var(--ease-out);
}

.lightbox.is-open { opacity: 1; }

/* The frame is a fixed 3:2 box sized by the screen alone — never by the
   photograph in it — so nothing moves as the arrows run through landscape
   and portrait shots alike. Its width is the smallest of: the space left
   once the arrows have their gutters either side, 74rem, and whatever width
   keeps a 3:2 box (plus the caption bar under it) inside the viewport's
   height. Each photograph is fitted whole inside it with `contain`; a
   portrait one simply stands with the dark ground either side. */
.lightbox__figure {
  --lb-arrow: 2.7rem;
  --lb-gutter: calc(var(--lb-arrow) + clamp(0.8rem, 1.6vw, 1.4rem));
  width: min(100% - 2 * var(--lb-gutter), 74rem, (100vh - 9rem) * 1.5);
  margin: 0;
  display: grid;
  gap: clamp(0.8rem, 2vw, 1.2rem);
}

.lightbox__frame { position: relative; }

.lightbox__stage {
  aspect-ratio: 3 / 2;
  border-radius: 10px;
  overflow: hidden;
  background: rgba(246, 245, 242, 0.04);
}

.lightbox__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The arrows hang off the frame's own sides, centred on its height — fixed
   to the frame, so they stand still whatever shape the photograph is. */
.lightbox__arrow {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
}

.lightbox__arrow--prev { right: calc(100% + var(--lb-gutter) - var(--lb-arrow)); }
.lightbox__arrow--next { left: calc(100% + var(--lb-gutter) - var(--lb-arrow)); }

.lightbox__bar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
}

.lightbox__caption {
  max-width: 42rem;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  color: rgba(246, 245, 242, 0.82);
}

.lightbox__counter {
  flex: none;
  font-size: 0.78rem;
  letter-spacing: 0.18em;
  color: rgba(246, 245, 242, 0.8);
  font-variant-numeric: tabular-nums;
}

.lightbox__arrow,
.lightbox__close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.7rem;
  height: 2.7rem;
  padding: 0 0 0.15rem;
  border-radius: 999px;
  border: 1px solid rgba(246, 245, 242, 0.5);
  background: rgba(30, 43, 41, 0.18);
  color: var(--pebble);
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease;
}

.lightbox__arrow:hover,
.lightbox__close:hover {
  background: rgba(246, 245, 242, 0.92);
  border-color: var(--pebble);
  color: var(--ink);
}

.lightbox__close {
  position: absolute;
  top: clamp(1rem, 3vw, 2rem);
  right: clamp(1rem, 3vw, 2rem);
  font-size: 1.5rem;
  padding: 0 0 0.2rem;
}

/* On a phone there is no room for gutters: the frame takes the full width
   and the arrows sit just inside its edges, on a darker disc so they hold
   against a bright photograph. The frame turns 4:5 there, so a portrait
   shot fills more of the screen and a landscape one still reads whole. */
@media (max-width: 560px) {
  .lightbox__figure {
    --lb-gutter: 0px;
    width: min(100%, (100vh - 12rem) * 0.8);
  }
  .lightbox__stage { aspect-ratio: 4 / 5; }
  .lightbox__arrow { background: rgba(30, 43, 41, 0.55); }
  .lightbox__arrow--prev { right: auto; left: 0.5rem; }
  .lightbox__arrow--next { left: auto; right: 0.5rem; }
  .lightbox__caption { font-size: 0.76rem; }
}

/* ==========================================================================
   Amenities
   ========================================================================== */

/* The features at a glance, before the lists spell them out. Centred
   wrapping rather than a grid: eight items never divide evenly, and a
   short last row reads as deliberate when it is centred. */
.amenity-facts {
  list-style: none;
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 0;
  padding: clamp(1.6rem, 3.5vw, 2.2rem) 0 0;
  border-top: 1px solid var(--stone-soft);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  column-gap: clamp(1.5rem, 4vw, 3rem);
  row-gap: 1.15rem;
}

.amenity-fact {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.76rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--stone);
}

/* Drawn at the weight of the hairlines they sit among */
.amenity-icon {
  width: 1.15rem;
  height: 1.55em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: color-mix(in srgb, var(--aegean) 45%, var(--stone));
}

/* Larger, so the stroke steps down to keep the same apparent weight */
.amenity-icon--fact {
  width: 1.35rem;
  height: 1.35rem;
  stroke-width: 1.15;
  color: var(--aegean);
}

.amenities {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(2rem, 4vw, 3.5rem) clamp(2rem, 5vw, 4.5rem);
}

.amenities__group h3 {
  font-size: 1.2rem;
  letter-spacing: 0.05em;
  margin-bottom: 1rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--stone-soft);
}

.amenities__group ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.7rem;
}

/* Two columns, so a label that wraps stays clear of its icon. The icon's
   1.55em height is one line box, which centres it on the first line. */
.amenities__group li {
  display: grid;
  grid-template-columns: 1.15rem 1fr;
  column-gap: 0.7rem;
  align-items: start;
  font-size: 0.98rem;
  line-height: 1.55;
}

@media (max-width: 900px) {
  .amenities { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .amenities { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Guest words
   ========================================================================== */

/* A photograph behind the quotes rather than a tint, drifting on the same
   --plx the location window uses — half its distance, since there are no
   words crossing it here, only the page. The section keeps the deep ground
   underneath, so the copy stays legible for as long as the image is loading
   and if it never arrives at all. */
.section--photo {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--aegean-deep);
  color: var(--pebble);
}

.section__frame {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
}

.section__plate {
  position: absolute;
  left: 0;
  top: -9%;
  width: 100%;
  height: 118%;
  transform: translate3d(0, calc(var(--plx, 0) * 5%), 0);
  transition: transform var(--dur-drift) var(--ease-drift);
  will-change: transform;
}

.section__bg {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Timber and a lit wall behind quiet type: the scrim has to be heavy and
   even, not a corner gradient, or the slats read straight through the words. */
.section__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg,
    rgba(30, 43, 41, 0.72) 0%,
    rgba(30, 43, 41, 0.6) 45%,
    rgba(30, 43, 41, 0.75) 100%);
}

/* The section's own type, re-lit for the dark ground it now stands on */
.section--photo .eyebrow { color: var(--gold); }
.section--photo .quote { border-left-color: rgba(246, 245, 242, 0.28); }
.section--photo .quote cite { color: rgba(246, 245, 242, 0.6); }
.section--photo .quotes__note { color: rgba(246, 245, 242, 0.78); }

.section--photo .badge {
  background: rgba(246, 245, 242, 0.08);
  border-color: rgba(246, 245, 242, 0.26);
  color: rgba(246, 245, 242, 0.92);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.55rem 1.3rem;
  background: #FDFCFA;
  border: 1px solid var(--stone-soft);
  border-radius: 999px;
  font-size: 0.88rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.badge__star { color: var(--gold); font-size: 1rem; }

.quotes {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
}

.quote {
  margin: 0;
  padding: 0 0 0 1.6rem;
  border-left: 1px solid var(--stone-soft);
}

.quote p {
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 2.2vw, 1.5rem);
  line-height: 1.4;
}

.quote cite {
  display: block;
  margin-top: 0.9rem;
  font-style: normal;
  font-size: 0.85rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--stone);
}

.quotes__note {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  text-align: center;
  font-style: italic;
  color: color-mix(in srgb, var(--ink) 75%, var(--stone));
}

@media (max-width: 720px) {
  .quotes { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Location — the map, with the distances layered on it
   ========================================================================== */

/* `#location` is the map band itself now — the id the nav's "Location" link
   points at. The heading-only section that used to carry it ("Where you'll
   be" over the guest-words panel, and before that a parallax window) is gone
   entirely: a title with nothing under it, two screens above the map it
   named, read as a signpost to nowhere. The link lands on the map and the
   card of distances, which is the answer to the question anyway. */

/* Not a `.rise`. That class lifts a section over a full-bleed panel above it,
   and the panel this one used to climb over — the dark parallax window — is
   gone; against the cream section that replaced it, the rounded shoulders and
   the upward shadow drew a card edge across its own colour instead of a join.
   What has to stay is `overflow: hidden`: the wash below is deliberately
   taller than the map and this is what clips it. The background shows only
   where the embed does not reach — the strip under the card below 860px. */
.locmap-band {
  position: relative;
  overflow: hidden;
  background: var(--pebble);
}

/* The height belongs to the embed, not to this box. `.locmap-band` clips with
   overflow:hidden, so anything that overhangs a fixed height here is cut off —
   which is exactly what the card does once it leaves absolute positioning
   below 860px. Letting the box grow keeps it in flow. */
.locmap {
  --locmap-h: min(82vh, 48rem);
  position: relative;
  background: var(--pebble-deep);
}

/* The top join. The page colour dissolves into the map rather than meeting it
   on a line, so the section above appears to open into the photograph — the
   mirror of what `.locmap__wash` now does at the foot, both fading to the
   same `--pebble`.

   It is anchored to the map and deliberately NOT part of that wash, which
   travels with --plx: a ramp that moved would slide off the edge it exists to
   cover, showing a strip of raw map against the cream at one end of its
   travel and a cream band lying over the map at the other. Eased stops for
   the reason every other ramp here carries them — two stops leave a readable
   line where the fade begins. */
/* Disabled for now: the map meets the sections either side on a plain edge.
   Uncomment to restore the top fade.
.locmap::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  z-index: 1;
  height: clamp(3.5rem, 9vh, 7rem);
  pointer-events: none;
  background: linear-gradient(180deg,
    var(--pebble) 0%,
    rgba(246, 245, 242, 0.78) 26%,
    rgba(246, 245, 242, 0.34) 54%,
    rgba(246, 245, 242, 0) 100%);
}
*/

.locmap__embed {
  display: block;
  width: 100%;
  height: var(--locmap-h);
  border: 0;
  /* Pulled towards the page's stone-and-sea palette; full colour returns the
     moment the visitor reaches for the map. */
  filter: grayscale(0.82) sepia(0.1) saturate(1.05);
  transition: filter 0.6s ease;
}

.locmap:hover .locmap__embed,
.locmap:focus-within .locmap__embed { filter: none; }

/* Dissolves the map's lower edge into the page colour, the mirror of what
   `.locmap::before` does at the top — this section now sits between two
   ordinary `--pebble` sections (`#know` above, `#book`'s dark panel below),
   not directly over the drawn Pelion map, so there is no longer a second
   surface for the satellite photograph to "become". That effect — this ramp
   landing on `--pmap-sea` so the two maps met on one colour, and the Pelion
   veil giving up its own top ramp in exchange — belonged to when this section
   sat directly above `#pelion`; see "Architecture: the location section" in
   CLAUDE.md for where it moved and why.

   It still travels: the section is a `[data-plx]` one, and the dissolve edge
   rises a little as the page passes. The extra height below the map is the
   headroom that keeps the solid end of the ramp over the map's foot at both
   extremes of that travel — `.locmap-band` clips it. */
.locmap__wash {
  position: absolute;
  inset: 0 0 auto;
  height: calc(var(--locmap-h) + 3rem);
  pointer-events: none;
  transform: translate3d(0, calc(var(--plx, 0) * -1.4rem), 0);
  transition: transform var(--dur-drift) var(--ease-drift);
  /* Load-bearing for the same reason `.pelion .wrap`'s is, and for a worse
     backdrop: this gradient travels directly over `.locmap__embed`, a Google
     Maps iframe wearing a CSS filter. Unpromoted, every frame of its travel
     invalidated the iframe underneath, and a filtered iframe has to be
     rendered offscreen and re-filtered to be repainted at all. */
  will-change: transform;
  /* Eased in stops, for the reason the Pelion veil carries them: a plain
     two-stop ramp leaves a readable line where the fade begins. The map's top
     edge is not this element's business — it belongs to `.locmap::before`,
     which does not travel. */
  background:
    linear-gradient(180deg,
      rgba(246, 245, 242, 0) 68%,
      rgba(246, 245, 242, 0.28) 79%,
      rgba(246, 245, 242, 0.72) 87%,
      var(--pebble) 93%,
      var(--pebble) 100%);
}

/* Centred without transform — the reveal animates transform, and the two
   would fight. inset-block + auto margins do the same centring statically. */
.locmap__card {
  position: absolute;
  z-index: 2; /* clear of the top join, which is painted over the embed */
  inset-block: 0;
  right: clamp(1.25rem, 5vw, 4rem);
  margin-block: auto;
  height: fit-content;
  max-height: calc(var(--locmap-h) - 3rem);
  width: min(23rem, 40%);
  border-radius: 14px;
  border: 1px solid var(--stone-soft);
  background: rgba(253, 252, 250, 0.94);
  box-shadow: 0 24px 60px -30px rgba(30, 43, 41, 0.5);
  overflow: hidden;
}

.locmap__lead {
  padding: clamp(1.1rem, 2.2vw, 1.5rem) clamp(1.2rem, 2.4vw, 1.6rem) 0;
  font-size: 0.72rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--stone);
}

.locmap__card .distances {
  margin: 0.6rem clamp(1.2rem, 2.4vw, 1.6rem) 0.8rem;
}

.locmap__link {
  display: block;
  padding: 1.1rem 1rem;
  border-top: 1px solid var(--stone-soft);
  background: #FDFCFA;
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  text-decoration: none;
  color: var(--aegean);
  transition: background 0.3s ease;
}

.locmap__link:hover { background: var(--pebble-deep); }

.distances {
  list-style: none;
  margin: 0;
  padding: 0;
}

.distances__item {
  display: grid;
  gap: 0.3rem;
  padding: 1.15rem 0;
  border-bottom: 1px solid var(--stone-soft);
}

.distances__item:first-child { padding-top: 0.75rem; }
.distances__item:last-child { border-bottom: 0; padding-bottom: 0.4rem; }

.distances__item strong {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.02rem;
  letter-spacing: 0.02em;
}

.distances__item span {
  font-size: 0.86rem;
  color: var(--stone);
  letter-spacing: 0.03em;
}

/* Below the split the card leaves the map entirely and sits under it. There is
   not enough width for a card laid over the map to leave any of it readable,
   and an overhanging card fights the wash at the map's foot. */
@media (max-width: 860px) {
  .locmap { --locmap-h: max(52vh, 20rem); }

  .locmap__card {
    position: relative;
    inset: auto;
    margin: clamp(1.25rem, 4vw, 2rem) clamp(1.25rem, 5vw, 2.5rem) 0;
    width: auto;
    max-height: none;
  }

  /* The gap under the card belongs to `.locmap`, not to the section around it.
     `.locmap` is tinted and `.locmap-band` is not, so padding on the section leaves
     the tint ending exactly on the card's bottom border — the card reads as
     sitting on the seam between two sections rather than resting inside one.
     Held a little above the card's top margin: an optical bottom edge needs
     more air than a top one to read as the same distance. */
  /* Disabled with the distances card (commented out in index.html): with no
     card under the map, this padding is only a tinted strip below it.
  .locmap { padding-bottom: clamp(1.75rem, 6vw, 3rem); }

  .locmap-band { padding-bottom: clamp(1rem, 3vw, 1.75rem); }
  */
}

/* ==========================================================================
   Pelion destinations — the map, its roads, and the cards around it
   ========================================================================== */

/* The map is not a picture inside the section — it IS the section. `.pmap` is
   the box the svg covers, and its *height* is what sets the map's scale: the
   svg is `slice`-fitted to a viewBox far wider than any screen, so the scale
   is always height-derived (see `--pmap-cap`), and a wider box simply shows
   more open ground either side of the drawn peninsula rather than zooming in.

   Everything else is measured against the *stage* — `--pmap-stage`, the width
   the 1370×944 drawn map takes at that scale, centred in the box. The cards
   are positioned and sized in percentages of it, and the heading is placed
   against it. On any screen where the box is not height-capped, stage and box
   are the same width; on a wide, short one the stage is narrower, and the
   composition stays locked to the coastline while the box runs on to the
   viewport edge. */

.pelion {
  position: relative;
  padding: 0;
  background: var(--pebble);
  /* The map is never taller than the screen. Its natural height is the
     width × 944/1370 of the drawn map, which on a 27" monitor ran to a third
     more than the viewport: the heading at the top and the cards along the
     bottom could not be on screen together, and everything in between was
     drawn at a scale nothing else on the page reaches. Capping at the space
     under the fixed nav (5rem — the page's `scroll-padding-top`, so the
     "Pelion" link lands the whole map on screen) puts the scale in the hands
     of the viewport height instead of its width, which is what a landscape
     screen should do. The 46rem floor is where the stage below comes out at
     1067px — the cards' own boundary, where 12% of it meets their 8rem
     floor — so a short window stops shrinking the map exactly where the
     cards would stop shrinking with it and start to bunch. */
  --pmap-cap: max(calc(100vh - 5rem), 46rem);
  /* The stage width, derived from the same cap so it always agrees with the
     box height `.pmap` ends up with: `min(100%, cap × 1370/944)`. The `100%`
     resolves at the point of use, and the two places that use it —
     `.pmap__cards` (a child of `.pelion`) and `.pelion__head` (a child of
     `.pmap`) — both sit in a containing block the full section wide. */
  --pmap-stage: min(100%, calc(var(--pmap-cap) * 1370 / 944));
}

.pmap {
  position: relative;
  overflow: hidden;
  /* `width: 100%` must stay explicit: it makes the width definite, so the
     ratio computes the *height* and `max-height` then clamps only that. With
     an auto width the constraint transfers the other way and the box narrows
     instead — the trap the narrow layout below documents. */
  width: 100%;
  aspect-ratio: 1370 / 944;
  max-height: var(--pmap-cap);
  /* `.pelion` is a `.rise` now — it lifts over the dark guest-words panel
     above it — and its rounded shoulders would be painted over by this box
     otherwise, since a child clips to its own corners, not its parent's. */
  border-radius: inherit;
  /* Nothing inside paints outside this box — it already could not, given the
     overflow — so damage from the heading drifting across the map, or from a
     road drawing itself, cannot invalidate anything in the sections above and
     below. */
  contain: paint;
}

.pmap__svg {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
}

/* Dissolves all four map edges into the page colour, at the rendered edge
   rather than in viewBox space, so it holds however the map crops. The
   neighbouring sections are the same colour, so they read straight through. */
.pelion__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Extra stops ease each ramp. A plain two-stop gradient ramps its alpha
     linearly, which leaves a faint but readable line where the fade begins —
     exactly the seam this is here to get rid of. */
  background:
    /* The top ramp. The section is a `.rise` card set down on the dark
       guest-words panel, so its top edge is the card's own — this ramp is
       not dissolving into a neighbour any more, it is laying the plain
       `--pebble` ground the heading sits on, top-left, before the map
       emerges from under it. (It used to answer to whatever section sat
       above: the Google map once, then a heading-only `#location`, both
       gone.) Unconditional at every width. */
    linear-gradient(to bottom,
      var(--pebble) 0%,
      rgba(246, 245, 242, 0.82) 9%,
      rgba(246, 245, 242, 0.4) 17%,
      rgba(246, 245, 242, 0) 27%),
    linear-gradient(to bottom,
      rgba(246, 245, 242, 0) 74%,
      rgba(246, 245, 242, 0.3) 86%,
      rgba(246, 245, 242, 0.72) 94%,
      var(--pebble) 100%),
    linear-gradient(to right,
      var(--pebble) 0%,
      rgba(246, 245, 242, 0.5) 4%,
      rgba(246, 245, 242, 0) 10%,
      rgba(246, 245, 242, 0) 90%,
      rgba(246, 245, 242, 0.5) 96%,
      var(--pebble) 100%);
}

/* The heading sits in the north-west of the map, over the stretch of inland
   Pelion that carries no destination — the one quarter of the drawing with
   nothing in it. It used to stand centred in the band of open sea along the
   top, which put the map's only words directly above its busiest coast and
   pushed the whole drawing a heading's height further down the page.

   It is placed against the stage, not the box: `left` is 7% in from the
   stage's own left edge, so on a wide screen where the box runs past the
   drawn map the heading keeps its place over the coastline instead of
   sliding off to the viewport's corner. `top` can be a plain percentage,
   since the box's height *is* the stage's height. The two are the one
   element here that holds the drift, and the h2 inside it carries the
   reveal, because `.reveal.in` ends at `transform: none` and would wipe
   the drift off any element that held both. */
.pelion__head {
  position: absolute;
  z-index: 3;
  top: 13%;
  left: calc(50% - var(--pmap-stage) * 0.43);
  /* The heading races the page while the map underneath it stands still, so
     the two cross — the location window's gesture, carried across the join.
     The map itself must not move: `.pmap`'s ratio is what locks its scale
     against the cards, and translating a `slice`-fitted svg would uncover
     its edge.

     The distance is set by what is *below* the heading, not by taste. --plx
     is -1 as the section's top reaches the fold and ~0 as the heading leaves
     the top, so the whole visible pass runs in the negative half: the heading
     enters displaced downward by the full distance and rises to nothing. That
     entry dip is what has to clear the nearest place name, MAKRINITSA, whose
     label sits ~130 viewBox units under the heading's resting foot at the
     narrowest wide layout (1067px, where the map is smallest and the heading,
     in rem, largest in proportion) and further at every larger size. Nothing
     here scales with the sea band any more; the clamp simply keeps the dip
     under that gap at both ends. */
  --pelion-drift: clamp(1.4rem, 5.6vw, 4.5rem);
  transform: translate3d(0, calc(var(--plx, 0) * -1 * var(--pelion-drift)), 0);
  transition: transform var(--dur-drift) var(--ease-drift);
  /* Load-bearing, and the one --plx traveller that once lacked it: without a
     layer of its own the heading is repainted every frame *over the map*, and
     the map is a full-viewBox svg carrying a blur filter — so each pixel of
     drift re-rasterized the whole thing and the section scrolled visibly
     behind the page. Promoted, the move is a recomposite and the map below is
     never touched. */
  will-change: transform;
}

.pelion__title {
  max-width: 24rem;
  margin-bottom: 0;
  text-align: left;
}

/* Set on the wrapping group, not on the path, so the three <use> clones that
   draw the shadow can inherit a colour of their own — see the note in the
   markup. */
.pmap__land {
  fill: var(--pebble-deep);
  stroke: #232E2C;
  stroke-opacity: 0.14;
  stroke-width: 1.2;
}

/* The shadow that replaced #pmap-landshadow. Three copies of the coastline
   offset downward and stacked; the falloff is the stack, not a blur. */
.pmap__lift {
  fill: #232E2C;
  fill-opacity: 0.05;
  stroke: none;
}

.pmap__sea {
  fill: #8FA49D;
  font-family: var(--font-body);
  font-size: 16px;
  font-style: italic;
  letter-spacing: 4px;
}

.pmap__isle { fill: #A6A293; font-style: normal; }

.pmap__places {
  font-family: var(--font-body);
  font-size: 15px;
  letter-spacing: 3px;
  fill: #59625C;
  pointer-events: none;
}

/* ---- The roads ----------------------------------------------------------
   Each road is hidden until its destination wakes, then a mask sweeps along
   it (stroke-dashoffset 100 -> 0) so the dotted line draws itself outward
   from the villa. Turning off carries no transition, so it snaps back ready
   to be drawn again. */

.pmap__route path {
  stroke: var(--ink);
  stroke-width: 2.6;
  stroke-dasharray: 0.5 8.5;
  stroke-linecap: round;
  opacity: 0;
  transition: opacity 0.35s ease;
}

.pmap__route.is-on path { opacity: 1; }

.pmap__reveal {
  stroke-dasharray: 100 100;
  stroke-dashoffset: 100;
}

.pmap__reveal.is-on {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.5s cubic-bezier(0.45, 0, 0.25, 1);
}

.pmap__min {
  font-family: var(--font-body);
  font-size: 14px;
  font-style: italic;
  letter-spacing: 2px;
  fill: #4A544F;
  opacity: 0;
  transition: opacity 0.5s ease 0.5s;
}

.pmap__route.is-on .pmap__min { opacity: 1; }

/* ---- The villa, at the centre of it all ---- */

/* The one infinite animation on the site, and it sits *inside* the map's svg —
   where an animating child re-rasterises the svg around it, since there is no
   per-layer compositing to hide behind. So it runs only while `.is-live`: on
   screen, and the page not currently being scrolled. See main.js. */
.pmap__ping {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
}

.pelion.is-live .pmap__ping {
  animation: pmap-ping 3.2s ease-out infinite;
}

@keyframes pmap-ping {
  0%   { transform: scale(0.45); opacity: 0.8; }
  100% { transform: scale(1.5); opacity: 0; }
}

.pmap__homelabel {
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: 500;
  letter-spacing: 4px;
  fill: var(--ink);
}

/* ---- The pins ---- */

.pmap__pin { cursor: pointer; }

.pmap__hit { fill: rgba(0, 0, 0, 0); }

.pmap__disc {
  r: 15px;
  fill: rgba(246, 245, 242, 0.6);
  stroke: rgba(35, 46, 44, 0.45);
  stroke-width: 1.2;
  transition: r 0.35s ease, fill 0.35s ease, stroke 0.35s ease;
}

.pmap__pin.is-on .pmap__disc { r: 19px; fill: var(--ink); stroke: var(--ink); }

.pmap__glyph-ring {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.7;
  transition: stroke 0.35s ease;
}

.pmap__glyph-stem { fill: var(--ink); transition: fill 0.35s ease; }

.pmap__pin.is-on .pmap__glyph-ring { stroke: var(--pebble); }
.pmap__pin.is-on .pmap__glyph-stem { fill: var(--pebble); }

/* ---- The cards, scattered around the coastline ---- */

/* The stage: the same height as the box, the width of the drawn map at that
   height, centred — `inset: 0` pins both edges, the explicit width overrides
   them, and the auto margins settle the difference evenly. The cards are
   positioned and sized in percentages of this, so they hold their place on
   the coastline whether or not the box has run wider than the map. It is
   `.pelion`'s child rather than `.pmap`'s because a woken card lifts and
   scales past the map's edge, and `.pmap` clips.

   It is also the container the cards' type is measured against: the number
   on each card is sized in `cqw`, so it scales with the stage (which is to
   say with the map and the cards) rather than with the viewport, which on a
   wide, short screen is far wider than the stage. */
.pmap__cards {
  position: absolute;
  inset: 0;
  width: var(--pmap-stage);
  margin-inline: auto;
  container-type: inline-size;
  z-index: 2;
  pointer-events: none;
}

.pmap__card {
  position: absolute;
  z-index: 1;
  /* 12% of the stage is the proportion the scattered layout is composed at,
     and it is a percentage of the stage rather than of the viewport so the
     cards scale with the map and only the map: the two were briefly tied to
     different things (cards to `vw`, map to its own width, then to a cap
     the cards lacked) and came apart at one end of the range or the other
     every time. There is no ceiling — the stage is already bounded by the
     viewport height. The 8rem floor is not arbitrary: it is the width at
     which the longest name (Mylopotamos) still fits on one line and the head
     stays unwrapped. Below it the cards stop shrinking with the map — while
     the map keeps shortening — so they bunch and the names clip. That is
     what sets the breakpoint below. */
  width: max(8rem, 12%);
  aspect-ratio: 4 / 5;
  display: flex;
  flex-direction: column;
  border-radius: 16px;
  overflow: hidden;
  background: #EFEBE0;
  box-shadow: inset 0 0 0 1px rgba(35, 46, 44, 0.14), 0 20px 44px -24px rgba(35, 46, 44, 0.5);
  pointer-events: auto;
  cursor: pointer;
  transform-origin: 50% 85%;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.45s ease;
}

/* The cards deliberately overlap, so a woken one has to climb out from under
   its neighbours: z-index switches instantly (it cannot be transitioned) while
   the lift and the swelling shadow carry the movement. */
.pmap__card.is-on {
  z-index: 10;
  transform: translateY(-14px) scale(1.07);
  box-shadow: inset 0 0 0 2px var(--aegean), 0 30px 60px -22px rgba(35, 46, 44, 0.62);
}

.pmap__card--dark { background: var(--aegean-deep); }

.pmap__card--dark.is-on {
  box-shadow: inset 0 0 0 2px var(--pebble), 0 30px 60px -22px rgba(35, 46, 44, 0.62);
}

/* 0.8rem at the sides, not 0.9: at the 8rem floor the longer padding left
   99px for a name — MYLOPOTAMOS, at its tracking — that needs 101. */
.pmap__card-head {
  padding: 0.85rem 0.8rem 0.7rem;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.4rem 0.55rem;
}

.pmap__mins {
  font-family: var(--font-display);
  /* Of the stage, not the viewport — see `.pmap__cards`. */
  font-size: clamp(1.9rem, 2.5cqw, 2.5rem);
  line-height: 0.9;
  color: var(--ink);
}

.pmap__unit {
  padding-top: 0.2rem;
  font-size: 0.56rem;
  line-height: 1.3;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #6D746F;
}

.pmap__name {
  flex: 1 0 100%;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pmap__card--dark .pmap__mins,
.pmap__card--dark .pmap__name { color: var(--pebble); }
.pmap__card--dark .pmap__unit { color: rgba(246, 245, 242, 0.7); }

.pmap__card-photo { flex: 1; min-height: 0; }

.pmap__card-photo.is-empty::after {
  padding: 0.35rem 0.55rem;
  font-size: 0.55rem;
  letter-spacing: 0.08em;
  text-transform: none;
}

/* ---- Narrow: the map zooms in on its labels, the cards become a rail ---- */

@media (max-width: 1060px) {
  /* 12vw meets the cards' 8rem floor at 1067px, and that is exactly where the
     scattered layout stops holding: above it every width shows the same
     composition (cards 12% of the map, ~9% overlap, heads on one line); below
     it the cards freeze while the map keeps shortening, so they bunch into the
     lower left and Mylopotamos clips. So the switch waits for the boundary
     itself; it used to come at 1150px, ~90px early.

     The svg is `xMidYMid slice`, so it always covers this box, and the box's
     ratio alone decides how much of the viewBox shows: at 7/8 the scale is
     height-derived (boxW * 8/7 / 944), leaving ~826 viewBox units across at
     *every* width — the framing the 1.7x pins and the label nudges below are
     tuned for. Which is why the width has to stay 100%. Constraining the
     height instead, as a bare max-height on an auto-width box does, resolves
     the ratio the other way and shrinks the *width*: that is what once left
     the map 476px wide in an 854px viewport with dead space beside it. */
  .pmap {
    width: 100%;
    aspect-ratio: 7 / 8;
    /* Binds only on a short window — landscape, where the natural 8/7 height
       would run to two screens. Because the width is definite it costs zoom
       rather than width: the box stays full-bleed and the slice just shows
       more of the map. */
    max-height: 115vh;
  }

  .pmap__pin,
  .pmap__home {
    transform:
      translate(var(--px), var(--py)) scale(1.7)
      translate(calc(var(--px) * -1), calc(var(--py) * -1));
  }

  .pmap__shift { transform: translate(var(--dx), var(--dy)); }

  /* The map zooms to roughly 800 viewBox units wide here, so the labels come
     down a size and the offshore one drops out rather than being sliced. */
  .pmap__places { font-size: 20px; }
  .pmap__min { font-size: 24px; }
  .pmap__sea { font-size: 28px; }
  .pmap__isle { display: none; }
  .pmap__route path { stroke-width: 4.4; stroke-dasharray: 0.85 14.45; }

  /* The heading returns to the flow at the top of the map, centred, as the
     section's title rather than a label in the drawing: the north-west corner
     it sits in on a wide screen is ~105px of land on a phone once the veil's
     side ramp is taken off, not room for two lines of display type. */
  .pelion__head {
    position: relative;
    inset: auto;
    padding-top: clamp(2.5rem, 5vw, 4rem);
    text-align: center;
  }

  .pelion__title {
    margin-inline: auto;
    text-align: center;
  }

  .pmap__cards {
    position: static;
    width: auto;
    margin-inline: 0;
    z-index: auto;
    display: flex;
    gap: 0.7rem;
    overflow-x: auto;
    overflow-y: hidden;
    /* The scattered layout's `pointer-events: none` is a hole punched through
       a box that lies over the whole map — here the box lies over nothing and
       is a scroller, and a scroller that cannot be hit cannot be dragged. A
       touch landing in the gap between two cards, or on the padding at either
       end, fell straight through to the page and took the swipe with it. */
    pointer-events: auto;
    /* The strip has two ends; running past one must not hand the swipe to the
       page — on iOS that is Safari's back-navigation gesture. Same guard the
       photograph strip carries. */
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scroll-padding-left: 1.25rem;
    padding: 1.2rem clamp(1.25rem, 5vw, 2.5rem) clamp(2.5rem, 6vw, 3.5rem);
  }

  .pmap__card {
    position: static;
    flex: 0 0 auto;
    width: clamp(10rem, 38vw, 12rem);
    transform: none;
    aspect-ratio: 3 / 5;
    flex-direction: column-reverse;
    border-radius: 12px;
    scroll-snap-align: start;
    background: #EFEBE0;
    box-shadow: inset 0 0 0 1px rgba(35, 46, 44, 0.14), 0 14px 30px -18px rgba(35, 46, 44, 0.45);
  }

  .pmap__card.is-on,
  .pmap__card--dark.is-on {
    transform: translateY(-6px);
    box-shadow: inset 0 0 0 2px var(--aegean), 0 14px 30px -18px rgba(35, 46, 44, 0.45);
  }

  .pmap__card-head {
    background: #C8D0BE;
    padding: 0.7rem 0.6rem 0.85rem;
    justify-content: center;
    align-items: baseline;
    text-align: center;
    gap: 0.2rem 0.3rem;
  }

  .pmap__unit br { display: none; }

  .pmap__mins,
  .pmap__card--dark .pmap__mins,
  .pmap__unit,
  .pmap__card--dark .pmap__unit {
    padding-top: 0;
    font-family: var(--font-body);
    font-size: 0.62rem;
    line-height: 1.4;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: #4A544F;
  }

  .pmap__name,
  .pmap__card--dark .pmap__name {
    font-family: var(--font-display);
    font-size: 1rem;
    letter-spacing: 0.08em;
    color: var(--ink);
  }
}

@media (prefers-reduced-motion: reduce) {
  .pmap__ping { animation: none; opacity: 0; }
  .pmap__reveal,
  .pmap__reveal.is-on,
  .pmap__route path,
  .pmap__min,
  .pmap__disc,
  .pmap__glyph-ring,
  .pmap__glyph-stem,
  .pmap__card { transition: none; }
}

/* ==========================================================================
   Good to know
   ========================================================================== */

.know {
  margin: clamp(2rem, 4vw, 3rem) 0 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem) clamp(2rem, 4vw, 3.5rem);
}

.know__item {
  padding-top: 1rem;
  border-top: 1px solid var(--stone-soft);
}

.know__item dt {
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--stone);
  margin-bottom: 0.35rem;
}

.know__item dd {
  margin: 0;
  font-size: 1rem;
}

@media (max-width: 860px) {
  .know { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .know { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Book (dusk)
   ========================================================================== */

.section--dusk .wrap { text-align: center; }

.section--dusk .prose { margin-inline: auto; }

/* The enquiry widget sits on pebble, lifted off the dusk section behind it. */
.booking {
  max-width: 44rem;
  margin: 2.6rem auto 0;
  padding: clamp(1.1rem, 2.4vw, 1.8rem);
  background: var(--pebble);
  color: var(--aegean-deep);
  border-radius: 1.25rem;
  text-align: left;
  box-shadow: 0 46px 90px -54px rgba(0, 0, 0, 0.8);
}

@keyframes bookIn {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

.booking__panel { animation: bookIn 0.55s cubic-bezier(0.22, 1, 0.36, 1) both; }

/* A panel's own display rule would otherwise outrank the browser default for
   [hidden], leaving every step on screen at once. */
.booking__panel[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .booking__panel { animation: none; }
}

.booking__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 1.2rem;
  border-bottom: 1px solid var(--stone-soft);
}

.booking__head--dates { flex-wrap: wrap; align-items: flex-end; gap: 0.9rem; }

.booking__step {
  margin: 0;
  font-size: 0.74rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--stone);
}

.booking__step--gold { color: #A88B3F; }

.booking__months-nav { display: flex; gap: 0.45rem; }

.booking__arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.3rem;
  height: 2.3rem;
  border: 1px solid var(--stone-soft);
  border-radius: 999px;
  background: transparent;
  color: var(--aegean-deep);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.3s ease, border-color 0.3s ease;
}

.booking__arrow:hover:not(:disabled) {
  background: rgba(30, 43, 41, 0.06);
  border-color: var(--aegean-deep);
}

.booking__arrow:disabled { opacity: 0.35; cursor: default; }

.booking__months {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: clamp(1.1rem, 2.4vw, 2rem);
  padding: 1.3rem 0 0.2rem;
}

/* ---- One month ---- */
.cal__title {
  margin: 0 0 0.75rem;
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: 0.05em;
}

.cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  row-gap: 0.1rem;
}

.cal__wd {
  display: flex;
  align-items: center;
  justify-content: center;
  padding-bottom: 0.4rem;
  font-size: 0.58rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #A9A7A0;
}

.cal__blank { aspect-ratio: 1; }

.cal__day {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  font-family: inherit;
  font-size: 0.82rem;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
  transition: background 0.2s ease;
}

/* The hovered day is also the preview's end, and that has to win — the plain
   grey hover would otherwise sit where the range's edge is being shown. */
.cal__day:hover:not(.is-edge):not(.is-preview-end) { background: rgba(30, 43, 41, 0.08); }

.cal__day.is-past { color: #D2CFC7; cursor: default; }

/* Hatching reads as "unavailable" without relying on colour alone. */
.cal__day.is-taken {
  color: #B7B4AC;
  cursor: default;
  border-radius: 3px;
  background: repeating-linear-gradient(45deg,
    rgba(140, 138, 131, 0.28) 0 2px, transparent 2px 5px);
}

.cal__day.is-inrange {
  background: rgba(46, 90, 94, 0.13);
  border-radius: 0;
  color: var(--aegean-deep);
}

.cal__day.is-edge {
  background: var(--aegean-deep);
  color: var(--pebble);
  font-weight: 500;
}

/* The two edges are squared off towards the nights between them, so a chosen
   range reads as one continuous bar rather than two pills with a stripe. */
.cal__day.is-start { border-radius: 999px 3px 3px 999px; }
.cal__day.is-end { border-radius: 3px 999px 999px 3px; }
.cal__day.is-start.is-end { border-radius: 999px; }

/* The pointer's shadow of a range, while the checkout is still open. Lighter
   than .is-inrange on purpose: nothing here is chosen yet. */
.cal__day.is-preview {
  background: rgba(46, 90, 94, 0.08);
  border-radius: 0;
  color: var(--aegean-deep);
}

.cal__day.is-preview-end {
  background: rgba(46, 90, 94, 0.2);
  border-radius: 3px 999px 999px 3px;
  color: var(--aegean-deep);
}

/* ---- What has been chosen so far ---- */
.booking__picked {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1.1rem;
  /* Flush against .booking__head's own border-bottom, the pill's rounded
     corner met that straight line with no gap between them — the rule ran
     into the arc instead of stopping short of it. */
  margin: 1.3rem 0;
  padding: 0.85rem 1rem;
  border: 1px solid var(--stone-soft);
  border-radius: 8px;
  background: rgba(46, 90, 94, 0.045);
}

.booking__picked-slot { margin: 0; }

.booking__picked-label {
  display: block;
  font-size: 0.58rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--stone);
}

.booking__picked-date {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.95rem;
  color: var(--aegean-deep);
}

/* The half-made range says so, rather than showing a blank second slot. */
.booking__picked-date.is-waiting {
  color: var(--stone);
  font-style: italic;
}

.booking__picked-arrow { color: var(--stone); }

.booking__picked-clear { margin-left: auto; }

@media (max-width: 520px) {
  .booking__picked-clear { margin-left: 0; }
}

/* The form panel takes focus when it opens (see showForm in main.js); it is a
   container, not a control, so it must not draw a ring for it. */
.booking__panel:focus { outline: none; }

.booking__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.9rem;
  margin-top: 1.4rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--stone-soft);
  font-size: 0.82rem;
  color: var(--stone);
}

.booking__legend { display: flex; align-items: center; gap: 0.5rem; }

.booking__swatch {
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 3px;
  background: repeating-linear-gradient(45deg,
    rgba(140, 138, 131, 0.35) 0 2px, transparent 2px 5px);
}

.booking__error {
  margin: 0.9rem 0 0;
  font-size: 0.85rem;
  color: #8A5A3B;
}

.booking__error a {
  color: inherit;
  text-underline-offset: 0.2em;
}

/* The honeypot. Taken out of the page rather than `display: none`, which the
   better bots know to skip; a visitor never reaches it because it is also
   out of the tab order and hidden from the accessibility tree. */
.booking__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* The button says what it is doing while the enquiry is in flight, and cannot
   be pressed twice into two emails. */
.booking__submit .btn[aria-busy="true"] {
  opacity: 0.7;
  pointer-events: none;
}

/* ---- Chosen dates, and the form ---- */
.booking__range {
  margin: 0.35rem 0 0;
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2.6vw, 1.7rem);
  line-height: 1.2;
}

.booking__nights { margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--stone); }

.booking__link {
  padding: 0 0 0.2rem;
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(46, 90, 94, 0.45);
  color: var(--aegean);
  font-family: inherit;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.3s ease, border-color 0.3s ease;
}

.booking__link:hover { color: var(--aegean-deep); border-color: var(--aegean-deep); }

.booking__form { display: grid; gap: 1.5rem; padding-top: 1.6rem; }

.booking__fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
  gap: 1.5rem;
}

.field { display: grid; gap: 0.45rem; }

/* There are six fields and no special case among them: at three columns they
   fill two rows exactly, at two columns three rows, at one column they stack.
   Phone used to carry a `--wide` modifier spanning it to the end of the row,
   which only worked while there were four ordinary fields — the fifth (last
   name) pushed it onto a row of its own and left a hole beside it. */

.field__label {
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--stone);
}

/* Underline-only inputs, so the form reads as writing on paper. */
.field__input,
.field__select select {
  width: 100%;
  padding: 0.45rem 0 0.55rem;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--stone-soft);
  border-radius: 0;
  color: var(--aegean-deep);
  font-family: inherit;
  font-size: 1rem;
  outline: none;
}

.field__input:focus,
.field__select select:focus { border-bottom-color: var(--aegean-deep); }

.field__select { position: relative; display: block; }

.field__select select {
  appearance: none;
  padding-right: 1.4rem;
  cursor: pointer;
}

.field__select::after {
  content: "▾";
  position: absolute;
  right: 0.2rem;
  bottom: 0.5rem;
  color: var(--stone);
  font-size: 0.7rem;
  pointer-events: none;
}

/* The dialling code sits beside the number on the same underline, but is not
   itself an input — it is set from the chosen country and nothing here can
   edit it. Sharing one underline (rather than giving the code its own field)
   reads as one answer, "your phone number", not two related ones. */
.field__tel {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  border-bottom: 1px solid var(--stone-soft);
}

.field__tel:focus-within { border-bottom-color: var(--aegean-deep); }

.field__dial {
  flex: none;
  padding: 0.45rem 0 0.55rem;
  color: var(--stone);
  font-variant-numeric: tabular-nums;
}

.field__tel-input {
  flex: 1;
  min-width: 0;
  border-bottom: 0;
}

.field__tel:focus-within .field__dial { color: var(--aegean-deep); }

.consent {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  font-size: 0.85rem;
  color: #6E6C66;
  cursor: pointer;
}

.consent input {
  width: 1.05rem;
  height: 1.05rem;
  margin-top: 0.28rem;
  accent-color: var(--aegean);
  flex: none;
  cursor: pointer;
}

.consent a { color: var(--aegean); border-bottom: 1px solid rgba(46, 90, 94, 0.4); }

.booking__submit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.2rem;
}

.booking__reassure { font-size: 0.82rem; color: var(--stone); }

.btn--dark {
  background: var(--aegean-deep);
  color: var(--pebble);
  border: 0;
}

.btn--dark:hover { background: var(--aegean); transform: translateY(-2px); }

.btn--ghost {
  border: 1px solid var(--stone-soft);
  color: var(--aegean-deep);
  background: transparent;
}

.btn--ghost:hover { background: rgba(30, 43, 41, 0.05); border-color: var(--aegean-deep); }

/* ---- Sent ---- */
.booking__panel--sent {
  display: grid;
  gap: 0.9rem;
  padding: clamp(0.4rem, 2vw, 1.2rem) 0;
}

.booking__thanks {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 3vw, 2rem);
  line-height: 1.2;
}

.booking__summary { margin: 0; max-width: 34rem; color: #6E6C66; }

.booking__after {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.1rem;
  margin-top: 0.6rem;
}

.booking__note { margin: 0; font-size: 0.9rem; color: var(--stone); }

/* ==========================================================================
   Footer
   ========================================================================== */

/* Pale and quiet, per the Claude Design file: the same ground as the page,
   separated by a hairline rather than by a dark block. */
.footer {
  background: var(--pebble);
  color: var(--stone);
  padding: clamp(2.8rem, 6vw, 4rem) 0;
  border-top: 1px solid var(--stone-soft);
}

.footer__inner {
  max-width: var(--wrap-wide);
  display: grid;
  gap: 0.35rem;
}

.footer__name {
  font-family: var(--font-display);
  font-size: 1.15rem;
  letter-spacing: 0.06em;
  color: var(--ink);
}

.footer__inner p { font-size: 0.92rem; }

.footer__fine {
  margin-top: 0.6rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem !important;
  letter-spacing: 0.04em;
  color: color-mix(in srgb, var(--stone) 72%, var(--pebble));
}

.footer__link {
  color: var(--stone);
  border-bottom: 1px solid var(--stone-soft);
  text-decoration: none;
  transition: color 0.3s ease, border-color 0.3s ease;
}

.footer__link:hover {
  color: var(--ink);
  border-bottom-color: var(--stone);
}

/* ==========================================================================
   Floating WhatsApp button
   ========================================================================== */

.wa-float {
  position: fixed;
  right: clamp(1rem, 3vw, 1.8rem);
  bottom: clamp(1rem, 3vw, 1.8rem);
  z-index: 30;
  height: 3.4rem;
  padding: 0 0.95rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--aegean);
  color: var(--pebble);
  border-radius: 999px;
  box-shadow: 0 10px 26px -10px rgba(30, 43, 41, 0.55);
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), background 0.3s ease;
}

.wa-float svg { width: 1.5rem; height: 1.5rem; flex: none; }

/* The number unfurls on hover — closed, the button stays a circle. */
.wa-float__number {
  display: block;
  max-width: 0;
  margin-left: 0;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  transition: max-width 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.35s ease, margin-left 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}

.wa-float:hover .wa-float__number,
.wa-float:focus-visible .wa-float__number {
  max-width: 12rem;
  opacity: 1;
  margin-left: 0.6rem;
}

@media (prefers-reduced-motion: reduce) {
  .wa-float__number { transition: none; }
}

.wa-float:hover {
  background: #37686C;
  transform: translateY(-3px);
}

/* ==========================================================================
   Legal pages — privacy, and anything else set in plain prose
   ========================================================================== */

.legal { background: var(--pebble); }

.legal__head { border-bottom: 1px solid var(--stone-soft); }

.legal__head-inner {
  max-width: 48rem;
  margin-inline: auto;
  padding: clamp(1rem, 2.5vw, 1.5rem) clamp(1.25rem, 5vw, 2.5rem);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.legal__brand {
  display: flex;
  text-decoration: none;
  align-items: center;
  gap: 0.6rem;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 1.05rem;
  letter-spacing: 0.12em;
}

.legal__brand svg { width: 1.35rem; height: 1.35rem; color: var(--aegean); }

.legal__back {
  text-decoration: none;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--stone);
  transition: color 0.3s ease;
}

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

.legal__main {
  max-width: 48rem;
  margin-inline: auto;
  padding: clamp(3rem, 8vw, 5.5rem) clamp(1.25rem, 5vw, 2.5rem) clamp(4rem, 9vw, 6rem);
}

.eyebrow--legal { color: #A88B3F; }

.legal__title {
  margin: 0 0 1rem;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2rem, 5vw, 3rem);
  line-height: 1.15;
}

.legal__intro {
  margin: 0 0 3rem;
  max-width: 34rem;
  color: #6E6C66;
}

.legal__section {
  display: grid;
  gap: 0.6rem;
  padding: 1.8rem 0;
  border-top: 1px solid var(--stone-soft);
}

.legal__section--last { border-bottom: 1px solid var(--stone-soft); }

.legal__section h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.35rem;
}

.legal__section p { margin: 0; }

.legal__section a {
  color: var(--aegean);
  border-bottom: 1px solid rgba(46, 90, 94, 0.4);
}

.legal__cta { margin-top: 2.5rem; }
