/**
 * OGD Explore Stays -- brand, structure, and premium refinement pass.
 * Every interactive control styled here is a real, relocated Voxel
 * element (or a thin trigger wrapping one) -- this file never targets
 * anything that isn't backed by working native filtering underneath.
 *
 * IMPORTANT constraint learned the hard way during Increment 2: .ogd-panel
 * and .ogd-drawer must NEVER receive a `transform` (even a resting
 * translateY(0)) or `position: absolute/fixed`. Either establishes a CSS
 * containing block for descendants, and Voxel's own nested filter popups
 * (Destination Area, Land Access, etc.) render pinned to the viewport's
 * top-left corner when their ancestor chain includes one. Verified live
 * against production. Motion on these two containers is opacity-only;
 * transform/lift effects are reserved for elements with no Voxel popup
 * descendants (triggers, chips, cards).
 */

.elementor-widget-ts-search-form,
.elementor-widget-ts-post-feed,
.elementor-widget-ts-map,
.ogd-hero,
.ogd-explore,
.ogd-panel,
.ogd-drawer,
.ogd-modal-backdrop {
	--ogd-forest: #16271a;
	--ogd-sage: #5d8745;
	--ogd-sage-deep: #4a6d38;
	--ogd-cream: #fbf8f1;
	--ogd-sand: #f4f0e4;
	--ogd-line: #e6e1d3;
	--ogd-muted: #767c72;
	--ogd-sage-wash: rgba(93, 135, 69, 0.09);
	--ogd-sage-wash-strong: rgba(93, 135, 69, 0.16);
	--ogd-shadow-sm: 0 2px 8px rgba(22, 39, 26, 0.06);
	--ogd-shadow-md: 0 12px 32px rgba(22, 39, 26, 0.1);
	--ogd-shadow-lg: 0 24px 60px rgba(22, 39, 26, 0.16);
	--ogd-ease: cubic-bezier(0.4, 0, 0.2, 1);
}

.elementor-widget-ts-post-feed .ts-preview,
.elementor-widget-ts-map .ts-marker-wrapper,
.ogd-explore,
.ogd-panel,
.ogd-drawer {
	font-family: 'Questrial', Arial, sans-serif;
}

/* ============ Editorial intro ============ */
/* Fraunces is reserved for these curatorial moments only -- every
   functional control (buttons, inputs, labels) stays on Questrial. This
   pairing is the main lever for reading as editorial travel content
   rather than a styled directory. */

/* Thin utility strip, not the old full editorial hero moment -- no more
   decorative wave texture or generous card padding, both of which were
   sized for a band that also had to carry a headline. Flat sand ground,
   a bottom hairline standing in for the old card's shadow/radius, and
   padding tight enough that this reads as a search bar first.
   .ogd-hero has no nested-popup descendants of its own concern (the
   Where/Filters/Guests panels are all appended to document.body as
   fixed modals, not nested in here), so it's safe to give it a
   position/margin treatment.
   Full-bleed background: .ogd-hero's own sticky ancestor (Elementor
   element b69103e) carries its own 22px side padding, which was
   insetting the sand strip from the true page edges while the white
   site-nav bar above it (a separate element) reads edge to edge --
   confirmed live via computed rects (hero box stopped 22px short of
   the viewport on each side). The negative margin cancels that
   ancestor's padding so the BACKGROUND spans the full sticky width;
   the matching +22px added to this rule's own left/right padding
   keeps the actual search controls sitting at the same visual inset
   they were at before, so nothing here shifts position. */
.ogd-hero {
	background-color: var(--ogd-sand);
	border-bottom: 1px solid var(--ogd-line);
	padding: 14px 48px;
	margin: 0 -22px;
}

/* The search-form widget's own Elementor row is already full page width
   (1454px) -- only the widget itself was shrink-to-fit sized (flex: 0 1
   auto, no explicit width) to roughly half of it, leaving genuine dead
   space beside the hero. This is a pure flex-sizing override, not an
   Elementor structural change: no widgets move between containers, no
   template edit, nothing Voxel-functional is touched. Confirmed via
   direct inspection before writing this rule. */
.elementor-widget-ts-search-form {
	flex-grow: 1;
	width: 100%;
}

/* Results/map split -- was a lopsided ~26/74 (results column measured
   500px against the map's 1419px on a 1920px viewport), leaving cards
   cramped. Rebalanced toward the results column since that's where the
   actual data lives; the map still gets a full, usable width. IDs read
   live from the results row's two direct children.
   1025px, not 1024px -- confirmed live this is a real, pre-existing bug
   (present before any of tonight's iPad-specific work): Voxel's own
   native tablet mode covers 768-1024px INCLUSIVE (its map is hidden and
   a single list/map view is shown via its own "List view"/"Map view"
   toggle in that whole range), so at exactly 1024px this rule and
   Voxel's own tablet CSS were BOTH active at once -- this rule forcing
   the visible panel (list OR map, whichever Voxel was showing) down to
   its 58%/42% desktop share even though the OTHER panel was actually
   hidden, leaving that missing percentage as dead blank space instead
   of the visible panel using the full width. 1025px exactly matches
   Voxel's own desktop threshold (confirmed live via its injected
   stylesheet: .vx-hidden-desktop's own hide rule is
   min-width:1025px), so this can never again overlap with its tablet
   range. */
@media (min-width: 1025px) {
	.elementor-element-2633c3a {
		width: 58% !important;
		flex: 0 0 58% !important;
	}

	.elementor-element-ead9d7c {
		width: 42% !important;
		flex: 0 0 42% !important;
	}
}

.ogd-explore-intro {
	margin: 0 2px 28px;
	max-width: 640px;
}

.post-feed-header {
	font-family: 'Questrial', Arial, sans-serif;
	color: var(--ogd-forest);
}

/* Results section eyebrow -- editorial framing above Voxel's own real
   count/sort row, not a duplicate or invented number. */
.ogd-results-eyebrow {
	font-family: 'Questrial', Arial, sans-serif;
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ogd-sage);
	margin-bottom: 6px;
}

.ogd-explore-eyebrow {
	display: block;
	font-family: 'Questrial', Arial, sans-serif;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: #5d8745;
	margin-bottom: 8px;
}

.ogd-explore-heading {
	font-family: 'Fraunces', Georgia, serif;
	font-optical-sizing: auto;
	font-weight: 500;
	font-size: clamp(20px, 2.4vw, 38px);
	line-height: 1.15;
	letter-spacing: -0.01em;
	color: #16271a;
	margin: 0;
	white-space: nowrap;
}

@media (max-width: 640px) {
	.ogd-explore-heading {
		white-space: normal;
	}
}

/* ============ Cards ============ */

.elementor-widget-ts-post-feed .ts-preview {
	border-radius: 20px !important;
	overflow: hidden;
	border: 1px solid var(--ogd-line);
	box-shadow: var(--ogd-shadow-sm);
	transition: transform 0.3s var(--ogd-ease), box-shadow 0.3s var(--ogd-ease);
	/* Forces its own compositing layer -- fixes a Chromium rendering bug
	   where a rounded overflow:hidden container's clip mask leaves a thin
	   dark antialiasing seam around an image child at the rounded corner.
	   translateZ(0) is visually a no-op (nothing moves). */
	transform: translateZ(0);
}

.elementor-widget-ts-post-feed .ts-preview:hover {
	transform: translate3d(0, -4px, 0);
	box-shadow: var(--ogd-shadow-md);
}

/* Photography -- full-bleed, a consistent 4:3 crop regardless of each
   photo's native ratio (was rendering at whatever Voxel's own default box
   produced, ~5:3, a thin letterboxed strip). Taller crop gives the image
   more of the card's real estate, closer to a boutique collection photo
   than a listing thumbnail. !important because Voxel's own theme CSS
   already sets a fixed pixel height on this box at comparable specificity. */
.elementor-widget-ts-post-feed .ts-preview .elementor-widget-image {
	overflow: hidden;
	border-radius: 14px;
	aspect-ratio: 4 / 3 !important;
	height: auto !important;
}

.elementor-widget-ts-post-feed .ts-preview .elementor-widget-image img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.6s var(--ogd-ease);
}

/* :not(.ogd-slide) -- on a card with a slideshow, this rule used to
   apply to every stacked slide at once (all 6 images share the
   .elementor-widget-image img selector, not just the visible one),
   competing with the slide crossfade/arrow-click interaction. Cards
   without a slideshow (a single real photo, no .ogd-slide class ever
   added) keep the hover zoom exactly as before. */
.elementor-widget-ts-post-feed .ts-preview:hover .elementor-widget-image img:not(.ogd-slide) {
	transform: scale(1.05);
}

/* A faint bottom wash on the photo -- depth, not decoration. No text ever
   sits on it (no badges/overlays), it just keeps the photo from ending in
   a hard, flat edge, the way a printed plate has a touch of vignette. */
.elementor-widget-ts-post-feed .ts-preview .elementor-widget-image {
	position: relative;
}
.elementor-widget-ts-post-feed .ts-preview .elementor-widget-image::after {
	content: '';
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, rgba(22, 39, 26, 0) 70%, rgba(22, 39, 26, 0.12) 100%);
	pointer-events: none;
}

/* Card photo slideshow -- only present on cards with real, additional
   gallery photos (see [ogd_field key="gallery_urls"] in the plugin PHP;
   single-photo listings never get a .ogd-gallery-data element, so this
   never applies to them). Slides stack via opacity, not display, so the
   crossfade is smooth; dots/arrows only appear once JS has actually
   built more than one slide. */
/* !important on opacity here specifically: found live that the card's
   root <a> carries elementskit-parallax-multi-container (a GSAP-based
   hover-parallax effect from the ElementsKit plugin, unrelated to this
   plugin) which sets every stacked slide's opacity to 0.9 via inline
   style on hover -- confirmed via computed style during a real hover (all
   6 slides read opacity:0.9 simultaneously instead of 0/1), which is what
   made hovering look like it jumped to a random photo and made a
   following click look like it did nothing. Inline styles beat external
   non-important rules regardless of specificity, so only !important wins
   here reliably against a script neither of us controls. */
.elementor-widget-ts-post-feed .ts-preview .ogd-slide {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	opacity: 0 !important;
	transition: opacity 0.5s var(--ogd-ease), transform 0.6s var(--ogd-ease);
	pointer-events: none;
}

.elementor-widget-ts-post-feed .ts-preview .ogd-slide.ogd-slide-active {
	opacity: 1 !important;
	pointer-events: auto;
}

.elementor-widget-ts-post-feed .ts-preview .ogd-slide-dots {
	position: absolute;
	bottom: 6px;
	left: 0;
	right: 0;
	display: flex;
	justify-content: center;
	align-items: center;
	gap: 0;
	z-index: 2;
}

/* padding + background-clip keeps the visual dot small (6px, matching the
   quiet aesthetic) while giving it a real ~18px tap target -- a plain 6px
   button is easy to miss with a real click, confirmed live before adding
   this. */
.elementor-widget-ts-post-feed .ts-preview .ogd-slide-dot {
	width: 6px;
	height: 6px;
	padding: 6px;
	background-clip: content-box;
	border: none;
	border-radius: 100px;
	background-color: rgba(255, 255, 255, 0.55);
	cursor: pointer;
	transition: background-color 0.2s var(--ogd-ease), width 0.2s var(--ogd-ease);
}

.elementor-widget-ts-post-feed .ts-preview .ogd-slide-dot.is-active {
	background: #fff;
	width: 16px;
}

.elementor-widget-ts-post-feed .ts-preview .ogd-slide-arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 28px;
	height: 28px;
	border-radius: 100%;
	border: none;
	background: rgba(255, 255, 255, 0.85);
	color: var(--ogd-forest);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	opacity: 0;
	/* Invisible arrows were still clickable at their fixed position even
	   when not hovered (opacity alone doesn't affect hit-testing) --
	   blocking pointer events until the card is actually hovered removes
	   that stray, invisible hit target. */
	pointer-events: none;
	z-index: 2;
	transition: opacity 0.2s var(--ogd-ease), background 0.2s var(--ogd-ease);
}

.elementor-widget-ts-post-feed .ts-preview:hover .ogd-slide-arrow {
	opacity: 1;
	pointer-events: auto;
}

.elementor-widget-ts-post-feed .ts-preview .ogd-slide-arrow:hover {
	background: #fff;
}

.elementor-widget-ts-post-feed .ts-preview .ogd-slide-prev {
	left: 10px;
}

.elementor-widget-ts-post-feed .ts-preview .ogd-slide-next {
	right: 10px;
}

/* Card title only -- confirmed via the card template's own data that the
   title widget is the sole h3 in the card (price/review-count widgets
   use span/h2), so this can't accidentally restyle price or review text. */
.elementor-widget-ts-post-feed .ts-preview h3.elementor-heading-title {
	font-family: 'Fraunces', Georgia, serif;
	font-weight: 500;
	font-size: 18px;
	letter-spacing: -0.01em;
	line-height: 1.32;
}

/* Card content -- editorial reading order (type, title, place, atmosphere,
   existing rating row, specs, price). New lines are plain shortcode-widget
   siblings; every one gets 22px of horizontal breathing room (the photo is
   deliberately full-bleed with none, so the text block reading in from the
   edge is what was making the card feel flush/empty rather than composed)
   plus its own margin-top for a considered rhythm -- tight within the
   "identity" (type+title) and "place" (location+community) groups, an
   open beat before the atmosphere line, and a clear close before price. */

.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdtype,
.elementor-widget-ts-post-feed .ts-preview .elementor-element-dd4f83f,
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdlocation,
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdcommunity,
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdatmosphere,
.elementor-widget-ts-post-feed .ts-preview .elementor-element-c969299,
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdspecs,
.elementor-widget-ts-post-feed .ts-preview .elementor-element-5aa4e00 {
	padding-left: 22px !important;
	padding-right: 22px !important;
}

.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdtype {
	margin-top: 16px;
}
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdtype .elementor-shortcode {
	font-family: 'Questrial', sans-serif;
	font-size: 11px;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--ogd-sage-deep);
}

.elementor-widget-ts-post-feed .ts-preview .elementor-element-dd4f83f {
	margin-top: 3px;
}

.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdlocation {
	margin-top: 7px;
}
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdlocation .elementor-shortcode {
	font-size: 13px;
	color: var(--ogd-muted);
}

.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdcommunity {
	margin-top: 2px;
}
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdcommunity .elementor-shortcode {
	font-size: 12.5px;
	color: var(--ogd-muted);
}

/* The one storytelling line on the card -- more present than a caption
   (larger, less transparent than before) and given real air above it so
   it reads as a beat the reader arrives at, not a continuation of the
   place/spec fields around it. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdatmosphere {
	margin-top: 16px;
}
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdatmosphere .elementor-shortcode {
	font-family: 'Fraunces', Georgia, serif;
	font-style: italic;
	font-size: 14.5px;
	color: var(--ogd-forest);
	opacity: 0.85;
	line-height: 1.45;
}

.elementor-widget-ts-post-feed .ts-preview .elementor-element-c969299 {
	margin-top: 14px;
}

/* Specs -- a quiet footnote, not a spec-sheet row. Dropped the hairline
   divider that sat above it (read too much like a UI panel boundary);
   space alone now separates it from the atmosphere/rating content above. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdspecs {
	margin-top: 14px;
}
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdspecs .elementor-shortcode {
	font-size: 11.5px;
	color: var(--ogd-muted);
	letter-spacing: 0.02em;
}

/* Hide the whole line, not just the empty text node, when a field has no
   real data -- keeps the card honest with no blank gaps left behind.
   Doesn't use Voxel's own _voxel_visibility_rules mechanism because that
   only resolves fields registered in Voxel's post-type schema; these are
   plugin-computed values rendered via a plain shortcode instead. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdcommunity:has(.elementor-shortcode:empty),
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdatmosphere:has(.elementor-shortcode:empty),
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdspecs:has(.elementor-shortcode:empty) {
	display: none;
}

/* Retire the native icon-chip specs row (rooms/beds icons) in favor of the
   plain-text specs line above -- icon chips read as real-estate/marketplace
   UI, which this pass is explicitly moving away from. The widget itself is
   left untouched in the template, just visually suppressed here, so it's a
   one-line revert if ever wanted back. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-56a0e87 {
	display: none;
}

/* The gallery_urls data widget (slideshow source data) is invisible by
   design, but ONLY its inner span was ever hidden -- the Elementor widget
   wrapper itself stayed display:block, so it was silently consuming a real
   grid cell/row as a 9th item the horizontal card layout's row math never
   accounted for. Root cause of price rendering under the photo instead of
   in the content column (confirmed live): everything after it shifted by
   one slot, and the overflow item landed in an implicit row the photo's
   1/-1 span didn't cover, so column 1 looked "free" there. Hiding the
   wrapper itself removes it from layout entirely, in every context, not
   just the horizontal one. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdgalleryurls {
	display: none;
}

/* Price moved to the end of the card by the JSON restructure. Its own
   Elementor settings paint it as a bold rust-red callout (#A33E3E, weight
   600) -- exactly the "booking-card" loudness this pass is undoing, so it's
   overridden here rather than left as the one jarring note on an otherwise
   quiet card. Understated forest tone, lighter weight, generous close. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-5aa4e00 {
	margin-top: 18px !important;
	padding-bottom: 20px !important;
}
.elementor-widget-ts-post-feed .ts-preview .elementor-element-5aa4e00 .elementor-heading-title {
	color: var(--ogd-forest) !important;
	font-weight: 500 !important;
	font-size: 15px !important;
	opacity: 0.9;
}

/* elementor-element-fe8d989 is always the monthly-price sub-container
   (elementor-element-558915b is always the nightly-price one, empty
   when a listing has no nightly rate) -- confirmed live by comparing a
   dual-rate card against a monthly-only one, both share this exact
   pair of ids. Scoped to fe8d989 specifically: an earlier, broader fix
   (targeting every heading under the price row) also caught the
   nightly price, right-aligning it inside its own box and dragging it
   inward to sit close to the monthly price instead of flush left --
   confirmed live via a regression screenshot. Same "Elementor's own
   per-instance style resolves differently by load path" bug as
   flex-direction/gap above, this time on text-align: the monthly
   heading's own text-align computes to "end" (right-aligned, correct)
   on a normal page load but "start" (left-aligned) on a card swapped
   in by the map's AJAX reload -- the box's own right edge stays put in
   both cases, but flipping the text to start-aligned pulls the visible
   price away from the card's right edge. Pinning it, on this one
   sub-container only, locks both load paths to the correct, always-was
   right-aligned look without touching the nightly price at all. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-fe8d989 .elementor-heading-title {
	text-align: right !important;
}

/* Same root cause as the card's own flex-direction bug fixed earlier
   today: this price row's own flex-direction is never explicitly set
   by us, so it inherits Elementor's "e-con" default of column -- which
   normally loses to Elementor's *other* per-instance <style> tag that
   happens to set it to row, except that tag isn't guaranteed to be
   present/win on markup swapped in via the map's AJAX reload. Confirmed
   live: night/month price computed flex-direction:column post-reload
   (big vertical gap) vs row on a fresh load (tight, side-by-side).
   Pinning it explicitly removes the dependency on which per-instance
   style happens to load first. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-5aa4e00 .e-con-inner {
	flex-direction: row !important;
}

/* The native Voxel availability/price-range widget (shown instead of a
   flat rate for bookable-calendar listings) carries the same rust-red by
   default -- quieted for the same reason, same scope-limited approach. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-2ac8bbe,
.elementor-widget-ts-post-feed .ts-preview .elementor-element-2ac8bbe * {
	color: var(--ogd-forest) !important;
}

/* "Enquire for rates" fallback -- keep its sage identity (it's signaling
   "no fixed price, ask us", which earns a touch of emphasis) but drop it
   to the same weight as the rest of the quieted price row. */
.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdnofare .elementor-heading-title {
	font-weight: 500 !important;
}

/* Card layout: photo left / content right at desktop, laptop, and iPad-Pro
   widths (matches Plum Guide's list-card composition); vertical (photo
   top, content below -- the default flex-column layout above) at iPad
   Air/mini and phone widths, where a fixed-width photo column would leave
   the content column too narrow to read comfortably. Pure CSS Grid
   override on the card's existing root container -- no Elementor template
   edit, no change to what widgets exist or their own settings/logic.
   1025px, not 1024px -- same reason as the results/map split rule above:
   Voxel's own native tablet mode covers 768-1024px inclusive (a
   2-column vertical card grid), so at exactly 1024px this horizontal
   photo-left layout was firing at the same time as Voxel's own vertical
   grid, producing the narrow/tall broken-looking cards confirmed live
   in a screenshot at exactly that width. 1025px matches Voxel's own
   desktop threshold exactly, so the two can never overlap again. */
@media (min-width: 1025px) {
	/* Flexbox, not CSS Grid -- this replaced a grid-row-spanning approach
	   (image spanning grid-row:1/-1 across 8 declared row tracks sized
	   via grid-template-rows:repeat(8,min-content)) that broke specifically
	   on cards inserted via Voxel's map-triggered AJAX reload: the exact
	   same CSS, !important and all, produced correct ~264px cards on
	   normal page load but had every row track balloon to ~150-190px
	   (one card measured 1500px total) for AJAX-inserted ones. That's the
	   browser's multi-track min-content negotiation computing wrong for
	   that specific insertion path -- not a losing CSS cascade battle
	   (confirmed: adding !important to every grid property changed
	   nothing). Flexbox needs none of that: the image is just one flex
	   item stretching to match its single sibling's (the content wrapper,
	   built by wrapCardContent() in explore-stays.js) own simple
	   block-flow height. */
	.elementor-widget-ts-post-feed .ts-preview .elementor-element-2fcb7150 {
		display: flex !important;
		/* This was missing, and it's the actual cause of the giant-image
		   bug reported live: Elementor's own default for "e-con" flex
		   containers is column, and since display:flex alone doesn't
		   imply a direction, that default won the flex-direction property
		   specifically even with display:flex !important set (confirmed
		   live: a broken card computed flex-direction:column). In column
		   direction, flex-basis refers to height not width, and the two
		   children stack instead of sitting side by side -- exactly the
		   observed symptom (a very tall image stacked above the text). */
		flex-direction: row !important;
		align-items: stretch !important;
		/* Same "Elementor's own per-instance style wins differently
		   depending on load path" bug as flex-direction above, this time
		   on gap: confirmed live this container's own computed gap is
		   5px on a normal page load but 20px on cards swapped in by the
		   map's AJAX reload -- a 15px difference that widened the visual
		   gap between the photo and the text column and, since the price
		   row's own width is squeezed from its left edge, made the price
		   line look shifted. Pinning it locks both load paths to the
		   same, already-correct fresh-load value. */
		gap: 5px !important;
	}

	/* .elementor-element-bf5794f IS the .elementor-widget-image element --
	   Elementor puts both classes on one wrapper div, not on an ancestor
	   and a descendant (a lesson from earlier today: writing them as a
	   descendant selector with a space matches nothing, since no element
	   is a descendant of itself). Wider photo column -- the photo is the
	   card's main selling point, not a thumbnail beside a data sheet.
	   min-width alongside a percentage flex-basis is what actually
	   enforces a floor on narrow cards (flex-basis alone can shrink past
	   it; CSS Grid's minmax() has no flexbox equivalent). */
	.elementor-widget-ts-post-feed .ts-preview .elementor-element-bf5794f {
		flex: 0 0 46% !important;
		min-width: 260px !important;
		height: auto !important;
		aspect-ratio: auto !important;
		/* Elementor's own per-post <style id="elementor-post-28804"> sets
		   margin:0 0 15px 0 on this exact element (its default "widget
		   spacing" below every widget) -- confirmed live via the injected
		   stylesheet's own text. With align-items:stretch, a flex item's
		   rendered content-box height is the stretched line height MINUS
		   its own margin, so that 15px bottom margin was silently eating
		   into the image's height, leaving a 15px gap under the photo
		   that the content column (no such margin) didn't have -- exact
		   match confirmed live (208.328px stretched line height -
		   193.328px measured image height = 15px). This wrapper is a
		   flex item, not a block widget in a stack, so any bottom margin
		   is meaningless here regardless of its value. */
		margin: 0 !important;
		/* Only the photo's left edge is a true outer corner of the card in
		   this layout -- its right edge sits against the content column,
		   an internal division, not a card boundary. Rounding it there
		   looked like an unintentional bite out of the layout. Matches the
		   card's own 20px radius so the left edge reads as one continuous
		   curve with the card, not a smaller nested one. */
		border-radius: 20px 0 0 20px !important;
	}

	.elementor-widget-ts-post-feed .ts-preview .elementor-element-bf5794f img {
		/* position:absolute + inset:0, not height/width:100% in normal
		   flow. Root cause of the "Beehives" card inflating to ~1500px
		   specifically right after a map-triggered AJAX reload (never on
		   a fresh page load): height:100% on a plain block-flow <img>
		   only resolves against a *definite* parent height. This
		   wrapper's height comes from flexbox align-items:stretch, not
		   an intrinsic value, and for this one listing's unusually tall
		   source photo, immediately after AJAX insertion (before the
		   slideshow JS has converted the <img> into a .ogd-slide, which
		   already used this exact technique below) the percentage failed
		   to resolve and the <img> fell back to sizing itself from its
		   own natural aspect ratio at the constrained width -- producing
		   a very tall image that then stretched the whole flex row via
		   align-items:stretch. absolute positioning against the
		   wrapper's already-established position:relative box (line
		   ~209) sidesteps percentage-height resolution entirely, exactly
		   like .ogd-slide already does, so it can never depend on JS
		   timing or this parent/child height circularity again. */
		position: absolute !important;
		inset: 0 !important;
		height: 100% !important;
		width: 100% !important;
		aspect-ratio: auto !important;
		object-fit: cover;
		/* The wrapper's overflow:hidden clip was correctly square on the
		   right (20px 0 0 20px), but the <img> itself still carried
		   Elementor's own default 12px radius on all four corners
		   independently -- confirmed live via computed style -- showing
		   as a visible inner curve at the top-right/bottom-right despite
		   the outer clip being square there. Matching the wrapper's radius
		   here removes that inner curve entirely. */
		border-radius: 20px 0 0 20px !important;
	}

	/* The content wrapper wrapCardContent() builds at runtime, grouping
	   every non-image child so it can be ONE flex sibling to the photo.
	   Existing per-item rules (padding, margin-top, etc.) below don't
	   need to change -- they target each item by its own class, and
	   ancestry within .ts-preview still holds regardless of this new
	   intermediate parent. */
	.elementor-widget-ts-post-feed .ts-preview .ogd-card-content-wrap {
		flex: 1 1 auto !important;
		min-width: 0 !important;
		display: flex !important;
		flex-direction: column !important;
	}

	/* The vertical layout's margin-top on the first content item exists to
	   create space below the photo; in the horizontal layout that item
	   sits beside the photo's top edge instead, so it gets a column-
	   appropriate top inset here rather than the vertical rhythm value. */
	.elementor-widget-ts-post-feed .ts-preview .elementor-element-ogdtype {
		margin-top: 20px;
	}

	/* Anchors price to the bottom of the content column -- combined with
	   align-content: start above, any leftover height from a photo taller
	   than the real content lands in exactly one place (between specs and
	   price) instead of being scattered, and price still lines up with the
	   photo's bottom edge rather than hugging specs with a random gap
	   below it. */
	.elementor-widget-ts-post-feed .ts-preview .elementor-element-5aa4e00 {
		margin-top: auto !important;
	}
}

/* ============ Map ============ */

.elementor-widget-ts-map .ts-marker-cluster {
	background: var(--ogd-sage) !important;
	border-color: var(--ogd-forest) !important;
	box-shadow: 0 2px 10px rgba(22, 39, 26, 0.25);
}

/* Frame the map itself so it reads as an integrated OGD surface rather
   than a bare embed -- pure presentation, Voxel's own map/clustering
   logic is completely untouched. */
.elementor-widget-ts-map {
	border-radius: 0;
	overflow: hidden;
	box-shadow: var(--ogd-shadow-sm);
	border: 1px solid var(--ogd-line);
	/* Same Chromium rounded-corner seam fix as .ts-preview above -- Google
	   Maps' own canvas rendering was showing a thin dark line through the
	   clip mask at the rounded corner specifically. Harmless at
	   border-radius:0 too (no-op clip), left in case the radius ever
	   comes back. */
	transform: translateZ(0);
}

/* ============ Native filter controls, wherever they end up living ============ */

.ts-form-group .ts-filter {
	border-radius: 100px !important;
	color: var(--ogd-forest);
	transition: background 0.2s var(--ogd-ease), color 0.2s var(--ogd-ease), border-color 0.2s var(--ogd-ease);
}

.ts-form-group .ts-filter.ts-filled {
	background: var(--ogd-sage-wash) !important;
	color: var(--ogd-sage-deep) !important;
}

.addon-buttons li {
	border-radius: 100px !important;
	transition: transform 0.15s var(--ogd-ease), background 0.15s var(--ogd-ease), box-shadow 0.15s var(--ogd-ease);
}

.addon-buttons li:hover {
	transform: translateY(-1px);
	box-shadow: var(--ogd-shadow-sm);
}

.addon-buttons li.adb-selected {
	background: var(--ogd-sage) !important;
	color: #fff !important;
	border-color: var(--ogd-sage) !important;
	box-shadow: 0 4px 14px rgba(93, 135, 69, 0.3);
}

/* ============ OGD Explore hierarchy ============ */

/* .ogd-explore no longer carries its own box (border/shadow/background) --
   that visual weight moved to the shared .ogd-hero wrapper above, so the
   search controls read as part of one continuous hero moment with the
   editorial copy, not a separate "widget card" beneath it. */
.ogd-explore {
	display: flex;
	flex-direction: column;
	gap: 14px;
}

/* Left-packed, not stretched -- the search-form widget's own row is
   full page width, and a first pass here (flex:1 on the AI slot) let
   the controls stretch edge to edge to fill it, which made even a
   compacted 3-field row read as visually just as heavy as the old
   5-pill layout. Nothing here grows any more; .ogd-filters-trigger
   gets pushed to the far right via margin-left:auto below instead,
   leaving real open ground in between, the same restrained scale as
   the reference this was modeled on (Plum Guide's search bar). */
.ogd-explore-primary {
	display: flex;
	align-items: center;
	gap: 12px;
	flex-wrap: wrap;
}

.ogd-filters-trigger {
	margin-left: auto;
}

/* AI slot -- still a functionally inert (disabled) placeholder ahead of
   a later increment, so it now gets an icon-only entry point rather
   than the full-width text input it used to be: a small circular
   button carrying the same sparkle glyph, with a "soon" dot instead of
   a floating badge label. The badge text and input placeholder are
   kept in the DOM (screen-reader-visible via the input's own attributes
   and the wrapper's title) but visually collapsed -- nothing here
   changes what this control actually does. */
.ogd-ai-slot {
	position: relative;
}

.ogd-ai-slot.ogd-ai-compact {
	width: 44px;
	height: 44px;
	flex-shrink: 0;
}

.ogd-ai-compact .ogd-ai-input {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	border: 1px solid var(--ogd-line);
	background-color: #fff;
	padding: 0;
	color: transparent;
	caret-color: transparent;
	box-shadow: var(--ogd-shadow-sm);
	cursor: default;
	transition: border-color 0.2s var(--ogd-ease), box-shadow 0.2s var(--ogd-ease);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235d8745' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: center;
	background-size: 16px 16px;
}

.ogd-ai-compact .ogd-ai-input::placeholder {
	color: transparent;
}

/* Kept off-screen rather than display:none -- the wrapper's title
   attribute is the visible (hover) affordance now, but the badge text
   stays in the accessibility tree for anyone not hovering a mouse. */
.ogd-ai-compact .ogd-ai-badge {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
}

.ogd-ai-soon-dot {
	position: absolute;
	top: -2px;
	right: -2px;
	width: 11px;
	height: 11px;
	border-radius: 50%;
	background: var(--ogd-sage);
	border: 2px solid var(--ogd-sand);
	pointer-events: none;
}

/* Custom trigger buttons (Where / Stay type / More filters) -- quiet by
   default so the headline carries the visual weight; the richer shadow
   and lift only appear on interaction, matching the "controls are part
   of a discovery moment, not a form" composition. */
.ogd-trigger {
	font-family: inherit;
	font-size: 14.5px;
	font-weight: 500;
	color: var(--ogd-forest);
	background: rgba(255, 255, 255, 0.6);
	border: 1.5px solid var(--ogd-line);
	border-radius: 100px;
	padding: 12px 20px;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	cursor: pointer;
	white-space: nowrap;
	transition: border-color 0.2s var(--ogd-ease), color 0.2s var(--ogd-ease),
		background 0.2s var(--ogd-ease), transform 0.15s var(--ogd-ease), box-shadow 0.2s var(--ogd-ease);
}

.ogd-trigger:hover {
	background: #fff;
	border-color: var(--ogd-sage);
	transform: translateY(-1px);
	box-shadow: var(--ogd-shadow-sm), 0 4px 14px rgba(93, 135, 69, 0.12);
}

.ogd-trigger svg {
	transition: transform 0.2s var(--ogd-ease);
	opacity: 0.6;
}

.ogd-trigger[aria-expanded="true"] svg {
	transform: rotate(180deg);
}

.ogd-trigger[aria-expanded="true"],
.ogd-trigger.has-active {
	background: var(--ogd-sage-wash);
	color: var(--ogd-sage-deep);
	border-color: var(--ogd-sage);
}

.ogd-trigger[aria-expanded="true"] svg,
.ogd-trigger.has-active svg {
	opacity: 1;
}

.ogd-relocated-slot {
	display: contents;
}

/* Shared-pill treatment for the Where/Dates/Guests cluster -- one
   continuous rounded shell instead of three separate pills, closer to
   how Plum Guide's own "Map area / Add dates / Add guests" bar reads.
   .ogd-where-trigger is ours; the dates/guests buttons are Voxel's own
   native .ts-filter elements relocated in here (their wrapping
   .ogd-relocated-slot is display:contents, so they participate in this
   flex row directly) -- each child's own border/background/radius is
   stripped and replaced with a single divider between segments, with
   the whole cluster taking its shared chrome from .ogd-search-group
   instead. !important on the .ts-filter rules specifically: that
   element's base border/background comes from Voxel's own theme CSS,
   and this file has repeatedly found Voxel/Elementor-owned styles need
   !important to reliably lose a cascade tie (documented elsewhere in
   this file, e.g. the card image/price fixes). border-left is set in
   the same rule as (and after) the border:none shorthand rather than
   as a separate rule, so it isn't itself at risk of losing a
   specificity tie to that shorthand. */
.ogd-search-group {
	display: flex;
	align-items: stretch;
	background: #fff;
	border: 1.5px solid var(--ogd-line);
	border-radius: 100px;
	box-shadow: var(--ogd-shadow-sm);
	overflow: hidden;
}

.ogd-search-group .ogd-trigger {
	border: none;
	border-left: 1px solid var(--ogd-line);
	border-radius: 0;
	background: transparent;
	box-shadow: none;
}
.ogd-search-group .ogd-trigger:hover {
	transform: none;
	box-shadow: none;
	background: var(--ogd-sage-wash);
}
.ogd-search-group .ogd-where-trigger {
	border-left: none;
}

.ogd-search-group .ts-filter {
	border: none !important;
	border-left: 1px solid var(--ogd-line) !important;
	border-radius: 0 !important;
	background: transparent !important;
	box-shadow: none !important;
}
.ogd-search-group .ts-filter:hover {
	background: var(--ogd-sage-wash) !important;
}

/* Re-assert the active/expanded look at higher specificity than the
   plain override above, which would otherwise win a cascade tie
   against .ogd-trigger.has-active purely by appearing later in the
   file (both are two-class selectors). */
.ogd-search-group .ogd-trigger.has-active,
.ogd-search-group .ogd-trigger[aria-expanded="true"] {
	background: var(--ogd-sage-wash);
	color: var(--ogd-sage-deep);
}

/* Where / Stay-type panels: expand inline, directly below the bar, in
   normal document flow -- deliberately NOT position:absolute, and
   deliberately never given a `transform` (see file header). Opacity-only
   fade for the open/close motion. */
.ogd-panel {
	margin-top: 12px;
	background: #fff;
	border: 1px solid var(--ogd-line);
	border-radius: 18px;
	box-shadow: var(--ogd-shadow-lg);
	padding: 22px;
	max-width: 440px;
	opacity: 0;
	transition: opacity 0.2s var(--ogd-ease);
}

.ogd-panel.is-open {
	opacity: 1;
}

.ogd-panel-section + .ogd-panel-section {
	margin-top: 18px;
	padding-top: 18px;
	border-top: 1px solid var(--ogd-line);
}

.ogd-panel h4,
.ogd-drawer h4 {
	margin: 0 0 10px;
	font-size: 11.5px;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--ogd-muted);
	font-weight: 600;
}

/* Inside a relocated slot, the real Voxel .ts-form-group needs to render
   like a normal inline block again (Voxel's own CSS assumes a filter-bar
   context) -- reset just enough for it to sit naturally in our panels. */
.ogd-panel .ts-form-group,
.ogd-drawer .ts-form-group {
	width: 100%;
}

.ogd-panel .ts-filter,
.ogd-drawer .ts-filter {
	width: 100%;
	justify-content: space-between;
	padding: 12px 16px;
	border: 1px solid var(--ogd-line);
}

.ogd-panel .ts-filter:hover,
.ogd-drawer .ts-filter:hover {
	border-color: var(--ogd-sage);
}

/* More Filters drawer: same opacity-only motion, same no-transform rule. */
.ogd-drawer {
	margin-top: 12px;
	background: #fff;
	border: 1px solid var(--ogd-line);
	border-radius: 22px;
	box-shadow: var(--ogd-shadow-lg);
	max-height: min(600px, calc(100vh - 120px));
	display: flex;
	flex-direction: column;
	opacity: 0;
	transition: opacity 0.2s var(--ogd-ease);
}

.ogd-drawer.is-open {
	opacity: 1;
}

.ogd-drawer-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 20px 26px;
	border-bottom: 1px solid var(--ogd-line);
}

.ogd-drawer-header h3 {
	margin: 0;
	font-size: 19px;
	font-weight: 500;
	color: var(--ogd-forest);
	letter-spacing: -0.01em;
}

.ogd-drawer-close {
	background: none;
	border: none;
	font-size: 22px;
	line-height: 1;
	color: var(--ogd-muted);
	cursor: pointer;
	padding: 6px 10px;
	border-radius: 100px;
	transition: background 0.15s var(--ogd-ease), color 0.15s var(--ogd-ease);
}

.ogd-drawer-close:hover {
	background: var(--ogd-cream);
	color: var(--ogd-forest);
}

.ogd-drawer-body {
	padding: 22px 26px 26px;
	overflow-y: auto;
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 22px 24px;
}

.ogd-filter-group:empty,
.ogd-filter-group:has(.ogd-relocated-slot:empty) {
	display: none;
}

/* Applied-filter chips */
.ogd-chips-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.ogd-chip {
	font-family: inherit;
	font-size: 13px;
	font-weight: 500;
	background: #fff;
	border: 1.5px solid var(--ogd-sage);
	color: var(--ogd-sage-deep);
	border-radius: 100px;
	padding: 7px 14px;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	transition: background 0.15s var(--ogd-ease), transform 0.15s var(--ogd-ease);
}

.ogd-chip:hover {
	background: var(--ogd-sage-wash);
	transform: translateY(-1px);
}

.ogd-chip-clear-all {
	border-color: var(--ogd-line);
	color: var(--ogd-muted);
	font-weight: 400;
}

.ogd-chip-clear-all:hover {
	border-color: var(--ogd-forest);
	color: var(--ogd-forest);
	background: var(--ogd-cream);
}

/* Property-type chips, wherever they're rendered (now only inside the
   Filters modal's Stay-type section). */
.ogd-popular-chips,
.ogd-overflow-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	list-style: none;
	margin: 0;
	padding: 0;
}

.ogd-overflow-chips {
	margin-top: 8px;
}

/* NOTE: do NOT hide .ts-filter-wrapper.vx-hidden-desktop (the native
   mobile "Filter results" toggle). Confirmed live: below Voxel's own
   responsive breakpoint, .ts-filter-wrapper.flexify:not(.vx-hidden-desktop)
   (the element buildDesktopHierarchy operates on) is not merely
   CSS-hidden -- it does not exist in the DOM at all -- so
   buildDesktopHierarchy never runs and this native toggle is mobile's
   only way to reach filters. An earlier deploy hid it based on a resize-
   tool misreading; reverted once real mobile testing caught it. */

/* ============ Filters modal ============ */
/* True fixed-position overlay -- deliberately different from
   .ogd-panel/.ogd-drawer above. Confirmed safe specifically for
   position:fixed (not position:absolute, which is what actually breaks
   Voxel's nested popups -- see the file header and explore-stays.js). */
.ogd-modal-backdrop {
	position: fixed;
	inset: 0;
	z-index: 9999;
	background: rgba(22, 39, 26, 0.4);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 24px;
	opacity: 0;
	transition: opacity 0.2s var(--ogd-ease);
}

.ogd-modal-backdrop.is-open {
	opacity: 1;
}

body.ogd-modal-open {
	overflow: hidden;
}

.ogd-modal-panel {
	background: #fff;
	border-radius: 22px;
	box-shadow: var(--ogd-shadow-lg);
	width: min(640px, 100%);
	max-height: min(720px, calc(100vh - 48px));
	display: flex;
	flex-direction: column;
	font-family: 'Questrial', Arial, sans-serif;
}

/* "Where" uses the same modal shell, just narrower and single-column --
   three simple sections rather than a full filter set. */
.ogd-modal-panel-narrow {
	width: min(420px, 100%);
}

.ogd-modal-body-single {
	grid-template-columns: 1fr;
}

.ogd-modal-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 20px 26px;
	border-bottom: 1px solid var(--ogd-line);
	flex-shrink: 0;
}

.ogd-modal-header h3 {
	margin: 0;
	font-size: 19px;
	font-weight: 500;
	color: var(--ogd-forest);
	letter-spacing: -0.01em;
}

.ogd-modal-close {
	background: none;
	border: none;
	font-size: 22px;
	line-height: 1;
	color: var(--ogd-muted);
	cursor: pointer;
	padding: 6px 10px;
	border-radius: 100px;
	transition: background 0.15s var(--ogd-ease), color 0.15s var(--ogd-ease);
}

.ogd-modal-close:hover {
	background: var(--ogd-cream);
	color: var(--ogd-forest);
}

.ogd-modal-body {
	padding: 22px 26px 26px;
	overflow-y: auto;
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 22px 24px;
}

.ogd-filter-group-wide {
	grid-column: 1 / -1;
}

.ogd-modal-footer {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 18px 26px;
	border-top: 1px solid var(--ogd-line);
	flex-shrink: 0;
}

.ogd-modal-clear {
	background: none;
	border: none;
	font-family: inherit;
	font-size: 14px;
	color: var(--ogd-muted);
	text-decoration: underline;
	cursor: pointer;
	padding: 8px 4px;
}

.ogd-modal-clear:hover {
	color: var(--ogd-forest);
}

.ogd-modal-apply {
	background: var(--ogd-forest);
	color: #fff;
	border: none;
	border-radius: 100px;
	font-family: inherit;
	font-size: 14.5px;
	font-weight: 500;
	padding: 13px 28px;
	cursor: pointer;
	transition: background 0.15s var(--ogd-ease), transform 0.15s var(--ogd-ease);
}

.ogd-modal-apply:hover {
	background: var(--ogd-sage-deep);
	transform: translateY(-1px);
}

.ogd-modal-panel .ts-form-group,
.ogd-modal-panel .ogd-chip-slot {
	width: 100%;
}

.ogd-modal-panel .ts-filter {
	width: 100%;
	justify-content: space-between;
	padding: 12px 16px;
	border: 1px solid var(--ogd-line);
}

.ogd-modal-panel .ts-filter:hover {
	border-color: var(--ogd-sage);
}

@media (max-width: 640px) {
	.ogd-modal-backdrop {
		padding: 0;
		align-items: flex-end;
	}

	.ogd-modal-panel {
		width: 100%;
		max-height: 88vh;
		border-radius: 22px 22px 0 0;
	}

	.ogd-modal-body {
		grid-template-columns: 1fr;
	}
}

@media (max-width: 1024px) {
	.ogd-explore-primary {
		flex-direction: column;
		align-items: flex-start;
	}

	.ogd-search-group,
	.ogd-filters-trigger {
		width: 100%;
		margin-left: 0;
	}

	.ogd-drawer-body {
		grid-template-columns: 1fr;
	}
}
