/* =============================================================================
   ZOEI — one stylesheet for the whole site.

   Order: fonts · tokens · reset · typography · layout · buttons · header ·
   sticky CTA · footer · hero · components · forms · lightbox · utilities.

   There is no build step and no preprocessor. What is here is what ships.

   Cache busting is the ?v= on every <link> — bump it in all nine pages AND in
   worker/index.js when this file changes, or returning visitors keep the old
   copy for a year. See README, "Changing the stylesheet or the script".
   ========================================================================== */

/* ------------------------------------------------------------------ fonts */
/* Self-hosted, latin only. The latin range is U+0000-00FF, which covers every
   accented character in Latin-1 — latin-ext is for Eastern European and this
   site has no copy that needs it. Dropping it saved 47KB.

   Both are variable, weight only. Playfair Display has no optical size axis,
   so its contrast is fixed rather than thinning out as the display sizes grow.
   Its hairlines are sturdier than a true didone's to begin with, which is what
   makes it hold together at the smaller headings — but it also means the very
   large sizes need a touch of negative tracking to stop them looking loose.
   See --fs-h1 and .display below. */

@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('/assets/fonts/playfair-display-latin.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/archivo-latin.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ----------------------------------------------------------------- tokens */
/* The palette is taken from the house itself rather than invented: limestone
   and travertine walls, walnut joinery, the deep wine bouclé in the living
   room, and the warm near-black of the shadows.

   Every ratio below was measured against the ground the token actually sits
   on, not estimated. They are not decoration: a token marked "never text" may
   only tint a rule, a border or a fill. */

:root {
  --bone: #f6f2ea;        /* page ground */
  --limestone: #e4dcce;   /* light band */
  --travertine: #c9bda9;  /* NEVER text on a light ground — 1.5:1 on bone.
                             Only used for text as a bullet fill on --ink, 9.0:1 */
  --walnut: #6e5138;      /* eyebrows + secondary — 6.5:1 bone, 5.3:1 limestone */
  --wine: #5c2030;        /* primary action — 11.0:1 on bone */
  --wine-deep: #471825;   /* wine, pressed */
  --ink: #221d19;         /* headings + body — 15.0:1 bone, 12.3:1 limestone */
  --white: #ffffff;       /* on --wine, 12.3:1 */

  /* Measured, not guessed. --ink-quiet and --ink-soft are both checked against
     the two grounds they ever sit on: --bone and --limestone. --ink-soft is the
     lightest value that still clears AA on limestone, which is why it is 0.65
     and not the 0.44 it started as — that failed at 2.69:1 and was being used
     for the "optional" markers and the section ordinals, which are text. */
  --ink-quiet: rgba(34, 29, 25, 0.68);  /* quiet text — 5.5:1 bone, 5.0:1 limestone */
  --ink-soft:  rgba(34, 29, 25, 0.65);  /* small text — 5.0:1 bone, 4.6:1 limestone */
  --ink-faint: rgba(34, 29, 25, 0.44);  /* NEVER text — rules and borders only, 2.7:1 */
  --ink-hair: rgba(34, 29, 25, 0.14);   /* hairlines */

  --on-dark: rgba(255, 255, 255, 0.94);
  --on-dark-quiet: rgba(255, 255, 255, 0.74);  /* quiet text on ink — 9.6:1 */
  --on-dark-hair: rgba(255, 255, 255, 0.24);

  --font-display: 'Playfair Display', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
  --font-body: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;

  /* Display sizes run large — a high-contrast serif at 18px is a liability and
     at 80px is the entire identity. Body measure comes down to compensate.
     Playfair Display carries a larger x-height than the Bodoni this replaced,
     so it reads a size bigger at the same value; h1 and h2 came down slightly
     to land in the same place optically. */
  --fs-h1: clamp(2.6rem, 1.6rem + 4.35vw, 5.35rem);
  --fs-h2: clamp(2.05rem, 1.45rem + 2.7vw, 3.75rem);
  --fs-h2-sm: clamp(1.6rem, 1.3rem + 1.4vw, 2.35rem);
  --fs-h3: clamp(1.2rem, 1.1rem + 0.45vw, 1.5rem);
  --fs-body: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --fs-lede: clamp(1.15rem, 1.03rem + 0.52vw, 1.5rem);
  --fs-small: 0.9375rem;
  --fs-micro: 0.75rem;

  --track-wide: 0.18em;   /* nav, eyebrows, buttons */
  --track-wordmark: 0.3em;

  --wrap: 1180px;
  --wrap-wide: 1520px;
  --wrap-narrow: 720px;
  --gutter: clamp(1.25rem, 0.6rem + 2.6vw, 3.5rem);
  --section-y: clamp(4.5rem, 2.5rem + 7.5vw, 10rem);

  --radius: 2px;          /* almost square — a rounded corner reads friendly, not expensive */
  --header-h: 72px;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* ------------------------------------------------------------------ reset */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchors land below the fixed header rather than under it. */
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  margin: 0;
  background: var(--bone);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

img { height: auto; }

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl { margin: 0; }
ul, ol { padding: 0; list-style: none; }

/* A fieldset draws a border and notches the legend into it by default, which
   is the one piece of browser chrome the underlined form cannot live with.
   min-width: 0 stops a fieldset refusing to shrink below its content — the
   long-standing flex/grid bug that makes date inputs overflow on a phone. */
fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
legend { padding: 0; }

a { color: inherit; text-decoration: none; }

button,
input,
select,
textarea { font: inherit; color: inherit; }

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

/* Never a bare `outline: none` — this only removes the ring for pointer users,
   where :focus-visible already withholds it. */
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 200;
  padding: 0.85rem 1.25rem;
  background: var(--ink);
  color: var(--on-dark);
  font-size: var(--fs-small);
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ------------------------------------------------------------- typography */

.display {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.04;
  letter-spacing: -0.012em;
  text-wrap: balance;
}

h1.display { font-size: var(--fs-h1); }
h2.display { font-size: var(--fs-h2); }

.display--sm { font-size: var(--fs-h2-sm); line-height: 1.12; }

/* Uppercase, tracked, small. Eyebrows, nav, buttons, labels — everything that
   is a signpost rather than a sentence. */
.eyebrow {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--walnut);
  margin-bottom: 1.5rem;
}

.eyebrow--on-dark { color: var(--on-dark-quiet); }

.wordmark {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.4rem;
  letter-spacing: var(--track-wordmark);
  /* The tracking adds space after the final letter too, which throws the
     wordmark visually left of centre in any flex row. */
  text-indent: var(--track-wordmark);
  text-transform: uppercase;
  line-height: 1;
}

.section__title { margin-bottom: 1.5rem; }

.section__lede {
  font-size: var(--fs-lede);
  line-height: 1.5;
  color: var(--ink-quiet);
  max-width: 46ch;
  text-wrap: pretty;
}

.section__lede--plain { font-size: var(--fs-body); }
.center .section__lede { margin-inline: auto; }

.section__head { margin-bottom: clamp(2.5rem, 1.5rem + 3vw, 4.5rem); }
.section__actions { margin-top: clamp(2rem, 1.4rem + 2vw, 3rem); }

.rule {
  border: 0;
  border-top: 1px solid var(--ink-hair);
  margin: clamp(2.5rem, 1.5rem + 3vw, 4rem) 0;
}

.prose p + p { margin-top: 1.35em; }
.prose { max-width: 62ch; color: var(--ink-quiet); text-wrap: pretty; }
.prose strong { color: var(--ink); font-weight: 500; }

/* A paragraph set as a series of short lines, as the copy is written. Each
   line is its own <p>, so this only tightens the space between them. */
.stack p + p { margin-top: 0.35em; }

/* Long-form pages — privacy, terms. Subheadings inside running copy, which
   nothing else on the site needs. */
.legal { max-width: 68ch; }
.legal h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: 1.25;
  color: var(--ink);
  margin-top: 2.75em;
  margin-bottom: 0.75em;
}
.legal h2:first-of-type { margin-top: 2em; }
.legal .ticks { margin-block: 1.35em; }
.legal a { border-bottom: 1px solid var(--ink-faint); }
.legal a:hover { border-bottom-color: var(--wine); color: var(--wine); }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  border-bottom: 1px solid currentColor;
  padding-bottom: 0.35em;
  transition: gap 0.25s var(--ease);
}
.link-arrow:hover { gap: 0.9em; }

/* ----------------------------------------------------------------- layout */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--wide { max-width: var(--wrap-wide); }
.wrap--narrow { max-width: var(--wrap-narrow); }

.section { padding-block: var(--section-y); }
.section--tight { padding-block: calc(var(--section-y) * 0.55); }
.section--flush-top { padding-top: 0; }

.band--limestone { background: var(--limestone); }
.band--ink { background: var(--ink); color: var(--on-dark); }
.band--wine { background: var(--wine); color: var(--on-dark); }

.band--ink .section__lede,
.band--wine .section__lede,
.band--ink .prose,
.band--wine .prose { color: var(--on-dark-quiet); }

.band--ink .eyebrow,
.band--wine .eyebrow { color: var(--on-dark-quiet); }

.band--ink .rule,
.band--wine .rule { border-color: var(--on-dark-hair); }

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  padding: 1.05em 2.2em;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--wine);
  color: var(--on-dark);
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  /* NOT line-height: 1. Uppercase at 0.18em tracking makes these labels far
     wider than they look, and the moment one wraps — "Explore Poolside Luxe"
     in a three-up card does so below about 1275px — a unit line-height stacks
     the two lines on top of each other inside a box that never grows.
     text-wrap: balance splits a wrapped label evenly instead of leaving one
     orphaned word. */
  line-height: 1.25;
  text-align: center;
  text-wrap: balance;
  cursor: pointer;
  transition: background-color 0.25s var(--ease), color 0.25s var(--ease),
    border-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.btn:hover { background: var(--wine-deep); transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn[disabled] { opacity: 0.55; cursor: not-allowed; transform: none; }

.btn--dark { background: var(--ink); }
.btn--dark:hover { background: #100d0b; }

.btn--ghost {
  background: transparent;
  border-color: var(--ink-faint);
  color: var(--ink);
}
.btn--ghost:hover { background: var(--ink); border-color: var(--ink); color: var(--on-dark); }

/* Over a photograph or an ink band. */
.btn--on-dark {
  background: transparent;
  border-color: rgba(255, 255, 255, 0.5);
  color: var(--on-dark);
}
.btn--on-dark:hover { background: var(--white); border-color: var(--white); color: var(--ink); }

.btn--light { background: var(--bone); color: var(--ink); }
.btn--light:hover { background: var(--white); }

.btn--sm { padding: 0.85em 1.6em; }

/* A full-width button does not need generous side padding — it is already as
   wide as its column — and giving it back buys roughly 30px of label before
   anything wraps. */
.btn--block { display: flex; width: 100%; padding-inline: 1rem; }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  align-items: center;
}
.center .btn-row { justify-content: center; }

/* ----------------------------------------------------------------- header */
/* Fixed, and transparent while the hero is still on screen so the photograph
   runs edge to edge behind it. `show-sticky-cta` is set by main.js once the
   hero's own button has scrolled away, at which point the header takes a solid
   ground and dark text.

   With JavaScript off that class never arrives, so `html:not(.js)` below makes
   the header solid from the start. A transparent header stuck over white
   limestone would be unreadable, and that is not an acceptable fallback. */

.site-header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  color: var(--on-dark);
  transition: background-color 0.35s var(--ease), color 0.35s var(--ease),
    border-color 0.35s var(--ease);
  border-bottom: 1px solid transparent;
}

/* Keeps the wordmark and nav legible against a bright sky or a pale wall,
   without darkening the whole photograph. */
.site-header::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(20, 16, 14, 0.5), rgba(20, 16, 14, 0));
  pointer-events: none;
  transition: opacity 0.35s var(--ease);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  width: 100%;
  position: relative;
  z-index: 1;
}

.header__brand { display: flex; align-items: center; }

.site-nav ul {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 0.4rem + 1.5vw, 2.25rem);
}

.site-nav a {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  padding-block: 0.5rem;
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s var(--ease), opacity 0.25s var(--ease);
}
.site-nav a:hover { border-bottom-color: currentColor; }
.site-nav a[aria-current='page'] { border-bottom-color: currentColor; }

/* The drawer's own Enquire link. Redundant on desktop, where .header__cta is
   the button beside the burger — but that button is hidden below 64rem, and
   without this the drawer had no route to the form at all. */
.site-nav__enquire { display: none; }

.header__right { display: flex; align-items: center; gap: 1.25rem; }

.header__ig {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  opacity: 0.85;
  transition: opacity 0.25s var(--ease);
}
.header__ig:hover { opacity: 1; }
.header__ig svg { width: 18px; height: 18px; fill: currentColor; }

/* The header's own enquiry button. Always on — the header is wide enough to
   carry it, and a permanent route to the form is worth more than avoiding a
   duplicate call to action in the first viewport, which is what it used to
   fade in to sidestep.

   It sits over the hero photograph before the header takes its solid ground,
   so it stays solid wine throughout rather than inheriting the header's
   transparent treatment — white on wine is 12.3:1 against either backdrop. */
.header__cta { flex: none; }

/* Scrolled state — solid, dark text. */
.show-sticky-cta .site-header,
html:not(.js) .site-header {
  background: var(--bone);
  color: var(--ink);
  border-bottom-color: var(--ink-hair);
}
.show-sticky-cta .site-header::before,
html:not(.js) .site-header::before { opacity: 0; }

/* Pages whose hero is not a photograph start solid and stay solid. */
.has-light-hero .site-header {
  background: var(--bone);
  color: var(--ink);
  border-bottom-color: var(--ink-hair);
}
.has-light-hero .site-header::before { opacity: 0; }

/* -------------------------------------------------------- header: drawer */

.nav-toggle {
  display: none;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  position: relative;
}
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  content: '';
  position: absolute;
  left: 50%;
  width: 22px;
  height: 1px;
  margin-left: -11px;
  background: currentColor;
  transition: transform 0.3s var(--ease), opacity 0.2s var(--ease);
}
.nav-toggle span { top: 50%; }
.nav-toggle span::before { margin-left: 0; left: 0; top: -6px; }
.nav-toggle span::after { margin-left: 0; left: 0; top: 6px; }

.nav-open .nav-toggle span { background: transparent; }
.nav-open .nav-toggle span::before { transform: translateY(6px) rotate(45deg); }
.nav-open .nav-toggle span::after { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 64rem) {
  .nav-toggle { display: block; }

  .site-nav {
    position: fixed;
    inset: var(--header-h) 0 auto;
    background: var(--bone);
    color: var(--ink);
    border-bottom: 1px solid var(--ink-hair);
    padding: 2rem var(--gutter) 2.5rem;
    transform: translateY(-8px);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.28s var(--ease), transform 0.28s var(--ease), visibility 0.28s;
  }

  .nav-open .site-nav { opacity: 1; visibility: visible; transform: none; }

  .site-nav ul { flex-direction: column; align-items: flex-start; gap: 0; }
  .site-nav li { width: 100%; border-bottom: 1px solid var(--ink-hair); }
  .site-nav li:last-child { border-bottom: 0; }
  .site-nav a { display: block; padding: 1.1rem 0; font-size: var(--fs-small); }

  /* Carries the wine of the primary button without being a button, so it reads
     as the end of the list rather than a sixth equal option. */
  .site-nav__enquire { display: block; }
  .site-nav__enquire a { color: var(--wine); }

  /* Two calls to action side by side in a 375px header is one too many. */
  .header__cta { display: none; }

  /* Without JavaScript the drawer cannot open, so the nav is shown inline
     underneath the wordmark instead — long, but reachable, which a button that
     does nothing is not. */
  html:not(.js) .site-nav {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    padding: 0;
    border: 0;
  }
  html:not(.js) .site-header { height: auto; padding-block: 1rem; }
  html:not(.js) .header__inner { flex-direction: column; align-items: flex-start; }
  html:not(.js) .nav-toggle { display: none; }
}

/* ------------------------------------------------------------- sticky CTA */
/* The phone equivalent of the header button: a price and a way to act, pinned
   once the hero has gone. The price is on it deliberately — it is the question
   every visitor has, and answering it early is what separates this from the
   location sites that make you enquire to find out. */

.mobile-cta {
  position: fixed;
  inset: auto 0 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem var(--gutter);
  padding-bottom: calc(0.75rem + env(safe-area-inset-bottom));
  background: var(--ink);
  color: var(--on-dark);
  transform: translateY(100%);
  transition: transform 0.35s var(--ease);
}
.show-sticky-cta .mobile-cta { transform: none; }

.mobile-cta__price {
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--on-dark-quiet);
}

@media (min-width: 64.0625rem) {
  .mobile-cta { display: none; }
}

/* No JavaScript means no scroll class, so it would sit over the footer for
   ever. The header nav carries the same link. */
html:not(.js) .mobile-cta { display: none; }

/* ----------------------------------------------------------------- footer */

.site-footer {
  background: var(--ink);
  color: var(--on-dark);
  padding-block: clamp(3.5rem, 2rem + 5vw, 6rem) 2.5rem;
}

.footer__top {
  display: grid;
  gap: clamp(2.5rem, 1rem + 4vw, 4rem);
  grid-template-columns: 1fr;
  padding-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem);
  border-bottom: 1px solid var(--on-dark-hair);
}

@media (min-width: 48rem) {
  .footer__top { grid-template-columns: 1.6fr 1fr 1fr 1fr; }
}

.footer__blurb { color: var(--on-dark-quiet); max-width: 34ch; margin-top: 1.5rem; }
.footer__tags {
  margin-top: 1rem;
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

.footer__heading {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  margin-bottom: 1.25rem;
}

.footer__list li + li { margin-top: 0.7rem; }
.footer__list a { color: var(--on-dark-quiet); transition: color 0.2s var(--ease); }
.footer__list a:hover { color: var(--on-dark); }

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  align-items: center;
  justify-content: space-between;
  padding-top: 2.5rem;
  font-size: var(--fs-small);
  color: rgba(255, 255, 255, 0.55);
}
.footer__bottom a:hover { color: var(--on-dark); }
.footer__legal { display: flex; flex-wrap: wrap; gap: 1.5rem; }

/* ------------------------------------------------------------------- hero */

.hero {
  position: relative;
  min-height: min(92svh, 900px);
  display: flex;
  align-items: flex-end;
  padding-top: var(--header-h);
  padding-bottom: clamp(3rem, 2rem + 4vw, 6rem);
  color: var(--on-dark);
  isolation: isolate;
  overflow: hidden;
}

.hero--short { min-height: min(72svh, 700px); }

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.hero__media img { width: 100%; height: 100%; object-fit: cover; }

/* A hero crops hard at every viewport, and centring the crop is only right
   when the subject is centred. These name where the subject actually is, so a
   short viewport keeps it in frame rather than cutting a head off. */
.hero__media--top img { object-position: 50% 22%; }
.hero__media--bottom img { object-position: 50% 78%; }

/* Weighted to the bottom, where the copy is, so the top of the photograph
   keeps its own light. */
.hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(20, 16, 14, 0.78) 0%,
    rgba(20, 16, 14, 0.42) 38%,
    rgba(20, 16, 14, 0.12) 68%,
    rgba(20, 16, 14, 0.28) 100%
  );
}

.hero__inner { position: relative; z-index: 1; width: 100%; }
.hero__title { max-width: 16ch; }
.hero__lede {
  margin-top: 1.75rem;
  font-size: var(--fs-lede);
  line-height: 1.5;
  color: var(--on-dark);
  max-width: 44ch;
  text-wrap: pretty;
}
.hero .btn-row { margin-top: clamp(2rem, 1.4rem + 2vw, 2.75rem); }

.hero__meta {
  margin-top: 1.75rem;
  font-size: var(--fs-small);
  color: var(--on-dark-quiet);
}

/* A hero with no photograph, for the inner pages that open on words. */
.hero--plain {
  min-height: 0;
  color: var(--ink);
  padding-top: calc(var(--header-h) + clamp(3.5rem, 2rem + 6vw, 8rem));
  padding-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem);
  align-items: flex-start;
}
.hero--plain .hero__lede { color: var(--ink-quiet); }

/* ------------------------------------------------------------- statement */
/* A single large sentence on its own, used to change the pace between two
   dense sections. */

.statement { text-align: center; }
.statement .display { max-width: 20ch; margin-inline: auto; }

/* ------------------------------------------------------- two-column split */
/* Sticky photograph, scrolling copy. CSS only — position: sticky does all of
   it and there is no JavaScript involved. */

.split {
  display: grid;
  gap: clamp(2.5rem, 1.5rem + 4vw, 5rem);
  align-items: start;
}

@media (min-width: 56rem) {
  .split { grid-template-columns: 1fr 1fr; gap: clamp(3rem, 1rem + 6vw, 7rem); }
  .split--media-right .split__media { order: 2; }
  .split__media { position: sticky; top: calc(var(--header-h) + 3rem); }
}

.split__media img { width: 100%; }

/* --------------------------------------------------------------- the list */
/* "At ZOEI you'll find" — twelve short items. A plain list, but set in two or
   three columns with a hairline between so it reads as an inventory rather
   than a paragraph broken up. */

.inventory {
  display: grid;
  gap: 0;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--ink-hair);
}
@media (min-width: 40rem) { .inventory { grid-template-columns: 1fr 1fr; } }
@media (min-width: 64rem) { .inventory { grid-template-columns: repeat(3, 1fr); } }

.inventory li {
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--ink-hair);
  font-size: var(--fs-small);
  display: flex;
  gap: 1rem;
  align-items: baseline;
}

/* Column rules on the inner edges only. */
@media (min-width: 40rem) {
  .inventory li { padding-inline: 1.5rem; }
  .inventory li:nth-child(odd) { padding-left: 0; border-right: 1px solid var(--ink-hair); }
  .inventory li:nth-child(even) { padding-right: 0; }
}
@media (min-width: 64rem) {
  .inventory li:nth-child(odd),
  .inventory li:nth-child(even) { padding-inline: 1.5rem; border-right: 1px solid var(--ink-hair); }
  .inventory li:nth-child(3n + 1) { padding-left: 0; }
  .inventory li:nth-child(3n) { padding-right: 0; border-right: 0; }
}

.inventory li::before {
  content: counter(inventory, decimal-leading-zero);
  counter-increment: inventory;
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.inventory { counter-reset: inventory; }

/* ------------------------------------------------------------------ items */
/* "Shoot it at ZOEI" and "More from one shoot" — a title and a paragraph,
   repeated. Numbered, because a numbered list of seven reads as a considered
   range and a bulleted one reads as a pile. */

.items {
  display: grid;
  gap: clamp(2rem, 1rem + 3vw, 3.5rem);
  grid-template-columns: 1fr;
}
@media (min-width: 44rem) { .items { grid-template-columns: 1fr 1fr; } }
@media (min-width: 68rem) { .items--three { grid-template-columns: repeat(3, 1fr); } }

.item { border-top: 1px solid var(--ink-hair); padding-top: 1.5rem; }
.band--ink .item { border-top-color: var(--on-dark-hair); }

.item__no {
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  display: block;
  margin-bottom: 1rem;
}
.band--ink .item__no { color: rgba(255, 255, 255, 0.55); }

.item__title {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  margin-bottom: 0.9rem;
}

.item p { color: var(--ink-quiet); font-size: var(--fs-small); text-wrap: pretty; }
.band--ink .item p { color: var(--on-dark-quiet); }

/* ------------------------------------------------------------- the spaces */
/* The Location page: seven blocks, each a wide photograph with the copy set
   beside it, alternating side. */

.space + .space { margin-top: clamp(4rem, 2rem + 7vw, 9rem); }

.space__grid {
  display: grid;
  gap: clamp(1.75rem, 1rem + 3vw, 3.5rem);
  align-items: center;
}
@media (min-width: 56rem) {
  .space__grid { grid-template-columns: 1.35fr 1fr; }
  .space--flip .space__lead { order: 2; }
}

.space__lead img { width: 100%; }

.space__no {
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  margin-bottom: 1rem;
}

.space__body p { color: var(--ink-quiet); text-wrap: pretty; }
.space__body p + p { margin-top: 1rem; }

/* The supporting shots under each block. */
.space__strip {
  margin-top: clamp(1.25rem, 0.8rem + 1.5vw, 2rem);
  display: grid;
  gap: clamp(0.5rem, 0.3rem + 0.8vw, 1rem);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 44rem) { .space__strip { grid-template-columns: repeat(4, 1fr); } }
.space__strip img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; }

/* --------------------------------------------------------------- packages */
/* Three prices, stated plainly. Most location sites hide these behind an
   enquiry; putting them on the page is the whole pitch, so they get room. */

.packages {
  display: grid;
  gap: clamp(1.5rem, 0.8rem + 2vw, 2.5rem);
  grid-template-columns: 1fr;
}
@media (min-width: 52rem) { .packages { grid-template-columns: repeat(3, 1fr); } }

.package {
  display: flex;
  flex-direction: column;
  background: var(--bone);
  border: 1px solid var(--ink-hair);
  border-radius: var(--radius);
  padding: clamp(1.75rem, 1.2rem + 1.6vw, 2.75rem);
}
.band--limestone .package { background: var(--bone); }

.package--feature { background: var(--ink); color: var(--on-dark); border-color: var(--ink); }
.package--feature .package__note,
.package--feature .package__rates dt { color: var(--on-dark-quiet); }
.package--feature .package__rates { border-color: var(--on-dark-hair); }
.package--feature .package__rates div { border-color: var(--on-dark-hair); }

.package__name {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  margin-bottom: 1.25rem;
}

.package__rate {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 1.8rem + 2.4vw, 3.5rem);
  line-height: 1;
  letter-spacing: -0.02em;
  display: flex;
  align-items: baseline;
  gap: 0.15em;
}
.package__rate span {
  font-family: var(--font-body);
  font-size: var(--fs-small);
  letter-spacing: 0;
  color: var(--ink-quiet);
}
.package--feature .package__rate span { color: var(--on-dark-quiet); }

.package__note {
  margin-top: 0.6rem;
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-quiet);
}

.package__blurb {
  margin-top: 1.5rem;
  font-size: var(--fs-small);
  color: var(--ink-quiet);
  text-wrap: pretty;
}
.package--feature .package__blurb { color: var(--on-dark-quiet); }

.package__rates {
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--ink-hair);
  font-size: var(--fs-small);
}
.package__rates div {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.6rem;
  border-bottom: 1px solid var(--ink-hair);
}
.package__rates div:last-child { border-bottom: 0; }
.package__rates dt { color: var(--ink-quiet); }
.package__rates dd { margin: 0; font-weight: 500; font-variant-numeric: tabular-nums; }

/* Pushes the button to the bottom so three cards of different copy lengths
   still line their actions up.

   The min-height is the other half of that: these labels are long enough that
   one can wrap while its neighbours do not, and a two-line button beside two
   one-line buttons breaks the row far more visibly than a little extra height
   ever would. Two lines' worth, so the three agree either way. */
.package .btn {
  margin-top: auto;
  min-height: 3.4rem;
}

/* The separation from the rates above is a margin-BOTTOM on the rates, not a
   margin-top on the button. A margin-top here would override the `auto` that
   pushes the button to the card floor — which it did, leaving the two shorter
   cards' buttons floating 49px above Full House's. */
.package__rates { margin-bottom: 1.75rem; }

.package__best {
  margin-top: 1.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--ink-hair);
}
.package__best h4 {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--ink-quiet);
  margin-bottom: 0.9rem;
}
.package__best ul { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.package__best li {
  font-size: var(--fs-micro);
  letter-spacing: 0.04em;
  padding: 0.4em 0.85em;
  border: 1px solid var(--ink-hair);
  border-radius: 100px;
  color: var(--ink-quiet);
}

/* The same rate block used outside a card, on the pricing page, where each
   package gets a full section rather than a column. The rates would otherwise
   stretch the width of the column and pull the figure miles from its label. */
.split__body .package__rate { margin-bottom: 0.5rem; }
.split__body .package__rates { max-width: 22rem; }

/* Package details sitting directly on an ink band rather than inside a
   .package--feature card. Same job, different container — without these the
   quiet greys stay dark and vanish. */
.band--ink .package__rate span,
.band--ink .package__note,
.band--ink .package__rates dt,
.band--ink .package__best h3 { color: var(--on-dark-quiet); }

.band--ink .package__rates,
.band--ink .package__rates div,
.band--ink .package__best { border-color: var(--on-dark-hair); }

.band--ink .package__best li {
  border-color: var(--on-dark-hair);
  color: var(--on-dark-quiet);
}

/* ------------------------------------------------------- comparison table */

.compare-wrap { overflow-x: auto; }

.compare {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  font-size: var(--fs-small);
}
.compare th,
.compare td {
  text-align: left;
  padding: 1.1rem 1.25rem;
  border-bottom: 1px solid var(--ink-hair);
  vertical-align: top;
}
.compare thead th {
  font-family: var(--font-body);
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
}
.compare tbody th { font-weight: 400; color: var(--ink-quiet); }
.compare td { font-variant-numeric: tabular-nums; }
.compare th:first-child,
.compare td:first-child { padding-left: 0; }

/* Below the table's own min-width the wrapper would scroll sideways, and this
   is the one piece of content on the site nobody should have to swipe to
   read. So the table stops being a table on a phone: each package becomes a
   stacked block with its figures labelled from data-label. */
@media (max-width: 36rem) {
  .compare {
    min-width: 0;
    display: block;
  }
  .compare caption { display: block; }
  .compare thead { display: none; }
  .compare tbody { display: block; }

  .compare tr {
    display: block;
    padding-block: 1.25rem;
    border-bottom: 1px solid var(--ink-hair);
  }
  .compare tr:last-child { border-bottom: 0; }

  .compare tbody th {
    display: block;
    padding: 0 0 0.5rem;
    border: 0;
    font-family: var(--font-body);
    font-size: var(--fs-micro);
    letter-spacing: var(--track-wide);
    text-transform: uppercase;
    color: var(--ink);
  }

  .compare td {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.5rem 0;
    border: 0;
  }
  .compare td + td { border-top: 1px solid var(--ink-hair); }

  .compare td::before {
    content: attr(data-label);
    color: var(--ink-quiet);
    font-variant-numeric: normal;
  }
}

/* --------------------------------------------------------------- facts */

.facts {
  display: grid;
  gap: 0;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--ink-hair);
}
@media (min-width: 44rem) { .facts { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .facts { grid-template-columns: repeat(5, 1fr); } }

.facts > div {
  padding: 1.75rem 0;
  border-bottom: 1px solid var(--ink-hair);
}
@media (min-width: 44rem) {
  .facts > div { padding-inline: 1.75rem; border-right: 1px solid var(--ink-hair); }
  .facts > div:first-child { padding-left: 0; }
  .facts > div:last-child { border-right: 0; padding-right: 0; }
}

.facts dt {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem);
  /* Not 1, and not 1.15 either. Playfair's line box is about 1.25em tall, so
     anything under that clips the figures — measured at 35px of content in a
     32px box before this. */
  line-height: 1.3;
  margin-bottom: 0.6rem;
}
.facts dd {
  margin: 0;
  font-size: var(--fs-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-quiet);
}

/* ------------------------------------------------------------ good to know */

.know {
  display: grid;
  gap: 0;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--ink-hair);
}
@media (min-width: 40rem) { .know { grid-template-columns: 1fr 1fr; } }
@media (min-width: 64rem) { .know { grid-template-columns: repeat(3, 1fr); } }

.know > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 1rem 0;
  border-bottom: 1px solid var(--ink-hair);
  font-size: var(--fs-small);
}
@media (min-width: 40rem) {
  .know > div { padding-inline: 1.5rem; }
  .know > div:nth-child(odd) { padding-left: 0; }
  .know > div:nth-child(even) { padding-right: 0; }
}
@media (min-width: 64rem) {
  .know > div:nth-child(odd),
  .know > div:nth-child(even) { padding-inline: 1.5rem; }
  .know > div:nth-child(3n + 1) { padding-left: 0; }
  .know > div:nth-child(3n) { padding-right: 0; }
}

.know dt { color: var(--ink-quiet); }
.know dd { margin: 0; text-align: right; }
.know dd[data-no] { color: var(--ink-soft); }

/* -------------------------------------------------------------- galleries */

.grid-gallery {
  display: grid;
  gap: clamp(0.5rem, 0.3rem + 0.8vw, 1rem);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 44rem) { .grid-gallery { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 68rem) { .grid-gallery--four { grid-template-columns: repeat(4, 1fr); } }

.grid-gallery img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; }

/* Full-bleed run of photographs, used to break between sections. */
.photo-strip {
  display: grid;
  gap: clamp(0.5rem, 0.3rem + 0.8vw, 1rem);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 48rem) { .photo-strip { grid-template-columns: 5fr 8fr 5fr 8fr; } }
.photo-strip img { width: 100%; height: 100%; aspect-ratio: 3 / 4; object-fit: cover; }
@media (min-width: 48rem) { .photo-strip img { aspect-ratio: 4 / 5; } }

/* A single photograph running the full width of the viewport. */
.bleed { width: 100%; }
.bleed img { width: 100%; max-height: 82svh; object-fit: cover; }

/* Only where the behaviour exists. Without JavaScript there is no lightbox,
   so a zoom cursor would be a promise the page cannot keep. */
html.js [data-gallery] img { cursor: zoom-in; }
[data-gallery] img { transition: opacity 0.3s var(--ease); }
html.js [data-gallery] img:hover { opacity: 0.86; }

/* --------------------------------------------------------------- logo wall */

.logo-wall {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.5rem, 1rem + 2vw, 3rem);
  align-items: center;
}
@media (min-width: 44rem) { .logo-wall { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 64rem) { .logo-wall { grid-template-columns: repeat(6, 1fr); } }

/* Placeholder slots until there are real brand logos to put here. Set as
   wordmarks rather than grey boxes so the section reads as finished. */
.logo-wall li {
  text-align: center;
  font-size: var(--fs-micro);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--ink-soft);
  padding-block: 1rem;
  border-top: 1px solid var(--ink-hair);
}

/* ------------------------------------------------------------- instagram */

.ig-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(0.4rem, 0.25rem + 0.6vw, 0.75rem);
}
@media (min-width: 40rem) { .ig-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 64rem) { .ig-grid { grid-template-columns: repeat(6, 1fr); } }

.ig-grid img { width: 100%; aspect-ratio: 1; object-fit: cover; }
.ig-grid a { display: block; position: relative; overflow: hidden; }
.ig-grid a img { transition: transform 0.5s var(--ease); }
.ig-grid a:hover img { transform: scale(1.04); }

/* -------------------------------------------------------------------- FAQ */

.faq { border-top: 1px solid var(--ink-hair); }

.faq details { border-bottom: 1px solid var(--ink-hair); }

.faq summary {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1.5rem;
  padding: 1.5rem 0;
  cursor: pointer;
  list-style: none;
  font-size: clamp(1.05rem, 1rem + 0.3vw, 1.25rem);
  text-wrap: pretty;
}
.faq summary::-webkit-details-marker { display: none; }

.faq summary::after {
  content: '+';
  flex: none;
  font-size: 1.35rem;
  line-height: 1;
  color: var(--ink-quiet);
  transition: transform 0.25s var(--ease);
}
.faq details[open] summary::after { transform: rotate(45deg); }

.faq__answer { padding-bottom: 1.75rem; max-width: 62ch; color: var(--ink-quiet); }
.faq__answer p + p { margin-top: 0.85em; }

.faq__group + .faq__group { margin-top: clamp(2.5rem, 1.5rem + 3vw, 4rem); }
.faq__group h2 {
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--walnut);
  margin-bottom: 1.25rem;
}

/* ------------------------------------------------------------------ ticks */

.ticks {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.85rem 2rem;
}
@media (min-width: 40rem) { .ticks { grid-template-columns: 1fr 1fr; } }
@media (min-width: 64rem) { .ticks--three { grid-template-columns: repeat(3, 1fr); } }

.ticks li {
  display: flex;
  gap: 0.85rem;
  align-items: baseline;
  font-size: var(--fs-small);
}
.ticks li::before {
  content: '';
  flex: none;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--wine);
  transform: translateY(-2px);
}
.band--ink .ticks li::before { background: var(--travertine); }

/* ------------------------------------------------------------------ forms */

.form { max-width: 44rem; }

/* The form is flat — no fieldsets — so the rhythm between fields lives here
   rather than on a group wrapper. */
.field + .field { margin-top: 1.75rem; }
.form__row + .field,
.field + .form__row,
.form__row + .form__row { margin-top: 1.75rem; }

.field label {
  display: block;
  font-size: var(--fs-small);
  margin-bottom: 0.5rem;
}

/* Sits between the label and the input, not after it. A hint below the input
   reads as though it belongs to the next question. */
.field__hint {
  display: block;
  font-size: var(--fs-micro);
  letter-spacing: 0.02em;
  color: var(--ink-quiet);
  margin-top: -0.15rem;
  margin-bottom: 0.5rem;
}

.field__optional { color: var(--ink-soft); }

/* Underlined rather than boxed: fewer lines on the page, and a long form of
   boxes reads as bureaucracy. */
.form--underline input,
.form--underline select,
.form--underline textarea {
  width: 100%;
  padding: 0.7rem 0;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--ink-faint);
  border-radius: 0;
  transition: border-color 0.2s var(--ease);
  -webkit-appearance: none;
  appearance: none;
}

.form--underline input:hover,
.form--underline select:hover,
.form--underline textarea:hover { border-bottom-color: var(--ink); }

.form--underline input:focus,
.form--underline select:focus,
.form--underline textarea:focus {
  outline: none;
  border-bottom-color: var(--wine);
  border-bottom-width: 2px;
  /* Keeps the field from shifting 1px when the border thickens. */
  padding-bottom: calc(0.7rem - 1px);
}

.form--underline textarea { min-height: 8rem; resize: vertical; }

/* The arrow the appearance reset removed. */
.form--underline select {
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-quiet) 50%),
    linear-gradient(135deg, var(--ink-quiet) 50%, transparent 50%);
  background-position: calc(100% - 10px) 1.25rem, calc(100% - 5px) 1.25rem;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 1.75rem;
  cursor: pointer;
}

.form--underline input[type='file'] {
  border-bottom: 0;
  padding-left: 0;
  font-size: var(--fs-small);
  color: var(--ink-quiet);
}
.form--underline input[type='file']::file-selector-button {
  margin-right: 1rem;
  padding: 0.6em 1.2em;
  border: 1px solid var(--ink-faint);
  border-radius: var(--radius);
  background: transparent;
  font-size: var(--fs-micro);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  cursor: pointer;
}

/* The date pair, side by side. */
.form__row { display: grid; gap: 1.75rem; }
@media (min-width: 34rem) { .form__row { grid-template-columns: 1fr 1fr; } }
.form__row .field + .field { margin-top: 0; }

/* Marks the answer that travelled here from the pricing page, so the visitor
   can see their choice arrived rather than wondering if it did. */
.field--prefilled label::after {
  content: 'from your selection';
  margin-left: 0.75rem;
  font-size: var(--fs-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--wine);
}

.form__submit {
  margin-top: clamp(2.5rem, 1.5rem + 3vw, 4rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem 2rem;
}

.form__reply { font-size: var(--fs-small); color: var(--ink-quiet); }

[data-form-status] {
  margin-top: 1.25rem;
  font-size: var(--fs-small);
  color: var(--walnut);
}
[data-form-status]:empty { margin-top: 0; }
[data-form-status][data-state='error'] { color: var(--wine); }

/* ------------------------------------------------------------- lightbox */
/* Built by main.js. These styles exist for markup this stylesheet never sees
   in public/ — keep them in step with the innerHTML in the script. */

.lightbox {
  width: 100vw;
  max-width: 100vw;
  height: 100svh;
  max-height: 100svh;
  padding: 0;
  border: 0;
  background: rgba(18, 15, 13, 0.96);
  overflow: hidden;
}
.lightbox::backdrop { background: rgba(18, 15, 13, 0.9); }

.lightbox img {
  position: absolute;
  inset: 0;
  margin: auto;
  max-width: min(92vw, 1400px);
  max-height: 88svh;
  width: auto;
  height: auto;
  object-fit: contain;
  touch-action: pan-y;
}

.lightbox__close,
.lightbox__nav {
  position: absolute;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  color: var(--white);
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.2s var(--ease), opacity 0.2s var(--ease);
}
.lightbox__close:hover,
.lightbox__nav:hover { background: rgba(255, 255, 255, 0.22); }

.lightbox__close { top: 1rem; right: 1rem; font-size: 1.5rem; }
.lightbox__nav { top: 50%; transform: translateY(-50%); }
.lightbox__nav--prev { left: 1rem; }
.lightbox__nav--next { right: 1rem; }

@media (max-width: 40rem) {
  /* Swiping is the gesture on a phone, and two 48px targets over a photograph
     on a 375px screen cover most of it. */
  .lightbox__nav { display: none; }
}

/* ------------------------------------------------------------ tap targets */
/* 44px is the accessible floor for anything you tap. Measured across the site
   the controls came in at 16-42px, which is fine for a mouse and fiddly for a
   thumb — the header wordmark was 22px and the price-table links 16px.

   Scoped to touch OR a narrow window rather than applied globally, because
   inflating every desktop button to 44px would coarsen a deliberately fine
   interface for pointers that never needed it. The width half of the query is
   what makes this testable in a desktop browser. */

@media (pointer: coarse), (max-width: 64rem) {
  .btn,
  .btn--sm { min-height: 44px; }

  .header__brand { padding-block: 0.75rem; }

  .header__ig {
    width: 44px;
    height: 44px;
  }

  .nav-toggle {
    width: 44px;
    height: 44px;
  }

  .link-arrow { padding-block: 0.75rem; }

  /* Row links in the price table were 16px of bare text. */
  .compare th[scope='row'] a {
    display: block;
    padding-block: 0.78rem;
  }

  .footer__list a,
  .footer__legal a {
    display: inline-block;
    padding-block: 0.56rem;
  }
  .footer__list li + li { margin-top: 0; }

  /* The 404's three routes onward are links inside headings, so they inherit
     the 12px eyebrow size and were a 13px tap target. */
  .item__title a {
    display: inline-block;
    padding-block: 0.95rem;
  }
}

/* -------------------------------------------------------------- utilities */

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

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.mt-gap { margin-top: clamp(2rem, 1.4rem + 2vw, 3rem); }
.measure { max-width: 62ch; }

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* The transform is what these two use to arrive; without it they need to be
     placed outright or they never appear at all. */
  .show-sticky-cta .mobile-cta { transform: none; }
  .ig-grid a:hover img { transform: none; }
}

/* ------------------------------------------------------------------ print */

@media print {
  .site-header,
  .mobile-cta,
  .site-footer,
  .skip-link { display: none; }
  body { background: #fff; color: #000; }
}
