/**
 * Labrador overrides.
 *
 * Loaded AFTER dist/main.css so these rules win on equal specificity.
 *
 * WHY THIS FILE EXISTS: dist/main.css and dist/main.js are periodically
 * replaced wholesale by a separate build/deploy process, which silently
 * discards edits made directly to them. Anything in here survives that.
 * If a change belongs in the theme's real source, move it there and delete
 * it from this file.
 */

/* ---- Display font: Medium (500) cut ----

   Served from wp-content/uploads, NOT dist/fonts. The Medium files lived in
   dist/fonts and disappeared: that directory is replaced wholesale by the
   separate build/deploy process, which silently took the font with it, and
   every heading on the site fell back to Regular 400 (the browser reported
   the 500 face as "error"). uploads/ is outside that blast radius, same as
   this stylesheet. If these files ever 404 again, they were moved, not
   overwritten. Absolute URLs because this file is loaded from the theme,
   so a relative path would resolve against the theme directory. */
@font-face {
	font-display: swap;
	font-family: "Neue Haas Unica";
	font-style: normal;
	font-weight: 500;
	src: url(/wp-content/uploads/neue-haas-unica-medium.woff2) format("woff2"),
		url(/wp-content/uploads/neue-haas-unica-medium.woff) format("woff");
}

/* ---- Tokens ---- */
:root {
	--color-green-dark: #0f4a38;
	--text-2xl: clamp(2.05rem, 4.3vw, 3.25rem);
	--text-xl: clamp(1.5rem, 2.6vw, 2.05rem);
}

/* ---- Focus must never be invisible ----

   dist/main.css does `:focus { outline: none }` and paints the ring back
   only on `:focus-visible`. That makes every keyboard user on this site
   dependent on one browser heuristic, and when it does not fire there is
   no focus indicator anywhere: focus still moves, silently, which reads
   as "keyboard navigation is broken". Reported in Safari, where the
   heuristic is more conservative than in Chrome, and where the only ring
   left on screen belonged to Intercom's launcher because it lives in an
   iframe with its own styles.

   So the ring is restored on plain `:focus` and suppressed only when the
   browser can positively tell us the focus did not come from the
   keyboard. The failure mode now runs the safe way: a browser that does
   not understand `:focus-visible` drops the second rule as invalid and
   keeps showing the ring, rather than hiding it. Loaded after
   dist/main.css, so equal specificity resolves here. */
:focus {
	outline: 2px dashed var(--color-ink);
	outline-offset: 2px;
}

/* The dark bands need the light ring, matching main.css's own variants. */
.section--say-hi :focus,
.site-footer :focus {
	outline-color: var(--color-paper);
}

:focus:not(:focus-visible) {
	outline: none;
}

/* ---- Mobile menu opens under its button, not across the page ----

   dist/main.css gives the panel `flex: 1 1 100%; order: 3` below 900px, so
   it wrapped onto its own full-width row: opening the menu shoved the whole
   page down and the links ran along the far left edge, nowhere near the
   button that opened them. Anchoring it under the button instead makes it
   read as what it is, a disclosure belonging to that control, and it uses
   the same panel grammar as every other surface here: paper ground, 1.5px
   ink hairline, card radius, flat (The Flat Field Rule). */
@media (max-width: 900px) {
	.site-header {
		position: relative;
		/* Above page content, but below the cookie banner (800) and the
		   dialogs (2147483200+), both of which must still cover it. */
		z-index: 60;
	}

	.site-header__inner {
		position: relative;
	}

	.site-header.is-nav-open .site-header__panel {
		background: var(--color-paper);
		border: 1.5px solid var(--color-ink);
		border-radius: var(--radius-card);
		/* Starting position only. The button moves depending on whether it
		   fits beside the logo: on a narrow phone it wraps onto its own row
		   at the left, and on a wide phone or small tablet it sits at the
		   right end of the brand row. CSS cannot tell which happened, so
		   overrides.js measures the button and sets `left` to match it.
		   This value is the no-JS fallback and the pre-measurement default. */
		left: clamp(1rem, 4vw, var(--content-pad));
		/* Long menus stay on screen instead of running off the bottom. */
		max-height: calc(100dvh - 100% - 2rem);
		max-width: min(20rem, calc(100vw - 2rem));
		min-width: 12rem;
		overflow-y: auto;
		padding: 1.1rem 1.25rem;
		position: absolute;
		top: calc(100% + 0.5rem);
		width: max-content;
	}
}

/* ---- The Medium Rule: headings are 500 at scale, never bold ---- */
.section__heading,
h1,
h2,
h3,
.section__heading--hero,
.beliefs__title,
.numbers__value,
.background__heading,
.prose__heading,
.services__heading,
.services__title,
.cta__heading,
.say-hi__heading {
	font-weight: 500;
}

/* ---- Hero sits close to the nav ---- */
.section--hero {
	padding-block: clamp(1.75rem, 4vw, 3rem) clamp(3.5rem, 8vw, 6rem);
}

/* ---- Mobile menu: labelled pill, not a hamburger ---- */
.site-header__menu-toggle {
	background: transparent;
	border: 1.5px solid var(--color-ink);
	border-radius: var(--radius-pill);
	color: var(--color-ink);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	font-weight: 600;
	height: auto;
	line-height: 1;
	padding: 0.5rem 1.1rem;
	transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
	width: auto;
}

.site-header__menu-toggle:focus-visible,
.site-header__menu-toggle:hover {
	background: var(--color-mint);
	border-color: var(--color-green);
}

/* ---- Forest CTA button ---- */
.btn--forest {
	background: var(--color-green-dark);
	border-color: var(--color-green-dark);
	color: var(--color-paper);
}

.btn--forest:focus-visible,
.btn--forest:hover {
	background: var(--color-green);
	border-color: var(--color-green);
	color: var(--color-paper);
}

/* ---- Inline links inside regulation/service terms ---- */
.regulations__term a {
	color: inherit;
	text-decoration: underline;
	text-decoration-color: var(--color-green);
	text-decoration-thickness: 2px;
	text-underline-offset: 0.28em;
	transition: color 0.18s ease, text-decoration-color 0.18s ease;
}

.regulations__term a:focus-visible,
.regulations__term a:hover {
	color: var(--color-green);
}

/* ---- Blog post spacing, separators and featured image crop ---- */
.site-main--single {
	padding-block: clamp(1.75rem, 4vw, 3rem) 0;
}

.single-post {
	padding-block: 0;
}

.single-post__content h2 {
	font-size: var(--text-xl);
}

.single-post__content .wp-block-separator {
	background: none;
	border: 0;
	border-top: 1.5px solid var(--color-border);
	margin: 2rem 0 1.75rem;
	max-width: none;
	opacity: 1;
	width: 100%;
}

/* Crop the featured image centrally instead of clipping its bottom edge. */
.single-post__img {
	max-height: 50vh;
	object-fit: cover;
	object-position: center;
}

/* ---- Book a call: time-zone picker dialog ---- */
.call-picker {
	background: var(--color-paper);
	border: 1px solid var(--color-muted);
	border-radius: var(--radius-card);
	color: var(--color-ink);
	max-width: 26rem;
	padding: 0;
	width: calc(100% - 2rem);
}

.call-picker:not([open]) {
	display: none;
}

.call-picker::backdrop {
	background: rgba(14, 16, 15, 0.55);
}

.call-picker__inner {
	padding: 1.6rem;
}

.call-picker__title {
	font-family: var(--font-display);
	font-size: var(--text-lg);
	font-weight: 500;
	letter-spacing: -0.015em;
	margin: 0 0 0.35rem;
}

.call-picker__hint {
	color: var(--color-muted);
	font-size: var(--text-sm);
	line-height: 1.55;
	margin: 0 0 1.1rem;
}

.call-picker__options {
	display: grid;
	gap: 0.35rem;
	list-style: none;
	margin: 0 0 1.25rem;
	padding: 0;
}

.call-picker__option {
	border-radius: 0.5rem;
	color: var(--color-ink);
	display: block;
	padding: 0.7rem 0.8rem;
	text-decoration: none;
	transition: background-color 0.18s ease, color 0.18s ease;
}

.call-picker__option:focus-visible,
.call-picker__option:hover {
	background: var(--color-mint);
	color: var(--color-green);
}

.call-picker__who {
	display: block;
	font-family: var(--font-display);
	font-size: var(--text-base);
	font-weight: 500;
}

.call-picker__where {
	color: var(--color-muted);
	display: block;
	font-size: var(--text-sm);
}

.call-picker__option:focus-visible .call-picker__where,
.call-picker__option:hover .call-picker__where {
	color: var(--color-green);
}

/* ==========================================================================
   Quote dialog — accessibility fixes from the 2026-08-12 review.
   The dialog itself lives in dist/, which is rebuilt elsewhere, so its fixes
   live here. Verified live: the validation-error summary is NOT hidden and
   Gravity Forms focuses it on failure, so nothing here touches error handling.
   ========================================================================== */

/* WCAG 3.3.2 Labels or Instructions (A).
   The dialog CSS hides every required indicator, so screen reader users hear
   "required" on 14 fields while sighted users see nothing at all. Confirmed
   live: no visible required marker existed anywhere in either form. */
.quote-dialog .gform_wrapper .gfield_required,
.quote-dialog .gform_wrapper .gfield_required_asterisk,
.quote-dialog .gform_wrapper .gfield_required_text {
	display: inline !important;
}

.quote-dialog .gform_wrapper .gform_heading,
.quote-dialog .gform_wrapper .gform_required_legend {
	display: block !important;
}

.quote-dialog .gform_wrapper .gform_heading {
	margin: 0 0 1.25rem;
	padding: 0;
}

.quote-dialog .gform_wrapper .gform_required_legend {
	color: var(--color-muted);
	font-size: var(--text-sm);
	line-height: 1.55;
	margin: 0;
}

.quote-dialog .gform_wrapper .gfield_required {
	color: var(--color-muted);
	font-weight: 400;
	margin-left: 0.25rem;
}

/* WCAG 1.4.10 Reflow (AA).
   The path chooser was a fixed two-column grid with no media query; at a 320px
   viewport each text column collapsed to ~44px, narrower than the word
   "remediation". auto-fit drops to one column when two won't fit. */
.quote-path__options {
	grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
}

.quote-path__option-title {
	overflow-wrap: anywhere;
}

@media (max-width: 34rem) {
	.quote-dialog__inner {
		padding: 1.25rem;
	}
}

/* WCAG 1.4.11 Non-text Contrast (AA): the card border was 1.72:1 on paper. */
.quote-path__option {
	border-color: var(--color-muted);
}

/* Gravity Forms scopes its own custom properties with an ID selector, which
   outranks the dialog's class-based overrides, so controls were still rendering
   in GF's default blue inside a green-branded dialog. !important on a custom
   property is the reliable way to win that. */
.quote-dialog .gform_wrapper.gform-theme {
	--gf-color-primary: var(--color-green) !important;
	--gf-color-primary-darker: var(--color-green-dark) !important;
	--gf-ctrl-border-color: var(--color-muted) !important;
	--gf-ctrl-outline-color-focus: var(--color-ink) !important;
	--gf-ctrl-outline-width-focus: 2px !important;
}

/* Gravity Forms replaces the site's focus ring on its own controls with a
   translucent blue one measured at 3.05:1 against this panel. Restore the
   site's indicator so focus looks the same everywhere and clears 1.4.11
   comfortably (ink on the panel surface is roughly 15:1). The order form on
   /homepage-audit/ carries .audit-order, not .quote-dialog, so it is named
   here too; without it the checkout's own fields kept GF's off-brand blue
   ring while the card field beside them showed the site's dashed ink one. */
.quote-dialog .gform_wrapper :is(input, select, textarea, button):focus-visible,
.audit-order .gform_wrapper :is(input, select, textarea, button):focus-visible {
	outline: 2px dashed var(--color-ink) !important;
	outline-offset: 2px !important;
}

/* Choice rows highlight the full row on hover but only the label activated,
   so pointer users aiming at the highlight missed. Make the label's hit area
   match what the highlight advertises. */
.quote-dialog .gform_wrapper .gfield_checkbox .gchoice,
.quote-dialog .gform_wrapper .gfield_radio .gchoice {
	position: relative;
}

.quote-dialog .gform_wrapper .gfield_checkbox .gchoice label::before,
.quote-dialog .gform_wrapper .gfield_radio .gchoice label::before {
	content: "";
	inset: 0;
	position: absolute;
}

/* The criterion-view screenshot once lived bolted inside the light
   regulations section on /our-tool/ and needed caption recolouring and a
   width breakout to survive there. It now has its own showcase section, the
   same dark-panel treatment as the sample-report one, so the stock showcase
   styling applies and those overrides are gone. These rules stay only as the
   safety net for any future figure hand-authored into a regulations body. */
.regulations__body .showcase__caption {
	color: var(--color-ink, #1a1d1c);
}

.regulations__body .showcase__image {
	margin-inline: 0;
	width: 100%;
}

/* Click-to-enlarge. overrides.js wraps the image in a real button and builds
   one shared native <dialog>; wp_kses_post strips button/dialog from section
   body fields, which is why the interactive shell is not in the content.
   The wrapper's overflow:hidden would swallow the site's offset focus ring,
   so the trigger draws its ring inset instead. */
.shot-lightbox__trigger {
	background: none;
	border: 0;
	border-radius: inherit;
	cursor: zoom-in;
	display: block;
	padding: 0;
	width: 100%;
}

.shot-lightbox__trigger:focus-visible {
	outline: 2px dashed var(--color-ink, #1a1d1c);
	outline-offset: -4px;
}

/* The lightbox: chromeless dialog, ink-tinted backdrop, image capped to the
   viewport. Flat Field Rule: the dark plate and backdrop do the separation,
   no shadows. */
/* The dialog is the full viewport and scrolls; the image inside fills its
   width. This screenshot is portrait (2423x2560), so capping height would
   bind first and leave it barely larger than inline: a tall image gets big
   by filling the WIDTH and scrolling, not by fitting on one screen. */
.shot-lightbox {
	background: transparent;
	border: 0;
	height: 100dvh;
	max-height: 100dvh;
	max-width: 100vw;
	overflow: auto;
	overscroll-behavior: contain;
	padding: 0;
	width: 100vw;
}

.shot-lightbox::backdrop {
	background: rgba(26, 29, 28, 0.92);
}

.shot-lightbox__img {
	background: #111;
	border-radius: 1rem;
	display: block;
	height: auto;
	margin: 3.5rem auto 2rem;
	width: min(96vw, 1600px);
}

/* Sticky rather than fixed: inside a scrolling dialog it stays reachable as
   the image scrolls, and remains the first tab stop. */
.shot-lightbox__close {
	background: var(--color-paper, #f7f8f7);
	border: 0;
	border-radius: 999px;
	color: var(--color-ink, #1a1d1c);
	cursor: pointer;
	float: right;
	font-weight: 600;
	margin: 1rem 1rem 0 0;
	padding: 0.6rem 1.4rem;
	position: sticky;
	top: 1rem;
	transition: background-color 0.18s ease;
	z-index: 1;
}

.shot-lightbox__close:hover,
.shot-lightbox__close:focus-visible {
	background: #6fe3b4;
}

/* Keep the page from scrolling underneath the open lightbox. */
html:has(.shot-lightbox[open]) {
	overflow: hidden;
}

@media (prefers-reduced-motion: no-preference) {
	.shot-lightbox[open] {
		animation: shot-lightbox-in 0.18s ease-out;
	}
}

@keyframes shot-lightbox-in {
	from {
		opacity: 0;
	}
}

/* "What happens next" note above the quote forms' submit button (rendered by
   labrador_quote_form_next_steps() in inc/helpers.php).

   Surface fill plus a hairline rather than mint: green marks action on this
   site and mint is the hover response, so a mint panel here would read as
   something you can press. Flat by doctrine, so no shadow. Not scoped to
   .quote-dialog, so the note still looks right if a quote form is ever
   embedded directly on a page instead of in the modal.

   Selector is p.labrador-quote-next, not .labrador-quote-next, on purpose.
   Gravity Forms' theme reset unsets almost every property via
   .gform-theme--framework :where(...), and :where() contributes no
   specificity, so that rule ties a bare class at (0,1,0) and wins on source
   order because GF's stylesheet is enqueued after this one. Adding the element
   selector takes it to (0,1,1), which clears the reset without !important and
   without depending on a .gform_wrapper ancestor. */
p.labrador-quote-next {
	background: var(--color-surface, #f1f3f2);
	border: 1px solid var(--color-hairline, #bbc1be);
	border-radius: 1.25rem;
	color: var(--color-ink);
	font-size: 1rem;
	line-height: 1.55;
	margin: 0 0 1.25rem;
	padding: 0.9rem 1.15rem;
	width: 100%;
}

.labrador-quote-next strong {
	font-weight: 600;
}

/* Gravity Forms lays the footer out with flex, which would otherwise put the
   note beside the submit button instead of above it. */
.gform_wrapper .gform_footer {
	flex-wrap: wrap;
}

/* Pages without page_sections (privacy policy, HTML sitemap) render through
   page.php's plain-content branch, which historically emitted no title at all,
   so those documents had no h1. page.php now renders .entry-header; these
   rules give it the theme's Medium-Rule heading treatment and keep the
   prose at a readable measure. */
/* dist/main.css lays out .entry-content with the site's standard inner
   geometry (.site-main > article .entry-content: clamp padding + width
   formula), but the h1 lives in .entry-header, which got none of it, so the
   title hung ~29px outside the prose edge. Rather than fight the system,
   give both boxes the exact same geometry; they then align at every
   viewport, and the measure stays capped for comfortable prose.

   :not(.site-main--single) scopes this to page.php's plain-content branch
   (Privacy Policy, the HTML sitemap) only. Blog single posts share the
   .entry-content class on .single-post__content, so without this guard the
   rule also caught them and clamped article body copy to 42rem — dropping
   real, measured average line length to ~61 characters. That regression is
   fixed below with a measure sized for blog prose specifically. */
.site-main:not(.site-main--single) > article .entry-header,
.site-main:not(.site-main--single) > article .entry-content {
	margin-inline: auto;
	max-width: var(--measure, 42rem);
	padding-inline: clamp(1rem, 4vw, var(--content-pad));
	width: min(100% - 2rem, var(--content-width));
}

/* Blog post body measure. main.css sets .single-post__content to
   max-width: none, deferring entirely to the shared width formula
   (min(100% - 2rem, --content-width: 74rem)) — at desktop that renders body
   copy ~1170px wide, averaging 100+ characters per line. 56rem, measured
   against real rendered paragraphs on this site's actual body font
   (Atkinson Hyperlegible Next), lands at a 72.9-character average: inside
   the 65-75ch target and short of the point where longer paragraphs start
   plateauing past it. margin-inline:auto and padding-inline already come
   from the shared .section__inner rule this element also carries; only the
   cap was missing. */
.single-post__content {
	max-width: 56rem;
}

.site-main .entry-header {
	margin-bottom: 0;
	padding-top: clamp(1.75rem, 4vw, 3rem);
}

.entry-title {
	font-size: var(--text-2xl);
	font-weight: 500;
	letter-spacing: -0.025em;
	line-height: 1.1;
	margin-bottom: 0.5rem;
	text-wrap: balance;
}

/* Prose rhythm. The page rendered with a uniform 1rem on every margin, so
   headings floated equidistant between the section they end and the one they
   start. Strong space above a heading, little below, groups each section
   with its own copy. Heading presence comes from Title-scale size (the
   Medium Rule: 500 at scale, never bold); the 1.8rem floor in the max() is
   the browser-default size these h2s rendered at before the size rule
   existed, so no viewport gets smaller headings than it had. */
.site-main .entry-content {
	padding-bottom: clamp(3.5rem, 8vw, 6rem);
}

.site-main .entry-content h2 {
	font-size: max(1.8rem, var(--text-xl));
	letter-spacing: -0.015em;
	line-height: 1.2;
	margin-bottom: 0.75rem;
	margin-top: 2.5rem;
}

.site-main .entry-content p {
	margin-bottom: 1rem;
	text-wrap: pretty;
}

.site-main .entry-content ul,
.site-main .entry-content ol {
	margin-bottom: 1rem;
	padding-left: 1.5rem;
}

.site-main .entry-content li {
	margin-block: 0.35rem;
}

/* The "Last updated" line is document metadata, not prose. Muted and one step
   down, it stops competing with the opening section. Muted #585f5c on paper
   is 7.5:1, comfortably past AA. */
.site-main .entry-content > p:first-child em {
	color: var(--color-muted, #585f5c);
	font-size: 1rem;
}

/* Required indicators shipped at 12px against a 16px label, and Gravity Forms
   colours them #c02b0a. Two problems: a glyph two thirds the size of the text
   it modifies is hard to see for exactly the people most likely to need it,
   and red is out on this site (the Panic-Free Palette Rule). Sized in em so it
   scales with whatever label it sits in rather than being pinned to a pixel
   value, and recoloured to the palette's text-safe amber, which clears AA on
   paper at roughly 5.9:1.

   :not(.gfield_required_text) spares form 5, which uses the "(Required)" word
   indicator rather than an asterisk and would read as shouting at this size. */
.gform_wrapper span.gfield_required:not(.gfield_required_text) {
	color: var(--color-amber, #855713);
	font-size: 1.15em;
	font-weight: 700;
	line-height: 1;
	margin-left: 0.15em;
}

/* Gravity Forms nests the indicator on some fields, as
   <span class="gfield_required"><span class="gfield_required_asterisk">*</span></span>,
   so the rule above matches twice and the em compounds: 16px becomes 18.4px
   becomes 21.16px. Neutralise the inner span so every asterisk lands on the
   same size no matter which markup shape the field used. */
.gform_wrapper span.gfield_required span.gfield_required {
	font-size: 1em;
	margin-left: 0;
}

/* Form 5 marks required fields with the word "(Required)" rather than an
   asterisk, and Gravity Forms wraps it in a plain .gfield_required span. That
   wrapper matches the rule above, so the word inherited the asterisk's 1.15em
   and bold weight and rendered larger than its own label. Enlarging a lone
   glyph aids legibility; enlarging a word just shouts. Put it back to label
   size and weight, and keep the colour GF gives it. */
.gform_wrapper span.gfield_required:has(> span.gfield_required_text),
.gform_wrapper span.gfield_required span.gfield_required_text {
	font-size: 1rem;
	font-weight: 400;
}

.audit-order__points {
	display: grid;
	gap: 0.7rem;
	list-style: none;
	margin: 0 0 1.5rem;
	padding: 0;
}

.audit-order__points li {
	border-left: 2px solid var(--color-mint-strong);
	font-size: var(--text-sm);
	line-height: 1.55;
	padding-left: 0.8rem;
}

.audit-order__fine {
	color: var(--color-muted);
	font-size: 0.875rem;
	line-height: 1.5;
	margin: 1.1rem 0 0;
}

/* The point-of-commitment terms notice, rendered by helpers.php inside
   the form immediately before the Pay button, so nobody reaches the
   button without passing it. Ink, not muted: this line is load-bearing. */
.audit-order__agree {
	color: var(--color-ink);
	font-size: 0.875rem;
	line-height: 1.5;
	margin: 0 0 0.8rem;
}

/* The terms links in the agree line and the fine print. Amber, the
   palette's text-safe gold (the pale golds fail contrast at this size),
   with the house 2px underline: the required-field asterisks above
   already speak amber, so the two read as one signal, and the links stop
   dissolving into the surrounding text. Hover and focus resolve to ink;
   the focus ring itself comes from the global :focus-visible rule. */
.audit-order__agree a,
.audit-order__fine a {
	color: var(--color-amber, #855713);
	font-weight: 600;
	text-decoration: underline;
	text-decoration-color: var(--color-amber, #855713);
	text-decoration-thickness: 2px;
	text-underline-offset: 0.28em;
	transition: color 0.18s ease;
}

.audit-order__agree a:focus-visible,
.audit-order__agree a:hover,
.audit-order__fine a:focus-visible,
.audit-order__fine a:hover {
	color: var(--color-ink);
}

/* Shown only when the card field failed to load (see overrides.js). Amber
   hairline, not red: it is an alternative route, not an alarm. */
.audit-order__card-fallback {
	border-left: 2px solid var(--color-amber, #855713);
	color: var(--color-ink);
	font-size: var(--text-sm);
	line-height: 1.55;
	margin: 0.8rem 0 0;
	padding-left: 0.8rem;
}

/* Gravity Forms' own required-fields legend. It ships hidden in this theme's
   dialog styling, which left a bare asterisk with nothing defining it: a
   learned convention rather than self-evident text, and WCAG 3.3.2 expects
   its meaning to be on the form. Shown, and sized as the footnote it is
   rather than as body copy competing with the first field label. */
.audit-order .gform_wrapper .gform_required_legend {
	color: var(--color-muted);
	display: block !important;
	font-size: 0.875rem;
	line-height: 1.5;
	margin: 0 0 1rem;
}

/* GF marks the asterisk inside the legend with this class; match the amber of
   the indicators it describes so the two read as the same signal. */
.audit-order .gform_wrapper .gform_required_legend .gfield_required {
	color: var(--color-amber, #855713);
	font-weight: 700;
}

/* The confirmation carries the same mint hairline as the pre-purchase points,
   so the panel reads as one component before and after paying rather than
   swapping to plain prose at the moment that matters most. Values match
   .audit-order__points li exactly; if that changes, change this with it. */
.audit-order .gform_confirmation_message p {
	border-left: 2px solid var(--color-mint-strong);
	font-size: var(--text-sm);
	line-height: 1.55;
	margin: 0 0 0.7rem;
	padding-left: 0.8rem;
}

/* Once the order is placed, Gravity Forms swaps the form for its confirmation
   but everything around it stays put. The selling points and the Stripe
   fine print are written for someone deciding whether to buy: one of them even
   says "the email address you enter here", and there is no longer a form.
   Left in, they also push the actual confirmation under the fold on a phone.
   Hide them once a confirmation is present so the section says one thing. */
.hpa-order:has(.gform_confirmation_message) .audit-order__points,
.hpa-order:has(.gform_confirmation_message) .audit-order__fine {
	display: none;
}

.audit-order .gform_confirmation_message {
	display: grid;
	gap: 0.7rem;
}

.audit-order .gform_confirmation_message p:last-child {
	margin-bottom: 0;
}

/* The order form inherits the quote dialog's Gravity Forms treatment: same
   green accent, ink pill buttons, and visible required indicators. Those
   rules are scoped to .quote-dialog, so the selectors below re-point the
   handful that matter rather than restating the whole theme. */
/* Every value here carries !important, which is not laziness. Gravity Forms
   emits a per-form <style> block scoped as
   #gform_wrapper_6[data-form-index="0"].gform-theme, so its own properties
   land at (1,2,0) and inline in the page, beating any class selector from
   this stylesheet on both specificity and source order. Without !important
   the submit button renders in GF's default blue (#204ce5), which is not a
   colour that appears anywhere in this design system. Setting the custom
   property is the supported way in; !important is what makes it stick. */
.audit-order .gform_wrapper,
.audit-order .gform_wrapper.gform-theme--orbital {
	margin: 0;
	--gf-color-primary: var(--color-ink) !important;
	--gf-color-primary-rgb: 26, 29, 28 !important;
	--gf-color-primary-contrast: var(--color-paper) !important;
	--gf-color-primary-darker: var(--color-green) !important;
	--gf-color-primary-lighter: var(--color-mint-strong) !important;
	/* Validation errors ship in Gravity Forms' default red (#c02b0a), which
	   this site does not use anywhere (the Panic-Free Palette Rule). DESIGN.md
	   names amber as the severity ceiling for form errors, and the required
	   asterisks already speak amber, so point GF's danger token at it: message
	   text and the error field border both read this token. Verified live by
	   triggering an empty-form submit, the "Please enter your card details"
	   message and its border render #855713 (5.35:1 on the mint panel, past
	   AA) instead of red. */
	--gf-color-danger: var(--color-amber) !important;
	--gf-ctrl-border-color: var(--color-muted) !important;
	--gf-ctrl-label-color-primary: var(--color-ink) !important;
	--gf-radius: 8px !important;
	--gf-ctrl-btn-bg-color: var(--color-ink) !important;
	--gf-ctrl-btn-bg-color-hover: var(--color-green) !important;
	--gf-ctrl-btn-color: var(--color-paper) !important;
	--gf-ctrl-btn-color-hover: var(--color-paper) !important;
	--gf-ctrl-btn-border-color: var(--color-ink) !important;
	--gf-ctrl-btn-border-color-hover: var(--color-green) !important;
	--gf-ctrl-btn-border-radius: 999px !important;
	--gf-ctrl-btn-radius: 999px !important;
}

/* Belt and braces on the button itself: the custom properties above cover
   the orbital theme, but the submit button is the one control that must not
   fall back to blue if GF renames a property in a future release. */
.audit-order .gform_wrapper .gform_button,
.audit-order .gform_wrapper .gform-button--primary {
	background: var(--color-ink) !important;
	border-color: var(--color-ink) !important;
	border-radius: var(--radius-pill) !important;
	color: var(--color-paper) !important;
}

.audit-order .gform_wrapper .gform_button:focus-visible,
.audit-order .gform_wrapper .gform_button:hover,
.audit-order .gform_wrapper .gform-button--primary:hover {
	background: var(--color-green) !important;
	border-color: var(--color-green) !important;
}

.audit-order .gform_wrapper .gfield_required,
.audit-order .gform_wrapper .gfield_required_asterisk,
.audit-order .gform_wrapper .gfield_required_text {
	display: inline !important;
}

/* Gravity Forms already puts .gform-theme-field-control on both the Stripe
   element and the name-on-card input, so they inherit the same chrome as
   every other control in the form. The container around them must therefore
   stay bare: styling it as a control too drew a second box around the two
   real inputs, so the card area read as one bordered panel containing two
   more bordered panels. */
.audit-order .ginput_container_creditcard {
	background: none;
	border: 0;
	padding: 0;
}

/* ---- The card field must never lay out at zero height ----

   NVDA (through 2025.3) treats a control with zero width or height and no
   children as invisible: it is still reachable by Tab, but it vanishes from
   the browse-mode buffer, so arrow keys, the f quick-nav key and the
   Elements List all skip straight past it. Reported live: tabbing went from
   the price field to Cardholder Name with the card number never found.

   This site is unusually prone to hitting that. Stripe used to mount its
   element while the old dialog was still display:none, measure a
   zero-height box, and size the iframe to nothing. On the page the form is
   visible from load, but the floors stay: they are cheap, they survive
   whatever the add-on does next, and NVDA's buffer is not something to
   gamble on twice.

   min-height is the durable answer because used height is
   clamp(min-height, height, max-height), so it wins over Stripe's inline
   height no matter what it computes, and it survives every remount without
   JavaScript. Both floors sit BELOW the healthy rendered sizes (wrapper
   38px, iframe 17px), so nothing moves visually in the normal case. */
.audit-order .StripeElement {
	box-sizing: border-box;
	display: block;
	min-height: 2.25rem;
}

.audit-order .StripeElement .__PrivateStripeElement {
	display: block !important;
}

.audit-order .StripeElement iframe[name^="__privateStripe"] {
	display: block !important;
	min-height: 1rem !important;
}

/* The card field's focus indicator (WCAG 1.4.1 Use of Color, 2.4.7).
   Stripe exposes .StripeElement--focus on its mount node when the card
   iframe takes focus, since focus inside a cross-origin iframe cannot
   drive :focus-within out here.

   The ring is drawn on the field's OWN Gravity Forms row, not on the
   .StripeElement itself. Stripe owns that node's box: it forces
   outline-width and border with !important and strips inline styles it
   did not set, so a ring set directly on it is silently dropped. Proven
   live: only outline-STYLE (which Stripe leaves alone) ever landed, which
   means the previous green-border-on-focus cue was not merely weak, it
   was invisible, leaving the most consequential field in the checkout with
   no focus indicator at all before a payment is authorised.

   The row wrapper (.ginput_full) is a plain GF div this stylesheet
   controls, so :has(.StripeElement--focus) puts the site's dashed ink ring
   around exactly the card-number row. Only that row contains a
   .StripeElement, so the sibling Cardholder Name row (a real input with
   its own :focus-visible ring) is never caught. :has() is already a
   baseline dependency on this site. */
.audit-order .ginput_full:has(.StripeElement--focus) {
	border-radius: 8px;
	outline: 2px dashed var(--color-ink);
	outline-offset: 2px;
}

/* The green border stays as a second cue where Stripe permits it; it is
   no longer the only one, so colour perception is not required. */
.audit-order .StripeElement--focus {
	border-color: var(--color-green);
}

/* ---- Process steps can hold more than one paragraph ----

   dist/main.css sets `.services__process-body p { margin: 0 }`, written when
   every step body was a single paragraph. The template now runs the field
   through wpautop, so a blank line in the field becomes a real <p>; with the
   zero margin and nothing else, two paragraphs would butt together and read
   as one block with a stray break in it. Only the gap BETWEEN siblings is
   set, so a one-paragraph step still sits exactly where it always did. */
.services__process-body p + p {
	margin-top: 0.9em;
}

/* Order button inside a pricing rate card. The card's own rhythm is tight
   (title/price row, then body), so the button gets one clear step of air
   rather than inheriting the section-level actions margin. */
.services__rate-action {
	margin: 1.1rem 0 0;
}

/* ---- Criterion screenshot inside its showcase panel ----

   The sample-report showcase never needed this: its screenshot is a light UI
   on the Footer Ink panel, so the image edge is the contrast. The criterion
   view is Labrador in dark mode, near the panel's own value, and without a
   boundary the two surfaces read as one murky field. A paper mat gives the
   dark screenshot the same figure-ground pop the light one gets for free:
   a frame, not a glow, per The Flat Field Rule. Scoped by filename because
   the template passes its own class to wp_get_attachment_image, which
   replaces the default classes, wp-image-ID included; and the section
   carries no unique id by design (the showcase template only mints ids for
   headings containing "export" or "report"). */
.section--showcase .showcase__image img[src*="Labrador-criterion-testing-view"] {
	/* The mat scales with the frame: 10px reads right against the desktop
	   image, but is proportionally twice as heavy on a phone-width one. */
	border: clamp(6px, 2vw, 10px) solid var(--color-paper);
	border-radius: 14px;
}

/* Gravity Forms puts 6px between the label text and the indicator, which on top
   of the indicator's own margin left the asterisk floating about 9px off the
   word it modifies, reading as unattached rather than as part of the label.

   It applies that gap as padding-inline-end: var(--gf-label-req-gap) from a
   :where(:has(+ .gfield_required)) rule. :where() contributes no specificity,
   so a class-based padding override merely ties it and loses on source order
   (GF's stylesheet is enqueued after this one). Setting the custom property is
   the supported way in, and !important is needed because GF scopes its own
   properties with an ID selector. */
.gform_wrapper.gform-theme {
	--gf-label-req-gap: 0px !important;
}

/* ---- Footer legal links clear the chat launcher ----

   Intercom pins its launcher to the bottom-right of the viewport at z-index
   2147483000: a 48px button with a 20px offset, so it owns the rightmost
   68px of the screen. The legal list is right-aligned and sat flush with the
   content edge, which put the launcher's left edge about 3px inside "Terms"
   at the foot of the page. Since it outranks everything in the footer, it
   swallowed those pixels and that part of the link stopped being clickable.

   The list stays on the right. It gets inset instead, and the inset tracks
   the viewport the same way the footer's own padding already does
   (padding-inline: clamp(1rem, 4vw, 4rem)), so the two move together:

     68px launcher + 34px gap - 16px centring margin = 86px,
     less the 4vw the footer padding already contributes.

   The gap is 34px rather than the 24px minimum: at 24px the button still
   read as crowding "Terms", because the launcher is a circle and its visual
   mass sits further left than its bounding box implies.

   That lands on exactly 34px of clearance at every width where the padding
   sits in its 4vw range, with no breakpoint to maintain. Because both terms
   read the same 4vw, the scrollbar's effect on vw cancels out. max() drops
   the inset to zero on very wide screens, where the content stops growing at
   its max-width and the gap opens on its own.

   Padding the footer's bottom edge would also clear the launcher, but costs
   about 90px of dead space on every page. Raising the footer's z-index would
   not work at all: a fixed element at the top of the stacking order wins. */
.site-footer__legal {
	margin-right: max(0px, calc(5.375rem - 4vw));
}

/* WCAG 2.2 Target Size Minimum (2.5.8) asks for 24px. These links were 19px
   tall and passed only via the spacing exception, which is a thin argument to
   rest on in an accessibility agency's own footer. inline-flex so min-height
   applies to an anchor; the text itself does not move. */
.site-footer__legal a {
	align-items: center;
	display: inline-flex;
	min-height: 24px;
}

/* The cookie-preferences control sits in the legal row and dresses exactly
   like its link neighbours (dist/main.css .site-footer__legal a), but it is
   a real button because it changes state on this page rather than going
   anywhere. Same 24px target-size treatment as the links beside it. */
.site-footer__legal-button {
	align-items: center;
	background: transparent;
	border: 0;
	color: var(--color-footer-muted);
	cursor: pointer;
	display: inline-flex;
	font-family: inherit;
	font-size: inherit;
	line-height: inherit;
	min-height: 24px;
	padding: 0;
	transition: color 0.18s ease;
}

/* The color shift alone is a 1.37:1 change, near-invisible feedback, so
   hover and focus also underline; the focus ring itself comes from
   dist/main.css (.site-footer :focus-visible, 2px dashed white). */
.site-footer__legal-button:focus-visible,
.site-footer__legal-button:hover {
	color: var(--color-mint-strong);
	text-decoration: underline;
	text-underline-offset: 0.28em;
}

/* ---- Cookie consent banner ----

   Same panel grammar as the dialogs: paper ground, 1.5px ink hairline, card
   radius, flat (The Flat Field Rule). Bottom-left, so it never crowds the
   Intercom launcher's corner, and non-modal so the page stays fully usable
   behind it: a consent notice that trapped focus or blocked reading would be
   its own accessibility failure on this site of all sites. */
.cookie-consent {
	/* Mint, not paper: paper-on-paper dissolved into the page. Mint is
	   the system's interaction color and the Working Green hairline
	   marks the panel as a place where something can be done, which is
	   the panel's whole job. Calm spark, no alarm. */
	background: var(--color-mint);
	border: 1.5px solid var(--color-green);
	border-radius: var(--radius-card);
	bottom: 1rem;
	color: var(--color-ink);
	left: 1rem;
	max-width: 23rem;
	padding: 1.4rem 1.5rem;
	position: fixed;
	width: calc(100% - 2rem);
	/* Above page content; far below the quote and call dialogs, which
	   must cover this like everything else. */
	z-index: 800;
}

/* The outline button sits on mint here, so it gets a paper fill to stay
   clearly a button; hover keeps its green-border signal. */
.cookie-consent .btn--secondary {
	background: var(--color-paper);
}

.cookie-consent__title {
	font-family: var(--font-display);
	font-size: var(--text-lg);
	font-weight: 500;
	letter-spacing: -0.015em;
	line-height: 1.2;
	margin: 0 0 0.6rem;
}

.cookie-consent__body {
	font-size: var(--text-sm);
	line-height: 1.55;
	margin: 0 0 1.1rem;
}

.cookie-consent__body a {
	color: var(--color-green);
	text-decoration: underline;
	text-decoration-color: var(--color-green);
	text-decoration-thickness: 2px;
	text-underline-offset: 0.28em;
}

/* Hover/focus keeps the underline in Working Green: the sitewide
   mint-strong swap belongs to dark grounds, where it earns 12:1; on paper
   it fades to 1.48:1, exactly when a low-vision user engages the link.
   Focus rings come from the global :focus-visible rule in dist/main.css. */
.cookie-consent__body a:focus-visible,
.cookie-consent__body a:hover {
	color: var(--color-ink);
	text-decoration-color: var(--color-green);
}

/* Populated by overrides.js only when the banner is reopened with a
   standing choice; states the current setting, and warns when a change
   will reload. Label weight so it reads as the panel's one fact. */
.cookie-consent__current {
	font-size: var(--text-sm);
	font-weight: 600;
	line-height: 1.55;
	margin: 0 0 0.8rem;
}

/* While the banner is open, focus-driven scrolling keeps the focused
   element clear of the fixed panel (WCAG 2.4.11 Focus Not Obscured);
   overrides.js toggles the class in step with the banner. */
html.cookie-consent-open {
	scroll-padding-bottom: min(45vh, 20rem);
}

.cookie-consent__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
}

/* Where there is width for it, the banner lies down: one shallow strip
   along the bottom with the words on the left and both choices on the
   right, instead of a tall card squatting on the hero copy. Same DOM,
   same reading order; only the geometry changes. The right inset stops
   the strip short of the corner Intercom's launcher owns, so the
   buttons never sit underneath the bubble. */
@media (min-width: 900px) {
	/* :not([hidden]) is load-bearing, not decoration. The hidden attribute
	   hides an element only through the UA rule [hidden] { display: none },
	   and any author display declaration outranks it. Unscoped, the
	   "display: grid" below meant the banner could never be dismissed on a
	   desktop screen: the choice saved and the trackers booted, but the
	   panel stayed put, so accepting looked like it did nothing. */
	.cookie-consent:not([hidden]) {
		align-items: center;
		column-gap: 2rem;
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		/* Capped rather than edge-to-edge: a full-width strip reads as
		   page furniture, a bounded panel reads as one object asking one
		   question. Anchored bottom left, well clear of Intercom. */
		max-width: 52rem;
		padding: 1.25rem 1.6rem;
		right: auto;
		width: auto;
	}

	/* Full Title scale in the bar too. Shrinking it to body size made the
	   panel headless; presence comes from size, never from weight (The
	   Medium Rule), and the type is already ink at ~15:1 on mint. */
	.cookie-consent__title {
		margin-bottom: 0.4rem;
	}

	.cookie-consent__current {
		margin-bottom: 0.35rem;
	}

	.cookie-consent__body {
		margin: 0;
		max-width: 62ch;
	}

	.cookie-consent__actions {
		flex-wrap: nowrap;
		grid-column: 2;
		grid-row: 1 / span 3;
	}
}

/* A gentle settle-in, and none at all when motion is reduced. */
@media (prefers-reduced-motion: no-preference) {
	.cookie-consent:not([hidden]) {
		animation: cookie-consent-in 0.3s cubic-bezier(0.16, 1, 0.3, 1);
	}

	@keyframes cookie-consent-in {
		from {
			opacity: 0;
			transform: translateY(0.5rem);
		}

		to {
			opacity: 1;
			transform: none;
		}
	}
}

/* ==========================================================================
   /homepage-audit/ — the product page and checkout for the $495 audit
   (page-homepage-audit.php). The checkout moved here from the footer
   <dialog> on 2026-08-27: a page needs none of the hand-rolled modality
   that kept breaking (inert on Stripe's frames, focus traps, 3DS
   layering), and it gives the offer room for real copy.

   Ground is paper throughout; mint is the accent that marks the two
   moments that matter, the price in the hero and the checkout panel
   itself, echoing the grammar the cookie banner set. Working Green stays
   on action only. Flat everywhere: hairlines and tonal panels, no
   shadows (The Flat Field Rule).
   ========================================================================== */

/* The one green word in the hero heading. DESIGN.md sanctions Working
   Green as "the accent word in a heading"; at hero scale it measures
   5.9:1 on paper, past even the normal-text AA bar. */
.hpa-accent {
	color: var(--color-green);
}

/* The price, highlighted mint in running text: ink on mint is 14.6:1.
   Padding is in em so the pill scales with the lede's type size. */
.hpa-price {
	background: var(--color-mint);
	border-radius: 0.45em;
	padding: 0.04em 0.32em;
	white-space: nowrap;
}

/* Section headings on this page. dist's shared rule gives h2 the display
   family and tracking but no size (each section family sizes its own);
   these are bespoke sections, so the size ramp is set here. Medium Rule:
   presence from scale, weight stays 500 via the sitewide override. */
.hpa h2 {
	font-size: var(--text-2xl);
	margin: 0 0 1rem;
	max-width: 22ch;
	text-wrap: balance;
}

.hpa h3 {
	font-size: var(--text-lg);
	line-height: 1.25;
	margin: 0 0 0.5rem;
}

/* ---- Why one page is enough: copy beside the deliverables list ---- */
.hpa-why__grid {
	display: grid;
	gap: 2.5rem 5rem;
	grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
	margin-top: 1.5rem;
}

.hpa-why__copy p {
	font-size: var(--text-base);
	line-height: 1.6;
	margin: 0 0 1rem;
	max-width: 58ch;
	text-wrap: pretty;
}

.hpa-why__copy p:last-child {
	margin-bottom: 0;
}

/* Deliverables: mint-strong square markers, the same marker grammar as
   the audits hero's gold dots, in this page's accent. The square is
   decorative (1.5:1 on paper); the text beside it carries the meaning. */
.hpa-gets {
	display: grid;
	gap: 1rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.hpa-gets li {
	font-size: var(--text-base);
	line-height: 1.6;
	padding-left: 1.4rem;
	position: relative;
}

.hpa-gets li::before {
	background: var(--color-mint-strong);
	border-radius: 2px;
	content: "";
	height: 0.55rem;
	left: 0;
	position: absolute;
	top: 0.5em;
	width: 0.55rem;
}

.hpa-gets strong {
	font-weight: 600;
}

/* ---- How it goes: a real three-step sequence, so numbers earn their
   place. Amber numerals, matching the site's numeral voice (the audits
   process, the old stats), never green: numbers are not actions. ---- */
.hpa-steps {
	counter-reset: hpa-step;
	display: grid;
	gap: 2.25rem 3rem;
	grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
	list-style: none;
	margin: 2rem 0 0;
	padding: 0;
}

.hpa-steps li {
	border-top: 1px solid var(--color-border);
	counter-increment: hpa-step;
	padding-top: 1.1rem;
}

.hpa-steps h3::before {
	color: var(--color-amber);
	content: counter(hpa-step);
	display: block;
	font-size: var(--text-base);
	font-weight: 500;
	margin-bottom: 0.35rem;
}

.hpa-steps p {
	color: var(--color-muted);
	font-size: var(--text-base);
	line-height: 1.6;
	margin: 0;
}

/* The two standing offers after the steps: re-test and the credit.
   Body-size ink, not a footnote; these carry real terms. */
.hpa-steps-after {
	font-size: var(--text-base);
	line-height: 1.6;
	margin: 2.25rem 0 0;
	max-width: 58ch;
	text-wrap: pretty;
}

/* ---- The order section: points on paper, the form in the mint panel.
   Panel column is capped so the form holds a comfortable measure while
   the section spans the content width. ---- */
.hpa-order__grid {
	align-items: start;
	display: grid;
	gap: 2.5rem 5rem;
	grid-template-columns: minmax(0, 1fr) minmax(0, 30rem);
	margin-top: 1.5rem;
}

.audit-order--page {
	background: var(--color-mint);
	border: 1.5px solid var(--color-green);
	border-radius: var(--radius-panel);
	color: var(--color-ink);
}

.audit-order--page .audit-order__inner {
	padding: clamp(1.5rem, 3.5vw, 2.5rem);
}

/* Ordering offline (form option unset): the panel keeps its shape and
   offers the invoice route instead of rendering a dead section. */
.audit-order__offline {
	font-size: var(--text-base);
	line-height: 1.6;
	margin: 0;
}

/* ---- Responsive: columns stack, the panel goes full width. ---- */
@media (max-width: 900px) {
	.hpa-why__grid,
	.hpa-order__grid {
		gap: 2rem;
		grid-template-columns: 1fr;
	}
}

/* ---- Entrance: the hero settles in once, the same rise the cookie
   banner uses, staggered a beat between columns. Content is visible by
   default; `backwards` only holds the start frame during the short
   delay, and reduced motion gets no animation at all. ---- */
@media (prefers-reduced-motion: no-preference) {
	.hpa-hero .hero__main,
	.hpa-hero .hero__aside {
		animation: hpa-rise 0.45s cubic-bezier(0.16, 1, 0.3, 1) backwards;
	}

	.hpa-hero .hero__aside {
		animation-delay: 0.12s;
	}

	@keyframes hpa-rise {
		from {
			opacity: 0;
			transform: translateY(0.6rem);
		}
	}
}
