/* ==========================================================================
   TransPharm FSE — shared component layer. Front end AND editor canvas.
   Per-block CSS files stay empty — see Homepage Redesign task note, rule 10.
   ========================================================================== */

/* -- 1. Reset ------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

/* theme.json only declares font-family *names* as presets — it doesn't load
   the webfonts or apply one globally. Without this, every element that
   doesn't set its own font-family (most plain <p>) falls back to the
   browser's serif default (Times), and 1rem reads small next to a real
   typeface. Bumped slightly past the mockup's own 1rem body copy per
   explicit request — 17px, not just "whatever the browser default is". */
body {
	font-family: var(--wp--preset--font-family--sans);
	font-size: 17px;
	line-height: 1.6;
	color: var(--wp--preset--color--gray-900);
	background: var(--wp--preset--color--white);
	-webkit-font-smoothing: antialiased;
}

/* Vertical space comes from `gap`, from nothing else (rule 2). Zero
   specificity via :where() — an :is() version here silently beats prose
   rules below it. Running text is re-enabled explicitly further down. */
:where(section[class*="tph-"], .tph-site-footer)
:where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, dd, dt, figure, blockquote, fieldset, table) {
	margin-block: 0;
}

/* WordPress adds margin at two levels; both need killing (rule 3):
   `:root :where(.is-layout-flow) > *` (24px between every top-level
   block) and `:where(.wp-site-blocks) > *` (on <main> itself).
   ⚠️ SECTIONS NEVER HAVE margin-top. Only padding, and padding is always
   EQUAL top and bottom (see rule 1 below) — this is a hard rule, not a
   preference, because margin is exactly how this bug happens: WordPress
   silently reintroducing a gap the moment it wins a specificity fight.
   This exact defect already happened once on Top Notch Pro Painting and
   is written into that project's own Spacing System doc for the same
   reason it's written here now — it is not a one-off.

   WHY THIS BROKE (round 1): `:root :where(.is-layout-flow) > *` LOOKS
   like a zero-specificity default because of the `:where()` — but
   `:root` is a plain, un-neutralized pseudo-class, contributing real
   specificity (0,1,0). Wrapping THIS override in `:where()` (0,0,0)
   during an earlier, unrelated fix (see the padding note below) made it
   lose to WordPress outright — every section except the first silently
   kept its 24px top margin, and it never showed up as an error anywhere,
   only as "too much white space" in a screenshot. The fix must be a REAL
   selector — section[data-pad] is (0,1,1), which reliably beats
   WordPress's (0,1,0) regardless of file order.

   WHY IT BROKE AGAIN (round 2, footer→page.html rollout): the SAME
   mistake, a different mechanism. WordPress ALSO injects its own inline
   global-styles `<style>` block containing `:where(.wp-site-blocks) > *
   { margin-block: 24px 0px; }` — core's copy, not this file's. Our
   override here used to be `:where(.wp-site-blocks) > *` too: same
   selector, same (0,0,0) specificity as core's. With a genuine
   specificity TIE, the cascade falls back to source order — and core's
   global-styles block prints into <head> AFTER this enqueued stylesheet,
   so core silently won every time, giving every non-<main> direct child
   of .wp-site-blocks (in practice: the footer template part) a real
   24px margin-top nobody could see in the HTML, only measure in
   DevTools. Confirmed with a real headless-Chrome render (Playwright),
   not guessed: `getComputedStyle` measured `margin-top: 24px` on
   `footer.wp-block-template-part`, and enumerating matched stylesheet
   rules showed core's inline `:where(.wp-site-blocks) > *` block as the
   winner. Same fix as round 1: drop `:where()`, make it a real selector
   so it wins on specificity instead of gambling on load order. This is
   the second place in this file a default is deliberately NOT
   :where()'d, and it must stay that way too. */
section[data-pad] { margin-block: 0; }
.wp-site-blocks > * { margin-block-start: 0; }

/* -- 2. Spacing tokens ------------------------------------------------------
   Two vertical distances inside a section. Nothing else. */

:root {
	--head-label-gap: var(--wp--preset--spacing--40); /* 16px — label to heading, one object */
	--block-gap:       var(--wp--preset--spacing--60); /* 32px — everything else */
	/* horizontal only — always. THE one column-gap for every 2-column
	   content split (hero, media-text, content-split, tph-head--split,
	   process--workflow, pathogen-tags, info-panel, contact-form) — never a
	   block-local rem value. Two blocks drifted to a hardcoded 5rem/3rem
	   (2026-09-23, caught live — "os gaps dos layouts de 2 colunas estão
	   todos desiguais"), the exact "eight different ratios" mistake rule 6
	   already warns about, just for the gap instead of the fr ratio. */
	--col-gap:  clamp(28px, 4.5vw, 64px);
	--shell-pad: clamp(20px, 5vw, 56px);

	--split:      minmax(0, 1.15fr) minmax(0, 1fr); /* matches the mockup's hero-grid ratio — declared once, reused everywhere */
	--split-flip: minmax(0, 1fr) minmax(0, 1.15fr);
	--split-even: minmax(0, 1fr) minmax(0, 1fr);
}

/* Running text is the one exception to the margin-block:0 reset — a gap
   cannot reach inside a WYSIWYG field's output. .tph-head__sub joined
   this list once it started holding wysiwyg content too (several blocks'
   `body`/`sub` fields) — multi-paragraph copy in there needs the same
   between-paragraph spacing tph-text__body already gets, or paragraphs
   collapse straight into each other under the blanket reset above. */
.tph-text__body > * + *,
.tph-quote__body > * + *,
.tph-head__sub > * + *,
.tph-hero__sub > * + * {
	margin-block-start: 1em;
}

/* A wysiwyg `body` field can hold a real subheading + bullet list inline
   (formatselect + bullist in the "TPH Rich" toolbar) instead of a block
   needing separate label/body/checklist fields for the same content. Any
   heading the editor picks (H2, H3, …) gets its typography from the
   theme's own global styles.elements (theme.json) — never a per-block
   override on a raw h1–h6/p selector, even scoped to one wrapper class.
   Only the bullet-list treatment is styled here, since lists have no
   pre-existing themed default to override — this is a design addition,
   not a typography override.

   `margin: 0` here used to also zero margin-block-start, which beats the
   sibling-gap rule above on specificity (.tph-text__body ul is more
   specific than .tph-text__body > * + *) — the list sat flush against
   the paragraph before it instead of getting the same 1em every other
   paragraph gets. Resetting only margin-inline/margin-block-end lets the
   sibling rule's margin-block-start through again. Also dropped the li
   color override (was gray-700, a visibly lighter shade than every other
   paragraph's gray-900 from body{} — no reason for list text to be the
   one exception) so it inherits the page's normal text color. */
.tph-text__body ul { list-style: none; margin-inline: 0; margin-block-end: 0; padding: 0; }
.tph-text__body ul li { position: relative; padding-left: 1.5rem; line-height: 1.6; }
.tph-text__body ul li + li { margin-block-start: 0.75rem; }
.tph-text__body ul li::before { content: ''; position: absolute; left: 0; top: 8px; width: 7px; height: 7px; background: var(--wp--preset--color--teal); border-radius: 50%; }

/* -- 3. Layout shell -------------------------------------------------------*/

.tph-shell {
	max-width: var(--wp--style--global--wide-size, 1340px);
	margin-inline: auto;
	padding-inline: var(--shell-pad);
}

/* :where() — zero specificity, same reasoning as the margin reset above.
   Written as a plain `section[data-pad="lg"]` this was (0,1,1), which beat
   `.tph-hero`'s own (0,1,0) padding-block regardless of source order —
   hero silently got the generic value instead of what it declared for
   itself. A default must never be able to out-specify a component. (This
   one stays :where()'d, unlike the margin reset above — it only competes
   with other AUTHOR rules at equal-or-lower specificity, never with a
   real-specificity WordPress default, so :where() is correct here.)

   ⚠️ Top and bottom are always the SAME value. A section's padding-block
   is ONE number, never a pair — an asymmetric top/bottom is how this
   ramp was first built (misreading Top Notch's own "32–48" range as
   fixed top/bottom numbers instead of a responsive clamp() applied
   equally to both sides) and it reads as uneven spacing the moment a
   fixed nav or anything else changes what's visually above the section.

   ⚠️ ONE canonical value, no per-block tier choice (corrected 2026-09-23,
   Round 14 — this was a real bug reported live, not a style preference).
   Every block used to expose its own `section_spacing` (sm/md/lg) field on
   its Layout tab, so two instances of the SAME visual "section" idea could
   end up with different padding for no reason other than which radio an
   editor happened to click — and worse, back-to-back sections sharing a
   background color would each keep their own full padding at the shared
   edge, summing into a dead gap that reads as a rendering bug (confirmed:
   a card-grid's 64px bottom + a cta-banner's 48px top = 112px of empty
   space, both sections plain white). `section_spacing` is gone from every
   ACF field group; `tph_section_open()` no longer accepts a `pad` variance
   from block content, only ever emitting `data-pad="md"`. The ONLY padding
   override left is Module Settings' explicit `padding_top`/`padding_bottom`
   px fields — a deliberate, visible, rare exception, never a routine
   per-block default. See the FSE Theme Playbook's "fewer numbers beat
   better numbers" rule — this is that rule applied one level up, to
   section padding itself instead of just the gap tokens inside a section. */
:where(section[data-pad="md"]) { padding-block: clamp(48px, 6vw, 64px); }

/* Two sections sharing the same background must not each keep their own
   padding at the shared edge — that sums into a dead gap that reads as a
   rendering bug, even though it visually reads as ONE continuous colored
   area. This is NOT automated in CSS — a blanket adjacent-sibling rule
   changes a section's rendered spacing invisibly, from a background choice
   made on a DIFFERENT block, with no field anywhere showing why. Per
   explicit instruction: when this happens, it is fixed per instance, in
   content, via that section's own Module Settings `padding_top`/
   `padding_bottom` override fields (set the second section's padding-top
   override to 0) — a deliberate, visible editorial decision, not hidden
   platform behavior. See the Homepage Redesign task note / WordPress Theme
   Standards doc for the worked example. */

/* Nav is position:fixed (back to it — see its own rule for why sticky
   didn't work out) and pinned to a hard --nav-height, so <main> reserves
   exactly that same variable. Both sides read the one number; there is
   nothing to measure or let drift apart a second time. Must be a real
   selector, not :where()'d — same reasoning as rule 15/section[data-pad]
   above: it needs to beat WordPress's own margin rules, which are not
   actually zero-specificity despite looking like it. */
.wp-site-blocks > main { margin-top: var(--nav-height); }

/* -- 4. Colour schemes -------------------------------------------------------
   A section declares data-scheme once; everything inside follows. A block
   never sets a text colour directly — rule 6 (one --split etc. hold the same
   "declare once" lesson; schemes are the colour equivalent). */

[data-scheme="white"]  { background: var(--wp--preset--color--white); color: var(--wp--preset--color--gray-900); }
[data-scheme="paper"]  { background: var(--wp--preset--color--off-white); color: var(--wp--preset--color--gray-900); }
[data-scheme="navy"],
[data-scheme="navy-mid"],
[data-scheme="deep"]     { background: var(--wp--preset--color--navy); color: var(--wp--preset--color--white); }
[data-scheme="navy-mid"] { background: var(--wp--preset--color--navy-mid); }
[data-scheme="deep"]     { background: var(--wp--preset--color--navy-deep); }

/* theme.json's global h2/h3 default is navy (styles.elements) — every
   heading's ambient background text color would otherwise still inherit
   correctly, but color set directly on an element always wins over
   inheritance, navy or not. This is the one deliberate exception: not
   decorative styling, a contrast necessity — navy-on-navy is invisible,
   so a dark section's own heading color is switched explicitly, and only
   here. Catches every heading regardless of variant class (.tph-heading,
   raw wysiwyg h2/h3, anything else), since it targets the tag itself. */
[data-scheme="navy"] h2, [data-scheme="navy"] h3,
[data-scheme="navy-mid"] h2, [data-scheme="navy-mid"] h3,
[data-scheme="deep"] h2, [data-scheme="deep"] h3 { color: var(--wp--preset--color--white); }

[data-scheme="navy"] .tph-label,
[data-scheme="navy-mid"] .tph-label,
[data-scheme="deep"] .tph-label { color: var(--wp--preset--color--teal-light); }
[data-scheme="navy"] .tph-label::before,
[data-scheme="navy-mid"] .tph-label::before,
[data-scheme="deep"] .tph-label::before { background: var(--wp--preset--color--teal-light); }
[data-scheme="navy"] .tph-head__sub,
[data-scheme="navy-mid"] .tph-head__sub,
[data-scheme="deep"] .tph-head__sub,
[data-scheme="navy"] .tph-page-header__lead,
[data-scheme="navy-mid"] .tph-page-header__lead,
[data-scheme="deep"] .tph-page-header__lead { color: rgba(255,255,255,0.75); }

/* -- 5. Section header (tph_section_header) --------------------------------
   label -> --head-label-gap -> heading -> --block-gap -> content.
   Alignment is inherited via --head-align, never hardcoded (rule 12). */

.tph-head { display: flex; flex-direction: column; gap: var(--block-gap); margin-bottom: var(--block-gap); }
.tph-head__main { display: flex; flex-direction: column; gap: var(--head-label-gap); align-items: var(--head-align, flex-start); text-align: var(--head-align, left); }

/* .tph-head's own margin-bottom is for plain block-flow contexts (most
   blocks just drop it into .tph-shell). Wherever it instead lands as a
   flex child of an already gap-based column, that margin adds ON TOP of
   the parent's gap — the exact double-spacing bug rule 4 exists to
   prevent. Zero it in every such container (found three: media-text and
   two pre-existing, previously undetected instances in info-panel and
   contact-form — newsletter-signup already had its own scoped fix). */
.tph-media-text__text .tph-head,
.tph-info-panel__main .tph-head,
.tph-contact-form__main .tph-head { margin-bottom: 0; }

/* Split layout — label+heading left, subtitle right, matching the handoff's
   `.grid-2-even` section-header pattern (In Vitro "Foundation Layer",
   In Vivo "Efficacy Layer"). Overrides display/flex-direction; column-gap
   and row-gap are declared separately (rule 5) so the grid's horizontal
   value can never leak into vertical rhythm on the single-column fallback. */
.tph-head--split {
	display: grid;
	grid-template-columns: 1fr 1fr;
	column-gap: var(--col-gap);
	row-gap: var(--block-gap);
	align-items: start;
}
.tph-head--split .tph-head__sub { max-width: none; margin-top: 0; }

@media (max-width: 900px) {
	.tph-head--split { grid-template-columns: 1fr; }
}

.tph-label {
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 0;
	line-height: 1;
	font-size: var(--wp--preset--font-size--label);
	font-weight: 500;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--wp--preset--color--teal);
}

.tph-label::before {
	content: '';
	display: inline-block;
	width: 16px;
	height: 1px;
	background: var(--wp--preset--color--teal);
}

.tph-heading {
	margin: 0;
	font-family: var(--wp--preset--font-family--serif);
	font-weight: 400;
	font-size: var(--wp--preset--font-size--heading-md);
	line-height: 1.2;
	max-width: 40ch;
}

/* 560px matches the mockup's real `.section-body` default — an earlier
   42ch guess computed to ~336px, far narrower than intended (caught from a
   DevTools measurement on the live page, not from reading the mockup CSS
   closely enough the first time). */
.tph-head__sub { max-width: 560px; color: var(--wp--preset--color--gray-500); font-size: var(--wp--preset--font-size--medium); line-height: 1.8; font-weight: 300; }

/* -- 6. Button --------------------------------------------------------------*/

.tph-btn {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 0.875rem 1.75rem;
	border-radius: 4px;
	font-size: 0.9375rem;
	font-weight: 500;
	text-decoration: none;
	transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.tph-btn--primary { background: var(--wp--preset--color--orange); color: var(--wp--preset--color--white); }
.tph-btn--primary:hover { background: var(--wp--preset--color--orange-dark); }

.tph-btn--ghost { background: transparent; color: inherit; border: 1px solid currentColor; }
[data-scheme="white"] .tph-btn--ghost,
[data-scheme="paper"] .tph-btn--ghost { border-color: var(--wp--preset--color--gray-200); }

/* -- 7. Image ---------------------------------------------------------------
   Always a real <img>, never background-image (rule: images §). */

.tph-img { display: block; width: 100%; height: 100%; object-fit: cover; }

.tph-bg-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.tph-bg-overlay { position: absolute; inset: 0; z-index: 1; }

/* -- 8. Quote ---------------------------------------------------------------*/

.tph-quote { display: flex; flex-direction: column; gap: var(--head-label-gap); }
.tph-quote__body p { font-size: var(--wp--preset--font-size--heading-sm); line-height: 1.6; font-family: var(--wp--preset--font-family--serif); font-weight: 400; }
/* quote's wysiwyg can now carry an image (media_upload enabled on the CPT
   field) — this block isn't wrapped in .tph-text__body, so it never
   inherited that class's own img rule; same fix, same reasoning. */
.tph-quote__body img { max-width: 100%; height: auto; display: block; }
.tph-quote__meta { display: flex; align-items: center; gap: 12px; }
.tph-quote__avatar {
	display: inline-flex; align-items: center; justify-content: center;
	width: 40px; height: 40px; border-radius: 4px;
	background: var(--wp--preset--color--navy);
	color: var(--wp--preset--color--white);
	font-family: var(--wp--preset--font-family--sans);
	font-size: 0.8125rem; font-weight: 500;
}
.tph-quote__who { display: flex; flex-direction: column; gap: 2px; font-size: 0.875rem; }
.tph-quote__name { font-weight: 500; color: var(--wp--preset--color--gray-900); }
.tph-quote__role { color: var(--wp--preset--color--gray-500); }

/* -- 9. Navbar (tph/navbar) -------------------------------------------------
   Ported from the SEO handoff mockup 1:1 (colors/spacing), tph- prefixed.
   Fixed, solid navy at rest (matching the hero it sits above), darkens to
   navy-deep + blur once scrolled — see the .tph-nav rule below for why this
   isn't literally transparent-until-scroll like the mockup's CSS. */

/* Nav height is a hard, explicit number (--nav-height, on .tph-nav__inner
   below) — not measured, not guessed, not left to reflow with content.
   Two things this session tried instead and both cost a real regression:
   (1) a hardcoded --nav-height reserved on <main> while nav's real height
   was left to its own content — 0.6px of drift between the guess and
   reality was a visible white hairline; (2) position: sticky, so nav
   reserved its own space automatically — this rendered worse (a visible
   duplicate/larger gap), and this session has no way to inspect *why* in
   a real browser to fix it properly rather than guess again. Pinning the
   number and setting it as nav's ACTUAL height, once, in one place, means
   there is nothing left to measure or drift — .tph-nav__inner IS 80px,
   full stop, and everything that needs to know nav's height reads the
   same --nav-height variable instead of re-deriving it. */
:root { --nav-height: 80px; }

.tph-nav {
	position: fixed;
	top: var(--wp-admin--admin-bar--height, 0px);
	left: 0; right: 0;
	z-index: 50;
	/* Solid at all times (not transparent) because this nav does NOT
	   overlay the hero — <main> reserves nav's own height via margin-top
	   instead (see --nav-height above), a deliberate choice after real
	   position:sticky/overlay regressions. That means nav never sits on
	   top of the hero at rest; it sits directly ABOVE it, so its rest
	   color has to match the hero's own color or it reads as a visibly
	   different band, not "transparent gone wrong". That was the actual
	   bug here: this was navy-deep (#0a1e3a) while every hero on the site
	   is navy (#102a4e) — two different shades stacked with a hard seam
	   between them. Fixed to match the hero at rest; the mockup's
	   "arrival" darkening on scroll is preserved via .is-scrolled below,
	   which now also happens to be correct once scroll has moved the nav
	   off the hero and onto lighter content. */
	background: var(--wp--preset--color--navy);
	border-bottom: 1px solid transparent;
	transition: background 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}

.tph-nav__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	height: var(--nav-height);
	padding-inline: 4rem;
}

.tph-nav.is-scrolled {
	background: rgba(11, 30, 61, 0.92);
	backdrop-filter: blur(10px);
	-webkit-backdrop-filter: blur(10px);
	border-bottom-color: rgba(255,255,255,0.08);
	box-shadow: 0 4px 20px rgba(0,0,0,0.15);
}

.tph-nav__logo { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.tph-nav__logo img { height: 42px; width: auto; display: block; }
/* Design's own logo file is blue-on-white; the navbar is always dark navy
   in this build (no transparent/scrolled state), so invert straight to
   white rather than the handoff's softer 0.95-opacity "not yet scrolled" mix. */
.tph-nav__logo-img { filter: brightness(0) invert(1); }
.tph-nav__logo-text { color: var(--wp--preset--color--white); font-size: 15px; font-weight: 500; line-height: 1.2; }
.tph-nav__logo-text span { display: block; font-size: 10px; font-weight: 300; letter-spacing: 0.12em; text-transform: uppercase; opacity: 0.7; }

.tph-nav__links > ul { display: flex; align-items: center; gap: 2rem; list-style: none; margin: 0; padding: 0; }
.tph-nav__links a {
	color: rgba(255,255,255,0.75);
	text-decoration: none;
	font-size: 14px;
	letter-spacing: 0.01em;
	transition: color 0.2s;
}
.tph-nav__links > ul > li > a:hover { color: var(--wp--preset--color--white); }

.tph-has-dropdown { position: relative; }
.tph-has-dropdown > a::after {
	content: '';
	display: inline-block;
	width: 5px; height: 5px;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate(45deg) translateY(-2px);
	margin-left: 6px;
	opacity: 0.6;
	vertical-align: middle;
}

.tph-dropdown {
	position: absolute;
	top: 100%;
	left: 50%;
	transform: translateX(-50%) translateY(8px);
	background: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--gray-200);
	border-radius: 10px;
	box-shadow: 0 18px 50px -12px rgba(16,42,78,0.28);
	padding: 0.5rem;
	min-width: 230px;
	list-style: none;
	margin: 0;
	opacity: 0;
	visibility: hidden;
	transition: opacity 0.18s, transform 0.18s, visibility 0.18s;
	z-index: 200;
}
.tph-has-dropdown:hover .tph-dropdown,
.tph-has-dropdown:focus-within .tph-dropdown {
	opacity: 1;
	visibility: visible;
	transform: translateX(-50%) translateY(0);
}
/* hover bridge so the menu doesn't close in the gap */
.tph-dropdown::before { content: ''; position: absolute; top: -10px; left: 0; right: 0; height: 10px; }
.tph-dropdown li { display: block; width: 100%; }
.tph-dropdown a {
	display: block;
	padding: 0.6rem 0.85rem;
	font-size: 13.5px;
	color: var(--wp--preset--color--gray-700) !important;
	border-radius: 6px;
	white-space: nowrap;
	transition: background 0.15s, color 0.15s;
}
.tph-dropdown a:hover { background: var(--wp--preset--color--off-white); color: var(--wp--preset--color--navy) !important; }

.tph-nav__cta-item { display: flex; }
.tph-nav__cta.tph-btn { padding: 0.5rem 1.25rem; font-size: 14px; }

/* Mobile — not in the source mockup (handoff has no toggle/breakpoint for
   nav), built to spec here: hamburger + slide-down panel. */

.tph-nav__toggle {
	display: none;
	flex-direction: column;
	justify-content: center;
	gap: 5px;
	width: 32px; height: 32px;
	background: none; border: 0; cursor: pointer; padding: 0;
}
.tph-nav__toggle span { display: block; width: 100%; height: 2px; background: var(--wp--preset--color--white); border-radius: 2px; }

.tph-nav__mobile {
	display: none;
	background: var(--wp--preset--color--navy-deep);
	max-height: 0;
	overflow: hidden;
	transition: max-height 0.25s ease;
}
.tph-nav__mobile ul { list-style: none; margin: 0; padding: var(--head-label-gap) var(--shell-pad) var(--block-gap); display: flex; flex-direction: column; gap: var(--head-label-gap); }
.tph-nav__mobile a { color: var(--wp--preset--color--white); text-decoration: none; font-size: 15px; }
.tph-nav__mobile ul ul { padding: 8px 0 0 16px; gap: 8px; opacity: 0.75; }
.tph-nav__mobile-cta { color: var(--wp--preset--color--orange) !important; font-weight: 500; }

.tph-nav.is-open .tph-nav__mobile { max-height: 80vh; }

@media (max-width: 860px) {
	.tph-nav__inner { padding-inline: var(--shell-pad); }
	.tph-nav__links { display: none; }
	.tph-nav__toggle { display: flex; }
	.tph-nav__mobile { display: block; }
}

/* Search toggle — always visible, desktop and mobile alike (unlike
   .tph-nav__toggle, which is hamburger-only and hidden above 860px). Same
   slide-down-panel mechanism as .tph-nav__mobile, driven by nav.js toggling
   .is-search-open on .tph-nav. */
.tph-nav__search-toggle {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	background: none;
	border: 0;
	padding: 0;
	cursor: pointer;
	color: rgba(255,255,255,0.75);
	transition: color 0.2s;
}
.tph-nav__search-toggle:hover,
.tph-nav__search-toggle[aria-expanded="true"] { color: var(--wp--preset--color--white); }
.tph-nav__search-toggle svg { width: 19px; height: 19px; }

.tph-nav__search {
	background: var(--wp--preset--color--navy-deep);
	max-height: 0;
	overflow: hidden;
	transition: max-height 0.25s ease;
}
.tph-nav.is-search-open .tph-nav__search { max-height: 120px; }

.tph-nav__search-form {
	display: flex;
	gap: 0.75rem;
	max-width: 640px;
	margin: 0 auto;
	padding: var(--head-label-gap) var(--shell-pad);
}
.tph-nav__search-input {
	flex: 1;
	min-width: 0;
	background: rgba(255,255,255,0.08);
	border: 1px solid rgba(255,255,255,0.18);
	border-radius: 6px;
	padding: 0.65rem 1rem;
	color: var(--wp--preset--color--white);
	font-family: var(--wp--preset--font-family--sans);
	font-size: 15px;
}
.tph-nav__search-input::placeholder { color: rgba(255,255,255,0.45); }
.tph-nav__search-input:focus { outline: 2px solid var(--wp--preset--color--teal); outline-offset: 1px; }
.tph-nav__search-submit {
	background: var(--wp--preset--color--orange);
	color: var(--wp--preset--color--white);
	border: 0;
	border-radius: 6px;
	padding: 0 1.25rem;
	font-size: 14px;
	font-weight: 500;
	cursor: pointer;
	transition: background 0.2s;
}
.tph-nav__search-submit:hover { background: var(--wp--preset--color--orange-dark); }

@media (max-width: 480px) {
	.tph-nav__search-form { flex-direction: column; }
}

/* -- 10. Site footer (tph/site-footer) --------------------------------------
   Ported 1:1 from the mockup — one row, not the 4-column footer I first
   (wrongly) assumed. See Homepage Redesign task note. */

.tph-site-footer {
	background: var(--wp--preset--color--navy-deep);
	padding: 3rem 4rem;
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: var(--head-label-gap);
}

.tph-site-footer__logo { font-family: var(--wp--preset--font-family--serif); font-size: 1rem; color: rgba(255,255,255,0.85); }

.tph-site-footer__links { display: flex; gap: 2rem; list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.tph-site-footer__links a { font-size: 13px; color: rgba(255,255,255,0.72); text-decoration: none; transition: color 0.2s; }
.tph-site-footer__links a:hover { color: var(--wp--preset--color--orange-light); }

.tph-site-footer__meta { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.tph-site-footer__copy { font-size: 12.5px; color: rgba(255,255,255,0.6); }

.tph-site-footer__social { display: flex; gap: 14px; list-style: none; margin: 0; padding: 0; }
.tph-site-footer__social a {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 18px;
	height: 18px;
	color: rgba(255,255,255,0.6);
	transition: color 0.2s;
}
.tph-site-footer__social a:hover { color: var(--wp--preset--color--orange-light); }
.tph-site-footer__social svg { width: 100%; height: 100%; }

@media (max-width: 640px) {
	.tph-site-footer { flex-direction: column; align-items: flex-start; padding: 2.5rem var(--shell-pad); }
	.tph-site-footer__meta { align-items: flex-start; }
}

/* -- 11. Hero (tph/hero) -----------------------------------------------------*/

.tph-hero {
	position: relative;
	overflow: hidden;
	/* Padding is untouched — the standard data-pad="lg" scale applies here
	   same as any other section (:where(section[data-pad="lg"]) above).
	   Nav clearance is <main>'s job now, not hero's. */
}
.tph-hero::before { content: ''; position: absolute; top: -120px; right: -120px; width: 600px; height: 600px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.04); pointer-events: none; }
.tph-hero::after  { content: ''; position: absolute; top: -40px; right: -40px; width: 400px; height: 400px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.05); pointer-events: none; }

/* Ambient drift, not rotation — a perfect circle outline looks identical
   rotated, so the only motion that actually reads is a slow positional
   float. Two different periods/offsets so the pair doesn't move in lockstep.
   Gated on .tph-motion (motion.js, same prefers-reduced-motion check every
   other animation in this theme uses) — CSS ships the static state by
   default. */
@keyframes tph-hero-drift-1 { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(-18px, 14px); } }
@keyframes tph-hero-drift-2 { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(14px, -10px); } }
html.tph-motion .tph-hero::before { animation: tph-hero-drift-1 26s ease-in-out infinite; }
html.tph-motion .tph-hero::after  { animation: tph-hero-drift-2 32s ease-in-out infinite; }

.tph-hero__grid {
	display: grid;
	grid-template-columns: var(--split);
	column-gap: var(--col-gap);
	row-gap: var(--block-gap);
	align-items: center;
	position: relative;
	z-index: 2;
}

.tph-hero__headline {
	margin: 0 0 var(--block-gap);
	font-family: var(--wp--preset--font-family--serif);
	font-weight: 400;
	font-size: var(--wp--preset--font-size--heading-xl);
	line-height: 1.1;
	color: var(--wp--preset--color--white);
	max-width: 19ch;
}
.tph-hero__headline em { font-style: italic; color: rgba(255,255,255,0.85); }

.tph-hero__sub { max-width: 34rem; font-size: 1.0625rem; line-height: 1.75; color: rgba(255,255,255,0.8); margin-bottom: var(--block-gap); }

.tph-hero__actions { display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap; }
.tph-hero__actions .tph-btn--ghost { color: var(--wp--preset--color--orange-light); border: none; border-bottom: 1px solid var(--wp--preset--color--orange); border-radius: 0; padding: 0 0 3px; }
.tph-hero__actions .tph-btn--ghost:hover { color: var(--wp--preset--color--white); border-bottom-color: var(--wp--preset--color--white); }

.tph-hero__media { position: relative; }
.tph-hero__placeholder {
	aspect-ratio: 4 / 3;
	width: 100%;
	background: rgba(255,255,255,0.03);
	border: 1px dashed rgba(255,255,255,0.15);
	border-radius: 10px;
	display: flex; align-items: center; justify-content: center;
	padding: 2rem;
	position: relative;
	overflow: hidden;
}
.tph-hero__placeholder::before {
	content: '';
	position: absolute; inset: 0;
	background: linear-gradient(135deg, rgba(217,117,54,0.06) 0%, transparent 60%), linear-gradient(315deg, rgba(14,138,114,0.05) 0%, transparent 60%);
	pointer-events: none;
}
.tph-hero__placeholder-inner { text-align: center; position: relative; z-index: 1; }
.tph-hero__play {
	width: 68px; height: 68px; border-radius: 50%;
	background: rgba(217,117,54,0.15); border: 1px solid rgba(217,117,54,0.35); color: var(--wp--preset--color--orange-light);
	display: inline-flex; align-items: center; justify-content: center;
	margin-bottom: var(--head-label-gap); padding-left: 4px;
}
.tph-hero__media-label { font-size: 13px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(255,255,255,0.7); margin-bottom: 6px; }
.tph-hero__media-hint { font-size: 12px; color: rgba(255,255,255,0.65); font-style: italic; }
.tph-hero__photo { aspect-ratio: 4/3; border-radius: 10px; }

@media (max-width: 960px) {
	.tph-hero__grid { grid-template-columns: 1fr; }
	.tph-hero__placeholder { aspect-ratio: 16/10; }
}

/* Simple variant — breadcrumb + H1 + lead, the page-banner reading of the
 * same block. No media panel, no dual-CTA row, shorter — see the variant
 * switch in block-tph-hero.php. */
/* Colors match the handoff's .crumb/.crumb a/.crumb i/.crumb span exactly.
 * Markup is Rank Math's own breadcrumb output (see block-tph-hero.php) —
 * .last is its "current page" span, .separator its delimiter span. */
.tph-hero--simple .tph-page-header__crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-bottom: var(--head-label-gap); font-size: 12.5px; color: rgba(255,255,255,0.5); }
.tph-hero--simple .tph-page-header__crumbs a { color: rgba(255,255,255,0.5); text-decoration: none; transition: color 0.2s; }
.tph-hero--simple .tph-page-header__crumbs a:hover { color: rgba(255,255,255,0.85); }
.tph-hero--simple .tph-page-header__crumbs .separator { color: rgba(255,255,255,0.3); }
.tph-hero--simple .tph-page-header__crumbs .last { color: rgba(255,255,255,0.85); }
.tph-hero--simple .tph-page-header__title { font-family: var(--wp--preset--font-family--serif); font-weight: 400; font-size: var(--wp--preset--font-size--heading-xl); line-height: 1.15; max-width: 26ch; margin: 0 0 var(--head-label-gap); }
.tph-hero--simple .tph-page-header__lead { max-width: 640px; font-size: var(--wp--preset--font-size--medium); line-height: 1.75; font-weight: 300; }
.tph-hero--simple .tph-page-header__lead p { margin: 0; }

/* -- 12. Stats (tph/stats) --------------------------------------------------*/

.tph-stats__row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--head-label-gap); }
.tph-stats__item { display: flex; align-items: center; gap: 12px; }
.tph-stats__icon {
	width: 32px; height: 32px; flex-shrink: 0; border-radius: 50%;
	background: rgba(255,255,255,0.06);
	display: flex; align-items: center; justify-content: center;
}
.tph-stats__icon svg { width: 15px; height: 15px; fill: var(--wp--preset--color--teal-light); }
.tph-stats__icon img { width: 18px; height: 18px; object-fit: contain; }
.tph-stats__item > span:last-child { display: flex; flex-direction: column; gap: 2px; }
.tph-stats__label { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(255,255,255,0.7); line-height: 1.2; }
.tph-stats__value { font-size: 14px; font-weight: 500; color: rgba(255,255,255,0.85); line-height: 1.2; }
.tph-stats__divider { width: 1px; height: 32px; background: rgba(255,255,255,0.1); flex-shrink: 0; }

@media (max-width: 900px) {
	.tph-stats__row { justify-content: flex-start; }
	.tph-stats__divider { display: none; }
}

/* Number variant — no icon, the number itself leads (large serif, on top),
   the label follows underneath in small caps. A 4-col grid, not the icon
   variant's flex row, since there's no divider/inline-pair to lay out. */
.tph-stats--number .tph-stats__row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; text-align: center; }
.tph-stats--number .tph-stats__item { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.tph-stats--number .tph-stats__value { font-family: var(--wp--preset--font-family--serif); font-size: 2.6rem; font-weight: 400; color: var(--wp--preset--color--white); line-height: 1; }
.tph-stats--number .tph-stats__label { font-size: 12px; color: rgba(255,255,255,0.6); text-transform: uppercase; letter-spacing: 0.06em; line-height: 1.2; }

/* Stays 2 columns all the way down to phone widths — 4 short number+label
   pairs read fine at 2-up even on a 375px screen, no reason to collapse
   to a single column and quadruple the section's height. */
@media (max-width: 900px) { .tph-stats--number .tph-stats__row { grid-template-columns: repeat(2, 1fr); } }

/* -- 13. Card grid (tph/card-grid) ------------------------------------------*/

.tph-card-grid__grid {
	display: grid;
	grid-template-columns: repeat(var(--tph-cols, 4), 1fr);
	column-gap: 1.5px;
	row-gap: 1.5px;
}
/* Grid's default align-items:stretch already equalizes card height PER ROW
   — which is exactly right on desktop (2+ cols share a row) and a no-op on
   mobile (1 col = 1 card per row, so each keeps its own natural height,
   nothing to override at the breakpoint). What was missing is pinning the
   link to the bottom of that stretched box instead of letting it float
   right under a description of whatever length — flex column + margin-top:
   auto on the link does that; when there's no extra height to absorb (the
   mobile case), auto resolves to 0 and it sits in normal flow instead. */
.tph-card-grid__card { display: flex; flex-direction: column; }
/* A card with a link field renders as a real <a> (see block-tph-card-grid.php)
   so the whole card is the click target, not just the small text link at the
   bottom — reset the browser's default anchor styling so it reads exactly
   like the no-link <div> case. */
a.tph-card-grid__card { color: inherit; text-decoration: none; }

/* Numbered variant: hairline grid, like the mockup's services-grid. */
.tph-card-grid--numbered .tph-card-grid__grid { background: var(--wp--preset--color--gray-200); border: 1.5px solid var(--wp--preset--color--gray-200); border-radius: 10px; overflow: hidden; }
.tph-card-grid--numbered .tph-card-grid__card { background: var(--wp--preset--color--white); padding: 2.5rem 2rem; }
.tph-card-grid--numbered .tph-card-grid__card:hover { background: var(--wp--preset--color--off-white); }
.tph-card-grid--numbered .tph-card-grid__number { font-family: var(--wp--preset--font-family--serif); font-size: 2.5rem; color: var(--wp--preset--color--gray-200); line-height: 1; margin-bottom: var(--head-label-gap); transition: color 0.25s ease; }
.tph-card-grid--numbered .tph-card-grid__card:hover .tph-card-grid__number { color: var(--wp--preset--color--orange); }
.tph-card-grid--numbered .tph-card-grid__link { font-size: 12px; font-weight: 500; color: var(--wp--preset--color--teal); text-decoration: none; letter-spacing: 0.04em; margin-top: auto; padding-top: 0.5rem; display: inline-block; }
.tph-card-grid--numbered .tph-card-grid__card:hover .tph-card-grid__link { color: var(--wp--preset--color--orange-dark); }

/* Audience variant: separated cards, teal top border that flips orange. */
.tph-card-grid--audience .tph-card-grid__grid { column-gap: 1.25rem; row-gap: 1.25rem; background: none; }
.tph-card-grid--audience .tph-card-grid__card {
	background: var(--wp--preset--color--off-white);
	border: 1px solid var(--wp--preset--color--gray-200);
	border-top: 3px solid var(--wp--preset--color--teal);
	border-radius: 6px;
	padding: 1.75rem 1.5rem;
	transition: border-color 0.2s, transform 0.2s;
}
.tph-card-grid--audience .tph-card-grid__card:hover { border-top-color: var(--wp--preset--color--orange); transform: translateY(-2px); }
.tph-card-grid--audience .tph-card-grid__tag { font-size: 10.5px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--wp--preset--color--teal); margin-bottom: 0.875rem; }
.tph-card-grid--audience .tph-card-grid__title { font-family: var(--wp--preset--font-family--serif); font-size: 1.05rem; line-height: 1.35; }
.tph-card-grid--audience .tph-card-grid__link { font-size: 12px; font-weight: 500; color: var(--wp--preset--color--teal); text-decoration: none; letter-spacing: 0.04em; margin-top: auto; padding-top: 0.75rem; display: inline-block; }
.tph-card-grid--audience .tph-card-grid__card:hover .tph-card-grid__link { color: var(--wp--preset--color--orange-dark); }

.tph-card-grid__title { color: var(--wp--preset--color--gray-900); margin-bottom: 0.75rem; font-weight: 500; line-height: 1.3; }
.tph-card-grid__desc { color: var(--wp--preset--color--gray-700); line-height: 1.7; }

@media (max-width: 900px) { .tph-card-grid__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .tph-card-grid__grid { grid-template-columns: 1fr; } }

/* -- 14. Media + text (tph/media-text) --------------------------------------*/

.tph-media-text__grid { display: grid; grid-template-columns: var(--split-even); column-gap: var(--col-gap); row-gap: var(--block-gap); align-items: center; }
/* Vertical space between the heading group, body, credentials panel and CTA
   link comes from gap on the column itself — this was missing entirely, so
   those elements sat flush against each other with zero space (rule 2). */
.tph-media-text__text { display: flex; flex-direction: column; gap: var(--block-gap); }
.tph-inline-link { align-self: flex-start; }
.tph-media-text--left .tph-media-text__media { order: -1; }

.tph-media-text__media, .tph-media-text__placeholder { border-radius: 8px; height: 440px; }
.tph-media-text__img { border-radius: 8px; }
/* Stacked media layout (media_layout=stacked) — 2 images split the same
   440px box this slot always had, instead of 1 wide photo. Reuses
   --block-gap (the same token every other stacked element in this theme
   uses) rather than a new value. min-height:0 is required on a flex
   column's children or object-fit:cover's own intrinsic height (from
   .tph-img) wins the flex-basis calculation and neither image shrinks to
   its share of the box. */
.tph-media-text__stack { display: flex; flex-direction: column; gap: var(--block-gap); height: 100%; }
.tph-media-text__stack .tph-img { flex: 1 1 0; height: auto; min-height: 0; }
.tph-media-text__video { width: 100%; height: 100%; border-radius: 8px; overflow: hidden; }
.tph-media-text__video iframe { width: 100%; height: 100%; display: block; }
.tph-media-text__placeholder {
	background: var(--wp--preset--color--gray-100);
	border: 1px dashed var(--wp--preset--color--gray-200);
	display: flex; align-items: center; justify-content: center; flex-direction: column;
	color: var(--wp--preset--color--gray-500);
	padding: 2rem;
}
.tph-media-text__ph-label { font-size: 13px; font-weight: 500; margin-top: var(--head-label-gap); }
.tph-media-text__ph-hint { font-size: 12px; font-weight: 300; opacity: 0.75; margin-top: 4px; }

/* No margin-top — .tph-media-text__text is a gap-based flex column (rule 2),
   so a margin here would double up with the parent's gap, exactly the
   dead-space bug rule 4 exists to prevent (first element after a gap has
   no top spacing of its own). */
/* max-width: 440px matches the handoff's own .credentials-panel — correct
   when this sits EMBEDDED in a flowing text column next to body copy
   (media-text's "Who We Are"), so the panel doesn't stretch as wide as the
   paragraphs above it. Wrong the moment it's the ENTIRE content of its own
   dedicated grid column (content-split's "Credentials & Facility") — there,
   the cap just leaves dead space beside it instead of filling the column
   the grid already gave it, because this shell's column width (~640px) is
   wider than the handoff's own narrower one where 440px never looked capped. */
.tph-credentials { padding: 1.5rem 1.75rem; background: var(--wp--preset--color--off-white); border: 1px solid var(--wp--preset--color--gray-200); border-left: 3px solid var(--wp--preset--color--teal); border-radius: 4px; max-width: 440px; }
.tph-content-split__panel .tph-credentials { max-width: none; }
.tph-credentials__label { font-size: 11px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--wp--preset--color--gray-500); margin-bottom: 1rem; }
.tph-credentials__list { display: flex; flex-direction: column; gap: 0.875rem; }
.tph-credentials__item { display: flex; align-items: flex-start; gap: 12px; }
.tph-credentials__dot { width: 7px; height: 7px; background: var(--wp--preset--color--teal); border-radius: 50%; margin-top: 7px; flex-shrink: 0; }
.tph-credentials__name { display: block; font-size: 14px; font-weight: 500; color: var(--wp--preset--color--gray-900); line-height: 1.4; }
.tph-credentials__desc { display: block; color: var(--wp--preset--color--gray-700); line-height: 1.5; margin-top: 2px; }

/* Feature card — content-split's other real panel type (e.g. "Built for
   Resistance"): a tag + serif title + body, no key/value list. Not media
   — this only ever appears on tph/content-split, never paired with an
   actual photo. */
.tph-feature-card { display: flex; flex-direction: column; align-items: flex-start; gap: var(--head-label-gap); background: var(--wp--preset--color--white); border: 1px solid var(--wp--preset--color--gray-200); border-radius: 10px; padding: 2rem; }
.tph-feature-card__tag { font-size: 10.5px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--wp--preset--color--teal); }
.tph-feature-card__title { font-family: var(--wp--preset--font-family--serif); font-size: 1.15rem; font-weight: 400; color: var(--wp--preset--color--gray-900); line-height: 1.35; }
.tph-feature-card__body { color: var(--wp--preset--color--gray-700); line-height: 1.7; }
.tph-feature-card__body strong { color: var(--wp--preset--color--orange-dark); }

/* Embed — content-split's third panel type (a map or video iframe pasted
   into the embed_body wysiwyg field's Text tab, see tph_kses_embed_html()).
   aspect-ratio keeps the iframe's own width/height attributes from dictating
   this column's real width, same reasoning as tph-media-text__video. The
   iframe is positioned absolute, not a plain width/height:100%, because
   wpautop always wraps wysiwyg output in a <p> — an auto-height element
   sitting between this box and the iframe, which breaks a plain
   height:100% (it has no definite height to resolve the percentage
   against, so the iframe silently falls back to the browser's 150px
   replaced-element default regardless of the wrapper's own real height). */
.tph-content-split__embed { position: relative; aspect-ratio: 4 / 3; border-radius: 10px; overflow: hidden; }
.tph-content-split__embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; display: block; border: 0; }

@media (max-width: 900px) { .tph-media-text__grid { grid-template-columns: 1fr; } .tph-media-text--left .tph-media-text__media { order: 0; } }

/* -- 14a. Content split (tph/content-split) — two columns, no media -------
   The panel and text are rarely the same height, so this one is centered
   on the block axis instead of media-text's default top-align — a photo
   fills its column so alignment rarely matters there; a short credentials
   panel beside a longer paragraph looks stranded at the top otherwise. */
.tph-content-split__grid { display: grid; grid-template-columns: var(--split-even); column-gap: var(--col-gap); row-gap: var(--block-gap); align-items: center; }
.tph-content-split--right .tph-content-split__panel { order: 1; }
.tph-content-split__text { display: flex; flex-direction: column; gap: var(--block-gap); }
.tph-content-split__text .tph-head { margin-bottom: 0; }

@media (max-width: 900px) { .tph-content-split__grid { grid-template-columns: 1fr; } .tph-content-split--right .tph-content-split__panel { order: 0; } }

/* -- 15. Process (tph/process) — two variants, one component ---------------
   "sequence": full-bleed 2-column split, deliberately NOT wrapped in
   .tph-shell (matches the homepage's diff-section exactly), always-navy
   by design (not scheme-driven — this variant never renders on anything
   else, so hardcoded white text is correct here, not a bug).
   "workflow": bounded (.tph-shell, added conditionally in the PHP), scheme-
   driven colors like every other block, steps are feature-card-style
   bordered boxes with a text tag instead of a number. See block-tph-
   process.php's own doc comment for why these are genuinely two different
   shapes, not one block with a paint job. */

.tph-process__grid { display: grid; grid-template-columns: var(--split-even); column-gap: 0; row-gap: 0; }
.tph-process--sequence .tph-process__grid { max-width: var(--wp--style--global--wide-size, 1340px); margin-inline: auto; padding-inline: var(--shell-pad); }
.tph-process--workflow .tph-process__grid { column-gap: var(--col-gap); align-items: center; }

.tph-process--sequence .tph-process__intro { padding: var(--wp--preset--spacing--70) var(--col-gap) var(--wp--preset--spacing--70) 0; border-right: 1px solid rgba(255,255,255,0.07); }
.tph-process--sequence .tph-process__intro .tph-heading { color: var(--wp--preset--color--white); max-width: 440px; }
.tph-process--sequence .tph-process__intro .tph-head__sub { color: rgba(255,255,255,0.75); }
.tph-process--sequence .tph-process__steps { padding: var(--wp--preset--spacing--70) 0 var(--wp--preset--spacing--70) var(--col-gap); display: flex; flex-direction: column; justify-content: center; }
.tph-process--sequence .tph-process__grid > .tph-process__steps:only-child { grid-column: 1 / -1; padding-inline: 0; }

.tph-process--workflow .tph-process__cta { margin-top: var(--block-gap); }
.tph-process--workflow .tph-process__steps { display: flex; flex-direction: column; gap: 1rem; }

.tph-process__hq {
	margin-top: var(--block-gap);
	padding: 1.25rem 1.5rem;
	background: rgba(255,255,255,0.04);
	border: 1px solid rgba(255,255,255,0.08);
	border-left: 3px solid var(--wp--preset--color--teal-light);
	border-radius: 4px;
	display: inline-block;
}
.tph-process__hq-label { font-size: 10px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255,255,255,0.7); margin-bottom: 6px; }
.tph-process__hq-value { font-family: var(--wp--preset--font-family--serif); font-size: 1.25rem; color: var(--wp--preset--color--white); line-height: 1.2; margin-bottom: 4px; }
.tph-process__hq-meta { font-size: 13px; color: rgba(255,255,255,0.75); }

@media (max-width: 900px) {
	.tph-process__grid { grid-template-columns: 1fr; }
	.tph-process--sequence .tph-process__intro { border-right: none; border-bottom: 1px solid rgba(255,255,255,0.07); }
}

.tph-process__label { font-size: 11px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: var(--wp--preset--color--teal-light); margin-bottom: var(--block-gap); padding-bottom: var(--head-label-gap); border-bottom: 1px solid rgba(255,255,255,0.08); }

.tph-process--sequence .tph-process__step { display: flex; align-items: flex-start; gap: 1.5rem; padding-block: 1.25rem; border-bottom: 1px solid rgba(255,255,255,0.06); }
.tph-process--sequence .tph-process__step:last-child { border-bottom: none; }
.tph-process__num { font-family: var(--wp--preset--font-family--serif); font-size: 2rem; color: var(--wp--preset--color--orange-light); line-height: 1; min-width: 70px; padding-top: 2px; }
.tph-process--sequence .tph-process__title { font-size: 17px; font-weight: 500; color: var(--wp--preset--color--white); margin-bottom: 8px; line-height: 1.3; }
.tph-process--sequence .tph-process__sub { color: rgba(255,255,255,0.75); line-height: 1.65; }

/* Workflow variant: feature-card-style bordered box per step, scheme-driven
   text color (inherits from [data-scheme] like any other block — no
   hardcoded white, since this variant can sit on white/paper too). */
.tph-process--workflow .tph-process__step {
	background: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--gray-200);
	border-radius: 10px;
	padding: 1.4rem 1.5rem;
	transition: box-shadow 0.2s, transform 0.2s;
}
.tph-process--workflow .tph-process__step:hover { box-shadow: 0 18px 40px -16px rgba(16,42,78,0.18); transform: translateY(-2px); }
.tph-process__tag { font-size: 10px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--wp--preset--color--teal); margin-bottom: 0.6rem; }
.tph-process--workflow .tph-process__title { font-family: var(--wp--preset--font-family--serif); font-weight: 400; font-size: 1.05rem; color: var(--wp--preset--color--gray-900); margin-bottom: 0.4rem; line-height: 1.3; }
.tph-process--workflow .tph-process__sub { color: var(--wp--preset--color--gray-700); line-height: 1.6; }

/* -- 16. Testimonial (tph/testimonial) --------------------------------------*/

.tph-testimonial__grid { display: grid; grid-template-columns: repeat(2, 1fr); column-gap: 2rem; row-gap: 2rem; margin-top: var(--block-gap); }
.tph-testimonial--single .tph-testimonial__grid { grid-template-columns: 1fr; max-width: 640px; }
.tph-testimonial__card { background: var(--wp--preset--color--white); border: 1px solid var(--wp--preset--color--gray-200); border-radius: 8px; padding: 2rem; }
.tph-testimonial__card .tph-quote__body p { font-size: 1.125rem; font-style: italic; color: var(--wp--preset--color--gray-700); }
.tph-testimonial__card .tph-quote__avatar { background: var(--wp--preset--color--teal); width: 36px; height: 36px; border-radius: 50%; }

@media (max-width: 760px) { .tph-testimonial__grid { grid-template-columns: 1fr; } }

/* -- 17. Post list (tph/post-list) ------------------------------------------*/

.tph-post-list__grid { display: grid; grid-template-columns: repeat(3, 1fr); column-gap: 1.5rem; row-gap: 1.5rem; }
.tph-post-list__card {
	display: flex;
	flex-direction: column;
	background: var(--wp--preset--color--off-white);
	border: 1px solid var(--wp--preset--color--gray-200);
	border-radius: 8px;
	padding: 1.75rem;
	text-decoration: none;
	transition: border-color 0.2s;
}
.tph-post-list__card:hover { border-color: var(--wp--preset--color--orange); }
.tph-post-list__tag { font-size: 10.5px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--wp--preset--color--teal); margin-bottom: var(--head-label-gap); }
.tph-post-list__title { font-family: var(--wp--preset--font-family--serif); font-size: 1.05rem; color: var(--wp--preset--color--gray-900); line-height: 1.4; margin-bottom: 0.875rem; }
/* margin-top:auto pins meta (and any .tph-post-list__more after it) to the
   bottom of the card, so equal-height cards in a row line up regardless of
   how long each excerpt runs — matches the handoff's flex-column feature-card. */
.tph-post-list__meta { margin-top: auto; padding-top: 1rem; font-size: 12.5px; color: var(--wp--preset--color--gray-700); line-height: 1.5; }
.tph-post-list__more { margin-top: 0.5rem; font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--wp--preset--color--teal); }
.tph-post-list__card:hover .tph-post-list__more { color: var(--wp--preset--color--orange); }
.tph-post-list__cta { margin-top: var(--block-gap); text-align: center; }
.tph-post-list__note { margin-top: var(--head-label-gap); font-size: 12.5px; color: var(--wp--preset--color--gray-500); font-style: italic; text-align: center; }

.tph-inline-link {
	font-size: 13px; font-weight: 500; color: var(--wp--preset--color--teal);
	text-decoration: none; border-bottom: 1px solid var(--wp--preset--color--teal); padding-bottom: 2px;
	display: inline-flex; align-items: center; gap: 6px;
	transition: color 0.2s, border-color 0.2s;
}
.tph-inline-link:hover { color: var(--wp--preset--color--orange-dark); border-bottom-color: var(--wp--preset--color--orange); }

@media (max-width: 900px) { .tph-post-list__grid { grid-template-columns: 1fr; } }

/* -- 17a. Resource archive pagination (tph/resource-archive) ---------------*/

.tph-post-list__pagination { margin-top: var(--block-gap); display: flex; justify-content: center; }
.tph-post-list__pagination ul { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }
.tph-post-list__pagination a,
.tph-post-list__pagination span {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 2.25rem; height: 2.25rem; padding: 0 0.75rem;
	border: 1px solid var(--wp--preset--color--gray-200); border-radius: 6px;
	font-size: 13px; color: var(--wp--preset--color--gray-700); text-decoration: none;
	transition: border-color 0.2s, color 0.2s;
}
.tph-post-list__pagination a:hover { border-color: var(--wp--preset--color--orange); color: var(--wp--preset--color--orange-dark); }
.tph-post-list__pagination span.current { border-color: var(--wp--preset--color--teal); color: var(--wp--preset--color--teal); font-weight: 600; }
.tph-post-list__pagination span.dots { border-color: transparent; }

/* Own classes, not tph-post-list__meta/__more: that pair's margin-top:auto
   on __meta is scoped to tph/post-list's short one-line meta text, pinning
   IT to the card bottom. Here the excerpt can run several lines and must
   sit in normal flow right under the title; __more is what always pins to
   the bottom instead, present or not. */
.tph-resource-archive__excerpt { margin-top: 0.875rem; color: var(--wp--preset--color--gray-700); line-height: 1.5; }
.tph-resource-archive__excerpt p { margin: 0; }
.tph-resource-archive__more { margin-top: auto; padding-top: 1rem; font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--wp--preset--color--teal); }
.tph-post-list__card:hover .tph-resource-archive__more { color: var(--wp--preset--color--orange); }

/* -- 18. Pathogen tags (tph/pathogen-tags) — the HOMEPAGE's own curated
   teaser (resistant vs. standard columns, "+ hundreds more" links), a
   manual repeater since it's deliberately a curated subset, not the real
   library. NOT the Pathogen Library hub page's block — see §18b,
   tph/pathogen-group, for that real per-category listing. ------------- */

.tph-pathogen-tags__grid { display: grid; grid-template-columns: var(--split-flip); column-gap: var(--col-gap); row-gap: var(--block-gap); align-items: start; }
.tph-pathogen-tags__text .tph-btn { margin-top: var(--head-label-gap); }
.tph-pathogen-tags__columns { display: grid; grid-template-columns: repeat(3, 1fr); column-gap: 1.5rem; row-gap: var(--block-gap); }

.tph-pathogen-tags__col-label {
	display: flex; align-items: center; gap: 8px;
	font-size: 11px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase;
	color: var(--wp--preset--color--gray-500);
	padding-bottom: 0.75rem; margin-bottom: var(--head-label-gap);
	border-bottom: 1px solid var(--wp--preset--color--gray-200);
}
.tph-pathogen-tags__col-desc { font-size: 12.5px; color: var(--wp--preset--color--gray-500); line-height: 1.6; margin: -0.5rem 0 var(--head-label-gap); }
.tph-pathogen-tags__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--wp--preset--color--orange); flex-shrink: 0; }

.tph-pathogen-tags__list { display: flex; flex-direction: column; gap: 0.5rem; }
.tph-pathogen-tags__tag {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	background: var(--wp--preset--color--off-white);
	border: 1px solid var(--wp--preset--color--gray-200);
	border-radius: 4px;
	padding: 0.55rem 0.875rem;
	font-size: 13px;
	color: var(--wp--preset--color--gray-700);
	text-decoration: none;
	transition: border-color 0.18s, color 0.18s, background 0.18s, transform 0.18s;
}
.tph-pathogen-tags__tag:hover { border-color: var(--wp--preset--color--orange); color: var(--wp--preset--color--gray-900); transform: translateY(-1px); }
.tph-pathogen-tags__tag.is-resistant { background: rgba(217,117,54,0.08); border-color: rgba(217,117,54,0.3); color: var(--wp--preset--color--orange-dark); font-weight: 500; }
.tph-pathogen-tags__tag.is-resistant:hover { background: rgba(217,117,54,0.14); border-color: var(--wp--preset--color--orange); }
.tph-pathogen-tags__more { font-style: italic; font-weight: 300; opacity: 0.75; background: transparent; border-style: dashed; }

@media (max-width: 960px) { .tph-pathogen-tags__grid { grid-template-columns: 1fr; } .tph-pathogen-tags__columns { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .tph-pathogen-tags__columns { grid-template-columns: 1fr; } }

/* -- 18b. Pathogen group (tph/pathogen-group) — the Pathogen Library hub
   page's real listing. ONE instance = ONE `pathogen_category` term,
   queried live from the `pathogen` CPT — the editor repeats this block
   once per category to build the full page (see block PHP for why this
   is a separate block from tph/pathogen-tags above, not a variant of it).
   `.tph-pathogen-group`/`.tph-pathogen-link` are the design's own
   `.pathogen-group`/`.pathogen-link` component, ported 1:1. ----------- */

/* Adjacent-instance spacing is the explicit `padding_top` override field
   (Module Settings), set per instance where needed — not inferred here,
   per the standing "overrides must be visible, never automatic" rule. */
.tph-pathogen-group { scroll-margin-top: 90px; }
.tph-pathogen-group__head { margin-bottom: 1.25rem; }
.tph-pathogen-group__title { font-family: var(--wp--preset--font-family--serif); font-size: 1.5rem; color: var(--wp--preset--color--navy); font-weight: 400; margin-bottom: 0.35rem; }
.tph-pathogen-group__blurb { color: var(--wp--preset--color--gray-500); font-weight: 300; max-width: 70ch; line-height: 1.6; }

.tph-pathogen-link-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.85rem; }
.tph-pathogen-link {
	display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
	padding: 0.9rem 1rem;
	background: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--gray-200);
	border-radius: 8px;
	text-decoration: none;
	transition: border-color 0.18s, box-shadow 0.18s, transform 0.18s;
}
.tph-pathogen-link:hover { border-color: var(--wp--preset--color--teal); box-shadow: 0 10px 26px -12px rgba(16,42,78,0.22); transform: translateY(-1px); }
.tph-pathogen-link__name { font-size: 0.95rem; font-weight: 500; color: var(--wp--preset--color--navy); }
.tph-pathogen-link.is-resistant { background: rgba(217,117,54,0.05); border-color: rgba(217,117,54,0.25); }
.tph-pathogen-link.is-resistant:hover { border-color: var(--wp--preset--color--orange); }
.tph-pathogen-link__tag {
	font-size: 9px; font-weight: 700; letter-spacing: 0.06em;
	color: var(--wp--preset--color--orange-dark);
	background: rgba(217,117,54,0.14);
	padding: 2px 6px; border-radius: 4px; flex-shrink: 0;
}

.tph-pathogen-group__note { font-size: 12.5px; color: var(--wp--preset--color--gray-500); font-style: italic; margin-top: 2.5rem; }

@media (max-width: 900px) { .tph-pathogen-link-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .tph-pathogen-link-grid { grid-template-columns: 1fr; } }

/* -- 19. CTA banner (tph/cta-banner) -----------------------------------------*/

.tph-cta-banner { position: relative; overflow: hidden; }
/* Decorative — only reads against a dark scheme, so scope it there rather
   than hardcoding a rgba(255,255,255,…) border that would vanish (correctly,
   but invisibly) on the Centered variant's more common white/paper use. */
[data-scheme="navy"] .tph-cta-banner::before,
[data-scheme="navy-mid"] .tph-cta-banner::before,
[data-scheme="deep"] .tph-cta-banner::before { content: ''; position: absolute; top: -100px; left: -100px; width: 400px; height: 400px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.04); pointer-events: none; }

.tph-cta-banner__row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--block-gap); position: relative; z-index: 1; }
.tph-cta-banner__copy { display: flex; flex-direction: column; gap: var(--head-label-gap); }
/* No hardcoded white — inherits from [data-scheme] like tph/process's
   workflow variant, since Centered runs on white/paper as often as navy. */
.tph-cta-banner__heading { font-family: var(--wp--preset--font-family--serif); font-size: 2.25rem; font-weight: 400; max-width: 540px; line-height: 1.2; margin: 0; }
.tph-cta-banner__heading em { font-style: italic; opacity: .8; }
.tph-cta-banner__text { max-width: 560px; color: var(--wp--preset--color--gray-500); font-size: 1rem; line-height: 1.8; font-weight: 300; margin: 0; }
.tph-cta-banner__btn { white-space: nowrap; flex-shrink: 0; }

.tph-cta-banner--centered .tph-cta-banner__row { flex-direction: column; justify-content: center; text-align: center; }
.tph-cta-banner--centered .tph-cta-banner__copy { align-items: center; }
.tph-cta-banner--centered .tph-cta-banner__text { margin-inline: auto; }

[data-scheme="navy"] .tph-cta-banner__text,
[data-scheme="navy-mid"] .tph-cta-banner__text,
[data-scheme="deep"] .tph-cta-banner__text { color: rgba(255,255,255,0.75); }

/* -- 19a. Related links (tph/related-links) ----------------------------------*/
/* Split out of cta-banner — no heading, no button, so it was never a CTA
   shape. Centered label + a row of inline text links, nothing else. */

.tph-related-links__inner { text-align: center; }
.tph-related-links__row { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; column-gap: 2rem; row-gap: var(--head-label-gap); margin-top: var(--head-label-gap); }


/* -- 20a. Shell modifier — narrow column for FAQ/prose-heavy sections ------*/

.tph-shell--narrow { max-width: 820px; }

/* -- 20b. Single post (single.php) --------------------------------------- */

.tph-post-single__meta { margin: 0; font-size: 12.5px; color: rgba(255, 255, 255, 0.55); }
/* Legacy post bodies carry real <img> tags with fixed width/height
   attributes from the old Divi build (up to 1230px wide) — the theme has
   no global image reset, so without this they overflow the narrow column. */
.tph-text__body img { max-width: 100%; height: auto; display: block; }
.tph-text__body a { color: var(--wp--preset--color--teal); }
.tph-text__body a:hover { color: var(--wp--preset--color--orange-dark); }
.tph-text__body hr { border: 0; border-top: 1px solid var(--wp--preset--color--gray-200); margin-block: var(--block-gap); }

/* -- 21. FAQ (tph/faq) --------------------------------------------------- */

/* Matches the handoff's own .faq-item/.faq-q/.faq-a exactly: a bottom-
   border divider list (no boxed card, no border-radius, no separate flex
   gap — the border + each item's own vertical padding IS the spacing),
   a serif teal "Q" marker before the question, sans-serif weight-500 navy
   question text (not the serif heading font used elsewhere in the theme).
   The +/- toggle is our own addition — the design's list isn't
   collapsible, but a native <details> accordion is a reasonable
   progressive enhancement over always-open text once there's more than a
   couple of questions, as long as it doesn't touch the visual language. */
/* Plain block flow, not flex: <details> is already block-level so this
   stacks identically to flex-column at 1 column, but CSS multi-column
   (the 2-column variant below) has no effect inside a flex container —
   it needs a normal block formatting context to work at all. */
.tph-faq__list { display: block; }
.tph-faq__item { border-bottom: 1px solid var(--wp--preset--color--gray-200); padding: 1.4rem 0; break-inside: avoid; }
/* 2 columns is desktop-only and auto-balances by height (CSS columns),
   not a strict left/right split — narrower widths stay single-column so
   answers never get squeezed into an unreadably thin strip. */
@media (min-width: 700px) {
	.tph-faq__list[data-columns="2"] { column-count: 2; column-gap: 3rem; }
}
.tph-faq__q { display: flex; align-items: baseline; gap: 0.7rem; cursor: pointer; list-style: none; }
.tph-faq__q::-webkit-details-marker { display: none; }
.tph-faq__q-mark { font-family: var(--wp--preset--font-family--serif); color: var(--wp--preset--color--teal); font-size: 1.15rem; line-height: 1; flex-shrink: 0; }
.tph-faq__q-text { margin: 0; font-family: var(--wp--preset--font-family--sans); font-size: 1.05rem; font-weight: 500; color: var(--wp--preset--color--navy); }
.tph-faq__q-toggle { margin-left: auto; flex-shrink: 0; color: var(--wp--preset--color--teal); }
.tph-faq__q-toggle::before { content: '+'; }
.tph-faq__item[open] .tph-faq__q-toggle::before { content: '−'; }
.tph-faq__a { margin-top: 0.55rem; padding-left: 1.7rem; font-weight: 300; color: var(--wp--preset--color--gray-700); line-height: 1.75; max-width: 70ch; }
.tph-faq__a p { margin: 0; }

/* -- 22. Info panel (tph/info-panel) — Scholarship, Internship ------------- */

.tph-info-panel__grid { display: grid; grid-template-columns: var(--split); column-gap: var(--col-gap); row-gap: var(--block-gap); align-items: start; }
.tph-info-panel--left .tph-info-panel__sidebar { order: -1; }
.tph-info-panel__main { display: flex; flex-direction: column; gap: var(--block-gap); }
.tph-info-panel__sidebar { padding: 1.85rem; background: var(--wp--preset--color--off-white); border: 1px solid var(--wp--preset--color--gray-200); border-radius: 10px; }
.tph-info-panel__sidebar-label { font-size: 12px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--wp--preset--color--teal); margin-bottom: 1rem; }
.tph-info-panel__sidebar-list { list-style: none; margin: 0 0 var(--block-gap); padding: 0; }
.tph-info-panel__sidebar-list li { display: flex; justify-content: space-between; gap: 1rem; padding: 0.55rem 0; border-bottom: 1px solid var(--wp--preset--color--gray-200); font-size: 13.5px; font-weight: 500; color: var(--wp--preset--color--gray-900); }
.tph-info-panel__sidebar-list li:last-child { border-bottom: none; }
.tph-info-panel__sidebar-k { font-weight: 400; color: var(--wp--preset--color--gray-500); }
.tph-info-panel__prompt { color: var(--wp--preset--color--gray-700); margin-bottom: var(--head-label-gap); }
.tph-info-panel__cta { display: block; width: 100%; text-align: center; }

@media (max-width: 900px) {
	.tph-info-panel__grid { grid-template-columns: 1fr; }
	.tph-info-panel--left .tph-info-panel__sidebar { order: 0; }
}
/* Sticky sidebar, matching the handoff's .aside-card — desktop-only (a
   sticky element stacked below the content on mobile has nothing to stick
   against, so this only exists inside the 2-column breakpoint). 100px
   clears the fixed navbar with room to spare. */
@media (min-width: 901px) { .tph-info-panel__sidebar { position: sticky; top: 100px; } }

/* Pathogen profile (single-pathogen.php) — related-pathogens list under the
   reused .tph-info-panel__sidebar, same key/value list rhythm as its own
   .tph-info-panel__sidebar-list but as plain links, not label/value pairs. */
.tph-pathogen-profile__related { margin-top: 1.75rem; }
.tph-pathogen-profile__related-list { list-style: none; margin: 0; padding: 0; }
.tph-pathogen-profile__related-list li { border-bottom: 1px solid var(--wp--preset--color--gray-200); }
.tph-pathogen-profile__related-list li:last-child { border-bottom: none; }
.tph-pathogen-profile__related-list a { display: block; padding: 0.55rem 0; font-size: 13.5px; color: var(--wp--preset--color--teal); text-decoration: none; }
.tph-pathogen-profile__related-list a:hover { color: var(--wp--preset--color--orange-dark); }

/* -- 23. Team grid (tph/team-grid) ------------------------------------------*/

/* Mobile-first: 1 column is the unconditional base (never overridden by a
   desktop column-count variant — the two live in non-overlapping media
   queries, not competing on specificity), 2 at tablet, 3/4/5 desktop-only. */
.tph-team-grid__grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; }
.tph-team-grid__card {
	display: block;
	text-align: left;
	color: inherit;
	text-decoration: none;
	border-radius: 8px;
	transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.tph-team-grid__card:hover,
.tph-team-grid__card:focus-visible {
	transform: translateY(-4px);
	box-shadow: 0 14px 28px rgba(16, 42, 78, 0.1);
}
.tph-team-grid__photo { aspect-ratio: 1; border-radius: 6px; overflow: hidden; background: var(--wp--preset--color--off-white); margin-bottom: 1rem; }
.tph-team-grid__img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.35s ease; }
.tph-team-grid__card:hover .tph-team-grid__img { transform: scale(1.04); }
/* Photo bleeds to the card edge; the text body gets its own breathing room
   so the hover shadow doesn't read as text glued to the card boundary. */
.tph-team-grid__body { padding: 0 1.5rem 1.5rem; }
.tph-team-grid__placeholder { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; color: var(--wp--preset--color--gray-500); }
.tph-team-grid__placeholder span { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; }
.tph-team-grid__role { font-size: 12.5px; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase; color: var(--wp--preset--color--teal); margin-bottom: 0.375rem; }
.tph-team-grid__name { font-family: var(--wp--preset--font-family--serif); font-size: 1.05rem; transition: color 0.25s ease; }
.tph-team-grid__card:hover .tph-team-grid__name { color: var(--wp--preset--color--teal); }
.tph-team-grid__credentials { font-family: var(--wp--preset--font-family--sans); font-size: 0.85rem; color: var(--wp--preset--color--gray-500); }
.tph-team-grid__desc { margin-top: 0.5rem; color: var(--wp--preset--color--gray-700); line-height: 1.65; }
.tph-team-grid__desc p { margin: 0; }
.tph-team-grid__links { display: flex; gap: 0.625rem; margin-top: 0.625rem; }
.tph-team-grid__link { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; background: var(--wp--preset--color--off-white); color: var(--wp--preset--color--navy); }
.tph-team-grid__link:hover { background: var(--wp--preset--color--teal); color: #fff; }
.tph-team-grid__note { margin-top: var(--block-gap); font-size: 12.5px; color: var(--wp--preset--color--gray-500); font-style: italic; }
.tph-team-grid__cta { margin-top: var(--block-gap); }

.tph-team-grid__iacuc { margin-top: var(--block-gap); padding-top: var(--block-gap); border-top: 1px solid var(--wp--preset--color--off-white); }
.tph-team-grid__iacuc-heading { font-family: var(--wp--preset--font-family--serif); font-size: 1.15rem; margin: 0 0 1rem; }
.tph-team-grid__iacuc-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.5rem 2rem; }
.tph-team-grid__iacuc-item { font-size: 13.5px; color: var(--wp--preset--color--gray-700); display: flex; flex-wrap: wrap; gap: 0.35em; }
.tph-team-grid__iacuc-name { font-weight: 500; }
.tph-team-grid__iacuc-note { color: var(--wp--preset--color--gray-500); }

@media (min-width: 601px) and (max-width: 900px) { .tph-team-grid__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 901px) {
	.tph-team-grid__grid { grid-template-columns: repeat(3, 1fr); }
	.tph-team-grid__grid[data-columns="4"] { grid-template-columns: repeat(4, 1fr); }
	.tph-team-grid__grid[data-columns="5"] { grid-template-columns: repeat(5, 1fr); }
}
@media (max-width: 600px) { .tph-team-grid__iacuc-list { grid-template-columns: 1fr; } }

/* -- 23b. Team profile (single-team_member.php) ------------------------------
   🔶 DELIBERATE, EXPLICIT EXCEPTION to every other detail page's navy-hero-
   carries-the-H1 shape (user's direct call, 2026-10-02, after 3 rounds that
   tried keeping the name/photo in a hero). The navy strip here is crumb-only,
   shrunk via a real class override — NOT a new data-pad tier, which the
   standing "one canonical section padding" rule (WordPress Theme Standards
   §6) forbids; this is scoped to one section on one page, not reusable.
   Name/role/photo/bio moved to the white body, reusing tph/media-text's own
   grid classes verbatim (--left modifier, __grid/__text/__media/__placeholder)
   — the same component the About page already uses for image+text sections,
   not a new one. Only the heading itself (.tph-team-profile__name/
   __heading/__credentials) is page-specific, since no existing class renders
   an H1 on a white background anywhere else in the theme. "More from our
   team" renders a REAL tph/team-grid block instance via render_block() (not
   a hand-rolled copy of its markup) — its 4-column variant and optional
   grid_cta button (23a above) were both already real, nothing forked here. */

.tph-team-profile-crumbs { padding-block: 20px; }

.tph-team-profile__heading { display: flex; flex-direction: column; gap: var(--head-label-gap); }
.tph-team-profile__name { font-family: var(--wp--preset--font-family--serif); font-weight: 400; font-size: var(--wp--preset--font-size--heading-xl); line-height: 1.15; color: var(--wp--preset--color--navy); margin: 0; }
.tph-team-profile__credentials { font-family: var(--wp--preset--font-family--sans); font-size: 1rem; color: var(--wp--preset--color--gray-500); }

/* -- 24. Newsletter signup (tph/newsletter-signup) --------------------------*/

.tph-newsletter-signup__row { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--block-gap); }
.tph-newsletter-signup__row .tph-head { margin-bottom: 0; flex: 1 1 320px; }
.tph-newsletter-signup__form { display: flex; gap: 0.75rem; flex: 0 1 420px; }
.tph-newsletter-signup__form input[type="email"] {
	flex: 1;
	min-width: 0;
	padding: 0.875rem 1rem;
	border: 1px solid var(--wp--preset--color--gray-200);
	border-radius: 4px;
	font-size: 14px;
	font-family: var(--wp--preset--font-family--sans);
}
.tph-newsletter-signup__form button,
.tph-newsletter-signup__form input[type="submit"] { flex-shrink: 0; border: none; cursor: pointer; }
.tph-newsletter-signup__form .wpcf7-form-control-wrap:not(.cf7apps-honeypot-wrap) { display: contents; }
.tph-newsletter-signup__form .wpcf7-not-valid-tip { flex-basis: 100%; font-size: 12px; color: var(--wp--preset--color--red, #c0392b); }
.tph-newsletter-signup__form .wpcf7-response-output { flex-basis: 100%; margin: 0; padding: 0.5rem 0.75rem; border-radius: 4px; font-size: 13px; }

@media (max-width: 700px) { .tph-newsletter-signup__form { flex: 1 1 100%; } }

/* -- 25. Contact form (tph/contact-form) -------------------------------------*/

.tph-contact-form__grid { display: grid; grid-template-columns: var(--split); column-gap: var(--col-gap); row-gap: var(--block-gap); align-items: start; }
.tph-contact-form__main { display: flex; flex-direction: column; gap: var(--block-gap); }
.tph-contact-form__card { background: var(--wp--preset--color--white); border: 1px solid var(--wp--preset--color--gray-200); border-radius: 12px; padding: 2.25rem; }
.tph-contact-form__form { display: flex; flex-direction: column; gap: 1.25rem; }
.tph-contact-form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
.tph-contact-form__field { display: flex; flex-direction: column; gap: 0.5rem; }
.tph-contact-form__field label { font-size: 12.5px; font-weight: 500; color: var(--wp--preset--color--gray-700); }
.tph-contact-form__field input,
.tph-contact-form__field select,
.tph-contact-form__field textarea {
	width: 100%;
	box-sizing: border-box;
	padding: 0.875rem 1rem;
	border: 1px solid var(--wp--preset--color--gray-200);
	border-radius: 4px;
	font-size: 14px;
	font-family: var(--wp--preset--font-family--sans);
	color: inherit;
	background: transparent;
}
.tph-contact-form__form button,
.tph-contact-form__form input[type="submit"] { align-self: flex-start; border: none; cursor: pointer; }
.tph-contact-form__form .wpcf7-form-control-wrap:not(.cf7apps-honeypot-wrap) { display: contents; }
.tph-contact-form__form .wpcf7-not-valid-tip { font-size: 12px; color: var(--wp--preset--color--red, #c0392b); margin-top: 0.25rem; }
.tph-contact-form__form .wpcf7-response-output { margin: 0; padding: 0.75rem 1rem; border-radius: 4px; font-size: 13.5px; }
.tph-contact-form__footnote { font-size: 12.5px; color: var(--wp--preset--color--gray-500); }
.tph-contact-form__sidebar { padding: 1.85rem; background: var(--wp--preset--color--off-white); border: 1px solid var(--wp--preset--color--gray-200); border-radius: 10px; display: flex; flex-direction: column; gap: 0.625rem; }
.tph-contact-form__sidebar-label { font-size: 11px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--wp--preset--color--gray-500); margin-bottom: 0.5rem; }
.tph-contact-form__company { font-weight: 500; color: var(--wp--preset--color--gray-900); }
.tph-contact-form__address,
.tph-contact-form__phone { font-size: 13.5px; color: var(--wp--preset--color--gray-700); }

@media (max-width: 900px) { .tph-contact-form__grid { grid-template-columns: 1fr; } }
@media (max-width: 600px) { .tph-contact-form__row { grid-template-columns: 1fr; } }

/* -- 26. Logo slider (tph/logo-slider) ---------------------------------------
   Known failure mode this was built to avoid (seen repeatedly on Akadeum's
   logo strip): letting each logo's own uploaded file dimensions dictate its
   box, plus ad hoc per-logo padding/margin instead of one shared gap token —
   together that produces uneven whitespace and a row that doesn't line up.
   Fixed here by giving every logo the same fixed-height box with
   object-fit:contain (never distorts/crops, just letterboxes inside an
   identical box regardless of source aspect ratio) and one shared gap token
   between logos — no logo can push its neighbors around. Uses --block-gap,
   not --col-gap: --col-gap is a 2-column SPLIT gap (clamp(28px,4.5vw,64px),
   meant for one gap between two large columns) — far too wide and viewport-
   dependent for a row of many small logos; --block-gap (32px fixed) is this
   theme's actual general-purpose gap. */
/* Reuses the sitewide .tph-label component (teal, left-aligned, dash
   prefix) instead of a one-off centered gray label — same component
   every other section's eyebrow label already uses. */
.tph-logo-slider .tph-label { margin-bottom: var(--head-label-gap); }
.tph-logo-slider__wrap { overflow: hidden; }
.tph-logo-slider__track { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: calc(var(--block-gap) * 1.5); }
.tph-logo-slider__logo { flex-shrink: 0; height: 64px; display: flex; align-items: center; }
/* Overrides tph_image()'s own .tph-img (width/height:100%, object-fit:cover —
   correct for a photo filling a fixed box, wrong here: a logo must never be
   cropped). A compound selector beats the bare .tph-img on specificity
   regardless of stylesheet order, same technique as .tph-media-text__stack. */
.tph-logo-slider__logo .tph-img { width: auto; height: 100%; max-width: 200px; object-fit: contain; }

/* No JS/motion (prefers-reduced-motion, or disabled): the duplicate copy
   (rendered for the seamless loop below) stays hidden and the track is a
   plain wrapped, centered, static row — real content, never a broken one. */
.tph-logo-slider__logo--dup { display: none; }

/* Motion enabled: a single continuous line, the list doubled end-to-end.
   TphSlider (assets/js/slider.js) drives the actual translateX() every
   frame — this just lays the track out ready for that (nowrap, intrinsic
   width) instead of letting it wrap. No @keyframes here: a %-based CSS
   animation's visual speed drifts with track width (more logos = faster
   same-duration loop); TphSlider's px/second loop doesn't. */
.tph-motion .tph-logo-slider__track { flex-wrap: nowrap; justify-content: flex-start; width: max-content; }
.tph-motion .tph-logo-slider__logo--dup { display: flex; }

/* -- 20. Motion — one mechanism, CSS decides what .is-in means -------------
   Ships the finished (visible) state by default. `.tph-motion` (added by
   motion.js only after confirming motion is wanted) is what opts into
   hiding things first — no JS, or prefers-reduced-motion, and everything
   is simply visible. Same approach as Top Notch. */

.tph-motion [data-reveal] {
	transition: opacity 0.7s cubic-bezier(.22, 1, .36, 1), transform 0.7s cubic-bezier(.22, 1, .36, 1);
}
.tph-motion [data-reveal]:not(.is-in) {
	opacity: 0;
	transform: translateY(18px);
}
.tph-motion [data-reveal].is-in {
	opacity: 1;
	transform: none;
}

.tph-motion [data-reveal-child] {
	transition: opacity 0.5s cubic-bezier(.22, 1, .36, 1), transform 0.5s cubic-bezier(.22, 1, .36, 1);
	transition-delay: calc(var(--i, 0) * 80ms);
}
.tph-motion [data-reveal]:not(.is-in) [data-reveal-child] {
	opacity: 0;
	transform: translateY(12px);
}
.tph-motion [data-reveal].is-in [data-reveal-child] {
	opacity: 1;
	transform: none;
}
