/* ==========================================================================
   Services Panels — Services §4 (Figma 10534:3876, panels 11102:4950 ff)

   Six sibling panels, each 1600 x 1000 in the frame and contiguous. Everything
   drawn is expressed as a percentage of the 1600 frame, so the section is either
   exactly Chris's pixels (a 1600 box) or a faithful scale of them, never a mix.
   Vertical padding is a percentage too — percentages in `padding` resolve against
   the WIDTH, which is exactly the proportional behaviour wanted here.

   THIS FILE IS THE JS-OFF STATE. No sticky, no overlay, no transform: six plain
   readable sections. tbl-services-panels.js adds `is-stacked` only inside
   (min-width: 1024px) and (prefers-reduced-motion: no-preference), so no-JS,
   GSAP-failed, reduced-motion, sub-1024 and the BB editor all land here.

   THE ROW MUST BE FULL WIDTH / FULL CONTENT / 0 SIDE PADDING. In a fixed-width
   row every percentage below resolves against 1560 instead of 1600 and the whole
   section renders 2.5% small — silently, proportionally, with no error anywhere.
   ========================================================================== */

.tbl-services-panels {
	position: relative;
	display: block;
}

/* §3 sits directly above this section and §4's panels drift UPWARD out of their
   own row. BB rows are siblings at one stacking level, so without this the
   escaping panel paints over the expanding image. Fading the escaping element is
   not the fix — that was measured both ways on About §4. #tb-site-header is
   z-index 9998, so 1 stays under it. Verify with elementsFromPoint, never with a
   z-index comparison: a z-index number is meaningless across stacking contexts. */
.fl-row:has(.tbl-expanding-image) {
	position: relative;
	z-index: 1;
}

/* ---------------------------------------------------------------- the panel */

/* min-height rather than the frame's literal 1000px: the panel is a screen that
   grows with its content, which is Motto's behaviour and Ben's Round 2 answer.
   121 / 1600 = 7.5625% top, and the frame leaves 211.12 under the body = 13.195%.
   The background must be OPAQUE — the stack's layering falls out of DOM order
   plus opaque backgrounds, and a translucent panel is how it stops working. */
.tbl-services-panels__panel {
	position: relative;
	display: flex;
	flex-direction: column;
	min-height: 100vh;
	padding: 7.5625% 0 13.195%;
	background-color: var(--White);
	color: var(--Black);
}

/* --------------------------------------------------------------- the header */

/* Frame 2083935143 — x 78, width 1436.26, height 132. The row's height is the
   number's line box, not a set value: the heading's 88 box sits centred in it
   (22 + 88 + 22 = 132), which is what align-items: center reproduces. */
.tbl-services-panels__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 4%;
	padding: 0 5.35875% 0 4.875%;
}

/* H0 — 90 / 88 / -1.5. --h0-font-size clamps to its 90px maximum at --vp-max
   (1400px), so at the frame's 1600 it is exactly 90; --h0-line-height 0.978
   gives 88.02. Measured against the node: "Brand Strategy" at 90px/-1.5px in
   Sequel Sans Roman Disp is 569.67 against the frame's 567. */
.tbl-services-panels__heading {
	margin: 0;
	font-family: var(--header-font);
	font-size: var(--h0-font-size);
	line-height: var(--h0-line-height);
	font-weight: var(--header-font-weight);
	letter-spacing: -1.5px;
	color: inherit;
}

/* The number is TOKENLESS — it is not H0, whose line-height is 88 against this
   node's 132 box. Derived, not assumed: the node is 142.26 x 132 with its right
   edge on the header row's right edge (78 + 1294 + 142.26 = 1514.26), and the
   digits render at the same ink height as the heading beside them. Sequel Sans
   puts 'B' and '0' within 0.001em of each other (0.714 / 0.715 of em), so equal
   ink means equal size: 90px. The 132 is the box, i.e. the row height, which is
   why it is written as the line-height and not as a size.

   The 142.26 width is a resized box, not an advance sum — "01" at 90px measures
   84.42 — so it carries no tracking information and none is applied. */
.tbl-services-panels__number {
	flex: 0 0 auto;
	margin: 0;
	font-family: var(--header-font);
	font-size: var(--h0-font-size);
	line-height: 132px;
	font-weight: var(--header-font-weight);
	letter-spacing: 0;
	color: inherit;
	font-variant-numeric: lining-nums;
}

/* ---------------------------------------------------- the rule and the body */

/* The header sits at the top of the screen and the body at the bottom of it,
   with the flexible distance between them — the frame's 87px header-to-rule gap
   is what a 1000-tall panel happens to leave, not a fixed value. */
.tbl-services-panels__lower {
	margin-top: auto;
}

/* HorizontalBorder (11102:4952) — the hairline runs the full 1600, edge to edge,
   so it sits outside the body's own inset. #E0E0E0 is the same literal §1's jump
   rule writes: no token in variables.css carries it (--Neutral-40 is #b2b2b2).
   theme.css:1915 sets `margin: 10px auto !important` on every hr, and theme.css
   is imported UNLAYERED, so cancelling it needs an unlayered important too. */
.tbl-services-panels__rule {
	display: block;
	width: 100%;
	height: 0;
	border: 0;
	border-top: 1px solid #E0E0E0;
	opacity: 1;
	margin: 0 0 2.3125% !important;
}

/* Frame 2083935147 — x 86.06, width 1427.61, 37 below the rule. The copy column
   is 827 of that (57.9287%) and the media 337.61 (23.6486%), and the frame puts
   the media's right edge on the body's own right edge, so space-between is the
   placement rather than a measured gap. */
.tbl-services-panels__body {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	padding: 0 5.39563% 0 5.37866%;
}

.tbl-services-panels__copy {
	flex: 0 0 57.9287%;
	display: flex;
	flex-direction: column;
	align-self: stretch;
}

/* H3 — 38 / 44 / 0. --h3-font-size clamps to 2.375rem at --vp-max and
   --h3-line-height 1.158 gives 44.0 exactly. */
.tbl-services-panels__statement {
	margin: 0;
	font-family: var(--header-font);
	font-size: var(--h3-font-size);
	line-height: var(--h3-line-height);
	font-weight: var(--header-font-weight);
	letter-spacing: 0;
	color: inherit;
}

/* Frame 2083935145 — the two lists and the link sit on the BOTTOM of the copy
   column (their 86-tall row ends 8.88 above the body's own bottom edge), which
   is what margin-top: auto reproduces while the media drives the column height.
   align-items: flex-end is what puts "Start a Project" (30 tall, at y 56 of 86)
   on the lists' third line, which is where the frame draws it. */
.tbl-services-panels__lists {
	display: flex;
	align-items: flex-end;
	margin-top: auto;
	padding-top: 5%;
}

/* 257 and 268 of the 827 copy column, 95 apart. list-style and padding are both
   reset here rather than inherited: theme.css gives `ul, ol` padding-left 18px
   and every `ul li` a padding-bottom of 6px, which would add 6px to a line pitch
   the frame draws at exactly 30. .tbl-services-panels__list li is (0,1,1)
   against that rule's (0,0,2), so it wins without an important. */
.tbl-services-panels__list {
	flex: 0 0 31.0762%;
	margin: 0;
	padding: 0;
	list-style: none;
}

.tbl-services-panels__list + .tbl-services-panels__list {
	flex: 0 0 32.4063%;
	margin-left: 11.4873%;
}

/* Paragraph - Large — 20 / 30 / letterSpacing 1. That 1 is ONE PERCENT on this
   token, not one pixel (the panels' Category Alt sibling reports the same bare 1
   meaning 1px) — 1% of 20px is 0.2px. */
.tbl-services-panels__list li {
	margin: 0;
	padding: 0;
	font-family: var(--body-font);
	font-size: 20px;
	line-height: 30px;
	font-weight: var(--body-font-weight);
	letter-spacing: 0.2px;
	color: inherit;
}

/* theme.css draws an underline on every <a> inside a p/ul/ol/li in .body-inner at
   (0,5,3) and non-important. An unlayered important here is the strongest tier,
   not the inverted-layer trap — theme.css is imported unlayered (style.css:26). */
.tbl-services-panels__list li a {
	text-decoration-line: none !important;
	color: inherit;
}

/* The site's own .text-link component carries the type (20 / 1.5 = 30) and the
   states; only the placement is written here. 80 of the 827 column from the
   second list's right edge. The transition rides .text-link's own bare
   `var(--button-easing)` — do not restate it with a property prefix, which makes
   the declaration invalid CSS and silently drops it. */
.tbl-services-panels__link {
	flex: 0 0 auto;
	margin-left: 9.6735%;
	white-space: nowrap;
}

/* file.mp4 (11102:4956) — 337.61 x 411.88, right-aligned on the body's edge. */
.tbl-services-panels__media {
	flex: 0 0 23.6486%;
	aspect-ratio: 337.61 / 411.88;
	overflow: hidden;
}

.tbl-services-panels__asset {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* ----------------------------------------------------------- the JS-on state */

/* Both gates repeated from the JS. The class is only ever added inside the same
   matchMedia, so this is belt to the JS's braces rather than a second source of
   truth — but it means a stale class left on the element by a failed teardown
   cannot pin anything at a width where the design says it must not. */
.tbl-services-panels__overlay {
	display: none;
}

@media (min-width: 1200px) and (prefers-reduced-motion: no-preference) {

	/* The pin is CSS sticky, never ScrollTrigger's `pin:`. header.php opens
	   .tb-reveal-content, a transformed ancestor sitewide, which breaks
	   position: fixed in Chrome 116+/Safari 26+ while still working in Firefox —
	   and ScrollTrigger pins with position: fixed. Sticky positions against the
	   nearest scrollport and a transform does not create one.

	   `top` is written inline by the JS as min(0, -(panelHeight - innerHeight)),
	   so a panel taller than the viewport pins flush to the viewport BOTTOM. It
	   cannot be expressed here: top: calc(100vh - 100%) is not a substitute,
	   because percentages in `top` resolve against the containing block.

	   Sticky releases at the bottom of the containing block, which is the section
	   wrapping all six panels — that is the entire reason this is one module and
	   not six BB rows. Six rows would each release at their own row's bottom and
	   there would be no stack at all. */
	.tbl-services-panels.is-stacked .tbl-services-panels__panel {
		position: sticky;
	}

	/* Last child and unpositioned siblings, so it paints over the panel content
	   with no z-index anywhere. The JS drives opacity from 0. */
	.tbl-services-panels.is-stacked .tbl-services-panels__overlay {
		display: block;
		position: absolute;
		inset: 0;
		background-color: #000000;
		opacity: 0;
		pointer-events: none;
	}
}

/* ------------------------------------------------- 1024-1200: compressed, static */

/* THREE tiers, not two, and the motion gate is 1200 rather than 1024. Ben's call
   after looking at four takes side by side in a prototype: "option C looks the
   best. However, I don't think they need to be at 100vh when it switches to the
   compressed look. When it switches, the animation should not happen, and the
   sections should just be scrollable like normal at whatever height they render
   at with proper spacing between each panel."

   The 1024 gate was measured wrong, not written wrong. At 1024 the two-column
   desktop layout still fits, but the left sub-list wraps every item to two lines
   while the right wraps only its third — so the columns stop aligning line for
   line — and the panel's forced 100vh leaves ~172px of dead white between the
   header and the hairline. Both are only visible on a rendered page; every
   number in the layout is correct.

   So this band keeps the desktop SHAPE — copy beside media — and drops the two
   things that break at this width: the second list column, and the full screen.
   No pin, no overlay, no blur, no drift; the JS gate matches at 1200, so nothing
   here is a second code path for the motion, it is simply below it.

   Nothing between 1199.98 and 1200 and nothing between 1023.98 and 1024: the
   three tiers are exact complements. A bare max-width: 1024px against a JS gate
   of min-width: 1024px matches at BOTH ends of that pixel, which is the overlap
   that pinned a mobile-layout panel at exactly 1024.

   The second query is reduced motion at or above the stack gate. It lands here
   rather than in the tier below because the reason this tier exists is Ben's —
   "when it switches, the animation should not happen, and the sections should
   just be scrollable like normal at whatever height they render at" — and that
   is a statement about the motion being off, not about the width. Without it,
   reduced motion at 1600 got the desktop shape minus its motion: panels still
   forced to 100vh, still carrying the ~172px of dead band that only a stack's
   rhythm justifies. Handing a 1600 viewport the sub-1024 mobile column instead
   was the other option and was rejected on the render. */
@media (min-width: 1024px) and (max-width: 1199.98px),
       (min-width: 1200px) and (prefers-reduced-motion: reduce) {

	/* The panel stops being a screen and becomes a section. The padding is the
	   spacing BETWEEN panels — they run white-on-white with no divider, the same
	   way the tier below already does, so the rhythm is the only separation. */
	.tbl-services-panels__panel {
		min-height: 0;
		padding: 7.5625% 0 9%;
	}

	/* margin-top: auto only does anything when the panel has spare height, which
	   it no longer has. Written as a real gap so the header-to-rule distance is
	   authored rather than left over. */
	.tbl-services-panels__lower {
		margin-top: 7%;
	}

	/* H0 clamps to 90 at 1400 and reads ~70 here, which is too heavy against a
	   1024 box once the copy column narrows. Stepped down with the statement so
	   the three sizes stay in proportion to each other. */
	.tbl-services-panels__heading {
		font-size: 68px;
		letter-spacing: -1.2px;
	}

	.tbl-services-panels__number {
		font-size: 68px;
		line-height: 1;
	}

	.tbl-services-panels__statement {
		font-size: 32px;
		line-height: 40px;
	}

	.tbl-services-panels__copy {
		flex: 0 0 62%;
	}

	/* ONE column, not two. This is the actual fix for the ragged wrap: at this
	   width neither 257 nor 268 of the frame's 827 survives the scale, so the
	   longer list wraps and the shorter does not and the two stop reading as a
	   pair. Stacked, every item gets the full column and none of them wrap. */
	.tbl-services-panels__lists {
		flex-direction: column;
		align-items: flex-start;
		gap: 0;
		margin-top: 0;
		padding-top: 6%;
	}

	.tbl-services-panels__list,
	.tbl-services-panels__list + .tbl-services-panels__list {
		flex: 0 0 auto;
		width: 100%;
		margin-left: 0;
	}

	/* The frame puts the link on the lists' third line, which only means anything
	   while the lists are a pair. Below the pair it goes under them. */
	.tbl-services-panels__link {
		margin-left: 0;
		margin-top: 24px;
	}

	.tbl-services-panels__media {
		flex: 0 0 30%;
	}
}

/* ------------------------------------------------------------- below the gate */

/* The design is desktop-only, so everything below is derived: the body collapses
   to one column, the panel stops being a screen and becomes a section, and the
   two sub-lists sit side by side for as long as they read.

   1023.98 rather than 1024 — see the tier above: complementary gates written as
   min-width: N and max-width: N both match at exactly N. */
@media (max-width: 1023.98px) {

	.tbl-services-panels__panel {
		min-height: 0;
		padding: 7.5625% 0 9%;
	}

	.tbl-services-panels__head {
		align-items: flex-start;
		gap: 5%;
	}

	.tbl-services-panels__number {
		line-height: var(--h0-line-height);
		/* Sits on the heading's last line rather than its first (Pastel #78). */
		align-self: flex-end;
	}

	.tbl-services-panels__body {
		flex-direction: column;
		align-items: stretch;
	}

	.tbl-services-panels__copy {
		flex: 0 0 auto;
	}

	.tbl-services-panels__lists {
		flex-wrap: wrap;
		gap: 30px 6%;
		padding-top: 40px;
	}

	.tbl-services-panels__list,
	.tbl-services-panels__list + .tbl-services-panels__list {
		flex: 0 1 40%;
		margin-left: 0;
	}

	.tbl-services-panels__link {
		flex: 1 0 100%;
		margin-left: 0;
	}

	.tbl-services-panels__media {
		flex: 0 0 auto;
		width: 100%;
		max-width: 420px;
		margin-top: 40px;
		aspect-ratio: 337.61 / 411.88;
	}
}

@media (max-width: 767px) {

	.tbl-services-panels__list,
	.tbl-services-panels__list + .tbl-services-panels__list {
		flex: 1 0 100%;
	}

	/* Stacked, the two lists should read as one (Pastel #76): drop the row gap
	   between them and give the link back its own separation. */
	.tbl-services-panels__lists {
		gap: 0 6%;
	}

	.tbl-services-panels__link {
		margin-top: 30px;
	}

	/* The heading's line-height is under 1, so its descenders sit ~5px below the
	   box and were touching the hairline (Pastel #77). 18px of box clearance is
	   ~13px of visible air. Parent in the selector because theme.css:1929 zeroes
	   this with `hr:first-child { margin-top: 0 !important }` at (0,1,1) — a bare
	   class important loses to it. */
	.tbl-services-panels__lower > .tbl-services-panels__rule {
		margin-top: 18px !important;
	}

	.tbl-services-panels__media {
		max-width: none;
	}
}
