/* About §7 Team Grid (Figma node 11016:4424) — a full-bleed 4-column grid of 2:3
   portraits, columns 2 and 4 staggered 200px lower. Geometry source: ticket
   .cortex/about-page/06-team-grid.md. CSS-only; the module registers no JS. */

.tbl-team-grid {
	/* No outer padding — BB row settings own section spacing (house convention).
	   Full-bleed is editor-owned too: the row must be Full Width with 0 side
	   padding. No CSS breakout here, which is what keeps this module JS-free. */

	/* The frame's gutter and stagger as a fraction of its 1600-wide container, so
	   both scale with the cards at every viewport: 34/1600 and 199.7/1600. cq
	   units resolve against this element for every descendant that reads them. */
	container-type: inline-size;
	--tbl-tg-gap: 2.125cqi;
	--tbl-tg-stagger: 12.48cqi;
	--tbl-tg-cols: 4;

	/* Scroll drift (Ben, 2026-08-17). How far each column rises across the section's
	   own traversal of the viewport. Columns 2 and 4 take the larger value, so they
	   climb faster and visibly close part of the 200px stagger — the 120px
	   differential is the whole effect, and the pair of numbers is the only thing to
	   touch when tuning it. 200px and 320px at a 1600 container.

	   Sized against the SECTION's scroll, not a viewport height: the grid is ~7500px
	   tall, so its cover range is ~8400px and a 100px drift would be 1.2% of the
	   scroll — real in a measurement and invisible on the page. */
	--tbl-tg-drift: 12.5cqi;
	--tbl-tg-drift-fast: 20cqi;

	/* One card's width, derived from the same arithmetic the grid uses, so the row
	   track below can be expressed in terms of it at every tier. 23.40625cqi = 374.5
	   at a 1600 container. */
	--tbl-tg-card-w: calc((100cqi - (var(--tbl-tg-cols) - 1) * var(--tbl-tg-gap)) / var(--tbl-tg-cols));

	/* Caption metrics (Pastel #111). Named rather than inlined because the row
	   track below is the SUM of them plus the image — the pitch and the caption
	   cannot drift apart if there is only one place to edit either. Chris: tighten
	   the name-to-role lead, put the role on its own line, and leave a little air
	   under it before the next photo. */
	--tbl-tg-caption-lead: 4.09px;
	--tbl-tg-name-lh: 22px;
	--tbl-tg-role-lh: 20px;
	--tbl-tg-caption-pad: 16px;
}

.tbl-team-grid__list {
	display: grid;
	grid-template-columns: repeat(var(--tbl-tg-cols), minmax(0, 1fr));
	gap: var(--tbl-tg-gap);
	/* A FIXED row track, and this is the whole reason the pitch matches the frame.
	   A grid row sizes to the tallest card across all four columns, so one wrapped
	   caption in column 2 would lengthen the pitch of columns 1, 3 and 4 as well —
	   measured at 661.65 against the frame's 631.5 in check round 2.

	   Chris's columns are independent at a uniform 631.5 whether a card is 596.75 or
	   614 tall: a taller caption hangs into the gap below rather than pushing the
	   next card down (frame col 4 leaves 18px under Ashley's two-line card and col 1
	   leaves 35.25 under Geoff's one-line card). Pinning the track reproduces that.
	   card_w x 1.5 is the 2:3 image, and the caption block is the sum of its own
	   four metrics — so the track follows the caption automatically instead of
	   carrying a hardcoded 35.75 that goes stale the moment the caption changes
	   (it did, at Pastel #111: the role moved onto a second line). The 0.75 is the
	   slack the original track carried over its 35 of caption; kept so the gap
	   under a one-line card is unchanged in kind. */
	grid-auto-rows: calc(
		var(--tbl-tg-card-w) * 1.5
		+ var(--tbl-tg-caption-lead)
		+ var(--tbl-tg-name-lh)
		+ var(--tbl-tg-role-lh)
		+ var(--tbl-tg-caption-pad)
		+ 0.75px
	);
	/* The stagger is a transform and so adds no height. Without this the last
	   offset row would hang out of the section and over §8. */
	padding: 0 0 var(--tbl-tg-stagger);
	margin: 0;
	list-style: none;
}

.tbl-team-grid__item {
	/* Cards sit at their own height rather than stretching to the tallest in the
	   row, so one wrapped caption does not lengthen its neighbours. */
	align-self: start;
	min-width: 0;
	/* theme.css puts padding-bottom: 6px on every li but the last. margin: 0 does
	   not cancel it, and left in it adds 6px to every grid cell — walking the row
	   pitch 6px off the frame per row. Same reset as tbl-stats-facts__stat. */
	padding: 0;
}

/* Columns 2 and 4 sit 199.7px lower. A transform, not a margin: the shift has to
   stay visual, leaving the grid's row pitch and the DOM's row-major reading order
   — and therefore the tab order — untouched. */
.tbl-team-grid__item:nth-child(4n + 2),
.tbl-team-grid__item:nth-child(4n) {
	transform: translateY(var(--tbl-tg-stagger));
}

.tbl-team-grid__card {
	display: block;
	color: inherit;
	text-decoration: none;
	/* Read by the drift keyframes below. Columns 2 and 4 override it. */
	--tbl-tg-drift-distance: var(--tbl-tg-drift);
}

.tbl-team-grid__item:nth-child(4n + 2) .tbl-team-grid__card,
.tbl-team-grid__item:nth-child(4n) .tbl-team-grid__card {
	--tbl-tg-drift-distance: var(--tbl-tg-drift-fast);
}

/* ------------------------------------------------------------ scroll drift ---- */

/* Ben, 2026-08-17: "make columns 1 and 3 move upward as the user scrolls down and
   columns 2 and 4 move slightly faster with the same animation."

   ONE timeline for the whole section, named on the container and referenced by
   every card — not a per-card `view()`. A per-card timeline would give each card
   its own progress, so cards within a column would drift apart from each other
   instead of the column moving as a body.

   The drift rides the CARD while the stagger stays on the ITEM, so the two
   transforms compose on separate elements and neither has to know about the
   other. The stagger stays a static offset; only the drift animates. */
@media (prefers-reduced-motion: no-preference) {
	@supports (animation-timeline: view()) {

		.tbl-team-grid {
			view-timeline: --tbl-tg block;
			/* Load-bearing, NOT a default restated. The default `auto` adopts the
			   scrollport's scroll-padding, and this site sets
			   `scroll-padding-top: 80px` for the sticky header — which silently
			   shortens the scrub. Same trap §5 was measured hitting. */
			view-timeline-inset: 0;
		}

		.tbl-team-grid__card {
			/* linear is mandatory: the shorthand's timing function defaults to
			   ease, which would accelerate the drift against a linear scroll
			   input (scroll-scrub-pin-needs-linear). */
			animation: tbl-tg-drift linear both;
			animation-timeline: --tbl-tg;
			animation-duration: 1ms;
		}
	}
}

a.tbl-team-grid__card {
	cursor: pointer;
	/* theme.css:652-657 underlines every anchor inside an li in .body-inner, at a
	   specificity three :not()s deep that a class cannot reach. Nothing in the frame
	   draws an underline, and left in it makes a linked card look unlike an unlinked
	   one. Both sheets are unlayered (style.css:26 imports theme.css outside every
	   layer) and the theme's declaration is not itself important, so this wins. */
	text-decoration-line: none !important;
}

a.tbl-team-grid__card:focus-visible {
	outline: 2px solid var(--Black);
	outline-offset: 4px;
}

/* ------------------------------------------------------------------ media ---- */

.tbl-team-grid__media {
	/* 374.5 × 561.75 at 1600 — held as a ratio so it survives every viewport, and
	   so a row with no photo still occupies its cell instead of collapsing. */
	display: block;
	aspect-ratio: 2 / 3;
	overflow: hidden;
	/* The frame fills every card's Background with #f3f3f5 and the portraits are
	   transparent cut-outs composited onto it, so the empty state and the photo
	   state have to be the same grey. */
	background: #f3f3f5;
}

.tbl-team-grid__media img {
	display: block;
	width: 100%;
	height: 100%;
	/* A portrait of any aspect is cropped, never letterboxed or stretched. */
	object-fit: cover;
}

/* ---------------------------------------------------------------- caption ---- */

/* (0,2,0), not (0,1,0): the caption is its card's last child, and theme.css:435
   `p:last-child { padding-bottom: 0 }` is (0,1,1) — it silently zeroed the
   padding-bottom below (measured live, the track grew and the caption did not).
   Both sheets are unlayered, so specificity alone settles it. */
.tbl-team-grid .tbl-team-grid__caption {
	/* The frame's 15px is measured to the glyph box (20 of the name's line box),
	   not to the line box, so the margin that reproduces it reads smaller than 15.
	   padding-bottom is Pastel #111's "small padding below the title before the
	   next photo" — on the caption rather than the card, so it is part of the same
	   sum the row track reads. */
	margin: var(--tbl-tg-caption-lead) 0 0;
	padding-left: 16px;
	padding-bottom: var(--tbl-tg-caption-pad);
	font-family: var(--body-font);
	/* H6: 16 at every viewport, but the theme's heading defaults are lh 0.95 and
	   ls -0.01em, so both need saying out loud. Weight 410 is unreachable — only
	   Roman Disp 400 is licensed. */
	font-size: 16px;
	/* Both spans are blocks now and carry their own line boxes, so this only sets
	   the strut a wrapped role inherits from. */
	line-height: var(--tbl-tg-role-lh);
	letter-spacing: 0.32px;
	font-weight: 400;
	color: var(--Black);
}

/* Cased here, not in the source string: the frame's own layer strings are
   inconsistently cased and every one renders as full caps. Chromium computes the
   accessible name from the transformed text — accepted on the TT-01/01 precedent
   (see the ticket); do not "fix" it without asking. */
.tbl-team-grid__name {
	/* Its own block, so the role below is a layout break rather than a wrap — and
	   the lead between them is this line box, not a separator glyph. Tightened from
	   30.91 at Pastel #111: at 16px caps that was a 1.93 lead and read as a gap. */
	display: block;
	text-transform: uppercase;
	line-height: var(--tbl-tg-name-lh);
}

/* Paragraph - Small. No CSS custom property exists for this Figma token. The fill
   is a raw hex on the text node with no bound variable — read off 11016:4496. */
.tbl-team-grid__role {
	display: block;
	font-size: 15px;
	line-height: var(--tbl-tg-role-lh);
	letter-spacing: 1px;
	color: #7b7b7b;
}

/* ------------------------------------------------------------- responsive ---- */

/* The frame is desktop-only at 1600; both tiers below are judgment. 1024 is
   inclusive so the grid is already 2-up at exactly 1024. */
@media (max-width: 1024px) {

	.tbl-team-grid {
		--tbl-tg-cols: 2;
		/* Both re-derived against the CARD, not the container: at two columns a
		   plain 2.125cqi shrinks the gutter to 21.76 while the cards grow to 501,
		   which reads as a hairline. Solving g = 0.090788 × (W − g)/2 for the
		   frame's gutter:card ratio gives 4.3423cqi, and the same treatment of
		   199.7:374.5 gives 25.5cqi — so this tier is a true scale of the frame. */
		--tbl-tg-gap: 4.3423cqi;
		--tbl-tg-stagger: 25.5cqi;
	}

	.tbl-team-grid__item:nth-child(4n + 2),
	.tbl-team-grid__item:nth-child(4n) {
		transform: none;
	}

	.tbl-team-grid__item:nth-child(2n) {
		transform: translateY(var(--tbl-tg-stagger));
	}

	/* The fast column moves with the stagger: at two tracks the offset column is
	   every second card, not every fourth. */
	.tbl-team-grid__item:nth-child(4n + 2) .tbl-team-grid__card,
	.tbl-team-grid__item:nth-child(4n) .tbl-team-grid__card {
		--tbl-tg-drift-distance: var(--tbl-tg-drift);
	}

	.tbl-team-grid__item:nth-child(2n) .tbl-team-grid__card {
		--tbl-tg-drift-distance: var(--tbl-tg-drift-fast);
	}
}

@media (max-width: 767px) {

	.tbl-team-grid {
		--tbl-tg-cols: 1;
		/* A proportional gutter would be ~8px here, and the type does not scale
		   with it. Fixed from this tier down. */
		--tbl-tg-gap: 32px;
	}

	/* A single column offset from nothing is a 200px hole. */
	.tbl-team-grid__item:nth-child(2n),
	.tbl-team-grid__item:nth-child(4n + 2),
	.tbl-team-grid__item:nth-child(4n) {
		transform: none;
	}

	.tbl-team-grid__list {
		/* Pastel #120 (Chris): the portraits ran edge to edge at 375 and he asked
		   for 10-20px back. 16 matches the caption's own indent, so the caption
		   keeps sitting one step in from the image the way it does at every other
		   width rather than becoming flush with it. */
		padding-inline: 16px;
		padding-bottom: 0;
		/* One column cannot couple its pitch to a neighbour, so the fixed track buys
		   nothing here — and at a 350px card a three-line caption would overrun it
		   into the card below. Let the rows size to content again. */
		grid-auto-rows: auto;
	}

	/* And with one column there is nothing for a differential drift to read
	   against — every card would rise together, which is just a slower section. */
	.tbl-team-grid__card {
		animation: none;
	}
}

@keyframes tbl-tg-drift {
	from { transform: translateY(0); }
	to   { transform: translateY(calc(var(--tbl-tg-drift-distance) * -1)); }
}
