/**
 * Realist — STRUCTURE layer.
 *
 * Functional CSS only: rules whose absence doesn't make the plugin ugly, it makes
 * it BROKEN (dead click targets, a visible honeypot, distorted photos, missing
 * loading feedback). This file always loads and must not be dequeued.
 *
 * Anything purely presentational belongs in frontend-skin.css.
 */

/* ---------- Filter bar ---------- */
.realist-filter-bar { display: flex; flex-wrap: wrap; }
.realist-filter-group { flex: 1 1 200px; display: flex; flex-direction: column; }
.realist-filter-group select,
.realist-filter-group input[type="number"],
.realist-filter-group input[type="text"] { width: 100%; }
.realist-filter-range { display: flex; }
.realist-filter-range > * { flex: 1 1 0; min-width: 0; }
.realist-filter-checks { display: flex; flex-wrap: wrap; }
.realist-filter-checks label { display: inline-flex; align-items: center; }

/* Availability toggle: All ↔ Available. The native checkbox is visually
   replaced by the track/thumb but kept in the layout (off-screen) so it stays
   keyboard-operable and focusable — losing it would break accessibility. */
.realist-toggle { display: inline-flex; align-items: center; }
.realist-toggle-input { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; }
.realist-toggle-track { position: relative; flex: 0 0 auto; }
.realist-toggle-thumb { position: absolute; top: 50%; transform: translateY( -50% ); }

/* ---------- Grid & loading state ---------- */
.realist-index-body { position: relative; } /* positioning context for the loader */
.realist-grid-container {
	display: grid;
	/* --realist-index-columns defaults to auto-fill (responsive by card width);
	   a style variation can set a fixed count (e.g. 3) instead. Card min-width
	   and the count are both variation-settable via theme.json custom props. */
	grid-template-columns: repeat(
		var( --realist-index-columns, var( --wp--custom--realist--index--columns, auto-fill ) ),
		minmax( var( --realist-card-min, var( --wp--custom--realist--index--card-min, 320px ) ), 1fr )
	);
	min-height: 200px;
}
.realist-grid-container.is-loading { opacity: 0.5; pointer-events: none; }
.realist-loader {
	display: none;
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate( -50%, -50% );
	animation: realist-spin 1s linear infinite;
}
#realist-property-grid.is-loading + .realist-loader { display: block; }
@keyframes realist-spin {
	0%   { transform: translate( -50%, -50% ) rotate( 0deg ); }
	100% { transform: translate( -50%, -50% ) rotate( 360deg ); }
}

/* ---------- Card ---------- */
.realist-property-card { position: relative; display: flex; flex-direction: column; overflow: hidden; }
/* Stretched link: the title's anchor covers the whole card, so the card stays
   fully clickable AND the one crawlable link carries the property address as its
   anchor text. Without the ::after the card is dead (no click target). */
.realist-card-link { text-decoration: none; color: inherit; }
.realist-card-link::after { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 10; }
.realist-card-image {
	position: relative;
	width: 100%;
	padding-top: var( --realist-card-ratio, 66.66% ); /* reserves aspect ratio, prevents layout shift */
	overflow: hidden;
}
.realist-card-image img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; }
.realist-card-badge { position: absolute; top: 15px; right: 15px; z-index: 2; }
.realist-card-content { display: flex; flex-direction: column; flex-grow: 1; }
/* Title + price share a row; if either is long the price wraps to its own line
   instead of overflowing or crushing the title. */
.realist-card-header-row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; }
.realist-card-meta { display: flex; }
.realist-no-results { grid-column: 1 / -1; }

/* Load-more sits centred under the grid; visibility (display) is toggled by
   the filter script / server, never set here. */
#realist-load-more-wrapper { text-align: center; margin-top: 40px; }

/* wpautop can leave a stray trailing <p> after the map embed; that margin
   breaks the wrapper box. */
.realist-map-wrapper p:last-child { margin: 0 !important; }

/* The pin (0.62) is inline SVG filled with currentColor, so a theme or style
   variation recolours it with one custom property; the palette's secondary
   otherwise, as the server-drawn image it replaced was. */
.realist-map-pin { color: var( --realist-map-pin, var( --wp--preset--color--secondary, #22224A ) ); }
.realist-map-pin svg { display: block; width: 100%; height: 100%; fill: currentColor; }

/* A pin's popup on the Properties Map: the listing's card in miniature. */
.realist-map-card { display: block; position: relative; color: inherit; text-decoration: none; }
.realist-map-card-image { display: block; position: relative; padding-top: var( --realist-map-card-ratio, 62% ); overflow: hidden; }
.realist-map-card-image img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; }
.realist-map-card-body,
.realist-map-card-title,
.realist-map-card-price,
.realist-map-card-type,
.realist-map-card-place { display: block; }
.realist-map-card-head,
.realist-map-card-meta { display: flex; justify-content: space-between; align-items: baseline; gap: 0.75em; }
.realist-map-card-price,
.realist-map-card-place { flex-shrink: 0; text-align: right; }

/* ---------- Feature tags ---------- */
.realist-features-tags { display: flex; flex-wrap: wrap; }

/* ---------- Single property: sidebar & actions ---------- */
.realist-sidebar p { display: flex; align-items: baseline; }
.realist-sidebar i.fa { width: 25px; text-align: center; }
.realist-badge { display: inline-block; }
.realist-action-buttons { display: flex; flex-wrap: wrap; }
.realist-action-btn { display: inline-flex; align-items: center; justify-content: center; flex: 1 1 auto; }
/* Extra gallery files are collected by GLightbox at init but never shown as
   their own buttons — they're reached via the carousel behind the one button. */
.realist-gallery-hidden { display: none; }

@media ( max-width: 600px ) {
	.realist-action-buttons { flex-direction: column; }
	.realist-action-btn { width: 100%; }
}

/* ---------- Carousel ---------- */
.realist-property-carousel { width: 100%; }
.realist-property-carousel .splide__slide img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	aspect-ratio: 3 / 2;
}

/* ---------- Lead form ----------
 * A flex-wrap layout whose column count is a single custom property, so a theme
 * (or a block style variation) switches single- vs multi-column by setting
 * --realist-form-columns alone. Flex is used rather than grid on purpose: a
 * short field left orphaned on an incomplete row grows to fill it (so Name +
 * Email pair up, a leftover Phone becomes full-width) — grid would leave it as a
 * half-width stub. :where() keeps specificity at zero so themes/Global Styles win.
 */
:where( .realist-lead-form ) {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: var( --realist-form-gap, var( --wp--custom--realist--form--gap, 1rem ) );
}
/* Default: every direct child takes a whole row. */
:where( .realist-lead-form ) > * { flex: 1 1 100%; min-width: 0; }
/* Short fields share rows N-up; flex-grow lets an orphan fill its row.
   Precedence for the column count: a per-form block style variation (sets
   --realist-form-columns) wins; else the theme default from theme.json
   (--wp--custom--realist--form--columns); else single column. */
:where( .realist-lead-form ) .realist-lead-field {
	--realist-form-cols: var( --realist-form-columns, var( --wp--custom--realist--form--columns, 1 ) );
	flex: 1 1 calc( ( 100% - ( var( --realist-form-cols ) - 1 ) * var( --realist-form-gap, var( --wp--custom--realist--form--gap, 1rem ) ) ) / var( --realist-form-cols ) );
}
/* Large / structural fields always take the whole row. */
:where( .realist-lead-form ) .realist-lead-field--wide,
:where( .realist-lead-form ) .realist-lead-consent { flex: 1 1 100%; }

/* Fields are <p> elements, so the UA's 1em block margin would stack on top of the
   flex gap above — doubling the row pitch and pushing the submit button out of
   line with the fields it sits beside. Spacing here is the gap's job alone. */
.realist-lead-field { display: flex; flex-direction: column; margin: 0; }
.realist-lead-field input[type="text"],
.realist-lead-field input[type="email"],
.realist-lead-field input[type="tel"],
.realist-lead-field input[type="number"],
.realist-lead-field input[type="datetime-local"],
.realist-lead-field select,
.realist-lead-field textarea { width: 100%; font: inherit; }
.realist-lead-consent { flex-direction: row; }
.realist-lead-consent label { display: flex; align-items: flex-start; }
/* The status message is rendered up front so it can be an aria-live region, but
   it is empty until a submit resolves. As a full-width flex child it would then
   reserve a blank row (its skin padding plus a gap) above every untouched form —
   and in a single-row layout it would push the submit onto its own line. Drop it
   from the flow while empty; it returns the moment the script writes into it. */
:where( .realist-lead-message:empty ) { display: none; }

/* The bot check's box (specs/turnstile.md). Turnstile runs in interaction-only
   mode, so for almost everyone it shows nothing — but its box still exists, and
   as a full-width flex child it would reserve a blank row and, in a single-row
   strip, push the submit onto its own line (the same trap as the empty message
   above). It cannot be display:none, because the invisible check still has to
   run inside it, so it is taken out of the flow instead. frontend-lead.js adds
   .is-visible for as long as Cloudflare actually asks the person to click. */
:where( .realist-lead-turnstile ) { flex: 1 1 100%; }
:where( .realist-lead-turnstile:not( .is-visible ) ) {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset( 50% );
}

/* ---------- Form controls inherit the page's typeface ----------
 * Browsers do not inherit font-family into form controls: the UA stylesheet
 * gives button/input/select/textarea its own system font, so every other Realist
 * element picks up the theme's typography for free and these silently do not.
 * It shows up as one stray Arial button in an otherwise correctly-typeset form,
 * and a theme is unlikely to catch it — the Realist theme's own base rule lists
 * `input, select, textarea` but not `button`, which is exactly how the lead
 * form's submit ended up as Arial on a Poppins site.
 *
 * Only the family is set. `font: inherit` would also reset size and weight,
 * wiping the skin layer's per-control values (this file loads before the skin,
 * but these selectors outrank its zero-specificity :where() rules).
 */
.realist-lead-submit,
.realist-load-more-btn,
.realist-filter-group select,
.realist-filter-group input,
.realist-filter-checks input { font-family: inherit; }

/* ---------- Property video (oEmbed) ---------- */
.realist-property-video { position: relative; }
.realist-property-video iframe,
.realist-property-video video { width: 100%; aspect-ratio: 16 / 9; height: auto; display: block; border: 0; }

/* ---------- Building / divider gallery ---------- */
.realist-divider-carousel .splide__slide img {
	display: block;
	width: 100%;
	height: var( --realist-divider-height, 220px );
	object-fit: cover;
}

/* ---------- Agent card ---------- */
.realist-agent-card { display: flex; gap: 1rem; align-items: flex-start; }
.realist-agent-photo-wrap { flex: 0 0 auto; }
.realist-agent-body { flex: 1 1 auto; min-width: 0; }
.realist-agent-card p { margin: 0.15rem 0; }

/* ---------- Team Member (about-us profile) ---------- */
.realist-team-member { display: flex; gap: 1rem; }
.realist-team-member--stacked,
.realist-team-member--minimal { flex-direction: column; align-items: center; text-align: center; }
.realist-team-member--horizontal { flex-direction: row; align-items: flex-start; text-align: left; }
.realist-team-member-photo-wrap { flex: 0 0 auto; }
.realist-team-member-body { flex: 1 1 auto; min-width: 0; }
.realist-team-member p { margin: 0.2rem 0; }
.realist-team-member-socials { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.realist-team-member--stacked .realist-team-member-socials,
.realist-team-member--minimal .realist-team-member-socials { justify-content: center; }

/* Honeypot: must stay visually hidden or real users see it and the spam trap inverts. */
.realist-lead-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Feedback visibility is behaviour, not decoration. */
.realist-lead-message { display: none; }
.realist-lead-message.is-error,
.realist-lead-message.is-success { display: block; }

/* ---------- Currency switch ---------- */
/* Behaviour, not decoration: the switch is hidden until its script runs, and a
   theme's `display` on a div or a button must not be able to show it early
   (the WordPress-admin `[hidden]` lesson of 0.48). Specificity 0,2,0. */
.realist-currency-switch[hidden] { display: none; }
.realist-currency-switch { display: flex; align-items: center; justify-content: flex-end; gap: 0.4em; }
.realist-currency-options { display: inline-flex; }
.realist-currency-option,
.realist-currency-info-toggle { cursor: pointer; font: inherit; }

/* The (?) and what it explains. Shown on hover, on KEYBOARD focus (not on the
   focus a mouse click leaves behind, or a second click could never close it),
   and while the script has it open for a click or a tap. Opens leftwards and
   downwards: the switch sits at the right-hand end of the price row. */
.realist-currency-info { position: relative; display: inline-flex; }
.realist-currency-note {
	position: absolute;
	top: calc( 100% + 6px );
	right: 0;
	z-index: 20;
	width: max-content;
	max-width: min( 18rem, 80vw );
	display: none;
}
.realist-currency-info:hover .realist-currency-note,
.realist-currency-info.is-open .realist-currency-note { display: block; }
/* Its own rule: a browser without :has() drops the whole selector list it
   appears in, and would lose hover and click along with it. */
.realist-currency-info:has( .realist-currency-info-toggle:focus-visible ) .realist-currency-note { display: block; }
.realist-price { white-space: nowrap; }

/* ---------- Testimonials (0.60) ----------
 * One quote at a time. Every slide keeps the tallest quote's height and centres
 * its own in it, so the page below never jumps as the quotes change. The arrows
 * sit in a gutter either side of the words rather than over them; a theme moves
 * that gutter with --realist-testimonials-gutter. The dots sit below the quotes,
 * not on top of them as Splide places them for photographs.
 */
.realist-testimonials { position: relative; width: 100%; }
.realist-testimonials .splide__slide {
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	padding: 0 var( --realist-testimonials-gutter, clamp( 2.75rem, 12%, 12.5rem ) );
}
.realist-testimonials .splide__slide .wp-block-quote { margin: 0; max-width: 100%; }
.realist-testimonials .splide__pagination { position: static; padding: 0; margin-top: var( --realist-space-lg, 30px ); }
.realist-testimonials-toggle { position: absolute; right: 0; bottom: 0; }

/* ---------- Featured properties, Carousel style (0.61) ----------
 * One card centred, its neighbours showing either side (Splide's padding, set
 * in frontend-featured-carousel.js), a gutter between cards a theme moves with
 * --realist-featured-gutter. Until Splide mounts — and always in the editor,
 * which never runs it — the cards sit in a row that scrolls, rather than in a
 * column that would read as broken.
 */
.realist-featured-carousel { position: relative; width: 100%; }
.realist-featured-carousel .splide__slide { box-sizing: border-box; padding: 0 var( --realist-featured-gutter, clamp( .75rem, 3vw, 2.8125rem ) ); }
/* One slide per view: Splide leaves a slide as wide as its content then, so a
   card would be as narrow as its text. Each takes the track's full width (the
   track's padding is what shows the neighbours). */
.realist-featured-carousel .splide__slide,
.realist-testimonials .splide__slide { width: 100%; }
.realist-featured-carousel .realist-property-card { height: 100%; }
.realist-featured-carousel:not( .is-initialized ) .splide__list { display: flex; overflow-x: auto; }
.realist-featured-carousel:not( .is-initialized ) .splide__slide { flex: 0 0 80%; }
