/* ==========================================================================
   The Cliffs — Tasmania
   --------------------------------------------------------------------------
   Full bleed, edge to edge — no inset frame. The elevation marker runs down
   the left margin on its own, counting 280m at the hero to 0m at the CTA.

   Palette is drawn from the east coast: paper, coastal mist, eucalypt scrub,
   wet dolerite. Sage is the primary accent; the lichen orange is rationed
   down to almost nothing.

   CONTRAST — every pairing below was computed, not eyeballed. Ratios are
   recorded inline. Sage and lichen each need a light-ground and a dark-ground
   tuning; one hex cannot pass AA on both. Do not swap them around.
   ========================================================================== */

:root {
  /* Ground */
  --paper:  #F4F0E9;   /* warm off-white — dominant */
  --mist:   #DDE2DE;   /* pale cool grey-green — secondary */
  --ink:    #1B252C;   /* deep — bar, footer, photo overlays */

  /* Text */
  --slate:  #39434B;   /* body on light — 8.90:1 on paper */

  /* Accent — sage leads, orange barely appears */
  --eucalypt:      #7C8A7E;  /* rules, and text on ink — 4.30:1 on ink */
  --eucalypt-lit:  #9DAA9E;  /* small text on ink — 6.59:1, clears AA */
  --eucalypt-deep: #536352;  /* small text on light — 5.65:1 paper, 4.89:1 mist */
  --lichen-deep:   #9E430F;  /* light grounds — 5.67:1 on paper */
  --lichen-lit:    #E8863F;  /* dark grounds — 5.87:1 on ink */

  /* Type */
  --display: "Bodoni Moda", "Didot", "Bodoni MT", Georgia, serif;
  --body:    "Archivo", "Helvetica Neue", Arial, sans-serif;

  /* Scale */
  --t-display: clamp(2.75rem, 8vw, 7rem);
  --t-h2:      clamp(1.875rem, 4vw, 3.25rem);
  --t-lead:    clamp(1.3125rem, 2.2vw, 1.9375rem);
  --t-body:    clamp(1rem, 1.1vw, 1.125rem);
  --t-caps:    0.6875rem;

  /* Frame */
  --nav-h:  4.5rem;
  --bar-h:  4rem;

  /* Rhythm */
  --gutter:  clamp(1.5rem, 4.5vw, 4.5rem);
  --band-y:  clamp(6rem, 13vh, 10rem);
  --measure: 34rem;

  --hair: rgba(57, 67, 75, 0.22);
  --hair-lit: rgba(244, 240, 233, 0.34);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ---------- Reset ---------- */

*, *::before, *::after { box-sizing: border-box; }
/* scrollbar-gutter keeps the page from jumping when the reveal locks scroll */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scrollbar-gutter: stable; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--slate);
  font-family: var(--body);
  font-size: var(--t-body);
  font-weight: 300;
  line-height: 1.65;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { display: block; max-width: 100%; height: auto; }
h1, h2, p, dl, dd, figure { margin: 0; }

/* ---------- Shared type ---------- */

/* The utility voice of all three references: wide-tracked caps, never mono. */
.caps, .eyebrow, .nav__links a, .nav__book, .nav__mark,
.bar__facts, .bar__cta, .marker__figure, .hour__when, .spec dt,
.link, .button, .book__note, .footer__meta, .edge__figure {
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.eyebrow {
  color: var(--eucalypt-deep);
  margin-bottom: 2.25rem;
}
.eyebrow--lit { color: var(--lichen-lit); }

.h2 {
  font-family: var(--display);
  font-size: var(--t-h2);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: -0.012em;
  max-width: 15ch;
}

.lead {
  font-family: var(--display);
  font-size: var(--t-lead);
  font-weight: 400;
  line-height: 1.3;
  margin-bottom: 1.75rem;
}

.col { max-width: var(--measure); }
.col > p + p { margin-top: 1.35rem; }

/* ---------- Editorial split ----------
   A single narrow left column left the right half of the text sections empty,
   which read as unfinished rather than spacious. The label and heading now
   hold the left, the content takes the right, and the pair spans the measure. */

.split {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(2.5rem, 5vw, 5.5rem);
  align-items: start;
}
.split--centre { align-items: center; }
.split__head { max-width: 24rem; }
.split__body { max-width: 44rem; }
.split__body > p + p { margin-top: 1.35rem; }

/* ---------- Links ---------- */

.link {
  color: var(--lichen-deep);
  text-decoration: none;
  padding-bottom: 0.45em;
  border-bottom: 1px solid currentColor;
  transition: opacity 0.35s var(--ease);
}
.link--lit { color: var(--lichen-lit); }
.link:hover { opacity: 0.6; }
.link::after {
  content: "\2192";
  display: inline-block;
  margin-left: 0.7em;
  transition: transform 0.35s var(--ease);
}
.link:hover::after { transform: translateX(0.28em); }

.cta-line { margin-top: 2.75rem; }

.button {
  display: inline-block;
  color: var(--paper);
  text-decoration: none;
  padding: 1.25rem 2.5rem;
  border: 1px solid var(--hair-lit);
  transition: border-color 0.4s var(--ease), color 0.4s var(--ease);
}
.button:hover { border-color: var(--lichen-lit); color: var(--lichen-lit); }

:focus-visible { outline: 2px solid var(--lichen-deep); outline-offset: 4px; }

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: var(--paper);
  padding: 1rem 1.5rem;
  text-decoration: none;
}
.skip-link:focus { left: 0; }

/* ==========================================================================
   The elevation marker — the signature. Runs down the left margin and
   flips light over photographs, dark over paper. JS toggles .is-lit.
   ========================================================================== */


.marker {
  position: fixed;
  left: var(--gutter);
  top: 50%;
  transform: translateY(-50%);
  z-index: 31;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  pointer-events: none;
  color: var(--eucalypt-deep);
  transition: color 0.6s var(--ease);
}
.marker.is-lit { color: var(--paper); text-shadow: 0 1px 12px rgba(27, 37, 44, 0.7); }

.marker__tick { width: 1.5rem; height: 1px; background: currentColor; opacity: 0.5; }
.marker__figure { font-variant-numeric: tabular-nums; letter-spacing: 0.14em; }

/* ==========================================================================
   Nav — pinned to the top of the viewport
   ========================================================================== */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--nav-h);
  /* above .menu (60): the toggle lives in here and doubles as the close
     button, and z-index inside .nav is scoped to its own stacking context. */
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 0 clamp(1.25rem, 3vw, 2.5rem);
  border-bottom: 1px solid transparent;
  transition: background-color 0.5s var(--ease), border-color 0.5s var(--ease);
}
.nav.is-stuck {
  background: rgba(244, 240, 233, 0.92);
  backdrop-filter: blur(16px);
  border-bottom-color: var(--hair);
}

.nav__mark {
  font-family: var(--display);
  font-size: 1.0625rem;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--slate);
  text-decoration: none;
  white-space: nowrap;
}

.nav__links { display: flex; align-items: center; gap: 1.25rem; }
.nav__links a {
  color: var(--slate);
  text-decoration: none;
  transition: color 0.3s var(--ease);
}
.nav__links a:hover { color: var(--lichen-deep); }
.nav__dot { color: var(--eucalypt); font-size: 0.5rem; }

.nav__book {
  color: var(--slate);
  text-decoration: none;
  padding-bottom: 0.4em;
  border-bottom: 1px solid currentColor;
  white-space: nowrap;
  transition: color 0.3s var(--ease);
}
.nav__book:hover { color: var(--lichen-deep); }

/* Over the hero the nav sits under a soft paper scrim and keeps DARK text.
   Light text was measured at 1.5:1 against this hero's bright sky, and would
   have needed ~83% ink over the photograph to reach AA — which kills exactly
   the luminosity the direction is built on. The scrim reads as haze on a
   bright sky and as a translucent panel on a dark one, so it holds either
   way if the photograph is ever swapped. Measured 8.8:1. */
.nav.is-lit {
  background-image: linear-gradient(to bottom,
    rgba(244, 240, 233, 0.88) 0%,
    rgba(244, 240, 233, 0.46) 55%,
    rgba(244, 240, 233, 0) 100%);
}
.nav.is-stuck { background-image: none; }

/* ==========================================================================
   The booking bar — reference placement and weight, honest content.
   It holds no state and does not pretend to.
   ========================================================================== */

.bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--bar-h);
  z-index: 32;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 0 0 0 clamp(1.25rem, 3vw, 2.5rem);
  background: rgba(27, 37, 44, 0.9);
  backdrop-filter: blur(16px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.bar.is-hidden {
  opacity: 0;
  transform: translateY(100%);
  pointer-events: none;
}

.bar__facts {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  color: var(--eucalypt);
  min-width: 0;
  /* never wrap to a second line inside a 3.5rem bar */
  white-space: nowrap;
}
.bar__sep { opacity: 0.6; }

.bar__cta {
  align-self: stretch;
  display: flex;
  align-items: center;
  padding: 0 clamp(1.5rem, 3vw, 2.75rem);
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 0.35s var(--ease);
}
.bar__cta:hover { background: var(--lichen-lit); }

/* ==========================================================================
   Hero — scroll-to-reveal
   --------------------------------------------------------------------------
   The cliff photograph is the ground. A panel holding the branded showcase
   frame sits centred on it and grows as you scroll, while the ground fades
   and the headline splits apart. Progress is one custom property, --p (0..1),
   written by JS onto .hero; everything below derives from it.

   The showcase image carries its own burnt-in title, so the overlay headline
   fades out before the panel is large enough for the two to collide.
   ========================================================================== */

.hero {
  --p: 0;
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--ink);
}

/* The ground: the cliff shot, fading as the panel takes over */
.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: calc(1 - var(--p));
}
.hero__img { width: 100%; height: 100%; object-fit: cover; }

/* Even scrim — the headline is centred now, not bottom-left, so the old
   bottom-weighted gradient no longer sits under it. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(27, 37, 44, 0.62) 0%,
    rgba(27, 37, 44, 0.34) 34%,
    rgba(27, 37, 44, 0.26) 62%,
    rgba(27, 37, 44, 0.30) 100%);
}

.hero__stage {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100svh;
}

/* JS writes width/height inline; everything else is declarative */
.hero__panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  overflow: hidden;
  margin: 0;
  /* No caps: at full progress the panel is the viewport. The drop shadow
     reads as a floating card while it is small and has no business once the
     image is edge to edge, so it fades out with the same progress. */
  box-shadow: 0 24px 80px rgba(27, 37, 44, calc(0.45 * (1 - var(--p))));
}
/* The showcase frame carries its wordmark top-left. At rest the panel is a
   narrow portrait crop, so centring it would slice through that lettering and
   collide with the headline. Start the crop on the house at the right, and
   drift back to centre as the panel opens out to its full 3:2. */
.hero__panel img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: calc(92% - var(--p) * 42%) 50%;
}

/* Lifts the panel's own scrim off as it expands, so the burnt-in title
   on the showcase frame arrives clean */
.hero__panel::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(27, 37, 44, 0.74);
  opacity: calc(1 - var(--p));
  pointer-events: none;
}

.hero__type {
  position: relative;
  z-index: 2;
  text-align: center;
  opacity: clamp(0, calc(1 - var(--p) * 1.7), 1);
  pointer-events: none;
}

.hero__title {
  font-family: var(--display);
  font-size: var(--t-display);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--paper);
  text-shadow: 0 2px 30px rgba(27, 37, 44, 0.55);
  display: flex;
  flex-direction: column;
  gap: 0.1em;
}

/* Says plainly what the display line only implies. It sits inside .hero__type
   so it fades with the headline as the panel opens, and it does not split —
   only the two headline words carry the translateX. */
.hero__qualifier {
  max-width: 34ch;
  margin: clamp(1.25rem, 3vh, 2rem) auto 0;
  font-family: var(--body);
  font-size: clamp(1rem, 1.45vw, 1.1875rem);
  font-weight: 300;
  line-height: 1.5;
  color: var(--paper);
  text-shadow: 0 1px 14px rgba(27, 37, 44, 0.75);
}

.hero__word { display: block; will-change: transform; }
.hero__word--a { transform: translateX(calc(var(--p) * -46vw)); }
.hero__word--b { transform: translateX(calc(var(--p) * 46vw)); }

/* Cue and skip sit together. The wrapper owns the position and the --p fade;
   the cue itself is inert, the button is not. */
.hero__foot {
  position: absolute;
  left: 50%;
  bottom: calc(var(--bar-h) + 2.25rem);
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 1.6rem;
  opacity: clamp(0, calc(1 - var(--p) * 2.4), 1);
}
/* opacity 0 is still clickable, so the whole footer leaves the page once the
   reveal is most of the way open */
.hero__foot.is-gone { visibility: hidden; pointer-events: none; }

.hero__skip {
  padding: 0.45rem 0;
  border: 0;
  border-bottom: 1px solid currentColor;
  background: none;
  cursor: pointer;
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(244, 240, 233, 0.92);
  text-shadow: 0 1px 14px rgba(27, 37, 44, 0.7);
  transition: color 0.3s var(--ease);
}
.hero__skip:hover { color: var(--lichen-lit); }

.hero__cue {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(244, 240, 233, 0.92);
  text-shadow: 0 1px 14px rgba(27, 37, 44, 0.7);
  pointer-events: none;
  white-space: nowrap;
  margin: 0;
}
.hero__cue-rule {
  width: 2.75rem;
  height: 1px;
  background: currentColor;
  transform-origin: left;
  animation: cue 3.4s var(--ease) infinite;
}
@keyframes cue {
  0%, 100% { transform: scaleX(0.28); opacity: 0.45; }
  50%      { transform: scaleX(1);    opacity: 1; }
}

/* While the reveal is running the page cannot scroll normally */
body.is-locked,
body.menu-open { overflow: hidden; }

/* ==========================================================================
   Bands
   ========================================================================== */

/* The nav is fixed, so anchor targets must clear it */
[id] { scroll-margin-top: var(--nav-h); }

.band {
  padding: var(--band-y) var(--gutter);
  padding-left: var(--gutter);
}
.band--paper { background: var(--paper); color: var(--slate); }

/* The days band carries a photograph. It is the only band that does, so it
   inverts: light type on a dark scrim rather than slate on mist. --mist stays
   as the paint-behind colour while the image loads. */
.band--mist {
  position: relative;
  isolation: isolate;
  background-color: var(--ink);
  background-image: url("../images/day.jpg");
  background-size: cover;
  /* Anchored right: the photograph carries its own "THE CLIFFS" wordmark near
     the left edge, which landed directly behind the Morning column. Cropping
     from the right drops it and puts open coast behind the type instead. */
  background-position: right center;
  /* Parallax: the background holds still in the viewport while the section
     scrolls over it. Note this makes `cover` size against the VIEWPORT rather
     than the element, so the crop geometry differs from a scrolling one. */
  background-attachment: fixed;
  color: var(--paper);
}

/* iOS Safari does not honour background-attachment: fixed — it renders the
   image at the wrong scale or drops the effect entirely. Coarse pointers and
   narrow screens fall back to a normal scrolling background, and so does
   anyone who has asked the system for less motion. */
@media (max-width: 900px), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .band--mist,
  .count { background-attachment: scroll; }
}
.band--mist::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Heavier and flatter than a static band would need. With parallax the
     image slides under a scrim that is fixed to the section, so the pale
     sunset sky reaches the text at some scroll positions. Measured at 0.66
     mid, body copy fell to 3.32:1 there; 0.82 still only reached 4.32:1. */
  background: linear-gradient(to bottom,
    rgba(27, 37, 44, 0.88) 0%,
    rgba(27, 37, 44, 0.84) 45%,
    rgba(27, 37, 44, 0.88) 100%);
}

/* Type on the photograph needs the dark-ground tunings */
/* A lifted lichen for this band only: --lichen-lit measured 4.48:1 against the
   brightest slice of the parallax sky — right on the threshold. Brightening the
   accent is cheaper than veiling the photograph further. */
.band--mist .eyebrow      { color: #F0A05C; text-shadow: 0 1px 12px rgba(27, 37, 44, 0.8); }
.band--mist .hour__when {
  color: var(--paper);
  text-shadow: 0 1px 12px rgba(27, 37, 44, 0.8);
  border-bottom-color: rgba(244, 240, 233, 0.38);
}
.band--mist .hour__what   { color: var(--paper); text-shadow: 0 1px 10px rgba(27, 37, 44, 0.7); }

/* ---------- The house ---------- */

.pairs {
  margin-top: clamp(4rem, 9vh, 7rem);
  display: flex;
  flex-direction: column;
  gap: clamp(3rem, 7vh, 5.5rem);
}

/* Asymmetric: the photograph leads, the text settles on its baseline */
/* The media column sizes to the capped image rather than a fixed fraction.
   With a height cap the picture no longer fills a fr-based column, and the
   leftover space opened a dead gap between image and text. */
.pair {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 4rem);
  align-items: center;
}
.pair--flip { grid-template-columns: minmax(0, 1fr) auto; }
.pair--flip .media { order: 2; }

.pair__text {
  max-width: 30ch;
  margin: auto;
  font-size: clamp(1.0625rem, 1.25vw, 1.1875rem);
}

/* Height is capped at 500px. The box is driven by aspect-ratio and the column
   width, not by the file, so the cap is expressed as a max-width per ratio —
   that keeps 4:5 and 3:2 intact instead of squashing the crop:
     portrait  4:5  ->  400px wide = 500 tall
     landscape 3:2  ->  750px wide = 500 tall  (columns are narrower, so ~470) */
.media {
  position: relative;
  overflow: hidden;
  background: var(--mist);
  max-height: 500px;
}
.media--portrait  { aspect-ratio: 4 / 5; max-width: 400px; }
.media--landscape { aspect-ratio: 3 / 2; max-width: 750px; }

/* Capped media no longer fills its column, so flipped pairs would leave a gap
   at the outer edge. Push them back to the edge they belong on. */
.pair--flip .media { justify-self: end; }
.media img { width: 100%; height: 100%; object-fit: cover; }

/* Placeholder until the photographs arrive. JS adds .is-placeholder and
   writes the expected filename into data-missing. */
.is-placeholder::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--mist);
  background-image: repeating-linear-gradient(
    45deg,
    rgba(57, 67, 75, 0.06) 0 1px,
    transparent 1px 9px
  );
}
/* Only .media gets a filename label: on the full-bleed slots ::before is the
   hatch and ::after is the legibility gradient, so there is no free pseudo
   element to put it on. The hatch alone reads fine at that size. */
.media[data-missing]::after {
  content: attr(data-missing);
  position: absolute;
  left: 1.25rem;
  bottom: 1.25rem;
  font-family: var(--body);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--eucalypt-deep);
}
img.is-missing { display: none; }

/* Full-bleed slots sit on ink, so their placeholder hatch is the light one */
.hero__media.is-placeholder::before,
.edge__media.is-placeholder::before,
.book__media.is-placeholder::before {
  background-color: var(--ink);
  background-image: repeating-linear-gradient(
    45deg,
    rgba(244, 240, 233, 0.05) 0 1px,
    transparent 1px 9px
  );
}

/* ---------- The days ---------- */

/* Stacked, not three across: inside the right column three abreast would be
   ~200px each. A vertical list also suits morning/afternoon/night, which is a
   real sequence rather than three parallel options. */
.hours {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(1.75rem, 3vh, 2.5rem);
}

.hour__when {
  color: var(--eucalypt-deep);
  padding-bottom: 1.25rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--hair);
}

/* ---------- The edge: the one dark moment ---------- */

/* Sized to its content, not the viewport. At 100svh the section reserved a
   full screen and — with no photograph in yet — that read as a large empty
   slab above the text. Padding is symmetric so it holds either way, and the
   photograph becomes a band behind the type when it lands. */
.edge {
  position: relative;
  display: flex;
  align-items: center;
  padding: var(--band-y) var(--gutter);
  overflow: hidden;
  background: var(--ink);
}

.edge__media { position: absolute; inset: 0; z-index: 0; }
.edge__img { width: 100%; height: 100%; object-fit: cover; }
.edge__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(27, 37, 44, 0.82) 0%, rgba(27, 37, 44, 0.18) 62%, rgba(27, 37, 44, 0.3) 100%);
}

.edge__body { position: relative; z-index: 1; width: 100%; }

.edge__title {
  font-family: var(--display);
  font-size: var(--t-h2);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.018em;
  color: var(--paper);
}
.edge__figure { color: var(--lichen-lit); margin: 1.25rem 0 1.75rem; }
.edge__text { color: rgba(244, 240, 233, 0.88); }

/* ---------- The details ---------- */

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

.spec {
  display: grid;
  grid-template-columns: 12rem minmax(0, 1fr);
  gap: 1.5rem;
  padding: 1.15rem 0;
  border-bottom: 1px solid var(--hair);
}
.spec dt { color: var(--eucalypt-deep); padding-top: 0.3em; }
.spec dd { margin: 0; }

/* ---------- Sea level ---------- */

/* Matched to .edge: sized by its content rather than the viewport, so the
   missing photograph does not leave a full screen of empty ink. */
.book {
  position: relative;
  display: flex;
  align-items: center;
  padding: var(--band-y) var(--gutter);
  overflow: hidden;
  background: var(--ink);
}

.book__media { position: absolute; inset: 0; z-index: 0; }
.book__img { width: 100%; height: 100%; object-fit: cover; opacity: 0.5; }
.book__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(27, 37, 44, 0.7) 0%, rgba(27, 37, 44, 0.93) 100%);
}

.book__body { position: relative; z-index: 1; width: 100%; }

.book__title {
  font-family: var(--display);
  font-size: var(--t-h2);
  font-weight: 400;
  line-height: 1.04;
  letter-spacing: -0.018em;
  color: var(--paper);
}
.book__text { color: rgba(244, 240, 233, 0.86); margin-bottom: 2.75rem; }
.book__note { color: var(--eucalypt); margin-top: 1.75rem; }

/* ---------- Footer ----------
   The last thing on the page, and the only place with a street address, an
   inbox and a telephone number in it. Three columns on wide screens — the
   name, where the house is, how to reach a person — over a hairline, with
   the coordinates and the listing link on the bottom row. The bottom padding
   clears the fixed booking bar. */

.footer {
  background: var(--ink);
  padding: clamp(3.5rem, 8vh, 5.5rem) var(--gutter) calc(var(--bar-h) + 3.5rem);
  border-top: 1px solid rgba(244, 240, 233, 0.12);
}

.footer__inner {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1.2fr;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}

.footer__name {
  font-family: var(--display);
  font-size: clamp(1.5rem, 2.6vw, 2.125rem);
  line-height: 1.1;
  color: var(--paper);
  margin-bottom: 0.85rem;
}
.footer__line {
  color: rgba(244, 240, 233, 0.7);
  font-size: 0.9375rem;
  line-height: 1.55;
  max-width: 24ch;
  margin: 0;
}

.footer__label {
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--eucalypt-lit);
  margin: 0 0 1.15rem;
}

.footer__address {
  font-style: normal;      /* <address> italicises by default */
  color: rgba(244, 240, 233, 0.86);
  font-size: 0.9375rem;
  line-height: 1.8;
}

.footer__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.35rem;
}

.footer__link {
  color: rgba(244, 240, 233, 0.86);
  font-size: 0.9375rem;
  text-decoration: none;
  /* the underline sits off the baseline so the descenders stay legible */
  border-bottom: 1px solid rgba(244, 240, 233, 0.22);
  padding-bottom: 0.15rem;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.footer__link:hover,
.footer__link:focus-visible { color: var(--lichen-lit); border-bottom-color: var(--lichen-lit); }

.footer__hint {
  color: var(--eucalypt-lit);
  font-size: 0.875rem;
  line-height: 1.6;
  max-width: 30ch;
  margin: 1.35rem 0 0;
}

.footer__foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem 2rem;
  margin-top: clamp(2.75rem, 6vh, 4rem);
  padding-top: 1.75rem;
  border-top: 1px solid rgba(244, 240, 233, 0.12);
}
.footer__meta { color: var(--eucalypt-lit); margin: 0; }
.footer__link--quiet {
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--eucalypt-lit);
  border-bottom-color: rgba(157, 170, 158, 0.4);
}

@media (max-width: 900px) {
  .footer__inner { grid-template-columns: 1fr 1fr; }
  .footer__brand { grid-column: 1 / -1; margin-bottom: 0.5rem; }
}
@media (max-width: 560px) {
  .footer__inner { grid-template-columns: 1fr; gap: 2.25rem; }
  .footer__brand { margin-bottom: 0; }
  .footer__foot { flex-direction: column; align-items: flex-start; gap: 0.85rem; }
}

/* ---------- Scroll reveals ---------- */

.reveal {
  opacity: 0;
  transform: translateY(1.25rem);
  transition: opacity 1s var(--ease), transform 1s var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 900px) {
  :root { --nav-h: 3.75rem; }

  .nav__links { display: none; }
  .marker { display: none; }

  .pair { grid-template-columns: 1fr; gap: 2rem; }
  .pair--flip { grid-template-columns: 1fr; }
  .pair--flip .media { order: 0; }
  .pair__text { max-width: 46ch; }

  .split { grid-template-columns: 1fr; gap: 2.25rem; }
  .split__head, .split__body { max-width: 46ch; }

  .spec { grid-template-columns: 1fr; gap: 0.35rem; }
  .spec dt { padding-top: 0; }
}

/* Below 400px only the essential fact survives — squeezing the flex any
   further wrapped it onto a second line inside a 3.5rem bar. */
@media (max-width: 400px) {
  .bar__beds, .bar__sep--beds { display: none; }
}

@media (max-width: 560px) {
  :root { --bar-h: 3.5rem; }

  .bar { padding-left: 1rem; }
  .bar__facts { gap: 0.5rem; font-size: 0.5625rem; letter-spacing: 0.12em; }
  .bar__place { display: none; }
  .bar__sep--wide { display: none; }
  .bar__cta { padding: 0 1.15rem; font-size: 0.5625rem; letter-spacing: 0.12em; }
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */

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

  /* the scale-in start state would otherwise stick */
  .media img { transform: none !important; }
  .reveal { opacity: 1; transform: none; }
}


/* ==========================================================================
   Reveal-open state
   --------------------------------------------------------------------------
   When the showcase panel is open it fills the viewport with a bright
   photograph, inverting the tonal environment the fixed furniture was tuned
   for. Measured at full reveal before this rule: nav 3.84:1, marker 2.38:1 —
   both failing. These rules flip them onto the light ground.
   Declared after .is-lit so they win at equal specificity.
   ========================================================================== */

.nav.is-solid {
  background-color: rgba(244, 240, 233, 0.95);
  background-image: none;
  backdrop-filter: blur(16px);
  border-bottom-color: var(--hair);
}
.nav.is-solid .nav__mark,
.nav.is-solid .nav__links a,
.nav.is-solid .nav__book { color: var(--slate); }

/* Dark type with a light halo: holds on a mid-tone photograph where neither
   pure light nor pure dark clears AA on its own. 5.77:1 as measured. */
.marker.is-onlight {
  color: var(--ink);
  text-shadow: 0 1px 10px rgba(244, 240, 233, 0.8);
}

/* ==========================================================================
   Motion
   --------------------------------------------------------------------------
   The hero reveal is the orchestrated moment; everything here stays quiet
   around it. Transform and opacity only, so it composites on the GPU.
   ========================================================================== */

@keyframes rise-in {
  from { opacity: 0; transform: translateY(1.25rem); }
  to   { opacity: 1; transform: none; }
}

@keyframes panel-in {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.94); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

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

/* Page load: the type arrives before the frame it sits in.
   fill-mode is `backwards`, never `both` — .hero__cue and .hero__type own
   their opacity through --p, and a forwards fill would freeze those at 1
   and break the scroll reveal. */
.nav           { animation: fade-in 1.2s var(--ease) 0.1s backwards; }
.hero__eyebrow { animation: rise-in 0.9s var(--ease) 0.25s backwards; }
.hero__title   { animation: rise-in 1.1s var(--ease) 0.4s backwards; }
.hero__panel   { animation: panel-in 1.3s var(--ease) 0.15s backwards; }
.hero__qualifier { animation: rise-in 0.9s var(--ease) 0.65s backwards; }
.hero__foot    { animation: rise-in 0.9s var(--ease) 1s backwards; }
.bar           { animation: rise-in 0.9s var(--ease) 0.8s backwards; }

/* Photographs settle rather than snap: they enter fractionally oversized and
   come to rest, which reads as a camera finding focus. The crop is clipped by
   .media's overflow, so nothing spills. */
.media img {
  transform: scale(1.06);
  transition: transform 1.6s var(--ease);
}
.pair.is-in .media img { transform: none; }

/* Morning, afternoon, night arrive in order. The stagger is the one place the
   motion carries meaning — it is a day passing, not decoration. */
.hour { transition-delay: 0ms; }
.hours .hour:nth-child(2) { transition-delay: 140ms; }
.hours .hour:nth-child(3) { transition-delay: 280ms; }

/* ==========================================================================
   Mobile menu
   ========================================================================== */

.nav__toggle {
  display: none;
  align-items: center;
  justify-content: flex-end;
  gap: 0.6rem;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  z-index: 70;
}
.nav__toggle-box {
  position: relative;
  display: block;
  width: 1.5rem;
  height: 0.75rem;
}
.nav__toggle-box span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transition: transform 0.4s var(--ease), opacity 0.3s var(--ease);
}
.nav__toggle-box span:first-child { top: 0; }
.nav__toggle-box span:last-child  { bottom: 0; }

/* the two rules meet and cross */
.nav__toggle[aria-expanded="true"] .nav__toggle-box span:first-child {
  transform: translateY(0.375rem) rotate(45deg);
}
.nav__toggle[aria-expanded="true"] .nav__toggle-box span:last-child {
  transform: translateY(-0.375rem) rotate(-45deg);
}

.menu {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(2rem, 6vh, 3.5rem);
  padding: calc(var(--nav-h) + 2rem) var(--gutter) var(--gutter);
  background: var(--paper);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.75rem);
  /* visibility flips instantly on open and only waits on close. Transitioning
     it in both directions left the panel hidden for the first frame, and
     focus() silently fails on a visibility:hidden element — so the focus trap
     never engaged. */
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease), visibility 0s 0.45s;
}
.menu.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease), visibility 0s 0s;
}

.menu__nav { display: flex; flex-direction: column; }
.menu__nav a {
  font-family: var(--display);
  font-size: clamp(2rem, 9vw, 3.25rem);
  font-weight: 400;
  line-height: 1.18;
  letter-spacing: -0.015em;
  color: var(--slate);
  text-decoration: none;
  padding: 0.35rem 0;
  transition: color 0.3s var(--ease);
}
.menu__nav a:hover { color: var(--lichen-deep); }

.menu__book {
  align-self: start;
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper);
  background: var(--ink);
  text-decoration: none;
  padding: 1.15rem 2rem;
  transition: background-color 0.35s var(--ease);
}
.menu__book:hover { background: var(--lichen-deep); }

.menu__meta {
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--eucalypt-deep);
}

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

/* With the panel open the nav sits on paper, so its type goes dark, and the
   small Book link stands down — the panel carries a full-size CTA already. */
body.menu-open .nav__mark,
body.menu-open .nav__toggle { color: var(--slate); }
body.menu-open .nav__book {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s var(--ease);
}
body.menu-open .nav { background-image: none; background-color: transparent; backdrop-filter: none; border-bottom-color: transparent; }

/* Above the breakpoint the inline links are back, so the panel must never
   appear even if it was left open when the window was widened. */
@media (min-width: 901px) {
  .menu { display: none; }
}

/* ==========================================================================
   Counters
   --------------------------------------------------------------------------
   Same parallax treatment as the days band. It reuses images/day.jpg rather
   than a second copy of the same file. Cropped from the right for the same
   reason as the days band: the photograph carries its own "THE CLIFFS"
   wordmark near the left edge, and with parallax that slice drifts through
   the frame as you scroll — straight behind the stats.
   ========================================================================== */

.count {
  position: relative;
  isolation: isolate;
  background-color: var(--ink);
  background-image: url("../images/day.jpg");
  background-size: cover;
  background-position: right center;
  background-attachment: fixed;
  color: var(--paper);
}

/* Heavy and flat for the same reason as the days band: with a fixed
   background the pale sky slides under a scrim that is fixed to the section. */
.count::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom,
    rgba(27, 37, 44, 0.88) 0%,
    rgba(27, 37, 44, 0.84) 50%,
    rgba(27, 37, 44, 0.88) 100%);
}

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

.count__item {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(244, 240, 233, 0.3);
}

.count__value {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-family: var(--display);
  font-size: clamp(2.75rem, 6vw, 4.75rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--paper);
  /* digits must not shift width while the number counts up */
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 16px rgba(27, 37, 44, 0.6);
}

/* --eucalypt is tuned against pure ink and measured only 3.30:1 here: the
   scrimmed photograph sits lighter than the flat token. Lifted sage clears
   AA at 5.9:1, with a shadow for the brighter slices the parallax brings. */
.count__suffix {
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #A9BBAE;
  text-shadow: 0 1px 10px rgba(27, 37, 44, 0.8);
}

.count__label {
  margin: 0;
  font-family: var(--body);
  font-size: var(--t-caps);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper);
  text-shadow: 0 1px 12px rgba(27, 37, 44, 0.8);
}

@media (max-width: 760px) {
  .count__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 420px) {
  .count__grid { grid-template-columns: 1fr; gap: 1.75rem; }
}
