/*
 * Features slider block — frontend + editor styles.
 * Every selector is scoped to the root class .wsc-features-slider (three-way sync rule).
 * Tokens only: theme.json presets.
 *
 * The pill switcher is the shared .wsc-switcher component — its rules, its
 * colours and its reduced-motion handling all live in assets/css/ui-switcher.css.
 * The only thing this file says about it is the mobile full-width split, which
 * is a layout decision belonging to this block's header row.
 *
 * Geometry follows the Figma desktop frame (1680x866, node 16424:7082): a 1200px
 * body that is a 40px-gap column of [header row 40] + [slides row 546] +
 * [controls row 40]. Two cards of 588px with a 24px gap. All px converted at
 * 16px = 1rem.
 *
 * The theme resolves Figma's unspecified "normal" line height to 1.25 (see
 * styling-tokens.md), which leaves several elements a fraction taller than the
 * frame. Accepted rather than clawed back, because the only lever is
 * padding-block and the compensating values are not spacing tokens. The card
 * button is the one place that rule was overridden back to 1 — see the note on
 * .wsc-features-slider__button.
 *
 * Control colours are read from the exported SVGs, not from the screenshot:
 * arrows are a 1.5px ring (muted enabled / border disabled) around a 1.5px
 * chevron (ink-soft enabled / border disabled); the active dot is ink-soft, and
 * inactive dots are 2px border rings with a transparent centre — not filled
 * grey. Every one of those resolves to an existing preset.
 *
 * Three deliberate departures from the frame:
 *
 * 1. The horizontal gutter is the theme-wide .wsc-section primitive rather than
 *    the frame's 240px, so the block lines up with the hero and the features bar.
 *    The frame's 240px is a min-gutter around a centred 1200px body, which
 *    .wsc-container reproduces on its own above 1280px.
 * 2. Cards in a row match heights and the button is pushed to the bottom edge.
 *    The frame instead pads the shorter card's copy with two empty line breaks —
 *    a comp-time hack that would break on any real copy length.
 * 3. The button and the switcher pills have hover states, which the frame does
 *    not define at all. An inert pill CTA reads as broken.
 */

.wsc-features-slider {
	/*
	 * The slider deliberately overflows its container so the next card peeks into
	 * the gutter; this is what clips it back at the viewport edge.
	 *
	 * `hidden` first as the fallback, then `clip`, which wins wherever it is
	 * supported. They look identical, but `hidden` also makes this a scroll
	 * container — so tabbing to a button on the off-screen peeking card would
	 * scroll the section sideways and shove the layout out of frame. `clip` has
	 * no such side effect.
	 */
	overflow: hidden;
	overflow: clip;
}

.wsc-features-slider__inner {
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--60); /* 40px */
}

/* Header --------------------------------------------------------------- */

.wsc-features-slider__head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--40); /* 24px */
}

/* Size, weight and line-height come from the global h2 element style. */
.wsc-features-slider__heading {
	margin-block: 0;
}

/*
 * NO ENTRANCE ANIMATION ON THE PANEL. Switching tabs used to fade the incoming
 * one in over 0.35s and lift it a rem, keyed off the panel leaving `hidden`. The
 * swap is now instant, which also removes the transform that used to sit on the
 * panel at exactly the moment the tab's Swiper was constructed and measured
 * itself — that was reasoned to be safe, but not having it is simpler than
 * arguing it is harmless.
 *
 * Nothing in script.js was ever involved: the effect was purely a property of
 * being revealed, so removing it needs no counterpart there.
 */

/* Slider --------------------------------------------------------------- */

/*
 * Swiper's own box must NOT clip — that is what lets the next card show in the
 * gutter. The section clips instead. Removing this line removes the peek.
 */
/* Doubled class so this beats Swiper's own .swiper{overflow:hidden} on
   specificity rather than on stylesheet order. The peek is the block's whole
   visual premise; it should not depend on which file prints first. */
.wsc-features-slider .wsc-features-slider__swiper {
	overflow: visible;
}

/* Stretch, so two cards of uneven copy length still share a bottom edge. */
.wsc-features-slider__swiper .swiper-wrapper {
	align-items: stretch;
}

.wsc-features-slider__swiper .swiper-slide {
	height: auto;
}

/* Card ----------------------------------------------------------------- */

/*
 * position: relative makes the card the containing block for the CTA's click
 * overlay, which is what turns the whole card into one link target. See the
 * ::before on .wsc-features-slider__button.
 */
.wsc-features-slider__card {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--wp--preset--spacing--40); /* 24px */
	height: 100%;
	padding-block-end: var(--wp--preset--spacing--40); /* 24px */
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--sm); /* 16px */
	background-color: var(--wp--preset--color--surface);
	overflow: hidden; /* clips the image into the card's top corners */
}

/* Its own overflow, not the card's: a scaled image has to clip at the media
   band rather than bleed down over the eyebrow. */
.wsc-features-slider__media {
	height: 20rem; /* 320px */
	overflow: hidden;
}

.wsc-features-slider__image {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.6s cubic-bezier(0.645, 0.045, 0.355, 1);
}

/*
 * Slow and shallow on purpose — the point is that the card feels live under the
 * cursor, not that the photo moves. focus-within so keyboard users get the same
 * signal when the card's link takes focus.
 */
.wsc-features-slider__card:hover .wsc-features-slider__image,
.wsc-features-slider__card:focus-within .wsc-features-slider__image {
	transform: scale(1.04);
}

/*
 * flex: 1 so the button's margin-block-start: auto has room to push into.
 *
 * No flex gap: the frame's vertical rhythm inside the card is uneven — 16px
 * above the title, 4px below it, 16px under the text — so the spacing is carried
 * by the children's own margins instead of one uniform gap.
 */
.wsc-features-slider__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	padding-inline: var(--wp--preset--spacing--40); /* 24px */
}

/*
 * Inter Bold 12/18, +0.12px tracking = 0.01em at this size.
 * The dash is decoration, not content: editors type the label in sentence case
 * and CSS supplies both the dash and the uppercasing, so it cannot be forgotten,
 * typed as a hyphen, or lost in translation.
 */
.wsc-features-slider__eyebrow {
	margin-block: 0;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small); /* 12px */
	font-weight: 700;
	line-height: 1.5; /* 18/12 */
	letter-spacing: 0.01em;
	text-transform: uppercase;
	color: var(--wp--preset--color--primary);
}

.wsc-features-slider__eyebrow::before {
	content: '—';
	margin-inline-end: 0.5em;
}

/*
 * An <h3> for the document outline — it sits under the section's h2 — carrying
 * the design's H4 metrics. Declared here rather than leaning on the global h4
 * element style, same as the features bar does for its H6 label.
 */
.wsc-features-slider__title {
	/* Frame spacing: 16px above, 4px below. 4px has no preset, so it is a literal
	   rem alongside the spacing token. */
	margin-block: var(--wp--preset--spacing--30) 0.25rem; /* 16px / 4px */
	font-family: var(--wp--preset--font-family--heading);
	font-size: 1.25rem; /* 20px */
	font-weight: 600;
	line-height: 1.2; /* 24/20, desktop and mobile alike */
	letter-spacing: 0.02em;
	color: var(--wp--preset--color--ink);
}

/* Size and line-height come from the global body style (14/20). */
.wsc-features-slider__text {
	/* 16px below, to the button — the button's margin-block-start: auto absorbs
	   any extra height, so this is the minimum text-to-button gap. */
	margin-block: 0 var(--wp--preset--spacing--30); /* 16px */
	letter-spacing: -0.01em;
	color: var(--wp--preset--color--ink-soft);
}

/*
 * margin-block-start: auto pins the button to the card's bottom edge, which is
 * what keeps two cards of different copy length aligned. align-self stops it
 * stretching to the card's full width.
 */
/*
 * Hover is the hero's wipe, adapted for an outline button.
 *
 * The hero fills its pill with a two-stop gradient in a box twice the button's
 * height and animates background-position, so the incoming colour has one
 * antialiased edge instead of two disagreeing ones. Same technique here, except
 * the resting half is `transparent` rather than a second colour — that is what
 * keeps the frame's outline default while still wiping a fill upward on hover.
 *
 * Cubic in-out matches the hero, which matches the GSAP power2.inOut reference.
 */
.wsc-features-slider__button {
	/* Explicit, and load-bearing: see the ::before below. */
	position: static;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	align-self: flex-start;
	margin-block-start: auto;
	padding-block: var(--wp--preset--spacing--20); /* 12px */
	padding-inline: var(--wp--preset--spacing--40); /* 24px */
	border: 1.5px solid var(--wp--preset--color--ink-soft);
	border-radius: var(--wp--custom--radius--pill);
	background-image: linear-gradient(
		to top,
		var(--wp--preset--color--ink-soft) 50%,
		transparent 50%
	);
	/* The +4px is there for the reason ui-button.css documents at length: at a
	   flat 200% the gradient's hard stop lands on a travel endpoint's window
	   edge, and its antialiased row then paints a hairline of the outgoing
	   colour inside the pill. The 4px moves the stop 2px clear of both ends. */
	background-size: 100% calc(200% + 4px);
	background-position: 0 0; /* transparent half showing — the outline state */
	/*
	 * border-box, not the default padding-box: the fill is painted across the
	 * border box but positioned against its origin box, so with the default the
	 * 200%-tall gradient stops at the padding edge and leaves the 1.5px border
	 * band uncovered at the top and bottom — the fill visibly not reaching the
	 * pill's full height on hover. Anchoring the origin to the border box closes
	 * that band.
	 */
	background-origin: border-box;
	color: var(--wp--preset--color--ink-soft);
	font-family: var(--wp--preset--font-family--mono);
	font-size: var(--wp--preset--font-size--mono); /* 13px */
	letter-spacing: 0; /* The Mono token is 0; the inherited body default is -1%. */
	/*
	 * A deliberate departure from styling-tokens.md, recorded there as the one
	 * sanctioned exception. Here 1.25 fights the design instead of serving it: at
	 * 1 the pill is exactly the frame's 40px — 13px label + 24px padding + 3px
	 * border — and 1.25 pushes it to 43.25px. The rule's reasoning does not bite
	 * on this element: the label is one uppercase mono line in a flex-centred box
	 * with nothing beneath it to crowd, so the only descender the set can produce
	 * (Q) has the block padding to fall into.
	 */
	line-height: 1;
	text-transform: uppercase;
	text-decoration: none;
	transition: background-position 0.4s cubic-bezier(0.645, 0.045, 0.355, 1),
		color 0.4s cubic-bezier(0.645, 0.045, 0.355, 1);
}

/*
 * Stretches the CTA's hit area over the entire card.
 *
 * The overlay is absolutely positioned against the CARD, not the button, which
 * only works while the button itself stays unpositioned — hence the explicit
 * `position: static` above rather than relying on it being the default. If a
 * later change gives the button `position: relative`, the overlay silently
 * collapses to the button's own box and the card stops being clickable.
 *
 * Trade-off accepted: the overlay sits above the card's text, so the copy is no
 * longer selectable. That is inherent to the pattern. Keyboard and screen-reader
 * users are unaffected — the <a> is still the only focusable thing, and
 * render.php names it with the card title so the links stay distinguishable.
 */
.wsc-features-slider__button::before {
	content: '';
	position: absolute;
	inset: 0;
}

/*
 * Inert on the peeking slide only.
 *
 * The next card peeks in by design, and the instinctive gesture on that sliver
 * is to tap it to bring it forward. Without this the tap follows the peeking
 * card's link and leaves the page — a behaviour the card only acquired by
 * becoming clickable. Swiper's own click suppression does not cover it: that
 * cancels a click after a DRAG, and a tap is not a drag.
 *
 * The --clipped marker is written by script.js (markClickableSlides) with a 1px
 * tolerance, NOT by Swiper's own swiper-slide-fully-visible class: that test is
 * exact and the second of two per-view cards sits right on its boundary, so
 * rounding stripped the class from a fully-shown card and killed its hit area.
 * No marker exists before JS runs, so a no-JS render keeps every card clickable.
 *
 * The modifier hangs off Swiper's own .swiper-slide — there is no
 * wsc-features-slider__slide element in the markup, Swiper owns that node — so
 * the base is intentionally absent; the class is root-prefixed purely to keep
 * the selector scoped to this block.
 */
.wsc-features-slider__slide--clipped .wsc-features-slider__button::before {
	pointer-events: none;
}

/* Not in the frame — see the header note. */
.wsc-features-slider__button:hover,
.wsc-features-slider__button:focus-visible {
	background-position: 0 100%; /* ink-soft wipes up over the transparent half */
	color: var(--wp--preset--color--bg);
}

/* Controls ------------------------------------------------------------- */

/*
 * 40px below the slider, matching the frame's body gap.
 *
 * Deliberately a margin on the child rather than a flex gap on the panel: the
 * panel has no `display` of its own, which is what lets the UA's
 * `[hidden] { display: none }` keep working. Giving it `display: flex` here
 * would override that and every inactive tab would render on top of the active
 * one.
 */
.wsc-features-slider__controls {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--40); /* 24px */
	margin-block-start: var(--wp--preset--spacing--60); /* 40px */
	min-height: 2.5rem; /* 40px — the arrows' own height */
}

/*
 * Swiper adds these when watchOverflow finds nothing to scroll. Hiding them is
 * what makes a two-slide desktop tab show no dead controls, without render.php
 * counting anything.
 */
.wsc-features-slider .swiper-pagination-lock,
.wsc-features-slider .swiper-button-lock {
	display: none;
}

/*
 * Hiding the controls is not enough on its own — the row itself still holds a
 * 40px min-height and the 40px column gap above it, so a two-slide tab would end
 * in an 80px band of nothing. When both controls are locked, the row goes too.
 */
.wsc-features-slider__controls:has(.swiper-pagination-lock):has(.swiper-button-lock) {
	display: none;
}

/* Dots ----------------------------------------------------------------- */

/*
 * Swiper positions its pagination absolutely by default; this one is a flex item
 * in the controls row, so that is unset. The bullets themselves are overridden
 * wholesale — Swiper's default is a filled circle at partial opacity, but the
 * design uses a 2px ring with a transparent centre for the inactive state and a
 * solid fill only for the active one.
 */
.wsc-features-slider__dots.swiper-pagination {
	/*
	 * Swiper spaces horizontal bullets with a margin driven by this variable, from
	 * a (0,3,0) selector that no single-class rule here can outrank. Zeroing the
	 * variable is how you win that contest without a specificity arms race — the
	 * 16px then comes from this flex gap alone.
	 */
	--swiper-pagination-bullet-horizontal-gap: 0;

	position: static;
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--30); /* 16px */
	width: auto;
}

.wsc-features-slider__dots .swiper-pagination-bullet {
	width: 0.75rem; /* 12px */
	height: 0.75rem;
	border: 2px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--pill);
	background-color: transparent;
	opacity: 1;
	transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
		border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}

.wsc-features-slider__dots .swiper-pagination-bullet-active {
	border-color: var(--wp--preset--color--ink-soft);
	background-color: var(--wp--preset--color--ink-soft);
}

/* Arrows --------------------------------------------------------------- */

.wsc-features-slider__nav {
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--30); /* 16px */
}

/*
 * 40px ring with a 1.5px stroke, matching the exported SVG. The chevron inherits
 * currentColor, so the disabled state is two colour swaps and no second icon.
 */
.wsc-features-slider__arrow {
	display: inline-flex;
	flex: none;
	align-items: center;
	justify-content: center;
	width: 2.5rem; /* 40px */
	height: 2.5rem;
	padding: 0;
	border: 1.5px solid var(--wp--preset--color--muted);
	border-radius: var(--wp--custom--radius--pill);
	background-color: transparent;
	color: var(--wp--preset--color--ink-soft);
	cursor: pointer;
	transition: border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
		background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
		color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}

.wsc-features-slider__arrow svg {
	display: block;
	width: 100%;
	height: 100%;
}

/*
 * One chevron asset, flipped for "previous". Swiper reverses travel direction in
 * RTL, so which arrow needs flipping reverses with it — otherwise the prev
 * chevron would point left while pressing it moved the slides right. WPML makes
 * an RTL market a live possibility on this network.
 */
.wsc-features-slider__arrow--prev {
	transform: rotate(180deg);
}

[dir='rtl'] .wsc-features-slider__arrow--prev {
	transform: none;
}

[dir='rtl'] .wsc-features-slider__arrow--next {
	transform: rotate(180deg);
}

.wsc-features-slider__arrow:hover:not(.swiper-button-disabled),
.wsc-features-slider__arrow:focus-visible:not(.swiper-button-disabled) {
	border-color: var(--wp--preset--color--ink-soft);
	background-color: var(--wp--preset--color--ink-soft);
	color: var(--wp--preset--color--bg);
}

/* Both the ring and the chevron go to border grey, per the exported SVG. */
.wsc-features-slider__arrow.swiper-button-disabled {
	border-color: var(--wp--preset--color--border);
	color: var(--wp--preset--color--border);
	cursor: default;
}

/* Narrow viewports ----------------------------------------------------- */

/*
 * Below the desktop breakpoint the slider runs one card at a time at 92% width,
 * so the next one still peeks. slidesPerView: 'auto' in script.js is what makes
 * this width authoritative — Swiper clears the inline width it would otherwise
 * set and measures the CSS one instead.
 */
@media (max-width: 767px) {
	.wsc-features-slider__swiper .swiper-slide {
		width: 92%;
	}

	/*
	 * The mobile frame stacks the switcher ABOVE the heading and runs it full
	 * width, each pill taking half the row. column-reverse gets that order without
	 * touching the DOM — the <h2> still precedes the switcher in source, so it
	 * keeps labelling the tablist (aria-labelledby) and reads first. `stretch`
	 * spans the switcher edge to edge; the tabs then split it via `flex: 1`. The
	 * 40px gap is the frame's tab-to-title distance.
	 */
	.wsc-features-slider__head {
		flex-direction: column-reverse;
		align-items: stretch;
		gap: var(--wp--preset--spacing--60); /* 40px */
	}

	/*
	 * Splitting the row is this block's layout decision, not the shared
	 * switcher's, so it lives here and reaches into the component's option class
	 * rather than becoming a default every consumer inherits.
	 */
	.wsc-features-slider .wsc-switcher__option {
		flex: 1; /* two equal-width pills across the full switcher */
	}

	/*
	 * Square media, per the mobile frame. `height: auto` releases the desktop
	 * 20rem band so aspect-ratio can drive the height instead — the image then
	 * scales with the ~92vw card rather than letterboxing inside a fixed strip.
	 */
	.wsc-features-slider__media {
		height: auto;
		aspect-ratio: 1 / 1;
	}
}

/*
 * At the smallest widths one card fills the row — the peek is dropped. Below the
 * 480px calibration floor there is too little width left for a meaningful sliver,
 * and a full-width slide reads cleaner than a hairline of the next card.
 */
@media (max-width: 480px) {
	.wsc-features-slider__swiper .swiper-slide {
		width: 100%;
	}
}

/* Reduced motion ------------------------------------------------------- */

/* The switcher carries its own reduced-motion rules in ui-switcher.css. */
@media (prefers-reduced-motion: reduce) {
	.wsc-features-slider__button,
	.wsc-features-slider__arrow,
	.wsc-features-slider__image,
	.wsc-features-slider__dots .swiper-pagination-bullet {
		transition: none;
	}

	.wsc-features-slider__card:hover .wsc-features-slider__image,
	.wsc-features-slider__card:focus-within .wsc-features-slider__image {
		transform: none;
	}
}
