/* ===========================================================================
   motion-gsap.css — the scrub tier
   ---------------------------------------------------------------------------
   Optional, and one level above css/motion.css. Load it only for sections that
   need scroll to *drive* a sequence rather than merely trigger one — a pinned
   track, a scrubbed reveal. Everything cheaper stays on the CSS layer.

   The tier obeys the same rule as the rest of the library, and it matters more
   here than anywhere else:

       The resting state is the finished state.

   A pinned track is the most dangerous thing in the library, because its
   finished state is "one panel visible and the rest stacked underneath". If
   that were the default, a browser without JS would show one panel and hide
   the other four for good.

   So the default in this file is the OPPOSITE: a plain vertical list of
   panels, fully readable, no pinning, no absolute positioning, no bands. Only
   when js/motion-gsap.js has actually loaded GSAP, and the user has not asked
   for reduced motion, does it add html.vb-gsap — and only then does the track
   layout switch on. Reduced motion never reaches the pinned state at all; it
   keeps the list.
   ========================================================================= */

/* ===========================================================================
   Default — the fallback, and the thing most people will actually read
   ========================================================================= */
[data-vb-track] .vb-track__viewport { position: relative; }

[data-vb-track] .vb-track__items {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 3rem;
}
[data-vb-track] .vb-track__item { position: relative; }

/* Generated by the script, so they simply do not exist without it. Hidden here
   as well for the case where the script ran, built them, and then GSAP failed
   to load. */
[data-vb-track] .vb-track__bands { display: none; }

/* The rail is a progress readout for the pinned state; in list mode it has
   nothing to report. */
[data-vb-track] .vb-track__rail { display: none; }

/* ===========================================================================
   Pinned — only once GSAP is confirmed present
   ========================================================================= */
html.vb-gsap [data-vb-track] {
	--vb-track-bands: 24;
	--vb-track-step: 100;   /* vh of scroll per step */
}

html.vb-gsap [data-vb-track] .vb-track__viewport {
	block-size: 100svh;
	overflow: hidden;
}

html.vb-gsap [data-vb-track] .vb-track__items {
	display: block;
	gap: 0;
	block-size: 100%;
}

html.vb-gsap [data-vb-track] .vb-track__item {
	position: absolute;
	inset: 0;
}

html.vb-gsap [data-vb-track] .vb-track__bands {
	display: block;
	position: absolute;
	inset: 0;
	pointer-events: none;
}
html.vb-gsap [data-vb-track] .vb-track__band {
	position: absolute;
	inset-inline: 0;
	/* A hair of overlap: sub-pixel rounding between 24 bands would otherwise
	   leave visible seams across the plate. */
	margin-block-start: -1px;
	transform-origin: 50% 0;
}

html.vb-gsap [data-vb-track] .vb-track__rail { display: flex; }

/* ===========================================================================
   Reduced motion — never pin
   ---------------------------------------------------------------------------
   js/motion-gsap.js already refuses to add .vb-gsap under reduced motion, so
   this is belt and braces for the case where the preference changes after load.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
	html.vb-gsap [data-vb-track] .vb-track__viewport { block-size: auto; overflow: visible; }
	html.vb-gsap [data-vb-track] .vb-track__items { display: grid; gap: 3rem; block-size: auto; }
	html.vb-gsap [data-vb-track] .vb-track__item { position: relative; inset: auto; }
	html.vb-gsap [data-vb-track] .vb-track__bands,
	html.vb-gsap [data-vb-track] .vb-track__rail { display: none; }
}

/* ===========================================================================
   [data-vb-cinema] — one image, several deliberate motion profiles
   ---------------------------------------------------------------------------
   Fallback: the section owns an ordinary image + copy composition in normal
   flow. GSAP can supply a pin, an aperture / scan / reframe / pan, or a
   real-flow note pass. A profile only writes the properties it owns; no default zoom is
   applied. No image is generated or duplicated.
   =========================================================================== */
/* :where() keeps these structural defaults intentionally weak. Section CSS
   owns the actual composition; the old higher-specificity selectors were
   overriding Field Notes and Craft even though their styles loaded later. */
:where([data-vb-cinema] .vb-cinema__viewport,
	[data-vb-cinema] .vb-cinema__media,
	[data-vb-cinema] .vb-cinema__chapters,
	[data-vb-cinema] .vb-cinema__chapter) { position: relative; }

:where([data-vb-cinema] .vb-cinema__progress,
	[data-vb-cinema] .vb-cinema__scan) { display: none; }

html.vb-gsap :where([data-vb-cinema]:not([data-vb-cinema-pin="false"]) .vb-cinema__scroll) {
	block-size: var(--vb-cinema-scroll, 300vh);
}
html.vb-gsap :where([data-vb-cinema]:not([data-vb-cinema-pin="false"]) .vb-cinema__viewport) {
	block-size: 100svh;
	overflow: clip;
}
html.vb-gsap :where([data-vb-cinema-motion="aperture"] .vb-cinema__media) {
	will-change: clip-path;
}
html.vb-gsap :where([data-vb-cinema-motion="frame"] .vb-cinema__media) {
	will-change: transform, width, height, border-radius;
}
html.vb-gsap :where([data-vb-cinema-motion="pan"] .vb-cinema__media > img) {
	will-change: transform;
}
html.vb-gsap :where([data-vb-cinema]:not([data-vb-cinema-pin="false"]) .vb-cinema__chapters) {
	display: block;
	position: relative;
}
html.vb-gsap :where([data-vb-cinema]:not([data-vb-cinema-pin="false"]) .vb-cinema__chapter) {
	position: absolute;
	inset: 0;
	will-change: transform, opacity;
}
html.vb-gsap :where([data-vb-cinema]:not([data-vb-cinema-pin="false"]) .vb-cinema__chapter:first-child) {
	position: relative;
}
html.vb-gsap :where([data-vb-cinema]:not([data-vb-cinema-pin="false"]) .vb-cinema__progress) { display: block; }
html.vb-gsap :where([data-vb-cinema] .vb-cinema__progress-bar) {
	transform-origin: 0 50%;
	will-change: transform;
}
html.vb-gsap :where([data-vb-cinema-motion="scan"] .vb-cinema__scan) {
	display: block;
	will-change: transform;
}
html.vb-gsap :where([data-vb-cinema-chapters="read"] .vb-cinema__reveal) {
	will-change: transform, opacity, clip-path;
}
html.vb-gsap :where([data-vb-cinema-chapters="masked-lines"] .vb-cinema__masked-line) {
	will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
	html.vb-gsap :where([data-vb-cinema] .vb-cinema__scroll,
		[data-vb-cinema] .vb-cinema__viewport) {
		block-size: auto;
		overflow: visible;
	}
	html.vb-gsap :where([data-vb-cinema] .vb-cinema__chapters) {
		display: grid;
		position: relative;
	}
	html.vb-gsap :where([data-vb-cinema] .vb-cinema__chapter) {
		position: relative;
		inset: auto;
		opacity: 1 !important;
		visibility: visible !important;
		transform: none !important;
	}
	html.vb-gsap :where([data-vb-cinema] .vb-cinema__media,
		[data-vb-cinema] .vb-cinema__media > img,
		[data-vb-cinema] .vb-cinema__reveal,
		[data-vb-cinema] [data-vb-cinema-lines],
		[data-vb-cinema] .vb-cinema__masked-line) {
		clip-path: none !important;
		transform: none !important;
		opacity: 1 !important;
		visibility: visible !important;
	}
	html.vb-gsap :where([data-vb-cinema] .vb-cinema__progress,
		[data-vb-cinema] .vb-cinema__scan) { display: none; }
}

/* ===========================================================================
   THE FX PRIMITIVES
   ---------------------------------------------------------------------------
   Ten primitives ported from the madewithgsap 040–050 studies. They obey the
   same contract as [data-vb-track] above and for the same reason:

       The default in this file is the READABLE state.

   Every one of these effects ends up with content stacked, clipped, rotated
   off-axis or scaled to zero — states that are correct only while something is
   driving them. So each primitive is written twice: once as a plain, boring,
   fully-legible layout, and once under html.vb-gsap. If GSAP never loads, or
   the user asked for reduced motion, the boring version is what ships.

   Sizing knobs are declared on the gated block so a section can retune the
   geometry without touching this file.
   ========================================================================= */

/* ---------------------------------------------------------------------------
   [data-vb-dial] — two counter-rotating rails driven by a pinned scrub
   Fallback: a word list and a media row, side by side.
   --------------------------------------------------------------------------- */
[data-vb-dial] .vb-dial__rail {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem 2rem;
	align-items: center;
}
[data-vb-dial] .vb-dial__rail--media {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
	gap: 1rem;
}
[data-vb-dial] .vb-dial__item { position: relative; }

html.vb-gsap [data-vb-dial] {
	--vb-dial-size: 80vw;
	--vb-dial-media: 12vw;
	--vb-dial-split: 52%;
}
html.vb-gsap [data-vb-dial] .vb-dial__scroll { block-size: var(--vb-dial-scroll, 500vh); }
html.vb-gsap [data-vb-dial] .vb-dial__viewport {
	block-size: 100svh;
	overflow: hidden;
	position: relative;
}
html.vb-gsap [data-vb-dial] .vb-dial__rail {
	display: block;
	position: absolute;
	inline-size: var(--vb-dial-size);
	block-size: var(--vb-dial-size);
	inset-block-start: 50%;
	transform: translate(0, -50%);
	gap: 0;
}
html.vb-gsap [data-vb-dial] .vb-dial__rail--words { inset-inline-end: var(--vb-dial-split); }
html.vb-gsap [data-vb-dial] .vb-dial__rail--media { inset-inline-start: var(--vb-dial-split); }
html.vb-gsap [data-vb-dial] .vb-dial__item { position: absolute; inset: 0; }
/* The counter-child cancels its item's rotation, so labels stay upright while
   the rail turns underneath them. */
html.vb-gsap [data-vb-dial] .vb-dial__rail--words .vb-dial__counter {
	position: absolute;
	inset-inline-end: 50%;
	inset-block-start: 100%;
	transform: translate(0, -50%);
	transform-origin: 100% 50%;
	white-space: nowrap;
}
html.vb-gsap [data-vb-dial] .vb-dial__rail--media .vb-dial__counter {
	position: absolute;
	inset-inline-start: 50%;
	inset-block-start: 0;
	inline-size: var(--vb-dial-media);
	block-size: var(--vb-dial-media);
	transform: translate(0, -50%);
	transform-origin: 0 50%;
}

/* ---------------------------------------------------------------------------
   [data-vb-deck] — a pinned deck; each card advances a place, then is thrown
   Fallback: the cards as an ordinary grid.
   --------------------------------------------------------------------------- */
[data-vb-deck] .vb-deck__stage {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	gap: 1.5rem;
}

html.vb-gsap [data-vb-deck] { --vb-deck-card: 22%; --vb-deck-gap: 30; }
html.vb-gsap [data-vb-deck] .vb-deck__scroll { block-size: var(--vb-deck-scroll, 500vh); }
html.vb-gsap [data-vb-deck] .vb-deck__viewport {
	block-size: 100svh;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}
html.vb-gsap [data-vb-deck] .vb-deck__stage {
	display: block;
	position: relative;
	inline-size: var(--vb-deck-card);
	min-inline-size: 220px;
	aspect-ratio: 3 / 4;
	perspective: 25vw;
}
html.vb-gsap [data-vb-deck] .vb-deck__card {
	position: absolute;
	inset: 0;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-fan] — cards spread as a hand, springing open on entry
   Fallback: the cards as an ordinary grid.
   --------------------------------------------------------------------------- */
[data-vb-fan] .vb-fan__arc {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
	gap: 1.5rem;
}

html.vb-gsap [data-vb-fan] { --vb-fan-card: 20vw; --vb-fan-radius: 300vw; }
html.vb-gsap [data-vb-fan] .vb-fan__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
}
/* An arc this wide is what makes the fan read as a segment of a huge circle
   rather than a shallow tilt. It is centred by half its own width. */
html.vb-gsap [data-vb-fan] .vb-fan__arc {
	display: block;
	position: absolute;
	inline-size: var(--vb-fan-radius);
	block-size: var(--vb-fan-radius);
	inset-inline-start: 50%;
	margin-inline-start: calc(var(--vb-fan-radius) / -2);
}
html.vb-gsap [data-vb-fan] .vb-fan__slot {
	position: absolute;
	inset: 0;
	display: flex;
	justify-content: center;
	pointer-events: none;
}
html.vb-gsap [data-vb-fan] .vb-fan__card {
	inline-size: var(--vb-fan-card);
	min-inline-size: 150px;
	margin-block-start: 50svh;
	transform: translate(0, -50%);
}
/* Only once the spring has settled, or the pointer would hit cards mid-flight. */
html.vb-gsap [data-vb-fan].is-fanned .vb-fan__card { pointer-events: auto; }
html.vb-gsap [data-vb-fan]:has(.vb-fan__slot:hover) .vb-fan__slot:not(:hover) { opacity: 0.25; }
html.vb-gsap [data-vb-fan] .vb-fan__slot { transition: opacity var(--vb-dur-base) var(--vb-ease-out-soft); }

/* ---------------------------------------------------------------------------
   [data-vb-veil] — a second copy of the layout, revealed through a cursor hole
   Fallback: the base layer alone. The veil layer is decorative and duplicated,
   so hiding it costs nothing.
   --------------------------------------------------------------------------- */
/* Scoped to the ungated state on purpose. Hiding it unconditionally would force
   the gated rule to name a display to bring it back — and naming one there
   overrides whatever layout the SECTION gave the layer, so the duplicate stops
   matching the original it is supposed to sit exactly on top of. */
html:not(.vb-gsap) [data-vb-veil] .vb-veil__layer--top { display: none; }

html.vb-gsap [data-vb-veil] { --vb-veil-hole: 20%; --vb-veil-feather: 25%; }
/* Both layers occupy ONE grid cell. That is the entire alignment strategy, and
   it replaces absolute positioning plus a percentage width plus a matching
   padding — three values that all had to agree, and did not. In one cell the
   two boxes are identical because the grid says so, and neither layer needs to
   know anything about the other. */
html.vb-gsap [data-vb-veil] .vb-veil__stack { display: grid; }
html.vb-gsap [data-vb-veil] .vb-veil__layer { grid-area: 1 / 1; }
html.vb-gsap [data-vb-veil] .vb-veil__layer--top {
	/* No display here — the section owns it, and the two layers must lay out
	   identically or the veil reveals a copy that does not line up. */
	pointer-events: none;
	--vb-veil-x: 50%;
	--vb-veil-y: 50%;
	-webkit-mask-image: radial-gradient(circle at var(--vb-veil-x) var(--vb-veil-y),
		black var(--vb-veil-hole), transparent var(--vb-veil-feather));
	mask-image: radial-gradient(circle at var(--vb-veil-x) var(--vb-veil-y),
		black var(--vb-veil-hole), transparent var(--vb-veil-feather));
}

/* ---------------------------------------------------------------------------
   [data-vb-exposure-field] — a sharp photograph revealed by a distorted cursor lens
   Fallback: the authored primary image. The transparent canvas stays hidden
   until both textures and a WebGL context produce a successful first frame.
   --------------------------------------------------------------------------- */
[data-vb-exposure-stage] .vb-exposure-field__canvas {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: block;
	inline-size: 100%;
	block-size: 100%;
	max-inline-size: none;
	opacity: 0;
	pointer-events: none;
	transition: opacity var(--vb-dur-slow) var(--vb-ease-out-soft);
}

[data-vb-exposure-ready] [data-vb-exposure-stage] .vb-exposure-field__canvas {
	opacity: 1;
}

/* ---------------------------------------------------------------------------
   [data-vb-tunnel] — slides receding down a corridor, driven by wheel or drag
   Fallback: the slides as an ordinary grid.
   --------------------------------------------------------------------------- */
[data-vb-tunnel] .vb-tunnel__stage {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: 1.5rem;
}

html.vb-gsap [data-vb-tunnel] { --vb-tunnel-slide: 56vw; --vb-tunnel-depth: 220vw; }
html.vb-gsap [data-vb-tunnel] .vb-tunnel__viewport {
	block-size: 100svh;
	overflow: hidden;
	perspective: 150vw;
	touch-action: pan-y;
}
html.vb-gsap [data-vb-tunnel] .vb-tunnel__stage {
	display: block;
	position: relative;
	block-size: 100%;
	transform-style: preserve-3d;
}
html.vb-gsap [data-vb-tunnel] .vb-tunnel__slide {
	position: absolute;
	inline-size: var(--vb-tunnel-slide);
	min-inline-size: 260px;
	aspect-ratio: 16 / 9;
	inset-inline-start: 50%;
	inset-block-start: 50%;
	margin-inline-start: calc(var(--vb-tunnel-slide) / -2);
	margin-block-start: calc(var(--vb-tunnel-slide) / -4);
	will-change: transform, opacity;
}

/* ---------------------------------------------------------------------------
   [data-vb-scatter] — a pinned statement whose letters arrive out of order
   Fallback: the statement, set and readable.
   --------------------------------------------------------------------------- */
html.vb-gsap [data-vb-scatter] .vb-scatter__scroll { block-size: var(--vb-scatter-scroll, 400vh); }
html.vb-gsap [data-vb-scatter] .vb-scatter__viewport {
	block-size: 100svh;
	display: flex;
	flex-direction: column;
	justify-content: center;
	overflow: hidden;
}
/* Generated by the script. The clip is what the letters rise out of; the
   inset is generous at the top so ascenders are not shaved. */
html.vb-gsap [data-vb-scatter] .vb-scatter__word { display: inline-block; }
html.vb-gsap [data-vb-scatter] .vb-scatter__glyph {
	display: inline-block;
	clip-path: polygon(-5% -8%, -5% 114%, 105% 114%, 105% -8%);
}
html.vb-gsap [data-vb-scatter] .vb-scatter__glyph > span { display: inline-block; }

/* ---------------------------------------------------------------------------
   [data-vb-strip] — an endless horizontal band; items squash at both edges
   Fallback: a native horizontally-scrollable strip. This one is genuinely
   usable without JS — it scrolls by wheel, trackpad, touch and keyboard.
   --------------------------------------------------------------------------- */
[data-vb-strip] .vb-strip__viewport {
	overflow-x: auto;
	overscroll-behavior-x: contain;
}
[data-vb-strip] .vb-strip__track {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	align-items: flex-end;
	gap: 1rem;
	inline-size: max-content;
}
[data-vb-strip] .vb-strip__item { flex: 0 0 auto; inline-size: clamp(140px, 20vw, 320px); }
/* The duplicate exists only to make the loop seamless once JS is driving. */
[data-vb-strip] .vb-strip__track--clone { display: none; }

html.vb-gsap [data-vb-strip] .vb-strip__viewport {
	overflow: hidden;
	cursor: grab;
	touch-action: pan-y;
}
html.vb-gsap [data-vb-strip] .vb-strip__viewport.is-dragging { cursor: grabbing; }
html.vb-gsap [data-vb-strip] .vb-strip__track--clone { display: flex; }
html.vb-gsap [data-vb-strip] .vb-strip__rows { display: flex; inline-size: max-content; }
html.vb-gsap [data-vb-strip] .vb-strip__squash {
	display: block;
	transform-origin: 0% 50%;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-turn] — media turning a full revolution as it crosses the viewport
   Fallback: a plain column of media. Nothing is hidden at any point, so this
   one degrades to exactly what it looks like at rest.
   --------------------------------------------------------------------------- */
html.vb-gsap [data-vb-turn] { perspective: 200vw; }
html.vb-gsap [data-vb-turn] .vb-turn__rows { transform-style: preserve-3d; }
html.vb-gsap [data-vb-turn] .vb-turn__item { will-change: transform; }

/* ---------------------------------------------------------------------------
   [data-vb-assemble] — a pinned chapter title whose letters climb into place
   Fallback: ordinary headings above ordinary chapters.
   --------------------------------------------------------------------------- */
html.vb-gsap [data-vb-assemble] .vb-assemble__stage { block-size: 100svh; }
html.vb-gsap [data-vb-assemble] .vb-assemble__glyph {
	display: inline-block;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-pile] — an endless stack of plates, driven by wheel or drag
   Fallback: the plates as an ordinary grid.
   --------------------------------------------------------------------------- */
[data-vb-pile] .vb-pile__frame {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1rem;
}

html.vb-gsap [data-vb-pile] .vb-pile__frame {
	display: block;
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	touch-action: pan-y;
	cursor: grab;
}
html.vb-gsap [data-vb-pile] .vb-pile__frame.is-dragging { cursor: grabbing; }
html.vb-gsap [data-vb-pile] .vb-pile__plate {
	position: absolute;
	inset: 0;
	transform: scale(0);
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-roll] — a statement whose words turn over as you climb them
   Fallback: the statement, set and readable. Every part of the flap is built
   by the script, so with no GSAP there is nothing here to undo.
   --------------------------------------------------------------------------- */
html.vb-gsap [data-vb-roll] { --vb-roll-lead: 1.22; --vb-roll-trim: -0.11em; }
html.vb-gsap [data-vb-roll] .vb-roll__word { display: inline-block; }
html.vb-gsap [data-vb-roll] .vb-roll__cyl {
	position: relative;
	display: inline-block;
	/* The clip is EXACTLY the cylinder's own box, so the face parked one full
	   height above cannot show a sliver of itself. That only holds if the
	   glyphs fit inside that box — and at a display line-height of 1.0 they do
	   not, because ascenders and descenders hang outside it. So the cylinder is
	   leaded loose enough to contain them and pulled back by the difference:
	   the line occupies the same space it always did, and the clip is honest.
	   Horizontally it is generous, for italics and overhanging glyphs. */
	line-height: var(--vb-roll-lead);
	margin-block: var(--vb-roll-trim);
	clip-path: polygon(-8% 0%, -8% 100%, 108% 100%, 108% 0%);
	transform-origin: 50% 100%;
	will-change: transform;
}
html.vb-gsap [data-vb-roll] .vb-roll__face { display: block; }
html.vb-gsap [data-vb-roll] .vb-roll__face--in {
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 0;
	inline-size: 100%;
}

/* ---------------------------------------------------------------------------
   [data-vb-swap] — one slot, two strings, the second replacing the first
   Fallback: both strings set, one under the other. The exchange still reads —
   it is two lines instead of one line that turned over.
   --------------------------------------------------------------------------- */
[data-vb-swap] .vb-swap__cyl,
[data-vb-swap] .vb-swap__face { display: block; }

html.vb-gsap [data-vb-swap] { --vb-swap-lead: 1.18; --vb-swap-trim: -0.09em; }
html.vb-gsap [data-vb-swap] .vb-swap__slot {
	display: block;
	/* Leaded loose enough to contain ascenders and descenders and pulled back by
	   the difference, exactly as the roll cylinder is: the line occupies the
	   space it always did and the clip is honest. Horizontally generous, so an
	   overhanging glyph is not shaved. clip-path rather than overflow, because
	   an overflow box is a scroll container and would capture any view()
	   timeline a section hangs inside it. */
	line-height: var(--vb-swap-lead);
	margin-block: var(--vb-swap-trim);
	clip-path: polygon(-6% 0%, -6% 100%, 106% 100%, 106% 0%);
}
html.vb-gsap [data-vb-swap] .vb-swap__cyl {
	position: relative;
	transform-origin: 50% 100%;
	will-change: transform;
}
/* Parked exactly one slot below, out of flow, so the cylinder is one line tall
   whatever the second string is. */
html.vb-gsap [data-vb-swap] .vb-swap__face--in {
	position: absolute;
	inset-block-start: 100%;
	inset-inline-start: 0;
	inline-size: 100%;
}

/* ---------------------------------------------------------------------------
   [data-vb-orbit] — frames swinging in along arms to land in one window
   Fallback: the frames as an ordinary grid.
   --------------------------------------------------------------------------- */
[data-vb-orbit] .vb-orbit__ring {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: 1.25rem;
}

html.vb-gsap [data-vb-orbit] { --vb-orbit-hub: min(46vh, 30vw); }
html.vb-gsap [data-vb-orbit] .vb-orbit__scroll { block-size: var(--vb-orbit-scroll, 400vh); }
html.vb-gsap [data-vb-orbit] .vb-orbit__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	perspective: 120vw;
}
/* Centred by margin rather than a translate, because the hub is a GSAP target:
   a transform written here is a transform the pointer tilt has to parse back
   out, and one it will happily drop on the first tween. */
html.vb-gsap [data-vb-orbit] .vb-orbit__hub {
	position: absolute;
	inline-size: var(--vb-orbit-hub);
	block-size: var(--vb-orbit-hub);
	inset-block-start: 50%;
	inset-inline-start: 50%;
	margin-block-start: calc(var(--vb-orbit-hub) / -2);
	margin-inline-start: calc(var(--vb-orbit-hub) / -2);
	transform-style: preserve-3d;
}
html.vb-gsap [data-vb-orbit] .vb-orbit__ring {
	display: block;
	position: absolute;
	inset: 0;
	gap: 0;
}
html.vb-gsap [data-vb-orbit] .vb-orbit__item { position: absolute; inset: 0; }
html.vb-gsap [data-vb-orbit] .vb-orbit__plate {
	position: absolute;
	inset: 0;
	will-change: transform, opacity;
}

/* ---------------------------------------------------------------------------
   [data-vb-queue] — cards filing up a pinned screen, each dwelling at centre
   Fallback: the cards as an ordinary grid.
   --------------------------------------------------------------------------- */
[data-vb-queue] .vb-queue__rail {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1.5rem;
}

html.vb-gsap [data-vb-queue] { --vb-queue-card: 19vw; --vb-queue-overlap: 6vw; }
html.vb-gsap [data-vb-queue] .vb-queue__scroll { block-size: var(--vb-queue-scroll, 520vh); }
html.vb-gsap [data-vb-queue] .vb-queue__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
}
/* The row never moves sideways — the overlap is static and the cascade comes
   entirely from each card being at a different point in its own timeline. The
   leading pad balances the negative margin so the row stays centred. */
html.vb-gsap [data-vb-queue] .vb-queue__rail {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0;
	padding-inline-start: var(--vb-queue-overlap);
}
html.vb-gsap [data-vb-queue] .vb-queue__card {
	flex: 0 0 auto;
	inline-size: var(--vb-queue-card);
	min-inline-size: 140px;
	margin-inline-start: calc(var(--vb-queue-overlap) * -1);
	will-change: transform, opacity;
}

/* ---------------------------------------------------------------------------
   [data-vb-belt] — a column that drifts by itself and squashes when pushed
   Fallback: the frames as an ordinary grid. No clone is made, so the list is
   also exactly what a screen reader hears.
   --------------------------------------------------------------------------- */
[data-vb-belt] .vb-belt__track {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1rem;
}

html.vb-gsap [data-vb-belt] { --vb-belt-gap: 1.5rem; }
html.vb-gsap [data-vb-belt] .vb-belt__viewport {
	position: relative;
	block-size: var(--vb-belt-height, 100svh);
	overflow: hidden;
	cursor: grab;
	touch-action: pan-y;
}
html.vb-gsap [data-vb-belt] .vb-belt__viewport.is-dragging { cursor: grabbing; }
html.vb-gsap [data-vb-belt] .vb-belt__stage {
	block-size: 100%;
	transform-origin: 50% 50%;
	will-change: transform;
}
/* The trailing pad is load-bearing. Two sets of n frames in one column carry
   2n-1 gaps, but the loop's period is n frames AND n gaps — so without one
   more gap's worth of space at the end the wrap lands a gap short and the
   seam visibly stutters once per revolution. */
html.vb-gsap [data-vb-belt] .vb-belt__track {
	display: flex;
	flex-direction: column;
	gap: var(--vb-belt-gap);
	padding-block-end: var(--vb-belt-gap);
	inline-size: 100%;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-wake] — frames painted along the pointer's own path
   Fallback: the contact sheet the section already shows, untouched. The pool
   is built by the script and does not exist without it.
   --------------------------------------------------------------------------- */
html.vb-gsap [data-vb-wake] .vb-wake__stage { position: relative; }
/* Only once the primitive has actually taken the stage — it declines on a
   coarse pointer, and clipping a stage nothing is being thrown across would
   crop the section for no reason. */
html.vb-gsap [data-vb-wake].is-wake .vb-wake__stage { overflow: hidden; }
html.vb-gsap [data-vb-wake] .vb-wake__pool {
	position: absolute;
	inset: 0;
	pointer-events: none;
	z-index: var(--vb-wake-layer, 3);
}
html.vb-gsap [data-vb-wake] .vb-wake__mote {
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 0;
	inline-size: var(--vb-wake-mote, 15vw);
	block-size: var(--vb-wake-mote, 15vw);
	/* base.css caps every image at 100% of its container; a mote is positioned
	   against the whole stage, so that cap is not the size anyone asked for. */
	max-inline-size: none;
	object-fit: cover;
	border-radius: var(--vb-wake-radius, var(--vb-radius-card));
	will-change: transform, opacity;
}
html.vb-gsap [data-vb-wake] .vb-wake__source {
	transition: opacity var(--vb-dur-base) var(--vb-ease-out-soft),
	            filter var(--vb-dur-base) var(--vb-ease-out-soft);
}
[data-vb-wake] .vb-wake__hint { transition: opacity var(--vb-dur-slow) var(--vb-ease-out-soft); }
html.vb-gsap [data-vb-wake].is-painted .vb-wake__hint { opacity: 0; }

/* ---------------------------------------------------------------------------
   [data-vb-sweep] — a statement swept sideways, letters landing as they cross
   Fallback: the statement as an ordinary wrapping block of type.
   --------------------------------------------------------------------------- */
html.vb-gsap [data-vb-sweep] .vb-sweep__viewport {
	position: relative;
	block-size: 100svh;
	display: flex;
	align-items: center;
	/* Clipped on the pinned element itself rather than on the section, because
	   overflow on an ancestor of a pin changes how ScrollTrigger pins it. */
	overflow: hidden;
}
/* The pad is what carries the line on from off the right edge to off the left:
   one screen of nothing at each end, so the first letter arrives and the last
   leaves rather than both being on screen at the extremes. */
html.vb-gsap [data-vb-sweep] .vb-sweep__line {
	inline-size: max-content;
	max-inline-size: none;
	white-space: nowrap;
	padding-inline: var(--vb-sweep-pad, 101vw);
	will-change: transform;
}
html.vb-gsap [data-vb-sweep] .vb-sweep__word { display: inline-block; }
html.vb-gsap [data-vb-sweep] .vb-sweep__glyph {
	display: inline-block;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-rush] — plates coming out of the depth, endlessly
   Fallback: the plates as an ordinary grid.
   --------------------------------------------------------------------------- */
[data-vb-rush] .vb-rush__field {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: 1.25rem;
}

html.vb-gsap [data-vb-rush] { --vb-rush-plate: 22%; }
html.vb-gsap [data-vb-rush] .vb-rush__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	perspective: 100vw;
	touch-action: pan-y;
}
html.vb-gsap [data-vb-rush] .vb-rush__field {
	display: block;
	position: relative;
	block-size: 100%;
	gap: 0;
	transform-style: preserve-3d;
}
html.vb-gsap [data-vb-rush] .vb-rush__plate {
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 0;
	inline-size: var(--vb-rush-plate);
	will-change: transform;
}
html.vb-gsap [data-vb-rush] .vb-rush__art { will-change: transform; }

/* ---------------------------------------------------------------------------
   [data-vb-band] — a tilted band of type that drifts and takes a shove
   Fallback: the phrase as an ordinary block of type, level and wrapping.
   --------------------------------------------------------------------------- */
[data-vb-band] .vb-band__track { display: block; }

html.vb-gsap [data-vb-band] { --vb-band-tilt: -10deg; }
html.vb-gsap [data-vb-band] .vb-band__inner {
	display: flex;
	align-items: center;
	block-size: var(--vb-band-height, 100svh);
	overflow: hidden;
	cursor: grab;
	touch-action: pan-y;
	/* The tilt lives here and nowhere else. GSAP owns the track's transform. */
	transform: rotate(var(--vb-band-tilt));
}
html.vb-gsap [data-vb-band] .vb-band__inner.is-dragging { cursor: grabbing; }
html.vb-gsap [data-vb-band] .vb-band__track {
	display: flex;
	inline-size: max-content;
	white-space: nowrap;
	will-change: transform;
}
html.vb-gsap [data-vb-band] .vb-band__phrase { white-space: nowrap; }

/* ---------------------------------------------------------------------------
   [data-vb-reel] — frames dealt to the middle of a pinned screen
   Fallback: the sheet as an ordinary responsive gallery grid, which is a
   complete section on its own. The dealt frames are generated and do not
   exist without the script.
   --------------------------------------------------------------------------- */
html.vb-gsap [data-vb-reel] .vb-reel__scroll { block-size: var(--vb-reel-scroll, 600vh); }
html.vb-gsap [data-vb-reel] .vb-reel__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
}
/* Only once the primitive has taken over. The sheet is the whole section
   otherwise, and hiding it on the class alone would empty a section whose
   script never ran. */
html.vb-gsap [data-vb-reel].is-reel .vb-reel__sheet { display: none; }
html.vb-gsap [data-vb-reel] .vb-reel__stage {
	position: absolute;
	inset: 0;
	pointer-events: none;
}
html.vb-gsap [data-vb-reel] .vb-reel__frame {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: 50%;
	inline-size: var(--vb-reel-w, 24vw);
	block-size: auto;
	max-inline-size: none;
	border-radius: var(--vb-reel-radius, var(--vb-radius-card));
	will-change: transform, opacity;
}

/* ===========================================================================
   THE 001-009 PRIMITIVES
   =========================================================================== */

/* ---------------------------------------------------------------------------
   [data-vb-card-drift] — pinned horizontal convoy with card-local drift
   Fallback: the cards as an ordinary responsive grid.
   --------------------------------------------------------------------------- */
:where([data-vb-card-drift]) .vb-card-drift__track {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1rem;
}

html.vb-gsap [data-vb-card-drift] .vb-card-drift__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
}
html.vb-gsap [data-vb-card-drift] .vb-card-drift__track {
	display: flex;
	align-items: center;
	inline-size: max-content;
	block-size: 100%;
	gap: var(--vb-card-drift-gap, 1vw);
	padding-inline: var(--vb-card-drift-runway, 120vw);
	will-change: transform;
}
html.vb-gsap [data-vb-card-drift] .vb-card-drift__item {
	flex: 0 0 auto;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-cursor-cycle] — a pointer-following card with travel-based images
   Fallback: the first frame remains centred. Hoverless devices keep the source
   study's type-only composition.
   --------------------------------------------------------------------------- */
[data-vb-cursor-cycle] .vb-cursor-cycle__stage { position: relative; }
[data-vb-cursor-cycle] .vb-cursor-cycle__card {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: 50%;
	transform: translate(-50%, -50%);
}
[data-vb-cursor-cycle] .vb-cursor-cycle__medias { position: relative; }
[data-vb-cursor-cycle] .vb-cursor-cycle__media {
	position: absolute;
	inset: 0;
	visibility: hidden;
}
[data-vb-cursor-cycle] .vb-cursor-cycle__media:first-child,
[data-vb-cursor-cycle] .vb-cursor-cycle__media.is-active { visibility: visible; }

html.vb-gsap [data-vb-cursor-cycle] .vb-cursor-cycle__card,
html.vb-gsap [data-vb-cursor-cycle] .vb-cursor-cycle__medias { will-change: transform; }

@media (hover: none), (pointer: coarse) {
	html.vb-gsap [data-vb-cursor-cycle] .vb-cursor-cycle__card { display: none; }
}

/* ---------------------------------------------------------------------------
   [data-vb-radial-stack] — cards accumulating around enormous circles
   Fallback: all cards in a normal grid.
   --------------------------------------------------------------------------- */
:where([data-vb-radial-stack]) .vb-radial-stack__field {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 1rem;
}

html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
}
html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__field {
	display: block;
	position: relative;
	block-size: 100%;
	transform-origin: 50% 50%;
	will-change: transform;
}
html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__orbit {
	position: absolute;
	inline-size: var(--vb-radial-stack-size, 250vw);
	block-size: var(--vb-radial-stack-size, 250vw);
	inset-block-start: 50%;
	inset-inline-start: 50%;
	border-radius: 50%;
	will-change: transform;
}
html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__card {
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 50%;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-word-sweep] — paragraph words arriving from the right
   Fallback: the original paragraph in ordinary flow.
   --------------------------------------------------------------------------- */
html.vb-gsap [data-vb-word-sweep] .vb-word-sweep__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
}
html.vb-gsap [data-vb-word-sweep] .vb-word-sweep__word {
	display: inline-block;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-word-exchange] — overlaid paragraphs exchanging masked words
   Fallback: all paragraphs remain a readable stack.
   --------------------------------------------------------------------------- */
[data-vb-word-exchange] .vb-word-exchange__stack { display: grid; gap: 1.25rem; }

html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
}
html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__stack {
	display: block;
	position: relative;
	gap: 0;
}
html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__paragraph:not(:first-child) {
	position: absolute;
	inset-block-start: 0;
	inset-inline: 0;
}
html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__word {
	position: relative;
	display: inline-block;
	overflow: hidden;
	vertical-align: bottom;
}
html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__inner {
	display: block;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-arc-sweep] — a staggered train on one giant orbit
   Fallback: the cards as an ordinary grid.
   --------------------------------------------------------------------------- */
[data-vb-arc-sweep] .vb-orbit-sweep__viewport {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
	gap: 1rem;
}

html.vb-gsap [data-vb-arc-sweep] .vb-orbit-sweep__viewport {
	display: block;
	position: relative;
	block-size: 100svh;
	overflow: hidden;
}
html.vb-gsap [data-vb-arc-sweep] .vb-orbit-sweep__orbit {
	position: absolute;
	inline-size: var(--vb-orbit-sweep-size, 300vw);
	block-size: var(--vb-orbit-sweep-size, 300vw);
	inset-block-start: 50%;
	inset-inline-start: 50%;
	margin-inline-start: calc(var(--vb-orbit-sweep-size, 300vw) / -2);
	transform-origin: 50% 50%;
	will-change: transform;
}
html.vb-gsap [data-vb-arc-sweep] .vb-orbit-sweep__card {
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 50%;
	transform: translate(-50%, -50%);
}

/* ---------------------------------------------------------------------------
   [data-vb-drag-scatter] — auto drift, direct drag, press deformation
   Fallback: the seven authored cards as a responsive grid. Clones exist only
   after successful initialisation and are hidden from assistive technology.
   --------------------------------------------------------------------------- */
[data-vb-drag-scatter] .vb-drag-scatter__track {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: 1rem;
}

html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__viewport {
	position: relative;
	block-size: 100svh;
	display: flex;
	align-items: center;
	overflow: hidden;
}
html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__track {
	display: flex;
	align-items: center;
	inline-size: max-content;
	gap: var(--vb-drag-scatter-gap, 1vw);
	padding-inline-end: var(--vb-drag-scatter-gap, 1vw);
	user-select: none;
	cursor: grab;
	touch-action: pan-y;
	will-change: transform;
}
html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__track:active { cursor: grabbing; }
html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__item {
	flex: 0 0 auto;
	will-change: transform;
}

/* ---------------------------------------------------------------------------
   [data-vb-sentence-relay] — compounded sentence and glyph travel
   Fallback: all four sentences in source order.
   --------------------------------------------------------------------------- */
[data-vb-sentence-relay] .vb-sentence-relay__stack { display: grid; gap: 1rem; }

html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
}
html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__stack {
	display: block;
	position: relative;
	inline-size: 100%;
	gap: 0;
}
html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__sentence:not(:first-child) {
	position: absolute;
	inset: 0;
}
html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__word,
html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__glyph {
	display: inline-block;
	will-change: transform;
}

/* ===========================================================================
   THE 051-066 PRIMITIVES
   ---------------------------------------------------------------------------
   Their art-directed geometry lives with each section because the dimensions,
   media ratios and copy widths are the design. This shared layer declares the
   compositor contract only; every compact layout is additionally gated by the
   driver's per-root ready class in the section stylesheet.
   =========================================================================== */
html.vb-gsap [data-vb-photo-cascade].is-photo-cascade .vb-photo-cascade__card,
html.vb-gsap [data-vb-letter-rail].is-letter-rail .vb-letter-rail__track,
html.vb-gsap [data-vb-letter-rail].is-letter-rail .vb-letter-rail__inner,
html.vb-gsap [data-vb-perspective-exchange].is-perspective-exchange .vb-perspective-exchange__line,
html.vb-gsap [data-vb-counterflip].is-counterflip .vb-counterflip__deck,
html.vb-gsap [data-vb-letter-hinge].is-letter-hinge .vb-letter-hinge__inner,
html.vb-gsap [data-vb-focus-reel].is-focus-reel .vb-focus-reel__stack,
html.vb-gsap [data-vb-focus-reel].is-focus-reel .vb-focus-reel__slide,
html.vb-gsap [data-vb-depth-bloom].is-depth-bloom .vb-depth-bloom__media,
html.vb-gsap [data-vb-row-panorama].is-row-panorama .vb-row-panorama__row,
html.vb-gsap [data-vb-line-reading].is-line-reading .vb-line-reading__copy {
	will-change: transform;
}

html.vb-gsap [data-vb-letter-rail].is-letter-rail .vb-letter-rail__inner,
html.vb-gsap [data-vb-depth-bloom].is-depth-bloom .vb-depth-bloom__media {
	will-change: transform, opacity;
}

html.vb-gsap [data-vb-focus-reel].is-focus-reel .vb-focus-reel__media {
	will-change: clip-path;
}

html.vb-gsap [data-vb-line-reading].is-line-reading .vb-line-reading__line {
	will-change: mask-image;
}

/* ===========================================================================
   THE 067-079 PRIMITIVES
   ---------------------------------------------------------------------------
   Their exact stage geometry remains section-owned. This layer only records
   which generated or moving nodes should remain on the compositor; the ready
   classes guarantee these hints never affect the authored fallback.
   =========================================================================== */
html.vb-gsap [data-vb-depth-type].is-depth-type .vb-depth-type__char,
html.vb-gsap [data-vb-depth-type].is-depth-type .vb-depth-type__inner,
html.vb-gsap [data-vb-postcard-flight].is-postcard-flight .vb-postcard-flight__card,
html.vb-gsap [data-vb-card-conveyor].is-card-conveyor .vb-card-conveyor__card,
html.vb-gsap [data-vb-proximity-grid].is-proximity-grid .vb-proximity-grid__grid,
html.vb-gsap [data-vb-proximity-grid].is-proximity-grid .vb-proximity-grid__media,
html.vb-gsap [data-vb-photo-flight].is-photo-flight .vb-photo-flight__media,
html.vb-gsap [data-vb-era-timeline].is-era-timeline .vb-era-timeline__track,
html.vb-gsap [data-vb-era-timeline].is-era-timeline .vb-era-timeline__year,
html.vb-gsap [data-vb-flip-deck].is-flip-deck .vb-flip-deck__card,
html.vb-gsap [data-vb-product-bloom].is-product-bloom .vb-product-bloom__stage,
html.vb-gsap [data-vb-product-bloom].is-product-bloom .vb-product-bloom__ring,
html.vb-gsap [data-vb-parallax-postcard].is-parallax-postcard .vb-parallax-postcard__card,
html.vb-gsap [data-vb-parallax-postcard].is-parallax-postcard .vb-parallax-postcard__background,
html.vb-gsap [data-vb-weight-rail].is-weight-rail .vb-weight-rail__text {
	will-change: transform;
}

html.vb-gsap [data-vb-postcard-flight].is-postcard-flight .vb-postcard-flight__card,
html.vb-gsap [data-vb-proximity-grid].is-proximity-grid .vb-proximity-grid__media,
html.vb-gsap [data-vb-photo-flight].is-photo-flight .vb-photo-flight__media,
html.vb-gsap [data-vb-product-bloom].is-product-bloom .vb-product-bloom__ring {
	will-change: transform, opacity;
}

/* ===========================================================================
   Reduced motion — every FX primitive falls back to its readable layout
   ---------------------------------------------------------------------------
   js/motion-gsap.js refuses to add .vb-gsap under reduced motion, so this only
   matters when the preference changes after load. Each rule undoes the gated
   geometry rather than merely stopping the animation, because a stopped
   animation would leave content at its start state — clipped, scaled to zero,
   or stacked out of sight.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
	html.vb-gsap [data-vb-dial] .vb-dial__scroll,
	html.vb-gsap [data-vb-deck] .vb-deck__scroll,
	html.vb-gsap [data-vb-scatter] .vb-scatter__scroll,
	html.vb-gsap [data-vb-orbit] .vb-orbit__scroll,
	html.vb-gsap [data-vb-queue] .vb-queue__scroll,
	html.vb-gsap [data-vb-reel] .vb-reel__scroll { block-size: auto; }

	html.vb-gsap [data-vb-dial] .vb-dial__viewport,
	html.vb-gsap [data-vb-deck] .vb-deck__viewport,
	html.vb-gsap [data-vb-fan] .vb-fan__viewport,
	html.vb-gsap [data-vb-tunnel] .vb-tunnel__viewport,
	html.vb-gsap [data-vb-scatter] .vb-scatter__viewport,
	html.vb-gsap [data-vb-assemble] .vb-assemble__stage,
	html.vb-gsap [data-vb-pile] .vb-pile__frame,
	html.vb-gsap [data-vb-orbit] .vb-orbit__viewport,
	html.vb-gsap [data-vb-queue] .vb-queue__viewport,
	html.vb-gsap [data-vb-belt] .vb-belt__viewport,
	html.vb-gsap [data-vb-sweep] .vb-sweep__viewport,
	html.vb-gsap [data-vb-rush] .vb-rush__viewport,
	html.vb-gsap [data-vb-band] .vb-band__inner,
	html.vb-gsap [data-vb-reel] .vb-reel__viewport {
		block-size: auto;
		overflow: visible;
		perspective: none;
	}

	html.vb-gsap [data-vb-dial] .vb-dial__rail,
	html.vb-gsap [data-vb-deck] .vb-deck__stage,
	html.vb-gsap [data-vb-fan] .vb-fan__arc,
	html.vb-gsap [data-vb-tunnel] .vb-tunnel__stage,
	html.vb-gsap [data-vb-pile] .vb-pile__frame,
	html.vb-gsap [data-vb-orbit] .vb-orbit__ring,
	html.vb-gsap [data-vb-queue] .vb-queue__rail,
	html.vb-gsap [data-vb-belt] .vb-belt__track,
	html.vb-gsap [data-vb-rush] .vb-rush__field {
		display: grid;
		position: static;
		inline-size: auto;
		block-size: auto;
		margin-inline-start: 0;
		transform: none;
	}

	html.vb-gsap [data-vb-dial] .vb-dial__item,
	html.vb-gsap [data-vb-deck] .vb-deck__card,
	html.vb-gsap [data-vb-fan] .vb-fan__slot,
	html.vb-gsap [data-vb-tunnel] .vb-tunnel__slide,
	html.vb-gsap [data-vb-pile] .vb-pile__plate,
	html.vb-gsap [data-vb-orbit] .vb-orbit__item,
	html.vb-gsap [data-vb-orbit] .vb-orbit__plate,
	html.vb-gsap [data-vb-queue] .vb-queue__card,
	html.vb-gsap [data-vb-rush] .vb-rush__plate {
		position: relative;
		inset: auto;
		transform: none;
		opacity: 1;
		inline-size: auto;
		margin: 0;
	}

	html.vb-gsap [data-vb-dial] .vb-dial__counter { position: static; transform: none; }
	html.vb-gsap [data-vb-scatter] .vb-scatter__glyph { clip-path: none; }
	html.vb-gsap [data-vb-veil] .vb-veil__layer--top { display: none; }
	html.vb-gsap [data-vb-strip] .vb-strip__viewport { overflow-x: auto; }
	html.vb-gsap [data-vb-strip] .vb-strip__track--clone,
	html.vb-gsap [data-vb-belt] .vb-belt__frame--clone,
	html.vb-gsap [data-vb-wake] .vb-wake__pool,
	html.vb-gsap [data-vb-band] .vb-band__phrase--clone,
	html.vb-gsap [data-vb-reel] .vb-reel__stage { display: none; }
	html.vb-gsap [data-vb-reel].is-reel .vb-reel__sheet { display: grid; }
	html.vb-gsap [data-vb-band] .vb-band__inner { transform: none; cursor: auto; }
	html.vb-gsap [data-vb-band] .vb-band__track { display: block; inline-size: auto; white-space: normal; }
	html.vb-gsap [data-vb-band] .vb-band__phrase { white-space: normal; }
	html.vb-gsap [data-vb-sweep] .vb-sweep__line {
		inline-size: auto;
		white-space: normal;
		padding-inline: 0;
		transform: none;
	}
	html.vb-gsap [data-vb-orbit] .vb-orbit__hub {
		position: static;
		inline-size: auto;
		block-size: auto;
		margin: 0;
	}
	html.vb-gsap [data-vb-roll] .vb-roll__cyl { clip-path: none; margin-block: 0; }
	html.vb-gsap [data-vb-roll] .vb-roll__face--in { display: none; }
	html.vb-gsap [data-vb-belt] .vb-belt__stage { transform: none; }
	html.vb-gsap [data-vb-belt] .vb-belt__viewport { cursor: auto; }
	html.vb-gsap [data-vb-wake].is-wake .vb-wake__stage { overflow: visible; }

	/* 001-009: restore every primitive to its authored readable layout. */
	html.vb-gsap [data-vb-card-drift] .vb-card-drift__scroll,
	html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__scroll,
	html.vb-gsap [data-vb-word-sweep] .vb-word-sweep__scroll,
	html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__scroll,
	html.vb-gsap [data-vb-arc-sweep] .vb-orbit-sweep__scroll,
	html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__scroll { block-size: auto; }

	html.vb-gsap [data-vb-card-drift] .vb-card-drift__viewport,
	html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__viewport,
	html.vb-gsap [data-vb-word-sweep] .vb-word-sweep__viewport,
	html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__viewport,
	html.vb-gsap [data-vb-arc-sweep] .vb-orbit-sweep__viewport,
	html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__viewport,
	html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__viewport {
		block-size: auto;
		overflow: visible;
	}

	html.vb-gsap [data-vb-card-drift] .vb-card-drift__track,
	html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__field,
	html.vb-gsap [data-vb-arc-sweep] .vb-orbit-sweep__viewport,
	html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__track,
	html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__stack {
		display: grid;
		position: static;
		inline-size: auto;
		block-size: auto;
		padding: 0;
		transform: none !important;
	}

	html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__orbit,
	html.vb-gsap [data-vb-radial-stack] .vb-radial-stack__card,
	html.vb-gsap [data-vb-arc-sweep] .vb-orbit-sweep__orbit,
	html.vb-gsap [data-vb-arc-sweep] .vb-orbit-sweep__card,
	html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__paragraph,
	html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__sentence {
		position: relative;
		inset: auto;
		inline-size: auto;
		block-size: auto;
		margin: 0;
		transform: none !important;
	}

	html.vb-gsap [data-vb-card-drift] .vb-card-drift__item,
	html.vb-gsap [data-vb-word-sweep] .vb-word-sweep__word,
	html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__inner,
	html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__item,
	html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__word,
	html.vb-gsap [data-vb-sentence-relay] .vb-sentence-relay__glyph {
		transform: none !important;
		opacity: 1 !important;
		visibility: visible !important;
	}

	html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__stack {
		display: grid;
		gap: 1.25rem;
	}
	html.vb-gsap [data-vb-word-exchange] .vb-word-exchange__word { overflow: visible; }
	html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__item--clone { display: none; }
	html.vb-gsap [data-vb-drag-scatter] .vb-drag-scatter__viewport { cursor: auto; }
	html.vb-gsap [data-vb-cursor-cycle] .vb-cursor-cycle__card { display: none; }

	/* 051-066: clear the inline start/progress state if the preference changes
	   after load. Section-local no-preference gates simultaneously restore the
	   authored contact-sheet, statement-stack and reading layouts. */
	html.vb-gsap [data-vb-letter-rail] .vb-letter-rail__track,
	html.vb-gsap [data-vb-letter-rail] .vb-letter-rail__inner,
	html.vb-gsap [data-vb-perspective-exchange] .vb-perspective-exchange__line,
	html.vb-gsap [data-vb-counterflip] .vb-counterflip__deck,
	html.vb-gsap [data-vb-letter-hinge] .vb-letter-hinge__inner,
	html.vb-gsap [data-vb-focus-reel] .vb-focus-reel__stack,
	html.vb-gsap [data-vb-depth-bloom] .vb-depth-bloom__media,
	html.vb-gsap [data-vb-row-panorama] .vb-row-panorama__row,
	html.vb-gsap [data-vb-line-reading] .vb-line-reading__copy {
		transform: none !important;
	}

	html.vb-gsap [data-vb-letter-rail] .vb-letter-rail__inner,
	html.vb-gsap [data-vb-perspective-exchange] .vb-perspective-exchange__line,
	html.vb-gsap [data-vb-counterflip] .vb-counterflip__card,
	html.vb-gsap [data-vb-letter-hinge] .vb-letter-hinge__inner,
	html.vb-gsap [data-vb-depth-bloom] .vb-depth-bloom__media {
		opacity: 1 !important;
		visibility: visible !important;
	}

	html.vb-gsap [data-vb-focus-reel] .vb-focus-reel__stack {
		block-size: auto !important;
	}
	html.vb-gsap [data-vb-focus-reel] .vb-focus-reel__slide {
		flex: initial !important;
	}
	html.vb-gsap [data-vb-focus-reel] .vb-focus-reel__media {
		clip-path: none !important;
	}
	html.vb-gsap [data-vb-line-reading] .vb-line-reading__line {
		mask-image: none !important;
	}

	/* 067-079: reset every inline transform/start state if reduced motion is
	   enabled after load. The section-local no-preference gates restore their
	   complete contact-sheet, timeline and single-card fallback layouts. */
	html.vb-gsap [data-vb-depth-type] .vb-depth-type__scroll,
	html.vb-gsap [data-vb-postcard-flight] .vb-postcard-flight__scroll,
	html.vb-gsap [data-vb-card-conveyor] .vb-card-conveyor__scroll,
	html.vb-gsap [data-vb-photo-flight] .vb-photo-flight__scroll,
	html.vb-gsap [data-vb-era-timeline] .vb-era-timeline__scroll,
	html.vb-gsap [data-vb-flip-deck] .vb-flip-deck__scroll,
	html.vb-gsap [data-vb-product-bloom] .vb-product-bloom__scroll,
	html.vb-gsap [data-vb-weight-rail] .vb-weight-rail__scroll {
		block-size: auto !important;
		height: auto !important;
	}

	html.vb-gsap [data-vb-depth-type] .vb-depth-type__viewport,
	html.vb-gsap [data-vb-postcard-flight] .vb-postcard-flight__viewport,
	html.vb-gsap [data-vb-card-conveyor] .vb-card-conveyor__viewport,
	html.vb-gsap [data-vb-photo-flight] .vb-photo-flight__viewport,
	html.vb-gsap [data-vb-flip-deck] .vb-flip-deck__viewport,
	html.vb-gsap [data-vb-product-bloom] .vb-product-bloom__viewport,
	html.vb-gsap [data-vb-weight-rail] .vb-weight-rail__viewport {
		block-size: auto;
		overflow: visible;
	}

	html.vb-gsap [data-vb-depth-type] .vb-depth-type__char,
	html.vb-gsap [data-vb-depth-type] .vb-depth-type__inner,
	html.vb-gsap [data-vb-postcard-flight] .vb-postcard-flight__card,
	html.vb-gsap [data-vb-card-conveyor] .vb-card-conveyor__card,
	html.vb-gsap [data-vb-proximity-grid] .vb-proximity-grid__grid,
	html.vb-gsap [data-vb-proximity-grid] .vb-proximity-grid__media,
	html.vb-gsap [data-vb-photo-flight] .vb-photo-flight__media,
	html.vb-gsap [data-vb-era-timeline] .vb-era-timeline__track,
	html.vb-gsap [data-vb-era-timeline] .vb-era-timeline__year,
	html.vb-gsap [data-vb-flip-deck] .vb-flip-deck__card,
	html.vb-gsap [data-vb-product-bloom] .vb-product-bloom__stage,
	html.vb-gsap [data-vb-product-bloom] .vb-product-bloom__ring,
	html.vb-gsap [data-vb-parallax-postcard] .vb-parallax-postcard__card,
	html.vb-gsap [data-vb-parallax-postcard] .vb-parallax-postcard__background,
	html.vb-gsap [data-vb-weight-rail] .vb-weight-rail__text,
	html.vb-gsap [data-vb-weight-rail] .vb-weight-rail__char {
		transform: none !important;
	}

	html.vb-gsap [data-vb-postcard-flight] .vb-postcard-flight__card,
	html.vb-gsap [data-vb-card-conveyor] .vb-card-conveyor__card,
	html.vb-gsap [data-vb-proximity-grid] .vb-proximity-grid__media,
	html.vb-gsap [data-vb-photo-flight] .vb-photo-flight__media,
	html.vb-gsap [data-vb-era-timeline] .vb-era-timeline__panel,
	html.vb-gsap [data-vb-flip-deck] .vb-flip-deck__card,
	html.vb-gsap [data-vb-product-bloom] .vb-product-bloom__ring,
	html.vb-gsap [data-vb-product-bloom] .vb-product-bloom__count,
	html.vb-gsap [data-vb-parallax-postcard] .vb-parallax-postcard__card,
	html.vb-gsap [data-vb-weight-rail] .vb-weight-rail__text {
		opacity: 1 !important;
		visibility: visible !important;
	}
}

/* ===========================================================================
   The Tell primitives — topple, unfold, path-walk, tally, clockwork
   ---------------------------------------------------------------------------
   Same gate. Each fallback is the authored layout: a list, a row, a drawn
   route, a wall of final figures, a dial with its hours listed beneath.
   ========================================================================= */

/* --- [data-vb-topple] ------------------------------------------------------ */
[data-vb-topple] .vb-topple__stack {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 1rem;
}
html.vb-gsap [data-vb-topple] .vb-topple__scroll { block-size: var(--vb-topple-scroll, 480vh); }
html.vb-gsap [data-vb-topple] .vb-topple__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	display: grid;
	place-items: center;
}
html.vb-gsap [data-vb-topple] .vb-topple__stack {
	position: absolute;
	inset: 0;
	display: block;
}
html.vb-gsap [data-vb-topple] .vb-topple__plate {
	position: absolute;
	inset-inline-start: 50%;
	inset-block-start: 50%;
	translate: -50% -50%;
	inline-size: min(92vw, var(--vb-topple-plate, 44rem));
	will-change: transform, opacity;
}
html.vb-gsap [data-vb-topple] .vb-topple__rest {
	position: absolute;
	inset-inline-start: 50%;
	inset-block-start: 50%;
	translate: -50% -50%;
	inline-size: min(92vw, var(--vb-topple-plate, 44rem));
}

/* Folded Atlas uses ordinary flow in every mode; the matchMedia context owns
   transforms. This guard also covers a live switch to reduced motion. */
@media (prefers-reduced-motion: reduce) {
	html.vb-gsap [data-vb-fold-pair] .vb-fold-pair__leaf {
		transform: none !important;
		opacity: 1 !important;
	}
}

/* --- [data-vb-unfold] ------------------------------------------------------ */
[data-vb-unfold] .vb-unfold__leaflet {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
	gap: 1rem;
}
html.vb-gsap [data-vb-unfold] .vb-unfold__scroll { block-size: var(--vb-unfold-scroll, 400vh); }
html.vb-gsap [data-vb-unfold] .vb-unfold__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	display: grid;
	place-items: center;
	perspective: 1600px;
}
html.vb-gsap [data-vb-unfold] .vb-unfold__leaflet {
	display: flex;
	align-items: stretch;
	gap: 0;
	inline-size: min(96vw, var(--vb-unfold-width, 76rem));
	transform-style: preserve-3d;
}
html.vb-gsap [data-vb-unfold] .vb-unfold__leaf {
	flex: 1 1 0;
	min-inline-size: 0;
	backface-visibility: hidden;
	will-change: transform, opacity;
}

/* --- [data-vb-path-walk] --------------------------------------------------- */
html.vb-gsap [data-vb-path-walk] .vb-path-walk__scroll { block-size: var(--vb-path-walk-scroll, 400vh); }
html.vb-gsap [data-vb-path-walk] .vb-path-walk__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	display: grid;
	place-items: center;
}
html.vb-gsap [data-vb-path-walk] .vb-path-walk__path { will-change: stroke-dashoffset; }

/* --- [data-vb-tally] ------------------------------------------------------- */
html.vb-gsap [data-vb-tally] .vb-tally__scroll { block-size: var(--vb-tally-scroll, 300vh); }
html.vb-gsap [data-vb-tally] .vb-tally__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	display: grid;
	place-items: center;
}
[data-vb-tally] .vb-tally__figure { font-variant-numeric: tabular-nums; }

/* --- [data-vb-clockwork] --------------------------------------------------- */
[data-vb-clockwork] .vb-clockwork__steps {
	list-style: none;
	margin: 0;
	padding: 0;
}
html.vb-gsap [data-vb-clockwork] .vb-clockwork__scroll { block-size: var(--vb-clockwork-scroll, 400vh); }
html.vb-gsap [data-vb-clockwork] .vb-clockwork__viewport {
	position: relative;
	block-size: 100svh;
	overflow: hidden;
	display: grid;
	place-items: center;
}
html.vb-gsap [data-vb-clockwork] .vb-clockwork__step {
	position: absolute;
	translate: -50% -50%;
	will-change: opacity;
}
html.vb-gsap [data-vb-clockwork] .vb-clockwork__hand { will-change: transform; }

/* Reduced motion after load: undo the gated geometry, not just the tween. */
@media (prefers-reduced-motion: reduce) {
	html.vb-gsap [data-vb-topple] .vb-topple__scroll,
	html.vb-gsap [data-vb-unfold] .vb-unfold__scroll,
	html.vb-gsap [data-vb-path-walk] .vb-path-walk__scroll,
	html.vb-gsap [data-vb-tally] .vb-tally__scroll,
	html.vb-gsap [data-vb-clockwork] .vb-clockwork__scroll { block-size: auto; }
	html.vb-gsap [data-vb-topple] .vb-topple__viewport,
	html.vb-gsap [data-vb-unfold] .vb-unfold__viewport,
	html.vb-gsap [data-vb-path-walk] .vb-path-walk__viewport,
	html.vb-gsap [data-vb-tally] .vb-tally__viewport,
	html.vb-gsap [data-vb-clockwork] .vb-clockwork__viewport { block-size: auto; overflow: visible; }
	html.vb-gsap [data-vb-topple] .vb-topple__stack,
	html.vb-gsap [data-vb-topple] .vb-topple__plate,
	html.vb-gsap [data-vb-topple] .vb-topple__rest,
	html.vb-gsap [data-vb-unfold] .vb-unfold__leaf,
	html.vb-gsap [data-vb-clockwork] .vb-clockwork__step {
		position: static;
		translate: none;
		transform: none !important;
		opacity: 1 !important;
	}
	html.vb-gsap [data-vb-path-walk] .vb-path-walk__path { stroke-dashoffset: 0 !important; stroke-dasharray: none !important; }
}
