/**
 * Testimonial widget.
 *
 * Colour and type resolve through component tokens declared on `.goxom-ts`,
 * each pointing at a theme global first (--color-primary, --color-black,
 * --font-title â€¦) with a literal fallback for the Elementor editor, where the
 * theme stylesheet is not always loaded.
 *
 * The slider is a scroll-snap row that works with no JavaScript at all. Swiper
 * is an upgrade, not a requirement, which is why the track carries both sets of
 * class names and why nothing here depends on Swiper's stylesheet.
 */

.goxom-ts {
	/* Brand */
	--ts-primary: var(--color-primary, #1273C4);
	--ts-primary-dark: var(--color-primary-dark, #0B5596);
	--ts-star: var(--color-star, #F0A93B);

	/* Ink */
	--ts-title: var(--color-black, #0C2338);
	--ts-body: var(--color-body, #5A7387);
	--ts-white: var(--color-white, #ffffff);

	/* Surface */
	--ts-card: var(--color-white, #ffffff);
	--ts-tint: var(--color-tint, #F0F6FC);
	--ts-line: var(--color-border, #D5E3EF);

	/* Type */
	--ts-font-title: var(--font-title, inherit);
	--ts-font-body: var(--font-body, inherit);

	/* Shape */
	--ts-radius: 20px;
	--ts-avatar: 54px;
	--ts-ease: cubic-bezier(.22, .61, .36, 1);

	position: relative;
	/* Its own stacking context, so the overlay's z-index: -1 paints above the
	   section background yet stays behind the content. */
	isolation: isolate;
	font-family: var(--ts-font-body);
}

/* Background overlay. Always present but empty until the Style tab gives it a
   colour or gradient, so an untouched widget renders exactly as before. */
.goxom-ts::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: inherit;
	opacity: var(--ts-overlay-opacity, 0.6);
	pointer-events: none;
}

.goxom-ts__inner {
	position: relative;
	max-width: 1320px;
	margin-inline: auto;
}

/* ================================================================== heading */

.goxom-ts__head {
	margin-bottom: 52px;
	text-align: center;
}

.goxom-ts__subtitle {
	display: block;
	margin-bottom: 12px;
	font-family: var(--ts-font-title);
	font-size: 15px;
	font-weight: 700;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--ts-primary);
}

.goxom-ts__title {
	margin: 0;
	font-family: var(--ts-font-title);
	font-size: clamp(30px, 4vw, 52px);
	line-height: 1.14;
	font-weight: 700;
	color: var(--ts-title);
}

/* Word masking for the default title animation â€” the JS injects these, so with
   no JS the heading stays plain text. */
.goxom-ts__title .goxom-ts__w {
	display: inline-block;
	overflow: hidden;
	vertical-align: top;
}

.goxom-ts__title .goxom-ts__w > span,
.goxom-ts__title .goxom-ts__char {
	display: inline-block;
	will-change: transform, opacity;
}

/* The summary is centred by its own flex, not by the heading's text-align, so
   that switching the heading to left still leaves a tidy row. */
.goxom-ts__summary {
	display: inline-flex;
	align-items: center;
	flex-wrap: wrap;
	justify-content: center;
	gap: 6px 12px;
	margin-top: 20px;
	padding: 10px 18px;
	border-radius: 999px;
	background-color: color-mix(in srgb, var(--ts-primary) 8%, transparent);
	font-size: 15px;
	color: var(--ts-body);
}

.goxom-ts__score {
	font-family: var(--ts-font-title);
	font-weight: 700;
	color: var(--ts-title);
}

/* ==================================================================== stars */

.goxom-ts__stars {
	display: inline-flex;
	align-items: center;
	gap: 3px;
	color: var(--ts-star);
}

.goxom-ts__star {
	display: block;
	flex: 0 0 18px;
	width: 18px;
	height: 18px;
}

/* ==================================================================== cards */

.goxom-ts__card {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 16px;
	overflow: hidden;
	height: 100%;
	margin: 0;
	padding: 32px;
	border: 1px solid var(--ts-line);
	border-radius: var(--ts-radius);
	background-color: var(--ts-card);
	transition: transform .45s var(--ts-ease), box-shadow .45s var(--ts-ease),
	            border-color .45s var(--ts-ease);
}

.goxom-ts__card:hover,
.goxom-ts__card.is-hold {
	transform: translateY(-6px);
	border-color: color-mix(in srgb, var(--ts-primary) 45%, var(--ts-line));
	box-shadow: 0 28px 48px -28px color-mix(in srgb, var(--ts-title) 45%, transparent);
}

/* The quote mark is decoration, so it sits behind the text and never takes
   space in the flow â€” a floated glyph would push the stars off the top line. */
.goxom-ts__mark {
	position: absolute;
	top: -6px;
	right: 18px;
	z-index: 0;
	font-family: var(--ts-font-title);
	font-size: 120px;
	line-height: 1;
	font-weight: 700;
	color: color-mix(in srgb, var(--ts-primary) 12%, transparent);
	pointer-events: none;
	user-select: none;
}

.goxom-ts__mark--img {
	top: 22px;
	width: 46px;
	height: auto;
	opacity: .3;
}

.goxom-ts__card > *:not(.goxom-ts__mark) {
	position: relative;
	z-index: 1;
}

.goxom-ts__quote {
	margin: 0;
	font-size: 16px;
	line-height: 1.75;
	color: var(--ts-body);
}

.goxom-ts__quote p:last-child {
	margin-bottom: 0;
}

/* --------------------------------------------------------------- author */

.goxom-ts__author {
	display: flex;
	align-items: center;
	gap: 14px;
	/* Pinned to the bottom, so author rows line up across cards whose quotes
	   run to different lengths. */
	margin-top: auto;
	padding-top: 6px;
}

/* The parent class is in this selector to win a specificity fight, not for
   readability: Elementor ships `.elementor img { border-radius: 0; height: auto }`
   at (0,1,1), which outranks a lone `.goxom-ts__avatar` at (0,1,0) â€” and takes
   the portrait's circle and its square box with it. */
.goxom-ts__author .goxom-ts__avatar {
	flex: 0 0 var(--ts-avatar);
	width: var(--ts-avatar);
	height: var(--ts-avatar);
	min-width: var(--ts-avatar);
	border-radius: 50%;
	object-fit: cover;
	background-color: var(--ts-tint);
}

.goxom-ts__avatar--initial {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background-color: color-mix(in srgb, var(--ts-primary) 14%, transparent);
	color: var(--ts-primary);
	font-family: var(--ts-font-title);
	font-size: calc(var(--ts-avatar) * .4);
	font-weight: 700;
	line-height: 1;
}

.goxom-ts__who {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.goxom-ts__name {
	font-family: var(--ts-font-title);
	font-size: 17px;
	font-weight: 700;
	line-height: 1.3;
	color: var(--ts-title);
}

.goxom-ts__role {
	font-size: 14px;
	line-height: 1.4;
	color: var(--ts-body);
}

/* ===================================================================== grid */

.goxom-ts__grid {
	--goxom-ts-cols: 3;
	display: grid;
	grid-template-columns: repeat(var(--goxom-ts-cols), minmax(0, 1fr));
	gap: 30px;
	align-items: stretch;
}

/* =================================================================== slider */

.goxom-ts__slider {
	position: relative;
}

/* Scroll-snap first: this is a working carousel before any script runs. Swiper
   takes over `overflow` and the transform when it initialises, and the
   --swiper class below stands the snap behaviour down so the two never fight. */
.goxom-ts__track {
	--goxom-ts-per-view: 3;
	display: flex;
	gap: 30px;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	scrollbar-width: none;
	padding-bottom: 6px;
}

.goxom-ts__track::-webkit-scrollbar {
	display: none;
}

.goxom-ts__slide {
	flex: 0 0 calc((100% - (var(--goxom-ts-per-view) - 1) * 30px) / var(--goxom-ts-per-view));
	scroll-snap-align: start;
	height: auto;
}

/* Once Swiper owns the track, its own sizing applies and ours has to stop
   competing â€” particularly `overflow`, which would clip Swiper's translation,
   and `gap`, since Swiper spaces slides itself via spaceBetween. */
/* The root class is doubled in this selector to win a specificity fight, not by
   accident: the Gap control writes `gap` at `.elementor-element-x
   .goxom-ts__track`, which is (0,2,0) and merely ties a single-class version of
   this rule â€” leaving the winner down to stylesheet order. Losing that tie
   leaves the gap in place on top of Swiper's own spaceBetween, so every gutter
   is doubled, the slides no longer add up to the container width, and the row
   ends up with a sliver on one edge and a cut card on the other. */
.goxom-ts.goxom-ts--swiper .goxom-ts__track {
	overflow: visible;
	scroll-snap-type: none;
	gap: 0;
	padding-bottom: 0;
}

.goxom-ts--swiper .goxom-ts__slide {
	flex: 0 0 auto;
}

/* With the track's overflow opened up, something has to clip the slides that
   Swiper has translated off-screen, or they run the full width of the page.
   Swiper's own stylesheet does this on `.swiper`, but it is not a dependency
   here, so the clip lives on our container instead.
   The padding/negative-margin pair gives the hover lift and its shadow room to
   breathe inside the clip without changing the section's spacing. */
.goxom-ts--swiper .goxom-ts__slider {
	overflow: hidden;
	padding: 14px 0 20px;
	margin: -14px 0 -20px;
}

.goxom-ts__navs {
	display: flex;
	justify-content: center;
	gap: 12px;
	margin-top: 32px;
}

.goxom-ts__nav {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 48px;
	height: 48px;
	padding: 0;
	border: 1px solid var(--ts-line);
	border-radius: 50%;
	background-color: var(--ts-card);
	color: var(--ts-title);
	cursor: pointer;
	transition: background-color .3s ease, color .3s ease, border-color .3s ease,
	            transform .3s var(--ts-ease);
}

.goxom-ts__nav:hover,
.goxom-ts__nav:focus-visible {
	background-color: var(--ts-primary);
	border-color: var(--ts-primary);
	color: var(--ts-white);
	transform: translateY(-2px);
}

.goxom-ts__nav[disabled] {
	opacity: .4;
	cursor: default;
	transform: none;
}

.goxom-ts__dots {
	display: flex;
	justify-content: center;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 20px;
}

.goxom-ts__dot {
	width: 8px;
	height: 8px;
	padding: 0;
	border: 0;
	border-radius: 999px;
	background-color: color-mix(in srgb, var(--ts-title) 22%, transparent);
	cursor: pointer;
	transition: width .35s var(--ts-ease), background-color .3s ease;
}

.goxom-ts__dot.is-active {
	width: 26px;
	background-color: var(--ts-primary);
}

/* ================================================================ spotlight */

.goxom-ts__spotlight {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 32px;
}

.goxom-ts__stage {
	width: min(860px, 100%);
}

.goxom-ts__card--spotlight {
	align-items: center;
	gap: 20px;
	padding: 48px 44px;
	text-align: center;
}

.goxom-ts__card--spotlight:hover {
	transform: none;
}

.goxom-ts__card--spotlight .goxom-ts__quote {
	font-family: var(--ts-font-title);
	font-size: clamp(18px, 1.9vw, 24px);
	line-height: 1.6;
	font-weight: 500;
	color: var(--ts-title);
}

.goxom-ts__card--spotlight .goxom-ts__author {
	flex-direction: column;
	gap: 10px;
	margin-top: 8px;
	text-align: center;
}

.goxom-ts__card--spotlight .goxom-ts__who {
	align-items: center;
}

.goxom-ts__card--spotlight .goxom-ts__mark {
	right: auto;
	left: 50%;
	transform: translateX(-50%);
}

.goxom-ts__picker {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 12px;
}

.goxom-ts__pick {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	width: 56px;
	height: 56px;
	padding: 0;
	border: 2px solid transparent;
	border-radius: 50%;
	background-color: var(--ts-tint);
	cursor: pointer;
	opacity: .6;
	transition: opacity .3s ease, border-color .3s ease, transform .35s var(--ts-ease);
}

/* Two classes deep for the same reason as the avatar above. */
.goxom-ts__picker .goxom-ts__pick img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.goxom-ts__pick-initial {
	font-family: var(--ts-font-title);
	font-size: 20px;
	font-weight: 700;
	color: var(--ts-primary);
}

.goxom-ts__pick:hover,
.goxom-ts__pick:focus-visible {
	opacity: 1;
	transform: translateY(-3px);
}

.goxom-ts__pick.is-active {
	opacity: 1;
	border-color: var(--ts-primary);
	transform: scale(1.08);
}

/* ================================================================== notices */

.goxom-ts__notice {
	padding: 22px;
	border: 1px dashed color-mix(in srgb, var(--ts-title) 35%, transparent);
	border-radius: 12px;
	text-align: center;
	color: var(--ts-title);
	background-color: color-mix(in srgb, var(--ts-primary) 7%, transparent);
}

/* ================================================================== reveals */

/* Only hide cards once JS has confirmed it will animate them. */
.goxom-ts--js[data-reveal="rise"] .goxom-ts__grid .goxom-ts__card,
.goxom-ts--js[data-reveal="scale"] .goxom-ts__grid .goxom-ts__card,
.goxom-ts--js[data-reveal="fade"] .goxom-ts__grid .goxom-ts__card,
.goxom-ts--js[data-reveal="rise"] .goxom-ts__slide,
.goxom-ts--js[data-reveal="scale"] .goxom-ts__slide,
.goxom-ts--js[data-reveal="fade"] .goxom-ts__slide {
	opacity: 0;
}

.goxom-ts--css[data-reveal] .goxom-ts__grid .goxom-ts__card,
.goxom-ts--css[data-reveal] .goxom-ts__slide {
	animation: goxom-ts-rise .7s var(--ts-ease) both;
}

.goxom-ts--css[data-reveal="scale"] .goxom-ts__grid .goxom-ts__card,
.goxom-ts--css[data-reveal="scale"] .goxom-ts__slide { animation-name: goxom-ts-scale; }

.goxom-ts--css[data-reveal="fade"] .goxom-ts__grid .goxom-ts__card,
.goxom-ts--css[data-reveal="fade"] .goxom-ts__slide { animation-name: goxom-ts-fade; }

.goxom-ts--css[data-reveal=""] .goxom-ts__grid .goxom-ts__card,
.goxom-ts--css[data-reveal=""] .goxom-ts__slide {
	animation: none;
	opacity: 1;
}

.goxom-ts--css .goxom-ts__grid .goxom-ts__card:nth-child(2) { animation-delay: .08s; }
.goxom-ts--css .goxom-ts__grid .goxom-ts__card:nth-child(3) { animation-delay: .16s; }
.goxom-ts--css .goxom-ts__grid .goxom-ts__card:nth-child(4) { animation-delay: .24s; }
.goxom-ts--css .goxom-ts__grid .goxom-ts__card:nth-child(n+5) { animation-delay: .32s; }

@keyframes goxom-ts-rise {
	from { opacity: 0; transform: translateY(38px); }
	to   { opacity: 1; transform: none; }
}

@keyframes goxom-ts-scale {
	from { opacity: 0; transform: scale(.93); }
	to   { opacity: 1; transform: none; }
}

@keyframes goxom-ts-fade {
	from { opacity: 0; }
	to   { opacity: 1; }
}

.goxom-ts--css[data-title-anim] .goxom-ts__title {
	animation: goxom-ts-rise .8s var(--ts-ease) both;
}

/* Spotlight panels cross-fade rather than cutting. */
.goxom-ts__panel {
	animation: goxom-ts-fade-in .45s var(--ts-ease) both;
}

@keyframes goxom-ts-fade-in {
	from { opacity: 0; transform: translateY(12px); }
	to   { opacity: 1; transform: none; }
}

/* =============================================================== breakpoints */

@media (max-width: 1024px) {
	.goxom-ts__grid {
		--goxom-ts-cols: 2;
	}

	.goxom-ts__track {
		--goxom-ts-per-view: var(--goxom-ts-per-view-tablet, 2);
	}
}

@media (max-width: 767px) {
	.goxom-ts__grid {
		--goxom-ts-cols: 1;
		gap: 20px;
	}

	.goxom-ts__track {
		--goxom-ts-per-view: var(--goxom-ts-per-view-mobile, 1);
	}

	.goxom-ts__card {
		padding: 26px 22px;
	}

	.goxom-ts__card--spotlight {
		padding: 34px 24px;
	}

	.goxom-ts__mark {
		font-size: 90px;
	}

	.goxom-ts__pick {
		width: 46px;
		height: 46px;
	}
}

/* color-mix is recent enough that a couple of shipping browsers still miss it;
   without these the tints and the quote mark render transparent. */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
	.goxom-ts__summary {
		background-color: rgba(14, 159, 110, .08);
	}

	.goxom-ts__mark {
		color: rgba(14, 159, 110, .12);
	}

	.goxom-ts__avatar--initial {
		background-color: rgba(14, 159, 110, .14);
	}

	.goxom-ts__card:hover {
		border-color: rgba(14, 159, 110, .45);
		box-shadow: 0 28px 48px -28px rgba(12, 35, 56, .45);
	}

	.goxom-ts__dot {
		background-color: rgba(12, 35, 56, .22);
	}

	.goxom-ts__notice {
		border-color: rgba(12, 35, 56, .35);
		background-color: rgba(14, 159, 110, .07);
	}
}

@media (prefers-reduced-motion: reduce) {
	.goxom-ts__card,
	.goxom-ts__nav,
	.goxom-ts__dot,
	.goxom-ts__pick {
		transition: none !important;
	}

	.goxom-ts__track {
		scroll-behavior: auto;
	}

	.goxom-ts__panel,
	.goxom-ts--css[data-reveal] .goxom-ts__grid .goxom-ts__card,
	.goxom-ts--css[data-reveal] .goxom-ts__slide,
	.goxom-ts--css[data-title-anim] .goxom-ts__title,
	.goxom-ts--js[data-reveal] .goxom-ts__card,
	.goxom-ts--js[data-reveal] .goxom-ts__slide {
		animation: none !important;
		opacity: 1 !important;
		transform: none !important;
	}
}
