/**
 * Holler Blocks — Global CSS
 *
 * Site-wide custom CSS that goes beyond theme.json tokens.
 * Design tokens (colors, type scale, spacing) belong in theme.json —
 * reference them here as CSS custom properties, e.g.:
 *
 *   var(--wp--preset--color--primary)
 *   var(--wp--preset--font-size--heading-2)
 *   var(--wp--preset--spacing--space-md)
 *
 * Elementor widgets and custom code can use the same variables, so
 * theme.json stays the single source of truth for the design system.
 */

/* ---------------------------------------------------------------------------
 * Forms
 *
 * Baseline styling for text inputs, textareas, and selects on the tokens —
 * covers the comment form, the search block, and plugin-rendered forms.
 * Element-level selectors on purpose: form plugins and Elementor can
 * override without specificity fights.
 * ------------------------------------------------------------------------ */

input[type="text"],
input[type="email"],
input[type="url"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="number"],
input[type="date"],
textarea,
select {
	font: inherit;
	color: var(--wp--preset--color--neutral-dark);
	background-color: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--neutral-light);
	border-radius: var(--wp--custom--radius--small);
	padding: 0.5rem 0.75rem;
	max-width: 100%;
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

textarea {
	line-height: 1.5;
}

input[type="text"]:focus,
input[type="email"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="tel"]:focus,
input[type="number"]:focus,
input[type="date"]:focus,
textarea:focus,
select:focus {
	outline: none;
	border-color: var(--wp--preset--color--primary);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--wp--preset--color--primary) 20%, transparent);
}

::placeholder {
	color: var(--wp--preset--color--neutral-mid);
	opacity: 0.7;
}

form label {
	font-size: var(--wp--preset--font-size--text-small);
	font-weight: 600;
	color: var(--wp--preset--color--neutral-dark);
}

input[type="checkbox"],
input[type="radio"] {
	accent-color: var(--wp--preset--color--primary);
}

/* Comment form rhythm */
.comment-form p {
	margin: 0 0 1rem;
}
.comment-form label {
	display: block;
	margin-bottom: 0.25rem;
}
.comment-form input:not([type="checkbox"]):not([type="submit"]),
.comment-form textarea {
	width: 100%;
}

@media (prefers-reduced-motion: reduce) {
	input, textarea, select { transition: none; }
}

/* ---------------------------------------------------------------------------
 * Gravity Forms token skin
 *
 * GF ≥2.7 (theme framework / Orbital) exposes --gf-* variables scoped on
 * .gform-theme--framework — mapping them to the theme tokens replaces the
 * per-site "GF styler" addons. The -rgb triplet (used for focus glows)
 * can't be derived from a var, so it stays the default-brand indigo; under
 * a style variation only that subtle glow tint keeps the default hue.
 * ------------------------------------------------------------------------ */

.gform-theme--framework,
.gform-theme--api {
	--gf-color-primary: var(--wp--preset--color--primary);
	--gf-color-primary-rgb: 65, 62, 197;
	--gf-color-primary-contrast: var(--wp--preset--color--white);
	--gf-color-primary-darker: var(--wp--preset--color--primary);
	--gf-color-primary-lighter: var(--wp--preset--color--secondary);
	--gf-ctrl-border-color: var(--wp--preset--color--neutral-light);
	--gf-ctrl-color: var(--wp--preset--color--neutral-dark);
	--gf-ctrl-bg: var(--wp--preset--color--white);
	--gf-ctrl-radius: 6px;
	--gf-ctrl-accent-color: var(--wp--preset--color--primary);
	--gf-color-in-ctrl-primary: var(--wp--preset--color--primary);
	--gf-color-in-ctrl-primary-contrast: var(--wp--preset--color--white);
}

/* Legacy gravity-theme (pre-framework) submit button + labels */
.gform_wrapper .gform_button,
.gform_wrapper input[type="submit"] {
	font: inherit;
	font-weight: 600;
	color: var(--wp--preset--color--white);
	background: var(--wp--preset--color--primary);
	border: none;
	border-radius: var(--wp--custom--radius--small);
	padding: 0.6rem 1.4rem;
	cursor: pointer;
}
.gform_wrapper .gfield_label {
	font-size: var(--wp--preset--font-size--text-small);
	font-weight: 600;
	color: var(--wp--preset--color--neutral-dark);
}
.gform_wrapper .gfield_description {
	color: var(--wp--preset--color--neutral-mid);
	font-size: var(--wp--preset--font-size--caption);
}

/* ---------------------------------------------------------------------------
 * Pattern helpers
 * ------------------------------------------------------------------------ */

/* Dual-color heading block style: italicize the words to highlight —
   they render upright in the primary color. */
.is-style-dual-color em {
	font-style: normal;
	color: var(--wp--preset--color--primary);
}
.has-dark-bg-background-color .is-style-dual-color em {
	color: var(--wp--preset--color--secondary);
}

/* Stats pattern: lining, columnar digits */
.stat-number {
	font-variant-numeric: tabular-nums;
	line-height: 1;
}

/* Dark section block style (core/group): one class instead of hand-setting
   dark-bg + white on the group and every child. :where() keeps specificity
   at zero, so explicit has-*-color choices and preset classes still win. */
.is-style-section-dark {
	background-color: var(--wp--preset--color--dark-bg);
	color: var(--wp--preset--color--white);
}
.is-style-section-dark :where(h1, h2, h3, h4, h5, h6) {
	color: inherit;
}
.is-style-section-dark :where(p, li, cite, figcaption) {
	color: inherit;
}
.is-style-section-dark :where(a:not(.wp-block-button__link)) {
	color: var(--wp--preset--color--secondary);
}

/* Outline buttons on dark grounds: they inherit dark text and vanish.
   Flip to white wherever the section is dark. */
.has-dark-bg-background-color .wp-block-button.is-style-outline .wp-block-button__link,
.is-style-section-dark .wp-block-button.is-style-outline .wp-block-button__link {
	color: var(--wp--preset--color--white);
	border-color: currentColor;
}

/* ---------------------------------------------------------------------------
 * Accordion
 *
 * The comps rule each row off with a hairline and set the question at 600
 * with the marker pushed to the right edge. Core ships a bare <details>
 * with a list marker, so without this the rows run together as plain text
 * — which is what "doesn't match the mockup" looks like.
 * ------------------------------------------------------------------------ */

.wp-block-details {
	border-top: 1px solid var(--wp--preset--color--hairline);
}
.wp-block-details:last-of-type {
	border-bottom: 1px solid var(--wp--preset--color--hairline);
}
.is-style-section-dark .wp-block-details,
.has-dark-bg-background-color .wp-block-details {
	border-color: var(--wp--custom--on-dark--hairline);
}

.wp-block-details > summary {
	list-style: none;
	cursor: pointer;
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
	gap: 1rem;
	padding-block: 1.5rem;
	font-size: var(--wp--preset--font-size--heading-6);
	font-weight: 600;
	letter-spacing: -0.025em;
	transition: color 0.2s ease;
}
.wp-block-details > summary:hover,
.wp-block-details[open] > summary {
	color: var(--wp--preset--color--primary);
}
.wp-block-details > summary::marker,
.wp-block-details > summary::-webkit-details-marker {
	content: "";
	display: none;
}

/* One glyph that rotates, rather than swapping + for −: the comps turn the
   plus into a cross on open. */
.wp-block-details > summary::after {
	content: "+";
	font-family: var(--wp--preset--font-family--mono);
	font-size: 1.3em;
	font-weight: 400;
	line-height: 1;
	flex-shrink: 0;
	color: var(--wp--preset--color--neutral-light);
	transition: transform 0.35s var(--wp--custom--ease), color 0.2s ease;
}
.wp-block-details[open] > summary::after {
	transform: rotate(45deg);
	color: var(--wp--preset--color--primary);
}

/* The panel keeps a readable measure and clears the row below it. */
.wp-block-details > *:not(summary) {
	max-width: var(--wp--custom--measure--default);
	padding-bottom: 1.75rem;
}

/* The leading number in a self-check question is a quiet mono label. */
.sc-n {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.7em;
	font-weight: 500;
	color: var(--wp--preset--color--neutral-light);
	margin-right: 0.9rem;
}

/* Logo strip: mute logos until hover */
.logo-strip img {
	filter: grayscale(1);
	opacity: 0.6;
	transition: filter 0.2s ease, opacity 0.2s ease;
}
.logo-strip img:hover {
	filter: none;
	opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
	.logo-strip img { transition: none; }
}

/* ---------------------------------------------------------------------------
 * Legacy content guard
 *
 * Pages exported from Elementor carry inline icon SVGs with no width
 * attribute; without Elementor's CSS they expand to full container width.
 * Cap unsized SVGs in post content at icon size until those pages are
 * rebuilt as blocks.
 * ------------------------------------------------------------------------ */

.wp-block-post-content svg:not([width]) {
	max-width: 2em;
	max-height: 2em;
}

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

.site-header {
	z-index: 100;
	border-bottom-style: solid;
}

/* The lockup, the button and the menu icon needed 347px of a 390px phone's
   342px of content, so the header wrapped to two lines on every handset
   narrower than about 430. The three rules below buy back 21px of it. The
   outer gap is the cheapest: that row is `space-between`, so its gap is only
   a minimum separation and the space between the lockup and the button is
   whatever is left over either way. */
@media (max-width: 460px) {
	.site-header .wp-block-group.is-content-justification-space-between {
		gap: 0.5rem;
	}

	.site-header .wp-block-group:has(> .header-cta) {
		gap: 0.75rem;
	}

	.site-header .header-cta .wp-block-button__link {
		padding-left: 1rem;
		padding-right: 1rem;
	}
}

/* ---------------------------------------------------------------------------
 * Flyout menu
 *
 * The core Navigation block's mobile overlay, restyled as a right-hand
 * slide-in panel.
 *
 * `.is-menu-open` is doubled throughout, on the full container selector. Core
 * styles the same elements from `.wp-block-navigation__responsive-container
 * .is-menu-open X` — three classes — and loads after this file, so anything
 * that only ties with it loses — and it loses selectively: core's `padding` shorthand wipes a
 * `padding-block` set here while `display` and `border-bottom` survive, which
 * looks like the rule half-applying rather than being overridden.
 * ------------------------------------------------------------------------ */

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open {
	/* Without border-box the width is content-box and core's root padding is
	   added on top: a 22rem panel measured 399px on a 390px phone, hanging off
	   the left edge, and left a stripe of the page showing on anything wider.
	   Full width below 30em, because a panel that nearly covers the screen
	   reads as a mistake rather than as a panel. */
	box-sizing: border-box;
	left: auto;
	right: 0;
	width: 100vw;
	background-color: var(--wp--preset--color--white);
	box-shadow: var(--wp--preset--shadow--large);
	padding: 1rem 1.5rem 1.5rem;
	animation: holler-flyout-in 0.25s ease both;
	overscroll-behavior: contain;
}

@media (min-width: 30em) {
	.wp-block-navigation__responsive-container.is-menu-open.is-menu-open {
		width: 23rem;
		/* Above this width the page shows beside the panel, so it needs to
		   recede. The second shadow is the scrim: a spread large enough to
		   cover the viewport paints everything outside the panel without an
		   extra element or a stacking-context change. */
		box-shadow: var(--wp--preset--shadow--large),
			0 0 0 100vmax rgba(20, 33, 61, 0.38);
	}
}

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .wp-block-navigation__responsive-container-content {
	/* Clears the close button, which core pins to the top right. */
	padding-top: 3.5rem;
	width: 100%;
	align-items: stretch;
	justify-content: flex-start;
}

/* The header bar sets justifyContent:right and a 1.5rem blockGap for the
   desktop row. Down a vertical panel that gives every item a different left
   edge and floats them apart — the ragged column is what reads as unfinished. */
.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .wp-block-navigation__container {
	align-items: stretch;
	gap: 0;
	margin: 0;
	padding: 0;
	/* The list is a flex item in a column the header justifies to the right,
	   so it shrank to its widest label and was pushed off the panel's gutter —
	   the menu's left edge never lined up with the close button on the right. */
	width: 100%;
}

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .wp-block-navigation-item {
	width: 100%;
}

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .wp-block-navigation-item__content {
	display: block;
	box-sizing: border-box;
	width: 100%;
	padding: 0.85rem 0;
	font-size: var(--wp--preset--font-size--heading-4);
	line-height: 1.25;
	border-bottom: 1px solid var(--wp--preset--color--hairline);
}

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open
	.wp-block-navigation__container > .wp-block-navigation-item:last-child
	.wp-block-navigation-item__content {
	border-bottom: 0;
}

/* A submenu parent in this menu is a "#" link — a section label, not a
   destination. The overlay shows its children regardless, so the chevron that
   would toggle them is noise. */
.wp-block-navigation__responsive-container.is-menu-open.is-menu-open
	.wp-block-navigation-submenu > .wp-block-navigation-item__content {
	margin-top: 1.4rem;
	padding: 0 0 0.4rem;
	font-size: var(--wp--preset--font-size--overline);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.14em;
	color: var(--wp--preset--color--neutral-light);
	border-bottom: 0;
	pointer-events: none;
}

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .wp-block-navigation__submenu-icon {
	display: none;
}

/* Core styles this one from a five-class selector
   (`…is-menu-open:where(…) .…-container-content .has-child .…submenu-container`)
   which sets `padding-left: 2rem` and `width: auto`. Six classes to beat it:
   `width: auto` is what made the sub-list shrink to its widest label and sit
   wherever the header's right justification left it. */
.wp-block-navigation__responsive-container.is-menu-open.is-menu-open
	.wp-block-navigation__responsive-container-content
	.has-child .wp-block-navigation__submenu-container {
	gap: 0;
	margin: 0;
	padding: 0 0 0 1rem;
	width: 100%;
	min-width: 0;
	border: 0;
	background: transparent;
}

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open
	.wp-block-navigation__submenu-container .wp-block-navigation-item__content {
	padding: 0.65rem 0;
	font-size: var(--wp--preset--font-size--text-normal);
	color: var(--wp--preset--color--neutral-mid);
}

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open
	.wp-block-navigation__submenu-container
	.wp-block-navigation-item:last-child .wp-block-navigation-item__content {
	border-bottom: 0;
}

/* The header's "Book a call" sits behind the open panel, so the menu carries
   its own. It rides in the menu itself to stay editable, and is hidden
   wherever the header bar is showing its button already. */
.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .nav-cta .wp-block-navigation-item__content {
	margin-top: 1.75rem;
	padding: 0.9rem 1.25rem;
	border: 0;
	border-radius: var(--wp--custom--radius--pill);
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
	font-size: var(--wp--preset--font-size--text-normal);
	font-weight: 600;
	text-align: center;
}

.wp-block-navigation:not(.is-responsive) .nav-cta,
.wp-block-navigation__responsive-container:not(.is-menu-open) .nav-cta {
	display: none;
}

@keyframes holler-flyout-in {
	from { transform: translateX(100%); }
	to   { transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
	.wp-block-navigation__responsive-container.is-menu-open.is-menu-open {
		animation: none;
	}
}

/* ---------------------------------------------------------------------------
 * Mega menu panels
 *
 * The panel is a child of its nav item but has to span the whole header, so
 * it hangs off the header's own box. `.site-header` is sticky, which makes it
 * a containing block for an absolutely positioned descendant — no extra
 * wrapper needed, and the panel lines up with the header's bottom edge
 * whatever the header's height.
 * ------------------------------------------------------------------------ */

/* Core positions a submenu parent so its dropdown can hang off it. A mega
   panel spans the header instead, so the item has to stop being the
   containing block — at more classes than core's rule, which is two. */
.wp-block-navigation .has-child.has-holler-mega,
.has-holler-mega.has-holler-mega {
	position: static;
}

.holler-mega-panel {
	/* Fixed, not absolute. Between the item and the header sit four elements
	   core positions itself — the nav, the responsive container, its dialog
	   and the list — and the responsive container has to keep its position
	   because that is what makes the mobile overlay a full-screen layer.
	   Fixed skips all of them; the script hands over where the header ends. */
	position: fixed;
	left: 0;
	right: 0;
	top: var(--holler-mega-top, 4.5rem);
	z-index: 90;
	background-color: var(--wp--preset--color--white);
	border-top: 1px solid var(--wp--preset--color--hairline);
	box-shadow: var(--wp--preset--shadow--large);
	opacity: 0;
	transform: translateY(-6px);
	transition: opacity 0.16s var(--wp--custom--ease),
		transform 0.16s var(--wp--custom--ease);
}

.holler-mega-panel.is-mega-open {
	opacity: 1;
	transform: none;
}

.holler-mega-panel__inner {
	max-width: var(--wp--style--global--content-size);
	margin: 0 auto;
	padding: var(--wp--custom--section--large) var(--wp--custom--section--inline);
}

/* The chevron turns over while the panel is open, the same way core's does. */
.has-holler-mega.is-mega-open .wp-block-navigation__submenu-icon svg {
	transform: rotate(180deg);
}

.has-holler-mega .wp-block-navigation__submenu-icon svg {
	transition: transform 0.16s var(--wp--custom--ease);
}

@media (prefers-reduced-motion: reduce) {
	.holler-mega-panel,
	.has-holler-mega .wp-block-navigation__submenu-icon svg {
		transition: none;
	}
}

/* Inside the flyout the panel is not a panel: core stacks the menu open on
   mobile, so the content simply flows where the dropdown would have been. */
.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .holler-mega-panel {
	/* The panel ships hidden so it cannot flash before the script runs. In
	   the overlay there is nothing to open it — core stacks the menu — so it
	   has to be shown here, or the item's links are unreachable on a phone. */
	display: block;
	position: static;
	opacity: 1;
	transform: none;
	border-top: 0;
	box-shadow: none;
	background: transparent;
}

.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .holler-mega-panel__inner {
	padding: 0 0 var(--wp--preset--spacing--space-sm);
	max-width: none;
}

/* A panel's framing — its eyebrow, headline and standfirst — earns its place
   on a wide bar and is dead weight in a phone menu, where the reader is
   scrolling past it to reach the links. Put `mega-intro` on that part of a
   panel and the overlay leaves it out. */
.wp-block-navigation__responsive-container.is-menu-open.is-menu-open .mega-intro {
	display: none;
}

/* ---------------------------------------------------------------------------
 * Link-in-bio stack
 *
 * The whole card is one anchor, so the tap target is the card rather than the
 * words in it — which is the only thing that matters on the page these get
 * opened from. Blocks have no wrapper-link, so each card is a paragraph whose
 * content is that anchor.
 * ------------------------------------------------------------------------ */

.linkbio {
	max-width: 34rem;
	margin-inline: auto;
}

.linkbio > * + * {
	margin-top: 0.9rem;
}

.linkbio-card a {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	position: relative;
	padding: 1.15rem 1.35rem;
	border: 2px solid transparent;
	border-radius: var(--wp--custom--radius--base);
	background-color: var(--wp--preset--color--white);
	color: var(--wp--preset--color--neutral-dark);
	text-decoration: none;
	box-shadow: var(--wp--preset--shadow--medium);
	transition: transform 0.15s var(--wp--custom--ease),
		border-color 0.15s var(--wp--custom--ease),
		box-shadow 0.15s var(--wp--custom--ease);
}

.linkbio-card a:hover,
.linkbio-card a:focus-visible {
	transform: translateY(-2px);
	border-color: var(--wp--preset--color--primary);
	box-shadow: var(--wp--preset--shadow--large);
	color: var(--wp--preset--color--neutral-dark);
}

.linkbio-title {
	display: block;
	font-weight: 700;
	font-size: var(--wp--preset--font-size--text-large);
	line-height: 1.25;
}

.linkbio-sub {
	display: block;
	margin-top: 0.2rem;
	font-weight: 500;
	font-size: var(--wp--preset--font-size--text-small);
	color: var(--wp--preset--color--neutral-mid);
}

.linkbio-arrow {
	flex-shrink: 0;
	width: 2rem;
	height: 2rem;
	border-radius: var(--wp--custom--radius--pill);
	background-color: var(--wp--preset--color--light-bg);
	color: var(--wp--preset--color--primary);
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 800;
}

/* The tag rides on the card's top edge, so the card needs the room above it
   that the stack's own gap would otherwise swallow. */
.linkbio-card.has-tag {
	margin-top: 1.6rem;
}

.linkbio-tag {
	position: absolute;
	top: -0.7rem;
	left: 1.25rem;
	padding: 0.2rem 0.65rem;
	border-radius: var(--wp--custom--radius--pill);
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
	font-size: var(--wp--preset--font-size--overline);
	font-weight: 800;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	line-height: 1.6;
}

.linkbio-card.has-tag a {
	border-color: var(--wp--preset--color--primary);
}

/* One card carries the offer; it is filled rather than outlined so it is the
   thing the eye lands on first. */
.linkbio-card.is-featured a {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
}

.linkbio-card.is-featured a:hover,
.linkbio-card.is-featured a:focus-visible {
	border-color: var(--wp--preset--color--white);
	color: var(--wp--preset--color--white);
}

.linkbio-card.is-featured .linkbio-sub {
	color: var(--wp--custom--on-dark--secondary);
}

.linkbio-card.is-featured .linkbio-arrow {
	background-color: var(--wp--preset--color--white);
	color: var(--wp--preset--color--primary);
}

.linkbio-avatar img {
	border-radius: var(--wp--custom--radius--pill);
	border: 3px solid var(--wp--preset--color--primary);
}

@media (prefers-reduced-motion: reduce) {
	.linkbio-card a {
		transition: none;
	}
}

/* ── Per-post display states ──────────────────────────────────────────
   Set from the Holler Display metabox, or the set-display-settings
   ability. Hiding the header or footer removes the block outright, so
   only the two positional states need CSS. */

.holler-sticky-header .wp-site-blocks > header {
	position: sticky;
	top: 0;
	z-index: 100;
}

/* The header lifts out of flow and sits over the first section, so that
   section has to carry its own top spacing — a hero band usually does.
   :where() keeps this at zero specificity so a header part's own
   background still wins if it sets one deliberately. */
.holler-transparent-header .wp-site-blocks > header {
	position: absolute;
	inset-inline: 0;
	z-index: 100;
}

.holler-transparent-header .wp-site-blocks > :where(header),
.holler-transparent-header .wp-site-blocks > :where(header) :where(.wp-block-group) {
	background-color: transparent;
	border-bottom-color: transparent;
}

/* ── Full-bleed sections butt together ────────────────────────────────
   A section carries its own vertical padding, so the root blockGap
   between two of them is not rhythm — it is a strip of page background
   showing between two coloured bands, which reads as a seam wherever
   the two colours differ. Zero it for full-width blocks only; ordinary
   top-level blocks keep their spacing. */

.wp-block-post-content > .alignfull,
.entry-content > .alignfull {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* The same gap exists one level up, between the header, the main
   region and the footer. It is invisible while the header and the
   first section are both white, and a seam the moment either is not —
   most obviously above the footer, which is a dark band. */

.wp-site-blocks > header,
.wp-site-blocks > main,
.wp-site-blocks > footer {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* ── Section scale: mobile step ───────────────────────────────────────
   The section scale is fixed pixel values on a 4/8 grid rather than
   fluid clamps, because a vw-based middle lands on fractional pixels
   (14.5vw was 208.8px at 1440, 79.2px for the next step down) and no
   grid survives that. Stepping at the breakpoint keeps every value a
   multiple of 8 at every viewport width. */

@media (max-width: 767px) {
	:root {
		--wp--custom--section--xxl: 80px;
		--wp--custom--section--xl: 64px;
		--wp--custom--section--large: 48px;
		--wp--custom--section--inline: 24px;
	}
}

/* ---------------------------------------------------------------------------
 * Mockup vocabulary
 *
 * The Claude Design comps share one design system across pages: cards, spec
 * lists, checklists, chip rows, a sketched underline. The converter emits
 * these class names; the styling belongs here so a rule fixes every page at
 * once and the block markup stays token-only.
 * ------------------------------------------------------------------------ */

/* Cards. A column the comp draws with a border and a radius. */
.wp-block-column.hd-card,
.wp-block-group.hd-card {
	background-color: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--base);
	box-shadow: var(--wp--preset--shadow--medium);
	transition:
		transform 0.4s var(--wp--custom--ease),
		box-shadow 0.4s,
		background-color 0.35s;
}
.is-style-section-dark .wp-block-column.hd-card,
.is-style-section-dark .wp-block-group.hd-card,
.has-dark-bg-background-color .wp-block-column.hd-card,
.has-dark-bg-background-color .wp-block-group.hd-card {
	background-color: var(--wp--custom--on-dark--surface);
	border-color: var(--wp--custom--on-dark--hairline);
	box-shadow: none;
}

/* The comp lifts a card under the cursor: .rate:hover is translateY(-4px)
   onto the large shadow, from the medium shadow it rests on.
 *
 * Keyed on the card class. The older rule in site.css keys the same lift on
 * [style*="border-radius"] -- an inline style attribute -- so a card styled
 * by class alone never matched it, which is why the four cards on /seo/ sat
 * dead under the cursor while the homepage's lifted. A card is a card
 * because it says .hd-card, not because of what is in its style attribute.
 */
.wp-block-column.hd-card:hover,
.wp-block-group.hd-card:hover {
	transform: translateY(-4px);
	box-shadow: var(--wp--preset--shadow--large);
}

/* On a dark band the lift reads as the surface brightening: the large
   shadow is a dark shadow on a dark ground, and shows nothing. */
.is-style-section-dark .wp-block-column.hd-card:hover,
.is-style-section-dark .wp-block-group.hd-card:hover,
.has-dark-bg-background-color .wp-block-column.hd-card:hover,
.has-dark-bg-background-color .wp-block-group.hd-card:hover {
	background-color: var(--wp--custom--on-dark--surface-hover);
	box-shadow: 0 26px 60px rgba(0, 0, 0, 0.3);
}

@media (prefers-reduced-motion: reduce) {
	.wp-block-column.hd-card,
	.wp-block-group.hd-card {
		transition: none;
	}
	.wp-block-column.hd-card:hover,
	.wp-block-group.hd-card:hover {
		transform: none;
	}
}

/* Spec list: a label and its value, ruled apart. */
.wp-block-list.spec-list {
	list-style: none;
	padding-left: 0;
	margin-block: 1rem;
}
.wp-block-list.spec-list li {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 1rem;
	font-size: var(--wp--preset--font-size--text-small);
	padding-block: 0.35rem;
}
.wp-block-list.spec-list li b,
.wp-block-list.spec-list li strong {
	font-weight: 600;
	text-align: right;
	white-space: nowrap;
}

/* `nowrap` is what keeps a spec row reading as one line, and it holds only
   while the value is short. A value that is a sentence — "Custom build,
   memberships, payments, integrations" — does not shrink to fit, it pushes
   the page sideways instead: every case-study page scrolled horizontally on
   a phone, by as much as 120px, with nothing but a stray scrollbar to say so.
   Narrow screens stack the pair, which gives the value the whole width to
   wrap into and drops the right-alignment that only made sense in a column. */
@media (max-width: 600px) {
	.wp-block-list.spec-list li {
		display: block;
	}

	.wp-block-list.spec-list li b,
	.wp-block-list.spec-list li strong {
		display: block;
		margin-top: 0.15rem;
		text-align: left;
		white-space: normal;
	}
}

/* Checklist: inclusions, ticked. */
.wp-block-list.check-list {
	list-style: none;
	padding-left: 0;
	margin-block: 1rem;
}
.wp-block-list.check-list li {
	position: relative;
	padding-left: 1.6em;
	margin-block: 0.55rem;
	font-size: var(--wp--preset--font-size--text-small);
}
.wp-block-list.check-list li::before {
	content: "✓";
	position: absolute;
	left: 0;
	font-weight: 700;
	color: var(--wp--preset--color--primary);
}
.is-style-section-dark .wp-block-list.check-list li::before {
	color: var(--wp--preset--color--secondary);
}

/* Chip row: short qualifiers set as pills. */
.wp-block-list.pill-row {
	list-style: none;
	padding-left: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-block: 1.5rem;
}
.wp-block-list.pill-row li {
	font-size: var(--wp--preset--font-size--caption);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--wp--preset--color--neutral-mid);
	background-color: var(--wp--preset--color--light-bg);
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--pill);
	padding: 8px 14px;
}
.is-style-section-dark .wp-block-list.pill-row li {
	color: inherit;
	background-color: var(--wp--custom--on-dark--surface);
	border-color: var(--wp--custom--on-dark--hairline);
}

/* A totals figure: the number leads, its unit sits under it. */
.stat-total {
	font-variant-numeric: tabular-nums;
	line-height: 1.1;
}
.stat-total span {
	display: block;
	font-size: var(--wp--preset--font-size--caption);
	font-weight: 500;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	opacity: 0.6;
}

/* The unit suffix on a stat figure (6x, +164%) is set back from the number. */
.stat-number .u {
	color: var(--wp--preset--color--neutral-light);
	font-size: 0.62em;
	letter-spacing: 0;
	vertical-align: 0.06em;
}

/* Sketched underline: a hand-drawn rule the comps put under a phrase. The
   svg is absolutely positioned, so it needs the span to be the containing
   block — and an explicit size, or the theme's 2em cap shrinks it. */
.sketch {
	position: relative;
	display: inline-block;
}
.sketch svg {
	position: absolute;
	left: 0;
	bottom: -0.1em;
	width: 100%;
	height: 0.2em;
	overflow: visible;
}
.sketch path {
	stroke: var(--wp--preset--color--primary);
	stroke-width: 5;
	fill: none;
	stroke-linecap: round;
}

/* Eyebrow: a short rule, then the label. The comps lead with a drawn dash,
   not a bullet. */
.eyebrow-dot::before {
	content: "";
	display: inline-block;
	width: 24px;
	height: 2px;
	margin-right: 10px;
	vertical-align: 0.3em;
	background-color: currentColor;
}

/* Price figure: the number carries, the unit rides small beside it. */
.price-figure {
	font-variant-numeric: tabular-nums;
	letter-spacing: -0.03em;
}
.price-figure .per,
.price-figure .unit {
	font-size: 0.42em;
	font-weight: 500;
	letter-spacing: 0;
	color: var(--wp--preset--color--neutral-mid);
}

/* Verdict badge inside a self-check answer. */
.verdict-badge {
	display: inline-block;
	border-radius: var(--wp--custom--radius--pill);
	padding: 6px 12px;
	margin-bottom: 0.75rem;
	background-color: var(--wp--preset--color--light-bg);
	color: var(--wp--preset--color--primary);
}
.is-style-section-dark .verdict-badge {
	background-color: var(--wp--custom--on-dark--surface);
	color: var(--wp--preset--color--secondary);
}

/* A lead paragraph keeps a readable measure instead of running the column. */
/* A capped measure. Nothing here fights the layout: a paragraph narrower
   than its container is centred by *constrained* layout, so the converter
   puts it in a flow-layout group instead, where it sits flush left. */
.lead-measure {
	max-width: var(--wp--custom--measure--default);
}
.lead-measure.measure-tight {
	max-width: var(--wp--custom--measure--tight);
}
.lead-measure.measure-wide {
	max-width: var(--wp--custom--measure--wide);
}

/* Body copy on a dark band sits back from the headings, as in the comps. */
.is-style-section-dark :where(p, li):not(.has-text-color) {
	color: color-mix(in srgb, currentColor 72%, transparent);
}

/* Spec-list labels read as secondary; the value is the answer. */
.wp-block-list.spec-list li {
	color: var(--wp--preset--color--neutral-mid);
}
.is-style-section-dark .wp-block-list.spec-list li {
	color: inherit;
}


/* ---------------------------------------------------------------------------
 * Modifiers
 *
 * The comps mark rows and cards out: a winning column, a warned row, a
 * value that is the answer. These travel from the mockup as class names so
 * the meaning survives the conversion.
 * ------------------------------------------------------------------------ */

/* The column the comp is arguing for. */
.hd-card.win {
	border-color: var(--wp--preset--color--primary);
}
.is-style-section-dark .hd-card.win,
.has-dark-bg-background-color .hd-card.win {
	border-color: var(--wp--preset--color--primary);
}
.hd-card.win .mono-label {
	color: var(--wp--preset--color--accent);
}
.hd-card.win .stat-total {
	color: var(--wp--preset--color--accent);
}

/* A row the comp flags: warn is a shortfall, good is the answer. */
.spec-list li.warn b,
.spec-list li.warn strong {
	color: var(--wp--preset--color--primary);
}
.spec-list li.good b,
.spec-list li.good strong {
	color: var(--wp--preset--color--accent);
}

/* A totals figure closes a card, ruled off from the rows above it. */
.stat-total {
	padding-top: 1rem;
	border-top: 1px solid var(--wp--preset--color--hairline);
	color: var(--wp--preset--color--neutral-dark);
}
.is-style-section-dark .stat-total,
.has-dark-bg-background-color .stat-total {
	border-top-color: var(--wp--custom--on-dark--hairline);
	color: var(--wp--preset--color--white);
}

/* Mono labels: uppercase and tracked, set back from the copy. */
.mono-label {
	font-family: var(--wp--preset--font-family--mono);
}

/* A handwritten aside is Caveat, larger than the body it sits beside. */
.hand-aside {
	font-size: 1.5em;
	line-height: 1.3;
}

/* Stat bands are ruled between figures rather than gapped apart. */
.stat-band .wp-block-column + .wp-block-column {
	border-left: 1px solid var(--wp--preset--color--hairline);
	padding-left: var(--wp--custom--section--medium);
}
.is-style-section-dark .stat-band .wp-block-column + .wp-block-column,
.has-dark-bg-background-color .stat-band .wp-block-column + .wp-block-column {
	border-left-color: var(--wp--custom--on-dark--hairline);
}
.stat-band .wp-block-column p:last-child {
	max-width: 22ch;
}

/* Below the width where columns stop sitting side by side, a rule *between*
   two figures becomes a rule down the left of every figure but the first, and
   the padding that went with it indents them by 33px. The visible symptom is
   not the stray vertical line — it is that the first counter sits proud of the
   others, on every page carrying a stat band.
   Stacked, the separation belongs on top, which is the same rule the band
   always wanted, turned through ninety degrees. */
@media (max-width: 781px) {
	.stat-band .wp-block-column + .wp-block-column {
		border-left: 0;
		padding-left: 0;
		border-top: 1px solid var(--wp--preset--color--hairline);
		padding-top: var(--wp--preset--spacing--space-md);
	}

	.is-style-section-dark .stat-band .wp-block-column + .wp-block-column,
	.has-dark-bg-background-color .stat-band .wp-block-column + .wp-block-column {
		border-top-color: var(--wp--custom--on-dark--hairline);
	}
}

/* The tier the comp is steering you to.
 *
 * The comp says so twice: a heavier border, and a label sitting astride the
 * top edge. Only the border was here, which is the expensive kind of
 * half-done -- the card looks deliberate, so nothing reads as missing, and
 * the one word doing the actual persuading is gone. The label is a
 * pseudo-element in the comp too, so there is no markup to carry: a card
 * says .hot and the theme says which word. */
.hd-card.hot {
	position: relative;
	border-color: var(--wp--preset--color--primary);
	border-width: 2px;
	box-shadow: var(--wp--preset--shadow--large);
}

.hd-card.hot::after {
	content: "Most chosen";
	position: absolute;
	top: -11px;
	left: var(--wp--custom--section--medium);
	padding: 0.3rem 0.6rem;
	border-radius: var(--wp--custom--radius--pill);
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.594rem;
	font-weight: 500;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	white-space: nowrap;
}

/* The quiet arrow link
 *
 * Where a card offers somewhere else to read rather than something to buy.
 * The comps set it as a rule under the words with an arrow that steps
 * right on hover -- deliberately lighter than a button, so a rate card can
 * point at four other pages without reading as four calls to action.
 * Colour is inherited, so it works on a dark band as it does on a card. */
.arrow-link {
	margin-top: var(--wp--preset--spacing--space-sm);
}

.arrow-link a {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding-bottom: 3px;
	color: inherit;
	font-weight: 600;
	text-decoration: none;
	border-bottom: 1px solid var(--wp--preset--color--hairline);
	transition: color 0.25s, border-color 0.25s;
}

.is-style-section-dark .arrow-link a {
	border-bottom-color: var(--wp--custom--on-dark--hairline);
}

.arrow-link a:hover {
	color: var(--wp--preset--color--primary);
	border-bottom-color: var(--wp--preset--color--primary);
}

.arrow-link .btn-arrow {
	display: inline-block;
	transition: transform 0.3s var(--wp--custom--ease);
}

.arrow-link a:hover .btn-arrow {
	transform: translateX(5px);
}

@media (prefers-reduced-motion: reduce) {
	.arrow-link a,
	.arrow-link .btn-arrow {
		transition: none;
	}
	.arrow-link a:hover .btn-arrow {
		transform: none;
	}
}

/* A price at the end of a row -- the add-on rows on the rate card, where
 * the name and the number sit at opposite ends of the same line. Set in the
 * accent so the number is what the eye lands on, with its unit below it in
 * the mono label rather than trailing the figure and competing with it. */
.addon-p {
	margin: 0;
	color: var(--wp--preset--color--primary);
	font-size: var(--wp--preset--font-size--heading-5);
	font-weight: 800;
	letter-spacing: -0.04em;
	line-height: 1.1;
	text-align: right;
	white-space: nowrap;
}

/* The row is flex with space-between and wrap, so a description long
 * enough to fill the line pushes the price onto a line of its own, at the
 * left, where it reads as an afterthought rather than the answer. The comp
 * prevents it by capping the paragraph; here the cap is a measure token,
 * which is the same intent said in the theme's own terms. */
.hd-card:has(> .addon-p) > *:not(.addon-p) {
	flex: 1 1 auto;
	max-width: var(--wp--custom--measure--default);
}

.addon-p span {
	display: block;
	margin-top: 3px;
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.594rem;
	font-weight: 500;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	color: var(--wp--preset--color--neutral-light);
}

/* ---------------------------------------------------------------------------
 * The elastic curve
 *
 * The transition out of a dark band is an arc that springs into place
 * rather than arriving flat. The comp marks the path .elastic but ships no
 * animation for it, so the spring is built here: the bulge stretches past
 * its resting depth and settles back, damped.
 *
 * scaleY on the path is what does it. The path's top edge sits at y=0, so
 * scaling from the top pins that edge and stretches only the bulge —
 * transform-box: view-box puts the origin in the viewBox's coordinates
 * rather than the element's own bounding box.
 * ------------------------------------------------------------------------ */

.section-curve path {
	transform-box: view-box;
	transform-origin: 50% 0;
}
.hd-motion .section-curve path {
	transform: scaleY(0.18);
}
.hd-motion .section-curve.is-in path {
	animation: hd-curve-spring 1.15s var(--wp--custom--ease) both;
}

@keyframes hd-curve-spring {
	0%   { transform: scaleY(0.18); }
	42%  { transform: scaleY(1.38); }
	64%  { transform: scaleY(0.86); }
	80%  { transform: scaleY(1.08); }
	92%  { transform: scaleY(0.97); }
	100% { transform: scaleY(1); }
}

@media (prefers-reduced-motion: reduce) {
	.hd-motion .section-curve path,
	.hd-motion .section-curve.is-in path {
		transform: none;
		animation: none;
	}
}

/* ---------------------------------------------------------------------------
 * Gravity Forms on the theme's tokens
 *
 * Gravity Forms is what these sites use, and its Orbital theme is built on
 * its own custom properties — so the right way to brand a form is to map
 * those properties onto the design tokens once, here, rather than to
 * out-specify the plugin's stylesheet rule by rule. The plugin's CSS loads
 * after the theme's, so that fight is one the theme loses by default and
 * wins only by piling on classes.
 *
 * Scoped to the Orbital wrapper, so a site still rendering the legacy form
 * theme is left exactly as it was. The selector names two classes that are
 * both on that same element, and it has to: Gravity Forms declares these
 * same properties on .gform-theme--framework and its stylesheet is enqueued
 * after the theme's, so a single class ties on specificity and loses on
 * order — silently, with the form simply staying Gravity Forms blue.
 * ------------------------------------------------------------------------ */

.gform-theme.gform-theme--framework {
	/* Brand */
	--gf-color-primary: var(--wp--preset--color--primary);
	--gf-color-primary-darker: var(--wp--preset--color--secondary);
	--gf-color-primary-lighter: var(--wp--custom--tint--primary);

	/* Fields: the ink, the fill, and the line around them */
	--gf-color-in-ctrl: var(--wp--preset--color--light-bg);
	--gf-color-in-ctrl-contrast: var(--wp--preset--color--neutral-dark);
	--gf-color-in-ctrl-dark-lighter: var(--wp--preset--color--hairline);
	--gf-color-in-ctrl-light-darker: var(--wp--preset--color--hairline);

	/* Text outside the fields: labels, descriptions, legends */
	--gf-color-out-ctrl-dark-darker: var(--wp--preset--color--neutral-dark);
	--gf-color-out-ctrl-dark-lighter: var(--wp--preset--color--neutral-mid);
	--gf-color-out-ctrl-light-darker: var(--wp--preset--color--hairline);

	--gf-radius: var(--wp--custom--radius--small);
	--gf-font-family-base: var(--wp--preset--font-family--body);

	/* The label takes the overline role: the same mono, uppercase,
	   wide-tracked small text the design system uses for section eyebrows.
	   Size and family are tokens; case and tracking are not, so they are
	   set as rules below. */
	--gf-ctrl-label-font-family-primary: var(--wp--preset--font-family--mono);
	--gf-ctrl-label-font-size-primary: 0.625rem;
	--gf-ctrl-label-color-primary: var(--wp--preset--color--neutral-mid);

	/* "(Required)" is a note, not a warning; the plugin colours it danger. */
	--gf-ctrl-label-font-size-req: 0.625rem;
	--gf-ctrl-label-color-req: var(--wp--preset--color--neutral-light);
}

.gform-theme.gform-theme--framework .gform-field-label {
	letter-spacing: 0.13em;
	text-transform: uppercase;
}

/* The step progress bar carries its colour as a class in the markup rather
   than a token, so it has to be named to be moved off Gravity Forms blue. */
.gform-theme.gform-theme--framework .gf_progressbar {
	background-color: var(--wp--preset--color--light-bg);
	border-radius: var(--wp--custom--radius--pill);
}

.gform-theme.gform-theme.gform-theme--framework .gf_progressbar_percentage,
.gform-theme.gform-theme.gform-theme--framework .gf_progressbar_percentage.percentbar_blue {
	background-color: var(--wp--preset--color--primary);
	border-radius: var(--wp--custom--radius--pill);
	color: var(--wp--preset--color--white);
}

.gform-theme.gform-theme.gform-theme--framework .gf_progressbar_title {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.625rem;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--wp--preset--color--neutral-light);
}

/* ---------------------------------------------------------------------------
 * Form card (.fwrap)
 *
 * An opt-in wrapper for a form sitting inside a page section — the closer
 * band's right-hand column, typically. Only the card itself: the form inside
 * it is styled by the token mapping above, so this stays composition rather
 * than a second set of form rules.
 * ------------------------------------------------------------------------ */

.fwrap {
	background-color: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--large);
	padding: 2.375rem 2.25rem 2.5rem;
}

/* A submit that fills the card reads as the section's one action, which is
   what a closer form is for. The footer is a flex row, so the button is
   told to grow rather than given a width — a width on a flex item is the
   item's starting size, not its final one. On a multi-page form's later
   pages that also lets Previous and Submit share the row evenly instead of
   both trying to be full width. */
.fwrap .gform_footer,
.fwrap .gform_page_footer {
	gap: 0.75rem;
	margin: 1.25rem 0 0;
	padding: 0;
}

.fwrap .gform_footer .gform-theme-button,
.fwrap .gform_page_footer .gform-theme-button {
	flex: 1 1 auto;
}

.fwrap textarea {
	min-height: 6.5rem;
}

@media (max-width: 781px) {
	.fwrap {
		padding: 1.75rem 1.5rem 2rem;
	}
}

/* ---------------------------------------------------------------------------
 * Credential badges
 *
 * A claim and, under it, who issued it — the issuer is the half that makes
 * the claim worth anything, so it gets its own line in the mono label role
 * rather than being run onto the end of the title.
 * ------------------------------------------------------------------------ */

.badge-row {
	justify-content: center;
}

.badge {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 0.3125rem;
	max-width: 14.5rem;
	padding: 0.9375rem 1.25rem;
	background-color: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--base);
	/* The card sits on the same white the section does, so the ring and
	   the shadow are the only things separating them. */
	box-shadow: var(--wp--preset--shadow--medium);
	transition: border-color 0.3s, transform 0.3s var(--wp--custom--ease),
		box-shadow 0.3s;
}

.badge:hover {
	border-color: var(--wp--preset--color--accent);
	transform: translateY(-3px);
	box-shadow: var(--wp--preset--shadow--large);
}

.badge .badge-title {
	margin: 0;
	line-height: 1.3;
	letter-spacing: -0.015em;
}

.badge .badge-source {
	margin: 0;
	font-family: var(--wp--preset--font-family--mono);
	line-height: 1.3;
}

/* ---------------------------------------------------------------------------
 * Sticky side column
 *
 * A section that pairs a short heading with a long list — the FAQ, the
 * due-diligence questions — holds the heading in place while the list
 * scrolls past it, so the question you are reading always has its context
 * beside it. The offset clears the sticky header.
 *
 * Only above the tablet breakpoint: stacked on a phone, the two are one
 * column and a sticky heading would sit on top of its own answers.
 * ------------------------------------------------------------------------ */

@media (min-width: 782px) {
	.faq-side,
	.sc-side {
		position: sticky;
		top: 7.5rem;
		align-self: start;
	}
}

/* ---------------------------------------------------------------------------
 * Pricing tiers
 *
 * Tiers rarely hold the same amount of copy, so without this the call to
 * action sits wherever its list happens to end and the buttons stagger
 * across the row. The list takes the slack instead, which puts every CTA
 * on the same line — the one place a price comparison is actually read.
 * ------------------------------------------------------------------------ */

.hd-card.tier {
	display: flex;
	flex-direction: column;
}

.hd-card.tier .check-list {
	flex: 1;
}

/* ---------------------------------------------------------------------------
 * Single post: body, rail, contents
 *
 * The measure is the whole point. Body copy set to the full content width
 * runs past 150 characters a line, which is roughly twice what anyone reads
 * comfortably, and no amount of type scale fixes it — the cap has to be on
 * the column.
 *
 * The rail holds what you want while reading and not before: how long this
 * is, where you are in it, the way back. It stays put on a wide screen and
 * falls above the article on a narrow one, where a sticky rail would be a
 * lid.
 * ------------------------------------------------------------------------ */

.post-head .wp-block-post-title {
	max-width: var(--wp--custom--measure--wide);
}

.post-body {
	max-width: var(--wp--custom--measure--default);
}

.post-body > * + * {
	margin-top: var(--wp--preset--spacing--space-sm);
}

.post-body h2 {
	margin-top: var(--wp--preset--spacing--space-xl);
	/* Clears the sticky header when a contents link jumps here. */
	scroll-margin-top: 7.5rem;
}

.post-body h3 {
	margin-top: var(--wp--preset--spacing--space-lg);
}

.post-body :where(img, figure) {
	border-radius: var(--wp--custom--radius--base);
}

@media (min-width: 782px) {
	.post-rail {
		position: sticky;
		top: 7.5rem;
		align-self: start;
	}
}

.hd-reading-time {
	margin: 0;
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--wp--preset--color--neutral-light);
}

.hd-contents {
	margin-top: var(--wp--preset--spacing--space-sm);
	padding-top: var(--wp--preset--spacing--space-sm);
	border-top: 1px solid var(--wp--preset--color--hairline);
}

.hd-contents__label {
	margin: 0 0 0.75rem;
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--wp--preset--color--neutral-light);
}

/* Numbered, because the order is the argument in a how-to — but the
   numbers stay quiet so the headings read as the list. */
.hd-contents__list {
	margin: 0;
	padding: 0;
	list-style: none;
	counter-reset: hd-contents;
}

.hd-contents__list li {
	counter-increment: hd-contents;
	position: relative;
	padding-left: 1.75rem;
	margin-bottom: 0.625rem;
	font-size: var(--wp--preset--font-size--text-small);
	line-height: 1.4;
}

.hd-contents__list li::before {
	content: counter(hd-contents, decimal-leading-zero);
	position: absolute;
	left: 0;
	top: 0.15em;
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.625rem;
	letter-spacing: 0.1em;
	color: var(--wp--preset--color--neutral-light);
}

.hd-contents__list a {
	color: var(--wp--preset--color--neutral-mid);
	text-decoration: none;
	border-bottom: 1px solid transparent;
	transition: color 0.2s, border-color 0.2s;
}

.hd-contents__list a:hover {
	color: var(--wp--preset--color--neutral-dark);
	border-bottom-color: var(--wp--preset--color--primary);
}

.rail-back a {
	text-decoration: none;
}

/* ---------------------------------------------------------------------------
 * Author card
 *
 * Who wrote this, with a face and a claim to know — the thing a reader (and
 * a search engine assessing expertise) looks for at the end of a piece and
 * usually cannot find. The bio comes from the user profile, so it is one
 * description maintained in one place rather than repeated per post.
 * ------------------------------------------------------------------------ */

.author-card .wp-block-avatar img {
	display: block;
	border-radius: 50%;
}

.author-card .wp-block-post-author-name a {
	color: var(--wp--preset--color--neutral-dark);
	text-decoration: none;
	border-bottom: 1px solid var(--wp--preset--color--hairline);
	transition: border-color 0.2s;
}

.author-card .wp-block-post-author-name a:hover {
	border-bottom-color: var(--wp--preset--color--primary);
}

.author-card .wp-block-post-author-biography {
	margin: 0;
	max-width: var(--wp--custom--measure--tight);
}

@media (max-width: 600px) {
	.author-card {
		flex-wrap: wrap;
	}
}


/* ---------------------------------------------------------------------------
 * Post cards — the archive
 *
 * The single post opens with a category, a date, a title and a picture, and
 * the archive is the same four things repeated. So the card is built from
 * the same parts in the same order rather than a second, competing idea of
 * what a post looks like.
 *
 * The image is the reason to stop, so it is full-bleed to the card's edges
 * rather than inset like the padded .hd-card: a picture with a margin round
 * it reads as an illustration of the text, and this one is the headline.
 * ------------------------------------------------------------------------ */

/* The grid item is the loop's own <li>; the card is the group inside it.
   Qualified as .wp-block-post this matched nothing at all, and the symptom
   was a page that still looked reasonable -- images, titles, a grid -- with
   every border, radius and shadow silently absent. */
.post-grid > li {
	display: flex;
}

.wp-block-group.post-card {
	display: flex;
	flex-direction: column;
	height: 100%;
	overflow: hidden;
	background-color: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--base);
	box-shadow: var(--wp--preset--shadow--medium);
	transition:
		transform 0.4s var(--wp--custom--ease),
		box-shadow 0.4s;
}

.wp-block-group.post-card:hover {
	transform: translateY(-4px);
	box-shadow: var(--wp--preset--shadow--large);
}

/* One ratio for every card, whatever was uploaded. Without it a portrait
   photo and a landscape one make two different cards and the row stops
   being a row. */
.post-card .wp-block-post-featured-image {
	margin: 0;
	border-radius: 0;
}

.post-card .wp-block-post-featured-image img {
	display: block;
	width: 100%;
	aspect-ratio: 3 / 2;
	object-fit: cover;
	border-radius: 0;
	transition: transform 0.6s var(--wp--custom--ease);
}

.post-card:hover .wp-block-post-featured-image img {
	transform: scale(1.03);
}

/* A post with no picture still has to be a card: the space the image would
   have taken becomes a rule, so the row keeps its rhythm. */
.post-card .wp-block-post-featured-image:empty {
	display: none;
}

.post-card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
}

.post-card__body .wp-block-post-title {
	text-wrap: balance;
}

.post-card__body .wp-block-post-title a {
	color: var(--wp--preset--color--neutral-dark);
	text-decoration: none;
	transition: color 0.25s;
}

.post-card:hover .wp-block-post-title a {
	color: var(--wp--preset--color--primary);
}

/* The excerpt takes the slack so the meta line sits on the floor of every
   card in the row, however long the titles run. */
.post-card__body .wp-block-post-excerpt {
	flex: 1;
}

.post-card__body .wp-block-post-excerpt__more-text {
	display: none;
}

.post-card__meta {
	margin-top: var(--wp--preset--spacing--space-sm);
	padding-top: 0.875rem;
	border-top: 1px solid var(--wp--preset--color--hairline);
	color: var(--wp--preset--color--neutral-light);
}

.post-card__meta .hd-reading-time::before {
	content: "·";
	margin-right: 0.5rem;
}

/* The lead card
 *
 * The single opens on a full-width picture, and the archive should say the
 * same thing about its newest piece rather than ranking it level with a
 * post from two years ago. So the first card on each page spans the grid
 * and turns on its side — picture beside the words, at the size the single
 * gives it. Below the breakpoint it is an ordinary card again, because a
 * side-by-side split at 390px is two columns of nothing. */
@media (min-width: 782px) {
	.wp-block-post-template.post-grid > li:first-child {
		grid-column: 1 / -1;
	}

	.post-grid > li:first-child .post-card {
		flex-direction: row;
		align-items: stretch;
	}

	.post-grid > li:first-child .wp-block-post-featured-image {
		flex: 0 0 46%;
	}

	.post-grid > li:first-child .wp-block-post-featured-image img {
		height: 100%;
		aspect-ratio: auto;
	}

	.post-grid > li:first-child .post-card__body {
		justify-content: center;
		padding: var(--wp--custom--section--large);
	}

	/* A lead is read first, so it is set at the size that says so. */
	.post-grid > li:first-child .wp-block-post-title {
		font-size: var(--wp--preset--font-size--heading-3);
	}

	.post-grid > li:first-child .wp-block-post-excerpt {
		flex: 0 1 auto;
		max-width: var(--wp--custom--measure--tight);
	}
}

@media (prefers-reduced-motion: reduce) {
	.wp-block-group.post-card,
	.post-card .wp-block-post-featured-image img {
		transition: none;
	}
	.wp-block-group.post-card:hover {
		transform: none;
	}
	.post-card:hover .wp-block-post-featured-image img {
		transform: none;
	}
}

/* ---------------------------------------------------------------------------
 * Archive pagination
 *
 * Core renders bare numbers in a row. Given a page break has to exist, it
 * should at least look deliberate.
 * ------------------------------------------------------------------------ */

.wp-block-query-pagination {
	margin-top: var(--wp--custom--section--large);
	padding-top: var(--wp--preset--spacing--space-md);
	border-top: 1px solid var(--wp--preset--color--hairline);
	gap: 0.5rem;
	align-items: center;
}

.wp-block-query-pagination a,
.wp-block-query-pagination .page-numbers {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.6875rem;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	text-decoration: none;
	color: var(--wp--preset--color--neutral-mid);
	padding: 0.5rem 0.75rem;
	border-radius: var(--wp--custom--radius--small);
	transition: color 0.2s, background-color 0.2s;
}

.wp-block-query-pagination a:hover {
	color: var(--wp--preset--color--neutral-dark);
	background-color: var(--wp--preset--color--light-bg);
}

.wp-block-query-pagination .page-numbers.current {
	color: var(--wp--preset--color--white);
	background-color: var(--wp--preset--color--primary);
}

/* Previous and next are the ones people actually use, so they read as
   controls rather than as two more numbers. */
.wp-block-query-pagination-previous,
.wp-block-query-pagination-next {
	color: var(--wp--preset--color--primary);
}


/* ---------------------------------------------------------------------------
 * The work filter
 *
 * A bar of real <button> elements, so it comes through as core/html rather
 * than as blocks: no core block produces a button, and as paragraphs the
 * chips rendered the words and lost the pills, the aria-pressed state and
 * the filtering all at once.
 *
 * Each card carries its keys as t-* classes (see tag_classes() in the
 * converter) because className is the one thing that survives an edit in
 * the block editor untouched.
 * ------------------------------------------------------------------------ */

.pw-filter {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: var(--wp--preset--spacing--space-md);
	padding-bottom: var(--wp--preset--spacing--space-sm);
	border-bottom: 1px solid var(--wp--preset--color--hairline);
}

.pw-filter button {
	padding: 9px 15px;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--pill);
	background-color: var(--wp--preset--color--white);
	color: var(--wp--preset--color--neutral-mid);
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.656rem;
	font-weight: 500;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	cursor: pointer;
	transition: color 0.25s, border-color 0.25s, background-color 0.25s;
}

.pw-filter button:hover {
	border-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--primary);
}

/* The chosen one is filled, not merely outlined: with seven chips an
   outline-on-outline difference is not enough to find at a glance. */
.pw-filter button[aria-pressed="true"] {
	background-color: var(--wp--preset--color--neutral-dark);
	border-color: var(--wp--preset--color--neutral-dark);
	color: var(--wp--preset--color--white);
}

.pw-filter button:focus-visible {
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
}

/* A filter that can return nothing needs something to say so. Hidden until
   the script has a reason to show it. */
.pw-empty {
	grid-column: 1 / -1;
	padding: var(--wp--custom--section--large) 0;
	text-align: center;
	color: var(--wp--preset--color--neutral-mid);
}

.pw-empty[hidden] {
	display: none !important;
}

@media (prefers-reduced-motion: reduce) {
	.pw-filter button {
		transition: none;
	}
}


/* A figure inside a card is not a figure in a band.
 *
 * On a stat band the number is the whole point and takes the accent. In a
 * project card it sits among a heading, a client line and a paragraph that
 * are already competing, and three accent figures per card across four
 * cards is the page shouting its own evidence. The comps set them in ink
 * and spend the accent on the client name instead, which is the thing a
 * reader is actually scanning for. */
.hd-card .stat-number {
	color: var(--wp--preset--color--neutral-dark);
	font-size: var(--wp--preset--font-size--heading-3);
}

/* Only reachable because the converter leaves a card figure's colour
   unnamed. A fontSize attribute is printed with !important too, so the
   size above works on the has-heading-1-font-size class the block still
   carries -- but a named colour would have beaten this outright, which is
   why .n sets none. */

.is-style-section-dark .hd-card .stat-number,
.has-dark-bg-background-color .hd-card .stat-number {
	color: var(--wp--preset--color--white);
}
