/* ==========================================================================
   BlinkShift — Motion
   --------------------------------------------------------------------------
   RULE: CSS never hides content.

   Earlier this file set opacity:0 on reveal targets and relied on JavaScript
   to bring them back. When a tween failed to complete, the copy simply never
   appeared. Now the start state is applied by GSAP itself, only once GSAP is
   confirmed running, and a watchdog in <head> force-reveals everything if the
   motion layer has not reported ready. Worst case: no animation. Never a
   blank page.
   ========================================================================== */

/* The pre-paint hold. Added by an inline script in <head> and removed either
   by the motion layer or by its watchdog — never left on. */
.bs-prehold [data-reveal],
.bs-prehold [data-split],
.bs-prehold [data-clip] {
	opacity: 0;
}

/* Watchdog release: everything visible, no exceptions. */
.bs-motion-fallback [data-reveal],
.bs-motion-fallback [data-split],
.bs-motion-fallback [data-clip],
.bs-motion-fallback .bs-split-line > * {
	opacity: 1 !important;
	visibility: visible !important;
	transform: none !important;
	clip-path: none !important;
}

[data-reveal],
[data-clip] {
	will-change: auto;
}

.is-revealed {
	opacity: 1;
	transform: none;
}

/* --- split-text line masking -------------------------------------------- */

/* Applied only after SplitText has successfully wrapped the lines. The extra
   padding stops descenders being sheared off by the mask. */
.bs-split-line {
	display: block;
	overflow: hidden;
	padding-block: 0.14em;
	margin-block: -0.14em;
}

.bs-split-line > span {
	display: block;
	will-change: transform;
}

/* --- clip reveal for images --------------------------------------------- */

[data-clip] {
	overflow: hidden;
}

/* --- counters ----------------------------------------------------------- */

.bs-counter {
	font-variant-numeric: tabular-nums;
}

/* --- parallax ----------------------------------------------------------- */

[data-parallax] {
	will-change: transform;
}

/* --- hover choreography -------------------------------------------------- */

.bs-lift {
	transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}

.bs-lift:hover {
	transform: translateY(-4px);
	box-shadow: var(--shadow-md);
}

.bs-zoom {
	overflow: hidden;
}

.bs-zoom img {
	transition: transform 900ms var(--ease-out), filter 600ms var(--ease-out);
}

.bs-zoom:hover img,
a:hover .bs-zoom img,
.bs-service:hover .bs-zoom img {
	transform: scale(1.05);
}

/* Images are treated: high-contrast greyscale that warms to full tone on
   hover. Keeps a monochrome brand coherent while photography stays alive. */
.bs-treat img {
	/* filter: grayscale(1) contrast(1.08); */
	transition: filter 700ms var(--ease-out), transform 900ms var(--ease-out);
}

.bs-treat:hover img,
a:hover .bs-treat img {
	filter: grayscale(0.55) contrast(1.02);
}

/* ==========================================================================
   REDUCED MOTION
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}

	*,
	*::before,
	*::after {
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
		scroll-behavior: auto !important;
	}

	.bs-prehold [data-reveal],
	.bs-prehold [data-split],
	.bs-prehold [data-clip],
	[data-reveal],
	[data-split],
	[data-clip] {
		opacity: 1 !important;
		visibility: visible !important;
		transform: none !important;
		clip-path: none !important;
	}

	.bs-marquee__track {
		animation: none !important;
	}

	.bs-treat img { filter: grayscale(1) contrast(1.05); }
}
