/* ===========================================================================
   showcase-sticky-scroll — pinned chapter rail, scrolling panel stack
   ---------------------------------------------------------------------------
   Scope: .vbs-showcase-sticky-scroll
   Depends on: css/tokens.css, css/base.css, css/motion.css

   Two columns. The left one holds the heading and a numbered chapter index and
   stays pinned while the right one runs a tall stack of media panels past it.

   THE STICKY MATHS — four things make this hold up rather than "work on my
   machine":

   1. The sticky element is the RAIL, not the grid column. The column
      (__aside) is a plain block that stretches to the full height of the grid
      row, which gives the rail its travel distance. Sticking the column
      itself would work in Chrome and misbehave elsewhere, because a grid
      item's sticky containing block is its grid area, not the container.

   2. The grid must NOT set align-items / align-content. A grid item defaults
      to stretch; the moment you write `align-items: start` the aside collapses
      to its own height, the sticky travel becomes zero, and the rail simply
      stops moving. Individual items are aligned with align-self instead.

   3. No ancestor may set `overflow: hidden` — that turns the ancestor into a
      scroll container and sticky silently binds to it. Nothing in this file
      clips; if you need to clip, use `overflow: clip`, which does not.

   4. Sticky is only switched on when the viewport can actually hold the rail
      (min-width 64rem AND min-height 48rem). Measured across every preset the
      rail runs 627–723px tall, so 48rem (768px) plus the 48px pin offset
      clears the worst of them. On a shorter window the rail would be taller
      than the screen and pinning it would cut its bottom off permanently, so
      below that it simply scrolls with the page. Keep the rail short if you
      lengthen the copy — that ceiling is the constraint the whole thing rests
      on.

   MOTION — four shared primitives plus one bespoke effect:

     data-vb-split      the heading rises word by word
     data-vb-stagger    chapters arrive in sequence; so do each panel's three
                        blocks — head, plate, copy
     data-vb-parallax   plates drift at four different rates, alternating sign
     __gauge-fill       accent gauge beside the chapter list, scaled by the
                        aside's own view progress (bespoke; see the block at
                        the foot of this file for why it can't be a primitive)

   Note the plate is wrapped in its own div. The stagger animates its
   children's transform and the parallax animates the plate's — two transforms
   on one element would fight, and the scroll-driven one would win.

   Resting state: rail at the top of its travel, every word up, every plate
   in place and un-drifted, gauge full. That is a finished composition — the
   animation only re-times it.

   Contains no literal colour, font, size, weight, radius, shadow or duration.
   ========================================================================= */

.vbs-showcase-sticky-scroll {
	/* One knob for the pin offset. Raise it on a site with a fixed header so
	   the rail clears the bar instead of sliding under it. */
	--vbs-showcase-sticky-scroll-top: clamp(1.5rem, 4vw, 3rem);
	/* Named because the spine below has to sit exactly half a gutter out. */
	--vbs-showcase-sticky-scroll-gutter: clamp(3rem, 7vw, 6rem);
}

.vbs-showcase-sticky-scroll__layout {
	display: grid;
	gap: clamp(2.5rem, 6vw, 4rem);
}

@media (min-width: 64rem) {
	.vbs-showcase-sticky-scroll__layout {
		/* No align-items here — see note 2 at the top of this file. */
		grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
		column-gap: var(--vbs-showcase-sticky-scroll-gutter);
	}
}

/* --- left column ----------------------------------------------------------
   Stretches full height on purpose: it is the track the rail slides along,
   and (below) the element that measures scroll progress for the gauge. */
.vbs-showcase-sticky-scroll__aside {
	position: relative;
}

/* A hairline down the full height of the column, centred in the gutter. The
   rail is short and the column is enormous, so without it the left half of
   the section reads as a hole rather than as a margin. It also draws the
   track the rail actually travels along. */
@media (min-width: 64rem) {
	.vbs-showcase-sticky-scroll__aside::after {
		content: "";
		position: absolute;
		inset-block: 0;
		inset-inline-end: calc(var(--vbs-showcase-sticky-scroll-gutter) / -2);
		inline-size: var(--vb-border-hair);
		background-color: var(--vb-color-rule);
	}
}

@media (min-width: 64rem) and (min-height: 48rem) {
	.vbs-showcase-sticky-scroll__rail {
		position: sticky;
		top: var(--vbs-showcase-sticky-scroll-top);
	}
}

.vbs-showcase-sticky-scroll__title {
	margin-block-start: 1.25rem;
	max-inline-size: 15ch;
}

/* Words rise out of their own mask; a hair of padding keeps descenders off
   the mask edge. */
.vbs-showcase-sticky-scroll__title .vb-split__inner { padding-block-end: 0.06em; }

.vbs-showcase-sticky-scroll__intro {
	margin-block-start: clamp(1rem, 2vw, 1.35rem);
	max-inline-size: 46ch;
}

/* --- chapter index --------------------------------------------------------
   Gauge in a fixed column, list in the flexible one, so the hairline runs the
   exact height of the list whatever the chapter count. */
.vbs-showcase-sticky-scroll__index {
	margin-block-start: clamp(1.75rem, 3vw, 2.25rem);
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	column-gap: clamp(0.9rem, 2vw, 1.35rem);
}

.vbs-showcase-sticky-scroll__gauge {
	position: relative;
	inline-size: var(--vb-border-hair);
	background-color: var(--vb-color-rule);
	border-radius: var(--vb-radius-pill);
}

.vbs-showcase-sticky-scroll__gauge-fill {
	position: absolute;
	inset: 0;
	background-color: var(--vb-color-accent);
	border-radius: inherit;
	transform-origin: 50% 0;
}

.vbs-showcase-sticky-scroll__chapter + .vbs-showcase-sticky-scroll__chapter {
	border-block-start: var(--vb-border-hair) solid var(--vb-color-rule);
}

.vbs-showcase-sticky-scroll__chapter-link {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	column-gap: 0.9rem;
	align-items: baseline;
	padding-block: clamp(0.6rem, 1.2vw, 0.8rem);
	transition: transform var(--vb-dur-base) var(--vb-ease-out-soft);
}

/* On a phone the detail sits under its label, which is how a list of four
   wants to read at 390. From 64rem up — where the rail is pinned — the two
   share one line instead, and that single-line row is what keeps the whole
   rail shorter than a laptop viewport. */
.vbs-showcase-sticky-scroll__chapter-body { display: block; }

@media (min-width: 64rem) {
	.vbs-showcase-sticky-scroll__chapter-body {
		display: flex;
		flex-wrap: wrap;
		align-items: baseline;
		justify-content: space-between;
		gap: 0.1rem 1rem;
	}
}

.vbs-showcase-sticky-scroll__chapter-num {
	font-family: var(--vb-font-mono);
	font-size: var(--vb-fs-small);
	line-height: var(--vb-lh-small);
	color: var(--vb-color-mute);
	font-variant-numeric: tabular-nums;
	transition: color var(--vb-dur-base) var(--vb-ease-out-soft);
}

.vbs-showcase-sticky-scroll__chapter-label {
	font-family: var(--vb-font-display);
	font-size: var(--vb-fs-h3);
	line-height: var(--vb-lh-h3);
	letter-spacing: var(--vb-ls-h3);
	font-weight: var(--vb-weight-heading);
	color: var(--vb-color-ink);
	transition: color var(--vb-dur-base) var(--vb-ease-out-soft);
}

.vbs-showcase-sticky-scroll__chapter-meta {
	display: block;
	margin-block-start: 0.1rem;
	flex: none;
}

@media (min-width: 64rem) {
	.vbs-showcase-sticky-scroll__chapter-meta { margin-block-start: 0; }
}

/* Hover and keyboard focus get the same treatment — the list is the section's
   navigation, so it has to be usable without a pointer. */
.vbs-showcase-sticky-scroll__chapter-link:hover,
.vbs-showcase-sticky-scroll__chapter-link:focus-visible {
	transform: translateX(0.25rem);
}
.vbs-showcase-sticky-scroll__chapter-link:hover .vbs-showcase-sticky-scroll__chapter-label,
.vbs-showcase-sticky-scroll__chapter-link:focus-visible .vbs-showcase-sticky-scroll__chapter-label,
.vbs-showcase-sticky-scroll__chapter-link:hover .vbs-showcase-sticky-scroll__chapter-num,
.vbs-showcase-sticky-scroll__chapter-link:focus-visible .vbs-showcase-sticky-scroll__chapter-num {
	color: var(--vb-chroma-ink);
}

.vbs-showcase-sticky-scroll__cta {
	margin-block-start: clamp(1.25rem, 2.5vw, 1.75rem);
}

/* --- the stack ------------------------------------------------------------
   Row gap is deliberately larger than any plate's drift, so a panel can never
   collide with its neighbour at the extremes of the parallax range. */
.vbs-showcase-sticky-scroll__stack {
	display: grid;
	row-gap: clamp(3rem, 6vw, 4.75rem);
}

.vbs-showcase-sticky-scroll__panel {
	display: grid;
	row-gap: clamp(1.75rem, 2.6vw, 2.25rem);
	/* Anchor jumps from the chapter list land clear of the top edge. */
	scroll-margin-block-start: clamp(1.5rem, 5vw, 4rem);
	/* Head, plate and copy arrive in that order. */
	--vb-motion-distance: 18px;
	--vb-motion-step: 110ms;
}

/* Unequal widths, alternating side: the rhythm that keeps a four-panel stack
   from reading as a spreadsheet. Panel 4 anchors the bottom at full width. */
@media (min-width: 64rem) {
	/* Width follows the crop: the taller the ratio, the narrower the panel, so
	   no single plate ever runs away with the page. Narrow, wide-right, narrow,
	   full — the cadence is what stops four panels reading as a spreadsheet.
	
	   The rem in each max() is a MEASURE FLOOR, not a second design. On a 1024
	   screen the stack track is only ~515px, and a bare 52% put panel 3's body
	   copy at 268px — about 33 characters, which sets as a ragged ribbon under
	   every preset. The floor holds each panel to a readable column on a small
	   laptop and goes inert from about 1400px up, where the percentages are
	   already the larger value and the composition is exactly as drawn. The
	   min(100%, …) stops a floor ever overflowing the track. */
	.vbs-showcase-sticky-scroll__panel:nth-child(1) { inline-size: min(100%, max(62%, 26rem)); }
	.vbs-showcase-sticky-scroll__panel:nth-child(2) { inline-size: min(100%, max(88%, 28rem)); margin-inline-start: auto; }
	.vbs-showcase-sticky-scroll__panel:nth-child(3) { inline-size: min(100%, max(52%, 24rem)); }
	.vbs-showcase-sticky-scroll__panel:nth-child(4) { inline-size: 100%; }
}

/* --- panel head -----------------------------------------------------------
   Numeral, a rule that eats the slack, then the stage tag. Even panels run it
   the other way round, which is what makes the mobile stack alternate. */
.vbs-showcase-sticky-scroll__panel-head {
	display: flex;
	align-items: center;
	gap: clamp(0.75rem, 2vw, 1.25rem);
}

.vbs-showcase-sticky-scroll__panel:nth-child(even) .vbs-showcase-sticky-scroll__panel-head {
	flex-direction: row-reverse;
}

.vbs-showcase-sticky-scroll__panel-num {
	font-family: var(--vb-font-display);
	font-size: var(--vb-fs-display-2);
	font-weight: var(--vb-weight-display);
	letter-spacing: var(--vb-ls-display-2);
	line-height: 1;
	color: var(--vb-chroma-ink);
	font-variant-numeric: tabular-nums;
	flex: none;
}

.vbs-showcase-sticky-scroll__panel-rule {
	flex: 1 1 auto;
	block-size: var(--vb-border-hair);
	background-color: var(--vb-color-rule);
}

.vbs-showcase-sticky-scroll__panel-tag {
	flex: none;
}

/* --- plate ----------------------------------------------------------------
   __plate exists only to separate the two transforms: the stagger moves the
   wrapper, the parallax moves the media inside it. */
.vbs-showcase-sticky-scroll__media {
	border-radius: var(--vb-radius-lg);
}

/* --- panel copy ----------------------------------------------------------- */
.vbs-showcase-sticky-scroll__panel-copy {
	display: grid;
	row-gap: 0.75rem;
	max-inline-size: 48ch;
}

/* --- foot -----------------------------------------------------------------
   Two items so the closing rule runs the full width of the section. Capping
   the paragraph alone would shrink the rule with it, and under a preset with
   a wide face that leaves a stub of a line floating under the left column. */
.vbs-showcase-sticky-scroll__foot {
	margin-block-start: clamp(2.5rem, 5vw, 4rem);
	padding-block-start: clamp(1rem, 2vw, 1.5rem);
	border-block-start: var(--vb-border-hair) solid var(--vb-color-rule);
	display: grid;
	gap: 0.5rem clamp(2rem, 5vw, 4rem);
	align-items: baseline;
}

.vbs-showcase-sticky-scroll__foot-note { max-inline-size: 60ch; }

@media (min-width: 48rem) {
	.vbs-showcase-sticky-scroll__foot { grid-template-columns: minmax(0, 1fr) auto; }
}

/* It is a third item in the two-column layout grid, so it has to be told to
   span both tracks or it lands under the rail. */
@media (min-width: 64rem) {
	.vbs-showcase-sticky-scroll__foot { grid-column: 1 / -1; }
}

/* ===========================================================================
   Bespoke: the chapter gauge
   ---------------------------------------------------------------------------
   None of the shared scroll primitives can do this. data-vb-progress is bound
   to document scroll, which would make the gauge report the whole page rather
   than this section, and data-vb-parallax / data-vb-zoom drive transforms off
   the animated element's OWN view range — useless here, because the gauge is
   inside a pinned rail and therefore barely moves.

   So the timeline is named on __aside, the full-height grid column, whose view
   range is exactly the stack's. A named view timeline is referenceable by the
   declaring element's descendants, and the gauge is one — no timeline-scope
   needed. `contain` is the right range: it runs from the moment the column's
   top reaches the top of the scrollport to the moment its bottom reaches the
   bottom, which is precisely the window during which the rail is pinned.

   The base state is scaleY(1) — a full accent gauge. Everything that can go
   wrong here (no animation-timeline support, reduced motion, the section used
   at a width where the sticky layout never engages) lands on that, which reads
   as a deliberate accent rule beside the list rather than as a broken bar.
   ========================================================================= */
@supports (animation-timeline: view()) {
	@media (min-width: 64rem) and (min-height: 48rem) {
		.vbs-showcase-sticky-scroll__aside {
			view-timeline-name: --vbs-showcase-sticky-scroll-run;
			view-timeline-axis: block;
		}
		.vbs-showcase-sticky-scroll__gauge-fill {
			animation: vbs-showcase-sticky-scroll-gauge linear both;
			animation-timeline: --vbs-showcase-sticky-scroll-run;
			animation-range: contain 0% contain 100%;
		}
	}
}

@keyframes vbs-showcase-sticky-scroll-gauge {
	from { transform: scaleY(0); }
	to   { transform: scaleY(1); }
}

@media (prefers-reduced-motion: reduce) {
	.vbs-showcase-sticky-scroll__gauge-fill {
		animation: none !important;
		transform: none;
	}
	.vbs-showcase-sticky-scroll__chapter-link:hover,
	.vbs-showcase-sticky-scroll__chapter-link:focus-visible {
		transform: none;
	}
}
