/* =============================================================================
   ROAM — stylesheet

   Grows one component at a time. NOTHING is copied from the old site's sheet:
   that one is 8,557 lines carrying 397 !important declarations, nearly all of
   them written to out-specify a WPRentals parent theme that does not exist
   here. A rule earns its way in.

   There is no !important in this file and there should not need to be. If one
   ever looks necessary, the specificity is wrong somewhere above it.
   ========================================================================== */

/* ── Reset, only what actually needs resetting ───────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
img, picture, svg, video { display: block; max-width: 100%; height: auto; }

html {
  /* Reserve the scrollbar track permanently. Without it, navigating from a
     short page to a long one shifts the whole layout sideways by ~15px. */
  scrollbar-gutter: stable;
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--roam-cream);
  color: var(--roam-forest);
  font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: 'Playfair Display', Georgia, serif;
  font-weight: 500;
  line-height: 1.2;
  margin: 0 0 0.5em;
}

a { color: inherit; }

/* ── Page measure ────────────────────────────────────────────────────────── */
/* The interior-page wrapper and the footer read the gutter TOKEN, not a 25px
   literal — otherwise the property page, search and single posts keep a 25px
   inset on a phone while the homepage, header and modal have moved to 16
   (--roam-page-x narrows to 16 at ≤767). 1330 is the desktop shell (1280 + 2×25). */
.roam-wrap,
.roam-foot__inner {
  max-width: 1330px;
  margin-inline: auto;
  padding-inline: var(--roam-page-x);
}

/*
 * Air before the footer, on every page except the homepage.
 *
 * The homepage sets its own rhythm — its last section carries the padding that
 * closes the page, and adding more here would double it. Everywhere else the
 * content simply stops, and when the last thing on the page happens to be a
 * grid with no pagination under it (a search that returned one page of results)
 * the final row of cards butts straight into the dark footer.
 */
body:not(.home) .roam-main { padding-bottom: var(--roam-sp-80); }
/* NO GHOST BLOCK ON A PROPERTY PAGE. Its last element is the full-bleed green
   explore band, which must butt straight into the footer — the 80px breathing
   room above would otherwise show as a cream strip between two dark bands. */
body.single-roam_property .roam-main { padding-bottom: 0; }

/*
 * The footer sits at the bottom of the viewport on a short page.
 *
 * Without this the 404 — which is deliberately brief — ended its footer around
 * two-thirds down and left a band of cream beneath it, which reads as the page
 * having failed to finish loading rather than as the page being short. dvh
 * rather than vh so the mobile address bar collapsing does not push the footer
 * off the bottom.
 */
body { display: flex; flex-direction: column; min-height: 100dvh; }
.roam-main { flex: 1 0 auto; }

/* ── Skip link ───────────────────────────────────────────────────────────── */
/* Present for keyboard and screen-reader users, off-screen until focused.
   Not display:none — that would remove it from the tab order, which is the
   entire point of it. */
.roam-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 20px;
  background: var(--roam-forest);
  color: var(--roam-cream);
  text-decoration: none;
}
.roam-skip:focus { left: 0; }


/* ── The announcement bar ────────────────────────────────────────────────── */
/* A WHOLE NUMBER OF PIXELS, and it is not cosmetic. Measured on the dev site
   2026-07-31: at 13px on line-height 1.4 the text box is 18.2px, and with two
   9px paddings the strip rendered 36.28px tall. Everything below inherited that
   fraction — the header's bottom edge landed on a half pixel, and the sticky
   bar's at-rest position resolved a whole pixel away from its stuck position,
   so the header appeared to jump by 1px the moment it stuck. Nothing was
   moving; a fractional strip two elements up was being rounded away.

   line-height in px removes the fraction at its source. The height and
   border-box are belt and braces, so a font swap cannot reintroduce one. */
.roam-promo {
  box-sizing: border-box;
  height: 36px;
  padding: 9px 0;                  /* 9 + 18 + 9 = 36 exactly */
  background: var(--roam-gold);
  color: var(--roam-forest);
  text-align: center;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: 0.04em;
  line-height: 18px;
}
/* The same measure as .roam-head__row, so the message centres on the header's
   centre and the close lands on the header's right edge rather than the
   viewport's. */
.roam-promo__inner {
  position: relative;              /* anchors the close control */
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding-inline: var(--roam-page-x);
}
.roam-promo__link {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: var(--roam-fw-semibold);
}
.roam-promo__link:hover { opacity: 0.75; }

/* The close sits ON the bar rather than in its flow, so the message stays
   optically centred at every width — a flex row would shift it left by half the
   button. 44px square for the tap target, with a much smaller visible glyph. */
.roam-promo__x {
  position: absolute;
  top: 50%;
  /* The 44px tap target is centred on its glyph, so a flush `right: page-x`
     would leave the mark 12px inside the column. The negative pull is the
     difference between the box and the 20px X inside it. */
  right: calc(var(--roam-page-x) - 12px);
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  /* A <button> resets the font, and the bar's 600 is what the dev site's close
     inherits. Nothing here is text, so it changes no pixel — it keeps the two
     descriptions of the same control from disagreeing. */
  font: inherit;
  opacity: 0.65;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.15s ease;
}
.roam-promo__x:hover,
.roam-promo__x:focus-visible { opacity: 1; }

/* THE CANONICAL X. Client instruction 2026-07-27: "our menu X is the canonical
   X we use", and this one must match it. It is not an icon — it is two bars
   rotated onto each other, 20x2px, 6px apart, radius 1px. The rotate-then-
   translate ORDER matters: the translate happens in the rotated frame, which is
   what brings the two bars onto one point.

   Colour stays currentColor rather than the menu's cream: this X sits on gold,
   and cream on gold is unreadable. Shape and weight are what the instruction is
   about. */
.roam-x {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 6px;
}
.roam-x__bar {
  width: 20px;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
}
.roam-x__bar:first-child { transform: rotate(45deg) translateY(4px); }
.roam-x__bar:last-child  { transform: rotate(-45deg) translateY(-4px); }

/* One of the two messages, never both. The long one is the default and the
   short one only exists because the long one is four lines on a phone — see
   inc/chrome.php, where both strings live and both are filterable. */
.roam-promo__short { display: none; }

/* The long sentence only fits one 36px line above ~1100px; below that it wrapped
   and the fixed-height bar clipped the second line. So the mobile treatment — the
   concise message, the bar free to grow, the copy held clear of the close —
   extends the whole way up through tablet, not just to 600. */
@media (max-width: 1100px) {
  /* Keep the message clear of the button where the copy wraps. */
  .roam-promo { height: auto; }
  .roam-promo__msg { display: inline-block; padding-right: 34px; }  /* dev: 34 */
  .roam-promo__long { display: none; }
  .roam-promo__short { display: inline; }
}

/* ── Header ──────────────────────────────────────────────────────────────── */
/* PORTED FROM THE DEV SITE, NOT FROM docs/phase1-spec.md. The spec is a
   snapshot of the design; the dev site is where the client's deviations from it
   were made, and they are the decisions. Where the two disagree the dev site
   wins, and the disagreements here are not small:

     row height   spec 48px   dev 72px   (--roam-hdr-h, client 2026-07-31)
     logo         spec 30px   dev 32px
     CTA pill     spec 11/22  dev 40px tall, 0 20px

   Everything in this header sizes from --roam-hdr-h rather than setting its own
   height, which is how the old site did it too: change the token and the nav
   links, the logo and the row move together. Setting them individually is how
   you end up with a 72px row containing a 71px one. */
/* EVERY NUMBER BELOW IS A MEASUREMENT, not a reading of the dev site's CSS.
   Taken in Chromium at four viewports — .github/scripts/header-measure.mjs,
   results in qa/measure/header.json. Where a number changes by viewport, it
   changes because the dev site's rendered value changes. */
:root {
  --roam-hdr-h: 72px;      /* dev: row 72 at 1440 / 1024 / 768 */
  --roam-hdr-ctl: 40px;    /* dev: account link 40x40, CTA 40 tall */
  --roam-hdr-logo: 32px;   /* dev: logo ::before 156x32 at 1440, 1024 and 768 */
  --roam-hdr-pill: 48px;   /* dev: docked pill 48 tall, go button 40 */
  /* THE TOUCH BOX, and it is the client's number rather than a measured one.
     Client instruction 2026-08-01: the account glyph is 40x40 in a 48x48 target
     and the hamburger is a 28x28 mark in one. The dev site's own targets are
     44 — the WCAG 2.5.5 AAA floor — so this is above the standard rather than a
     correction to it, and it is deliberate: 48 is the size a thumb finds
     without aiming.

     Every target in the bar reads this, so raising it once raises all of them
     and the boxes cannot drift apart. */
  --roam-hdr-tap: 48px;
  --roam-hdr-burger: 28px; /* the hamburger's icon box, inside that target */
  /* Between the controls in the nav cluster. dev: 8 at 1440, 10 at 1024, 768
     and 390 — and the 10 is confirmed twice over, because the dev hamburger's
     -16 margin against a 44px button nets out to exactly the -6 measured
     between its account glyph and its button, which only closes at a gap of
     10. */
  --roam-nav-gap: 8px;
  /* The bar's own gutter. Measured off the logo's left edge: 25 at 1440, 1024
     and 768, and 15 at 390. Scoped to the header rather than folded into
     --roam-page-x, which the page content also reads and which the dev site
     does not narrow. */
  --roam-hdr-x: var(--roam-page-x);
}
/* THE 16px GUTTER IS TABLET + PHONE (client 2026-08-03). One token pair drives
   every gutter on the site — the page measure, the header, the flyout, the
   modals — so narrowing it here narrows all of them together, and nothing reads
   a 25 or 16 literal of its own. Desktop (≥1025) keeps 25. */
@media (max-width: 1024px) {
  :root {
    --roam-page-x: 16px;
    --roam-hdr-x: 16px;
  }
}
@media (max-width: 767px) {
  :root {
    --roam-hdr-h: 64px;    /* dev: mobile bar 64 tall at 390 */
    --roam-hdr-logo: 24px; /* dev: logo ::before 117x24 at 390 */
    --roam-hdr-pill: 40px; /* dev: docked pill 40 tall, go button 32 */
  }
}

/* A GRADIENT, NOT A FLAT FILL — and this was flat forest until it was measured.
   The dev site paints linear-gradient(145deg, #1B2B27 0%, #2A4A40 100%) on the
   header at every viewport, which lifts the right-hand end of the bar just
   enough to separate it from a dark hero. A flat --roam-forest is the first
   colour in that gradient and nothing else.

   The 1px hairline under it is rgba(250, 246, 241, 0.06) — a sixteenth of an
   opacity, which at this contrast reads as an edge rather than as a line, and
   is why the dev header measures 73px against a 72px row. */
.roam-head {
  position: sticky;
  top: 0;
  z-index: 990;
  background: linear-gradient(145deg, rgb(27, 43, 39) 0%, rgb(42, 74, 64) 100%);
  border-bottom: 1px solid rgba(250, 246, 241, 0.06);
}
.roam-head__row {
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding-inline: var(--roam-hdr-x);
  display: flex;
  align-items: center;
  /* dev: 8px at every viewport. This was 18, which pushed the nav 40px right of
     where the dev site puts it across five gaps. */
  gap: var(--roam-sp-8);
  min-height: var(--roam-hdr-h);
}
/* THE LOCKUP, AT 4.875:1.
   roam-logo-lockup.svg is roam-logo-full.svg with the viewBox cropped to the
   artwork — the source carries 11.4 units of empty space to its left and 8.6
   above, so its rendered box was never its visible edge, which is what made the
   header logo look inset. Cropped, the ratio is 4.875, so the width follows the
   height by construction: 32 -> 156 and 24 -> 117, both of which are exactly
   what the dev site paints.

   Width is set rather than left to `auto` because the PNG this replaces resolved
   to 152.375 at 32 tall — the old 4:1 artwork's ratio — and 3.6px of drift in a
   logo is visible when it is the leftmost thing on the page. */
.roam-logo { display: flex; align-items: center; flex: 0 0 auto; }

/* Full row height on the DESKTOP bar only, which is what the dev site does and
   the direction this got wrong twice. Its desktop logo anchor measures 156x72
   around a 32px mark — home is the one link a visitor reaches for without
   looking, and a 32px-tall target makes them look. Its mobile bar does not: the
   anchor there is 156x32, and 117x24 on a phone, exactly the artwork.

   Stretching at every width overshot in the other direction and made the phone
   anchor 64 tall. The artwork is unchanged either way; only the click box
   moves. */
@media (min-width: 1025px) {
  .roam-logo { align-self: stretch; }
}
/* TWO ARTWORKS, ONE SHOWING, AND ONLY ONE CASE SHOWS THE SECOND.

   Client instruction 2026-08-01, in its settled form after three passes:

     mobile, undocked   combomark   the full lockup
     mobile, docked     logo only   the mark, at the search bar's height
     tablet & desktop   combomark   both states, at one size — "Tablet plus,
                                    combomark always stays, no size change
                                    when search docks"

   So it is one condition with two parts, not two independent ones: a phone,
   AND the search docked into the bar. Everywhere else the lockup stands. The
   earlier builds got this wrong in both directions — first dropping the
   wordmark on every phone whether docked or not, then dropping it on every
   docked bar including a 1440 one.

   THAT COMPOUND CONDITION IS WHY THIS IS TWO ELEMENTS AND NOT A <picture>. A
   media source is the better tool when the swap is purely about width, because
   the browser chooses before it downloads anything; it cannot also read a class
   on <body>. The mark is about a kilobyte of SVG, and it is only fetched by the
   browsers that will use it — see header.php.

   Each width comes from a height and its own ratio rather than `auto`. That is
   not caution about SVG: the PNG this replaced resolved to 152.375 at 32 tall,
   the old 4:1 artwork's ratio rather than the 4.875 it was drawn at, and 3.6px
   of drift is visible in the leftmost thing on the page.

   THE MARK TAKES THE PILL'S HEIGHT, not the lockup's — "the logo size increases
   to the size of the search bar". That is also why it reads: at 24 the mark
   alone looks like a lockup with its wordmark missing, and at 40 beside a 40px
   pill it looks like a mark.

   EVERY `display` IS ON A BARE CLASS OR TIGHTER, and that is load-bearing. This
   carried a `.roam-logo img { display: block }` for one build to kill the inline
   element's descender gap — an element-plus-class selector, which outranks a
   bare class, so it beat the hide rule and the header painted both artworks side
   by side. It measured 184px wide at 1440 against 156, and the docked state
   still looked right because those selectors carry two classes and won anyway.
   A rule that breaks three states out of four while the fourth keeps working is
   what this file gets wrong by being read rather than looked at. */
.roam-logo__lockup {
  display: block;
  height: var(--roam-hdr-logo);
  width: calc(var(--roam-hdr-logo) * 4.875);
}
.roam-logo__mark { display: none; }

@media (max-width: 767px) {
  body.roam-docked .roam-logo__lockup { display: none; }
  body.roam-docked .roam-logo__mark {
    display: block;
    height: var(--roam-hdr-pill);
    width: calc(var(--roam-hdr-pill) * 0.882);   /* 50.9 x 57.7, cropped to the artwork */
  }
}

/* Absorbs the slack. Empty until the docked search pill exists; the layout is
   already correct for it. */
.roam-head__center { flex: 1 1 0; min-width: 0; }

/* THE CLUSTER'S OWN GAP, and its absence was a real defect: the CTA, the
   account glyph and the hamburger sat with 0.0px between them at every
   viewport, measured, while the dev site holds 8 at 1440 and 10 below 1025.

   The 8px everyone assumed was doing this job is on .roam-head__row, and it
   spaces the LOGO, the CENTRE SLOT and the NAV — the nav as one block. Nothing
   was spacing the things inside it. What looked like spacing was a 6px
   margin-left on Book Now and two negative margins on the hamburger, each added
   for its own local reason, none of them a rhythm.

   So the nav gets a real gap, and the ad-hoc margins come off. The value is the
   dev site's, viewport for viewport. */
.roam-nav {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  gap: var(--roam-nav-gap);
}
.roam-nav__links {
  display: flex;
  align-items: center;
  /* dev: 2px. Small, but it is the difference between five links reading as a
     row and reading as one run-on string. */
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Full row height with horizontal padding, rather than a gap between short
   links: the whole column is then a click target, which is what a nav on a
   72px bar should be. */
.roam-nav__links a {
  display: inline-flex;
  align-items: center;
  height: var(--roam-hdr-h);
  padding: 0 var(--roam-sp-12);
  color: rgba(250, 246, 241, 0.82);
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-medium);
  /* dev: 0.65px, which is 0.05em at 13px. */
  letter-spacing: var(--roam-ls-xs);
  /* dev: 13px — the same as the font size, i.e. 1. This inherited the body's
     1.6 and came out at 20.8, which is invisible on a single-line link that is
     vertically centred anyway, and wrong the moment one wraps. */
  line-height: 1;
  text-decoration: none;
  transition: color 0.3s var(--roam-easing);
}
.roam-nav__links a:hover,
.roam-nav__links a:focus-visible { color: var(--roam-gold); }

.roam-booknow {
  display: inline-flex;
  align-items: center;
  justify-content: center;   /* dev: center */
  height: var(--roam-hdr-ctl);
  padding: 0 var(--roam-sp-20);
  background: var(--roam-gold);
  color: var(--roam-forest);
  border: 0;
  border-radius: var(--roam-r-full);
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
  line-height: 1;               /* dev: 13px at 13px */
  white-space: nowrap;
  text-decoration: none;
  transition: background-color 0.2s ease, color 0.2s ease;
}
.roam-booknow:hover,
.roam-booknow:focus-visible { background: var(--roam-gold-active); color: var(--roam-forest); }

/* The 6px lead-in that used to be here is gone. It was the only thing spacing
   Book Now from the links, and it was measured off the dev site's CTA — where
   that 6 sits INSIDE its nav list, on top of the list's own 2px gap, and comes
   to the same 8 the cluster gap now gives. Keeping both would have made it 14. */

/* Below 1025 the dev site's bar carries its own CTA with its own type: 14px on
   0.08em against the desktop's 13 on 0.05em, 18px of padding against 20, and
   32 tall rather than 40 on a phone. Measured at 1024, 768 and 390. */
@media (max-width: 1024px) {
  :root { --roam-nav-gap: 10px; }
  .roam-booknow {
    padding: 0 var(--roam-sp-18);
    font-size: var(--roam-fs-14);
    letter-spacing: var(--roam-ls-sm);
  }
}
@media (max-width: 767px) {
  .roam-booknow { height: 32px; }
}

/* THE ACCOUNT GLYPH — three numbers, and every one of them measured.

   LAYOUT BOX 40x40, the control height. Not 44. The box has to stay at the
   control height or the cluster's gaps stop being even, and the dev site holds
   that line: its link measures exactly 40x40 at 1440, 1024 and 768.

   SVG 40x40 WITH scale(1.2549). That factor is 256/204, and it is not a
   flourish: Phosphor's user-circle draws its ink across 204 of a 256 viewBox,
   so a 40px svg paints a 31.9px circle and reads a size smaller than everything
   beside it. The scale puts the INK back up to 40. The painted box becomes
   50.2, which overhangs the layout box — harmless, because nothing around it is
   opaque. This is what an earlier 36px svg was groping at and getting wrong
   from the other direction.

   TAP TARGET 48x48, VIA ::after. This is the part worth copying. The visible
   control is 40 and the touch target is larger, and the dev site reconciles the
   two with an absolutely positioned ::after inset by half the shortfall — so the
   hit area grows and the layout does not move. Both numbers are tokens, so the
   inset is arithmetic rather than a magic -4: change either and it still closes.

   The dev site's own target is 44, the WCAG 2.5.5 AAA floor. 48 is the client's
   instruction of 2026-08-01 and is above that floor, not a correction to it.

   GOLD, measured rgb(198, 151, 91). */
.roam-acct {
  position: relative;
  /* Above the hamburger, which overlaps it — see the note on
     .roam-head__toggle. Without this the button wins the contested strip by
     document order and takes it off the glyph a thumb is aiming at. */
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: var(--roam-hdr-ctl);
  height: var(--roam-hdr-ctl);
  color: var(--roam-gold);
  text-decoration: none;
  transition: color 0.2s ease;
}
.roam-acct:hover,
.roam-acct:focus-visible { color: var(--roam-gold-active); }
.roam-acct svg {
  display: block;
  width: var(--roam-hdr-ctl);
  height: var(--roam-hdr-ctl);
  transform: scale(1.2549);   /* 256 / 204 — the box back up to the ink */
}
.roam-acct::after {
  content: '';
  position: absolute;
  inset: calc((var(--roam-hdr-tap) - var(--roam-hdr-ctl)) / -2);
}

/* The panel's account row and its Book Now foot. Both present in the markup at
   every width so the menu is one list, and both hidden on the desktop bar —
   the account row shows only on the phone panel, the CTA only once the links
   collapse into the flyout (<=1024). */
.roam-nav__acct { display: none; }
.roam-nav__cta { display: none; }

/* ── The docked search pill ──────────────────────────────────────────────── */
/* Hidden until the morph docks it — [hidden] is the switch, and the opacity and
   transform in between are written by assets/js/roam-header.js because they are
   a continuous value rather than a state. */
/* A WHITE PILL WITH A SHADOW, not a translucent one on the forest bar. That is
   the dev site's, value for value: white ground, no border, a lifted shadow,
   14px between the fields, 4px of padding round the gold circle and 14px in
   front of the first word.

   An earlier version here made it a tinted ghost of the header — which looks
   deliberate on its own and reads as a disabled control next to a solid gold
   Book Now. The design's pill is the same object as the hero's search bar,
   shrunk: white, floating, gold action. It should read as the search you were
   just using, because it is. */
.roam-hs {
  display: flex;
  align-items: center;
  /* 10px, everywhere, and it is arithmetic rather than a reading. The dev
     pill's own computed gap says 14, but its children do not sit 14 apart:
     14 (pad) + 65 + 10 + 1 + 10 + 63 + 10 + 1 + 10 + 57 + 10 + 40 + 4 (pad) is
     exactly the 295 it measures at 1440, and no other gap closes it. Whatever
     the 14 belongs to, it is not the space between these boxes. */
  gap: 10px;
  max-width: 100%;
  height: var(--roam-hdr-pill);   /* dev: 48 above 768, 40 at 390 */
  padding: 4px 4px 4px 14px;
  border: 0;
  border-radius: var(--roam-r-full);
  background: var(--roam-white);
  font-family: var(--roam-font-body);
  cursor: pointer;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.28);
  transition: box-shadow 0.25s ease;
}
.roam-hs[hidden] { display: none; }
.roam-hs:hover { box-shadow: 0 8px 28px rgba(0, 0, 0, 0.34); }

/* The three fields as one block, so the phone can stack them without the gold
   circle joining the stack. dev: 10px between every child of this row, and the
   1px dividers are children of it too — 14 + 65 + 10 + 1 + 10 + 63 + 10 + 1 +
   10 + 57 + 10 + 40 + 4 is exactly the 295 the dev pill measures at 1440. */
.roam-hs__text,
.roam-hs__sub {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.roam-hs__seg {
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-medium);
  /* dev: 22.4px at 1440 — 1.6 on a 14px inherited size, which is what makes the
     dev segment's box 22.41 tall against a bare 17 here. It is invisible on one
     centred line and it is the difference between the two pills measuring the
     same and measuring 5px apart. */
  line-height: 22.4px;
  color: var(--roam-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Bold and full-strength: Where is the field that answers "which search is
   this", and the other two are its qualifiers. */
.roam-hs__where { font-weight: 700; color: var(--roam-text-primary); }

/* THE DIVIDER IS AN 18px LINE, NOT A FULL-HEIGHT BORDER.
   Measured on the dev site: a 1x18 element in rgb(224, 220, 215) at 1440, and
   1x10 at 390. A border-left on the segment would run the full line-box height
   instead, which at a 48px pill is nearly three times too long — the same mark,
   drawn wrong.

   It was a ::before with 14px of padding-left on the segment to make room. That
   padding belongs to the SEGMENT's box, which put every field 14px wider than
   the dev site's and the pill 31px wider overall; it also meant the dev site's
   divider had nothing here to be compared against, so the diff read "missing"
   rather than "wrong". A real element is measurable and can be hidden on its
   own, which the phone layout needs. */
.roam-hs__div {
  flex: 0 0 auto;
  width: 1px;
  height: 18px;
  background: rgb(224, 220, 215);
}

.roam-hs__go {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  /* 4 + (pill - 8) + 4 = pill. Four points of padding round the circle at every
     size, so the arithmetic closes by construction and one token carries both.
     Sized off the PILL, not the control: dev measures 40 inside a 48 pill and
     32 inside a 40 one — this was reading --roam-hdr-ctl and coming out 32
     everywhere, which is the phone's number on a desktop. */
  width: calc(var(--roam-hdr-pill) - 8px);
  height: calc(var(--roam-hdr-pill) - 8px);
  border-radius: 50%;
  background: var(--roam-gold);
  color: var(--roam-forest);
}
.roam-hs__go svg { width: 15px; height: 15px; }

/* The centre slot takes the slack and centres what is in it. */
.roam-head__center { display: flex; justify-content: center; }

/* ALL THREE FIELDS STAY, at every width the pill is shown.
   This used to drop guests below 1200 and dates below 900, and the dev site
   does neither: measured at 390 its pill is 175x40 carrying where, dates AND
   guests. Its own comment settles the intent — "the search bar doesn't need to
   collapse to just 2 fields on tablet, there's room for all", client
   instruction 2026-07-27. Nothing tightens either — the pill's spacing is 10
   at every width, so there is no narrow-viewport override to write. */

/* THE PHONE PILL IS TWO LINES, NOT ONE — measured, and it is not a guess:
   at 390 the dev site puts "Anywhere" at y=16.5 on a 16px line and the
   dates/guests pair at y=32.5 on a 14px line at 11px, inside a 175x40 pill.
   One row of three fields does not fit in 175px and the dev site does not try.

   The top divider goes with it: at 390 the dev pill carries exactly one
   hairline, 1x10, and it is the one between dates and guests. */
@media (max-width: 767px) {
  .roam-hs__text {
    flex-direction: column;
    align-items: center;
    gap: 0;
  }
  .roam-hs__div--top { display: none; }
  .roam-hs__div { height: 10px; }
  .roam-hs__where { line-height: 16px; }
  .roam-hs__sub .roam-hs__seg {
    font-size: var(--roam-fs-11);
    line-height: 14px;
  }

  /* THE PILL HAS TO HAVE SOMEWHERE TO GO. Docked at 390 this bar was logo,
     pill, Book Now and the hamburger in 345px, and the pill is the one with
     flex slack — it collapsed to 32px, which is the gold circle and nothing
     else. Every field was still in the markup, still "visible", and completely
     unreadable, which is the failure mode a screenshot at scroll 0 cannot show.

     Book Now is what gives way. It is the only control in the bar that the pill
     duplicates — the pill's own gold circle goes to the same place — and the
     hamburger cannot go, because with the links collapsed it is the whole
     navigation. */
  body.roam-docked .roam-booknow { display: none; }
}

/* ── The docked header collapses its nav ─────────────────────────────────────
   Once the pill is in the bar there is no room for five links beside it, so the
   links give way to the hamburger — the dev site's behaviour, driven there by
   the same body.roam-docked class the morph sets here.

   Book Now STAYS. On the dev site the CTA is dropped when docked, because its
   docked cluster prints a second Book Now of its own; this header has one
   button and dropping it would remove the site's only standing call to action
   at the exact moment a guest has scrolled far enough to want it.

   THESE DECLARATIONS ARE THE SAME PANEL AS THE ≤1024px ONE BELOW, and they are
   written twice on purpose. A media query cannot be combined with a class in
   one selector, and the alternatives are worse: a JS-applied class would make
   the phone menu's LAYOUT depend on JavaScript, and the custom-property
   space-toggle that switches `display` is a trick nobody reading this should
   have to decode. If one of these two blocks changes, change the other. */
body.roam-docked .roam-nav__links {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  right: var(--roam-hdr-x);
  left: auto;
  width: min(360px, calc(100vw - 2 * var(--roam-hdr-x)));
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding: var(--roam-sp-8) var(--roam-sp-24) var(--roam-sp-24);
  border-radius: var(--roam-r-lg);
  background: var(--roam-surface-forest-down);
  box-shadow: 0 24px 60px rgba(15, 26, 22, 0.45);
}
body.roam-docked .roam-nav[data-open='1'] .roam-nav__links { display: flex; }
body.roam-docked .roam-nav__links > li { border-bottom: 1px solid rgba(250, 246, 241, 0.12); }
body.roam-docked .roam-nav__links > li:last-child { border-bottom: 0; }
body.roam-docked .roam-nav__links a {
  display: flex;
  align-items: center;
  height: auto;
  padding: var(--roam-sp-16) 0;
  width: 100%;
  font-size: var(--roam-fs-15);
  color: var(--roam-cream);
}
body.roam-docked .roam-nav__cta { display: block; border-bottom: 0; margin-top: var(--roam-sp-16); }
body.roam-docked .roam-nav__cta .roam-booknow {
  height: auto;
  padding: var(--roam-sp-12) var(--roam-sp-24);
  border-radius: var(--roam-r-full);
  background: var(--roam-gold);
  color: var(--roam-forest);
  font-weight: var(--roam-fw-semibold);
}
body.roam-docked .roam-head__toggle { display: flex; }

/* THE DOCKED PILL IS CENTRED ON THE BAR, NOT ON THE SLACK — reported, and
   measured 77px left of centre at 1440, 80 at 1024 and 80 at 768. The dev site
   centres it at every width: 719.98 / 504.50 / 376.50 / 187.50, each of which
   is its row's own centre to within a pixel.

   The cause is that `flex: 1 1 0` on the centre slot centres the pill inside
   whatever is LEFT OVER between the logo and the nav, and those two are not the
   same width — a 156px lockup against a nav carrying Book Now, the account
   glyph and the hamburger. Half of an off-centre gap is off-centre.

   Three equal-ish tracks fix it: 1fr for the logo, auto for the pill, 1fr for
   the nav. The two 1fr columns are equal by construction, so the auto column
   between them is the row's centre whatever the two sides contain.

   NOT `flex: 1 1 0` on .roam-logo, which was the first idea and is wrong:
   .roam-logo IS the anchor (header.php), so growing it to a third of the bar
   would make a third of the bar a link to the homepage. A grid track grows
   without the anchor growing with it.

   Only when docked. Undocked the centre slot is empty and the flex row already
   puts the logo left and the nav right, which is correct and cheaper. */
body.roam-docked .roam-head__row {
  display: grid;
  /* minmax(0, …) ON ALL THREE, and the zeros are what make it exact.

     A bare `1fr` has a min-content floor, so a side column that cannot fit its
     contents takes more than its share and the centre shifts by half the
     difference. That is measurable: at 768 the pill landed 12.5px left, because
     the nav's Book Now, account glyph and hamburger come to slightly more than
     the third the free space allowed once a 308px pill had taken its width.

     With a floor of 0 the two side tracks are equal by construction and the
     PILL gives way instead — its segments already carry overflow:hidden and
     text-overflow:ellipsis for exactly this. A search summary that truncates is
     a smaller lie than one that sits off-centre and looks deliberate.

     The 308 is itself the placeholder's fault: "Add guests" is 17px wider than
     the "2 guests" the dev site had set when it was measured, and at the dev's
     291 there is enough room for the naive version. It is not worth relying on
     the copy staying short. */
  grid-template-columns: minmax(0, 1fr) minmax(0, auto) minmax(0, 1fr);
  align-items: center;
}
body.roam-docked .roam-nav { justify-self: end; }
body.roam-docked .roam-head__center { min-width: 0; }
/* The panel hangs off the row, so the row is what it must measure against. */
.roam-head__row { position: relative; }

/* THE HAMBURGER'S SHAPE IS DEFINED HERE, NOT IN THE MEDIA QUERY.
   It used to be: everything — the padding, the tap target, the three bars —
   sat inside @media (max-width: 760px), and only `display` was switched. That
   was invisible for as long as narrow was the only way to see the button. The
   moment the docked header showed it at 1440 it rendered as a single 22px dash,
   because the bars' own geometry had never been declared outside the query.

   So the shape is unconditional and only the DISPLAY is a state. A styled
   button that is display:none costs nothing. */
/* TWO ASYMMETRIC BARS, RIGHT-ALIGNED — the dev site's mark, measured at 390:
   two bars in cream, radius 1, 6px apart, the short one at 70% of the long one
   so it sits under its tail. Before that it drew three equal 22x2 bars centred,
   which is a different mark. The dev site's literal pixels were 20x2 over 14x1
   in a 44x44 button; the sizes below are the client's, the drawing is the same
   one scaled. */
/* THREE BOXES, AND THEY ARE NOT THE SAME BOX. Keeping them separate is the
   whole point of the wrapper in header.php:

     48x48   .roam-head__toggle   what a thumb hits
     28x28   .roam-head__bars     the space RESERVED for the mark
     20x2 + 14x1                  the mark itself

   The middle one is the one that gets misread. It is not the drawing's size —
   the drawing is 20 wide and always has been — it is how much room the drawing
   is given. Conflating the two produced a 28px-wide hamburger for one build.

   THE MARGINS ARE ARITHMETIC ON THE OTHER TOKENS, not taste.

   Right is -(tap - reserved) / 2 = -10: exactly the dead space between the
   reserved box's right edge and the button's. The bars are right-aligned inside
   the reserved box, so this lands the BARS on the 25px gutter while the target
   overhangs it — the dev site's construction, at a bigger target.

   Left cancels the dead space on the button's OTHER side — the 10px of padding
   plus the 8px of reserved box the 20px bar does not fill — so the air a reader
   sees between the account glyph and the first bar is the cluster's own gap and
   nothing else. It comes out as -(tap - burger)/2 - (burger - 20), which is the
   right margin minus the slack, and it does not mention the gap at all: the
   flex row has already inserted that, and this only has to stop the button's
   empty left half adding to it.

   IT WAS +4 AND IT PAINTED 22px THERE, against the dev site's 10 — reported as
   "looks like double the space", and it was more than double. +4 came from
   holding two 48px targets exactly apart, which sounds right and is not
   possible: a 40px glyph, 10px of air and a 20px bar span 70 points, and two
   48px targets need 96. Something has to overlap. The dev site overlaps too —
   its button covers 8px of the space its own account glyph's target wants.

   SO THEY OVERLAP, AND THE ACCOUNT WINS IT. That is the z-index below, and it
   is the whole reason this is acceptable: the contested strip sits immediately
   right of the account's ink and 10px clear of the nearest bar, so it belongs
   to the account by proximity. The hamburger gives up reserved space on the one
   side its mark never occupies — it is right-aligned in its box — and keeps
   every point on and around the bars. Without the z-index the hamburger would
   win by document order, which is the wrong way round and is what the earlier
   +4 was written to avoid. */
.roam-head__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: var(--roam-hdr-tap);
  height: var(--roam-hdr-tap);
  margin-left: calc(
    ((var(--roam-hdr-tap) - var(--roam-hdr-burger)) / -2) - (var(--roam-hdr-burger) - 20px)
  );
  margin-right: calc((var(--roam-hdr-tap) - var(--roam-hdr-burger)) / -2);
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}
.roam-head__bars {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-end;
  width: var(--roam-hdr-burger);
  height: var(--roam-hdr-burger);
  gap: 6px;
}
.roam-head__bar {
  display: block;
  height: 2px;
  border-radius: 1px;
  background: var(--roam-cream);
  transition: transform 0.2s ease, width 0.2s ease;
}
/* 20x2 OVER 14x1 — THE DEV SITE'S LITERAL PIXELS, not a ratio scaled to fill
   the icon box. These were 28 and 20 for one build, on the reading that "render
   within a 28x28 box" meant fill it. It does not: the 28 is the space RESERVED
   for the mark, and the mark is the size it has always been. Corrected on the
   client's instruction to go and look at the dev site rather than at a drawing,
   and the dev site is unambiguous — 20x2 and 14x1 at 1024, 768 and 390 alike,
   cream, radius 1, six apart, right-aligned so the short one sits under the
   long one's tail.

   Right-aligned inside the box is what keeps the arithmetic honest: the box's
   right edge is the gutter (see the button's negative right margin), so the
   BARS land on the gutter whatever width they are, and the reserved space can
   change without moving the mark. */
.roam-head__bar--long  { width: 20px; }
.roam-head__bar--short { width: 14px; height: 1px; }

/* Open: both bars go to the LONG bar's width — 20, the mark's own size, not the
   28 of the box it is reserved space in — and rotate onto one point.
   The rotate-then-translate ORDER matters — the translate happens in the rotated
   frame, which is what brings the two onto the same centre. 4px each is half the
   8px between their centres once both are 2px tall on a 6px gap, so the number
   follows from the gap rather than being tuned by eye. Same construction as the
   promo bar's X, which is the canonical one. */
.roam-nav[data-open='1'] .roam-head__bar { width: 20px; height: 2px; }
.roam-nav[data-open='1'] .roam-head__bar--long  { transform: rotate(45deg) translateY(4px); }
.roam-nav[data-open='1'] .roam-head__bar--short { transform: rotate(-45deg) translateY(-4px); }

/* 1024, NOT 760 — and this was the largest single error in the header.
   The dev site drops its nav links and raises the hamburger at 1025, not at
   761: measured at 1024 and at 768 it shows logo, Book Now, the account glyph
   and the hamburger, and no links at all. Ours kept five links in the bar from
   761 all the way to 1024, which is a different header at two of the four
   viewports the design is drawn at.

   Only DISPLAY is switched here. The button's size, its icon box and its
   negative margins are declared unconditionally further up, which is the rule
   this file has already been burned by twice: geometry inside a media query is
   invisible until something shows the element outside it, and then it renders
   wrong. The docked desktop bar shows this button at 1440. */
/* THE FLYOUT PANEL — two shapes on one list.
   At <=1024 the links leave the bar and drop into a forest flyout: a compact
   card anchored under the right of the bar on a tablet, and a full-width sheet
   on a phone. The links stack with a hairline between them, the Book Now pill
   closes the panel, and (on a phone) the account row opens it. */
@media (max-width: 1024px) {
  .roam-head__toggle { display: flex; }

  .roam-nav__links {
    display: none;
    position: absolute;
    top: calc(100% + 8px);
    right: var(--roam-hdr-x);
    left: auto;
    width: min(360px, calc(100vw - 2 * var(--roam-hdr-x)));
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--roam-sp-8) var(--roam-sp-24) var(--roam-sp-24);
    border-radius: var(--roam-r-lg);
    background: var(--roam-surface-forest-down);
    box-shadow: 0 24px 60px rgba(15, 26, 22, 0.45);
  }
  .roam-nav[data-open='1'] .roam-nav__links { display: flex; }
  /* Each link is a full-width row with a hairline under it. */
  .roam-nav__links > li { border-bottom: 1px solid rgba(250, 246, 241, 0.12); }
  .roam-nav__links > li:last-child { border-bottom: 0; }
  .roam-nav__links a {
    display: flex;
    align-items: center;
    height: auto;
    padding: var(--roam-sp-16) 0;
    width: 100%;
    font-size: var(--roam-fs-15);
    color: var(--roam-cream);
  }
  /* Book Now closes the panel as a gold pill. */
  .roam-nav__cta { display: block; border-bottom: 0; margin-top: var(--roam-sp-16); }
  .roam-nav__cta .roam-booknow {
    display: inline-flex;
    height: auto;
    padding: var(--roam-sp-12) var(--roam-sp-24);
    border-radius: var(--roam-r-full);
    background: var(--roam-gold);
    color: var(--roam-forest);
    font-size: var(--roam-fs-15);
    font-weight: var(--roam-fw-semibold);
  }
  .roam-nav__cta .roam-booknow:hover { background: var(--roam-gold-active); }
}

@media (max-width: 767px) {
  /* A PHONE OPENS A FULL-WIDTH SHEET, not the tablet card. The account glyph
     leaves the bar — the phone bar is logo, Book Now and the hamburger — and
     leads the panel as a gold row, the way the design opens the menu. */
  .roam-head__row > .roam-nav > .roam-acct { display: none; }
  .roam-nav__links {
    left: 0;
    right: 0;
    width: auto;
    max-width: none;
    padding: var(--roam-sp-8) var(--roam-hdr-x) var(--roam-sp-32);
    /* All four corners — the sheet floats 8px below the header (top from the
       ≤1024 rule), so the top corners round like the bottom rather than reading
       as a slab butted against the bar. */
    border-radius: var(--roam-r-lg);
  }
  .roam-nav__acct { display: block; }
  .roam-nav__acct a {
    gap: var(--roam-sp-12);
    color: var(--roam-gold);
    font-family: var(--roam-font-display);
    font-size: var(--roam-fs-20);
  }
  .roam-nav__acct svg { width: 30px; height: 30px; flex: 0 0 auto; }
}

/* ── The property page's sticky bar ──────────────────────────────────────── */
/* ONE BAR, THREE STATES, ONE HEIGHT. It never changes size and never jumps;
   only what is inside it cross-fades. Ported rule by rule from the old site,
   which is the design of record for this component — and the port is where the
   17 selectors' worth of `#all_wrapper` prefix and their four !important
   declarations come off, because there is no parent theme here to out-specify.

   IT STICKS UNDER THE SITE HEADER, not at 0. The header is sticky at the top of
   the viewport and 1px of border under it, so this bar's resting place is
   exactly that far down — get it wrong by a pixel either way and you get a
   hairline of page showing through the seam, which is the defect the old sheet
   has three paragraphs about. Written as arithmetic on the header's own token
   so it cannot drift when the header's height changes. */
:root { --roam-sticky-h: 52px; }
.roam-sticky {
  position: sticky;
  /* The measured header, not an assumed one — assets/js/roam-header.js keeps
     --roam-hdr-total honest, so this bar comes to rest against the real bottom
     edge rather than one pixel of hairline away from it at a width where the
     header is a different height. */
  top: var(--roam-hdr-total);
  z-index: 100;
  background: rgba(250, 246, 241, 0.94);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  /* No top border — the hairline at that seam is the header's bottom border and
     belongs to the header. The underside is lighter than the header's rule
     because the two edges do different work: this one separates the bar from
     the page, the header's separates it from the header. */
  border-top: 0;
  border-bottom: 1px solid #F0EDE8;
}
.roam-sticky__in {
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding-inline: var(--roam-page-x);
  height: var(--roam-sticky-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-20);
}
/* The two left panes share ONE GRID CELL, so the bar's width never depends on
   which state is showing and the swap cannot reflow.

   overflow:hidden at every width, not only on phones: the links pane is a run
   of nowrap text, and where the bar is narrower than that plus the right-hand
   controls it spilled out of its own box and painted over Share. Clipping the
   left pane is the honest failure — a link runs off the edge and can be
   scrolled to. Painting over the control that must never move is not. */
.roam-sticky__l {
  display: grid;
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
}
.roam-sticky__pane {
  grid-area: 1 / 1;
  display: flex;
  align-items: center;
  gap: var(--roam-sp-8);
  min-width: 0;
  transition: opacity 0.2s var(--roam-easing);
}
.roam-sticky__pane--links { gap: 22px; }
/* Hidden panes are INERT, not merely transparent. A faded-out breadcrumb still
   takes clicks and still reaches the keyboard otherwise, which is a tab stop on
   something nobody can see. */
.roam-sticky:not([data-state='1']) .roam-sticky__pane--crumbs,
.roam-sticky[data-state='1'] .roam-sticky__pane--links {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}

/* The breadcrumb. The padding is a TOUCH TARGET, not spacing: the first crumb
   is the route back to the results, and at its natural width it is around 32px
   — under the minimum, on the one control here that most owes it. */
.roam-sticky__crumb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  padding: 14px 6px;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-12);
  font-weight: var(--roam-fw-medium);
  color: #6B7671;
  text-decoration: none;
  white-space: nowrap;
}
a.roam-sticky__crumb:hover,
a.roam-sticky__crumb:focus-visible { color: var(--roam-gold); }
.roam-sticky__crumb.is-current {
  /* Not a control — it is the page you are on, so it needs no tap target and
     should not reserve one. */
  display: inline;
  min-width: 0;
  padding-inline: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--roam-forest);
  font-weight: var(--roam-fw-semibold);
}
.roam-sticky__chev { color: #B9B2A8; flex: none; }

/* Section links.
   THE UNDERLINE MARKS WHERE YOU ARE, AND NOTHING ELSE. Hover moves the text to
   forest and stops there; only the active link is underlined, so the mark means
   one thing.

   It is a pseudo-element rather than a border-bottom. The link carries 15px of
   vertical padding it needs for its touch target, and a border sits at the
   bottom of the PADDING box — which put the rule 15px under the words, reading
   as unattached to them. ::after positions against the text instead. */
.roam-sticky__link {
  position: relative;
  padding: 15px 0;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-medium);
  color: #7C8A84;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.2s var(--roam-easing);
}
.roam-sticky__link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 9px;
  height: 2px;
  background: var(--roam-gold);
  opacity: 0;
  transition: opacity 0.2s var(--roam-easing);
}
a.roam-sticky__link:hover,
a.roam-sticky__link:focus-visible { color: var(--roam-forest); }
.roam-sticky__link.is-active { color: var(--roam-forest); }
.roam-sticky__link.is-active::after { opacity: 1; }

/* The right anchor. Above the left pane and opaque, so nothing can render
   through it or over it — see the overflow note on .roam-sticky__l. */
.roam-sticky__r {
  display: flex;
  align-items: center;
  gap: 18px;
  flex: none;
  position: relative;
  z-index: 2;
  background: inherit;
}
/* Padding is HIT AREA, not spacing. The design asks for no button chrome, which
   is a look and not a licence to ship a 22px tap target: the vertical padding
   fills the bar's height and changes nothing visible. */
.roam-sticky__act {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 15px 0;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-forest);
  transition: opacity 0.2s var(--roam-easing);
}
.roam-sticky__act:hover { opacity: 0.6; }
.roam-share { white-space: nowrap; }
/* A confirmation colour, and the one green on the site. It is not a token
   because nothing else needs it: a --roam-green would read as a palette entry
   and invite use, and the palette is forest, gold and cream. */
.roam-share.is-copied { color: #3A7D52; }

/* Save — the outline heart by default, the gold fill when saved. The button
   carries the state (.is-on, set by roam-favs.js); roam.css picks the weight,
   so the outline and the fill can never both show. */
.roam-save { white-space: nowrap; }
.roam-save__on { display: none; }
.roam-save.is-on { color: var(--roam-gold); }
.roam-save.is-on .roam-save__off { display: none; }
.roam-save.is-on .roam-save__on { display: block; }
/* "Save" becomes "Saved" once it is on — the label carries the state alongside
   the gold fill, so the change is not colour-only. */
.roam-save__txt--on { display: none; }
.roam-save.is-on .roam-save__txt--off { display: none; }
.roam-save.is-on .roam-save__txt--on { display: inline; }

/* State 3 — the rate and the CTA arrive, and Share drops its label to make room
   for them without the right anchor moving. */
.roam-sticky__price,
.roam-sticky__cta { display: none; }
.roam-sticky[data-state='3'] .roam-sticky__price {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-forest);
  white-space: nowrap;
}
.roam-sticky__amt { font-family: var(--roam-font-display); font-size: var(--roam-fs-15); font-weight: 700; }
.roam-sticky__per { font-weight: var(--roam-fw-normal); color: var(--roam-text-muted); }
.roam-sticky__star { color: var(--roam-gold); }
.roam-sticky[data-state='3'] .roam-sticky__cta {
  display: inline-flex;
  align-items: center;
  padding: 9px var(--roam-sp-20);
  background: var(--roam-gold);
  color: var(--roam-forest);
  border-radius: var(--roam-r-full);
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.roam-sticky[data-state='3'] .roam-sticky__cta:hover,
.roam-sticky[data-state='3'] .roam-sticky__cta:focus-visible { background: var(--roam-gold-active); }
/* The label is hidden from the EYE and kept for the screen reader — the icon
   alone is not a name. Save drops its label the same way Share does when the
   bar docks, so the rate and CTA have room without the right anchor moving. */
.roam-sticky[data-state='3'] .roam-share__txt,
.roam-sticky[data-state='3'] .roam-save__txt {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.roam-sticky[data-state='3'] .roam-sticky__act { gap: 0; }

/* The short link forms exist only for the phone; a wider screen shows the full
   descriptions ("The Home", not "Home"). */
.roam-sticky__link-short { display: none; }

/* Tablet + phone: the full breadcrumb and full link labels may not fit, and a
   breadcrumb that wraps is worse than one that scrolls. The left pane scrolls
   sideways (scrollbar hidden) when it overruns; on a wide screen it does not. */
@media (max-width: 1024px) {
  .roam-sticky__pane {
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .roam-sticky__pane::-webkit-scrollbar { display: none; }
  .roam-sticky__pane--links { gap: var(--roam-sp-16); }
  .roam-sticky__r { gap: var(--roam-sp-12); }
}

/* PHONE ONLY (client 2026-08-03) — the tablet bar stays the desktop bar. A phone
   collapses the crumb to "Stays › place", swaps the link labels to their short
   forms, and drops the Save/Share text to icons. */
@media (max-width: 767px) {
  .roam-sticky__crumb--mid,
  .roam-sticky__chev--mid { display: none; }
  .roam-sticky__link-full { display: none; }
  .roam-sticky__link-short { display: inline; }
  .roam-sticky .roam-share__txt,
  .roam-sticky .roam-save__txt {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }
}

/* A jumped-to section must clear BOTH the header and this bar, and this is the
   no-JavaScript half of that: roam-property.js does the same arithmetic for the
   smooth scroll, and scroll-margin-top does it for a plain #hash landing, a
   reload on an anchor, and a link followed from another page. Without it the
   heading lands underneath the bar and the guest reads the paragraph after the
   one they asked for. */
.roam-sec[id],
.roam-std[id] { scroll-margin-top: calc(var(--roam-hdr-total) + var(--roam-sticky-h) + 12px); }

/* The homepage's own anchor targets — the header's Why Book Direct / Roam
   Rewards / Contact links land on #why-direct, #rewards and #contact. There is
   no sticky sub-nav here, only the header, so each landing clears the header
   alone. Without it the section heading tucks up under the bar and the link
   reads as having missed. */
body.home [id] { scroll-margin-top: calc(var(--roam-hdr-total, var(--roam-header-h)) + 16px); }

/* ── The book-direct comparison ───────────────────────────────────────────── */
/* A TABLE, DRAWN AS A TABLE. The old site built this out of a grid of spans,
   which looks identical and tells a screen reader nothing: two column headers
   and a row header per line are what make "$271" mean "guest service fee, on a
   listing site" rather than a number in a list. The cost of using the real
   element is a few resets; the cost of not using it is that the section is
   unreadable to anyone not looking at it.

   It sits inside the why-direct band, on the forest ground, so the type is the
   cream family rather than the page's. */
/* The comparison lives inside .roam-why__panel now, which supplies the box and
   the padding — so the table fills the panel and adds nothing of its own. */
.roam-cmp {
  margin-top: 0;
  max-width: none;
}
/* The bordered panel that holds the comparison — the right column of the card.
   A barely-there fill and hairline lift it off the gradient without a second
   solid block; its own 20px radius and 24px padding are the design's. */
.roam-why__panel {
  flex: 1 1 360px;
  position: relative;
  align-self: start;
  background: rgba(250, 246, 241, 0.05);
  border: 1px solid rgba(250, 246, 241, 0.1);
  /* Nested inside the r-xl (20px) card, so it steps DOWN a tier to r-lg (12px):
     a same-radius box inside a rounded box reads as two stacked cards, not one
     nested in the other. The savings box below steps down again. */
  border-radius: var(--roam-r-lg);
  padding: var(--roam-sp-24);
}
.roam-cmp__t {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-14);
  color: var(--roam-dark-text-body);
}
.roam-cmp__t th,
.roam-cmp__t td {
  padding: var(--roam-sp-6) 0;
  text-align: right;
  font-weight: var(--roam-fw-normal);
}
.roam-cmp__t th[scope='row'] { text-align: left; }
.roam-cmp__t thead th {
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-md);
  text-transform: uppercase;
  color: var(--roam-dark-text-muted);
  border-bottom: 1px solid rgba(250, 246, 241, 0.14);
}
/* "3 nights" LEFT over its column — client instruction 2026-08-02, superseding
   the earlier centring: centred, it floated in from the left edge and no longer
   read as the header of the row-label column it sits above. Left-aligned it
   lines up with the "Nightly rate ×3" labels beneath it. The other two headers
   stay right-aligned over the figures they sit above. */
.roam-cmp__t thead th:first-child { text-align: left; }
/* The Direct column is the win — its header and its one $0 cell carry the gold
   the total already uses, so the eye reads down the column that matters. */
.roam-cmp__t thead th:last-child { color: var(--roam-gold); }
.roam-cmp__free { color: var(--roam-gold); font-weight: var(--roam-fw-semibold); }
/* NO LINES BETWEEN THE BODY ROWS — client instruction. Nightly rate, service fee
   and taxes read as one block; the only rules are under the header and around
   the total. */
.roam-cmp__t tfoot th,
.roam-cmp__t tfoot td {
  border-top: 1px solid rgba(250, 246, 241, 0.14);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-cream);
}
/* The one number the section exists to make obvious. */
.roam-cmp__win { color: var(--roam-gold); }
.roam-cmp__t s { text-decoration-thickness: 1px; opacity: 0.7; }

/* The summary. A rule ABOVE it is the line below the total — client instruction
   to add one there. The dev site's structure exactly: two single-line rows and
   the gold "You save total" highlight. */
.roam-cmp__sum {
  border-top: 1px solid rgba(250, 246, 241, 0.14);
  margin-top: 2px;
  padding-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
/* SINGLE LINE — label and its parenthetical on the left, the figure on the
   right, on one row. The sub was display:block, which dropped it to a second
   line; it is inline now, the way the dev sets it. */
.roam-cmp__sumrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-16);
}
.roam-cmp__sumlab { font-size: var(--roam-fs-13); color: rgba(250, 246, 241, 0.7); }
.roam-cmp__sumsub { color: rgba(250, 246, 241, 0.45); }
.roam-cmp__sumval {
  font-family: var(--roam-font-display);
  font-weight: 700;
  font-size: var(--roam-fs-20);
  color: var(--roam-cream);
}
/* THE SAVINGS HIGHLIGHT, the dev site's gold box: a gold-tinted panel with a
   gold hairline, the label small and tracked in gold, the figure in Playfair at
   28. It was a plain bigger row before — the highlight the section builds to. */
.roam-cmp__save {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-16);
  padding: 12px 16px;
  background: rgba(198, 151, 91, 0.12);
  border: 1px solid rgba(198, 151, 91, 0.35);
  /* Nested inside the r-lg (12px) panel — steps down again to r-sm (8px). */
  border-radius: var(--roam-r-sm);
}
.roam-cmp__savelab {
  font-size: var(--roam-fs-12);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--roam-gold);
  font-weight: var(--roam-fw-semibold);
  line-height: 1;
}
.roam-cmp__saveval {
  font-family: var(--roam-font-display);
  font-weight: 700;
  font-size: var(--roam-fs-28);
  color: var(--roam-gold);
  /* line-height:1 so Playfair's tall line box collapses to the glyph — align-
     items:center then lines the number's centre up with "YOU SAVE TOTAL"
     instead of leaving it riding high off the display font's ascent. */
  line-height: 1;
}
/* THE COMPARISON STAYS ONE TABLE ON A PHONE. It is a read-across comparison —
   Listing against Direct, row by row — so splitting it into two cards or
   stacking Listing over Direct would break the very thing it exists to show.
   Instead the box gives the table back the width the nested paddings were
   taking: the section's own inset drops, the panel's padding drops, and the
   figures step down a size so three columns sit comfortably at 360px. */
@media (max-width: 560px) {
  .roam-why { padding: var(--roam-sp-20); gap: var(--roam-sp-24); }
  .roam-why__panel { padding: var(--roam-sp-16); }
  .roam-cmp__t { font-size: var(--roam-fs-13); }
  /* Tighten the gap so "You earn  Roam Rewards, 5%" and its +$80 stay on one
     line at 360–390 — the 16px gap tipped the label to a second row by ~2px. */
  .roam-cmp__sumrow { gap: 10px; }
  .roam-cmp__sumval { font-size: var(--roam-fs-18); flex: none; white-space: nowrap; }
  .roam-cmp__saveval { font-size: var(--roam-fs-24); }
  .roam-cmp__save { padding: 10px 12px; }
}

/* The three stats under the copy — $0 / 5% / 48h. A three-across grid that
   wraps on its own, capped so it does not stretch the full column width, the
   way the design sets it (repeat(auto-fit, minmax(150px, 1fr)); gap 18). */
.roam-why__stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: var(--roam-sp-18);
  max-width: 520px;
}
/* THE GOLD LEFT BAR, the dev site's stat structure exactly: a 2px gold rule down
   the left of each figure, the number in Playfair, the label small and tracked
   under it. */
.roam-why__stat {
  border-left: 2px solid var(--roam-gold);
  padding-left: 14px;
}
.roam-why__statn {
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-28);
  font-weight: 700;
  /* Cream, not gold — the gold is the bar. The figure is the card's own ink, the
     way the dev site sets it; a gold figure beside a gold bar doubles the colour
     and loses the number's weight. */
  color: var(--roam-cream);
}
.roam-why__statl {
  margin-top: 4px;
  font-size: var(--roam-fs-12);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
  color: rgba(250, 246, 241, 0.6);
}

/* The gold rule that opens each quote card on the old site. */
.roam-quote__bar {
  display: block;
  width: 32px;
  height: 2px;
  border-radius: 1px;
  background: var(--roam-gold);
  margin-bottom: var(--roam-sp-16);
}
.roam-cmp__note {
  margin: var(--roam-sp-8) 0 0;
  font-size: var(--roam-fs-12);
  color: var(--roam-dark-text-muted);
}

/* ── The three homepage sections that were missing ────────────────────────── */
/* Rewards, Guest feedback and Field Notes were left out of the first build
   because their copy carried claims nothing could verify. That was the wrong
   call — client, 2026-08-01: "you don't deliberately leave anything out… for
   now it's more important to load the sections properly." A section that is
   absent cannot be reviewed, and a homepage missing four of its nine blocks is
   not something copy QA can find. */

/* Rewards: the claim on the left, the mechanism on the right. Two columns
   because the three steps are a sequence and reading them under a paragraph
   makes them look like a list of features. */
.roam-rw { padding-block: var(--roam-sec-y); }
/* THE COLUMNS ARE UNEVEN, and TOP-ALIGNED. The design gives the copy the wide
   column and the steps the narrow one — 860 to 380 at 1440, roughly two to one.
   Client instruction 2026-08-02: the eyebrow, title and body align to the top of
   the card, not to its vertical centre — a centred copy column floated the
   eyebrow down off the top edge the steps start at. */
.roam-rw__inner {
  display: grid;
  gap: var(--roam-sp-40);
  align-items: start;
}
@media (min-width: 900px) {
  .roam-rw__inner { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--roam-sp-48); }
}
/* This is the one homepage heading the design sizes down — clamp(28,3.5vw,38)
   against the shared clamp(30,4vw,40) — and it takes 14/12 margins rather than
   the shared 8/16. It is a heading over a short numbered list, not over a grid,
   so it wants less air under it. */
.roam-rw .roam-hsec__h {
  font-size: clamp(28px, 3.5vw, 38px);
  margin: var(--roam-flow-related) 0 var(--roam-sp-12);
}
.roam-rw .roam-hsec__body { max-width: 480px; margin-bottom: 0; }
.roam-rw__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--roam-sp-12);
}
/* EACH STEP IS A CARD, not a hairline-separated row. The design draws a white
   box with a light border and a 12px radius; ours drew rules between the steps.
   A card reads as a discrete step, which is what a numbered list of them is
   trying to say. */
.roam-rw__step {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-16);
  padding: 16px 18px;
  background: var(--roam-white);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
}
/* A BARE NUMERAL, not a filled disc. The design sets the ordinal in Playfair at
   20px in gold with no ground behind it — the numeral IS the mark. A 32px
   gold disc with forest text was a second, louder idea competing with it. */
.roam-rw__n {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-20);
  font-weight: 700;
  /* line-height:1 collapses Playfair's tall line box to the glyph, so
     place-items:center actually centres the numeral in its box and it lines up
     with the step copy beside it rather than riding high. */
  line-height: 1;
  color: var(--roam-gold);
}
.roam-rw__t { font-size: var(--roam-fs-14); color: var(--roam-text-body); }

/* Guest feedback. The heading is a real average over a real count — see
   roam_home_reviews() — so there is no styling here pretending otherwise. */
.roam-hsec__note {
  margin: calc(var(--roam-sp-8) * -1) 0 var(--roam-sp-24);
  font-size: var(--roam-fs-13);
  color: var(--roam-text-muted);
}
/* auto-fit, NOT a hard 1→3 switch. The design's grid is
   repeat(auto-fit, minmax(280px, 1fr)) at every width, so it shows two columns
   in the 900–1230 band where a fixed three cramps them. */
/* THREE ITEMS, NEVER TWO. auto-fit put two across on a tablet and left the third
   orphaned on its own row. With exactly three cards there is no balanced
   two-row layout — that wants four or six — so the row goes straight from one
   column (stacked, as on a phone) to three, skipping the two-up stage entirely.
   The same rule holds for Field Notes below. */
.roam-quotes {
  display: grid;
  gap: var(--roam-sp-24);
  grid-template-columns: 1fr;
}
@media (min-width: 820px) {
  .roam-quotes { grid-template-columns: repeat(3, 1fr); }
}
/* A CREAM card on the white band, no border — the design's own, and the left
   accent is what separates it from the ground rather than an outline. 8px
   radius, 26px padding. */
.roam-quote {
  position: relative;
  margin: 0;
  padding: 26px;
  padding-left: 30px;
  background: var(--roam-cream);
  border-radius: var(--roam-r-sm);
  overflow: hidden;
}
/* THE ACCENT IS A FULL-HEIGHT RULE, not a 32x2 dash. It was drawn as the same
   little gold mark the eyebrows use, in flow above the stars; the design runs a
   3px bar the whole height of the card down its left edge, blue at the top
   fading to gold — the brand's two accent colours, which the gradient token
   already holds. */
.roam-quote__bar {
  position: absolute;
  top: 0;
  left: 0;
  width: 3px;
  height: 100%;
  background: var(--roam-gradient-accent, linear-gradient(180deg, #5B8FA8, #C6975B));
  border-radius: var(--roam-r-sm) 0 0 var(--roam-r-sm);
}
/* THE QUOTE IS PLAYFAIR ITALIC — the single most visible type in the section
   and the thing that makes it read as a voice rather than a testimonial block.
   It was DM Sans upright. */
.roam-quote__text {
  margin: var(--roam-sp-12) 0 var(--roam-sp-16);
  font-family: var(--roam-font-display);
  font-style: italic;
  font-size: 17px;
  line-height: 1.5;
  color: var(--roam-forest);
}
.roam-quote__who {
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-text-primary);
}

/* ── Field Notes ──────────────────────────────────────────────────────────
   A cream card with the media clipped to its own radius, a meta line, a
   heading, an excerpt and a read-more — the design's guide card, value for
   value. It was a bare block with a floating rounded thumbnail and no meta
   line and no read-more at all. */
/* One column to three, never two — the same three-item rule as the reviews
   above, for the same reason: a 2+1 orphan on a tablet. */
.roam-guides {
  display: grid;
  gap: var(--roam-sp-24);
  grid-template-columns: 1fr;
}
@media (min-width: 820px) {
  .roam-guides { grid-template-columns: repeat(3, 1fr); }
}
.roam-guide {
  display: flex;
  flex-direction: column;
  background: var(--roam-cream);
  border: 1px solid var(--roam-border-cream);
  border-radius: var(--roam-r-xl);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: transform 0.3s var(--roam-easing), box-shadow 0.3s var(--roam-easing);
}
/* Not a.roam-guide — the placeholder cards are <div> (no post to link to yet),
   and they must lift like the real ones, the way the property cards' hover keys
   off .roam-card rather than the anchor. */
.roam-guide:hover { transform: var(--roam-lift-hover); box-shadow: var(--roam-shadow-hover); }
/* The media is clipped by the card's own 20px radius rather than carrying its
   own — a 4/3 window at the top, filtered like every other photograph on the
   site. */
.roam-guide__media {
  display: block;
  aspect-ratio: 3 / 2;
  overflow: hidden;
}
.roam-guide__media .roam-guide__img,
.roam-guide__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--roam-photo-filter);
  /* The photo zooms on hover, the same 1.04 the property cards use. */
  transition: transform 0.4s var(--roam-easing);
}
.roam-guide__body {
  display: block;
  padding: 22px 24px 24px;
}
.roam-guide__meta {
  display: block;
  margin-bottom: var(--roam-flow-coupled);
  font-size: var(--roam-fs-12);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
  text-transform: uppercase;
  color: var(--roam-text-muted);
}
/* The category leads the line in gold; the place and read-time stay muted. */
.roam-guide__cat { color: var(--roam-gold); }
.roam-guide__sep,
.roam-guide__rt { color: var(--roam-text-muted); }
.roam-guide__h {
  margin: 0 0 var(--roam-flow-related);
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-20);
  line-height: var(--roam-lh-snug);
  color: var(--roam-text-primary);
}
.roam-guide__x {
  margin: 0 0 var(--roam-flow-related);
  font-size: var(--roam-fs-14);
  line-height: var(--roam-type-body-lh);
  color: var(--roam-text-body);
}
.roam-guide__more {
  display: block;
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
  color: var(--roam-gold);
}
/* Rollover matches the property cards: the photo zooms 1.04 and the title
   shifts to forest-soft (not gold — the card's gold is the meta and the CTA). */
.roam-guide:hover .roam-guide__img { transform: scale(1.04); }
.roam-guide:hover .roam-guide__h { color: var(--roam-forest-soft); }
.roam-guide:hover .roam-guide__more { color: var(--roam-gold-active); }

/* ── The placeholder tile ─────────────────────────────────────────────────
   Where a lead photo will go and does not yet. NOT a stock image and NOT a
   silent gap — a marked ground that says what it is, keyed by a data attribute
   the QA harness reads. It carries the card's own aspect so the card's shape is
   right the day the photo lands. */
.roam-guide__media--placeholder,
.roam-ph {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  background:
    repeating-linear-gradient(-45deg, rgba(27, 43, 39, 0.05) 0 12px, transparent 12px 24px),
    var(--roam-sand);
}
.roam-ph__tag {
  padding: 5px 12px;
  border-radius: var(--roam-r-full);
  background: rgba(27, 43, 39, 0.55);
  color: var(--roam-cream);
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
}
/* A placeholder card is not a link — the whole card dims a touch so it reads as
   pending against the real ones beside it. */
.roam-guide--placeholder { cursor: default; }

/* ── The full-bleed bands ─────────────────────────────────────────────────
   Reviews and field notes sit on a white ground with a hairline top and bottom;
   the design runs both edge to edge. A section goes full-bleed by being emitted
   BETWEEN the .roam-wrap groups rather than inside one — .roam-main is full
   width — so the only thing these modifiers add is the paint and an inner cap,
   the same job .roam-wrap does for the in-measure sections. */
.roam-hsec--white {
  background: var(--roam-white);
  border-block: 1px solid var(--roam-border-cream);
}
/* THE SAME BOX AS .roam-wrap, TO THE PIXEL. This has to align with the sections
   above and below it, which sit in .roam-wrap — max-width 1330 (= --roam-shell,
   the 1280 content box plus its two 25px gutters), NOT --roam-max-width 1280.
   With 1280 here the band's content capped 50px narrower than the wrapped
   sections and stepped in from them: the "gutter that doesn't align" on guest
   feedback and field notes was exactly that 50px. --roam-shell is the token for
   a box that pads inside its own max-width, which is what this is. */
.roam-hsec__inner {
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding-inline: var(--roam-page-x);
}
/* The dark band paints full width and caps its own content at .roam-ct__inner,
   so it needs no __inner of its own — but it DOES need the section's default
   horizontal cap removed, since it is now outside .roam-wrap. .roam-hsec has
   none, so there is nothing to undo; this is a note, not a rule. */

/* ── Footer ──────────────────────────────────────────────────────────────── */
/* The same forest as the header, so the page is bookended rather than merely
   ending. margin-top is 0: every section already pays its own bottom padding,
   and adding more here made the seam above the footer the largest gap on the
   page. */
.roam-foot {
  padding-block: var(--roam-sp-64) var(--roam-sp-32);
  background: var(--roam-forest);
  color: var(--roam-cream);
}

/* Two equal columns from 768 up, stacked below. Not space-between: on a 1280px
   container that pushes Contact to the far edge, which read as an alignment
   bug on the dev site and was corrected there to 1fr 1fr — Contact begins at
   the midline. */
.roam-foot__top {
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;
  gap: var(--roam-sp-32);
  margin-bottom: var(--roam-sp-48);
}
@media (min-width: 768px) {
  .roam-foot__top { grid-template-columns: 1fr 1fr; }
}

.roam-foot__brand { max-width: 360px; }
.roam-foot__mark { display: flex; align-items: center; gap: var(--roam-sp-12); margin-bottom: var(--roam-sp-16); }
.roam-foot__logo { height: 32px; width: auto; display: block; }
/* The divider label. The rule on its left is a border rather than a separate
   element, so it cannot drift out of line with the text it divides. */
.roam-foot__sub {
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: 2.5px;
  text-transform: uppercase;
  color: var(--roam-dark-text-body);
  border-left: 1px solid rgba(250, 246, 241, 0.25);
  padding-left: var(--roam-sp-12);
}
.roam-foot__blurb {
  font-size: var(--roam-fs-14);
  line-height: 1.7;
  color: var(--roam-dark-text-body);
  margin: 0 0 var(--roam-sp-18);
}

/* h3 on every footer heading, Contact included: they are peers. An h4 on
   Contact left the footer's headings running 4,3,3,3,3,3,3 — a level skipped
   backwards on every page of the site. */
.roam-foot__h {
  font-family: inherit;
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  line-height: 1.4;
  letter-spacing: var(--roam-ls-lg);
  text-transform: uppercase;
  color: var(--roam-dark-text-muted);
  margin: 0 0 var(--roam-sp-12);
  padding: 0;
}
.roam-foot__h--light { color: var(--roam-teal); }

.roam-foot__contact {
  display: flex;
  flex-direction: column;
  gap: 9px;
  font-size: var(--roam-fs-13);
}

/* No top padding on the row stack: each row carries its own bottom margin and
   the block above already ends on 48px. */
.roam-foot__rows { display: flex; flex-direction: column; }
.roam-foot__row { margin-bottom: var(--roam-sp-20); }
/* The teal-to-gold hairline that introduces each row. A gradient, not a border,
   because a border cannot carry two colours. */
.roam-foot__hair {
  height: 1px;
  background: linear-gradient(to right, var(--roam-teal), var(--roam-gold));
  margin-bottom: var(--roam-sp-20);
}
.roam-foot__links {
  display: flex;
  gap: var(--roam-sp-6) var(--roam-sp-28);
  flex-wrap: wrap;
  font-size: var(--roam-fs-12);
}

.roam-foot__copy {
  border-top: 1px solid rgba(250, 246, 241, 0.1);
  padding-top: var(--roam-sp-20);
  text-align: center;
  font-size: var(--roam-fs-12);
  color: var(--roam-dark-text-whisper);
  letter-spacing: var(--roam-ls-xs);
}
/* The build number, muted rather than hidden: findable when a bug report needs
   it, invisible when it does not. */
.roam-foot__build { margin-left: var(--roam-sp-8); opacity: 0.75; }

/* Footer links. One base tone, two modifiers, and a gold hover — which needed
   !important on the old site only because WPRentals restyled every <a> in the
   colophon after us. There is no colophon and no parent theme here. */
.roam-fl {
  text-decoration: none;
  color: rgba(250, 246, 241, 0.8);
  transition: color 0.3s var(--roam-easing);
}
.roam-fl:hover,
.roam-fl:focus-visible { color: var(--roam-gold); }
.roam-fl--contact { color: var(--roam-dark-text-body); }
.roam-fl--label { color: inherit; }
/* The social row — a line of icon links under the blurb. */
.roam-foot__social {
  display: flex;
  gap: var(--roam-sp-12);
  margin-top: var(--roam-sp-16);
}
.roam-fl--social {
  display: inline-flex;
  color: rgba(250, 246, 241, 0.7);
}
.roam-fl--social:hover,
.roam-fl--social:focus-visible { color: var(--roam-gold); }

/* =============================================================================
   PROPERTY DETAIL
   Written for this theme. Nothing here is copied from the old sheet — that one
   carries an #all_wrapper prefix on nearly every property rule, to beat a
   WPRentals parent that is not in this install.
   ========================================================================== */

/* ── Title block ─────────────────────────────────────────────────────────── */
.roam-crumb {
  font-size: var(--roam-fs-13);
  letter-spacing: var(--roam-ls-xs);
  color: var(--roam-text-muted);
  margin-top: var(--roam-sp-32);
}
.roam-crumb a { text-decoration: none; }
.roam-crumb a:hover { text-decoration: underline; }

.roam-prop__title {
  font-size: clamp(30px, 4vw, var(--roam-fs-40));
  margin: var(--roam-sp-12) 0 var(--roam-sp-8);
}
.roam-prop__headline {
  margin: 0 0 var(--roam-sp-8);
  font-size: var(--roam-fs-20);
  color: var(--roam-text-secondary);
}
.roam-prop__facts {
  margin: 0 0 var(--roam-sp-24);
  font-size: var(--roam-fs-15);
  color: var(--roam-text-body);
}

/* ── Gallery ─────────────────────────────────────────────────────────────── */
/* The collage shape is driven by data-count so a property with three photos
   gets a deliberate layout rather than a five-cell grid with two holes in it. */
.roam-gal {
  display: grid;
  gap: var(--roam-sp-8);
  border-radius: var(--roam-r-lg);
  overflow: hidden;
  aspect-ratio: 2 / 1;
}
.roam-gal[data-count='1'] { grid-template-columns: 1fr; }
.roam-gal[data-count='2'] { grid-template-columns: 1fr 1fr; }
.roam-gal[data-count='3'] { grid-template-columns: 2fr 1fr; grid-template-rows: 1fr 1fr; }
.roam-gal[data-count='3'] .roam-gal__cell:first-child { grid-row: span 2; }
.roam-gal[data-count='4'] { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.roam-gal[data-count='5'] { grid-template-columns: repeat(4, 1fr); grid-template-rows: 1fr 1fr; }
.roam-gal[data-count='5'] .roam-gal__cell:first-child { grid-column: span 2; grid-row: span 2; }
/* The five-tile collage is the design's own proportion — a 1280×460 band, a
   half-width hero beside two-by-two quarters — rather than the generic 2:1 the
   other counts use. Wider and shorter than 2:1. */
.roam-gal[data-count='5'] { aspect-ratio: 128 / 46; }

.roam-gal__cell { position: relative; margin: 0; overflow: hidden; background: var(--roam-cream-active); }
/* Overlays the design places ON the photographs — the favourite badge on the
   hero tile, the "view all" button on the last. */
.roam-gal__fav {
  position: absolute;
  top: 14px;
  left: 14px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-8);
  padding: 7px 14px;
  border-radius: var(--roam-r-full);
  background: rgba(250, 246, 241, 0.94);
  /* Forest words between gold laurels — the design's accolade, the same mark
     the cards wear, on a cream chip over the photograph. */
  color: var(--roam-forest);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  box-shadow: 0 2px 10px rgba(27, 43, 39, 0.15);
}
.roam-gal__all {
  position: absolute;
  right: 14px;
  bottom: 14px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-8);
  padding: 9px 16px;
  border: 0;
  border-radius: var(--roam-r-full);
  background: var(--roam-white);
  color: var(--roam-forest);
  font: inherit;
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(27, 43, 39, 0.2);
  transition: background-color 0.15s ease;
}
.roam-gal__all:hover { background: var(--roam-cream); }
.roam-gal__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--roam-photo-filter);
}

.roam-gal__more { margin-top: var(--roam-sp-12); }
.roam-gal__more summary {
  cursor: pointer;
  display: inline-block;
  padding: var(--roam-sp-10) var(--roam-sp-18);
  border: 1px solid var(--roam-forest);
  border-radius: var(--roam-r-sm);
  font-size: var(--roam-fs-14);
}
.roam-gal__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--roam-sp-8);
  margin-top: var(--roam-sp-16);
}
.roam-gal__grid .roam-gal__cell { aspect-ratio: 4 / 3; border-radius: var(--roam-r-sm); }

/* The tappable strip shows a pointer at every width — the whole gallery opens
   the photos modal. */
.roam-gal[data-roam-modal-open] { cursor: pointer; }
@media (max-width: 767px) {
  /* A full-bleed swipeable row rather than a collage: the hero runs edge to edge
     at 3:2 with a peek of the next photo showing there is more to swipe. It
     breaks out of the page gutter to the true viewport width, square-cornered. */
  /* The [data-count] selector matches the count-specific container ratios (e.g.
     the five-tile collage's aspect-ratio:128/46) with equal specificity, so this
     later rule wins and the row is driven by its cells, not a desktop-collage
     shape that would clip a taller cell. */
  .roam-gal,
  .roam-gal[data-count] {
    display: flex;
    /* flex-start, not the default stretch — a stretched cell fills the tallest
       line box and defeats its own 3:2 aspect-ratio. */
    align-items: flex-start;
    gap: var(--roam-sp-6);
    aspect-ratio: auto;
    height: auto;
    width: 100vw;
    margin-inline: calc(50% - 50vw);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    border-radius: 0;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .roam-gal::-webkit-scrollbar { display: none; }
  .roam-gal__cell {
    flex: 0 0 90vw;
    width: 90vw;
    /* An explicit height from the known width, not aspect-ratio alone: a flex
       item holding a height:100% image can ignore aspect-ratio and collapse
       off-shape. 90vw wide at 3:2 is 60vw tall. */
    height: 60vw;
    aspect-ratio: 3 / 2;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    border-radius: 0;
  }
  /* Any collage cell that spanned two rows/columns on desktop is a plain 3:2
     slide here — reset the spans so none renders double-height. */
  .roam-gal[data-count] .roam-gal__cell:first-child {
    grid-column: auto;
    grid-row: auto;
  }
}

/* ── Modals (M1–M3) ───────────────────────────────────────────────────────
   Progressive enhancement of the gallery / amenities / reviews disclosures —
   see assets/js/roam-modal.js. A forest bar over a cream panel, 20px radius and
   a soft shadow on desktop; full-screen with square corners on a phone. */
.roam-modal[hidden] { display: none; }
.roam-modal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--roam-sp-24);
}
.roam-modal__scrim {
  position: absolute;
  inset: 0;
  background: rgba(27, 43, 39, 0.55);
}
.roam-modal__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 720px;
  max-height: 90vh;
  background: var(--roam-cream);
  border-radius: var(--roam-r-xl);
  box-shadow: 0 20px 60px rgba(27, 43, 39, 0.3);
  overflow: hidden;
}
.roam-modal--reviews .roam-modal__panel { max-width: 920px; }
.roam-modal--photos .roam-modal__panel { max-width: 1040px; }
/* A LIGHT HEADER, the way the reference draws every modal: cream bar, dark
   serif title, dark close — with a hairline under it — not the forest bar the
   first pass used. */
.roam-modal__bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-16);
  padding: var(--roam-sp-16) var(--roam-sp-24);
  background: var(--roam-cream);
  color: var(--roam-forest);
  border-bottom: 1px solid var(--roam-border-cream);
}
.roam-modal__title { font-family: var(--roam-font-display); font-size: var(--roam-fs-22); color: var(--roam-forest); }
/* A bare, stylized X — no circle chip, at rest or on hover (client 2026-08-03).
   The 36px box stays the tap target; hover just dims the mark. */
.roam-modal__x {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--roam-forest);
  cursor: pointer;
  transition: opacity 0.2s ease;
}
.roam-modal__x:hover { opacity: 0.6; }
.roam-modal__body {
  padding: var(--roam-sp-24);
  overflow-y: auto;
}
/* Photos stack full-width; reviews are a single-column LIST in the modal (so a
   long review just makes its row taller — no card grid, no "See more"). */
.roam-modal--photos .roam-gal__grid { grid-template-columns: 1fr; gap: var(--roam-sp-12); margin-top: 0; }
.roam-modal--reviews .roam-reviews { display: block; }
body.roam-modal-open { overflow: hidden; }
@media (max-width: 767px) {
  .roam-modal { padding: 0; }
  .roam-modal__panel {
    max-width: none;
    width: 100%;
    height: 100%;
    max-height: none;
    border-radius: 0;
  }
  /* The full-screen modal has no outer gutter to spend, so the inner padding is
     all that stands between the content and the edge — 24px each side took a
     seventh of a 360px screen. Tighten the bar and body so more of the amenity
     list, the gallery and the reviews fit across. */
  .roam-modal__bar { padding: var(--roam-sp-12) var(--roam-sp-16); }
  .roam-modal__body { padding: var(--roam-sp-16); }
}

/* The trigger that replaces a disclosure summary — the same bordered pill the
   inline <summary> buttons already use, so nothing jumps when JS swaps them. */
.roam-modal-trigger {
  cursor: pointer;
  display: inline-block;
  margin-top: var(--roam-sp-12);
  padding: var(--roam-sp-10) var(--roam-sp-18);
  border: 1px solid var(--roam-forest);
  border-radius: var(--roam-r-sm);
  background: transparent;
  font: inherit;
  font-size: var(--roam-fs-14);
  color: var(--roam-text-primary);
}
.roam-modal-trigger:hover,
.roam-modal-trigger:focus-visible { background: var(--roam-cream-active); }

/* ── Share modal ("Share this stay") ─────────────────────────────────────── */
.roam-sharem[hidden] { display: none; }
.roam-sharem {
  position: fixed;
  inset: 0;
  z-index: 2100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--roam-sp-24);
}
.roam-sharem__scrim { position: absolute; inset: 0; background: rgba(27, 43, 39, 0.5); }
.roam-sharem__panel {
  position: relative;
  width: 100%;
  max-width: 760px;
  max-height: 90vh;
  overflow-y: auto;
  background: var(--roam-cream);
  border-radius: var(--roam-r-xl);
  border-top: 3px solid var(--roam-gold);
  box-shadow: 0 20px 60px rgba(27, 43, 39, 0.3);
  padding: var(--roam-sp-32);
}
.roam-sharem__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--roam-sp-16); }
.roam-sharem__title { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: 0; }
.roam-sharem__x {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--roam-forest);
  cursor: pointer;
  transition: background-color 0.2s ease;
}
.roam-sharem__x:hover { background: var(--roam-cream-active); }
.roam-sharem__prop { display: flex; align-items: center; gap: var(--roam-sp-16); margin: var(--roam-sp-20) 0; }
.roam-sharem__thumb {
  flex: 0 0 auto;
  width: 64px;
  height: 64px;
  border-radius: var(--roam-r-md);
  background-size: cover;
  background-position: center;
  filter: var(--roam-photo-filter);
}
.roam-sharem__thumb--empty { background: var(--roam-sand); filter: none; }
.roam-sharem__name { font-weight: var(--roam-fw-semibold); color: var(--roam-text-primary); font-size: var(--roam-fs-16); }
.roam-sharem__facts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--roam-sp-6);
  margin-top: 2px;
  font-size: var(--roam-fs-14);
  color: var(--roam-text-muted);
}
.roam-sharem__star { color: var(--roam-gold); }
.roam-sharem__dot { color: var(--roam-text-faint); }
.roam-sharem__note {
  display: flex;
  align-items: flex-start;
  gap: var(--roam-sp-10);
  padding: var(--roam-sp-14) var(--roam-sp-16);
  margin-bottom: var(--roam-sp-20);
  border: 1px solid rgba(198, 151, 91, 0.35);
  border-radius: var(--roam-r-md);
  background: rgba(198, 151, 91, 0.12);
  color: var(--roam-warning);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-medium);
  line-height: var(--roam-lh-snug);
}
.roam-sharem__shield { flex: 0 0 auto; color: var(--roam-gold); margin-top: 1px; }
.roam-sharem__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--roam-sp-12); }
.roam-sharem__opt {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-12);
  padding: var(--roam-sp-14) var(--roam-sp-16);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-md);
  background: var(--roam-white);
  font: inherit;
  font-size: var(--roam-fs-15);
  font-weight: var(--roam-fw-medium);
  color: var(--roam-text-primary);
  text-decoration: none;
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s ease;
}
.roam-sharem__opt:hover { border-color: var(--roam-forest); }
.roam-sharem__oi {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-sm);
  color: var(--roam-forest);
}
.roam-sharem__opt.is-copied { border-color: var(--roam-success); color: var(--roam-success); }
@media (max-width: 559px) {
  .roam-sharem__grid { grid-template-columns: 1fr; }
  .roam-sharem__panel { padding: var(--roam-sp-24); }
}

/* ── The two-column region ───────────────────────────────────────────────── */
.roam-prop__cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--roam-sp-48);
  margin-top: var(--roam-sp-32);
}
@media (min-width: 768px) {
  /* A fixed 400px booking card, the content column taking the rest — now from
     tablet up (client 2026-08-03), not 900. The gap tightens to 32 at tablet so
     the content column keeps a usable width beside the fixed card, and opens back
     to 48 on the desktop layout. */
  .roam-prop__cols { grid-template-columns: minmax(0, 1fr) 400px; gap: var(--roam-sp-32); }
  /* Sticks BELOW the header and the property sub-nav, not 24px from the viewport
     top where those two bars slid over its head and hid the rate. The offset is
     the same measured header + the sub-nav's own height the sticky bar uses, so
     the card comes to rest just under them with a little air. */
  .roam-prop__rail {
    position: sticky;
    top: calc(var(--roam-hdr-total, var(--roam-header-h)) + var(--roam-sticky-h) + var(--roam-sp-16));
    align-self: start;
  }
}
@media (min-width: 1100px) {
  .roam-prop__cols { gap: var(--roam-sp-48); }
}

/* ── Sections ────────────────────────────────────────────────────────────── */
.roam-sec {
  padding-block: var(--roam-sec-y);
  border-top: 1px solid var(--roam-border-cream);
}
.roam-sec:first-child { border-top: 0; padding-top: 0; }
/* NO DOUBLE PADDING. Now that each block is its own section, any leading or
   trailing margin on the section's own content stacks on top of the section
   padding and reads as too much air. Collapse the first child's top margin and
   the last child's bottom margin so the section padding is the only vertical
   space, whichever block it holds. */
.roam-sec__body > :first-child { margin-top: 0; }
.roam-sec__body > :last-child { margin-bottom: 0; }
/* BODY COPY CAPS AT A READING MEASURE — client instruction 2026-08-02, 720px.
   Long lines of prose across the full content column are hard to read; the cap
   holds the paragraphs to a comfortable measure. It is scoped to prose — the
   bare paragraphs wpautop emits into a section body, and the About flyout's
   .roam-prose — so the grids and cards (sleep, amenities, neighbourhood), which
   are <div> children rather than <p>, keep the column's full width. */
.roam-sec__body > p,
.roam-lede,
.roam-prose { max-width: 720px; }
/* The title sits flush at the top of its section — no top padding — its own top
   margin dropped so the name aligns with the top of the booking card across the
   gutter. */
.roam-prop__head .roam-prop__title { margin-top: 0; }
/* .roam-sec__h is the shared .roam-subhead rule now — a heading INSIDE a
   section, which is a different job from naming one. It kept its own font-size,
   margin and tracking here and they happened to agree with the shared rule to
   within a hair; leaving them would have meant two places to change one thing
   the first time they stopped agreeing. */
.roam-sec__h3 { font-size: var(--roam-fs-18); margin: 0 0 var(--roam-sp-8); }

.roam-lede { font-size: var(--roam-fs-18); line-height: var(--roam-lh-snug); }
.roam-lede p:first-child { margin-top: 0; }
/* The full-description flyout gathers several prose blocks, each under its own
   heading — the space, the location, activities, the story. */
.roam-about__block + .roam-about__block { margin-top: var(--roam-sp-32); }
.roam-about__h { font-family: var(--roam-font-display); font-size: var(--roam-fs-20); margin: 0 0 var(--roam-sp-8); }
/* A subline under a section heading, above its card grid ("Same region, same
   standard."). */
.roam-cards__sub {
  margin: 0 0 var(--roam-sp-20);
  color: var(--roam-text-secondary);
  font-size: var(--roam-fs-15);
}
.roam-prose { line-height: var(--roam-lh-body); color: var(--roam-text-body); }
.roam-prose--notes { margin-top: var(--roam-sp-24); }

/* A statement about what the site does or does not know. Quieter than body
   copy, never smaller than 13px — this is where the honest caveats go, and a
   caveat nobody can read is decoration. */
.roam-note {
  margin: var(--roam-sp-10) 0 0;
  font-size: var(--roam-fs-13);
  line-height: var(--roam-lh-snug);
  color: var(--roam-text-muted);
}

/* ── Icon facts (type · occupancy · place) ───────────────────────────────── */
.roam-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--roam-sp-24) var(--roam-sp-40);
  /* No own margin — the facts sit in their own section now, so the section's
     padding is the only vertical space. The 20/24 here stacked on top of it and
     read as too much white space. */
  margin: 0;
}
.roam-fact { display: flex; align-items: flex-start; gap: var(--roam-sp-12); }
.roam-fact__ico { flex: 0 0 auto; color: var(--roam-forest); margin-top: 1px; }
.roam-fact__tx { display: flex; flex-direction: column; }
.roam-fact__k { font-weight: var(--roam-fw-semibold); color: var(--roam-text-primary); font-size: var(--roam-fs-15); }
.roam-fact__v { font-size: var(--roam-fs-13); color: var(--roam-text-muted); }

/* ── Guest-favourite band ────────────────────────────────────────────────── */
/* THE GUEST-FAVOURITE CARD IS GOLD AND ELEVATED — the accolade earns the one
   gold, lifted card in the intro cluster, against the white feature cards. */
.roam-gfav {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-24);
  padding: var(--roam-sp-20) var(--roam-sp-24);
  border: 1px solid rgba(198, 151, 91, 0.40);
  border-radius: var(--roam-r-lg);
  background: rgba(198, 151, 91, 0.16);
  box-shadow: var(--roam-shadow-rest);
}
.roam-gfav__score { display: flex; align-items: center; gap: var(--roam-sp-8); }
/* Bigger laurels than the chip's — this is the accolade at full size. */
.roam-gfav__score .roam-laurel { width: 7.7px; height: 44px; }
.roam-gfav__num { display: flex; flex-direction: column; align-items: center; }
.roam-gfav__avg { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); font-weight: 700; line-height: 1; }
.roam-gfav__rev {
  margin-top: 2px;
  font-size: var(--roam-fs-11);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
  color: var(--roam-text-muted);
}
.roam-gfav__h { font-family: var(--roam-font-display); font-size: var(--roam-fs-20); }
.roam-gfav__sub { margin: var(--roam-sp-4) 0 0; color: var(--roam-text-body); font-size: var(--roam-fs-14); }
/* TABLET + PHONE — the constrained card stacks: the laurels flank the score on
   one centred top row (the score and review count still stacked, as on desktop),
   then the title and body sit in rows below, left-justified (client 2026-08-03). */
@media (max-width: 1024px) {
  /* Stacked, centred and TIGHT (client 2026-08-03): the title and body both
     centre, and the 12px column gap drops to 4 — the roomy laurel row plus a
     12px gap read far too airy on a phone or tablet. */
  .roam-gfav { flex-direction: column; align-items: stretch; gap: var(--roam-sp-4); }
  .roam-gfav__score { justify-content: center; }
  .roam-gfav__txt { text-align: center; }
  .roam-gfav__sub { margin-top: var(--roam-sp-2); }
}

/* ── Featured amenities — icon cards ─────────────────────────────────────── */
/* The merged intro cluster — facts, the guest-favourite band and the featured
   call-outs in ONE section. A tight internal gap replaces the three section
   seams they used to sit between; each block's own block-margins are dropped so
   the gap is the only spacing. */
.roam-prop__intro { display: flex; flex-direction: column; gap: var(--roam-sp-16); }
.roam-prop__intro > * { margin-block: 0; }
.roam-feat {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--roam-sp-16);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* WHITE, ELEVATED, TEAL ICONS — the feature call-outs are lifted white cards
   with teal glyphs, against the gold guest-favourite card beside them. */
.roam-feat__item {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-12);
  padding: var(--roam-sp-12) var(--roam-sp-16);
  border: 0;
  border-radius: var(--roam-r-md);
  background: var(--roam-white);
  box-shadow: var(--roam-shadow-rest);
  font-size: var(--roam-fs-15);
}
.roam-feat__ico {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--roam-r-sm);
  background: rgba(91, 143, 168, 0.12);
  color: var(--roam-teal);
}
/* Normalise the amenity mark to one size regardless of which set it came from. */
.roam-feat__ico svg { width: 20px; height: 20px; }
.roam-feat__nm { font-weight: var(--roam-fw-semibold); color: var(--roam-text-primary); }
/* The name over its optional per-property context subheader ("Lakefront" /
   "Round Lake"), stacked beside the icon. */
.roam-feat__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.roam-feat__sub { font-size: var(--roam-fs-13); font-weight: var(--roam-fw-normal); color: var(--roam-text-secondary); }

/* ── Rooms (Where you'll sleep — bedroom cards) ──────────────────────────── */
/* The design's summary line under the heading. */
.roam-rooms__meta {
  margin: 0 0 var(--roam-sp-20);
  color: var(--roam-text-secondary);
  font-size: var(--roam-fs-15);
}
/* A SIDE-SCROLLING RAIL. Two cards plus a peek on a phone, three plus a peek
   from tablet up, where the arrow buttons show. The rail swipes at every width;
   the peek is what says there is more. */
.roam-rooms-wrap { position: relative; }
.roam-rooms {
  display: flex;
  gap: var(--roam-sp-16);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: 0;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.roam-rooms::-webkit-scrollbar { display: none; }
.roam-room {
  /* Two cards + a peek: each takes a bit under half, so the third's edge shows. */
  flex: 0 0 42%;
  scroll-snap-align: start;
  overflow: hidden;
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
  background: var(--roam-white);
}
/* Three cards + a peek from tablet up. */
@media (min-width: 768px) {
  .roam-room { flex-basis: 30%; }
}
/* The side-scroll arrows — pointer convenience, tablet up. Circular chips over
   the media band, tucked just inside each edge; roam-property.js hides the one
   at the end you have reached. */
.roam-rooms__arrow { display: none; }
@media (min-width: 768px) {
  .roam-rooms__arrow {
    position: absolute;
    top: calc((100% - 3rem) * 0.32);
    transform: translateY(-50%);
    z-index: 2;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--roam-border-tag);
    border-radius: 50%;
    background: var(--roam-white);
    color: var(--roam-forest);
    box-shadow: 0 2px 10px rgba(27, 43, 39, 0.18);
    cursor: pointer;
    transition: background-color 0.15s ease, opacity 0.15s ease;
  }
  .roam-rooms__arrow:hover { background: var(--roam-cream); }
  .roam-rooms__arrow[hidden] { display: none; }
  .roam-rooms__arrow--prev { left: -8px; }
  .roam-rooms__arrow--next { right: -8px; }
}
/* A 3/2 window, the same ratio as the listing cards. A room photo fills it; a
   bedroom without one shows the bed mark on a soft ground. */
.roam-room__media {
  aspect-ratio: 3 / 2;
  background-image: var(--roam-room-img);
  background-size: cover;
  background-position: center;
}
.roam-room__media--empty {
  display: grid;
  place-items: center;
  /* 15% teal wash behind the bed mark on a photo-less room card. */
  background: color-mix(in srgb, var(--roam-teal) 15%, transparent);
}
.roam-room__mark { color: var(--roam-teal); }
.roam-room__body { padding: var(--roam-sp-12) var(--roam-sp-16); }
.roam-room__h { font-size: var(--roam-fs-16); margin: 0; }
.roam-room__beds { margin: var(--roam-sp-4) 0 0; font-size: var(--roam-fs-14); color: var(--roam-text-secondary); }

/* ── Amenities ───────────────────────────────────────────────────────────── */
/* The inline sample: a flat two-column list, no category headings — the
   design's "what this place offers" preview. The full grouped set lives in the
   modal behind the pill. */
.roam-amen__flat {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--roam-sp-4) var(--roam-sp-40);
  font-size: var(--roam-fs-15);
}
.roam-amen__flat li {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-12);
  padding: var(--roam-sp-10) 0;
}
@media (max-width: 640px) {
  .roam-amen__flat { grid-template-columns: 1fr; }
}
.roam-amen__ico { flex: 0 0 auto; color: var(--roam-forest); }

/* The full list inside the modal: category groups, each a display heading, and
   every amenity a row split by a hairline — the reference's grouped table. */
.roam-amen__group + .roam-amen__group { margin-top: var(--roam-sp-24); }
.roam-amen__cat {
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-18);
  margin: 0 0 var(--roam-sp-4);
}
.roam-amen__list { margin: 0; padding: 0; list-style: none; font-size: var(--roam-fs-15); }
.roam-amen__list li {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-12);
  padding: var(--roam-sp-14) 0;
  border-bottom: 1px solid var(--roam-border-cream);
}
.roam-amen__list li:last-child { border-bottom: 0; }
/* TWO COLUMNS in the modal — the design lays the full grouped list two-up on a
   wide screen (one column on a phone), groups kept whole across the split. */
.roam-modal--amenities .roam-modal__panel { max-width: 880px; }
.roam-amen__full { columns: 2; column-gap: var(--roam-sp-40); }
.roam-amen__group { break-inside: avoid; }
.roam-amen__group:first-child { margin-top: 0; }
@media (max-width: 640px) {
  .roam-amen__full { columns: 1; }
}
/* THE SHARED "SHOW ALL N" PILL. The design's disclosure control: a white
   rounded-full button with a hairline tan edge, forest label. One rule so the
   amenities "show all", the description flyout and any other reveal read as the
   same affordance, matching the reference's pill exactly. */
.roam-morebtn {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--roam-sp-8);
  /* A proper 48px button, matching the site's button system (the booking CTA and
     the search Search button), not the ~34px squished pill it was. */
  min-height: 48px;
  padding: 0 var(--roam-sp-24);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  background: var(--roam-white);
  color: var(--roam-forest);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  line-height: 1;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.roam-morebtn:hover { background: var(--roam-cream); border-color: var(--roam-forest-soft); }
.roam-morebtn::-webkit-details-marker { display: none; }
.roam-morebtn::marker { content: ''; }

/* THE FULL-DESCRIPTION FLYOUT — the dev site's full-width warm-tan bar, not the
   see-all pill. The lead in forest, the headings it reveals listed after it in
   muted, and a chevron on the right that flips when the accordion opens. */
/* THE DESCRIPTION SECTION — matched to the dev site (theme/wprentals-child
   .roam-desc) rule for rule: a lead, a clipped-and-faded preview of the rest,
   and a centred tan pill that expands it. Colours, opacities, radii and the
   112px/88px clip are the reference's own values. */
.roam-about { position: relative; margin-top: var(--roam-sp-16); }
.roam-about__rule {
  display: block; width: 38px; height: 2px; margin: 0 0 16px;
  background: linear-gradient(90deg, #5B8FA8, #A89E7B, #C6975B);
}
.roam-about__blurb,
.roam-about__body { font-size: var(--roam-fs-16); line-height: 1.7; color: var(--roam-text-body); }
.roam-about__blurb p:last-child { margin-bottom: 0; }
.roam-about__body { position: relative; margin-top: 14px; }
/* The clip and the fade only apply once JS can expand the body again — without
   it, the whole description shows and the button below is hidden. */
.roam-about--js:not(.is-open) .roam-about__body { max-height: 112px; overflow: hidden; }
.roam-about--js:not(.is-open) .roam-about__body::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 88px;
  background: linear-gradient(to top, var(--roam-cream) 28%, transparent);
  pointer-events: none;
}
.roam-about__toggle {
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: center; gap: var(--roam-sp-8);
  width: 100%; margin-top: -8px;             /* overlaps the fade, per the design */
  padding: 13px 26px;
  background: rgba(198, 151, 91, 0.1);
  border: 1px solid rgba(198, 151, 91, 0.55);
  border-radius: var(--roam-r-full);
  font-family: var(--roam-font-body); font-size: var(--roam-fs-14); font-weight: var(--roam-fw-semibold);
  color: var(--roam-forest); cursor: pointer;
  transition: background-color 200ms ease, border-color 200ms ease;
}
.roam-about__toggle:hover { background: var(--roam-gold); border-color: var(--roam-gold); }
.roam-about__toggle-lines {
  display: flex; flex-direction: row; align-items: baseline; justify-content: center;
  gap: var(--roam-sp-8); min-width: 0; flex-wrap: wrap;
}
.roam-about__toggle-sub { font-size: 11.5px; font-weight: var(--roam-fw-medium); color: #7A5A24; }
.roam-about.is-open .roam-about__toggle-sub { display: none; }
.roam-about__chev { flex: none; transition: transform 200ms ease; }
.roam-about.is-open .roam-about__chev { transform: rotate(180deg); }
/* Open: the pill drops its gold fill for a plain outlined "Show less". */
.roam-about.is-open .roam-about__toggle {
  margin-top: 16px; background: transparent; border-color: var(--roam-border-cream);
}
.roam-about.is-open .roam-about__toggle:hover { border-color: var(--roam-gold); background: transparent; }
/* No JS: the toggle is inert, so hide it — the copy above is already all there. */
.roam-about:not(.roam-about--js) .roam-about__toggle { display: none; }

/* The category disclosure. A button rather than a whisper: it hides most of the
   table, so it has to look like the way to the rest of it. Wears the shared
   outline pill — the design's "Show all N" control. */
.roam-amen__more { margin-top: var(--roam-sp-20); }
.roam-amen__more[open] > summary { margin-bottom: var(--roam-sp-20); }

.roam-amen__not { margin-top: var(--roam-sp-24); }
.roam-amen__not summary { cursor: pointer; font-size: var(--roam-fs-14); color: var(--roam-text-secondary); }
.roam-amen__list--not {
  columns: 2;
  margin-top: var(--roam-sp-12);
  color: var(--roam-text-light);
  /* Struck through rather than prefixed with a cross: the strike survives
     being read aloud as ordinary text, and the list already sits under a
     heading that says what it is. */
  text-decoration: line-through;
}

/* ── House rules ─────────────────────────────────────────────────────────── */
/* The permit number. Small print, and deliberately so — it is a compliance
   line, not a selling point — but full-strength enough to be legible, because
   the ordinances that require it require it to be readable. */
.roam-rules__reg {
  margin: var(--roam-sp-20) 0 0;
  font-size: var(--roam-fs-13);
  color: var(--roam-text-muted);
  letter-spacing: var(--roam-ls-xs);
}

.roam-rules {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 0 var(--roam-sp-40);
  margin: 0;
}
.roam-rule {
  display: flex;
  justify-content: space-between;
  gap: var(--roam-sp-16);
  padding: var(--roam-sp-12) 0;
  border-bottom: 1px solid var(--roam-border-cream);
}
.roam-rule__k { font-size: var(--roam-fs-15); color: var(--roam-text-secondary); }
.roam-rule__v { margin: 0; font-size: var(--roam-fs-15); text-align: right; }

.roam-policy { margin-top: var(--roam-sp-24); }
.roam-policy p { color: var(--roam-text-body); line-height: var(--roam-lh-body); margin: 0 0 var(--roam-sp-12); }

/* ── Where you'll be ─────────────────────────────────────────────────────── */
.roam-where__place {
  margin: 0 0 var(--roam-sp-12);
  font-size: var(--roam-fs-18);
}

/* ── Setting & View ──────────────────────────────────────────────────────── */
/* OwnerRez's own Location > Setting & View ticks — lake, river, forest, rural.
   Under "Where you'll be" because that is the section they live in there. */
.roam-setting {
  display: flex;
  flex-wrap: wrap;
  gap: var(--roam-sp-8);
  margin: 0 0 var(--roam-sp-16);
  padding: 0;
  list-style: none;
}
.roam-setting__item {
  padding: var(--roam-sp-4) var(--roam-sp-12);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  font-size: var(--roam-fs-13);
}

/* ── The ROAM Standard — a contained dark card ───────────────────────────────
   The design of record contains it in a rounded forest card in the measure, not
   a full-bleed band — the same move the homepage's why-direct card makes. The
   card is .roam-std__inner.

   NO OWN MEASURE OR GUTTERS. It is printed inside .roam-wrap, which already
   caps the width and adds the --roam-page-x gutters; a second max-width and
   padding-inline here inset the card 25px past every other section on each
   side. Only the vertical rhythm is its own. */
.roam-std {
  /* The band joins the section rhythm rather than carrying its own 40px margin —
     so the seam where the two-column region meets this full-width band is the
     same 2×--roam-sec-y as any other section boundary, not a doubled gap. Tied
     to the token, so it tightens with everything else. */
  margin-block: var(--roam-sec-y);
}
.roam-std__inner {
  background: var(--roam-surface-forest-down);
  color: var(--roam-cream);
  border-radius: var(--roam-r-xl);
  padding: var(--roam-sp-56) var(--roam-sp-48);
}
/* The eyebrow inherits .roam-eyebrow's type and rule; only the ground changes,
   so it takes the lifted gold the hero uses rather than the brand value. */
.roam-std__eyebrow { color: var(--roam-gold-lifted); }
.roam-std__eyebrow::before { background: var(--roam-gold-lifted); }
/* Size and measure are this band's own — it is narrower than a section heading
   and sits on a photograph. THE MARGINS ARE NOT: 12 above and 40 below were a
   fourth and fifth number for eyebrow-to-title and prose-to-grid, invented
   beside the rule that wanted them while the tokens for both already existed. */
.roam-std__h {
  font-size: clamp(26px, 3.2vw, var(--roam-fs-32));
  margin: var(--roam-flow-coupled) 0 var(--roam-flow-block);
  max-width: 20ch;
}
.roam-std__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--roam-sp-16);
}
/* EACH ITEM IS A CARD, structurally the best-price-guarantee card — padding, a
   hairline and a soft radius — but tuned for the forest ground: a faint cream
   lift and a translucent cream border rather than white on tan (client
   2026-08-03). */
.roam-std__card {
  padding: var(--roam-sp-20);
  border: 1px solid var(--roam-dark-border-visible);
  border-radius: var(--roam-r-md);
  background: rgba(250, 246, 241, 0.05);
}
.roam-std__ico { display: block; color: var(--roam-gold); margin-bottom: var(--roam-sp-12); }
.roam-std__name {
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-20);
  margin-bottom: var(--roam-sp-8);
}
.roam-std__note { color: rgba(250, 246, 241, 0.7); line-height: var(--roam-lh-snug); }

/* ── Best-price guarantee ────────────────────────────────────────────────── */
/* One warm parchment panel — a promise, not a fact about the house — set apart
   from the plain sections around it. Its tint and edge are mixed from the gold
   and cream tokens rather than a new literal, so it stays in the palette. */
.roam-guar {
  padding: var(--roam-sp-32);
  border: 1px solid color-mix(in srgb, var(--roam-gold) 30%, var(--roam-cream));
  border-radius: var(--roam-r-xl);
  background: color-mix(in srgb, var(--roam-gold) 11%, var(--roam-cream));
}
.roam-guar__head {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-12);
  margin-bottom: var(--roam-sp-16);
}
/* The shield sits in a soft rounded badge, the way the design fronts the box. */
.roam-guar__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--roam-r-md);
  background: color-mix(in srgb, var(--roam-gold) 18%, var(--roam-white));
  color: var(--roam-gold);
  flex: 0 0 auto;
}
.roam-guar__h { margin: 0; font-size: var(--roam-fs-24); }
.roam-guar__lede {
  margin: 0 0 var(--roam-sp-24);
  max-width: 62ch;
  color: var(--roam-text-body);
  line-height: var(--roam-lh-body);
}
.roam-guar__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--roam-sp-16);
}
/* White cards against the parchment, the reverse of the plain-section cards. */
.roam-guar__card {
  padding: var(--roam-sp-20);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-md);
  background: var(--roam-white);
}
.roam-guar__ico { display: block; color: var(--roam-gold); margin-bottom: var(--roam-sp-10); }
.roam-guar__name { font-size: var(--roam-fs-16); margin: 0 0 var(--roam-sp-6); }
.roam-guar__note { margin: 0; color: var(--roam-text-body); line-height: var(--roam-lh-snug); }
/* A hairline, then the match-it prompt — the box's closing line. */
.roam-guar__foot {
  margin-top: var(--roam-sp-24);
  padding-top: var(--roam-sp-20);
  border-top: 1px solid color-mix(in srgb, var(--roam-gold) 24%, var(--roam-cream));
  font-size: var(--roam-fs-14);
}
.roam-guar__q { color: var(--roam-text-muted); }
.roam-guar__link { color: var(--roam-forest); font-weight: var(--roam-fw-semibold); text-decoration: underline; text-underline-offset: 2px; }
.roam-guar__link:hover { color: var(--roam-gold-active); }

/* THE PROPERTY CARDS TIGHTEN THEIR INTERNAL PADDING ON TABLET AND PHONE (client
   2026-08-03), the same move as the gutters — the desktop insets (Standards 56,
   guarantee 32) eat a phone's width. One step down at ≤1024, a second on the
   biggest panels at ≤767. */
@media (max-width: 1024px) {
  .roam-std__inner { padding: var(--roam-sp-32) var(--roam-sp-24); }
  .roam-std__card { padding: var(--roam-sp-16); }
  .roam-guar { padding: var(--roam-sp-24); }
  .roam-guar__card { padding: var(--roam-sp-16); }
  .roam-gfav { padding: var(--roam-sp-16); }
  .roam-room__body { padding: var(--roam-sp-10) var(--roam-sp-12); }
}
@media (max-width: 767px) {
  .roam-std__inner { padding: var(--roam-sp-24) var(--roam-sp-20); }
  .roam-guar { padding: var(--roam-sp-20); }
}

/* ── Managed by ROAM ─────────────────────────────────────────────────────── */
/* One white card: the brand-mark avatar with the name and blurb on the left,
   the counted stats set off by a rule on the right. Stacks on a phone. */
.roam-host {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--roam-sp-40);
  align-items: start;
  padding: var(--roam-sp-32);
  border: 1px solid var(--roam-border-cream);
  border-radius: var(--roam-r-xl);
  background: var(--roam-white);
  box-shadow: 0 1px 3px rgba(27, 43, 39, 0.06);
}
.roam-host__intro {
  display: flex;
  align-items: flex-start;
  gap: var(--roam-sp-16);
}
.roam-host__avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--roam-forest);
}
.roam-host__name { margin: 0 0 var(--roam-sp-8); font-size: var(--roam-fs-20); }
.roam-host__blurb {
  margin: 0;
  max-width: 52ch;
  color: var(--roam-text-body);
  line-height: var(--roam-lh-body);
}
.roam-host__stats {
  display: flex;
  flex-direction: column;
  gap: var(--roam-sp-20);
  padding-left: var(--roam-sp-40);
  border-left: 1px solid var(--roam-border-cream);
}
.roam-host__n {
  display: block;
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-24);
  line-height: 1;
}
.roam-host__k {
  display: block;
  margin-top: var(--roam-sp-4);
  font-size: var(--roam-fs-13);
  color: var(--roam-text-muted);
}
@media (max-width: 700px) {
  .roam-host { grid-template-columns: 1fr; gap: var(--roam-sp-24); }
  .roam-host__stats {
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--roam-sp-24);
    padding-left: 0;
    padding-top: var(--roam-sp-24);
    border-left: 0;
    border-top: 1px solid var(--roam-border-cream);
  }
}

/* ── Cards ───────────────────────────────────────────────────────────────── */
/* ── The card grid, as a subgrid ──────────────────────────────────────────
   THE CARDS SHARE THE PARENT'S ROWS RATHER THAN EACH SIZING ITSELF. Without
   this every card is its own little stack: one property's title wraps to two
   lines and its meta, chips and price all sit a line lower than the card
   beside it, so a row of three reads as three separate objects that happen to
   be adjacent. With it, every title baseline, every meta line and every price
   lines up across the row, and a card with a short name simply has more air
   under it.

   SIX TRACKS, AND EVERY CARD EMITS ALL SIX — see inc/card.php. One is the
   photograph; the other five are the title row, the meta line, the blurb, the
   chips and the stay total. A card that skipped its blurb would span one fewer
   and knock every card after it in the row out of step. An empty one collapses
   to zero for the whole row, so a grid where nothing has chips has no chip row.

   The count has to match the markup exactly. Span too few and the last child
   is auto-placed into an IMPLICIT row, which is not part of the subgrid and so
   is not aligned with anything — the one failure mode here that looks like it
   is working until a card in the row is taller than its neighbours.

   row-gap: 0 on the card, and it is not cosmetic. A subgrid inherits the
   parent's gutters for the tracks it spans, so the parent's 28px would land
   between the photo and the title and between every line of the body. The
   parent's own 28 still applies BETWEEN cards, because that gap is between
   tracks no single card spans.

   The chain is three deep — grid, link, body — because subgrid only inherits
   from its immediate parent, and the rows that need aligning are inside the
   anchor inside the card. */
.roam-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: var(--roam-sp-28);
}

/* SIMILAR STAYS — A HORIZONTAL RAIL OF ITS OWN CARDS.
   A column-flow grid: each .roam-sim fills one column and the row stretches
   every card to a common height. The column width per breakpoint sets how many
   show, and every step leaves a peek of the next so the rail reads as
   scrollable — one on a phone, two then three on a tablet, four on desktop —
   and it scrolls inside its own box, never the page. */
.roam-simrail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 82%;
  gap: var(--roam-sp-20);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--roam-sp-12);
  scrollbar-width: thin;
  scrollbar-color: var(--roam-border-tag) transparent;
}
.roam-simrail::-webkit-scrollbar { height: 8px; }
.roam-simrail::-webkit-scrollbar-thumb { background: var(--roam-border-tag); border-radius: var(--roam-r-full); }
@media (min-width: 600px)  { .roam-simrail { grid-auto-columns: 46%; } }
@media (min-width: 900px)  { .roam-simrail { grid-auto-columns: 30%; } }
@media (min-width: 1200px) { .roam-simrail { grid-auto-columns: 22%; } }

/* ── The similar-stays card ──────────────────────────────────────────────── */
.roam-sim { position: relative; scroll-snap-align: start; display: flex; }
.roam-sim__link {
  display: flex;
  flex-direction: column;
  width: 100%;
  background: var(--roam-white);
  border-radius: var(--roam-r-xl);
  overflow: hidden;
  box-shadow: var(--roam-shadow-rest);
  color: inherit;
  text-decoration: none;
  transition: transform 0.25s var(--roam-easing), box-shadow 0.25s var(--roam-easing);
}
/* Same lift, shadow, image-zoom and title-colour as the main property card. */
.roam-sim__link:hover { transform: var(--roam-lift-hover); box-shadow: var(--roam-shadow-hover); }
.roam-sim__link:hover .roam-sim__img { transform: scale(1.04); }
.roam-sim__link:hover .roam-sim__title { color: var(--roam-forest-soft); }
.roam-sim__media {
  position: relative;
  aspect-ratio: 3 / 2;
  background: var(--roam-sand);
  overflow: hidden;
}
.roam-sim__img {
  position: absolute;
  inset: 0;
  background-image: var(--roam-sim-img);
  background-size: cover;
  background-position: center;
  filter: var(--roam-photo-filter);
  transition: transform 0.4s var(--roam-easing);
}
/* The rate pill on the photo, bottom-right. */
.roam-sim__rate {
  position: absolute;
  right: 12px;
  bottom: 12px;
  padding: var(--roam-sp-6) var(--roam-sp-12);
  border-radius: var(--roam-r-full);
  background: var(--roam-white);
  color: var(--roam-forest);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  box-shadow: 0 2px 8px rgba(27, 43, 39, 0.18);
}
.roam-sim__per { font-weight: var(--roam-fw-normal); color: var(--roam-text-muted); }
/* The earned Guest-favourite chip, top-left, with its laurels. */
.roam-sim__badge {
  position: absolute;
  left: 12px;
  top: 12px;
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-4);
  padding: var(--roam-sp-4) var(--roam-sp-10);
  border-radius: var(--roam-r-full);
  background: var(--roam-cream);
  color: var(--roam-gold);
  font-size: var(--roam-fs-12);
  font-weight: var(--roam-fw-semibold);
}
.roam-sim__badge > span { color: var(--roam-forest); }
/* The body hierarchy: name, headline, rating + location, then the spec line. */
.roam-sim__body {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--roam-sp-4);
  padding: var(--roam-sp-16);
}
.roam-sim__title { margin: 0; font-family: var(--roam-font-display); font-size: var(--roam-fs-18); line-height: var(--roam-lh-tight); transition: color 0.25s var(--roam-easing); }
.roam-sim__headline { font-size: var(--roam-fs-14); color: var(--roam-text-secondary); line-height: var(--roam-lh-snug); }
.roam-sim__ratingloc {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-8);
  margin-top: var(--roam-sp-4);
  font-size: var(--roam-fs-14);
  color: var(--roam-text-secondary);
}
.roam-sim__rating { display: inline-flex; align-items: center; gap: var(--roam-sp-4); color: var(--roam-forest); }
.roam-sim__rating .roam-star { color: var(--roam-gold); }
.roam-sim__dot { color: var(--roam-text-muted); }
/* The spec line sits at the foot, so it lines up across cards of unequal name length. */
.roam-sim__specs {
  margin-top: auto;
  padding-top: var(--roam-sp-8);
  font-size: var(--roam-fs-13);
  color: var(--roam-text-secondary);
}
/* THE CARDS SHARE THE PARENT'S ROWS RATHER THAN EACH SIZING ITSELF.
   grid → card → link → body, a three-level subgrid so a row of cards aligns
   line for line: every title baseline, every meta line, every price on the same
   row across the whole row of cards.

   WHY OURS AND NOT THE DEV SITE'S PLAIN CARD. The dev card is static seed data —
   whoever built it chose exactly the pills that made each card the same height,
   so no alignment mechanism was needed. Ours is dynamic: the meta, the blurb and
   the amenity chips come from whatever OwnerRez holds, and left alone they make
   every card a different height. So the control moves from the DATA to the CARD.
   Subgrid is that control — it aligns the rows — and the card also caps what the
   variable rows may show (see the chip limit in inc/card.php) so the alignment
   has little slack to distribute.

   row-gap: 0 on each level, because a subgrid inherits the parent's gutters for
   the tracks it spans — the parent's 28px would otherwise land between the photo
   and the title and between every body line. The 28 still applies BETWEEN cards,
   which is a gap no single card spans. */
.roam-card,
.roam-card__link,
.roam-card__body {
  display: grid;
  grid-template-rows: subgrid;
  row-gap: 0;
}
/* Eight tracks: the photo, then the body's seven — title, headline, place,
   specs, blurb, chips, total. The place line is its own track so it aligns
   across the row like every other. */
.roam-card { grid-row: span 8; }
.roam-card__link { grid-row: 1 / -1; }
.roam-card__body { grid-row: 2 / -1; }

/* NO SUBGRID, NO HARM. Where the browser lacks it the declarations drop,
   `display: grid` stands, and the cards fall back to sizing themselves — the old
   behaviour, not a broken one, so no @supports guard is needed. */

/* ── The property card ────────────────────────────────────────────────────
   THE DEV SITE'S CARD, value for value. Ours was a frame with a photo, a place
   line, a title, a meta line and a rate under it — a list item with a picture.
   The design's card is a photograph with things ON it, and a body beneath: a
   badge top-left, the nightly rate bottom-right, then a title row with the
   rating opposite the name, the meta line, a blurb and amenity chips.

   Its own names carried a `pcard` prefix so it could sit beside a WPRentals
   card on the same page. There is no second card here, so the prefix does not
   come with it — see inc/card.php. */
.roam-card { position: relative; }
.roam-card__link {
  background: var(--roam-white);
  border-radius: var(--roam-r-xl);
  overflow: hidden;
  box-shadow: var(--roam-shadow-rest);
  color: inherit;
  text-decoration: none;
  transition: transform 0.25s var(--roam-easing), box-shadow 0.25s var(--roam-easing);
}
.roam-card:hover .roam-card__link { transform: var(--roam-lift-hover); box-shadow: var(--roam-shadow-hover); }

/* THE HEART SITS OVER THE PHOTOGRAPH AND OUTSIDE THE LINK — see inc/card.php
   for why the markup is a sibling rather than a child. Positioned against
   .roam-card, which is the container, so it does not ride the card's hover
   lift: a control that moves away as you reach for it is a control you miss.

   44 square, on a 19px mark. The visible heart is small by design and a tap
   target is not, and this one sits in the corner of a photograph where a thumb
   arrives at an angle. */
/* A 36px DISC, which is the dev site's and is what a drop-shadow was standing
   in for here. The photograph underneath is any colour at all — a bare mark
   with a shadow survives that, but it does not read as a control. A tinted
   circle does both: it separates the heart from the picture and it says the
   thing is pressable.

   Saved flips the disc to gold and the mark to forest, so the state is carried
   by the whole control rather than by the colour of a 19px outline. */
.roam-fav {
  position: absolute;
  top: 13px;
  right: 13px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(27, 43, 39, 0.35);
  color: rgba(250, 246, 241, 0.9);
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease, transform 0.15s ease;
}
/* The 36px disc is under the 44 a thumb wants, and growing it would put a
   grey circle over a quarter of the photograph. The ::after adds the reach
   without adding any ink — the same construction as the header's account
   glyph. */
.roam-fav::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
}
.roam-fav:hover { background: rgba(27, 43, 39, 0.55); transform: scale(1.08); }
.roam-fav:active { transform: scale(0.94); }
.roam-fav.is-on { background: rgba(198, 151, 91, 0.95); color: var(--roam-forest); }
.roam-fav svg { display: block; pointer-events: none; }
.roam-fav__on { display: none; }
.roam-fav.is-on .roam-fav__off { display: none; }
.roam-fav.is-on .roam-fav__on { display: block; }
.roam-fav__off,
.roam-fav__on { grid-area: 1 / 1; }

.roam-card__media {
  display: block;
  position: relative;
  /* 3/2 — client instruction 2026-08-01. A wider, shorter window than 4/3, so
     the photo shows more width and less height. Consistent across every card. */
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--roam-sand);
}
/* NO --roam-card-img MEANS NO PHOTOGRAPH, and nothing else has to know. The
   declaration is invalid at computed-value time, the media's own fill shows
   through, and the placeholder falls out of the cascade instead of needing a
   second class that somebody has to remember to add. */
.roam-card__img {
  position: absolute;
  inset: 0;
  background-image: var(--roam-card-img);
  background-size: cover;
  background-position: center;
  transition: transform 0.5s var(--roam-easing);
}
.roam-card:hover .roam-card__img { transform: scale(1.04); }

/* THE LAUREL IS THE TALLEST THING IN THE PILL, so its height IS the pill's
   height and the vertical padding has to pay for it — 3px, not the 7 a text-only
   chip would take. Raising the mark without that trade just inflates the pill,
   which on the dev site read as no change at all. */
.roam-card__badge {
  position: absolute;
  top: 14px;
  left: 14px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: var(--roam-r-full);
  background: rgba(27, 43, 39, 0.88);
  /* THE WORDS ARE WHITE AND ONLY THE LAURELS ARE GOLD — client instruction
     2026-08-01. It was gold throughout, which made the label compete with the
     mark either side of it; white text lets the branches be the thing that
     catches the eye and the words be the thing that is read. */
  color: var(--roam-white);
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
}
/* The laurel keeps the gold wherever it is used, since it inherits colour from
   its parent and its parents are now different colours. */
.roam-laurel {
  color: var(--roam-gold-lifted);
  flex: none;
  width: 4.2px;      /* 24 tall at the mark's own 5.6/32 ratio */
  height: 24px;
}
/* The right branch is the left one mirrored, so there is one drawing. */
.roam-laurel--r { transform: scaleX(-1); }

.roam-card__rate {
  position: absolute;
  bottom: 14px;
  right: 14px;
  padding: 7px 12px;
  border-radius: var(--roam-r-full);
  background: rgba(250, 246, 241, 0.94);
  color: var(--roam-forest);
  font-size: var(--roam-fs-13);
  font-weight: 700;
}
.roam-card__from { font-weight: var(--roam-fw-normal); }
.roam-card__per { font-weight: var(--roam-fw-normal); color: var(--roam-text-muted); }
/* When a dated search prices the card and a discount applies, the pre-discount
   per-night is struck through before the real one — the same read as the card
   breakdown and the mobile bar. */
.roam-card__rate s { font-weight: var(--roam-fw-normal); color: var(--roam-text-muted); text-decoration: line-through; margin-right: 4px; }

.roam-card__body { padding: 20px 22px 22px; }
.roam-card__titlerow {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--roam-sp-12);
}
.roam-card__title {
  margin: 0;
  font-family: var(--roam-font-display);
  font-weight: 700;
  font-size: var(--roam-fs-20);
  line-height: 1.15;
}
.roam-card:hover .roam-card__title { color: var(--roam-forest-soft); }
.roam-card__rating {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-text-primary);
  white-space: nowrap;
}
/* The star is gold; the number takes the row's own ink. currentColor on the SVG
   would paint it the text colour, so the gold is set here on the star alone. */
.roam-card__rating .roam-star { color: var(--roam-gold); }
/* The headline under the name — the OwnerRez tagline. Two lines at most so a
   long one does not size every card's track (the subgrid point again). Empty on
   a listing with no headline, where the track collapses to nothing. */
.roam-card__headline {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  margin-top: 4px;
  font-size: var(--roam-fs-14);
  color: var(--roam-text-secondary);
  line-height: var(--roam-lh-snug);
}
/* The place, behind a pin, on its own line above the spec figures. */
.roam-card__loc {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin: 8px 0 0;
  font-size: var(--roam-fs-13);
  color: var(--roam-text-muted);
}
.roam-card__pin { flex: none; }
.roam-card__meta {
  display: block;
  margin: 3px 0 12px;
  font-size: var(--roam-fs-13);
  color: var(--roam-text-muted);
}
/* CLAMPED TO TWO LINES, because this is the row that made the cards uneven.
   The blurb comes from whatever OwnerRez holds and runs one to four lines; the
   subgrid then sizes every card's blurb track to the tallest, and the short ones
   carry the slack. Capping it to two lines is the card controlling the dynamic
   content rather than the content sizing the card — the point the subgrid is
   here to serve. A one-line blurb still leaves one line of slack; a four-line
   one is trimmed to two with an ellipsis, so the track is at most two lines for
   every card and the row is close to even. */
.roam-card__desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  margin: 0 0 12px;
  font-size: var(--roam-fs-14);
  line-height: var(--roam-type-body-lh);
  color: var(--roam-text-body);
}
/* flex-start ON BOTH AXES, and it is the subgrid that makes it necessary.
   A subgrid item's default align-self is `stretch`, so this row is as tall as
   the tallest chip row in the whole grid — and a flex container that has been
   stretched passes that height on to its own items, which is why the pills
   came out tall on the cards with less under them. The container may be as tall
   as the track wants; the chips inside it keep their own height and sit at the
   top of it. */
.roam-card__tags {
  display: flex;
  gap: var(--roam-sp-8);
  flex-wrap: wrap;
  align-items: flex-start;
  align-content: flex-start;
}
.roam-card__tag {
  padding: 5px 10px;
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-medium);
  letter-spacing: var(--roam-ls-xs);
  color: var(--roam-text-body);
}
/* The stay total, beneath the chips behind a hairline. Above them it puts the
   biggest number on the card between the title and the bedroom count. */
.roam-card__total {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--roam-sp-12);
  margin-top: var(--roam-sp-16);
  padding-top: var(--roam-sp-16);
  border-top: 1px solid var(--roam-border-cream);
}
/* ── AN EMPTY ROW PAINTS NOTHING ──────────────────────────────────────────
   The subgrid needs every card to emit every row so the cards stay in step —
   card.php does exactly that. What it does NOT need is for an emitted-but-empty
   row to spend its own margins: .roam-card__meta carries 6/12 and
   .roam-card__total a hairline and 16/16, so a card outside a dated search drew
   a rule across its bottom and a band of space under it — the "unnecessary div
   line and bottom row".

   :empty is the right test and it is exact here: these spans are emitted with
   no children and no text when they have nothing, so the selector matches the
   absence itself rather than a class somebody has to remember to add. It is
   strict — a single space between the tags would stop it matching — which is
   why card.php concatenates with no whitespace. An empty row still occupies its
   subgrid track, but a track holding only empty rows across the row collapses to
   zero, so the cards stay aligned and nothing paints. */
.roam-card__loc:empty,
.roam-card__meta:empty,
.roam-card__desc:empty,
.roam-card__tags:empty,
.roam-card__total:empty {
  margin: 0;
  padding: 0;
  border: 0;
}
.roam-card__range {
  display: block;
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--roam-text-muted);
}
.roam-card__totall { min-width: 0; }
.roam-card__nights { display: block; font-size: var(--roam-fs-13); color: var(--roam-text-body); }
.roam-card__totalr { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; flex: none; }
.roam-card__sum { font-size: var(--roam-fs-18); font-weight: 700; color: var(--roam-forest); }
/* "total · direct price" — the direct-booking figure, in the savings green. */
.roam-card__sumlab { font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); color: var(--roam-success-active); white-space: nowrap; }

/* ── The rate panel ──────────────────────────────────────────────────────── */
.roam-rate {
  padding: var(--roam-sp-24);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
  box-shadow: var(--roam-shadow-rest);
  background: var(--roam-white);
}
.roam-rate__n { margin: 0; font-size: var(--roam-fs-28); font-family: var(--roam-font-display); }
/* The pre-discount per-night, struck through before the calculated rate. */
.roam-rate__n s { font-size: var(--roam-fs-20); color: var(--roam-text-muted); text-decoration: line-through; margin-right: var(--roam-sp-6); }
/* "From" — a smaller lead-in on the base nightly figure. */
.roam-rate__from { font-family: var(--roam-font-body); font-size: var(--roam-fs-15); font-weight: var(--roam-fw-semibold); color: var(--roam-text-secondary); }
.roam-rate__unit { font-family: var(--roam-font-body); font-size: var(--roam-fs-15); color: var(--roam-text-secondary); }
.roam-rate__min { margin: var(--roam-sp-6) 0 0; font-size: var(--roam-fs-14); color: var(--roam-text-secondary); }

/* The pledge banner beneath the price — gold Best-Price-Guarantee by default,
   green discount call-out (icon + bold name + saving) once a discounted quote
   lands. Same gold/green semantics as the compact version in the mobile bar.
   A negative top margin pulls it up tight under the price, inside the card's
   flex gap, so the banner reads as attached to the number above it. */
/* Negative top AND bottom margins trim the card's 12px flex gap to 8px on both
   sides of the badge, so it sits tight between the price and the fields. */
.roam-pledge { margin: calc(-1 * var(--roam-sp-2)) 0; }
.roam-pledge__gold,
.roam-pledge__green {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-8);
  padding: var(--roam-sp-8) var(--roam-sp-12);
  border-radius: var(--roam-r-sm);
  font-size: var(--roam-fs-12);
  line-height: var(--roam-lh-snug);
}
.roam-pledge__gold[hidden],
.roam-pledge__green[hidden] { display: none; }
.roam-pledge__gold { background: color-mix( in srgb, var(--roam-gold) 14%, transparent ); color: var(--roam-forest); }
.roam-pledge__gold .roam-pledge__ico { color: var(--roam-gold-active); }
.roam-pledge__green { background: color-mix( in srgb, var(--roam-success) 12%, transparent ); color: var(--roam-success-active); }
.roam-pledge__ico { flex: none; }
.roam-pledge__green strong { font-weight: var(--roam-fw-semibold); }
.roam-pledge__claim { color: inherit; }
/* A middot between the discount name and the urgency phrase, only when there is
   a phrase to separate. */
.roam-pledge__claim:not(:empty)::before { content: '·'; margin: 0 5px; opacity: 0.55; }
.roam-pledge__terms { color: inherit; text-decoration: underline; }
.roam-pledge__tx { min-width: 0; }

/* The savings + rewards cards beneath the CTA — a 15% GREEN "You Save vs Airbnb"
   and a 15% GOLD "You earn", each with a coloured figure. Shown on the rail card
   AND inside the mobile booking sheet; hidden until a stay is priced. Spacing
   comes from the card's own flex gap, so no extra top margin. */
.roam-earn {
  display: flex;
  flex-direction: column;
  gap: var(--roam-sp-8);
}
.roam-earn[hidden] { display: none; }
.roam-earn__card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-10);
  padding: var(--roam-sp-8) var(--roam-sp-14);
  border-radius: var(--roam-r-sm);
  /* fs-12 to match the pledge badge above, so the two cards read the same
     height. */
  font-size: var(--roam-fs-12);
  color: var(--roam-forest);
}
.roam-earn__card--save { background: color-mix( in srgb, var(--roam-success) 15%, transparent ); }
.roam-earn__card--save .roam-earn__val { color: var(--roam-success-active); }
.roam-earn__card--earn { background: color-mix( in srgb, var(--roam-gold) 15%, transparent ); }
.roam-earn__card--earn .roam-earn__val { color: var(--roam-gold-active); }
.roam-earn__lab { display: inline-flex; align-items: center; gap: 5px; }
.roam-earn__brand { display: inline-flex; align-items: center; gap: 4px; font-weight: var(--roam-fw-semibold); }
.roam-earn__brand .roam-ota__logo { width: 14px; height: 14px; }
.roam-earn__val { font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── The booking panel (S4) ──────────────────────────────────────────────────
   The rail's differentiator: the rate, the book-direct comparison and one CTA,
   in a single card. .roam-rate is the card by itself on a listing with no
   comparison; wrapped in .roam-book it drops its own frame so the two do not
   double-border. */
.roam-book {
  display: flex;
  flex-direction: column;
  /* 10px between rows matches the form's own gap, so the space after the CTA
     equals the space above it. Less top padding tightens the space above the
     price title; the 24px sides are kept so the urgency banner still bleeds to
     the card edge. */
  gap: var(--roam-sp-10);
  padding: var(--roam-sp-16) var(--roam-sp-24) var(--roam-sp-24);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
  box-shadow: var(--roam-shadow-rest);
  background: var(--roam-white);
}
.roam-book .roam-rate {
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
}
.roam-book__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--roam-sp-8);
  min-height: 48px;
  padding: 0 var(--roam-sp-18);
  /* No border — a solid gold pill is the brand CTA; the native <button> border
     was showing through (client 2026-08-03). */
  border: 0;
  border-radius: var(--roam-r-full);
  background: var(--roam-gold);
  color: var(--roam-forest);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-sm);
  text-decoration: none;
  cursor: pointer; /* native <button> defaults to the arrow; match the other controls' hand */
  transition: background-color 0.2s var(--roam-easing);
}
.roam-book__bolt { flex: 0 0 auto; }
.roam-book__cta:hover,
.roam-book__cta:focus-visible { background: var(--roam-gold-active); }

/* ── The mobile sticky expandable footer ─────────────────────────────────────
   Above the mobile breakpoint none of this shows: the card is the rail panel,
   the peek bar and the scrim are display:none, and the close control is hidden.
   On a phone the card leaves the flow and becomes a bottom sheet that slides up
   from a fixed peek bar. */
.roam-book__close,
.roam-bookbar,
.roam-book__scrim { display: none; }

@media (max-width: 767px) {
  /* The card is a bottom sheet: fixed, rounded at the top, scrollable, slid off
     screen until opened. Its rounded corners clip the full-bleed urgency banner
     back to the sheet's shape. */
  .roam-book {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1100;
    /* dvh, not vh: vh is the LARGE viewport (URL bar retracted), so 88vh can be
       taller than what's actually visible and hides the sheet's top and bottom
       behind the browser chrome with no way to scroll to them. dvh tracks the
       real visible height. The vh line is the fallback for older browsers. */
    max-height: 88vh;
    max-height: 88dvh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    border: 0;
    border-radius: var(--roam-r-xl) var(--roam-r-xl) 0 0;
    box-shadow: 0 -18px 48px rgba(27, 43, 39, 0.28);
    transform: translateY(100%);
    transition: transform 0.3s var(--roam-easing);
  }
  body.roam-booksheet-open .roam-book { transform: translateY(0); }
  /* The full-bleed urgency banner is the sheet's bottom edge: square it off and
     let it carry the home-indicator inset, so teal (not a white strip) fills the
     safe area and its text clears the indicator. Its -24px bottom margin already
     bleeds it flush to the sheet's border-box bottom. */
  .roam-book__urgency {
    border-radius: 0;
    padding-bottom: calc(var(--roam-sp-12) + env(safe-area-inset-bottom));
  }

  /* A bare, stylized X — no circle chip behind it (client 2026-08-03). The 36px
     box is the tap target; the mark itself sits transparent on the sheet. */
  .roam-book__close {
    position: absolute;
    top: var(--roam-sp-12);
    right: var(--roam-sp-12);
    z-index: 1;
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--roam-forest);
    cursor: pointer;
    transition: opacity 0.15s ease;
  }
  .roam-book__close:hover { opacity: 0.6; }

  .roam-book__scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 1090;
    background: rgba(27, 43, 39, 0.5);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
  }
  body.roam-booksheet-open .roam-book__scrim { opacity: 1; visibility: visible; }

  /* The peek bar — always visible, the rate on the left and Reserve on the
     right, sliding away while the sheet is open. */
  .roam-bookbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1080;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--roam-sp-16);
    padding: var(--roam-sp-12) var(--roam-page-x);
    padding-bottom: calc(var(--roam-sp-12) + env(safe-area-inset-bottom));
    background: var(--roam-white);
    border-top: 1px solid var(--roam-border-tag);
    box-shadow: 0 -8px 24px rgba(27, 43, 39, 0.12);
    transition: transform 0.3s var(--roam-easing);
  }
  body.roam-booksheet-open .roam-bookbar { transform: translateY(100%); }
  .roam-bookbar__info { display: flex; flex-direction: column; min-width: 0; }
  .roam-bookbar__price { font-size: var(--roam-fs-15); color: var(--roam-text-secondary); }
  .roam-bookbar__from { color: var(--roam-text-secondary); }
  /* The pre-discount per-night, struck through before the discounted one. */
  .roam-bookbar__price s { color: var(--roam-text-muted); text-decoration: line-through; margin-right: var(--roam-sp-4); }
  .roam-bookbar__price s strong { font-weight: var(--roam-fw-regular, 400); }
  /* The selected dates, a smaller line beneath the rate. */
  .roam-bookbar__dates { font-size: var(--roam-fs-12); color: var(--roam-text-secondary); }
  .roam-bookbar__dates[hidden] { display: none; }
  /* Second line: the dates (when picked) and a compact pledge sitting together;
     with no dates the pledge sits alone under the "From" price. Wraps rather than
     truncating if the phone is narrow. */
  .roam-bookbar__line2 { display: flex; align-items: center; gap: var(--roam-sp-8); flex-wrap: wrap; }
  .roam-bookbar__pledge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--roam-fs-12);
    font-weight: var(--roam-fw-semibold);
    color: var(--roam-gold-active);
  }
  .roam-bookbar__pico { flex: none; }
  .roam-bookbar__ptag { display: none; }
  /* Discount state: green semantic, the tag icon instead of the shield. */
  .roam-bookbar__pledge.is-discount { color: var(--roam-success-active); }
  .roam-bookbar__pledge.is-discount .roam-bookbar__pshield { display: none; }
  .roam-bookbar__pledge.is-discount .roam-bookbar__ptag { display: inline-flex; }
  .roam-bookbar__price strong { font-size: var(--roam-fs-18); color: var(--roam-text-primary); font-weight: var(--roam-fw-semibold); }
  .roam-bookbar__unit { color: var(--roam-text-secondary); }
  .roam-bookbar__cta {
    flex: 0 0 auto;
    min-height: 46px;
    padding: 0 var(--roam-sp-28);
    border: 0;
    border-radius: var(--roam-r-full);
    background: var(--roam-gold);
    color: var(--roam-forest);
    font-weight: var(--roam-fw-semibold);
    cursor: pointer;
  }
  .roam-bookbar__cta:hover { background: var(--roam-gold-active); }

  /* The empty rail must not leave the grid's row-gap as trailing space, and the
     page needs clearance so its last content is not hidden under the peek bar.
     Clearance goes on .roam-wrap (the in-measure content), NOT .roam-main — the
     full-bleed explore band is .roam-main's last child, so padding there left an
     84px cream strip between the green band and the dark footer. On .roam-wrap the
     clearance sits above the band, where cream is expected, and the band still
     butts the footer. */
  .roam-prop__cols { row-gap: 0; }
  body.single-roam_property .roam-wrap { padding-bottom: 84px; }
  body.roam-booksheet-open { overflow: hidden; }
}

/* The booking form — dates, guests, a running estimate and the reserve CTA. */
.roam-bk { display: flex; flex-direction: column; gap: var(--roam-sp-10); }
/* ONE BORDERED BOX, the dev site's field group: check-in and check-out on the
   top row, guests beneath, hairlines between the cells rather than a frame on
   each input. */
.roam-bk__fields {
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
  overflow: hidden;
}
.roam-bk__row { display: grid; grid-template-columns: 1fr 1fr; }
.roam-bk__row .roam-bk__f + .roam-bk__f { border-left: 1px solid var(--roam-border-tag); }
.roam-bk__f--wide { border-top: 1px solid var(--roam-border-tag); }
.roam-bk__f {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  min-width: 0;
  padding: var(--roam-sp-8) var(--roam-sp-14);
}
.roam-bk__lab {
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
  color: var(--roam-text-muted);
  line-height: 1;
}
/* The inputs blend into the box — no frame of their own, transparent ground.
   The native date control keeps its own picker and calendar glyph. */
.roam-bk__in {
  width: 100%;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: var(--roam-fs-14);
  line-height: 1.2;
  color: var(--roam-text-primary);
}
/* An INSET focus ring — a normal outline would be clipped by the box's
   overflow:hidden. */
.roam-bk__in:focus-visible { outline: 2px solid var(--roam-gold); outline-offset: -2px; }

/* When JS takes over, the native date/number controls give way to the trigger
   buttons, which open the picker modal. The buttons reuse .roam-bk__in so they
   sit and read exactly where the inputs did. */
.roam-bk--js input.roam-bk__in { display: none; }
.roam-bk__trigger {
  text-align: left;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.roam-bk__trigger.is-empty { color: var(--roam-text-muted); }

/* ── Date + guest picker popover (property booking widget) ────────────────
   Reuses the hero calendar's .roam-cal__* and .roam-guest-* styles; only the
   shell is new. On the rail (tablet+) it anchors under the field it opened from,
   with its right edge on the booking card's right edge — roam-property.js sets
   top/right inline from the field and card rects. On mobile there is no rail
   card to anchor to, so .roam-bkmodal--center centers it as a dialog. */
.roam-bkmodal {
  position: fixed;
  inset: 0;
  z-index: 1200;
}
.roam-bkmodal[hidden] { display: none; }
.roam-bkmodal__scrim { position: absolute; inset: 0; background: rgba(27, 43, 39, 0.5); }
.roam-bkmodal__panel {
  position: absolute;
  z-index: 1;
  width: min(760px, calc(100vw - 16px));
  max-height: 90vh;
  overflow: auto;
  padding: var(--roam-sp-28);
  background: var(--roam-white);
  border-radius: var(--roam-r-xl);
  box-shadow: var(--roam-shadow-hover);
}
/* Centered fallback (mobile / no card to anchor to). */
.roam-bkmodal--center {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--roam-sp-24);
}
.roam-bkmodal--center .roam-bkmodal__panel {
  position: relative;
  inset: auto;
  max-width: 100%;
}
/* Stylized X, no circle — the site convention for close controls. */
.roam-bkmodal__x {
  position: absolute;
  top: 14px;
  right: 14px;
  padding: 6px;
  border: 0;
  background: none;
  line-height: 0;
  color: var(--roam-text-primary);
  cursor: pointer;
}
.roam-bkmodal__x:hover { color: var(--roam-forest); }
/* Dates and guests are separate popups, so the panel sizes to its one section:
   wide enough for two months on dates, narrow for the guest steppers. */
.roam-bkmodal__panel--guests { width: min(420px, calc(100vw - 16px)); }
.roam-bkmodal__title {
  margin: 0 var(--roam-sp-32) var(--roam-sp-16) 0;
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-18);
}
.roam-bkmodal__guests { margin-top: var(--roam-sp-4); }
/* Done is always on the right; Clear (dates only) is pushed to the far left.
   With Clear hidden in the guests popup, Done stays right rather than sliding
   left the way space-between would leave it. */
.roam-bkmodal__foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-top: var(--roam-sp-16);
}
.roam-bkmodal__clear {
  margin-right: auto;
  border: 0;
  background: none;
  font: inherit;
  color: var(--roam-text-primary);
  text-decoration: underline;
  cursor: pointer;
}
.roam-step.is-disabled { opacity: 0.35; cursor: default; }
html.roam-bkmodal-lock { overflow: hidden; }
.roam-bk__estimate {
  padding: var(--roam-sp-12) 0 var(--roam-sp-4);
  border-top: 1px solid var(--roam-border-cream);
}
.roam-bk__erow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--roam-sp-10);
  font-size: var(--roam-fs-15);
  color: var(--roam-text-primary);
  margin-top: var(--roam-sp-6);
}
.roam-bk__erow:first-child { margin-top: 0; }
/* A discount sits on its own line, per the brief, coloured for contrast so both
   its description and its saving read as money off — the amount a little heavier. */
.roam-bk__erow--discount { color: var(--roam-success); }
.roam-bk__erow--discount span:last-child { font-weight: var(--roam-fw-semibold); }
/* The total, set off by a hairline above and the heavier weight. */
.roam-bk__erow--total {
  margin-top: var(--roam-sp-10);
  padding-top: var(--roam-sp-10);
  border-top: 1px solid var(--roam-border-cream);
  font-weight: 700;
  color: var(--roam-forest);
}
/* Pre-discount figures struck through — the accommodation rate and amount, and
   the pre-discount subtotal before the real total — lighter and lighter-weight
   so the discounted figures are the ones that read. */
.roam-bk__erow s {
  font-weight: var(--roam-fw-regular, 400);
  color: var(--roam-text-muted);
  text-decoration: line-through;
}
.roam-bk__erow--total s { margin-right: var(--roam-sp-6); }
.roam-bk__eloading {
  margin: 0;
  font-size: var(--roam-fs-13);
  color: var(--roam-text-muted);
}
.roam-bk__enote {
  margin: var(--roam-sp-6) 0 0;
  font-size: var(--roam-fs-12);
  line-height: var(--roam-lh-snug);
  color: var(--roam-text-muted);
}
/* A real OwnerRez validation ("3 night minimum") shown in place of the total. */
.roam-bk__enote--warn { color: var(--roam-text-primary); font-weight: var(--roam-fw-semibold); }

/* THE PER-NIGHT OTA COMPARISON — the dev site's design of record. A dark header,
   ROAM Direct as the bold anchor row, the three listing sites above it by their
   fee (each with a brand mark and a +delta), and the perks line beneath. */
.roam-ota {
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
  overflow: hidden;
  background: var(--roam-white);
}
.roam-ota__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-12);
  padding: var(--roam-sp-12) var(--roam-sp-16);
  background: var(--roam-surface-stat);
}
.roam-ota__headl {
  font-size: var(--roam-fs-12);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-md);
  text-transform: uppercase;
  color: var(--roam-gold-lifted);
}
.roam-ota__headr { font-size: var(--roam-fs-13); color: rgba(250, 246, 241, 0.72); }
.roam-ota__rows { padding: var(--roam-sp-6) 0; }
.roam-ota__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-12);
  padding: var(--roam-sp-8) var(--roam-sp-16);
}
.roam-ota__brand { display: inline-flex; align-items: center; gap: var(--roam-sp-10); min-width: 0; }
.roam-ota__logo { flex: 0 0 auto; display: block; width: 24px; height: 24px; }
.roam-ota__name { font-size: var(--roam-fs-15); }
.roam-ota__figs { display: inline-flex; align-items: baseline; gap: var(--roam-sp-10); flex: 0 0 auto; }
.roam-ota__delta { font-size: var(--roam-fs-13); color: var(--roam-text-muted); }
.roam-ota__total { font-size: var(--roam-fs-16); }
/* ROAM Direct is the anchor: bold forest, the lowest, cleanest number. */
.roam-ota__row--roam .roam-ota__name { font-weight: var(--roam-fw-bold); color: var(--roam-forest); }
.roam-ota__row--roam .roam-ota__total { font-weight: var(--roam-fw-bold); color: var(--roam-forest); font-size: var(--roam-fs-18); }
/* The listing sites read back — muted name and total, so ROAM Direct wins the eye. */
.roam-ota__row:not(.roam-ota__row--roam) .roam-ota__name,
.roam-ota__row:not(.roam-ota__row--roam) .roam-ota__total { color: var(--roam-text-secondary); }
/* The reassurance strip sits between the comparison card and the OwnerRez note,
   on its own — not framed like the OTA card, just a check + line of text. */
.roam-book__perks {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-8);
  margin: 0;
  font-size: var(--roam-fs-13);
  color: var(--roam-text-secondary);
}
.roam-book__perkscheck { flex: 0 0 auto; color: var(--roam-success); }
/* The urgency banner — a full-bleed teal strip at the very bottom of the card.
   Negative margins cancel the card's padding so it meets the bottom edge, and the
   bottom corners are rounded to the card's radius. */
.roam-book__urgency {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-8);
  margin: var(--roam-sp-4) calc(-1 * var(--roam-sp-24)) calc(-1 * var(--roam-sp-24));
  padding: var(--roam-sp-12) var(--roam-sp-24);
  /* A soft 15% teal tint set off by a 1px teal top border, with teal text —
     no longer a solid teal fill. */
  background: color-mix( in srgb, var(--roam-teal) 15%, transparent );
  border-top: 1px solid var(--roam-teal);
  color: var(--roam-teal-active);
  border-radius: 0 0 var(--roam-r-lg) var(--roam-r-lg);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  line-height: var(--roam-lh-snug);
}
.roam-book__urgencyico { flex: 0 0 auto; }

/* ── Tablet+ booking-widget tuning ──────────────────────────────────────────
   The rail card (768px and up); the mobile bottom sheet keeps its own sizing. */
@media (min-width: 768px) {
  /* Date and guest fields to a 60px cell. */
  .roam-bk__f { min-height: 44px; justify-content: center; }

  /* Comparison card — smaller header type, and a 40px header row so it matches
     the rows below it. */
  .roam-ota__head { min-height: 30px; padding-top: 0; padding-bottom: 0; }
  .roam-ota__headl { font-size: var(--roam-fs-11); }
  .roam-ota__headr { font-size: var(--roam-fs-12); }

  /* A uniform 36px per comparison row, with the brand mark and figures scaled
     down to sit comfortably inside it (~8px above and below their content). */
  .roam-ota__rows { padding: 0; }
  .roam-ota__row { min-height: 36px; padding-top: 0; padding-bottom: 0; }
  .roam-ota__logo { width: 16px; height: 16px; }
  .roam-ota__name { font-size: var(--roam-fs-12); }
  .roam-ota__total { font-size: var(--roam-fs-13); }
  .roam-ota__delta { font-size: var(--roam-fs-11); }
  .roam-ota__row--roam .roam-ota__total { font-size: var(--roam-fs-14); }

  /* ROAM Direct row — a 15% gold wash so the anchor price wins the eye. */
  .roam-ota__row--roam { background: color-mix(in srgb, var(--roam-gold) 15%, transparent); }

  /* The urgency banner is one line — it never wraps; a message too long for the
     card is clipped with an ellipsis rather than pushing the card taller. */
  .roam-book__urgency { flex-wrap: nowrap; white-space: nowrap; }
  .roam-book__urgency span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}

/* ── The map (S5) ─────────────────────────────────────────────────────────── */
/* A keyless OpenStreetMap embed. The figure is the rounded, clipped frame; the
   "larger map" link sits outside it so the rounding does not clip it. */
.roam-map {
  margin: 0;
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-xl);
  overflow: hidden;
  /* A sand ground under the tiles: while the map loads (or if a guest blocks
     third-party frames) the box reads as a deliberately empty map, not a broken
     white rectangle. */
  background: var(--roam-sand);
}
.roam-map__frame {
  display: block;
  width: 100%;
  height: 460px;
  border: 0;
}
.roam-map__link {
  display: inline-block;
  margin-top: var(--roam-sp-8);
  font-size: var(--roam-fs-13);
  color: var(--roam-teal);
}
@media (max-width: 767px) {
  .roam-map__frame { height: 300px; }
}

/* ── Explore link tree (S9) ──────────────────────────────────────────────────
   Two clusters of internal links that close the page — related places and
   types — on a green-gradient band. The band is printed outside .roam-wrap so
   it runs full width the way the footer does; its inner element holds the
   links to the page measure. */
.roam-exp-band {
  background: var(--roam-surface-forest-down);
  color: var(--roam-cream);
}
.roam-exp-band__inner {
  /* --roam-shell (content + gutters), NOT --roam-max-width — the inner pads
     itself by --roam-page-x, so pairing that with the bare 1280 content width
     inset the links a whole gutter past every other section (client 2026-08-03).
     Shell + the same padding lands them on the page's own left edge. */
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding: var(--roam-sp-48) var(--roam-page-x);
}
.roam-exp + .roam-exp {
  margin-top: var(--roam-sp-40);
  padding-top: var(--roam-sp-40);
  border-top: 1px solid rgba(250, 246, 241, 0.14);
}
/* Gold uppercase eyebrow on the dark ground, the way the ROAM Standard band
   sets its own. */
.roam-exp__eyebrow {
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-12);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-lg);
  text-transform: uppercase;
  color: var(--roam-gold-lifted);
  margin: 0 0 var(--roam-sp-12);
}
/* The section title: a large cream serif, the design's. */
.roam-exp__h {
  font-family: var(--roam-font-display);
  font-size: clamp(26px, 3.2vw, var(--roam-fs-32));
  font-weight: var(--roam-fw-normal);
  color: var(--roam-cream);
  margin: 0 0 var(--roam-sp-20);
}
.roam-exp__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--roam-sp-12) var(--roam-sp-24);
}
.roam-exp__link {
  font-size: var(--roam-fs-15);
  color: rgba(250, 246, 241, 0.72);
  text-decoration: none;
}
.roam-exp__link:hover,
.roam-exp__link:focus-visible { color: var(--roam-cream); text-decoration: underline; }
/* The place tiles stack the city over "Vacation Rentals" — the city carries the
   emphasis, the kind sits under it in a quieter cream. */
.roam-exp__link--place {
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
  line-height: 1.3;
}
.roam-exp__place { font-weight: var(--roam-fw-semibold); color: var(--roam-cream); }
.roam-exp__kind { font-size: var(--roam-fs-13); color: rgba(250, 246, 241, 0.6); }
.roam-exp__link--place:hover .roam-exp__kind,
.roam-exp__link--place:focus-visible .roam-exp__kind { color: rgba(250, 246, 241, 0.85); }

/* ── City landing ("{City} vacation rentals") ────────────────────────────────
   A leaf location archive with the SEO hero band, value props, an FAQ and a
   counted summary layered on. No stock hero photograph — the brand gradient
   stands in, flagged data-roam-placeholder. */
/* Hero: a full-bleed forest band, headline left, booking search card right. */
.roam-city__hero {
  background: var(--roam-surface-hero);
  color: var(--roam-cream);
  padding-block: var(--roam-sp-56);
}
/* An editor's hero photo: the inline style supplies the darkened image; this
   makes it a cover. Copy stays legible on the 58% forest wash set inline. */
.roam-city__hero--photo { background-size: cover; background-position: center; }
.roam-city__heroinner {
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding-inline: var(--roam-page-x);
  display: grid;
  grid-template-columns: 1fr minmax(0, 440px);
  gap: var(--roam-sp-48);
  align-items: center;
}
.roam-city__h1 {
  font-family: var(--roam-font-display);
  font-size: clamp(34px, 5vw, 52px);
  line-height: 1.05;
  margin: 0 0 var(--roam-sp-16);
}
.roam-city__sub {
  max-width: 46ch;
  margin: 0;
  font-size: var(--roam-fs-15);
  line-height: var(--roam-lh-snug);
  color: rgba(250, 246, 241, 0.82);
}
/* The booking search card. */
.roam-city__search {
  background: var(--roam-white);
  color: var(--roam-text-primary);
  border-radius: var(--roam-r-xl);
  padding: var(--roam-sp-32);
  box-shadow: var(--roam-shadow-hover);
}
.roam-city__searchh { font-family: var(--roam-font-display); font-weight: var(--roam-fw-bold); font-size: var(--roam-fs-24); margin: 0 0 var(--roam-sp-4); }
.roam-city__searchsub { margin: 0 0 var(--roam-sp-20); font-size: var(--roam-fs-12); color: var(--roam-text-muted); }
.roam-city__field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--roam-sp-10) var(--roam-sp-16);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-md);
  background: var(--roam-white);
  margin-bottom: var(--roam-sp-12);
}
.roam-city__field:focus-within { border-color: var(--roam-gold); }
.roam-city__flab { font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1px; text-transform: uppercase; color: var(--roam-text-muted); }
.roam-city__fval { font-size: var(--roam-fs-15); color: var(--roam-text-primary); }
.roam-city__fin { width: 100%; border: 0; padding: 0; background: transparent; font: inherit; font-size: var(--roam-fs-15); color: var(--roam-text-primary); }
.roam-city__fin:focus-visible { outline: none; }
.roam-city__fieldrow { display: grid; grid-template-columns: 1fr 1fr; gap: var(--roam-sp-12); }
.roam-city__searchgo {
  width: 100%;
  margin-top: var(--roam-sp-8);
  padding: var(--roam-sp-16);
  border: 0;
  border-radius: var(--roam-r-full);
  background: var(--roam-gold);
  color: var(--roam-forest);
  font: inherit;
  font-weight: var(--roam-fw-bold);
  cursor: pointer;
  transition: background-color 0.2s var(--roam-easing);
}
.roam-city__searchgo:hover { background: var(--roam-gold-active); }

/* Value props — a cream band, three centred columns with gold icons. */
.roam-city__props {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--roam-sp-40);
  padding-block: var(--roam-sp-48);
  border-bottom: 1px solid var(--roam-border-cream);
  text-align: center;
}
.roam-city__prop { max-width: 34ch; margin-inline: auto; }
.roam-city__propico { display: inline-flex; color: var(--roam-gold); margin-bottom: var(--roam-sp-12); }
.roam-city__proph { font-family: var(--roam-font-display); font-size: var(--roam-fs-20); margin: 0 0 var(--roam-sp-8); }
.roam-city__propn { margin: 0; font-size: var(--roam-fs-14); line-height: var(--roam-lh-snug); color: var(--roam-text-secondary); }

/* "Guest favourites in {City}" — centred, over the listings. */
.roam-city__favhead { text-align: center; padding-block: var(--roam-sp-48) var(--roam-sp-32); }
.roam-city__favh { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: 0 0 var(--roam-sp-8); }
.roam-city__favsub { margin: 0; color: var(--roam-text-muted); font-size: var(--roam-fs-15); }

/* Two-column FAQ — "Your questions, answered" left, the accordion right. */
.roam-city__faqsec { padding-block: var(--roam-sp-48); border-top: 1px solid var(--roam-border-cream); margin-top: var(--roam-sp-40); }
.roam-city__faqgrid { display: grid; grid-template-columns: minmax(0, 300px) 1fr; gap: var(--roam-sp-48); align-items: start; }
.roam-city__faqh { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: 0; }
.roam-city__faqitem { border-bottom: 1px solid var(--roam-border-cream); }
.roam-city__faqitem:first-child { border-top: 1px solid var(--roam-border-cream); }
.roam-city__q {
  cursor: pointer;
  list-style: none;
  position: relative;
  padding: 18px var(--roam-sp-24) 18px 0;
  font-weight: var(--roam-fw-semibold);
  font-size: var(--roam-fs-16);
  color: var(--roam-forest);
}
.roam-city__q::-webkit-details-marker { display: none; }
.roam-city__q::after {
  content: '+';
  position: absolute;
  right: 0;
  top: 16px;
  font-weight: var(--roam-fw-normal);
  font-size: var(--roam-fs-20);
  line-height: 1;
  color: var(--roam-gold);
}
.roam-city__faqitem[open] .roam-city__q::after { content: '\2013'; }
.roam-city__a { padding: 0 0 var(--roam-sp-16); color: var(--roam-text-body); line-height: var(--roam-lh-body); }
.roam-city__a p { margin: 0; }

/* About — heading + the counted summary paragraph. */
.roam-city__about { padding-block: var(--roam-sp-40); }
.roam-city__abouth { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: 0 0 var(--roam-sp-12); }
.roam-city__summary { max-width: 80ch; margin: 0; color: var(--roam-text-body); font-size: var(--roam-fs-15); line-height: var(--roam-lh-body); }

/* Things to do — editor-curated cards under the listings. */
.roam-city__things { padding-block: var(--roam-sp-40); border-top: 1px solid var(--roam-border-cream); margin-top: var(--roam-sp-40); }
.roam-city__thingsh { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: 0 0 var(--roam-sp-24); }
.roam-city__hlgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--roam-sp-20); }
.roam-city__hl { padding: var(--roam-sp-20); background: var(--roam-cream); border: 1px solid var(--roam-border-cream); border-radius: var(--roam-r-md, 12px); }
.roam-city__hlh { font-family: var(--roam-font-display); font-size: var(--roam-fs-18); margin: 0 0 var(--roam-sp-6); color: var(--roam-text-primary); }
.roam-city__hlp { margin: 0; font-size: var(--roam-fs-14); line-height: var(--roam-lh-snug); color: var(--roam-text-body); }

/* Area overview — the rich guide body (brand voice runs here). */
.roam-city__overview { padding-block: var(--roam-sp-40); }
.roam-city__overviewh { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: 0 0 var(--roam-sp-16); }
.roam-city__prose { max-width: 72ch; color: var(--roam-text-body); font-size: var(--roam-fs-16); line-height: var(--roam-lh-body); }
.roam-city__prose > * + * { margin-top: var(--roam-sp-16); }
.roam-city__prose h3 { font-family: var(--roam-font-display); font-size: var(--roam-fs-20); margin-top: var(--roam-sp-24); color: var(--roam-text-primary); }
.roam-city__prose a { color: var(--roam-forest); text-decoration: underline; }
.roam-city__prose ul, .roam-city__prose ol { padding-left: 1.4em; }
.roam-city__prose li + li { margin-top: var(--roam-sp-6); }

/* Breadcrumb. */
.roam-city__crumb { padding-block: var(--roam-sp-24) var(--roam-sp-40); border-top: 1px solid var(--roam-border-cream); font-size: var(--roam-fs-13); color: var(--roam-text-muted); }
.roam-city__crumb a { color: var(--roam-text-muted); text-decoration: none; }
.roam-city__crumb a:hover { text-decoration: underline; }
.roam-city__crumbsep { color: var(--roam-text-light); }
.roam-city__crumbnow { color: var(--roam-text-primary); font-weight: var(--roam-fw-semibold); }

@media (max-width: 820px) {
  .roam-city__heroinner { grid-template-columns: 1fr; gap: var(--roam-sp-32); }
  .roam-city__props { grid-template-columns: 1fr; gap: var(--roam-sp-32); }
  .roam-city__faqgrid { grid-template-columns: 1fr; gap: var(--roam-sp-24); }
  .roam-city__hlgrid { grid-template-columns: 1fr; }
}
@media (max-width: 767px) {
  .roam-city__hero { padding-block: var(--roam-sp-40); }
  .roam-city__search { padding: var(--roam-sp-24); }
}

/* ── Field Notes — the article view chrome ───────────────────────────────────
   Layered onto a post body: a way back, a Stay Nearby homes band, Keep reading.
   Renders only when posts exist. */
.roam-notes__back { margin: 0 0 var(--roam-sp-24); }
.roam-notes__backlink { font-size: var(--roam-fs-14); color: var(--roam-teal); text-decoration: none; }
.roam-notes__backlink:hover { text-decoration: underline; }
.roam-article { margin-top: var(--roam-sp-48); }
.roam-article__h2 { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: var(--roam-sp-48) 0 var(--roam-sp-20); }
.roam-article__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--roam-sp-28);
}

/* The Stay Nearby band — a cream card with a compact list of homes. */
.roam-nearby {
  padding: var(--roam-sp-32);
  background: var(--roam-cream);
  border: 1px solid var(--roam-border-cream);
  border-radius: var(--roam-r-xl);
}
.roam-nearby__kicker {
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-md);
  text-transform: uppercase;
  color: var(--roam-gold);
}
.roam-nearby__h { font-family: var(--roam-font-display); font-size: var(--roam-fs-24); margin: var(--roam-sp-4) 0 var(--roam-sp-8); }
.roam-nearby__lede { margin: 0 0 var(--roam-sp-20); color: var(--roam-text-body); font-size: var(--roam-fs-15); line-height: var(--roam-lh-snug); }
.roam-nearby__list { display: grid; gap: var(--roam-sp-12); }
.roam-nearby__row {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-14);
  text-decoration: none;
  color: inherit;
}
.roam-nearby__thumb {
  flex: 0 0 auto;
  width: 68px;
  height: 68px;
  border-radius: var(--roam-r-md);
  background-size: cover;
  background-position: center;
  filter: var(--roam-photo-filter);
}
.roam-nearby__thumb--empty { background: var(--roam-sand); filter: none; }
.roam-nearby__name { display: block; font-weight: var(--roam-fw-semibold); color: var(--roam-text-primary); }
.roam-nearby__where { display: block; font-size: var(--roam-fs-13); color: var(--roam-text-muted); }
.roam-nearby__price { display: block; font-size: var(--roam-fs-13); color: var(--roam-text-body); }
.roam-nearby__per { color: var(--roam-text-muted); }
.roam-nearby__all { display: inline-block; margin-top: var(--roam-sp-16); font-size: var(--roam-fs-14); color: var(--roam-teal); text-decoration: none; }
.roam-nearby__all:hover { text-decoration: underline; }

/* The Field Notes card. */
.roam-note-card {
  display: block;
  background: var(--roam-white);
  border-radius: var(--roam-r-xl);
  overflow: hidden;
  box-shadow: var(--roam-shadow-rest);
  text-decoration: none;
  color: inherit;
  transition: transform 0.25s var(--roam-easing), box-shadow 0.25s var(--roam-easing);
}
/* Rollover matches the property cards: lift + shadow (already), the photo zooms
   1.04, and the title shifts to forest-soft. */
.roam-note-card:hover { transform: var(--roam-lift-hover); box-shadow: var(--roam-shadow-hover); }
.roam-note-card:hover .roam-note-card__media { transform: scale(1.04); }
.roam-note-card:hover .roam-note-card__title { color: var(--roam-forest-soft); }
.roam-note-card__media {
  aspect-ratio: 3 / 2;
  background-size: cover;
  background-position: center;
  filter: var(--roam-photo-filter);
  transition: transform 0.5s var(--roam-easing);
}
.roam-note-card__media--empty { background: var(--roam-sand); filter: none; }
.roam-note-card__body { padding: var(--roam-sp-18); }
.roam-note-card__eyebrow {
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
  color: var(--roam-gold);
}
.roam-note-card__title { font-family: var(--roam-font-display); font-size: var(--roam-fs-18); margin: var(--roam-sp-6) 0 var(--roam-sp-8); transition: color 0.25s var(--roam-easing); }
.roam-note-card__excerpt { margin: 0 0 var(--roam-sp-10); font-size: var(--roam-fs-14); line-height: var(--roam-lh-snug); color: var(--roam-text-body); }
.roam-note-card__more { font-size: var(--roam-fs-13); font-weight: var(--roam-fw-semibold); color: var(--roam-gold); }

/* ── Field Notes article (single.php) ─────────────────────────────────────────
   A full-bleed hero, a two-column body (prose measure + sticky sidebar), then
   Keep reading and a nearby-stays grid. single.php owns the layout so the sidebar
   is a real column, not a block stacked under the body. */
/* Matches the site shell: 1330 = 1280 content + 2×25 gutters (see .roam-wrap). */
.roam-fn { --roam-fn-max: 1330px; }
.roam-fn__hero {
  position: relative;
  min-height: 420px;
  display: flex;
  align-items: flex-end;
  background-color: var(--roam-forest);
  background-image: linear-gradient(180deg, rgba(27, 43, 39, 0.15) 0%, rgba(27, 43, 39, 0.78) 100%), var(--roam-fn-hero, none);
  background-size: cover;
  background-position: center;
  color: var(--roam-cream);
}
.roam-fn__hero--nophoto { background-image: linear-gradient(180deg, var(--roam-forest-light) 0%, var(--roam-forest) 100%); }
.roam-fn__heroinner {
  width: 100%;
  max-width: var(--roam-fn-max);
  margin: 0 auto;
  padding: var(--roam-sp-48) var(--roam-page-x);
}
.roam-fn__back {
  display: inline-block;
  margin-bottom: var(--roam-sp-24);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-cream);
  text-decoration: none;
  opacity: 0.9;
}
.roam-fn__back:hover { opacity: 1; }
.roam-fn__eyebrow {
  margin: 0 0 var(--roam-sp-12);
  font-size: var(--roam-fs-12);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--roam-gold-lifted);
}
.roam-fn__title {
  margin: 0;
  max-width: 18ch;
  font-family: var(--roam-font-display);
  font-size: clamp(2.2rem, 5vw, 3.4rem);
  line-height: 1.05;
  color: var(--roam-white);
}
.roam-fn__wrap {
  max-width: var(--roam-fn-max);
  margin: 0 auto;
  padding: var(--roam-sp-48) var(--roam-page-x) 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--roam-sp-48);
  align-items: start;
}
.roam-fn__main { min-width: 0; }
.roam-fn__dek {
  margin: 0 0 var(--roam-sp-28);
  font-family: var(--roam-font-display);
  font-style: italic;
  font-size: clamp(1.3rem, 2.4vw, 1.7rem);
  line-height: 1.3;
  color: var(--roam-forest);
}
.roam-fn__body { max-width: none; }
.roam-fn__side { position: sticky; top: var(--roam-sp-24); display: flex; flex-direction: column; gap: var(--roam-sp-20); }
.roam-fnside__card { border-radius: var(--roam-r-xl); padding: var(--roam-sp-24); background: var(--roam-white); box-shadow: var(--roam-shadow-rest); }
.roam-fnside__kicker { margin: 0 0 var(--roam-sp-2); font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1.5px; text-transform: uppercase; color: var(--roam-gold); }
.roam-fnside__region { margin: 0 0 var(--roam-sp-16); font-family: var(--roam-font-display); font-size: var(--roam-fs-20); color: var(--roam-forest); }
.roam-fnside__homes { display: flex; flex-direction: column; gap: var(--roam-sp-14); }
.roam-fnhome { display: flex; gap: var(--roam-sp-12); align-items: center; text-decoration: none; color: inherit; }
.roam-fnhome__thumb { flex: 0 0 auto; width: 56px; height: 44px; border-radius: var(--roam-r-sm); background-size: cover; background-position: center; filter: var(--roam-photo-filter); }
.roam-fnhome__thumb--empty { background: var(--roam-sand); filter: none; }
.roam-fnhome__tx { min-width: 0; }
.roam-fnhome__name { display: block; font-weight: var(--roam-fw-semibold); font-size: var(--roam-fs-14); color: var(--roam-forest); }
.roam-fnhome__where { display: block; font-size: var(--roam-fs-12); color: var(--roam-text-muted); }
.roam-fnhome__price { display: block; font-size: var(--roam-fs-13); font-weight: var(--roam-fw-semibold); color: var(--roam-forest); }
.roam-fnhome__per { font-weight: var(--roam-fw-normal); color: var(--roam-text-muted); }
.roam-fnside__all { display: block; margin-top: var(--roam-sp-18); padding: var(--roam-sp-12); border-radius: var(--roam-r-full); background: var(--roam-forest); color: var(--roam-cream); text-align: center; font-size: var(--roam-fs-13); font-weight: var(--roam-fw-semibold); text-decoration: none; }
.roam-fnside__all:hover { background: var(--roam-forest-active); }
.roam-fnside__card--search { background: var(--roam-forest); color: var(--roam-cream); }
.roam-fnside__kicker--light { color: var(--roam-gold-lifted); }
.roam-fnside__region--light { color: var(--roam-white); }
.roam-fnsearch__f { display: block; margin-bottom: var(--roam-sp-12); }
.roam-fnsearch__f span { display: block; margin-bottom: var(--roam-sp-4); font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1px; text-transform: uppercase; color: rgba(250, 246, 241, 0.7); }
.roam-fnsearch__f select {
  width: 100%; padding: var(--roam-sp-10) var(--roam-sp-12);
  border-radius: var(--roam-r-sm); border: 1px solid rgba(250, 246, 241, 0.28);
  background-color: rgba(250, 246, 241, 0.12); color: var(--roam-cream);
  font: inherit; font-size: var(--roam-fs-14); line-height: 1.3;
  /* A native arrow drawn in cream, since the browser default is invisible on the
     translucent dark field. */
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23FAF6F1' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--roam-sp-12) center; background-size: 11px;
  padding-right: var(--roam-sp-32);
}
/* The dropdown list itself renders in the OS palette, so cream-on-white was
   unreadable — force legible dark-on-cream options. */
.roam-fnsearch__f select option { color: var(--roam-text-primary); background: var(--roam-white); }
.roam-fnsearch__go { display: flex; align-items: center; justify-content: center; gap: var(--roam-sp-8); width: 100%; margin-top: var(--roam-sp-6); padding: var(--roam-sp-12); border: 0; border-radius: var(--roam-r-full); background: var(--roam-gold); color: var(--roam-forest); font: inherit; font-weight: var(--roam-fw-semibold); cursor: pointer; }
.roam-fnsearch__go:hover { background: var(--roam-gold-active); }
/* Share row — under the article body. */
.roam-fnshare { margin: var(--roam-sp-40) 0 0; padding-top: var(--roam-sp-24); border-top: 1px solid var(--roam-border-tag); display: flex; flex-wrap: wrap; align-items: center; gap: var(--roam-sp-16); }
.roam-fnshare__label { font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1.5px; text-transform: uppercase; color: var(--roam-text-muted); }
.roam-fnshare__row { display: flex; flex-wrap: wrap; gap: var(--roam-sp-8); }
.roam-fnshare__btn {
  display: inline-flex; align-items: center; gap: var(--roam-sp-8);
  padding: var(--roam-sp-8) var(--roam-sp-14); border-radius: var(--roam-r-full);
  border: 1px solid var(--roam-border-tag); background: var(--roam-white);
  color: var(--roam-forest); font: inherit; font-size: var(--roam-fs-13); font-weight: var(--roam-fw-semibold);
  text-decoration: none; cursor: pointer; transition: background-color 0.2s var(--roam-easing), border-color 0.2s var(--roam-easing);
}
.roam-fnshare__btn:hover { background: var(--roam-cream); border-color: var(--roam-gold); }
.roam-fnshare__copy.is-copied { background: color-mix(in srgb, var(--roam-success) 12%, transparent); border-color: var(--roam-success); color: var(--roam-success); }

/* Topic / tag cloud — under the share row. */
.roam-fntopics { margin: var(--roam-sp-28) 0 0; }
.roam-fntopics__h { font-family: var(--roam-font-display); font-size: var(--roam-fs-20); margin: 0 0 var(--roam-sp-14); color: var(--roam-forest); }
.roam-fntopics__cloud { display: flex; flex-wrap: wrap; gap: var(--roam-sp-8); }
.roam-fntopics__tag {
  display: inline-flex; padding: var(--roam-sp-6) var(--roam-sp-14); border-radius: var(--roam-r-full);
  background: var(--roam-cream); border: 1px solid var(--roam-border-tag);
  color: var(--roam-text-secondary); font-size: var(--roam-fs-13); text-decoration: none;
  transition: background-color 0.2s var(--roam-easing), color 0.2s var(--roam-easing);
}
.roam-fntopics__tag:hover { background: var(--roam-forest); color: var(--roam-cream); border-color: var(--roam-forest); }

/* Subscribe card — under the quick search in the sidebar. Light card, gold action. */
.roam-fnsub__lede { margin: 0 0 var(--roam-sp-14); font-size: var(--roam-fs-13); color: var(--roam-text-secondary); line-height: var(--roam-lh-snug); }
.roam-fnsub__f { display: block; margin-bottom: var(--roam-sp-10); }
.roam-fnsub__f span { display: block; margin-bottom: var(--roam-sp-4); font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1px; text-transform: uppercase; color: var(--roam-text-muted); }
.roam-fnsub__f input { width: 100%; padding: var(--roam-sp-10) var(--roam-sp-12); border-radius: var(--roam-r-sm); border: 1px solid var(--roam-border-tag); background: var(--roam-cream); font: inherit; font-size: var(--roam-fs-14); color: var(--roam-text-primary); }
.roam-fnsub__f input:focus-visible { outline: 2px solid var(--roam-gold); outline-offset: -1px; border-color: var(--roam-gold); }
.roam-fnsub__go { display: block; width: 100%; padding: var(--roam-sp-12); border: 0; border-radius: var(--roam-r-full); background: var(--roam-gold); color: var(--roam-forest); font: inherit; font-weight: var(--roam-fw-semibold); cursor: pointer; transition: background-color 0.2s var(--roam-easing); }
.roam-fnsub__go:hover { background: var(--roam-gold-active); }
.roam-fnsub__done { margin: 0; font-size: var(--roam-fs-14); font-weight: var(--roam-fw-semibold); color: var(--roam-success); }

/* About the author — a centred band above related/nearby. */
.roam-fnauthor { max-width: var(--roam-fn-max); margin: var(--roam-sp-56) auto 0; padding: 0 var(--roam-page-x); }
.roam-fnauthor__inner { display: flex; gap: var(--roam-sp-24); align-items: center; padding: var(--roam-sp-28) var(--roam-sp-32); background: var(--roam-white); border: 1px solid var(--roam-border-tag); border-radius: var(--roam-r-xl); box-shadow: var(--roam-shadow-rest); }
.roam-fnauthor__avatar { flex: 0 0 auto; }
.roam-fnauthor__img { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; }
.roam-fnauthor__kicker { margin: 0 0 var(--roam-sp-2); font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1.5px; text-transform: uppercase; color: var(--roam-gold); }
.roam-fnauthor__name { margin: 0 0 var(--roam-sp-6); font-family: var(--roam-font-display); font-size: var(--roam-fs-22); color: var(--roam-forest); }
.roam-fnauthor__bio { margin: 0; font-size: var(--roam-fs-14); color: var(--roam-text-body); line-height: var(--roam-lh-body); }

/* Related articles — a FULL-WIDTH band, inner content on the article measure. */
.roam-fnrelated { margin: var(--roam-sp-56) 0 0; padding: var(--roam-sp-56) 0; background: var(--roam-cream); width: 100vw; margin-left: calc(50% - 50vw); }
.roam-fnrelated__inner { max-width: var(--roam-fn-max); margin: 0 auto; padding: 0 var(--roam-page-x); }
.roam-fnrelated__h { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: 0 0 var(--roam-sp-24); color: var(--roam-forest); }
.roam-fnrelated__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--roam-sp-24); }

/* Nearby stays — the same rail as the property page's Similar stays. */
.roam-fnnearby { margin: var(--roam-sp-56) 0 var(--roam-sp-64); }
.roam-fnnearby__inner { max-width: var(--roam-fn-max); margin: 0 auto; padding: 0 var(--roam-page-x); }
.roam-fnnearby__h { font-family: var(--roam-font-display); font-size: var(--roam-fs-28); margin: 0 0 var(--roam-sp-6); color: var(--roam-forest); }
.roam-fnnearby .roam-cards__sub { margin-bottom: var(--roam-sp-20); }

@media (max-width: 900px) {
  .roam-fn__wrap { grid-template-columns: 1fr; gap: var(--roam-sp-32); }
  .roam-fn__side { position: static; }
  .roam-fnrelated__grid { grid-template-columns: 1fr; }
  .roam-fnauthor__inner { flex-direction: column; text-align: center; align-items: center; }
}

/* ── Contact page (template-contact.php) ────────────────────────────────────── */
.roam-contact { max-width: 1120px; margin: 0 auto; padding: var(--roam-sp-56) var(--roam-page-x) var(--roam-sp-64); }
.roam-contact__head { max-width: 640px; margin-bottom: var(--roam-sp-40); }
.roam-contact__eyebrow { display: flex; align-items: center; gap: var(--roam-sp-10); margin: 0 0 var(--roam-sp-16); font-size: var(--roam-fs-12); font-weight: var(--roam-fw-semibold); letter-spacing: 1.5px; text-transform: uppercase; color: var(--roam-gold); }
.roam-contact__rule { width: 28px; height: 1px; background: var(--roam-gold); }
.roam-contact__title { margin: 0 0 var(--roam-sp-16); font-family: var(--roam-font-display); font-size: clamp(2rem, 4.5vw, 3rem); line-height: 1.08; color: var(--roam-forest); }
.roam-contact__title em { font-style: italic; color: var(--roam-gold); }
.roam-contact__lede { margin: 0; font-size: var(--roam-fs-16); line-height: var(--roam-lh-snug); color: var(--roam-text-body); }

.roam-contact__grid { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: var(--roam-sp-40); align-items: start; }

/* The form card. */
.roam-contact__form { padding: var(--roam-sp-32); border-radius: var(--roam-r-xl); background: var(--roam-white); box-shadow: var(--roam-shadow-rest); }
.roam-contact__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--roam-sp-16); }
.roam-contact__f { display: block; margin-bottom: var(--roam-sp-16); }
.roam-contact__f > span { display: block; margin-bottom: var(--roam-sp-6); font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1px; text-transform: uppercase; color: var(--roam-text-muted); }
.roam-contact__f input,
.roam-contact__f select,
.roam-contact__f textarea {
  width: 100%;
  padding: var(--roam-sp-12) var(--roam-sp-14);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-sm);
  background: var(--roam-cream);
  font: inherit;
  font-size: var(--roam-fs-15);
  color: var(--roam-text-primary);
}
.roam-contact__f textarea { resize: vertical; min-height: 120px; }
.roam-contact__f input:focus-visible,
.roam-contact__f select:focus-visible,
.roam-contact__f textarea:focus-visible { outline: 2px solid var(--roam-gold); outline-offset: -1px; border-color: var(--roam-gold); }
.roam-contact__submit {
  width: 100%;
  margin-top: var(--roam-sp-8);
  padding: var(--roam-sp-14);
  border: 0;
  border-radius: var(--roam-r-full);
  background: var(--roam-gold);
  color: var(--roam-forest);
  font: inherit;
  font-weight: var(--roam-fw-semibold);
  cursor: pointer;
  transition: background-color 0.2s var(--roam-easing);
}
.roam-contact__submit:hover { background: var(--roam-gold-active); }
.roam-contact__notice { margin: 0 0 var(--roam-sp-16); padding: var(--roam-sp-12) var(--roam-sp-14); border-radius: var(--roam-r-sm); font-size: var(--roam-fs-14); }
.roam-contact__notice--ok { background: color-mix(in srgb, var(--roam-success) 15%, transparent); color: var(--roam-success-active); }
.roam-contact__notice--err { background: color-mix(in srgb, #b3261e 12%, transparent); color: #8c1d18; }

/* The direct-line cards. */
.roam-contact__aside { display: flex; flex-direction: column; gap: var(--roam-sp-16); }
.roam-contact__card { display: flex; align-items: center; gap: var(--roam-sp-14); padding: var(--roam-sp-18) var(--roam-sp-20); border-radius: var(--roam-r-xl); background: var(--roam-white); box-shadow: var(--roam-shadow-rest); text-decoration: none; color: inherit; transition: transform 0.2s var(--roam-easing); }
.roam-contact__card:hover { transform: var(--roam-lift-hover); }
.roam-contact__cardico { flex: 0 0 auto; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: var(--roam-cream); color: var(--roam-forest); }
.roam-contact__cardk { display: block; font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1px; text-transform: uppercase; color: var(--roam-text-muted); }
.roam-contact__cardk--gold { color: var(--roam-gold-lifted); }
.roam-contact__cardv { display: block; font-size: var(--roam-fs-16); font-weight: var(--roam-fw-semibold); color: var(--roam-forest); }
.roam-contact__office { padding: var(--roam-sp-24); border-radius: var(--roam-r-xl); background: linear-gradient(135deg, var(--roam-forest-light) 0%, var(--roam-forest) 100%); color: var(--roam-cream); }
.roam-contact__officeplace { margin: var(--roam-sp-6) 0 var(--roam-sp-8); font-family: var(--roam-font-display); font-size: var(--roam-fs-18); color: var(--roam-white); }
.roam-contact__officenote { margin: 0; font-size: var(--roam-fs-14); line-height: var(--roam-lh-snug); color: rgba(250, 246, 241, 0.72); }

@media (max-width: 820px) {
  .roam-contact__grid { grid-template-columns: 1fr; gap: var(--roam-sp-24); }
}
@media (max-width: 480px) {
  .roam-contact__row { grid-template-columns: 1fr; }
}

/* ── Checkout (template-checkout.php / inc/checkout.php) ─────────────────────── */
.roam-co { max-width: 1080px; margin: 0 auto; padding: var(--roam-sp-36) var(--roam-page-x) var(--roam-sp-64); }
.roam-co__back {
  display: inline-block;
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: 0.04em;
  color: var(--roam-forest);
  text-decoration: none;
}
.roam-co__back:hover { color: var(--roam-gold); }

/* The 1-2-3 progress rail. */
.roam-co__steps {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--roam-sp-12);
  list-style: none; margin: var(--roam-sp-20) 0 var(--roam-sp-32); padding: 0;
}
.roam-co__step { display: flex; align-items: center; gap: var(--roam-sp-8); font-size: var(--roam-fs-13); font-weight: var(--roam-fw-semibold); color: var(--roam-text-primary); }
.roam-co__step.is-todo { color: color-mix(in srgb, var(--roam-forest) 45%, transparent); }
.roam-co__stepn {
  width: 24px; height: 24px; border-radius: var(--roam-r-full);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--roam-fs-12); background: var(--roam-cream-active); color: var(--roam-forest);
}
.roam-co__step.is-current .roam-co__stepn { background: var(--roam-gold); color: var(--roam-forest); }
.roam-co__step.is-done .roam-co__stepn { background: var(--roam-success); color: var(--roam-white); }
.roam-co__steprule { width: 36px; height: 1px; background: var(--roam-cream-active); }

/* Two-column body: form panel + summary. */
.roam-co__grid { display: flex; gap: var(--roam-sp-36); flex-wrap: wrap; align-items: flex-start; }
.roam-co__main { flex: 1 1 480px; }
.roam-co__panel { background: var(--roam-white); border: 1px solid var(--roam-border-tag); border-radius: var(--roam-r-xl); padding: var(--roam-sp-32); }
.roam-co__h { font-family: var(--roam-font-display); font-weight: 700; font-size: var(--roam-fs-28); margin: 0 0 var(--roam-sp-6); }
.roam-co__sub { font-size: var(--roam-fs-13); color: var(--roam-text-secondary); margin: 0 0 var(--roam-sp-24); }

.roam-co__fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--roam-sp-14); }
.roam-co__f { display: flex; flex-direction: column; gap: var(--roam-sp-6); }
.roam-co__f--wide { grid-column: span 2; }
.roam-co__f > span { font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: 1px; text-transform: uppercase; color: var(--roam-text-muted); }
.roam-co__f input,
.roam-co__f textarea {
  width: 100%; padding: var(--roam-sp-12) var(--roam-sp-14);
  border: 1px solid var(--roam-border-tag); border-radius: var(--roam-r-sm);
  background: var(--roam-cream); font: inherit; font-size: var(--roam-fs-14); color: var(--roam-text-primary);
}
.roam-co__f textarea { resize: vertical; min-height: 90px; }
.roam-co__f input:focus-visible,
.roam-co__f textarea:focus-visible { outline: 2px solid var(--roam-gold); outline-offset: -1px; border-color: var(--roam-gold); }
.roam-co__submit {
  width: 100%; margin-top: var(--roam-sp-24); padding: var(--roam-sp-16);
  border: 0; border-radius: var(--roam-r-full);
  background: var(--roam-forest); color: var(--roam-cream);
  font: inherit; font-size: var(--roam-fs-15); font-weight: 700; letter-spacing: 0.05em;
  cursor: pointer; transition: background-color 0.25s var(--roam-easing);
}
.roam-co__submit:hover { background: var(--roam-forest-active, #12211d); }
.roam-co__notice { margin: 0 0 var(--roam-sp-16); padding: var(--roam-sp-12) var(--roam-sp-14); border-radius: var(--roam-r-sm); font-size: var(--roam-fs-14); }
.roam-co__notice--err { background: color-mix(in srgb, #b3261e 12%, transparent); color: #8c1d18; }
#roam-pay-error { margin: var(--roam-sp-16) 0 0; }

/* Native Stripe pay — the Payment Element sits in a bordered card, the mandate
   below it in small plain type (voice §9: boring about the money on purpose). */
.roam-co__paycard {
  margin-top: var(--roam-sp-20);
  padding: var(--roam-sp-16);
  border: 1px solid var(--roam-border-cream, #e5e1db);
  border-radius: var(--roam-r-md, 12px);
  background: var(--roam-white, #fff);
}
.roam-co__mandate {
  margin: var(--roam-sp-14) 0 0;
  font-size: var(--roam-fs-12);
  line-height: var(--roam-lh-snug);
  color: var(--roam-text-secondary);
}

/* Payment / confirmed placeholder panels. */
.roam-co__panel--wait { text-align: center; }
.roam-co__waitico { display: inline-flex; margin-bottom: var(--roam-sp-12); color: var(--roam-gold); }
.roam-co__waitcall { margin: var(--roam-sp-16) 0 0; font-size: var(--roam-fs-14); font-weight: var(--roam-fw-semibold); }
.roam-co__waitcall a { color: var(--roam-forest); }

/* The hand-off pill — the one gold action on the confirm step. */
.roam-co__pay {
  display: inline-flex; align-items: center; justify-content: center;
  margin: var(--roam-sp-24) 0 0; padding: var(--roam-sp-16) var(--roam-sp-36);
  border-radius: var(--roam-r-full);
  background: var(--roam-gold); color: var(--roam-forest);
  font-size: var(--roam-fs-15); font-weight: 700; letter-spacing: 0.03em;
  text-decoration: none; box-shadow: var(--roam-shadow-rest);
  transition: background-color 0.25s var(--roam-easing), transform 0.25s var(--roam-easing);
}
.roam-co__pay:hover { background: var(--roam-gold-lifted); transform: translateY(-1px); }
.roam-co__paysecure { margin: var(--roam-sp-14) 0 0; font-size: var(--roam-fs-12); color: var(--roam-text-secondary); }

/* Inline OwnerRez booking widget — the pay-in-place path. The widget renders as
   a cross-origin iframe, so only its frame is ours to style here: its fields and
   buttons are themed from the CSS box in the widget's OwnerRez settings
   (docs/ownerrez-widget-css.md). This panel is left-aligned, not centred like the
   hand-off fallback. */
.roam-co__panel--widget { text-align: left; }
.roam-co__widget { margin-top: var(--roam-sp-20); }
/* Full-bleed and a floor height so the frame never collapses before the widget
   script measures and grows it. !important overrides the fixed width the embed
   script writes onto the iframe. */
.roam-co__widget iframe {
  width: 100% !important; max-width: 100% !important;
  /* The bare form (no widget.js auto-resize) measured ~707px with the guest
     fields open; floor a little above that so it never clips, and it can still
     grow if a taller state (validation errors, agreement text) appears. */
  min-height: 760px; border: 0; display: block;
}
.roam-co__panel--widget .roam-co__paysecure { margin-top: var(--roam-sp-16); text-align: center; }

/* Trust row under the panel. */
.roam-co__trust { display: flex; gap: var(--roam-sp-20); flex-wrap: wrap; margin: var(--roam-sp-18) 0 0; padding: 0 var(--roam-sp-6); list-style: none; }
.roam-co__trust li { display: flex; align-items: center; gap: var(--roam-sp-8); font-size: var(--roam-fs-12); color: var(--roam-text-secondary); }

/* Order summary card. */
.roam-co__summary { flex: 1 1 320px; max-width: 400px; background: var(--roam-white); border-radius: var(--roam-r-xl); overflow: hidden; border: 1px solid var(--roam-border-tag); box-shadow: var(--roam-shadow-rest); }
.roam-co__sumhead { width: 100%; height: 170px; background-size: cover; background-position: center; background-color: var(--roam-cream-active); filter: var(--roam-photo-filter, saturate(0.9)); }
.roam-co__sumbody { padding: var(--roam-sp-24); }
.roam-co__sumtitle { font-family: var(--roam-font-display); font-weight: 700; font-size: var(--roam-fs-20); margin: 0; }
.roam-co__sumplace { font-size: var(--roam-fs-13); color: var(--roam-text-secondary); margin: var(--roam-sp-4) 0 var(--roam-sp-16); }
.roam-co__row { display: flex; justify-content: space-between; gap: var(--roam-sp-12); font-size: var(--roam-fs-13); padding: var(--roam-sp-8) 0; border-top: 1px solid var(--roam-border-tag); }
.roam-co__rowk { color: var(--roam-text-secondary); }
.roam-co__rowv { font-weight: var(--roam-fw-semibold); }
.roam-co__row--discount { color: var(--roam-success); }
.roam-co__row--discount .roam-co__rowk { color: var(--roam-success); }
.roam-co__row--total { font-size: var(--roam-fs-15); font-weight: 700; padding-top: var(--roam-sp-12); border-top: 1px solid var(--roam-cream-active); }
.roam-co__row--total .roam-co__rowk { color: var(--roam-text-primary); }
.roam-co__saved { margin-top: var(--roam-sp-14); padding: var(--roam-sp-8) var(--roam-sp-12); border-radius: var(--roam-r-sm); text-align: center; font-size: var(--roam-fs-12); font-weight: var(--roam-fw-semibold); background: color-mix(in srgb, var(--roam-success) 8%, transparent); color: var(--roam-success); }
.roam-co__sumnote { margin: var(--roam-sp-12) 0 0; font-size: var(--roam-fs-13); color: var(--roam-text-secondary); }

/* Empty state — no home/dates in the URL. */
.roam-co--empty { text-align: center; padding-top: var(--roam-sp-64); padding-bottom: var(--roam-sp-64); }
.roam-co__emptyh { font-family: var(--roam-font-display); font-weight: 700; font-size: var(--roam-fs-28); margin: 0 0 var(--roam-sp-12); }
.roam-co__emptyp { font-size: var(--roam-fs-15); color: var(--roam-text-secondary); margin: 0 auto var(--roam-sp-24); max-width: 420px; }
.roam-co__emptycta { display: inline-block; padding: var(--roam-sp-14) var(--roam-sp-28); border-radius: var(--roam-r-full); background: var(--roam-gold); color: var(--roam-forest); font-weight: var(--roam-fw-semibold); text-decoration: none; }
.roam-co__emptycta:hover { background: var(--roam-gold-active); }

@media (max-width: 880px) {
  .roam-co__summary { max-width: none; order: -1; width: 100%; }
}
@media (max-width: 480px) {
  .roam-co__fields { grid-template-columns: 1fr; }
  .roam-co__f--wide { grid-column: span 1; }
}

/* The Field Notes index (home.php — the posts page). */
.roam-notes-index__head { max-width: 760px; }
.roam-notes-index__h1 {
  font-family: var(--roam-font-display);
  font-size: var(--roam-type-title-size);
  line-height: var(--roam-type-title-lh);
  margin: var(--roam-flow-coupled) 0 var(--roam-flow-related);
}
.roam-notes-index__lede { margin: 0; font-size: var(--roam-fs-18); line-height: var(--roam-lh-snug); color: var(--roam-text-body); }
.roam-notes-index__grid { margin-top: var(--roam-sp-28); }
.roam-notes-index__empty { color: var(--roam-text-muted); }

/* The index toolbar — the collection archive's control bar, reduced for a
   reading index: a search field (sized like the sort control) on the left, the
   Filters button and the sort menu on the right. Reuses .roam-sortsel,
   .roam-filterbtn and the .roam-filters modal wholesale; only the bar shell and
   the search field are new here. (roam-notes-filter.js) */
.roam-notesbar {
  position: sticky;
  top: var(--roam-hdr-total, var(--roam-header-h, 0px));
  z-index: 20;
  margin-top: var(--roam-sp-28);
  padding: var(--roam-sp-12) 0;
  background: var(--roam-cream);
  border-top: 1px solid var(--roam-border-cream);
  border-bottom: 1px solid var(--roam-border-cream);
}
.roam-notesbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-16);
}
.roam-notesbar__tools {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--roam-sp-12);
}
/* The search field, sized to match .roam-sortsel (same vertical padding, border,
   radius and type) so it reads as the sort control's sibling. */
.roam-notesearch {
  flex: 1 1 auto;
  max-width: 360px;
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-8);
  padding: var(--roam-sp-8) var(--roam-sp-16);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  background: var(--roam-white);
  color: var(--roam-forest);
  transition: border-color 0.2s var(--roam-easing), box-shadow 0.2s var(--roam-easing);
}
.roam-notesearch:focus-within {
  border-color: var(--roam-forest);
  box-shadow: 0 0 0 3px rgba(91, 143, 168, 0.22);
}
.roam-notesearch svg { flex: 0 0 auto; color: var(--roam-forest-soft); }
.roam-notesearch input {
  width: 100%;
  border: 0;
  padding: 0;
  background: transparent;
  font: inherit;
  font-size: var(--roam-fs-14);
  color: var(--roam-text-primary);
}
.roam-notesearch input:focus { outline: none; }
.roam-notesearch input::placeholder { color: var(--roam-text-muted); }
.roam-notesearch input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* The Filters button's active state + count badge on the index — a light
   outlined count, not a filled pill, so it reads as a quiet tally. */
.roam-filterbtn.is-active { border-color: var(--roam-forest-soft); }
.roam-filterbtn__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--roam-r-full);
  background: transparent;
  border: 1px solid var(--roam-border-tag);
  color: var(--roam-forest-soft);
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-medium);
  line-height: 1;
}

.roam-notes-index__noresults {
  margin: var(--roam-sp-40) 0;
  text-align: center;
  font-size: var(--roam-fs-16);
  color: var(--roam-text-muted);
}

/* Filtered-out cards. display:none drops them from the grid so survivors reflow
   into clean rows with no gaps. */
.roam-note-card--hidden { display: none !important; }

/* Lock the page behind the open Filters modal. */
.roam-noscroll { overflow: hidden; }

@media (max-width: 767px) {
  /* One line: the search shrinks to fill what's left, the Filters button holds.
     The sort menu moves into the modal (.roam-fset--sort) and the total count is
     dropped for room — matching the collection bar's own phone behaviour. */
  .roam-notesearch { flex: 1 1 auto; max-width: none; min-width: 0; }
  .roam-notesearch input { min-width: 0; }
  .roam-notesbar__tools .roam-sortsel { display: none; }
  .roam-notesbar__tools .roam-results__count { display: none; }
}

/* ── Neighborhood highlights — the four-card grid ────────────────────────── */
.roam-hood {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--roam-sp-20);
}
.roam-hood__card {
  border: 1px solid var(--roam-border-cream);
  border-radius: var(--roam-r-lg);
  overflow: hidden;
  background: var(--roam-white);
}
.roam-hood__top {
  display: grid;
  place-items: center;
  aspect-ratio: 3 / 2;
  background: color-mix(in srgb, var(--roam-forest) 8%, var(--roam-sand));
  color: color-mix(in srgb, var(--roam-forest) 45%, transparent);
}
.roam-hood__top--photo { background-size: cover; background-position: center; }
.roam-hood__card--link { display: block; text-decoration: none; color: inherit; transition: transform 0.2s var(--roam-easing), box-shadow 0.2s var(--roam-easing); }
.roam-hood__card--link:hover { transform: var(--roam-lift-hover, translateY(-3px)); box-shadow: var(--roam-shadow-hover, 0 10px 24px rgba(27,43,39,0.12)); }
.roam-hood__body { padding: var(--roam-sp-16); }
.roam-hood__kind { display: block; margin-bottom: var(--roam-sp-4); font-size: var(--roam-fs-11); font-weight: var(--roam-fw-semibold); letter-spacing: var(--roam-ls-sm); text-transform: uppercase; color: var(--roam-text-muted); }
.roam-hood__name { font-family: var(--roam-font-display); font-size: var(--roam-fs-16); margin: 0 0 var(--roam-sp-4); }
.roam-hood__time {
  display: block;
  margin-bottom: var(--roam-sp-6);
  font-size: var(--roam-fs-12);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
  color: var(--roam-gold);
}
.roam-hood__blurb { margin: 0; font-size: var(--roam-fs-13); line-height: var(--roam-lh-snug); color: var(--roam-text-body); }

/* ── Skeleton shimmers ───────────────────────────────────────────────────────
   A placeholder that says "content is coming" without inventing any: a soft
   ground with a light sweep across it. Stands in for the neighbourhood cards
   and the getting-around line until real data arrives. */
.roam-skel {
  position: relative;
  overflow: hidden;
  background: color-mix(in srgb, var(--roam-forest) 8%, var(--roam-sand));
}
.roam-skel::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  animation: roam-shimmer 1.5s ease-in-out infinite;
}
@keyframes roam-shimmer { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .roam-skel::after { animation: none; } }
/* Text/label bars — the media tile keeps the card's own square corners. */
.roam-skel--title,
.roam-skel--meta,
.roam-skel--text {
  display: block;
  border-radius: var(--roam-r-sm);
  margin-bottom: var(--roam-sp-8);
}
.roam-skel--title { height: 15px; width: 62%; }
.roam-skel--meta  { height: 11px; width: 32%; }
.roam-skel--text  { height: 12px; width: 100%; }
.roam-skel--short { width: 70%; }
.roam-hood__body .roam-skel:last-child,
.roam-skel-lines .roam-skel:last-child { margin-bottom: 0; }
/* The getting-around lines sit a touch wider, like a paragraph. */
.roam-skel-lines { max-width: 60ch; }
.roam-skel-lines .roam-skel--text { height: 13px; }

/* ── Collection ──────────────────────────────────────────────────────────── */
.roam-arch__head { padding-block: var(--roam-sp-40) var(--roam-sp-24); }
.roam-arch__title {
  font-size: clamp(30px, 4vw, var(--roam-fs-40));
  margin: 0;
  padding-top: var(--roam-sp-20);
  position: relative;
}
.roam-arch__title::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 32px;
  height: 2px;
  background: var(--roam-gold);
}
.roam-arch__count { margin: var(--roam-sp-8) 0 0; color: var(--roam-text-muted); }
.roam-pagination { padding-block: var(--roam-sp-40); }
.roam-pagination .page-numbers { padding: var(--roam-sp-8) var(--roam-sp-12); text-decoration: none; }
.roam-pagination .current { font-weight: var(--roam-fw-semibold); text-decoration: underline; }

/* ═══ SEARCH PAGE — category rail, toolbar, four views ════════════════════════ */
/* TWO STICKY BARS, STACKED UNDER THE HEADER. The category rail sits first, at the
   header's measured bottom; the results toolbar sits flush under the rail. Both
   are opaque cream so the cards scroll away underneath them cleanly — the same
   trick the property page's .roam-sticky bar uses, sized off the same measured
   --roam-hdr-total so nothing drifts when the header's height changes.

   The results bar is ONE HEIGHT with the property navbar (--roam-sticky-h); the
   rail is a touch taller so the icon-over-label tabs have room to breathe. */
:root { --roam-catrail-h: 66px; }
.roam-search { display: block; }

/* ── The category rail ─────────────────────────────────────────────────────
   A cream sub-bar under the header: the categories scroll on a narrow screen,
   the Filters button holds the right. Sticky, so it stays reachable as the
   results scroll. */
.roam-catrail {
  position: sticky;
  top: var(--roam-hdr-total, var(--roam-header-h));
  z-index: 100;
  height: var(--roam-catrail-h);
  border-bottom: 1px solid var(--roam-border-cream);
  background: var(--roam-cream);
}
/* The bar is full-bleed cream; its content rides the same measure as the card
   body below, so tabs and the Filters button line up with the grid's edges. */
.roam-catrail__inner {
  display: flex;
  align-items: stretch;
  gap: var(--roam-sp-24);
  height: 100%;
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding-inline: var(--roam-page-x);
}
.roam-catrail__rail {
  display: flex;
  align-items: stretch;
  gap: var(--roam-sp-8);
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.roam-catrail__rail::-webkit-scrollbar { display: none; }
/* The tabs fill the rail's height so the underline lands on the bar's bottom
   edge, and the icon-over-label pair centres in what's left. */
.roam-cat {
  flex: 0 0 auto;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--roam-sp-6);
  padding: 0 var(--roam-sp-8);
  border-bottom: 2px solid transparent;
  color: var(--roam-text-muted);
  text-decoration: none;
  font-size: var(--roam-fs-12);
  white-space: nowrap;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.roam-cat__ico { width: 24px; height: 24px; }
/* Property-type icons (roam_type_icon) — solid currentColor marks; keep them at
   the rail's 24px and let them take the tab colour, so home-type and amenity
   tabs read as one consistent, filled icon set. */
.roam-type__ico { width: 24px; height: 24px; color: inherit; }
/* The amenity tabs draw from the established amenity icon set (.roam-amen__ico,
   18px and forest-coloured in its native contexts). In the rail, size them up to
   the 24px home-type mark and let them inherit the tab's own colour — muted grey,
   forest on hover/active — so they read as the same row rather than a darker
   second colour. */
.roam-cat--amen .roam-amen__ico { width: 24px; height: 24px; color: inherit; }
/* A hairline divider between the home-type tabs and the amenity tabs (Hot Tub,
   Sauna, King bed, Fireplace) — centred in the taller rail, not stretched. */
.roam-cat__sep { flex: 0 0 auto; align-self: center; width: 1px; height: 24px; margin: 0 var(--roam-sp-6); background: var(--roam-border-tag); }
/* THE ROLLOVER AND UNDERLINE OF THE PROPERTY NAVBAR (.roam-sticky__link): hover
   moves the text to forest and stops there; only the active tab carries the
   gold rule, so the mark means one thing — where you are. */
.roam-cat:hover { color: var(--roam-forest); }
.roam-cat.is-active { color: var(--roam-forest); border-bottom-color: var(--roam-gold); font-weight: var(--roam-fw-semibold); }
/* The Filters button — a bordered pill on the right of the rail. Centred in the
   taller rail rather than stretched to its height. */
.roam-filterbtn {
  flex: 0 0 auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-8);
  padding: var(--roam-sp-10) var(--roam-sp-16);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  background: var(--roam-white);
  color: var(--roam-forest);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  cursor: pointer;
}
.roam-filterbtn:hover { background: var(--roam-cream-active); }

/* ── The body measure ─────────────────────────────────────────────────────── */
.roam-search__body {
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding: var(--roam-sp-20) var(--roam-page-x) var(--roam-sp-56);
}
/* The map views break the measure so the map can fill the window — the two
   sticky bars widen with it so their content stays flush with the body. */
.roam-view-halfmap .roam-search__body,
.roam-view-fullmap .roam-search__body,
.roam-view-halfmap .roam-catrail__inner,
.roam-view-fullmap .roam-catrail__inner,
.roam-view-halfmap .roam-results__inner,
.roam-view-fullmap .roam-results__inner { max-width: none; }

/* ── The results toolbar ──────────────────────────────────────────────────── */
/* Sticky under the category rail, at the same height as the property navbar. It
   is a full-bleed sibling of the rail (moved out of the measured body in the
   template) so the two bars stack flush and the cards pass under an opaque edge. */
.roam-results__bar {
  position: sticky;
  top: calc(var(--roam-hdr-total, var(--roam-header-h)) + var(--roam-catrail-h));
  z-index: 99;
  height: var(--roam-sticky-h);
  border-bottom: 1px solid var(--roam-border-cream);
  background: var(--roam-cream);
}
/* Same measure as the category rail and the card body — the count sits over the
   grid's left edge, the sort + view switcher over its right. */
.roam-results__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-16);
  height: 100%;
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding-inline: var(--roam-page-x);
}
.roam-results__count { margin: 0; font-size: var(--roam-fs-15); color: var(--roam-text-secondary); }
.roam-results__tools { display: flex; align-items: center; gap: var(--roam-sp-12); }
/* Failed-search banner — a full-bleed red strip flush under the toolbar. Rendered
   only when the live availability check errored; a working search shows nothing. */
.roam-results__alert { background: var(--roam-error); color: var(--roam-white); }
.roam-results__alertinner {
  max-width: var(--roam-shell);
  margin-inline: auto;
  padding: var(--roam-sp-10) var(--roam-page-x);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--roam-sp-4) var(--roam-sp-10);
  font-size: var(--roam-fs-13);
  line-height: var(--roam-lh-snug);
}
.roam-results__alerttext { font-weight: var(--roam-fw-semibold); }
.roam-results__alertdebug { font-weight: var(--roam-fw-normal); opacity: 0.82; }
.roam-sortsel select {
  appearance: none;
  padding: var(--roam-sp-8) var(--roam-sp-32) var(--roam-sp-8) var(--roam-sp-12);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  background: var(--roam-white) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%231B2B27' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat right var(--roam-sp-12) center;
  color: var(--roam-forest);
  font: inherit;
  font-size: var(--roam-fs-14);
  cursor: pointer;
}
/* The four-view switcher — icon buttons, the active one filled. */
.roam-viewbar { display: inline-flex; gap: var(--roam-sp-4); padding: var(--roam-sp-4); border: 1px solid var(--roam-border-tag); border-radius: var(--roam-r-full); background: var(--roam-white); }
.roam-viewbtn {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--roam-forest);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.roam-viewbtn:hover { background: var(--roam-cream-active); }
.roam-viewbtn.is-active { background: var(--roam-forest); color: var(--roam-cream); }
.roam-viewbtn__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ── The results area + views ─────────────────────────────────────────────── */
.roam-results { position: relative; }
.roam-results__map {
  display: none;
  place-items: center;
  background: color-mix(in srgb, var(--roam-forest) 8%, var(--roam-sand));
  border-radius: var(--roam-r-lg);
}
.roam-results__mapnote { margin: 0; color: var(--roam-text-muted); font-size: var(--roam-fs-14); }
.roam-results__empty { padding-block: var(--roam-sp-40); color: var(--roam-text-secondary); }

/* GRID (default): the shared card grid, three-up. */
.roam-cards--arch { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--roam-sp-28); }
/* The search grid is capped at three across on desktop rather than auto-filling
   to four — twelve results then read as 3×4, and each card is wide enough for
   the image and its copy to breathe. Below 960 it falls back to the auto-fill. */
@media (min-width: 960px) {
  .roam-view-grid .roam-cards--arch { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* LIST: one column of wide horizontal cards. */
.roam-view-list .roam-cards--arch { grid-template-columns: 1fr; gap: var(--roam-sp-20); }
@media (min-width: 720px) {
  .roam-view-list .roam-card__link { grid-template-columns: 300px 1fr; grid-template-rows: auto; }
  .roam-view-list .roam-card__media { grid-row: 1; grid-column: 1; height: 100%; }
  .roam-view-list .roam-card__body { grid-row: 1; grid-column: 2; align-content: center; }
  /* The horizontal card drops the vertical subgrid — it is one row, two columns. */
  .roam-view-list .roam-card,
  .roam-view-list .roam-card__link,
  .roam-view-list .roam-card__body { grid-template-rows: none; }
  .roam-view-list .roam-card { grid-row: auto; }
}

/* HALF MAP: results scroll on the left, a sticky map holds the right. */
.roam-view-halfmap .roam-results { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 46%); gap: var(--roam-sp-24); align-items: start; }
.roam-view-halfmap .roam-cards--arch { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--roam-sp-20); }
.roam-view-halfmap .roam-results__map {
  display: grid;
  position: sticky;
  top: calc(var(--roam-header-h) + var(--roam-sp-16));
  height: calc(100vh - var(--roam-header-h) - var(--roam-sp-40));
}

/* FULL MAP: the map takes the window; the list steps aside. */
.roam-view-fullmap .roam-results__list { display: none; }
.roam-view-fullmap .roam-results__map {
  display: grid;
  height: calc(100vh - var(--roam-header-h) - 160px);
  min-height: 420px;
}

@media (max-width: 860px) {
  /* No side-by-side map on a narrow screen — half map falls back to a stacked
     map above the list. */
  .roam-view-halfmap .roam-results { grid-template-columns: 1fr; }
  .roam-view-halfmap .roam-results__map { position: static; height: 320px; order: -1; }
}

@media (max-width: 767px) {
  /* On a phone the forest search header scrolls away with the page instead of
     staying docked, and the two nav bars take the top of the viewport — the
     category rail flush at 0, the results toolbar directly under it. Their `top`
     otherwise offsets by --roam-hdr-total, which is right only while the header
     is still stuck; once it scrolls off that offset is a strip of the header
     left showing above the bars, which is the green sliver that was reported.
     (Desktop keeps the header docked at top:0, so those offsets stay correct.) */
  body.roam-searchpage .roam-head { position: static; }
  .roam-catrail { top: 0; }
  .roam-results__bar { top: var(--roam-catrail-h); }
}

/* ══ THE FILTERS MODAL ══════════════════════════════════════════════════════
   A GET form in a centred dialog: header, a scrolling body of filter sets, and
   a sticky Clear all / Show homes footer. Opened by roam-collection.js. */
.roam-filters {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: var(--roam-sp-24);
}
.roam-filters[hidden] { display: none; }
.roam-filters__scrim {
  position: absolute;
  inset: 0;
  background: rgba(27, 43, 39, 0.5);
}
.roam-filters__dialog {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 780px;
  max-height: min(90vh, 900px);
  background: var(--roam-white);
  border-radius: var(--roam-r-xl);
  box-shadow: 0 30px 80px rgba(27, 43, 39, 0.35);
  overflow: hidden;
}
.roam-filters__form { display: flex; flex-direction: column; min-height: 0; }
/* Header + footer hold their ground; only the body scrolls. */
.roam-filters__head,
.roam-filters__foot { flex: 0 0 auto; }
.roam-filters__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--roam-sp-20) var(--roam-sp-32);
  border-bottom: 1px solid var(--roam-border-cream);
}
.roam-filters__title { margin: 0; font-family: var(--roam-font-display); font-size: var(--roam-fs-26, 1.6rem); font-weight: var(--roam-fw-bold, 700); color: var(--roam-text-primary); }
/* A bare, stylized X — no circle chip (client 2026-08-03). */
.roam-filters__x {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin-right: calc(var(--roam-sp-8) * -1);
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--roam-text-primary);
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.roam-filters__x:hover { opacity: 0.6; }
.roam-filters__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 var(--roam-sp-32);
}
/* Each filter set is a row with a hairline above it, save the first. */
.roam-fset { padding: var(--roam-sp-28) 0; border-top: 1px solid var(--roam-border-cream); }
.roam-fset:first-child { border-top: 0; }
.roam-fset__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--roam-sp-16); }
.roam-fset__h { margin: 0; font-family: var(--roam-font-display); font-size: var(--roam-fs-22, 1.35rem); font-weight: var(--roam-fw-bold, 700); color: var(--roam-text-primary); }
.roam-fset__sub { margin: var(--roam-sp-6) 0 var(--roam-sp-16); color: var(--roam-text-secondary); font-size: var(--roam-fs-15); }
/* The Sort set lives in the modal only on a phone — on the desktop bar the
   toolbar menu holds it, so this is hidden there to avoid two sort controls. */
.roam-fset--sort { display: none; }
.roam-sortsel--block { display: block; margin-top: var(--roam-sp-4); }
.roam-sortsel--block select { width: 100%; }
@media (max-width: 767px) {
  .roam-fset--sort { display: block; }
  .roam-results__tools .roam-sortsel { display: none; }
}

/* ── Nightly rate slider ─────────────────────────────────────────────────── */
.roam-rate__val { color: var(--roam-gold); font-weight: var(--roam-fw-semibold); font-size: var(--roam-fs-17, 1.05rem); }
.roam-rate__slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  margin: var(--roam-sp-20) 0 var(--roam-sp-12);
  border-radius: var(--roam-r-full);
  background: var(--roam-gold);
  cursor: pointer;
}
.roam-rate__slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--roam-white);
  border: 2px solid var(--roam-gold);
  box-shadow: 0 2px 6px rgba(27, 43, 39, 0.25);
}
.roam-rate__slider::-moz-range-thumb {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--roam-white);
  border: 2px solid var(--roam-gold);
  box-shadow: 0 2px 6px rgba(27, 43, 39, 0.25);
}
.roam-rate__ends { display: flex; justify-content: space-between; color: var(--roam-text-secondary); font-size: var(--roam-fs-15); }

/* ── Home type chips (multi-select) ──────────────────────────────────────── */
.roam-chipset { display: flex; flex-wrap: wrap; gap: var(--roam-sp-12); }
.roam-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-10);
  padding: var(--roam-sp-12) var(--roam-sp-20);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  color: var(--roam-text-primary);
  font-size: var(--roam-fs-15);
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.roam-chip svg { width: 22px; height: 22px; }
.roam-chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.roam-chip:hover { border-color: var(--roam-forest); }
.roam-chip.is-on { border-color: var(--roam-forest); background: var(--roam-cream); box-shadow: inset 0 0 0 1px var(--roam-forest); }

/* ── Counted pill rows ───────────────────────────────────────────────────── */
.roam-pillrow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--roam-sp-10); }
.roam-pill {
  display: inline-grid;
  place-items: center;
  /* 40px to match the home-type chips (client 2026-08-03) — they read as one
     family of controls rather than the pills towering over the chips at 56. */
  min-width: 44px;
  height: 40px;
  padding: 0 var(--roam-sp-16);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  color: var(--roam-text-primary);
  font-size: var(--roam-fs-15);
  font-weight: var(--roam-fw-medium, 500);
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}
/* The number steps are circles at the chip height; "Any" keeps its pill width. */
.roam-pill--num { width: 40px; padding: 0; }
.roam-pill input { position: absolute; opacity: 0; width: 0; height: 0; }
.roam-pill:hover { border-color: var(--roam-forest); }
.roam-pill.is-on { background: var(--roam-forest); border-color: var(--roam-forest); color: var(--roam-cream); }

/* ── Standouts ───────────────────────────────────────────────────────────── */
.roam-standouts { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--roam-sp-16); }
/* While Luxe is hidden, Guest favourite is the only card — let it span the row
   rather than sit in a lonely half. Two cards each take a column as normal. */
.roam-standout:only-child { grid-column: 1 / -1; }
.roam-standout {
  display: grid;
  gap: var(--roam-sp-8);
  padding: var(--roam-sp-20);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.roam-standout input { position: absolute; opacity: 0; width: 0; height: 0; }
.roam-standout:hover { border-color: var(--roam-forest); }
.roam-standout.is-on { border-color: var(--roam-forest); box-shadow: inset 0 0 0 1px var(--roam-forest); }
/* Luxe carries a dark sparkle on a light disc; Guest favourite overrides that
   to a pair of gold laurels with no disc, the badge used across the site. */
.roam-standout__ico { display: inline-grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: var(--roam-cream); color: var(--roam-text-primary); }
.roam-standout__ico--fav { width: auto; height: 30px; border-radius: 0; background: transparent; color: var(--roam-gold); display: inline-flex; align-items: center; gap: 2px; }
.roam-standout__ico .roam-laurel { height: 30px; width: auto; }
/* Guest favourite: the two laurels flank the words, the badge used site-wide,
   rather than sitting above them. */
.roam-standout__fav { display: inline-flex; align-items: center; gap: var(--roam-sp-8); }
.roam-standout__fav .roam-laurel { height: 26px; width: auto; flex: 0 0 auto; color: var(--roam-gold); }
.roam-standout__t { font-weight: var(--roam-fw-semibold); color: var(--roam-text-primary); font-size: var(--roam-fs-17, 1.05rem); }
.roam-standout__s { color: var(--roam-text-secondary); font-size: var(--roam-fs-14); }

/* ── Amenities checklist ─────────────────────────────────────────────────── */
/* Grouped by OwnerRez category, mirroring the property page's amenity section:
   a small caps-y heading per group, then the two-column checklist. */
.roam-amengroups { display: flex; flex-direction: column; gap: var(--roam-sp-24); }
.roam-amengroup__h {
  margin: 0 0 var(--roam-sp-12);
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-16);
  font-weight: 600;
  color: var(--roam-text-primary);
}
.roam-amencheck { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--roam-sp-16) var(--roam-sp-24); }
.roam-check { display: inline-flex; align-items: center; gap: var(--roam-sp-12); cursor: pointer; font-size: var(--roam-fs-16); color: var(--roam-text-primary); }
.roam-check input { position: absolute; opacity: 0; width: 0; height: 0; }
.roam-check__box {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border: 1.5px solid var(--roam-border-tag);
  border-radius: var(--roam-r-sm);
  background: var(--roam-white);
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.roam-check:hover .roam-check__box { border-color: var(--roam-forest); }
.roam-check input:checked + .roam-check__box {
  border-color: var(--roam-forest);
  background: var(--roam-forest) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23FAF6F1' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") no-repeat center;
}
.roam-check input:focus-visible + .roam-check__box { outline: 2px solid var(--roam-forest); outline-offset: 2px; }
/* Amenity checkboxes carry the amenity's own icon between the box and the label. */
.roam-check__ico { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--roam-forest); }
.roam-check__ico svg { width: 18px; height: 18px; }

/* ── Footer ──────────────────────────────────────────────────────────────── */
.roam-filters__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--roam-sp-16) var(--roam-sp-32);
  border-top: 1px solid var(--roam-border-cream);
}
.roam-filters__clear {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--roam-text-primary);
  font: inherit;
  font-weight: var(--roam-fw-semibold);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.roam-filters__submit {
  padding: var(--roam-sp-14) var(--roam-sp-32);
  border: 0;
  border-radius: var(--roam-r-full);
  background: var(--roam-gold);
  color: var(--roam-text-primary);
  font: inherit;
  font-weight: var(--roam-fw-semibold);
  cursor: pointer;
  transition: background-color 0.15s ease;
}
.roam-filters__submit:hover { background: var(--roam-gold-active); }

/* Keyboard focus for the label-wrapped inputs lands a ring on the control. */
.roam-chip input:focus-visible ~ span,
.roam-pill input:focus-visible ~ span { outline: 2px solid var(--roam-forest); outline-offset: 4px; border-radius: var(--roam-r-full); }

@media (max-width: 640px) {
  .roam-filters { padding: 0; }
  .roam-filters__dialog { max-width: none; max-height: 100vh; height: 100vh; border-radius: 0; }
  .roam-filters__head, .roam-filters__body, .roam-filters__foot { padding-inline: var(--roam-sp-20); }
  .roam-standouts, .roam-amencheck { grid-template-columns: 1fr; }
}

/* The page must not scroll behind an open dialog. */
body.roam-filters-open { overflow: hidden; }

/* ── Collection filter ───────────────────────────────────────────────────── */
.roam-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--roam-sp-16);
  padding: var(--roam-sp-20);
  margin-bottom: var(--roam-sp-24);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
}
.roam-filter__f { margin: 0; display: flex; flex-direction: column; gap: var(--roam-sp-4); }
.roam-filter__f label {
  font-size: var(--roam-fs-12);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
  color: var(--roam-text-muted);
}
.roam-filter__f input,
.roam-filter__f select {
  padding: var(--roam-sp-8) var(--roam-sp-10);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-sm);
  font: inherit;
  font-size: var(--roam-fs-15);
  background: var(--roam-white);
  /* 44px, the smallest reliable touch target — a date field people miss is a
     search nobody runs. */
  min-height: 44px;
}
.roam-filter__f--check { flex-direction: row; align-items: center; }
.roam-filter__f--check label {
  flex-direction: row;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--roam-fs-15);
  color: var(--roam-text-body);
  display: flex;
  align-items: center;
  gap: var(--roam-sp-8);
  min-height: 44px;
}
.roam-filter__go { margin: 0; }
.roam-note--warn { color: var(--roam-warning); }

/*
 * get_search_form() — WordPress's own markup, which appears on the 404 and on
 * an empty search. Left alone it renders a browser-default input beside a
 * black submit button, and it was the only control on the site that looked
 * like it came from a different design. The class names are core's, so this is
 * the one block here that cannot use the roam- prefix.
 */
.search-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--roam-sp-10);
  align-items: center;
  max-width: 560px;
}
.search-form label { flex: 1 1 240px; margin: 0; }
.search-field {
  width: 100%;
  min-height: 44px;
  padding: var(--roam-sp-8) var(--roam-sp-14);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  background: var(--roam-white);
  font: inherit;
  font-size: var(--roam-fs-15);
  color: var(--roam-text-primary);
}
.search-field:focus-visible { outline: 2px solid var(--roam-gold); outline-offset: 1px; }
.search-submit {
  min-height: 44px;
  padding: 0 var(--roam-sp-28);
  border: 1px solid var(--roam-forest);
  border-radius: var(--roam-r-full);
  background: var(--roam-forest);
  color: var(--roam-cream);
  font: inherit;
  font-size: var(--roam-fs-15);
  cursor: pointer;
  transition: background 0.3s var(--roam-easing), color 0.3s var(--roam-easing);
}
.search-submit:hover,
.search-submit:focus-visible { background: var(--roam-gold); border-color: var(--roam-gold); color: var(--roam-forest); }

@media (max-width: 600px) {
  .roam-filter__f,
  .roam-filter__go { flex: 1 1 100%; }
}



/* What the nightly figure does and does not cover, under the number it
   qualifies. Smaller and quieter than the rate itself — it is a caveat, not a
   second headline. */
.roam-rate__notes {
  margin-top: var(--roam-sp-12);
  font-size: var(--roam-fs-13);
  color: var(--roam-text-secondary);
}
.roam-rate__notes p { margin: 0 0 var(--roam-sp-6); }
.roam-rate__notes p:last-child { margin-bottom: 0; }

/* =============================================================================
   AVAILABILITY CALENDAR
   ========================================================================== */

/* Three months side by side where there is room, stacked where there is not.
   240px is the narrowest a seven-column grid can be before the numbers start
   touching. */
.roam-cal {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  /* Wide column gap between months, tight row gap so the legend/clear footer
     sits just beneath the last week rather than a month-gap below it. */
  column-gap: var(--roam-sp-32);
  row-gap: var(--roam-sp-16);
}
.roam-cal__title {
  font-size: var(--roam-fs-16);
  margin: 0 0 var(--roam-sp-12);
}
/* ── ONE calendar rule set, shared by the on-page calendar and the picker modal.
   Both emit the same markup — a .roam-cal__dow weekday row and a .roam-cal__grid
   of .roam-cal__cell days — so there is a single source of truth for sizing,
   spacing and colour. There is no second .roam-cal__day / modal-cell block. */
.roam-cal__dow {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  margin-bottom: 4px;
}
/* A clean two-letter weekday label, centred over its column (no letter-spacing,
   which pushed a short label off centre; no uppercase). */
.roam-cal__dow span {
  text-align: center;
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  color: rgba(27, 43, 39, 0.45);
  padding: var(--roam-sp-4) 0;
}
.roam-cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
/* The day cell: a white square with a softly rounded corner and a hairline
   border — NOT a pill. */
.roam-cal__cell {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--roam-r-sm);
  font-size: var(--roam-fs-13);
  font-variant-numeric: tabular-nums;
  background: var(--roam-white);
  border: 1px solid var(--roam-border-cream);
  color: var(--roam-forest);
  cursor: pointer;
}
/* Selectable days are buttons on the on-page calendar and spans in the modal —
   strip the button chrome so both read as the same plain cell. */
button.roam-cal__cell {
  font: inherit;
  padding: 0;
  -webkit-appearance: none;
  appearance: none;
}
/* Hover only on clickable cells — never on a blank pad, a past day or a booked
   night — so the gold cue means "you can pick this". */
.roam-cal__cell:not(.is-off):not(.is-past):not(.roam-cal__cell--blank):hover {
  border-color: var(--roam-gold);
}
.roam-cal__cell--blank { background: none; border-color: transparent; cursor: default; }
.roam-cal__cell.is-past {
  background: transparent;
  border-color: transparent;
  color: var(--roam-text-light);
  cursor: default;
}
.roam-cal__cell.is-off { cursor: default; }

/* ── Shared calendar states (picker cells + on-page days, identical) ─────────
   Colour alone fails in sunlight, greyscale, and for the ~8% of men who cannot
   separate these tints — so a booked night is struck through as well as tinted,
   and every state also carries its meaning in the accessible name. */
/* Booked / unavailable: the greige fill, struck through. */
.roam-cal__cell.is-off {
  background: var(--roam-cream-active);
  color: var(--roam-text-muted);
  text-decoration: line-through;
  border-color: transparent;
}
/* Checkout-only: a booked night with a free night before it — a stay can END
   here, so it must NOT read as fully unavailable. A greige BORDER (not fill)
   with a whisper of greige inside; normal text, no strike. */
.roam-cal__cell.is-checkout {
  background: color-mix( in srgb, var(--roam-cream-active) 15%, transparent );
  border: 1px solid var(--roam-cream-active);
  color: var(--roam-text-body);
  text-decoration: none;
}
/* The stay: a FULL gold fill on the endpoints and a 15% gold fill between, all
   square so consecutive days read as one bar — rounded only on the outside of
   each end (left on check-in, right on check-out). */
.roam-cal__cell.is-range {
  background: color-mix( in srgb, var(--roam-gold) 15%, transparent );
  border-radius: 0;
  border-color: transparent;
  color: var(--roam-forest);
  text-decoration: none;
}
.roam-cal__cell.is-start,
.roam-cal__cell.is-end {
  background: var(--roam-gold);
  color: var(--roam-forest);
  border-color: transparent;
  font-weight: 700;
  text-decoration: none;
}
.roam-cal__cell.is-start { border-radius: var(--roam-r-sm) 0 0 var(--roam-r-sm); }
.roam-cal__cell.is-end { border-radius: 0 var(--roam-r-sm) var(--roam-r-sm) 0; }
/* A single chosen day (check-in with no check-out yet) rounds on all corners. */
.roam-cal__cell.is-start.is-end { border-radius: var(--roam-r-sm); }

/* ── Calendar legend (both calendars) ──────────────────────────────────────── */
.roam-cal__legend {
  display: flex;
  flex-wrap: wrap;
  /* Left-justified, its items sitting together — not stretched across the full
     width. Same on both calendars. */
  justify-content: flex-start;
  gap: var(--roam-sp-16);
  margin: var(--roam-sp-16) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--roam-fs-11);
  color: var(--roam-text-secondary);
}

/* The on-page calendar's footer: the legend on the left, Clear dates on the
   right, spanning the full month grid so it reads as one row under the months.
   The picker modal reuses the legend + a Clear control the same way. */
.roam-cal__legendbar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--roam-sp-16);
  flex-wrap: wrap;
}
.roam-cal__legendbar .roam-cal__legend { margin: 0; }
.roam-cal__clear {
  border: 0;
  background: none;
  padding: 0;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-forest);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  white-space: nowrap;
}
.roam-cal__clear[hidden] { display: none; }
/* display:flex would otherwise beat the [hidden] attribute, so the guests popup
   kept showing the date legend — this restores it. */
.roam-cal__legend[hidden] { display: none; }
.roam-cal__legend li { display: inline-flex; align-items: center; gap: var(--roam-sp-6); white-space: nowrap; }
.roam-cal__legend i {
  width: 15px;
  height: 15px;
  border-radius: var(--roam-r-sm);
  border: 1px solid transparent;
  flex: none;
}
.roam-cal__legend .sw-avail { background: var(--roam-white); border-color: var(--roam-border-tag); }
.roam-cal__legend .sw-booked { background: var(--roam-cream-active); }
.roam-cal__legend .sw-out { background: color-mix( in srgb, var(--roam-cream-active) 15%, transparent ); border-color: var(--roam-cream-active); }
.roam-cal__legend .sw-sel { background: var(--roam-gold); }
.roam-cal__note {
  margin: var(--roam-sp-20) 0 0;
  font-size: var(--roam-fs-13);
  color: var(--roam-text-muted);
}

/* =============================================================================
   GUEST REVIEWS
   ========================================================================== */

/* THE STARS.
   Two rows of the same five glyphs — outlines underneath, solid on top, the top
   one clipped to the rating. A 4.3 is not four stars and not five.

   The fill width comes from a data attribute rather than style="width:86%",
   because there are no inline styles on this site: they cannot be overridden
   without !important and they are invisible to anyone reading this file. The
   value is snapped to 5% steps in roam_stars(), which is a quarter of one star —
   finer than anybody can see. */
.roam-stars {
  position: relative;
  display: inline-block;
  vertical-align: -0.15em;
  width: 5.4em;
  height: 1em;
  font-size: var(--roam-fs-14);
  flex: 0 0 auto;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='108' height='20' viewBox='0 0 108 20'%3E%3Cg fill='none' stroke='%23C6975B' stroke-width='1.4' stroke-linejoin='round'%3E%3Cpath d='M10 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2L2.5 8.1l5.2-.8z'/%3E%3Cpath d='M32 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8z'/%3E%3Cpath d='M54 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8z'/%3E%3Cpath d='M76 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8z'/%3E%3Cpath d='M98 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8z'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-repeat: no-repeat;
}
.roam-stars__on {
  position: absolute;
  inset: 0 auto 0 0;
  overflow: hidden;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='108' height='20' viewBox='0 0 108 20'%3E%3Cg fill='%23C6975B'%3E%3Cpath d='M10 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2L2.5 8.1l5.2-.8z'/%3E%3Cpath d='M32 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8z'/%3E%3Cpath d='M54 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8z'/%3E%3Cpath d='M76 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8z'/%3E%3Cpath d='M98 2.6l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8z'/%3E%3C/g%3E%3C/svg%3E");
  /* 5.4em, matching the track above, so the clipped row does not squash as the
     width narrows — background-size must stay the FULL five stars. */
  background-size: 5.4em 100%;
  background-repeat: no-repeat;
}
.roam-stars__on[data-fill='0'] { width: 0%; }
.roam-stars__on[data-fill='5'] { width: 5%; }
.roam-stars__on[data-fill='10'] { width: 10%; }
.roam-stars__on[data-fill='15'] { width: 15%; }
.roam-stars__on[data-fill='20'] { width: 20%; }
.roam-stars__on[data-fill='25'] { width: 25%; }
.roam-stars__on[data-fill='30'] { width: 30%; }
.roam-stars__on[data-fill='35'] { width: 35%; }
.roam-stars__on[data-fill='40'] { width: 40%; }
.roam-stars__on[data-fill='45'] { width: 45%; }
.roam-stars__on[data-fill='50'] { width: 50%; }
.roam-stars__on[data-fill='55'] { width: 55%; }
.roam-stars__on[data-fill='60'] { width: 60%; }
.roam-stars__on[data-fill='65'] { width: 65%; }
.roam-stars__on[data-fill='70'] { width: 70%; }
.roam-stars__on[data-fill='75'] { width: 75%; }
.roam-stars__on[data-fill='80'] { width: 80%; }
.roam-stars__on[data-fill='85'] { width: 85%; }
.roam-stars__on[data-fill='90'] { width: 90%; }
.roam-stars__on[data-fill='95'] { width: 95%; }
.roam-stars__on[data-fill='100'] { width: 100%; }

/* The rating line under the property title. */
.roam-rating {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-8);
  margin: var(--roam-sp-8) 0 0;
  font-size: var(--roam-fs-15);
}
.roam-rating__grp { display: inline-flex; align-items: center; gap: var(--roam-sp-6); }
.roam-rating__n { font-weight: var(--roam-fw-semibold); }
.roam-rating__link { color: var(--roam-text-secondary); }
.roam-rating__link:hover { color: var(--roam-forest); }
.roam-rating__dot { color: var(--roam-text-faint); }
/* City, state with a pin — inline after the reviews. */
.roam-rating__loc { display: inline-flex; align-items: center; gap: var(--roam-sp-4); color: var(--roam-text-secondary); }
.roam-rating__loc svg { color: var(--roam-forest); flex: none; }
/* TABLET + PHONE — the place drops to its own line under the rating (client
   2026-08-03); the middot that joined them goes with it. */
@media (max-width: 1024px) {
  .roam-rating { flex-wrap: wrap; }
  .roam-rating__dot { display: none; }
  .roam-rating__loc { flex-basis: 100%; }
}

/* The summary above the review list. */
.roam-reviews__sum {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-8);
  margin: 0 0 var(--roam-sp-24);
  font-size: var(--roam-fs-16);
}

/* Two up on a wide screen, one below. Not three: a review is prose, and three
   columns of prose in a 1280px measure is 380px a column, which is too narrow
   for a paragraph to read as a paragraph. */
/* TWO COLUMNS, SUBGRID CARDS. The cards share the grid's rows so the stars, the
   quote and the byline line up across a row, the same mechanism the property
   cards use. Each card spans three tracks (stars · body · byline); the quote is
   truncated so a long review does not blow its track out. */
/* One review per row on phone AND tablet, two only on desktop (client
   2026-08-03) — a review is prose, and two columns of it need a desktop measure
   to read as paragraphs rather than slivers. */
.roam-reviews {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--roam-sp-24);
}
@media (min-width: 1025px) {
  .roam-reviews { grid-template-columns: 1fr 1fr; }
}
/* A ROW-SUBGRID for the inline cards: each card spans two of the grid's row
   tracks — head, then body — and inherits them, so a 2-up row keeps its two
   cards' headers and quotes on the same lines however tall either grows. The
   reply is NOT a third track here: inline it is only a link, living inside the
   body cell, so the card is cleanly two rows. (The modal, which does carry the
   full nested reply, opts out of the subgrid via .roam-review--list below.) */
.roam-review {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 2;
  row-gap: var(--roam-sp-16);
  padding: var(--roam-sp-24);
  border: 1px solid var(--roam-border-cream);
  border-radius: var(--roam-r-xl);
  background: var(--roam-white);
  box-shadow: 0 1px 3px rgba(27, 43, 39, 0.06);
}
/* THE MODAL LIST. A plain stacked item — no card, no subgrid, no fixed row
   count — so the full quote and the full nested reply can run as long as they
   like and simply push the next review down. Divider between, not a border box. */
.roam-review--list {
  display: block;
  grid-row: auto;
  padding: var(--roam-sp-24) 0;
  border: 0;
  border-bottom: 1px solid var(--roam-border-cream);
  border-radius: 0;
  background: none;
  box-shadow: none;
}
.roam-review--list:last-child { border-bottom: 0; }
.roam-review--list .roam-review__head { margin-bottom: var(--roam-sp-12); }
/* The header row: avatar, then name over city/state, then a right column with
   the stars over the stay date. Top-aligned so the two stacks start level. */
.roam-review__head {
  display: flex;
  align-items: flex-start;
  gap: var(--roam-sp-12);
}
.roam-review__avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--roam-forest-soft);
  color: var(--roam-cream);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
}
.roam-review__who { display: flex; flex-direction: column; min-width: 0; }
.roam-review__name { font-weight: var(--roam-fw-semibold); color: var(--roam-text-primary); }
/* The guest's city/state under their name. */
.roam-review__place { font-size: var(--roam-fs-13); color: var(--roam-text-muted); }
/* The right column: stars over the stay date, pushed to the far edge. */
.roam-review__meta {
  margin-left: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--roam-sp-4);
  flex: 0 0 auto;
}
.roam-review__stars { white-space: nowrap; }
.roam-review__date { font-size: var(--roam-fs-13); color: var(--roam-text-muted); }
.roam-review__text {
  margin: 0;
  font-size: var(--roam-fs-15);
  color: var(--roam-text-body);
  line-height: var(--roam-lh-body);
}
/* The quote/response cell — text, then its "See more" below. */
.roam-review__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--roam-sp-8);
  min-width: 0;
}
/* "See more" — a naked underlined control; the hidden tail (character-truncated
   server-side) reveals in place. */
.roam-review__more {
  padding: 0;
  border: 0;
  background: none;
  color: var(--roam-forest);
  font: inherit;
  font-weight: var(--roam-fw-semibold);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.roam-review__more:hover { color: var(--roam-gold-active); }

/* The inline "Response from ROAM" LINK — opens the modal at this review. */
.roam-review__replylink {
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-8);
  padding: 0;
  border: 0;
  background: none;
  color: var(--roam-forest);
  font: inherit;
  font-weight: var(--roam-fw-semibold);
  cursor: pointer;
}
.roam-review__replylink > span { text-decoration: underline; text-underline-offset: 2px; }
.roam-review__replylink:hover { color: var(--roam-gold-active); }

/* The NESTED host response (in the modal) — a set-in card with the ROAM mark,
   the label over the date, and the response text, so the thread reads as a
   thread rather than a second review. */
.roam-review__reply {
  align-self: stretch;
  margin-top: var(--roam-sp-4);
  padding: var(--roam-sp-16);
  border-radius: var(--roam-r-md);
  background: var(--roam-cream);
}
.roam-review__reply-head {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-10);
  margin-bottom: var(--roam-sp-10);
}
.roam-review__reply-mark {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--roam-forest-soft);
  color: var(--roam-cream);
}
.roam-review__reply-who { display: flex; flex-direction: column; min-width: 0; }
.roam-review__reply-name {
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-text-primary);
  font-size: var(--roam-fs-14);
}
.roam-review__reply-date { font-size: var(--roam-fs-12); color: var(--roam-text-muted); }
.roam-review__reply-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--roam-sp-6);
}
.roam-review__reply-body .roam-review__text { font-size: var(--roam-fs-14); color: var(--roam-text-secondary); }

/* The review a response link jumped to gets a brief gold ring in the modal. */
.roam-review.is-target { box-shadow: 0 0 0 2px var(--roam-gold); }
/* The reveal wears the shared show-all pill (.roam-morebtn on the summary). */
.roam-reviews__more { margin-top: var(--roam-sp-20); }
.roam-reviews__more[open] > summary { margin-bottom: var(--roam-sp-24); }

/* =============================================================================
   PAGES, POSTS, ARCHIVES, SEARCH, 404
   ========================================================================== */

.roam-page { padding-block: var(--roam-sp-48) var(--roam-sp-32); }
.roam-page__title {
  font-size: clamp(30px, 4vw, var(--roam-fs-40));
  margin: 0 0 var(--roam-sp-24);
  padding-top: var(--roam-sp-20);
  position: relative;
  max-width: 20ch;
}
.roam-page__title::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 32px;
  height: 2px;
  background: var(--roam-gold);
}
.roam-page__media { margin-bottom: var(--roam-sp-32); }
.roam-page__img { width: 100%; border-radius: var(--roam-r-lg); filter: var(--roam-photo-filter); }

/* PROSE HAS A MEASURE. Past roughly 75 characters a line the eye loses its
   place returning to the left edge, which is why body copy is capped here and
   the property pages — which are tables and grids, not prose — are not. */
.roam-page__body,
.roam-note__body { max-width: 68ch; }
.roam-prose h2 { font-size: var(--roam-fs-28); margin: var(--roam-sp-40) 0 var(--roam-sp-12); }
.roam-prose h3 { font-size: var(--roam-fs-20); margin: var(--roam-sp-32) 0 var(--roam-sp-8); }
.roam-prose ul,
.roam-prose ol { padding-left: var(--roam-sp-24); line-height: var(--roam-lh-body); }
.roam-prose blockquote {
  margin: var(--roam-sp-32) 0;
  padding-left: var(--roam-sp-24);
  border-left: 2px solid var(--roam-gold);
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-20);
  font-style: italic;
}
.roam-prose img { border-radius: var(--roam-r-md); }
.roam-prose a { text-underline-offset: 3px; }

/* ── Field notes ─────────────────────────────────────────────────────────── */
.roam-note { padding-block: var(--roam-sp-48) var(--roam-sp-32); }
.roam-note__meta,
.roam-reslist__meta {
  margin: 0 0 var(--roam-sp-8);
  font-size: var(--roam-fs-12);
  letter-spacing: var(--roam-ls-md);
  text-transform: uppercase;
  color: var(--roam-sage);
}
.roam-note__meta a,
.roam-reslist__meta a { text-decoration: none; }
.roam-note__title { font-size: clamp(28px, 4vw, var(--roam-fs-36)); margin: 0 0 var(--roam-sp-24); max-width: 24ch; }
.roam-note__media { margin-bottom: var(--roam-sp-32); }
.roam-note__media img { width: 100%; border-radius: var(--roam-r-lg); filter: var(--roam-photo-filter); }

.roam-postnav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--roam-sp-24);
  justify-content: space-between;
  margin-top: var(--roam-sp-56);
  padding-top: var(--roam-sp-24);
  border-top: 1px solid var(--roam-border-cream);
  font-size: var(--roam-fs-15);
}
.roam-postnav a { text-decoration: none; }
.roam-postnav span {
  display: block;
  font-size: var(--roam-fs-11);
  letter-spacing: var(--roam-ls-md);
  text-transform: uppercase;
  color: var(--roam-text-muted);
}

/* ── Result rows (WP search + blog archive) ──────────────────────────────────
   A READING-COLUMN list, renamed off .roam-results because that class also names
   the property collection's card-grid container — sharing it capped the grid at
   68ch and dropped it to two cards across. This component is its own thing now. */
.roam-reslist { display: grid; gap: var(--roam-sp-32); max-width: 68ch; }
.roam-reslist__item { padding-bottom: var(--roam-sp-32); border-bottom: 1px solid var(--roam-border-cream); }
.roam-reslist__item:last-child { border-bottom: 0; }
.roam-reslist__h { font-size: var(--roam-fs-24); margin: 0 0 var(--roam-sp-8); }
.roam-reslist__h a { text-decoration: none; }
.roam-reslist__h a:hover { color: var(--roam-forest-soft); }
.roam-reslist__b { margin: 0; color: var(--roam-text-body); }

.roam-arch__desc { margin-top: var(--roam-sp-16); max-width: 68ch; }

/* ── 404 ─────────────────────────────────────────────────────────────────── */
.roam-404 { padding-block: var(--roam-sp-80); max-width: 56ch; }
.roam-404__title { font-size: clamp(28px, 4vw, var(--roam-fs-36)); margin: 0 0 var(--roam-sp-12); }
.roam-404__body { margin: 0 0 var(--roam-sp-32); color: var(--roam-text-body); }
.roam-404__search { margin-top: var(--roam-sp-40); }

/* ── Search form (WordPress's own markup) ────────────────────────────────── */
.search-form { display: flex; gap: var(--roam-sp-8); flex-wrap: wrap; }
.search-form label { flex: 1 1 220px; }
.search-form .search-field {
  width: 100%;
  padding: var(--roam-sp-12) var(--roam-sp-16);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-sm);
  font: inherit;
  min-height: 44px;
  background: var(--roam-white);
}
.search-form .search-submit {
  padding: var(--roam-sp-12) var(--roam-sp-24);
  border: 1px solid var(--roam-forest);
  border-radius: var(--roam-r-sm);
  background: var(--roam-forest);
  color: var(--roam-cream);
  font: inherit;
  cursor: pointer;
  min-height: 44px;
}
.search-form .search-submit:hover { background: var(--roam-forest-light); border-color: var(--roam-forest-light); }

/* ── Contact block ───────────────────────────────────────────────────────── */
.roam-contact__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--roam-sp-24);
  margin: var(--roam-sp-24) 0 0;
  text-align: left;
}
.roam-contact__k {
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-md);
  text-transform: uppercase;
  color: var(--roam-text-muted);
}
.roam-contact__v { margin: var(--roam-sp-4) 0 0; font-size: var(--roam-fs-18); }
.roam-contact__v a { text-underline-offset: 3px; }

/* ── The build note ──────────────────────────────────────────────────────── */
/* Editors only, and it should look like scaffolding rather than like content —
   nobody should mistake it for part of the page. */
.roam-build-note {
  margin: var(--roam-sp-48) 0;
  padding: var(--roam-sp-20);
  border: 2px dashed var(--roam-warning);
  border-radius: var(--roam-r-sm);
  font-size: var(--roam-fs-13);
  background: var(--roam-white);
}
.roam-build-note h2 {
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-14);
  text-transform: uppercase;
  letter-spacing: var(--roam-ls-md);
  color: var(--roam-warning);
  margin: 0 0 var(--roam-sp-12);
}
.roam-build-note dl { margin: 0; }
.roam-build-note dt { font-weight: var(--roam-fw-semibold); margin-top: var(--roam-sp-8); }
.roam-build-note dd { margin: 0; color: var(--roam-text-secondary); }

/* =============================================================================
   HOMEPAGE
   ========================================================================== */

/* ── Hero ────────────────────────────────────────────────────────────────── */
/* PORTED FROM THE DEV SITE. Four of these are client changes made AFTER the
   design spec was written, and building from the spec got all four wrong:

     eyebrow    LIFTED gold #E0B978, not brand #C6975B
     "em" line  GOLD (client 2026-07-27, "Book it direct goes gold"),
                not cream at 85% as the design had it
     subhead    FULL-STRENGTH cream, not 85% — it was the only hero text
                carrying an alpha, which is why it was the only thing that
                changed between variants
     rhythm     ONE gap between all five blocks, not the design's 22/18/36/26

   LIFTED GOLD is its own token rather than a lighter --roam-gold, and stays
   separate on purpose: the section eyebrows keep the brand value, and folding
   the two together would move all eight of them. What the lifted pair share is
   a ground — both sit on something dark and photographic rather than on a flat
   cream band. */
:root {
  /* THE HERO'S FLOW, AND IT IS THE FLOW TOKENS' FIRST ACTUAL USE.
     --roam-flow-coupled / -related / -separated have been declared in
     roam-tokens.css since the port and referenced NOWHERE — a system on paper,
     with every gap on the site still picked by hand next to the rule that
     needed it. That is why the hero's elements drift: nothing was holding them
     to a scale.

     Three relationships, not five numbers:

       eyebrow -> h1     coupled     a label and the thing it labels
       h1 -> sub         related     two parts of one statement
       sub -> search     separated   copy ends, a control begins
       search -> trust   coupled     the reassurances belong to the bar

     The vh term stays, because a hero has to answer to the height of the screen
     as well as to the scale — but it scales the SYSTEM rather than replacing
     it. --roam-hero-unit is the viewport-answerable version of
     --roam-flow-related's 16px, and the other two relationships are that unit
     at the flow tokens' own ratios: 8/16 and 24/16, so a short screen tightens
     the whole block together instead of one gap being tuned by hand.

     Written as 0.5 and 1.5 rather than as calc() on the tokens themselves,
     because dividing a length by a length is not something calc() will do. The
     numbers are the ratio and the comment is the derivation. */
  --roam-hero-unit: clamp(14px, 2.2vh, 20px);
  --roam-hero-coupled: calc(var(--roam-hero-unit) * 0.5);
  --roam-hero-separated: calc(var(--roam-hero-unit) * 1.5);
  --roam-hero-gap: var(--roam-hero-unit);
}

/* PEEKABOO — THE HERO IS SIZED SO THE CARDS BELOW IT SHOW.
   Client instruction 2026-08-01. The hero's job is not to fill the screen; it
   is to fill the screen MINUS enough of the next section to prove there is one.
   A hero that ends exactly at the fold reads as the whole page, and the single
   most common thing a visitor does on one is not scroll.

   So the height is the viewport less the three things above and below it:

     100svh                 what the browser is actually showing
     - promo bar            36, and it is above the header at rest
     - header               72 + its hairline
     - --roam-hero-peek     what the next section is allowed to show

   IT WAS `padding: 104px … 92px` AND NOTHING ELSE, so the height was whatever
   the copy happened to need — which is a different number on every viewport and
   is nobody's decision.

   svh, NOT vh. On a phone `vh` is the height with the browser chrome hidden, so
   a `100vh` hero is taller than the screen until you scroll, and the peek is
   invisible at exactly the moment it has to work. svh is the small viewport —
   chrome showing — which is what a visitor sees first. The vh line above it is
   the fallback for browsers that predate svh and is deliberately left in.

   min-height rather than height: if the copy grows past the fold the hero grows
   with it rather than clipping the search bar out of the page. */
:root {
  /* HOW MUCH OF THE SECTION BELOW SHOWS AT REST — measured on the dev site, not
     chosen. It was 96 here, which was a guess and was less than half of it.

     The dev figures, taken as the y of the first section below the hero against
     the viewport height:

       1440 x 900    202.9
       1024 x 800    117.8
        768 x 1024   364.0
        390 x 844    133.5

     That spread is not a designed curve. Its hero is a FIXED band — around 588,
     573, 551 and 610 tall once the promo bar and header are taken off — so the
     peek is whatever the viewport has left over, and 364 at 768 is an accident
     of a tall screen rather than a decision.

     So: the dev site's own number at each of the two viewports the design is
     actually drawn at, held constant in between. 202.9 and 133.5, rounded. A
     constant peek is what "peekaboo" means and it is what was asked for; it is
     also better behaved than the original at the two widths nobody drew. */
  --roam-hero-peek: 200px;
  /* The promo bar, which sits above the header at scroll 0. It is dismissible,
     and when it is dismissed the peek simply gets 36px bigger — the wrong
     direction to fail in, which is why it is subtracted rather than ignored. */
  --roam-promo-h: 36px;
  /* The header and its hairline. Overwritten at runtime with the measured
     value; this is the fallback. */
  --roam-hdr-total: calc(var(--roam-hdr-h) + 1px);
}
/* Dismissed. The bar is gone, so the hero takes its 36px back rather than
   handing them to the peek — see the inline script in inc/chrome.php, which
   sets this class before the first paint for the same reason it hides the bar
   there rather than in roam-header.js. */
html.roam-nopromo { --roam-promo-h: 0px; }
.roam-hero {
  position: relative;
  overflow: hidden;
  display: flex;
  /* TOP-WEIGHTED, NOT CENTRED — measured. The dev site's eyebrow sits 31px
     below the header and its content ends 96px above the next section: 31 above
     and 96 below is not a centred block, and centring ours put every element
     25-33px lower than its counterpart. Reported as the eyebrow being high,
     which it was, but the whole block came with it.

     flex-start plus a fixed top pad means the copy starts in the same place on
     every screen, and the min-height's slack all falls below the search bar
     where there is nothing to move. */
  align-items: flex-start;
  /* --roam-hdr-total defaults to the header's own height plus its hairline and
     is OVERWRITTEN AT RUNTIME with what the bar actually measures, along with
     --roam-promo-h — see assets/js/roam-header.js. The constants here are the
     first-paint and no-JavaScript answer; they are right at most widths and
     wrong at any width where the promo bar's sentence wraps, and every pixel it
     gains comes out of the peek. */
  min-height: calc(100vh - var(--roam-promo-h) - var(--roam-hdr-total) - var(--roam-hero-peek));
  min-height: calc(100svh - var(--roam-promo-h) - var(--roam-hdr-total) - var(--roam-hero-peek));
  color: var(--roam-cream);
  background: var(--roam-surface-hero);
  padding: 32px var(--roam-page-x) var(--roam-sp-56);   /* dev: 31 above the eyebrow */
}
/* A CAP ON TALL PORTRAITS. The min-height above fills the viewport, which is
   right in landscape but wrong in portrait: on a phone or a tablet held upright
   the band runs far taller than the copy needs, and the photo — background-size:
   cover — crops to a narrow, over-zoomed column that reads as stretched. min()
   holds the band to a friendlier height, and because it stays a *min*-height the
   copy can still grow the band past it rather than ever being clipped. */
@media (orientation: portrait) {
  .roam-hero {
    min-height: min(
      calc(100svh - var(--roam-promo-h) - var(--roam-hdr-total) - var(--roam-hero-peek)),
      640px
    );
  }
}
/* The band is a flex row now, so the content block has to be told to take the
   width it used to have by being the only thing in a block container. */
.roam-hero__content { flex: 1 1 auto; }
/* NO FILTER ON THE HERO PHOTOGRAPH, and that is a decision rather than an
   omission. --roam-photo-filter is saturate(.8) contrast(1.05) brightness(.95)
   and it is right for the small photographs elsewhere on the site. The dev site
   carried it here too and the client took it off on 2026-07-27, choosing from
   three rendered variants: they wanted the picture as the picture.

   It came off TOGETHER WITH the scrim's top stop going 0.5 → 0.72 — see below.
   The two are one decision: the gradient became the copy's only protection the
   moment the photo stopped being knocked back, which is why it had to get
   stronger. Shipping the filter here and the 0.72 there is half of each. */
.roam-hero__photo {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center center;
}
/* NO TINT LAYER. There was one here — teal at 0.1, multiply — and the dev site
   does not have it. Its hero is the photograph and the gradient, nothing else,
   and the client removed even the photo's own saturate/contrast/brightness
   filter on 2026-07-27 because they wanted the picture as the picture. A teal
   multiply invented here was quietly re-grading it. The element is kept in the
   markup with no paint of its own so the layer order below it is unchanged. */
.roam-hero__tint { display: none; }
/* THE DEV SITE'S GRADIENT, VALUE FOR VALUE.

   0.72 / 0.38 / 0.86. The top stop is 0.72 and not 0.5: the client lifted it on
   2026-07-27, at the same time as removing the photo's own
   saturate(.85) contrast(1.03) brightness(.97) filter. Those two changes go
   together — this gradient became the copy's only protection when the filter
   came off, which is exactly why it had to get stronger. Building this theme
   from the spec's 0.5 undid half of a paired decision.

   An earlier version here added a SECOND, horizontal scrim, to cope with the
   hero being a randomly chosen property photo that might be a sunlit lawn. The
   hero is the design's own image again, so the compensation is not needed and
   is gone: one gradient, the dev site's. */
.roam-hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    rgba(27, 43, 39, 0.72) 0%,
    rgba(27, 43, 39, 0.38) 42%,
    rgba(27, 43, 39, 0.86) 100%
  );
}
.roam-hero--nophoto .roam-hero__tint,
.roam-hero--nophoto .roam-hero__scrim { display: none; }

.roam-hero__content {
  position: relative;
  max-width: var(--roam-max-width);
  margin-inline: auto;
}

/* The rule is drawn with ::before rather than a <span>, so the markup is the
   label and only the label — and an eyebrow cannot be added in future WITHOUT
   its rule by forgetting a child element. */
.roam-hero__eyebrow {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0;
  font-family: var(--roam-font-body);
  font-weight: var(--roam-fw-semibold);
  font-size: var(--roam-fs-12);
  letter-spacing: var(--roam-ls-section);
  text-transform: uppercase;
  color: var(--roam-gold-lifted);
}
.roam-hero__eyebrow::before {
  content: '';
  display: inline-block;
  flex: none;
  width: 32px;
  height: 2px;
  background: var(--roam-gold-lifted);
}

/* min(6.5vw, 8vh) — the design's width clamp, made answerable to HEIGHT too.
   Three lines of Playfair at 78px is 239px of hero, and on an 800px viewport
   that one block is most of the reason nothing below it can be seen. The vw
   term still governs on a tall screen, so the proportions are untouched where
   there is room for them; the vh term only bites where the screen is short. */
.roam-hero__h1 {
  display: block;
  font-family: var(--roam-font-display);
  font-weight: var(--roam-fw-bold);
  font-size: clamp(38px, min(6.5vw, 8vh), 78px);
  line-height: 1.02;
  letter-spacing: var(--roam-ls-tight);
  color: var(--roam-cream);
  /* Tight to the eyebrow above it, which labels it; a full gap below, where the
     statement continues. It was --roam-hero-gap on both sides, which put a
     label and the thing it labels as far apart as two separate ideas — the
     reason the eyebrow reads as floating high. */
  margin: var(--roam-hero-coupled) 0 var(--roam-hero-gap);
  max-width: 820px;
  text-shadow: 0 2px 24px rgba(27, 43, 39, 0.4);
}
/* INLINE, NOT A BLOCK. It was display:block, which is a second way of saying
   "start a new line" — and now that the markup breaks before it with a <br>,
   the way the dev site does, a block would break twice and open a blank line
   in the middle of the headline. One mechanism for line breaks in this h1, and
   it is the <br> elements, because they are the only ones a media query can
   turn off. */
.roam-hero__em {
  font-style: italic;
  font-weight: 300;
  color: var(--roam-gold);
}

/* ── The headline's two cadences ──────────────────────────────────────────
   The same words, split differently at the two widths — client instruction to
   the dev site 2026-07-28, copied here:

     desktop   Your adventure starts / at the front door. / Book it direct.
     phone     Your adventure / starts at the / front door. / Book it direct.

   Both sets of breaks are in the markup and CSS chooses between them, so one
   string serves both and a resize past 768 re-wraps on its own. Doing it in
   JavaScript would mean the headline was wrong until a script ran, on the one
   element above the fold that is guaranteed to be seen first.

   `inline`, not `initial`: <br> has no useful initial display and the point of
   the pair is that exactly one of them is in flow at any width. */
.roam-br-d { display: inline; }
.roam-br-m { display: none; }
@media (max-width: 768px) {
  .roam-br-d { display: none; }
  .roam-br-m { display: inline; }
}

.roam-hero__sub {
  display: block;
  font-family: var(--roam-font-body);
  font-weight: var(--roam-fw-normal);
  font-size: 17px;
  line-height: var(--roam-lh-snug);
  color: var(--roam-cream);
  /* Copy ends and a control begins — the one genuine break in the block. */
  margin: 0 0 var(--roam-hero-separated);
  max-width: 520px;
  text-shadow: 0 1px 12px rgba(27, 43, 39, 0.5);
}

.roam-hero__trust {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  /* The reassurances belong to the bar above them, not to the hero at large. */
  margin: var(--roam-hero-coupled) 0 0;
  padding: 0 4px;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  color: var(--roam-cream);
  text-shadow: 0 1px 12px rgba(27, 43, 39, 0.5);
}
.roam-hero__trustitem {
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-8);
  white-space: nowrap;
}
.roam-hero__tick { width: 14px; height: 14px; flex: none; color: var(--roam-gold); }

@media (max-width: 768px) {
  :root {
    /* Less peek on a phone, because there is less screen to take it out of and
       the hero's own copy needs the room first. A card's top edge and its photo
       starting is enough to say "there is more". */
    --roam-hero-peek: 132px;   /* dev: 133.5 at 390x844 */
    --roam-promo-h: 54px;   /* the phone bar wraps to two lines — measured */
  }
  /* Top pad matches the desktop's 32 so the eyebrow lands in the same relation
     to the header; the bottom is tighter because on a phone every point below
     the search bar is a point the peek does not get. */
  .roam-hero { padding-block: 32px var(--roam-sp-24); }
  .roam-hero__sub { font-size: 16px; }
  .roam-hero__trust { gap: 14px 22px; }
}

/* ── Section eyebrow ─────────────────────────────────────────────────────── */
/* The canonical one, shared by every section that carries a label. BRAND gold,
   not lifted: these sit on flat cream bands rather than on a photograph. */
/* ── The four type roles ──────────────────────────────────────────────────
   One family for eyebrow, title, subtitle and body, replacing three that had
   drifted apart — .roam-hsec__h at clamp(24,3vw,32), .roam-why__title at
   clamp(28,3.5vw,36) and .roam-sec__h flat at 24, all naming a section.

   The old class names are kept ON THESE RULES rather than deleted, so every
   template that already emits .roam-hsec__h gets the shared type without a
   markup change and there is no window where half the site is on one system.
   New markup uses the role name.

   THE GOLD RULE BELONGS TO THE EYEBROW. It was declared on the eyebrow AND on
   two of the title rules, so a section with both drew it twice, 20px apart.
   Titles keep it only where there is no eyebrow above them to carry it — which
   the adjacent-sibling rule below works out on its own rather than needing a
   modifier class somebody has to remember. */
/* FLEX AND fit-content, NOT inline-flex, and the difference is four pixels of
   air that no token could see.

   inline-flex makes this <p> an INLINE-level box, so it sits in an anonymous
   block box whose line box is sized by the section's own strut — body type at
   1.7. The eyebrow's box is 14px tall; the line box around it was 25. The
   difference is half-leading, split above and below, and it lands between the
   eyebrow and the title on top of whatever --roam-flow-coupled says. A block
   box has no strut and no half-leading: the gap becomes exactly the margin,
   which is the only way a flow token can be the thing that sets it.

   fit-content keeps the shrink-to-fit width that inline-flex was providing, so
   the gold rule still ends where the words end rather than running the width
   of the section. */
.roam-eyebrow,
.roam-hero__eyebrow,
.roam-std__eyebrow {
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 14px;
  margin: 0;
  font-family: var(--roam-font-body);
  font-size: var(--roam-type-eyebrow-size);
  line-height: var(--roam-type-eyebrow-lh);
  font-weight: var(--roam-type-eyebrow-weight);
  letter-spacing: var(--roam-type-eyebrow-track);
  text-transform: uppercase;
  color: var(--roam-gold);
}

.roam-title,
.roam-hsec__h,
.roam-why__title,
.roam-std__h {
  margin: var(--roam-flow-coupled) 0 var(--roam-flow-related);
  font-family: var(--roam-font-display);
  font-weight: 700;
  font-size: var(--roam-type-title-size);
  line-height: var(--roam-type-title-lh);
  /* NO TRACKING. The dev site's section heading sets none — only its contact
     heading, which is half again the size, pulls -0.3px. Playfair at 40 does
     not need tightening and gets muddy when it is given it. */
  letter-spacing: normal;
}
/* A heading INSIDE a section, which is a different job from naming one. */
.roam-subhead,
.roam-sec__h {
  margin: 0 0 var(--roam-flow-related);
  font-family: var(--roam-font-display);
  font-size: var(--roam-type-subhead-size);
  line-height: var(--roam-type-subhead-lh);
  letter-spacing: var(--roam-ls-tight);
}
/* Section header ROW — heading left, "see more" link right (the similar-stays
   section, mirroring the homepage featured row). */
.roam-sec__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--roam-sp-16);
  margin-bottom: var(--roam-flow-related);
}
.roam-sec__head .roam-sec__h { margin-bottom: 0; }
.roam-sec__more {
  flex: 0 0 auto;
  color: var(--roam-forest-light);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
  white-space: nowrap;
  text-decoration: none;
}
.roam-sec__more:hover { color: var(--roam-gold-active); }
.roam-subtitle,
.roam-why__lede {
  margin: 0 0 var(--roam-flow-related);
  font-size: var(--roam-type-subtitle-size);
  line-height: var(--roam-type-subtitle-lh);
  max-width: 56ch;
}
.roam-body,
.roam-hsec__body {
  margin: 0 0 var(--roam-flow-separated);
  font-size: var(--roam-type-body-size);
  line-height: var(--roam-type-body-lh);
  max-width: var(--roam-type-body-measure);
  color: var(--roam-text-body);
}
.roam-eyebrow + .roam-title::before,
.roam-eyebrow + .roam-hsec__h::before,
.roam-eyebrow + .roam-why__title::before,
.roam-eyebrow + .roam-std__h::before { content: none; }
/* AND THE PADDING GOES WITH THE MARK.
   The rule above suppressed the drawing and left its clearance behind, so every
   section on the homepage carried 20px of empty space between its eyebrow and
   its title — on top of the 8px --roam-flow-coupled had set — reserved for a
   mark that is not there. That is the whole of the gap that read as too loose.
   The two declarations belong on the same condition: no mark, no clearance. */
.roam-eyebrow + .roam-title,
.roam-eyebrow + .roam-hsec__h,
.roam-eyebrow + .roam-why__title,
.roam-eyebrow + .roam-std__h { padding-top: 0; }
.roam-eyebrow::before,
.roam-hero__eyebrow::before,
.roam-std__eyebrow::before {
  content: '';
  display: inline-block;
  flex: none;
  width: 32px;
  height: 2px;
  background: var(--roam-gold);
}
/* Contact is the one section eyebrow that carries no rule — client instruction,
   reverting it out of the unification. A MODIFIER rather than a standalone
   class, so the type treatment stays in step with the other eight and only the
   rule differs. A standalone class would drift the moment the canonical one is
   adjusted. */
.roam-eyebrow--contact::before { display: none; }

.roam-btn {
  display: inline-block;
  padding: var(--roam-sp-14) var(--roam-sp-28);
  border: 1px solid currentColor;
  border-radius: var(--roam-r-full);
  font-size: var(--roam-fs-15);
  text-decoration: none;
  transition: background 0.3s var(--roam-easing), color 0.3s var(--roam-easing);
}
.roam-btn:hover { background: var(--roam-forest); color: var(--roam-cream); }
.roam-btn--light:hover { background: var(--roam-cream); color: var(--roam-forest); }
.roam-btn--gold {
  background: var(--roam-gold);
  border-color: var(--roam-gold);
  color: var(--roam-forest);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
}
.roam-btn--gold:hover { background: var(--roam-gold-active); border-color: var(--roam-gold-active); color: var(--roam-forest); }

/* --roam-sec-y, WHICH IS 36 AND WAS 56 HERE. Reported as too much air above
   the eyebrow, and it was exactly 20px too much.

   The token exists and its own comment in roam-tokens.css explains that it is
   ONE unit precisely so the seam between any two sections is 2x it and there is
   nothing to reason about. It was being used once, on the property page, while
   every homepage section wrote --roam-sp-56 beside itself — which is the same
   failure as the flow tokens: a system declared and not applied. */
.roam-hsec { padding-block: var(--roam-sec-y); }
/* The gold rule from the hero eyebrow, carried down the page. It is the one
   repeated mark that ties the sections to the hero without repeating the
   hero's weight — a 32px line costs nothing and does all the work a second
   accent colour would have done badly. */
/* Type comes from the shared .roam-title rule. What is left here is only what
   supports the gold mark above it. */
.roam-hsec__h {
  padding-top: var(--roam-flow-mark);
  position: relative;
}
.roam-hsec__h::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 32px;
  height: 2px;
  background: var(--roam-gold);
}
/* .roam-hsec__body is the shared .roam-body rule now — nothing left to say. */
/* A HEADING ENDS AND A GRID BEGINS — 32, the dev site's, measured on its
   regions, amenity and type bands and declared there as
   `.roam-hs__h2 { margin: 8px 0 32px }`. It was 40 here, chosen by eye.

   Scoped to .roam-hsec on purpose. .roam-tiles and .roam-chips carry their own
   24 for the term archive, where they sit under a page heading rather than
   under a section's copy — a different relationship, and it keeps its number. */
.roam-hsec .roam-cards,
.roam-hsec .roam-tiles,
.roam-hsec .roam-chips,
.roam-hsec .roam-amtiles,
.roam-hsec .roam-quotes,
.roam-hsec .roam-guides { margin-top: var(--roam-flow-block); }
/* PROSE ENDS AND A GRID BEGINS — 22, and TIGHTER than the heading case rather
   than looser. That is the design's arrangement, not an accident of it: a
   heading labels the whole band and wants air under it, while a lede has
   already begun explaining the thing the grid is about, so the grid is the rest
   of that sentence.

   An adjacent-sibling rule rather than a margin on the paragraph, because
   adjacent margins collapse to the LARGER of the two — a 22 under the lede and
   a 32 over the grid would resolve to 32 and the tighter value would never
   paint. The gap has to be set by whichever element is second. */
.roam-hsec__body + .roam-cards,
.roam-hsec__body + .roam-tiles,
.roam-hsec__body + .roam-chips,
.roam-hsec__body + .roam-quotes,
.roam-hsec__body + .roam-guides,
.roam-hsec__note + .roam-quotes { margin-top: var(--roam-flow-blockprose); }
/* The why-direct lede sets its own bottom margin (--roam-flow-block) because the
   stats under it are the same column, not a full-bleed grid across the band. */
/* The header row owns the gap to what follows it — the heading inside it has no
   bottom margin of its own to contribute, so this is the whole of it. */
.roam-hsec__head { margin-bottom: var(--roam-flow-separated); }
.roam-hsec__head .roam-hsec__h { margin-bottom: 0; }
.roam-hsec__head .roam-hsec__body { margin-bottom: 0; }
/* ── The section header ROW ───────────────────────────────────────────────
   Eyebrow and heading on the left, an affordance on the right — the design's
   own construction, value for value from the dev site's .roam-hs__head.

   flex-end, not center: the right-hand item sits on the heading's bottom edge,
   so a two-line heading pushes the link down with it instead of leaving it
   floating in the middle of a tall block. --baseline is the variant the design
   uses where the right-hand item is a NOTE rather than a link, and there it
   aligns to the text baseline instead.

   flex-wrap is what makes this safe on a phone: the right-hand item drops under
   the heading rather than crushing it, and the 16px gap becomes the space
   between them when it does. */
.roam-hsec__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--roam-flow-related);
  flex-wrap: wrap;
}
.roam-hsec__head--baseline { align-items: baseline; }
/* The left column is a block so the eyebrow, heading and lede stack normally
   inside a row that is otherwise flex — without it they become three flex
   items side by side. */
.roam-hsec__headl { min-width: 0; }
/* nowrap because "View all 12 homes →" breaking after "all" is worse than the
   row wrapping, and flex-wrap has already given it somewhere to go. */
.roam-hsec__more {
  margin: 0;
  /* #2A4A40 — the dev site's own value for this link, which is
     --roam-forest-light here, not --roam-forest-soft (#3D6858). */
  color: var(--roam-forest-light);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
  white-space: nowrap;
  text-decoration: none;
}
.roam-hsec__more:hover { color: var(--roam-gold-active); }
/* The right-hand NOTE — reviews' "Verified guests · unedited". Quieter than the
   link because it is a caveat rather than a way out of the section. */
.roam-hsec__note {
  margin: 0;
  font-size: var(--roam-fs-13);
  color: var(--roam-text-muted);
  white-space: nowrap;
}

/* ONE BAND VALUE, EVERYWHERE. Measured: every section on the dev homepage pads
   36 top and bottom at 1440 and 24 at 390 — the contact block and the closing
   CTA included. This doubled it on the reasoning that a closing section should
   sit apart, which is a defensible piece of design and is not what the
   reference does. The hairline above is what sets this section apart there. */
.roam-hsec--cta {
  text-align: center;
  padding-block: var(--roam-sec-y);
  border-top: 1px solid var(--roam-border-cream);
}

/* ── The dark band ────────────────────────────────────────────────────────
   Contact sits on the header's own gradient rather than on cream — the design's
   arrangement, and the only other place on the homepage besides why-direct
   where the ground changes. It is what makes the section read as the end of the
   page rather than as one more band. */
.roam-hsec--dark {
  position: relative;
  overflow: hidden;
  /* --roam-surface-stat, which is the 145deg #1B2B27 → #2A4A40 the dev site
     names --roam-header-bg and uses on this band. Not --roam-surface-forest-down
     (180deg, and the two stops the other way round), which is the why-direct
     strip's — a diagonal lightening toward the bottom right against a vertical
     darkening downward. Two different grounds, and the difference is visible. */
  background: var(--roam-surface-stat);
  color: var(--roam-cream);
}
/* THE WASH IS NOT DECORATION. A flat forest rectangle at this size reads as an
   unstyled block — as something that failed to load. Two soft radial stops,
   gold at the top right and the brand's blue at the bottom left, give it a
   direction and cost nothing: no image, no extra request, and it cannot fail to
   arrive. Same geometry on both dark panels, different stops, because the
   design places the glow against each panel's own content. */
.roam-glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.roam-hsec--dark .roam-glow {
  background:
    radial-gradient(at 70% 20%, rgba(198, 151, 91, 0.12) 0%, transparent 55%),
    radial-gradient(at 20% 90%, rgba(91, 143, 168, 0.1) 0%, transparent 50%);
}
.roam-ct__inner {
  position: relative;
  max-width: 920px;
  margin-inline: auto;
  /* A gutter, so the heading and lede do not run to the viewport edge on a phone
     — the dark band is full-bleed and this inner had no inline padding. */
  padding-inline: var(--roam-page-x);
  text-align: center;
}
/* THE PAGE'S LARGEST HEADING. The dev site sets the contact heading half again
   the section size — clamp(30,4vw,40) → ~clamp(44,6vw,60) — and gives it the
   -0.3px tracking Playfair wants there. Ours was rendering at the shared section
   size and read too small to close the page. Client instruction 2026-08-02. */
.roam-ct__inner .roam-hsec__h {
  font-size: clamp(44px, 6vw, 60px);
  letter-spacing: -0.3px;
}
/* The eyebrow centres with everything else, so its shrink-to-fit box needs a
   centring margin — width:fit-content leaves it hard left otherwise. */
.roam-hsec--dark .roam-eyebrow { margin-inline: auto; }
.roam-ct__lede {
  font-size: 17px;
  line-height: var(--roam-lh-snug);
  color: rgba(250, 246, 241, 0.72);
  max-width: 560px;
  /* Prose ends and a pair of controls begins — the same seam as a heading to a
     grid, and the design uses the same 32 for it. */
  margin: 0 auto var(--roam-flow-block);
}
.roam-ct__actions {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
  align-items: center;
}
.roam-ct__btn {
  background: var(--roam-gold);
  color: var(--roam-forest);
  padding: 15px 28px;
  border-radius: var(--roam-r-full);
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-xs);
  text-decoration: none;
  transition: background 0.3s var(--roam-easing);
}
.roam-ct__btn:hover { background: var(--roam-gold-active); }
/* The ghost pays for its 1px border out of its own padding, so the two buttons
   come out the same height — 15+15+ink against 14+14+ink+2. */
.roam-ct__btn--ghost {
  background: none;
  border: 1px solid rgba(250, 246, 241, 0.3);
  color: var(--roam-cream);
  padding: 14px 28px;
}
.roam-ct__btn--ghost:hover { background: rgba(250, 246, 241, 0.08); }
.roam-ct__note {
  font-size: var(--roam-fs-13);
  color: rgba(250, 246, 241, 0.5);
  margin: var(--roam-flow-blockprose) 0 0;
}
/* The italic turn in a section heading, the same move the hero's headline makes.
   Inline, because the <br> before it does the breaking. */
.roam-hsec__em {
  font-style: italic;
  font-weight: 300;
}
.roam-hsec--cta .roam-hsec__body { margin-inline: auto; }

/* ── Hero search bar ─────────────────────────────────────────────────────── */
/* Desktop: one horizontal pill with a gold circle on the end. Mobile: a stacked
   card with hairlines between fields and a full-width submit. Per spec §6 —
   four segments squeezed into 390px is four unusable fields. */
/* 980, WHICH IS THE DEV SITE'S — measured. This was 860, and since the form
   inside it is capped at 980 the SLOT was the thing making the bar narrow; the
   max-width on the form never got to do anything.

   And no top margin. The 36 here was a fourth number for a gap the flow system
   already sets — .roam-hero__sub carries --roam-hero-separated below it — so
   the two stacked and the bar sat a whole extra gap down the page. This is
   exactly the drift the flow tokens exist to stop, still in the file one rule
   below where they were introduced. */
/* min-height reserves the bar's height so hiding the fallback (below) and
   mounting the widget a moment later does not shift the hero. */
.roam-hero__search { margin: 0; max-width: 980px; min-height: 68px; }
/* NO FLASH OF THE NATIVE FORM. When JS is on (html.roam-js, set by a head
   script before first paint), the fallback is hidden from the start and the
   widget takes its place — rather than the form painting for a frame and being
   swapped. With JS off, .roam-js is never set and the real form shows, which is
   the whole point of keeping it. */
.roam-js .roam-hero__search .roam-filter { display: none; }

/* THE HERO SEARCH BAR, against the dev site's own .roam-searchbar rather than
   against a drawing. Every number below is one of its declared values.

   The words come with it — Where / Check in / Check out / Who, in inc/search.php.
   They were Where / Arrive / Leave / Guests, which is the same meaning in
   different words, and on this bar the words ARE the design.

   ONE THING IS DELIBERATELY NOT MATCHED. The dev bar's four segments are
   BUTTONS that open JavaScript panels — a region list, a calendar, a guest
   stepper — so their empty state can read "Add dates" and "Add guests" in
   prose. Ours are a real <form> with native controls, so an empty date reads
   mm/dd/yyyy and an empty guest count reads as a spinner. That is not a
   styling gap: it is the difference between a widget and a form, and the form
   is what works with JavaScript off and submits to a URL a guest can bookmark.
   Building the panels is a separate piece of work, recorded in
   docs/deferred.md. */
.roam-filter--hero {
  max-width: 980px;             /* dev: .roam-search-slot max-width */
  background: var(--roam-white);
  border: 0;
  border-radius: var(--roam-r-full);
  padding: var(--roam-sp-8);    /* dev: 8px all round, not 24 on the left */
  gap: 0;
  margin-bottom: 0;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
  align-items: center;
}
/* dev: .roam-seg padding 10px 22px, and the segments are weighted rather than
   equal — Where takes two shares, Who one and a half, the two dates one each,
   because "Traverse City, Michigan" needs more room than a date. */
.roam-filter--hero .roam-filter__f {
  position: relative;
  min-width: 0;
  /* dev: 8px vertical, 22 horizontal — computed off the rendered segment, which
     is 52 tall against ours at 65.59. Its own stylesheet says 10px; the render
     says 8, and the render is what this file follows. */
  padding: 8px 22px;
  gap: 2px;
}
/* dev: .roam-seg--where 2 1 190px, the two dates 1 1 120px, --who 1.4 1 140px.
   The BASIS is the half that matters and my first pass left it out: with a 0
   basis the four segments start equal and the weights only divide the leftover,
   which is not the same arrangement. It is also what makes the bar wrap sanely
   when it runs out of room — see the phone rule. */
.roam-filter--hero { flex-wrap: wrap; }
.roam-filter--hero .roam-filter__f { flex: 1 1 120px; }
.roam-filter--hero .roam-filter__f:nth-child(1) { flex: 2 1 190px; }
.roam-filter--hero .roam-filter__f:nth-child(4) { flex: 1.4 1 140px; }
/* A 34px LINE, not a full-height border. The same mistake as the docked pill's
   divider and the same fix: border-left runs the whole line box, which on a
   segment with 10px of vertical padding is half again too long.

   A ::before here, where the docked pill got real elements — because there the
   phone layout has to hide one divider and not the other, and a pseudo-element
   cannot be addressed on its own. Nothing hides these. */
.roam-filter--hero .roam-filter__f + .roam-filter__f::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 34px;
  background: var(--roam-border-tag);
}
.roam-filter--hero .roam-filter__f label {
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  /* 1.2, not the body's 1.6. Measured: the dev bar is 68 tall and ours was 82,
     and all 14 points of that are inside one segment — its label was taking
     17.6px of line box for 11px of type and its value 26px for 14px. A label
     and a value on one line each do not need leading; the bar's own padding is
     the air. */
  line-height: 1.2;
  /* dev: 1.5px, written in px because that is what it paints. The nearest em
     token is 0.12em, which is 1.32 at 11px — close, and wrong. */
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--roam-text-muted);
}
/* The inputs are the pill, so they carry none of their own chrome. dev: the
   value line is 14/600 at full strength, against the label's muted 11. */
.roam-filter--hero .roam-filter__f input,
.roam-filter--hero .roam-filter__f select {
  border: 0;
  background: none;
  padding: 0;
  /* 26 was a guess at a comfortable control height and it is 9px of the 14 the
     bar was over. dev: 10 + 13.2 + 2 + 16.8 + 10 = 52 for the segment, 68 for
     the bar. */
  min-height: 0;
  line-height: 1.2;
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-text-primary);
  width: 100%;
}
.roam-filter--hero .roam-filter__go { flex: 0 0 auto; margin-left: auto; }
.roam-filter--hero .roam-filter__submit { width: 52px; height: 52px; }  /* dev: 52 */

/* ── Making the native controls read as the dev site's value line ──────────
   The dev bar's four segments are buttons showing text. Ours are real form
   controls, and each one paints something of its own that the design does not
   ask for. Three of the four can be made identical; the fourth is honest about
   where it cannot.

   THE SELECT'S CARET. appearance:none removes the browser's arrow, so "Anywhere"
   renders as the same 14/600 line the dev site paints. No caret is added back:
   the dev segment has none, and a select that looks like text still opens on
   click and still reaches the keyboard. */
.roam-filter--hero select {
  appearance: none;
  -webkit-appearance: none;
  background-image: none;
  cursor: pointer;
}

/* THE NUMBER FIELD'S SPINNER. Removed, and the empty state now says "Add
   guests" rather than showing a bare 2. The placeholder is painted at the
   VALUE's own weight and colour rather than the browser's grey, because on this
   bar an empty segment is not a hint — it is the segment's current answer, and
   the dev site paints it at full strength. */
.roam-filter--hero input[type='number'] {
  -moz-appearance: textfield;
  appearance: textfield;
}
.roam-filter--hero input[type='number']::-webkit-outer-spin-button,
.roam-filter--hero input[type='number']::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.roam-filter--hero input::placeholder {
  color: var(--roam-text-primary);
  font-weight: var(--roam-fw-semibold);
  opacity: 1;              /* Firefox dims placeholders by default. */
}

/* THE DATE FIELDS, AND THIS IS THE ONE THAT CANNOT BE MADE EXACT EVERYWHERE.
   An empty date input paints mm/dd/yyyy, and that text is browser UI rather
   than content: `placeholder` does not apply to type="date", and the parts that
   draw it are reachable only through -webkit- pseudo-elements. So:

     Chrome, Edge, Safari, and every mobile browser on either engine — exact.
       The browser's own text is hidden while the field is empty and "Add dates"
       is painted over it in the value's own type. The input is untouched
       underneath: same element, same native picker, same date wheel on a phone,
       same value in the same query string.

     Firefox — mm/dd/yyyy stays. Its date input exposes none of these
       pseudo-elements, and the alternatives all cost more than they buy: a text
       input with a placeholder would match the words and lose the picker, and a
       JavaScript calendar would be a third date UI to keep in step.

   data-empty is written by assets/js/roam-header.js. With JavaScript off the
   attribute is absent, the browser's own affordance shows, and the form still
   submits — the words are the enhancement, not the function. */
.roam-filter--hero input[type='date'] {
  position: relative;
  cursor: pointer;
}
.roam-filter--hero input[type='date'][data-empty]::-webkit-datetime-edit {
  color: transparent;
}
.roam-filter--hero input[type='date'][data-empty]:focus::-webkit-datetime-edit {
  color: var(--roam-text-primary);
}
.roam-filter--hero input[type='date']::-webkit-calendar-picker-indicator {
  /* The browser's little calendar glyph is not on the dev site's bar. It stays
     in the layout at zero opacity rather than display:none, so the whole
     segment remains a click target for the picker. */
  opacity: 0;
  cursor: pointer;
}
/* THE WORDS ARE AN ELEMENT, NOT A `content:` STRING. The obvious version of
   this is ::after { content: 'Add dates' }, and it puts a piece of English copy
   in a stylesheet where no translator will find it and no editor will look. The
   span is rendered by inc/search.php, goes through the same __() every other
   string on this site does, and is aria-hidden because the input beside it
   already has a label. */
.roam-filter__ph { display: none; }
.roam-filter--hero input[type='date'][data-empty]:not(:focus) ~ .roam-filter__ph {
  display: block;
  position: absolute;
  left: 22px;                       /* the segment's own padding */
  bottom: 10px;                     /* ditto, so it lands on the value line */
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  line-height: 1.2;                 /* the input's own, so they sit on one line */
  color: var(--roam-text-primary);
  pointer-events: none;             /* the click belongs to the input under it */
}

.roam-filter__submit {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border: 0;
  border-radius: var(--roam-r-full);
  background: var(--roam-gold);
  color: var(--roam-forest);
  cursor: pointer;
  transition: background-color 0.25s var(--roam-easing);
}
.roam-filter__submit:hover { background: var(--roam-gold-active); }

/* THE PHONE BAR WRAPS, IT DOES NOT STACK — and this was the single largest
   thing wrong with the hero.

   It used to go flex-direction: column, four fields one above another with a
   full-width button under them, and it measured 342px tall at 390. The dev
   site's is 133. That 209px difference is why our hero came out 881 tall in an
   844 viewport: the peek was MINUS 156, so nothing of the section below showed
   at all, on the one screen size where a visitor most needs telling there is
   more page.

   Wrapping is the dev site's arrangement and it falls out of the flex bases
   above without a second layout: 190 + 120 wraps to two per row at 325, the go
   button takes the end of the last row, and the whole thing is two rows tall.
   The vertical rules become horizontal ones between the rows. */
@media (max-width: 760px) {
  .roam-filter--hero {
    border-radius: var(--roam-r-xl);
    padding: var(--roam-sp-8);
    row-gap: var(--roam-sp-4);
  }
  .roam-filter--hero .roam-filter__f { padding: var(--roam-sp-8) var(--roam-sp-12); }
  /* ONE BASIS FOR ALL FOUR, AND IT IS ARITHMETIC. At 390 the bar has 309 points
     of usable width, and the desktop bases put 190 + 120 = 310 on the first row
     — one point over, so `where` took a row to itself and the bar became THREE
     rows and 174 tall against the dev site's 133. That single point cost 50px
     of hero and most of the peek below it.

     120 makes every row hold two: where + arrive on the first, leave + guests
     on the second with the 52px circle beside them, 292 of 309. Two rows, and
     the arithmetic is written down so the next person to change a basis can see
     what it has to clear. */
  .roam-filter--hero .roam-filter__f,
  .roam-filter--hero .roam-filter__f:nth-child(1),
  .roam-filter--hero .roam-filter__f:nth-child(4) { flex: 1 1 120px; }
  /* The 34px vertical hairline is wrong between two segments that are now side
     by side in a wrapped row of two — it is right for the pair that share a row
     and wrong for the pair that do not, and CSS cannot tell them apart. So the
     rules come off entirely at this width; the wrap itself is the separation. */
  .roam-filter--hero .roam-filter__f + .roam-filter__f::before { display: none; }
  .roam-filter--hero input[type='date'][data-empty]:not(:focus) ~ .roam-filter__ph { left: 12px; bottom: 8px; }
  .roam-filter--hero .roam-filter__go { margin-left: auto; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE WHERE / WHEN / WHO SEARCH WIDGET  (assets/js/roam-search.js)
   Ported value-for-value from the design of record. The native .roam-filter
   form above is the no-JS fallback; when the widget mounts it takes [hidden] and
   this takes over. One widget, two mounts — the hero and the header overlay —
   so every rule here serves both.
   ═══════════════════════════════════════════════════════════════════════════ */
/* The no-JS form sets display:flex, which beats [hidden]'s UA display:none — so
   the widget's setAttribute('hidden') needs this attribute rule (specificity
   0,2,0 over the class's 0,1,0) to actually take it off screen once mounted. */
.roam-filter[hidden] { display: none; }
.roam-search-slot { width: 100%; max-width: 980px; margin: 0 auto 0 0; font-family: var(--roam-font-body); }
.roam-search { position: relative; font-family: var(--roam-font-body); }
@keyframes roam-fadeDown { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

.roam-searchbar {
  background: var(--roam-white);
  border-radius: var(--roam-r-full);
  padding: 8px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  box-sizing: border-box;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
  transition: background-color 0.2s ease;
}
.roam-searchbar[data-panel-open] .roam-seg__div { display: none; }
.roam-seg {
  min-width: 0;
  text-align: left;
  border: 0;
  padding: 10px 22px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: transparent;
  border-radius: var(--roam-r-full);
  transition: background-color 0.2s ease;
}
.roam-seg--where { flex: 2 1 190px; }
.roam-seg--in, .roam-seg--out { flex: 1 1 120px; }
.roam-seg--who { flex: 1.4 1 140px; }
.roam-seg:hover { background: rgba(198, 151, 91, 0.12); }
.roam-seg.is-active { background: rgba(198, 151, 91, 0.20); }
.roam-seg__label {
  font-size: var(--roam-fs-11);
  /* Standard weight — client instruction 2026-08-02. The field titles (Where,
     Check in, Check out, Who) were semibold and read heavier than the values
     they label; the tracked uppercase already sets them apart. */
  font-weight: var(--roam-fw-normal);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--roam-text-muted);
}
.roam-seg__val {
  font-size: var(--roam-fs-14);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.roam-seg__div { width: 1px; height: 34px; background: var(--roam-border-tag); flex: 0 0 auto; }
.roam-search-go {
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--roam-gold);
  border: 0;
  margin-left: auto;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.25s ease;
}
.roam-search-go:hover { background: var(--roam-gold-active); }
.roam-search-go svg { width: 20px; height: 20px; }

/* ── Popover shell + panel host ─────────────────────────────────────────── */
.roam-search-panelhost { position: absolute; left: 0; right: 0; top: 100%; z-index: 1000; }
/* The next section peeks up into the hero (--roam-hero-peek) and, being later in
   the DOM, painted over the dropdown that hangs below the search bar. TWO things
   were hiding it: the hero's own `overflow: hidden` CLIPPED the dropdown at the
   hero's bottom edge, and the following section painted OVER what escaped. So
   while a field is open, release the hero's clip and lift the search host above
   the page. Both are scoped to the open state, so nothing is affected otherwise.
   (The hero's photo/tint fill the hero exactly, so visible overflow shows nothing
   extra — only the dropdown, which is meant to escape.) */
.roam-hero:has( [data-panel-open] ) { overflow: visible; }
.roam-hero__search { position: relative; }
.roam-hero__search:has( [data-panel-open] ) { z-index: 1000; }
.roam-pop {
  position: absolute;
  top: 8px;
  background: var(--roam-white);
  border-radius: 24px;
  box-shadow: 0 20px 60px rgba(27, 43, 39, 0.3);
  animation: roam-fadeDown 0.2s ease both;
}
.roam-pop--where { left: 0; width: 400px; max-width: calc(100vw - 50px); padding: 8px; overflow: hidden; }
.roam-pop__scroll { max-height: 414px; overflow-y: auto; padding: 6px; }
.roam-pop__head {
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: rgba(27, 43, 39, 0.55);
  padding: 6px 10px 10px;
}
.roam-where-row {
  display: flex;
  gap: 14px;
  align-items: center;
  padding: 9px 10px;
  border-radius: 12px;
  cursor: pointer;
  transition: background-color 0.2s ease;
}
.roam-where-row:hover { background: var(--roam-cream); }
.roam-where-ic {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  border: 1.5px solid;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.roam-where-ic svg { width: 18px; height: 18px; }
.roam-where-ic--gold { border-color: rgba(198, 151, 91, 0.4); }
.roam-where-ic--teal { border-color: rgba(91, 143, 168, 0.4); }
.roam-where-nm { display: block; font-size: var(--roam-fs-14); font-weight: var(--roam-fw-semibold); color: var(--roam-forest); }
.roam-where-sub { display: block; font-size: var(--roam-fs-12); color: rgba(27, 43, 39, 0.55); margin-top: 2px; }

/* ── Calendar (dual month) ──────────────────────────────────────────────── */
.roam-pop--dates { left: 0; right: 0; max-width: 980px; padding: 26px 28px; }
.roam-cal__body { display: flex; gap: 40px; flex-wrap: wrap; justify-content: center; }
.roam-cal__col { flex: 1 1 300px; min-width: 0; }
.roam-cal__mhead { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.roam-cal__title { font-family: var(--roam-font-display); font-weight: 700; font-size: 17px; color: var(--roam-forest); }
.roam-cal__nav {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--roam-border-tag);
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.roam-cal__nav svg { width: 15px; height: 15px; }
.roam-cal__nav.is-disabled { opacity: 0.3; cursor: default; }
.roam-cal__spacer { width: 34px; flex: 0 0 auto; }
/* The weekday row, day grid and cells are defined ONCE, up in the shared
   calendar block — this modal (and the hero-search calendar) reuse the same
   .roam-cal__dow / .roam-cal__grid / .roam-cal__cell rules, so all three read
   identically. Only the modal's month chrome (nav, spacer, title) is local. */
.roam-cal__foot { display: flex; justify-content: flex-end; margin-top: 14px; }
.roam-cal__done {
  background: var(--roam-forest);
  color: var(--roam-cream);
  border: 0;
  padding: 9px 20px;
  border-radius: 9999px;
  font-family: var(--roam-font-body);
  font-size: var(--roam-fs-13);
  font-weight: var(--roam-fw-semibold);
  cursor: pointer;
}

/* ── Guests (steppers) ──────────────────────────────────────────────────── */
.roam-pop--guests { right: 0; width: 380px; max-width: calc(100vw - 50px); padding: 8px 26px; }
.roam-guest-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 0;
  border-bottom: 1px solid var(--roam-border-cream);
}
.roam-guest-row.is-last { border-bottom: 0; }
.roam-guest-nm { display: block; font-size: var(--roam-fs-16); font-weight: 700; color: var(--roam-forest); }
.roam-guest-sub { display: block; font-size: var(--roam-fs-13); color: rgba(27, 43, 39, 0.55); margin-top: 2px; }
.roam-guest-sub.is-link { text-decoration: underline; }
.roam-guest-ctl { display: flex; align-items: center; gap: 14px; }
.roam-guest-val { min-width: 20px; text-align: center; font-size: var(--roam-fs-15); font-weight: var(--roam-fw-semibold); color: var(--roam-forest); }
.roam-step {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--roam-gold);
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.roam-step svg { width: 14px; height: 14px; }

/* ── The hero's own phone pill (built by the widget on the home hero) ─────── */
.roam-hs-pill {
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: 100%;
  background: var(--roam-white);
  border: 0;
  border-radius: var(--roam-r-full);
  padding: 8px 8px 8px 20px;
  cursor: pointer;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.28);
  font-family: var(--roam-font-body);
}
.roam-hs-fields, .roam-hs-meta { display: flex; align-items: center; gap: 10px; min-width: 0; }
.roam-hs-val { font-size: 13px; font-weight: var(--roam-fw-medium); color: var(--roam-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.roam-hs-where { font-weight: 700; color: var(--roam-text-primary); }
.roam-hs-div { width: 1px; height: 18px; background: var(--roam-border-tag); flex: 0 0 auto; }
.roam-hs-go { width: 40px; height: 40px; border-radius: 50%; background: var(--roam-gold); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.roam-hs-go svg { width: 15px; height: 15px; }

/* HIDDEN ON DESKTOP — and this MUST come after the base .roam-hs-pill rule.
   The hero pill carries both classes (roam-hs-pill roam-hs-pill--hero); when the
   hide sat before .roam-hs-pill{display:flex}, that base rule won by source order
   at equal specificity and the collapsed pill printed on top of the expanded
   hero bar on desktop. Client-reported. The ≤759 rule below turns it back on for
   the phone, where the pill is the whole search. */
.roam-hs-pill--hero { display: none; }

/* ── The header search overlay + scrim ──────────────────────────────────── */
.roam-hdrsearch__scrim {
  position: fixed;
  inset: 0;
  z-index: 990;
  background: rgba(27, 43, 39, 0.42);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
}
.roam-hdrsearch {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 991;
  display: flex;
  justify-content: center;
  padding: 8px 24px 0;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-8px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.roam-hdrsearch-open .roam-hdrsearch__scrim { opacity: 1; pointer-events: auto; }
.roam-hdrsearch-open .roam-hdrsearch { opacity: 1; pointer-events: auto; transform: none; }
.roam-hdrsearch .roam-search { width: 100%; }
.roam-hdrsearch .roam-searchbar { box-shadow: 0 18px 48px rgba(27, 43, 39, 0.28); }
/* The panel caps the width on desktop and becomes the full-screen card on a
   phone (see the ≤767 block); the footer is a phone-only static action bar. */
.roam-hdrsearch__panel { width: 100%; max-width: 980px; display: flex; flex-direction: column; min-height: 0; }
.roam-hdrsearch__body { min-height: 0; }
.roam-hdrsearch__foot { display: none; }

/* ── Responsive ─────────────────────────────────────────────────────────── */
@media (min-width: 768px) {
  .roam-searchbar { height: 68px; }
  .roam-seg { padding-top: 8px; padding-bottom: 8px; }
  .roam-seg__label { line-height: 15px; }
  .roam-seg__val { line-height: 19px; }
}
/* ≥760 joined range highlight — the active in/out corners meet, and the who
   highlight runs behind the 52px submit. */
@media (min-width: 760px) {
  .roam-seg--in.is-active { border-top-right-radius: 0; border-bottom-right-radius: 0; }
  .roam-seg--out.is-active { border-top-left-radius: 0; border-bottom-left-radius: 0; }
  .roam-seg--who { flex: 1 1 auto; }
  .roam-seg--who.is-active { margin-right: -52px; padding-right: 74px; }
  .roam-search-go { margin-left: 0; }
}
/* ≤759 stacked bar — the four-segment row becomes a column, the panel drops
   into the column as an accordion, and the popover pins to the bar's width. */
@media (max-width: 759px) {
  .roam-searchbar { border-radius: 24px; gap: 6px; }
  /* Tighter than desktop — the dev modal packs the stacked fields closer, so the
     whole set (Where, the dates, Who, and the guest steppers below) sits up the
     screen and the last stepper clears the fold. */
  .roam-seg { flex: 1 1 100% !important; border-radius: 16px; padding: 6px 20px; }
  /* Check in and check out share a row, left and right — the dev site's mobile
     modal. Where and Who keep their own full-width rows above and below; the
     6px bar gap sets the two dates apart and lines the row up to full width. */
  .roam-seg--in, .roam-seg--out { flex: 1 1 calc(50% - 3px) !important; }
  .roam-guest-row { padding: 13px 0; }
  .roam-seg__div { display: none; }
  .roam-search-go { width: 100%; height: 48px; border-radius: 16px; margin-left: 0; margin-top: 4px; }
  .roam-search-inline { flex: 1 1 100%; margin: 2px 0 6px; }
  .roam-search-inline .roam-pop {
    position: static !important;
    left: auto !important; right: auto !important; top: auto !important;
    width: auto !important; max-width: none !important;
    box-shadow: none; border: 1px solid var(--roam-border-tag); border-radius: 16px; animation: none;
  }
  .roam-search-inline .roam-pop--dates .roam-cal__body { flex-direction: column; }
  /* On a phone the hero shows its pill; the expanded bar and its panelhost hide.
     Scoped to the hero mount — the overlay keeps its full bar even on a phone. */
  .roam-hs-pill--hero { display: flex; min-height: 56px; padding: 8px 8px 8px 20px; }
  .roam-hs-pill--hero .roam-hs-where { font-size: 15px; }
  .roam-hero__search .roam-searchbar,
  .roam-hero__search .roam-search-panelhost { display: none; }
  .roam-hdrsearch .roam-pop, .roam-search .roam-pop { left: 0 !important; right: 0 !important; width: auto !important; max-width: none !important; }
}

/* THE MOBILE SEARCH IS A FULL-SCREEN MODAL — a pop-up locked to the viewport
   minus the header spacing (its top is set to the header's bottom in JS) and the
   page gutters. A scrolling body holds the Where/When/Who sections and the dates
   calendar (single column), and a static footer anchors Clear + Search. */
@media (max-width: 767px) {
  .roam-hdrsearch {
    bottom: 0;
    padding: 8px var(--roam-hdr-x) calc(var(--roam-hdr-x) + env(safe-area-inset-bottom));
    align-items: stretch;
  }
  .roam-hdrsearch__panel {
    max-width: none;
    max-height: 100%;
    background: var(--roam-white);
    border-radius: var(--roam-r-xl);
    box-shadow: 0 24px 60px rgba(15, 26, 22, 0.4);
    overflow: hidden;
  }
  .roam-hdrsearch__body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    /* Clips — the OPEN card scrolls internally, so When/Who stay pinned in view
       the way the design's sheet holds them. */
    overflow: hidden;
    padding: var(--roam-sp-16);
  }

  /* ── The Where / When / Who cards ──────────────────────────────────────────
     The stacked bar and its panel host are replaced by three cards; the widget
     root and the cards column become the body's flex column. One card is open
     (Where by default) and flexes to fill; the other two collapse to a header
     row and sit under it. */
  .roam-hdrsearch .roam-searchbar,
  .roam-hdrsearch .roam-search-panelhost { display: none; }
  .roam-hdrsearch .roam-search,
  .roam-hdrsearch .roam-cards {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
  }
  .roam-hdrsearch .roam-cards { gap: var(--roam-sp-12); }
  .roam-hdrsearch .roam-card {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    background: var(--roam-white);
    border: 1px solid var(--roam-border-cream);
    border-radius: var(--roam-r-lg);
    box-shadow: var(--roam-shadow-rest);
    overflow: hidden;
  }
  .roam-hdrsearch .roam-card.is-open { flex: 1 1 auto; min-height: 0; }
  .roam-hdrsearch .roam-card__head {
    flex: 0 0 auto;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--roam-sp-16);
    padding: 18px 20px;
    border: 0;
    background: transparent;
    font: inherit;
    text-align: left;
    cursor: pointer;
  }
  .roam-hdrsearch .roam-card.is-open .roam-card__head { padding-bottom: var(--roam-sp-8); }
  .roam-hdrsearch .roam-card__label {
    font-size: var(--roam-fs-13);
    color: var(--roam-text-muted);
  }
  .roam-hdrsearch .roam-card__value {
    font-size: var(--roam-fs-15);
    font-weight: var(--roam-fw-semibold);
    color: var(--roam-text-primary);
  }
  .roam-hdrsearch .roam-card__title {
    font-family: var(--roam-font-display);
    font-size: var(--roam-fs-22);
    line-height: var(--roam-lh-heading);
    color: var(--roam-text-primary);
  }
  .roam-hdrsearch .roam-card__body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 0 var(--roam-sp-16) var(--roam-sp-16);
  }
  /* Inside a card the panel is static content, not a floating dropdown — strip
     the popover's own position, shadow, radius and width caps. */
  .roam-hdrsearch .roam-card .roam-pop {
    position: static;
    top: auto;
    width: auto;
    max-width: none;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    animation: none;
  }
  .roam-hdrsearch .roam-card .roam-pop__scroll {
    max-height: none;
    overflow: visible;
    padding: 0;
  }
  .roam-hdrsearch .roam-card .roam-cal__foot { display: none; }
  /* The two calendar months stack, so the picker fits the column. */
  .roam-hdrsearch .roam-cal__body { flex-direction: column; gap: var(--roam-sp-20); }

  .roam-hdrsearch__foot {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--roam-sp-16);
    padding: var(--roam-sp-12) var(--roam-sp-16);
    border-top: 1px solid var(--roam-border-cream);
    background: var(--roam-white);
  }
  .roam-hdrsearch__clear {
    padding: var(--roam-sp-12) var(--roam-sp-8);
    border: 0;
    background: transparent;
    color: var(--roam-forest);
    font: inherit;
    font-weight: var(--roam-fw-semibold);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
  }
  .roam-hdrsearch__search {
    flex: 0 0 auto;
    min-height: 48px;
    padding: 0 var(--roam-sp-24);
    border: 0;
    border-radius: var(--roam-r-full);
    background: var(--roam-gold);
    color: var(--roam-forest);
    font: inherit;
    font-weight: var(--roam-fw-semibold);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: var(--roam-sp-8);
  }
  .roam-hdrsearch__search svg { display: block; }
  .roam-hdrsearch__search:hover { background: var(--roam-gold-active); }

  body.roam-hdrsearch-open { overflow: hidden; }
}

/* ── Why book direct — the contained gradient card ───────────────────────── */
/* A ROUNDED CARD, IN MEASURE, NOT A FULL-BLEED BAND. The design contains this
   section in a gradient card sitting inside the page measure, the opposite of
   how it was built — it was the one section made full-bleed while the three the
   design DOES bleed (reviews, notes, contact) were trapped in the measure. It
   is emitted inside .roam-wrap now, so the wrap sets its width; this rule is the
   card itself: the 145deg forest gradient, a 20px radius, and the design's
   clamp padding.

   display:flex with a 48px gap and flex-wrap is the two-column split — copy and
   stats on the left, the comparison panel on the right — that collapses to a
   stack on a phone on its own. */
.roam-why {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-wrap: wrap;
  gap: var(--roam-sp-48);
  background: var(--roam-surface-stat);
  color: var(--roam-cream);
  border-radius: var(--roam-r-xl);
  padding: clamp(20px, 2.5vw, 32px);
  margin-block: var(--roam-sp-40);
}
/* The left column takes the remaining width and never drops below a readable
   measure; the panel (below) claims its own basis, so these two are what wrap. */
.roam-why__copy {
  position: relative;
  flex: 1 1 360px;
  min-width: 0;
}
/* The heading carries no gold mark here — the eyebrow above it does, in the
   lifted gold the dark ground wants. So no ::before and no clearance for one. */
.roam-why__title {
  margin: var(--roam-flow-coupled) 0 var(--roam-flow-related);
  font-family: var(--roam-font-display);
  font-weight: 700;
  font-size: var(--roam-type-title-size);
  line-height: var(--roam-type-title-lh);
}
.roam-eyebrow--lifted { color: var(--roam-gold-lifted); }
.roam-eyebrow--lifted::before { background: var(--roam-gold-lifted); }
/* Cream on the dark ground, and a full break below because stats follow. */
.roam-why__lede {
  margin: 0 0 var(--roam-flow-block);
  /* Normal body size (15px), not the 18px subtitle it was — and the same dimmed
     cream the stat labels use, so the paragraph reads as body copy on the dark
     ground rather than near-white. */
  font-size: var(--roam-type-body-size);
  line-height: var(--roam-type-body-lh);
  color: var(--roam-dark-text-body);
}

/* ── Amenity chips ───────────────────────────────────────────────────────── */
.roam-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--roam-sp-8);
  margin-top: var(--roam-sp-24);
}
.roam-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--roam-sp-8);
  padding: var(--roam-sp-8) var(--roam-sp-16);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-full);
  font-size: var(--roam-fs-14);
  text-decoration: none;
  transition: border-color 0.3s var(--roam-easing), background-color 0.3s var(--roam-easing);
}
.roam-chip:hover { border-color: var(--roam-gold); background: var(--roam-white); }
.roam-chip__n { font-size: var(--roam-fs-11); color: var(--roam-text-muted); }

/* ── Amenity browse tiles ────────────────────────────────────────────────────
   The design's amenity row is bordered text cards, name over "N homes →" — the
   two-line tile the regions and types use, minus the photo. These are their own
   class rather than a variant of .roam-tile, because .roam-tile is now a photo
   frame (240/170 tall, position relative, overflow hidden) and an amenity tile
   is a short bordered box; sharing the class would mean overriding most of it. */
/* Fixed columns rather than auto-fill so the twelve tiles read as a deliberate
   grid: two across on a phone, four on a tablet, six on desktop. */
.roam-amtiles {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--roam-sp-16);
}
@media (min-width: 768px) {
  .roam-amtiles { grid-template-columns: repeat(4, 1fr); }
}
@media (min-width: 1024px) {
  .roam-amtiles { grid-template-columns: repeat(6, 1fr); }
}
.roam-amtile {
  display: flex;
  align-items: center;
  gap: var(--roam-sp-12);
  padding: 18px 20px;
  background: var(--roam-white);
  border: 1px solid var(--roam-border-tag);
  border-radius: var(--roam-r-lg);
  text-decoration: none;
  transition: border-color 0.25s var(--roam-easing), transform 0.25s var(--roam-easing);
}
.roam-amtile:hover { border-color: var(--roam-gold); transform: var(--roam-lift-hover); }
/* Column one: the amenity's own icon, in forest, sized to the card's two text
   lines. flex:0 0 auto so the label column takes the rest and the row height
   stays what it was. */
.roam-amtile__ico { flex: 0 0 auto; display: flex; color: var(--roam-forest); }
.roam-amtile__ico .roam-amen__ico { width: 26px; height: 26px; }
.roam-amtile__body { min-width: 0; }
.roam-amtile__name {
  display: block;
  font-size: var(--roam-fs-15);
  font-weight: var(--roam-fw-semibold);
  color: var(--roam-text-primary);
}
.roam-amtile__n {
  display: block;
  margin-top: 5px;
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
  color: var(--roam-text-muted);
}
.roam-amtile__count { color: var(--roam-text-primary); }

/* ── Taxonomy tiles ──────────────────────────────────────────────────────── */
/* PHOTO TILES — the design draws a picture with the name and count over a scrim
   at the bottom, and that is what these are now. The old text-box tile carried a
   comment arguing AGAINST a photo, on the grounds that one house cannot stand
   for a county; the design's answer is that the photograph is of the PLACE, not
   a property, and the tile is the picture. Until we have those photographs the
   media is a marked placeholder — see roam_tile_placeholder() — so the tile has
   its real shape and reads as unfinished rather than as a text box by choice.

   Two sizes: regions are the taller tile (240) on a wider track, types the
   shorter (170) on a narrower one — the design's own dimensions. */
.roam-tiles {
  display: grid;
  gap: var(--roam-sp-20);
  margin-top: var(--roam-flow-block);
}
.roam-tiles--region { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.roam-tiles--type { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: var(--roam-sp-16); }
/* On a term archive the tiles sit between the heading and the filter bar, so
   they need air beneath them as well as above — on the homepage the section's
   own padding supplies it. */
.roam-tiles--arch { margin-bottom: var(--roam-sp-24); }

/* Browse-by-type tiles reuse the region tile exactly — same 3:2 frame, same
   name + count on a bottom scrim — and change only the media layer: a coloured
   gradient cover carrying the property-type icon in place of a photograph. The
   three cover layers (diagonal sheen, warm gold glow, then the per-tile 180°
   gradient) are identical across tiles; only the two stops vary, set per tile
   via --cover-c1 / --cover-c2. */
.roam-tile__cover {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background:
    linear-gradient(135deg, transparent 30%, rgba(250, 246, 241, 0.08) 50%, transparent 70%),
    radial-gradient(circle at 70% 30%, rgba(198, 151, 91, 0.4) 0%, transparent 50%),
    linear-gradient(180deg, var(--cover-c1, #4a6c5a), var(--cover-c2, #1B2B27));
  transition: transform 0.4s var(--roam-easing);
}
.roam-tile:hover .roam-tile__cover { transform: scale(1.04); }
.roam-tile__typeico { color: #faf6f1; display: flex; transform: translateY(-8px); opacity: 0.92; }
.roam-tile__typeico .roam-type__ico { width: 56px; height: 56px; }

.roam-tile {
  position: relative;
  display: block;
  height: 240px;
  border-radius: var(--roam-r-xl);
  overflow: hidden;
  text-decoration: none;
  transition: transform 0.3s var(--roam-easing), box-shadow 0.3s var(--roam-easing);
}
.roam-tiles--type .roam-tile { height: auto; aspect-ratio: 3 / 2; }
/* The "Where We Roam" region tiles are 3:2 — client instruction. Their height
   follows the column width at that ratio rather than the fixed 240px the other
   tile rows use, so every region photo keeps the same frame as it reflows. */
.roam-tiles--region .roam-tile { height: auto; aspect-ratio: 3 / 2; }
.roam-tile:hover { transform: var(--roam-lift-hover); box-shadow: var(--roam-shadow-hover); }
/* The photograph fills the frame and takes the site's photo grade. */
.roam-tile__img {
  position: absolute;
  inset: 0;
  background-image: var(--roam-tile-img);
  background-size: cover;
  background-position: center;
  filter: var(--roam-photo-filter);
  transition: transform 0.4s var(--roam-easing);
}
.roam-tile:hover .roam-tile__img { transform: scale(1.04); }
/* The placeholder ground fills the same frame and carries its tag centred, so a
   tile with no photo still has the tile's shape and says what it is. */
.roam-tile__img--placeholder {
  display: grid;
  place-items: center;
  background:
    repeating-linear-gradient(-45deg, rgba(27, 43, 39, 0.06) 0 14px, transparent 14px 28px),
    var(--roam-sand);
  filter: none;
}
/* The scrim is the design's own two-stop wash — nearly clear at the top, deep
   forest at the base — so the caption reads on any photograph under it. */
.roam-tile__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(27, 43, 39, 0.05) 30%, rgba(27, 43, 39, 0.84) 100%);
}
/* The placeholder needs no darkening wash — its ground is already light and the
   tag carries its own. Dropping the scrim keeps the "photo to come" legible. */
.roam-tile__img--placeholder + .roam-tile__scrim { display: none; }
.roam-tile__cap {
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: 18px;
  color: var(--roam-cream);
}
/* On a placeholder tile the caption sits on a light ground, so it takes forest
   ink instead of cream — the one thing that changes when there is no photo. */
.roam-tile__img--placeholder ~ .roam-tile__cap { color: var(--roam-forest); }
.roam-tile__name {
  display: block;
  font-family: var(--roam-font-display);
  font-weight: 700;
  font-size: var(--roam-fs-22);
}
.roam-tiles--type .roam-tile__name { font-size: var(--roam-fs-20); }
/* The count line is uppercase and tracked, the figure in the caption's own
   weight and "homes →" a touch quieter. */
.roam-tile__n {
  display: block;
  margin-top: 5px;
  font-size: var(--roam-fs-11);
  font-weight: var(--roam-fw-semibold);
  letter-spacing: var(--roam-ls-sm);
  text-transform: uppercase;
  opacity: 0.75;
}

/* ── Utility ─────────────────────────────────────────────────────────────── */
.screen-reader-text {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ══ THE SEARCH-RESULTS MAP (Leaflet, keyless) ══════════════════════════════
   The Leaflet canvas fills the map panel the two map views open; each result is
   an Airbnb-style price pill pinned to its synced coordinates. */
.roam-map__canvas {
  width: 100%;
  height: 100%;
  align-self: stretch;
  justify-self: stretch;
  border-radius: var(--roam-r-lg);
  overflow: hidden;
  z-index: 0;
}
/* Strip Leaflet's default white icon box — the pill is the whole marker. */
.leaflet-div-icon.roam-pin { width: auto !important; height: auto !important; background: transparent; border: 0; }
.roam-pin__d {
  display: inline-block;
  transform: translate(-50%, -50%);
  padding: 4px 10px;
  border-radius: var(--roam-r-full);
  background: var(--roam-white);
  border: 1px solid var(--roam-border-tag);
  box-shadow: 0 2px 6px rgba(27, 43, 39, 0.3);
  color: var(--roam-text-primary);
  font-family: var(--roam-font-body);
  font-size: 13px;
  font-weight: var(--roam-fw-semibold);
  line-height: 1;
  white-space: nowrap;
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.roam-pin:hover .roam-pin__d { background: var(--roam-forest); border-color: var(--roam-forest); color: var(--roam-cream); z-index: 1; }
.roam-pin__pop { display: block; text-decoration: none; color: var(--roam-text-primary); font-family: var(--roam-font-body); }
.roam-pin__pop strong { display: block; font-size: var(--roam-fs-14); }
.roam-pin__pop span { color: var(--roam-text-secondary); font-size: var(--roam-fs-13, 13px); }

/* ── The overlaid map-card row (full-map view) ─────────────────────────────
   Small property cards scrolling along the bottom of the full map — no ground
   behind the row and no scrollbar, laid directly over the map. The map anchors
   them; the row is hidden on the half map, where the grid already shows cards. */
[data-roam-map] { position: relative; }
.roam-mapcards {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--roam-sp-16);
  z-index: 500;
  display: flex;
  gap: var(--roam-sp-12);
  padding: 0 var(--roam-sp-16);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.roam-mapcards::-webkit-scrollbar { display: none; }
.roam-view-halfmap .roam-mapcards { display: none; }

.roam-mapcard {
  flex: 0 0 clamp(240px, 80vw, 300px);
  scroll-snap-align: center;
  display: flex;
  gap: var(--roam-sp-12);
  padding: var(--roam-sp-8);
  background: var(--roam-white);
  border-radius: var(--roam-r-lg);
  /* Reads as clearly floating above the map — a deeper, softer drop than a
     resting card gets, since these sit ON the map rather than on the page. */
  box-shadow: 0 10px 30px rgba(27, 43, 39, 0.30), 0 2px 6px rgba(27, 43, 39, 0.18);
  text-decoration: none;
  color: var(--roam-text-primary);
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.roam-mapcard.is-active,
.roam-mapcard:hover { box-shadow: 0 16px 40px rgba(27, 43, 39, 0.38), 0 3px 8px rgba(27, 43, 39, 0.22); transform: translateY(-3px); }
.roam-mapcard__media {
  flex: 0 0 92px;
  aspect-ratio: 1 / 1;
  border-radius: var(--roam-r-sm);
  overflow: hidden;
  background: var(--roam-sand);
}
.roam-mapcard__media img { width: 100%; height: 100%; object-fit: cover; filter: var(--roam-photo-filter); }
.roam-mapcard__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding-right: var(--roam-sp-4);
}
.roam-mapcard__rating {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--roam-fs-12);
  font-weight: var(--roam-fw-semibold);
}
.roam-mapcard__rating svg { color: var(--roam-gold); flex: none; }
.roam-mapcard__name {
  font-family: var(--roam-font-display);
  font-size: var(--roam-fs-15);
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.roam-mapcard__headline {
  font-size: var(--roam-fs-12);
  color: var(--roam-text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.roam-mapcard__facts {
  font-size: var(--roam-fs-12);
  color: var(--roam-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
