/**
 * 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-count,
.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;
}

/**
 * Containing block for .lpc-sk-fill, and a fallback aspect box.
 *
 * The widget's own CSS normally supplies both, but it is almost always scoped
 * under the widget's root wrapper -- so if a skeleton renders without that
 * ancestor, the fill has no positioned parent and stretches to whatever is
 * positioned further up, covering the entire widget area. These are the floor;
 * the widget's rules are more specific and win wherever they do apply.
 */
.lpc-sk-media,
.lpc-sk-aspect {
	position: relative;
}

.lpc-sk-aspect {
	padding-bottom: 65%;
}

/* Cards-style skins overlay the content on the image. Anchored to
   .lpc-sk-media (its parent), matching where the widget's own rule anchors it. */
.lpc-sk-overlay {
	position: absolute;
	left: 0;
	bottom: 0;
	width: 100%;
	padding: 0 10px 10px;
}

/**
 * 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 );
}

/* An inline element has no width of its own -- its box is just the &nbsp; --
   so the percentage above resolves against a few pixels and the bar vanishes.
   Give inline bars a real box sized by --w and let the bar fill it, so --w
   means the same thing whether the caller asked for a <div> or a <span>. */
span.lpc-sk-text {
	display: inline-block;
	width: var( --w, 100% );
	vertical-align: middle;
}

span.lpc-sk-text::before {
	width: 100%;
}

/* 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.
 * ------------------------------------------------------------------------ */

/* The listings-count badge: a pill matching the real one (min-width 18px,
   border-radius 500px), not a percentage-width text bar. */
.lpc-sk-count {
	display: inline-block;
	width: 18px;
	height: 18px;
	border-radius: 500px;
	vertical-align: middle;
}

/* Saved-links rows: the bar stands in for the anchor text. The inline-block
   box now comes from span.lpc-sk-text above; min-width used to be here to stop
   the bar collapsing, but it made --w compound against it (72% of a 40% box),
   so every row came out near-identical. --w is authoritative now. */
