/* ============================================================
   Kaltasin Beachouse Farmstay, Sooke BC
   Palette drawn from the aerial dusk photo of Sooke Basin:
   basin navy / steel water / fog / spit sand / garden moss
   ============================================================ */

/* The logo face. Self-hosted rather than linked, so it is covered by the same
   immutable cache and content-addressed filename as the rest of the assets.
   swap keeps the wordmark readable in the fallback while the file loads. */
@font-face {
  font-family: "Butnerfly";
  src: url("fonts/butnerfly.4adec8d3.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --basin: #1c3a52;      /* deep water ink */
  --basin-deep: #142c40;
  --water: #49708e;      /* steel blue */
  --water-soft: #7d9cb4;
  --water-light: #9fbcd2; /* passes AA on basin navy */
  --fog: #f2f6f8;        /* pale sky */
  --sand: #e6dcc6;       /* the spit */
  --sand-deep: #cdbf9e;
  --moss: #6f7f52;       /* the farm */
  --moss-deep: #55633e;
  --ink: #24313c;
  --ink-soft: #56646f;
  --line: #e2e8ec;
  --white: #ffffff;

  /* Butnerfly is the logo face, so the wordmark and the script accents match
     the mark in the hero photo. Pacifico stays next in the stack on purpose:
     Butnerfly has no left double quote, so the decorative marks on the reviews
     keep rendering in Pacifico exactly as before. Browsers fall back per glyph,
     not per element, so this needs no special casing. */
  --script: "Butnerfly", "Pacifico", cursive;
  --heading: "Jost", "Avenir Next", sans-serif;
  --body: "Karla", "Helvetica Neue", sans-serif;

  --max: 1100px;
  --radius: 14px;
  --header-h: 64px;
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 16px);
  -webkit-text-size-adjust: 100%;
}

@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;
  }
}

body {
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--white);
  overflow-x: hidden;
}

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

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 24px; }

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

.skip-link {
  position: absolute;
  top: -100px;
  left: 12px;
  z-index: 200;
  background: var(--basin);
  color: var(--white);
  font-family: var(--heading);
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 12px 20px;
  border-radius: 0 0 8px 8px;
  transition: top 0.2s ease;
}
.skip-link:focus { top: 0; }

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

h1, h2, h3 { font-family: var(--heading); font-weight: 400; }

.script { font-family: var(--script); font-weight: 400; }

.eyebrow {
  font-family: var(--heading);
  font-size: 13px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--water);
  margin-bottom: 10px;
}

.section-title {
  font-size: clamp(28px, 4.5vw, 44px);
  line-height: 1.25;
  letter-spacing: 0.02em;
  color: var(--basin);
  margin-bottom: 18px;
  text-wrap: balance;
}

/* Pacifico has a much smaller x-height than Jost, so matching nominal sizes
   makes the script read as a different, smaller face dropped into the line.
   Size it up and lift it to bring the x-heights level. */
.section-title .script {
  color: var(--moss);
  font-size: 1.08em;
  line-height: 1;
  vertical-align: -0.04em;
}

.lede { font-size: 19px; color: var(--ink-soft); max-width: 62ch; }
.lede-center { margin-left: auto; margin-right: auto; }

/* ---------- header ---------- */

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 28px;
  min-height: var(--header-h);
  transition: background 0.35s ease, box-shadow 0.35s ease;
}

/* scrim so white nav stays legible over the pale sky in the hero photo */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(20, 44, 64, 0.62), rgba(20, 44, 64, 0));
  transition: opacity 0.35s ease;
}

/* fully opaque: at 0.97 the page ghosted through the bar on every scroll */
.site-header.solid { background: var(--white); box-shadow: 0 1px 14px rgba(20, 44, 64, 0.12); }
.site-header.solid::before { opacity: 0; }

.brand {
  font-family: var(--script);
  font-size: 24px;
  line-height: 1;
  color: var(--white);
  text-decoration: none;
  transition: color 0.35s ease;
  flex: none;
}
.site-header.solid .brand { color: var(--basin); }

/* The full name only fits alongside six nav links on wide screens, so the
   900-1180px band keeps the short mark. Below 900px those links collapse into
   the menu and that space comes free, so the full name is restored there (see
   the mobile header block) and phones read the same wordmark as desktop.
   The link's aria-label carries the full name at every width. */
.brand-rest { display: none; }
/* 1240 not 1180: Butnerfly sets about 15% wider than the Pacifico placeholder
   it replaced, so the full name grew from 303px to 347px and no longer cleared
   the nav at the old threshold. */
@media (min-width: 1240px) { .brand-rest { display: inline; } }

.site-nav { display: flex; gap: 26px; align-items: center; }

.site-nav a {
  font-family: var(--heading);
  font-size: 13px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  color: var(--white);
  transition: color 0.35s ease;
}
.site-header.solid .site-nav a { color: var(--ink); }
.site-nav a:hover { color: var(--sand-deep); }
.site-header.solid .site-nav a:hover { color: var(--water); }

.site-nav .book-link {
  border: 1px solid currentColor;
  padding: 8px 18px;
  border-radius: 999px;
}

/* ---------- mobile nav ---------- */

.nav-toggle {
  display: none;
  align-items: center;
  gap: 9px;
  background: none;
  border: 0;
  padding: 8px 4px;
  cursor: pointer;
  color: var(--white);
  font-family: var(--heading);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.site-header.solid .nav-toggle { color: var(--ink); }

.nav-toggle-bars {
  display: block;
  width: 22px;
  height: 14px;
  position: relative;
}
.nav-toggle-bars span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform 0.28s ease, opacity 0.2s ease;
}
.nav-toggle-bars span:nth-child(1) { top: 0; }
.nav-toggle-bars span:nth-child(2) { top: 6px; }
.nav-toggle-bars span:nth-child(3) { top: 12px; }

.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 900px) {
  :root { --header-h: 52px; }

  .site-header { padding: 8px 16px; }
  /* One uniform size across the whole wordmark, matching the desktop header.
     It has to be fluid: "Beachouse Farmstay" is 18 characters, so a single size
     that clears the toggle is capped near 13px on a 320px screen but can reach
     the full 20px by about 434px. */
  .brand { font-size: clamp(12.5px, 6.6vw - 8.6px, 20px); white-space: nowrap; }
  .nav-toggle { display: inline-flex; }

  /* Nav is collapsed here, so the full name fits beside the toggle. */
  .brand-rest { display: inline; }

  .site-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--white);
    padding: 4px 16px 16px;
    box-shadow: 0 12px 24px rgba(20, 44, 64, 0.16);
    display: none;
  }
  .site-nav.open { display: flex; }

  .site-nav a,
  .site-header.solid .site-nav a {
    color: var(--ink);
    font-size: 13px;
    letter-spacing: 0.14em;
    padding: 15px 2px;
    border-bottom: 1px solid var(--line);
  }

  .site-nav .book-link,
  .site-header.solid .site-nav .book-link {
    margin-top: 14px;
    border: 0;
    border-radius: 999px;
    background: var(--basin);
    color: var(--white);
    text-align: center;
    padding: 15px 18px;
  }
}

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

.hero {
  position: relative;
  min-height: 88vh;
  min-height: 88svh;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
  color: var(--white);
  background:
    linear-gradient(180deg, rgba(20, 44, 64, 0.1) 0%, rgba(20, 44, 64, 0) 55%, rgba(20, 44, 64, 0.35) 100%),
    url("images/hero.5fea6cf3.jpg") center / cover no-repeat,
    linear-gradient(180deg, #a8bfd0 0%, #49708e 38%, #2b4d6b 70%, #1c3a52 100%);
}

/* The wordmark is baked into the photo, so a portrait box crops it off at the
   sides. The landscape crop is only safe on landscape viewports: switch to the
   portrait crop on anything narrow OR taller than it is wide. Costs some
   sharpness on large tablets, which beats slicing the brand name in half. */
@media (max-width: 900px), (max-aspect-ratio: 20/19) {
  .hero {
    min-height: 70vh;
    min-height: 70svh;
    /* biased above centre so the frame holds hills and spit, not empty water */
    background:
      linear-gradient(180deg, rgba(20, 44, 64, 0.1) 0%, rgba(20, 44, 64, 0) 55%, rgba(20, 44, 64, 0.35) 100%),
      url("images/hero-mobile.fd0cda93.jpg") center 42% / cover no-repeat,
      linear-gradient(180deg, #a8bfd0 0%, #49708e 38%, #2b4d6b 70%, #1c3a52 100%);
  }
}

/* the tideline: the sandbar curve, redrawn to close the hero */
.tideline {
  position: absolute;
  bottom: -1px;
  left: 0;
  width: 100%;
  /* fixed 90px ate into the wordmark's descenders on narrow viewports, and
     squashed the wave into a choppy short period at phone widths */
  height: clamp(40px, 5.5vw, 90px);
  pointer-events: none;
}

/* ---------- quick facts (the spit) ---------- */

.facts {
  background: var(--sand);
  padding: 26px 0 30px;
}

.facts-tag {
  font-family: var(--heading);
  font-size: 13px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  text-align: center;
  color: var(--moss-deep);
  margin: 0 24px 18px;
}

.facts .wrap {
  display: flex;
  justify-content: center;
  gap: clamp(22px, 6vw, 84px);
  flex-wrap: wrap;
  text-align: center;
}

.fact .n {
  font-family: var(--heading);
  font-size: 34px;
  color: var(--basin);
  line-height: 1.1;
}

.fact-suffix { font-size: 0.55em; }

.fact .l {
  font-family: var(--heading);
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--moss-deep);
}

/* ---------- sections ---------- */

section { padding: 84px 0; }
.hero, .facts { padding-top: 0; }
.facts { padding: 26px 0 30px; }

/* separates the three consecutive white sections; sand was 1.35:1 on white
   and effectively invisible */
.divider {
  display: block;
  width: 100%;
  height: 46px;
  color: var(--sand-deep);
}

/* two-column feature */
.feature {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
}
.feature.flip .feature-media { order: 2; }

/* stack below 900 so tablet portrait does not centre a short image
   against a much taller text column and leave a dead gap */
@media (max-width: 900px) {
  .feature { grid-template-columns: 1fr; }
  .feature.flip .feature-media { order: 0; }
}

.feature-media {
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: linear-gradient(160deg, var(--water-soft), var(--basin));
  position: relative;
}

.feature-media.farm { background: linear-gradient(160deg, #9aa77e, var(--moss-deep)); }

.feature-media img { width: 100%; height: 100%; object-fit: cover; }

/* square source, wide frame: hold the egg basket rather than centre-cropping it away */
.feature-media.farm img { object-position: center 64%; }

.feature-body p + p { margin-top: 14px; }
.feature-body ul { margin: 16px 0 0 18px; color: var(--ink-soft); }

/* ---------- gallery ---------- */

.gallery-grid {
  margin-top: 36px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  list-style: none;
}

.shot {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--fog);
}

.shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.5s ease;
}

.shot:hover img { transform: scale(1.03); }

.shot figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 32px 14px 12px;
  font-family: var(--heading);
  font-size: 12.5px;
  line-height: 1.35;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--white);
  /* dense where the glyphs actually sit, so captions stay legible over pale photos */
  background: linear-gradient(
    180deg,
    rgba(20, 44, 64, 0) 0%,
    rgba(20, 44, 64, 0.72) 55%,
    rgba(20, 44, 64, 0.94) 100%
  );
}

/* six equal tiles: three clean rows of two here, two rows of three above */
@media (max-width: 900px) {
  .gallery-grid { grid-template-columns: 1fr 1fr; }
}

/* The sailing frame is a square shot of sails against forest, so a centred 4:3
   crop throws away the hulls and the water and keeps dead canopy. Biasing the
   crop downward at least keeps the boats in it. The real fix is a wider,
   higher resolution photo: this source is 853px and soft. */
.shot-sail img { object-position: center 64%; }

@media (max-width: 520px) {
  .gallery-grid { grid-template-columns: 1fr; gap: 12px; }
  .shot figcaption { font-size: 11.5px; letter-spacing: 0.1em; }
}

/* ---------- map ---------- */

.map-frame {
  margin-top: 44px;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 8px 28px rgba(20, 44, 64, 0.14);
}

.map-frame iframe {
  display: block;
  width: 100%;
  height: 340px;
  border: 0;
}

/* ---------- amenities ---------- */

.amenities { background: var(--fog); }

/* nine amenities fill three clean rows at three columns; auto-fit gave four
   and left the last row with a single item stranded */
.amenity-grid {
  margin-top: 36px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  list-style: none;
}

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

.amenity-note {
  margin-top: 20px;
  text-align: center;
  font-size: 15px;
  color: var(--ink-soft);
}

.amenity {
  background: var(--white);
  border-radius: var(--radius);
  padding: 18px 20px;
  display: flex;
  gap: 14px;
  align-items: center;
  box-shadow: 0 1px 5px rgba(20, 44, 64, 0.06);
}

.amenity svg { flex: none; width: 26px; height: 26px; color: var(--water); }

.amenity span { font-size: 15.5px; color: var(--ink); }

/* The tick sits at the end of the row, so the column reads as a list being
   checked off rather than a second icon competing with the themed one. */
.amenity .tick {
  margin-left: auto;
  width: 19px;
  height: 19px;
  color: var(--moss);
}

/* draw each tick on as its card arrives, which is what gives the column its
   ticking-off feel. Falls back to simply being visible without JS. */
.js .amenity .tick path {
  stroke-dasharray: 26;
  stroke-dashoffset: 26;
}
.js .reveal.in .amenity .tick path {
  animation: tick-draw 0.42s cubic-bezier(0.65, 0, 0.35, 1) forwards;
  animation-delay: calc(160ms + var(--i, 0) * 55ms);
}
@keyframes tick-draw { to { stroke-dashoffset: 0; } }

@media (prefers-reduced-motion: reduce) {
  .js .amenity .tick path { stroke-dashoffset: 0; animation: none; }
}

/* ---------- area guide ---------- */

.area-grid {
  margin-top: 36px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  list-style: none;
}

.area-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 26px 24px;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.area-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(20, 44, 64, 0.1);
}

/* one icon per place, in a tinted disc so it reads as a marker rather than
   decoration floating in the card */
.area-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  margin-bottom: 14px;
  border-radius: 50%;
  background: var(--sand);
  color: var(--moss-deep);
  transition: transform 0.25s ease, background-color 0.25s ease;
}

.area-icon svg { width: 23px; height: 23px; }

.area-card:hover .area-icon {
  transform: scale(1.08) rotate(-4deg);
  background: var(--sand-deep);
}

@media (prefers-reduced-motion: reduce) {
  .area-card:hover .area-icon { transform: none; }
}

.area-card .dist {
  font-family: var(--heading);
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--moss-deep);
}

.area-card h3 {
  font-size: 21px;
  color: var(--basin);
  margin: 6px 0 8px;
}

.area-card p { font-size: 15.5px; color: var(--ink-soft); }

/* ---------- reviews ---------- */

.reviews { background: var(--basin); color: var(--fog); }

.reviews .eyebrow { color: var(--sand-deep); }
.reviews .section-title { color: var(--white); }
.reviews .section-title .script { color: var(--sand-deep); }

/* auto-fit gave 2 columns across the whole tablet band, stranding the third
   card alone. Go 3-up or 1-up, never 2-up. */
.review-grid {
  margin-top: 36px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}

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

.review {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  padding: 28px 26px;
}

.review .quote-mark {
  display: block;
  font-family: var(--script);
  font-size: 40px;
  color: var(--sand-deep);
  line-height: 1;
}

.review blockquote p {
  font-size: 16px;
  margin: 10px 0 16px;
  color: rgba(242, 246, 248, 0.94);
}

.review figcaption {
  font-family: var(--heading);
  font-size: 12.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--water-light);
}

/* ---------- booking ---------- */

/* the map already reads as a full stop, so the old 84px below it left a
   band of dead white before the footer */
.booking { text-align: center; padding-bottom: 44px; }

.booking .section-title .script { color: var(--water); }

.book-buttons {
  margin-top: 34px;
  display: flex;
  gap: 16px;
  justify-content: center;
  flex-wrap: wrap;
}

.btn {
  font-family: var(--heading);
  font-size: 14px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 16px 34px;
  border-radius: 999px;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

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

/* ---------- offer ---------- */

.offer {
  max-width: 620px;
  margin: 0 auto 30px;
  padding: 28px 30px 26px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(20, 44, 64, 0.08);
  text-align: left;
}

.offer-tag {
  font-family: var(--heading);
  font-size: 12px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--moss-deep);
  margin: 0 0 8px;
}

.offer-head {
  font-family: var(--heading);
  font-size: clamp(20px, 2.4vw, 24px);
  font-weight: 400;
  color: var(--basin);
  margin: 0 0 16px;
}

.offer-stack { list-style: none; margin: 0 0 18px; padding: 0; }

.offer-stack li {
  position: relative;
  padding-left: 30px;
  margin-bottom: 9px;
  font-size: 16px;
  color: var(--ink);
}

/* the tick is decorative, so it is drawn rather than set as text */
.offer-stack li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 17px;
  height: 10px;
  border-left: 2.4px solid var(--moss);
  border-bottom: 2.4px solid var(--moss);
  transform: rotate(-45deg) scale(0.82);
  transform-origin: center;
}

.offer-flag {
  margin: 0 0 14px;
  padding: 11px 14px;
  border-radius: 10px;
  background: var(--sand);
  color: var(--moss-deep);
  font-size: 15px;
  font-weight: 700;
}

.offer-proof { margin: 0; font-size: 15px; color: var(--ink-soft); line-height: 1.6; }
.offer-proof strong { color: var(--basin); }

@media (max-width: 600px) {
  .offer { padding: 24px 20px; }
}

.btn-primary {
  background: var(--basin);
  color: var(--white);
  box-shadow: 0 6px 18px rgba(20, 44, 64, 0.25);
}

.btn-secondary {
  border: 1.5px solid var(--basin);
  color: var(--basin);
}

.booking .note { margin-top: 22px; font-size: 15px; color: var(--ink-soft); }

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

.site-footer {
  background: var(--basin-deep);
  color: var(--water-light);
  padding: 54px 0 40px;
  text-align: center;
}

.site-footer .brand-foot {
  font-family: var(--script);
  font-size: 30px;
  line-height: 1.2;
  color: var(--sand);
}

.site-footer .foot-sub {
  font-family: var(--heading);
  font-size: 12px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  margin-top: 4px;
  color: var(--sand-deep);
}

.site-footer p { font-size: 14px; margin-top: 22px; }
.site-footer .brand-foot, .site-footer .foot-sub { margin-top: 0; }
.site-footer .copyright { color: var(--water-soft); }

.site-footer a { color: var(--sand-deep); text-decoration: underline; text-underline-offset: 3px; }
.site-footer a:hover { color: var(--white); }

/* ---------- reveal on scroll (only when JS is running) ---------- */

.js .reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

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

/* Children of a revealed block cascade in rather than arriving as one slab.
   --i is set by script.js; the cap keeps a long grid from feeling slow. */
.js .stagger > * {
  opacity: 0;
  transform: translateY(16px);
}

.js .reveal.in .stagger > *,
.js .stagger.in > * {
  opacity: 1;
  transform: none;
  transition: opacity 0.55s ease, transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: calc(var(--i, 0) * 55ms);
}

/* Page-load settle. Kept on the wrapper rather than body so the fixed header
   is not caught in it, and it clears itself once run. */
.js .page-fade { animation: page-in 0.7s ease both; }
@keyframes page-in { from { opacity: 0; } to { opacity: 1; } }

/* Scroll-linked drift on the decorative waves. Deliberately not the hero photo:
   its crop is tuned so the wordmark baked into the image is not sliced, and
   shifting it would cut the brand name. These are pure decoration, so they can
   move freely. script.js writes --drift as a fraction from -1 to 1. */
.tideline,
.divider { will-change: transform; }

.js .tideline { transform: translateX(calc(var(--drift, 0) * 22px)); }
.js .divider { transform: translateX(calc(var(--drift, 0) * -16px)); }

@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; }
  .js .stagger > * { opacity: 1; transform: none; transition: none; }
  .js .page-fade { animation: none; }
  .js .tideline, .js .divider { transform: none; }
}

/* ---------- focus ---------- */

/* two-tone ring so focus stays visible on photos, navy, and white alike */
a:focus-visible,
button:focus-visible {
  outline: 3px solid var(--moss);
  outline-offset: 3px;
  border-radius: 2px;
  box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.92);
}

.reviews a:focus-visible,
.site-footer a:focus-visible {
  outline-color: var(--sand);
  box-shadow: 0 0 0 6px rgba(20, 44, 64, 0.85);
}

/* ---------- phones ---------- */

@media (max-width: 640px) {
  section { padding: 58px 0; }

  .wrap { padding: 0 20px; }

  .facts { padding: 22px 0 26px; }

  .facts-tag {
    font-size: 11.5px;
    letter-spacing: 0.18em;
    margin: 0 20px 16px;
  }

  /* four across leaves the last stat stranded on its own row, so pair them up */
  .facts .wrap {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px 12px;
  }

  .fact .n { font-size: 30px; }
  .fact .l { font-size: 11px; letter-spacing: 0.16em; }

  .lede { font-size: 17.5px; }

  .section-title { margin-bottom: 14px; }

  .feature-media { aspect-ratio: 3 / 2; }

  .amenity { padding: 15px 16px; }
  .amenity span { font-size: 15px; }

  .area-card { padding: 22px 20px; }

  .review { padding: 24px 22px; }

  .book-buttons { flex-direction: column; align-items: center; gap: 12px; }
  .btn { width: 100%; max-width: 330px; text-align: center; padding: 16px 24px; }

  .map-frame { margin-top: 32px; }
  .map-frame iframe { height: 260px; }

  .site-footer { padding: 44px 0 34px; }
  .site-footer p { margin-top: 18px; }
}

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

@media print {
  .site-header, .nav-toggle, .map-frame, .skip-link, .tideline, .divider { display: none !important; }
  body { font-size: 11pt; color: #000; background: #fff; }
  .hero { min-height: 0; height: 240px; }
  section { padding: 18pt 0; break-inside: avoid; }
  .reviews, .amenities, .facts { background: #fff !important; color: #000 !important; }
  .reviews .section-title, .review blockquote p, .review figcaption { color: #000 !important; }
  .site-footer { background: #fff; color: #000; }
  .site-footer a { color: #000; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; }
  .js .reveal { opacity: 1 !important; transform: none !important; }
}
