/**
 * Zone Six Worlds — the Dial, and the hub header built from the same stage.
 *
 * How the switching works, and why it looks like this:
 *
 * Six radio inputs sit as the first children of .zsw-dial, before the stage. A dial
 * button is a <label for> pointing at one of them, so a click activates that radio with
 * no JavaScript at all. The stage then reacts through sibling :checked selectors.
 *
 * The selectors match on POSITION — :nth-of-type on the radios, :nth-child on the layers
 * and buttons — never on the input ids. That is deliberate: the ids carry a per-instance
 * random suffix so two Dials on one page don't share a radio group, and if the CSS named
 * them it would have to be generated per instance and inlined. Position is stable, so
 * one static stylesheet serves any number of Dials.
 *
 * The transition is the circular wipe from the design: the incoming world grows from a
 * point at the bottom centre over one second, while the outgoing one is held in place
 * until the wipe has finished and only then collapsed (transition-delay 0s 1s), so no
 * gap of background ever shows through.
 */

.zsw-dial {
	position: relative;
	height: var(--zsw-stage-h, 740px);
	min-height: 380px;
	overflow: hidden;
	background: #0f0f14;
}

/* The radios drive everything and are never seen. Not display:none — a hidden input
 * can still be focused and read, which is what makes the dial keyboard-operable. */
.zsw-dial__radio {
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
	margin: 0;
}

.zsw-dial__stage {
	position: absolute;
	inset: 0;
}

/**
 * All six layers sit on top of each other, filling the stage.
 *
 * The selector carries three classes on purpose. Every layer also has the kit's
 * `.world` class, and `.zsw .world` sets `position: relative` — two classes, so it
 * outranks a bare `.zsw-dial__layer` and the layers stack down the page in normal flow
 * instead of overlaying. Since the load order of the kit and this file isn't guaranteed
 * by the stylesheet dependencies, winning on specificity rather than on order is the
 * only reliable fix.
 */
.zsw-dial__stage > .zsw-dial__layer.world,
.zsw-dial__layer {
	position: absolute;
	inset: 0;
	clip-path: circle(0% at 50% 95%);
	z-index: 1;
	/* Collapse only after the incoming layer's wipe has covered it. */
	transition: clip-path 0s linear 1s;
}

/* Position-matched pairs: radio N selected shows layer N. */
.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1),
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2),
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3),
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4),
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5),
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) {
	clip-path: circle(150% at 50% 95%);
	z-index: 3;
	transition: clip-path 1s cubic-bezier(.7, 0, .2, 1);
}

/**
 * Only the stage you can see is allowed to animate.
 *
 * All six stages are always in the page — that is what makes the switching CSS-only —
 * and five of them are clipped away at any moment. A clip-path hides a layer; it does
 * not stop it. Every stage kept all of its motion running regardless, and several of
 * those motions are the expensive kind: huge conic gradients spinning under blur
 * filters, which force a full repaint of their area every frame. On staging that was
 * enough to saturate the page's main thread outright — twelve worlds' worth of motion
 * rendered to show one.
 *
 * So play state is one inherited custom property. Every layer defaults it to paused,
 * the checked layer sets it to running, and every animated descendant reads it. The
 * `!important` is there because each animation is declared with the `animation`
 * shorthand, which resets play state to running and outranks a descendant selector —
 * this is a performance control, and it has to win.
 */
.zsw-dial__layer {
	--zsw-play: paused;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1),
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2),
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3),
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4),
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5),
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) {
	--zsw-play: running;
}

/* Scrolled out of view, even the visible stage stops. The script sets is-offscreen. */
.zsw-dial.is-offscreen .zsw-dial__layer {
	--zsw-play: paused !important;
}

.zsw-dial__layer *,
.zsw-dial__layer *::before,
.zsw-dial__layer *::after,
.zsw-hdr *,
.zsw-hdr *::before,
.zsw-hdr *::after {
	animation-play-state: var(--zsw-play, running) !important;
}

/* A hub header is a single stage, always the one showing; it only stops offscreen. */
.zsw-hdr {
	--zsw-play: running;
}

.zsw-hdr.is-offscreen {
	--zsw-play: paused;
}

/* The copy rises in once the wipe is most of the way across. */
.zsw-dial__layer .zsw-l__inner {
	opacity: 0;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-l__inner,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-l__inner,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-l__inner,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-l__inner,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-l__inner,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-l__inner {
	animation: zsw-up .8s cubic-bezier(.2, .8, .2, 1) .4s both;
}

/* ------------------------------------------------------------- the stage copy
 *
 * Each world's stage has its own layout, because in the Dial artboard each world has
 * its own furniture. What is shared is only the frame: a padded box inside the layer,
 * clearing the dial at the bottom.
 *
 * Every text block that can grow is clamped to a line count. The stage is a fixed
 * height and the copy is whatever Hutch titled the post — Zone Six headlines run long
 * ("Deadpool & Wolverine: The Door Everything Else Walked Through") — so without a
 * ceiling the copy overflows the stage and the lower items get clipped off.
 */

.zsw-l__inner {
	position: relative;
	z-index: 2;
	height: 100%;
	padding: clamp(22px, 3.4vw, 52px);
	/* Clears the dial: 58px of button plus its 22px offset, plus air. */
	padding-bottom: clamp(92px, 11vw, 116px);
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: center;
	gap: clamp(8px, 1.2vw, 16px);
	color: var(--zsw-world-ink, #fff);
}

/* The two-column worlds: copy on the left, art on the right. */
.zsw-l__inner--marvel,
.zsw-l__inner--doctor-who,
.zsw-l__inner--wheel-of-time,
.zsw-l__inner--litrpg {
	display: grid;
	grid-template-columns: repeat(12, minmax(0, 1fr));
	gap: clamp(18px, 2.4vw, 36px);
	align-items: center;
}

/**
 * Shared clamps, so each world's own rules stay about type rather than overflow.
 *
 * Not `-webkit-line-clamp`. Every element that needs clamping here is a flex or grid
 * item — a headline inside a flex lead panel, a card inside a grid of cards — and
 * Chrome blockifies flex and grid items, so `display: -webkit-box` never survives on
 * them (it computes to `flow-root`). The clamp silently did nothing: the cards were
 * simply cut off at whatever height the grid row stretched to, which left a sliver of
 * the next line showing under the last one.
 *
 * A max-height in `lh` units clamps at an exact number of the element's own lines and
 * does not care what formatting context the element is in. `em` covers browsers
 * without `lh`, using each element's known line-height.
 */
.zsw-l__clamp-3,
.zsw-mv__headline,
.zsw-who__headline,
.zsw-bt__headline,
.zsw-dn__headline,
.zsw-wot__headline,
.zsw-lt__headline {
	overflow: hidden;
	max-height: 3.5em;
}

/* Deks: two lines. They sit under a headline that already says what the post is. */
.zsw-l__clamp-2,
.zsw-who__dek,
.zsw-wot__dek {
	overflow: hidden;
	max-height: 3em;
}

/**
 * Cards and tiles: four lines, measured rather than guessed.
 *
 * Two lines cut "Thunderbolts: What Do You Do With the…" off mid-thought on the first
 * render against real content; three still clipped two of three Marvel panels, a Who
 * card and every Wheel of Time card on a 1074px window, because three cards share a
 * seven-column copy block and each is only ~185px wide. Four lines clears every real
 * title on every stage. The stages have the room — each except Marvel had 200px+ of
 * spare height — so this costs nothing but a slightly taller card.
 *
 * A card is a promise of a post, and half a title is not one.
 */
.zsw-mv__panel,
.zsw-who__card,
.zsw-wot__card,
.zsw-lt__slot,
.zsw-bt__row,
.zsw-dn__gap-title {
	overflow: hidden;
	max-height: 5.2em;
}

/* Cinzel is a wide face; at four lines one Wheel of Time card still clipped. */
.zsw-wot__card {
	max-height: 6.5em;
}

@supports (max-height: 3lh) {
	.zsw-l__clamp-3,
	.zsw-mv__headline,
	.zsw-who__headline,
	.zsw-bt__headline,
	.zsw-dn__headline,
	.zsw-wot__headline,
	.zsw-lt__headline {
		max-height: 3lh;
	}

	.zsw-l__clamp-2,
	.zsw-who__dek,
	.zsw-wot__dek {
		max-height: 2lh;
	}

	.zsw-mv__panel,
	.zsw-who__card,
	.zsw-wot__card,
	.zsw-lt__slot,
	.zsw-bt__row,
	.zsw-dn__gap-title {
		max-height: 4lh;
	}

	.zsw-wot__card {
		max-height: 5lh;
	}
}

/* The clamp caps a card's height, so the padding has to live inside it. */
.zsw-mv__panel,
.zsw-who__card,
.zsw-wot__card,
.zsw-lt__slot,
.zsw-bt__row {
	box-sizing: content-box;
}

.zsw-who__dek,
.zsw-wot__dek {
	font-size: clamp(14px, 1.1vw, 16px);
	line-height: 1.5;
}

/**
 * The plain stage.
 *
 * Only reached by a seventh world added through the `zsw_worlds` filter, which has no
 * furniture of its own. Each of the six real worlds ignores all of this.
 */
.zsw-l__world {
	display: block;
	font-size: clamp(34px, 6vw, 82px);
	line-height: .95;
	color: #fff;
	text-shadow: 0 4px 30px rgba(0, 0, 0, .7);
}

.zsw-l__lead {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: clamp(14px, 1.8vw, 22px);
	max-width: 56ch;
	background: rgba(10, 9, 20, .62);
	border: 1px solid rgba(255, 255, 255, .22);
	backdrop-filter: blur(6px);
	color: inherit;
	transition: transform .3s ease, border-color .3s ease;
}

.zsw-l__lead:hover {
	transform: translateY(-4px);
	border-color: var(--zsw-on-accent, #fff);
}

.zsw-l__kicker {
	font-size: 11px;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--zsw-on-accent, #fff);
}

.zsw-l__headline {
	font-size: clamp(20px, 2.2vw, 30px);
	font-weight: 700;
	line-height: 1.15;
}

.zsw-l__more {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	align-items: start;
	gap: 10px;
	width: 100%;
	max-width: 68ch;
}

.zsw-l__more-item {
	padding: 11px 13px;
	font-size: 14px;
	font-weight: 600;
	line-height: 1.3;
	background: rgba(10, 9, 20, .55);
	border: 1px solid rgba(255, 255, 255, .18);
	color: inherit;
	box-sizing: content-box;
	overflow: hidden;
	max-height: 2.6em;
	transition: background .25s ease;
}

@supports (max-height: 2lh) {
	.zsw-l__more-item {
		max-height: 2lh;
	}
}

.zsw-l__more-item:hover {
	background: rgba(10, 9, 20, .8);
}

/* The empty-world stand-in, in whatever panel shape its world uses. */
.zsw-l__lead--empty {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: clamp(14px, 1.8vw, 22px);
}

.zsw-l__lead--empty .zsw-l__kicker {
	font-size: 11px;
	letter-spacing: 2px;
	text-transform: uppercase;
	opacity: .8;
}

.zsw-l__lead--empty .zsw-l__headline {
	font-size: clamp(18px, 1.9vw, 26px);
	font-weight: 700;
	line-height: 1.2;
}

/* ===== MARVEL · the comic splash ========================================== */

.zsw-mv__copy {
	grid-column: span 7;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	gap: clamp(10px, 1.4vw, 18px);
	min-width: 0;
	height: 100%;
}

.zsw-mv__title {
	display: block;
	font-size: clamp(54px, 8.8vw, 128px);
	line-height: .82;
	color: #fff;
	text-shadow: 7px 7px 0 #14121a;
	transform: rotate(-3deg);
	transform-origin: left bottom;
}

.zsw-mv__lead {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: clamp(14px, 1.6vw, 22px) clamp(16px, 1.8vw, 24px);
	transform: rotate(-1deg);
	min-width: 0;
}

.zsw-mv__lead:hover {
	transform: rotate(-1deg) translate(-3px, -3px);
	box-shadow: 11px 11px 0 #14121a;
}

.zsw-mv__kicker {
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: #C8102E;
}

.zsw-mv__headline {
	font-size: clamp(24px, 3.2vw, 48px);
	line-height: .95;
	color: #14121a;
}

.zsw-mv__badge {
	align-self: flex-start;
	margin-top: 6px;
	padding: 7px 16px;
	font-size: clamp(16px, 1.5vw, 22px);
	line-height: 1;
	background: #ffd23f;
	border: 3px solid #14121a;
	box-shadow: 4px 4px 0 #14121a;
	color: #14121a;
}

.zsw-mv__panels {
	display: grid;
	align-items: start;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: clamp(8px, 1vw, 16px);
}

.zsw-mv__panel {
	padding: 10px 12px;
	font-size: clamp(14px, 1.3vw, 20px);
	line-height: 1;
}

/* The artboard tilts each panel a different way and fills them differently. */
.zsw-mv__panel--1 { transform: rotate(1deg); background: #fff; }
.zsw-mv__panel--2 { transform: rotate(-1deg); background: #ffd23f; }
.zsw-mv__panel--3 { transform: rotate(.6deg); }

.zsw-mv__art {
	grid-column: span 5;
	position: relative;
	height: 100%;
	min-width: 0;
}

/* The lead post's featured image, standing in for the theatrical poster. */
.zsw .zsw-mv__poster {
	position: absolute;
	right: 4%;
	top: 2%;
	width: min(300px, 82%);
	aspect-ratio: 2 / 3;
	transform: rotate(3deg);
	background: #4A0A14;
	overflow: hidden;
	padding: 0;
}

.zsw-mv__poster img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.zsw-mv__burst {
	position: absolute;
	left: 0;
	top: 0;
	width: clamp(110px, 12vw, 170px);
	aspect-ratio: 17 / 15;
	z-index: 2;
}

.zsw-mv__burst .mv-burst {
	width: 100%;
	height: 100%;
}

.zsw-mv__burst .f-mv {
	font-size: clamp(18px, 2vw, 28px);
	line-height: .9;
}

/* ===== DUNE · the sandworm's mouth =========================================
 *
 * A still, not a sequence. The stage looks straight into the open maw; the post copy
 * sits over the blurred desert haze on the left of that shot, which is why the art has
 * an empty left third.
 */

.zsw-sw__maw {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 70% 50%;
	z-index: 1;
}

@media (max-width: 900px) {
	.zsw-sw__maw {
		object-position: 62% 50%;
	}
}

/* ===== DOCTOR WHO · the TARDIS flight =======================================
 *
 * In from deep space, tumbling; slams into the top of the frame, ricochets into the
 * right side, comes at the camera and is gone past it; a flash; and there it is, doors
 * open, console lit. The last shot holds.
 *
 * The flight keeps to the right of the stage on purpose. The post copy fills the left
 * seven columns, and a hit behind those glass panels would never be seen.
 *
 * Every piece runs on the same 4.5s clock so the hits line up: the box meets the top
 * edge at 30% (1.35s) and the right at 52% (2.34s), and the frame shakes and the edge
 * flares at exactly those moments. Change one percentage and change them all.
 *
 * The animations are attached only while the stage is the chosen one, so choosing
 * Doctor Who again plays the arrival again. On a hub header the stage is always
 * showing, so it plays once, on load. With reduced motion, zs-base.css cuts every
 * animation to an instant, and `both` fill lands each piece on its last frame — the
 * final shot, with the flight already over.
 */

.zsw-dw {
	position: absolute;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
	background: #05040f;
}

.zsw-dw .fill,
.zsw-dw__space,
.zsw-dw__arrival {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* The box sits in the right third of both shots; keep it in frame on narrow stages. */
	object-position: 72% 50%;
}

/* The copy sits on the left. Darken that side so it reads over the nebula. */
.zsw-dw::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(90deg, rgba(5, 4, 15, .78) 0%, rgba(5, 4, 15, .45) 42%, rgba(5, 4, 15, 0) 70%);
	z-index: 4;
}

/* Resting state, before the stage has been chosen: the final shot. */
.zsw-dw__arrival { opacity: 1; z-index: 3; }
.zsw-dw__fly,
.zsw-dw__hit,
.zsw-dw__flash { opacity: 0; }

/* The flying box. The wrapper is the size of the stage, so translate percentages are
 * fractions of the stage: translateX(40%) puts the box's centre 10% from the right. */
.zsw-dw__fly {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 2;
	will-change: transform, opacity;
}

.zsw-dw__tardis {
	height: 58%;
	width: auto;
	filter: drop-shadow(0 0 18px rgba(120, 170, 255, .45));
	will-change: transform;
}

/* The flare where it hits the edge. */
.zsw-dw__hit {
	position: absolute;
	z-index: 3;
	mix-blend-mode: screen;
}

.zsw-dw__hit--t {
	top: 0;
	left: 50%;
	width: 44%;
	height: 30%;
	background: radial-gradient(ellipse 45% 70% at 50% 0%, rgba(200, 225, 255, .95) 0%, rgba(110, 150, 255, .45) 35%, rgba(90, 60, 200, 0) 75%);
}

.zsw-dw__hit--r {
	top: 0;
	right: 0;
	bottom: 0;
	width: 22%;
	background: radial-gradient(ellipse 60% 40% at 100% 55%, rgba(200, 225, 255, .95) 0%, rgba(110, 150, 255, .45) 35%, rgba(90, 60, 200, 0) 75%);
}

.zsw-dw__flash {
	position: absolute;
	inset: 0;
	z-index: 5;
	background: radial-gradient(circle at 70% 50%, #fff 0%, rgba(210, 225, 255, .9) 30%, rgba(120, 140, 255, .35) 60%, rgba(40, 30, 120, 0) 100%);
}

/* --- the sequence, attached only while the stage is showing ----------------- */

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-dw,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-dw,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-dw,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-dw,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-dw,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-dw,
.zsw-hdr .zsw-dw {
	animation: zsw-dw-shake 4.5s linear .3s both;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-dw__space,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-dw__space,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-dw__space,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-dw__space,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-dw__space,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-dw__space,
.zsw-hdr .zsw-dw__space {
	animation: zsw-dw-rush 4.5s cubic-bezier(.2, .6, .3, 1) .3s both;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-dw__fly,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-dw__fly,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-dw__fly,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-dw__fly,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-dw__fly,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-dw__fly,
.zsw-hdr .zsw-dw__fly {
	animation: zsw-dw-path 4.5s linear .3s both;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-dw__tardis,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-dw__tardis,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-dw__tardis,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-dw__tardis,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-dw__tardis,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-dw__tardis,
.zsw-hdr .zsw-dw__tardis {
	animation: zsw-dw-tumble 4.5s linear .3s both;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-dw__hit--t,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-dw__hit--t,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-dw__hit--t,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-dw__hit--t,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-dw__hit--t,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-dw__hit--t,
.zsw-hdr .zsw-dw__hit--t {
	animation: zsw-dw-hit-t 4.5s linear .3s both;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-dw__hit--r,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-dw__hit--r,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-dw__hit--r,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-dw__hit--r,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-dw__hit--r,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-dw__hit--r,
.zsw-hdr .zsw-dw__hit--r {
	animation: zsw-dw-hit-r 4.5s linear .3s both;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-dw__flash,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-dw__flash,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-dw__flash,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-dw__flash,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-dw__flash,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-dw__flash,
.zsw-hdr .zsw-dw__flash {
	animation: zsw-dw-flash 4.5s linear .3s both;
}

.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(1) .zsw-dw__arrival,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(2) .zsw-dw__arrival,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(3) .zsw-dw__arrival,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(4) .zsw-dw__arrival,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(5) .zsw-dw__arrival,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__stage > .zsw-dial__layer:nth-child(6) .zsw-dw__arrival,
.zsw-hdr .zsw-dw__arrival {
	animation: zsw-dw-arrive 4.5s linear .3s both;
}

/* Where the box is. Ease in to each hit, snap back out of it. */
@keyframes zsw-dw-path {
	0%   { transform: translate(28%, 4%) scale(.05); opacity: 0; animation-timing-function: ease-in; }
	4%   { opacity: 1; }
	30%  { transform: translate(22%, -33%) scale(.62); animation-timing-function: cubic-bezier(.1, .7, .3, 1); }
	38%  { transform: translate(27%, -18%) scale(.64); animation-timing-function: ease-in; }
	52%  { transform: translate(43%, 4%) scale(.74); animation-timing-function: cubic-bezier(.1, .7, .3, 1); }
	60%  { transform: translate(31%, 0%) scale(.8); animation-timing-function: cubic-bezier(.5, 0, .9, .4); }
	80%  { transform: translate(24%, 12%) scale(2.2); opacity: 1; }
	86%  { transform: translate(34%, 34%) scale(4.5); opacity: 0; }
	100% { transform: translate(34%, 34%) scale(4.5); opacity: 0; }
}

/* How it turns. Spinning one way until each hit knocks it back the other. */
@keyframes zsw-dw-tumble {
	0%   { transform: rotate(-25deg); filter: drop-shadow(0 0 18px rgba(120, 170, 255, .45)) blur(0); }
	26%  { filter: drop-shadow(0 0 18px rgba(120, 170, 255, .45)) blur(1.5px); }
	30%  { transform: rotate(150deg); filter: drop-shadow(0 0 30px rgba(200, 225, 255, .9)) blur(0); }
	38%  { transform: rotate(118deg); }
	52%  { transform: rotate(330deg); filter: drop-shadow(0 0 30px rgba(200, 225, 255, .9)) blur(0); }
	60%  { transform: rotate(300deg); }
	80%  { transform: rotate(420deg); filter: drop-shadow(0 0 18px rgba(120, 170, 255, .45)) blur(2px); }
	100% { transform: rotate(470deg); filter: blur(4px); }
}

/* The frame takes the hits. */
@keyframes zsw-dw-shake {
	0%, 29.5%, 34%, 51.5%, 56%, 100% { transform: translate(0, 0); }
	30%  { transform: translate(4px, -14px); }
	31%  { transform: translate(-5px, 10px); }
	32%  { transform: translate(3px, -6px); }
	33%  { transform: translate(-1px, 3px); }
	52%  { transform: translate(14px, -5px); }
	53%  { transform: translate(-10px, 6px); }
	54%  { transform: translate(6px, -3px); }
	55%  { transform: translate(-3px, 1px); }
}

/* Stars rushing past while it flies, easing to still for the final shot. */
@keyframes zsw-dw-rush {
	0%   { transform: scale(1.18); opacity: 1; }
	84%  { transform: scale(1.02); opacity: 1; }
	92%  { transform: scale(1); opacity: 0; }
	100% { transform: scale(1); opacity: 0; }
}

@keyframes zsw-dw-hit-t {
	0%, 29%   { opacity: 0; }
	30%       { opacity: 1; }
	36%, 100% { opacity: 0; }
}

@keyframes zsw-dw-hit-r {
	0%, 51%   { opacity: 0; }
	52%       { opacity: 1; }
	58%, 100% { opacity: 0; }
}

@keyframes zsw-dw-flash {
	0%, 84%   { opacity: 0; }
	88%       { opacity: .95; }
	100%      { opacity: 0; }
}

/* On a phone the copy runs the full width, so there is no clear right-hand side for the
 * box to sit in. Dim the whole scene evenly instead of just the left, so the text reads
 * and the TARDIS still shows through behind it. */
@media (max-width: 900px) {
	.zsw-dw::after {
		background: linear-gradient(180deg, rgba(5, 4, 15, .72) 0%, rgba(5, 4, 15, .55) 60%, rgba(5, 4, 15, .7) 100%);
	}
}

/* The final shot comes up under the flash, and stays. */
@keyframes zsw-dw-arrive {
	0%, 86% { opacity: 0; transform: scale(1.04); }
	100%    { opacity: 1; transform: scale(1); }
}

/* ===== DOCTOR WHO · glass over the vortex ================================= */

.zsw-who__copy {
	grid-column: span 7;
	display: flex;
	flex-direction: column;
	gap: clamp(10px, 1.3vw, 18px);
	min-width: 0;
}

.zsw-who__title {
	display: block;
	font-size: clamp(26px, 4.4vw, 64px);
	line-height: 1.02;
	color: #fff;
}

.zsw-who__lead {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: clamp(14px, 1.6vw, 20px) clamp(16px, 1.8vw, 22px);
	transition: transform .3s ease, border-color .3s ease;
}

.zsw-who__lead:hover {
	transform: translateY(-4px);
	border-color: #AFA9EC;
}

.zsw-who__kicker {
	font-size: 10px;
	letter-spacing: 2px;
	color: #AFA9EC;
}

.zsw-who__headline {
	font-size: clamp(20px, 2.2vw, 30px);
	font-weight: 700;
	line-height: 1.12;
	color: #fff;
}

.zsw-who__dek {
	color: #c9c5e2;
}

.zsw-who__cards {
	display: grid;
	align-items: start;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: clamp(8px, .9vw, 12px);
}

.zsw-who__card {
	padding: 12px;
	font-size: 14px;
	font-weight: 600;
	line-height: 1.25;
	color: #e8e6f7;
	transition: transform .25s ease;
}

.zsw-who__card:hover {
	transform: translateY(-3px);
}

/* ===== BATTLETECH · one armour plate ====================================== */

/**
 * No second column: the mech photograph is the right-hand half of the backdrop, so the
 * plate gets a little under half the stage — the artboard's `span 6` of 12.
 *
 * It is a px cap, not `ch`: `ch` is measured in the inherited font, which on this
 * stage is Barlow, while the thing that has to fit is BATTLETECH set in Chakra Petch.
 * At 62ch the plate came out 465px and the 70px title needed 437px inside 398px of
 * usable width, so it ran off the edge.
 */
.zsw-l__inner--battletech {
	max-width: min(760px, 100%);
}

.zsw-bt__plate {
	display: flex;
	flex-direction: column;
	gap: clamp(8px, 1vw, 14px);
	padding: clamp(20px, 2.2vw, 30px) clamp(22px, 2.4vw, 34px);
	min-width: 0;
	width: 100%;
}

.zsw-bt__brk {
	align-self: flex-start;
	margin-left: 14px;
}

.zsw-bt__title {
	display: block;
	font-size: clamp(30px, 4.4vw, 62px);
	line-height: 1;
	text-transform: uppercase;
	color: #eef3f1;
	/* The brackets sit 14px outside the title on each side. */
	max-width: calc(100% - 32px);
}

.zsw-bt__lead {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 12px 0;
	border-top: 2px solid rgba(59, 224, 160, .45);
	color: #e6ecea;
}

.zsw-bt__kicker {
	font-size: 12px;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: #3be0a0;
}

.zsw-bt__headline {
	font-size: clamp(18px, 1.9vw, 26px);
	font-weight: 600;
	letter-spacing: .3px;
	line-height: 1.15;
}

.zsw-bt__row {
	display: flex;
	gap: 12px;
	align-items: baseline;
	padding: 9px 0;
	border-top: 1px solid rgba(59, 224, 160, .25);
	color: #e6ecea;
	font-size: clamp(15px, 1.3vw, 17px);
	font-weight: 600;
	letter-spacing: .3px;
	/* Explicit, so the 2lh clamp above has a number to work from rather than `normal`. */
	line-height: 1.3;
}

.zsw-bt__row:hover {
	color: #3be0a0;
}

/* ===== DUNE · full bleed, gaps shown ====================================== */

.zsw-l__inner--dune {
	max-width: 900px;
	gap: clamp(10px, 1.5vw, 20px);
}

.zsw-dn__title {
	display: block;
	font-size: clamp(44px, 8.6vw, 124px);
	line-height: .9;
	letter-spacing: clamp(10px, 2.5vw, 36px);
	color: #fff4e0;
	text-shadow: 0 6px 40px rgba(74, 27, 12, .8);
}

.zsw-dn__lead {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: clamp(14px, 1.5vw, 18px) clamp(16px, 1.8vw, 22px);
	transition: transform .3s ease;
}

.zsw-dn__lead:hover {
	transform: translateY(-4px);
}

.zsw-dn__kicker {
	font-size: 9px;
	letter-spacing: 4px;
	color: #e8b877;
}

.zsw-dn__headline {
	font-size: clamp(19px, 2vw, 28px);
	font-weight: 700;
	line-height: 1.15;
}

/* Up to five gap tiles, one per scheduled book. */
.zsw-dn__gaps {
	display: grid;
	/* 180px, not 120: seven tiles to a row gave each title 107px and clipped all eight. */
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: 10px;
	width: 100%;
}

.zsw-dn__gap {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 10px;
	font-size: 14px;
	font-weight: 600;
}

.zsw-dn__gap-title {
	line-height: 1.3;
}

/* A live review: solid glass and a link, so it reads as "here" beside the dashed
 * "coming" tiles. */
.zsw-dn__tile {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 10px;
	font-size: 14px;
	font-weight: 600;
	transition: transform .25s ease, border-color .25s ease;
}

.zsw-dn__tile:hover {
	transform: translateY(-3px);
	border-color: #fff4e0;
}

.zsw-dn__gap-date {
	font-size: 11px;
	font-weight: 500;
	letter-spacing: 1px;
	text-transform: uppercase;
	opacity: .75;
}

.zsw-dn__gap-note {
	font-size: 9px;
	letter-spacing: 4px;
	color: #e8b877;
}

/* ===== WHEEL OF TIME · gold over the pattern ============================== */

.zsw-wot__copy {
	grid-column: span 7;
	display: flex;
	flex-direction: column;
	gap: clamp(10px, 1.2vw, 16px);
	min-width: 0;
}

.zsw-wot__title {
	display: block;
	font-size: clamp(32px, 5.2vw, 74px);
	line-height: 1;
}

.zsw-wot__rule {
	width: 100%;
	max-width: 560px;
}

.zsw-wot__lead {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: clamp(14px, 1.5vw, 18px) clamp(16px, 1.8vw, 22px);
	transition: transform .3s ease;
}

.zsw-wot__lead:hover {
	transform: translateY(-4px);
}

.zsw-wot__kicker {
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: #c9973a;
}

.zsw-wot__headline {
	font-size: clamp(20px, 2.2vw, 30px);
	line-height: 1.15;
}

.zsw-wot__dek {
	color: #d9cfae;
}

.zsw-wot__cards {
	display: grid;
	align-items: start;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: clamp(8px, .9vw, 12px);
}

.zsw-wot__card {
	padding: 12px 14px;
	font-size: 15px;
	line-height: 1.2;
}

.zsw-wot__card:hover {
	transform: translateY(-3px);
	transition: transform .25s ease;
}

/* ===== LITRPG · the save file ============================================= */

.zsw-lt__win {
	grid-column: span 7;
	display: flex;
	flex-direction: column;
	gap: clamp(8px, .9vw, 12px);
	padding: clamp(18px, 2vw, 28px);
	min-width: 0;
}

.zsw-lt__head {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 12px;
}

.zsw-lt__title {
	font-size: clamp(46px, 7vw, 100px);
	line-height: .75;
	color: #b8e07a;
}

.zsw-lt__log {
	font-size: clamp(16px, 1.7vw, 24px);
	color: #97C459;
	white-space: nowrap;
}

.zsw-lt__kicker {
	font-size: clamp(16px, 1.5vw, 21px);
	color: #97C459;
}

.zsw-lt__lead {
	display: flex;
	gap: 14px;
	align-items: flex-start;
	color: #d8f0b0;
}

.zsw-lt__lead .lt-tri {
	margin-top: 8px;
}

.zsw-lt__headline {
	font-size: clamp(22px, 2.7vw, 38px);
	line-height: 1;
}

.zsw-lt__next {
	font-size: clamp(15px, 1.4vw, 20px);
	color: #97C459;
	margin-top: 4px;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 1;
	line-clamp: 1;
	overflow: hidden;
}

.zsw-lt__xp {
	width: 100%;
}

.zsw-lt__inventory {
	grid-column: span 5;
	display: flex;
	flex-direction: column;
	gap: clamp(8px, 1vw, 14px);
	min-width: 0;
}

.zsw-lt__inv-label {
	font-size: clamp(18px, 1.8vw, 26px);
	color: #b8e07a;
}

.zsw-lt__slot {
	padding: 12px 14px;
	font-size: clamp(17px, 1.8vw, 26px);
	line-height: 1;
}

/* Decorative furniture placed inside a stage. */
.zsw-l__photo { opacity: .8; }

/**
 * Per-world treatment of the backdrop photograph, following the artboard.
 *
 * Each world's stage has something that must stay the brightest thing on it — the
 * spice motes and the sun for Dune, the gold shimmer for Wheel of Time, the green
 * terminal window for LitRPG — so the picture behind is knocked back to make room
 * rather than left at full strength.
 */
.zsw-l--dune .zsw-l__photo {
	opacity: .95;
}

.zsw-l--wheel-of-time .zsw-l__photo {
	opacity: .9;
	filter: saturate(.85);
}

/* The artboard dims the pixel dungeon hard, so the save-file window reads as the
 * lit thing on the stage and the dungeon as the room it is sitting in. */
.zsw-l--litrpg .zsw-l__photo {
	opacity: 1;
	filter: brightness(.62);
}

/**
 * BattleTech's mech bay, placed the way the artboard places it.
 *
 * The photograph takes the right two-thirds and is masked so it fades in from the
 * left, which leaves the left third as clean dark metal for the armour plate to sit
 * on. Without the mask the plate lands over the Atlas's chest and both stop reading.
 *
 * The filter knocks the saturation and brightness back so the green radar sweep, the
 * hazard stripe and the plate's own lighting stay the brightest things on the stage.
 */
.zsw-l__photo--right {
	position: absolute;
	right: 0;
	top: 0;
	width: 66%;
	height: 100%;
	object-fit: cover;
	object-position: 55% center;
	display: block;
	pointer-events: none;
	opacity: 1;
	filter: saturate(.75) brightness(.9);
	-webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 36%);
	mask-image: linear-gradient(90deg, transparent 0%, #000 36%);
}

/* On a phone the plate takes the full width, so the mech would only ever be a strip
 * behind it. It spans the stage instead and drops further back. */
@media (max-width: 600px) {
	.zsw-l__photo--right {
		width: 100%;
		opacity: .55;
		-webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 92%);
		mask-image: linear-gradient(180deg, #000 0%, transparent 92%);
	}
}
.zsw-l__haz   { position: absolute; left: 0; right: 0; top: 0; z-index: 1; }
.zsw-l__radar { position: absolute; right: clamp(16px, 4vw, 64px); top: clamp(30px, 6vw, 62px); width: clamp(80px, 10vw, 140px); aspect-ratio: 1; z-index: 1; }
.zsw-l__sun   { right: 12%; top: 14%; width: clamp(120px, 18vw, 300px); aspect-ratio: 1; }
.zsw-l__px    { position: absolute; inset: 0; pointer-events: none; }

.zsw-l__scrim {
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.zsw-l__scrim--dune {
	background: linear-gradient(90deg, rgba(40, 14, 6, .82) 0%, rgba(74, 27, 12, .45) 50%, rgba(74, 27, 12, .05) 100%);
}

.zsw-l__scrim--wot {
	background: radial-gradient(ellipse 70% 70% at 30% 50%, rgba(2, 20, 38, .2) 0%, rgba(2, 20, 38, .8) 70%);
}


.zsw-l__wheel {
	position: absolute;
	right: 6%;
	top: 50%;
	width: clamp(180px, 27vw, 440px);
	aspect-ratio: 1;
	margin-top: -13.5%;
	/* Metal, not line art: held back so the headline stays the brightest thing, and
	 * given its own glow so it reads as lit rather than pasted on. */
	opacity: .92;
	filter: drop-shadow(0 0 26px rgba(201, 151, 58, .45));
	z-index: 1;
}

/* ------------------------------------------------------------------- the dial */

.zsw-dial__controls {
	position: absolute;
	left: 50%;
	bottom: 22px;
	transform: translateX(-50%);
	z-index: 10;
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 6px;
	padding: 7px;

	/**
	 * `width: max-content` is doing real work here.
	 *
	 * An absolutely positioned flex container sizes itself shrink-to-fit, and with
	 * `left: 50%` the space it thinks it has is only half the stage — so at 1400px the
	 * box came out 693px wide and wrapped six 154px buttons onto two rows, on a stage
	 * with 1400px to spare. max-content asks for the buttons' real total instead, and
	 * max-width still forces the wrap on a narrow screen, where it is wanted.
	 */
	width: max-content;
	max-width: calc(100% - 24px);
	background: rgba(12, 12, 16, .88);
	border: 1px solid rgba(255, 255, 255, .16);
	backdrop-filter: blur(8px);
}

.zsw-dial__btn {
	position: relative;
	overflow: hidden;
	width: clamp(88px, 11vw, 170px);
	height: 58px;
	border: 0;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	padding: 4px 8px;
	background: transparent;
	color: #cfcdd6;
	transition: background .3s ease, color .3s ease;
}

.zsw-dial__btn:hover {
	background: rgba(255, 255, 255, .1);
	color: #fff;
}

.zsw-dial__btn-label {
	line-height: 1;
}

/* The selected button takes its world's colour. */
.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(1),
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(2),
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(3),
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(4),
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(5),
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(6) {
	background: var(--zsw-c, #3b96e6);
	color: #fff;
	box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .85);
}

/**
 * Keyboard focus has to show on the label, because the thing actually focused is the
 * hidden radio. Arrow keys move between radios in a group natively, so the dial is
 * keyboard-operable with no script — the ring just has to follow.
 */
.zsw-dial__radio:nth-of-type(1):focus-visible ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(1),
.zsw-dial__radio:nth-of-type(2):focus-visible ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(2),
.zsw-dial__radio:nth-of-type(3):focus-visible ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(3),
.zsw-dial__radio:nth-of-type(4):focus-visible ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(4),
.zsw-dial__radio:nth-of-type(5):focus-visible ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(5),
.zsw-dial__radio:nth-of-type(6):focus-visible ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(6) {
	outline: 3px solid #fff;
	outline-offset: -3px;
}

.zsw-dial__bar {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 3px;
	background: #fff;
	transform-origin: left;
	transform: scaleX(0);
}

/* Full bar on the selected button. */
.zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(1) .zsw-dial__bar,
.zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(2) .zsw-dial__bar,
.zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(3) .zsw-dial__bar,
.zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(4) .zsw-dial__bar,
.zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(5) .zsw-dial__bar,
.zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(6) .zsw-dial__bar {
	transform: scaleX(1);
}

/**
 * While autoplay is running, the selected button's bar fills across the interval so a
 * reader can see the switch coming. `is-playing` is added by the script, so the bar is
 * a static full width when there is no JavaScript — which matches reality, because
 * without JavaScript nothing advances on its own.
 *
 * The animation restarts by itself on each switch: the rule stops applying to the old
 * button and starts applying to the new one, which is a fresh animation each time.
 */
.zsw-dial.is-playing .zsw-dial__radio:nth-of-type(1):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(1) .zsw-dial__bar,
.zsw-dial.is-playing .zsw-dial__radio:nth-of-type(2):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(2) .zsw-dial__bar,
.zsw-dial.is-playing .zsw-dial__radio:nth-of-type(3):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(3) .zsw-dial__bar,
.zsw-dial.is-playing .zsw-dial__radio:nth-of-type(4):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(4) .zsw-dial__bar,
.zsw-dial.is-playing .zsw-dial__radio:nth-of-type(5):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(5) .zsw-dial__bar,
.zsw-dial.is-playing .zsw-dial__radio:nth-of-type(6):checked ~ .zsw-dial__controls > .zsw-dial__btn:nth-child(6) .zsw-dial__bar {
	animation: zsw-dial-prog var(--zsw-interval, 6.5s) linear forwards;
}

@keyframes zsw-dial-prog {
	from { transform: scaleX(0); }
	to   { transform: scaleX(1); }
}

.zsw-dial__pause {
	position: absolute;
	right: 16px;
	bottom: 26px;
	z-index: 11;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 8px 14px;
	font: 600 12px/1 'Barlow', sans-serif;
	letter-spacing: 1.5px;
	text-transform: uppercase;
	color: #fff;
	background: rgba(12, 12, 16, .88);
	border: 1px solid rgba(255, 255, 255, .28);
	cursor: pointer;
}

.zsw-dial__pause:hover {
	background: rgba(12, 12, 16, 1);
}

/**
 * The button ships with the `hidden` attribute and is unhidden by the script, so it
 * only ever appears when there is autoplay to pause. `display: inline-flex` above
 * outranks the UA's [hidden] rule, so it has to be restated here — without this the
 * button leaks through on a no-JS page and for a reader on reduced motion, offering to
 * pause something that was never moving.
 */
.zsw-dial__pause[hidden] {
	display: none;
}

.zsw-dial__pause-icon {
	width: 9px;
	height: 11px;
	border-left: 3px solid currentColor;
	border-right: 3px solid currentColor;
}

/* Pressed: the icon becomes a play triangle and the label flips to Play. */
.zsw-dial__pause[aria-pressed="true"] .zsw-dial__pause-icon {
	width: 0;
	height: 0;
	border: 6px solid transparent;
	border-left: 9px solid currentColor;
	border-right: 0;
}

/* --------------------------------------------------- the hub header variant */

.zsw-hdr {
	position: relative;
	height: var(--zsw-stage-h, 560px);
	min-height: 320px;
	overflow: hidden;
	background: #0f0f14;
}

.zsw-hdr__inner {
	position: relative;
	z-index: 2;
	height: 100%;
	padding: clamp(24px, 4vw, 56px);
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: center;
	gap: clamp(10px, 1.6vw, 18px);
	max-width: 58ch;
	color: var(--zsw-world-ink, #fff);
}

.zsw-hdr__title {
	margin: 0;
	font-size: clamp(38px, 8vw, 104px);
	line-height: .92;
	color: #fff;
	text-shadow: 0 4px 30px rgba(0, 0, 0, .7);
}

.zsw-hdr.zsw-l--marvel .zsw-hdr__title {
	text-shadow: 6px 6px 0 #14121a;
}

.zsw-hdr.zsw-l--doctor-who .zsw-hdr__title {
	font-size: clamp(26px, 5vw, 70px);
	text-shadow: 0 0 22px rgba(120, 160, 255, .65), 0 0 2px rgba(255, 255, 255, .8);
}

.zsw-hdr__lede {
	margin: 0;
	font-size: clamp(15px, 1.4vw, 19px);
	line-height: 1.5;
	max-width: 46ch;
	text-shadow: 0 2px 12px rgba(0, 0, 0, .9);
}

.zsw-hdr__ctas {
	display: flex;
	flex-wrap: wrap;
	gap: 14px;
	margin-top: 6px;
}

.zsw-hdr__cta {
	display: inline-block;
	padding: 11px 22px;
	font-size: clamp(15px, 1.5vw, 22px);
	line-height: 1;
	transition: transform .25s ease;
}

.zsw-hdr__cta:hover {
	transform: translateY(-3px);
}

.zsw-hdr__cta--primary {
	background: var(--zsw-on-accent, #ffd23f);
	color: #14121a;
	border: 3px solid #14121a;
	box-shadow: 5px 5px 0 #14121a;
}

.zsw-hdr__cta--ghost {
	background: transparent;
	color: #f4efe2;
	border: 3px solid #f4efe2;
}

/* On a phone the stage is portrait, so the copy stops competing with the art: the
 * decorative SVGs come out and the text gets the whole frame. */
@media (max-width: 600px) {
	.zsw-dial {
		height: auto;
		min-height: 0;
	}

	.zsw-dial__stage {
		position: relative;
		inset: auto;
		height: var(--zsw-stage-h-m, 520px);
	}

	.zsw-l__wheel,
	.zsw-l__radar,
	.zsw-l__sun {
		display: none;
	}

	/**
	 * Out of the stage and into the flow, full width, three buttons to a row.
	 *
	 * `width: auto` matters: the desktop rule sets `width: max-content` to stop the
	 * dial wrapping at 1400px, and max-content is ~970px. Clearing `max-width` here
	 * without also clearing that would leave a 970px bar inside a 390px screen,
	 * scrolling the page sideways.
	 */
	.zsw-dial__controls {
		position: static;
		transform: none;
		width: auto;
		max-width: none;
		border-left: 0;
		border-right: 0;
		border-bottom: 0;
	}

	.zsw-dial__btn {
		width: calc(33.333% - 4px);
		height: 48px;
	}

	.zsw-dial__pause {
		right: 10px;
		bottom: auto;
		top: 10px;
	}

	.zsw-l__inner {
		padding-bottom: clamp(24px, 6vw, 40px);
	}

	/**
	 * On a phone every stage becomes one column.
	 *
	 * The two-column worlds drop their art column entirely — a 130px-wide poster or
	 * inventory panel is worse than none — and the copy column takes the full width.
	 */
	.zsw-l__inner--marvel,
	.zsw-l__inner--doctor-who,
	.zsw-l__inner--wheel-of-time,
	.zsw-l__inner--litrpg {
		display: flex;
		flex-direction: column;
	}

	.zsw-mv__art,
	.zsw-lt__inventory {
		display: none;
	}

	.zsw-mv__copy,
	.zsw-who__copy,
	.zsw-wot__copy,
	.zsw-lt__win {
		width: 100%;
		height: auto;
	}

	/* Three 100px-wide link boxes are unreadable; two stacked ones are fine, and the
	 * stage has the vertical room because the controls moved out of it. */
	.zsw-mv__panels,
	.zsw-who__cards,
	.zsw-wot__cards {
		grid-template-columns: minmax(0, 1fr);
	}

	.zsw-mv__panel:nth-child(n + 3),
	.zsw-who__card:nth-child(n + 3),
	.zsw-wot__card:nth-child(n + 3),
	.zsw-dn__gap:nth-child(n + 4) {
		display: none;
	}

	/* The rotations cost horizontal room a phone hasn't got. */
	.zsw-mv__title,
	.zsw-mv__lead {
		transform: none;
	}

	.zsw-mv__lead:hover {
		transform: translate(-3px, -3px);
	}

	/* 36px of letter-spacing pushes DUNE off the screen. */
	.zsw-dn__title {
		letter-spacing: 8px;
	}

	.zsw-lt__head {
		flex-direction: column;
		align-items: flex-start;
		gap: 2px;
	}
}
