/* ============================================================
   John Lee Haus Inn — design system
   601 W Oak Street, Arcadia FL · est. 1892

   Palette is drawn from the property's own vernacular:
   Florida Cracker heart pine, galvanized tin roofing,
   limewashed walls, and deep veranda shade.

   RESAMPLED 2026-09-08 from five frames of the actual hero
   footage (median-cut quantised). The real property reads
   OLIVE and warm, not the cool blue-green first guessed:
   shadows #273217, mids #637836, highlights #ACB894, and a
   rust #582B14 in the brick path. The greens below were
   shifted accordingly; the heart-pine accent was already
   sitting between the footage's rust and warm brown, so it
   stands. Change them HERE only.
   ============================================================ */

/* ---------- 1. Tokens ---------- */
:root {
  /* -- surface & ink -- */
  --limewash:      #F1EFE6;   /* primary page ground, faint green cast */
  --limewash-lift: #F9F8F1;   /* raised cards, inputs */
  --limewash-sink: #E5E3D6;   /* inset bands, hairline fills */

  --pine-900:      #1B2015;   /* veranda shadow — near-black green */
  --pine-800:      #242B1C;
  --pine-700:      #303826;
  --pine-500:      #4C5738;
  --pine-300:      #646D58;

  --tin-500:       #98A184;   /* galvanized roofing */
  --tin-300:       #BCC1AC;
  --tin-100:       #DBDCCD;

  /* -- accent: oxidized heart pine -- */
  --heart-700:     #7A4419;
  --heart-600:     #975A2A;
  --heart-400:     #C08A52;
  --heart-100:     #EFE0CF;

  /* -- semantic roles (use THESE in components) -- */
  --ground:        var(--limewash);
  --surface:       var(--limewash-lift);
  --surface-sink:  var(--limewash-sink);
  --ink:           var(--pine-900);
  --ink-soft:      var(--pine-700);
  --ink-mute:      var(--pine-300);
  --rule:          var(--tin-100);
  --rule-strong:   var(--tin-300);
  --accent:        var(--heart-600);
  --accent-deep:   var(--heart-700);
  --accent-wash:   var(--heart-100);

  /* on dark bands */
  --ground-dark:   var(--pine-900);
  --ink-on-dark:   #EFEDE2;
  --ink-on-dark-soft: #BFC4AE;
  --rule-on-dark:  rgba(239,237,226,.16);

  /* -- type -- */
  --display: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --body:    "Jost", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  --t-xs:   0.75rem;
  --t-sm:   0.875rem;
  --t-base: 1.0rem;
  --t-md:   1.125rem;
  --t-lg:   clamp(1.25rem, 1.6vw, 1.5rem);
  --t-xl:   clamp(1.6rem,  2.6vw, 2.25rem);
  --t-2xl:  clamp(2.1rem,  4.2vw, 3.4rem);
  --t-3xl:  clamp(2.8rem,  7vw,   5.5rem);

  --lede:   clamp(1.05rem, 1.35vw, 1.28rem);

  /* -- space -- */
  --s-1: .25rem;  --s-2: .5rem;   --s-3: .75rem;  --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;    --s-8: 4rem;
  --s-9: 6rem;    --s-10: 8rem;

  --band:  clamp(4rem, 10vw, 8rem);   /* vertical rhythm between sections */
  --gutter: clamp(1.25rem, 5vw, 4rem);

  --max:      1280px;
  --max-text: 68ch;
  --max-narrow: 640px;

  --radius: 2px;                       /* deliberately near-square: historic, not app-like */
  --ease: cubic-bezier(.22,.61,.36,1);
}

/* ---------- 2. Reset & base ---------- */
*, *::before, *::after { box-sizing: border-box; }

/* A class that sets `display` beats the UA sheet's [hidden]{display:none},
   which silently leaves a "hidden" full-screen overlay on top of the page
   swallowing every click. Anything hidden must actually be gone. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--t-base);
  font-weight: 400;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

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

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: -0.01em;
  margin: 0;
  text-wrap: balance;
}
h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-2xl); }
h3 { font-size: var(--t-xl); }
h4 { font-size: var(--t-lg); }

p { margin: 0; }
p, li { max-width: var(--max-text); }

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

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

::selection { background: var(--accent-wash); color: var(--pine-900); }

/* ---------- 3. Utility type ---------- */
.eyebrow {
  font-family: var(--body);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0;
}
.eyebrow--mute { color: var(--ink-mute); }
.eyebrow--on-dark { color: var(--heart-400); }

.lede {
  font-size: var(--lede);
  line-height: 1.55;
  color: var(--ink-soft);
  font-weight: 300;
}

.display-italic { font-style: italic; color: var(--ink-soft); }

.script-rule {
  width: 56px; height: 1px; background: var(--accent);
  border: 0; margin: 0;
}

/* ---------- 4. Layout primitives ---------- */
.wrap {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: 760px; }

.band { padding-block: var(--band); }
.band--tight { padding-block: clamp(2.5rem, 6vw, 4.5rem); }

/* A floating booking panel is pulled up into the hero above it, so its own
   section must not also reserve a full band underneath: the following
   section brings its own top padding and the first card adds more again.
   Left alone those stack into roughly 13rem of empty page below the panel. */
.band--flush { padding-bottom: 0; }

.band--dark {
  background: var(--ground-dark);
  color: var(--ink-on-dark);
}
.band--dark h1, .band--dark h2, .band--dark h3, .band--dark h4 { color: var(--ink-on-dark); }
.band--dark .lede { color: var(--ink-on-dark-soft); }
.band--dark .rule { background: var(--rule-on-dark); }

.band--sink { background: var(--surface-sink); }

.stack { display: flex; flex-direction: column; }
.stack--2 { gap: var(--s-2); }
.stack--3 { gap: var(--s-3); }
.stack--4 { gap: var(--s-4); }
.stack--5 { gap: var(--s-5); }
.stack--6 { gap: var(--s-6); }
.stack--7 { gap: var(--s-7); }

.rule { height: 1px; background: var(--rule); border: 0; width: 100%; }

/* The header is fixed, so any anchor target has to reserve room for it or
   the thing you jumped to lands underneath it. Applies to the room
   anchors (#hearth-room) and the booking panel (#book) alike. */
[id] { scroll-margin-top: 96px; }

/* two-column editorial split — the core alternating pattern */
.split {
  display: grid;
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; }
  .split--flip > :first-child { order: 2; }
  .split--wide-media { grid-template-columns: 1.15fr .85fr; }
}

.section-head {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  max-width: 46ch;
  margin-bottom: var(--s-7);
}
.section-head--center { margin-inline: auto; text-align: center; align-items: center; }

/* ---------- 5. Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6em;
  font-family: var(--body);
  font-size: var(--t-sm);
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  padding: 1.05em 2.1em;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
  text-align: center;
  line-height: 1;
}
.btn--solid { background: var(--accent); color: #FFF8F0; border-color: var(--accent); }
.btn--solid:hover { background: var(--accent-deep); border-color: var(--accent-deep); }

.btn--outline { background: transparent; color: var(--ink); border-color: var(--rule-strong); }
.btn--outline:hover { border-color: var(--ink); background: var(--ink); color: var(--limewash); }

.btn--on-dark { background: transparent; color: var(--ink-on-dark); border-color: rgba(239,237,226,.4); }
.btn--on-dark:hover { background: var(--ink-on-dark); color: var(--pine-900); border-color: var(--ink-on-dark); }

.btn--ghost {
  padding: 0; border: 0; background: none; color: var(--accent);
  border-bottom: 1px solid currentColor; border-radius: 0; padding-bottom: .35em;
}
.btn--ghost:hover { color: var(--accent-deep); }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* ---------- 6. Header ---------- */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  transition: background-color .4s var(--ease), border-color .4s var(--ease);
  border-bottom: 1px solid transparent;
}
.site-header__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  padding-block: var(--s-4);
}
/* The wordmark is an image, not type. Its hairlines are ~1/210 of its
   width, so below about 240px they fall under one device pixel and render
   as grey mush - do NOT shrink it to save header height. 1x/2x/3x sources
   keep it crisp on high-DPI screens. */
.brand {
  display: block;
  line-height: 0;
  flex: 0 0 auto;
}
.brand__mark {
  display: block;
  width: 240px;
  max-width: 46vw;
  height: auto;
  transition: opacity .4s var(--ease);
}
/* Phones. The warning above is about 1x screens: at 1/210 of its width a
   hairline needs the width to stay large to survive a single device pixel.
   Every phone is 2x or 3x, so 148px still puts ~2-3 device pixels on each
   hairline while giving back roughly 30px of a permanently fixed header. */
@media (max-width: 520px) {
  .brand__mark   { width: 148px; }
  .site-header__in { padding-block: var(--s-3); }
}

/* Two states: white over the dark hero, ink once the header goes solid.
   The dark copy is stacked on top of the light one and cross-faded. */
.brand__mark--dark {
  position: absolute;
  top: 0; left: 0;
  opacity: 0;
  pointer-events: none;
}
.brand { position: relative; }
.site-header.is-solid .brand__mark--light,
.site-header.is-static .brand__mark--light { opacity: 0; }
.site-header.is-solid .brand__mark--dark,
.site-header.is-static .brand__mark--dark { opacity: 1; }

.nav { display: none; }
@media (min-width: 940px) {
  .nav { display: flex; align-items: center; gap: var(--s-6); }
}
.nav a {
  font-size: var(--t-xs);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-on-dark);
  padding-block: .4em;
  border-bottom: 1px solid transparent;
  transition: color .4s var(--ease), border-color .25s var(--ease);
}
.nav a:hover, .nav a[aria-current="page"] { border-bottom-color: currentColor; }

/* scrolled / solid state */
.site-header.is-solid {
  background: var(--limewash);
  border-bottom-color: var(--rule);
}
.site-header.is-solid .nav a { color: var(--ink); }
.site-header.is-solid .nav-toggle span { background: var(--ink); }

/* pages without a dark hero start solid */
.site-header.is-static { position: sticky; background: var(--limewash); border-bottom-color: var(--rule); }
.site-header.is-static .nav a { color: var(--ink); }
.site-header.is-static .nav-toggle span { background: var(--ink); }

.nav-toggle {
  display: inline-flex; flex-direction: column; justify-content: center; gap: 5px;
  width: 40px; height: 40px; padding: 8px; background: none; border: 0; cursor: pointer;
}
@media (min-width: 940px) { .nav-toggle { display: none; } }
/* Phones: a smaller icon inside a BIGGER hit area - 40px was under the 44px
   touch minimum. Declared after the base rule above, not before it: equal
   specificity means source order decides. */
@media (max-width: 520px) {
  .nav-toggle { width: 44px; height: 44px; padding: 13px 12px; gap: 4px; }
}
.nav-toggle span {
  display: block; height: 1px; width: 100%; background: var(--ink-on-dark);
  transition: transform .3s var(--ease), opacity .2s var(--ease), background-color .4s var(--ease);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.mobile-nav {
  position: fixed; inset: 0; z-index: 99;
  background: var(--pine-900); color: var(--ink-on-dark);
  display: flex; flex-direction: column; gap: var(--s-5);
  /* Centred, but SAFELY. A plain `center` on a column flexbox overflows in
     BOTH directions once the content is taller than the screen, so the first
     link is pushed above the top edge where nothing can scroll it back into
     view. That is exactly what happened on a 568px-tall phone the moment the
     nav went from five links to six. `safe center` falls back to flex-start
     rather than overflowing; the scroll below is the belt to that braces. */
  justify-content: safe center;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  /* Clear the fixed header so the first link never sits under the wordmark. */
  padding: 6.5rem var(--gutter) var(--gutter);
  transform: translateY(-100%);
  transition: transform .5s var(--ease);
}
/* Short screens - landscape, or a small phone with the URL bar showing. */
@media (max-height: 640px) {
  .mobile-nav { gap: var(--s-3); padding-top: 5.5rem; }
  .mobile-nav a { font-size: 1.6rem; padding-bottom: var(--s-2); }
}
.mobile-nav.is-open { transform: translateY(0); }
.mobile-nav a {
  font-family: var(--display); font-size: 2rem; line-height: 1.15;
  border-bottom: 1px solid var(--rule-on-dark); padding-bottom: var(--s-3);
}
.mobile-nav .btn { margin-top: var(--s-4); align-self: flex-start; }
body.nav-open { overflow: hidden; }

/* ---------- 7. Hero ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-items: end;
  background: var(--pine-900);
  overflow: hidden;
}
.hero__media {
  position: absolute; inset: 0;
  /* Fallback ground so the hero reads as intentional even with no
     poster or video present yet. */
  background:
    radial-gradient(120% 90% at 70% 15%, #3A4229 0%, transparent 60%),
    linear-gradient(160deg, #2A3020 0%, #1B2015 70%);
}
.hero__media video, .hero__media img, .hero__media picture {
  width: 100%; height: 100%; object-fit: cover;
}
.hero__poster { position: absolute; inset: 0; display: block; z-index: 0; }
.hero__poster img { width: 100%; height: 100%; object-fit: cover; }

/* Visible by default. Nothing may gate the hero's visibility on JavaScript
   or on a media event firing - the browser's own poster attribute covers
   the buffering gap. */
.hero__video {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; object-fit: cover;
}
/* Scrim. The footage is bright midday daylight — pale green tin roof, white
   porch rail — so a gentle wash is not enough to hold text. Two layers: one
   raking in from the side the copy sits on, one lifting from the bottom.
   Tuned against the real hero frame, not guessed. */
.hero__media::after {
  content: ""; position: absolute; inset: 0; z-index: 2;
  background:
    linear-gradient(105deg,
      rgba(19,22,14,.94) 0%,
      rgba(19,22,14,.78) 26%,
      rgba(19,22,14,.42) 52%,
      rgba(19,22,14,.10) 78%),
    linear-gradient(to top,
      rgba(19,22,14,.82) 0%,
      rgba(19,22,14,.30) 42%,
      rgba(19,22,14,.18) 68%,
      rgba(19,22,14,.55) 100%);
}

/* On a phone the copy runs the full width, so a side-raking scrim does
   nothing — it has to come straight up, and harder. */
@media (max-width: 900px) {
  .hero__media::after {
    background:
      linear-gradient(to top,
        rgba(19,22,14,.94) 0%,
        rgba(19,22,14,.88) 34%,
        rgba(19,22,14,.62) 62%,
        rgba(19,22,14,.42) 82%,
        rgba(19,22,14,.62) 100%);
  }
  /* Favour the gable and roofline over the foreground path when the frame
     is cropped to a tall viewport. */
  .hero__media video, .hero__media picture, .hero__poster img {
    object-position: center 38%;
  }
}
.hero__body {
  position: relative;
  /* Above the scrim (z-index 2 inside .hero__media). Without this the
     scrim paints over the copy and washes it out. */
  z-index: 3;
  /* Extra bottom padding reserves room for .scroll-cue. */
  padding-block: clamp(3rem, 10vh, 7rem) clamp(6rem, 16vh, 10rem);
  color: var(--ink-on-dark);
  display: flex; flex-direction: column; gap: var(--s-5);
  max-width: 780px;
}
.hero__body h1 { color: var(--ink-on-dark); }
.hero__body .lede { color: var(--ink-on-dark-soft); max-width: 46ch; }

.hero--interior { min-height: 62svh; align-items: center; }

/* A tall portrait photograph inside a 62svh banner survives only as a narrow
   horizontal band, and centred that band lands above the people in the frame.
   Pulling the window up puts the subject back in the middle. Below 900px the
   banner is taller than it is wide, the whole photograph fits, and this does
   nothing. */
.hero__media--focus-upper img { object-position: 50% 40%; }
.hero--interior .hero__body { padding-block: clamp(7rem, 15vh, 10rem) var(--s-7); }

/* The scrim above was tuned for the home page: full-height hero, bright
   midday VIDEO, copy over the left third. An interior hero is a still at
   62svh, and those two gradients multiply - .94 over .82 is ~99% opaque
   where the copy sits, so the photograph vanishes and the header reads as a
   plain black bar. Same shape, much lighter, so the picture survives and
   white text still holds. */
.hero--interior .hero__media::after {
  background:
    linear-gradient(105deg,
      rgba(19,22,14,.86) 0%,
      rgba(19,22,14,.64) 34%,
      rgba(19,22,14,.24) 64%,
      rgba(19,22,14,0)   88%),
    linear-gradient(to top,
      rgba(19,22,14,.38) 0%,
      rgba(19,22,14,.06) 45%,
      rgba(19,22,14,.04) 70%,
      rgba(19,22,14,.22) 100%);
}

.scroll-cue {
  position: absolute; z-index: 3; bottom: var(--s-5); left: 50%; transform: translateX(-50%);
  font-size: var(--t-xs); letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-on-dark-soft); display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
}
.scroll-cue::after {
  content: ""; width: 1px; height: 40px;
  background: linear-gradient(to bottom, var(--ink-on-dark-soft), transparent);
}

/* ---------- 8. Cards & media ---------- */
.figure { display: flex; flex-direction: column; gap: var(--s-3); }
.figure img, .figure video { width: 100%; border-radius: var(--radius); }
.figure figcaption { font-size: var(--t-sm); color: var(--ink-mute); }

.ratio { position: relative; overflow: hidden; border-radius: var(--radius); background: var(--surface-sink); }
.ratio > * { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* <picture> is only a wrapper — it takes the rule above, but the <img> that
   actually paints is a level deeper and would otherwise size naturally and
   letterbox inside the frame. */
.ratio > picture > img,
.figure picture > img,
.zoom picture > img {
  display: block; width: 100%; height: 100%; object-fit: cover;
}
.ratio--4x5  { aspect-ratio: 4 / 5; }
.ratio--3x2  { aspect-ratio: 3 / 2; }
.ratio--1x1  { aspect-ratio: 1 / 1; }
.ratio--16x9 { aspect-ratio: 16 / 9; }

/* ---------- Form switch (contact page) ----------
   Two forms share one panel: send a note, or get the pricing guide. The second
   tab is a real link, upgraded by site.js into an in-place swap, so the guide
   is reachable with or without JavaScript. */
.switch {
  display: flex;
  gap: 0;
  margin-bottom: var(--s-5);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}
.switch__btn {
  flex: 1 1 0;
  min-width: 0;
  padding: var(--s-3) var(--s-3);
  border: 0;
  background: transparent;
  color: var(--ink-mute);
  font: inherit;
  font-size: var(--t-xs);
  letter-spacing: .08em;
  text-transform: uppercase;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease);
}
.switch__btn + .switch__btn { border-left: 1px solid var(--rule); }
.switch__btn:hover { color: var(--ink); }
.switch__btn.is-on {
  background: var(--pine-900);
  color: var(--limewash);
}

/* ---------- Gallery scroller ----------
   A horizontal filmstrip: one row of photographs you swipe or drag through,
   left to right. Every photograph keeps its own shape - the row has a single
   height and each tile's WIDTH comes from its aspect ratio, so nothing is
   cropped and nothing is letterboxed.

   --ar (width/height) is written onto each tile by build_pages.py from the
   dimensions in media-manifest.json.

   Scrolling needs no JavaScript. site.js only adds the two arrows, for people
   on a mouse who have nothing to swipe. */
.gallery { position: relative; --gal-h: clamp(17rem, 46vh, 30rem); }

.gallery__track {
  display: flex;
  gap: clamp(.75rem, 1.4vw, 1.1rem);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  /* Runs out through the page gutter on both sides, so the strip reads as a
     band rather than a boxed-in row, while the first photograph still lines
     up with the text above it. */
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  padding-block: 2px;                 /* room for the focus ring */
  scrollbar-width: none;              /* Firefox */
}
.gallery__track::-webkit-scrollbar { display: none; }

.gallery__item {
  flex: 0 0 auto;
  height: var(--gal-h);
  width: calc(var(--ar) * var(--gal-h));
  max-width: calc(100vw - var(--gutter) * 2);
  scroll-snap-align: center;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--surface-sink);
}
.gallery__item picture,
.gallery__item img {
  display: block; width: 100%; height: 100%; object-fit: cover;
}

/* Arrows. Shipped hidden and revealed by site.js — see the note there. */
.gallery__nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 2.75rem; height: 2.75rem;
  display: grid; place-items: center;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--limewash);
  color: var(--ink);
  font-size: 1.25rem; line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
  transition: opacity .25s var(--ease), background .25s var(--ease);
}
.gallery__nav:hover { background: #fff; }
.gallery__nav:disabled { opacity: 0; pointer-events: none; }
.gallery__nav--prev { left: calc(var(--gutter) * -1 + .25rem); }
.gallery__nav--next { right: calc(var(--gutter) * -1 + .25rem); }

/* On a touch screen the arrows are redundant and would only cover a
   photograph, and a coarse pointer can swipe. */
@media (pointer: coarse) {
  .gallery__nav { display: none; }
}

.grid { display: grid; gap: clamp(1.25rem, 3vw, 2.25rem); grid-template-columns: 1fr; }
@media (min-width: 640px)  { .grid--2 { grid-template-columns: repeat(2, 1fr); } }
/* Mobile-first order matters here: these two carry the same specificity, so
   whichever is declared last wins wherever both match. With the 900px rule
   written first, every .grid--3 on the site rendered as two columns on
   desktop - the homepage rooms, both events grids, the story gallery and the
   wedding packages. Narrow breakpoint first, wide breakpoint last. */
@media (min-width: 640px)  { .grid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px)  { .grid--3 { grid-template-columns: repeat(3, 1fr); } }


.card { display: flex; flex-direction: column; gap: var(--s-4); }
.card__body { display: flex; flex-direction: column; gap: var(--s-3); }
.card__meta {
  font-size: var(--t-xs); letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-mute); display: flex; flex-wrap: wrap; gap: var(--s-3);
}
.card a.card__title:hover { color: var(--accent); }

/* image hover — subtle, historic, not bouncy */
.zoom { overflow: hidden; }
.zoom img, .zoom video { transition: transform .9s var(--ease); }
.zoom:hover img, .zoom:hover video { transform: scale(1.035); }

/* ---------- 9. Specs list (rooms, packages) ---------- */
.specs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.specs li {
  display: flex; justify-content: space-between; gap: var(--s-4);
  padding-block: var(--s-3); border-bottom: 1px solid var(--rule);
  font-size: var(--t-sm); max-width: none;
}
.specs li:last-child { border-bottom: 0; }
.specs dt, .specs .k {
  letter-spacing: .14em; text-transform: uppercase; font-size: var(--t-xs); color: var(--ink-mute);
}
.specs .v { text-align: right; color: var(--ink); }

.tags { display: flex; flex-wrap: wrap; gap: var(--s-2); list-style: none; margin: 0; padding: 0; }
.tags li {
  font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase;
  padding: .45em .9em; border: 1px solid var(--rule-strong); border-radius: var(--radius);
  color: var(--ink-soft); max-width: none;
}

/* ---------- 10. Booking widget frame ---------- */
.booking-frame {
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  padding: clamp(1.25rem, 3vw, 2rem);
  display: flex; flex-direction: column; gap: var(--s-4);
}
.booking-frame__label {
  font-size: var(--t-xs); letter-spacing: .2em; text-transform: uppercase; color: var(--ink-mute);
}
.booking-frame--float {
  position: relative; margin-top: calc(var(--s-7) * -1); z-index: 5;
  box-shadow: 0 18px 50px -28px rgba(26,33,28,.5);
}

/* ---------- 11. Forms ---------- */
.field { display: flex; flex-direction: column; gap: var(--s-2); }
.field label {
  font-size: var(--t-xs); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-mute);
}
.field input, .field textarea, .field select {
  font-family: var(--body); font-size: var(--t-base); color: var(--ink);
  background: var(--surface); border: 1px solid var(--rule-strong); border-radius: var(--radius);
  padding: .85em 1em; width: 100%;
  transition: border-color .25s var(--ease);
}
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--accent); outline: none;
}
.field textarea { min-height: 150px; resize: vertical; }
.form-grid { display: grid; gap: var(--s-4); grid-template-columns: 1fr; }
@media (min-width: 700px) { .form-grid--2 { grid-template-columns: 1fr 1fr; } .form-grid .full { grid-column: 1 / -1; } }

/* A checkbox has to opt out of the full-width text-input styling above, or
   it stretches the width of the form and stops looking like a checkbox. */
.field--check { flex-direction: row; align-items: center; gap: var(--s-3); }
.field--check input[type="checkbox"] {
  width: 1.15em; height: 1.15em; flex: none; padding: 0;
  accent-color: var(--heart-600);
}
.field--check label {
  font-size: var(--t-sm); text-transform: none; letter-spacing: 0;
  color: var(--ink); cursor: pointer;
}

/* ---------- 12. Footer ---------- */
.site-footer {
  background: var(--pine-900);
  color: var(--ink-on-dark);
  padding-block: var(--s-9) var(--s-6);
}
.site-footer a { color: var(--ink-on-dark-soft); transition: color .25s var(--ease); }
.site-footer a:hover { color: var(--ink-on-dark); }
.footer-grid {
  display: grid; gap: var(--s-7); grid-template-columns: 1fr;
  padding-bottom: var(--s-7); border-bottom: 1px solid var(--rule-on-dark);
}
@media (min-width: 780px) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.footer-col h4 {
  font-family: var(--body); font-size: var(--t-xs); font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase; color: var(--heart-400); margin-bottom: var(--s-4);
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.footer-col li { font-size: var(--t-sm); max-width: none; }
.colophon {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s-4);
  padding-top: var(--s-5); font-size: var(--t-xs); color: var(--ink-on-dark-soft);
  letter-spacing: .06em;
}

/* ---------- 13. Motion ---------- */
/* Reveal is opt-in: the ground state is VISIBLE. The .js class is set by an
   inline script in <head>, so if JavaScript fails or is blocked, every
   section still renders instead of staying permanently invisible. */
.js .reveal { opacity: 0; transform: translateY(18px); }
.js .reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity .9s var(--ease), transform .9s var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .reveal, .reveal.is-in { opacity: 1; transform: none; transition: none; }
  .zoom img, .zoom video, .zoom:hover img, .zoom:hover video { transition: none; transform: none; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---------- 14. Helpers ---------- */
.skip-link {
  position: absolute; left: var(--s-4); top: var(--s-4); z-index: 200;
  background: var(--limewash); color: var(--ink); padding: .75em 1.25em;
  border-radius: var(--radius); transform: translateY(-200%);
}
.skip-link:focus { transform: translateY(0); }

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

.empty-state {
  border: 1px dashed var(--rule-strong); border-radius: var(--radius);
  padding: var(--s-8) var(--s-5); text-align: center; color: var(--ink-mute);
  display: flex; flex-direction: column; gap: var(--s-3); align-items: center;
}

/* Numbered placeholder tile. The big number IS the filename: drop
   `04.jpg` into _media-source/img/ and this tile becomes that photo. */
.placeholder__num {
  font-family: var(--display);
  font-size: clamp(2.2rem, 5vw, 3.4rem);
  line-height: 1;
  letter-spacing: .02em;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}
.placeholder__label {
  font-size: var(--t-xs); letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-mute); margin-top: .55em;
}
.placeholder__hint {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: .62rem; letter-spacing: .04em; text-transform: none;
  color: var(--ink-mute); opacity: .7; margin-top: .5em;
}
.hero__media .placeholder__num,
.hero__media .placeholder__label,
.hero__media .placeholder__hint { color: rgba(239,237,226,.42); }

/* placeholder tile — stands in until real photography/footage lands */
.placeholder {
  background:
    repeating-linear-gradient(135deg, var(--limewash-sink) 0 12px, var(--limewash) 12px 24px);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  color: var(--ink-mute); font-size: var(--t-xs);
  letter-spacing: .18em; text-transform: uppercase; text-align: center; padding: var(--s-4);
}

/* Inside a hero the ground is dark, so the placeholder has to be too —
   and its caption sits behind the headline, so keep it very quiet. */
.hero__media .placeholder {
  height: 100%;
  background:
    repeating-linear-gradient(135deg, #232C26 0 14px, #1A211C 14px 28px);
  color: rgba(237, 237, 229, .22);
  align-content: end;
  padding-bottom: var(--s-6);
}

/* ============================================================
   15. Admin — staff-only event editor (admin.html)
   Utilitarian on purpose: this is operated, not read. It borrows
   the site's tokens so it never feels like a different product.
   ============================================================ */
.admin { min-height: 70svh; }
.admin__center { display: grid; place-items: center; min-height: 46svh; }

.admin__bar {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  align-items: flex-end; justify-content: space-between;
  padding-bottom: var(--s-5); border-bottom: 1px solid var(--rule);
  margin-bottom: var(--s-5);
}
.admin__bar-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); }

.btn--sm { padding: .7em 1.2em; font-size: var(--t-xs); }
.btn--danger { color: #8E3B1F; border-color: #D8B6A6; }
.btn--danger:hover { background: #8E3B1F; border-color: #8E3B1F; color: #FFF8F0; }

.hint { font-size: var(--t-sm); color: var(--ink-mute); max-width: none; }
.hint code {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: .92em; background: var(--surface-sink); padding: .1em .35em; border-radius: 2px;
}
.form-error {
  font-size: var(--t-sm); color: #8E3B1F;
  background: #F6E7DF; border: 1px solid #E4C4B4;
  padding: .7em .9em; border-radius: var(--radius); max-width: none;
}

/* --- event rows --- */
.admin__list { display: flex; flex-direction: column; }
.row {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  align-items: center; justify-content: space-between;
  padding: var(--s-4) 0; border-bottom: 1px solid var(--rule);
}
.row--past { opacity: .62; }
.row__main { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; }
.row__main h3 { font-size: var(--t-md); font-family: var(--body); font-weight: 500; }
.row__meta {
  display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center;
  font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase; color: var(--ink-mute);
  max-width: none;
}
.row__actions { display: flex; flex-wrap: wrap; gap: var(--s-2); }

.tag {
  padding: .2em .6em; border-radius: 2px; letter-spacing: .1em;
  font-size: var(--t-xs); text-transform: uppercase;
}
.tag--live  { background: var(--ok-soft, #DFE9DA); color: #3D6B33; }
.tag--draft { background: var(--surface-sink); color: var(--ink-mute); }
.tag--past  { background: transparent; color: var(--ink-mute); border: 1px solid var(--rule-strong); }

/* --- editor --- */
.editor {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(19,22,14,.62);
  display: grid; place-items: start center;
  padding: var(--s-5) var(--s-4);
  overflow-y: auto;
}
.editor__panel {
  background: var(--surface); border: 1px solid var(--rule-strong);
  border-radius: var(--radius); padding: clamp(1.25rem, 3vw, 2rem);
  width: 100%; max-width: 720px;
  box-shadow: 0 30px 70px -40px rgba(19,22,14,.7);
}
.editor__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); }
.editor__close {
  background: none; border: 0; cursor: pointer; font-size: 1.7rem; line-height: 1;
  color: var(--ink-mute); padding: 0 .2em;
}
.editor__close:hover { color: var(--ink); }
.editor__foot { display: flex; flex-wrap: wrap; gap: var(--s-3); padding-top: var(--s-2); }
.editor__foot .btn--danger { margin-left: auto; }

.switch { display: flex; align-items: center; gap: var(--s-3); cursor: pointer; font-size: var(--t-sm); }
.switch input { width: 1.1rem; height: 1.1rem; accent-color: var(--accent); }

/* --- image picker --- */
.picker { display: grid; grid-template-columns: repeat(auto-fill, minmax(108px, 1fr)); gap: var(--s-3); }
/* The empty-state hint is prose, not a tile — let it span the whole grid
   instead of being squeezed into one 108px column. */
.picker .hint { grid-column: 1 / -1; }
.pick {
  background: var(--surface-sink); border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 0; cursor: pointer; overflow: hidden; display: flex; flex-direction: column;
  transition: border-color .2s var(--ease);
}
.pick img { width: 100%; aspect-ratio: 3/2; object-fit: cover; display: block; }
.pick span {
  font-size: var(--t-xs); color: var(--ink-mute); padding: .45em .5em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pick__none { display: grid; place-items: center; min-height: 76px; }
.pick:hover { border-color: var(--rule-strong); }
.pick.is-on { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

/* --- toast --- */
.toast {
  position: fixed; left: 50%; bottom: var(--s-5); transform: translateX(-50%);
  z-index: 300; max-width: min(560px, 92vw);
  padding: .85em 1.2em; border-radius: var(--radius);
  font-size: var(--t-sm); box-shadow: 0 16px 40px -24px rgba(19,22,14,.8);
}
.toast--ok  {
  background: var(--pine-900); color: var(--ink-on-dark);
  /* The toast is fixed to the bottom of the viewport, which is often the
     dark footer — without an edge it vanishes into it. */
  border: 1px solid var(--tin-500);
}
.toast--bad { background: #8E3B1F; color: #FFF3EC; border: 1px solid #C9764F; }

/* ============================================================
   16. TicketsCandy checkout modal
   Their iframe brings its own look, so we frame it rather than
   fight it — a quiet sheet in the site's own materials, with the
   widget sitting inside like a card in a wallet.
   ============================================================ */
.tc-overlay {
  position: fixed; inset: 0; z-index: 400;
  background: rgba(19,22,14,.72);
  display: grid; place-items: center;
  padding: var(--s-4);
}
body.tc-open { overflow: hidden; }

.tc-sheet {
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  width: min(560px, 100%);
  max-height: min(88svh, 900px);
  display: flex; flex-direction: column;
  box-shadow: 0 30px 80px -40px rgba(19,22,14,.85);
}
.tc-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--rule);
}
.tc-title {
  font-family: var(--display); font-size: var(--t-lg); line-height: 1.2;
  margin: 0; max-width: none;
}
.tc-close {
  background: none; border: 0; cursor: pointer; line-height: 1;
  font-size: 1.7rem; color: var(--ink-mute); padding: 0 .15em;
}
.tc-close:hover { color: var(--ink); }

.tc-body { flex: 1; min-height: 420px; display: flex; }
.tc-body iframe { flex: 1; width: 100%; border: 0; min-height: 420px; }
.tc-loading {
  margin: auto; color: var(--ink-mute); font-size: var(--t-sm);
  letter-spacing: .14em; text-transform: uppercase;
}
.tc-foot {
  padding: var(--s-3) var(--s-5) var(--s-4);
  border-top: 1px solid var(--rule);
  font-size: var(--t-xs); color: var(--ink-mute); max-width: none;
}

/* --- admin tabs + content editor --- */
.admin__tabs { display: flex; gap: var(--s-4); border-bottom: 1px solid var(--rule); margin-bottom: var(--s-5); }
.admin__tab {
  background: none; border: 0; cursor: pointer; padding: 0 0 var(--s-3);
  font-family: var(--body); font-size: var(--t-xs); letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-mute);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.admin__tab:hover { color: var(--ink); }
.admin__tab.is-on { color: var(--ink); border-bottom-color: var(--accent); }

.cms-group { border-top: 1px solid var(--rule); padding: var(--s-5) 0; }
.cms-group > h3 {
  font-family: var(--body); font-size: var(--t-xs); font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--accent);
  margin-bottom: var(--s-4);
}
.cms-item { margin-bottom: var(--s-5); }
.cms-item > h4 {
  font-family: var(--body); font-size: var(--t-base); font-weight: 500;
  margin-bottom: var(--s-3);
}
.cms-field { margin-bottom: var(--s-3); }
.cms-field__head {
  display: flex; align-items: center; gap: var(--s-3);
  margin-bottom: var(--s-2);
}
.cms-field label {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-xs); letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-mute);
}
.cms-field textarea, .cms-field input {
  font-family: var(--body); font-size: var(--t-sm); color: var(--ink);
  background: var(--surface); border: 1px solid var(--rule-strong);
  border-radius: var(--radius); padding: .7em .85em; width: 100%;
}
.cms-field textarea { min-height: 78px; resize: vertical; }
.cms-field.is-changed textarea, .cms-field.is-changed input { border-color: var(--accent); }
.cms-changed {
  font-size: .6rem; letter-spacing: .1em; padding: .15em .5em; border-radius: 2px;
  background: var(--accent-wash); color: var(--accent-deep);
}
.cms-reset {
  background: none; border: 0; cursor: pointer; padding: 0; margin-left: auto;
  font-size: var(--t-xs); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-mute); text-decoration: underline; text-underline-offset: 3px;
}
.cms-reset:hover { color: var(--accent-deep); }
.admin__save-bar {
  position: sticky; bottom: 0; background: var(--ground);
  border-top: 1px solid var(--rule); padding: var(--s-4) 0;
  display: flex; align-items: center; gap: var(--s-4);
}

/* ---------- 17. Photograph behind a dark band ----------
   Same idea as the hero: the picture is a real <picture> element so it keeps
   its srcset, and a scrim sits over it so pale text stays readable whatever
   the photograph is doing underneath. */
.band--photo { position: relative; isolation: isolate; overflow: hidden; }
.band__media { position: absolute; inset: 0; z-index: 0; }
.band__media picture,
.band__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.band--photo::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(27,32,21,.86) 0%, rgba(27,32,21,.72) 100%);
}
/* The content has to clear both the picture and the scrim. */
.band--photo > .wrap { position: relative; z-index: 2; }

/* ---------- 18. Before / after comparison ----------
   Used on the home page for the haus then and now. The slider is a real
   <input type="range"> laid transparently over the images: it drags with a
   mouse, a finger and the arrow keys, and it is announced to screen readers,
   none of which a hand-rolled drag handler gives you for free. JS only
   mirrors its value into --ba; with JS off the clip stays at the 50% default
   below and the component reads as a deliberate split image. */
.ba {
  --ba: 50%;
  position: relative; overflow: hidden;
  border-radius: var(--radius); aspect-ratio: 3 / 2;
  background: var(--surface-sink);
}
.ba__img { position: absolute; inset: 0; }
.ba__img picture, .ba__img img {
  display: block; width: 100%; height: 100%; object-fit: cover;
}
.ba__img--before { clip-path: inset(0 calc(100% - var(--ba)) 0 0); }

.ba__handle {
  position: absolute; top: 0; bottom: 0; left: var(--ba);
  width: 2px; background: var(--limewash); z-index: 2;
  pointer-events: none; transform: translateX(-1px);
}
.ba__handle::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 40px; height: 40px; margin: -20px 0 0 -20px;
  border-radius: 50%; background: var(--limewash);
  box-shadow: 0 2px 14px rgba(26,33,28,.45);
}
.ba__label {
  position: absolute; bottom: var(--s-3); z-index: 2;
  font-size: var(--t-xs); letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-on-dark); background: rgba(27,32,21,.62);
  padding: .35em .8em; border-radius: 2px; pointer-events: none;
}
.ba__label--a { left: var(--s-3); }
.ba__label--b { right: var(--s-3); }

/* The control itself is invisible but covers the whole image. */
.ba__range {
  position: absolute; inset: 0; z-index: 3;
  width: 100%; height: 100%; margin: 0; padding: 0;
  opacity: 0; cursor: ew-resize; -webkit-appearance: none; appearance: none;
  background: transparent;
}
.ba__range::-webkit-slider-thumb { -webkit-appearance: none; width: 44px; height: 100%; }
.ba__range::-moz-range-thumb { width: 44px; height: 100%; border: 0; }
.ba__range:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---------------------------------------------------------------- 17. seasonal offer
   A dated promotion on the Creatives page: the client's own flyer beside a
   short editable summary. The flyer carries the prices, so they are NOT
   repeated as text next to it - two visible copies of the same list read as
   an error the moment one of them is edited. */
/* The poster carries the price list, so it gets the wider half - at an
   even 1fr the eight prices render around six pixels tall. */
@media (min-width: 900px) {
  .split--flyer { grid-template-columns: 1.15fr 1fr; }
}

.offer__flyer img,
.offer__flyer picture { display: block; width: 100%; height: auto; }
.offer__flyer {
  /* The artwork is a tall portrait poster. Without a ceiling it dwarfs the
     column it sits in and pushes the button far below the fold. */
  max-width: 31rem; margin-inline: auto; width: 100%;
  border-radius: var(--radius); overflow: hidden;
  display: block; transition: opacity .3s var(--ease, ease);
}
/* Even at full column width the fine print is small, so the poster opens
   full size in a new tab. */
a.offer__flyer:hover, a.offer__flyer:focus-visible { opacity: .88; }

.offer__copy { justify-content: center; }

.offer__dates {
  font-size: var(--t-xs); letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-on-dark);
  padding-block: var(--s-3); border-block: 1px solid var(--rule-on-dark);
}

.offer__note {
  font-size: var(--t-xs); color: var(--ink-on-dark-soft);
  max-width: 34rem; line-height: 1.7;
}

/* Replaceable pictures in the admin (section 15 continued). The thumbnail is
   capped hard: an uploaded flyer is a tall poster and at natural size it
   would push the file input off the bottom of the screen. */
.cms-image { padding-block: var(--s-4); border-bottom: 1px solid var(--rule); }
.cms-image:last-child { border-bottom: 0; }
.cms-image__row {
  display: grid; grid-template-columns: 96px 1fr;
  gap: var(--s-4); align-items: start; margin-top: var(--s-2);
}
.cms-image__thumb {
  display: grid; place-items: center; min-height: 96px;
  background: var(--surface-sink); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: var(--s-2); text-align: center;
}
.cms-image__thumb img {
  display: block; max-width: 100%; max-height: 132px;
  width: auto; height: auto;
}
.cms-image input[type="file"] { font-size: var(--t-sm); max-width: 100%; }

/* An inline link inside running text. The global `a { text-decoration: none }`
   is right for nav and buttons and wrong here: in a sentence, a link with no
   underline and no colour change is invisible, and the gift-certificate link
   sits in exactly that position. */
.link {
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-thickness: 1px; color: var(--accent);
}
.link:hover { color: var(--accent-deep); }
