/*
 * Campaign slider (template-parts/home/campaign.php, assets/js/home-campaign.js).
 * The frame sits in the page container with the banner radius and is a size container: every slide's art box is
 * sized and placed with container units, so the plate, the product layer, the LED glows and the reveal origin share
 * one geometry at every size.
 *
 * Side layout (wide screens): the 21:9 art covers the frame and slides sideways so the product box starts right
 * after the copy column (text left) or ends before it (text right).
 * Stack layout (phones, portrait tablets): the 4:5 art spans the width, sits as high as it must for the product to
 * clear the copy, and fades into the night ground the copy stands on.
 * Motion: the push (CSS, pausable), the intro (CSS, first paint), the reveal (GSAP, in the script).
 */

.campaign {
	--slide-dur: 7s;
	--push-dur: 8.2s;
	padding-bottom: clamp(28px, 3.4vw, 48px);
}

/* ---------- Frame ---------- */
.campaign__frame {
	/* The comp's inner margin: copy and tabs sit well in from the card's edge; the column gives the width back, so
	   where the copy ends (and the art's lead with it) does not move. */
	--pad: clamp(28px, 5.25cqw, 80px);
	--copy-w: min(560px, 44cqw);
	--controls-h: clamp(66px, 11cqh, 112px);
	--night: #0A1222;
	position: relative;
	isolation: isolate;
	/* The comp's card: 83 % of the viewport, ending a short gap above the fold. */
	height: clamp(560px, 83vh, 820px);
	overflow: hidden;
	overflow: clip;
	border-radius: var(--r-lg);
	clip-path: inset(0 round var(--r-lg));
	background: var(--night);
	color: var(--on-ink);
	container-type: size;
	touch-action: pan-y;
	-webkit-tap-highlight-color: transparent;
}
.campaign__frame ::selection { background: var(--paper); color: var(--ink); }

.campaign__slides { position: absolute; inset: 0; z-index: 1; }

/* ---------- Slides ---------- */
.campaign__slide { position: absolute; inset: 0; visibility: hidden; }
.campaign__slide.is-current,
.campaign__slide.is-leaving { visibility: visible; }
.campaign__slide.is-leaving { z-index: 1; }
.campaign__slide.is-entering { z-index: 2; }

/* The art box. Side layout: cover the frame, the product box just after the copy column. */
.campaign__art {
	--r: var(--r-d, 2.3333);
	--fx: var(--fx-d, 0.5);
	--fy: var(--fy-d, 0.5);
	--aw: max(100cqw, 100cqh * var(--r));
	--ah: max(100cqh, 100cqw / var(--r));
	--lead: calc(var(--pad) + var(--copy-w) + 2.2cqw);
	position: absolute;
	width: var(--aw);
	height: var(--ah);
	left: clamp(100cqw - var(--aw), var(--lead) - var(--bx, 0.4) * var(--aw), 0px);
	top: calc((100cqh - var(--ah)) * var(--fy));
	transform-origin: calc(var(--fx) * 100%) calc(var(--fy) * 100%);
}
.campaign__slide--text-right .campaign__art {
	left: clamp(100cqw - var(--aw), 100cqw - var(--lead) - (var(--bx, 0.4) + var(--bw, 0.36)) * var(--aw), 0px);
}
.campaign__plate,
.campaign__cutout { position: absolute; inset: 0; display: block; }
.campaign__plate img,
.campaign__cutout img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* The product layer: the maker's cut-out in its box of the wide scene, a hair nearer than the plate. */
.campaign__product {
	position: absolute;
	left: calc(var(--px) * 100%);
	top: calc(var(--py) * 100%);
	width: calc(var(--pw) * 100%);
	height: calc(var(--ph) * 100%);
	transform-origin: 50% 55%;
}
/* An approved camera plate (slides.json "camera"): its own box of the product layer, over the shadow and LED spill
   the layer keeps outside it, so the two move as one. The browser snaps each image's box to whole pixels, so two hard
   edges meeting on one line leave a hairline wherever they round apart. Instead the plate fades in over its outer
   24 px (--fw, --fh: where only its soft shadow lies) and the product layer, which carries the same render that far
   into the box, fades out over the same band: the two masks sum to one, and a pixel of rounding spreads over the
   band instead of drawing a line. */
.campaign__camera {
	--ramp-x: linear-gradient(90deg, transparent, #000 calc(var(--fw) * 100%), #000 calc(100% - var(--fw) * 100%), transparent);
	--ramp-y: linear-gradient(180deg, transparent, #000 calc(var(--fh) * 100%), #000 calc(100% - var(--fh) * 100%), transparent);
	position: absolute;
	left: calc(var(--cx) * 100%);
	top: calc(var(--cy) * 100%);
	width: calc(var(--cw) * 100%);
	height: calc(var(--ch) * 100%);
	-webkit-mask: var(--ramp-x), var(--ramp-y);
	-webkit-mask-composite: source-in;
	mask: var(--ramp-x), var(--ramp-y);
	mask-composite: intersect;
}
.campaign__product.has-camera > .campaign__cutout {
	--x0: calc(var(--cx) * 100%);
	--x1: calc((var(--cx) + var(--cw)) * 100%);
	--y0: calc(var(--cy) * 100%);
	--y1: calc((var(--cy) + var(--ch)) * 100%);
	--rx: calc(var(--fw) * var(--cw) * 100%);
	--ry: calc(var(--fh) * var(--ch) * 100%);
	/* 0.43 at mid-band, not 0.5: two half-covering layers of the same shadow stack lighter than the shadow
	   (1 - (1 - a/2)²), so this side holds a little more; even at the shadow's strongest edge (alpha 0.38) they then sum
	   to within half a level */
	--hole-x: linear-gradient(90deg, transparent var(--x0), rgb(0 0 0 / 0.43) calc(var(--x0) + var(--rx) / 2), #000 calc(var(--x0) + var(--rx)), #000 calc(var(--x1) - var(--rx)), rgb(0 0 0 / 0.43) calc(var(--x1) - var(--rx) / 2), transparent var(--x1));
	--hole-y: linear-gradient(180deg, transparent var(--y0), rgb(0 0 0 / 0.43) calc(var(--y0) + var(--ry) / 2), #000 calc(var(--y0) + var(--ry)), #000 calc(var(--y1) - var(--ry)), rgb(0 0 0 / 0.43) calc(var(--y1) - var(--ry) / 2), transparent var(--y1));
	/* everything, less the plate's box with the same soft edge */
	-webkit-mask: linear-gradient(#000 0 0), var(--hole-x), var(--hole-y);
	-webkit-mask-composite: xor, source-in;
	mask: linear-gradient(#000 0 0), var(--hole-x), var(--hole-y);
	mask-composite: exclude, intersect;
}
.campaign__focus { position: absolute; left: calc(var(--fx) * 100%); top: calc(var(--fy) * 100%); width: 0; height: 0; pointer-events: none; }

/* The warm LED glow at the manifest's LED points, breathing very gently over the LEDs already lit in the scene. */
.campaign__glows { position: absolute; inset: 0; pointer-events: none; mix-blend-mode: screen; }
.campaign__glow {
	position: absolute;
	left: calc(var(--x) * 100%);
	top: calc(var(--y) * 100%);
	width: var(--gs, 4%);
	aspect-ratio: 1;
	translate: -50% -50%;
	border-radius: 50%;
	background: radial-gradient(closest-side, rgb(255 240 210 / 0.5), rgb(255 206 140 / 0.24) 40%, rgb(255 180 100 / 0.08) 72%, rgb(255 170 90 / 0));
}
.campaign__glow::after {
	content: "";
	position: absolute;
	inset: -120%;
	border-radius: 50%;
	background: radial-gradient(closest-side, rgb(255 196 128 / 0.14), rgb(255 180 110 / 0.05) 55%, rgb(255 170 100 / 0));
}
.campaign__glows--p { display: none; }

/* The scrim behind the copy (verified by sampling the pixels behind the text). Its stops follow the copy column
   (--lead-x: where the column ends), so text that runs to the column's edge always stands on enough shade. */
.campaign__scrim {
	--lead-x: calc(var(--pad) + var(--copy-w));
	--shade: 6 12 28;
	--a0: 0.5; --a1: 0.4; --a2: 0.34; --a3: 0.1;
	--bottom: linear-gradient(0deg, rgb(var(--shade) / 0.6) 0%, rgb(var(--shade) / 0.28) max(12%, 64px), rgb(var(--shade) / 0) max(24%, 120px));
	position: absolute; inset: 0; pointer-events: none;
	background:
		linear-gradient(var(--scrim-dir, 90deg),
			rgb(var(--shade) / var(--a0)) 0,
			rgb(var(--shade) / var(--a1)) calc(var(--lead-x) * 0.5),
			rgb(var(--shade) / var(--a2)) var(--lead-x),
			rgb(var(--shade) / var(--a3)) calc(var(--lead-x) + 5cqw),
			rgb(var(--shade) / 0) calc(var(--lead-x) + 12cqw)),
		var(--bottom);
}
.campaign__slide--text-right .campaign__scrim { --scrim-dir: 270deg; }
/* A scene graded dark behind its copy already (slides.json "scrim": "soft"): a lighter, neutral dusk shade, so the
   plate's lavender wall and warm ground read through; the copy's secondary greys come up to hold 4.5:1 on it. */
.campaign__slide--scrim-soft .campaign__scrim {
	--shade: 24 24 34;
	--a0: 0.34; --a1: 0.28; --a2: 0.22; --a3: 0.07;
	--bottom: linear-gradient(0deg, rgb(var(--shade) / 0.5) 0%, rgb(var(--shade) / 0.22) max(12%, 64px), rgb(var(--shade) / 0) max(24%, 120px));
}
.campaign__slide--scrim-soft .campaign__specs { color: rgb(255 255 255 / 0.95); }
.campaign__slide--scrim-soft .campaign__price .price__tax { color: rgb(255 255 255 / 0.66); } /* the comp's grey TTC: 5.1:1 on the lightest ground behind it */
.campaign__slide--scrim-soft .campaign__wa { background-color: rgb(20 20 30 / 0.34); }
/* Its lit doorway stands just past the copy column: the specs line breaks before reaching it on narrower cards. */
.campaign__slide--scrim-soft .campaign__specs { max-width: 38cqw; }
.campaign__slide--light .campaign__scrim {
	--shade: 255 255 255;
	--a0: 0.9; --a1: 0.78; --a2: 0.52; --a3: 0.16;
	--bottom: linear-gradient(0deg, rgb(255 255 255 / 0.8) 0%, rgb(255 255 255 / 0.42) max(12%, 64px), rgb(255 255 255 / 0) max(24%, 120px));
}

/* ---------- Copy ---------- */
.campaign__copy {
	position: absolute;
	z-index: 2;
	left: var(--pad);
	top: 0;
	bottom: var(--controls-h);
	width: var(--copy-w);
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding-top: var(--pad);
}
.campaign__slide--text-right .campaign__copy { left: auto; right: var(--pad); }

.campaign__title {
	margin: 0;
	color: inherit;
	font-family: var(--font-display);
	font-weight: 600;
	font-size: min(var(--text-4xl), 10.4cqh, 5.6cqw);
	line-height: 1.02;
	letter-spacing: -0.028em;
	text-wrap: balance;
}
.campaign__name {
	margin: clamp(8px, 1.6cqh, 16px) 0 0; /* the name reads with the headline, as its line */
	font-size: var(--text-md);
	font-weight: 500;
	line-height: 1.35;
	color: inherit;
	text-wrap: pretty;
}
.campaign__name strong { font-weight: 800; }
.campaign__name-link { display: none; } /* phones only, below */
.campaign__specs {
	margin: 4px 0 0;
	font-size: var(--text-base);
	font-weight: 500;
	line-height: 1.45;
	color: rgb(255 255 255 / 0.86);
	text-wrap: balance;
}
.campaign__spec { white-space: nowrap; }
.campaign__dot { color: rgb(255 255 255 / 0.5); }

.campaign__buy {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 0 22px;
	margin-top: clamp(10px, 2.2cqh, 20px);
}
.campaign__price {
	color: inherit;
	font-size: clamp(2rem, 1.2rem + 1.9vw, 3.125rem);
	letter-spacing: -0.03em;
	font-variant-numeric: proportional-nums; /* one price, not a column: the comp's proportional figures */
}
/* TTC sits on the price's baseline, a narrow step after it (the space less a quarter of its own size). */
.campaign__price .price__tax { font-size: max(14px, 0.36em); letter-spacing: normal; margin-left: -0.25em; color: rgb(255 255 255 / 0.8); }
.campaign__more {
	color: var(--on-ink);
	min-height: 44px;
	font-size: var(--text-base);
	text-decoration: underline;
	text-decoration-color: transparent;
	text-underline-offset: 0.28em;
	text-decoration-thickness: 1.5px;
	transition: text-decoration-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.campaign__more:hover { color: var(--on-ink); text-decoration-color: currentColor; }
.campaign__more:focus-visible { outline-offset: 2px; border-radius: 4px; }

.campaign__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	margin-top: clamp(12px, 2.2cqh, 18px);
}
.campaign__actions .btn { --btn-h: 52px; padding: 0 1.45em; }
.campaign__add .icon { width: 1.2em; height: 1.2em; margin-left: -0.15em; }
.campaign__wa { -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); background-color: rgb(6 12 28 / 0.2); }
@media (hover: hover) { .campaign__wa:hover { background-color: var(--paper); } }

/* Light scenes: ink copy. */
.campaign__slide--light { color: var(--ink); }
.campaign__slide--light .campaign__specs { color: var(--ink-2); }
.campaign__slide--light .campaign__dot { color: var(--muted); }
.campaign__slide--light .campaign__price .price__tax { color: var(--ink-2); }
.campaign__slide--light .campaign__more,
.campaign__slide--light .campaign__more:hover { color: var(--ink); }
.campaign__slide--light .campaign__wa { --btn-fg: var(--ink); --btn-line: var(--ink); --btn-bg-hover: var(--ink); --btn-line-hover: var(--ink); --btn-bg-press: #1F2125; background-color: rgb(255 255 255 / 0.3); }
@media (hover: hover) { .campaign__slide--light .campaign__wa:hover { color: var(--on-ink); background-color: var(--ink); } }
.campaign__slide--light ::selection { background: var(--action); color: var(--on-action); }

/* ---------- Controls: tabs with progress lines, pause / play, prev / next ---------- */
.campaign__controls {
	--ctl: var(--on-ink);
	--ctl-dim: rgb(255 255 255 / 0.8);
	--ctl-track: rgb(255 255 255 / 0.34);
	--ctl-track-on: rgb(255 255 255 / 0.85);
	position: absolute;
	z-index: 4;
	left: var(--pad);
	right: calc(var(--pad) - 8px);
	bottom: clamp(14px, 3cqh, 26px);
	display: flex;
	flex-direction: row-reverse;
	align-items: center;
	justify-content: space-between;
	gap: 24px;
	color: var(--ctl);
	transition: color var(--dur-3) var(--ease-out);
}
.campaign[data-tone="light"] .campaign__controls {
	--ctl: var(--ink);
	--ctl-dim: #262A31; /* between ink and ink-2: holds 4.5:1 even where a light scene darkens at the bottom */
	--ctl-track: rgb(0 0 0 / 0.22);
	--ctl-track-on: rgb(0 0 0 / 0.6);
}

.campaign__tabs {
	flex: 1 1 auto;
	min-width: 0;
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: minmax(0, 196px);
	gap: clamp(14px, 2cqw, 26px);
}
.campaign__tab {
	position: relative;
	min-width: 0;
	min-height: 48px;
	padding: 16px 0 6px;
	border: 0;
	background: none;
	color: var(--ctl-dim);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	font-weight: 700;
	line-height: 1.2;
	text-align: left;
	cursor: pointer;
	transition: color var(--dur-2) var(--ease-out);
}
.campaign__tab-label { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.campaign__track {
	position: absolute;
	left: 0; right: 0; top: 4px;
	height: 3px;
	border-radius: 3px;
	background: var(--ctl-track);
	overflow: hidden;
	transition: background-color var(--dur-2) var(--ease-out);
}
.campaign__fill {
	position: absolute; inset: 0;
	border-radius: inherit;
	background: var(--accent);
	transform: scaleX(0);
	transform-origin: 0 50%;
}
.campaign__tab[aria-current="true"] { color: var(--ctl); }
.campaign__tab[aria-current="true"] .campaign__track { background: var(--ctl-track-on); }
@media (hover: hover) {
	.campaign__tab:hover { color: var(--ctl); }
	.campaign__tab:hover .campaign__track { background: var(--ctl-track-on); }
}
.campaign__tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 6px; }

/* Progress: the current tab fills while the slide plays; stopped (or reduced motion) shows it full. */
.campaign.is-auto .campaign__tab[aria-current="true"] .campaign__fill { animation: campaign-fill var(--slide-dur) linear forwards; }
.campaign:not(.is-auto) .campaign__tab[aria-current="true"] .campaign__fill { transform: none; transition: transform var(--dur-3) var(--ease-out); }
/* Held (hover, keyboard focus, hidden tab, out of view, a swipe): the fill stops where it is. Same weight as the rule
   that starts it, so the shorthand there cannot reset the play state. */
.campaign.is-held .campaign__tab[aria-current="true"] .campaign__fill { animation-play-state: paused; }
@keyframes campaign-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.campaign__buttons { display: flex; gap: 8px; flex: none; }
.campaign__btn {
	width: 44px; height: 44px;
	display: inline-grid; place-items: center;
	flex: none;
	border: 0;
	border-radius: 50%;
	background: var(--paper);
	color: var(--ink);
	box-shadow: var(--shadow-float);
	cursor: pointer;
	transition: background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out), transform var(--dur-2) var(--ease-spring);
}
.campaign__btn .icon { width: 20px; height: 20px; }
@media (hover: hover) { .campaign__btn:hover { background: var(--mist-2); } }
.campaign__btn:active { transform: scale(0.92); }
.campaign__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Pause / play is the quiet one, a glass ring, so the arrows stay the obvious way forward. */
.campaign__btn--play {
	margin-right: 4px;
	background: rgb(6 12 28 / 0.28);
	color: var(--on-ink);
	box-shadow: inset 0 0 0 1.5px rgb(255 255 255 / 0.72);
	-webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
@media (hover: hover) { .campaign__btn--play:hover { background: rgb(6 12 28 / 0.5); } }
.campaign[data-tone="light"] .campaign__btn--play { background: rgb(255 255 255 / 0.5); color: var(--ink); box-shadow: inset 0 0 0 1.5px rgb(0 0 0 / 0.6); }
.campaign__btn--play .icon { width: 18px; height: 18px; }
.campaign__btn--play .campaign__icon-play { display: none; margin-left: 2px; }
.campaign.is-stopped .campaign__btn--play .campaign__icon-pause { display: none; }
.campaign.is-stopped .campaign__btn--play .campaign__icon-play { display: block; }

/* Without JavaScript the first slide stands alone. */
.campaign:not(.is-ready) .campaign__controls { display: none; }

/* The iris: a thin ring riding the edge of the reveal, as if the lens opened. */
.campaign__iris {
	position: absolute; left: 0; top: 0; z-index: 3;
	width: 10px; height: 10px;
	border-radius: 50%;
	box-shadow: inset 0 0 0 2.5px rgb(255 255 255 / 0.5), 0 0 32px rgb(16 130 254 / 0.22);
	pointer-events: none;
	opacity: 0;
	visibility: hidden;
}

/* ---------- Motion (none of it under reduced motion) ---------- */
@media (prefers-reduced-motion: no-preference) {
	/* The push: the plate moves in 1.00 to 1.06 across the slide; the product layer (inside it) to 1.084 overall. */
	.campaign__slide.is-current .campaign__art,
	.campaign__slide.is-leaving .campaign__art { animation: campaign-push var(--push-dur) linear both; }
	.campaign__slide.is-current .campaign__product,
	.campaign__slide.is-leaving .campaign__product { animation: campaign-push-near var(--push-dur) linear both; }

	/* The LED glow breathes. */
	.campaign__slide.is-current .campaign__glow { animation: campaign-breathe 5.6s var(--ease-in-out) infinite alternate; }

	/* First paint: the same moves as a change, without hiding the image (it is the LCP). */
	.campaign.is-intro .is-current .campaign__copy > * { animation: campaign-rise 0.9s var(--ease-out-expo) backwards; }
	.campaign.is-intro .is-current .campaign__copy > :nth-child(1) { animation-delay: 0.1s; }
	.campaign.is-intro .is-current .campaign__copy > :nth-child(2) { animation-delay: 0.16s; }
	.campaign.is-intro .is-current .campaign__copy > :nth-child(3) { animation-delay: 0.22s; }
	.campaign.is-intro .is-current .campaign__copy > :nth-child(4) { animation-delay: 0.28s; }
	.campaign.is-intro .is-current .campaign__copy > :nth-child(5) { animation-delay: 0.34s; }
	.campaign.is-intro .is-current .campaign__product { animation: campaign-push-near var(--push-dur) linear both, campaign-product-in 1.1s var(--ease-out-expo) 0.05s backwards; }
	.campaign.is-intro .is-current .campaign__scrim { animation: campaign-fade 0.8s var(--ease-out) backwards; }
	.campaign.is-intro .campaign__controls { animation: campaign-fade 0.7s var(--ease-out) 0.45s backwards; }

	/* Held or paused: the push stops too (after the intro rules, at their weight, so it wins over them). The pause
	   button stops all of the slider's motion, not only the rotation; the product's entrance (the intro's second
	   animation) still finishes, so a pause in the first second never leaves the camera half faded in. */
	.campaign.is-held .campaign__slide .campaign__art,
	.campaign.is-stopped .campaign__slide .campaign__art { animation-play-state: paused; }
	.campaign.is-held.is-intro .campaign__slide.is-current .campaign__product,
	.campaign.is-stopped.is-intro .campaign__slide.is-current .campaign__product,
	.campaign.is-held .campaign__slide .campaign__product,
	.campaign.is-stopped .campaign__slide .campaign__product { animation-play-state: paused, running; }
}
@keyframes campaign-push { from { scale: 1; } to { scale: 1.06; } }
@keyframes campaign-push-near { from { scale: 1; } to { scale: 1.0226; } }
@keyframes campaign-breathe { from { opacity: 0.7; scale: 0.96; } to { opacity: 1; scale: 1.04; } }
@keyframes campaign-rise { from { opacity: 0; transform: translateY(26px); } }
@keyframes campaign-product-in { from { opacity: 0; transform: translateY(8%) scale(0.96); } }
@keyframes campaign-fade { from { opacity: 0; } }

/* ---------- Side layout on wide desktops: the comp's boxes ----------
   The copy block keeps the comp's rhythm: the name line sits where the comp sets its specs, the specs follow it
   tight, and the price and the buttons stand on the comp's lines. The block centres a little low (the comp's own
   optical centre, where its name line rode above the headline). Tabs, arrows and their gaps are the comp's. */
@media (min-width: 1280px) {
	.campaign__copy { padding-top: calc(var(--pad) + 3cqh); }
	.campaign__specs { margin-top: 2px; line-height: 1.3; } /* tight, so the price stands on the comp's line */
	.campaign__buy { margin-top: 6px; } /* a breath between the specs and the price */
	.campaign__actions { margin-top: clamp(8px, 1.45cqh, 20px); }
	.campaign__actions .btn { --btn-h: 48px; }
	.campaign__controls { bottom: clamp(12px, 3.35cqh, 28px); right: max(20px, calc(var(--pad) - 26px)); }
	/* The comp's arrows centred on the tabs' line (above the tab box, which carries the track on its top edge); the
	   tabs step in a few pixels from the copy, as the comp sets them. */
	.campaign__tabs { margin-left: 0.25cqw; }
	.campaign__tab { font-weight: 600; } /* the comp's labels: lighter than the bold copy */
	.campaign__track { height: 4px; right: 4px; } /* the comp's track: a 4 px line ending short of the next tab */
	.campaign__controls { --ctl-track-on: rgb(255 255 255 / 0.94); }
	.campaign__buttons { align-self: flex-start; margin-top: -8px; }
	.campaign__tabs { grid-auto-columns: minmax(0, 190px); gap: clamp(12px, 1.25cqw, 20px); }
	.campaign__buttons { gap: 3px; }
	.campaign__btn { width: 48px; height: 48px; }
}

/* ---------- Side layout on narrower desktops and landscape tablets ---------- */
@media (max-width: 1279px) {
	/* Narrower cards keep the tighter margin, so both buttons stay on one row. */
	.campaign__frame { --pad: clamp(28px, 5.2cqw, 80px); --copy-w: min(480px, 47cqw); }
	/* A slide whose product would run past the card here (slides.json "narrow_scale") shows it a little smaller,
	   anchored at its box's left edge so the mount stays on its wall. */
	.campaign__product {
		width: calc(var(--pw) * var(--ns, 1) * 100%);
		height: calc(var(--ph) * var(--ns, 1) * 100%);
		top: calc((var(--py) + var(--ph) * (1 - var(--ns, 1)) * 0.55) * 100%);
	}
	.campaign__actions .btn { --btn-h: 48px; padding: 0 1.2em; }
	.campaign__title { font-size: min(var(--text-4xl), 10cqh, 6cqw); }
}
/* Landscape tablets and small laptops: both buttons keep one row; under 1100 px the product also sits a little
   closer to the copy. */
@media (max-width: 1279px) and (min-aspect-ratio: 1/1) {
	.campaign__actions { gap: 10px; }
	.campaign__actions .btn { padding: 0 1em; font-size: 0.9375rem; }
}
@media (max-width: 1099px) and (min-aspect-ratio: 1/1) {
	.campaign__art { --lead: calc(var(--pad) + var(--copy-w) + 1cqw); }
}
/* Short landscape screens (phones turned sideways, short desktop windows): the frame fits the space under the
   header, the specs line steps out, both buttons keep one row. */
@media (max-height: 620px) and (min-aspect-ratio: 1/1) {
	.campaign__frame {
		--controls-h: 60px;
		--pad: clamp(28px, 5.2cqw, 80px);
		--copy-w: min(500px, 50cqw);
		height: max(220px, calc(100svh - var(--header-h) - var(--utility-h) - var(--bottom-bar-h) - 40px));
	}
	.campaign__copy { padding-top: 12px; }
	.campaign__specs { display: none; }
	.campaign__name { margin-top: 6px; font-size: var(--text-base); }
	.campaign__title { font-size: min(var(--text-3xl), 10.6cqh, 4.6cqw); }
	.campaign__price { font-size: clamp(1.5rem, 1rem + 1.5vw, 2rem); }
	.campaign__buy { margin-top: 4px; }
	.campaign__actions { margin-top: 6px; flex-wrap: nowrap; }
	.campaign__actions .btn { --btn-h: 44px; }
	.campaign__controls { bottom: clamp(8px, 2.4cqh, 16px); }
}
@media (max-height: 400px) and (min-aspect-ratio: 1/1) {
	.campaign__frame { --controls-h: 52px; }
	.campaign__name { margin-top: 4px; }
}
/* Phones turned sideways: the round WhatsApp button, and the tabs as progress bars (names kept for screen readers). */
@media (max-height: 620px) and (min-aspect-ratio: 1/1) and (max-width: 1023px) {
	.campaign__frame { border-radius: var(--r-md); clip-path: inset(0 round var(--r-md)); }
	.campaign__actions .campaign__wa { width: 44px; padding: 0; flex: none; }
	.campaign__wa-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
	.campaign__tab { min-height: 44px; padding: 0; }
	.campaign__track { top: 50%; margin-top: -1.5px; }
	.campaign__tab-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}

/* ---------- Stack layout: phones and portrait tablets ---------- */
@media (max-width: 1023px) and (max-aspect-ratio: 1/1) {
	.campaign__frame {
		--pad: clamp(20px, 5.4cqw, 48px);
		--controls-h: 66px;
		--copy-h: 380px;
		height: clamp(560px, 78svh, 880px);
	}
	/* The 4:5 art spans the width; it rises until its product clears the copy, then fades into the night. */
	.campaign__art,
	.campaign__slide--text-right .campaign__art {
		--r: var(--r-p, 0.8);
		--fx: var(--fx-p, 0.5);
		--fy: var(--fy-p, 0.45);
		--aw: 100cqw;
		--ah: calc(100cqw / var(--r));
		left: 0;
		top: clamp(-0.42 * var(--ah), (100cqh - var(--copy-h)) * 0.5 - var(--fy) * var(--ah), 0px);
		-webkit-mask-image: linear-gradient(to bottom, #000 84%, transparent 99.5%);
		mask-image: linear-gradient(to bottom, #000 84%, transparent 99.5%);
	}
	/* A light scene fades into its own ground (slides.json "ground", lifted toward paper), not into the night,
	   so no cool grey band opens between the warm photo and the white scrim. */
	.campaign__slide--light { background: color-mix(in srgb, var(--ground, var(--mist)) 24%, var(--paper)); }
	.campaign__product { display: none; }
	.campaign__glows--d { display: none; }
	.campaign__glows--p { display: block; }

	.campaign__scrim,
	.campaign__slide--text-right .campaign__scrim {
		background: linear-gradient(0deg, rgb(6 12 28 / 0.9) 0%, rgb(6 12 28 / 0.66) calc(var(--copy-h) * 0.5), rgb(6 12 28 / 0.4) calc(var(--copy-h) * 0.85), rgb(6 12 28 / 0) calc(var(--copy-h) * 1.2));
	}
	.campaign__slide--light .campaign__scrim,
	.campaign__slide--light.campaign__slide--text-right .campaign__scrim {
		background: linear-gradient(0deg, rgb(255 255 255 / 0.94) 0%, rgb(255 255 255 / 0.84) calc(var(--copy-h) * 0.5), rgb(255 255 255 / 0.5) calc(var(--copy-h) * 0.85), rgb(255 255 255 / 0) calc(var(--copy-h) * 1.15));
	}

	.campaign__copy,
	.campaign__slide--text-right .campaign__copy {
		left: var(--pad);
		right: var(--pad);
		top: auto;
		width: auto;
		padding-top: 0;
		justify-content: flex-end;
	}
	.campaign__title { font-size: min(var(--text-4xl), 9.6cqw, 7.4cqh); max-width: 15ch; }
	.campaign__slide--scrim-soft .campaign__specs { max-width: none; }
	/* Its phone art runs down to the card's foot (the ground continues below the threshold): it fades out only at the
	   very end, and the shade over it eases so the doorway and its ground read behind the buttons. */
	.campaign__slide--scrim-soft .campaign__art {
		-webkit-mask-image: linear-gradient(to bottom, #000 90%, transparent 100%);
		mask-image: linear-gradient(to bottom, #000 90%, transparent 100%);
	}
	.campaign__slide--scrim-soft .campaign__scrim {
		background: linear-gradient(0deg, rgb(6 12 28 / 0.84) 0%, rgb(6 12 28 / 0.58) calc(var(--copy-h) * 0.5), rgb(6 12 28 / 0.34) calc(var(--copy-h) * 0.85), rgb(6 12 28 / 0) calc(var(--copy-h) * 1.2));
	}
	.campaign__price { font-size: clamp(1.75rem, 1.3rem + 1.9vw, 2.5rem); }

	.campaign__controls { left: var(--pad); right: var(--pad); bottom: 12px; gap: 16px; }
	.campaign__tabs { grid-auto-columns: minmax(0, 1fr); gap: 12px; }
}

@media (max-width: 767px) and (max-aspect-ratio: 1/1) {
	.campaign { padding-bottom: 16px; } /* the category row follows closely, as in the phone comp */
	.campaign__frame {
		--pad: 20px;
		--controls-h: 52px;
		border-radius: var(--r-md);
		clip-path: inset(0 round var(--r-md));
	}
	.campaign__title { font-size: min(2rem, 9cqw, 7.9cqh); line-height: 1.04; } /* the comp's 30-32 px */
	.campaign__name { margin-top: 8px; font-size: var(--text-base); }
	.campaign__buy { margin-top: 8px; justify-content: space-between; gap: 0 12px; }
	.campaign__price { font-size: clamp(1.75rem, 1.2rem + 2.4vw, 2.125rem); }
	.campaign__actions { flex-wrap: nowrap; gap: 8px; margin-top: 6px; }
	.campaign__actions .btn { --btn-h: 44px; }
	.campaign__add { flex: 1 1 auto; min-width: 0; }
	/* The bottom bar already says "Commander sur WhatsApp": here it is the round WhatsApp button. */
	.campaign__actions .campaign__wa { width: 44px; padding: 0; flex: none; }
	.campaign__wa .icon { width: 22px; height: 22px; }
	.campaign__wa-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

	/* Tabs become progress bars; their names stay for screen readers. */
	.campaign__controls { bottom: 4px; gap: 10px; right: calc(var(--pad) - 10px); }
	.campaign__buttons .campaign__btn--prev,
	.campaign__buttons .campaign__btn--next { display: none; }
	.campaign__btn--play,
	.campaign[data-tone="light"] .campaign__btn--play { margin: 0; box-shadow: none; background: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
	@media (hover: hover) {
		.campaign__btn--play:hover { background: rgb(255 255 255 / 0.1); }
		.campaign[data-tone="light"] .campaign__btn--play:hover { background: rgb(0 0 0 / 0.06); }
	}
	.campaign__tab { min-height: 44px; padding: 0; }
	.campaign__track { top: 50%; margin-top: -1.5px; }
	.campaign__tab-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
/* Phones: the comp's card, about half the screen (48svh), so the category tiles show under it in the first
   viewport. As in the comp, the card holds the headline, the name, the price and the buttons on every slide: the
   specs line and "Voir le produit" step out (the product page carries the specs; the tiles below lead to it).
   A slide with "phone_zoom" shows its art a little larger, anchored at "phone_anchor" and lowered by "phone_shift",
   so a scene element (the lantern of slide 01) stands clear of the headline. */
@media (max-width: 767px) and (max-aspect-ratio: 1/1) {
	.campaign__frame { --copy-h: 270px; height: clamp(400px, 48svh, 560px); } /* 400: short phones keep the products whole */
	.campaign__specs,
	.campaign__more { display: none; }
	/* The name line takes over as the product link: a 44 px tall target (the padding is given back by the margin, so
	   the line keeps its place), underlined at rest in the copy's own colour (ink on a light scene, white on a dark
	   one: the text's own contrast), the stroke thickening on press, and the focus ring. Phones have no hover, so the
	   underline is the cue that the line leads to the product. */
	.campaign__name-text { display: none; }
	.campaign__name-link {
		display: inline-block;
		padding: 11px 6px;
		margin: -11px -6px;
		border-radius: 6px;
		color: inherit;
		text-decoration: underline;
		text-decoration-color: currentColor;
		text-underline-offset: 0.24em;
		text-decoration-thickness: 1px;
		transition: text-decoration-thickness var(--dur-1) var(--ease-out);
	}
	.campaign__name-link:hover,
	.campaign__name-link:active { color: inherit; text-decoration-thickness: 2px; }
	.campaign__name-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }
	/* A light scene's card meets the white page: its frame keeps a hairline, so the rounded lower edge reads where
	   the scrim has lifted the scene to near white (the dark scenes carry their own edge). */
	.campaign__frame::after {
		content: "";
		position: absolute;
		inset: 0;
		z-index: 5;
		border-radius: inherit;
		box-shadow: inset 0 0 0 1px var(--hairline);
		opacity: 0;
		pointer-events: none;
		transition: opacity var(--dur-3) var(--ease-out);
	}
	.campaign[data-tone="light"] .campaign__frame::after { opacity: 1; }
	.campaign__art,
	.campaign__slide--text-right .campaign__art {
		--aw: calc(100cqw * var(--pz, 1));
		--ah: calc(var(--aw) / var(--r));
		left: calc((100cqw - var(--aw)) * var(--pzx, 0.5));
		top: clamp(-0.42 * var(--ah), (100cqh - var(--copy-h)) * 0.5 - var(--fy) * var(--ah) + var(--pdy, 0) * var(--ah), 0px);
	}
}

/* Short phones (an iPhone SE's 667 px and less): the card stops 20 px shorter and its art stays where the 400 px card
   puts it (the copy rises over the art's foot, still clear of every product), so the labelled category tiles reach the
   first viewport, as in the phone comp's own 390x699 frame (home-catalogue.css does the rest). */
@media (max-width: 767px) and (max-aspect-ratio: 1/1) and (max-height: 700px) {
	.campaign { padding-bottom: 12px; }
	.campaign__frame { height: 380px; }
	.campaign__art,
	.campaign__slide--text-right .campaign__art {
		top: clamp(-0.42 * var(--ah), (400px - var(--copy-h)) * 0.5 - var(--fy) * var(--ah) + var(--pdy, 0) * var(--ah), 0px);
	}
}

@media (prefers-reduced-motion: reduce) {
	.campaign__btn, .campaign__tab, .campaign__track { transition: none; }
}
