/* =============================================================================
   Client Says V2 — testimonial section
   Markup:  template-parts/client-says-v2.php
   Script:  assets/js/client-says-v2.js  (slider mode only)

   Namespaced to .ldv-tst throughout. Swiper 11 classes are only ever styled as
   descendants of it, so the theme's five other carousels are untouched.

   Two modes off one stylesheet:
     .ldv-tst--slider   Swiper, arrows + dots, contained to .max-con
     .ldv-tst--marquee  CSS animation, full-bleed, no script

   Contents
     1. Tokens
     2. Layout      — section, heading, stage, slider + marquee containers
     3. Components  — card, quote, footer, arrows, dots
     4. State       — entrance stagger
     5. Animations
     6. Media queries  (one block per breakpoint, widest first)
   ========================================================================== */


/* -----------------------------------------------------------------------------
   1. Tokens + section shell
   -------------------------------------------------------------------------- */

.ldv-tst {
	--ldv-tst-ink: #f2f3f5;
	--ldv-tst-muted: #9aa0a8;
	--ldv-tst-ease: cubic-bezier(0.25, 0.8, 0.25, 1);
	--ldv-tst-dur: 0.4s;
	--ldv-tst-dur-in: 0.6s;
	/* JS copies the visible card's --acc here on every slide change */
	--ldv-tst-dot-acc: 255, 126, 61;

	position: relative;
	isolation: isolate;
	overflow: hidden;
	padding: 110px 0 120px;
	background:
		radial-gradient(circle at top right, rgba(255, 126, 61, 0.18), transparent 45%),
		radial-gradient(circle at bottom left, rgba(120, 76, 255, 0.15), transparent 40%),
		#101113;
	color: var(--ldv-tst-ink);
}

/* very subtle grain so the large flat areas do not band */
.ldv-tst::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
	background-size: 160px 160px;
	opacity: 0.035;
}

/* 2.2 Heading — mirrors the theme's `.clientSays .main` treatment
       (responsive.css:724-746) so both sections share one heading language.
       Its scale steps use the theme's own 1600/1440/1200/991/767 breakpoints. */

.ldv-tst__head {
	margin-bottom: 40px;
	text-align: center;
}

.ldv-tst__title {
	margin: 0 0 20px;
	color: #fff;
	font-size: 46px;
	font-weight: 500;
	line-height: 1.2;
}

.ldv-tst__title span {
	position: relative;
	z-index: 1;
	font-family: "Scotch Text Comp Sm It", serif;
	font-size: 58px;
}

.ldv-tst__title span::before {
	content: "";
	position: absolute;
	bottom: 8px;
	z-index: -1;
	width: 100%;
	border-bottom: 7px solid #e3725d;
}

.ldv-tst__intro {
	width: 60%;
	margin: auto;
}

.ldv-tst__intro,
.ldv-tst__intro p {
	margin: 0;
	color: #fff;
	font-size: 20px;
	font-weight: 500;
	line-height: 32px;
	margin :auto !important
}

.ldv-tst__intro p + p {
	margin-top: 12px;
}

/* 2.3 Stage — arrows flank the slider, dots sit underneath */

.ldv-tst__stage {
	display: grid;
	grid-template-columns: 60px minmax(0, 1fr) 60px;
	grid-template-areas:
		"prev slider next"
		".    dots   .";
	align-items: center;
	column-gap: 30px;
}

/* 2.4 Slider mode */

.ldv-tst__swiper {
	/* breathing room so the cards' ambient glow and shadow are not clipped */
	grid-area: slider;
	margin: -34px -8px -44px;
	padding: 34px 8px 44px;
}

.ldv-tst .swiper-wrapper {
	transition-timing-function: var(--ldv-tst-ease);
}

.ldv-tst__slide {
	display: flex;
	height: auto;
}

/* 2.5 Marquee mode — the track holds the cards twice and slides exactly -50%,
       so the loop is seamless and hovering only flips animation-play-state.
       Breaks out of .max-con to run edge to edge; the section's own
       `overflow: hidden` clips it, so no horizontal page scroll appears. */

.ldv-tst--marquee .ldv-tst__stage {
	display: block;
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
}

.ldv-tst__marquee {
	overflow: hidden;
	margin: -30px 0 -40px;
	padding: 30px 0 40px;
}

/* No `gap` and no horizontal padding here: either would leave the -50%
   translate half a gutter short of the seam. */
.ldv-tst__track {
	display: flex;
	width: max-content;
	will-change: transform;
	animation: ldv-tst-drift 70s linear infinite;
}

/* Hover pauses the drift. Note this is the only pause affordance: the section
   holds no focusable elements, so :focus-within never fires today and keyboard
   and touch users cannot stop the animation. That is a known WCAG 2.2.2
   (Pause, Stop, Hide) Level A gap, accepted deliberately rather than missed.
   Adding a pause button, or making the cards focusable, would close it.
   :focus-within is kept so the pause works the moment anything focusable — a
   link in a testimonial, say — is added to a card. */
.ldv-tst__marquee:hover .ldv-tst__track,
.ldv-tst__marquee:focus-within .ldv-tst__track {
	animation-play-state: paused;
}

/* The track is full-bleed, so a card's advance (width + margin) is exactly
   100vw / columns. Solving for width keeps the intended number of cards
   filling the viewport — only the two variables change per breakpoint. */
.ldv-tst__item {
	--ldv-tst-cols: 3;
	--ldv-tst-gap: 32px;

	display: flex;
	flex: 0 0 auto;
	width: calc(100vw / var(--ldv-tst-cols) - var(--ldv-tst-gap));
	margin-right: var(--ldv-tst-gap);
}


/* -----------------------------------------------------------------------------
   3. Components
   -------------------------------------------------------------------------- */

/* 3.1 Card — every accent tint reads from --acc, which the template writes
       per row from the ACF colour picker. This is the fallback. */

.ldv-tst__card {
	--acc: 255, 126, 61;

	position: relative;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	width: 100%;
	min-height: 500px;
	padding: 36px;
	border-radius: 24px;
	background:
		radial-gradient(120% 90% at 100% 0%, rgba(var(--acc), 0.16), transparent 60%),
		linear-gradient(160deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.012)),
		#141518;
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.06),
		0 30px 60px -30px rgba(0, 0, 0, 0.85),
		0 0 70px -30px rgba(var(--acc), 0.28);
	-webkit-backdrop-filter: blur(6px) saturate(120%);
	backdrop-filter: blur(6px) saturate(120%);
}

/* soft corner bloom, layered over the base gradient */
.ldv-tst__card::after {
	content: "";
	position: absolute;
	top: -30%;
	right: -20%;
	width: 62%;
	padding-bottom: 62%;
	border-radius: 50%;
	pointer-events: none;
	background: radial-gradient(circle, rgba(var(--acc), 0.22), transparent 68%);
	opacity: 0.85;
	filter: blur(28px);
}

/* 3.2 Card top — quote mark and brandmark */

.ldv-tst__card-top {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 24px;
	margin-bottom: 26px;
}

.ldv-tst__mark {
	display: block;
	flex: 0 0 auto;
	width: 58px;
	color: rgb(var(--acc));
	filter: drop-shadow(0 0 18px rgba(var(--acc), 0.45));
}

.ldv-tst__mark svg {
	display: block;
	width: 100%;
	height: auto;
}

/* the brandmark is either an uploaded logo or a typographic company lockup */
.ldv-tst__brandmark {
	flex: 0 1 auto;
	opacity: 0.78;
	transition:
		opacity var(--ldv-tst-dur) var(--ldv-tst-ease),
		transform var(--ldv-tst-dur) var(--ldv-tst-ease);
}

/* width/height attributes are emitted for CLS; these keep the box fluid so the
   intrinsic size never overrides the caps below */
.ldv-tst__logo {
	display: block;
	width: auto;
	height: auto;
	max-width: 180px;
	max-height: 88px;
	object-fit: contain;
}

.ldv-tst__brand {
	display: block;
	padding-top: 6px;
	text-align: right;
	line-height: 1;
}

.ldv-tst__brand strong {
	display: block;
	color: rgb(var(--acc));
	font-size: 30px;
	font-weight: 700;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	text-shadow: 0 0 26px rgba(var(--acc), 0.45);
}

.ldv-tst__brand em {
	display: block;
	margin-top: 8px;
	color: rgba(255, 255, 255, 0.6);
	font-size: 12px;
	font-style: normal;
	font-weight: 500;
	letter-spacing: 0.4em;
	text-indent: 0.4em;
	text-transform: uppercase;
}

/* 3.3 Quote */

.ldv-tst__quote {
	flex: 1 1 auto;
	max-width: 30ch;
	margin: 0 0 30px;
	padding: 0;
	border: 0;
	color: #e7e9ec;
	font-size: 18px;
	font-style: italic;
	font-weight: 400;
	line-height: 1.8;
}

.ldv-tst__quote span {
	color: rgb(var(--acc));
	font-style: italic;
	font-weight: 700;
}

.ldv-tst__rule {
	height: 1px;
	margin-bottom: 26px;
	background: linear-gradient(90deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.02) 70%, transparent);
}

/* 3.4 Footer — person and review source */

.ldv-tst__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 24px;
	margin-top: auto;
}

.ldv-tst__person {
	display: flex;
	align-items: center;
	gap: 18px;
	min-width: 0;
}

.ldv-tst__avatar {
	flex: 0 0 auto;
	width: 72px;
	height: 72px;
	border-radius: 50%;
	object-fit: cover;
	box-shadow:
		0 0 0 1px rgba(255, 255, 255, 0.1),
		0 10px 24px -12px rgba(0, 0, 0, 0.9);
}

.ldv-tst__identity {
	min-width: 0;
}

.ldv-tst__name {
	margin: 0 0 4px;
	color: #fff;
	font-size: 26px;
	font-weight: 700;
	line-height: 1.2;
}

.ldv-tst__role {
	margin: 0;
	color: #fff;
	font-size: 16px;
	line-height: 1.4;
}

.ldv-tst__source {
	display: flex;
	flex: 0 0 auto;
	flex-direction: column;
	align-items: flex-end;
	gap: 8px;
	text-align: right;
}

/* editor-supplied review-platform logo */
.ldv-tst__platform {
	display: block;
	width: auto;
	height: auto;
	max-width: 150px;
	max-height: 28px;
	object-fit: contain;
}

.ldv-tst__score {
	display: flex;
	align-items: center;
	gap: 8px;
}

.ldv-tst__rating {
	color: #e7e9ec;
	font-size: 16px;
	font-weight: 600;
}

.ldv-tst__stars {
	display: inline-flex;
	align-items: center;
	gap: 2px;
}

.ldv-tst__star {
	display: block;
	width: 15px;
	height: 15px;
}

.ldv-tst__reviews {
	color: var(--ldv-tst-muted);
	font-size: 13px;
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

/* 3.5 Arrows */

.ldv-tst__nav {
	/* the swiper's negative outer margins overhang the arrow and dot rows —
	   both controls must stack above it or their clicks get swallowed */
	position: relative;
	z-index: 2;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 56px;
	height: 56px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: #d8dade;
	cursor: pointer;
	box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.12);
	transition:
		transform var(--ldv-tst-dur) var(--ldv-tst-ease),
		color var(--ldv-tst-dur) var(--ldv-tst-ease),
		box-shadow var(--ldv-tst-dur) var(--ldv-tst-ease);
}

.ldv-tst__nav--prev {
	grid-area: prev;
	justify-self: start;
}

.ldv-tst__nav--next {
	grid-area: next;
	justify-self: end;
}

.ldv-tst__nav svg {
	width: 22px;
	height: 22px;
}

.ldv-tst__nav:hover,
.ldv-tst__nav:focus-visible {
	color: #fff;
}

.ldv-tst__nav:hover {
	transform: scale(1.08);
	box-shadow:
		0 0 0 1px rgba(var(--ldv-tst-dot-acc), 0.55),
		0 0 26px -4px rgba(var(--ldv-tst-dot-acc), 0.45);
}

.ldv-tst__nav:focus-visible {
	outline: none;
	box-shadow:
		0 0 0 2px rgba(var(--ldv-tst-dot-acc), 0.9),
		0 0 26px -4px rgba(var(--ldv-tst-dot-acc), 0.45);
}

/* only reachable if loop is ever switched off in the init */
.ldv-tst__nav.swiper-button-disabled {
	opacity: 0.35;
	cursor: default;
}

/* 3.6 Dots — the active dot borrows the visible card's accent */

.ldv-tst__dots {
	position: relative;
	z-index: 2;
	grid-area: dots;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	margin-top: 38px;
	line-height: 0;
}

.ldv-tst__dots .swiper-pagination-bullet {
	width: 9px;
	height: 9px;
	margin: 0;
	border-radius: 999px;
	background: rgba(255, 255, 255, 0.22);
	opacity: 1;
	transition:
		width var(--ldv-tst-dur) var(--ldv-tst-ease),
		background-color var(--ldv-tst-dur) var(--ldv-tst-ease),
		box-shadow var(--ldv-tst-dur) var(--ldv-tst-ease);
}

.ldv-tst__dots .swiper-pagination-bullet:hover {
	background: rgba(255, 255, 255, 0.4);
}

.ldv-tst__dots .swiper-pagination-bullet-active {
	width: 30px;
	background: rgb(var(--ldv-tst-dot-acc));
	box-shadow: 0 0 16px -2px rgba(var(--ldv-tst-dot-acc), 0.7);
}


/* -----------------------------------------------------------------------------
   4. State — entrance stagger, released once Swiper marks the slide visible
   -------------------------------------------------------------------------- */

.ldv-tst__mark,
.ldv-tst__brandmark,
.ldv-tst__quote,
.ldv-tst__rule,
.ldv-tst__person,
.ldv-tst__source {
	opacity: 0;
	transform: translateY(14px);
}

/* .ldv-tst__brandmark is absent here on purpose: it carries its own shorter
   transition from 3.2, which doubles as its entrance. */
.ldv-tst__mark,
.ldv-tst__quote,
.ldv-tst__rule,
.ldv-tst__person,
.ldv-tst__source {
	transition:
		opacity var(--ldv-tst-dur-in) var(--ldv-tst-ease),
		transform var(--ldv-tst-dur-in) var(--ldv-tst-ease);
}

.swiper-slide-visible .ldv-tst__mark,
.swiper-slide-visible .ldv-tst__quote,
.swiper-slide-visible .ldv-tst__rule,
.swiper-slide-visible .ldv-tst__person,
.swiper-slide-visible .ldv-tst__source {
	opacity: 1;
	transform: translateY(0);
}


.swiper-slide-visible .ldv-tst__mark { transition-delay: 0.06s; }
/* the brandmark settles at its resting 0.78, not full opacity */
.swiper-slide-visible .ldv-tst__brandmark {
	opacity: 0.78;
	transform: translateY(0);
	transition-delay: 0.12s;
}
.swiper-slide-visible .ldv-tst__quote { transition-delay: 0.18s; }
.swiper-slide-visible .ldv-tst__rule { transition-delay: 0.24s; }
.swiper-slide-visible .ldv-tst__person { transition-delay: 0.3s; }
.swiper-slide-visible .ldv-tst__source { transition-delay: 0.34s; }

/* Marquee slides cross the visible boundary continuously, so a per-slide
   entrance would re-fire and flicker. Content is present from the start.
   Same for a failed Swiper boot — show the cards rather than a blank band. */
.ldv-tst--marquee .ldv-tst__mark,
.ldv-tst--marquee .ldv-tst__quote,
.ldv-tst--marquee .ldv-tst__rule,
.ldv-tst--marquee .ldv-tst__person,
.ldv-tst--marquee .ldv-tst__source,
.swiper:not(.swiper-initialized) .ldv-tst__mark,
.swiper:not(.swiper-initialized) .ldv-tst__brandmark,
.swiper:not(.swiper-initialized) .ldv-tst__quote,
.swiper:not(.swiper-initialized) .ldv-tst__rule,
.swiper:not(.swiper-initialized) .ldv-tst__person,
.swiper:not(.swiper-initialized) .ldv-tst__source {
	opacity: 1;
	transform: none;
}

.ldv-tst--marquee .ldv-tst__mark,
.ldv-tst--marquee .ldv-tst__quote,
.ldv-tst--marquee .ldv-tst__rule,
.ldv-tst--marquee .ldv-tst__person,
.ldv-tst--marquee .ldv-tst__source,
.ldv-tst--marquee .ldv-tst__brandmark {
	transition-delay: 0s;
}

/* the brandmark keeps its resting 0.78 rather than the full opacity above */
.ldv-tst--marquee .ldv-tst__brandmark {
	opacity: 0.78;
	transform: none;
}

/* marquee card sizing that is independent of viewport width */
.ldv-tst--marquee .ldv-tst__card {
	min-height: 0;
	padding: 30px;
}

.ldv-tst--marquee .ldv-tst__name {
	font-size: 22px;
}

.ldv-tst--marquee .ldv-tst__role {
	font-size: 15px;
}


/* -----------------------------------------------------------------------------
   5. Animations
   -------------------------------------------------------------------------- */

@keyframes ldv-tst-drift {
	from {
		transform: translate3d(0, 0, 0);
	}
	to {
		/* half the track is the duplicated set, so -50% lands on a perfect seam */
		transform: translate3d(-50%, 0, 0);
	}
}


/* -----------------------------------------------------------------------------
   6. Media queries — one block per breakpoint, widest first.

      Two ladders run side by side and deliberately do not share edges:
        1600 / 1440 / 1200 / 991 / 767  heading scale, copied from the theme
        1599 / 1199 / 767               marquee column count
      Merging 1599 into 1600 would drop a desktop at exactly 1600px from three
      cards to two, so the one-pixel offset is load-bearing.
   -------------------------------------------------------------------------- */

@media (max-width: 1600px) {
	.ldv-tst__title {
		font-size: 42px;
		line-height: 55px;
	}

	.ldv-tst__title span {
		font-size: 48px;
	}

	.ldv-tst__intro,
	.ldv-tst__intro p {
		font-size: 18px;
		line-height: 30px;
		margin : auto !important
	}
}

/* laptop — 2 cards across */
@media (max-width: 1599px) {
	.ldv-tst__item {
		--ldv-tst-cols: 2;
	}
}

@media (max-width: 1440px) {
	.ldv-tst__title {
		font-size: 40px;
		line-height: 50px;
	}

	.ldv-tst__title span {
		font-size: 45px;
	}
}

@media (max-width: 1399px) {
	.ldv-tst__stage {
		column-gap: 20px;
	}

	.ldv-tst__card {
		padding: 32px;
	}

	.ldv-tst__quote {
		max-width: none;
	}
}

@media (max-width: 1200px) {
	.ldv-tst__title {
		font-size: 35px;
		line-height: 1.2;
	}

	.ldv-tst__title span {
		font-size: 40px;
	}

	.ldv-tst__intro,
	.ldv-tst__intro p {
		font-size: 16px;
		line-height: 1.5;
		margin :auto
	}
}

/* tablet — 2 cards across on a tighter gutter, and the marquee card is now too
   narrow to keep the name and the review platform on one line */
@media (max-width: 1199px) {
	.ldv-tst {
		padding: 90px 0 100px;
	}

	.ldv-tst__card {
		min-height: 470px;
	}

	.ldv-tst__logo {
		max-width: 160px;
		max-height: 80px;
	}

	.ldv-tst__item {
		--ldv-tst-cols: 2;
		--ldv-tst-gap: 24px;
	}

	.ldv-tst--marquee .ldv-tst__foot {
		flex-direction: column;
		align-items: flex-start;
		gap: 18px;
	}

	.ldv-tst--marquee .ldv-tst__source {
		align-items: flex-start;
		text-align: left;
	}
}

@media (max-width: 991px) {
	.ldv-tst__title {
		font-size: 30px;
	}

	.ldv-tst__title span {
		font-size: 35px;
	}

	.ldv-tst__title span::before {
		display: none;
	}

	.ldv-tst__intro {
		width: 100%;
	}

	.ldv-tst__stage {
		grid-template-columns: auto minmax(0, 1fr) auto;
		grid-template-areas:
			"slider slider slider"
			"prev   dots   next";
		column-gap: 14px;
	}

	.ldv-tst__card {
		min-height: 0;
		padding: 28px;
	}

	.ldv-tst__mark {
		width: 50px;
	}

	.ldv-tst__quote {
		margin-bottom: 26px;
		font-size: 17px;
	}

	.ldv-tst__avatar {
		width: 64px;
		height: 64px;
	}

	.ldv-tst__name {
		font-size: 22px;
	}

	.ldv-tst__nav {
		width: 48px;
		height: 48px;
	}

	.ldv-tst__nav svg {
		width: 20px;
		height: 20px;
	}

	.ldv-tst__stage > .ldv-tst__nav,
	.ldv-tst__dots {
		margin-top: 30px;
	}
}

/* mobile — 1 card across. Slightly over 1 so the card in view reads whole with
   a sliver of its neighbours; at a flat 1 it sits bisected by the screen edge
   for most of the drift. */
@media (max-width: 767px) {
	.ldv-tst__title {
		font-size: 28px;
	}

	.ldv-tst__title span {
		font-size: 32px;
	}

	.ldv-tst__intro,
	.ldv-tst__intro p {
		font-size: 14px;
	}

	.ldv-tst__item {
		--ldv-tst-cols: 1.15;
		--ldv-tst-gap: 18px;
	}
}

@media (max-width: 575px) {
	.ldv-tst {
		padding: 70px 0 80px;
	}

	.ldv-tst__card {
		padding: 24px 22px;
		border-radius: 20px;
	}

	.ldv-tst__logo {
		max-width: 120px;
		max-height: 62px;
	}

	.ldv-tst__quote {
		font-size: 16px;
		line-height: 1.75;
	}

	.ldv-tst__foot {
		flex-direction: column;
		align-items: flex-start;
		gap: 20px;
	}

	.ldv-tst__source {
		align-items: flex-start;
		text-align: left;
	}

	.ldv-tst__avatar {
		width: 56px;
		height: 56px;
	}

	.ldv-tst__name {
		font-size: 20px;
	}

	.ldv-tst__role {
		font-size: 15px;
	}
}

/* The !important here is load-bearing: it has to beat every transition and
   animation declared above, including the marquee's. */
@media (prefers-reduced-motion: reduce) {
	.ldv-tst *,
	.ldv-tst *::before,
	.ldv-tst *::after {
		transition-duration: 0.01ms !important;
		transition-delay: 0ms !important;
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
	}

	.ldv-tst__mark,
	.ldv-tst__brandmark,
	.ldv-tst__quote,
	.ldv-tst__rule,
	.ldv-tst__person,
	.ldv-tst__source {
		opacity: 1;
		transform: none;
	}

	.ldv-tst__track {
		animation: none;
	}

	/* the drift is gone, so give the reader a scrollable strip instead of
	   leaving most testimonials frozen off-screen */
	.ldv-tst__marquee {
		overflow-x: auto;
		scroll-snap-type: x mandatory;
	}

	.ldv-tst__item {
		scroll-snap-align: center;
	}
}
