/**
 * YouTube facade styles.
 *
 * The thumbnail arrives as a custom property (--mf-ytf-thumb) set on the element, not as
 * an inline background-image: the suite passes values in, keeps rules here.
 */

.mf-ytf {
	--mf-ytf-radius: 8px;
	--mf-ytf-bg: #1a1a1a;
	--mf-ytf-fg: #fff;
	--mf-ytf-accent: #c00;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.mf-ytf__stage {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	aspect-ratio: 16 / 9;
	overflow: hidden;
	border-radius: var(--mf-ytf-radius);
	background-color: var(--mf-ytf-bg);
	background-image: var(--mf-ytf-thumb, none);
	background-position: center;
	background-size: cover;
}

.mf-ytf__frame {
	width: 100%;
	height: 100%;
	border: 0;
}

.mf-ytf__play {
	display: inline-flex;
	align-items: center;
	gap: 0.6em;
	padding: 0.7em 1.2em;
	border: 0;
	border-radius: 999px;
	background: rgba(0, 0, 0, 0.72);
	color: var(--mf-ytf-fg);
	font: inherit;
	font-weight: 600;
	cursor: pointer;
}

.mf-ytf__play:hover,
.mf-ytf__play:focus-visible {
	background: rgba(0, 0, 0, 0.88);
}

.mf-ytf__play-icon {
	width: 0;
	height: 0;
	border-top: 0.5em solid transparent;
	border-bottom: 0.5em solid transparent;
	border-left: 0.85em solid var(--mf-ytf-accent);
}

.mf-ytf__title {
	position: absolute;
	inset-inline: 0;
	inset-block-end: 0;
	margin: 0;
	padding: 0.6em 0.8em;
	background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent);
	color: var(--mf-ytf-fg);
	font-size: 0.9rem;
}

/*
 * The consent notice. Visible before the click and never collapsed behind a control —
 * it is what makes the click informed, so hiding it would remove the legal basis rather
 * than tidy the layout.
 */
.mf-ytf__notice {
	margin: 0;
	font-size: 0.8rem;
	line-height: 1.45;
	opacity: 0.85;
}

.mf-ytf__withdrawal {
	display: block;
}

.mf-ytf__privacy {
	white-space: nowrap;
}

/* --- The carousel ------------------------------------------------------- */

.mf-ytf-car {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.mf-ytf-car__label {
	margin: 0;
}

.mf-ytf-car__nav {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.mf-ytf-car__prev,
.mf-ytf-car__next {
	min-width: 2.2em;
	padding: 0.2em 0.6em;
	border: 1px solid currentcolor;
	border-radius: 999px;
	background: transparent;
	color: inherit;
	font: inherit;
	cursor: pointer;
}

.mf-ytf-car__prev[disabled],
.mf-ytf-car__next[disabled] {
	opacity: 0.4;
	cursor: default;
}

.mf-ytf-car__count {
	font-variant-numeric: tabular-nums;
}

/*
 * Scroll-snap, not a transform. With JavaScript off this stays a scrollable row of every
 * video instead of one visible slide and the rest unreachable — and a loaded iframe is
 * never moved by a transform it did not expect.
 */
.mf-ytf-car__track {
	display: flex;
	gap: 1rem;
	margin: 0;
	padding: 0;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	list-style: none;
}

.mf-ytf-car__slide {
	flex: 0 0 100%;
	scroll-snap-align: start;
}

/*
 * One notice for the whole carousel, under the track and across its full width (#85).
 * The track is a flex row of slides at 50% or 100% basis; this sits outside it, so it
 * spans whatever the carousel spans and stays still while the track scrolls.
 *
 * Not a style tweak in kind: each slide used to carry its own copy, which meant the legal
 * text scrolled away with the video it described. Here it does not move at all.
 */
.mf-ytf-car__notice {
	width: 100%;
}

@media (min-width: 48rem) {

	.mf-ytf-car__slide {
		flex-basis: calc(50% - 0.5rem);
	}
}

@media (prefers-reduced-motion: reduce) {

	.mf-ytf-car__track {
		scroll-behavior: auto;
	}
}

/* --- The lightbox ------------------------------------------------------- */

/*
 * The overlay route. Its reason for existing is not looks: an animated gallery cannot
 * contain a playing iframe (SPEC §14), and an iframe in an overlay is an iframe the
 * gallery never holds. It is also the only route where closing the player really removes
 * it, which is what the consent notice's withdrawal sentence promises.
 */
body.mf-ytf-lb-open {
	overflow: hidden;
}

.mf-ytf-lb {
	position: fixed;
	z-index: 999999;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: clamp(0.75rem, 4vw, 3rem);
	inset: 0;
}

.mf-ytf-lb__backdrop {
	position: absolute;
	background: rgba(0, 0, 0, 0.82);
	inset: 0;
}

.mf-ytf-lb__panel {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	width: min(100%, 68rem);
	max-height: 100%;
	overflow-y: auto;
	color: #fff;
}

.mf-ytf-lb__stage {
	aspect-ratio: 16 / 9;
	overflow: hidden;
	border-radius: 8px;
	background: #000;
}

.mf-ytf-lb__stage .mf-ytf__frame {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

/*
 * First in the panel so a Tab reaches it first, but visually above the stage on the
 * right — the place a reader looks for it. `order` moves a box, and this one never
 * contains the iframe, so the rule in SPEC §14 is not in play here.
 */
.mf-ytf-lb__close {
	align-self: flex-end;
	width: 2.4em;
	height: 2.4em;
	padding: 0;
	border: 1px solid rgba(255, 255, 255, 0.45);
	border-radius: 999px;
	background: rgba(0, 0, 0, 0.5);
	color: #fff;
	font: inherit;
	font-size: 1rem;
	line-height: 1;
	cursor: pointer;
}

.mf-ytf-lb__close::before {
	content: "\00d7";
	font-size: 1.5em;
}

.mf-ytf-lb__close:hover,
.mf-ytf-lb__close:focus-visible {
	border-color: #fff;
	background: rgba(0, 0, 0, 0.8);
}

/*
 * The notice travels into the overlay with the video, so it needs to be readable on the
 * backdrop rather than on the page's own background.
 */
.mf-ytf-lb .mf-ytf__notice {
	color: #e8e8e8;
	opacity: 1;
}

.mf-ytf-lb .mf-ytf__privacy {
	color: inherit;
}

/* --- The galleries ------------------------------------------------------ */

/*
 * Four layouts from DIVB/design/video-galerie-katalog.html: 01 Editorial Grid,
 * 02 Filmstrip Feature, 09 Split Player and 04 Magazine Cover.
 *
 * The catalogue's own typography is NOT reproduced. It loads Fraunces and IBM Plex Mono
 * from fonts.googleapis.com, and this module exists precisely to stop a third-party asset
 * request reaching a visitor unasked — so the display faces become the system serif and
 * monospace stacks, and Editorial Grid loses part of its character. That is the correct
 * trade, and never a font URL (SPEC §17.2).
 *
 * The other rule these layouts obey is SPEC §14: nothing here transforms, animates or
 * re-parents a box that can hold a playing iframe. Where a layout wants motion, the video
 * goes to the lightbox instead.
 */
.mf-ytf-gal {

	/*
	 * The ring a track draws around its current or focused pick, as two numbers plus the
	 * room they need. Shared, because the filmstrip and the split player both scroll and a
	 * scrolling box clips on both axes: whatever a variant sets here, the same value has to
	 * become clearance inside the track or the ring is cut off.
	 */
	--mf-ytf-ring: 2px;
	--mf-ytf-ring-gap: 2px;
	--mf-ytf-ring-room: calc(var(--mf-ytf-ring) + var(--mf-ytf-ring-gap));
	--mf-ytf-gal-serif: ui-serif, georgia, "Times New Roman", times, serif;
	--mf-ytf-gal-mono: ui-monospace, sfmono-regular, "SF Mono", menlo, consolas, monospace;
	--mf-ytf-gal-rule: rgba(0, 0, 0, 0.14);
	--mf-ytf-gal-dim: #6b6f76;
	--mf-ytf-gal-dark: #14171c;
	--mf-ytf-gal-accent: #c00;
	--mf-ytf-gal-gap: 1.5rem;
	display: flex;
	flex-direction: column;
	gap: 1rem;
}

.mf-ytf-gal__label {
	margin: 0;
}

.mf-ytf-gal__items {
	display: grid;
	gap: var(--mf-ytf-gal-gap);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * The stage holds every facade, and the script marks itself with `is-live` before this
 * rule is allowed to hide any of them. Without that gate a reader with JavaScript off
 * would be left with one video and the rest unreachable — the exact failure v1 rejected
 * when it chose a scroll-snap track over a transform.
 */
.mf-ytf-gal__stage {
	display: flex;
	flex-direction: column;
	gap: var(--mf-ytf-gal-gap);
	min-width: 0;
}

.mf-ytf-gal.is-live .mf-ytf-gal__slot:not(.is-current) {
	display: none;
}

.mf-ytf-gal__track {
	display: flex;
	gap: 0.75rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.mf-ytf-gal__pick {
	display: flex;
	width: 100%;
	padding: 0;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	text-align: start;
	cursor: pointer;
}

/*
 * An explicit focus ring, because `border: 0; background: none` above strips whatever the
 * theme would have drawn. Each variant then adds its own emphasis on top — an outlined
 * thumbnail, a highlighted row, an underlined headline — but none of those may be the only
 * indication: a keyboard user has to see where they are even in a variant that has not
 * been styled yet.
 */
.mf-ytf-gal__pick:focus-visible {
	outline: 2px solid currentcolor;
	outline-offset: 2px;
}

/*
 * The picks arrive disabled and the script enables them, so this is the state a reader
 * without JavaScript sees: a legible static index rather than controls that look operable
 * and do nothing. The stack of facades above it is the working route.
 */
.mf-ytf-gal__pick[disabled] {
	opacity: 0.75;
	cursor: default;
}

.mf-ytf-gal__pick-thumb {
	display: block;
	flex: 0 0 auto;
	aspect-ratio: 16 / 9;
	border-radius: 6px;
	background-color: var(--mf-ytf-bg, #1a1a1a);
	background-image: var(--mf-ytf-thumb, none);
	background-position: center;
	background-size: cover;
}

.mf-ytf-gal__pick-num {
	font-family: var(--mf-ytf-gal-mono);
	font-size: 0.7rem;
	font-variant-numeric: tabular-nums;
	color: var(--mf-ytf-gal-dim);
}

.mf-ytf-gal__pick-title {
	font-size: 0.82rem;
	font-weight: 600;
	line-height: 1.35;
}

/*
 * One consent notice for a whole gallery, below it (#85). The grid uses it; the three stage
 * layouts do not, because they show one facade at a time and so only ever display one
 * notice anyway.
 *
 * It is a child of `.mf-ytf-gal`, which is a column flex container, so it already spans the
 * gallery's width — there is nothing to declare for that. What it does need is a little air
 * above it, because the gallery's own `gap` puts it as far from the tiles as the tiles are
 * from each other, and it reads as another tile at that distance.
 */
.mf-ytf-gal__notice {
	margin-block-start: 0.25rem;
}

/* --- 01 Editorial Grid -------------------------------------------------- */

/*
 * The quiet default, and the only layout that needs no JavaScript whatsoever: every video
 * is a live facade in a static raster, and nothing moves after a click.
 */
.mf-ytf-gal--grid .mf-ytf-gal__items {
	grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
	column-gap: 2rem;
}

.mf-ytf-gal--grid .mf-ytf-gal__item {
	padding-top: 1rem;
	border-top: 1px solid var(--mf-ytf-gal-rule);
}

.mf-ytf-gal--grid .mf-ytf__stage {
	border-radius: 4px;
}

/*
 * The catalogue draws Editorial Grid's headline BELOW the image. It cannot go there: the
 * title is a child of `.mf-ytf__stage` in the facade's frozen markup, and the stage is a
 * centring flex container — taking the title out of `position: absolute` makes it a flex
 * item beside the play button, which is a headline drawn straight through the button.
 * (Found on the live check; no gate here can see an overlap.)
 *
 * So the caption stays the caption, as in every other layout, and the editorial character
 * comes from the serif and the thin rule above the cell instead. Moving it below the image
 * would mean changing the facade's markup, which is a bigger promise than a layout.
 */
.mf-ytf-gal--grid .mf-ytf__title {
	font-family: var(--mf-ytf-gal-serif);
	font-size: 1.05rem;
	font-weight: 600;
	line-height: 1.25;
}

/* --- 02 Filmstrip Feature ----------------------------------------------- */

/*
 * A stage above, a horizontal strip below. The strip scrolls; the stage never moves, so
 * the only box that can hold an iframe is also the only box that stays put.
 */
.mf-ytf-gal--filmstrip {
	padding: 1.75rem;
	border-radius: 10px;
	background: #0b0d10;
	color: #fff;
}

/*
 * The heading inherits the theme's colour, which on a near-black panel is a heading nobody
 * can read. Found on the live check, not in a test — a contrast failure is invisible to
 * every gate this repo has.
 */
.mf-ytf-gal--filmstrip .mf-ytf-gal__label,
.mf-ytf-gal--split .mf-ytf-gal__label {
	color: #fff;
}

/*
 * The strip scrolls, and a scroll container clips on BOTH axes: when `overflow-x` is not
 * `visible`, `overflow-y` computes to `auto` too. So the ring around the active thumbnail —
 * 2px of outline standing 2px off the box — was cut away at the top, and at the left and
 * right on the first and last item. The keyboard focus ring on the pick was clipped the same
 * way, which is the worse half: a keyboard user could not see where they were (#85, Tom).
 *
 * The fix is room inside the scroll box, plus an equal negative margin so the strip still
 * lines up with the stage above it. Padding alone would indent the first thumbnail, and in
 * an editorial layout the edges are the design.
 */
.mf-ytf-gal--filmstrip .mf-ytf-gal__track {
	margin-block-start: calc(-1 * var(--mf-ytf-ring-room));
	margin-inline: calc(-1 * var(--mf-ytf-ring-room));
	overflow-x: auto;

	/*
	 * And the snap positions have to respect that padding, or the browser undoes it: a
	 * `scroll-snap-align: start` cell aligns to the SCROLLPORT's edge, not to the content
	 * box, so the track quietly scrolled itself 4px and clipped the first ring again.
	 * `scroll-padding` is what moves the snapport inwards.
	 */
	scroll-padding-inline: var(--mf-ytf-ring-room);
	scroll-snap-type: x proximity;
	padding-block: var(--mf-ytf-ring-room) calc(0.25rem + var(--mf-ytf-ring-room));
	padding-inline: var(--mf-ytf-ring-room);
}

.mf-ytf-gal--filmstrip .mf-ytf-gal__cell {
	flex: 0 0 9.5rem;
	scroll-snap-align: start;
}

.mf-ytf-gal--filmstrip .mf-ytf-gal__pick {
	flex-direction: column;
	gap: 0.4rem;
}

.mf-ytf-gal--filmstrip .mf-ytf-gal__pick-thumb {
	width: 100%;
	outline: var(--mf-ytf-ring) solid transparent;
	outline-offset: var(--mf-ytf-ring-gap);
	opacity: 0.55;
}

.mf-ytf-gal--filmstrip .mf-ytf-gal__pick:hover .mf-ytf-gal__pick-thumb,
.mf-ytf-gal--filmstrip .mf-ytf-gal__pick:focus-visible .mf-ytf-gal__pick-thumb,
.mf-ytf-gal--filmstrip .mf-ytf-gal__pick[aria-current] .mf-ytf-gal__pick-thumb {
	outline-color: var(--mf-ytf-gal-accent);
	opacity: 1;
}

.mf-ytf-gal--filmstrip .mf-ytf-gal__pick-num,
.mf-ytf-gal--filmstrip .mf-ytf-gal__pick-title {
	color: #cfd3d8;
}

/* --- 09 Split Player ---------------------------------------------------- */

/*
 * The same mechanism as the filmstrip with the axis turned: playlist beside a large
 * preview. Two variants out of one piece of behaviour, which is why they were chosen
 * together.
 */
.mf-ytf-gal--split {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 1.25rem;
	padding: 1.5rem;
	border-radius: 10px;
	background: var(--mf-ytf-gal-dark);
	color: #fff;
}

.mf-ytf-gal--split .mf-ytf-gal__label {
	grid-column: 1 / -1;
}

/*
 * Same clipping as the filmstrip's strip, one axis turned (#85, X8): the rows are flush with
 * the track on the left, the right and the top, so the keyboard focus ring's 4px of reach had
 * 0px of room. Only the focus ring — the active row is marked with a border and a background,
 * which are inside the box and were never at risk. It is the invisible half of the same bug:
 * you only meet it without a mouse.
 *
 * No `scroll-padding` here, unlike the filmstrip: this track does not snap, so nothing
 * realigns the padding away.
 */
.mf-ytf-gal--split .mf-ytf-gal__track {
	flex-direction: column;
	gap: 0;
	max-height: 28rem;
	margin: calc(-1 * var(--mf-ytf-ring-room));
	overflow-y: auto;
	padding: var(--mf-ytf-ring-room);
}

.mf-ytf-gal--split .mf-ytf-gal__pick {
	align-items: center;
	gap: 0.75rem;
	padding: 0.7rem 0.8rem;
	border-inline-start: 3px solid transparent;
}

.mf-ytf-gal--split .mf-ytf-gal__pick-thumb {
	width: 5.25rem;
}

.mf-ytf-gal--split .mf-ytf-gal__pick:hover,
.mf-ytf-gal--split .mf-ytf-gal__pick:focus-visible,
.mf-ytf-gal--split .mf-ytf-gal__pick[aria-current] {
	border-inline-start-color: var(--mf-ytf-gal-accent);
	background: rgba(255, 255, 255, 0.07);
}

.mf-ytf-gal--split .mf-ytf-gal__pick-num {
	color: #9aa1ab;
}

@media (min-width: 48rem) {

	.mf-ytf-gal--split {
		grid-template-columns: 20rem minmax(0, 1fr);
	}

	/* The track comes first in the reading order too, so this is a layout change only. */
	.mf-ytf-gal--split .mf-ytf-gal__stage {
		grid-column: 2;
		grid-row: 2;
	}

	.mf-ytf-gal--split .mf-ytf-gal__track {
		grid-column: 1;
		grid-row: 2;
	}
}

/* --- 04 Magazine Cover -------------------------------------------------- */

/*
 * One hero and a numbered table of contents. The catalogue draws the contents as a static
 * list; here it selects into the cover (Tom, 2026-08-19), because a list of videos nobody
 * can play is an announcement rather than a gallery — and once the stage mechanism exists,
 * making it clickable costs nothing.
 */
.mf-ytf-gal--magazine {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 1.5rem;
}

.mf-ytf-gal--magazine .mf-ytf-gal__label {
	grid-column: 1 / -1;
	font-family: var(--mf-ytf-gal-serif);
}

.mf-ytf-gal--magazine .mf-ytf__title {
	font-family: var(--mf-ytf-gal-serif);
	font-size: 1.35rem;
	font-weight: 700;
	line-height: 1.1;
}

.mf-ytf-gal--magazine .mf-ytf-gal__track {
	flex-direction: column;
	gap: 0;

	/*
	 * Room for the picks' focus ring, because the script turns this into a scroll box
	 * (divb-hq#90) and a scroll box clips BOTH axes: `overflow-y: auto` makes `overflow-x`
	 * compute to `auto` too, so the 2px ring and its 2px offset would be shaved off left
	 * and right. Same trap as the filmstrip's (v2.20.1), same fix — plus an equal negative
	 * margin, or the contents would sit indented against the rule below it.
	 *
	 * The BLOCK axis needs real room too, and measuring is what showed it: with padding on
	 * the inline axis only, the first row's ring had `topRoom: 0` — flush with the
	 * scrollport, so its top 2px was shaved. `scroll-padding-block` cannot save that one,
	 * because at `scrollTop: 0` there is nothing left to scroll into view. Padding creates
	 * the room; scroll-padding then keeps it while scrolling to a row further down.
	 *
	 * Every padding is answered by an equal negative margin, so the rows' own boxes stay
	 * exactly where they were: the contents still lines up with the cover beside it and with
	 * the rule below it.
	 */
	margin-block: calc(-1 * var(--mf-ytf-ring-room));
	margin-inline: calc(-1 * var(--mf-ytf-ring-room));
	padding-block: var(--mf-ytf-ring-room);
	padding-inline: var(--mf-ytf-ring-room);
	scroll-padding-block: var(--mf-ytf-ring-room);
}

.mf-ytf-gal--magazine .mf-ytf-gal__pick {
	align-items: baseline;
	gap: 0.75rem;
	padding: 0.8rem 0;
	border-bottom: 1px solid var(--mf-ytf-gal-rule);
}

/* The contents is a list of headlines; a thumbnail per row would make it a second grid. */
.mf-ytf-gal--magazine .mf-ytf-gal__pick-thumb {
	display: none;
}

.mf-ytf-gal--magazine .mf-ytf-gal__pick-num {
	flex: 0 0 1.5rem;
	font-family: var(--mf-ytf-gal-serif);
	font-style: italic;
	font-size: 0.85rem;
}

.mf-ytf-gal--magazine .mf-ytf-gal__pick-title {
	flex: 1 1 auto;
	font-size: 0.95rem;
}

.mf-ytf-gal--magazine .mf-ytf-gal__pick:hover .mf-ytf-gal__pick-title,
.mf-ytf-gal--magazine .mf-ytf-gal__pick:focus-visible .mf-ytf-gal__pick-title {
	text-decoration: underline;
	text-underline-offset: 3px;
}

.mf-ytf-gal--magazine .mf-ytf-gal__pick[aria-current] {
	color: var(--mf-ytf-gal-accent);
}

/*
 * The capped table of contents (divb-hq#90).
 *
 * A twelve-item playlist made this column 856px tall against a 255px cover, and because the
 * two are grid columns in one row, the SHORTER one got stretched: the cover ended up with a
 * 600px hole under it rather than the list simply being long. Four rows is the window, which
 * is 3.75 rows' worth of hero rounded up — the script measures where the fourth row actually
 * ends, so a title wrapping to a third line widens the window instead of being sliced.
 *
 * The scroll box is created by the script (`data-visible` on the track), so everything here
 * is about surviving it:
 *
 * - **`overflow-y: auto` clips the OTHER axis too.** A scroll container's `overflow-x`
 *   computes to `auto` as well, so the picks' 2px focus ring plus its 2px offset would be
 *   shaved off at the left and right edges. Same trap as the filmstrip's (v2.20.1), same
 *   fix: room inside, and an equal negative margin so the contents still lines up with the
 *   rule below it — an indented first row would be visible in an editorial layout.
 * - **`scroll-padding-block` is what keeps the ring whole while scrolling.** A pick focused
 *   by Tab is scrolled into view by the browser, which aligns it flush with the scrollport
 *   edge unless the scrollport is inset; padding alone makes room, it does not move where
 *   the browser scrolls to.
 */
.mf-ytf-gal--magazine .mf-ytf-gal__scroll {
	position: relative;
}

/*
 * The fade is a MASK, not a gradient over the page's background — the module does not know
 * what colour it is sitting on, and the ribbon's edge fade solved the same problem the same
 * way. It fades the rows themselves to transparent, so it is right on any background.
 *
 * Only while something is actually below the fold: the script adds `is-more` and drops it
 * again at the end of the scroll, so the page never suggests there is more when there is not.
 */
.mf-ytf-gal--magazine .mf-ytf-gal__scroll.is-more .mf-ytf-gal__track {
	mask-image: linear-gradient(to bottom, #000 calc(100% - 2.5rem), transparent);
}

/*
 * The counter is `aria-hidden` and it has to be: it repeats what the list already says, and
 * the list is complete in the markup. So it may not swallow a click meant for the row
 * underneath it either.
 */
.mf-ytf-gal--magazine .mf-ytf-gal__more {
	position: absolute;
	right: 0;
	bottom: 0;
	font-family: var(--mf-ytf-gal-serif);
	font-style: italic;
	font-size: 0.85rem;
	color: var(--mf-ytf-gal-accent);
	pointer-events: none;
}

/*
 * The divider, and the notice under it — both across the cover AND the contents (#85, Tom).
 *
 * The notice used to sit inside the left column under the cover, which read as a caption to
 * the cover rather than a note about the gallery. Spanning both columns says what it is, and
 * the rule above it gives the block the bottom edge it never had: the two columns are
 * independent tracks, so whichever is shorter used to trail off into nothing (part of M1).
 *
 * The rule is a border rather than an element, because a decorative line is presentation and
 * has no business in the markup. It is heavier than the module's other hairlines (2px against
 * 1px) on purpose: the others separate items of a list, this one closes a composition.
 *
 * The type is monospace and the rule is full strength, which is the editorial contrast —
 * but the SIZE and the opacity are the module's own defaults, deliberately. A first pass
 * set 0.72rem and multiplied the notice's 0.85 opacity down to about 0.6, purely for the
 * look. Making the one text the click's validity rests on smaller and fainter than
 * everywhere else is the wrong direction on this surface, whatever it does for the page.
 */
.mf-ytf-gal--magazine .mf-ytf-gal__notice {
	grid-column: 1 / -1;
	margin-block-start: 0;
	padding-block-start: 1.4rem;
	border-block-start: 2px solid currentcolor;
	font-family: var(--mf-ytf-gal-mono);
	line-height: 1.65;
	letter-spacing: 0.01em;
}

@media (min-width: 48rem) {

	.mf-ytf-gal--magazine {
		grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
	}
}

/* --- The four scrolling carousels ---------------------------------------- */

/*
 * Peek-Carousel, Karten-Carousel, Vollbild-Rotator and Kompakt-Ribbon, from
 * DIVB/design/video-galerie-carousels.html (divb-hq#86).
 *
 * **Every one of them scrolls; none of them transforms.** Two were drawn with
 * `transform: translateX` on the track, which is the failure SPEC §14 describes — a loaded
 * iframe must not be displaced — so all four are `overflow-x` + `scroll-snap` driven by
 * `scrollTo`, the mechanism v1 chose for exactly this reason. The pay-off is that
 * `play="inline"` stays valid in all four, so nothing has to constrain the author.
 *
 * **And the rule reaches the panels, not just the reel.** The Peek design dims its
 * neighbours with `transform: scale(.92)`; that is a transform on the very box that can hold
 * a playing iframe, so the dimming here is OPACITY ONLY. Losing 8% of scale costs a little
 * of the design; a player that reloads when the reader advances costs the feature.
 *
 * The catalogue's own type (Inter, from fonts.googleapis.com) is not reproduced — that
 * request is the thing this module exists to prevent. Its measured slider values are:
 * arrows 32x32 at 5px radius on rgba(0,0,0,.67), dots round with the active one at 73%.
 */

.mf-ytf-gal__viewport {
	position: relative;
	min-width: 0;
}

/*
 * The reel. `scroll-padding-inline` is not decoration: the ring clearance the filmstrip
 * needed applies here too, and a snapped panel has to land on the content edge rather than
 * the scrollport edge (the trap from #85's F2).
 */
.mf-ytf-gal__reel {
	display: flex;
	margin: 0;
	overflow-x: auto;
	padding: 0;
	list-style: none;
	scrollbar-width: none;
}

.mf-ytf-gal__reel::-webkit-scrollbar {
	display: none;
}

.mf-ytf-gal__panel {
	min-width: 0;
}

.mf-ytf-gal.is-live .mf-ytf-gal__reel {
	scroll-snap-type: x mandatory;
}

.mf-ytf-gal__head {
	display: flex;
	gap: 1rem;
	align-items: baseline;
	justify-content: space-between;
}

.mf-ytf-gal__head .mf-ytf-gal__label {
	flex: 1 1 auto;
	min-width: 0;
}

.mf-ytf-gal__arrows {
	display: flex;
	flex: 0 0 auto;
	gap: 0.5rem;
}

/* Measured from the live slider on /categories/ai: 32px, 5px radius, black at 67%. */
.mf-ytf-gal__prev,
.mf-ytf-gal__next {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	padding: 0;
	border: 0;
	border-radius: 5px;
	background: rgba(0, 0, 0, 0.67);
	color: #fff;
	font: inherit;
	line-height: 1;
	cursor: pointer;
}

.mf-ytf-gal__prev:hover:enabled,
.mf-ytf-gal__next:hover:enabled {
	background: rgba(0, 0, 0, 0.85);
}

/*
 * The arrows are the Ribbon's ONLY signal that there is more to the right, so their disabled
 * state has to be unmistakable rather than a polite dimming.
 */
.mf-ytf-gal__prev:disabled,
.mf-ytf-gal__next:disabled {
	opacity: 0.35;
	cursor: default;
}

.mf-ytf-gal__dots {
	display: flex;
	gap: 0.5rem;
	justify-content: center;
}

.mf-ytf-gal__dot {
	width: 0.5rem;
	height: 0.5rem;
	padding: 0;
	border: 0;
	border-radius: 50px;
	background: rgba(17, 17, 17, 0.22);
	cursor: pointer;
}

.mf-ytf-gal__dot[aria-current] {
	background: rgba(17, 17, 17, 0.73);
}

.mf-ytf-gal__progress {
	position: relative;
	height: 2px;
	background: var(--mf-ytf-gal-rule);
}

.mf-ytf-gal__progress-bar {
	position: absolute;
	top: 0;
	left: 0;
	width: 0;
	height: 100%;
	background: currentcolor;
}

/* --- A · Peek-Carousel --------------------------------------------------- */

/*
 * The centre panel large, its neighbours cropped at the edges — "1:1 wie der bestehende
 * Slider", which is what makes the hand migration of /categories/ai a visual no-op.
 *
 * `scroll-snap-align: center` plus symmetric `scroll-padding` is what centres a panel
 * without a transform. The padding is 13% because the panel is 74% wide: (100 - 74) / 2.
 * That is also what lets the FIRST and LAST panel centre at all — without it they have no
 * scroll room on one side and stick to the edge.
 */
.mf-ytf-gal--peek .mf-ytf-gal__reel {

	/*
	 * 8% of the CONTENT box, which is 74% of the outer width — so ~6% of the gallery, the
	 * gap the design draws between its slides.
	 */
	gap: 8%;

	/*
	 * Real padding, not `scroll-padding`. `scroll-padding` only moves the snap positions; it
	 * creates no room, so at rest the first panel sat flush left with a peek on one side only.
	 * The original translate-based design shows the same empty space beside its first slide.
	 *
	 * Percentage padding resolves against the gallery's width, so this is 13% of the outer box
	 * on each side and leaves a content box of exactly 74% — which is why the panel below is
	 * `100%` and not `74%`. A flex basis resolves against the CONTENT box, so 74% there would
	 * have compounded with this padding and produced 54.75%; measured, it did.
	 */
	padding-inline: 13%;
}

.mf-ytf-gal--peek .mf-ytf-gal__panel {
	flex: 0 0 100%;
	scroll-snap-align: center;
	transition: opacity 0.35s ease;
	opacity: 0.4;
}

.mf-ytf-gal--peek .mf-ytf-gal__panel[aria-current] {
	opacity: 1;
}

/* Not `scale`: see the section comment. A transform here would reach a playing iframe. */
.mf-ytf-gal--peek .mf-ytf-gal__prev,
.mf-ytf-gal--peek .mf-ytf-gal__next {
	position: absolute;
	z-index: 2;
	top: 50%;
	margin-block-start: -1rem;
}

.mf-ytf-gal--peek .mf-ytf-gal__prev {
	left: 0.875rem;
}

.mf-ytf-gal--peek .mf-ytf-gal__next {
	right: 0.875rem;
}

.mf-ytf-gal--peek .mf-ytf-gal__arrows {
	display: contents;
}

/* --- B · Karten-Carousel ------------------------------------------------- */

/*
 * Two and a half cards, arrows beside the heading, a thin rule instead of dots. The half card
 * at the edge is the affordance, which is why the basis is 38% and not a third.
 */
.mf-ytf-gal--cards .mf-ytf-gal__reel {
	gap: 1.5rem;
}

.mf-ytf-gal--cards .mf-ytf-gal__panel {
	flex: 0 0 calc(38% - 1rem);
	scroll-snap-align: start;
}

@media (max-width: 43.75rem) {

	.mf-ytf-gal--cards .mf-ytf-gal__panel {
		flex-basis: 78%;
	}
}

/* --- C · Vollbild-Rotator ------------------------------------------------ */

/*
 * One panel across the full width, caption over the image. Start-aligned rather than centred:
 * with a 100%-wide panel the two are identical, and start needs no scroll padding.
 */
.mf-ytf-gal--rotator .mf-ytf-gal__reel {
	gap: 0;
}

.mf-ytf-gal--rotator .mf-ytf-gal__panel {
	flex: 0 0 100%;
	scroll-snap-align: start;
}

.mf-ytf-gal--rotator .mf-ytf-gal__prev,
.mf-ytf-gal--rotator .mf-ytf-gal__next {
	position: absolute;
	z-index: 2;
	top: 50%;
	margin-block-start: -1rem;
}

.mf-ytf-gal--rotator .mf-ytf-gal__prev {
	left: 1rem;
}

.mf-ytf-gal--rotator .mf-ytf-gal__next {
	right: 1rem;
}

.mf-ytf-gal--rotator .mf-ytf-gal__arrows {
	display: contents;
}

/*
 * The dots sit over the image, so they need the light treatment rather than the dark one.
 */
.mf-ytf-gal--rotator .mf-ytf-gal__dots {
	position: absolute;
	z-index: 2;
	inset-inline: 0;
	bottom: 0.875rem;
}

.mf-ytf-gal--rotator .mf-ytf-gal__dot {
	background: rgba(255, 255, 255, 0.4);
}

.mf-ytf-gal--rotator .mf-ytf-gal__dot[aria-current] {
	background: #fff;
}

/* --- D · Kompakt-Ribbon -------------------------------------------------- */

/*
 * Small chips for the end of an article or a sidebar. Its design has no dots, so the only
 * signals that the row continues are the arrows' disabled state and this fade — a silent
 * scroll region reads as a short list rather than a scrollable one (divb-hq#86).
 *
 * The fade is a mask on the REEL, not a positioned overlay: an overlay would sit above the
 * panels and swallow clicks on the last visible chip.
 */
.mf-ytf-gal--ribbon .mf-ytf-gal__reel {
	gap: 0.875rem;
}

.mf-ytf-gal--ribbon .mf-ytf-gal__panel {
	flex: 0 0 10.5rem;
	scroll-snap-align: start;
}

.mf-ytf-gal--ribbon.is-live .mf-ytf-gal__reel {
	mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
}

.mf-ytf-gal--ribbon .mf-ytf-gal__label {
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--mf-ytf-gal-dim);
}

.mf-ytf-gal--ribbon .mf-ytf-gal__prev,
.mf-ytf-gal--ribbon .mf-ytf-gal__next {
	width: 1.625rem;
	height: 1.625rem;
}

.mf-ytf-gal--ribbon .mf-ytf__title {
	font-size: 0.78rem;
	line-height: 1.35;
}

/*
 * The chips are small, so the notice under them would otherwise be the loudest thing in a
 * component meant for a sidebar.
 */
.mf-ytf-gal--ribbon .mf-ytf-gal__notice {
	font-size: 0.92em;
}

@media (prefers-reduced-motion: reduce) {

	.mf-ytf-gal__reel {
		scroll-behavior: auto;
	}

	.mf-ytf-gal--peek .mf-ytf-gal__panel {
		transition: none;
	}
}
