@layer components {
	/*
	 * Reveal-on-scroll.
	 *
	 * Driven by IntersectionObserver rather than `animation-timeline: view()`.
	 * A view() timeline ties the animation to scroll position, so content
	 * re-hides when the visitor scrolls back up — wrong for a reveal, which
	 * should happen once and stay.
	 *
	 * Everything is visible by default. The hidden state only applies once JS
	 * has stamped `has-js` on <html>, so content is never trapped invisible.
	 */

	.has-js [data-reveal] {
		opacity: 0;
		translate: 0 var(--reveal-shift, 1.75rem);
		transition:
			opacity var(--dur-slow) var(--ease-out),
			translate var(--dur-slow) var(--ease-out),
			filter var(--dur-slow) var(--ease-out);
		transition-delay: var(--reveal-delay, 0ms);
		will-change: opacity, translate;
	}

	.has-js [data-reveal="blur"] {
		filter: blur(6px);
	}

	/*
	 * Grows into place rather than shrinking into it. At 1.04 a card waiting to
	 * be revealed was 4% wider than its grid cell, and the last one in the row
	 * pushed the document 7px past the window — hidden by the body's overflow
	 * rule, but there. Under 1 it can never exceed the cell it sits in.
	 */
	.has-js [data-reveal="scale"] {
		translate: 0 0;
		scale: 0.96;
		transition:
			opacity var(--dur-slow) var(--ease-out),
			scale var(--dur-slow) var(--ease-out);
	}

	.has-js [data-reveal].is-revealed {
		opacity: 1;
		translate: 0 0;
		scale: 1;
		filter: none;
		will-change: auto;
	}

	/* Motion is decoration here; the content still arrives without it. */
	@media (prefers-reduced-motion: reduce) {
		.has-js [data-reveal] {
			opacity: 1;
			translate: none;
			scale: none;
			filter: none;
			transition: none;
		}
	}

	/* Hero introduction and brand-mark choreography live in intro.css. */

	/* ---------------------------------------------------- section headings */

	.section-head {
		display: flex;
		flex-wrap: wrap;
		align-items: flex-end;
		justify-content: space-between;
		gap: var(--space-m) var(--space-l);
		padding-block: clamp(var(--space-l), 5vh, var(--space-2xl)) var(--space-m);
	}

	.section-eyebrow {
		display: flex;
		align-items: center;
		gap: var(--space-s);
		margin: 0 0 var(--space-2xs);
		font-size: 0.68rem;
		letter-spacing: 0.22em;
		text-transform: uppercase;
		color: var(--ink-3);
	}

	/* Carries the label to the edge instead of leaving it stranded. */
	.section-eyebrow::after {
		content: "";
		flex: 1;
		height: 1px;
		background: var(--line);
	}

	.section-head h2 {
		font-size: clamp(1.9rem, 3.6vw, 3rem);
		line-height: 1;
		margin: 0;
	}

	.section-head-aside {
		display: flex;
		flex-wrap: wrap;
		align-items: flex-end;
		gap: var(--space-s) var(--space-l);
	}

	.section-count {
		margin: 0;
		display: flex;
		align-items: baseline;
		gap: 0.45em;
	}

	.section-count strong {
		font-family: var(--font-display);
		font-size: var(--step-3);
		font-weight: 400;
		color: var(--ink);
		font-variant-numeric: tabular-nums;
	}

	.section-count span {
		font-size: 0.68rem;
		letter-spacing: 0.16em;
		text-transform: uppercase;
		color: var(--ink-3);
	}

	.section-link {
		font-size: 0.72rem;
		letter-spacing: 0.16em;
		text-transform: uppercase;
		text-decoration: none;
		color: var(--ink-2);
		display: flex;
		align-items: center;
		gap: 0.8em;
		/*
		 * Takes the row it is on rather than demanding 20rem inside a 22rem
		 * box that already holds the count — which pushed it 150px past the
		 * page edge, where the body's overflow rule quietly cut it off.
		 */
		flex: 1 1 100%;
		min-width: 0;
		padding-bottom: 0.4em;
		border-bottom: 1px solid var(--line);
		white-space: nowrap;
		transition: color var(--dur-fast) var(--ease-out),
			gap var(--dur-fast) var(--ease-out),
			border-color var(--dur-fast) var(--ease-out);
	}

	.section-link:hover {
		color: var(--ink);
		gap: 1em;
		border-color: var(--ink);
	}

	@media (max-width: 48rem) {
		/*
		 * Four items on three full-width rows. Stacked in their own wrappers
		 * the heading sat alone on a short line with the rest of the column
		 * empty beside it; pairing the eyebrow with the count spans the width
		 * and gives the heading a row of its own to fill.
		 */
		.section-head {
			display: grid;
			grid-template-columns: 1fr auto;
			align-items: baseline;
			gap: var(--space-2xs) var(--space-m);
		}

		.section-head-main,
		.section-head-aside {
			display: contents;
		}

		.section-eyebrow {
			grid-row: 1;
			grid-column: 1;
			margin: 0;
		}

		.section-count {
			grid-row: 1;
			grid-column: 2;
			justify-self: end;
		}

		.section-count strong {
			font-size: var(--step-1);
		}

		.section-head h2 {
			grid-row: 2;
			grid-column: 1 / -1;
			font-size: clamp(2.6rem, 13.5vw, 3.6rem);
			letter-spacing: -0.035em;
		}

		.section-link {
			grid-row: 3;
			grid-column: 1 / -1;
			justify-self: stretch;
			justify-content: space-between;
			margin-top: var(--space-s);
			padding-top: var(--space-s);
			padding-bottom: 0;
			border-top: 1px solid var(--line);
			border-bottom: 0;
		}

		.section-link:hover {
			border-color: var(--line);
		}
	}

	/* A hairline that draws itself in as the section arrives */
	.rule {
		height: 1px;
		background: var(--line);
		transform-origin: left;
	}

	.has-js .rule[data-reveal] {
		opacity: 1;
		translate: none;
		scale: 0 1;
		transition: scale var(--dur-slow) var(--ease-out);
		transition-delay: var(--reveal-delay, 0ms);
	}

	.has-js .rule[data-reveal].is-revealed {
		scale: 1 1;
	}
}
