/**
 * LocalPro Caching -- async skeletons.
 *
 * Design rule: this stylesheet PAINTS, it does not SIZE.
 *
 * Every skeleton reuses the widget's own class names for structure, so the
 * geometry comes from the widget's own stylesheet. That is what makes the
 * skeleton and the finished markup occupy identical boxes -- and it keeps them
 * identical when someone changes a card's padding six months from now without
 * ever thinking about this file.
 *
 * The only sizing rules here are the text-bar overlays, which sit inside line
 * boxes established by a &nbsp; in the real element.
 */

/* ---------------------------------------------------------------------------
 * Shell
 *
 * display:contents keeps the wrapper out of layout entirely, so wrapping a
 * widget in the async shell cannot change how its outermost element sizes,
 * margins, or participates in a parent grid/flex container.
 * ------------------------------------------------------------------------ */

.lpc-async,
.lpc-async-skeleton,
.lpc-async-content {
	display: contents;
}

/* Themes and page builders love `div { display: block }`-style resets, which
   beat the `hidden` attribute on specificity. Make hiding non-negotiable. */
.lpc-async [hidden],
.lpc-async-content[hidden],
.lpc-async-skeleton[hidden] {
	display: none !important;
}

/* ---------------------------------------------------------------------------
 * Shimmer
 * ------------------------------------------------------------------------ */

.lpc-async-skeleton {
	--lpc-sk-bg: #e7eaee;
	--lpc-sk-hi: #f2f4f7;
	--lpc-sk-radius: 4px;
}

.lpc-sk-fill,
.lpc-sk-text::before {
	background-color: var(--lpc-sk-bg);
	background-image: linear-gradient(
		90deg,
		var(--lpc-sk-bg) 0%,
		var(--lpc-sk-hi) 50%,
		var(--lpc-sk-bg) 100%
	);
	background-size: 200% 100%;
	animation: lpc-sk-shimmer 1.4s ease-in-out infinite;
}

@keyframes lpc-sk-shimmer {
	0%   { background-position: 150% 0; }
	100% { background-position: -50% 0; }
}

/* Respect the OS setting, and stop animating once we have given up fetching --
   a placeholder that shimmers forever reads as "still loading" and it isn't. */
@media ( prefers-reduced-motion: reduce ) {
	.lpc-sk-fill,
	.lpc-sk-text::before {
		animation: none;
	}
}

.lpc-async[data-lpc-state="failed"] .lpc-sk-fill,
.lpc-async[data-lpc-state="failed"] .lpc-sk-text::before {
	animation: none;
	opacity: 0.6;
}

/* ---------------------------------------------------------------------------
 * Primitives
 * ------------------------------------------------------------------------ */

/**
 * A solid block that fills its parent. Use on elements whose size the widget's
 * own CSS already establishes -- image aspect boxes, map containers.
 */
.lpc-sk-fill {
	position: absolute;
	inset: 0;
	border-radius: inherit;
}

/**
 * A text bar.
 *
 * The element carries a &nbsp; so the browser builds a real line box at the
 * element's own font-size and line-height; the bar is painted over it. Height
 * therefore matches real single-line text exactly, without this file knowing
 * anything about the widget's typography.
 *
 * Width comes from --w so one class covers every bar length.
 */
.lpc-sk-text {
	position: relative;
	color: transparent !important;
}

.lpc-sk-text::before {
	content: "";
	position: absolute;
	left: 0;
	top: 50%;
	transform: translateY( -50% );
	width: var( --w, 100% );
	height: 0.72em;
	border-radius: var( --lpc-sk-radius );
}

/* Suppress decoration that would otherwise paint through a blanked element
   (status dots, bullet separators, icons). */
.lpc-sk-text::after {
	display: none !important;
}

/* ---------------------------------------------------------------------------
 * Determinism guard
 *
 * A skeleton can only be shift-free if the real content it stands in for has a
 * predictable line count. Address lines are the one field that genuinely wraps
 * at narrow column widths, so under async we clamp them to the line count the
 * skeleton assumes.
 *
 * Scoped to .lpc-async, so pages still rendering inline are untouched.
 * ------------------------------------------------------------------------ */

.lpc-async .lpe-property-item-street,
.lpc-async .lpe-property-item-location,
.lpc-async .aew-property-item-street,
.lpc-async .aew-property-item-location {
	display: -webkit-box;
	-webkit-line-clamp: 1;
	line-clamp: 1;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* ---------------------------------------------------------------------------
 * Accessibility
 *
 * The skeleton is decorative and already aria-hidden. Announce the real thing
 * once it lands so screen reader users are not left on a silent region.
 * ------------------------------------------------------------------------ */

.lpc-async-content {
	/* aria-busy is toggled by the runtime via data-lpc-state on the shell. */
}

.lpc-async[data-lpc-state="loading"] .lpc-async-content {
	speak: none;
}

/* ---------------------------------------------------------------------------
 * Toolbar placeholder
 *
 * The one place this file DOES size things. The widget's real toolbar classes
 * cannot be reused here, because its Elementor handler binds click/change
 * listeners to .lpe-view-btn and .lpe-sort-select at element_ready -- which
 * fires against the skeleton, before any fragment exists. Binding to nodes
 * that are about to be discarded is how you get a dead sort dropdown.
 *
 * Values mirror .lpe-listings-toolbar and its children. If that CSS changes,
 * this must follow -- the only such coupling in the system, and the reason
 * show_toolbar defaults to false.
 * ------------------------------------------------------------------------ */

.lpc-sk-toolbar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin-bottom: 15px;
	padding: 10px;
	background: #fff;
	border: 1px solid #e5e5e5;
	border-radius: 8px;
}

.lpc-sk-fill-inline {
	display: inline-block;
	background-color: var( --lpc-sk-bg );
	background-image: linear-gradient(
		90deg,
		var( --lpc-sk-bg ) 0%,
		var( --lpc-sk-hi ) 50%,
		var( --lpc-sk-bg ) 100%
	);
	background-size: 200% 100%;
	animation: lpc-sk-shimmer 1.4s ease-in-out infinite;
	border-radius: 6px;
}

/* padding 8px top+bottom + a 14px/1.4 line box = 36px, matching the real
   .lpe-action-btn and .lpe-sort-select. */
.lpc-sk-btn,
.lpc-sk-select {
	height: 36px;
}

.lpc-sk-btn {
	width: 120px;
}

.lpc-sk-select {
	width: 180px;
}

@media ( max-width: 768px ) {
	.lpc-sk-toolbar {
		flex-direction: column;
		align-items: stretch;
		gap: 1rem;
	}

	.lpc-sk-btn,
	.lpc-sk-select {
		width: 100%;
	}
}

@media ( prefers-reduced-motion: reduce ) {
	.lpc-sk-fill-inline {
		animation: none;
	}
}

.lpc-async[data-lpc-state="failed"] .lpc-sk-fill-inline {
	animation: none;
	opacity: 0.6;
}

/* ---------------------------------------------------------------------------
 * Carousel row placeholder
 *
 * Intentionally NOT sized here.
 *
 * The row's flex container and each slide's width are emitted as inline styles
 * by lpc_skeleton_listing_row(), with the percentages computed in PHP. Two
 * reasons: an optimiser that strips "unused" selectors (WP Rocket's Remove
 * Unused CSS) cannot break an inline style, and computing the widths in PHP
 * avoids a calc() that divides by a custom property -- which is invalid at
 * computed-value time if the property is ever missing, silently collapsing
 * every slide to full width.
 * ------------------------------------------------------------------------ */

/* Saved-links rows: the bar stands in for the anchor text, so it needs the
   inline-block box an <a> would have occupied. */
.lpc-sk-link {
	display: inline-block;
	min-width: 40%;
}
