/**
 * PropOffice Property UIs - shared base styles and design tokens.
 *
 * Version-specific styling lives in assets/css/versions/, so a page only ever
 * carries the CSS for the presentation it actually renders.
 *
 * The --ppui-* custom properties are the design system. Every component reads
 * them rather than hardcoding a value, which is what lets an Elementor control
 * or an agency stylesheet rebrand the whole component by setting one property.
 * Nothing below this block should contain a literal colour or size that a
 * client might reasonably want to change.
 */

.propoffice-properties {
	/* Brand */
	--ppui-primary: #1d4ed8;
	--ppui-primary-contrast: #ffffff;
	--ppui-accent: #0f172a;

	/* Surfaces and text */
	--ppui-surface: #ffffff;
	--ppui-border: #e5e7eb;
	--ppui-text: #0f172a;
	--ppui-text-inverse: #ffffff;
	--ppui-muted: #6b7280;
	--ppui-placeholder: #eef1f5;

	/* Overlay */
	--ppui-overlay: #0b1f3a;
	--ppui-overlay-opacity: 0.72;

	/*
	 * Metrics. Square corners by default: the reference composition is
	 * architectural, and rounding softens it. Agencies that want rounded images
	 * set the Corner Radius control.
	 */
	--ppui-gap: 16px;
	--ppui-radius: 0px;
	--ppui-pad: 24px;

	/* Typography scale */
	--ppui-price-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.25rem);
	--ppui-location-size: clamp(0.9375rem, 0.85rem + 0.35vw, 1.0625rem);
	--ppui-title-size: 0.8125rem;
	--ppui-stat-size: 0.9375rem;

	/* Component roles, defaulting to the tokens above */
	--ppui-title-color: rgba(255, 255, 255, 0.82);
	--ppui-price-color: var(--ppui-text-inverse);
	--ppui-location-color: rgba(255, 255, 255, 0.82);
	--ppui-stat-icon: rgba(255, 255, 255, 0.75);
	--ppui-stat-value: var(--ppui-text-inverse);
	--ppui-cta-bg: var(--ppui-primary);
	--ppui-cta-text: var(--ppui-primary-contrast);
	--ppui-badge-bg: var(--ppui-accent);
	--ppui-badge-text: var(--ppui-text-inverse);

	/*
	 * The information panel, as every version wears it.
	 *
	 * How wide a property's information is, what it is padded by, what frames
	 * it and what colour its words are, are decisions about Featured Properties
	 * rather than about any one composition. They are declared here, on the
	 * element every version's wrapper carries, and each version reads them - so
	 * an editor configures the basics once and switching layouts keeps them.
	 *
	 * A composition may still state its own starting point by supplying a
	 * fallback: `var(--ppui-panel-pad, 32px)` is a card that is padded by 32px
	 * until somebody says otherwise, and by whatever they said afterwards.
	 *
	 * --ppui-panel-width is deliberately absent from this block, and that is the
	 * one exception worth explaining. Declaring a token here satisfies every
	 * var() that reads it, so a fallback further down is dead code - which is
	 * what happened to the panel width: all three version stylesheets carried a
	 * starting width of their own and none of them could ever take effect,
	 * because 22.5% was declared here and printed again by the control's own
	 * default. The versions do not agree on how wide a panel starts (Version 1
	 * opens at 40%, Version 2 at 22.5%, Version 3 at 39%), so the token is left
	 * unset and each sheet states its own. The control writes it when an editor
	 * chooses a width, and then it means exactly what they chose.
	 */
	--ppui-panel-text: var(--ppui-text-inverse);
	--ppui-panel-border-width: 0;
	--ppui-panel-border-style: solid;
	--ppui-panel-border-color: transparent;

	/*
	 * Milestone 1 token names, kept so the Version 2-6 scaffolding stylesheets
	 * continue to resolve. New work should use the --ppui-* names above.
	 */
	--propoffice-gap: var(--ppui-gap);
	--propoffice-radius: var(--ppui-radius);
	--propoffice-badge-bg: var(--ppui-badge-bg);
	--propoffice-badge-color: var(--ppui-badge-text);
	--propoffice-muted: var(--ppui-muted);
	--propoffice-surface: var(--ppui-surface);
	--propoffice-border: var(--ppui-border);

	box-sizing: border-box;
}

/*
 * Stated rather than inherited, because an inherit chain is only as strong as
 * its weakest link and this one had a broken one.
 *
 * Swiper's stylesheet sets `.swiper-wrapper { box-sizing: content-box }`. It is
 * one class, exactly like the rule below, and Elementor enqueues it after this
 * file - so it wins on the wrapper, and every element beneath it then inherits
 * content-box: the slides, the frames, and the information card. A card asked
 * for 39% of the photograph painted at 39% *plus* its 80px of padding, which is
 * 47.8% - the width control quietly stopped meaning what it said, but only
 * inside a slider, and only on a page where Swiper is present.
 *
 * Naming the value removes the dependency. The wrapper carries no padding or
 * border of its own, so nothing Swiper needs changes.
 */
.propoffice-properties *,
.propoffice-properties *::before,
.propoffice-properties *::after {
	box-sizing: border-box;
}

.propoffice-properties__empty {
	color: var(--ppui-muted);
	padding: var(--ppui-gap);
	text-align: center;
}

.propoffice-properties__notice {
	background: #fff8e5;
	border: 1px solid #f0c36d;
	border-radius: var(--ppui-radius);
	color: #7a5b00;
	font-size: 0.875rem;
	padding: var(--ppui-gap);
}

.propoffice-property__badge {
	background: var(--ppui-badge-bg);
	border-radius: 999px;
	color: var(--ppui-badge-text);
	display: inline-block;
	font-size: 0.75rem;
	letter-spacing: 0.04em;
	margin: 0;
	padding: 0.35em 0.85em;
	text-transform: uppercase;
}

.propoffice-property__image {
	display: block;
	height: auto;
	max-width: 100%;
}

/**
 * Rendered in place of an image that could not be resolved, so a composition
 * keeps its shape instead of collapsing or showing a broken element.
 */
.propoffice-property__placeholder {
	background-color: var(--ppui-placeholder);
	background-image: linear-gradient(135deg, rgba(15, 23, 42, 0.04) 25%, transparent 25%, transparent 50%, rgba(15, 23, 42, 0.04) 50%, rgba(15, 23, 42, 0.04) 75%, transparent 75%);
	background-size: 16px 16px;
	min-height: 8rem;
}

/* ------------------------------------------------------- Entrance animation */

/**
 * One animation system for every Featured Properties version.
 *
 * The three layers a property is made of - the photograph, the information and
 * the navigation - each enter on their own animation, and each is chosen
 * independently: a background that zooms while the information slides up and
 * the navigation fades is an ordinary configuration rather than a special case.
 *
 * The mechanism is defined once, here, because it belongs to the component
 * rather than to any composition. A version applies it by giving an element one
 * of the three role classes; the keyframes, the tokens, the defaults, the
 * timing and the reduced-motion rule are shared, so two versions cannot end up
 * animating differently for reasons nobody chose.
 *
 * Slide Left is the default for all three, which is what the reference does.
 * They stay independently configurable regardless.
 *
 * The movement is written with the independent `translate` and `scale`
 * properties rather than with `transform`, and that is load-bearing rather than
 * stylistic. The navigation bar is *placed* by a transform - the slider's anchor
 * and its matching translation - so a keyframe that animated `transform` would
 * overwrite that placement and, with fill-mode both, keep it overwritten: the
 * bar was measured 52px below the foot of the hero, exactly its own height, with
 * its translation gone. `translate` and `scale` compose with an element's own
 * transform instead of replacing it, so a layer can animate without knowing how
 * it was positioned.
 */

.propoffice-properties {
	--ppui-anim-media: ppui-slide-left;
	--ppui-anim-panel: ppui-slide-left;
	--ppui-anim-nav: ppui-slide-left;

	--ppui-anim-media-duration: 700ms;
	--ppui-anim-panel-duration: 700ms;
	--ppui-anim-nav-duration: 700ms;

	--ppui-anim-media-delay: 0ms;
	--ppui-anim-panel-delay: 90ms;
	--ppui-anim-nav-delay: 160ms;

	--ppui-anim-easing: cubic-bezier(0.22, 0.61, 0.36, 1);
	--ppui-anim-distance: 48px;
	--ppui-anim-zoom: 1.08;
}

/*
 * The animation runs on the slide that is actually showing.
 *
 * Swiper marks the active slide, so a property animates as it arrives rather
 * than once when the page loads - which is what makes moving between properties
 * a transition rather than a jump. `both` holds the opening frame before the
 * animation starts, so a slide waiting its turn is never briefly visible in its
 * finished position.
 *
 * Outside a slider there is no active slide to wait for and the elements simply
 * animate in, which is the same rule with the requirement dropped.
 */
.propoffice-properties__anim-media,
.propoffice-properties__anim-panel,
.propoffice-properties__anim-nav {
	animation-fill-mode: both;
	animation-timing-function: var(--ppui-anim-easing);
}

.propoffice-properties__anim-media {
	animation-delay: var(--ppui-anim-media-delay);
	animation-duration: var(--ppui-anim-media-duration);
	animation-name: var(--ppui-anim-media);
}

.propoffice-properties__anim-panel {
	animation-delay: var(--ppui-anim-panel-delay);
	animation-duration: var(--ppui-anim-panel-duration);
	animation-name: var(--ppui-anim-panel);
}

.propoffice-properties__anim-nav {
	animation-delay: var(--ppui-anim-nav-delay);
	animation-duration: var(--ppui-anim-nav-duration);
	animation-name: var(--ppui-anim-nav);
}

/*
 * Inside a slider, only the active slide animates, and it animates again each
 * time it becomes active. A slide that is not showing holds its opening frame.
 */
.propoffice-properties .swiper-slide:not(.swiper-slide-active) .propoffice-properties__anim-media,
.propoffice-properties .swiper-slide:not(.swiper-slide-active) .propoffice-properties__anim-panel {
	animation-name: none;
}

/*
 * The card that is leaving.
 *
 * The rule above is what this has to beat. A slide stops being active the
 * instant the change starts, so by the time the outgoing card would animate out
 * it already matches "not active" and has had its animation switched off - it
 * holds its last frame and rides away with the slide. The leaving class is
 * added by slider.js on slideChangeTransitionStart and carries one more class
 * than that rule, so the exit wins for exactly as long as the change lasts.
 *
 * Only the panel leaves. The photograph is carried off by the slider's own
 * movement and animating it as well would be two motions describing one event.
 *
 * The direction comes from the root so that a forward change sends the card the
 * opposite way to a backward one, which is what makes previous read as a
 * reversal rather than as a second identical transition.
 *
 * Duration is the panel's own, so the exit takes as long as the enter and both
 * answer to the same control. The delay is dropped: the enter is deliberately
 * late so a card arrives after its photograph, and a card that left late would
 * still be on screen when its replacement arrived.
 */
.propoffice-properties .swiper-slide.propoffice-properties--leaving .propoffice-properties__anim-panel {
	animation-delay: 0ms;
	animation-duration: var(--ppui-anim-panel-duration);

	/*
	 * Not `both`, which every other animated layer here uses.
	 *
	 * `both` holds the last frame after an animation finishes, and the last
	 * frame of an exit is an invisible card. Any path that leaves the class
	 * behind - a change interrupted, an event that never arrives because the
	 * tab was in the background - would then strand a card at zero opacity for
	 * as long as the page lives. `backwards` fills only the waiting period
	 * before the animation starts, so a card that finishes leaving returns to
	 * its ordinary appearance instead of disappearing permanently.
	 *
	 * Nothing is visible at that moment either way: the slide it belongs to has
	 * already been carried off screen.
	 */
	animation-fill-mode: backwards;
	animation-name: ppui-exit-left;
}

.propoffice-pui-slider[data-propoffice-direction="prev"] .swiper-slide.propoffice-properties--leaving .propoffice-properties__anim-panel {
	animation-name: ppui-exit-right;
}

/*
 * Both ends are written out. A keyframe set with only a `to` leaves the browser
 * to infer the opening frame from whatever the element currently computes to,
 * which during an interrupted change is a card already part-way through
 * another animation - the exit then starts from wherever that had reached.
 */
@keyframes ppui-exit-left {
	from {
		opacity: 1;
		translate: 0 0;
	}

	to {
		opacity: 0;
		translate: calc(var(--ppui-anim-distance) * -1) 0;
	}
}

@keyframes ppui-exit-right {
	from {
		opacity: 1;
		translate: 0 0;
	}

	to {
		opacity: 0;
		translate: var(--ppui-anim-distance) 0;
	}
}

@keyframes ppui-slide-left {
	from {
		opacity: 0;
		translate: calc(var(--ppui-anim-distance) * -1) 0;
	}

	to {
		opacity: 1;
		scale: none;
		translate: none;
	}
}

@keyframes ppui-slide-right {
	from {
		opacity: 0;
		translate: var(--ppui-anim-distance) 0;
	}

	to {
		opacity: 1;
		scale: none;
		translate: none;
	}
}

@keyframes ppui-slide-up {
	from {
		opacity: 0;
		translate: 0 var(--ppui-anim-distance);
	}

	to {
		opacity: 1;
		scale: none;
		translate: none;
	}
}

@keyframes ppui-slide-down {
	from {
		opacity: 0;
		translate: 0 calc(var(--ppui-anim-distance) * -1);
	}

	to {
		opacity: 1;
		scale: none;
		translate: none;
	}
}

@keyframes ppui-fade {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

@keyframes ppui-zoom-in {
	from {
		opacity: 0;
		scale: calc(2 - var(--ppui-anim-zoom));
	}

	to {
		opacity: 1;
		scale: none;
		translate: none;
	}
}

@keyframes ppui-zoom-out {
	from {
		opacity: 0;
		scale: var(--ppui-anim-zoom);
	}

	to {
		opacity: 1;
		scale: none;
		translate: none;
	}
}

/*
 * Anyone who asked the system to reduce motion gets the component without any
 * of it. The elements are still shown - `none` would leave a panel holding an
 * opening frame it never animates out of.
 */
@media (prefers-reduced-motion: reduce) {
	.propoffice-properties__anim-media,
	.propoffice-properties__anim-panel,
	.propoffice-properties__anim-nav,
	.propoffice-properties .swiper-slide:not(.swiper-slide-active) .propoffice-properties__anim-media,
	.propoffice-properties .swiper-slide:not(.swiper-slide-active) .propoffice-properties__anim-panel,
	.propoffice-properties .swiper-slide.propoffice-properties--leaving .propoffice-properties__anim-panel,
	.propoffice-pui-slider[data-propoffice-direction="prev"] .swiper-slide.propoffice-properties--leaving .propoffice-properties__anim-panel {
		animation-name: ppui-fade;
		animation-duration: 1ms;
	}
}
