/**
 * Husna Villas — main.css
 *
 * Design tokens and base styles extracted exactly from
 * reference/design/brochure.css. No invented values.
 *
 * @package HusnaVillas
 */

/* ==========================================================================
   1. Design tokens
   ========================================================================== */

/*
 * Colour and type tokens live in tokens.css, which is enqueued first.
 * Only the spacing tokens below are owned by this file.
 */
:root {
	/*
	 * Section vertical rhythm, from the canonical villa design's own .sec
	 * padding — clamp(48px, 6vw, 86px). Was clamp(76px, 10vh, 140px),
	 * inherited from brochure.css: ~19% taller at desktop and over 50%
	 * taller on mobile, which is what made the page read as emptier than
	 * the design. vw rather than vh so the rhythm tracks the page's own
	 * measure rather than the height of whatever device is holding it.
	 */
	--pad-v:   clamp(48px, 6vw, 86px);

	/*
	 * Horizontal inset, matching the villa design's own .sec padding
	 * (clamp(24px,3.4vw,52px) in reference/design/husna-villa-internal
	 * page.html). 22px rather than 24px at the low end so the rendered
	 * mobile inset lands on the 23px the rest of the site already used.
	 */
	--wrap-pad: clamp(22px, 3.4vw, 52px);

	/*
	 * The READABLE COLUMN is 1240px. --wrap-max carries the inset on top
	 * of that (box-sizing:border-box is global, see the reset below), so
	 * the text column stays 1240px at full width while .wrap's own
	 * padding provides the inset.
	 *
	 * This replaces `min(1240px, 88vw)`. That version produced its inset
	 * purely from the 88vw term, which only works when .wrap is a child
	 * of something full-viewport-width. Inside a narrower container — the
	 * villa page's grid column — 1240px never binds, the vw term never
	 * applies, and the inset silently collapsed to zero, putting body
	 * text flush against the viewport edge. Padding holds the inset
	 * regardless of how wide the container happens to be.
	 */
	--wrap-max: calc(1240px + 2 * var(--wrap-pad));
}

/* ==========================================================================
   2. Reset — from brochure.css
   ========================================================================== */

*,
*::before,
*::after {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
	border-radius: 0;
}

html {
	scroll-behavior: smooth;
}

body {
	background: var(--paper);
	color: var(--ink);
	font-family: var(--ui);
	font-weight: 400;
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
}

img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

a {
	color: var(--ink);
	text-decoration: none;
}

/* :focus-visible is global in tokens.css — no link-only rule here. */

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

ul,
ol {
	list-style: none;
}

/* ==========================================================================
   3. Typography utility classes — from brochure.css
   ========================================================================== */

/*
 * .eyebrow (+ --dim / --on-dark) and the .disp display carrier live in
 * tokens.css. The old .display / .h2 / .h3 clamp scale it replaced is gone;
 * villa-head now sizes its own heading.
 */

/*
 * ⚠ .lede and .b COME FROM POST CONTENT, NOT FROM ANY TEMPLATE.
 *
 * They are typed by hand into each villa's description in wp-admin, so
 * NO grep of this repo will ever find them — searching the .php files
 * returns nothing and makes them look dead. They are not. They render
 * on the live villa pages today (curl any /accommodation/... URL and
 * grep for class="lede"). Deleting these rules strips the styling from
 * every villa description on the site.
 *
 * Before removing either one, check the RENDERED HTML of several villa
 * pages, not the templates.
 *
 * Kept on the current token scale deliberately, so they don't drift
 * back to being the last place carrying the superseded light-concept
 * values. .lede is the opening paragraph, one step up from body; .b is
 * the canonical body treatment (matches .sub in the villa design).
 */
.lede {
	font-size: var(--fs-lg);
	font-weight: 300;
	line-height: 1.6;
	color: var(--ink);
}

p.b {
	font-size: var(--fs-body);
	font-weight: 300;
	line-height: 1.65;
	color: var(--grey);
}

.cap {
	font-size: var(--fs-cta);
	font-weight: 600;
	letter-spacing: var(--ls-nav);
	text-transform: uppercase;
	color: var(--grey);
	margin-top: 12px;
}

/* ==========================================================================
   4. Layout utilities — from brochure.css
   ========================================================================== */

/*
 * Any component that also carries .wrap and needs its own vertical
 * padding must set padding-top/padding-bottom as longhands, never the
 * `padding` shorthand — the shorthand resets the horizontal padding
 * below and collapses the inset again. See .hv-tier-selector__inner.
 * (.hv-header__inner used to be the other example; it no longer carries
 * .wrap at all — the header is full bleed on every template.)
 */
.wrap {
	max-width: var(--wrap-max);
	margin: 0 auto;
	padding-left: var(--wrap-pad);
	padding-right: var(--wrap-pad);
}

.pad {
	padding: var(--pad-v) 0;
}

section {
	position: relative;
}

.frame {
	position: relative;
	overflow: hidden;
	background: var(--bone);
}

.frame img {
	transition: transform 1.2s cubic-bezier(.16, .7, .3, 1);
}

.frame:hover img {
	transform: scale(1.035);
}

/* ==========================================================================
   5. Reveal animation — from brochure.css
   ========================================================================== */

.rv {
	opacity: 0;
	transform: translateY(22px);
	transition:
		opacity .95s cubic-bezier(.16, .7, .3, 1),
		transform .95s cubic-bezier(.16, .7, .3, 1);
}

.rv.in {
	opacity: 1;
	transform: none;
}

.rv.d1 { transition-delay: .08s; }
.rv.d2 { transition-delay: .16s; }
.rv.d3 { transition-delay: .24s; }

@media (prefers-reduced-motion: reduce) {
	* {
		animation: none !important;
		transition: none !important;
	}

	.rv {
		opacity: 1;
		transform: none;
	}

	html {
		scroll-behavior: auto;
	}
}

/* ==========================================================================
   6. Buttons — moved to tokens.css
   ==========================================================================
   .btn is the outline default and .btn-solid the filled variant, which is
   the inverse of the old treatment here. .btn.ghost no longer exists.
   ========================================================================== */

/* ==========================================================================
   7. Header (.hv-header) — reconciled to the .hdr treatment in
   reference/design/husna-villas-light.html. Renamed to the hv- system:
   .hdr → .hv-header, .hdr-in → .hv-header__inner, .mark → .hv-header__mark,
   .hdr-nav → .hv-header__nav. The reference's .hdr-tel has no counterpart:
   the right-hand slot it occupied is now .hv-header__out, a link to the
   parent site, and the phone number it held lives in the footer.

   ONE HEADER FOR THE WHOLE SITE. Transparent, never sticky, and laid out
   identically on every template — same inset, same wordmark position, same
   nav position, same right-hand slot. No per-template geometry.

   THE INSET IS --wrap-pad, FULL BLEED — not .wrap. This is the rule that
   makes the header the same everywhere, so it is worth saying why .wrap
   cannot do the job. .wrap centres a fixed-width column, so its inset
   *grows* with the viewport once --wrap-max binds; the villa page then
   shifts it again, because its content .wrap sits inside the booking-panel
   grid. Measured wordmark position before this rule, on three templates:

       viewport   home    villa   page.php
       1400       84      73      73
       1600       96      173     52
       1920       115     333     52
       2560       154     653     52

   Three different columns, none of them agreeing with the page's own text.
   A flat --wrap-pad is one number at any width (22px → 48 → 52, capped),
   so the wordmark cannot drift and cannot differ between pages.

   It also lands on the page's own copy where that copy is not being moved
   by something else: exactly on the villa hero's breadcrumb up to ~1500px,
   and exactly on the home hero's text on mobile. Above ~1500px the villa
   page's content column is pushed inward by the booking-panel grid (see
   .hv-vhero__in) and the header does not follow it — the header belongs to
   the viewport, the content column belongs to the grid.

   TYPE COLOUR IS THE ONE THING THAT STILL VARIES, and it is forced by the
   transparency rather than chosen: light type over a photograph (home,
   villa), ink type on paper (page.php — the live booking-confirmation
   page — plus index/single/404). White on white would be invisible.

   POSITION varies for the same reason and is invisible in the result.
   Hero templates get position:absolute so the photo starts at the true top
   of the document and the header floats over it. Templates with no photo
   keep the header in normal flow, because absolute would drop it on their
   own text with nothing reserving its height. Both render the header at
   top 0, 80px tall, with content beginning at 80px — measured identical.

   Absolute rather than fixed, everywhere: the header leaves the viewport
   by scrolling, which is the whole point of "not sticky", and it needs no
   class toggled on it to get out of the way.

   One piece of history worth keeping. The ORIGINAL treatment was
   position:fixed with no explicit height, and header.php's old
   wp_page_menu() fallback (unstyled, nested) could render tall enough to
   sit on top of the page below it. Absolute has the same exposure — it is
   only safe on the two templates that have a tall hero underneath.
   Anything in normal flow cannot do that whatever the nav contains.
   ========================================================================== */

.hv-header {
	position: static;
	z-index: 60;
	background: transparent;
	backdrop-filter: none;
	border-bottom: none;
}

/*
 * The two templates with a photograph behind the header. WordPress's own
 * "home" body class corresponds to is_front_page() regardless of the
 * Reading Settings choice (see get_body_class() in core), and
 * single-mphb_room_type is the accommodation-type single template and
 * nothing else — so this is exactly the pair that has a hero.
 *
 * No positioned ancestor stands between this and the initial containing
 * block (#page carries no position), so top/left/right resolve against the
 * document, putting the header across the top of the hero.
 */
.home .hv-header,
.single-mphb_room_type .hv-header {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
}

/*
 * Light type over the photograph. --gold rather than --gold-deep for the
 * subtitle: the brand rule is that Gold Deep is the only gold allowed to
 * carry text on a LIGHT ground, and here the ground is a dark hero, same
 * as .eyebrow--on-dark.
 *
 * The text-shadow is insurance on top of each hero's own top scrim. Both
 * heroes darken behind the header, but a hero image is content — someone
 * will eventually set a bright one — and the shadow costs nothing.
 */
.home .hv-header__mark-name,
.home .hv-header__nav a,
.single-mphb_room_type .hv-header__mark-name,
.single-mphb_room_type .hv-header__nav a {
	color: var(--paper);
	text-shadow: 0 1px 3px rgba(11, 11, 11, .9), 0 1px 10px rgba(11, 11, 11, .6);
}

.home .hv-header__mark-sub,
.single-mphb_room_type .hv-header__mark-sub {
	color: var(--gold);
	text-shadow: 0 1px 3px rgba(11, 11, 11, .9), 0 1px 10px rgba(11, 11, 11, .6);
}

.home .hv-header__nav a:hover,
.single-mphb_room_type .hv-header__nav a:hover {
	color: var(--gold);
}

/*
 * EVERY TEMPLATE WITHOUT A PHOTOGRAPH BEHIND THE HEADER GETS AN INK GROUND.
 *
 * Written as the exact complement of the branch above rather than as a
 * per-page rule: the two hero templates keep the transparent header that lets
 * their photograph through, and everything else — the booking page, Terms,
 * anything added later — gets the bar. A `body.hv-checkout` variant would have
 * been a second header to keep in step with this one, and the site has one
 * header by rule.
 *
 * The base .hv-header is `background: transparent; border-bottom: none`, which
 * on a paper page rendered as ink type floating over white with nothing under
 * it. It was not missing, it had no ground.
 *
 * No text-shadow here, unlike the hero branch. That shadow is insurance against
 * a bright photograph someone uploads later; a flat ink ground cannot change
 * underneath the type, so the same insurance would be noise.
 */
body:not(.home):not(.single-mphb_room_type) .hv-header {
	background: var(--ink);
}

body:not(.home):not(.single-mphb_room_type) .hv-header__mark-name,
body:not(.home):not(.single-mphb_room_type) .hv-header__nav a {
	color: var(--paper);
}

body:not(.home):not(.single-mphb_room_type) .hv-header__mark-sub {
	color: var(--gold);
}

body:not(.home):not(.single-mphb_room_type) .hv-header__nav a:hover {
	color: var(--gold);
}

/*
 * THE HEADER'S RIGHT-HAND SLOT IS FILLED ON EVERY TEMPLATE, AND ITS OCCUPANT
 * IS NEVER HIDDEN PER-TEMPLATE.
 *
 * .hv-header__inner is justify-content: space-between, so a rule that hides
 * this item on some templates does not merely change what the header contains
 * — it relocates the nav to the far right on those templates and leaves it
 * where it was on the rest. Measured the one time such a rule existed: nav
 * text ending at 1301 on the home page against 906 on the villa page, at
 * 1400px. Anything put in this slot inherits the constraint, so hiding it
 * anywhere means accepting that the nav moves with it.
 *
 * Only the text-shadow varies by template, and it belongs to the branch below
 * for the reason written above it: over a photograph the ground is content and
 * someone will eventually upload a bright one. The colour does NOT vary —
 * --dim is tuned for a dark ground and both states have one, ink on the inner
 * pages and a scrimmed hero on the other two.
 */
.home .hv-header__out,
.single-mphb_room_type .hv-header__out {
	text-shadow: 0 1px 3px rgba(11, 11, 11, .9), 0 1px 10px rgba(11, 11, 11, .6);
}

/*
 * Full bleed, inset by --wrap-pad. Not .wrap, and no per-template
 * override — see the section comment above for the measurements that
 * settled this. The element carries no .wrap class in header.php either,
 * so the max-width that used to centre it is gone at source rather than
 * being overridden here.
 */
.hv-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 24px;
	width: 100%;
	padding: 16px var(--wrap-pad);
}

.hv-header__title {
	margin: 0;
}

/*
 * Lockup from reference/design/husna-hero-03-revised.html's .mark: name on
 * one line, a gold rule, a subtitle beneath — replacing the earlier
 * "Husna" / "Villas" stacked split. One header template, one lockup, used
 * on every page (not just the home hero).
 */
.hv-header__mark {
	display: inline-flex;
	flex-direction: column;
	line-height: 1;
}

.hv-header__mark-name {
	font-family: var(--display);
	font-weight: var(--disp-w);
	font-size: var(--fs-xl);
	letter-spacing: var(--ls-nav);
	text-transform: uppercase;
	color: var(--ink);
}

.hv-header__mark-rule {
	height: 1px;
	background: var(--gold);
	margin: 9px 0 7px;
}

.hv-header__mark-sub {
	display: block;
	font-family: var(--ui);
	font-size: var(--fs-3xs);
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
	color: var(--gold-deep);
	font-weight: 600;
}

/* wp_nav_menu() always outputs a real <ul><li><a> list, unlike the
   reference's flat row of <a> tags — flex the list itself instead. */
.hv-header__nav ul {
	display: flex;
	gap: 32px;
	list-style: none;
}

.hv-header__nav a {
	font-size: var(--fs-sm);
	letter-spacing: var(--ls-nav);
	text-transform: uppercase;
	font-weight: 500;
	color: var(--grey);
	transition: color .3s;
}

.hv-header__nav a:hover {
	color: var(--ink);
}

/*
 * The way out to the parent site. Quiet by construction: --dim rather than
 * --gold, because gold is the brand accent and is already carrying the
 * eyebrow and the wordmark's rule on the same screen — a second gold item in
 * the corner reserved for the primary action is what made the phone number
 * that stood here read as a call to act.
 *
 * Weight and size match .hv-header__nav a exactly, same family and same
 * --fs-sm, so this reads as chrome of the same rank as the nav rather than as
 * something above it.
 *
 * min-height, not vertical padding: the height is then independent of
 * whatever line-height the text and the arrow settle on, so the 44px tap
 * target cannot drift if either changes. It does not grow the bar — the
 * wordmark lockup is 44.5px and sets the row's height, and 44 is under it.
 * No horizontal padding, so the link's right edge stays on the same
 * --wrap-pad inset as every other piece of full-bleed chrome.
 *
 * Tracking is .04em rather than the .16em the phone number carried. That
 * tracking was tuned for an all-caps number; a lowercase domain read as
 * spaced-out at anything higher, and the letterforms are what carry it.
 */
.hv-header__out {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	min-height: 44px;
	font-size: var(--fs-sm);
	font-weight: 500;
	letter-spacing: .04em;
	color: var(--dim);
	white-space: nowrap;
	transition: color .3s;
}

.hv-header__out:hover {
	color: var(--gold);
}

/* flex: none so the row cannot stretch or shrink the arrow away from the
   9x9 box the markup gives it. */
.hv-header__out-arrow {
	flex: none;
}

@media (max-width: 880px) {
	.hv-header__nav {
		display: none;
	}
}

/* ==========================================================================
   8. Footer (.hv-footer) — matches reference/design/husna-hero-03-revised.html's
   .ft/.ftgrid/.ftbase (cqw converted to vw). Lives here, not home.css,
   because home.css only enqueues on the front page (is_front_page()) but
   footer.php renders on every template via get_footer() — the CSS has to
   be available everywhere the markup is.
   ========================================================================== */

.hv-footer {
	background: var(--ink);
	color: var(--dim);
	padding: clamp(50px, 7vw, 96px) 0;
}

.hv-footer__grid {
	display: grid;
	grid-template-columns: 1.4fr 1fr 1fr;
	gap: clamp(28px, 4vw, 64px);
}

.hv-footer__mark {
	display: flex;
	align-items: baseline;
	gap: 12px;
}

.hv-footer__mark-name {
	font-family: var(--display);
	font-weight: var(--disp-w);
	font-size: 20px;
	letter-spacing: .04em;
	color: var(--paper);
	text-transform: uppercase;
}

.hv-footer__mark-rule {
	width: 20px;
	height: 1px;
	background: var(--gold);
}

.hv-footer__mark-sub {
	font-size: var(--fs-sm);
	letter-spacing: var(--ls-nav);
	text-transform: uppercase;
	color: var(--dim-2);
}

.hv-footer__tagline {
	margin-top: 24px;
	max-width: 34ch;
	font-size: 13.5px;
	line-height: 1.7;
}

.hv-footer__col h5 {
	font-size: var(--fs-sm);
	font-weight: 600;
	letter-spacing: var(--ls-cta);
	text-transform: uppercase;
	color: var(--gold);
	margin-bottom: 16px;
}

.hv-footer__col a {
	display: block;
	color: var(--dim);
	font-size: var(--fs-ms);
	padding: 6px 0;
	text-decoration: none;
}

.hv-footer__col a:hover {
	color: var(--paper);
}

.hv-footer__base {
	display: flex;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 8px 20px;
	margin-top: clamp(40px, 5vw, 70px);
	padding-top: 24px;
	border-top: 1px solid var(--hair-dark);
	font-size: var(--fs-sm);
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--dim-2);
}

@media (max-width: 760px) {
	.hv-footer__grid {
		grid-template-columns: 1fr;
		gap: 40px;
	}
}

/* ==========================================================================
   9. Villa page — section 1: the hero (.hv-vhero)

   Image, scrim, and the title block overlaid on it, bottom-left. Shape from
   the canonical design's .vhero / .sc / .vheroin
   (reference/design/husna-villa-internal page.html:412-419).

   The hero carries the aspect ratio; .hv-gallery is an absolute layer inside
   it, so the image can sit UNDER the type without the type affecting the
   hero's height.
   ========================================================================== */

.hv-vhero {
	position: relative;
	width: 100%;
	aspect-ratio: 16 / 9;
	overflow: hidden;
	background: var(--ink);
	color: var(--paper);
	display: flex;
	align-items: flex-end;
}

@media (min-width: 900px) {
	.hv-vhero {
		aspect-ratio: 21 / 9;
	}
}

.hv-gallery {
	position: absolute;
	inset: 0;
	overflow: hidden;
}

/*
 * THE HERO IS ONE PHOTOGRAPH: THE FEATURED IMAGE, AND NOTHING ELSE CAN GET IN.
 *
 * Everything that used to sit here — .mphb-gallery, .flexslider, .gallery,
 * .gallery-item and its opacity-0 stack, .gallery-icon, .gallery-caption —
 * existed to tame the WordPress [gallery] markup MotoPress rendered INTO this
 * box, because the hero used to prefer a gallery over the featured image when
 * one existed. That branch is gone (husnavillas_render_gallery_before_content),
 * so none of that markup can appear here any more and the rules that shaped it
 * were styling nothing.
 *
 * Confirmed against the RENDERED OUTPUT of all ten tier pages, not by grepping
 * the templates: zero .gallery-item, .flexslider or .mphb-gallery anywhere on
 * any of them, and exactly one .wp-post-image in each hero. Puerto Estate still
 * has its nine-image per-tier gallery stored in the database and it now emits
 * nothing at all.
 */
.hv-gallery > img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border: 0;
	margin: 0;
}

/*
 * The scrim. A layer of its own between image and type — never a filter on
 * the image, which would dull the photograph itself rather than just the
 * ground behind the words.
 *
 * --ink (#0B0B0B) rather than pure black, and it stops at .88 rather than 1,
 * so the photograph still reads through at the very bottom.
 *
 * The header's own ground is a SEPARATE layer (.hv-vhero::before below),
 * not extra stops on this gradient. Percentage stops cannot do that job:
 * this hero is 594px tall at desktop and 340px on mobile, so a ramp
 * clearing at "16%" covers 95px in one case and 54px in the other, while
 * the header is 80px either way. Measured, the percentage version left the
 * Zacil Na wordmark at 1.11:1 on mobile. A fixed pixel height covers the
 * header identically at every viewport.
 *
 * THE STOPS ARE MEASURED, NOT CHOSEN BY EYE. Each type element's box was
 * sampled out of a real screenshot with the type hidden — the actual photo
 * with this scrim over it — and the LIGHTEST pixel in each box taken as the
 * ground. An earlier, gentler ramp put the white H1 at 3.73:1 over Zacil
 * Na's white sand, which looked fine in a screenshot and failed the number.
 * These stops clear 4.5:1 for the H1, the gold gloss and the crumb on all
 * three villas at both widths. Re-measure before softening any of them.
 */
.hv-vhero__scrim {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: linear-gradient(
		to bottom,
		rgba(11, 11, 11, 0) 0%,
		rgba(11, 11, 11, 0) 30%,
		rgba(11, 11, 11, .10) 45%,
		rgba(11, 11, 11, .36) 57%,
		rgba(11, 11, 11, .68) 68%,
		rgba(11, 11, 11, .78) 80%,
		rgba(11, 11, 11, .88) 100%
	);
}

/*
 * The header's ground. Sized in pixels so it covers the 80px transparent
 * header identically whether the hero is 594px tall (desktop) or 340px
 * (mobile) — see the note above for what the percentage version measured.
 *
 * Values are measured, not picked. Zacil Na's hero is white sand directly
 * under the nav — the worst ground on the site — and it is what sets these
 * numbers: at .62 the nav measured 4.08:1 there, just under. At .72 holding
 * to 40% it clears with room to spare on all three villas.
 *
 * It is a top-of-frame vignette rather than a bar, which is what keeps the
 * header reading as transparent, and it is clear by 150px — well above the
 * crop where the title block sits, so the H1 and gloss ratios measured for
 * the bottom gradient are untouched.
 */
.hv-vhero::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 150px;
	z-index: 1;
	pointer-events: none;
	background: linear-gradient(
		to bottom,
		rgba(11, 11, 11, .72) 0%,
		rgba(11, 11, 11, .62) 40%,
		rgba(11, 11, 11, .26) 72%,
		rgba(11, 11, 11, 0) 100%
	);
}

/*
 * Inset matches the body content below rather than centring on the hero's
 * own full-bleed width: the hero spans both grid columns while the copy
 * beneath it sits in the left one, so a centred .wrap here would land the
 * H1 a couple of dozen pixels right of the paragraph under it.
 *
 * ~40px of air under the gloss, per the approved spec — clamped so the
 * mobile crop does not spend a fixed 40px it cannot afford.
 */
.hv-vhero__in {
	position: relative;
	z-index: 2;
	width: 100%;
	padding-left: var(--wrap-pad);
	padding-right: var(--wrap-pad);
	padding-bottom: clamp(26px, 3vw, 40px);
}

/*
 * The hero spans the whole grid, but the copy below it lives in the content
 * column, so the hero's own text has to be inset to the same place or the two
 * do not line up.
 *
 * THIS USED TO CHASE A MOVING COLUMN AND NO LONGER HAS TO. The old formula was
 *
 *     padding-left: max(--wrap-pad, 100% - 430px - --wrap-max + --wrap-pad)
 *
 * written when the left column had no gutter and its .wrap drifted rightwards
 * as the viewport grew — its own comment recorded "52px against 823px at 2560",
 * and that 823px was measured again on the day this replaced it. The grid now
 * has gutter columns (section 17), so there is nothing to chase: the content
 * column sits at a known offset and this reproduces the SAME expression the
 * grid uses rather than a reconstruction of where the column ended up.
 *
 * --hv-gutter is what each 1fr gutter resolves to. Clamped at 0 so that below
 * the crossover — where the gutters collapse — this degrades to a plain
 * --wrap-pad inset, which is what it should be.
 *
 * 430px is the panel column from the grid template in section 17. If that
 * number changes, it changes here too.
 */
@media (min-width: 1024px) {
	.hv-vhero__in {
		--hv-gutter: max(0px, calc((100% - var(--wrap-max) - 430px) / 2));

		padding-left: calc(var(--hv-gutter) + var(--wrap-pad));
		padding-right: calc(430px + var(--hv-gutter) + var(--wrap-pad));
	}
}

/*
 * --dim (#CFCDC8), not --dim-2 (#8C8A85), for the muted segments. --dim-2 is
 * the canonical design's crumb colour, but the canonical crumb sits on a
 * heavier scrim than the one above; measured on this page at 9px it came out
 * between 1.1:1 and 1.9:1 against the lightest pixel behind it, which is not
 * a muted label, it is an invisible one. --dim is the palette's other muted
 * white and clears 4.5:1 here.
 */
.hv-vhero__crumb {
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	line-height: 1.6;
	color: var(--dim);
	margin-bottom: 16px;
}

.hv-vhero__crumb a {
	color: var(--dim);
	transition: color .25s;
}

.hv-vhero__crumb a:hover {
	color: var(--paper);
}

.hv-vhero__crumb span[aria-hidden] {
	margin: 0 .5em;
}

/* The segment for the villa being viewed, one step brighter than the rest. */
.hv-vhero__crumb-here {
	color: var(--paper);
}

/* --disp-ls-lg (not .disp's own --disp-ls) because this is hero-scale
   display type, which the canonical design tracks tighter than a section
   heading. */
.hv-vhero__name {
	font-size: var(--fs-disp-lg);
	letter-spacing: var(--disp-ls-lg);
	color: var(--paper);
	margin: 0;
}

.hv-vhero__gloss {
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: var(--ls-eyebrow);
	text-transform: uppercase;
	line-height: 1.6;
	color: var(--gold);
	margin-top: 16px;
}

/*
 * Below the tablet breakpoint the crop is tighter and the type has less
 * room: the name comes down a step, the crumb loses some of its tracking so
 * it still fits on one line at 390px, and the scrim covers more of the frame
 * — the same words now sit over a much larger share of the picture.
 */
@media (max-width: 899px) {
	/*
	 * A floor on the height, matching the canonical design's own mobile
	 * hero (.vhero{height:340px}). 16/9 of 390px is 219px, and four stacked
	 * lines of type in 219px push the crumb up into the thin end of the
	 * scrim: measured, the 9px crumb landed at 2.15:1 there. At 340px the
	 * same block sits far enough down the ramp to clear 4.5:1 without
	 * darkening the picture any further. The ratio still governs above
	 * ~605px wide, where 16/9 is the taller of the two.
	 */
	.hv-vhero {
		min-height: 340px;
	}

	.hv-vhero__name {
		font-size: clamp(30px, 8.6vw, 40px);
	}

	.hv-vhero__crumb {
		letter-spacing: var(--ls-nav);
		margin-bottom: 12px;
	}

	.hv-vhero__gloss {
		letter-spacing: var(--ls-wide);
		margin-top: 12px;
	}

	.hv-vhero__scrim {
		background: linear-gradient(
			to bottom,
			rgba(11, 11, 11, 0) 0%,
			rgba(11, 11, 11, .08) 18%,
			rgba(11, 11, 11, .34) 40%,
			rgba(11, 11, 11, .66) 58%,
			rgba(11, 11, 11, .84) 76%,
			rgba(11, 11, 11, .90) 100%
		);
	}
}

/* ==========================================================================
   10. Villa page — section 2: the band's head (.villa-head)

   The villa NAME is no longer here — it is the hero's <h1>. What is left is
   the "The house" section label on the left and the spec counts on the
   right, which is why this rule now aligns them on their baselines rather
   than on the bottom of a two-line name block.
   ========================================================================== */

.villa-head {
	display: flex;
	justify-content: space-between;
	align-items: flex-end;
	gap: 24px;
	flex-wrap: wrap;
	border-bottom: 1px solid var(--ink);
	/* Longhands, not a `padding` shorthand — see the note on .wrap above.
	   .villa-head also carries .wrap (title.php), so the shorthand was
	   resetting its horizontal inset to zero. */
	padding-top: clamp(32px, 5vw, 56px);
	padding-bottom: 22px;
}

/* The label and the house headline stack on the left; the spec counts stay
   on the right of the same row, bottom-aligned to them. min-width:0 so a
   long headline wraps inside the column instead of pushing the counts off
   the row. */
.villa-head__intro {
	flex: 1 1 24ch;
	min-width: 0;
}

.villa-head__label {
	margin: 0;
}

/* Narrower than the 19ch the section heads below use — this one shares its
   row with the spec counts rather than having the column to itself. */
.villa-head__intro .hv-sec__title {
	max-width: 17ch;
}

.villa-stats {
	display: flex;
	flex-wrap: wrap;
	gap: clamp(16px, 3vw, 44px) clamp(20px, 3vw, 44px);
}

.villa-stats b {
	display: block;
	font-size: clamp(1.3rem, 2.1vw, 1.75rem);
	font-weight: 300;
	line-height: 1;
}

/* font-weight 600, not the browser's <span>-in-a-<b>-sibling default of
   700 — nothing in the brand uses 700; 600 is the heaviest weight the
   type system loads. */
.villa-stats span {
	display: block;
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: var(--ls-nav);
	text-transform: uppercase;
	color: var(--grey);
	margin-top: 8px;
}

/*
 * "of 5" — what the HOUSE has, under what is OPEN at this selection.
 *
 * The band used to say "Bedrooms 5" while the panel said "1-2 Bedrooms", two
 * numbers on one screen with nothing connecting them. The pair reads as one
 * fact only when they sit together.
 *
 * Quieter than the label above it and NOT uppercase: the label is a tracked
 * utility caption, this is a plain phrase. Empty on the top tier — the
 * markup omits it where the house has no more than the selection, so this
 * collapses to nothing rather than leaving a gap.
 */
.villa-stats small {
	display: block;
	font-size: var(--fs-note);
	font-weight: 300;
	letter-spacing: 0;
	text-transform: none;
	color: var(--grey);
	margin-top: 3px;
}

.villa-stats small:empty {
	display: none;
}

/* ==========================================================================
   11. Villa page — section 3: Bedroom tier selector (.hv-tier-selector)

   One bordered box, matching the canonical design's .stepper: a −/+ pair
   either side of a centre cell, divided by internal 1px ink rules rather
   than sitting apart as three detached objects. The centre cell carries
   two lines — the value and a quieter "sleeps up to N" sub-label.

   The −/+ controls are still real <a> links to the sibling tiers'
   permalinks and the value is still a real <select>, so the whole thing
   works with zero JS; only the shape changed.
   ========================================================================== */

.hv-tier-selector {
	border-bottom: 1px solid var(--hair);
}

.hv-tier-selector__inner {
	/* Longhands, not a `padding` shorthand — see the note on .wrap above. */
	padding-top: clamp(20px, 3vw, 32px);
	padding-bottom: clamp(20px, 3vw, 32px);
}

.hv-tier-selector__field {
	max-width: 100%;
}

/* Above the box, centred — the design's own .flabel treatment. */
.hv-tier-selector__label {
	display: block;
	font-size: var(--fs-3xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	text-align: center;
	margin-bottom: 9px;
}

.hv-tier-selector__stepper {
	display: flex;
	align-items: stretch;
	border: 1px solid var(--ink);
	background: var(--paper);
	/* A grid-column child on the villa page — never wider than its column. */
	max-width: 100%;
}

/*
 * min-width:0 is load-bearing. A flex item defaults to min-width:auto,
 * which for a <select> resolves to the intrinsic width of its widest
 * option. That refuses to shrink, so the stepper pushed past its grid
 * column and past the viewport itself — measured at 1516px against a
 * 1400px viewport on the longest tier name.
 */
.hv-tier-selector__val {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 5px;
	padding: 8px 6px;
	min-height: 56px;
	border-left: 1px solid var(--ink);
	border-right: 1px solid var(--ink);
}

.hv-tier-selector__sub {
	font-size: var(--fs-3xs);
	font-weight: 600;
	letter-spacing: var(--ls-cta);
	text-transform: uppercase;
	color: var(--grey);
	line-height: 1;
}

/* The −/+ cells. Borderless: the box's own border plus the centre cell's
   side rules draw every division, exactly as the design does it. */
.hv-tier-selector__nav {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	min-height: 56px;
	flex: none;
	font-size: var(--fs-xl);
	font-weight: 200;
	line-height: 1;
	color: var(--ink);
	text-decoration: none;
	transition: background .2s, color .2s;
}

/*
 * Limestone fill with a black glyph, never gold — on hover, on focus and
 * while active, so the button cannot flash gold at any point in a press.
 * :active is listed explicitly because a plain :hover rule leaves the
 * mousedown moment to the UA, which is the exact instant a press is felt.
 *
 * The focus ring survives the change, checked by tabbing to the button for
 * real — a scripted .focus() does not match :focus-visible in Chromium and
 * reports the UA default instead. It is --gold-deep (#96762E) at 2px with a
 * 3px offset (tokens.css): 3.87:1 against --bone (#F5F4F1), clear of the
 * 3:1 that non-text UI needs, and the offset draws it outside the fill on
 * the stepper's white ground anyway, where it measures 4.26:1.
 */
.hv-tier-selector__nav:hover,
.hv-tier-selector__nav:focus-visible,
.hv-tier-selector__nav:active {
	background: var(--bone);
	color: var(--ink);
}

/* --disabled, not --hair: the hairline grey is so light against paper
   that a disabled control read as missing rather than unavailable. */
.hv-tier-selector__nav--disabled {
	color: var(--disabled);
	pointer-events: none;
}

/* Loading state, added by main.js for the moment between click and the
   browser actually navigating to the next tier's permalink — a real link,
   never prevented. */
.hv-tier-selector__nav.is-loading {
	opacity: .5;
	pointer-events: none;
}

/*
 * The value itself. Styled to read as the design's plain centred value
 * line while staying a real <select> — the accessible/no-JS way to jump
 * more than one tier at a time. No border or chevron: the box around it
 * already reads as the control.
 */
.hv-tier-selector__select {
	appearance: none;
	-webkit-appearance: none;
	background: transparent;
	border: 0;
	padding: 0;
	width: 100%;
	min-width: 0;
	font-size: var(--fs-body);
	font-weight: 400;
	letter-spacing: -.005em;
	line-height: 1;
	color: var(--ink);
	text-align: center;
	text-align-last: center;
	cursor: pointer;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Only rendered when the searched party exceeds every tier this villa
   has. Its counterpart on the home page is .hv-status-tag (home.css) in
   its "Too small for your party" state — different treatment now, since
   that one is a tag and this one is a notice, but the WORDING is kept in
   sync deliberately: both say "too small," never "not available," because
   changing dates won't fix it.

   The class this used to name, .hv-villa-detail__status-unavailable, was
   deleted when the two verdict families collapsed into that one tag. */
.hv-tier-selector__notice {
	margin: 9px 0 0;
	font-size: var(--fs-note);
	color: var(--gold-deep);
	text-align: center;
}

/* ==========================================================================
   11b. Villa page — guests stepper (.hv-guests) and quote (.hv-quote)

   The guests stepper deliberately reuses the tier stepper's shape rather
   than inventing a second control language — same bordered box, same
   −/+ cells, same two-line centre. It sits inside the booking panel, so
   its palette differs (paper on limestone rather than on white) but its
   geometry does not.

   Buttons here, not links: unlike bedrooms, a guest count has no
   permalink to point at, so there is nothing for a no-JS anchor to do.
   The <select> is the no-JS control and posts the value on its own.
   ========================================================================== */

.hv-guests {
	margin-bottom: 16px;
}

.hv-guests__label {
	display: block;
	font-size: var(--fs-3xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	text-align: center;
	margin-bottom: 9px;
}

.hv-guests__stepper {
	display: flex;
	align-items: stretch;
	border: 1px solid var(--ink);
	background: var(--paper);
	max-width: 100%;
}

/* min-width:0 for the same reason as the tier stepper — a flex <select>
   otherwise refuses to shrink below its widest option. */
.hv-guests__val {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 5px;
	padding: 8px 6px;
	min-height: 56px;
	border-left: 1px solid var(--ink);
	border-right: 1px solid var(--ink);
}

.hv-guests__select {
	appearance: none;
	-webkit-appearance: none;
	background: transparent;
	border: 0;
	padding: 0;
	width: 100%;
	min-width: 0;
	font-size: var(--fs-body);
	font-weight: 400;
	line-height: 1;
	color: var(--ink);
	text-align: center;
	text-align-last: center;
	cursor: pointer;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.hv-guests__sub {
	font-size: var(--fs-3xs);
	font-weight: 600;
	letter-spacing: var(--ls-cta);
	text-transform: uppercase;
	color: var(--grey);
	line-height: 1;
}

.hv-guests__nav {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	min-height: 56px;
	flex: none;
	font-size: var(--fs-xl);
	font-weight: 200;
	line-height: 1;
	color: var(--ink);
	transition: background .2s, color .2s;
}

/* Limestone fill, black glyph, never gold — same rule and same reasoning
   as .hv-tier-selector__nav above. The two steppers are the only +/−
   controls on the page and must behave identically. */
.hv-guests__nav:hover,
.hv-guests__nav:focus-visible,
.hv-guests__nav:active {
	background: var(--bone);
	color: var(--ink);
}

.hv-guests__nav:disabled {
	color: var(--disabled);
	pointer-events: none;
}

/* --------------------------------------------------------------------------
   .hv-guests--full — the home page search modal's configuration of the SAME
   control: stepper, dropdown and typed entry together.

   Deliberately more permissive than the villa panel's stepper-only mode, and
   not an inconsistency to reconcile — see createGuestsControl() in main.js
   for the reasoning. Geometry is shared; only the value cell's contents
   differ, the same way the villa panel already varies palette but not
   geometry.
   -------------------------------------------------------------------------- */

.hv-guests__entry {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 4px;
	width: 100%;
	min-width: 0;
}

.hv-guests__input {
	flex: 0 1 auto;
	width: 3.5ch;
	min-width: 0;
	background: transparent;
	border: 0;
	padding: 0;
	font-family: var(--ui);
	font-size: var(--fs-body);
	font-weight: 400;
	line-height: 1;
	color: var(--ink);
	text-align: center;
	/* Native spinners off: the -/+ cells either side already do that job,
	   and a second pair of arrows inside a 3.5ch box is clutter, not
	   redundancy. Typing and the dropdown are what this field adds. */
	-moz-appearance: textfield;
	appearance: textfield;
}

.hv-guests__input::-webkit-outer-spin-button,
.hv-guests__input::-webkit-inner-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

.hv-guests__input:focus-visible {
	outline: 2px solid var(--gold-deep);
	outline-offset: 3px;
}

/*
 * The dropdown trigger. The real <select> is laid over this box at zero
 * opacity, so clicking the chevron IS clicking the select — no scripting,
 * and it works in every browser. Opening a native select from JS is not
 * portable: showPicker() does not exist in Safari.
 *
 * Pinned to the right edge of the value cell rather than sitting inline
 * beside the number: inline, the number and the chevron centre as a PAIR,
 * which reads as a number nudged off-centre. Absolute here lets the typed
 * field centre in the cell on its own, with the chevron where a dropdown
 * arrow is expected.
 */
.hv-guests--full .hv-guests__val {
	position: relative;
}

/*
 * 44px, NOT 22px, and the width here is load-bearing rather than cosmetic.
 *
 * This box is the containing block for the invisible <select> laid over it,
 * which is position:absolute; inset:0. A native select's popup is laid out
 * from the select's own used width, so a 22px box produced a 22px option
 * list: every label from 10 up lost its second digit and "12" read as "1".
 * Confirmed by opening the real popup and looking at it, because a closed
 * select measures perfectly while its open list is clipped.
 *
 * There is no version of this fix that widens the list without widening the
 * control — for a native select they are the same number. 44px is also the
 * minimum touch target, and 22px was half of it on a control a guest uses on
 * a phone, so this is a fix in both respects rather than a fix with a side
 * effect.
 *
 * The chevron must not appear to move. box-sizing is border-box globally
 * (the reset at the top of this file), so the padding sits INSIDE the 44px:
 * right-aligning a 7px chevron with 17.5px of right padding puts its centre
 * 21px from the value cell's right edge, which is exactly where a centred
 * 22px box at right:10px put it. The select is unaffected by that padding —
 * an abs-pos child's containing block is the padding box, so it still fills
 * the whole 44px.
 *
 * It does not reach the typed number field: this box is anchored to the
 * right of the value cell and the input is centred, so they do not overlap
 * at either width. Asserted with elementFromPoint, not left to arithmetic.
 */
.hv-guests__pick {
	position: absolute;
	right: 0;
	top: 50%;
	transform: translateY(-50%);
	display: inline-flex;
	align-items: center;
	justify-content: flex-end;
	width: 44px;
	height: 44px;
	padding-right: 17.5px;
	cursor: pointer;
}

/*
 * Drawn with borders, not the ▾ character. Montserrat carries no glyph at
 * U+25BE, so the text version fell back to whatever the system offered and
 * rendered as a stray dot. A rotated corner is font-independent.
 */
.hv-guests__chevron {
	width: 7px;
	height: 7px;
	border-right: 1.5px solid var(--grey);
	border-bottom: 1.5px solid var(--grey);
	transform: translateY(-2px) rotate(45deg);
	pointer-events: none;
	transition: border-color .2s;
}

.hv-guests__pick:hover .hv-guests__chevron,
.hv-guests__pick:focus-within .hv-guests__chevron {
	border-color: var(--ink);
}

/* The invisible select is still tabbable, so the ring has to be drawn by
   its parent — :focus-within, because the select is the last child and no
   sibling combinator reaches backwards to the chevron. */
.hv-guests__pick:focus-within {
	outline: 2px solid var(--gold-deep);
	outline-offset: 2px;
}


.hv-guests--full .hv-guests__select {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	margin: 0;
	padding: 0;
	border: 0;
	opacity: 0;
	cursor: pointer;
	appearance: none;
	-webkit-appearance: none;
}

/*
 * The quote. Hidden until dates are chosen — the empty state is the
 * default, not a fallback, so nothing here implies a figure before the
 * guest has asked for one.
 */
.hv-quote {
	margin-top: 18px;
	padding-top: 16px;
	border-top: 1px solid var(--hair);
}

.hv-quote[hidden] {
	display: none;
}

/*
 * .hv-quote__total and its <strong> are styled in section 17b, with the rest
 * of the price block. They were defined here too — a centred, uppercase,
 * stacked treatment from before the block existed — and that older
 * `strong { display: block }` was still winning, because it sits earlier in
 * the file at equal specificity and nothing in 17b contradicted it. It put
 * "TOTAL" on its own line at display size, reading as a second heading.
 * Deleted rather than overridden: two live definitions of one component is
 * how that happens in the first place.
 */

.hv-quote__breakdown {
	margin: 6px 0 0;
	font-size: var(--fs-ms);
	color: var(--grey);
}

/* Both the "no rate" and "not available" cases. Never a zero: the panel
   says it has no price rather than showing one it would not honour. */
.hv-quote__none {
	margin: 0;
	font-size: var(--fs-note);
	color: var(--gold-deep);
}

.hv-quote__ask {
	color: var(--gold-deep);
	text-decoration: underline;
}

/*
 * In-flight state while a quote is being fetched. Opacity only, with the
 * panel's own height untouched — a spinner or a collapsed region would
 * shift the layout under the guest's cursor mid-interaction.
 */
.hv-price-panel.is-updating .hv-quote {
	opacity: .45;
}

/*
 * No quote, no checkout. MotoPress reveals its "Request these dates"
 * button whenever a date check succeeds, including when the price came
 * back as zero — so for a stay we deliberately refuse to quote, the
 * button would otherwise sit there clickable and land the guest on
 * checkout's "There are no rates for requested dates." dead end.
 *
 * The quantity select inside this section is NOT hidden or disabled by
 * this rule and must never be: if mphb_rooms_details stops posting,
 * checkout falls back to a cookie of the same name and books whatever
 * was in it last. Only the submit control goes.
 *
 * The .hv-quote message stays visible and carries the way forward.
 */
/*
 * MotoPress's availability sentence goes with the button, and for the same
 * reason: our quote says this stay cannot be taken, and "<villa> is available
 * for selected dates" directly contradicts it.
 *
 * The plugin shows and hides that section from its OWN check, which is not
 * re-run when the bedroom tier changes. So a guest who steps from an available
 * tier to a blocked one keeps the previous tier's section on screen — the one
 * state where the sentence can be visible while .hv-quote-blocked is set.
 *
 * hv-quote-blocked covers unavailable, too-short and no-rate alike
 * (setBookable in main.js), so one selector closes all three.
 */
.hv-price-panel.hv-quote-blocked .mphb-confirm-reservation,
.hv-price-panel.hv-quote-blocked .mphb-rooms-quantity-single {
	display: none;
}

/*
 * One price on screen, and it is ours.
 *
 * MotoPress renders its own "Prices start at: $7,200 for 4 nights" line
 * inside this panel, directly above .hv-quote. Two prices is bad enough;
 * two prices that word themselves differently is worse — they disagreed
 * on the night count in testing, one saying 4 nights and the other 3,
 * both from the same $7,200. Ours carries the breakdown and the
 * season-crossing caveat, so MotoPress's is the one that goes.
 *
 * Hidden, not removed: DirectBooking still writes into .mphb-period-price
 * and reads its own nodes back, so the element has to stay in the DOM.
 *
 * THIS COMMENT USED TO SAY the availability sentence was left alone because
 * "it states a fact rather than a figure". That reasoning is what left it
 * behind for so long: the fact it states is WHICH VILLA is available, and a
 * fact about which villa goes stale exactly as a figure does. It is repainted
 * from the payload now — see repaintTier() in main.js.
 *
 * WHAT IS STILL STALE IN HERE, AND WHY IT DOES NOT SHOW. This element, its
 * .mphb-price-period ("for 5 nights") and .mphb-available-rooms-count are all
 * written by MotoPress's own availability check, which is NOT re-run when the
 * bedroom tier changes. They therefore describe the tier the page loaded as,
 * indefinitely. They are invisible today — this one by the rule below, the
 * count by MotoPress's .mphb-hide on the multiple-accommodation wrapper — and
 * that is the ONLY reason they are harmless. Unhide any of them and it is
 * wrong the moment a guest touches the bedroom stepper.
 */
.hv-price-panel .mphb-regular-price {
	display: none;
}

/* ==========================================================================
   12. Villa page — section 4: Description (.hv-description)
   ========================================================================== */

.hv-description {
	background: var(--paper);
}

/* Rhythm applies to every paragraph, including the .lede / .b ones
   authored in wp-admin. */
.hv-description p {
	margin-top: 16px;
}

.hv-description p:first-of-type {
	margin-top: 0;
}

/*
 * Type default for description paragraphs that carry NO authored class.
 * .lede and .b are excluded by selector rather than by source order:
 * they come from post content (see the note on them in section 3) and
 * carry their own treatment, and this rule sits later in the file, so
 * without the :not() it would silently win over both and flatten the
 * lede the content author deliberately marked up.
 *
 * The values match the canonical design's .sub — flat body size in
 * --grey, replacing a treatment that ramped the first paragraph to
 * 20.3px in near-black and used two hardcoded off-token greys.
 */
.hv-description p:not(.lede):not(.b) {
	font-size: var(--fs-body);
	font-weight: 300;
	line-height: 1.65;
	color: var(--grey);
}

/* ==========================================================================
   13. Villa page — section 4: Amenities (.hv-amenities, .feats)
   ========================================================================== */

/*
 * --bone, so the left column alternates paper -> bone the way the
 * canonical design's sections do (.sec -> .sec.bone -> .sec.ink). The
 * ink step arrives with the Spaces section.
 */
.hv-amenities {
	background: var(--bone);
}

/*
 * MotoPress renders this heading and the attribute list itself, both
 * completely unstyled — the heading came through at 24px weight 700,
 * which is heavier than anything the brand loads and was the most
 * obviously undesigned element on the page. Restyled from outside;
 * MotoPress's own markup, classes and hooks are untouched.
 */
.hv-amenities .mphb-details-title {
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: var(--ls-eyebrow);
	text-transform: uppercase;
	color: var(--gold-deep);
	margin-bottom: clamp(20px, 3vw, 32px);
}

/* Label left, value right, hairline between — the design's own .brow. */
.hv-amenities .mphb-single-room-type-attributes {
	list-style: none;
}

.hv-amenities .mphb-single-room-type-attributes > li {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 14px;
	padding: 7px 0;
	font-size: var(--fs-ms);
	font-weight: 300;
	color: var(--grey);
	border-bottom: 1px solid var(--hair);
}

.hv-amenities .mphb-attribute-title {
	flex: none;
}

.hv-amenities .mphb-attribute-value {
	text-align: right;
	color: var(--ink);
}

.hv-amenities .mphb-attribute-value a {
	color: var(--ink);
}

.hv-amenities .mphb-attribute-value a:hover {
	color: var(--gold-deep);
}

/* .feats from brochure.css */
.feats {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(24px, 4vw, 60px);
	margin-top: clamp(30px, 4vw, 50px);
}

.feats h4 {
	font-size: var(--fs-sm);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	font-weight: 600;
	padding-bottom: 14px;
	border-bottom: 1px solid var(--ink);
	margin-bottom: 6px;
}

.feats ul {
	list-style: none;
}

.feats li {
	font-size: var(--fs-ms);
	font-weight: 400;
	padding: 10px 0;
	border-bottom: 1px solid var(--hair);
	color: #2E2E2E;
}

.hv-facilities-list {
	margin-top: clamp(20px, 3vw, 32px);
}

.hv-facilities-list li {
	font-size: var(--fs-ms);
	font-weight: 400;
	padding: 10px 0;
	border-bottom: 1px solid var(--hair);
	color: #2E2E2E;
}

.hv-facilities-list a {
	color: #2E2E2E;
}

.hv-facilities-list a:hover {
	color: var(--gold-deep);
}

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

/* ==========================================================================
   14. Villa page — section 5: Price panel / booking form (.hv-price-panel)
   ========================================================================== */

/*
 * MotoPress's mphb_tmpl_the_room_reservation_form() lives inside,
 * unmodified — this is the same "restyle from outside, don't restructure
 * the plugin's markup" approach as .hv-search (section 9) and the search
 * dialog. Two states, both driven by MPHB.DirectBooking (mphb.js):
 * initial (date fields + "Check Availability") and, after a successful
 * AJAX date check, revealed (.mphb-reserve-room-section, price line,
 * "Request these dates").
 *
 * One gold rule for this page, on the panel itself — nowhere else on the
 * villa page uses --gold (solid), matching the "one gold accent per
 * screen" convention used for the search dialog's card and the hero's
 * spec bar.
 */
/*
 * --bone, filling the whole column edge-to-edge. In the canonical design
 * the booking column is a solid limestone panel; leaving it transparent
 * was the single biggest colour miss on the page — it's the fill, not
 * the border, that makes it read as a panel rather than as more page.
 *
 * The tight padding overrides .pad's --pad-v (a full section rhythm,
 * ~84px) — this is a panel, not a section, and the design gives it
 * 26/28/30.
 */
.hv-price-panel {
	background: var(--bone);
	border-top: 1px solid var(--gold);
	padding-top: 26px;
	padding-bottom: 30px;
}

.hv-price-panel > .wrap {
	max-width: none;
	padding-left: 28px;
	padding-right: 28px;
}

/* MotoPress's own "Reservation Form" heading — unstyled by default,
   redundant next to the panel's own position in this page's layout. Hidden
   rather than restyled: nothing in the reference this panel follows has an
   equivalent heading here, and the hidden text stays in the DOM for
   accessibility purposes (still announced by a screen reader, describing
   the form) rather than being removed outright. */
.hv-price-panel .mphb-reservation-form-title {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* No side borders: each field below now draws its own box, and the panel
   itself is a filled limestone column — the old hairline frame around
   the whole form read as a second, competing container. */
.hv-price-panel .mphb-booking-form {
	display: flex;
	flex-wrap: wrap;
}

.hv-price-panel .mphb-required-fields-tip {
	flex-basis: 100%;
	order: -1;
	margin: 0 0 16px;
	padding: 0;
	text-align: center;
}

.hv-price-panel .mphb-required-fields-tip small {
	font-size: var(--fs-sm);
	color: var(--grey);
}

/*
 * Each date field is a full-width bordered box with centred content —
 * the design's own .datebtn shape (1px ink, 56px min-height, paper fill,
 * bone on hover), rather than the hairline half-width cells this used to
 * be. Stacked rather than side by side: at ~430px of panel, two of them
 * abreast leave each too narrow to read as a real field.
 *
 * This is shape only. They remain MotoPress's own two inputs, which is
 * the settled decision — the design's single chips-and-Apply range
 * picker is deliberately not being rebuilt here.
 */
.hv-price-panel .mphb-check-in-date-wrapper,
.hv-price-panel .mphb-check-out-date-wrapper {
	flex: 1 1 100%;
	margin: 0 0 12px;
	padding: 12px 16px;
	min-height: 56px;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	border: 1px solid var(--ink);
	background: var(--paper);
	text-align: center;
	transition: background .2s;
}

.hv-price-panel .mphb-check-in-date-wrapper:hover,
.hv-price-panel .mphb-check-out-date-wrapper:hover {
	background: var(--bone);
}

/* --grey and centred, matching the design's .flabel — gold-deep on every
   field label competed with the panel's one real gold accent. */
.hv-price-panel .mphb-booking-form label {
	display: block;
	font-size: var(--fs-3xs);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	font-weight: 600;
	margin-bottom: 9px;
	text-align: center;
}

.hv-price-panel .mphb-booking-form label abbr {
	text-decoration: none;
}

.hv-price-panel .mphb-booking-form br {
	display: none;
}

/* weight 400 and centred, matching the design's .datebtn .dv value. */
.hv-price-panel .mphb-booking-form input[type="text"] {
	width: 100%;
	background: none;
	border: 0;
	padding: 0;
	color: var(--ink);
	font-family: var(--ui);
	font-size: var(--fs-body);
	font-weight: 400;
	text-align: center;
}

.hv-price-panel .mphb-reserve-btn-wrapper {
	flex: 1 1 100%;
	margin: 0;
	padding: 0;
}

/* Reveal state — .mphb-reserve-room-section, hidden until MPHB.DirectBooking
   removes .mphb-hide after a successful AJAX date check (mphb.js). */
.hv-price-panel .mphb-reserve-room-section {
	flex: 1 1 100%;
	padding: 18px 24px;
	border-bottom: 1px solid var(--hair);
}

.hv-price-panel .mphb-reserve-room-section p {
	margin: 0 0 12px;
	font-family: var(--ui);
	font-size: var(--fs-ms);
	color: var(--grey);
}

.hv-price-panel .mphb-period-price {
	font-size: var(--fs-lg);
	color: var(--ink);
}

.hv-price-panel .mphb-reserve-btn,
.hv-price-panel .mphb-confirm-reservation {
	display: flex;
	width: 100%;
	align-items: center;
	justify-content: center;
	padding: 18px 40px;
	background: var(--ink);
	color: var(--paper);
	font-family: var(--ui);
	font-size: var(--fs-cta);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	border: 0;
	cursor: pointer;
	transition: background .25s;
}

.hv-price-panel .mphb-reserve-btn:hover,
.hv-price-panel .mphb-confirm-reservation:hover {
	background: var(--gold-deep);
}

.hv-price-panel .mphb-errors-wrapper {
	flex-basis: 100%;
	margin: 0;
	padding: 12px 24px;
	border-bottom: 1px solid var(--hair);
	color: var(--red);
	font-size: var(--fs-ms);
}

/* The date fields used to drop to 45% side-by-side here, from when they
   were hairline half-width cells. They're full-width bordered boxes now
   and stay stacked at every width, so that override is gone — leaving it
   in silently split them back into two cramped columns on mobile. */

/* --------------------------------------------------------------------------
   "Ask us anything" (.hv-ask) — the inquiry route on the VILLA PAGE, under its
   "Before you ask." section. One surface, not two: it was briefly on the home
   page FAQ as well, and having both meant two openers a screen apart on one
   page.

   A QUIET LINK, NOT A BUTTON, and that is the whole point of moving it out of
   the panel. There it was a bordered button beside "Request these dates", two
   comparable controls asking for different things. Under a list of answers it
   is the quiet offer to ask something the list did not cover, and it competes
   with nothing.

   The home page reaches the same modal from the closing band's "Ask a
   question" and from no-availability.php, both via .js-open-enquiry.
   -------------------------------------------------------------------------- */

.hv-ask {
	margin: clamp(28px, 3.5vw, 44px) 0 0;
	text-align: center;
}

.hv-ask__link {
	font-family: var(--ui);
	font-size: var(--fs-note);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--gold-deep);
	text-decoration: underline;
	text-underline-offset: 4px;
	/* A thumb target, without the link becoming a block that reads as a bar. */
	display: inline-block;
	padding: 12px 8px;
}

.hv-ask__link:hover,
.hv-ask__link:focus-visible {
	color: var(--ink);
}

/* .hv-faq .hv-ask__link and its :hover/:focus-visible were here, lifting the
   link's colour off the FAQ's ink ground. With the link gone from the home
   page there is no .hv-ask inside .hv-faq for them to match, and a selector
   matching nothing is a green light with nothing behind it. Removed rather
   than left in case it comes back — if it does, the rule is two lines. */

/* ==========================================================================
   15. Villa page — "Ask a question" (.hv-panel-ask)

   Replaces the fixed bottom bar that used to carry this action. The bar is
   gone entirely — markup, rules and all — rather than hidden; see the note
   where husnavillas_render_bottom_bar() used to be in functions.php.

   Full width at the foot of the panel, under Continue, so the enquiry route
   sits with the booking it is an alternative to instead of floating over
   the page.
   ========================================================================== */

.hv-panel-ask {
	width: 100%;
	margin-top: 12px;
}

/* ==========================================================================
   16. MotoPress calendar popup (kbwood/datepick)
   Retheme only — no plugin file touched, no hook, script or data attribute
   changed. Sitewide (not home.css) because the same calendar widget opens
   both from the home page search form and from the villa detail page's own
   booking calendar (single-room-type/calendar.php).
   ========================================================================== */

/*
 * MotoPress exposes the day-state colours as CSS custom properties with
 * hardcoded fallbacks (assets/css/mphb.min.css in the plugin) — defining
 * them here retints every day cell without overriding each of the
 * plugin's own selectors individually.
 *
 * Palette matches reference/design/husna-villas-light.html's .cal concept
 * (see its .cal-key legend): open days sit on --bone, a selected/booked
 * stay is solid --ink, and every unavailable state (booked by someone
 * else, not available, out of season) reads the same muted, background-
 * less grey rather than three different shades — the reference only
 * distinguishes "open" from "taken", not the reason a date is taken.
 * #BFBCB6 is that reference's own muted tone, lighter than --grey.
 */
:root {
	--mphb-available-date-bg: var(--bone);
	--mphb-available-date-color: var(--ink);
	/* Selected: ink ground, GOLD type — not paper. Measured on the real
	   hexes rather than eyeballed: --gold (#CAAA61) on --ink (#0B0B0B) is
	   8.85:1, comfortably AAA. --gold-deep would be 4.61:1, which is why
	   the deeper gold is wrong here even though it is the correct gold on
	   light grounds. The brand guide makes the same switch on dark
	   surfaces (.dark .numeral, .s-gold, .callout .stamp all use --gold). */
	--mphb-selected-date-bg: var(--ink);
	--mphb-selected-date-color: var(--gold);
	--mphb-booked-date-bg: transparent;
	--mphb-booked-date-color: #BFBCB6;
	--mphb-not-available-date-bg: transparent;
	--mphb-not-available-date-color: #BFBCB6;
	--mphb-out-of-season-date-bg: transparent;
	--mphb-out-of-season-date-color: #BFBCB6;
	--mphb-unselectable-date-color: #BFBCB6;
}

/*
 * The base kbwood/datepick library itself, and a couple of MotoPress
 * states, are hardcoded hex rather than routed through custom properties —
 * themed directly. Selectors are prefixed with .datepick-popup, matching
 * the specificity MotoPress's own compiled CSS uses, so this wins
 * regardless of which stylesheet the browser happens to load second.
 */
.datepick-popup .datepick {
	background: var(--paper);
	color: var(--ink);
	border: 1px solid var(--hair);
	border-radius: 0;
	font-family: var(--ui);
}

.datepick-popup .datepick button {
	border-radius: 0;
}

.datepick-popup .datepick-nav,
.datepick-popup .datepick-ctrl {
	background: var(--ink);
	color: var(--paper);
}

.datepick-popup .datepick-cmd:hover,
.datepick-popup .datepick-ctrl .datepick-cmd:hover {
	background: var(--gold-deep);
	color: var(--paper);
}

/* The month heading is set LIGHT, against date numbers set heavier below.
   That contrast is the brand's own: the guide runs display type at weight
   200 (h1,h2) and reserves 500/600 for utility type. The month is a label
   you read once; the numbers are what the eye scans, so the weight belongs
   on them. The selects have to be named explicitly — form controls do not
   inherit font-weight. */
.datepick-popup .datepick-month-header,
.datepick-popup .datepick-month-header select,
.datepick-popup .datepick-month-header input {
	background: var(--ink);
	color: var(--paper);
	font-weight: 200;
	letter-spacing: .02em;
}

.datepick-popup .datepick-month {
	border-color: var(--hair);
}

.datepick-popup .datepick-month th,
.datepick-popup .datepick-month th a {
	background: var(--bone);
	color: var(--grey);
}

.datepick-popup .datepick-month td {
	background: var(--paper);
	border-color: var(--hair);
}

/* Date numbers carry the weight — see the month-header note above. 500
   rather than 600: the numerals are small and set in the UI face, and 600
   at this size closes the counters up and reads as bold rather than as
   legible. */
.datepick-popup .datepick-month a,
.datepick-popup .datepick-month span {
	background: var(--paper);
	color: var(--ink);
	font-weight: 500;
}

.datepick-popup .datepick-month td .datepick-weekend {
	background: var(--bone);
}

/*
 * UNAVAILABLE DAYS HAVE TO READ AS UNAVAILABLE.
 *
 * The :root block above sets --mphb-*-date-color to #BFBCB6 for every
 * unavailable state, and none of it was reaching the page. MotoPress
 * consumes those properties in its own selectors, which lose to the
 * `.datepick-popup .datepick-month a` rule above — that rule sets
 * `color: var(--ink)` on EVERY day cell at (0,2,1) and was silently
 * flattening all of them.
 *
 * Measured before this rule existed, with a real range in the fields: a
 * booked day, a past day, a day that cannot start a stay and a fully
 * bookable day ALL painted rgb(11,11,11) at the same weight. The only
 * remaining signal was bone-versus-white background, which is a 10/255
 * difference. A guest could not tell what was for sale.
 *
 * Restated at (0,3,1) — three classes plus td — so it outranks the blanket
 * rule deterministically rather than by source order.
 *
 * COLOUR ONLY. Nothing here changes which dates are selectable; the
 * day-class map was captured before and after and is byte-identical on both
 * the home modal and the villa panel.
 *
 * Every unavailable state reads the SAME muted tone on purpose, per the
 * palette note at the top of this section: the reference design
 * distinguishes "open" from "taken", not the reason a date is taken.
 */
.datepick-popup .datepick-month td .mphb-not-available-date,
.datepick-popup .datepick-month td .mphb-unselectable-date,
.datepick-popup .datepick-month td .mphb-not-check-in-date,
.datepick-popup .datepick-month td .mphb-past-date,
.datepick-popup .datepick-month td .mphb-booked-date,
.datepick-popup .datepick-month td .mphb-out-of-season-date {
	color: #BFBCB6;
	/* Lighter, not just paler. The heavier numerals asked for in this pass
	   are meant to mark what a guest can actually book — carrying that
	   weight onto dates they cannot book spends it on the wrong days. */
	font-weight: 300;
}

/*
 * THE NIGHTS BETWEEN check-in and check-out.
 *
 * .hv-in-range is OURS, added by markInRange() in main.js — MotoPress marks
 * neither the range nor the check-out, so there was no existing class to
 * style and a chosen stay did not read as one. See that function for why
 * adding a class cannot affect which dates are selectable.
 *
 * A quiet band between two ink-and-gold ends: bone ground, ink type, the
 * same 500 the other bookable days carry. Deliberately NOT a third accent —
 * the two ends are the loud part, and the days between them are the stay,
 * not another decision. Sits at (0,3,1), so it beats the blanket day rule
 * but loses to the selected rules below, which is the order it needs:
 * check-in and check-out are never "in range".
 */
.datepick-popup .datepick-month td .hv-in-range {
	background: var(--bone);
	color: var(--ink);
	font-weight: 500;
	/* Hairline top and bottom rather than a full box, so consecutive nights
	   join into one continuous band across the week instead of reading as
	   separate chips. */
	box-shadow: inset 0 1px 0 var(--hair), inset 0 -1px 0 var(--hair);
}

/* An in-range day that MotoPress also considers unavailable still belongs to
   the stay the guest has chosen, so it keeps the band — but the muted rule
   above and this one are at equal specificity and it is declared later, so
   state this explicitly rather than relying on source order. (0,4,1) */
.datepick-popup .datepick-month td .hv-in-range.mphb-not-check-in-date,
.datepick-popup .datepick-month td .hv-in-range.mphb-unselectable-date {
	color: var(--ink);
	font-weight: 500;
}

/* The selected date wins over the muted rule above even when the cell also
   carries an unavailable class — which happens on the check-out picker,
   where the chosen check-in is no longer a legal check-in. Without this the
   guest's own choice greys itself out. (0,4,1). */
.datepick-popup .datepick-month td .datepick-selected.mphb-not-check-in-date,
.datepick-popup .datepick-month td .datepick-selected.mphb-unselectable-date {
	color: var(--gold);
	background: var(--ink);
	font-weight: 600;
}

.datepick-popup .datepick-month td .datepick-today {
	background: var(--bone);
	color: var(--gold-deep);
	font-weight: 600;
}

/*
 * The chosen date: ink ground, gold type. Both rules below are kept and both
 * updated, because they are NOT redundant — the first carries `a.` and so
 * outranks the second at (0,3,2) against (0,3,1). Changing only one would
 * leave the losing rule painting the old gold-on-ink and reintroduce the
 * exact ambiguity this replaces the moment either selector shifts.
 *
 * .datepick-highlight is datepick's hover/keyboard-focus state, not a range
 * marker — it rides along here deliberately so a keyboard user previewing a
 * date sees the same treatment they will get on commit.
 */
.datepick-popup .datepick-month td .datepick-highlight,
.datepick-popup .datepick-month td a.datepick-selected {
	background: var(--ink);
	color: var(--gold);
	font-weight: 600;
}

.datepick-popup .datepick-month td .datepick-selected {
	background: var(--ink);
	color: var(--gold);
	font-weight: 600;
}

/* MotoPress hardcodes this one rather than routing it through a custom
   property, and defines it twice at equal specificity for different
   contexts — !important makes the win deterministic regardless of which
   of the two, or which stylesheet load order, would otherwise apply.

   Moved onto the same ink/gold pair as .datepick-selected above so the
   chosen date cannot paint two different ways depending on which surface
   renders it.

   MEASURED, not assumed: this class does NOT appear on either the home
   search picker or the villa booking panel's picker. Both were read live
   with a completed 2026-11-23 → 11-27 range in the fields, and the full
   class vocabulary on both was identical and contained no mphb-check-in-date
   — only mphb-date-cell / mphb-available-date / mphb-not-available-date /
   mphb-selectable-date / mphb-unselectable-date / mphb-not-check-in-date,
   plus datepick-selected on exactly one cell. The rule is kept for whatever
   surface MotoPress does emit it on, but nothing on the two pages this
   theme styles is currently relying on it. */
.mphb-check-in-date {
	background-color: var(--ink) !important;
	color: var(--gold) !important;
	font-weight: 600;
}

/* ==========================================================================
   17. Villa page — shared CSS Grid (sticky booking column)

   hotel-booking/single-room-type.php renders every section as a flat
   sibling under one <div <?php post_class(); ?>> — no 2-column split
   exists in the markup. .type-mphb_room_type is WordPress's own standard
   post_class() output (post_class() always includes "type-{post_type}"),
   so this needs no new wrapper element and no hook reordering: each
   sibling just gets a named grid-area, and CSS alone reassembles them into
   a two-column layout with a sticky right-hand booking column.

   Named areas (not row-major auto-placement) so .hv-price-panel's grid
   cell can explicitly span both the description and amenities rows —
   giving it a tall sticky container to travel through, matching however
   long those two sections turn out to be, without hardcoding a height
   anywhere (the old reference markup's own .right{height:840px} doesn't
   carry over — see Stage 1, question 6).

   Single column, natural source order, no grid at all below 1024px —
   this section only exists inside the min-width media query.
   ========================================================================== */

/*
 * MotoPress emits an empty <p class="post-thumbnail ..."> wrapper (from
 * _renderFeaturedImageParagraphOpen/Close). featured-image.php produces
 * no visual output, so the paragraph renders at zero height — but it is
 * still a grid item, auto-placed into an implicit row with no grid-area
 * of its own. Hidden rather than removed: the hooks that emit it stay
 * exactly as they are.
 */
.type-mphb_room_type > .post-thumbnail {
	display: none;
}

/*
 * Same problem, different element, and this one was not cosmetic: MotoPress's
 * SingleRoomTypeView wraps the price template in
 * <p class="mphb-regular-price"><strong>Prices start at:</strong>…</p> from
 * the before/after_price hooks it fires ITSELF, around the template — so
 * hotel-booking/single-room-type/price.php declining to fire them does not
 * suppress the wrapper. The result was a live, unstyled "Prices start at:
 * $9,000 for 5 nights" sitting full-width just above the footer, outside the
 * panel and outside the grid's named areas.
 *
 * That is exactly the contradiction price.php was written to prevent: a
 * second price line, on the same page, quoted from a different calculation
 * than the panel's. Hidden here, alongside .post-thumbnail, because the
 * hooks that emit it stay untouched.
 */
.type-mphb_room_type > .mphb-regular-price {
	display: none;
}

@media (min-width: 1024px) {
	/*
	 * THE COMMENTS THAT USED TO STAND HERE DESCRIBED THE LAYOUT AND WENT STALE.
	 *
	 * One of them said the ink Spaces band "runs the full width of the page
	 * under the booking column". It never did: its area was `spaces spaces panel
	 * panel`, columns 1–2, and it stopped dead at the panel's left edge like
	 * every other band — measured 0→1729 against a panel edge at 1730. A reader
	 * had no way to check that claim without opening a browser, and it misled.
	 *
	 * So these describe the RULES the declarations enforce, not the picture they
	 * currently produce. A rule stays true as long as the declaration under it
	 * does; a description of the rendering has to be re-verified every time
	 * anything nearby moves, and nothing announces when it stops being true.
	 *
	 * The rules, in one place:
	 *
	 *   1. Every band spans all four columns, so its BACKGROUND is the full page
	 *      width. Its padding holds its CONTENT inside the content column, so
	 *      colour is the only thing that passes under the panel.
	 *   2. The panel column spans every content row, so it is a containing block
	 *      tall enough for the sticky card to travel through — and, being the
	 *      containing block, it is also what stops the card before the footer.
	 *   3. The panel's SURFACE belongs to the sticky card, not to the column.
	 *      A column-shaped fill is what made the panel read as a column.
	 *   4. The bedroom stepper is rendered inside the panel (see
	 *      husnavillas_render_tier_selector in functions.php), which is why the
	 *      booking side is one element rather than two.
	 */
	/*
	 * FOUR COLUMNS, NOT TWO — A GUTTER EACH SIDE, AND THIS IS THE FIX FOR THE
	 * PAGE SITTING OFF TO THE RIGHT ON A WIDE SCREEN.
	 *
	 * The grid used to be `minmax(0,1fr) 430px` with no max-width and no auto
	 * margins, so it filled the viewport: at 2560 the content column was 2115px
	 * wide and the panel was pinned to the right edge. The left column's .wrap
	 * then resolved to `margin-left: 771px; margin-right: 0` — because the rule
	 * below used to override ONE of .wrap's two auto margins, which turns a
	 * centred box into a right-aligned one and throws every spare pixel to the
	 * left. Measured: 0px at 1400, 131px at 1920, 771px at 2560.
	 *
	 * THIS IS THE THIRD SYMPTOM OF THAT ONE CAUSE. The other two are already
	 * patched elsewhere in this file: the header's wordmark drifted to 653px at
	 * 2560 (see the table in section 7) and .hv-vhero__in carries a max()
	 * padding formula written to chase the drifting column. Both patches
	 * compensated for the drift rather than removing it.
	 *
	 * IT IS NOT THE SHARED .wrap RULE. .wrap centres correctly on every other
	 * template — the home page measures margin-left:auto/margin-right:auto at
	 * every width and was never affected. The fault was this page overriding one
	 * of those margins inside an uncapped grid.
	 *
	 * The gutters absorb the slack symmetrically, so the content column is
	 * exactly --wrap-max and the composition centres. At or below 1774px
	 * (--wrap-max + 430) the gutters are 0 and nothing changes at all.
	 *
	 * The gutters are also what every band's padding is computed from, so
	 * widening or narrowing the composition moves the bands and the panel
	 * together. There is one number here, not two that have to be kept equal.
	 */
	/*
	 * NAMED AREAS ARE GONE, AND THE REASON IS A RULE RATHER THAN A PREFERENCE:
	 * grid-template-areas cannot express OVERLAP. Every band has to occupy all
	 * four columns while the panel occupies columns 3–4 of the same rows, and an
	 * area name can belong to only one rectangle. Explicit line placement is the
	 * only form that says two items share a cell.
	 *
	 * The COLUMNS are untouched — the same four that centre the composition
	 * (above). That is what keeps the centring true by construction rather than
	 * by luck: the content column is exactly --wrap-max, so wherever a band's
	 * content lands, the panel's mirror gutter is the same width.
	 *
	 * Rows are DOM order: hero, title, description, gallery, amenities, Spaces,
	 * what-to-know. The closed lightbox <dialog> is a direct child too, but a
	 * closed dialog is display:none and therefore not a grid item, so it needs
	 * no row of its own.
	 */
	.type-mphb_room_type {
		display: grid;
		grid-template-columns:
			minmax(0, 1fr)
			minmax(0, var(--wrap-max))
			430px
			minmax(0, 1fr);
		grid-template-rows: repeat(8, auto);
		column-gap: 0;
		align-items: start;
	}

	/*
	 * THE RULE THIS BLOCK EXISTS TO HOLD, stated once for all six bands:
	 *
	 *     the BACKGROUND spans the full page width;
	 *     the CONTENT stops at the panel's left edge.
	 *
	 * Colour is the only thing that passes under the panel. Text and images
	 * never do. Both halves come from the same two declarations: the band spans
	 * every column, so its border box is the page; its padding then pulls the
	 * content box back to exactly the width the old single-column area had —
	 * --wrap-max where there is slack, 100% − 430px where there is not.
	 *
	 * --hv-gutter is the SAME expression .hv-vhero__in uses. Sharing it is the
	 * point: the hero and the bands agree because they compute the same thing,
	 * not because two formulas were tuned to the same answer.
	 */
	.type-mphb_room_type > .mphb-room-type-title,
	.type-mphb_room_type > .hv-description,
	.type-mphb_room_type > .hv-villa-gallery,
	.type-mphb_room_type > .hv-amenities,
	.type-mphb_room_type > .hv-spaces,
	.type-mphb_room_type > .hv-area,
	.type-mphb_room_type > .hv-villa-info {
		grid-column: 1 / -1;
		--hv-gutter: max(0px, calc((100% - var(--wrap-max) - 430px) / 2));
		padding-left: var(--hv-gutter);
		padding-right: calc(430px + var(--hv-gutter));
	}

	/* The hero is the grid child now — .hv-gallery is a layer inside it. It
	   spans the columns AND has no panel to avoid, so it takes no padding. */
	.type-mphb_room_type > .hv-vhero {
		grid-row: 1;
		grid-column: 1 / -1;
	}

	.type-mphb_room_type > .mphb-room-type-title {
		grid-row: 2;
	}

	.type-mphb_room_type > .hv-description {
		grid-row: 3;
	}

	.type-mphb_room_type > .hv-villa-gallery {
		grid-row: 4;
	}

	.type-mphb_room_type > .hv-amenities {
		grid-row: 5;
	}

	.type-mphb_room_type > .hv-spaces {
		grid-row: 6;
	}

	.type-mphb_room_type > .hv-area {
		grid-row: 7;
	}

	.type-mphb_room_type > .hv-villa-info {
		grid-row: 8;
	}

	/*
	 * THE COLUMN IS NOW INVISIBLE, AND THAT IS THE WHOLE CHANGE.
	 *
	 * It keeps its grid area, because it is the sticky card's containing block:
	 * that is what bounds the card's travel and stops it before the footer
	 * without a single measured height. What it loses is its SURFACE — the
	 * limestone fill, the gold rule and the hairline seam all move down to the
	 * card below. A fill shaped like a column reads as a column, however the
	 * thing inside it behaves.
	 *
	 * z-index is not decoration. .hv-spaces and .hv-villa-info come AFTER the
	 * panel in the DOM, so with the bands now running underneath it, painting
	 * order alone would put the ink band on top of the card.
	 */
	.type-mphb_room_type > .hv-price-panel {
		grid-row: 2 / -1;
		grid-column: 3 / -1;
		align-self: stretch;
		background: transparent;
		border-top: none;
		padding-top: 0;
		padding-bottom: 0;
		position: relative;
		z-index: 2;
	}

	.type-mphb_room_type > .hv-price-panel > .wrap {
		position: sticky;
		/* Was 104px, to clear this page's own sticky header. The header no
		   longer sticks here (section 7), so that clearance became a gap
		   above the panel with nothing in it — 24px is now just the visual
		   breathing room, which is all that is left to account for. */
		top: 24px;
		padding-top: 26px;
		padding-bottom: 30px;

		/*
		 * THE FLOOR UNDER THE HEIGHT BUDGET — and it is a floor, not the first
		 * answer. Section 17c takes the priced panel down without removing
		 * anything, which is the real fix. It is still taller than a 768px
		 * viewport can show.
		 *
		 * (An earlier version of this comment also said the breakdown's first
		 * row had ZERO horizontal slack. That figure came from measuring a
		 * shrink-to-fit inline span against its own text, which are the same
		 * number by definition and would report 0 in any state. Measured against
		 * the row instead, the label has about 104px in hand. The claim was
		 * withdrawn at the time; it is removed here because it survived in this
		 * file and would have been read as a live constraint.)
		 *
		 * Buying those last pixels would mean cutting type sizes and the
		 * Continue button's padding, which trades a reachable foot for a
		 * cramped panel and still leaves nothing in hand. So the panel is
		 * allowed to scroll INSIDE ITSELF when, and only when, it genuinely
		 * cannot fit: at 1080 it has 285px of slack and at 1600 it has 805px,
		 * so this never engages there.
		 *
		 * Safe to make a scroll container: the date picker is appended to
		 * document.body with position:fixed (main.js), so it is not a
		 * descendant and cannot be clipped by this.
		 *
		 * overscroll-behavior stops the page from taking over the moment the
		 * panel reaches its end, which on a short window is most scrolls.
		 */
		max-height: calc(100vh - 48px);
		max-height: calc(100dvh - 48px);
		overflow-y: auto;
		overscroll-behavior: contain;
	}

	/*
	 * THE `margin-right: 0` PATCH THAT USED TO LIVE HERE IS DELETED.
	 *
	 * It existed to pin the left column's blocks to the panel seam, and it did
	 * that by overriding one of .wrap's two auto margins — which is exactly what
	 * made the page sit off to the right once the column grew wider than
	 * --wrap-max. With the gutter columns above, the content column IS
	 * --wrap-max, so .wrap fills it exactly and both margins resolve to 0 on
	 * their own. The patch is not merely harmless now, it is unreachable.
	 *
	 * Removing it rather than leaving it is the point: it was the third patch
	 * for one cause, and the cause is gone. Verified by measuring that nothing
	 * moves at 1400px, where the rule used to be a no-op anyway.
	 */

	/*
	 * The sticky box stays 430px wide even though its grid area spans the panel
	 * column and the right gutter, so the card sits against the content column
	 * and the gutter beside it shows whichever band is passing behind.
	 */
	.type-mphb_room_type > .hv-price-panel > .wrap {
		max-width: 430px;
		margin-left: 0;
		margin-right: auto;
	}

	/*
	 * THE CARD'S SURFACE, AND THE RULE ITS EDGE TREATMENT HAS TO SATISFY:
	 * it must read as a distinct object over EVERY ground it crosses, and it
	 * crosses three — ink (#0B0B0B, Spaces), paper (#FFFFFF, the description and
	 * the transparent bands over the page) and bone (#F5F4F1, amenities).
	 *
	 * No single flat fill can do that. Bone on ink is unmissable; bone on paper
	 * is a two-percent step; bone on bone is nothing whatsoever. So the fill
	 * cannot be the whole answer and the EDGE has to carry the card over the two
	 * light grounds:
	 *
	 *   gold hairline on top   reads on all three; the strongest single cue
	 *                          that this is an object rather than a region
	 *   --dim on three sides   defines the card over paper AND over bone, and
	 *                          disappears over ink, where the eye reads the
	 *                          bone-against-black boundary instead
	 *
	 * --dim, NOT --hair. --hair (#E4E2DD) is very nearly the card's own bone
	 * against the bone band and contributes almost nothing there; --dim
	 * (#CFCDC8) is the value this file already reaches for when a hairline is
	 * too light to survive its ground (see the note in section 6).
	 *
	 * AND NO DROP SHADOW, which is not what was planned. A soft two-stop shadow
	 * was built first, on the reasoning that only depth could separate bone from
	 * bone. Photographed against all three grounds at 4x beside this version, it
	 * lost on both of the grounds that matter: over bone it gives a diffuse
	 * ~30px halo where the flat border gives an exact corner, and over ink it
	 * leaves a faint dark smudge on a field that should be clean. The predicted
	 * objection to the flat version — that a heavier border would read as an
	 * outline over ink — did not survive being looked at.
	 *
	 * The deciding point is language: every other card on this page (the bedroom
	 * cards, the four what-to-know boxes) is a flat hairline box. This is the
	 * same kind of object, larger and sticky, and it should be built the same
	 * way rather than being the one element on the site with depth.
	 */
	.type-mphb_room_type > .hv-price-panel > .wrap {
		background: var(--bone);
		border: 1px solid var(--dim);
		border-top: 1px solid var(--gold);
	}
}

/* ==========================================================================
   17b. Villa page — the panel's approved running order

   price · DATES · nights · GUESTS · BEDROOMS OPEN · breakdown · CONTINUE.

   Achieved with flex `order`, and NOT ONE DOM NODE MOVES. That matters:
   MotoPress's mphb_tmpl_the_room_reservation_form() is a single monolithic
   function in the plugin with no hooks between the date fields and the
   reserve section, so guests and bedrooms cannot be injected into the middle
   of the form — and moving the form's own children out of it in JS would
   break both submission and the control's jQuery lookups.

   `display: contents` on the form is what makes it work: the form generates
   no box, so ITS children (the date fields, the reserve section) become flex
   items of .wrap alongside the guests stepper, the bedroom stepper and the
   three written regions, and one `order` scale arranges all of them. Form
   association is by DOM ancestry, which display:contents does not change, so
   the form still posts exactly what it posted before.
   ========================================================================== */

/*
 * A WRAPPING ROW, not a column — even though almost everything in it is
 * full width. The two date fields are the reason: they have to sit side by
 * side on one line, and with `display: contents` on the form they are
 * ordinary flex items of this container with no wrapper of their own to
 * make a row out of. So the container wraps, every item claims the full
 * width by default, and only the two date fields opt out.
 */
.hv-price-panel > .wrap {
	display: flex;
	flex-flow: row wrap;
	align-items: flex-start;
}

.hv-price-panel > .wrap > *,
.hv-price-panel .mphb-booking-form > * {
	flex: 0 0 100%;
	min-width: 0;
}

.hv-price-panel .mphb-booking-form {
	display: contents;
}

/* MotoPress's own H2, already visually suppressed elsewhere; pinned first so
   it cannot land between two ordered controls. */
.hv-price-panel .mphb-reservation-form-title { order: 0; }

.hv-price-panel [data-hv-quote]             { order: 1; }
.hv-price-panel .mphb-required-fields-tip   { order: 2; }
.hv-price-panel .mphb-check-in-date-wrapper { order: 3; }
.hv-price-panel .mphb-check-out-date-wrapper{ order: 4; }
.hv-price-panel [data-hv-nights]            { order: 5; }
.hv-price-panel .hv-guests                  { order: 6; }
.hv-price-panel .hv-tier-selector           { order: 7; }
.hv-price-panel [data-hv-breakdown]         { order: 8; }
.hv-price-panel .mphb-reserve-room-section  { order: 9; }

/* Last, under Continue. Needs an explicit order like everything else in
   this flex row: an item with no `order` defaults to 0, which put the
   enquiry link up beside the form title at the top of the panel. */
.hv-price-panel .hv-panel-ask               { order: 10; }

/* The stepper was a full-bleed grid item with its own fill and seam while it
   lived beside the title. Inside the panel it is one control among several,
   so it drops the borders and the bone fill and matches the guests stepper. */
.hv-price-panel .hv-tier-selector {
	background: none;
	border: 0;
	padding: 0;
	margin-top: 18px;
}

.hv-price-panel .hv-tier-selector > .wrap {
	padding: 0;
	margin: 0;
	max-width: none;
	width: auto;
}

/* --------------------------------------------------------------------------
   The price block. Left aligned, three lines, all derived from the engine's
   total for the stay (main.js renderQuote): the total, the nightly rate, and
   the per-person figure. Sizes step down so the eye reads them in that order.
   -------------------------------------------------------------------------- */

.hv-quote {
	text-align: left;
}

/* The headline: the NIGHTLY rate. The total is stated once, at the bottom,
   in the breakdown — it is not repeated here. */
.hv-quote__nightly {
	margin: 0;
	line-height: 1.05;
}

.hv-quote__nightly strong {
	font-family: var(--display);
	font-size: var(--fs-3xl);
	font-weight: var(--disp-w);
	letter-spacing: var(--disp-ls-lg);
	color: var(--ink);
}

/* "per night" is the unit, not part of the figure — kept small and on the
   same line, so the number reads as one thing rather than two headings. */
.hv-quote__nightly span {
	font-family: var(--ui);
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	white-space: nowrap;
}

.hv-quote__perperson {
	margin: 8px 0 0;
	font-size: var(--fs-ms);
	font-weight: 300;
	color: var(--grey);
}

/* --------------------------------------------------------------------------
   Dates side by side.

   A wrapping flex ROW, deliberately not a two-column grid: a rigid grid
   would hold two columns however long the formatted dates get, and this has
   to survive a locale whose date strings are much longer than "09/07/2026".
   The basis plus wrap lets the pair drop to two lines only when they
   genuinely cannot fit.
   -------------------------------------------------------------------------- */

.hv-price-panel .mphb-check-in-date-wrapper,
.hv-price-panel .mphb-check-out-date-wrapper {
	flex: 1 1 44%;
	min-width: 0;
	margin-top: 0;
}

/* The two boxes meet on one hairline seam rather than floating apart, the
   same treatment the steppers already use. */
.hv-price-panel .mphb-check-out-date-wrapper {
	margin-left: -1px;
}

.hv-nights {
	margin: 9px 0 4px;
	font-size: var(--fs-note);
	font-weight: 300;
	color: var(--grey);
	text-align: center;
}

/*
 * The stepper value cells. main.js hides each <select> and puts this plain
 * span in its place, so the steppers are the only thing the guest can
 * operate — the selects stayed openable otherwise, which is how an invalid
 * combination (10 guests, 1 bedroom) could be chosen and then silently
 * corrected at checkout. The selects are still in the markup and still
 * post; see enhanceSelect() in main.js.
 */
.hv-stepper-value {
	display: block;
	width: 100%;
	text-align: center;
}

/* A stay below the minimum is not a styling nicety — MotoPress refuses it at
   checkout, so the panel has to look like it means it. */
.hv-nights--short {
	color: var(--red);
}

/* The breakdown: label left, figure right, one row per line, with the total
   set apart above the CTA. */
.hv-breakdown {
	margin-top: 18px;
	padding-top: 14px;
	border-top: 1px solid var(--hair);
}

.hv-breakdown__row {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 16px;
	padding: 6px 0;
	font-size: var(--fs-ms);
	font-weight: 300;
	color: var(--grey);
}

.hv-breakdown__row span:last-child {
	white-space: nowrap;
}

.hv-breakdown__row--total {
	margin-top: 8px;
	padding-top: 12px;
	border-top: 1px solid var(--hair);
	font-size: var(--fs-body);
	color: var(--ink);
}

/* ==========================================================================
   17c. Villa page — the panel's height budget

   THE PANEL HAS TO FIT A 768px VIEWPORT, PRICED, WITH ITS FOOT ON SCREEN.

   Measured before this block existed: 505px with no dates in it, and 956px
   once a date range is quoted — the state a guest is actually looking at when
   they need Continue. At 1366x768 that overflowed by 182px, so the bottom of
   the panel was unreachable: sticky pins its top at 24px and the column
   scrolls past underneath, so nothing ever brings the foot up.

   NOTHING IS REMOVED AND NO BEHAVIOUR CHANGES. Every field, every region and
   every control is still here and still does what it did. This block only
   takes padding, gaps and control heights down, and it is scoped to
   .hv-price-panel throughout — .hv-guests and its label are SHARED with the
   home page's search modal, and tightening them globally would shrink a
   control on a page this work is not about.

   THE 44px TOUCH FLOOR IS NOT CROSSED. The stepper controls go 56px to 48px,
   which is still above it. That floor is why the diet stops where it does.

   Everything here is a measured number, not a guess — see the plan file for
   the item-by-item budget this was drawn from.
   ========================================================================== */

/* The panel's own frame. */
.type-mphb_room_type > .hv-price-panel > .wrap {
	padding-top: 16px;
	padding-bottom: 18px;
}

/* The price block. */
.hv-price-panel .hv-quote {
	margin-top: 10px;
	padding-top: 10px;
}

.hv-price-panel .hv-quote__perperson {
	margin-top: 6px;
}

/* MotoPress's "Required fields are followed by *" line. */
.hv-price-panel .mphb-required-fields-tip {
	margin-bottom: 6px;
}

/* The two date boxes. */
.hv-price-panel .mphb-check-in-date-wrapper,
.hv-price-panel .mphb-check-out-date-wrapper {
	margin-bottom: 8px;
	padding: 8px 12px;
	min-height: 48px;
}

.hv-price-panel .hv-nights {
	margin: 5px 0 2px;
}

/* The two steppers. Label, control and sub-caption all come in together so
   the pair stays visually identical to each other. */
.hv-price-panel .hv-guests {
	margin-bottom: 8px;
}

.hv-price-panel .hv-guests__label,
.hv-price-panel .hv-tier-selector__label {
	margin-bottom: 5px;
}

.hv-price-panel .hv-guests__val,
.hv-price-panel .hv-guests__btn,
.hv-price-panel .hv-tier-selector__val,
.hv-price-panel .hv-tier-selector__nav {
	min-height: 48px;
}

.hv-price-panel .hv-guests__val,
.hv-price-panel .hv-tier-selector__val {
	padding-top: 5px;
	padding-bottom: 5px;
	gap: 3px;
}

.hv-price-panel .hv-tier-selector {
	margin-top: 8px;
}

/* The breakdown: four rows, and each row's padding is paid four times. */
.hv-price-panel .hv-breakdown {
	margin-top: 10px;
	padding-top: 8px;
}

.hv-price-panel .hv-breakdown__row {
	padding: 3px 0;
}

.hv-price-panel .hv-breakdown__row--total {
	margin-top: 4px;
	padding-top: 8px;
}

/* Continue and its surround. */
.hv-price-panel .mphb-reserve-room-section {
	padding: 10px 24px;
}

.hv-price-panel .mphb-reserve-room-section p {
	margin-bottom: 8px;
}

.hv-price-panel .hv-panel-ask {
	margin-top: 8px;
}

/* ==========================================================================
   18. Villa page — counts-only status line: REMOVED

   .hv-tier-status held "N bedrooms open · N bathrooms · Sleeps up to N"
   under a gold rule, with "N bedrooms stay closed, locked, not cleaned and
   not charged" beneath it. All of it is gone — markup, these rules, and the
   block in main.js that repainted it on a tier change.

   Every fact it carried is now stated better elsewhere: the open/closed
   split IS the bedroom card grid, the explanation is the section's own
   standfirst, and the guest count is in both the spec row and the panel's
   stepper. Once the standfirst landed, its closed sentence and this one
   were near-verbatim duplicates sitting a few lines apart.
   ========================================================================== */

/* ==========================================================================
   18b. Villa page — the bedroom list (.hv-bedroom-list)

   One row per bedroom in the villa's largest tier, with the rows this tier
   does not open shown closed. It sits under the counts line and carries
   visually what that line states numerically; the counts line keeps the
   sentence that explains WHY a smaller tier costs less ("2 bedrooms stay
   closed, locked, not cleaned and not charged"), which a list of rows
   cannot say on its own.

   Closed rows are dimmed rather than removed. A list that simply got
   shorter would show the guest what they are buying; a list that keeps
   every room and greys the shut ones shows them what they are choosing
   between, which is the decision the stepper is asking them to make.
   ========================================================================== */

/*
 * .hv-cards — the shared card grid, used by the bedroom list and by the
 * Spaces list below it.
 *
 * repeat(3, 1fr), NOT auto-fit/minmax. With five bedrooms the second row
 * holds two cards, and auto-fit would stretch them across the full width;
 * fixed tracks keep every card the same width as the first row and leave
 * the third slot of the last row empty, which is the intended shape.
 *
 * Row heights equalise on their own: grid items default to
 * align-self:stretch, so cards in the same row match the tallest of that
 * row and rows are sized independently of each other.
 */
.hv-cards {
	list-style: none;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 14px;
	margin: 22px 0 0;
	padding: 0;
}

/* Two columns at every desktop width. Four Spaces cards make a 2×2 block;
   on the three-column track they came out 3 + 1, which left the last card
   stranded beside two empty slots. */
.hv-cards--two {
	grid-template-columns: repeat(2, 1fr);
}

@media (max-width: 1180px) {
	.hv-cards {
		grid-template-columns: repeat(2, 1fr);
	}
}

/* One column, and the cards keep their box border rather than collapsing
   back to hairline rows. */
@media (max-width: 620px) {
	.hv-cards {
		grid-template-columns: 1fr;
	}
}

/*
 * The card itself. An outline, not a tile: the fill is the section's own
 * limestone, so only the border draws the box. Resting border is the gold
 * accent at low opacity — present enough to read as a container, quiet
 * enough not to compete with the panel's one solid gold rule.
 */
.hv-bedroom,
.hv-space {
	display: grid;
	align-content: start;
	gap: 4px;
	padding: 18px 18px 20px;
}

/* ---- Bedroom card ---- */

/* White fill on the limestone section, so the card reads as a tile lifted
   off the ground rather than an outline drawn on it — the canonical
   design's own .room treatment (background:var(--paper)). */
.hv-bedroom {
	grid-template-columns: 1fr auto;
	background: var(--paper);
	border: 1px solid rgba(202, 170, 97, .38);
}

.hv-bedroom__name {
	font-size: var(--fs-body);
	font-weight: 300;
}

.hv-bedroom__desc {
	grid-column: 1;
	font-size: var(--fs-ms);
	font-weight: 300;
	color: var(--grey);
}

/* Top right of the card, on the name's line. */
.hv-bedroom__state {
	grid-column: 2;
	grid-row: 1;
	justify-self: end;
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--gold-deep);
}

/* A closed bedroom dims its contents AND its box, so a shut room reads as
   shut from the shape of the card and not only from the word in it. The
   fill drops back to the section's limestone, which is what the design does
   for .room.off. */
.hv-bedroom--closed {
	background: var(--bone);
	border-color: var(--hair);
}

.hv-bedroom--closed .hv-bedroom__name,
.hv-bedroom--closed .hv-bedroom__desc,
.hv-bedroom--closed .hv-bedroom__state {
	color: var(--disabled);
}

/* ==========================================================================
   18c. Villa page — section heads (.hv-sec__*) and the ink variant

   The canonical design gives every section on this page the same three-part
   head: .eb, .h2x, .sub (reference/design/husna-villa-internal page.html,
   lines 378-383). Those roles are carried here under this theme's own
   names, and the two that already have shared primitives — .eyebrow and
   .disp — are reused rather than restated, so only the standfirst and the
   per-context sizing live below.
   ========================================================================== */

.hv-sec__eyebrow {
	margin: 0;
}

/* --fs-disp-sm is the section-heading step (.h2x); --fs-disp-lg is hero
   scale and belongs to the villa name alone. */
.hv-sec__title {
	font-size: var(--fs-disp-sm);
	line-height: 1.07;
	max-width: 19ch;
	margin-top: 18px;
}

.hv-sec__sub {
	font-size: var(--fs-body);
	font-weight: 300;
	line-height: 1.65;
	color: var(--grey);
	max-width: 52ch;
	margin-top: 20px;
}

/*
 * Ink ground, full bleed. The design alternates section grounds down the
 * page — .sec, .sec.bone, .sec.ink — and Spaces is the ink one. It is not
 * a decorative choice: white headings and limestone body copy only exist
 * as legible type on a dark ground.
 *
 * WHY THIS RENDERS ON after_content RATHER THAN INSIDE attributes.php (see
 * husnavillas_render_spaces): only a direct child of .type-mphb_room_type can
 * be a grid item, and only a grid item can span the columns. From inside
 * .hv-amenities > .wrap it would be trapped in the content column and could
 * not carry a background at all.
 *
 * The previous comment here claimed this band "runs the full width of the page
 * under the booking column". It did not — its area was columns 1–2 and it
 * stopped at the panel's left edge, measured 0→1729 against a panel edge at
 * 1730 — and the claim was believed and acted on. It is replaced by the reason
 * the element sits where it does, which is checkable from the code beneath it.
 * The full-width behaviour it wrongly described is now real, and is stated as a
 * rule in section 17 where the declarations that enforce it live.
 */
.hv-sec--ink {
	background: var(--ink);
	color: var(--paper);
	padding-top: var(--pad-v);
	padding-bottom: var(--pad-v);
}

.hv-sec--ink .hv-sec__title {
	color: var(--paper);
}

.hv-sec--ink .hv-sec__sub {
	color: var(--dim);
}

/* ---- Bedrooms ---- */

/* Clear of MotoPress's attributes list above. Without this the "Bedrooms"
   eyebrow sat directly on the last attribute row, reading as another line
   of that table rather than as the head of a new section. */
.hv-rooms {
	margin-top: clamp(34px, 5vw, 56px);
}

/* ---- Spaces ---- */

/*
 * No borders on these cards at all. The divider is the GAP: the grid shows
 * its own gold background through a 1px gutter, and the opaque ink cards
 * mask everything else — so a 2×2 block draws exactly one cross and no
 * outer frame. A border on each card could not do this; it would double
 * every internal line and box the block in.
 */
.hv-spaces__list {
	gap: 1px;
	background: var(--gold);
}

.hv-space {
	background: var(--ink);
	border: 0;
	padding: clamp(20px, 2.4vw, 30px);
}

/* One column: a vertical stack of four gives three horizontal rules and no
   cross, which is the same idea and the only thing the shape can mean at
   this width. */
@media (max-width: 620px) {
	.hv-spaces__list {
		gap: 1px;
	}
}

/*
 * A display numeral, not a small-caps label — the design sets .sn at 26px
 * weight 200 with negative tracking, which is why it reads as a numbered
 * plate rather than as another eyebrow. --gold (not --gold-deep): gold-deep
 * is the brand's gold for text on a LIGHT ground; on ink the accent is
 * --gold, as the hero gloss already does.
 */
.hv-space__num {
	display: block;
	font-size: var(--fs-2xl);
	color: var(--gold);
	line-height: 1;
}

.hv-space__title {
	font-size: var(--fs-body);
	font-weight: 400;
	line-height: 1.35;
	color: var(--paper);
	margin-top: 16px;
}

/*
 * --bone (limestone), as specified. Worth knowing before this is settled:
 * the canonical design uses --dim (#CFCDC8) here, and limestone (#F5F4F1)
 * against the white title above it is a 4% step — a difference that does
 * not survive being looked at. --dim gives the hierarchy an actual step
 * down and is a one-token change if that reads better.
 */
.hv-space__body {
	font-size: var(--fs-ms);
	font-weight: 300;
	line-height: 1.7;
	color: var(--bone);
	margin-top: 10px;
}

/* ==========================================================================
   19. Villa page — enquiry modal (.hv-enquiry-modal)

   The two-fork design's enquiry path only — there is no payment gateway
   configured on this site, and no hold/card-number fork exists anywhere in
   this markup, hidden or otherwise (see functions.php,
   husnavillas_render_enquiry_modal()). Opened via <dialog>.showModal() by
   main.js in place of the bottom bar's real mailto: link, when JS is
   available — the mailto: link is the entire no-JS path, so this dialog
   is never the only way to reach someone.
   ========================================================================== */

.hv-enquiry-modal {
	max-width: 440px;
	width: calc(100% - 32px);
	border: 1px solid var(--hair);
	padding: clamp(24px, 4vw, 40px);
	background: var(--paper);
	color: var(--ink);
	/* A <dialog> in the top layer is centred by the user agent's own
	   `margin: auto`, which the theme's reset zeroes along with every other
	   margin — so this sat flush against the left edge of the viewport.
	   Restated here rather than exempting <dialog> from the reset, which
	   would change every dialog on the site at once. */
	margin: auto;
}

.hv-enquiry-modal::backdrop {
	background: rgba(11, 11, 11, .6);
}

.hv-enquiry-modal__close {
	position: absolute;
	top: 16px;
	right: 16px;
	width: 32px;
	height: 32px;
	border: 0;
	background: none;
	font-size: var(--fs-lg);
	line-height: 1;
	color: var(--grey);
	cursor: pointer;
}

.hv-enquiry-modal__close:hover {
	color: var(--ink);
}

.hv-enquiry-form__intro {
	font-size: var(--fs-ms);
	color: var(--grey);
	margin: 8px 0 24px;
}

.hv-enquiry-form__field {
	margin: 0 0 16px;
}

/* --grey, matching the design's .inp label — gold-deep on every field
   label spends the accent on chrome instead of on what matters. */
.hv-enquiry-form__field label {
	display: block;
	font-size: var(--fs-3xs);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	font-weight: 600;
	margin-bottom: 8px;
}

/* select included: the form gained a guests and a "which house" dropdown,
   and without it here they rendered as bare system controls beside the
   themed text fields. background:none would leave a select transparent on
   some platforms, so these get --paper explicitly.

   THE BOOKING PAGE'S FIELD TREATMENT, verbatim from .hv-co-field: a 48px
   floor, 13px/14px padding and --fs-ms. The two forms ask a guest for their
   name and email a few clicks apart and had two different answers for how
   tall that box is; this is the one that was settled.

   NOT SCOPED TO DESKTOP, deliberately, even though the layout change below
   is. "The two should not disagree" is a statement about the treatment, and
   leaving the phone on the old padding and --fs-body would keep exactly the
   disagreement this closes. On a phone the change is 1px of field height and
   one step of type size.

   It is worth being clear that this is NOT what makes the modal fit. Applied
   alone it takes the content from 899px to 894px — five pixels, with the
   submit button still 110px below the viewport. The rows are the height; see
   the grid below. */
.hv-enquiry-form__field input,
.hv-enquiry-form__field textarea,
.hv-enquiry-form__field select {
	width: 100%;
	min-height: 48px;
	border: 1px solid var(--hair);
	background: none;
	padding: 13px 14px;
	font-family: var(--ui);
	font-size: var(--fs-ms);
	color: var(--ink);
}

.hv-enquiry-form__field select {
	background: var(--paper);
	/* Appearance stays native so the disclosure arrow and the platform's own
	   picker are kept. The home search modal's select is the one that hides
	   its chrome, because there a painted control sits over it; nothing sits
	   over this one. The 44px floor that used to be restated here is gone —
	   the rule above now sets 48px for every control in the form, and two
	   floors for the same thing is how they drift apart. */
}

.hv-enquiry-form__field input:focus,
.hv-enquiry-form__field textarea:focus,
.hv-enquiry-form__field select:focus {
	outline: none;
	border-color: var(--gold-deep);
}

/*
 * The two date fields, side by side on one wrapping row — the same shape as
 * the villa panel's date pair, so the enquiry form and the booking panel
 * ask for a stay the same way.
 *
 * Wraps rather than shrinks: below ~380px two date inputs cannot both hold
 * a full date without clipping the year, and a clipped year on a date field
 * is worse than a second row.
 */
.hv-enquiry-form__stay {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
}

.hv-enquiry-form__field--date {
	flex: 1 1 8.5rem;
	min-width: 0;
}

/*
 * The native calendar icon is the ONLY thing Chrome opens the picker from,
 * which is why main.js adds a click handler covering the whole field. The
 * cursor follows suit so the affordance matches the behaviour — without it
 * the field says "text" while behaving like a button.
 */
.hv-enquiry-form__field--date input {
	cursor: pointer;
}

/* --------------------------------------------------------------------------
   TWO COLUMNS ABOVE 700px, and this is what makes the form fit.

   Measured at 1366x768: the modal box was 440x732 holding 899px of content
   against a 730px client, so it scrolled internally and the submit button sat
   at bottom 878 — 110px below the viewport, with elementFromPoint at its own
   centre returning null. The button was genuinely unreachable.

   Matching the booking page's FIELD treatment (above) accounts for 5px of
   that. The height is in the ROW COUNT, and the booking page has the answer
   to that too: .hv-co-grid is repeat(2, minmax(0, 1fr)) with .hv-co-field--wide
   for the ones that span. Nine stacked fields become five rows:

     NAME              | EMAIL
     WHATSAPP (OPTIONAL)                --wide
     CHECK IN          | CHECK OUT      __stay, already a pair
     GUESTS            | WHICH HOUSE
     MESSAGE                            --wide

   620px rather than 440, because at 440 the columns would be 172px each and
   the comment on .hv-enquiry-form__stay records that two date inputs below
   ~380px clip the year.

   Result, measured: 676px clean and 716px with a validation error showing,
   against a 768px viewport — 92px and 52px of headroom, no internal scroll,
   and the submit button hit-testing to itself. The error state is the one
   that matters, because an empty form is not the worst realistic state and
   an error is exactly when a guest needs the button.

   MOBILE IS UNTOUCHED. Below 700 the form stays one column at
   calc(100% - 32px) capped at 440.
   -------------------------------------------------------------------------- */
@media (min-width: 700px) {
	.hv-enquiry-modal {
		max-width: 620px;
		padding: 32px;
	}

	.hv-enquiry-form__step[data-step="form"] {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: 16px;
		/* Without this a short field stretches to the height of the tall one
		   beside it, which on a row holding a select and an input is visible. */
		align-items: start;
	}

	/* Everything that is not one of the four paired fields. The hidden inputs
	   are listed because a grid makes them items too, and an unplaced item
	   claims a cell — three of them would silently open a fifth row. */
	.hv-enquiry-form__step[data-step="form"] > .eyebrow,
	.hv-enquiry-form__step[data-step="form"] > .hv-enquiry-form__intro,
	.hv-enquiry-form__step[data-step="form"] > .hv-enquiry-form__stay,
	.hv-enquiry-form__step[data-step="form"] > .hv-enquiry-form__hp,
	.hv-enquiry-form__step[data-step="form"] > .hv-enquiry-form__error,
	.hv-enquiry-form__step[data-step="form"] > input[type="hidden"],
	.hv-enquiry-form__step[data-step="form"] > .hv-enquiry-form__submit,
	.hv-enquiry-form__step[data-step="form"] > .hv-enquiry-form__field--wide {
		grid-column: 1 / -1;
	}

	.hv-enquiry-form__field {
		margin: 0 0 12px;
	}
}

/* Honeypot — visually hidden, not display:none (some bots specifically
   skip display:none fields), never focusable via keyboard (tabindex="-1"
   on the input itself, see functions.php). */
.hv-enquiry-form__hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.hv-enquiry-form__error {
	color: var(--red);
	font-size: var(--fs-ms);
	margin: 0 0 16px;
}

.hv-enquiry-form__submit {
	width: 100%;
}

.hv-enquiry-form__step--done {
	text-align: center;
	padding: 20px 0;
}

/* Gold tick, from the design's own .done .tick — the confirmation is
   exactly the kind of moment it reserves the accent for. */
.hv-enquiry-form__step--done .eyebrow::before {
	content: "\2713";
	display: flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	margin: 0 auto 22px;
	border: 1px solid var(--gold);
	color: var(--gold-deep);
	font-size: var(--fs-xl);
	font-weight: 200;
}

.hv-enquiry-form__step--done p {
	margin: 16px auto 24px;
	max-width: 44ch;
	font-size: var(--fs-ms);
	font-weight: 300;
	color: var(--grey);
	line-height: 1.75;
}

/* ==========================================================================
   20. Villa page — unified date picker (.hv-picker)

   Calendar commit B. One overlay holding two chips, the datepick calendar
   itself, and an explicit Apply — see the block comment in main.js for why
   it wraps MotoPress's two existing instances rather than replacing them.

   The calendar inside .hv-picker__slot is MotoPress's OWN popup, moved here
   by datepick's popupContainer option. It arrives carrying inline
   `position:absolute; left:…; top:…` written by the library at render time
   (jquery.datepick.js:1334), which is why the overrides below need
   !important — a stylesheet rule alone loses to an inline style. Section 16
   already styles the popup's internals and still applies; only its
   positioning is neutralised here.
   ========================================================================== */

.hv-picker {
	position: fixed;
	inset: 0;
	z-index: 1000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
	background: rgba(11, 11, 11, .58);
	overflow-y: auto;
}

.hv-picker[hidden] {
	display: none;
}

body.hv-picker-open {
	overflow: hidden;
}

.hv-picker__panel {
	width: 100%;
	max-width: 480px;
	background: var(--paper);
	border: 1px solid var(--hair);
	padding: clamp(18px, 3vw, 26px);
	margin: auto;
}

/* Two chips sharing one bordered box, hairline between — the same shape as
   the bedroom stepper, so the panel and the overlay read as one component. */
.hv-picker__chips {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 1px;
	background: var(--hair);
	border: 1px solid var(--hair);
}

.hv-picker__chip {
	background: var(--paper);
	border: 0;
	padding: 12px 14px;
	text-align: left;
	cursor: pointer;
	font: inherit;
	color: inherit;
}

.hv-picker__chip.is-active {
	outline: 1px solid var(--ink);
	outline-offset: -1px;
}

/* The chip is the click target datepick's external-click check inspects,
   so nothing inside it may swallow the event and arrive as e.target. */
.hv-picker__chip > * {
	pointer-events: none;
}

.hv-picker__chip-k {
	display: block;
	font-size: var(--fs-3xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
}

.hv-picker__chip-v {
	display: block;
	margin-top: 5px;
	font-size: var(--fs-body);
	font-weight: 300;
}

.hv-picker__chip-v.is-empty {
	color: var(--disabled);
}

.hv-picker__slot {
	margin-top: 16px;
	min-height: 274px;
}

.hv-picker__slot .datepick-popup {
	position: static !important;
	left: auto !important;
	top: auto !important;
	/* datepick writes an explicit pixel width on the popup after measuring
	   its months, so the calendar is its own natural size. Centre it in the
	   slot rather than stretching it — stretching pulls the month grids
	   apart from the header rules drawn above them.
	   Centring is done with flex rather than `margin: 0 auto`, because the
	   bordered box the guest actually sees is .datepick INSIDE this wrapper,
	   and datepick.css floats its months; auto margins on the wrapper leave
	   the calendar pinned to the left, which is what it did. */
	display: flex;
	justify-content: center;
	width: 100% !important;
	max-width: 100%;
	box-shadow: none;
	border: 0;
}

/* datepick's own footer carries Clear and Close. Both are already in the
   overlay's footer, in its own type, doing the same job — and "Clear"
   appearing twice, six pixels apart, reads as a bug. Hidden rather than
   turned off in the instance's options, so nothing about how MotoPress
   configured the picker is changed; on any other surface (there are none
   today) the row still renders. */
.hv-picker__slot .datepick-popup .datepick-ctrl {
	display: none;
}

.hv-picker__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 14px;
	margin-top: 16px;
	padding-top: 16px;
	border-top: 1px solid var(--hair);
}

.hv-picker__clear {
	background: none;
	border: 0;
	padding: 0;
	cursor: pointer;
	font-size: var(--fs-note);
	font-weight: 300;
	color: var(--grey);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.hv-picker__apply {
	background: var(--ink);
	color: var(--paper);
	border: 0;
	padding: 14px 30px;
	cursor: pointer;
	font-size: var(--fs-cta);
	font-weight: 600;
	letter-spacing: var(--ls-cta);
	text-transform: uppercase;
}

.hv-picker__apply[disabled] {
	background: var(--disabled);
	cursor: not-allowed;
}

/* Phones get one month (main.js switches monthsToShow at the same
   breakpoint), and the sheet sits at the top of the screen rather than
   centred: with a single month there is far less calendar than screen, and
   a centred panel leaves the chips floating in the middle of nowhere. */
@media (max-width: 560px) {
	.hv-picker {
		padding: 0;
		align-items: flex-start;
	}

	.hv-picker__panel {
		max-width: none;
		border: 0;
		border-bottom: 1px solid var(--hair);
		margin: 0;
	}

	/* One month, so let it have the full width of the sheet — a 230px
	   calendar adrift in a 390px panel reads as broken, not as restraint.
	   The width has to go on .datepick (the bordered box) and on the month
	   table; the popup wrapper alone leaves the calendar its measured size. */
	.hv-picker__slot .datepick-popup .datepick {
		width: 100% !important;
	}

	.hv-picker__slot .datepick-popup .datepick-month {
		float: none !important;
		width: 100% !important;
	}

	/* Belt and braces behind the monthsToShow switch in main.js. If that
	   ever fails to apply, a calendar wider than the phone scrolls inside
	   its own box rather than pushing the page sideways.

	   The reserved height goes with it: it exists to stop the panel jumping
	   as the calendar swaps between fields, and one month is shorter than
	   the two-month reserve, so keeping it just adds dead space above the
	   footer on the screen with the least room to spare. */
	.hv-picker__slot {
		overflow-x: auto;
		min-height: 0;
	}
}

/* ==========================================================================
   21. Villa page — the photographs (.hv-villa-gallery)

   One gallery per VILLA, from hv_gallery term meta, shared by every bedroom
   tier. It sits between the description and the amenities: the guest reads
   what the house is, then looks at it.

   THIS IS NOT THE HERO AND CAN NEVER BECOME IT. The hero is the featured
   image, unconditionally — see husnavillas_render_gallery_before_content().
   ========================================================================== */

/*
 * NO TOP PADDING, REAL BOTTOM PADDING. Both measured rather than matched to a
 * neighbour by eye.
 *
 * .hv-description above already ends with 84px of its own padding, so a 56px
 * top here put 140px of empty white between the last line of the description
 * and the eyebrow — a gap big enough to read as the end of the page.
 *
 * The bottom is the opposite problem and worse: .hv-amenities begins its
 * limestone band at the gallery's very last pixel (measured, 1423 + 138 = 1561,
 * and .hv-amenities starts at 1561), so with no padding the bottom row of
 * photographs butts straight into the colour change and reads as clipped.
 */
.hv-villa-gallery {
	padding-bottom: clamp(40px, 5vw, 64px);
}

/*
 * FOUR PER ROW, AND THE LAST ROW IS RAGGED ON PURPOSE.
 *
 * repeat(4, 1fr) — deliberately NOT auto-fit or auto-fill, and with no
 * justify-content. Ten images give 4 / 4 / 2 and the final two sit at the
 * left with the gap left open beside them, which is what a plain grid does
 * unaided. auto-fit would stretch those two across the full width and
 * centring would float them into the middle; either makes the last row read
 * as a different design rather than the end of the same one.
 */
.hv-villa-gallery__grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 10px;
	list-style: none;
	margin: clamp(24px, 3vw, 34px) 0 0;
	padding: 0;
}

/*
 * aspect-ratio on the CELL, object-fit: cover on the image. Every cell is the
 * same size whatever shape the photograph is, which is the only way a grid of
 * mixed portrait and landscape uploads reads as a grid at all.
 */
.hv-villa-gallery__cell {
	margin: 0;
	aspect-ratio: 3 / 2;
}

.hv-villa-gallery__trigger {
	display: block;
	width: 100%;
	height: 100%;
	padding: 0;
	border: 0;
	background: var(--hair);
	cursor: zoom-in;
	overflow: hidden;
}

.hv-villa-gallery__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform .5s ease;
}

.hv-villa-gallery__trigger:hover .hv-villa-gallery__img,
.hv-villa-gallery__trigger:focus-visible .hv-villa-gallery__img {
	transform: scale(1.04);
}

.hv-villa-gallery__trigger:focus-visible {
	outline: 2px solid var(--gold-deep);
	outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
	.hv-villa-gallery__img {
		transition: none;
	}

	.hv-villa-gallery__trigger:hover .hv-villa-gallery__img,
	.hv-villa-gallery__trigger:focus-visible .hv-villa-gallery__img {
		transform: none;
	}
}

/*
 * 4 -> 3 -> 2, and NEVER 1. A single-column gallery is not a gallery, it is a
 * stack of hero images, and on a phone it would be several screens of
 * scrolling to pass four photographs.
 *
 * .98 on both bounds because max-width fires AT its value: 900 and 600 are
 * the widths that still hold the wider count.
 */
@media (max-width: 899.98px) {
	.hv-villa-gallery__grid {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (max-width: 599.98px) {
	.hv-villa-gallery__grid {
		grid-template-columns: repeat(2, 1fr);
		gap: 6px;
	}
}

/* ==========================================================================
   22. Villa page — the lightbox (.hv-lightbox)

   Built on <dialog>, which this stylesheet already dresses twice (the
   enquiry modal, the unified date picker). No third-party lightbox: the
   dependency would be larger than the feature, and showModal()'s top layer
   already gives the two things a lightbox is usually imported for — a real
   backdrop above everything, and inertness of the page behind it.
   ========================================================================== */

.hv-lightbox {
	width: 100vw;
	max-width: 100vw;
	height: 100dvh;
	max-height: 100dvh;
	padding: 0;
	border: 0;
	background: rgba(11, 11, 11, .94);
	color: var(--paper);
	overflow: hidden;
}

.hv-lightbox::backdrop {
	background: rgba(11, 11, 11, .94);
}

.hv-lightbox__stage {
	display: grid;
	place-items: center;
	width: 100%;
	height: 100%;
	padding: 56px 64px;
}

/*
 * The image is bounded by the STAGE, not by its own intrinsic size, so a
 * portrait photograph cannot run off the top and bottom of a short window.
 */
.hv-lightbox__img {
	display: block;
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
}

.hv-lightbox__nav,
.hv-lightbox__close {
	position: absolute;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* 44px is the floor for a touch target, and it is a floor rather than a
	   size: these sit over a photograph and have to be findable. */
	min-width: 48px;
	min-height: 48px;
	padding: 0;
	border: 1px solid rgba(255, 255, 255, .35);
	background: rgba(11, 11, 11, .55);
	color: var(--paper);
	font-size: 20px;
	line-height: 1;
	cursor: pointer;
}

.hv-lightbox__nav:hover,
.hv-lightbox__close:hover,
.hv-lightbox__nav:focus-visible,
.hv-lightbox__close:focus-visible {
	background: var(--paper);
	color: var(--ink);
	border-color: var(--paper);
}

.hv-lightbox__nav--prev {
	left: 12px;
	top: 50%;
	transform: translateY(-50%);
}

.hv-lightbox__nav--next {
	right: 12px;
	top: 50%;
	transform: translateY(-50%);
}

.hv-lightbox__close {
	right: 12px;
	top: 12px;
}

.hv-lightbox__bar {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	display: flex;
	align-items: baseline;
	justify-content: center;
	gap: 14px;
	padding: 14px 16px calc(14px + env(safe-area-inset-bottom, 0px));
	background: linear-gradient(180deg, rgba(11, 11, 11, 0) 0%, rgba(11, 11, 11, .75) 100%);
	pointer-events: none;
}

.hv-lightbox__count {
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--gold);
	font-variant-numeric: tabular-nums;
}

.hv-lightbox__caption {
	font-size: 13px;
	line-height: 1.5;
	color: var(--dim);
	max-width: 46ch;
}

@media (max-width: 760px) {
	.hv-lightbox__stage {
		padding: 56px 12px 72px;
	}

	/* The side arrows move to the bottom bar on a phone: at the vertical
	   centre they sit on top of the photograph and, at 48px each, take a
	   sixth of the screen width between them. */
	.hv-lightbox__nav--prev,
	.hv-lightbox__nav--next {
		top: auto;
		bottom: calc(10px + env(safe-area-inset-bottom, 0px));
		transform: none;
		z-index: 2;
	}
}

/* ==========================================================================
   22b. Villa page — the area (.hv-area)

   Place names and distances, between Spaces and What to know. It answers "what
   do we do while we are here", which is a different question from the two the
   What to know boxes answer, and is asked at a different point.

   THE ROWS ARE THE HOME PAGE'S .hv-place__list READING, WITH THE EMPHASIS
   INVERTED. There the left side is a LABEL — "Community", "Beach" — so it takes
   the small tracked uppercase treatment and the value carries the content. Here
   the left side is a PLACE NAME, which IS the content, so it is body type and
   the distance takes the tracked treatment instead. All-caps stays on the
   utility value and off the proper noun.

   TWO COLUMNS, NOT ONE, via the shared .hv-cards--two. In a half-width column
   on the home page, space-between leaves a name and its value a readable
   distance apart; in this band the content column runs to --wrap-max, and one
   column of six would strand each distance the better part of 1200px from its
   name. Reusing .hv-cards means the collapse to a single column at 620px is
   already handled and no new breakpoint is introduced here.

   Only the row gap is overridden: the hairlines should read as one continuous
   list down each column rather than as six separate rules with air between
   them. This block sits after .hv-cards in the file, which is what makes the
   override land.

   Paper ground. Ink belongs to Spaces immediately above and two dark bands
   running together would read as one; bone belongs to the amenities band.
   ========================================================================== */

.hv-area {
	background: var(--paper);
	padding: clamp(40px, 5vw, 64px) 0 clamp(48px, 6vw, 72px);
	border-top: 1px solid var(--hair);
}

.hv-area__list {
	gap: 0 clamp(28px, 4vw, 64px);
	margin-top: clamp(18px, 2.4vw, 28px);
}

.hv-area__row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	padding: 13px 0;
	border-bottom: 1px solid var(--hair);
}

.hv-area__name {
	font-size: 14.5px;
	color: var(--ink);
}

/* flex: none so a long name wraps rather than squeezing the distance, which is
   the shorter of the two and the one that becomes unreadable first. */
.hv-area__dist {
	flex: none;
	font-size: 10px;
	font-weight: 600;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--grey);
}

/* ==========================================================================
   23. Villa page — what to know (.hv-villa-info)

   The same four answers on every villa page, from four shared options. It
   closes the left column: reference rather than persuasion, and the things a
   guest still wants to know once they have decided they want the house.
   ========================================================================== */

.hv-villa-info {
	padding: clamp(40px, 5vw, 64px) 0 clamp(48px, 6vw, 72px);
	border-top: 1px solid var(--hair);
}

/*
 * A <dl>, because that is what this is: four terms and their definitions.
 * The rows are .hv-villa-info__item wrappers so a dt/dd pair can be one flex
 * item — a bare dt+dd cannot be, and splitting them is how these blocks end up
 * with a heading in one column and its answer in another.
 *
 * A ROW OF BOXES, NOT STACKED PROSE. flex-wrap rather than a fixed grid: four
 * equal boxes that stay equal and wrap together when the column is too narrow
 * for them, instead of a grid that would hold two columns at every width.
 *
 * `flex: 1 1 200px` is what makes them equal AND wrapping — the basis decides
 * how many survive on a line, and 1 1 lets the survivors share the leftover
 * evenly so a row of three is not three different widths.
 */
.hv-villa-info__list {
	--hv-info-gap: clamp(16px, 2vw, 24px);

	display: flex;
	flex-wrap: wrap;
	gap: var(--hv-info-gap);
	margin: clamp(24px, 3vw, 34px) 0 0;
}

/*
 * THE BASIS IS AN EXACT FRACTION, NOT A PIXEL GUESS, SO A LONE BOX CAN NEVER
 * ORPHAN AND STRETCH.
 *
 * `flex: 1 1 200px` was tried first and measured 3 boxes of 260px with the
 * fourth wrapped onto its own line at 828px wide — four boxes need
 * 4x200 + 3x20 = 860px and the content column at 1366 is 851. Nine pixels
 * short, and the failure mode is not "slightly narrower boxes", it is one box
 * alone on a row at four times the width of its siblings.
 *
 * Sizing the basis as a fraction of the row removes the arithmetic entirely:
 * four always fit, or the breakpoint below has already dropped them to two.
 */
.hv-villa-info__item {
	flex: 1 1 calc((100% - 3 * var(--hv-info-gap)) / 4);
	min-width: 0;
	border: 1px solid var(--hair);
	padding: 16px 18px 18px;
}

/*
 * Two up, then one. Between 1024 and 1366 the booking panel takes 430px out of
 * the viewport, so the content column is narrow enough that a quarter of it is
 * not a box any more. .98 on both because max-width fires AT its value and
 * 1366 is a width that must still get the four-across row.
 */
@media (max-width: 1365.98px) {
	.hv-villa-info__item {
		flex-basis: calc((100% - var(--hv-info-gap)) / 2);
	}
}

@media (max-width: 599.98px) {
	.hv-villa-info__item {
		flex-basis: 100%;
	}
}

.hv-villa-info__head {
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--gold-deep);
}

.hv-villa-info__body {
	margin: 10px 0 0;
	font-size: var(--fs-body);
	font-weight: 300;
	line-height: 1.65;
	color: var(--grey);
	max-width: 46ch;
}

/*
 * The body measure drops with the box. 46ch was written for a full-width
 * column; inside a quarter-width box it is never the binding constraint, and
 * leaving it would do nothing except confuse the next reader.
 */
.hv-villa-info__body {
	max-width: none;
}

/* ==========================================================================
   24. Villa page — the mobile booking bar (.hv-mbar) and its modal

   THE BOUNDARY, STATED: everything in this section lives inside
   `@media (max-width: 1023.98px)`, the exact complement of the villa grid's
   `@media (min-width: 1024px)` in section 17. 1024 itself falls on the DESKTOP
   side, where nothing here exists. The `.98` is not decoration — a measured
   bound means nothing until you say which side the boundary value falls on.

   The bar exists only where the booking panel is off screen. Above 1024 the
   panel is a sticky card already beside the content, and a bar there would be a
   second copy of an action already visible — which is exactly why the previous
   bottom bar was removed (see husnavillas_render_mobile_bar's note).

   --hv-mbar-h IS THE ONE NUMBER. The bar reads it for its own min-height and
   the footer reads it for the padding that keeps it clear of the bar. main.js
   overwrites it from a ResizeObserver, so a bar that grows a line cannot leave
   the footer behind it. The value here is the no-JS fallback and is deliberately
   generous rather than exact: too much padding under the footer is invisible,
   too little hides content permanently.
   ========================================================================== */

:root {
	--hv-mbar-h: 132px;
}

/* Hidden by default AND at every width above the breakpoint. `hidden` is on the
   element in the markup so it cannot flash before this file loads; the media
   query below is what actually reveals it. */
.hv-mbar[hidden] {
	display: none;
}

@media (max-width: 1023.98px) {
	/*
	 * THE PANEL LEAVES THE PAGE BELOW THE BREAKPOINT. It measured 390x480 at
	 * y2421 — in the flow, far below the fold, a second booking surface behind
	 * the one a guest is actually using. The bar is the booking surface here
	 * and the controls live behind "Change details".
	 *
	 * SCOPED TO ITS HOME PARENT, NOT TO THE CLASS, and the difference is the
	 * whole rule. The entire .hv-price-panel element travels into the bar's
	 * modal on open (see main.js — the .wrap alone was tried and lost every
	 * `.hv-price-panel <thing>` selector). `.hv-price-panel { display: none }`
	 * would therefore hide it inside the modal too, which is the one place it
	 * has to be visible. As a child of .type-mphb_room_type it is on the page;
	 * as a child of .hv-mbar-modal__slot it is not, and this stops matching.
	 */
	.type-mphb_room_type > .hv-price-panel {
		display: none;
	}

	.hv-mbar {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 90;
		background: var(--paper);
		border-top: 1px solid var(--gold);
		box-shadow: 0 -1px 0 var(--hair);
	}

	/* Revealed here and nowhere else, so the attribute stays in the markup as
	   the above-breakpoint state rather than being toggled in JS. */
	.hv-mbar[hidden] {
		display: block;
	}

	/* The modal carries the only action the bar offers, so the bar goes while
	   it is up — the dialog's backdrop is translucent and the two CTAs land
	   within three pixels of each other. visibility, not display: the footer's
	   padding reads the bar's measured height and must not move underneath the
	   guest mid-interaction. */
	body.hv-mbar-modal-open .hv-mbar {
		visibility: hidden;
	}

	/*
	 * 9px and an 8px gap, not 12 and 10. Measured: the bar came out 174px of an
	 * 844px screen — 20.6%, just past the fifth where it stops reading as a bar
	 * and starts reading as a panel stuck to the bottom. This takes it to 166px
	 * (19.7%).
	 *
	 * The 8px came out of padding and the gap and NOTHING ELSE. The button
	 * keeps its 48px, which is the touch floor and is why the diet stops where
	 * it does, and no type size moved — a bar that fits because its figures are
	 * too small to read has not been fixed.
	 */
	.hv-mbar__in {
		padding: 9px var(--wrap-pad) calc(9px + env(safe-area-inset-bottom, 0px));
		display: flex;
		flex-direction: column;
		gap: 8px;
	}

	/*
	 * THE BUTTON IS FULL WIDTH UNDERNEATH, NOT BESIDE THE TEXT. A tall button
	 * next to three short lines leaves the right half of the bar empty and the
	 * whole thing reads as a block rather than a bar.
	 */
	.hv-mbar__cta {
		display: block;
		width: 100%;
		text-align: center;
		min-height: 48px;
		line-height: 1.35;
		padding: 13px 16px;
	}

	.hv-mbar__read {
		min-width: 0;
	}

	/*
	 * THE BAR PRICES IN THE PANEL'S OWN VOICE, and every value below is a
	 * token taken from it. The figure is `.hv-quote__nightly` one step down
	 * the site's scale and the unit beside it is that block's treatment
	 * unchanged.
	 *
	 * What was here was 12.5px, 19px and 13.5px at weight 600 — three sizes
	 * that appear nowhere else in this file, and the only semibold figure on a
	 * page that prices in Montserrat 200. It read as a plugin default sitting
	 * under a panel it belonged to.
	 */
	.hv-mbar__line {
		margin: 0;
		font-size: var(--fs-ms);
		font-weight: 300;
		color: var(--grey);
		line-height: 1.45;
	}

	/* One baseline row, figure at one end and stay at the other. Two rows is
	   what the bar could not afford: putting them on one line is what pays for
	   a display-scale figure without the bar getting taller. */
	.hv-mbar__line--total {
		display: flex;
		align-items: baseline;
		justify-content: space-between;
		gap: 10px;
		font-size: var(--fs-note);
	}

	.hv-mbar__fig {
		display: flex;
		align-items: baseline;
		gap: 6px;
		min-width: 0;
	}

	.hv-mbar__fig b {
		font-family: var(--display);
		font-size: var(--fs-2xl);
		font-weight: var(--disp-w);
		letter-spacing: var(--disp-ls-lg);
		line-height: 1;
		color: var(--ink);
	}

	/* "total" is the UNIT, not part of the figure — the same rule the panel
	   applies to "per night", and the reason it is not styled away entirely:
	   a bare number beside a date range does not say what it is the price of. */
	.hv-mbar__unit {
		font-family: var(--ui);
		font-size: var(--fs-2xs);
		font-weight: 600;
		letter-spacing: var(--ls-label);
		text-transform: uppercase;
		color: var(--grey);
		white-space: nowrap;
	}

	/* Held on one line and never allowed to squeeze the figure: if a longer
	   date format or a larger party ever stops both fitting, the overflow is
	   what the check measures. */
	.hv-mbar__meta {
		flex: none;
		text-align: right;
		white-space: nowrap;
	}

	/* The no-dates line and the blocked lines both use this: it is the one
	   place the bar explains itself rather than pricing something. */
	.hv-mbar__line--lead {
		font-size: var(--fs-ms);
		color: var(--ink);
	}

	/*
	 * A QUIET LINK, NOT A SECOND BUTTON. It sits under the details line and
	 * must not compete with the primary — so no border, no fill, and the same
	 * size as the lines above it, separated only by the underline.
	 *
	 * In the blocked states it is not rendered at all: there the primary IS
	 * "Change details", and saying it twice in two weights reads as two
	 * different actions.
	 */
	.hv-mbar__change {
		align-self: flex-start;
		background: none;
		border: 0;
		padding: 2px 0 0;
		font: inherit;
		font-size: var(--fs-note);
		color: var(--ink);
		text-decoration: underline;
		text-underline-offset: 3px;
		cursor: pointer;
	}

	/*
	 * The footer clears the bar. One number, shared with the bar's own height —
	 * see the section note.
	 *
	 * SCOPED TO THE TEMPLATE THAT HAS THE BAR. This rule was unscoped, while the
	 * bar it clears renders only on is_singular('mphb_room_type')
	 * (husnavillas_render_mobile_bar), so every other page on the site carried
	 * 156px of empty space under its footer on a phone for a bar that was not
	 * there. Measured at 390: home 156px, Terms 156px, villa 118px — and the 156
	 * is the CSS fallback rather than a measured height, because nothing on those
	 * pages ever ran the ResizeObserver that would have shrunk it.
	 *
	 * The rule and the bar now have the same condition, which is the only way
	 * they cannot drift apart again.
	 */
	.single-mphb_room_type .site-footer {
		padding-bottom: calc(var(--hv-mbar-h) + 24px);
	}

	/* ---- the modal ---- */

	.hv-mbar-modal {
		width: 100%;
		max-width: none;
		max-height: 92dvh;
		margin: auto auto 0;
		padding: 0;
		border: 0;
		background: transparent;
	}

	.hv-mbar-modal::backdrop {
		background: rgba(11, 11, 11, .58);
	}

	.hv-mbar-modal__panel {
		background: var(--bone);
		border-top: 1px solid var(--gold);
		max-height: 92dvh;
		overflow-y: auto;
		overscroll-behavior: contain;
		padding-bottom: env(safe-area-inset-bottom, 0px);
	}

	.hv-mbar-modal__head {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 16px;
		padding: 14px var(--wrap-pad) 0;
	}

	.hv-mbar-modal__close-form {
		margin: 0;
	}

	.hv-mbar-modal__close {
		background: none;
		border: 0;
		font-size: 26px;
		line-height: 1;
		padding: 4px 8px;
		min-width: 44px;
		min-height: 44px;
		color: var(--ink);
		cursor: pointer;
	}

	/*
	 * THE PANEL'S OWN .wrap IS MOVED IN HERE, so it arrives carrying the
	 * padding it had in the page. Zeroed at the top only — the slot's own head
	 * already provides that space, and doubling it pushes the first control
	 * below the fold on a short phone.
	 */
	.hv-mbar-modal__slot > .hv-price-panel {
		border-top: 0;
		padding-top: 4px;
	}

	/*
	 * BOTH ENQUIRY OPENERS ARE SUPPRESSED WHILE THE PANEL IS IN THE MODAL, and
	 * this is a descendant selector on purpose: the node moves twice, and a
	 * class toggled in JS has to stay in sync with every move. This follows the
	 * node for free.
	 *
	 * There are TWO of them, which is not obvious from the markup:
	 *
	 *   .hv-panel-ask   #hv-btn-enquire, the panel's permanent button;
	 *   .hv-quote__ask  the "Ask us" link main.js writes INTO THE QUOTE when a
	 *                   tier is free but has no rate for the chosen dates.
	 *
	 * Either one would open the enquiry <dialog> over the top of this one, and
	 * two open dialogs is not a state this site should ever be in.
	 *
	 * MotoPress's own primary goes with them. This modal offers exactly one
	 * action — Done — and the bar behind it already carries Request dates, so
	 * a second primary in here is a choice between two spellings of the same
	 * thing rather than a choice between two outcomes.
	 *
	 * The BUTTONS are named, not their container. `.mphb-confirm-reservation`
	 * is an <input value="Request these dates"> inside
	 * .mphb-reserve-room-section, and that section also holds "<villa> is
	 * available for selected dates" — the one line in the box worth keeping,
	 * and the confirmation the guest needs before pressing Done.
	 *
	 * `.mphb-reserve-btn` is listed even though MotoPress currently hides it
	 * with its own .mphb-hide: that class is a live target of the bar's CTA,
	 * and whether it is visible is the plugin's decision to change, not ours.
	 *
	 * display:none rather than visibility:hidden so they leave the tab order as
	 * well as the screen.
	 *
	 * .hv-panel-ask used to be listed here and is not any more: the panel's
	 * "Ask a question" button has been removed from the page, so the selector
	 * matched nothing. A rule kept for an element that no longer exists reads
	 * as a live suppression and hides the next thing that takes the class.
	 * The enquiry route is now .hv-ask under "Before you ask.", outside the
	 * panel and therefore never inside this modal.
	 */
	.hv-mbar-modal__slot .hv-quote__ask,
	.hv-mbar-modal__slot .mphb-confirm-reservation,
	.hv-mbar-modal__slot .mphb-reserve-btn {
		display: none;
	}

	/*
	 * ONE WAY OUT, AND IT HAS TO BE REACHABLE WITHOUT SCROLLING.
	 *
	 * Every change in here is already applied to the panel — the modal holds
	 * the real form, not a copy of it — so there is nothing to accept and
	 * nothing to discard. Done, the X and the backdrop are therefore the same
	 * action, and Done exists only because it is the one a thumb finds.
	 *
	 * Sticky rather than sitting at the end of the sheet: the panel is taller
	 * than the viewport, and a button that appears only after scrolling past
	 * every control is a button the X has already replaced.
	 */
	.hv-mbar-modal__foot {
		position: sticky;
		bottom: 0;
		background: var(--bone);
		border-top: 1px solid var(--hair);
		padding: 12px var(--wrap-pad) calc(12px + env(safe-area-inset-bottom, 0px));
	}

	.hv-mbar-modal__done {
		display: block;
		width: 100%;
		text-align: center;
		min-height: 48px;
		line-height: 1.35;
		padding: 13px 16px;
	}

	/*
	 * .hv-picker IS MOVED IN HERE TOO, and this is the reason it has to be.
	 *
	 * It is a <div> appended to document.body at z-index 1000 with a
	 * role="dialog" panel inside it — but it is NOT a real <dialog>, so it gets
	 * no top layer. A dialog opened with showModal() IS in the top layer, which
	 * sits above the whole z-index stack, so the picker would open behind this
	 * modal: correct size, correct position, reported visible by
	 * getComputedStyle, and completely unreachable.
	 *
	 * Raising its z-index cannot fix that. Only moving the node into this
	 * dialog's subtree can. See CLAUDE.md, "The top layer beats every z-index".
	 */
	.hv-mbar-modal .hv-picker {
		z-index: 1;
	}
}

/* ==========================================================================
   25. The booking page (.hv-checkout)

   MotoPress's checkout body is nine actions on one hook, not a template, so
   the page is reshaped in functions.php and dressed here. Everything is
   scoped to body.hv-checkout: the same plugin classes appear on the villa
   panel, and a rule written loosely here would repaint that.

   The two columns are a grid ON MOTOPRESS'S OWN FORM. One form, one submit,
   no second copy of anything — and `order` is what puts the summary first on
   a phone rather than a duplicate block.
   ========================================================================== */

/*
 * The page's own heading, replacing the WordPress page title. Section-heading
 * scale, not hero scale: --fs-disp-lg belongs to a villa name over a
 * photograph, and there is no photograph here.
 *
 * IT RENDERS INSIDE THE FORM, so the summary can be ordered above it on a phone
 * — see husnavillas_reshape_checkout(). That makes it a grid item here, and the
 * two values below exist to leave the desktop page exactly where it was.
 *
 * The stack used to be   header -> this element's padding -> heading -> the
 * form's padding -> content. It is now   header -> the form's padding ->
 * heading -> this element's margin -> content. So the padding above the heading
 * has to shed what the form's padding now supplies, and the space below it has
 * to be re-added as a margin.
 *
 * BOTH ARE calc() ON THE SAME CLAMPS, not the two numbers that were measured.
 * A hardcoded 16px and 56px would be right at 1920 and wrong everywhere
 * between, and nobody looks at 1500.
 *
 *   1920:  80 + 56    + 16    = 152    (was 80 + 72   = 152)
 *   1366:  80 + 54.64 + 13.66 = 148.3  (was 80 + 68.3 = 148.3)
 *
 * No horizontal padding: the form already insets by --wrap-pad, and adding it
 * again here would indent the heading past the fields it sits above.
 */
.hv-co-head {
	grid-column: 1 / -1;
	grid-row: 1;
	padding: calc(clamp(36px, 5vw, 72px) - clamp(28px, 4vw, 56px)) 0 0;
	margin: 0 0 clamp(28px, 4vw, 56px);
}

.hv-co-head__title {
	font-size: var(--fs-disp-sm);
	line-height: 1.07;
	color: var(--ink);
	margin: 14px 0 0;
}

/*
 * ROW-GAP MUST STAY 0 WHILE THE SUMMARY SPANS A FIXED ROW COUNT. A gap between
 * two EMPTY tracks is still a gap, and .hv-co-summary spans twenty rows to give
 * its sticky travel a tall enough area (see below). Seventeen of those tracks
 * hold nothing and are 0px high — but the nineteen gaps between them are not.
 *
 * Measured at 1366 before this line existed: rows 561 + 321 + 96 = 978px of
 * content, plus 19 gaps at 68.3px (5vw) = 1298px of nothing, plus 151px of
 * padding — a 2427px form whose last visible content ended at y1303 and whose
 * footer began at y2560. Roughly 1250px of empty page, and the arithmetic
 * closes to the measured pixel.
 *
 * `gap` is therefore split: columns keep the separation, rows get none. The
 * left column's vertical rhythm is carried by margins on its own items
 * (.mphb-checkout-terms-wrapper and .hv-co-bar both set margin-top), which is
 * why it survives the row gap going to zero.
 */
.hv-checkout .mphb_sc_checkout-form {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	column-gap: clamp(28px, 4vw, 48px);
	row-gap: 0;
	align-items: start;
	max-width: var(--wrap-max);
	margin: 0 auto;
	padding: clamp(28px, 4vw, 56px) var(--wrap-pad) clamp(56px, 8vw, 96px);
}

@media (min-width: 1024px) {
	.hv-checkout .mphb_sc_checkout-form {
		grid-template-columns: minmax(0, 1fr) 380px;
		column-gap: clamp(40px, 5vw, 72px);
		row-gap: 0;
	}
}

/*
 * SUPPRESSED, NOT REMOVED — every one of these carries a field that has to
 * keep posting, and the section they sit in is what builds the booking.
 *
 * CLIPPED RATHER THAN display:none, and the difference is not cosmetic. The
 * guests control is required="required". A required control that is
 * display:none and empty makes Chrome refuse to submit with "An invalid form
 * control is not focusable" — no message a guest can see, no way forward, and
 * nothing on screen to explain it. A clipped control is still rendered, so a
 * validation message appears and the failure is visible instead of silent.
 */
.hv-checkout #mphb-booking-details {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* MotoPress's own submit. The visible control is ours and opens the payment
   modal; this stays so the card form has something to trigger. */
.hv-checkout .mphb_sc_checkout-submit-wrapper {
	display: none;
}

/* ---- the left column ---- */

.hv-checkout .hv-co-fields {
	grid-column: 1;
}

.hv-co-group__title {
	font-family: var(--ui);
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	margin: 0 0 16px;
}

.hv-co-group__title + * {
	margin-top: 0;
}

.hv-co-fields .hv-co-group__title:not(:first-child) {
	margin-top: clamp(32px, 4vw, 44px);
}

.hv-co-grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 16px;
}

.hv-co-field {
	margin: 0;
	min-width: 0;
}

.hv-co-field--wide {
	grid-column: 1 / -1;
}

.hv-co-field label {
	display: block;
	font-family: var(--ui);
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	margin-bottom: 8px;
}

/* The villa panel's field treatment, so the two read as one site: a hairline
   box on paper, gold on focus, and a 48px floor for a thumb. */
.hv-co-field input[type="text"],
.hv-co-field input[type="email"],
.hv-co-field textarea {
	width: 100%;
	min-height: 48px;
	padding: 13px 14px;
	font-family: var(--ui);
	font-size: var(--fs-ms);
	color: var(--ink);
	background: var(--paper);
	border: 1px solid var(--dim);
	border-radius: 0;
	appearance: none;
}

.hv-co-field textarea {
	min-height: 96px;
	resize: vertical;
}

.hv-co-field input:focus,
.hv-co-field textarea:focus {
	outline: none;
	border-color: var(--gold);
}

.hv-co-field small {
	display: block;
	margin-top: 8px;
	font-size: var(--fs-note);
	color: var(--grey);
}

/* ---- the payment block ------------------------------------------------- */

/*
 * MotoPress renders this section itself and it arrived on the page wearing
 * nothing but the browser's h3 defaults — 17.55px at weight 700, sentence
 * case, no tracking — beside labels that are 9px uppercase at .26em. It read
 * as a different site's form dropped into this one.
 *
 * Everything below matches .hv-co-group__title and .hv-co-field rather than
 * inventing a treatment, so the payment block and the guest block are visibly
 * the same form.
 */
.hv-checkout #mphb-billing-details {
	margin-top: clamp(32px, 4vw, 44px);
}

.hv-checkout .mphb-gateway-chooser-title {
	font-family: var(--ui);
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
	margin: 0 0 16px;
}

/*
 * "Payment Method" followed by "Card" is two headings for one choice. The
 * second is hidden ONLY while there is a single gateway, and the selector is
 * what makes that conditional rather than a decision: MotoPress renders the
 * input as `type="hidden"` when it is the only method and as a radio when it
 * is not, so adding a second gateway brings the label back by itself. No rule
 * to remember, and nothing here that can hide a real choice.
 */
.hv-checkout .mphb-billing-fields-wrapper input[type="hidden"] + .mphb-gateway-title {
	display: none;
}

.hv-checkout .mphb-gateway-description {
	font-size: var(--fs-note);
	line-height: 1.7;
	color: var(--grey);
	margin: 14px 0 0;
}

.hv-checkout .mphb-billing-fields {
	border: 0;
	padding: 0;
	margin: 0;
}

/*
 * SQUARE'S OWN 6px RADIUS AND GREY BORDER, TURNED OFF.
 *
 * !important IS REQUIRED HERE AND IS NOT LAZINESS. The rounding comes from
 * Square's own stylesheet at web.squarecdn.com/<version>/card-wrapper.css,
 * which is CROSS-ORIGIN: `sheet.cssRules` throws on it, so its rules cannot be
 * read and cannot be out-specified by inspection. A plain
 * `.hv-checkout .sq-card-iframe-container` — specificity 0,2,0, and confirmed
 * matching — lost to it. Against a third-party sheet on a CDN that we cannot
 * edit and cannot read, raising priority is the only lever left in CSS.
 *
 * The other lever is the SDK's own style object, passed at card.attach() in
 * husna-square. That is the better place for anything INSIDE the frame — the
 * input's own font and colour are cross-origin and unreachable from here — so
 * a change to the card text goes there, not to this rule.
 *
 * Check this after any Square SDK version bump: the class names above are
 * theirs, and a rename would leave these rules matching nothing while the
 * corners quietly came back.
 */
.hv-checkout .sq-card-iframe-container,
.hv-checkout iframe.sq-card-component,
.hv-checkout .sq-card-wrapper,
.hv-checkout .husna-square-card,
.hv-checkout .husna-square-card * {
	border-radius: 0 !important;
}

/* The field box itself, on the same hairline treatment as every other input. */
.hv-checkout .sq-card-iframe-container {
	border: 1px solid var(--dim) !important;
	background: var(--paper);
}

/*
 * The card copy beneath the field — the consent sentence, and the deposit
 * disclosure that replaces it. Both classes are listed because they are two
 * versions of the same paragraph: .husna-square-consent is what the gateway
 * rendered before the deposit split existed, .husna-square-disclosure is what
 * renders now. Styling only the current one would leave the older markup
 * looking like a different site for as long as any environment lags behind.
 *
 * It reads at the same size and colour as a field hint, because that is what it
 * is: a note about what happens to the input above it, not a paragraph of body
 * copy. It was rendering at 15px in full ink, which made it the loudest text on
 * a page whose own heading is quieter.
 */
.hv-checkout .husna-square-consent,
.hv-checkout .husna-square-disclosure {
	font-size: var(--fs-note);
	line-height: 1.7;
	color: var(--grey);
	margin: 14px 0 0;
}

/* The admin-only warning stays loud on purpose — it is not for guests. */
.hv-checkout .husna-square-admin-warning {
	color: var(--red);
}

.hv-checkout .husna-square-error {
	font-size: var(--fs-note);
	line-height: 1.6;
	color: var(--red);
	margin: 10px 0 0;
}

/* ---- terms ---- */

.hv-checkout .mphb-checkout-terms-wrapper {
	grid-column: 1;
	margin-top: clamp(32px, 4vw, 44px);
}

/*
 * SCROLLED ON THE PAGE, NOT LINKED OUT. MotoPress renders the terms page's
 * content inline whenever "Open the Terms & Conditions page in a new window"
 * is off, which is its default. A fixed height plus overflow is all that is
 * needed — and the height is deliberate: a box the guest can see the bottom of
 * without scrolling is a box that says the terms are shorter than they are.
 */
.hv-checkout .mphb-terms-and-conditions {
	max-height: 220px;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: 18px 20px;
	border: 1px solid var(--hair);
	background: var(--bone);
	font-size: var(--fs-ms);
	color: var(--grey);
}

.hv-checkout .mphb-terms-and-conditions h3 {
	font-size: var(--fs-ms);
	color: var(--ink);
	margin: 16px 0 6px;
}

.hv-checkout .mphb-terms-and-conditions > :first-child {
	margin-top: 0;
}

.hv-checkout .mphb-terms-and-conditions-accept {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	margin: 14px 0 0;
	font-size: var(--fs-ms);
	color: var(--ink);
}

.hv-checkout .mphb-terms-and-conditions-accept input[type="checkbox"] {
	width: 18px;
	height: 18px;
	margin: 2px 0 0;
	accent-color: var(--ink);
	flex: none;
}

.hv-checkout .mphb-terms-and-conditions-link {
	color: var(--gold-deep);
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* ---- continue, and the bar it becomes on a phone ---- */

/*
 * ONE WRAPPER, TWO LAYOUTS. Above the breakpoint .hv-co-bar is an ordinary grid
 * item in column 1 and its figure is hidden, which leaves the full-width button
 * where it has always been. Below it, the same wrapper is position:fixed and the
 * figure appears.
 *
 * The Continue button is never duplicated and never moved by script — it is
 * already inside the element that becomes the bar. So one node carries its own
 * listeners, its own disabled state, and its place in MotoPress's form in both
 * layouts. Two buttons would mean two gates, and a gate that exists twice will
 * eventually disagree with itself.
 */
.hv-co-bar {
	grid-column: 1;
	margin: clamp(28px, 3.5vw, 40px) 0 0;
}

.hv-co-bar__read {
	display: none;
}

.hv-co-continue {
	margin: 0;
}

.hv-co-continue__btn {
	display: block;
	width: 100%;
	text-align: center;
	min-height: 56px;
}

.hv-co-continue__btn[disabled] {
	opacity: .4;
	cursor: not-allowed;
}

@media (max-width: 1023.98px) {
	.hv-co-bar {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 90;
		margin: 0;
		background: var(--paper);
		border-top: 1px solid var(--gold);
		box-shadow: 0 -1px 0 var(--hair);
	}

	.hv-co-bar__in {
		display: flex;
		align-items: center;
		gap: 14px;
		padding: 10px var(--wrap-pad) calc(10px + env(safe-area-inset-bottom, 0px));
	}

	.hv-co-bar__read {
		display: block;
		min-width: 0;
	}

	.hv-co-bar__fig {
		display: flex;
		align-items: baseline;
		gap: 6px;
		margin: 0;
		min-width: 0;
	}

	.hv-co-bar__fig b {
		font-family: var(--display);
		font-size: var(--fs-2xl);
		font-weight: var(--disp-w);
		letter-spacing: var(--disp-ls-lg);
		line-height: 1;
		color: var(--ink);
	}

	/* "total" is the unit, not part of the figure — the same rule the villa
	   panel applies to "per night". A bare number beside a button does not say
	   what it is the price of. */
	.hv-co-bar__unit {
		font-family: var(--ui);
		font-size: var(--fs-2xs);
		font-weight: 600;
		letter-spacing: var(--ls-label);
		text-transform: uppercase;
		color: var(--grey);
		white-space: nowrap;
	}

	.hv-co-bar__meta {
		margin: 3px 0 0;
		font-size: var(--fs-note);
		color: var(--grey);
		white-space: nowrap;
	}

	/* The button takes what the figure does not, never less than a thumb. */
	.hv-co-continue {
		flex: 1 1 auto;
		min-width: 0;
	}

	.hv-co-continue__btn {
		min-height: 48px;
		padding: 13px 12px;
	}

	/*
	 * The footer clears this bar the way the villa page's footer clears its own,
	 * and reads its OWN height. --hv-mbar-h is the villa bar's number and the two
	 * bars are different heights; sharing one property would make each wrong on
	 * the other's page. main.js measures this one with a ResizeObserver; the
	 * value here is the no-JS fallback and is deliberately generous.
	 */
	.hv-checkout {
		--hv-co-bar-h: 84px;
	}

	.hv-checkout .site-footer {
		padding-bottom: calc(var(--hv-co-bar-h) + 24px);
	}
}

/* ---- the summary ---- */

.hv-co-summary {
	grid-column: 1;
	min-width: 0;
}

@media (min-width: 1024px) {
	.hv-co-summary {
		grid-column: 2;
		/* Row 2, not row 1: .hv-co-head now occupies row 1 across both columns.
		   The span is unchanged — see the row-gap note at the top of this
		   section for why the count matters and why the gap must stay 0. */
		grid-row: 2 / span 20;
		position: sticky;
		top: 24px;
	}
}

.hv-co-summary__card {
	background: var(--bone);
	border: 1px solid var(--dim);
	border-top: 1px solid var(--gold);
}

/* ---- the peek strip and the collapse ---- */

/*
 * THE TOGGLE EXISTS ONLY BELOW THE BREAKPOINT. On a desktop the summary is a
 * sticky card beside the form with room to show everything, and a control that
 * collapses something already visible is a control with nothing to do.
 */
.hv-co-summary__toggle {
	display: none;
}

/*
 * THE COLLAPSE IS OPT-IN, APPLIED BY SCRIPT. .hv-co-summary--collapsible is
 * added by main.js, so with scripting off the body has no rule hiding it and
 * renders open — a no-JS guest gets the whole summary rather than a strip that
 * will not open. Written expanded and collapsed by script, never the reverse.
 */
@media (max-width: 1023.98px) {
	.hv-co-summary__toggle {
		display: flex;
		flex-direction: column;
		gap: 6px;
		width: 100%;
		padding: 14px var(--wrap-pad);
		background: none;
		border: 0;
		font: inherit;
		text-align: left;
		cursor: pointer;
		-webkit-appearance: none;
		appearance: none;
	}

	.hv-co-summary--collapsible .hv-co-summary__body {
		display: none;
	}

	.hv-co-summary--collapsible.is-open .hv-co-summary__body {
		display: block;
		border-top: 1px solid var(--hair);
	}

	.hv-co-summary__peek {
		display: flex;
		align-items: baseline;
		justify-content: space-between;
		gap: 12px;
		min-width: 0;
	}

	.hv-co-summary__peek-what {
		font-size: var(--fs-ms);
		color: var(--ink);
		min-width: 0;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	/* Never allowed to squeeze the name: if a longer date format or a larger
	   party stops both fitting, the name ellipsises and the stay stays whole. */
	.hv-co-summary__peek-stay {
		flex: none;
		font-size: var(--fs-note);
		color: var(--grey);
		white-space: nowrap;
	}

	/* The figure, one step down the site's price scale — the same rule the
	   villa page's mobile bar applies. */
	.hv-co-summary__peek-total {
		font-family: var(--display);
		font-size: var(--fs-2xl);
		font-weight: var(--disp-w);
		letter-spacing: var(--disp-ls-lg);
		line-height: 1;
		color: var(--ink);
	}

	/* A tracked utility label, which is what earns the uppercase. Source text
	   is sentence case; the capitals are the treatment, not the copy. */
	.hv-co-summary__cue {
		flex: none;
		font-family: var(--ui);
		font-size: var(--fs-2xs);
		font-weight: 600;
		letter-spacing: var(--ls-label);
		text-transform: uppercase;
		color: var(--gold-deep);
		text-decoration: underline;
		text-underline-offset: 3px;
		white-space: nowrap;
	}

	/* Both cues are in the markup; aria-expanded picks which one shows, so the
	   label cannot disagree with the state a screen reader is told. */
	.hv-co-summary__cue-shut,
	[aria-expanded="true"] .hv-co-summary__cue-open {
		display: none;
	}

	[aria-expanded="true"] .hv-co-summary__cue-shut {
		display: inline;
	}
}

.hv-co-summary__media img {
	display: block;
	width: 100%;
	height: 180px;
	object-fit: cover;
}

.hv-co-summary__head {
	padding: 18px var(--wrap-pad) 0;
}

.hv-co-summary__name {
	font-family: var(--display);
	font-size: var(--fs-2xl);
	font-weight: var(--disp-w);
	letter-spacing: var(--disp-ls-lg);
	color: var(--ink);
	margin: 6px 0 0;
	line-height: 1.1;
}

.hv-co-summary__bar {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	padding: 20px var(--wrap-pad) 10px;
	border-bottom: 1px solid var(--hair);
}

.hv-co-summary__change {
	font-size: var(--fs-note);
	color: var(--ink);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.hv-co-summary__rows {
	margin: 0;
	padding: 4px var(--wrap-pad);
}

.hv-co-row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	padding: 10px 0;
	border-bottom: 1px solid var(--hair);
}

.hv-co-row dt {
	font-size: var(--fs-note);
	color: var(--grey);
	flex: none;
}

.hv-co-row dd {
	margin: 0;
	font-size: var(--fs-ms);
	color: var(--ink);
	text-align: right;
	min-width: 0;
}

.hv-co-row dd small {
	display: block;
	font-size: var(--fs-note);
	color: var(--grey);
	margin-top: 2px;
}

.hv-co-summary__included {
	list-style: none;
	margin: 0;
	padding: 14px var(--wrap-pad);
	border-bottom: 1px solid var(--hair);
}

.hv-co-summary__included li {
	position: relative;
	padding-left: 16px;
	font-size: var(--fs-note);
	color: var(--grey);
	line-height: 1.5;
	margin-bottom: 4px;
}

.hv-co-summary__included li::before {
	content: "";
	position: absolute;
	left: 0;
	top: .62em;
	width: 6px;
	height: 1px;
	background: var(--gold);
}

/* A fee gets its own row above the total, never folded into it. Same row
   treatment as the stay facts, so it reads as a line of the bill rather than a
   warning — the figure is not in dispute, it just has to be visible. */
.hv-co-summary__rows--fees {
	padding-top: 0;
	padding-bottom: 0;
}

.hv-co-row--fee:last-child {
	border-bottom: 0;
}

/* The total, in the panel's own voice: a light display figure with the word
   small and tracked beside it. Same rule as the mobile bar. */
.hv-co-summary__total {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	margin: 0;
	padding: 18px var(--wrap-pad) 22px;
}

/*
 * A CHILD SELECTOR, NOT A DESCENDANT ONE, AND THE REASON IS THE WHOLE BUG.
 *
 * mphb_format_price() does not return a string. It returns nested markup:
 *
 *     <span class="mphb-price"><span class="mphb-currency">$</span>12,750</span>
 *
 * so the figure inside <b> is ITSELF a span. Written as
 * `.hv-co-summary__total span` this rule matched the price as well as the word
 * beside it, and --fs-2xs won on the inner element whatever the <b> said. The
 * total rendered at 9px while `b` computed to 38px — which is why the figure
 * "read at the same weight as the rows above it", and why measuring the <b>
 * reported a size the page never showed.
 *
 * Anything styling a figure on this page has to assume it is wrapping a span.
 */
.hv-co-summary__total > span {
	font-family: var(--ui);
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--grey);
}

/* The price markup takes the figure's treatment rather than carrying its own.
   Stated explicitly so a future `.hv-co-summary__total span` cannot reintroduce
   the bug above by accident. */
.hv-co-summary__total b .mphb-price,
.hv-co-summary__total b .mphb-currency {
	font: inherit;
	letter-spacing: inherit;
	color: inherit;
	text-transform: none;
}

/*
 * --fs-3xl is the site's PRICE step (tokens.css) and is what
 * .hv-quote__nightly strong uses on the villa panel. This was --fs-2xl, which
 * is the same size as the villa's name three lines above it — so the most
 * important number on a payment page read at the weight of a heading rather
 * than as the figure being charged.
 */
.hv-co-summary__total b {
	font-family: var(--display);
	font-size: var(--fs-3xl);
	font-weight: var(--disp-w);
	letter-spacing: var(--disp-ls-lg);
	color: var(--ink);
	line-height: 1;
}

/*
 * MOBILE ORDER: the summary above the form. A guest should see what they are
 * paying for before they start typing. `order` on the one node, never a second
 * copy — two summaries is two things to keep in step.
 */
@media (max-width: 1023.98px) {
	.hv-checkout .mphb_sc_checkout-form {
		display: flex;
		flex-direction: column;
		/*
		 * NO TOP PADDING. The summary is the first thing on the page here and
		 * has to sit flush against the ink header — any padding would show as a
		 * band of paper between the two. Every other side keeps its inset, and
		 * the summary escapes those with a negative margin below.
		 */
		padding-top: 0;
		/* Clears the fixed bar, so the last field and the terms box can both be
		   scrolled clear of it rather than sitting permanently underneath. */
		padding-bottom: calc(var(--hv-co-bar-h) + 24px);
	}

	/*
	 * THE RECEIPT BEFORE THE QUESTION. A guest should see what they are paying
	 * for before being asked who is coming, so the summary comes first and the
	 * heading follows it.
	 *
	 * This is why the heading renders on MotoPress's form hook rather than in
	 * page.php: `order` sorts siblings, and from the page template the heading
	 * was a child of <article> while the summary sat three levels deeper inside
	 * this form. No value of `order` could have reached across that.
	 */
	.hv-co-summary { order: -1; }
	.hv-checkout .hv-co-head { order: 0; }
	.hv-checkout .hv-co-fields { order: 1; }

	/*
	 * THE CARD COMES AFTER THE GUEST, AND IT HAD TO BE SAID OUT LOUD.
	 *
	 * Every sibling here carries an explicit order except, until now,
	 * MotoPress's #mphb-billing-details — which therefore defaulted to `order:
	 * 0` and tied with the heading, putting the card field above the guest's own
	 * name. On a phone the page asked for a card before it asked who was
	 * coming, under a heading reading "Who is coming."
	 *
	 * Desktop was never affected: there is no flex ordering at that width, and
	 * MotoPress hooks its billing section at priority 45 against the theme's
	 * lead-guest block at 40, so the DOM order is already right there.
	 *
	 * The lesson is about `order` rather than about this rule: in a container
	 * where siblings are explicitly ordered, ANY sibling that arrives later and
	 * is not given a number silently sorts to the front. Give every child of
	 * this container an order, including ones that seem obvious.
	 */
	.hv-checkout #mphb-billing-details { order: 2; }

	.hv-checkout .mphb-checkout-terms-wrapper { order: 3; }

	/* .hv-co-bar is position:fixed at this width, so it is out of flow and its
	   order is moot — stated anyway, because the day the bar stops being fixed
	   is the day this silently becomes load-bearing. */
	.hv-co-bar { order: 4; }

	/*
	 * EDGE TO EDGE. The form insets by --wrap-pad; the summary cancels exactly
	 * that, reading the same token so the two cannot drift apart if the token
	 * changes.
	 *
	 * THE TEXT KEEPS ITS INSET WITHOUT ANYTHING BEING ADDED, because
	 * .hv-co-summary__toggle already pads by --wrap-pad — the same token again.
	 * So the background reaches the glass and every line of type stays clear of
	 * it. Both halves matter: a full-width card with text against the edge is as
	 * wrong as an inset card, and they fail in opposite directions.
	 */
	.hv-co-summary {
		/*
		 * STRETCH IS REQUIRED, not decoration. The form sets `align-items:
		 * start` for the desktop grid, which a column flex container reads as
		 * "size every item to its content" — so the card came out 333px wide,
		 * its own max-content, and the negative margin moved that 333px block
		 * left instead of widening it. Measured: left 0, right 333 of 390.
		 */
		align-self: stretch;
		margin-left: calc(-1 * var(--wrap-pad));
		margin-right: calc(-1 * var(--wrap-pad));
	}

	/*
	 * The side borders go with the margins. A vertical hairline on a full-bleed
	 * band runs off both edges of the screen and reads as a stray line rather
	 * than an edge. The gold top rule stays — it is the band's own edge against
	 * the ink header — and the bottom hairline separates it from the heading.
	 */
	.hv-co-summary__card {
		border-left: 0;
		border-right: 0;
	}

	/*
	 * The heading gets its spacing back. It inherited the form's padding-top
	 * before it moved, and that is now 0, so without this it would sit against
	 * the summary's bottom hairline.
	 */
	.hv-checkout .hv-co-head {
		padding-top: 0;
		margin: clamp(28px, 4vw, 40px) 0 0;
	}
}

/* ==========================================================================
   The reservation page — .hv-rc-*
   ========================================================================== */

/*
 * Rendered by inc/reservation-shortcode.php on BOTH confirmation pages:
 * Reservation Received, where a guest lands after checkout, and Booking
 * Confirmed, where the emailed %view_booking_link% resolves. They are separate
 * posts carrying the same shortcode, so anything scoped to one of them by page
 * id would leave the other unstyled.
 *
 * Named after the checkout's .hv-co-* rather than importing anything from the
 * reference design, which has no confirmation screen.
 */

.hv-rc {
	padding: var(--pad-v) 0;
}

/*
 * A single column narrower than the site measure. This page is read once,
 * carefully, by someone checking whether we got their phone number right —
 * short lines matter more here than filling the width.
 */
.hv-rc__wrap {
	max-width: calc(680px + 2 * var(--wrap-pad));
}

.hv-rc__head {
	padding-bottom: clamp(20px, 3vw, 32px);
	border-bottom: 1px solid var(--hair);
}

.hv-rc__eyebrow {
	margin: 0;
}

.hv-rc__title {
	font-family: var(--display);
	font-size: var(--fs-disp-sm);
	font-weight: var(--disp-w);
	letter-spacing: var(--disp-ls);
	line-height: 1.07;
	color: var(--ink);
	margin: 14px 0 0;
}

.hv-rc__h2 {
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: .36em;
	text-transform: uppercase;
	color: var(--grey);
	margin: 0 0 14px;
}

.hv-rc__lede {
	font-size: var(--fs-lg);
	font-weight: 300;
	line-height: 1.5;
	color: var(--ink);
	margin: 0;
}

.hv-rc__body {
	font-size: var(--fs-ms);
	line-height: 1.75;
	color: var(--grey);
	margin: 12px 0 0;
}

.hv-rc__body strong {
	font-weight: 600;
	color: var(--ink);
}

.hv-rc__tel {
	color: var(--gold-deep);
	border-bottom: 1px solid currentColor;
	white-space: nowrap;
}

/* --- the money block, one of four states -------------------------------- */

.hv-rc__money {
	padding: clamp(22px, 3vw, 30px) 0;
	border-bottom: 1px solid var(--hair);
}

/*
 * THE GOLD RULE MARKS "NOTHING HAS BEEN TAKEN", NOT "GOOD NEWS". It is on the
 * two states where the guest's money is untouched — a live hold, and a hold
 * that is gone with nothing captured — because that is the fact the block
 * exists to carry. The two states where money HAS moved are left unruled, so
 * the marker never has to mean two opposite things at once.
 */
.hv-rc__money--held,
.hv-rc__money--no-hold {
	border-left: 2px solid var(--gold);
	padding-left: clamp(18px, 2.4vw, 26px);
}

/* --- the two detail panels ---------------------------------------------- */

.hv-rc__panel {
	background: var(--bone);
	padding: clamp(22px, 3vw, 32px);
	margin-top: clamp(24px, 3.4vw, 40px);
}

.hv-rc__next,
.hv-rc__mail {
	margin-top: clamp(24px, 3.4vw, 40px);
}

.hv-rc__rows {
	margin: 0;
}

.hv-rc__row {
	display: flex;
	align-items: baseline;
	gap: 16px;
	padding: 11px 0;
	border-top: 1px solid var(--hair);
}

.hv-rc__row:first-child {
	border-top: 0;
	padding-top: 0;
}

.hv-rc__label {
	flex: 0 0 34%;
	font-size: var(--fs-2xs);
	font-weight: 600;
	letter-spacing: .36em;
	text-transform: uppercase;
	color: var(--grey);
}

/*
 * min-width:0 so a long email address wraps inside the row rather than forcing
 * the flex line wider than the panel. Values are guest-supplied and one of
 * them is an address we have never seen.
 */
.hv-rc__value {
	flex: 1 1 auto;
	min-width: 0;
	margin: 0;
	font-size: var(--fs-body);
	line-height: 1.5;
	color: var(--ink);
	overflow-wrap: anywhere;
}

.hv-rc__row--total {
	border-top: 1px solid var(--ink);
	margin-top: 4px;
	padding-top: 14px;
}

/*
 * THE PRICE IS SIZED ON THE INNER SPAN, NOT ON THE ROW.
 * mphb_format_price() returns
 * <span class="mphb-price"><span class="mphb-currency">$</span>11,200</span>,
 * so a font-size set on .hv-rc__total alone is inherited by the outer span and
 * can be overridden on the inner one by any descendant rule that happens to
 * match — which is exactly how a figure once computed 38px while rendering at
 * 9px. Setting it on the spans themselves leaves nothing between the rule and
 * the glyphs.
 */
.hv-rc__total,
.hv-rc__total .mphb-price,
.hv-rc__total .mphb-price * {
	font-size: var(--fs-xl);
	font-weight: 400;
	line-height: 1.2;
	color: var(--ink);
}

@media (max-width: 599.98px) {

	/*
	 * The label stops being a column and becomes a line above its value.
	 * At 390 a 34% label column leaves the value about 200px wide, which
	 * breaks a long email address across three lines beside a one-word label.
	 */
	.hv-rc__row {
		display: block;
	}

	.hv-rc__label {
		margin-bottom: 3px;
	}
}

