.dbw-partner-slider {
	position: relative;
	overflow: hidden;
	width: 100%;
	max-width: 100%;
	min-width: 0;
	min-height: 70px;
	margin-top: var(--outer-margin, 50px);
	margin-bottom: var(--outer-margin, 50px);

	// Defensive: WordPress core / common theme resets use
	//   html :where([style*="border-width"]) { border-style: solid; }
	// to auto-enable borders for blocks with inline border-width styles.
	// Our inline style emits the custom property --capsule-border-width,
	// which contains the literal substring "border-width" and matches that
	// attribute selector — turning on the browser default 3px border on
	// the slider container. Explicitly resetting border = 0 here neutralises
	// that and protects against similar future theme rules.
	border: 0;

	// Smooth initial load: start hidden and let the frontend script reveal
	// the carousel (.dbw-ready) once every row is measured and its images
	// are loaded. The delayed keyframe is a pure-CSS safety net that reveals
	// the carousel even if the script never runs; a <noscript> rule keeps it
	// visible when JavaScript is disabled.
	opacity: 0;
	animation: dbw-reveal-fallback 0.4s ease 5s forwards;

	&.dbw-ready {
		opacity: 1;
		animation: none;
		transition: opacity 0.4s ease;
	}

	&::before,
	&::after {
		content: "";
		position: absolute;
		top: 0;
		width: 100px;
		height: 100%;
		pointer-events: none;
		z-index: 2;
	}

	&::before {
		left: 0;
		background: linear-gradient(
			to right,
			var(--overlay-color, #fff) 0%,
			transparent 100%
		);
	}

	&::after {
		right: 0;
		background: linear-gradient(
			to left,
			var(--overlay-color, #fff) 0%,
			transparent 100%
		);
	}

	&.no-overlay::before,
	&.no-overlay::after {
		display: none;
	}

	// Transparent edge fade (v2.4): the logos themselves fade out through a
	// mask, so the edges work on any background, images and gradients
	// included. A mask would also clip the capsule glow vertically, so glow
	// capsules keep the colour overlay.
	&.dbw-overlay-fade:not(.dbw-cap-glow) {
		&::before,
		&::after {
			display: none;
		}

		.dbw-slider-wrapper {
			$fade: linear-gradient(
				to right,
				transparent,
				#000 var(--dbw-fade-width, 100px),
				#000 calc(100% - var(--dbw-fade-width, 100px)),
				transparent
			);
			-webkit-mask-image: $fade;
			mask-image: $fade;
		}
	}

	&.black-logos img {
		filter: brightness(0);
	}

	&.dbw-logos-white img {
		filter: brightness(0) invert(1);
	}

	&.dbw-logos-custom img {
		filter: var(--logo-filter, none);
	}

	// Grayscale mode: logos run desaturated.
	&.dbw-logos-gray img {
		filter: grayscale(1);
	}

	// Original colors on hover (v2.1) — an independent option that pairs with
	// every color mode (black, white, grayscale, custom tint) and also works
	// inside capsules. The selector carries four classes plus :hover, so it
	// outranks both the color-mode rules above and the capsule logo-color
	// rules further down without needing !important.
	&.dbw-color-hover img {
		transition: filter 0.25s ease;
	}

	&.dbw-color-hover .dbw-slider-item:hover img {
		filter: none;
	}

	&:hover .dbw-slider-track {
		animation-play-state: paused;
	}

	// Glow mode: allow the capsule box-shadow to escape vertically so it
	// does not get cropped flat by the container's overflow:hidden. We use
	// overflow:clip + overflow-clip-margin-block — that keeps horizontal
	// clipping strict (essential, otherwise the off-screen part of the
	// scrolling track would become visible past the edge overlay) while
	// granting just enough vertical room for the configured glow radius.
	// Older browsers that don't recognise overflow:clip / clip-margin
	// silently fall back to the inherited overflow:hidden — no regression.
	&.dbw-cap-glow {
		overflow: clip;
		overflow-clip-margin-block: var(--capsule-glow-size, 14px);
	}
}

.dbw-slider-wrapper {
	width: 100%;
	max-width: 100%;
	min-width: 0;
	min-height: 70px;
	overflow: hidden;

	// The scroll animation is built for a left-to-right track: the first
	// logo set starts at the left edge and the copies follow to the right.
	// On RTL pages the track would be anchored to the right edge with the
	// copies off-screen on the left, so after one pass the row runs empty.
	// Logo text direction is restored on the items below.
	direction: ltr;

	// Spotlight mode does not scroll; its slot alignment keeps following the
	// page direction, exactly as before 2.3.1.
	.dbw-layout-spotlight & {
		direction: inherit;
	}

	// Same vertical-glow exception as on the slider container — this inner
	// wrapper otherwise clips the glow first, before it can reach the outer
	// container's relaxed clip box.
	.dbw-cap-glow & {
		overflow: clip;
		overflow-clip-margin-block: var(--capsule-glow-size, 14px);
	}
}

.dbw-slider-track {
	display: flex;
	align-items: center;
	width: max-content;
	max-width: none;
	animation: dbw-infinity-scroll var(--scroll-duration, 25s) linear infinite;

	// Reverse-scrolling rows (no-JS fallback; the frontend script applies its
	// own exact-width animation including direction).
	&[data-direction="reverse"] {
		animation-direction: reverse;
	}
}

// Multi-row layout: each row is its own wrapper/track, stacked vertically.
.dbw-layout-rows {
	.dbw-slider-wrapper + .dbw-slider-wrapper {
		margin-top: var(--row-gap, 24px);
	}
}

// Capsule style: each logo sits inside a rounded background container.
.dbw-capsules {
	.dbw-capsule {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		box-sizing: border-box;
		border-radius: var(--capsule-radius, 999px);

		// Padding stays proportional to the *rendered* logo height. The
		// inline style sets --capsule-pad-y / -x from the user-chosen
		// --logo-height (fine for desktop) — on small viewports we cap
		// both sides against a fraction of the viewport so a large
		// user-set height doesn't produce capsules wider than the phone.
		// min() keeps the user/preset value as the upper bound; on wide
		// screens the inline value is smaller than the vw expression and
		// wins, on narrow screens the vw expression caps it.
		padding:
			min(
				var(--capsule-pad-y, calc(var(--logo-height, 50px) * 0.3)),
				calc(12vw * 0.3)
			)
			min(
				var(--capsule-pad-x, calc(var(--logo-height, 50px) * 0.55)),
				calc(12vw * 0.55)
			);
	}

	.dbw-cap-a {
		background: var(--capsule-color-a, #000000);
	}

	.dbw-cap-b {
		background: var(--capsule-color-b, #ffffff);
	}

	// Reset any general logo-color filter inside capsules so the
	// per-capsule class (or "no filter") takes precedence.
	.dbw-capsule img {
		filter: none;
		opacity: 1;
	}

	// Monochrome logos, contrasting their capsule background.
	.dbw-logo-light img {
		filter: brightness(0) invert(1);
	}

	.dbw-logo-dark img {
		filter: brightness(0);
	}

	// Outline style: transparent fill with a colored border instead of a
	// background. Higher specificity (slider has both classes) so it wins
	// over the .dbw-cap-a/.dbw-cap-b background rules.
	&.dbw-cap-outline .dbw-capsule {
		background: transparent;
		border: var(--capsule-border-width, 2px) solid
			var(--capsule-color-a, #000000);
	}

	// Optional glow: a soft box-shadow around each capsule, in its own color.
	&.dbw-cap-glow {
		.dbw-cap-a {
			box-shadow: 0 0 var(--capsule-glow-size, 14px)
				var(--capsule-color-a, #000000);
		}

		.dbw-cap-b {
			box-shadow: 0 0 var(--capsule-glow-size, 14px)
				var(--capsule-color-b, #ffffff);
		}
	}
}

.dbw-slider-item {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0 calc(var(--slide-gap, 40px) / 2);

	// Responsive logo height. The user picks a value in the editor
	// (30–150 px) which we treat as the desktop ceiling — on narrower
	// viewports the logo scales down with the viewport so capsules + gaps
	// don't overflow on phones. clamp(min, vw, user) is a single fluid
	// formula, no media-query break points:
	//   • below ~233 px viewport width the 28 px floor takes over
	//   • from there up to ~user/0.12 the 12 vw track grows smoothly
	//   • above that the user's chosen height caps it
	//
	// Example with --logo-height: 150 px
	//   375 px iPhone  →  45 px logo  (12 vw)
	//   768 px iPad    →  92 px logo  (12 vw)
	//  1280 px laptop  → 150 px logo  (capped at user value)
	//  1920 px desktop → 150 px logo  (capped at user value)
	//
	// --dbw-h is the resolved per-viewport height (the 600 px media query
	// below swaps in the optional fixed mobile height). --logo-scale is the
	// per-item balanced-size factor set by the frontend script (1 when the
	// "Balance logo sizes" option is off).
	--dbw-h: clamp(28px, 12vw, var(--logo-height, 50px));
	min-height: var(--dbw-h);

	img {
		display: block;
		// max-width: none defeats the theme's global `img { max-width: 100% }`,
		// which could otherwise squeeze wide logos. width stays driven purely
		// by the logo's aspect ratio so nothing is ever cropped.
		max-width: none;
		max-height: calc(var(--dbw-h) * var(--logo-scale, 1));
		min-height: calc(var(--dbw-h) * var(--logo-scale, 1));
		width: auto;
		height: auto;
		object-fit: contain;
	}

	a {
		display: inline-block;
		line-height: 0;
	}
}

// Optional pause/play button (accessibility, WCAG 2.2.2). The frontend script
// wires the click handler and aria attributes; the icon is pure CSS.
.dbw-pause-btn {
	position: absolute;
	inset-inline-end: 12px;
	bottom: 12px;
	z-index: 3;
	width: 34px;
	height: 34px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	// White icon on 70 % black: well above the 3:1 needed for controls,
	// on light and dark sections alike.
	background: rgba(0, 0, 0, 0.7);
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: background-color 0.2s ease;

	&:hover {
		background: rgba(0, 0, 0, 0.85);
	}

	&:focus-visible {
		outline: 2px solid currentColor;
		outline-offset: 2px;
		box-shadow: 0 0 0 4px #fff;
	}

	// Pause icon (two bars).
	&::before {
		content: "";
		box-sizing: border-box;
		width: 10px;
		height: 12px;
		border-left: 3px solid #fff;
		border-right: 3px solid #fff;
	}
}

// Play icon (triangle) while the slider is button-paused.
.dbw-paused .dbw-pause-btn::before {
	width: 0;
	height: 0;
	border: 0;
	border-left: 11px solid #fff;
	border-top: 7px solid transparent;
	border-bottom: 7px solid transparent;
	margin-left: 3px;
}

// Text marquee: same engine and structure, text items instead of logos.
.dbw-marquee {
	min-height: 0;

	.dbw-slider-wrapper {
		min-height: 0;
	}

	.dbw-slider-item {
		min-height: 0;
		padding: 0;
	}

	.dbw-mq-text,
	.dbw-mq-sep {
		font-size: var(--mq-size, 24px);
		line-height: 1.3;
		white-space: nowrap;
	}

	.dbw-mq-text {
		color: var(--mq-color, inherit);
	}

	.dbw-mq-sep {
		color: var(--mq-sep-color, var(--mq-color, inherit));
		margin: 0 calc(var(--slide-gap, 40px) / 2);
	}

	&.dbw-mq-upper .dbw-mq-text {
		text-transform: uppercase;
		letter-spacing: 0.05em;
	}

	// Without a separator the sep margins are gone — space the items directly.
	&.dbw-mq-nosep .dbw-slider-item {
		padding: 0 calc(var(--slide-gap, 40px) / 2);
	}
}

// Spotlight layout (v2.2): one logo at a time in a single slot, swapped on a
// timer by the frontend script. Every logo sits in the same grid cell, so the
// slot never changes height and the surrounding text never jumps.
.dbw-partner-slider.dbw-layout-spotlight {
	// The edge gradients exist to fade scrolling logos in and out — there is
	// nothing scrolling past an edge here, so they would just tint the logo.
	&::before,
	&::after {
		display: none;
	}

	// A single logo does not need the between-logos gap.
	.dbw-slider-item {
		padding: 0;
	}

	// Optional tint (one colour, or a colour per logo in cycle mode).
	//
	// Two mechanisms, in this order:
	//   1. The exact one — the item gets the colour as its background and the
	//      logo itself as a mask, so the logo takes the chosen colour pixel
	//      for pixel. The mask URL is attached by the frontend script (the
	//      editor preview attaches it directly), never by the saved markup,
	//      because post filtering strips url() values from style attributes.
	//   2. The fallback — an approximate CSS filter from --spot-filter, used
	//      while the script has not run (or cannot run) and inside capsules.
	// Four classes deep, so both outrank the general logo-colour rules and
	// the capsule reset.
	&.dbw-spot-tint .dbw-slider-item img {
		filter: var(--spot-filter, none);
		opacity: 1;
	}

	&.dbw-spot-tint .dbw-spot-masked {
		background-color: var(--spot-color, currentColor);
		-webkit-mask-image: var(--dbw-mask);
		mask-image: var(--dbw-mask);
		-webkit-mask-size: contain;
		mask-size: contain;
		-webkit-mask-position: center;
		mask-position: center;
		-webkit-mask-repeat: no-repeat;
		mask-repeat: no-repeat;
	}

	// The logo keeps its box (and stays in the accessibility tree) while the
	// coloured, masked surface behind it does the drawing.
	&.dbw-spot-tint .dbw-spot-masked img {
		opacity: 0;
	}

	// "Original colors on hover" also works on top of the spotlight tint.
	&.dbw-spot-tint.dbw-color-hover .dbw-slider-item:hover img {
		filter: none;
	}
}

.dbw-spotlight-stage {
	display: grid;
	width: 100%;
	align-items: center;

	> .dbw-slider-item {
		// Stack every logo in the same cell.
		grid-area: 1 / 1;
		opacity: 0;
		pointer-events: none;
		transition:
			opacity var(--spot-fade, 0.3s) ease,
			transform var(--spot-fade, 0.3s) ease;
	}

	> .dbw-spot-active {
		opacity: 1;
		pointer-events: auto;
		// The incoming logo waits for the outgoing one to clear the slot.
		// Without that head start both are half visible at the same time,
		// which reads as a double image rather than a hand-over.
		transition-delay: var(--spot-handover, 0.15s);
	}
}

// Horizontal position of the slot.
.dbw-spot-align-left .dbw-spotlight-stage {
	justify-items: start;
}

.dbw-spot-align-center .dbw-spotlight-stage {
	justify-items: center;
}

.dbw-spot-align-right .dbw-spotlight-stage {
	justify-items: end;
}

// Slide transition: the next logo rises into the slot, the previous one keeps
// moving up and out (.dbw-spot-out). The script strips that class again once
// the transition has finished, with transitions suppressed, so the logo is
// back below the slot before its next turn.
.dbw-spot-slide .dbw-spotlight-stage {
	> .dbw-slider-item {
		transform: translateY(var(--spot-shift, 22px));
	}

	> .dbw-spot-active {
		transform: translateY(0);
	}

	> .dbw-spot-out {
		transform: translateY(calc(-1 * var(--spot-shift, 22px)));
	}
}

// Hard cut: no transition at all.
.dbw-spot-none .dbw-spotlight-stage > .dbw-slider-item {
	transition: none;
}

// Used for a single frame while the script resets an outgoing logo.
.dbw-spotlight-stage > .dbw-spot-reset {
	transition: none !important;
}

@keyframes dbw-infinity-scroll {
	0% {
		transform: translateX(0);
	}
	// Exactly one logo set. The render filter sets --dbw-loop-shift per
	// track (the number of saved copies varies); -50 % is the old guess.
	100% {
		transform: translateX(var(--dbw-loop-shift, -50%));
	}
}

// Pure-CSS safety net for the initial reveal (see .dbw-partner-slider).
@keyframes dbw-reveal-fallback {
	to {
		opacity: 1;
	}
}

// Mobile responsive layout. The v1.5.2 clamp() on logo-height and the
// min() cap on padding already shrink each capsule proportionally with
// the viewport, but for extremely wide logos (5:1 / 6:1 aspect ratios)
// a single height-shrunk logo can still produce an item wider than a
// phone screen — see dbw-media.de's Academy slider where a 150 px
// user-height logo at 6:1 fills an entire 390 px row.
//
// Below 600 px we therefore lock the item to a fluid uniform width so
// ~2 capsules fit side by side, regardless of any individual logo's
// aspect ratio. The capsule then stretches to fill the item, and the
// image inside falls back to max-width:100% so very wide logos compress
// horizontally (object-fit: contain keeps the aspect correct and lets
// the min-height letterbox center them vertically). On wider viewports
// the carousel falls back to the natural content-sized layout, so
// desktop wide logos render at full natural width as before.
@media (max-width: 600px) {
	// v1.7.0: the uniform item width is now scoped to capsule mode — plain
	// (capsule-less) sliders keep their natural gap-driven spacing on phones
	// instead of being forced into 140–220 px cells with uneven gaps.
	.dbw-capsules .dbw-slider-item {
		width: clamp(140px, 46vw, 220px);
	}

	.dbw-capsules .dbw-capsule {
		width: 100%;
		justify-content: center;
	}

	.dbw-capsules .dbw-slider-item img {
		max-width: 100%;
	}

	// Safety net for capsule-less sliders: one very wide logo can never fill
	// more than ~80% of a phone row (object-fit: contain keeps the aspect).
	.dbw-slider-item img {
		max-width: 80vw;
	}

	// Optional fixed mobile logo height (v1.7.0). The block only emits
	// --logo-height-mobile when the user enabled the setting; without it the
	// fluid clamp() formula keeps applying via the fallback. All height rules
	// read --dbw-h, so overriding the variable here is the whole story.
	.dbw-slider-item {
		--dbw-h: var(
			--logo-height-mobile,
			clamp(28px, 12vw, var(--logo-height, 50px))
		);
	}

	// Capsule padding follows the mobile height when one is set (the block
	// emits pre-computed mobile paddings for the proportional presets).
	// The mobile variable appears in BOTH min() branches: when it is set the
	// min() collapses to exactly that value (the user's explicit choice wins
	// over the vw cap); when absent, the v1.5.2 formula applies unchanged.
	.dbw-capsules .dbw-capsule {
		padding:
			min(
				var(
					--capsule-pad-y-mobile,
					var(--capsule-pad-y, calc(var(--logo-height, 50px) * 0.3))
				),
				var(--capsule-pad-y-mobile, calc(12vw * 0.3))
			)
			min(
				var(
					--capsule-pad-x-mobile,
					var(--capsule-pad-x, calc(var(--logo-height, 50px) * 0.55))
				),
				var(--capsule-pad-x-mobile, calc(12vw * 0.55))
			);
	}
}

// RTL pages: the wrapper forces left-to-right for the scroll animation;
// give text inside the items (text marquee, captions) its direction back.
// :dir() follows the nearest dir attribute, so an LTR section inside an RTL
// page stays LTR.
.dbw-slider-item:dir(rtl) {
	direction: rtl;
}

// Static logo grid (v2.4): one set, wrapped and centred, no motion. Same
// layout the reduced-motion preference gets for a scrolling carousel.
.dbw-layout-grid {
	&::before,
	&::after {
		display: none;
	}

	.dbw-slider-track {
		animation: none;
		flex-wrap: wrap;
		justify-content: center;
		width: auto;
		row-gap: calc(var(--slide-gap, 40px) / 2);
	}

	.dbw-slider-wrapper {
		direction: inherit;
	}
}

// Accessibility: respect the reduced-motion preference. !important also
// overrides the inline animation applied by the frontend script. The pause
// button is pointless on a static carousel, so it is hidden too.
@media (prefers-reduced-motion: reduce) {
	// A stopped track would only ever show its first few logos, with the rest
	// cut off past the container edge. Wrap the row into a static grid
	// instead and drop the duplicated sets, so every logo is visible exactly
	// once. The duplicates are recognised by the aria-hidden they carry for
	// screen readers (v2.3; older saved markup gets it from the PHP filter).
	.dbw-slider-track {
		animation: none !important;
		flex-wrap: wrap;
		justify-content: center;
		width: auto;
		row-gap: 12px;
	}

	.dbw-slider-track > [aria-hidden="true"] {
		display: none;
	}

	// A static grid follows the page direction again.
	.dbw-slider-wrapper:dir(rtl) {
		direction: rtl;
	}

	.dbw-pause-btn {
		display: none;
	}

	// Spotlight mode never advances without motion, which would hide every
	// logo but the first one for good. Lay them all out side by side instead.
	.dbw-layout-spotlight .dbw-spotlight-stage {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: var(--slide-gap, 40px);
	}

	.dbw-spot-align-center .dbw-spotlight-stage {
		justify-content: center;
	}

	.dbw-spot-align-right .dbw-spotlight-stage {
		justify-content: flex-end;
	}

	.dbw-layout-spotlight .dbw-spotlight-stage > .dbw-slider-item {
		opacity: 1;
		transform: none;
		pointer-events: auto;
		transition: none;
	}
}
