/* TBL Insights — single.php Phase 11 blog interior styling.
   Token-only, in its own .tbl-single namespace so legacy .single .post-holder
   / .single-post-content / .single-post-navigation rules (theme.css) cannot
   bleed in. Enqueued separately (unlayered) from tbl-insights.php, loading
   only on is_singular(array('post','press')).

   NOTE — vertical rhythm ownership: this file owns .tbl-single__article's
   own padding/max-width/vertical spacing directly. The "BB owns section
   spacing" project convention does NOT apply here — this is a plain PHP
   template (get_header/get_footer), never a Beaver Builder row, so there is
   no BB row to defer to.

   Phase 11 rewrite (plan 11-05): page shell, local tokens, post header
   (title/deck/star/byline/cat-pills), hero + caption, the two-column body
   grid with its first-class --no-rail widened state, and the CONTENTS rail
   (card, numerals, share block, focus rings, 24px hit targets, mobile
   accordion). Body-content typography, the newsletter band and the related
   grid are owned by plans 11-07/11-08 — not duplicated here. */

.tbl-single__article {
	/* Local tokens — variables.css is F-1 fenced, so every new custom
	   property this phase needs is scoped here, never added at the global
	   document-root level. */
	--tbl-page-w:     1493px;                        /* measured content band, 1600 frame */
	--tbl-gutter:     clamp(16px, 4vw, 53.33px);     /* frame inset 53.33 each side */
	--tbl-measure:    720px;                         /* text measure, D-02 */
	--tbl-measure-wide: 780px;                       /* JUDGMENT — no Figma below 1600; no-rail state, see below */
	--tbl-breakout:   50px;                          /* media overhang each side, D-02 */
	--tbl-rail-w:     257px;                         /* TOC rail column */
	--tbl-rail-gutter: 100px;                        /* rail -> measure gutter */
	--tbl-rail-tint:  color-mix(in srgb, var(--nav-bolt-color) 10%, transparent);
	--tbl-muted:      color-mix(in srgb, var(--Neutral-100) 70%, transparent);
	--tbl-hairline:   color-mix(in srgb, var(--Neutral-100) 15%, transparent);  /* FLAG: not measured — caption/related rule colour is not in 11-MEASUREMENTS.md's colour table */
	/* R3-5 (round 3) — DELIBERATE DEVIATION FROM THE FRAME, on Ben's explicit instruction.
	   Frame measures 10px (author->band) / 280px (band->related); R2-6 already deviated once
	   to 48px/64px on an earlier Ben instruction; this is now a SECOND deviation to 140px on
	   desktop, scaling down for narrower viewports, with BOTH gaps kept equal at every width
	   (Ben: "the same amount of space" above the band as below it). One shared token so they
	   can never drift apart. 9vw reaches 144px (clamped to the 140px ceiling) at the 1600px
	   frame width; scales down to the 48px floor by ~906px viewport width. Chris delivered
	   desktop only, so the curve itself is judgement, consistent with this file's existing
	   clamp() pattern (see --tbl-gutter/--tbl-breakout above). */
	--tbl-band-gap:   clamp(48px, 9vw, 140px);

	width: min(var(--tbl-page-w), 100% - (2 * var(--tbl-gutter)));
	margin-inline: auto;
	/* Pastel #82 (Ben) / #17 (Chris) — main.tbl-single carries margin-top:-80px
	   so the article runs under the transparent nav, but the top padding was a
	   flat 60px: at 375 that put "Back to Insights" at y=62, i.e. 18px BEHIND
	   an 80px header bar. Derived from --header-height rather than restated as
	   a literal, so it stays correct if the bar's height changes. 110px is
	   Chris's "another 50px" on the old 60, and lands 30px clear of the bar. */
	padding-block: calc(var(--header-height, 80px) + 30px) var(--Spacing-PadM, 70px);
}

/* Screen-reader-only utility. No global one exists in this theme —
   theme.css:20173 scopes .screen-reader-text to .mobile-menu__toggle only. */
.tbl-single__article .screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* --- Header ---------------------------------------------------------- */

.tbl-single__header {
	margin-bottom: 32px;
}

/* Always-drawn underline; colour fades in on interaction (never on load).
   text-decoration-line is discrete and cannot tween, so the line is drawn
   at rest and only its colour animates (project convention). --button-easing
   already carries the full transition list, so no hand-rolled list is added. */
.tbl-single__back {
	display: inline-block;
	margin-bottom: 24px;
	/* Pastel #17 (Chris) — grey so it stops competing with the logo above it.
	   --Neutral-70 (#757575) rather than a lighter grey: it is the lightest
	   neutral in the ramp that still clears AA's 4.5:1 on white (4.6:1), and
	   this is a link, not decoration. */
	color: var(--Neutral-70);
	font-family: var(--body-font);
	font-size: var(--body-font-size-smaller);
	text-decoration-line: underline;
	text-decoration-color: transparent;
	transition: var(--button-easing);
}

.tbl-single__back:hover,
.tbl-single__back:focus-visible {
	text-decoration-color: currentColor;
}

.tbl-single__title {
	max-width: 1040px;
	margin: 0 auto;
	text-align: center;
	font-family: var(--header-font);
	font-size: 60px;
	line-height: 62px;
	letter-spacing: -1px;
	color: var(--Neutral-100);
}

.tbl-single__deck {
	max-width: 1040px;
	margin: 11px auto 0;
	text-align: center;
	font-family: var(--body-font);
	font-size: 20px;
	line-height: 30px;
	letter-spacing: 0.2px;
	/* Bespoke greenish-black tone, no achromatic token match — same
	   substitution the Phase 10 stopgap already makes for this exact color. */
	color: var(--tbl-muted);
}

.tbl-single__star {
	display: block;
	width: 20px;
	height: 20px;
	margin: 24px auto 0;   /* Pastel #83 — was 41px; the sparkle read as floating mid-gap rather than attached to the title */
}

.tbl-single__star svg {
	display: block;
	width: 100%;
	height: 100%;
}

.tbl-single__meta {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
	margin-top: 25px;
}

.tbl-single__byline {
	margin: 0;
}

/* R2-2 (11-ROUND2-SPEC.md, frame 10416:5957) — "Category - Small" treatment, the SAME
   recipe R2-7's Related Reading heading uses: Book Disp (fonts.css weight 300, aliased to
   the Roman file until Ben licenses the real weight — matches the related-card chip/
   read-time precedent from 11-08), 10px/17.78px, 2px tracking, uppercase. Two spans, two
   colours — a markup change in single.php, not CSS alone. */
.tbl-single__byline-author,
.tbl-single__byline-date {
	font-family: var(--body-font);
	font-weight: 300;
	font-size: 10px;
	line-height: 17.78px;
	letter-spacing: 2px;
	text-transform: uppercase;
}

.tbl-single__byline-author {
	color: var(--Black);
}

.tbl-single__byline-date {
	color: rgba(0, 0, 0, 0.4);
}

.tbl-single__byline-date time {
	color: inherit;
}

.tbl-single__cat-pills {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin: 0;
	padding: 0;
	list-style: none;
	/* D-08: wraps, no +N chip, no clipped/scrolling container — every
	   category renders in full, full stop. */
}

/* Category pill — reuses Phase 10's inactive filter-pill recipe verbatim
   (tbl-insights-feed.css:230-249), as a link rather than a toggle.
   R2-1 (11-ROUND2-SPEC.md) — Ben: "function just like the category filters from the feed.
   No underline." bg colour: the feed's OWN pill also reads var(--Primary-10) (#e8ddf7),
   the SAME token this rule already used — confirmed by reading tbl-insights-feed.css
   directly. The frame's #f2eef6 is a value the shipped, Ben-approved Phase 10 feed itself
   already diverges from; per Ben's explicit "follow the feed" instruction here, no colour
   change is needed — this rule already matches the feed exactly. */
.tbl-single__cat-pill {
	display: inline-block;
	padding: 6px;
	border-radius: 5px;
	font-family: var(--body-font);
	font-size: 10px;
	line-height: 17.78px;
	font-weight: var(--header-font-weight);
	letter-spacing: 1px;
	text-transform: uppercase;
	background-color: var(--Primary-10);
	color: var(--Black);
	/* Root-caused live (CDP getMatchedStylesForNode): an unlayered, non-important theme.css
	   rule with a higher-specificity nested "& a:not(.btn)..." selector was setting
	   text-decoration-line: underline, out-specificity-ing this plain class rule's
	   `text-decoration: none`. That opponent carries no !important of its own, so a plain
	   !important here wins outright — not the layered-vs-unlayered trap (that opponent is
	   also unlayered theme.css, not forms.css/reset.css's layer(layout)). */
	text-decoration: none !important;
	transition: var(--button-easing);
}

.tbl-single__cat-pill:hover,
.tbl-single__cat-pill:focus-visible {
	background-color: var(--Black);
	color: var(--White);
}

.tbl-single__thumb {
	margin: 13px 0 0;
}

/* aspect-ratio + object-fit:cover is what boxes the full-size featured
   image into the measured 1493x841 slot (D-09 — no new crop, no regen).
   Exactly ONE image rule; there is no blur duplicate (D-10). */
.tbl-single__thumb-img {
	display: block;
	width: 100%;
	aspect-ratio: 1493 / 841;
	height: auto;
	object-fit: cover;
	object-position: center;
	border-radius: 10px;
}

.tbl-single__caption {
	margin-top: 36px;
}

.tbl-single__caption-text {
	margin: 0 0 14px;
	font-size: var(--body-font-size-smaller);
	color: var(--tbl-muted);
}

/* This rule renders even when both the image and the caption text are
   absent (D-12, Ben verbatim: "The line will just look like it's
   underneath the author and category row."). No :has()/sibling selector
   may hide it — measured: 10 of 165 posts (6%) take this path. */
.tbl-single__caption-rule {
	margin: 0;
	border: 0;
	border-top: 1px solid var(--tbl-hairline);
}

/* JUDGMENT — no Figma below 1600 */
@media (max-width: 1199px) {
	.tbl-single__title {
		font-size: clamp(34px, 5vw, 60px);
		line-height: 1.05;
	}

	.tbl-single__deck {
		font-size: clamp(16px, 2vw, 20px);
	}
}

/* JUDGMENT — no Figma below 1600 */
@media (max-width: 700px) {
	.tbl-single__meta {
		flex-direction: column;
		align-items: flex-start;
	}
}

/* --- Body grid: rail + content ---------------------------------------- */

/* Rail state (53 of 165 posts, 32%): 257 | 100 | 720, LEFT-ALIGNED (F-A fix,
   orchestrator's live figma-local diff, 2026-07-30 — 11-QA.md's original pass had no Figma
   tool access and could not catch this). The frame's body region (`10416:5991`) is 1178.33
   wide, left-aligned at the article's own left edge, inside a 1493-wide container — NOT
   centred. `justify-content: center` here previously centred the 257+100+720=1077 track
   set inside the full 1493px container, landing the rail ~202.5px right of the frame
   (measured live: rail x=261.5 vs frame's x=59-relative-to-body-region ≈ container's own
   left edge). `justify-content: start` left-aligns the same fixed-width tracks at the
   container's start edge instead — the track sizes themselves were already exact, only the
   group's placement was wrong. */
/* R3-2 (round 3) — TRIED then REVERTED. Ben asked to centre .tbl-single__content-col
   inside .tbl-single__body; implemented (flexible 1fr second track + justify-self:center),
   measured, and reported plainly per the coordinator's instruction that it partially
   reintroduced F-A's displacement: the rail stayed flush left (unaffected), but the text
   column shifted +208px right of its frame-matching position. Ben reviewed the measured
   comparison and chose the frame: "back to the frame." Restored to the fixed
   var(--tbl-measure) second track (below), which is what F-A's comment above already
   documents — this rule is back to exactly its Round-1/2 state. */
.tbl-single__body {
	display: grid;
	grid-template-columns: var(--tbl-rail-w) var(--tbl-measure);
	column-gap: var(--tbl-rail-gutter);
	justify-content: start;
	align-items: start;
	margin-top: 8px;           /* measured: caption bottom 1396.55 -> body 1405 */
}

.tbl-single__content-col {
	min-width: 0;              /* without this, a long <pre>/<code> or a wide
	                               table blows the grid track out past the measure */
	max-width: var(--tbl-measure);
}

/* No-rail state (112 of 165 posts, 68%). D-01: "hide the whole rail ...
   and let the body widen." 97 of 165 posts have zero h2, so this is the
   layout MOST of the archive renders — it is designed, not a fallback.
   Chris delivered no frame for it (desktop-only, rail present), so the
   widened measure is a JUDGMENT: 780px rather than the rail state's 720.
   Rationale: at the frame's 20px/30px body token, 720px is ~80 characters
   and 780px is ~87 — still inside a comfortable reading measure, while
   visibly answering "let the body widen". Flagged for Ben at the 11-09
   checkpoint because it governs 68% of the archive. */
.tbl-single__body--no-rail {
	grid-template-columns: minmax(0, var(--tbl-measure-wide));
	column-gap: 0;
	/* F-A's left-align fix (above) targets the RAIL state, whose frame explicitly shows the
	   rail flush at the region's left edge. The no-rail state has zero Figma coverage of any
	   kind (JUDGMENT, per the comment above) and was ALREADY centred before F-A — preserve
	   that unchanged here rather than let it inherit the new `start` value and regress a
	   state nothing in this fix was meant to touch. */
	justify-content: center;
}

.tbl-single__body--no-rail .tbl-single__content-col {
	max-width: var(--tbl-measure-wide);
}

/* The breakout keeps its +/-50px in BOTH states (plan 11-07 implements the
   geometry); in the no-rail state the wider measure means media reaches
   880px instead of 820px. That is intentional. */

/* Sticky rail (D-22). align-self:start is required — a grid item defaults
   to stretch, which makes position:sticky inert because the item is
   already as tall as the row. */
.tbl-single__toc {
	position: sticky;
	top: calc(var(--header-height, 80px) + 24px);
	align-self: start;
	max-height: calc(100vh - var(--header-height, 80px) - 48px);
	overflow-y: auto;
	overscroll-behavior: contain;   /* a long TOC must not scroll-chain into Lenis */
	/* R2-4b (11-ROUND2-SPEC.md) — padding-top: 73px REMOVED. Root cause (orchestrator's
	   live figma-local diff): the frame's RAIL FRAME starts 73px above the body text, and
	   that padding is what brings the CARD down to meet the text there. In the live DOM
	   the rail and content already share a top edge before any padding is added, so the
	   73px was pushing the card 73px too low instead of aligning it. Target:
	   contentTop - tocCardTop ~= 0. */
	display: flex;
	flex-direction: column;
	gap: 32px;                      /* measured 31.928 — the card and the share block
	                                    are TWO blocks; the share block is OUTSIDE the card */
}

/* One post in the archive has completely empty content — guard the grid
   from collapsing badly rather than adding a placeholder/empty-state.
   Plan 11-07 adds the base body-copy typography here too, rather than a
   second `.tbl-single__content { }` block, so this selector is declared
   exactly once in the file. */
.tbl-single__content {
	min-height: 1px;
	color: var(--Neutral-100);
	font-family: var(--body-font);
	/* FLAG: 11-MEASUREMENTS.md does not itemise the in-body paragraph size.
	   The frame's body token is Paragraph - Large (20/30, tracking 0.2px)
	   and the deck uses it, so body copy adopts it too. Confirm at QA
	   against the frame, not against this file. */
	font-size: 20px;
	line-height: 30px;
	letter-spacing: 0.2px;
}

.tbl-single__content > * {
	margin: 0 0 1.5em;
}

/* R2-3 (11-ROUND2-SPEC.md) — Ben: "body text needs to be at 0.6 opacity." Scoped to the
   PROSE (paragraphs + list items) ONLY, not `.tbl-single__content` itself — a plain
   `opacity` on the container would create a new stacking context and fade the pull quote,
   headings, links, images and the author bio along with the running copy, none of which
   Ben asked for. Colour-with-alpha via color-mix keeps every other descendant's own
   explicit colour rule (headings, links, blockquote, figcaption, mark, code) untouched,
   since those all already set their own `color` and this rule never touches them. */
.tbl-single__content p,
.tbl-single__content li {
	color: color-mix(in srgb, var(--Neutral-100) 60%, transparent);
}

.tbl-single__content > *:last-child {
	margin-bottom: 0;
}

/* JUDGMENT — no Figma below 1600 */
@media (max-width: 1199px) {
	.tbl-single__body,
	.tbl-single__body--no-rail {
		grid-template-columns: minmax(0, 1fr);
		column-gap: 0;
	}

	.tbl-single__toc {
		position: static;
		max-height: none;
		overflow: visible;
		padding-top: 0;
		margin-bottom: 32px;
		gap: 0;
	}

	.tbl-single__content-col,
	.tbl-single__body--no-rail .tbl-single__content-col {
		max-width: none;
	}
}

/* JUDGMENT — no Figma below 1600. Below ~900px the +/-50px breakout
   overhang must scale down rather than clip (plan 11-07 consumes this
   token — setting it here is sufficient). */
@media (max-width: 899px) {
	.tbl-single__article {
		--tbl-breakout: clamp(0px, 4vw, 50px);
	}
}

/* --- CONTENTS rail: card, numerals, share block ------------------------ */

.tbl-single__toc-card {
	width: 100%;
	padding: 28px 0 24px;                    /* CONTENTS sits 28.28px below the card top */
	background: var(--tbl-rail-tint);        /* = #b390e7 @ 10%, measured rgba(179,144,231,0.1) */
	border-radius: 9px;                      /* measured 9.122; = var(--Corner-Radius-S) */
}

.tbl-single__toc-heading {
	margin: 0 0 12px;
	padding-inline-start: 24px;              /* measured left 23.72 */
	font-family: var(--body-font);
	font-size: 16px;
	line-height: 30.91px;
	letter-spacing: 0.32px;                  /* RENDERED px — H6's declared "2" is a % */
	text-transform: uppercase;
	color: var(--Black);
}

.tbl-single__toc-list {
	margin: 0;
	padding: 0;
	list-style: none;                        /* the numeral circles ARE the numbering */
}

.tbl-single__toc-item {
	margin: 0;
}

.tbl-single__toc-link {
	display: flex;
	/* R2-4d (11-ROUND2-SPEC.md) — real headings run longer than the frame's placeholder
	   text and must WRAP (Ben's instruction overrides the frame's whitespace-nowrap
	   placeholder). align-items: flex-start (was center) keeps the numeral circle
	   TOP-ALIGNED with the item's first line rather than vertically centred on the whole
	   wrapped block. */
	align-items: flex-start;
	gap: 6px;                                /* measured: circle ends 43.79, text starts 50.17 */
	min-height: 34.665px;                    /* measured circle pitch — a floor, not a cap;
	                                             wrapped items grow past it naturally */
	padding-inline: 24px;                    /* R3-3 (round 3) — Ben: 24px on BOTH sides, not
	                                             just the left. Left edge still aligns with
	                                             CONTENTS; right padding is new, giving wrapped
	                                             text breathing room before the card edge. */
	/* R2-4a (11-ROUND2-SPEC.md) — this plain `text-decoration:none` was ALREADY here and
	   was STILL losing (confirmed live via CDP getMatchedStylesForNode, same root cause as
	   R2-1's category pill): an unlayered, non-important theme.css rule with a
	   higher-specificity nested "& a:not(.btn)…" selector forces underline on every real
	   <a> in the document unless beaten. That opponent carries no !important, so a plain
	   !important here wins outright. */
	text-decoration: none !important;
	color: var(--Black);
	transition: var(--button-easing);        /* BARE — never prefix a property */
}

.tbl-single__toc-num {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 20px;                             /* measured 20.069 */
	height: 20px;
	border-radius: 50%;
	background-color: var(--tbl-rail-tint);  /* INACTIVE — FLAG: measured only qualitatively */
	color: color-mix(in srgb, var(--Black) 40%, transparent);   /* INACTIVE — FLAG */
	font-size: 11px;                         /* measured 10.947 */
	line-height: 1;
	transition: var(--button-easing);
}

.tbl-single__toc-num-text {
	display: block;
}

/* R2-4a/d (11-ROUND2-SPEC.md) — Ben: "TOC links need to not have underlines" (removed
   entirely, no hover-fade-in either — unlike this project's usual always-drawn/fades-in-
   on-hover convention, this link gets NO underline treatment at any state) "…each item
   should word-wrap" (white-space/overflow/ellipsis truncation removed — the frame's
   placeholder text was nowrap, but real headings are longer). */
/* R3-1 (round 3) — Ben's own root-cause: the frame's 34.665px was never intra-item leading.
   The frame's items are whitespace-nowrap single lines, so when items didn't wrap, item-to-
   item RHYTHM and per-line LEADING were the same number and the frame's export collapsed
   them into one "line-height" value. R2-4d made items wrap (Ben's own instruction), and
   applying 34.665px as actual line-height blew multi-line items apart (each wrapped line
   got 34.665px of vertical space). Split the two meanings apart: normal leading here
   (~1.3x the 12px font, ~16px) for the text itself; the 34.665px rhythm is carried by
   .tbl-single__toc-link's PRE-EXISTING min-height (below, unchanged) instead -- for a
   single-line item that floor alone reproduces the frame's circle-top-to-circle-top pitch
   exactly, with zero extra margin needed between items. */
.tbl-single__toc-text {
	font-family: var(--body-font);
	font-size: 12px;
	line-height: 16px;
	white-space: normal;
	overflow-wrap: break-word;
	transition: var(--button-easing);
}

/* ACTIVE — the design already defines it (Ellipse 4189): solid #b390e7 with
   a white numeral. D-22's scroll-spy is exactly this state toggle applied
   to whichever section is in view. Plan 11-06's JS moves .is-active. */
.tbl-single__toc-item.is-active .tbl-single__toc-num {
	background-color: var(--nav-bolt-color);
	color: var(--White);
}

/* --- Share block (outside and below the card) --------------------------- */

/* R3-4 (round 3) — DELIBERATE DEVIATION FROM THE FRAME, on Ben's explicit instruction.
   Frame measures 30.37px (R2-4c matched it in Round 2); Ben now wants 12px regardless.
   Recorded here — and in 11-QA.md — so a later reader does not "fix" this back to
   30.37/30px. */
.tbl-single__toc-share-label {
	margin: 0 0 12px;
	/* was: margin: 0 0 30px; (R2-4c, matching the frame's measured 30.37px) */
	font-family: var(--body-font);
	font-size: 10px;
	line-height: 17.78px;
	letter-spacing: 1px;                     /* RENDERED px — Category Alt IS px, not % */
	text-transform: uppercase;
	color: var(--Black);
}

.tbl-single__toc-share-icons {
	display: flex;
	gap: 12.4px;                             /* 24.8 pitch - 12.404 glyph */
}

.tbl-single__toc-share-icon,
.tbl-single__share-icon {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 12.404px;
	height: 12.404px;
	color: var(--Black);
	transition: var(--button-easing);
}

.tbl-single__toc-share-icon svg,
.tbl-single__share-icon svg {
	display: block;
	width: 100%;
	height: 100%;
}

/* A11y — WCAG 2.2 SC 2.5.8 Target Size Minimum. The measured glyph pitch
   (24.8px) leaves a ~21.3px hit area, under the 24x24 floor. Enlarge the
   tappable box only, never the glyph. At the 24.8px pitch adjacent boxes
   touch/overlap by ~0.8px — acceptable per the UI-SPEC; do not widen the
   pitch to compensate. */
.tbl-single__toc-share-icon::before,
.tbl-single__share-icon::before {
	content: "";
	position: absolute;
	inset: 50% auto auto 50%;
	width: 24px;
	height: 24px;
	transform: translate(-50%, -50%);
}

/* Focus rings — the rail sits on a pale tint where a default ring reads
   poorly. */
.tbl-single__toc-link:focus-visible,
.tbl-single__toc-share-icon:focus-visible,
.tbl-single__share-icon:focus-visible,
.tbl-single__toc-trigger:focus-visible {
	outline: 2px solid var(--Primary-Alt-50-Base);
	outline-offset: 2px;
}

/* --- Desktop/mobile split (D-23) ---------------------------------------- */

/* JUDGMENT — no Figma below 1600. >=1200: the rail is the rail. Trigger
   hidden, panel always open, inline share row hidden (its icons live in
   the rail). */
@media (min-width: 1200px) {
	.tbl-single__toc-trigger {
		display: none;
	}

	.tbl-single__toc-panel {
		display: flex;
		flex-direction: column;
		gap: 32px;
	}

	/* Defect #2 (11-QA.md) — the rule used to hide the header's inline share row
	   unconditionally at this width, assuming the rail's own copy always exists to take
	   over. D-01 removes the WHOLE rail (share block included) on any post with fewer than
	   2 h2's — 112 of 165 posts (68%) — so the unconditional version left NEITHER copy
	   visible on the majority of the archive. Only hide the inline row when the rail (and
	   therefore its own `.tbl-single__toc-share` copy) actually exists. */
	.tbl-single__article:has(.tbl-single__toc) .tbl-single__share-inline {
		display: none;
	}
}

/* <1200 (JUDGMENT — no Figma below 1600): closed accordion above the
   article, share icons move under the byline. */
@media (max-width: 1199px) {
	.tbl-single__toc-trigger {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 12px;
		width: 100%;
		padding: 14px 20px;
		background: var(--tbl-rail-tint);
		border: 0;
		border-radius: 9px;
		font-family: var(--body-font);
		font-size: 10px;
		line-height: 17.78px;
		letter-spacing: 1px;
		text-transform: uppercase;
		color: var(--Black);
		cursor: pointer;
		transition: var(--button-easing);
	}

	.tbl-single__toc-panel {
		display: none;
	}

	.tbl-single__toc-trigger[aria-expanded="true"] + .tbl-single__toc-panel {
		display: block;
	}

	.tbl-single__toc-card {
		margin-top: 8px;
	}

	.tbl-single__toc-share {
		display: none;   /* icons show under the byline instead */
	}

	.tbl-single__share-inline {
		display: flex;
		align-items: center;
		gap: 12px;
		margin-top: 16px;
	}
}

/* Accordion open/close indicator — CSS-drawn chevron, no SVG asset. */
.tbl-single__toc-trigger-icon {
	position: relative;
	display: inline-block;
	width: 10px;
	height: 10px;
	transition: var(--button-easing);
}

.tbl-single__toc-trigger-icon::before {
	content: "";
	position: absolute;
	inset: 0;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: rotate(45deg);
	margin: 1px;
}

.tbl-single__toc-trigger[aria-expanded="true"] .tbl-single__toc-trigger-icon {
	transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
	/* Plan 11-08 extends this block (does not duplicate it) to cover every
	   transitioned/animated selector the finished file adds: the newsletter
	   submit pill and the Related Reading section's view-all link, card
	   thumbnail and card title link. */
	.tbl-single__back,
	.tbl-single__cat-pill,
	.tbl-single__toc-link,
	.tbl-single__toc-num,
	.tbl-single__toc-text,
	.tbl-single__toc-share-icon,
	.tbl-single__share-icon,
	.tbl-single__toc-trigger,
	.tbl-single__toc-trigger-icon,
	.tbl-single__content a,
	.tbl-single__newsletter .frm_button_submit,
	.tbl-single__related-viewall,
	.tbl-single__related-card-img,
	.tbl-single__related-card-fallback,
	.tbl-single__related-card-title-link {
		transition: none;
	}

	/* The card entrance is already gated on prefers-reduced-motion:
	   no-preference (below), so it never runs under reduce -- this is
	   belt-and-braces so the rule holds even if a future edit changes that
	   gate. */
	.tbl-single__related-card {
		animation: none;
	}

	.tbl-single__related-card:hover .tbl-single__related-card-img,
	.tbl-single__related-card:focus-within .tbl-single__related-card-img,
	.tbl-single__related-card:hover .tbl-single__related-card-fallback,
	.tbl-single__related-card:focus-within .tbl-single__related-card-fallback {
		transform: none;
	}
}

/* =========================================================================
   Plan 11-07 — body content typography, D-24 neutralization, media
   breakout geometry, and the author bio block. Everything below is scoped
   under .tbl-single__content (or a direct .tbl-single__author-* sibling at
   the end of the body), extending — never restyling — plan 11-05's shell,
   header, hero, caption, body grid and TOC rail above.
   ========================================================================= */

/* --- Body content: measured legacy tag set (D-04) -----------------------
   Covers all 29 tags audited across the 165-post archive (11-AUDIT.md) —
   no more, no fewer. The base .tbl-single__content typography (20/30,
   tracking 0.2px) lives on the selector declared earlier in this file. */

.tbl-single__content a {
	color: var(--Neutral-100);
	text-decoration-line: underline;
	text-decoration-color: transparent;
	text-decoration-thickness: 1px;
	text-underline-offset: 3px;
	transition: var(--button-easing);
}

.tbl-single__content a:hover,
.tbl-single__content a:focus-visible {
	text-decoration-color: currentColor;
}

.tbl-single__content a:focus-visible {
	outline: 2px solid var(--Primary-Alt-50-Base);
	outline-offset: 2px;
}

.tbl-single__content strong,
.tbl-single__content b {
	font-weight: 600;
}

/* Pastel #113 (Chris): in-body headings read bold. The 600 above is right for
   emphasis inside prose, but these posts are authored with the whole heading
   wrapped in <strong> — an artefact of how the copy was pasted, not a decision —
   so an h2 came out at 600 while the article's own h1 sits at 400.

   Medium (500) is SequelSans-MediumDisp, a real licensed face, so this is a
   weight the browser loads rather than one it synthesises. Scoped to headings
   only: <strong> in a paragraph stays 600. (0,1,2) over the (0,1,1) above. */
.tbl-single__content h2 :is(strong, b),
.tbl-single__content h3 :is(strong, b),
.tbl-single__content h4 :is(strong, b),
.tbl-single__content h5 :is(strong, b),
.tbl-single__content h6 :is(strong, b) {
	font-weight: 500;
}

.tbl-single__content em,
.tbl-single__content i {
	font-style: italic;
}

.tbl-single__content cite {
	font-style: italic;
	color: var(--tbl-muted);
}

/* h2/h3/h4/h5/h6: NO font-size here. D-03's content filter puts .h3/.h4/
   .h5 on in-body h2/h3/h4 and theme.css's own h2,.h2 {} … h6,.h6 {} rules
   own the size at that class — setting one here would fight D-03, not
   support it. scroll-margin-top clears the fixed header on a fragment jump
   (a TOC link or a deep link), matching --header-height's 80px fallback
   already used elsewhere in this file. */
.tbl-single__content h2,
.tbl-single__content h3,
.tbl-single__content h4,
.tbl-single__content h5,
.tbl-single__content h6 {
	margin: 1.5em 0 0.5em;
	font-family: var(--header-font);
	color: var(--Neutral-100);
	scroll-margin-top: calc(var(--header-height, 80px) + 24px);
}

.tbl-single__content ul,
.tbl-single__content ol {
	padding-inline-start: 1.25em;
	margin-block: 0 1.5em;
}

.tbl-single__content li {
	margin-bottom: 0.5em;
}

.tbl-single__content li > ul,
.tbl-single__content li > ol {
	margin-block: 0.5em 0;
}

/* `br` (362 occurrences) needs no rule of its own. `span` (599 occurrences)
   is inline emphasis with no dedicated visual treatment — it inherits; the
   ones carrying a legacy colour/size class are handled by the D-24 reset
   below. */

.tbl-single__content figure {
	margin: 2em 0;
}

.tbl-single__content img {
	display: block;
	max-width: 100%;
	height: auto;
	border-radius: 10px;
}

.tbl-single__content figcaption {
	margin-top: 12px;
	font-size: 15px;                  /* Paragraph - Small */
	line-height: 25px;
	letter-spacing: 1px;
	color: var(--tbl-muted);
}

/* Stray legacy wrapper divs (36 occurrences across 23 posts) — stop one
   from blowing the measure; nothing else about it is styled. */
.tbl-single__content div {
	max-width: 100%;
}

/* Quote token, 42/52, 0 tracking. D-02 (revised) — the pull quote is a TEXT
   element and stays in the narrow measure; the frame's band is transparent,
   so there is no decorative border and no italic (both deleted from the
   Phase 10 stopgap's guess). Text insets 67px from the measure's left,
   56px from its right. */
.tbl-single__content blockquote {
	margin: 48px 0;
	padding-inline: 67px 56px;
	border: 0;
	font-family: var(--header-font);
	/* Figma's Quote token reads "weight: 410" — that is Roman Disp's own
	   internal value, and fonts.css registers Roman Disp at 400. theme.css
	   forces 600 on every blockquote, which is what this overrides. */
	font-weight: 400;
	font-size: 42px;
	line-height: 52px;
	color: var(--Neutral-100);
}

/* The Quote token tracks 0, but theme.css stamps `--header-letter-spacing`
   (-0.01em) on `blockquote > p` with !important, so the tracking the
   blockquote inherits never survives to the text. Both files are unlayered
   and this one is enqueued after theme.css, so a matching-specificity
   !important takes the tie on source order. */
.tbl-single__content blockquote > p {
	letter-spacing: 0 !important;
}

/* JUDGMENT — no Figma below 1600. Below 899px the fixed 67/56 inset would
   consume most of a narrow measure, so it collapses to a flat 24px and the
   quote text scales down with the viewport. */
@media (max-width: 899px) {
	.tbl-single__content blockquote {
		padding-inline: 24px;
		font-size: clamp(24px, 4vw, 42px);
	}
}

.tbl-single__content hr {
	margin: 3em 0;
	border: 0;
	border-top: 1px solid var(--tbl-hairline);
}

/* FLAG: not itemised in 11-MEASUREMENTS.md — mark has no measured frame
   occurrence; tinted with the existing rail accent token rather than
   inventing a new colour. Confirm at QA against a marked post if one is
   found (2 posts audited). */
.tbl-single__content mark {
	background-color: color-mix(in srgb, var(--nav-bolt-color) 25%, transparent);
	color: inherit;
	padding: 0 2px;
}

.tbl-single__content sup {
	font-size: 0.7em;
	line-height: 0;
	vertical-align: super;
}

/* FLAG: not itemised in 11-MEASUREMENTS.md — code has no measured frame
   occurrence (the archive's single post using it is a snippet, not a
   design moment). Confirm at QA if that post is found. */
.tbl-single__content code {
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 0.9em;
	background-color: color-mix(in srgb, var(--Neutral-100) 6%, transparent);
	padding: 2px 5px;
	border-radius: 4px;
}

/* Defensive only — 8 embed-loader <script> tags across 7 posts must never
   occupy layout space if a future reset changes the element's default
   rendering behaviour. This does not stop execution (nor should it — those
   are the posts' own embed loaders, already executing today); it only
   guarantees a reset can't make one visible. */
.tbl-single__content script {
	display: none;
}

/* ---- D-24: neutralize legacy old-theme classes ------------------------------
 *
 * Migrated content carries classes hardcoding the OLD palette and type scale.
 * Ben chose to neutralize them in scoped CSS rather than leave them visible or
 * strip them with a content filter — no content edits, reversible, and it fixes
 * all 165 posts at once.
 *
 * Two opponents, and only two:
 *   1. The block editor pairs `has-inline-color` (129x) with an INLINE
 *      style="color:#…". An unlayered important-priority rule beats a normal
 *      inline style — importance sorts BEFORE layer order in the cascade.
 *   2. WordPress core's wp-block-library stylesheet declares .has-black-color
 *      (81x), .has-luminous-vivid-amber-color (37x), .has-large-font-size (21x)
 *      etc. at important priority, specificity (0,1,0). Scoping under
 *      .tbl-single__content with :is() gives (0,2,0), which wins on specificity
 *      regardless of which stylesheet prints first.
 *
 * The six pure old-theme classes (copper-text, dark-grey-text, arch-header,
 * alphabet-list-letter, font-akkurat, arch-image) define NOTHING in the new
 * theme — verified, theme.css matches zero of them — so they are already inert.
 * They are listed anyway so a future reader can see the full audited set.
 * -------------------------------------------------------------------------- */

.tbl-single__content :is(
	.has-inline-color, .has-text-color, .has-link-color,
	.has-black-color, .has-luminous-vivid-amber-color,
	.copper-text, .dark-grey-text, .arch-header,
	.alphabet-list-letter, .font-akkurat, .arch-image
) {
	color: inherit !important;        /* beats the block editor's inline style="color:…" */
	font-family: inherit !important;  /* beats font-akkurat's old family */
}

.tbl-single__content :is(
	.has-small-font-size, .has-medium-font-size,
	.has-large-font-size, .has-huge-font-size, .has-inline-font-size
):not(h1, h2, h3, h4, h5, h6) {
	font-size: inherit !important;    /* beats core's preset !important at (0,1,0) */
	line-height: inherit !important;
}

/* Gutenberg button blocks in post content. The `.btn` class is added to these
   links, and most of the medium button already lands from it — fill, text
   colour, padding and border width all measure correct. Four things do not,
   and each needs its own reason to be beaten:
     - border-radius and text-transform are written INLINE by the block editor,
       so only !important reaches them;
     - line-height is caught by the has-*-font-size reset above, which is
       itself !important;
     - the underline comes from `.tbl-single__content a`, whose hover rule
       (0,2,1) is why the hover selector here carries a class more.
   Everything else is left alone deliberately — restating colour here would
   outrank `.btn`'s own (0,2,0) hover/focus/active rules and kill the states. */
.tbl-single__content .wp-block-button__link.btn {
	border-radius: var(--button-border-radius) !important;
	text-transform: var(--button-text-transform) !important;
	line-height: var(--button-line-height) !important;
	text-decoration: none;
}

.tbl-single__content .wp-block-button__link.btn:hover,
.tbl-single__content .wp-block-button__link.btn:focus-visible {
	text-decoration: none;
}

/* Heading-preset-font-size scan (2026-07-30, all 165 posts, paginated REST
   pull — 100 + 65): 17 h4 headings across 3 posts (18473, 18470, 17895)
   carry wp-block-library's has-large-font-size. Headings are deliberately
   excluded from the reset above (:not(h1…h6)) so D-03's downshift isn't
   silently defeated on headings that DON'T carry a preset class — but that
   guard leaves exactly this gap: core's important-priority rule still beats
   the theme's normal-priority .h5 { font-size: var(--h5-font-size) } on
   these 17.
   Restore only the affected level, mirroring D-03's map exactly
   (h4 -> --h5-font-size). No other level was found in the scan, so no
   other restore rule is added. */
.tbl-single__content h4:is(.has-large-font-size) {
	font-size: var(--h5-font-size) !important;   /* beats wp-block-library's !important; restores D-03's h4->.h5 downshift on the 17 affected headings */
}

/* --- Body content: media breakout geometry (D-02/D-05) -------------------
 * Arithmetic proof: 720 (--tbl-measure) + 2*50 (--tbl-breakout) = 820px,
 * within 2px of the frame's measured 821.87px. In the no-rail state
 * (--tbl-measure-wide: 780px) it computes to 880px — wider than the rail
 * state by design (the widened body applies to breakout media too).
 * --------------------------------------------------------------------- */

.tbl-single__content .tbl-single__breakout {
	width: calc(100% + (2 * var(--tbl-breakout)));
	max-width: none;
	margin-inline: calc(-1 * var(--tbl-breakout));
}

/* A figure overhangs, but its caption is text and D-02 keeps text narrow —
   percentage-relative maths, not var(--tbl-measure), because the measure
   differs between the rail state (720px) and the no-rail state (780px). */
.tbl-single__content .tbl-single__breakout > figcaption,
.tbl-single__content .tbl-single__breakout figcaption {
	width: calc(100% - (2 * var(--tbl-breakout)));
	margin-inline: auto;
}

.tbl-single__content .tbl-single__breakout > img,
.tbl-single__content .tbl-single__breakout img {
	width: 100%;
	height: auto;
	border-radius: 10px;
}

/* An <iframe> has no intrinsic aspect ratio, so 16:9 must be forced — that
   is what "staying 16:9 responsive" means for the 13 YouTube + 1 Vimeo
   embeds in the archive. */
.tbl-single__content iframe {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	height: auto;
	border: 0;
	border-radius: 10px;
}

/* A <video> DOES have an intrinsic ratio (2 in the archive). Forcing 16:9
   here would crop or letterbox a real file, so the intrinsic ratio is
   preserved and only the width is constrained. FLAG: a deliberate
   narrowing of D-05's "16:9" wording, recorded for QA. */
.tbl-single__content video {
	display: block;
	width: 100%;
	height: auto;
	border-radius: 10px;
}

/* Block-editor embed wrappers must not re-constrain the iframe. */
.tbl-single__content .wp-block-embed__wrapper {
	width: 100%;
}

.tbl-single__content .wp-block-embed {
	margin-inline: 0;
}

/* --- Author bio block (D-18/D-19) ----------------------------------------
   Renders on EVERY post, house-bylined included (measured split: house 77 /
   named 88 across 11 authors), so the page always ends the same way rather
   than changing shape with the byline. */

.tbl-single__author {
	display: flex;
	align-items: center;
	gap: 22px;                 /* measured: text at x99, avatar 77 wide */
	max-width: 660px;          /* measured block 660 x 77 */
	margin-top: 64px;
}

.tbl-single__author-avatar {
	flex: 0 0 auto;
	width: 77px;
	height: 77px;
	border-radius: 50%;
	overflow: hidden;
}

/* The avatar arrives as one of three shapes from tbl_insights_author(): an
   <img>, an empty house span, or an initials span. All three carry
   .tbl-insights-row__avatar, whose own stylesheet (the BB feed module,
   tbl-insights-feed.css) is NOT enqueued on a singular post — so the two
   span variants are re-declared here, adapted from the feed's own recipe
   (tbl-insights-feed.css:647-667) at this block's 77px avatar size. */
.tbl-single__author-avatar .tbl-insights-row__avatar {
	display: block;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	object-fit: cover;         /* the inner figure is 77x96 in the frame, i.e. cropped */
	object-position: center;
}

.tbl-single__author-avatar .tbl-insights-row__avatar--house {
	position: relative;
	background: var(--Primary-90);
}

.tbl-single__author-avatar .tbl-insights-row__avatar--house::before {
	content: 'TBL';
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-family: var(--header-font);
	font-size: 18px;           /* the feed uses 8px on a ~34px avatar; scaled to 77px */
	font-weight: 600;
	letter-spacing: 1px;
	color: var(--White);
}

.tbl-single__author-avatar .tbl-insights-row__avatar-initials {
	display: flex;
	align-items: center;
	justify-content: center;
	background: color-mix(in srgb, var(--Neutral-100) 10%, transparent);
	font-family: var(--header-font);
	font-size: 24px;
	color: var(--Neutral-100);
}

.tbl-single__author-text {
	min-width: 0;
}

.tbl-single__author-name {
	margin: 0;
	font-family: var(--body-font);
	font-size: var(--body-font-size-smaller);
	color: var(--Neutral-100);
}

/* Its own rule, deliberately independent of .tbl-single__author-name — no
   :has()/sibling dependency between them, so the title line can vanish
   (empty for ~10 of 11 authors on day one, per D-18) without affecting the
   name's layout. The block still reads as one deliberate line when it's
   gone: the flex row's align-items:center plus the text column's natural
   height collapse to just the name line, no fixed height or placeholder
   needed. */
.tbl-single__author-title {
	margin: 4px 0 0;
	font-family: var(--body-font);
	font-size: var(--body-font-size-smaller);
	color: var(--tbl-muted);   /* muted — Claude's discretion per UI-SPEC, no literal given */
}

/* JUDGMENT — no Figma below 1600 */
@media (max-width: 599px) {
	.tbl-single__author {
		flex-direction: column;
		align-items: flex-start;
		gap: 16px;
	}

	.tbl-single__author-avatar {
		width: 64px;
		height: 64px;
	}
}

/* Reduced motion: nothing in this author-bio block transitions, so no
   addition to the @media (prefers-reduced-motion: reduce) block above is
   needed. */

/* =========================================================================
   Plan 11-08 — newsletter band (D-13, Formidable restyle), Related Reading
   section + card grid (D-14..D-17), and the phase's complete motion
   contract (hover, entrance, reduced motion, no-JS). Everything below
   extends -- never restyles -- plans 11-05/11-07's shell, header, hero,
   caption, body grid, TOC rail, body content typography and author bio.
   ========================================================================= */

/* --- Newsletter band (D-13) -----------------------------------------------
   This is a RESTYLE of markup Formidable emits, not greenfield markup: the
   band runs the SAME form the footer does (tb_option('newsletter_form_id',
   2)). One subscriber list, one place to manage. The submit arrow is
   injected by functions.php's form-ID-keyed filter
   (tb_newsletter_submit_svg_arrow, functions.php:664-686) and needs zero
   code here -- it targets .tb-footer__submit-arrow site-wide, by form ID,
   not by location. */

.tbl-single__newsletter {
	width: min(822px, 100%);
	/* R3-5 (round 3, supersedes R2-6) — see --tbl-band-gap's own comment above for the full
	   deviation history (frame 10px -> R2-6's 48px -> this round's clamp(48px,9vw,140px)). */
	margin: var(--tbl-band-gap) auto 0;
	/* was: margin: 48px auto 0; (R2-6) / margin: 10px auto 0 (frame-exact, Round 1) */
	padding: 64px 91px 64px 87px;       /* measured insets; content column = 822-87-91 = 644 */
	/* Defect #1 (11-QA.md / orchestrator figma-local diff) — this read "80%" where the
	   comment's own stated target (rgba(217,217,217,0.2)) needed "20%": color-mix(X 80%,
	   transparent) yields the colour at 80% OPACITY, not 20% — a 4x-too-dark/opaque band
	   against the frame, confirmed against a live figma-local screenshot. --Neutral-20
	   (#dbdbdb = rgb(219,219,219)) is already the right hue, matching the frame's
	   rgb(217,217,217) closely — only the mix percentage was wrong. */
	background-color: color-mix(in srgb, var(--Neutral-20) 20%, transparent);
	border-radius: 10px;
}

/* F-B (orchestrator figma-local diff, 2026-07-30) — the frame centres this 822px band on
   the 720px CONTENT COLUMN (band centre 774 ~= column centre 776), not on the page/article.
   Before F-A's left-align fix (above) the whole body group was itself mis-centred, so the
   band's plain `margin: 0 auto` centring on the article coincidentally read as "wrong but
   in the same wrong place" as the body; now that the rail state is left-aligned per the
   frame, the band needs an explicit offset to land on the (now correctly positioned)
   content column rather than the article's own centre. Only applies once the two-column
   grid is active (>=1200px, the same breakpoint the rail/no-rail split already uses) —
   below that everything stacks to one column and the existing auto-centring is already
   correct. On a NO-RAIL post the widened single column is ITSELF centred on the article
   (preserved by the .tbl-single__body--no-rail override above), so the pre-existing
   auto-centring already lands the band correctly there too — only the rail state needs the
   explicit offset. content-column centre (article-relative) = rail-w + rail-gutter +
   measure/2 = 257+100+360 = 717px; band left = 717 - (822/2) = 306px. */
@media (min-width: 1200px) {
	.tbl-single__article:has(.tbl-single__body:not(.tbl-single__body--no-rail)) .tbl-single__newsletter {
		margin-left: calc(var(--tbl-rail-w) + var(--tbl-rail-gutter) + (var(--tbl-measure) / 2) - 411px);
		margin-right: auto;
	}
}

.tbl-single__newsletter-heading {
	margin: 0 0 12px;
	font-family: var(--header-font);
	font-size: 38px;
	line-height: 44px;
	color: var(--Black);
}

.tbl-single__newsletter-sub {
	margin: 0 0 32px;
	font-family: var(--body-font);
	font-size: 20px;
	line-height: 30px;
	letter-spacing: 0.2px;
	color: color-mix(in srgb, var(--Black) 70%, transparent);
}

/* --- The email input -- through the variable channel ONLY -----------------
   forms.css (assets/forms.css, loaded layer(layout) by the F-1-fenced
   style.css:19) sets every input property via var(--input-*) with
   !important. An unlayered !important is the WEAKEST !important tier, so it
   CANNOT win here. The custom-property declarations forms.css reads are
   NORMAL, though -- an unlayered redeclaration of those variables wins
   normally, and forms.css's !important rule then resolves against the new
   value. Identical technique to theme.css:24210's footer block (read-only,
   F-1 fenced -- not edited here). */
.tbl-single__newsletter-form {
	--input-background-color: transparent;
	--input-color: var(--Neutral-100);
	--input-font-style: normal;
	/* Pastel #84 — the measured 38px is a 1600 value and was unconditional, so
	   "Your email here" filled the whole 295px field at 375. Now linear from
	   20px at 375 to the measured 38px at 1600, written as an explicit clamp
	   rather than an --h* token: those divide a length by a length inside
	   clamp() and collapse to 16px in Firefox (variables.css:898-928). Line
	   height follows as the frame's own 44/38 ratio so the two cannot drift. */
	--input-font-size: clamp(20px, calc(14.49px + 1.47vw), 38px);
	--input-line-height: 1.158;
	--input-letter-spacing: normal;
	--input-font-weight: 410;
	--input-text-transform: none;
	--input-padding: 15px 90px 15px 0;  /* right pad clears the 77px submit pill */
	--input-border-width: 0;            /* see the shorthand note below */
	--input-border-color: transparent;
	--input-border-radius: 0;
	/* THE UNDERLINE. The 11-UI-SPEC.md recommendation of a four-value
	   (top/right/bottom/left) border-width shorthand for this variable is
	   WRONG and would be silently dropped: forms.css consumes it inside the
	   `border` SHORTHAND (border: var(--input-border-width) solid
	   var(--input-border-color)), which takes exactly ONE width value, so a
	   four-value list makes the whole declaration invalid -- the same
	   failure mode as prefixing --button-easing. An inset box-shadow is the
	   correct channel; forms.css reads --input-box-shadow with !important
	   too. */
	--input-box-shadow: inset 0 -1px 0 0 var(--Black);
	--focus-input-background-color: transparent;
	--focus-input-border-color: transparent;
	--focus-input-color: var(--Neutral-100);
	--focus-input-box-shadow: inset 0 -2px 0 0 var(--Black);
}

/* Placeholder investigation (forms.css:176-183, INSIDE the same
   layer(layout) !important block as the input rule above -- its
   &::-webkit-input-placeholder/:-moz-placeholder/&::-moz-placeholder/
   &:-ms-input-placeholder selector already sets `color` and `font-weight`
   from the SAME --input-color/--input-font-weight variables re-scoped
   above, both !important. A plain `color:` override here would therefore
   be entirely dead code -- it would lose to that layered !important, and
   the measured placeholder tint is nothing but --input-color at partial
   alpha anyway, not a different hue, so no separate colour channel is
   needed. Only two things are free to override with a plain unlayered
   rule: `opacity` (forms.css:181 sets `opacity: 1.0`, NORMAL priority, no
   !important -- unlayered normal always beats layered normal regardless of
   specificity) and `font-size` (NOT set by forms.css's placeholder block
   at all -- it is pinned to 14px by the FORMIDABLE PLUGIN'S OWN stylesheet,
   `.with_frm_style input::placeholder { font-size: var(--field-font-size)
   }`, unlayered and NORMAL -- confirmed by the identical documented
   precedent at theme-formidable.css:1687-1695, a DIFFERENT file from
   forms.css). Setting opacity: 0.25 against --input-color: var(--Neutral-
   100) (#0f0f0f) yields rgba(15,15,15,0.25) -- exactly the measured
   placeholder colour, same hex as the typed-text colour, just fainter. */
.tbl-single__newsletter-form input[type="email"]::placeholder {
	opacity: 0.25;
	font-size: inherit;
	line-height: inherit;
}

/* --- The submit pill -- forms.css has no !important rule for buttons, so
   plain unlayered overrides are correct here. --------------------------- */

.tbl-single__newsletter-form form {
	position: relative;
}

/* R2-5a continued — root-caused against the FOOTER's own already-working copy of this
   exact fix (theme.css:24364-24372, read-only reference, not edited here): Formidable's
   `.frm_form_field { position: relative; }` (theme-formidable.css:333) is the submit
   button's OWN field wrapper — one level closer than <form> in the ancestor chain — so it
   was silently HIJACKING the absolute-positioning containing block. `right:0; top:8px`
   was resolving against THAT small field-wrapper box (which itself sits ~102px into the
   form's own normal flow, below the input's row), not the 644x130 <form> box, landing the
   button far below-right of where intended even after the position/size fixes above.
   The footer's own comment states the fix plainly: "static hands the offset back to the
   form." Confirmed live against the footer's actual DOM (position:static there) before
   applying the identical technique here. */
/* Round 5 — Ben: "Decrease the space between the form field and the text below it."
   Root cause, found live (per-element box audit, not guessed): Formidable's default
   .frm_form_field carries its OWN padding-bottom:8px and margin-bottom:20px, and there
   are TWO such fields in this form (email's own wrapper, and the submit's own wrapper) --
   8+20+8+20 = the exact 56px of invisible trailing space the coordinator measured, none
   of it from the absolutely-positioned submit button itself (which contributes nothing to
   normal-flow height). Collapsing both to zero here -- not a negative margin on the
   privacy paragraph, which would silently break the moment the field markup, submit
   height or Formidable's own defaults change.
   First deploy of this fix did NOT close the gap (confirmed live, form height unchanged
   at 130). Root-caused via CDP getMatchedStylesForNode: the SAME element carries a SECOND
   Formidable class, `.form-field` (not `.frm_form_field` -- confirmed in the live
   className: "frm_form_field form-field  frm_required_field frm_none_container"), and
   `.frm_style_formidable-style.with_frm_style .form-field { margin-bottom:
   var(--field-margin) }` (specificity 0,3,0, --field-margin: 20px) out-specifies this
   rule's 0,2,0. That rule carries no !important, so !important here wins regardless of
   which of the element's two classes the opponent selector happens to key off. */
.tbl-single__newsletter-form .frm_form_field {
	position: static;
	margin-bottom: 0 !important;
	padding-bottom: 0 !important;
}

/* Round-3 residual chase — root-caused live via `wrapper.offsetParent`: even with the
   .frm_form_field fix above, the wrapper's REAL positioned ancestor turned out to be
   <fieldset> (also position:relative, un-neutralised), not <form>. Formidable's default
   fieldset carries its own padding/min-width/border that render it ~20px WIDER than the
   644px <form> (664px measured) — right:0 against that wider box landed the button ~10px
   past the form's own right edge. The footer's already-shipped CSS documents and fixes the
   identical fieldset-widening problem verbatim ("Formidable's fieldset carries default
   padding/min-width that widens the row", theme.css:24319-24325, read-only reference — not
   edited here): reset the fieldset's own box to match the form exactly rather than also
   forcing it position:static (either works; matching the footer's own proven technique). */
.tbl-single__newsletter-form fieldset {
	min-width: 0;
	margin: 0;
	padding: 0;
	border: 0;
}

/* Round-3 residual chase, continued — the fieldset reset above did NOT close the gap
   (still measured 20px wider than the form, 10px per side). Root cause, found via the
   SAME already-documented footer precedent (theme.css:24439-24442, read-only reference —
   "Form overflowed .tb-footer__content on the left: Formidable's .frm_form_fields carries
   margin:0 -10px"): Formidable's OWN `.frm_form_fields` (the fieldset's parent) ships a
   negative -10px inline margin, which is what was making the fieldset — a normal
   width:auto block child filling ITS parent — 20px wider than <form> even after the
   fieldset's own margin/padding/border were all confirmed zero. Same fix as the footer. */
.tbl-single__newsletter-form .frm_form_fields {
	margin-inline: 0;
}

/* Defect #2 (11-QA.md / orchestrator figma-local diff) — position the WRAPPER Formidable
   itself renders around the submit button (`.frm_submit`, confirmed live via
   `button.offsetParent` — it is `.frm_submit.frm_flex`, already `position:relative` by
   Formidable's own CSS, NOT the <form>), not the button directly. The button's own
   `position:absolute` resolves against that nearest positioned ancestor, so our old
   `right:0; bottom:0` on the BUTTON was silently being out-argued by Formidable's own
   left/top on the same over-constrained box, landing it below-left of the input instead of
   at its right end. Positioning the wrapper instead is the exact technique the footer's
   own copy of this same form already uses successfully (theme.css:24265-24271, read-only
   reference — not edited here, F-1 fence respected). Frame node `10416:5953` sits at
   x=567/y=8 within the 644-wide form — flush at the form's right edge (567+77=644),
   8px from the top. */
.tbl-single__newsletter-form .frm_submit {
	/* R2-5a (11-ROUND2-SPEC.md) — root-caused live (CDP getMatchedStylesForNode):
	   theme-formidable.css:963's `.frm_forms form .frm_submit { position: relative; ... }`
	   is specificity (0,2,1) -- one type-selector ("form") ahead of this rule's (0,2,0) --
	   so it was winning even though it carries no !important, leaving the wrapper
	   in-flow (this is why the button rendered ~110px below the input instead of
	   overlapping its right end: "inline with the field" per Ben). That opponent rule
	   is itself non-important, so a plain !important on the contested property wins
	   outright -- the footer's own working copy of this pattern (theme.css:24265) instead
	   wins on SPECIFICITY alone via its `footer#footer` ID, an advantage this band,
	   scoped only by class, does not have. */
	position: absolute !important;
	right: 0;
	top: 8px;
	margin: 0;
	padding: 0 !important;   /* theme-formidable.css:963 also sets padding:0 8px 0 !important;
	                             an 8px inset here would throw off the frame's flush-right
	                             x=567+77=644 alignment */
}

/* Defect #2 continued — size/shape. Formidable's own
   `.frm_style_formidable-style.with_frm_style ... .frm_submit button` rule (specificity
   0,3,1, no !important of its own) sets width/height/border-radius/padding/margin/
   box-shadow as plain literals and outranks this selector (0,2,0) on exactly those
   properties — confirmed live (rendered 60x39/8px-radius instead of 77x86/9999px). Its
   rule carries no !important, so a plain !important here wins outright; this is NOT the
   layered-vs-unlayered trap (forms.css's `layer(layout)` !important is a different file
   this button never touches) — it is a plain specificity fight against a plain,
   non-important, unlayered rule. */
/* Round 4 — Ben: "the submit button... needs to be just the arrow." Confirmed against
   node 10416:5953's own dev context: the 77x86 rounded-[9999px] box carries NO background
   class at all -- it's a transparent hit area, not a visible pill. A screenshot of the
   form node (10416:5949) confirms it live: a bare black arrow over the underline input,
   no fill/circle/border behind it. Removed the purple fill, the hover fill, and the
   border; kept the 77x86 transparent hit area (border-radius included, matching the
   frame's own transparent-but-rounded box) so the click/tap target stays generous. */
.tbl-single__newsletter .frm_button_submit {
	width: 77px !important;
	height: 86px !important;
	padding: 0 !important;
	margin: 0 !important;
	border: 0 !important;                /* was plain `border: 0` (no !important) -- computed
	                                         a stray 1px solid border before this fix, now
	                                         confirmed gone */
	border-radius: 9999px !important;    /* shape of the transparent hit area only, matches
	                                         the frame's own div -- nothing visible to round */
	box-shadow: none !important;
	background-color: transparent !important; /* was var(--Primary-90) -- "just the arrow" */
	/* Root-caused live after the first deploy of this fix: a plain `color` here still computed
	   white, not black. Formidable's own base button rule
	   (`.frm_style_formidable-style.with_frm_style ... .frm_submit button`, specificity
	   0,3,1) sets `color: #ffffff` -- the SAME specificity gap already !important'd above for
	   width/height/padding/margin/border-radius/box-shadow, just never surfaced for `color`
	   before now because both sides previously agreed on white. Changing the intended value
	   exposed the pre-existing loss. Its rule carries no !important, so !important here wins
	   outright, same resolution as every other property on this selector. */
	color: var(--Black) !important;      /* was var(--White); SVG path is fill="currentColor"
	                                         with no .btn class in the way (confirmed live),
	                                         so this cascades straight to the glyph */
	font-size: 0;                        /* the label stays in the DOM for a11y */
	cursor: pointer;
	transition: var(--button-easing);    /* BARE */
}

.tbl-single__newsletter .frm_button_submit:hover,
.tbl-single__newsletter .frm_button_submit:focus-visible {
	/* Was a filled-circle hover/focus swap (--Primary-Alt-50-Base background). With no fill
	   at all now, the affordance moves to the glyph's OWN colour instead of a background.
	   !important here too -- the base rule's `color` is now !important (see above), and a
	   plain declaration can never win against an !important one regardless of pseudo-class,
	   so without this the hover/focus colour-shift would be silently inert. */
	color: var(--Primary-90) !important;
}

/* Defect #3 (11-QA.md) — Formidable's own
   `.frm_style_formidable-style.with_frm_style ... .frm_submit button:focus` (specificity
   0,4,1, sets `outline: none`) outranked this `:focus-visible` rule (0,3,0) even without
   !important, purely on class-count. Same non-important-vs-!important resolution as above:
   its rule has no !important, so this plain !important wins outright, restoring a visible
   keyboard focus ring (WCAG 2.4.7). Confirmed live before this fix: outline-style computed
   "none" after a real keyboard Tab, settled past the transition. */
.tbl-single__newsletter .frm_button_submit:focus-visible {
	outline: 2px solid var(--Primary-90) !important;
	outline-offset: 3px !important;
}

/* The arrow SVG is injected by functions.php's form-ID-keyed filter and
   keeps its footer-named class site-wide -- DO NOT rename it. Icons inside
   a .btn are forced to color:inherit !important by theme.css, but this
   button carries no .btn class, and the SVG's path already uses
   fill="currentColor", so the button's own `color` cascades in cleanly. */
.tbl-single__newsletter .tb-footer__submit-arrow {
	display: block;
	width: 26px;
	height: 21px;
	margin: 0 auto;
}

/* --- Privacy line ----------------------------------------------------------
   Confirmed against the live single.php markup (D-13's section): the
   privacy sentence renders as a plain <p class="tbl-single__newsletter-
   privacy"> with an inline <a>, gated on get_privacy_policy_url() -- NOT a
   Formidable .frm_description (the live shared form emits no privacy copy
   of its own). Styling that one real selector only. */
/* Round 5 — target ~14px from the INPUT's own underline to this block's top (frame
   arithmetic: form top 5740 + input height 102.86 = border-bottom at 5842.86; privacy
   top-[5890.5px] with -translate-y-1/2 and h-[67px] = top 5857; 5857-5842.86 = 14.14px).
   Anchored to the input, NOT the form container's own bottom edge -- the frame's privacy
   block actually sits inside the form's own trailing area (form bottom 5873 > privacy top
   5857), so measuring against the form's box would be wrong. With the .frm_form_field
   trailing space collapsed to zero above, .tbl-single__newsletter-form's own bottom now
   lands exactly at the input's bottom, so this plain margin-top IS the input-to-privacy
   gap directly. */
.tbl-single__newsletter-privacy {
	margin: 14px 0 0;
	font-family: var(--body-font);
	font-size: 14px;
	line-height: 23.55px;
	letter-spacing: 0.14px;
	color: rgba(77, 81, 83, 0.5);       /* FLAG: bespoke, no clean token */
}

.tbl-single__newsletter-privacy a {
	color: inherit;
	text-decoration-line: underline;
	text-decoration-color: currentColor;
}

/* Admin-only "not connected" notice (single.php's FORMIDABLE_ACTIVE guard) --
   small, muted, obviously a dev notice, never seen by a real visitor once
   the form ID is set. */
.tbl-single__newsletter-flag {
	margin: 12px 0 0;
	font-size: 13px;
	font-style: italic;
	color: color-mix(in srgb, var(--Black) 50%, transparent);
}

/* --- Pastel #102 / #108 — dark-purple recolour ----------------------------
   Chris re-coloured this band in Figma after seeing it live (node 10416:5945,
   the Group 28 that composes frame 10416:5946); Ben's #108 points at the same
   node and asks for the whole component to match it. Both comments target the
   same element, so this is one change.

   Every value below is read off that group's own fills, not eyeballed:
     box            #200648   (10416:5946)
     heading        #B390E7   (10416:5947)
     sub / input /  #FFFFFF   (10416:5948 / :5952 / :5954)
       arrow
     input rule     #624292   (10416:5950 border-bottom, 1px)
     privacy        #6E5B8C   (10416:5955)
     badge halo     #B390E7 @ 20%  (11340:4879, 70x70 at -35, +61)
     badge mark     #AD8EE0 on #200648 (11340:4875, 73.65 at -37, +59)

   Placed AFTER the rules it supersedes rather than editing each in place: the
   selectors are identical, so this wins on order alone, and the whole
   treatment reverts by deleting one block. The two !important declarations
   are not new tiers — the base rules they override are already !important
   (see the Formidable specificity notes above), so a plain value here would
   be inert.

   Focus states have no Figma counterpart on the dark ground, so they are
   derived: the underline and outline both go to #B390E7, the heading colour,
   which is the only value in the set that clears the box at speed. */
.tbl-single__newsletter {
	background-color: #200648;
}

.tbl-single__newsletter-heading {
	color: #B390E7;
}

.tbl-single__newsletter-sub {
	color: #FFFFFF;
}

.tbl-single__newsletter-form {
	--input-color: #FFFFFF;
	--input-box-shadow: inset 0 -1px 0 0 #624292;
	--focus-input-color: #FFFFFF;
	--focus-input-box-shadow: inset 0 -2px 0 0 #B390E7;
}

/* The frame draws "Your email here" at solid white — the same value as typed
   text — so the 0.25 dim that made the old near-black placeholder read as grey
   is dropped rather than re-tuned. */
.tbl-single__newsletter-form input[type="email"]::placeholder {
	opacity: 1;
}

.tbl-single__newsletter .frm_button_submit {
	color: #FFFFFF !important;
}

.tbl-single__newsletter .frm_button_submit:hover,
.tbl-single__newsletter .frm_button_submit:focus-visible {
	color: #B390E7 !important;
}

.tbl-single__newsletter .frm_button_submit:focus-visible {
	outline-color: #B390E7 !important;
}

.tbl-single__newsletter-privacy {
	color: #6E5B8C;
	/* Frame lineHeightPx is 20, not the 23.55 measured in an earlier round. */
	line-height: 20px;
	/* Input's border-bottom sits at 310.86; the privacy line box (14/20 centred in the
	   frame's 29-tall text box) starts at 329.5. */
	margin-top: 18.64px;
}

/* --- Geometry ------------------------------------------------------------
   Every offset below is the frame's own, converted from Figma's fixed-height
   text boxes to CSS line boxes. Figma centres a line box inside a text frame
   whose height is not the line height, so the frame's y is never the CSS y:
   e.g. the heading's 38/44 type sits in a 37-tall box at y=77, which puts the
   real 44px line box at 77-(44-37)/2 = 73.5. Every number here is derived that
   way, and they sum back to the frame's own 431px box height exactly:

     73.5 pad-top + 44 heading + 30 sub + 60.5 gap + 102.86 input
     + 18.64 gap + 20 privacy + 81.5 pad-bottom = 431

   Left inset 87 and right inset 59 are literal (heading block 10416:5947/:5948
   is 676 wide at x=87). The form and the privacy line are narrower — 644, the
   frame's 10416:5949 — so they carry their own max-width rather than the box
   carrying two different right insets. */
.tbl-single__newsletter {
	padding: 73.5px 59px 81.5px 87px;
}

/* --header-letter-spacing is -0.01em, which computed -0.38px here; the frame's
   heading token is flat 0. The two text nodes are an itemSpacing-0 vertical
   auto-layout, so there is no gap between them at all. */
.tbl-single__newsletter-heading {
	letter-spacing: 0;
	margin-bottom: 0;
}

.tbl-single__newsletter-sub {
	margin-bottom: 60.5px;
}

.tbl-single__newsletter-form,
.tbl-single__newsletter-privacy {
	max-width: 644px;
}

/* Formidable ships `padding: 0 8px 0 !important` on the field wrapper — the same
   rule the .frm_submit block above already neutralises for the button. It was
   holding the input 8px in from the frame's x=87 and 16px short of its 644. */
.tbl-single__newsletter-form .frm_form_field {
	padding-inline: 0 !important;
}

/* Input box 10416:5950 is 102.86 tall; its placeholder line box runs 236.5-280.5,
   giving 28.5 above and 29.4 below. The bottom pad carries an extra 0.96: the
   underline is an inset box-shadow, not a border, so it adds no height of its
   own, while the frame's 102.86 counts its own 1px INSIDE stroke. Right inset is the frame's own Container
   (10416:5951, 590.21 of 643.54) = 53.33 — which is exactly where the arrow glyph
   starts, so text can never run under it. Was 15/90/15/0. */
.tbl-single__newsletter-form {
	--input-padding: 28.5px 53.33px 30.36px 0;
}

/* --- The badge -----------------------------------------------------------
   A translucent halo with the TBL play mark over it, hanging off the left edge.
   Both are decorative and are pseudo-elements, so neither needs markup.

   SIZES ARE FROM THE FRAME'S RENDER, NOT ITS BOUNDING BOXES. Group 31 reports
   an absoluteBoundingBox of 73.65, but a 1:1 PNG export of Group 28 measures
   the drawn mark at 53px — which is also exactly its SVG viewBox. The earlier
   73.65 made the badge ~40% too big and, because it then exceeded the 70px
   Ellipse 4194, hid the halo entirely and made it look absent from the design.
   Measured off that export, both circles are concentric on the box's own left
   edge at y=95.5: mark 53, halo 70. The halo is the bigger of the two, which is
   why it reads as a ring behind the mark.

   The halo is clipped to the box's left edge.

   The cut is done on the halo itself, not with overflow on the box: the mark
   has to stay UNclipped (it is a sibling of the box in the frame and hangs 37px
   outside it), and at this y the box's left edge is a straight line well below
   the 10px corner radius, so a plain inset() reproduces it exactly. Painting
   order gives the stack for free — ::before draws under ::after — and neither
   ever reaches the x=87 content edge, so nothing needs a z-index.

   Gated to >=1200px, the same breakpoint the rail/no-rail offset above uses.
   Below it the band grows toward full-bleed and the mark's -37px would push the
   document wider than the viewport. */
@media (min-width: 1200px) {
	.tbl-single__newsletter {
		position: relative;   /* containing block for the two pseudo-elements below */
	}

	.tbl-single__newsletter::before,
	.tbl-single__newsletter::after {
		content: "";
		position: absolute;
		pointer-events: none;
	}

	/* Ellipse 4194 — 70px on the box's left edge, everything left of it cut away. */
	.tbl-single__newsletter::before {
		left: -35px;
		top: 61px;
		width: 70px;
		height: 70px;
		border-radius: 50%;
		background: rgba(179, 144, 231, 0.2);
		clip-path: inset(0 0 0 35px);
	}

	/* Group 31 — 53px, concentric with the halo, unclipped. */
	.tbl-single__newsletter::after {
		left: -26.5px;
		top: 69.5px;
		width: 53px;
		height: 53px;
		background: no-repeat center / contain url("data:image/svg+xml,%3Csvg width='53' height='53' viewBox='0 0 53 53' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.0033457 26.3917C0.0746242 11.505 12.3245 -0.25388 26.958 0.00475211C43.5634 0.305087 55.6799 15.5455 52.4798 31.6852C49.9079 44.6588 38.0042 53.8042 24.7801 52.9445C11.5736 52.0809 0.807964 41.4809 0.0519494 28.0427L-9.47567e-05 26.3953L0.0033457 26.3917Z' fill='%23AD8EE0'/%3E%3Cpath d='M21.4448 29.2332C22.4728 27.6165 22.3944 25.655 21.351 24.109L15.3961 14.8956L41.816 26.0801L16.0524 38.682L21.4414 29.2368L21.4448 29.2332Z' fill='%23200648'/%3E%3C/svg%3E");
	}
}

/* JUDGMENT -- no Figma below 1600. The pill drops from an absolute overlay
   to a static full-width control below the input; the input's right
   clearance (sized for the overlay) is no longer needed once the pill
   moves below it. */
@media (max-width: 599px) {
	.tbl-single__newsletter {
		padding: 40px 24px;
	}

	.tbl-single__newsletter-form {
		--input-padding: 15px 0;
	}

	.tbl-single__newsletter-form form {
		position: static;
	}

	.tbl-single__newsletter .frm_button_submit {
		position: static;
		width: 100%;
		height: 58px;
		margin-top: 16px;
	}
}

/* ---------------------------------------------------------------------------
   Pastel #121 / #122 / #123 — Chris's mobile pass, 2026-08-28. All three are
   375-only observations, so all three live behind the same 599 tier rather than
   changing values the desktop frame actually specifies.
   -------------------------------------------------------------------------- */
@media (max-width: 599px) {

	/* #123: "reduce font size on blog copy by a point size OR increase the
	   heading size by a point or so, so there's more differentiation."
	   Body was 20/30 against an h2 of 26/30.1 — a 6px step with IDENTICAL line
	   boxes, which is why the two read as one texture. Taking the body to 18/28
	   is Chris's first option and opens the step to 8px with the leading pulling
	   in the same direction. The heading is left alone: it is the size the frame
	   specifies. */
	.tbl-single__content {
		font-size: 18px;
		/* A ratio, not a literal: theme.css's `p { line-height: var(--body-line-height) }`
		   matches the paragraph directly and beats an inherited value off this
		   container, so a literal here would apply to every child EXCEPT the running
		   copy it was written for. 1.5 is what --body-line-height resolves to, so
		   container and paragraphs agree at 27px either way. */
		line-height: 1.5;
	}

	/* #121: "arrow is a tiny bit lower than it should be". The 8px offset is read
	   off the desktop frame (node 10416:5953, y=8 in a 644-wide form), where the
	   input is taller than it is at 375 — so the same literal drops the 86px hit
	   box low against the narrower field. Zeroing it lifts the arrow those 8px so
	   it sits over the junction of the placeholder and its rule rather than under
	   the rule. */
	.tbl-single__newsletter-form .frm_submit {
		top: 0;
	}

	/* #161 (Chris, 2026-09-01): "arrow is not centered vertically". #121 above
	   zeroed the frame's 8px top offset, but the 86px hit box itself is also a
	   desktop number -- it was sized against the frame's 102.86px input, where
	   a top-aligned 86 lands the glyph within 1px of the field's centre. At
	   this tier the input is 53.4 (15px padding either side of a 23.4px line
	   box), so the same box hung the arrow ~16px low. Matching the box to the
	   input re-centres the glyph and still leaves a 77x53 tap target, well
	   over the 44px minimum. */
	.tbl-single__newsletter .frm_button_submit {
		height: 53.4px !important;
	}

	/* #122: "reduce space here". 118px under the related grid plus the article's
	   own 70px bottom padding left 188px of dead space above the footer, against
	   only 48px (--tbl-band-gap at this width) above the band. Matching the
	   bottom gap to the top one restores Ben's "same amount of space" rule, which
	   the 118px literal had only ever satisfied at desktop. */
	/* (0,2,0): the base rule below is (0,1,0) and declared LATER in this file, so a
	   plain `.tbl-single__related` here ties and loses on source order. Measured
	   live — the article's padding dropped to 40 and this one stayed at 118. */
	.tbl-single__article .tbl-single__related {
		margin-bottom: var(--tbl-band-gap);
	}

	.tbl-single__article {
		padding-bottom: 40px;
	}
}

/* --- Related Reading (D-14..D-17) ------------------------------------------
   The grid can never be empty (D-14 tops up to 4) and can never contain an
   external link (D-15 makes it posts-only), so there is no empty state and
   no external-link variant to style. */
/* R2-6/R2-9 (11-ROUND2-SPEC.md) — DELIBERATE DEVIATION FROM THE FRAME, on Ben's explicit
   instruction. The frame's band-bottom -> related-hairline gap measures 280px, and the
   LIVE page already matched it exactly (orchestrator's live figma-local diff confirmed
   both). Ben called this "way too much margin" regardless; do not "fix" this back to
   280px. Re-checked after R2-5 added the missing privacy paragraph (which grows the band
   toward the frame's 431px height and was itself eating into part of the perceived void) —
   64px chosen as a generous but no-longer-excessive gap, the same "3xl / page-level
   spacing" token already documented in 11-UI-SPEC.md's spacing scale. */
.tbl-single__related {
	/* R3-5 (round 3, supersedes R2-6) — kept EQUAL to the newsletter band's own top gap via
	   the same --tbl-band-gap token (Ben: "the same amount of space" above the band as
	   below it). See --tbl-band-gap's comment for the full deviation history. */
	margin-top: var(--tbl-band-gap);
	/* was: margin-top: 64px (R2-6) / margin-top: 280px (frame-exact, Round 1) */
	margin-bottom: 118px;               /* measured: grid bottom 6920 -> footer 7038 */
}

/* R3-6 (round 3) — root-caused live via CDP getMatchedStylesForNode: an UNLAYERED, !important
   `hr { margin: 10px auto !important; }` in theme.css (read-only reference, not edited —
   theme.css is F-1 fenced) was beating this rule's plain `margin: 0` on margin-bottom
   specifically, adding an extra 10px Ben correctly spotted as "margin underneath
   related-rule." That opponent is unlayered (not forms.css/reset.css's layer(layout)) and
   carries its own !important, so a plain !important here can't lose to the layer-inversion
   trap — it is a same-tier fight, and this selector's higher specificity (.related-rule,
   0,1,0) beats the opponent's bare `hr` (0,0,1) once both are !important. */
.tbl-single__related-rule {
	margin: 0 !important;
	/* R3-6 residual, closed out — the coordinator found the real remaining cause: this
	   element was computing `display: inline-block` (from a global rule elsewhere, not one
	   this file adds), and an inline-block sits on its parent's text baseline, so the
	   parent's line box adds phantom leading above AND below it — inflating both the
	   newsletter-to-rule gap (140 measured as 157) and the rule-to-head gap (20 measured as
	   26) by the same phantom-leading mechanism. An <hr> should be block-level anyway. */
	display: block;
	border: 0;
	border-top: 1px solid var(--tbl-hairline);
}

.tbl-single__related-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 24px;
	margin-top: 20px;                   /* R3-6 (round 3): Ben's explicit 20px, was 21px
	                                        (frame: hairline 6257 -> heading 6278) — combined
	                                        with the !important fix above (which removes a
	                                        rogue +10px from theme.css's global hr rule),
	                                        this is now the FULL rule-to-head gap, not just
	                                        a component of a larger, wrong total */
}

/* R2-7 (11-ROUND2-SPEC.md, frame 10416:5964) — was set to the H6 token (16px, Roman Disp);
   the frame reads as "Category - Small" instead, the SAME Book Disp recipe R2-2's byline
   uses (fonts.css weight 300, aliased to the Roman file until Ben licenses the real Book
   weight — matches the related-card chip/read-time precedent from 11-08). */
.tbl-single__related-heading {
	margin: 0;
	font-family: var(--body-font);
	font-weight: 300;
	font-size: 10px;
	line-height: 17.78px;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--Black);
}

/* Always-drawn underline, colour fades in -- same recipe as
   .tbl-single__back. text-decoration-line is DISCRETE and cannot tween;
   never toggle it. */
/* R2-8 (11-ROUND2-SPEC.md, frame 10435:7638) — the " +" suffix is a single.php markup
   change (Ben's explicit instruction); this rule corrects the type values only (was
   var(--body-font-size-smaller), ~13-14px). Decoration mechanism unchanged from this
   project's established "always drawn, colour fades in on interaction" convention (already
   confirmed rendering `underline` per the spec's own note) — decoration-thickness/
   skip-ink updated to the frame's literal values. */
.tbl-single__related-viewall {
	color: var(--Black);
	font-family: var(--body-font);
	font-size: 20px;
	line-height: 30px;
	letter-spacing: 0.16px;
	text-decoration-line: underline;
	text-decoration-color: transparent;
	text-decoration-style: solid;
	text-decoration-thickness: from-font;
	text-decoration-skip-ink: none;
	transition: var(--button-easing);   /* BARE */
}

.tbl-single__related-viewall:hover,
.tbl-single__related-viewall:focus-visible {
	text-decoration-color: currentColor;
}

.tbl-single__related-viewall:focus-visible {
	outline: 2px solid var(--Primary-Alt-50-Base);
	outline-offset: 2px;
}

.tbl-single__related-grid {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: 25px;                          /* measured: 340x4 + 25x3 = 1435 */
	width: calc(100% - 58px);           /* measured 29px inset inside the hairline, both sides */
	/* R3-7 (round 3) — Ben: 90px top margin on desktop, scaled down for smaller screens
	   (was 40px, not itemised against the frame). 6vw reaches 96px (clamped to the 90px
	   ceiling) at the 1600px frame width; scales down to the 32px floor ("xl / layout gaps"
	   token, 11-UI-SPEC.md's spacing scale) by ~1067px viewport width. Judgement curve, no
	   frame coverage below 1600 — consistent with this file's existing clamp() pattern. */
	margin: clamp(32px, 6vw, 90px) auto 0;
}

/* --- Related card (D-16, related-card.php) --------------------------------- */

.tbl-single__related-card {
	display: flex;
	flex-direction: column;
	gap: 14px;                          /* measured card internal gap */
	min-width: 0;
}

.tbl-single__related-card-media {
	position: relative;
	display: block;
	overflow: hidden;
	border-radius: 10px;
	aspect-ratio: 340 / 427;            /* enforce the box in CSS -- the registered crop is
	                                       progressive enhancement, not the layout mechanism */
}

.tbl-single__related-card-img,
.tbl-single__related-card-fallback {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	transition: var(--button-easing);   /* BARE -- the hover scale lives here */
	will-change: transform;
}

/* No featured image -- 10 of 165 posts (6%), so this is live, not
   theoretical. Adapted from the feed's own fallback card
   (tbl-insights-feed.css:324-345). */
.tbl-single__related-card-fallback {
	display: flex;
	align-items: flex-end;
	padding: 20px;
	background-color: var(--Primary-90);
}

.tbl-single__related-card-fallback-title {
	font-family: var(--header-font);
	font-size: 18px;
	line-height: 22px;
	color: var(--White);
}

.tbl-single__related-card-chip {
	position: absolute;
	left: 17px;
	top: 18px;
	padding: 4px 15px;
	border-radius: 30px;
	background-color: var(--nav-bolt-color);   /* #b390e7 -- Accent B, chip + active TOC circle only */
	color: var(--Black);
	font-family: var(--body-font);
	/* "Book Disp" -- CORRECTED from the plan's literal 405. fonts.css:149-160
	   registers Book Disp at font-weight: 300 (currently aliased to the
	   Roman Disp file until Ben licenses the real Book weight); 405 matches
	   NO registered face and browser font-matching would snap it to the
	   nearer 400 (Roman) slot, silently losing the Book Disp identity even
	   after Ben licenses it. There is also no shipped precedent to copy: as
	   investigated, tbl-insights-row__readtime (tbl-insights-feed.css:413-
	   415) does NOT apply the Category-Small/Book-Disp treatment at all --
	   it only overrides `color`, inheriting the parent .tbl-insights-row__
	   meta's Paragraph-Small (15px/25px). Still flagged for QA against the
	   frame. */
	font-weight: 300;                   /* FLAG: see comment above -- corrected literal, not a shipped precedent */
	font-size: 10px;
	line-height: 17.78px;
	letter-spacing: 2px;                /* Category-Small IS px */
	text-transform: uppercase;
}

.tbl-single__related-card-title {
	margin: 0;
	font-family: var(--header-font);
	font-size: 20px;
	line-height: 24px;
	color: var(--Black);
	/* UNCLAMPED. Card 3 in the frame is 3 lines at 67px tall -- the card
	   must grow with it. Never add a fixed-height title box, a clipped
	   overflow, or any line-limiting property. */
}

.tbl-single__related-card-title-link {
	color: inherit;
	text-decoration-line: underline;
	text-decoration-color: transparent;
	text-decoration-thickness: 1px;
	text-underline-offset: 3px;
	transition: var(--button-easing);   /* BARE */
}

.tbl-single__related-card-readtime {
	margin: 0;
	font-family: var(--body-font);
	font-weight: 300;                   /* Book Disp -- see the chip's comment above */
	font-size: 10px;
	line-height: 17.78px;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--Black);
}

/* JUDGMENT -- no Figma below 1600 */
@media (max-width: 899px) {
	.tbl-single__related-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* JUDGMENT -- no Figma below 1600 */
@media (max-width: 599px) {
	.tbl-single__related-grid {
		grid-template-columns: repeat(1, minmax(0, 1fr));
		width: 100%;
	}
}

/* --- Related card motion: hover ------------------------------------------
   Copied from the SHIPPED CSS (tbl-insights-feed.css:853-883), not from
   10-INSIGHTS-BUILD-SPEC.md's stale property-prefixed §7 example. */

.tbl-single__related-card:hover .tbl-single__related-card-img,
.tbl-single__related-card:focus-within .tbl-single__related-card-img,
.tbl-single__related-card:hover .tbl-single__related-card-fallback,
.tbl-single__related-card:focus-within .tbl-single__related-card-fallback {
	transform: scale(1.03);
}

/* Defect #6 (11-QA.md) — 11-05's original @media(prefers-reduced-motion:reduce) block
   (earlier in this file) already sets this EXACT selector list to `transform: none`, but
   it shares IDENTICAL specificity with the hover rule immediately above, and that hover
   rule comes LATER in source order — at an equal-specificity tie the later rule always
   wins, so the reduced-motion override was being silently defeated regardless of the
   user's OS-level preference. Confirmed live: with reduced-motion emulated and a real
   hover, the image still computed a scale transform. Re-asserting the override here, AFTER
   the hover rule it needs to beat (rather than editing 11-05's original block, which stays
   correct as written — it is simply not LAST anymore), fixes the tie without duplicating
   or restructuring that block. */
@media (prefers-reduced-motion: reduce) {
	.tbl-single__related-card:hover .tbl-single__related-card-img,
	.tbl-single__related-card:focus-within .tbl-single__related-card-img,
	.tbl-single__related-card:hover .tbl-single__related-card-fallback,
	.tbl-single__related-card:focus-within .tbl-single__related-card-fallback {
		transform: none;
	}
}

.tbl-single__related-card-title-link:hover,
.tbl-single__related-card-title-link:focus-visible {
	text-decoration-color: currentColor;
}

.tbl-single__related-card-title-link:focus-visible,
.tbl-single__related-card-media:focus-visible {
	outline: 2px solid var(--Primary-Alt-50-Base);
	outline-offset: 2px;
}

/* --- Related card motion: entrance -----------------------------------------
   DELIBERATE DEVIATION from 11-UI-SPEC.md's "IntersectionObserver stagger
   reveal" recipe. That recipe arms itself with a server-rendered opacity:0
   class which JS then releases. That releaser JS lives in the tbl-insights-
   feed BB MODULE's own script (tbl-insights-feed.js), which is not enqueued
   on a singular post -- and 11-06's tbl-single.js deliberately does not arm
   a reveal observer either (see 11-06-SUMMARY.md). Porting the arming half
   without its releaser would leave every related card permanently
   invisible -- the exact failure the feed module's own comments warn
   against.

   Used instead: a CSS-only, visible-by-default entrance. It is visible in
   EVERY browser with no support for it (the @supports gate simply never
   matches, so the base display-block state stands) and only ENHANCES where
   scroll-driven animation exists -- there is no state in which content can
   be hidden by this rule. */
@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {
		.tbl-single__related-card {
			animation: tbl-single-card-in linear both;
			animation-timeline: view();
			animation-range: entry 0% entry 45%;
		}
	}
}

@keyframes tbl-single-card-in {
	from { opacity: 0; }
	to   { opacity: 1; }
}

/* related-card.php emits style="--i:<n>" (mirroring row.php's pattern) but
   this recipe leaves it UNUSED, deliberately -- a scroll view-timeline
   staggers naturally by scroll position on its own, so no transition-delay
   built from --i is added here. A delay with no arming class to release
   would do nothing; this is a documented choice, not an oversight. */

/* --- No-JS safety net ------------------------------------------------------
   With JavaScript disabled: the rail renders every link (server-rendered
   in single.php), the first TOC item keeps its server-rendered .is-active,
   and every related card is visible by default -- nothing in this file
   depends on JS to become visible (see the entrance comment above). The one
   real gap is the <1200px accordion: with no click handler to flip
   aria-expanded, the panel has no interactive way to open. That is an
   ACCEPTED, conscious trade-off (D-23) -- the same links already exist in
   the body's own headings -- not an accident, and this block still makes
   the panel content reachable anyway rather than leaning on that
   fallback. */
@media (scripting: none) {
	@media (max-width: 1199px) {
		.tbl-single__toc-trigger {
			display: none;
		}

		.tbl-single__toc-panel {
			display: block;
		}
	}
}
