/**
 * Video Series block styles.
 *
 * Structure and design tokens only. The tokens below are taken from the
 * reference design; the visual pass that uses them fully is deliberately still
 * outstanding, so the rules here stay minimal and easy to override.
 */

/*
 * Under a classic theme this is the plugin's own template root (see
 * `templates/classic/`). A block theme lays the same blocks out inside its own
 * template, which already supplies width and padding, so this rule never
 * applies there.
 *
 * A classic theme also has no `theme.json`, so WordPress core's own default
 * global-styles stylesheet is the only thing that ever defines
 * `--wp--style--global--content-size` (800px) on `:root` -- there is no
 * "undefined" state for `.video-series-series-share` below to fall back from,
 * only the wrong value. Redeclaring it here, matching this element's own
 * `max-width`, keeps that block's end-aligned math (see
 * `.video-series-series-share.video-series-series-share`) pinned to *this*
 * content column's real edge instead of one 400px narrower. A block theme's
 * own value already matches its real content column, so this is a no-op
 * there -- it only overrides the core default a classic theme leaves in
 * place.
 */
.video-series-main {
	--wp--style--global--content-size: 1200px;
	margin-inline: auto;
	max-width: 1200px;
}

/*
 * A block theme's `<main class="wp-block-group">` sits inside `.wp-site-blocks`,
 * where core's global styles add `margin-block-start: var(--wp--style--block-gap)`
 * (1.2rem) to every sibling that follows it -- the footer template part
 * included -- producing the gap between the template's content and the
 * footer for free. A classic theme has neither `.wp-site-blocks` nor that
 * rule, so `.video-series-main` (this template root, see the comment above)
 * would otherwise butt straight up against the footer markup that follows it
 * in `get_footer()`. Reproduced here so the classic templates end with the
 * same breathing room a block theme supplies automatically.
 */
.video-series-main {
	margin-bottom: 1.2rem;
}

/*
 * Fallback inline gutter for when nothing else supplies one: a classic theme
 * that doesn't style `.container`, or a theme's own `single-episode.php`
 * wrapper without it (see `docs/decisions/0008-classic-theme-support.md`),
 * otherwise leaves the watch column flush against the viewport edges on
 * mobile. Excludes the two classes that already carry real padding -- a
 * block theme's `.has-global-padding` and a theme's `.container` -- by
 * name rather than by losing a specificity contest: a zero-specificity
 * `:where()` fallback ties with a theme's `* { padding: 0 }` reset and
 * loses to it on source order, since theme stylesheets load after this
 * one. Only below the column's own `max-width` plus both gutters: wider
 * than that the column is already centered with room to spare, and the
 * blocks keep the full 1200px they are sized for. `--vs-main-gutter`
 * exposes the value to the Episode player's mobile bleed below, so the
 * player still reaches both screen edges under this fallback.
 */
@media ( max-width: 1231px ) {
	.video-series-main:not( .has-global-padding, .container ) {
		--vs-main-gutter: 16px;

		padding-inline: var( --vs-main-gutter );
	}
}

.video-series-series-episodes,
.video-series-latest-episodes,
.video-series-series-share,
.video-series-episode-player,
.video-series-watch {
	--vs-accent: #f5a623;
	--vs-surface: #ffffff;
	--vs-surface-deep: #f2f0f4;
	--vs-ink: #07070f;
	--vs-text: var( --c-primary, #f0501e );
	--vs-text-muted: #5c5a68;
	--vs-text-dim: #9a97a6;
	--vs-border: rgba( 10, 10, 20, 0.08 );
	--vs-radius-sm: 5px;
	--vs-radius-md: 10px;
	--vs-radius-lg: 16px;
	--vs-gap: 12px;
}

/* Episode lists ---------------------------------------------------------- */

/*
 * Both feeds share one fixed grid rather than letting cards reflow with the
 * viewport -- `numberOfItems` is what actually caps each block's count (6 for
 * the reference design's Latest Episodes, however many belong to the Series
 * for More In This Series), this just matches the column count so the two
 * feeds read as the same visual system on the Series and Episode templates.
 */
.video-series-series-episodes,
.video-series-latest-episodes {
	display: grid;
	gap: 24px;
	grid-template-columns: repeat( 1, 1fr );
	list-style: none;
	margin: 0;
	padding: 0;
}

@media ( min-width: 600px ) {
	.video-series-series-episodes,
	.video-series-latest-episodes {
		grid-template-columns: repeat( 2, 1fr );
	}
}

@media ( min-width: 900px ) {
	.video-series-series-episodes,
	.video-series-latest-episodes {
		grid-template-columns: repeat( 3, 1fr );
	}
}

.video-series-episode {
	margin: 0;
}

.video-series-episode__link {
	color: inherit;
	display: block;
	text-decoration: none;
}

.video-series-episode__thumbnail {
	background-color: var( --vs-surface-deep );
	border-radius: var( --vs-radius-md );
	display: block;
	overflow: hidden;
	padding-top: 56.25%;
	position: relative;
}

.video-series-episode__thumbnail img {
	height: 100%;
	inset: 0;
	object-fit: cover;
	position: absolute;
	width: 100%;
}

/*
 * `BlockContext::render_poster_picture()` wraps the `<img>` in a `<picture>`
 * once an Episode has at least one breakpoint-specific poster set. `<picture>`
 * is inline by default, which would otherwise introduce its own box between
 * `.video-series-episode__thumbnail` and the `<img>` the rules above target --
 * `display: contents` removes it from layout entirely, so every rule above
 * keeps applying straight to the `<img>` as if it had no wrapper at all.
 */
.video-series-poster {
	display: contents;
}

.video-series-episode__badge {
	background-color: rgba( 245, 166, 35, 0.13 );
	border: 1px solid rgba( 245, 166, 35, 0.3 );
	border-radius: var( --vs-radius-sm );
	color: var( --vs-accent );
	font-size: 0.5625rem;
	font-weight: 700;
	inset-inline-start: 10px;
	letter-spacing: 1.26px;
	line-height: 1.5;
	padding: 3px 8px;
	position: absolute;
	text-transform: uppercase;
	top: 10px;
	z-index: 2;
}

.video-series-episode__play {
	background-color: var( --vs-accent );
	border-radius: 50%;
	display: block;
	height: 42px;
	left: 50%;
	position: absolute;
	top: 50%;
	transform: translate( -50%, -50% );
	width: 42px;
	z-index: 2;
}

.video-series-episode__play::before {
	border-color: transparent transparent transparent var( --vs-ink );
	border-style: solid;
	border-width: 7px 0 7px 11px;
	content: "";
	display: block;
	left: 55%;
	position: absolute;
	top: 50%;
	transform: translate( -50%, -50% );
}

.video-series-episode__body {
	display: block;
	padding-top: 8px;
}

.video-series-episode__title {
	display: block;
	font-weight: 700;
}

.video-series-episode__meta {
	color: var( --vs-text-muted );
	display: block;
	font-size: 0.8125rem;
}

/*
 * The reference design's grid thumbnails stay bright to the bottom edge --
 * this feed's title and meta sit below the thumbnail, not overlaid on it, so
 * the slider's own scrim (a heavy, tall fade sized for overlaid text) would
 * be far too dark here. This is the same dark ink and direction, shallow
 * enough to read as a light vignette rather than a legibility scrim.
 *
 * Shared with `.video-series-series-episodes` (More In This Series) so both
 * grid feeds read as the same card, on both the Series and Episode templates.
 */
.video-series-series-episodes .video-series-episode__thumbnail::after,
.video-series-latest-episodes .video-series-episode__thumbnail::after {
	background: linear-gradient( to top, rgba( 7, 7, 15, 0.45 ) 0%, rgba( 7, 7, 15, 0 ) 30% );
	content: "";
	inset: 0;
	pointer-events: none;
	position: absolute;
	z-index: 1;
}

/*
 * The reference design's grid cards carry only the date badge and no play
 * affordance -- unlike the slider's cards, which use the same shared markup
 * but are meant to look clickable-to-play at a glance.
 */
.video-series-series-episodes .video-series-episode__play,
.video-series-latest-episodes .video-series-episode__play {
	display: none;
}

.video-series-series-episodes .video-series-episode__body,
.video-series-latest-episodes .video-series-episode__body {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding-top: 12px;
}

.video-series-series-episodes .video-series-episode__title,
.video-series-latest-episodes .video-series-episode__title {
	color: var( --vs-text );
	font-size: 0.9375rem;
	font-weight: 700;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.video-series-series-episodes .video-series-episode__meta,
.video-series-latest-episodes .video-series-episode__meta {
	color: var( --vs-text-muted );
	font-size: 0.8125rem;
}

/* Player ----------------------------------------------------------------- */

.video-series-episode-player {
	margin: 0;
}

/*
 * `video-player` is the outer `video.js` custom element
 * `blocks/episode-player/render.php` wraps the video in (see
 * `docs/decisions/0010-video-js-for-episode-player.md`). It generates no box
 * of its own even before `view.js` defines it: this only mirrors, ahead of
 * time, the `display: contents` `video.js` itself applies once it has.
 * `video-minimal-skin` (the inner wrapper, video.js's minimal skin -- see
 * docs/decisions/0010-video-js-for-episode-player.md) is deliberately left
 * alone: it has to generate a box for its absolutely-positioned control bar
 * and poster to have something to resolve against.
 */
.video-series-episode-player video-player {
	display: contents;
}

.video-series-episode-player__video,
.video-series-episode-player iframe {
	aspect-ratio: 16 / 9;
	border: 0;
	border-radius: var( --vs-radius-lg );
	display: block;
	height: auto;
	width: 100%;
}

/*
 * `media-seek-button` (see `BlockContext::seek_controls()`) is slotted, as
 * light DOM, into `video-minimal-skin`'s `<media-container>`, which is the
 * skin's own `position: relative` box -- so an absolute overlay here
 * resolves against it the same way the skin's own poster and control bar do.
 * This is the only placement available without ejecting the packaged skin:
 * its template has no control-bar slot for a custom button. `left`/`right`
 * (not `inset-inline-*`) deliberately: rewind/fast-forward is a physical,
 * not a reading-direction, convention -- rewind stays on the physical left
 * and fast-forward on the physical right even under the `fa_IR` RTL
 * translation, instead of swapping sides the way `inset-inline-*` would.
 *
 * Undefined without `view.js` -- the no-JS fallback, and the block editor's
 * `ServerSideRender` preview, which loads no `viewScript` -- still renders
 * this light-DOM content even though the custom element never upgrades, so
 * it has to stay hidden explicitly rather than relying on the element ever
 * taking on the button behaviour that makes it visible on purpose.
 *
 * Hidden until the player is hovered or the button itself has keyboard
 * focus, matching the skin's own control bar instead of staying permanently
 * on top of the video. The focus check is scoped to the seek button itself
 * (`:focus-visible`, not `:focus-within` on the whole skin): the skin's own
 * controls (e.g. play/pause) keep DOM focus for as long as they stay on
 * screen after a click, so a skin-wide `:focus-within` stayed true for the
 * entire playback once the user pressed play, pinning both seek buttons
 * visible regardless of hover. `:focus-visible` also limits this to actual
 * keyboard focus, so a mouse click on the seek button itself doesn't pin it
 * open the same way.
 *
 * Known limitation, accepted rather than worked around: the skin's own
 * control bar hides itself on playback idle via a `data-visible` attribute
 * set inside its Shadow DOM (`.media-controls[data-visible]`, in the skin's
 * own `controls.css`), which a light-DOM stylesheet has no selector to read
 * -- the skin exposes no `part`, only internal `data-part` attributes (see
 * the "Packaged vs. ejected" trade-off in `concepts/skins.md`). These
 * buttons therefore stay visible/hidden purely on hover/focus rather than
 * also fading out on playback idle along with the rest of the controls.
 * Ejecting the skin (copying its full markup into this plugin, per
 * `how-to/customize-skins.md`) would fix this at the cost of owning the
 * whole control bar's markup and future updates; not done here for a
 * two-button addition.
 */
.video-series-episode-player media-seek-button:not( :defined ) {
	display: none;
}

.video-series-episode-player__seek {
	align-items: center;
	background: none;
	border: 0;
	border-radius: 999px;
	bottom: 4.5rem;
	/* The skin's own control bar colour (`.media-controls`, `oklch(1 0 0)`). */
	color: #fff;
	cursor: pointer;
	display: flex;
	height: 2.5rem;
	justify-content: center;
	opacity: 0;
	position: absolute;
	transition: opacity 0.15s ease;
	width: 2.5rem;
}

.video-series-episode-player video-minimal-skin:hover .video-series-episode-player__seek,
.video-series-episode-player__seek:focus-visible {
	opacity: 1;
}

/*
 * `42%` from each side on its own leaves the two 2.5rem buttons only 16% of
 * the player's width apart, so below a 500px-wide player they overlap. The
 * `min()` caps each offset at half the player less one button and half of a
 * 1rem gap, keeping them 1rem apart at any width; from a 600px-wide player
 * up, `42%` is the smaller value and the placement is unchanged.
 */
.video-series-episode-player__seek--backward {
	left: min( 42%, calc( 50% - 3rem ) );
}

.video-series-episode-player__seek--backward svg {
	transform: scaleX( -1 );
}

.video-series-episode-player__seek--forward {
	right: min( 42%, calc( 50% - 3rem ) );
}

/*
 * The circular-arrow icon is absolutely positioned to fill the button so the
 * seconds label (below) can sit centred on top of it, both within the same
 * flex-centred circle -- the icon traces the button's own edge rather than
 * floating as a smaller glyph inside it.
 */
.video-series-episode-player__seek svg {
	height: 100%;
	left: 0;
	position: absolute;
	top: 0;
	width: 100%;
}

.video-series-episode-player__seek-label {
	font-size: 0.625rem;
	font-weight: 600;
	pointer-events: none;
}

/*
 * Edge-to-edge on mobile, per the wireframe's "Player: 16:9, edge-to-edge on
 * mobile" note -- bleeds past whichever container padding is actually in
 * effect, rather than sitting inset with the rest of the watch column.
 * `width: auto` (the default for a block-level `<figure>`) expands to fill
 * the space the negative margin opens up.
 *
 * Reads `--wp--style--root--padding-*`, the block theme global-styles
 * variable, because that is the one value this stylesheet can trust: a
 * block theme's `.has-global-padding` sets it to the theme's real root
 * padding (`clamp(30px, 5vw, 50px)` under this project's own theme), so the
 * bleed exactly cancels it. Under a classic theme, or a theme's own
 * `single-episode.php` (see `docs/decisions/0008-classic-theme-support.md`),
 * that variable does not exist, and this stylesheet has no reliable way to
 * learn the real inline padding in effect there, except for this
 * stylesheet's own fallback gutter on `.video-series-main`, which it
 * publishes as `--vs-main-gutter` (see the rule above) and the bleed reads
 * next. A `container` class, where a theme supplies one, is that theme's
 * own value, not ours to guess. `0px`, not a flat `-24px`, is
 * therefore the fallback: a wrong flat guess would overshoot whatever
 * padding a given theme actually applies and read as a genuine horizontal
 * overflow rather than a slightly-off visual, exactly what shipped between
 * the `padding-inline: 24px` removal and this fix. `0px` degrades cleanly
 * instead -- the player simply keeps the watch column's normal inset
 * outside a block theme, forgoing the edge-to-edge treatment rather than
 * risking it. The upcoming-Episode poster gets the same treatment where
 * it's defined below, since it stands in for the player there.
 *
 * `--media-border-radius` (not just `border-radius: 0` on the `<video>`)
 * is what actually squares off a direct-media Episode: video.js's minimal
 * skin renders the visible rounded box itself, inside its own Shadow DOM,
 * via `.media-minimal-skin ::slotted(video){border-radius:var(--container-
 * border-radius)}` with `--container-border-radius:var(--media-border-
 * radius,.75rem)` -- a shadow-tree rule whose specificity beats this
 * stylesheet's single light-DOM class regardless of source order, so
 * `border-radius: 0` on `.video-series-episode-player__video` alone left the
 * skin's own corners rounded. `--media-border-radius` is that component's
 * documented custom-property hook, and custom properties inherit through the
 * shadow boundary, so setting it on the player wrapper reaches the skin's
 * internals. Kept alongside the `<video>`/`iframe` reset for the oEmbed and
 * no-JS-fallback paths, which have no skin to override.
 */
@media ( max-width: 599px ) {
	.video-series-episode-player {
		--media-border-radius: 0;

		margin-inline: calc( var( --wp--style--root--padding-left, var( --vs-main-gutter, 0px ) ) * -1 )
			calc( var( --wp--style--root--padding-right, var( --vs-main-gutter, 0px ) ) * -1 );
	}

	.video-series-episode-player__video,
	.video-series-episode-player iframe {
		border-radius: 0;
	}
}

/* Share box -------------------------------------------------------------- */

/*
 * Mobile-first: a single row (icon, text, Share button). At wider viewports
 * this switches to the stacked layout instead, matching the Figma design.
 */
.video-series-series-share {
	align-items: center;
	background-color: var( --vs-surface );
	border: 1px solid var( --vs-border );
	border-radius: var( --vs-radius-lg );
	color: var( --vs-text );
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
	padding: 20px;
}

/*
 * `templates/single-series.php` places this block inside a `constrained`
 * group, where core's global styles add `margin-block-start: var(--wp--
 * style--block-gap)` (1.2rem) between it and the "Previous Episodes" list
 * above -- the same mechanism the slider/kicker pairing above overrides in
 * the other direction. `templates/classic/single-series.php` renders this
 * card as a plain sibling of that list inside `.video-series-series`, with
 * no such wrapper, so the gap collapses to 0 there without this rule.
 */
.video-series-series > .video-series-series-share {
	margin: 1.2rem 0;
}

@media ( min-width: 600px ) {
	.video-series-series-share {
		align-items: flex-start;
		flex-direction: column;
	}

	/*
	 * Doubled selector for specificity: WP's global-styles layout rule
	 * (`.is-layout-constrained > :where(...)`) has the same specificity as a
	 * single class here and is injected later in the cascade, so it would
	 * otherwise win the tie and keep this at the theme's content width -- and,
	 * via its own `!important` auto margins, center it instead of pinning it
	 * to the end.
	 *
	 * The end margin is set to the same `(100% - content-size) / 2` gap the
	 * theme's own centering produces, so this box's end edge lands exactly on
	 * the content column's edge -- the same edge other content-width blocks
	 * like the series slider are aligned to -- rather than 50% of whatever
	 * (wider, unpadded) element happens to be the nearest positioned ancestor.
	 * Logical properties keep this on the end side in RTL too.
	 */
	.video-series-series-share.video-series-series-share {
		/*
		 * Clamped to 0: below this breakpoint, the content column itself is
		 * narrower than `content-size` and the theme's own centering has
		 * already collapsed to a flush 0 margin, not a negative one.
		 */
		margin-inline-start: max(
			0px,
			calc( ( 100% - var( --wp--style--global--content-size, 1200px ) ) / 2 )
		) !important;
		max-width: calc( var( --wp--style--global--content-size, 1200px ) / 2 );
	}
}

.video-series-series-share__icon {
	align-items: center;
	background-image: linear-gradient( 135deg, #fdeee6 0%, #fbdccb 100% );
	border-radius: 12px;
	display: flex;
	flex-shrink: 0;
	height: 48px;
	justify-content: center;
	order: 1;
	width: 48px;
}

.video-series-series-share__icon svg {
	height: 20px;
	width: 20px;
}

.video-series-series-share__body {
	display: flex;
	flex: 1 1 0%;
	flex-direction: column;
	gap: 8px;
	min-width: 0;
	order: 2;
}

.video-series-series-share__heading {
	color: var( --vs-text );
	font-size: 1rem;
	font-weight: 700;
	line-height: 1.4;
	margin: 0;
}

.video-series-series-share__description {
	color: var( --vs-text-dim );
	font-size: 0.8125rem;
	line-height: 1.4;
	margin: 0;
}

/* Always its own full-width line, whichever layout is active. */
.video-series-series-share__controls {
	display: flex;
	flex: 1 1 100%;
	flex-wrap: wrap;
	gap: 8px;
	order: 4;
}

.video-series-series-share__url {
	background-color: var( --vs-surface-deep );
	border: 1px solid var( --vs-border );
	border-radius: 8px;
	color: var( --vs-text-muted );
	flex: 1 1 12rem;
	min-width: 0;
	padding: 10px 12px;
}

.video-series-series-share__button {
	background-color: rgba( 245, 166, 35, 0.1 );
	border: 1px solid rgba( 245, 166, 35, 0.22 );
	border-radius: 8px;
	color: var( --vs-accent );
	cursor: pointer;
	font-weight: 700;
	padding: 10px 16px;
	white-space: nowrap;
}

/* Wider gap before the Share button than between the icon and the text. */
.video-series-series-share__share-button {
	flex-shrink: 0;
	margin-inline-start: 20px;
	order: 3;
}

@media ( min-width: 600px ) {
	.video-series-series-share__share-button {
		margin-inline-start: 0;
		margin-top: 20px;
	}
}

/* Countdown -------------------------------------------------------------- */

/*
 * Used in two places, differing only in placement: over a thumbnail, where it
 * replaces the play affordance, and over the poster on the player, where the
 * video would be.
 */
.video-series-countdown {
	align-items: center;
	display: flex;
	gap: 6px;
	justify-content: center;
}

.video-series-countdown__unit {
	align-items: baseline;
	background-color: rgba( 13, 13, 26, 0.72 );
	border: 1px solid rgba( 245, 166, 35, 0.28 );
	border-radius: var( --vs-radius-sm );
	display: flex;
	gap: 2px;
	padding: 6px 8px;
}

.video-series-countdown__value {
	color: var( --vs-accent );
	font-variant-numeric: tabular-nums;
	font-weight: 700;
	line-height: 1.1;
	/* Fixed width so the row does not jump as the figures tick down. */
	min-width: 1.6ch;
	text-align: end;
}

.video-series-countdown__symbol {
	color: var( --vs-text-muted );
	font-size: 0.6875rem;
	letter-spacing: 0.06em;
	line-height: 1.1;
	text-transform: uppercase;
}

/* Over a thumbnail: centred, in place of the play button. */
.video-series-countdown--thumbnail {
	left: 50%;
	position: absolute;
	top: 50%;
	transform: translate( -50%, -50% );
}

/*
 * On the player, and on the slider's stage (the currently selected Episode's
 * full-size poster): overlaid on the poster, centred both ways and enlarged
 * -- there is no video or body copy below it to leave room for, so the
 * middle of the poster reads clearer than pinned to an edge.
 */
.video-series-countdown--player,
.video-series-countdown--stage {
	gap: var( --vs-gap );
	left: 50%;
	position: absolute;
	top: 75%;
	transform: translate( -50%, -50% );
	z-index: 2;
}

/* Each unit stacks its value over its spelled-out label instead of the
 * abbreviated, side-by-side layout used where space is tight. */
.video-series-countdown--player .video-series-countdown__unit,
.video-series-countdown--stage .video-series-countdown__unit {
	align-items: center;
	flex-direction: column;
	gap: 4px;
	padding: 12px 18px;
}

.video-series-countdown--player .video-series-countdown__value,
.video-series-countdown--stage .video-series-countdown__value {
	font-size: 2rem;
	min-width: 2.2ch;
	text-align: center;
}

.video-series-countdown--player .video-series-countdown__symbol,
.video-series-countdown--stage .video-series-countdown__symbol {
	font-size: 0.8125rem;
}

/* The class sits on the `<img>` itself, as `get_the_post_thumbnail()` puts it. */
.video-series-episode-player__poster {
	aspect-ratio: 16 / 9;
	background-color: var( --vs-surface-deep );
	border-radius: var( --vs-radius-lg );
	display: block;
	height: auto;
	object-fit: cover;
	width: 100%;
}

/*
 * A scheduled Episode's `<figure>` becomes the positioning context for the
 * countdown overlaid on its poster, and gets the same dark scrim as an
 * upcoming thumbnail in the slider (`.video-series-series-slider__card
 * .video-series-episode__thumbnail::after` below) so the countdown stays
 * legible over a bright image. `border-radius` is repeated here rather than
 * inherited: it isn't an inherited property, and this box, not the poster
 * `<img>` it matches, is what the scrim's corners need to follow.
 */
.video-series-episode-player.is-upcoming {
	border-radius: var( --vs-radius-lg );
	overflow: hidden;
	position: relative;
}

.video-series-episode-player.is-upcoming::after {
	background: linear-gradient( to top, rgba( 7, 7, 15, 0.92 ) 0%, rgba( 7, 7, 15, 0.55 ) 45%, rgba( 7, 7, 15, 0 ) 85% );
	content: "";
	inset: 0;
	pointer-events: none;
	position: absolute;
	z-index: 1;
}

@media ( max-width: 599px ) {
	.video-series-episode-player__poster,
	.video-series-episode-player.is-upcoming {
		border-radius: 0;
	}
}

/* An upcoming Episode's date badge is muted, distinct from a released one's. */
.video-series-episode.is-upcoming .video-series-episode__badge {
	background-color: rgba( 40, 40, 70, 0.35 );
	border-color: rgba( 40, 40, 70, 0.4 );
	color: #3e3e5c;
}

/*
 * The accessible label must stay hidden whether or not the theme defines
 * `.screen-reader-text`. Core's block library stylesheet does, but a plugin
 * should not depend on another stylesheet to keep its own text out of sight.
 */
.video-series-countdown .screen-reader-text {
	border: 0;
	clip-path: inset( 50% );
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	white-space: nowrap;
	width: 1px;
}

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

/*
 * `templates/single-series.php` places `wp:post-title` directly above this
 * block; on that template it functions as a small, muted kicker line above
 * the slider's own bold heading rather than a second visible headline, so it
 * is restyled here to match the slider's own `__kicker` treatment. The color
 * is spelled out rather than referencing `--vs-text-dim` because that custom
 * property is only declared on the slider's own wrapper below, out of this
 * selector's scope.
 *
 * `templates/classic/single-series.php` renders the equivalent title as its
 * own `video-series-series__title` element rather than the `wp:post-title`
 * block a block theme's template uses, so it needs the same rule under its
 * own class.
 */
.single-series .wp-block-post-title,
.single-series .video-series-series__title {
	color: #9a97a6;
	font-size: 0.625rem;
	font-weight: 600;
	letter-spacing: 0.1em;
	margin: 0 0 4px;
	text-transform: uppercase;
}

/*
 * The theme's constrained-layout blockGap (1.2rem) applies between this
 * kicker and the slider block that follows it, same as any other two blocks
 * in the template -- but the reference design pairs them as a single
 * kicker/heading unit with only a few pixels between, matching the gap the
 * slider uses internally between its own `__kicker` and `__heading`. Override
 * it here rather than shrinking the theme's blockGap globally.
 */
.single-series .wp-block-post-title + .video-series-series-slider,
.single-series .video-series-series__title + .video-series-series-slider {
	margin-top: 4px;
}

.video-series-series-slider {
	--vs-accent: #f5a623;
	--vs-surface: #ffffff;
	--vs-surface-deep: #f2f0f4;
	--vs-ink: #07070f;
	--vs-text: var( --c-primary, #f0501e );
	--vs-text-muted: #5c5a68;
	--vs-text-dim: #9a97a6;
	--vs-border: rgba( 10, 10, 20, 0.08 );
	--vs-radius-sm: 5px;
	--vs-radius-md: 10px;
	--vs-radius-lg: 16px;
	--vs-gap: 12px;
}

.video-series-series-slider__header {
	margin-bottom: 20px;
}

.video-series-series-slider__kicker {
	color: var( --vs-text-dim );
	font-size: 0.625rem;
	font-weight: 600;
	letter-spacing: 0.1em;
	margin: 0 0 4px;
	text-transform: uppercase;
}

.video-series-series-slider__heading {
	color: var( --vs-ink );
	font-size: 1.5rem;
	font-weight: 800;
	margin: 0;
}

/*
 * Minimal structural rules Swiper's JS assumes about its host elements. The
 * plugin does not load Swiper's own stylesheet -- everything else about the
 * slider's look is the same hand-written CSS as every other block here -- so
 * this box model has to be defined instead of imported.
 */
.video-series-series-slider .swiper {
	overflow: hidden;
	position: relative;
	width: 100%;
}

.video-series-series-slider .swiper-wrapper {
	box-sizing: content-box;
	display: flex;
	height: 100%;
	position: relative;
	width: 100%;
	z-index: 1;
}

.video-series-series-slider .swiper-slide {
	/*
	 * `render_card()` puts the `swiper-slide` class directly on the same
	 * element as `video-series-series-slider__card`, so its border has to be
	 * absorbed into the declared width -- otherwise it renders wider than the
	 * size Swiper's translate math assumes, and every card drifts further off
	 * from where its slide should sit.
	 */
	box-sizing: border-box;
	flex-shrink: 0;
	height: auto;
	position: relative;
	width: 100%;
}

.video-series-series-slider__stage {
	margin-bottom: var( --vs-gap );
}

.video-series-series-slider__card {
	border: 2px solid transparent;
	border-radius: var( --vs-radius-lg );
	color: inherit;
	display: block;
	overflow: hidden;
	position: relative;
	text-decoration: none;
}

.video-series-series-slider__card--stage {
	border-color: var( --vs-accent );
	box-shadow: 0 0 48px rgba( 245, 166, 35, 0.15 );
}

.video-series-series-slider__card--thumb {
	border-color: var( --vs-border );
	border-radius: var( --vs-radius-md );
}

.video-series-series-slider__thumbs .swiper-slide-thumb-active.video-series-series-slider__card--thumb {
	border: 2px solid rgb( 245, 166, 35 );
	box-shadow: 0 0 20px rgba( 245, 166, 35, 0.25 );
	transition: border-color 0.22s;
}

/* Tablet+desktop-only: touch devices don't have a meaningful hover state for
 * the filmstrip's inactive thumbnails. */
@media ( min-width: 600px ) {
	.video-series-series-slider__thumbs .swiper-slide:not( .swiper-slide-thumb-active ).video-series-series-slider__card--thumb:hover {
		border: 2px solid rgba( 245, 166, 35, 0.3 );
		transition: border-color 0.22s;
	}
}

/*
 * `.video-series-episode__thumbnail` carries its own border-radius and
 * `overflow: hidden` for `EpisodeList`'s plain cards, where it isn't nested in
 * another clipped box. Here it sits directly inside `__card`, which already
 * clips the same corners -- stacking a second rounded `overflow: hidden`
 * immediately inside the first, around a `position: absolute`,
 * `object-fit: cover` image, is what produced a hairline seam across the
 * middle of every card in WebKit. Confirmed by removing the inner clip.
 */
.video-series-series-slider__card .video-series-episode__thumbnail {
	border-radius: 0;
	overflow: visible;
}

/* A scheduled card's thumbnail is desaturated, distinct from a released one's. */
.video-series-series-slider__card.is-upcoming .video-series-episode__thumbnail img {
	filter: grayscale( 0.35 );
}

/*
 * Swiper's own pagination stylesheet isn't loaded (see ADR 0009), so the
 * bullets it creates -- one per Episode, tracking whichever slide is active --
 * are styled by hand here, matching the reference design's pill-shaped
 * active dot.
 */
.video-series-series-slider__pagination {
	align-items: center;
	display: flex;
	gap: 6px;
	justify-content: center;
	margin-top: var( --vs-gap );
}

.video-series-series-slider__pagination:empty {
	display: none;
}

.video-series-series-slider__pagination .swiper-pagination-bullet {
	background-color: rgba( 10, 10, 20, 0.15 );
	border-radius: 3px;
	cursor: pointer;
	display: block;
	height: 6px;
	padding: 0;
	width: 6px;
}

.video-series-series-slider__pagination .swiper-pagination-bullet-active {
	background-color: var( --vs-accent );
	width: 20px;
}

/*
 * A slider card's title and meta sit inside its thumbnail as an overlay on a
 * gradient scrim, matching the reference design -- unlike `EpisodeList`'s
 * cards, which keep title and meta below the image.
 */
.video-series-series-slider__card .video-series-episode__thumbnail::after {
	background: linear-gradient( to top, rgba( 7, 7, 15, 0.92 ) 0%, rgba( 7, 7, 15, 0.55 ) 45%, rgba( 7, 7, 15, 0 ) 85% );
	content: "";
	inset: 0;
	pointer-events: none;
	position: absolute;
	z-index: 1;
}

.video-series-series-slider__card .video-series-episode__body {
	bottom: 10px;
	display: flex;
	flex-direction: column;
	gap: 2px;
	inset-inline: 12px;
	padding-top: 0;
	position: absolute;
	z-index: 2;
}

.video-series-series-slider__card .video-series-episode__title-row {
	align-items: flex-end;
	display: flex;
	gap: 8px;
	justify-content: space-between;
}

.video-series-series-slider__card .video-series-episode__title {
	color: #ffffff;
	flex: 1 1 auto;
	min-width: 0;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.video-series-series-slider__card--stage .video-series-episode__title {
	font-size: 1.25rem;
	font-weight: 800;
	line-height: 23px;
}

.video-series-series-slider__card--stage .video-series-episode__meta {
	color: #a0a0c0;
	font-size: 0.625rem;
	line-height: 15px;
}

.video-series-series-slider__card--thumb .video-series-episode__title {
	font-size: 0.6875rem;
	font-weight: 800;
	line-height: 13px;
}

.video-series-series-slider__card--thumb .video-series-episode__meta {
	color: #5a5a78;
	font-size: 0.5625rem;
	line-height: 13px;
}

/*
 * On tablet and desktop the filmstrip shows several thumbnails at once; only
 * the currently focused one carries a meta line, published or scheduled
 * alike. `visibility` (not `display`) keeps the meta line's box in flow on
 * every card, so the bottom-anchored `__body` above it (title, countdown)
 * stays put instead of jumping up when the focused card's meta line appears.
 */
@media ( min-width: 600px ) {
	.video-series-series-slider__thumbs .video-series-episode__meta {
		visibility: hidden;
	}

	.video-series-series-slider__thumbs .swiper-slide-thumb-active .video-series-episode__meta {
		visibility: visible;
	}
}

/* A scheduled thumbnail's title is dimmed until the Episode is released. */
.video-series-series-slider__card--thumb.is-upcoming .video-series-episode__title {
	color: #5a5a78;
}

/* A released stage card's play button is bigger, with a glow, than a thumbnail's. */
.video-series-series-slider__card--stage .video-series-episode__play {
	box-shadow: 0 0 20px rgba( 245, 166, 35, 0.55 );
	height: 52px;
	width: 52px;
}

.video-series-series-slider__card--stage .video-series-episode__play::before {
	border-width: 8px 0 8px 13px;
}

/*
 * A scheduled thumbnail's countdown sits next to the title instead of
 * centred over the image: a compact figure-over-label badge per unit, not
 * the larger boxed pair the player and the stage's poster use. `:not()`
 * carves the stage's countdown out of every rule below -- it keeps the
 * player-style treatment defined above instead.
 */
.video-series-series-slider__card .video-series-countdown:not( .video-series-countdown--stage ) {
	align-items: flex-end;
	flex-shrink: 0;
	gap: 3px;
	position: static;
	transform: none;
}

.video-series-series-slider__card .video-series-countdown:not( .video-series-countdown--stage ) .video-series-countdown__unit {
	align-items: center;
	background: none;
	border: 0;
	flex-direction: column;
	gap: 1px;
	padding: 0;
}

.video-series-series-slider__card .video-series-countdown:not( .video-series-countdown--stage ) .video-series-countdown__value {
	background-color: rgba( 245, 166, 35, 0.13 );
	border: 1px solid rgba( 245, 166, 35, 0.28 );
	border-radius: 4px;
	box-sizing: border-box;
	font-size: 0.6875rem;
	font-weight: 800;
	line-height: 1;
	padding: 3px 0;
	text-align: center;
	width: 24px;
}

.video-series-series-slider__card .video-series-countdown:not( .video-series-countdown--stage ) .video-series-countdown__symbol {
	color: #6b6b8a;
	font-size: 0.4375rem;
	line-height: 1.5;
}

/* The focused thumbnail's countdown grows 1.42x, matching the design's
 * emphasis on whichever scheduled Episode is currently in view. */
@media ( min-width: 600px ) {
	.video-series-series-slider__thumbs .swiper-slide-thumb-active .video-series-countdown {
		gap: 4px;
	}

	.video-series-series-slider__thumbs .swiper-slide-thumb-active .video-series-countdown__unit {
		gap: 1px;
	}

	.video-series-series-slider__thumbs .swiper-slide-thumb-active .video-series-countdown__value {
		font-size: 0.976rem;
		padding: 4px 0;
		width: 34px;
	}

	.video-series-series-slider__thumbs .swiper-slide-thumb-active .video-series-countdown__symbol {
		font-size: 0.621rem;
	}
}

/*
 * Below the breakpoint (mobile only -- tablet now shares the stage+thumbs
 * layout above) the reference design drops the full-width "stage" row
 * entirely and shows a single strip of cards instead, where the active
 * (swiped-to) card grows to the stage's own treatment -- accent border, Live
 * badge, bigger play button and type -- and every other card shrinks and
 * desaturates. `view.js` mirrors this: below 600px it never initialises the
 * stage swiper at all and drives the thumbs strip on its own, with Swiper's
 * `swiper-slide-active` class (not a `--card--stage` role fixed at render
 * time) marking whichever card gets the treatment.
 */
@media ( max-width: 599px ) {
	.video-series-series-slider__stage {
		display: none;
	}

	.video-series-series-slider__thumbs {
		margin-bottom: 0;
		/*
		 * Fixed to the active card's own height (see `swiper-slide-active`
		 * below) so the strip's height doesn't depend on which card
		 * currently carries that class. Without this, the container's auto
		 * height tracks whatever the browser happens to lay out as the
		 * tallest slide as Swiper translates the strip on swipe, so the
		 * whole block visibly resizes -- collapsing to the shorter inactive
		 * height and back -- as the active card scrolls past the edge of
		 * the visible strip instead of staying put.
		 */
		height: 280px;
	}

	/*
	 * The active thumb grows past the others (see the `swiper-slide-active`
	 * rule below), so the strip has to center its slides on the cross axis
	 * or the shorter, inactive cards sit pinned to the top instead of
	 * centered against the taller active one, as the reference design shows.
	 */
	.video-series-series-slider__thumbs .swiper-wrapper {
		align-items: center;
	}

	.video-series-series-slider__thumbs .video-series-episode__thumbnail {
		display: flex;
		flex-direction: column;
		height: 100%;
		padding: 0 12px;
	}

	.video-series-series-slider__thumbs .video-series-series-slider__card--thumb {
		border-radius: 14px;
		height: 200px;
		width: 140px;
	}

	/*
	 * Only a released Episode ever takes the enlarged "stage" treatment --
	 * a scheduled one stays at thumb size even when it is the active slide,
	 * matching the reference design.
	 */
	.video-series-series-slider__thumbs .swiper-slide-active.video-series-series-slider__card--thumb:not( .is-upcoming ) {
		border-color: var( --vs-accent );
		box-shadow: 0 0 32px rgba( 245, 166, 35, 0.18 );
		height: 280px;
		width: 200px;
	}

	.video-series-series-slider__thumbs .swiper-slide-active .video-series-episode__play {
		box-shadow: 0 0 12px rgba( 245, 166, 35, 0.5 );
		height: 48px;
		width: 48px;
	}

	/*
	 * The title, meta and countdown sit in normal flow instead of as an
	 * absolutely-positioned overlay. `margin-top: auto` pushes the group down
	 * to the card's bottom edge.
	 */
	.video-series-series-slider__thumbs .video-series-episode__body {
		bottom: auto;
		inset-inline: auto;
		margin-top: auto;
		position: static;
	}

	.video-series-series-slider__thumbs .video-series-episode__title-row {
		display: contents;
	}

	.video-series-series-slider__thumbs .video-series-episode__title {
		font-size: 0.8125rem;
		line-height: 1.2;
		order: 1;
	}

	.video-series-series-slider__thumbs .video-series-episode__meta {
		font-size: 0.5625rem;
		line-height: 1.5;
		order: 2;
	}

	.video-series-series-slider__thumbs .video-series-countdown {
		gap: 6px;
		order: 3;
		padding-bottom: 10px;
	}

	.video-series-series-slider__thumbs .video-series-countdown__value {
		font-size: 1.125rem;
		padding: 4px 0;
		width: 36px;
	}

	.video-series-series-slider__thumbs .video-series-countdown__symbol {
		font-size: 0.5rem;
	}

	.video-series-series-slider__thumbs .swiper-slide-active.video-series-series-slider__card--thumb:not( .is-upcoming ) .video-series-episode__title {
		color: #ffffff;
		font-size: 1.125rem;
		font-weight: 800;
		line-height: 1.1;
	}

	.video-series-series-slider__thumbs .swiper-slide-active.video-series-series-slider__card--thumb:not( .is-upcoming ) .video-series-episode__meta {
		color: #a0a0c0;
		font-size: 0.625rem;
	}

	.video-series-series-slider__thumbs .swiper-slide-active:not( .is-upcoming ) .video-series-episode__body {
		gap: 4px;
	}
}

/* Section headings (single-series and single-episode templates) ------------- */

/*
 * Matches the slider's own `__heading` treatment so every section heading
 * across the Series and Episode templates -- Previous Episodes, More In This
 * Series, Latest Episodes -- reads as one visual system, rather than falling
 * back to whatever color the active theme gives `wp:heading` by default.
 */
.video-series-section-heading {
	color: #ef501e;
	font-size: 1.5rem;
	font-weight: 800;
	margin: 0 0 20px;
}

/* Episode title and parent-Series link (single-episode template) ------------ */

/*
 * Two selectors because the two templates give this element different classes
 * -- `.wp-block-post-title` under a block theme (`templates/single-episode.php`),
 * `.video-series-episode-single__title` under a classic one
 * (`templates/classic/single-episode.php`) -- so both need the same rule.
 *
 * `margin-top` carries the whole gap above the player rather than the
 * player's own `margin-bottom`: a block theme's global styles set
 * `:root :where(.is-layout-flow) > * { margin-block-end: 0 }` on every child
 * of `.video-series-watch__main` (see `templates/single-episode.php`), a
 * same-specificity rule that -- loaded after this stylesheet -- wins and
 * zeroes out any margin-bottom set on the player. This selector's two classes
 * already out-specify that rule for margin-top, which is why the working gap
 * lives here instead.
 */
.single-episode .wp-block-post-title,
.single-episode .video-series-episode-single__title {
	color: var( --vs-ink );
	font-size: 1.75rem;
	font-weight: 800;
	line-height: 1.3;
	margin: 32px 0 16px;
}

@media ( min-width: 782px ) {
	.single-episode .wp-block-post-title,
	.single-episode .video-series-episode-single__title {
		font-size: 2rem;
	}
}

/*
 * Paired tightly with the title below it, the same kicker/heading pattern the
 * slider and the Series template's own kicker use, rather than leaving the
 * theme's full blockGap between the breadcrumb and the title it belongs with.
 */
.video-series-episode-single__series-link {
	margin: 0 0 6px;
}

.video-series-episode-single__series-link a {
	align-items: center;
	color: #5c5a68;
	display: inline-flex;
	font-size: 0.8125rem;
	font-weight: 600;
	gap: 6px;
	text-decoration: none;
}

.video-series-episode-single__series-link a:hover,
.video-series-episode-single__series-link a:focus-visible {
	color: #f5a623;
}

.video-series-episode-single__series-arrow {
	font-size: 0.75rem;
}

/* Core marks a right-to-left document with `.rtl` on `<body>`; the arrow glyph
 * itself does not flip with `direction`, so it is mirrored by hand here. */
.rtl .video-series-episode-single__series-arrow {
	display: inline-block;
	transform: scaleX( -1 );
}

/* Watch layout (single-episode template) ------------------------------------- */

/*
 * Two named columns rather than `core/columns`: the sidebar's fixed minimum
 * width (`minmax(295px, 3fr)`) and the mobile stacking below aren't
 * expressible with the columns block, and layout decisions for this
 * template already live in PHP/CSS (see
 * docs/decisions/0003-blocks-and-block-templates-as-presentation-layer.md).
 * Single column by default; the two-column grid only applies from
 * `lg` up, matching the reference design.
 */
.video-series-watch {
	display: block;
}

.video-series-watch__main {
	min-width: 0;
}

@media ( min-width: 1024px ) {
	.video-series-watch {
		align-items: start;
		display: grid;
		gap: 28px;
		grid-template-columns: minmax( 0, 7fr ) minmax( 295px, 3fr );
	}

	.video-series-watch__aside {
		position: sticky;
		top: 20px;
	}
}

/* Episode meta line (views, publish/release date) ---------------------------- */

.video-series-episode-meta {
	color: var( --vs-text-muted );
	font-size: 0.875rem;
	font-weight: 500;
	margin: 0 0 16px;
}

.video-series-episode-meta__separator {
	margin: 0 6px;
}

/* Action row (like, dislike, share, save) ------------------------------------ */

/*
 * A horizontal scroller on narrow viewports rather than wrapping: keeps every
 * action reachable with a thumb in one row, matching the mobile wireframe.
 */
/*
 * `display: flex` and `gap` are set explicitly rather than relied on from
 * `core/group`'s own layout styles: the classic-theme template
 * (`templates/classic/single-episode.php`) renders this as a plain `<div>`,
 * not a real `core/group` block, so it gets no inline layout styles of its
 * own -- this rule is what makes both templates render the same row.
 */
.video-series-action-row {
	display: flex;
	gap: 10px;
	margin: 0 0 20px;
	overflow-x: auto;
	padding-bottom: 2px;
	scrollbar-width: none;
}

.video-series-action-row::-webkit-scrollbar {
	display: none;
}

.video-series-action-row > * {
	flex-shrink: 0;
}

.video-series-episode-actions {
	display: flex;
	gap: 10px;
}

.video-series-episode-actions__button,
.video-series-series-share--compact .video-series-series-share__button {
	align-items: center;
	background-color: var( --vs-surface-deep );
	border: 1px solid var( --vs-border );
	border-radius: 999px;
	color: var( --vs-text );
	display: inline-flex;
	font-size: 0.875rem;
	font-weight: 700;
	gap: 8px;
	min-height: 44px;
	padding: 0 16px;
	transition: background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
	white-space: nowrap;
}

.video-series-episode-actions__button:hover,
.video-series-episode-actions__button:focus-visible,
.video-series-series-share--compact .video-series-series-share__button:hover,
.video-series-series-share--compact .video-series-series-share__button:focus-visible {
	border-color: var( --vs-accent );
	outline-style: none;
}

.video-series-episode-actions__button.is-active {
	background-color: rgba( 245, 166, 35, 0.14 );
	border-color: var( --vs-accent );
	color: var( --vs-accent );
}

.video-series-episode-actions__icon,
.video-series-series-share--compact .video-series-series-share__icon {
	align-items: center;
	display: inline-flex;
	height: 18px;
	width: 18px;
}

.video-series-episode-actions__icon svg,
.video-series-series-share--compact .video-series-series-share__icon svg {
	height: 18px;
	width: 18px;
}

.video-series-episode-actions__count {
	color: var( --vs-text-muted );
	font-weight: 600;
}

/*
 * The compact Share variant reuses the card variant's markup and Interactivity
 * store (see `blocks/series-share/render.php`), so only its box model needs
 * resetting here -- the shared pill look above already applies via the
 * `.video-series-series-share__button` selector.
 */
.video-series-series-share--compact {
	background: none;
	border: 0;
	display: contents;
	padding: 0;
}

/*
 * Resets the card variant's row-ordering margin (see `.video-series-series-share__share-button`
 * above): the compact button is this row's only child, laid out by
 * `.video-series-action-row`'s own flex gap instead.
 */
.video-series-series-share--compact .video-series-series-share__share-button {
	margin: 0;
	order: initial;
}

/* Series info (creator/channel row, mapped to the parent Series) ------------- */

/*
 * There is no user/channel/follower model in this plugin -- the Series is
 * the closest thing to a "channel", and the follower count and Follow button
 * are a visual mock only. See
 * docs/decisions/0014-episode-watch-page-redesign.md.
 */
.video-series-series-info {
	align-items: center;
	background-color: var( --vs-surface );
	border: 1px solid var( --vs-border );
	border-radius: var( --vs-radius-lg );
	display: flex;
	gap: 12px;
	margin: 0 0 20px;
	padding: 14px 16px;
}

.video-series-series-info__avatar {
	border-radius: 50%;
	display: block;
	flex-shrink: 0;
	height: 48px;
	overflow: hidden;
	width: 48px;
}

.video-series-series-info__avatar img {
	display: block;
	height: 100%;
	object-fit: cover;
	width: 100%;
}

.video-series-series-info__avatar-fallback {
	align-items: center;
	background-color: var( --vs-surface-deep );
	color: var( --vs-accent );
	display: flex;
	font-weight: 800;
	height: 100%;
	justify-content: center;
	width: 100%;
}

.video-series-series-info__body {
	flex: 1 1 0%;
	min-width: 0;
}

.video-series-series-info__name {
	color: var( --vs-text );
	display: block;
	font-weight: 700;
	overflow: hidden;
	text-decoration: none;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.video-series-series-info__followers {
	color: var( --vs-text-muted );
	font-size: 0.8125rem;
	margin: 2px 0 0;
}

.video-series-series-info__follow {
	background-color: var( --vs-text );
	border: 0;
	border-radius: 999px;
	color: #ffffff;
	flex-shrink: 0;
	font-size: 0.875rem;
	font-weight: 700;
	min-height: 44px;
	padding: 0 18px;
}

.video-series-series-info__follow.is-active {
	background-color: var( --vs-surface-deep );
	color: var( --vs-text );
}

/* Episode description (collapsible content card) ----------------------------- */

.video-series-episode-description {
	background-color: var( --vs-surface );
	border: 1px solid var( --vs-border );
	border-radius: var( --vs-radius-lg );
	margin: 0 0 20px;
	padding: 18px 20px;
}

/*
 * `the_content` paragraphs otherwise inherit the theme's own default body
 * font size (`wp-block-paragraph`, ~21.76px in this project's theme) --
 * every other run of text in this plugin's blocks sets its own `--vs-*`-scaled
 * size rather than relying on the surrounding theme, and this is the one
 * place that hadn't.
 */
.video-series-episode-description__body {
	color: var( --vs-text-muted );
	font-size: 0.9375rem;
	line-height: 1.6;
	max-height: 104px;
	overflow: hidden;
	transition: max-height 0.35s ease;
}

.video-series-episode-description.is-open .video-series-episode-description__body {
	max-height: 2000px;
}

.video-series-episode-description__toggle {
	background: none;
	border: 0;
	color: var( --vs-accent );
	font-weight: 700;
	margin-top: 10px;
	min-height: 44px;
	padding: 0;
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* Recommendations (sidebar) --------------------------------------------------- */

.video-series-recommendations__heading {
	color: var( --vs-text );
	font-size: 1.125rem;
	font-weight: 800;
	margin: 0 0 12px;
}

.video-series-recommendations__filters {
	display: flex;
	gap: 8px;
	margin: 0 0 16px;
}

.video-series-recommendations__filter {
	background-color: var( --vs-surface-deep );
	border-radius: 999px;
	color: var( --vs-text-muted );
	display: inline-block;
	font-size: 0.8125rem;
	font-weight: 700;
	padding: 8px 14px;
}

.video-series-recommendations__filter.is-active {
	background-color: var( --vs-text );
	color: #ffffff;
}

.video-series-recommendations__list {
	display: flex;
	flex-direction: column;
	gap: 14px;
	list-style: none;
	margin: 0;
	padding: 0;
}

.video-series-recommendations__list .video-series-episode__link {
	display: grid;
	gap: 10px;
	/*
	 * A percentage, not a fixed px breakpoint: this list's actual rendered
	 * width varies with `.video-series-watch`'s grid (a ~300px sidebar at
	 * 1024px+, but the full main-column width once the layout stacks below
	 * that), so a fixed thumbnail width either dwarfs the sidebar or gets
	 * lost in the stacked, much wider column. A share of the link's own
	 * width tracks both.
	 */
	grid-template-columns: minmax( 130px, 46% ) 1fr;
}

.video-series-recommendations__list .video-series-episode__thumbnail::after {
	background: linear-gradient( to top, rgba( 7, 7, 15, 0.45 ) 0%, rgba( 7, 7, 15, 0 ) 30% );
	content: "";
	inset: 0;
	pointer-events: none;
	position: absolute;
	z-index: 1;
}

.video-series-recommendations__list .video-series-episode__play {
	display: block;
}

.video-series-recommendations__list .video-series-episode__body {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding-top: 0;
}

.video-series-recommendations__list .video-series-episode__title {
	color: var( --vs-text );
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	font-size: 0.875rem;
	font-weight: 700;
	overflow: hidden;
}

/*
 * The unscaled countdown chip (three bordered unit boxes, sized for the
 * wide grid cards in `.video-series-series-episodes`/`.video-series-latest-episodes`)
 * is wider than this list's own thumbnail column at its `minmax()` floor,
 * so it overflows. Shrunk the same way the slider's own compact context
 * already does for its narrower cards, further down this file.
 */
.video-series-recommendations__list .video-series-countdown {
	gap: 4px;
}

.video-series-recommendations__list .video-series-countdown__unit {
	padding: 3px 4px;
}

.video-series-recommendations__list .video-series-countdown__value {
	font-size: 0.75rem;
	min-width: 1.1ch;
}

.video-series-recommendations__list .video-series-countdown__symbol {
	font-size: 0.5rem;
}

/* Dark theme -----------------------------------------------------------
 *
 * Everything above is the light palette: the site this plugin ships on has
 * no working light/dark switch of its own, so a visitor with no explicit
 * dark preference -- the common case -- must land on colors that read
 * against this site's always-light page, not the plugin's original dark
 * design. Dark is now the override, applied only on an explicit dark
 * signal, in the same `--vs-*` custom properties every rule above already
 * reads through, plus the small set of literal-color selectors called out
 * below.
 *
 * The accent yellow (`--vs-accent` and every hardcoded rgba(245, 166, 35, ...)
 * built from it -- badges, the play button, the countdown figures, the live
 * marker, the slider's active border and pagination dot) is left alone by
 * design: it is this plugin's one brand color and reads fine on either
 * background. `--vs-ink` is left alone too -- it only ever contrasts the play
 * triangle against the accent disc, never the page.
 *
 * The countdown chip (`.video-series-countdown`) is left alone in full,
 * background and label included: it is styled as a small self-contained
 * dark chip regardless of surrounding page theme, so it needs no override
 * either direction.
 *
 * The slider's cards have the same overlay problem in reverse: their title,
 * meta and "Live" marker sit on a fixed dark scrim over the thumbnail photo
 * (`.video-series-episode__thumbnail::after` above), not on the surface
 * color, so they're pinned to literal light-on-dark values in the base
 * rules above and need no override here in either direction.
 *
 * Only one signal puts a visitor in dark mode -- see
 * `docs/decisions/0015-dark-light-mode-theming.md`:
 *
 * - WP Dark Mode (a third-party plugin, not part of this repo) stamps
 *   `.wp-dark-mode-active` on `<html>` whenever *its* resolved state is
 *   dark, an explicit visitor choice independent of their OS setting. That
 *   class alone is authoritative, so the block below needs no media query.
 *
 * There is deliberately no `prefers-color-scheme: dark` fallback: the
 * site's actual theme (`ch_theme`) renders an always-light page regardless
 * of OS preference, so that media query used to darken this plugin's
 * containers inside a page that stayed light for every visitor whose
 * OS/browser reports a dark preference -- the exact "dark on light theme"
 * bug that removed it. Re-add an OS-driven fallback only once the host
 * theme itself has a working dark mode to agree with.
 */
html.wp-dark-mode-active .video-series-series-episodes,
html.wp-dark-mode-active .video-series-latest-episodes,
html.wp-dark-mode-active .video-series-series-share,
html.wp-dark-mode-active .video-series-episode-player,
html.wp-dark-mode-active .video-series-watch {
	--vs-surface: #0f0f1a;
	--vs-surface-deep: #0d0d1a;
	--vs-text: #ffffff;
	--vs-text-muted: #a0a0c0;
	--vs-text-dim: #5a5a78;
	--vs-border: rgba( 255, 255, 255, 0.06 );
}

html.wp-dark-mode-active .video-series-series-slider {
	--vs-surface: #0f0f1a;
	--vs-surface-deep: #0d0d1a;
	--vs-text: #ffffff;
	--vs-text-muted: #a0a0c0;
	--vs-text-dim: #5a5a78;
	--vs-border: rgba( 255, 255, 255, 0.06 );
}

/* Classic-theme template root only -- see the rule this overrides, above. */
html.wp-dark-mode-active .video-series-main {
	background-color: transparent;
}

html.wp-dark-mode-active .single-series .wp-block-post-title,
html.wp-dark-mode-active .single-series .video-series-series__title {
	color: #5a5a78;
}

html.wp-dark-mode-active .video-series-section-heading {
	color: #ffffff;
}

html.wp-dark-mode-active .single-episode .wp-block-post-title,
html.wp-dark-mode-active .single-episode .video-series-episode-single__title {
	color: #ffffff;
}

html.wp-dark-mode-active .video-series-episode-single__series-link a {
	color: #a0a0c0;
}

html.wp-dark-mode-active .video-series-series-share__icon {
	background-image: linear-gradient( 135deg, #1a1a2e 0%, #2a2a42 100% );
}

html.wp-dark-mode-active .video-series-series-slider__pagination .swiper-pagination-bullet {
	background-color: rgba( 255, 255, 255, 0.15 );
}
