/*
 * Tabbed Timeline — "A Legacy of Innovation", About Us.
 *
 * Glob-loaded as `ftm-pattern-tabbed-timeline` from patterns/{name}/{name}.css
 * (build-knowledge §1). The filename MUST match the directory; a style.css
 * here would be silently ignored.
 *
 * Breakpoints are written at the token values with the token named in a
 * comment — CSS vars cannot be used in @media conditions. Mobile-first,
 * min-width only.
 *
 * ── TWO DIFFERENT BREAKPOINTS DO TWO DIFFERENT JOBS HERE ────────────────────
 *
 *   breakpoint/md (48rem)  accordion -> TABS, and the tablet metrics.
 *                          Must stay in step with TABS_MQ in
 *                          tabbed-timeline.js.
 *   breakpoint/lg (64rem)  the entry's text+image 2-up. Nothing to do with the
 *                          mode switch.
 *
 * On services-tabs these were the same number and the distinction never came
 * up. Here they are 48 and 64 and conflating them would put the 2-up at
 * tablet, where all three boards stack it.
 *
 * ── The 48rem boundary is PER-PATTERN, not a theme-wide constant ────────────
 *
 * services-tabs switches at 64rem; this pattern switches at 48rem. That is
 * decided by each pattern's own frames and a future pass MUST NOT normalise
 * the two:
 *
 *   services-tabs   tablet board draws ACCORDION rows.  Six labels = 757px of
 *                   ink against a 688 content box at 768. Cannot fit.
 *   tabbed-timeline tablet board draws TABS.            Three labels = 519px
 *                   against the same 688. Fits with 169 to spare.
 *
 * The visible consequence is intended: at 768 the About page shows tabs while
 * the homepage shows an accordion. Full account in DECISIONS.md, 2026-07-30.
 *
 * Colour tokens are `--wp--custom--color--<family>--<step>`, double dash.
 * There is no preset step scale: `--wp--preset--color--primary-100` resolves
 * to nothing.
 *
 * ── Specificity ─────────────────────────────────────────────────────────────
 *
 * EVERY rule here is written at (0,2,0)+ by scoping through .tc-tabbed-timeline.
 * `:root :where(.is-layout-flow) > *` and `:root :where(p)` both land at
 * (0,1,0) — :where() contributes nothing — so a bare single-class selector
 * TIES and then loses on order, because WP's global styles are emitted after
 * the glob-loaded pattern CSS. Beat it on specificity, never on enqueue order.
 *
 * `!important` appears exactly TWICE, both on has-*-font-size overrides where
 * the emitted fluid preset is measurably wrong at tablet (J7), and nowhere
 * else. body-small and label-large are clean matches and are deliberately left
 * without one, so an editor's font-size choice still applies.
 *
 * ── Section padding: NOT declared here, and the standing mechanism is inert ─
 *
 * `.page-section { margin-block-start: var(--ftps-space-section-rhythm) }`
 * (core-blocks.css:147) resolves its variable correctly but the rule is
 * (0,1,0), ties with WP's `:root :where(.is-layout-constrained) > *`, and
 * loses on order — so every .page-section on the site computes a flat 24px
 * rather than the intended 48->96 clamp. Theme-wide, affecting all shipped
 * patterns identically, and NOT corrected here. On the maintainer report.
 *
 * @package WordPress
 * @subpackage FTM_Theme
 */

.tc-tabbed-timeline {
	/*
	 * Third per-pattern definition of the chevron (F22). There is no shared
	 * --tc-icon-* layer: the contract layer that would supply one is dead
	 * (build-knowledge §6/§17), so every pattern needing a glyph declares its
	 * own on its own root. The Figma export of `Arrows / Chevron Down`
	 * (542:31243), byte-identical to every other copy of the slug (2026-09-28,
	 * docs/reports/icon-audit.md §7). Painted through `mask` from
	 * background-color, so its own fill colour is irrelevant.
	 *
	 * NO arrow icon here. This section has no CTA of any kind (FT-L), unlike
	 * every other pattern in the family.
	 */
	--tc-icon-chevron-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5.29289 8.29289C5.65338 7.93241 6.22061 7.90468 6.6129 8.2097L6.70711 8.29289L12 13.585L17.2929 8.29289C17.6534 7.93241 18.2206 7.90468 18.6129 8.2097L18.7071 8.29289C19.0676 8.65338 19.0953 9.22061 18.7903 9.6129L18.7071 9.70711L12.7071 15.7071C12.3466 16.0676 11.7794 16.0953 11.3871 15.7903L11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z'/%3E%3C/svg%3E");

	/*
	 * label-large's LINE-HEIGHT is hoisted; its SIZE is not, and the asymmetry
	 * is the point. The size is a flat 15 at every width (`fluid: false`, and
	 * all three boards agree), so it is a clean match that needs no override
	 * at all. The leading is NOT flat — Label/Large has no line-height of its
	 * own and borrows `body large/line height`, which steps 20 -> 24 at md. So
	 * one property moves and the other does not.
	 *
	 * Hoisted because TWO elements must carry byte-identical leading: the <p>
	 * row label (which owns the preset class) and the <button> the script
	 * builds inside it (which owns no preset class and, per J15, declares its
	 * type explicitly rather than taking `font: inherit`).
	 */
	--tc-label-large-line-height: 1.3333; /* 20/15 */
}

/* ── Header ────────────────────────────────────────────────────────────────
   Flow layout injects margins a flex column must not inherit; zeroed at
   (0,2,0) so WP's `:root :where(.is-layout-flow) > *` at (0,1,0) cannot win
   on order. */

.tc-tabbed-timeline .tc-tabbed-timeline__header {
	display: flex;
	flex-direction: column;
	gap: var( --wp--preset--spacing--xsmall, 0.5rem ); /* 8 — mobile eyebrow->title */
}

.tc-tabbed-timeline .tc-tabbed-timeline__header > *,
.tc-tabbed-timeline .tc-tabbed-timeline__widget > *,
.tc-tabbed-timeline .tc-tabbed-timeline__item > *,
.tc-tabbed-timeline .tc-tabbed-timeline__panel > *,
.tc-tabbed-timeline .tc-tabbed-timeline__entry > *,
.tc-tabbed-timeline .tc-tabbed-timeline__entry-content > *,
.tc-tabbed-timeline .tc-tabbed-timeline__entry-text > * {
	margin-block: 0;
}

/* Header -> widget is 24 at EVERY width — the frames draw title -> tab row at
   24 on all three boards, and the tab row travels with the widget because the
   script inserts the tablist as the widget's first child. */
.tc-tabbed-timeline .tc-tabbed-timeline__widget {
	margin-block-start: var( --wp--preset--spacing--large, 1.5rem ); /* 24 */
}

/*
 * The shared .tc-eyebrow kit paints its 20x4 bar from `currentcolor`, which on
 * this white band is --black, not primary-600 — and the Figma variable on the
 * bar is literally `color/primary/600`.
 *
 * SIXTH consumer to pin it to primary-600: brand-grid, bulleted-list,
 * heading-banner, media-basic, services-tabs, this. (product-grid also
 * overrides the bar, but to secondary-600 — a genuine per-section variation
 * that would still be wanted after a kit fix, not another copy of this
 * workaround.) The fix belongs in the kit; escalated from a designer item to
 * the maintainer report.
 */
.tc-tabbed-timeline .tc-eyebrow::before {
	background-color: var( --wp--custom--color--primary--600, #123b9e );
}

/* ── Type stand-ins ────────────────────────────────────────────────────────
 *
 * The theme's typeStyles block is inert site-wide, so headings render at
 * weight 400 without these, and `elements.h3` resolves to heading-medium's 41
 * where the frames want title-large's 27. Root cause and the reason a
 * naming-only fix would not help: docs/reports/h2-diagnosis.md.
 *
 * Each clamp runs between its own preset's bounds and reaches max at
 * breakpoint/md — the designer's flat-at-tablet curve, which the emitted fluid
 * presets cannot express because WP's ramp runs 320 -> 1600 (F3 / H3 / F23).
 * Checked at 768 specifically, which is where the emitted preset is wrong:
 * heading-medium would be 29.3 against the frames' 41, and title-large 21.8
 * against 27.
 *
 * Only TWO of this pattern's five type roles need one. body-small, label-large
 * and label-medium are clean matches and take no !important, which is exactly
 * the case J7 preserves an editor's font-size choice for.
 *
 * NO text-transform anywhere. Every heading, era label and year binds CSS
 * `capitalize` in Figma over sentence-case stored strings (FT-K); the media
 * family already resolved this by dropping the transform.
 */

.tc-tabbed-timeline .tc-tabbed-timeline__title,
.tc-tabbed-timeline .tc-tabbed-timeline__entry-year {
	font-family: var( --wp--preset--font-family--display, inherit );
	font-weight: 700;
	letter-spacing: -0.02em; /* bound at -2% on both */
}

.tc-tabbed-timeline .has-heading-medium-font-size {
	/* 23 @320 -> 41 @768, then flat: 23 + 18 * (100vw - 320px) / 448.
	   Byte-identical to services-tabs.css:155, the media family's
	   --tc-heading-medium-size and brand-grid's copy. This is the SIXTH copy
	   in the theme — F23, raised again on the maintainer report. */
	font-size: clamp(1.4375rem, 0.634rem + 4.018vw, 2.5625rem) !important;
	line-height: 1.2174; /* 28/23 — mobile title is ONE line here, unlike J11 */
}

.tc-tabbed-timeline .has-title-large-font-size {
	/* 19 @320 -> 27 @768, then flat: 19 + 8 * (100vw - 320px) / 448.
	   Byte-identical to media-basic.css:73. SECOND copy in the theme. */
	font-size: clamp(1.1875rem, 0.83rem + 1.786vw, 1.6875rem) !important;
	line-height: 1.2632; /* 24/19 */
}

/* body-small is a CLEAN MATCH — `fluid: false`, emitted 1rem flat, and the
   frames draw 16/24 at all three widths. No clamp and no !important. */
.tc-tabbed-timeline .tc-tabbed-timeline__entry-body {
	line-height: 1.5; /* 24/16 */
	max-inline-size: 37.5rem; /* 600 — the measure cap, FT-H */
}

/* The frames separate the 1975 entry's two paragraphs with an empty 24px line
   rather than a paragraph break. The flex column's own gap is 8, so 16 more
   restores the drawn 24 — and it keeps working if an editor adds a third. */
.tc-tabbed-timeline
	.tc-tabbed-timeline__entry-body
	+ .tc-tabbed-timeline__entry-body {
	margin-block-start: var( --wp--preset--spacing--normal, 1rem ); /* 16 */
}

/* label-large is a CLEAN MATCH on size (flat 15 everywhere) — so no font-size
   and no !important. Only the leading is declared, because only the leading
   moves. */
.tc-tabbed-timeline .has-label-large-font-size {
	line-height: var( --tc-label-large-line-height );
}

/* ── Rows (accordion chrome) ───────────────────────────────────────────────
 *
 * NOT DRAWN ON ANY BOARD (FT-Q). All three boards draw the tab row and an
 * expanded timeline, so every value here is borrowed from services-tabs, which
 * is what the Zoho line points at by name ("just like the section on the
 * homepage").
 *
 * Two deliberate departures from that borrowing:
 *
 *   - The row title stays label-large 15, not services-tabs' title-small
 *     18/20. All three boards bind these labels to Label/Large, mobile
 *     included, so the frames do have something to say about the size even
 *     though they have nothing to say about the row. Raised as a designer item.
 *   - NO first-row top rule. services-tabs ships one because its own mobile
 *     board draws it; there is no such evidence here.
 *
 * Figma's 56 INCLUDES the 1px rule, and modern-normalize sets box-sizing:
 * border-box globally, so the border eats the padding: the CSS is `16px 15px`,
 * not a flat 16. The control is 24 tall (the chevron, not the 20px label), so
 * 16 + 24 + 15 + 1 = 56.
 *
 * There is no tablet variant of these numbers: the boundary is 48rem, so the
 * accordion never renders above 767px and services-tabs' 24/23 tablet row is
 * dead here.
 *
 * Left ungated by `.is-accordion` deliberately — with JS off neither mode
 * class is present, and row rules are the honest presentation of three stacked
 * sections. Zeroed under `.is-tabs` in the md block below.
 */

.tc-tabbed-timeline .tc-tabbed-timeline__item {
	border-block-end: 1px solid var( --wp--custom--color--neutral--200, #dee2e6 );
	padding-block: 1rem 0.9375rem; /* 16 / 15 */
}

/* ── The control ───────────────────────────────────────────────────────────
 *
 * Built by tabbed-timeline.js; it does not exist without JS, which is the
 * point — a server-rendered <button> that does nothing is the failure mode
 * being avoided.
 *
 * The Elementor kit resets below are LOAD-BEARING, not redundancy:
 * `.elementor-kit-6637 button { background-color:#002A8C; ... }` is (0,1,1)
 * and beats a bare `.tc-tabbed-timeline__control` at (0,1,0). Restated here at
 * (0,2,0). build-knowledge §5.
 *
 * Type is declared EXPLICITLY rather than with `font: inherit` (J15). Note the
 * family: Label/Large resolves `body large/font` = INTER, not Rethink Sans.
 * That is the reverse of services-tabs, whose row title needed the display
 * family and would not have inherited it. Here a <p> inherits
 * styles.typography.fontFamily -> Inter and is accidentally right; the button
 * still declares it, because "accidentally right" is what the footer's family
 * bug was for eight days.
 */

.tc-tabbed-timeline .tc-tabbed-timeline__control {
	/* Elementor kit resets. */
	background-color: transparent;
	border: 0;
	border-radius: 0;
	color: inherit;
	padding: 0;
	text-align: start;
	text-transform: none;
	align-items: center;
	cursor: pointer;
	display: flex;
	gap: var( --wp--preset--spacing--xsmall, 0.5rem ); /* 8 */
	justify-content: space-between;
	inline-size: 100%;

	/* Label/Large — Inter, 15 flat, per the frames. */
	font-family: var( --wp--preset--font-family--body, inherit );
	font-size: var( --wp--preset--font-size--label-large, 0.9375rem );
	font-weight: 700;
	letter-spacing: normal; /* frame binds 0.2 = 0.002em; normal at 15px */
	line-height: var( --tc-label-large-line-height );
}

/*
 * Chevron. Down when collapsed, rotating 180 when open. Accordion mode only —
 * the tab strip draws no glyph on any board.
 *
 * The flex floor is not decoration: this is an icon in a flex row beside text
 * the marketing team can lengthen at will, and without it a long label shrinks
 * the chevron instead of wrapping. The drawer's back chip declared
 * `inline-size` and still shrank to 24.91px, which is why BOTH flex-shrink and
 * min-inline-size are here.
 */
.tc-tabbed-timeline.is-accordion .tc-tabbed-timeline__control::after {
	background-color: currentcolor;
	block-size: 1.5rem; /* 24 */
	content: "";
	flex-shrink: 0;
	inline-size: 1.5rem;
	-webkit-mask: var( --tc-icon-chevron-down ) no-repeat center / contain;
	mask: var( --tc-icon-chevron-down ) no-repeat center / contain;
	min-inline-size: 1.5rem;
	transition: transform 0.2s var( --wp--custom--easing--standard, ease );
}

.tc-tabbed-timeline.is-accordion
	.tc-tabbed-timeline__control[aria-expanded="true"]::after {
	transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
	.tc-tabbed-timeline.is-accordion .tc-tabbed-timeline__control::after {
		transition: none;
	}
}

/* ── Panels, and the rail ──────────────────────────────────────────────────
 *
 * `hidden` alone will NOT collapse these. WP emits `.is-layout-flex { display:
 * flex }` at (0,1,0), and this file's own panel rule is (0,2,0) — both beat
 * the `hidden` attribute's UA `display: none`, so the panel would stay
 * visible. footer.css:461-463 hit this exactly. Restated at (0,3,0).
 *
 * THE PANEL IS ALSO THE RAIL'S CONTAINING BLOCK, which makes that restatement
 * do double duty: a display:none element generates no box and therefore no
 * ::before either, so a hidden panel cannot leave an orphan rail floating over
 * the section. Verified live rather than assumed — see the build report.
 */

.tc-tabbed-timeline .tc-tabbed-timeline__panel {
	display: flex;
	flex-direction: column;
	margin-block-start: 1rem; /* 16 — accordion row header -> content */
	position: relative;
}

.tc-tabbed-timeline .tc-tabbed-timeline__panel[hidden] {
	display: none;
}

/*
 * The rail. Four values, each from a measurement rather than a choice:
 *
 *   top     12  = the FIRST DOT'S CENTRE. Desktop draws 214 against a dot
 *                 centre of 215 and tablet 208 against 208; mobile draws 176
 *                 against 152 and is drift (FT-D), so 2 of 3 boards agree and
 *                 the third is not reproduced.
 *   bottom   0  = the LAST ENTRY'S CONTENT BOTTOM, not the last dot. All three
 *                 rectangles run the full height of the last entry, image
 *                 included.
 *   start 11.5  = the true centre of the 24px dot column, splitting the
 *                 boards' own disagreement (desktop draws x=12, tablet and
 *                 mobile x=11).
 *   colour      = primary/100. Only the desktop rectangle is bound to it;
 *                 tablet and mobile render Figma's default #D9D9D9 (FT-C). The
 *                 bound one is the intent, and it matches the dot's own disc.
 *
 * It sits BEHIND the dots and each dot's halo punches a 30px gap in it —
 * confirmed by scan, tablet declaring 3079px of rail and only 2867 rendering,
 * a 212 shortfall against 7 dots x 30 = 210.
 */
.tc-tabbed-timeline .tc-tabbed-timeline__panel::before {
	background-color: var( --wp--custom--color--primary--100, #dce5f6 );
	content: "";
	inline-size: 1px;
	inset-block: 0.75rem 0; /* 12 -> first dot centre; 0 -> last entry bottom */
	inset-inline-start: calc( 0.75rem - 0.5px ); /* 11.5 — centre of the 24 column */
	position: absolute;
}

/* ── Entries ───────────────────────────────────────────────────────────────
 *
 * Three nested flex containers because the frames run three nested gaps and a
 * flex container expresses one gap each:
 *
 *   __entry          dot -> content      8 / 40 / 80
 *   __entry-content  text -> image      24 stacked, 104 as a 2-up at lg
 *   __entry-text     year -> body        8 at every width
 */

.tc-tabbed-timeline .tc-tabbed-timeline__entry {
	align-items: flex-start;
	display: flex;
	gap: var( --wp--preset--spacing--xsmall, 0.5rem ); /* 8 — mobile dot->content */
	padding-block: var( --wp--preset--spacing--large, 1.5rem ); /* 24 */
}

/* The rail starts at the first dot's centre and ends at the last entry's
   content, so neither end carries padding. */
.tc-tabbed-timeline .tc-tabbed-timeline__entry:first-child {
	padding-block-start: 0;
}

.tc-tabbed-timeline .tc-tabbed-timeline__entry:last-child {
	padding-block-end: 0;
}

/*
 * The dot — one pseudo-element carrying all three of the frame's rings.
 *
 * The exported SVG is a 28px-diameter circle filled primary/100 with a 4px
 * white stroke centred on that path (so the stroke covers diameter 26 -> 30),
 * plus a 10px primary/600 core, rendered into a 24px layout box. Reproduced
 * with a border for the disc and a box-shadow for the halo: box-sizing is
 * border-box globally, so 24 - 2*7 leaves the core at exactly 10, and the
 * shadow takes the outer edge to exactly 30. The disc itself ships at 24
 * against the frame's 26 — 1px per side, inside tolerance, and the alternative
 * is a negative margin for no visible gain.
 *
 * A pseudo-element and not a block: the dot is chrome, and a block would put a
 * meaningless deletable item in List View and make deleting an entry a
 * two-step job.
 *
 * THE HALO IS --ftm-scheme-bg, NEVER #fff AND NEVER A PRESET (build-knowledge
 * §3). Its entire job is to punch a hole in the rail, so it has to track the
 * band rather than a literal.
 *
 * `position: relative` IS LOAD-BEARING AND IS NOT DECORATION. The rail is
 * `position: absolute`, so it paints in the positioned step; an in-flow dot
 * paints in an earlier step and the rail lands ON TOP of it. Caught by looking
 * at an 8x render, where a 1px primary/100 line ran straight through the navy
 * core and the halo punched nothing. Making the dot positioned too puts both
 * in the same step, where tree order decides — and the panel's ::before is the
 * panel's first child box, so every dot is later and paints above.
 *
 * NO z-index here, deliberately. The sticky tablist carries `z-index: 1`, and
 * any positive z-index on the dot would raise it above the stuck strip and
 * paint dots over it while scrolling. Auto keeps the dot above the rail and
 * below the tablist, which is the whole of what is needed.
 *
 * flex-shrink AND min-inline-size, per the drawer's back chip.
 */
.tc-tabbed-timeline .tc-tabbed-timeline__entry::before {
	background-color: var( --wp--custom--color--primary--600, #123b9e );
	block-size: 1.5rem; /* 24 */
	border: 7px solid var( --wp--custom--color--primary--100, #dce5f6 );
	border-radius: 50%;
	box-shadow: 0 0 0 3px var( --ftm-scheme-bg, #fff ); /* 24 -> 30 halo */
	content: "";
	flex: 0 0 auto;
	inline-size: 1.5rem;
	min-inline-size: 1.5rem;
	position: relative;
}

.tc-tabbed-timeline .tc-tabbed-timeline__entry-content {
	display: flex;
	flex: 1 1 0;
	flex-direction: column;
	gap: var( --wp--preset--spacing--large, 1.5rem ); /* 24 — text -> media */
	min-inline-size: 0;
}

.tc-tabbed-timeline .tc-tabbed-timeline__entry-text {
	display: flex;
	flex-direction: column;
	gap: var( --wp--preset--spacing--xsmall, 0.5rem ); /* 8 — year -> body */
}

/*
 * Image aspect is NATURAL, not pinned. Entry 1's image is 596x290 (2.055:1) on
 * every board while entries 3, 5 and 7 are ~3:2, so the ratio follows the
 * source asset rather than a crop. This is a deliberate departure from
 * services-tabs, which pins `aspect-ratio: 3 / 2`. Radius unified on 4 as the
 * family did.
 */
.tc-tabbed-timeline .tc-tabbed-timeline__entry-media {
	margin: 0;
}

.tc-tabbed-timeline .tc-tabbed-timeline__entry-media img {
	block-size: auto;
	border-radius: 4px;

	/*
	 * ⚠ REMOVES CORE'S 1500px PLACEHOLDER. WITHOUT THIS LINE EVERY LAZY ENTRY
	 * PHOTO RENDERS 596x1500 AND THE SECTION IS 4,700px TALL.
	 *
	 * Found on /about/ 2026-09-07, the first page to give this pattern
	 * ATTACHMENT-backed images. It is the same core behaviour
	 * testimonial-callout.css already documents at length, arriving here by the
	 * same route and needing a different answer:
	 *
	 *   img:is([sizes="auto" i], [sizes^="auto," i]) {
	 *     contain: size !important;
	 *     contain-intrinsic-size: 300px 150px;
	 *   }
	 *
	 * is in the HTML spec's UA sheet, and WordPress overrides the second
	 * declaration with `3000px 1500px` (wp-includes/media.php,
	 * `wp_enqueue_img_auto_sizes_contain_css_fix`, since 6.9). `contain: size`
	 * means the element is sized WITHOUT REGARD TO ITS OWN CONTENT, so
	 * `block-size: auto` above resolves to a flat 1500px and the width/height
	 * attributes' ratio never gets a say.
	 *
	 * ⚠ IT IS NOT A LOADING STATE AND IT DOES NOT RESOLVE ON A REAL SERVER —
	 * `contain: size` is `!important` in the UA sheet and applies whether or
	 * not the image has decoded. And it only appears on LAZY images, i.e. never
	 * on the first two or three: on /about/ the 1971 photo was correct at
	 * 596x290 while 1975, 1979 and 1987 were all 596x1500, which is what makes
	 * it read as an image problem rather than a CSS one.
	 *
	 * WHY THE PATTERN SPEC IS BLIND TO IT: `/pattern-library/` renders this
	 * pattern's seeded `<img src="picsum…">` — raw markup with no attachment,
	 * so WordPress adds neither `loading="lazy"` nor `sizes="auto"` and the UA
	 * rule never matches. Same shape as the testimonial-callout defect, where
	 * the library page was correct and /engineering/ was 1500.
	 *
	 * ⚠ `contain-intrinsic-size: none` RATHER THAN A NUMBER, AND THAT IS THE
	 * WHOLE POINT. `.tc-media` and services-tabs solve this by pinning an
	 * explicit `aspect-ratio: 3 / 2`, which works because an explicit ratio
	 * out-ranks the contained natural size. This pattern CANNOT do that: the
	 * note above records that entry 1 is 2.055:1 on every board while entries
	 * 3, 5 and 7 are ~3:2, so the ratio deliberately follows the source asset.
	 * `none` says the element has NO natural size, which lets the width/height
	 * attributes' own `aspect-ratio: auto W/H` resolve — measured on /about/ at
	 * 1920: 290 / 398 / 398 / 397 against the frames' 290 / 398 / 398 / 397.
	 *
	 * It removes core's guess instead of replacing it with ours — the same
	 * argument testimonial-callout.css makes for taking the image out of the
	 * row rather than overriding `contain-intrinsic-size` with a number. A
	 * number would re-break the moment an editor swapped in a photo of a
	 * different shape; this cannot, because it has no shape of its own.
	 *
	 * NOT `!important`: the UA sheet marks only `contain` important, and
	 * WordPress's override of `contain-intrinsic-size` is a plain author
	 * declaration this rule already out-specifies. Verified both ways.
	 */
	contain-intrinsic-size: none;
	display: block;
	inline-size: 100%;
}

/* ── Focus rings — these SUPERSEDE the frames, which draw none ─────────────
 *
 * Same standing as the footer's ring and the header's designer-approved hover.
 * With nothing declared, Chromium's UA ring is `outline: auto 1px
 * rgb(16,16,16)`; the footer's live keyboard pass found that near-black on a
 * navy band, i.e. invisible on the only surface it appeared on.
 *
 * secondary-700 measures 5.93:1 on white, comfortably past the 3:1 WCAG 2.4.11
 * needs for a focus indicator.
 *
 * NOT covered, deliberately: the tabpanel, which carries tabindex="0" in tabs
 * mode and so is focusable. It takes Chromium's UA ring, which is near-black
 * on this WHITE band and therefore legible — the footer's problem was the navy
 * surface, not the ring itself. Same call as services-tabs. The entry image is
 * not covered either, because it holds nothing focusable.
 */

.tc-tabbed-timeline .tc-tabbed-timeline__control:focus-visible {
	outline: 2px solid var( --wp--custom--color--secondary--700, #0369a1 );
	outline-offset: 2px;
}

/* ── breakpoint/md — the mode switch, and the tablet metrics ───────────────
 *
 * 48rem, NOT 64rem, and NOT a re-derivation of services-tabs' boundary. Three
 * independent signals: this pattern's tablet board draws TABS where the
 * services board draws accordion rows; the three labels measure 519px of ink
 * against a 688 content box at 768, where the services strip is 757 and cannot
 * fit; and the Zoho line says "on mobile", not "on tablet". Frames are
 * canonical (docs/design/README.md), so the frames win. Ruled 2026-07-30.
 *
 * The visual half is here; the ARIA half is in tabbed-timeline.js, and the two
 * are joined only by `.is-tabs` / `.is-accordion`, which the script sets. With
 * JS off NEITHER class is present, so none of the .is-tabs rules below apply
 * and the section stays three visible panels with row rules — the honest no-JS
 * path.
 */

@media (min-width: 48rem) { /* breakpoint/md */

	.tc-tabbed-timeline {
		--tc-label-large-line-height: 1.6; /* 24/15 */
	}

	.tc-tabbed-timeline .tc-tabbed-timeline__header {
		gap: var( --wp--preset--spacing--normal, 1rem ); /* 16 */
	}

	.tc-tabbed-timeline .has-heading-medium-font-size {
		line-height: 1.1707; /* 48/41 */
	}

	.tc-tabbed-timeline .has-title-large-font-size {
		line-height: 1.1852; /* 32/27 */
	}

	.tc-tabbed-timeline .tc-tabbed-timeline__entry {
		gap: 2.5rem; /* 40 — documented literal; nothing resolves to it */
		padding-block: 2.5rem; /* 40 */
	}

	/*
	 * The tab strip.
	 *
	 * No per-tab padding and no indicator bar — the row pads itself and the
	 * active state is carried entirely by colour and weight. That is the
	 * largest single chrome difference from services-tabs, whose tabs pad
	 * `16 8 14` and draw a 2px secondary/600 underline. The pixel scan of all
	 * three exports found exactly one rule in this row and no third rule
	 * anywhere in the section, so there is no seam model to resolve here.
	 *
	 * flex-wrap is graceful degradation, not a live case: 519px of ink clears
	 * a 980px content box at 1024 with 461 to spare. It is declared because
	 * the labels are marketing-editable and the frames are silent on a long
	 * one.
	 */
	.tc-tabbed-timeline.is-tabs .tc-tabbed-timeline__tablist {
		align-items: center;
		border-block-end: 1px solid var( --wp--custom--color--neutral--200, #dee2e6 );
		display: flex;
		flex-wrap: wrap;
		gap: 2.5rem; /* 40 */
		margin-block-end: 3rem; /* 48 — tablist -> first entry, K10 */
		padding-block-end: var( --wp--preset--spacing--normal, 1rem ); /* 16 */

		/*
		 * Sticky is a real designer setting on all three boards, not an
		 * artifact — and it is the theme's FIRST use of it, so two things are
		 * verified live rather than assumed: that no ancestor in the
		 * .page-section -> group -> widget chain has `overflow` other than
		 * visible (sticky dies silently inside one), and that the background
		 * actually reads over a scrolling entry.
		 *
		 * The background and the z-index are NOT in the frames and are
		 * necessary additions, same standing as the focus rings: a transparent
		 * sticky bar is not a design the frames expressed. z-index is required
		 * specifically because the rail is `position: absolute` and later in
		 * the DOM, so without it the rail paints over the stuck strip.
		 *
		 * THE OFFSET IS THE ADMIN BAR, AND ONLY THE ADMIN BAR. This used to be
		 * a flat `0`, on the reasoning that "the theme has no sticky or fixed
		 * header — verified, nothing anywhere else in the theme uses either".
		 * That is true of the THEME and it left out the one fixed header the
		 * theme does not own. WordPress's front-end admin bar is `position:
		 * fixed` across the whole tabs range, so for every LOGGED-IN reader the
		 * strip stuck itself entirely underneath the admin bar and no tablist
		 * was visible while scrolling at all — an editor previewing their own
		 * page saw none of it.
		 *
		 * `--wp-admin--admin-bar--height` is core's own token, declared on
		 * `html` in `wp-includes/css/admin-bar.css`: 32px, and 46px below 782px
		 * where the bar is taller for touch. That stylesheet is enqueued only
		 * when the bar renders, so the fallback IS the logged-out case and
		 * needs no separate rule, and using the token rather than a literal 32
		 * picks up the 46 without a second breakpoint. The fallback is written
		 * unitless because stylelint's `length-zero-no-unit` rejects `0px`; a
		 * bare `0` is a valid length here and is never fed to a `calc()`.
		 *
		 * THE SCROLL MARGIN BELOW IS DELIBERATELY NOT CHANGED. The same core
		 * stylesheet also sets `scroll-padding-top:
		 * var( --wp-admin--admin-bar--height )` on `html`, so an anchor jump
		 * already clears the admin bar; composing the token into the margin
		 * too would double-count it and drop the target 32/46px BELOW the bar.
		 * Measured logged-in with the margin left alone: the panel lands at
		 * 72.63 against a bar bottom of 73.0 at 1440, and 86.59 against 87.0 at
		 * 768 — flush, at both.
		 */
		background-color: var( --ftm-scheme-bg, #fff );
		inset-block-start: var( --wp-admin--admin-bar--height, 0 );
		position: sticky;
		z-index: 1;

		/*
		 * G35 (Gary, 2026-09-14): 16 of top padding so the labels do not touch
		 * the viewport edge while the bar is stuck. Measured before: control
		 * box at y=0 when pinned, ink against the top of the window.
		 *
		 * The matching NEGATIVE margin keeps the resting layout where the
		 * frames put it. The header→tablist gap is a board value (16, K-series)
		 * and Gary's item is about the stuck state only; without the margin
		 * the whole strip would sit 16 lower on every board while unpinned.
		 * The padding paints the scheme background, so once pinned the 16 is
		 * real headroom over the scrolling entry — which is the ask. The bar
		 * is now 57 tall (16 + 24 + 16 + 1) and the scroll margin below
		 * follows it.
		 */
		margin-block-start: -1rem;
		padding-block-start: var( --wp--preset--spacing--normal, 1rem ); /* 16 */
	}

	/*
	 * THE COST OF THE STICKY BAR, PAID BY THE THINGS IT COVERS.
	 *
	 * The widget mints real ids — `tc-tabbed-timeline-0-panel-0`,
	 * `-title` — for `aria-controls` and `aria-labelledby`, and an id is an
	 * anchor target whether or not it was minted to be one. Navigating to one
	 * lands its top edge at the viewport top, which is where the stuck tablist
	 * is. Measured at 1440 before this rule: target top -0.4, tablist bottom
	 * 41 — the first 41.4px of the panel arrives already hidden, and the reader
	 * sees the middle of a paragraph with no indication anything is above it.
	 *
	 * TABS STATE ONLY, which is why it lives inside this block rather than
	 * beside the panel rules. Below 48rem the widget is an accordion, nothing
	 * is sticky, and a scroll margin there would push the target 41px BELOW the
	 * viewport top for no reason.
	 *
	 * THE CONTROLS ARE DELIBERATELY NOT INCLUDED. They are inside the sticky
	 * bar, so they are never the thing it covers, and the widget moves focus
	 * between them with the keyboard — giving those a scroll margin would make
	 * arrow-key tab switching jump the page.
	 *
	 * 57px, composed from the parts rather than hardcoded, so it tracks the
	 * bar: the G35 16 `padding-block-start` + the control's 24px line box +
	 * this bar's own 16 `padding-block-end` + its 1px bottom border. Was 41
	 * before G35 added the top padding (measured 41.00 at both 768 and 1440 —
	 * the control type does not scale between them, so the bar is one height
	 * across the whole tabs state). It would grow if the tablist ever WRAPPED,
	 * which these three labels do not: they measure 519px against the 688
	 * content box at 768, the same headroom the JS header records.
	 *
	 * IT IS THE BAR'S HEIGHT AND NOTHING ELSE — do not add the admin bar to it.
	 * The bar now sticks at `var( --wp-admin--admin-bar--height, 0 )`, and the
	 * obvious follow-through is to compose the same token in here. It would be
	 * wrong: `wp-includes/css/admin-bar.css` already sets `scroll-padding-top:
	 * var( --wp-admin--admin-bar--height )` on `html`, so the browser subtracts
	 * the admin bar from a fragment scroll before this margin is applied.
	 * Adding it twice puts the target 32px (46 at 768) BELOW the bar it was
	 * meant to clear. Measured logged-in with this left at 41: panel top 72.63
	 * against bar bottom 73.0 at 1440, 86.59 against 87.0 at 768. Asserted in
	 * tabbed-timeline-verify.spec.ts, "the scroll margin is NOT double-counted
	 * against core scroll-padding".
	 */
	.tc-tabbed-timeline.is-tabs
		:is( .tc-tabbed-timeline__panel, .tc-tabbed-timeline__title ) {
		scroll-margin-block-start: calc(
			var( --wp--preset--spacing--normal, 1rem ) + 1.5rem +
				var( --wp--preset--spacing--normal, 1rem ) + 1px
		);
	}

	.tc-tabbed-timeline.is-tabs .tc-tabbed-timeline__control {
		font-weight: 500;
		inline-size: auto;
		justify-content: center;
	}

	/*
	 * The active tab. J16 CLOSED 2026-08-06 in its third application too: the
	 * desktop board draws secondary/600 (sampled #0284C7) and that binding is
	 * now what ships. The 700 remap was for the OLD 600's 4.09:1 on white, an
	 * AA fail at 15px bold — bold does not reach "large text", which needs
	 * 18.66px. secondary-600 is #057DB9 as of 2026-08-06 and measures 4.53:1,
	 * which clears the 4.5:1 this label needs.
	 *
	 * The tablet and mobile boards draw a different hue again, primary/500
	 * (#4175C9, 4.55:1) — the boards disagreeing about an active-state colour
	 * is FT-G and is on the designer list. One colour ships at every width.
	 *
	 * WCAG 1.4.1 is satisfied independently of the hue: the weight steps
	 * 500 -> 700 as well, so colour is not the sole differentiator.
	 */
	.tc-tabbed-timeline.is-tabs
		.tc-tabbed-timeline__control[aria-selected="true"] {
		color: var( --wp--custom--color--secondary--600, #057db9 );
		font-weight: 700;
	}

	/* The label <p> is empty once the script has reparented its button into
	   the tablist. Hiding it is gated on `.is-tabs` and NOT on the media query
	   alone — if the script never runs there is no tablist to hold the labels,
	   and hiding them on width alone would erase every label at tablet. */
	.tc-tabbed-timeline.is-tabs .tc-tabbed-timeline__item-title {
		display: none;
	}

	.tc-tabbed-timeline.is-tabs .tc-tabbed-timeline__item {
		border-block: 0;
		padding-block: 0;
	}

	.tc-tabbed-timeline.is-tabs .tc-tabbed-timeline__panel {
		margin-block-start: 0;
	}
}

/* ── breakpoint/lg — the entry becomes a 2-up ──────────────────────────────
 *
 * NOTHING to do with the mode switch, which happened at 48rem. This is purely
 * the entry's internal layout: all three boards stack text over image at 768
 * and only the 1400 board puts them side by side.
 *
 * `flex: 1 1 0` on both sides IS the frame: 596 + 104 + 596 = 1296 exactly, so
 * equal grow factors reproduce the comp at 1400 and stay proportional at every
 * other width. No frame-width-derived ratio is needed, unlike services-tabs.
 *
 * The image stays AFTER the text in source order and is drawn on the right, so
 * no reorder is needed and DOM order already matches reading order.
 */

@media (min-width: 64rem) { /* breakpoint/lg */

	.tc-tabbed-timeline .tc-tabbed-timeline__entry {
		gap: 5rem; /* 80 — documented literal, no preset resolves to it */
	}

	.tc-tabbed-timeline .tc-tabbed-timeline__entry-content {
		align-items: flex-start;
		flex-direction: row;
		gap: 6.5rem; /* 104 — documented literal */
	}

	.tc-tabbed-timeline .tc-tabbed-timeline__entry-text,
	.tc-tabbed-timeline .tc-tabbed-timeline__entry-media {
		flex: 1 1 0;
		min-inline-size: 0;
	}
}

/* ── Phone tap targets (QA batch A, 2026-09-27) ───────────────────────────
 *
 * The accordion control measured full-width × 24-26 at 360-430, against the
 * 44 the phone brief requires. A transparent ::before (the chevron owns
 * ::after) reaches 10px above and below the control = 44+. Each row is 16 /
 * 15 padding round the control, so the target stays inside its own row and
 * never reaches the next control. Nothing drawn moves, so the row-height
 * assertions in the spec still describe the frame.
 */
@media (max-width: 47.9375rem) {
	.tc-tabbed-timeline.is-accordion .tc-tabbed-timeline__control {
		position: relative;
	}

	.tc-tabbed-timeline.is-accordion .tc-tabbed-timeline__control::before {
		content: "";
		inset-block: -10px;
		inset-inline: 0;
		position: absolute;
	}
}
