/* Client Roster (§5, Figma node 10355:3845) — small uppercase section label above
   a horizontal client-logo row. Behavior source of truth: rejouice.com logo strip.
   See .planning/phases/03-home/assets/tbl-client-roster-SPEC.md for measured values.

   BB owns the section box (bb-owns-section-spacing convention): NO outer padding/
   min-height/max-width on the module root — BB row settings own spacing (SPEC §10).

   Colors are token-only (no raw hex). The only color is the label (--Neutral-100);
   logos render as-is (full-color, no recolor). Knobs emitted inline on the <section>
   (--tbl-roster-speed / --tbl-roster-gap / --tbl-roster-content-max), each with a CSS
   fallback; each logo fits a --tbl-roster-logo-max-w × --tbl-roster-logo-max-h box. */

.tbl-client-roster {
	position: relative;
	display: block;
	max-width: 100vw;
}

/* Non-logo content column — the section label sits in a centered, max-width-capped
   column and left-aligns to its left edge (Ben). The logo viewport below is full-bleed
   and centers its logos, so the two are intentionally not left-aligned to each other. */
.tbl-client-roster__inner {
	max-width: var(--tbl-roster-content-max, 1400px);
	margin-inline: auto;
	margin-bottom: 36px;
	padding-inline: var(--tbl-roster-gutter, 24px);
	box-sizing: border-box;
}

/* Section label — Figma "Category - Small": Sequel Sans (--body-font; the design
   system has no distinct "Book Disp" family string), 10px, weight 405, 2px tracking,
   uppercase, --Neutral-100. It is an <h2> for section-heading semantics but styled
   small (NOT the theme .h2 display class). SPEC §8. */
.tbl-client-roster__label {
	margin: 0 0 40px;
	text-align: left;
	font-family: var(--body-font);
	font-size: 10px;
	line-height: 1.778; /* ~17.78px at 10px */
	font-weight: 405;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--Neutral-100);
}

/* Viewport clips the marquee. Full-bleed; logos center when static, scroll when overflowing. */
.tbl-client-roster__viewport {
	position: relative;
	width: 100%;
	overflow: hidden;
	/* Optional edge-fade masks are OFF by default (Figma shows hard edges, SPEC §4).
	   Hook left for later if Ben wants it:
	   --tbl-roster-edge-fade: 0px;
	   -webkit-mask-image: linear-gradient(to right, transparent 0, black var(--tbl-roster-edge-fade), black calc(100% - var(--tbl-roster-edge-fade)), transparent 100%);
	           mask-image: linear-gradient(to right, transparent 0, black var(--tbl-roster-edge-fade), black calc(100% - var(--tbl-roster-edge-fade)), transparent 100%); */
}

.tbl-client-roster__track {
	display: flex;
	align-items: center;
	gap: var(--tbl-roster-gap, 72px);
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Static (fits): the track fills the viewport and CENTERS the logos (full-width BB
   section, Ben), no transform, no motion (SPEC §4 step 2). Server default + no-JS /
   editor state. */
.tbl-client-roster__track.is-static {
	width: 100%;
	justify-content: center;
	transform: none;
}

/* Marquee (overflows): the track is content-width (wider than the viewport) with a
   fixed minimum gap; JS packs a second aria-hidden clone after it and translates the
   whole track leftward (SPEC §4 step 3). */
.tbl-client-roster__track.is-marquee {
	width: max-content;
	justify-content: flex-start;
	flex-wrap: nowrap;
	will-change: transform;
}

.tbl-client-roster__item {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
}

.tbl-client-roster__link {
	display: inline-flex;
	align-items: center;
}

/* Drag-to-scrub (marquee mode only; mouse). The viewport is the grab surface; a linked
   logo shows the pointer cursor; during an active drag everything shows grabbing. Logo
   <img>s carry draggable="false" so the native image-drag ghost never fires — a link-less
   logo's only interaction is grabbing the marquee (Ben). */
.tbl-client-roster.is-draggable .tbl-client-roster__viewport {
	cursor: grab;
	-webkit-user-select: none;
	user-select: none;
}

.tbl-client-roster.is-draggable .tbl-client-roster__link {
	cursor: pointer;
}

.tbl-client-roster.is-grabbing .tbl-client-roster__viewport,
.tbl-client-roster.is-grabbing .tbl-client-roster__viewport * {
	cursor: grabbing !important;
}

/* Logo box — each logo fits inside a max 130×50 box, natural aspect preserved,
   centered (SPEC §2, Ben). draggable="false" is also set in markup; user-drag here
   is the CSS backstop against the native image-drag ghost. */
.tbl-client-roster__img {
	display: block;
	width: auto;
	height: auto;
	max-width: var(--tbl-roster-logo-max-w, 130px);
	max-height: var(--tbl-roster-logo-max-h, 70px);
	object-fit: contain;
	-webkit-user-drag: none;
	user-drag: none;
}

/* --- Entrance cascade — one-shot, viewport-triggered by the module JS. NOT
   scroll-scrubbed (the theme's own .fade-up-in uses animation-timeline:view(),
   which IS scroll-linked; deliberately not used here — Ben).

   Safe-by-default: logos/label are only hidden once JS adds .is-reveal-armed, so
   no-JS / reduced-motion / BB-editor always render them fully visible. On first
   viewport entry JS adds .is-revealed and each element eases up+in on a per-item
   --reveal-delay (label leads at 0). The reveal transform lives on the <li>/<h2>
   (translateY) and never collides with the marquee's track translateX. */
.tbl-client-roster.is-reveal-armed .tbl-client-roster__label,
.tbl-client-roster.is-reveal-armed .tbl-client-roster__item {
	opacity: 0;
	transform: translateY(var(--tbl-roster-reveal-shift, 18px));
	transition:
		opacity var(--tbl-roster-reveal-dur, 600ms) var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)),
		transform var(--tbl-roster-reveal-dur, 600ms) var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
	transition-delay: var(--reveal-delay, 0ms);
	will-change: opacity, transform;
}

.tbl-client-roster.is-reveal-armed.is-revealed .tbl-client-roster__label,
.tbl-client-roster.is-reveal-armed.is-revealed .tbl-client-roster__item {
	opacity: 1;
	transform: none;
}

/* Marquee clones are decorative duplicates — always fully visible, never part of
   the entrance cascade (and aria-hidden in markup). */
.tbl-client-roster__item--clone {
	opacity: 1 !important;
	transform: none !important;
	transition: none !important;
}

/* Mobile ≤767: logos almost always overflow → marquee scrolls. Tighten the gap and
   logo height by scaling the knobs (calc keeps the inline var authoritative — a plain
   media-query var override would lose to the inline style). Label sits closer. */
@media (max-width: 767px) {
	.tbl-client-roster__label {
		margin-bottom: 28px;
	}

	.tbl-client-roster__track {
		gap: calc(var(--tbl-roster-gap, 72px) * 0.6);
	}

	.tbl-client-roster__img {
		max-width: calc(var(--tbl-roster-logo-max-w, 130px) * 0.82);
		max-height: calc(var(--tbl-roster-logo-max-h, 70px) * 0.82);
	}
}

/* Reduced motion: never animate (JS forces is-static and never clones). When the set
   overflows, wrap and center so every logo stays visible — no clipping, no motion
   (SPEC §4 / §6). */
@media (prefers-reduced-motion: reduce) {
	.tbl-client-roster__track.is-static {
		flex-wrap: wrap;
		justify-content: center;
		row-gap: calc(var(--tbl-roster-gap, 72px) * 0.5);
		transform: none !important;
	}

	/* Belt-and-suspenders — JS should never set is-marquee under reduced motion. */
	.tbl-client-roster__track.is-marquee {
		transform: none !important;
	}

	/* Never trap the roster behind the entrance cascade under reduced motion —
	   JS also skips arming, this is the CSS backstop. */
	.tbl-client-roster.is-reveal-armed .tbl-client-roster__label,
	.tbl-client-roster.is-reveal-armed .tbl-client-roster__item {
		opacity: 1 !important;
		transform: none !important;
		transition: none !important;
	}
}
