/**
 * RJ4 Medspa — Experiential motion layer (2026-07-15)
 *
 * A calm, cinematic motion system layered over the existing site: smooth
 * scroll (Lenis), organic scroll reveals, parallax depth, drifting mist,
 * micro-interactions, and soft page transitions. Motion supports emotion,
 * never noise.
 *
 * Degradation contract:
 *   - No JS  → `.reveal` never hides (hidden state is scoped to html.js).
 *   - Reduced motion → all transforms/transitions collapse (block at end).
 *   - Everything here is additive; removing this file leaves a working site.
 */

/* =========================================================
   Smooth scroll (Lenis)
   ========================================================= */

html.lenis,
html.lenis body {
	height: auto;
}

.lenis.lenis-smooth {
	scroll-behavior: auto !important;
}

.lenis.lenis-smooth [data-lenis-prevent] {
	overscroll-behavior: contain;
}

.lenis.lenis-stopped {
	overflow: hidden;
}

/* =========================================================
   Reveal system — organic, slow, breathing
   Hidden state scoped to html.js so a no-JS visitor sees everything.
   ========================================================= */

html.js .reveal {
	opacity: 0;
	transform: translate3d(0, 34px, 0);
	filter: blur(6px);
	transition:
		opacity 1.15s var(--ease-drift),
		transform 1.15s var(--ease-drift),
		filter 1.15s var(--ease-drift);
	transition-delay: calc(var(--i, 0) * 90ms);
	will-change: opacity, transform;
}

html.js .reveal.is-inview {
	opacity: 1;
	transform: none;
	filter: none;
}

/* Heading rise — a slower, taller settle than body reveals, giving titles
   their own cadence. (Deliberately opacity+transform, not clip-path: a
   fully-clipped box reports zero IntersectionObserver area and would never
   trigger — the reveal would deadlock hidden.) */
html.js [data-reveal='mask'] {
	opacity: 0;
	transform: translate3d(0, 26px, 0);
	transition:
		opacity 1.3s var(--ease-drift),
		transform 1.3s var(--ease-drift);
	transition-delay: calc(var(--i, 0) * 90ms);
	will-change: opacity, transform;
}

html.js [data-reveal='mask'].is-inview {
	opacity: 1;
	transform: none;
}

/* Fade-only variant — frame fades while its child parallaxes independently */
html.js [data-reveal='fade'] {
	opacity: 0;
	transition: opacity 1.4s var(--ease-drift);
}

html.js [data-reveal='fade'].is-inview {
	opacity: 1;
}

/* Watchdog: if motion.js never initializes, reveal everything (set in <head>) */
html.motion-fallback .reveal,
html.motion-fallback [data-reveal] {
	opacity: 1 !important;
	transform: none !important;
	filter: none !important;
	clip-path: none !important;
}

/* Images breathe into view — settle from a soft over-scale */
html.js [data-reveal='image'] {
	opacity: 0;
	transition:
		opacity 1.4s var(--ease-drift),
		transform 1.6s var(--ease-drift);
	will-change: opacity, transform;
}

html.js [data-reveal='image'] > img,
html.js img[data-reveal='image'] {
	transform: scale(1.12);
	transition: transform 1.8s var(--ease-drift);
	will-change: transform;
}

html.js [data-reveal='image'].is-inview {
	opacity: 1;
}

html.js [data-reveal='image'].is-inview > img,
html.js img[data-reveal='image'].is-inview {
	transform: scale(1);
}

/* Word-drift — hero + section titles float their words up in sequence */
.rj4-word {
	display: inline-block;
	white-space: pre;
}

html.js [data-words] .rj4-word {
	opacity: 0;
	transform: translate3d(0, 0.5em, 0);
	transition:
		opacity 0.9s var(--ease-drift),
		transform 1s var(--ease-drift);
	transition-delay: calc(var(--wi, 0) * 55ms);
}

html.js [data-words].is-inview .rj4-word {
	opacity: 1;
	transform: none;
}

/* =========================================================
   Parallax depth — JS sets --py (px). Subtle, always.
   ========================================================= */

html.js [data-parallax] {
	will-change: transform;
	transform: translate3d(0, var(--py, 0), 0);
}

/* Editorial photos: own the transform (baseline over-scale for headroom) so
   parallax translate never fights the frame's fade or a hover scale. */
html.js .editorial__media img[data-parallax] {
	transform: translate3d(0, var(--py, 0), 0) scale(1.12);
	transition: none;
}

/* Photo bands: drift the background instead of the box (no transform conflict) */
html.js [data-parallax='bg'] {
	background-position: center calc(50% + var(--bgy, 0px));
}

/* =========================================================
   Drifting mist / steam — pure CSS, evokes warmth without imagery
   JS injects .mist layers into designated atmospheric sections.
   ========================================================= */

/* Mist hosts must clip — the layers bleed past their edges by design. The
   hero already clips; the benefits band must too, or the bleed adds page
   horizontal overflow. */
.hero-slider,
.benefits-band {
	overflow: hidden;
}

.mist {
	position: absolute;
	inset: -20% -10%;
	z-index: 1;
	pointer-events: none;
	opacity: 0;
	transition: opacity 2s var(--ease-default);
	background:
		radial-gradient(38% 46% at 22% 34%, rgba(255, 255, 255, 0.10) 0%, rgba(255, 255, 255, 0) 60%),
		radial-gradient(44% 40% at 74% 62%, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 62%);
	filter: blur(22px);
	/* Promote to its own layer: the blur rasterizes once, then only the
	   transform composites — keeps the perpetual drift cheap. */
	will-change: transform;
	animation: rj4-mist-drift 34s var(--ease-default) infinite alternate;
}

.mist.is-on {
	opacity: 1;
}

.mist--2 {
	animation-duration: 46s;
	animation-direction: alternate-reverse;
	background:
		radial-gradient(40% 44% at 60% 24%, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0) 60%),
		radial-gradient(50% 46% at 30% 74%, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0) 64%);
}

@keyframes rj4-mist-drift {
	0%   { transform: translate3d(-3%, 2%, 0) scale(1); }
	50%  { transform: translate3d(2%, -3%, 0) scale(1.08); }
	100% { transform: translate3d(4%, 3%, 0) scale(1.02); }
}

/* Ambient hero video sits as the full atmospheric background */
.hero-video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	z-index: 1;
	opacity: 1;
	pointer-events: none;
}

/* Layering: photo(0) < video(1) < glaze(2) < mist(3) < content(4) */
.hero-slider::before {
	z-index: 2;
}

.hero-slider .mist {
	z-index: 3;
}

/* Hero content sits above video, glaze, and mist */
.hero-slider__content {
	position: relative;
	z-index: 10;
	opacity: 1;
	visibility: visible;
}

.hero-slider__content > * {
	opacity: 1;
	visibility: visible;
}

/* Optional gentle entrance transition if hero-ready is present */
html.js.hero-ready .hero-slider__content > * {
	transition: opacity 0.8s var(--ease-drift), transform 0.8s var(--ease-drift);
}

/* Scroll cue fades once the visitor starts to move */
html.js .hero-slider__cue {
	transition: opacity 0.8s var(--ease-default);
}

html.js.has-scrolled .hero-slider__cue {
	opacity: 0;
}

/* =========================================================
   Background evolution — a faint warm wash deepens mid-journey
   JS sets --sp (0→1 scroll progress) on <html>.
   ========================================================= */

html.js body::before {
	content: '';
	position: fixed;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background: radial-gradient(120% 80% at 50% 0%, rgba(127, 141, 106, 0.06) 0%, rgba(127, 141, 106, 0) 55%);
	opacity: calc(0.35 + (var(--sp, 0) * 0.65));
	transition: opacity 0.6s linear;
}

/* Keep real content above the wash */
.site-header,
.topbar,
main,
.site-footer {
	position: relative;
	z-index: 1;
}

.site-header {
	z-index: 1000;
}

/* =========================================================
   Micro-interactions
   ========================================================= */

/* Buttons: soft lift + a slow sheen sweep */
.btn {
	position: relative;
	overflow: hidden;
	transition:
		background-color var(--duration-fast) var(--ease-default),
		color var(--duration-fast) var(--ease-default),
		border-color var(--duration-fast) var(--ease-default),
		transform 0.5s var(--ease-drift),
		box-shadow 0.6s var(--ease-drift);
}

.btn::after {
	content: '';
	position: absolute;
	top: 0;
	left: -60%;
	width: 40%;
	height: 100%;
	background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.22), transparent);
	transform: skewX(-18deg);
	opacity: 0;
	transition: none;
	pointer-events: none;
}

.btn:hover {
	transform: translateY(-2px);
	box-shadow: 0 14px 30px rgba(47, 52, 43, 0.16);
}

.btn:hover::after {
	animation: rj4-sheen 0.9s var(--ease-default);
}

.btn:active {
	transform: translateY(0);
}

@keyframes rj4-sheen {
	0%   { left: -60%; opacity: 0; }
	20%  { opacity: 1; }
	100% { left: 130%; opacity: 0; }
}

/* Secondary/ghost buttons lift without a shadow flare */
.btn--secondary:hover,
.btn--on-dark:hover {
	box-shadow: 0 10px 24px rgba(47, 52, 43, 0.10);
}

/* Text link — refine the underline wipe already defined in main.css */
.text-link::after {
	transition: transform 0.55s var(--ease-drift);
}

/* Inputs: soft focus ring + lifted label */
.form-field input,
.form-field select,
.form-field textarea {
	transition:
		border-color var(--duration-default) var(--ease-default),
		box-shadow var(--duration-default) var(--ease-default);
}

.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
	box-shadow: 0 0 0 4px rgba(127, 141, 106, 0.14);
}

.form-field:focus-within label {
	color: var(--color-deep-olive);
	transition: color var(--duration-default) var(--ease-default);
}

/* Menu rows: hover draws the eye with a gentle indent + warm hairline */
.menu-row {
	transition:
		border-color var(--duration-default) var(--ease-default),
		background-color var(--duration-default) var(--ease-default);
}

@media (hover: hover) {
	.menu-row {
		padding-inline: var(--space-sm);
		margin-inline: calc(-1 * var(--space-sm));
	}

	.menu-row:hover {
		background-color: rgba(127, 141, 106, 0.05);
		border-top-color: var(--color-sage-green);
	}

	.menu-row__title a {
		background-image: linear-gradient(var(--color-deep-olive), var(--color-deep-olive));
		background-size: 0% 1px;
		background-position: 0 100%;
		background-repeat: no-repeat;
		transition: background-size 0.5s var(--ease-drift), color var(--duration-fast) var(--ease-default);
	}

	.menu-row:hover .menu-row__title a {
		background-size: 100% 1px;
	}
}

/* Nav links: an underline indicator that grows from the left */
@media (min-width: 1024px) {
	.site-nav > ul > li > a {
		position: relative;
	}

	.site-nav > ul > li > a::after {
		content: '';
		position: absolute;
		left: 2px;
		right: 2px;
		bottom: 0;
		height: 1px;
		background: currentColor;
		transform: scaleX(0);
		transform-origin: left;
		transition: transform 0.45s var(--ease-drift);
	}

	.site-nav > ul > li > a:hover::after,
	.site-nav > ul > li.current-menu-item > a::after,
	.site-nav > ul > li.current-menu-ancestor > a::after {
		transform: scaleX(1);
	}

	/* the dropdown carets already own ::after — don't double up */
	.site-nav .menu-item-has-children > a::after {
		content: none;
	}
}

/* Category tiles + gallery: deepen the existing zoom into a slow settle */
.category-tile img,
.gallery-grid__item img,
.editorial__media img,
.service-card__media img,
.package-card__media img {
	transition: transform 1.4s var(--ease-drift);
}

/* =========================================================
   Page transition veil — soft porcelain wipe between pages
   ========================================================= */

.page-veil {
	position: fixed;
	inset: 0;
	z-index: 3000;
	background: var(--color-porcelain);
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transition: opacity 0.6s var(--ease-drift), visibility 0s linear 0.6s;
}

.page-veil__mark {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%) translateY(8px);
	font-family: var(--font-serif);
	font-size: 1.5rem;
	letter-spacing: 0.02em;
	color: var(--color-deep-olive);
	opacity: 0;
	transition: opacity 0.6s var(--ease-drift), transform 0.6s var(--ease-drift);
}

/* Leaving: veil closes in */
html.is-leaving .page-veil {
	opacity: 1;
	visibility: visible;
	pointer-events: all;
	transition: opacity 0.55s var(--ease-drift);
}

html.is-leaving .page-veil__mark {
	opacity: 0.85;
	transform: translate(-50%, -50%);
}

/* Arriving: a brief veil that lifts on load */
html.is-arriving .page-veil {
	opacity: 1;
	visibility: visible;
}

/* =========================================================
   Reduced motion — collapse everything to instant + static
   ========================================================= */

@media (prefers-reduced-motion: reduce) {
	html.js .reveal,
	html.js [data-reveal],
	html.js [data-words] .rj4-word,
	html.js .hero-slider__content--first > * {
		opacity: 1 !important;
		transform: none !important;
		filter: none !important;
		clip-path: none !important;
		transition: none !important;
	}

	html.js [data-reveal='image'] > img,
	html.js img[data-reveal='image'] {
		transform: none !important;
	}

	.mist {
		display: none;
	}

	html.js body::before {
		display: none;
	}

	.btn::after {
		display: none;
	}

	.page-veil {
		display: none;
	}

	html.js [data-parallax] {
		transform: none !important;
	}

	html.js [data-parallax='bg'] {
		background-position: center !important;
	}
}
