/**
 * Dealer-FX Brand Base / Global Overrides
 * -----------------------------------------------------------------------------
 * Applies the brand tokens to global elements on top of the parent theme. Component-
 * level styling (specific modules, headers, footers) should live in their own
 * enqueued stylesheets that also depend on brand-tokens.
 *
 * Depends on: brand-tokens.css (CSS custom properties)
 * Note: the parent theme carries high selector specificity, so a few rules use targeted
 * overrides. These are intentionally conservative global foundations.
 */

/* --- Layout: content container width ---------------------------------------- */
/* THE CONTAINER WIDTH IS SALIENT'S TO DECIDE NOW. Theme Options › General Settings
   › Functionality › "Max Website Container Width" sets it, and this file no longer
   argues with the result.

   WHAT USED TO BE HERE, AND WHY IT WENT. Three rules pinned the container to a
   1280px token: an override of Salient's own `--container-width`, a `max-width` on
   `.container` prefixed with `html body` to outrank
   `body[data-header-format="left-header"] .container`, and the block editor's
   content size. The reason was sound — Salient's slider floor is 1425px, so a
   narrower site genuinely cannot be set in the UI — but the consequence was that
   the Theme Options field did nothing, silently, from four files away. A setting
   that looks live and is inert costs more than the 145px it was buying.

   ONE LINE SURVIVES, and it is not an override: the block editor's content size has
   no Salient equivalent, so it is pointed at the token, which now reads Salient's
   value. Blocks therefore stay aligned with the container instead of ignoring it.

   TO GO NARROWER AGAIN, put a length back in --dfx-container-width rather than
   restoring these rules. One value, one place, and the Theme Options field is then
   knowingly inert rather than accidentally so. */
html body {
	--wp--style--global--content-size: var(--dfx-container-width);
}

/* --- Typography: body -------------------------------------------------------- */
body,
.container-wrap,
p,
.wpb_text_column,
input,
textarea,
select,
button {
	font-family: var(--dfx-font-body);
}

body {
	font-weight: var(--dfx-weight-body);
	line-height: var(--dfx-line-height-body);
	color: var(--dfx-color-body);
}

/* --- position: sticky, made possible ---------------------------------------------
   `body { overflow: hidden }` arrives from the parent theme, which needs it to stop
   full-bleed rows widening the document. The side effect is that it makes BODY a
   scroll container, and a scroll container that never scrolls — `html` is the scroll
   port here. Every `position: sticky` inside body therefore resolves against a box
   that has no scroll position, so it pins to nothing and behaves as `static`.

   MEASURED, NOT REASONED: a sticky probe at body level sat at -1297px with the
   parent theme's value and at 72px with the two below.

   `clip` RATHER THAN `visible` ON THE X AXIS, so the containment survives. `clip`
   does not create a scroll container, which is the whole difference, and it is the
   one value that legally pairs with `overflow-y: visible` — `hidden` would force the
   y axis back to `auto` and put us where we started.

   WHAT THIS FIXES BEYOND THE TABLE: `.dfx-integrations-grid__filter` has been
   declared sticky since the integrations wall was built and has never held, for this
   reason. It was written as "pins under whatever the header leaves" and pinned to
   nothing.

   THE FALLBACK IS TODAY'S BEHAVIOUR. A browser without `overflow: clip` — Safari
   before 16 — drops both declarations as invalid and keeps the parent theme's
   `hidden`: containment intact, sticky inert, exactly as now. */
body {
	overflow-x: clip;
	overflow-y: visible;
}

/* --- Typography: heading hierarchy ------------------------------------------ */
/* NOTE — OF THE THREE PROPERTIES BELOW, ONLY font-family LANDS ON H4–H6 FROM THIS
   RULE. All three are re-declared at higher specificity further down.
   Salient's generated `body h4` (salient-dynamic-styles.css) is specificity 0-0-2
   and beats the bare `h4` here at 0-0-1, so anything this rule alone declares for
   h4–h6 is overruled.
     LINE-HEIGHT  escalated — see the block three below.
     FONT-WEIGHT  escalated — see the block immediately below.
     LETTER-SPACING  escalated at ALL SIX levels — see the block two below, and read
       it before trusting anything about tracking. This was documented for a long time
       as a single CONCESSION affecting h4–h6, on the reasoning that h1–h3 already got
       the token and 0.01em is not visible anyway. Measuring the rendered page showed
       the token was live on none of the six: h1 came out at Salient's -0.015em, h3 at
       ZERO, and h2 at -0.01em only because Salient's h2 value happens to equal ours.
       That coincidence is what kept the concession looking half as large as it was.
   All three properties are still declared here, harmlessly, so this rule reads as
   the whole heading treatment anywhere Salient's generated file is absent — the
   editor, an unstyled context, or a future theme swap.
   Flagged here so the next person does not spend an afternoon proving the tokens
   are being ignored. */
h1, h2, h3, h4, h5, h6,
.nectar-fancy-heading,
.nectar-fancy-box h2,
.wpb_wrapper h1,
.wpb_wrapper h2,
.wpb_wrapper h3,
.wpb_wrapper h4,
.wpb_wrapper h5,
.wpb_wrapper h6 {
	font-family: var(--dfx-font-heading);
	line-height: var(--dfx-line-height-heading);
	letter-spacing: var(--dfx-letter-spacing-heading);
	/* Color is NOT set here — see the heading-color block below. It cannot be a
	   flat declaration on this rule, for the reason the previous comment gave. */
}

/* ============================================================================
   HEADING COLOR — the one place a heading gets a color, and it is indirect.
   ============================================================================

   THE PROBLEM THIS SOLVES. Headings used to declare no color at all and simply
   inherit from body, which is why they were the same ink as body copy: there was
   no heading color to be inconsistent with. The comment where this rule now sits
   said "headings inherit their context", and that was load-bearing, not lazy —
   a dark full-width section sets `color` on its wrapper and every heading inside
   goes light for free.

   SO WHY NOT JUST DECLARE A COLOR. Because inheritance loses to any direct
   declaration, always, at any specificity. `body h2 { color: … }` on the heading
   beats a `color` set on the section wrapper the heading sits inside — so a flat
   heading color turns every heading on a dark band graphite-on-obsidian. On the
   homepage alone that is 14 headings: 11 under Salient's `.light` marker and 3
   under a column carrying an inline color from the page builder.

   THE INDIRECTION. The color is read from --dfx-heading-ink, a custom property,
   and custom properties DO inherit. Any context that owns its text color
   re-points the property for everything beneath it, and the heading rule below
   picks that up without knowing the context exists.

   `currentColor` INSIDE THE color PROPERTY MEANS "the inherited color" — that is
   the spec behavior, not a trick — so re-pointing the property at currentColor
   restores exactly the old inherit-from-context result.

   THE THREE ESCAPE HATCHES, and why each is needed:
     .light          Salient's marker for a section with LIGHT TEXT (the name
                     describes the text, not the background — it is the dark-band
                     class). 11 of the 14 homepage cases.
     .dfx-band       This theme's own dark band; already sets --dfx-band-heading,
                     so headings track that instead.
     [style*="color"]  A page-builder column with an inline color. This
                     DELIBERATELY over-matches — `background-color` contains the
                     substring too — and over-matching is the safe direction: a
                     false positive means the heading inherits, i.e. the behavior
                     that shipped for the last year. A false negative would be a
                     silent contrast regression on a dark band. When the inline
                     colors are stripped from builder content this hatch stops
                     firing on its own and can be deleted.

   SPECIFICITY MIRRORS THE WEIGHT AND LINE-HEIGHT BLOCKS BELOW, and for the same
   reason: salient-dynamic-styles.css ships
   `body, body h1, … body h6 { color: var(--nectar-page-text-color, #525967) }`
   at 0-0-2 from its Typography options. #525967 is not in the Dealer-FX palette
   at all. A bare `h2` at 0-0-1 loses to it; `body h2` ties at 0-0-2 and wins on
   load order. `.row .col h*` is included because Salient ships the same value
   three ways and `body h4` alone leaves every heading inside a builder column
   untouched — the size scale already works around exactly this.

   Salient's own Text Color option should also be set to #424752 in Theme Options
   so the generated file agrees with the token instead of being overridden. That is
   a database change, not a code one; it is tracked with the builder cleanup. */
:root {
	/* SALIENT'S OWN LIGHT-TEXT COLOR, SET FROM THIS FILE RATHER THAN FOUGHT.
	   The parent theme paints every element inside a `.light` section — its dark-band
	   marker — with `color: var(--nectar-font-light-color, #fff)` at 0-1-1, which
	   outranks the heading and label rules below. The variable is populated from Theme
	   Options and resolves to Steel Gray #9AA0A6 here, so "light" text on a dark band
	   was a mid gray: legible at 6.35:1, but it also sat DIMMER than the muted labels
	   beside it, inverting the one relationship the label ink exists to express.

	   DEFINING THE PARENT THEME'S OWN CUSTOM PROPERTY IS THE CHEAP WAY IN. Salient's
	   rule already reads it, so pointing it at Signal White makes that rule produce the
	   right answer — no escalation, no new selectors, no !important, and the 0-1-1 rule
	   that used to be a problem becomes the thing doing the work. Headings on a dark band
	   now render Signal White at 15.5:1 and labels sit behind them at 8.16:1.

	   AND IT IS DELIBERATELY NOT A THEME OPTIONS CHANGE. The obvious fix is to set the
	   light font color in the options panel, and this project has already learned why
	   not: those values live in the WP database, are invisible to git, differ per
	   environment, and do not survive a Kinsta rebuild. The footer background was moved
	   out of Theme Options into CSS for exactly this reason, and the footer heading color
	   shipped wrong on staging twice because it was left to the database. The rule this
	   file states elsewhere applies here too — if the design decides a property, this file
	   states it. */
	--nectar-font-light-color: var(--dfx-signal-white);

	--dfx-heading-ink: var(--dfx-color-heading);
	/* The label step's ink, indirect for exactly the same reason and re-pointed by the
	   same list below. h6 is muted secondary rather than heading ink, because a label
	   annotating a set should sit under its contents rather than compete with them. */
	--dfx-label-ink: var(--dfx-color-text-muted);

	/* THE COLUMN-HEAD INK, SPLIT OFF FROM --dfx-label-ink AT DAVE'S DIRECTION 2026-08-12.
	   Two consumers only: the footer widget titles and the megamenu column heads — the
	   caps labels that sit above a list of links. Everything else reading the label role
	   (.dfx-eyebrow, the "Trusted by" separator) stays on Pewter via --dfx-label-ink,
	   which is what "it can stay pewter by default" means.

	   THIS IS A KNOWN AA FAILURE AND IT IS DELIBERATE. Steel Gray is 2.64:1 on white
	   against the 4.5:1 that WCAG AA requires for normal text — and these do not qualify
	   for the 3:1 large-text allowance either, since that needs 24px, or 18.66px at bold,
	   and these are 13px. So they fail both bars. Pewter, which they read until today,
	   is 5.11:1 and passes. Dave was shown these numbers and chose Steel for the quieter
	   look; recording the trade here rather than in a ticket so the next person to measure
	   the footer finds the reason instead of re-reporting the bug.

	   IT IS ALSO A REGRESSION OF A FIX MADE EARLIER THE SAME DAY, which is worth being
	   plain about: the megamenu heads had just moved off Salient's Steel Gray onto Pewter,
	   which is what retired the long-standing "column heads under AA" item. That item is
	   open again by choice. The difference from before is that the value is now stated
	   here, in git, identically on both environments, rather than arriving from the
	   database — so it is a decision instead of an accident.

	   INDIRECT, NOT A HEX ON THE RULES, so the dark-band list below can re-point it and so
	   reverting is one line. To go back to a passing ratio, point this at
	   var(--dfx-label-ink); Nickel #6F7688 at 4.54:1 is the lightest step on this hue that
	   still clears AA, if something between the two is ever wanted. */
	--dfx-column-head-ink: var(--dfx-steel-gray);
}

.light,
.dfx-band,
/* The product card is a dark context of its own — see .dfx-card--product in
   components.css, which sets a dark fill and a light `color`. Without this entry the
   `body h3` rule below would paint its title Graphite #2B2F36 on Obsidian #141E2C at
   1.25:1, because that rule is 0-0-2 and the card's own `color` is 0-1-0 on an
   ancestor. Joining this list is the fix rather than overriding the title, so the
   badge and any future heading inside the card inherit the same way. */
.dfx-card--product,
[style*="color"] {
	--dfx-heading-ink: currentColor;
}

/* THE LABEL INK ON DARK SURFACES, AND IT IS NOT A MIX OF currentColor — THAT WAS TRIED
   AND IT FAILED IN THE BROWSER.

   The reasoning behind the first attempt was sound: a heading on a dark band should take
   the band's full text color, so currentColor is right for --dfx-heading-ink, but a
   label should stay SUBDUED relative to that text, so 70% of it seemed like the same
   idea one step back.

   WHAT THAT MISSED is that currentColor is only the band's text color if nothing between
   the band and the element has changed it. On the homepage something has: the "Trusted by"
   separator sits under a builder column carrying an inline `color:#9AA0A6`, so
   currentColor was ALREADY a muted gray and taking 70% of it composited down to 1.19:1 on
   Obsidian. Measured, not predicted — the rule looked correct and rendered unreadable.

   SO THE MIX KEYS OFF THE BAND'S OWN TOKEN INSTEAD, with Signal White as the fallback.
   That is the same `--dfx-band-heading` pattern the band components in components.css
   already use for subdued text, and it cannot be polluted by an inline color halfway
   down the tree. On Obsidian it lands at 7.7:1.

   THE INLINE-COLOR HATCH IS SPLIT OUT BELOW because it is the one case where the surface
   is genuinely unknown — [style*="color"] deliberately over-matches and catches
   `background-color` too, so it can fire on a light surface as easily as a dark one. A
   light-on-light label would be the same bug in the other direction. */
.light,
.dfx-band,
.dfx-card--product {
	--dfx-label-ink: color-mix(in srgb, var(--dfx-band-heading, var(--dfx-signal-white)) 70%, transparent);
}

/* UNKNOWN SURFACE: INHERIT EXACTLY, and give up the step-back rather than guess. A label
   the same color as its heading is a missed nuance; a label the same color as its
   background is a bug. This rule comes after the one above so an element carrying both
   .light and an inline color takes this safer branch. */
[style*="color"] {
	--dfx-label-ink: currentColor;
}

/* ============================================================================
   .dfx-accent — BRASS, OPT-IN, AND DARK SURFACES ONLY.
   ============================================================================

   WHY THIS EXISTS. .dfx-eyebrow used to declare `color: var(--dfx-brass)` as its
   DEFAULT, which had it backwards twice over. Brass is 2.29:1 on Signal White, so
   the default state of a label utility was a contrast failure on the site's base
   surface. And the brand reserves Brass for premium, high-emphasis moments at
   "maximum restraint" — a color you are told to use sparingly cannot also be
   what you get by not choosing. Now the default is --dfx-label-ink (Pewter,
   4.72:1) and Brass is something you ask for.

   IT RE-POINTS THE INK RATHER THAN SETTING color, which is the whole reason it is
   worth having as its own class. `color: brass` would only ever fix the one
   element it is on. Re-pointing --dfx-label-ink means this works on .dfx-eyebrow,
   on h6 and .dfx-h6 labels, on the separator, and on anything added later that
   reads the label role — and because custom properties inherit, it can go on a
   wrapper and take everything beneath it with it.

   DARK SURFACES ONLY, AND THAT IS NOT A STYLE PREFERENCE. Measured against the
   two base surfaces:

     Brass  #B5A46A    2.29:1 on Signal White      6.78:1 on Obsidian
     Gold   #C5A84D    2.14:1                      7.26:1
     Amber  #C47E3A    3.04:1                      5.11:1
     Sienna #A0522D    5.19:1                      2.99:1

   The premium family is a dark-surface family. Sienna is the only member legible
   on light and it is the only one that fails on dark, so there is no accent that
   works everywhere — which is why this class does not try to be surface-aware.
   Resolving to a different hue per surface was considered and rejected: gold on
   dark and burnt orange on light reads as two brands, and a class that silently
   changes color is harder to reason about than one that is simply documented.
   Use it on bands, cards and imagery. On Signal White it will fail AA, visibly
   and predictably.

   LAST IN THE INK RULES ON PURPOSE. It sits after the dark-band hatches and the
   inline-color hatch, all at 0-1-0, so an explicit opt-in beats an automatic
   re-point when both land on one element. Asking for accent should win. */
.dfx-accent {
	--dfx-label-ink: var(--dfx-brass);
}

/* SALIENT'S .light MARKER OUTRANKS BOTH LABEL RULES, AND THAT MADE .dfx-accent INERT
   WHERE IT MATTERS MOST. Found by measuring, not by reading.

   The parent theme's style.css ships `.light h1, … .light h6, .light p { color:
   var(--nectar-font-light-color, #fff) }` at 0-1-1. That beats `.dfx-eyebrow` at 0-1-0
   and `body h6` at 0-0-2, so on any section carrying .light — Salient's dark-band
   marker, and 11 of the 14 dark headings on the homepage — the label's color came from
   the parent theme and NOTHING in the label-ink system applied.

   THE CONSEQUENCE WAS NOT A CONTRAST FAILURE, WHICH IS WHY IT WOULD HAVE SURVIVED A
   CASUAL CHECK. Salient's light-font color resolves to Steel Gray here, 6.35:1 on
   Obsidian, perfectly legible. Two real things were still broken: labels and headings
   both rendered the same gray, losing the step-back the muted ink exists to draw; and
   .dfx-accent did nothing at all on .light bands, i.e. the opt-in was dead in precisely
   the place it is documented as being for.

   THE ESCALATION IS THE SAME SHAPE THIS FILE USES FOR SIZE, WEIGHT AND LEADING.
   `.light .dfx-eyebrow` is 0-2-0 and beats `.light p` outright; `.light h6` and
   `.light .dfx-h6` tie Salient's 0-1-1 and win on load order, as this file already
   relies on elsewhere. Three selectors, and they restore the whole ink system —
   including the accent — inside dark bands. */
.light .dfx-eyebrow,
.light h6,
.light .dfx-h6 {
	color: var(--dfx-label-ink);
}

body h1, body h2, body h3, body h4, body h5, body h6,
.row .col h1, .row .col h2, .row .col h3,
.row .col h4, .row .col h5, .row .col h6,
.wpb_wrapper h1, .wpb_wrapper h2, .wpb_wrapper h3,
.wpb_wrapper h4, .wpb_wrapper h5, .wpb_wrapper h6 {
	color: var(--dfx-heading-ink);
}

h1, h2, h3 { font-weight: var(--dfx-weight-heading); }
h4, h5 { font-weight: var(--dfx-weight-heading-light); }
/* h6 IS NOT IN THAT SECOND LIST ANY MORE. It is the label step and takes 600 from the
   label block at the bottom of this section, not 500 from the small-heading pair. */

/* H4–H6 WEIGHT, ESCALATED TO 0-0-2 / 0-2-1 FOR THE SAME REASON AS LINE-HEIGHT.
   Salient's generated file sets `body h4` and `.row .col h4` to 500 while setting
   h5 and h6 to 600, all of which outrank the bare rule above. The result was three
   sibling levels running two different weights, neither of them the token's — a
   24px H4 at 500 sitting under a 40px H2 at 600, and h6 heavier than the h4 above
   it.

   THE RULE IS STILL REQUIRED THOUGH THE SPLIT IS OFF. --dfx-weight-heading-light points
   at --dfx-weight-heading as of 2026-08-14, so all six levels are 600 — but Salient
   still ships 500 on `body h4`, so removing this would put h4 back to 500 while h5 sat
   at 600. The rule fights the parent theme; the token decides the value.

   NO .dfx-h* CLASSES IN THIS LIST, DELIBERATELY. Weight follows the LEVEL in this theme
   — the pairing table's job is size — so `h2.dfx-h4` is 20px at its own h2 weight. With
   both tokens at 600 that distinction is invisible again, as it was before the split;
   the h3.dfx-h5 / h4.dfx-h5 collision the split produced is gone.

   No !important needed — Salient declares none on these, unlike the vc_custom_heading
   line-height below. */
body h4, body h5,
.row .col h4, .row .col h5,
.wpb_wrapper h4, .wpb_wrapper h5 {
	font-weight: var(--dfx-weight-heading-light);
}

/* HEADING TRACKING, ESCALATED AT ALL SIX LEVELS — and the audit that produced this
   rule is the reason it covers six rather than the three it started as.

   WHAT THIS WAS MEANT TO FIX. --dfx-letter-spacing-heading was believed live on h1–h3
   and conceded on h4–h6, where Salient's generated -0.02em beat the bare grouped
   selectors in the heading block above. So the first version of this rule escalated
   h4–h6 only, to close what looked like a half-applied token.

   WHAT MEASURING THE RENDERED PAGE FOUND. The token was live on NONE of the six.
   Salient's generated file ships three different tracking values, each at a
   specificity the bare `h1`–`h6` rule cannot reach:

     #page-header-bg h1, body h1, body .row .col.section-title h1, …   -0.015em
     #page-header-bg h2, body h2, …                                    -0.01em
     body h3, .row .col h3, …                                          0

   h1 rendered -0.015em. h3 rendered ZERO — no tracking at all, on the step that
   carries most section headings. And h2 rendered -0.01em, which IS the token's value,
   so h2 looked like proof the token was working. It was a coincidence, and it is why
   the concession was believed to stop at h4 for as long as it did. One level
   accidentally correct is worse than none correct, because it makes the other five
   look deliberate.

   SO THIS COVERS THE WHOLE LADDER. Three levels are the fix that was asked for and
   three are the correction the measurement forced; there is no version of this that
   only needs half. If a future value is ever split by size the way leading is, split
   this rule then — not now, on the assumption that h1–h3 are already fine.

   THE SELECTOR LIST IS THE WEIGHT RULE'S, EXTENDED — not a new pattern. Same three
   families, same 0-0-2 / 0-2-1 ties, same reliance on this file loading after the
   generated one. Nothing here needs !important; Salient declares none on tracking.
   The page-header h1 needs one further escalation, for exactly the reason size and
   leading already do — see the block below.

   NO .dfx-h* CLASSES, MATCHING THE WEIGHT RULE RATHER THAN THE LINE-HEIGHT ONE.
   Tracking is a property of the type at a given size, so an argument exists for the
   other choice — but the token is a single value across all six steps, so a borrowed
   size gets the same tracking either way and adding the classes would buy nothing
   while implying the value varies.
   SUPERSEDED — the token is no longer a single value and the classes are now in the
   list. Left here because the reasoning is still the right reasoning for a flat token,
   and the condition it depended on is named in it. See the band split below.

   ONE CONSEQUENCE WORTH NAMING: .dfx-hero__eyebrow in components.css already computed
   -0.01em from this token, and a real h4 beside it computed -0.02em. That mismatch was
   documented there as invisible-but-known; it is now simply gone. */
/* SPLIT BY SIZE NOW, WHICH IS THE THING THE PARAGRAPH ABOVE SAID TO DO WHEN THE VALUE
   STOPPED BEING SINGLE. Three bands: h1, then h2+h3, then h4+h5. h6 is absent from all
   of them because it is the caps label step and carries +0.14em of its own.

   THE .dfx-h* CLASSES ARE NOW IN THIS LIST, WHICH REVERSES THE NOTE ABOVE, and the
   measurement is what reversed it. The old note left them out because one flat value made
   a borrowed size track identically either way — true then, false the moment the value
   split by band. My first version of this rule kept them out anyway and argued the
   mismatch was negligible: 0.01em on a 20px heading, a fifth of a pixel per character.

   THEN I MEASURED A PRODUCT PAGE AND IT IS NOT AN EDGE CASE, IT IS THE DOMINANT CASE.
   Of 13 visible headings on /products/scheduling-package, TEN wear a borrowed size —
   seven `h3.dfx-h5` card titles at 18px and three `h2.dfx-h4` section headings at 20px,
   which is the pairing table's standard product-page shape. All ten were taking h2/h3's
   -0.02em while rendering at h4/h5 sizes. So the "negligible mismatch" was the tracking
   that nearly every heading on every product page actually got.

   SO THIS RULE NOW MIRRORS THE LINE-HEIGHT RULE, NOT THE WEIGHT RULE. That is the right
   parent to copy: tracking, like leading, is a property of the type at its rendered size,
   whereas weight follows the outline level by deliberate design. The classes sit at 0-1-0
   and beat `body h3` at 0-0-2, so a borrowed size now gets its own band — same mechanism
   and same justification the leading block already relies on.

   .dfx-h6 IS STILL ABSENT, and that is not an oversight: h6 is the caps label step and
   carries +0.14em from its own block. Adding it here would fight that. */
body h1,
.row .col h1,
.wpb_wrapper h1,
.dfx-h1 {
	letter-spacing: var(--dfx-letter-spacing-h1);
}

body h2, body h3,
.row .col h2, .row .col h3,
.wpb_wrapper h2, .wpb_wrapper h3,
.dfx-h2, .dfx-h3 {
	letter-spacing: var(--dfx-letter-spacing-heading);
}

body h4, body h5,
.row .col h4, .row .col h5,
.wpb_wrapper h4, .wpb_wrapper h5,
.dfx-h4, .dfx-h5 {
	letter-spacing: var(--dfx-letter-spacing-heading-small);
}

/* ============================================================================
   h6 IS THE LABEL STEP — the one level that is not a heading.
   ============================================================================

   h6 is 16px, the same as body copy, and that only works because nothing else
   about it looks like body copy: uppercase, 0.14em tracking, weight 600, muted
   ink. Those four properties are the role. The size is incidental to it, which
   is exactly why sharing 16px with a paragraph costs nothing.

   THIS FILE HAS MADE THE OPPOSITE MISTAKE, so the distinction is worth stating.
   h5 used to sit at 16px on the argument that weight 600 was enough to separate
   a card title from the copy beneath it. It was not, because both were sentence
   case and weight alone cannot carry a hierarchy across two sentence-case lines.
   Case and tracking can. That is the whole difference between this rule and the
   thing it learned from.

   IT IS A PROMOTION, NOT AN INVENTION. All four places the h6 step was used
   already wrote these same four declarations by hand — .dfx-product-group__title,
   the product feature list's group labels, the "Trusted by" separator and the
   footer widget titles. The role was already agreed; it was just stated four
   times. Those components now inherit it and keep only what is genuinely theirs.
   (The feature list's labels since became Salient toggle titles and left this
   step behind; the other three still take it.)

   THE .dfx-h6 CLASS IS IN THIS RULE, WHICH BREAKS THE "SIZE ONLY" INVARIANT the
   size-scale block below sets out — deliberately, and it is the one exception.
   The reasoning: for h1–h5 the class borrows a SIZE, so carrying anything else
   would be overreach. h6 is not a size, it is a ROLE, so a component reaching for
   `.dfx-h6` is asking for the label treatment and not for 16px — 16px it could
   have had from body. An `h2.dfx-h6` section label therefore looks like every
   other label on the site, which is the point of the pairing table's "small label
   beside a rule" row. If .dfx-h6 carried size alone, that row would produce an
   h2 indistinguishable from a paragraph.

   COLOR IS INDIRECT, FOR THE REASON THE HEADING-COLOR BLOCK ABOVE GIVES AT
   LENGTH. Pewter is 4.72:1 on Signal White and unreadable on a dark band, so a
   flat muted color here would break every label on an inverted section. The
   escape-hatch list re-points --dfx-label-ink alongside --dfx-heading-ink, and on
   a dark context it resolves to 70% of the inherited text color — the same
   subdued-on-band treatment the band components already use.

   SPECIFICITY MATCHES THE OTHER ESCALATIONS because it has to fight the same
   generated file: Salient sets tracking and weight on `body h6` and `.row .col h6`
   and would otherwise flatten two of the four properties. */
body h6, .row .col h6, .wpb_wrapper h6, .dfx-h6 {
	font-weight: var(--dfx-weight-emphasis);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--dfx-label-ink);
}

/* HEADING LINE-HEIGHT, AT 0-0-2 SO IT SURVIVES SALIENT'S GENERATED FILE.
   The one value from the block above that is escalated rather than conceded.

   salient-dynamic-styles.css ships `#page-header-bg h1, body h1, … { line-height:
   1.094 }` and the equivalent for h2 (1.094) and h3 (1.3), built from Salient's
   typography options. `body h1` is 0-0-2 and the grouped bare selectors above are
   0-0-1, so every heading on the site was running Salient's leading while wearing
   this theme's sizes — a 56px h1 with 61px of leading instead of 70px, which reads
   as cramped at display sizes and was reported as "tight" more than once.

   WHY THAT KEPT COMING BACK. Salient regenerates that file whenever its options are
   saved or its cache is cleared, so any fix made by editing or clearing it is undone
   by the next person who touches Theme Options. Winning on specificity from the
   child theme is the only version of this fix that stays fixed.

   THE SELECTOR LIST MIRRORS THE SIZE SCALE BELOW, INCLUDING `.row .col h*`, and it
   has to. Salient ships the same value three ways — `body h4` at 0-0-2, `.row .col
   h4` at 0-2-1, and `.row .vc_col-sm-4 h3` — so `body h4` alone fixes a heading in
   a template and leaves every heading inside a builder column untouched. That is
   exactly the split the size scale already worked around, which is why it reads
   `body h3, .row .col h3, .dfx-h3`: same problem, same shape, one property over.
   The ties at 0-0-2 and 0-2-1 fall to this file because it loads after the
   generated one.

   The .dfx-h* classes are included at 0-1-0 so a level wearing a borrowed size gets
   that size's leading with it. That is load-bearing now the value differs by size: an
   `h2.dfx-h4` section title gets 0-0-2 from `body h2` and 0-1-0 from `.dfx-h4`, so the
   class wins and the leading follows the SIZE it renders at rather than the level it
   claims in the outline. Which is the whole point — leading is a property of the type,
   not of the document structure.

   SPLIT AT THE h4 BOUNDARY, one value for display sizes and one for small. 1.25 across
   the whole scale gave a small wrapped title four pixels of air between two lines of
   600-weight type, which reads as a single block. See the token definitions for the
   arithmetic. The boundary is the same one --dfx-weight-heading-light already switches
   at, so the scale changes character in one place rather than two.

   No !important here. Specificity is enough against these rules, and a section that
   genuinely needs different leading — .dfx-display reads --dfx-line-height-display —
   should still be able to say so. The one place !important IS required is below. */
/* NOW SPLIT THREE WAYS ABOVE THE h4 BOUNDARY TOO, one selector list per level.
   This block was h1+h2+h3 sharing 1.25. It is three blocks because the three levels
   now hold three values — h1 fluid, h2 1.3, h3 1.35 — for the reason the token
   definitions set out: 1.25 across a 24→64px range is right at one point on the ramp.
   Same selector families, same specificity story, same reliance on this file loading
   after Salient's generated one. Nothing about the cascade changed; only the count. */
body h1,
.row .col h1,
.wpb_wrapper h1,
.dfx-h1 {
	line-height: var(--dfx-line-height-h1);
}

body h2,
.row .col h2,
.wpb_wrapper h2,
.dfx-h2 {
	line-height: var(--dfx-line-height-h2);
}

body h3,
.row .col h3,
.wpb_wrapper h3,
.dfx-h3 {
	line-height: var(--dfx-line-height-heading);
}

/* THE PAGE-HEADER TITLE AGAIN, for the reason set out in the size scale below.
   Salient carries size AND leading on the same over-specific selector list, so
   `.row .col h1` at 0-2-1 lost the 1.094 on the page title even while winning it
   everywhere else — the 64px headline was rendering with 70px of line box, and once the
   size was corrected to the ramp's h1 that same 70px became visibly tight against it.
   Fixing the size without this would leave the title at the ramp's size and the parent
   theme's leading, which is the pairing that reads as cramped. */
html body .row .col.section-title h1,
html body .row .col.section-title.span_12 h1,
html body #page-header-bg h1,
html body #page-header-bg .span_6 h1 {
	/* Reads the h1 band, not the old shared value — this IS an h1, and it is the one
	   most likely to be the fluid clamp's whole point: a long page title at the 34px
	   floor on a phone. */
	line-height: var(--dfx-line-height-h1);
	/* TRACKING RIDES ALONG, THIRD PROPERTY ON THE SAME SELECTOR LIST. Salient carries
	   size, leading AND tracking for the page title on one over-specific list —
	   `body .row .col.section-title h1` at 0-3-2 for the -0.015em — so the all-levels
	   rule above at 0-0-2 reaches every h1 on the site except this one. Adding it here
	   rather than a fourth block: the selectors, the reason and the fix are identical,
	   and three properties in one place is the thing that stops the next property being
	   forgotten. `html body …` at 0-3-3 outranks Salient's 0-3-2 without !important. */
	letter-spacing: var(--dfx-letter-spacing-h1);
}

body h4, body h5, body h6,
.row .col h4, .row .col h5, .row .col h6,
.wpb_wrapper h4, .wpb_wrapper h5, .wpb_wrapper h6,
.dfx-h4, .dfx-h5, .dfx-h6 {
	line-height: var(--dfx-line-height-heading-small);
}

/* WPBAKERY CUSTOM HEADINGS — THE ONE CASE THAT NEEDS !important.
   Salient's generated file declares `.wpb_wrapper h3.vc_custom_heading {
   line-height: 1.3 !important }` and the equivalent for the other levels. Nothing
   short of !important at equal-or-greater specificity beats that, so this matches
   it rather than losing quietly — which matters because vc_custom_heading is what
   the page builder emits for most headings on the homepage, i.e. the majority of
   headings a visitor actually sees.

   Verified before writing this: no per-element line-height is generated for these
   elements today, inline or in a <style> block, so this overrides Salient's global
   default and not an editor's deliberate choice. If someone ever does set leading
   on a single heading in the builder and finds it ignored, this rule is why.

   SPLIT PER LEVEL TO MATCH THE BLOCK ABOVE. Salient's 1.3 !important applies at every
   level, so each of the three bands has to beat it separately. This is the majority of
   headings a visitor actually sees, so a level left behind here would be the version of
   the ramp most people got. */
.wpb_wrapper h1.vc_custom_heading {
	line-height: var(--dfx-line-height-h1) !important;
}

.wpb_wrapper h2.vc_custom_heading {
	line-height: var(--dfx-line-height-h2) !important;
}

.wpb_wrapper h3.vc_custom_heading {
	line-height: var(--dfx-line-height-heading) !important;
}

/* Split the same way as the block above, for the same reason. A builder heading at h4
   or below is small type and needs the looser value; Salient's 1.3 !important applies
   at every level, so both halves have to match it rather than only the display one. */
.wpb_wrapper h4.vc_custom_heading,
.wpb_wrapper h5.vc_custom_heading,
.wpb_wrapper h6.vc_custom_heading {
	line-height: var(--dfx-line-height-heading-small) !important;
}

/* AN EYEBROW IS NOT A SMALL HEADING, and this is the one place that distinction has to
   be re-stated with !important. The Custom Heading element emits an <h6>, so an eyebrow
   built in the builder was picking up the heading leading above — 1.4 against the
   utility's 1.2. Identical class, identical margin, a 2.6px taller line box, and the
   label read as sitting further from its heading than the same eyebrow printed as a <p>
   by the theme. That is the gap that looked like a margin problem and was not.

   MUST BE !important AND MUST SIT AFTER THE RULE ABOVE. Salient declares 1.3 !important
   at this level and the rule above matches it, so a plain declaration loses at any
   specificity and an equal-specificity important one only wins on source order. 1.2 is
   the utility's own value; nothing new is chosen here, it is only carried past two
   important declarations.

   Scoped to the three levels the block above covers rather than all six: h1–h3 are
   display sizes no eyebrow should ever be, and a rule reaching them would be inviting
   one. */
.wpb_wrapper h4.dfx-eyebrow,
.wpb_wrapper h5.dfx-eyebrow,
.wpb_wrapper h6.dfx-eyebrow {
	line-height: 1.2 !important;
}

/* ============================================================================
   THE HEADING SIZE SCALE — six steps, and the only place a heading size exists.
   ============================================================================

   TWO AXES, DELIBERATELY SEPARATE.

     LEVEL       h1–h6, chosen for the document outline. This is the SEO contract:
                 one h1, no skipped levels, nesting that describes the page.
     SIZE        one of six steps. Chosen for the design.

   They usually agree, and when they do the element needs no class at all. When
   they disagree — a section that must be an h2 but should read at 20px — the
   element keeps its correct level and takes a `.dfx-h*` class for the size it
   should look like:

       <h2 class="dfx-h4">Workflows</h2>

   WHY THIS IS NOT "A BUNCH OF RANDOM CLASSES".

   The set is CLOSED and it mirrors the scale exactly: `.dfx-h4` is h4's size, to
   the pixel, because both read the same token in the same rule below. A component
   cannot invent a size — the worst it can do is pick a different rung of the ladder.
   That is the whole difference from what this replaced, where components declared
   1rem, 12px and body weights of their own and nothing tied them to anything.

   THE INVARIANT, and it is greppable: no rule outside this block may declare
   font-size on a heading. If a component sets one, that is the bug, not a style.

   THE CLASSES CARRY SIZE ONLY, WITH ONE EXCEPTION: .dfx-h6 also carries the label
   treatment — uppercase, tracking, weight and muted ink — because h6 is a ROLE rather
   than a size. See the label block above for the reasoning. Everything below describes
   .dfx-h1 through .dfx-h5.

   WEIGHT STILL COMES FROM THE LEVEL. These classes carry size only. Both weight tokens
   are 600 as of 2026-08-14, so a borrowed size is indistinguishable from the real level
   — which is how it was before the 500 split, and it is why the mechanism is easy to
   forget: the rule only shows itself when the two tokens differ.

   TRACKING NOW COMES FROM THE TOKEN AT EVERY HEADING LEVEL, escalated alongside weight,
   so -0.01em is live on h1–h5 — verified against the rendered page rather than assumed
   from the cascade, which is how it was found that the token had been live on none of
   them. h6 is excluded because it tracks +0.14em as a label, not negatively as a
   heading. See the NOTE at the top of this file.

   H1 IS FLUID, the one thing a fixed step cannot express. The clamp used to sit on
   .dfx-hero__title, so the product headline scaled on mobile and no other h1 did.
   As a property of the level it applies everywhere and the component declares
   nothing. 34px floor, 48px ceiling.

   ITS FLOOR HAS TO STAY ABOVE H2, and for a while it did not: a 36px floor against
   a fixed 40px h2 meant every viewport under ~782px rendered an h1 SMALLER than the
   h2 beneath it. 34 against 32 keeps 2px of headroom. Change h2 and this floor moves
   with it — the note on the tokens says the same thing from the other end, and adds why
   34 is as low as it can go.

   No !important anywhere. The `body` prefix matches the parent theme's own generic
   heading selectors and our later load order carries the override; the classes are
   a single class each, which outranks `body h4` on specificity. */
/* ----------------------------------------------------------------------------
   THE PAIRING TABLE — the answer to "how does this stay consistent".
   ----------------------------------------------------------------------------
   Consistency does not come from the classes existing. It comes from every
   context having ONE agreed pairing, written down here, so nobody decides case by
   case. A new component looks this up rather than choosing.

     Context                        Level   Size class    Renders
     ---------------------------------------------------------------------
     Page / product headline        h1      (none)        34→48px fluid
     Major page section             h2      .dfx-h4       20px @ 600
     Section inside a section       h3      .dfx-h5       18px @ 600
     Card / list item title         h3      .dfx-h5       18px @ 600
     Grid item name                 h4      .dfx-h5       18px @ 600
     Product name over a headline   p       (h4 styling)  20px — not a heading
     Eyebrow / overline             p       .dfx-eyebrow  14px caps, muted
     Small label beside a rule      h2/h3   .dfx-h6       16px caps @ 600, muted
     Chrome label (not in outline)  —       (see below)   14px caps, muted

   ALL THREE LABEL ROWS ARE MUTED, and Brass is opt-in via .dfx-accent on dark
   surfaces only. The eyebrow row used to say Brass; see the .dfx-accent rule for
   why a reserved color cannot be a default.

   Levels come from where the thing sits in the page, sizes from this table. If a
   design needs a size this table does not offer, the table changes — once, here —
   not the component.

   THE WEIGHT COLUMN STAYS THOUGH EVERY ROW NOW READS 600. It was added when
   --dfx-weight-heading-light was 500 and the three .dfx-h5 rows split 600/600/500 —
   same size, different weight, from one table. That collision is gone as of 2026-08-14,
   but the column is what makes it visible if the split is ever turned back on, which is
   a one-line edit in brand-tokens.css. Weight follows the LEVEL, not the class.

   THERE ARE TWO LABEL SIZES AND THE SPLIT IS STRUCTURAL, not decorative. Both are
   uppercase at 0.14em and weight 600; what separates them is whether the label is part
   of the document:

     IN THE OUTLINE   .dfx-h6 / h6, 16px, muted. Real h2/h3 elements — product group
                      titles, feature-list sub-headings. Structural work, so a heading
                      step is right.
     CHROME           --dfx-size-label, 14px. Not in the outline at all — .dfx-eyebrow,
                      the "Trusted by" separator and the footer widget titles. Furniture,
                      so it has its own token outside the ramp.

   ALL OF THEM ARE MUTED NOW, reading --dfx-label-ink. The eyebrow was the odd one out
   at Brass, which is dealt with in the .dfx-accent rule above: a color the brand
   reserves for restraint cannot be what a utility hands you by default, and it failed
   AA on the base surface besides. Ink is no longer the axis that separates these three
   — nothing does, which is the point. They are the same role in different markup.

   THE CHROME ROW HAS NO SINGLE CLASS, and that is not an oversight. Two of its three
   consumers are markup this theme does not own — WPBakery's separator and WordPress's
   widget h4 — so they are reached by CSS rules mirroring Salient's selectors rather
   than by a class. .dfx-eyebrow is the one that can be applied by hand.

   TWO PLACES THIS CAN STILL DRIFT, both governance rather than code:

     1. WPBakery's "Extra class name" lets an editor put any of these on anything.
        The theme's own elements avoid the problem by exposing a LEVEL selector and
        applying the size class in the template, so editors never hand-pick one.
     2. Builder content on pages we do not template — the homepage currently runs
        h1 → h4 → h6 → h2 because levels were chosen to get small text. Classes fix
        the size half of that; the levels still need correcting by hand.
   ---------------------------------------------------------------------------- */
/* THE UTILITIES NEED THE `.row .col` PREFIX TOO, AND WITHOUT IT THEY LOST INSIDE ONE.
   `.row .col h3` is 0-2-1 and `.dfx-h4` is 0-1-0, so an `h3.dfx-h4` rendered at 24px in
   Salient's row markup and at 20px outside it — the same component two sizes depending on
   how a page happened to be built. It surfaced on the tier ladder, which is 24px on the
   packages page and was 20px on a product page, but every borrowed size in the theme was
   exposed to it.
   `.row .col .dfx-h4` is 0-3-0, which clears the element rule and keeps the invariant the
   block above states: the size class is the authority on size. */
body h1, .dfx-h1, .row .col .dfx-h1 { font-size: var(--dfx-size-h1); }            /* 34 → 48px */
body h2, .dfx-h2, .row .col .dfx-h2 { font-size: var(--dfx-size-h2); }            /* 32px */
body h3, .row .col h3, .dfx-h3, .row .col .dfx-h3 { font-size: var(--dfx-size-h3); } /* 24px */
body h4, .row .col h4, .dfx-h4, .row .col .dfx-h4 { font-size: var(--dfx-size-h4); } /* 20px */
body h5, .row .col h5, .dfx-h5, .row .col .dfx-h5 { font-size: var(--dfx-size-h5); } /* 18px */
body h6, .row .col h6, .dfx-h6, .row .col .dfx-h6 { font-size: var(--dfx-size-h6); } /* 16px — the label step */

/* THE PAGE-HEADER TITLE, ESCALATED — the one h1 on the site the six lines above
   could not reach, and the reason a plain page rendered a 64px headline against what
   was then a 48px ceiling.

   Salient's page header is not builder content; it is `.row.page-header-no-bg >
   .container > .col.span_12.section-title > h1`, and its generated file addresses
   that element far more specifically than it addresses anything else:

     body .row .col.section-title h1                         0-3-3   4rem
     html body .row .col.section-title.span_12 h1            0-4-3   2.8rem  (≤1000px)
     .span_12 h1                                             0-1-1   2.2rem  (≤690px)
     #page-header-bg .span_6 h1                              1-1-2   all three

   `body h1` at 0-0-2 loses to every one of them, so the size scale was live on
   every h1 on the site EXCEPT the page title — which is the h1 a visitor to a
   content page actually sees. Same story as the h4 weight and the heading
   line-height above: Salient ships one value several ways, and reaching it means
   mirroring its selectors rather than out-declaring it once.

   THE ID SELECTORS ARE HERE FOR THE OTHER HALF OF THE TEMPLATE. With a page-header
   background set, the title moves into `#page-header-bg .span_6`, which no
   class-only selector can beat. Both branches are covered so the title does not
   change size the day an editor adds a background image.

   THE MEDIA-QUERY RULES ARE THE POINT, NOT AN AFTERTHOUGHT. --dfx-size-h1 is a
   clamp, so it needs no breakpoints of its own — but it has to beat Salient's, and
   a media query adds no specificity. `html body .row .col.section-title.span_12 h1`
   ties Salient's 0-4-3 exactly and wins on load order, the same tie this file
   already relies on elsewhere. The 1-1-3 selector strictly outranks its 1-1-2
   sibling. No !important anywhere. */
html body .row .col.section-title h1,
html body .row .col.section-title.span_12 h1,
html body #page-header-bg h1,
html body #page-header-bg .span_6 h1 {
	font-size: var(--dfx-size-h1);   /* 34 → 48px, from the scale above */
}

/* THE PAGE TITLE'S MEASURE — the same 36ch the readability section applies to every other
   heading, reaching the one heading that section cannot see.

   `.container-wrap h1, h2, h3 { max-width: 36ch }` is written to be global and is not,
   because of where Salient puts the page header. page.php calls
   `nectar_page_header( $post->ID )` on line 15 and opens `<div class="container-wrap">` on
   line 19 — so the title is a SIBLING of that wrapper, in
   `#page-header-wrap > #page-header-bg > .container > .row > .col`, and a descendant
   selector scoped to `.container-wrap` misses it by construction. Verified in the browser
   rather than reasoned about: the title computed `max-width: none` and ran 58 characters
   across 1265px on one line.

   THE SUBTITLE UNDER IT WAS ALREADY MEASURED, at 66ch in the block below, which is what
   makes this an omission rather than a policy. The deck was capped and the headline above
   it was not, so the two disagreed about the width of the header they share.

   36ch AND NOT A NEW NUMBER. The point is that the cap is one global rule with one value;
   adding a second figure for the page title would be the per-page adjustment this is meant
   to remove. It resolves to ~1116px at 48px rather than ~630px at body size, because `ch`
   is the width of the font's own zero and scales with the type — which is the property that
   makes one value work at every size.

   SELECTORS MIRRORED FROM THE FONT-SIZE RULE ABOVE, not because Salient declares a
   max-width here — it declares none — but because these four are the addresses this h1 can
   have, and the pair should not drift apart. The comment above documents why each one is
   needed.

   CENTERED HEADERS NEED THE AUTO MARGIN, exactly as the subtitle does below: a capped block
   in a centered column sits left of center unless told otherwise. Salient carries the
   alignment on a `data-alignment` attribute, which is the same hook the subtitle's centering
   rule uses. */
html body .row .col.section-title h1,
html body .row .col.section-title.span_12 h1,
html body #page-header-bg h1,
html body #page-header-bg .span_6 h1 {
	max-width: 36ch;
}

html body [data-alignment="center"] .col.section-title h1,
html body [data-alignment="center"] #page-header-bg h1,
html body #page-header-bg[data-alignment="center"] h1 {
	margin-inline: auto;
}

/* THE PAGE-HEADER SUBTITLE, ONE RULE FOR BOTH OF SALIENT'S BRANCHES.
   With a header background image the parent theme prints the subtitle as a SIBLING with
   a class — `<h1>Title</h1> <span class="subheader">Sub</span>` — and without one it
   nests it in the h1 as a bare span. inc/salient-page-header.php rewrites the second into
   the first on the way out of the buffer, so `.subheader` is the selector that matters
   and both branches land here.

   THE BARE-SPAN SELECTOR STAYS AS A FALLBACK. If Salient changes that markup the rewrite
   misses and the subtitle renders nested again; this keeps it looking right while
   somebody fixes the pattern. It is not dead code, it is the failure mode.

   `> span`, DIRECT CHILD, for the fallback: an editor can put a span inside the title
   itself for a highlight, and a descendant selector would flatten that into a deck.

   COLOR IS MIXED FROM currentColor RATHER THAN NAMED, because this markup has two homes.
   On a plain page the text is dark; inside #page-header-bg Salient sets
   `color: #fff !important` for the image branch. A named muted token would be right on
   the first and unreadable on the second, and could not beat that !important anyway.
   Mixing against the inherited color steps back from whatever it inherits. */
html body .row .col.section-title .subheader,
html body .row .col.section-title h1 > span,
html body #page-header-bg .subheader,
html body #page-header-bg .span_6 .subheader {
	display: block;
	margin-top: 0.75rem;
	/* Salient gives .subheader a 15px bottom margin, which was spacing it off a heading
	   it used to sit under rather than beside. The gap belongs above it now. */
	margin-bottom: 0;
	/* PADDING ZERO, AND THIS IS THE ALIGNMENT FIX. Salient ships
	   `.row .col.section-title span { padding-left: 10px }` at 0-3-1 — a word gap for a
	   subtitle that sat INLINE after the title on the same line. Once it is a block of its
	   own that 10px becomes an indent, so the subtitle started 10px right of the headline
	   and the two no longer shared a left edge. Both selectors above outrank that rule on
	   class count, so zeroing it here is enough. */
	padding: 0;
	font-family: var(--dfx-font-body);
	/* 20px, matching .dfx-lead. Salient sets 28px on the image branch, which made the
	   same field two different sizes depending on whether a page had a background —
	   the inconsistency this section exists to remove. */
	font-size: 1.25rem;
	font-weight: var(--dfx-weight-body);
	line-height: 1.6;
	/* A readable measure, as .dfx-lead uses. A two-sentence subtitle set to the h1's full
	   width runs to one very long line on a wide screen. */
	max-width: 66ch;
	color: color-mix( in srgb, currentColor 78%, transparent );
}

/* A CENTERD PAGE HEADER HAS TO CENTER THE MEASURE BOX, NOT JUST THE TEXT INSIDE IT.
   This is the bug the 66ch cap above creates and does not finish.

   Set the page header alignment to Center and Salient centers the text, correctly. But
   the cap makes the subtitle a 66ch block inside a wider container, and a block with
   zero horizontal margins hugs the left edge — so the text centers within a box that
   is itself left-aligned. Measured on /company: an 824px subtitle in a 1095px header,
   0px of slack on the left and 271px on the right. It reads as "centerd, but sitting
   left", which is exactly what it is.

   The h1 needs no equivalent because nothing caps it; only the subtitle and the
   `h1 > span` variant carry a measure.

   KEYED ON SALIENT'S OWN data-alignment, which it writes on `.row.page-header-no-bg`
   and on `#page-header-bg`. Using its attribute rather than a class of ours means left
   and right alignment are untouched — auto margins there would center a box the editor
   asked to be left, which is the same class of bug in the other direction.

   The one descendant selector covers both header branches, background or not, rather
   than mirroring the four-selector list above for a property that only has to win
   against `0`. */
html body [data-alignment="center"] .subheader,
html body [data-alignment="center"] .col.section-title h1 > span {
	margin-inline: auto;
}

/* [data-inherit-heading-family] — SALIENT'S "MATCH AN hN" CONTRACT, WIRED UP.
   It was INERT, not out-specified: no rule in any of the 27 loaded stylesheets matched
   it. Salient authors these selectors in css/fonts.php behind
   `if ( $nectar_options['hN_font_family']['attrs_in_use'] )`, and this theme leaves the
   Theme Options font fields empty, so the block is never printed. Grep `attrs_in_use`
   before assuming the attribute is the only mechanism lost that way.
   0-1-0 beats `body h1` (0-0-2), and ties what fonts.php would print if those fields
   are ever filled — this file loads later, so it stays correct either way.
   All five properties, not just family: both consumer elements push `font-*: inherit`
   onto their children, and family alone renders Poppins 600 at body size. The
   builder's own size control still wins (0-2-3 on a descendant `*`), so a block that
   already has a size keeps it — verified on /company. Images size off the inner
   element's line box, not this wrapper, so they do not scale.
   ONE OVERSHOOT: `letter-spacing: inherit` passes the computed LENGTH, so a block
   rendering below its level's size gets tracking scaled to the level. /company's
   headline is -1.44px (48px × -0.03em) at 37.62px = -0.038em, past the -0.035em
   ceiling the tokens set. Inside Poppins' -0.068em collision floor, so accepted. */
[data-inherit-heading-family="h1"] {
	font-family: var(--dfx-font-heading);
	font-weight: var(--dfx-weight-heading);
	font-size: var(--dfx-size-h1);
	line-height: var(--dfx-line-height-h1);
	letter-spacing: var(--dfx-letter-spacing-h1);
}

[data-inherit-heading-family="h2"] {
	font-family: var(--dfx-font-heading);
	font-weight: var(--dfx-weight-heading);
	font-size: var(--dfx-size-h2);
	line-height: var(--dfx-line-height-h2);
	letter-spacing: var(--dfx-letter-spacing-heading);
}

[data-inherit-heading-family="h3"] {
	font-family: var(--dfx-font-heading);
	font-weight: var(--dfx-weight-heading);
	font-size: var(--dfx-size-h3);
	line-height: var(--dfx-line-height-heading);
	letter-spacing: var(--dfx-letter-spacing-heading);
}

/* h4/h5 read the light weight token, which points at 600 — kept as the token rather
   than the level's literal so they follow if the split is ever turned back on. */
[data-inherit-heading-family="h4"] {
	font-family: var(--dfx-font-heading);
	font-weight: var(--dfx-weight-heading-light);
	font-size: var(--dfx-size-h4);
	line-height: var(--dfx-line-height-heading-small);
	letter-spacing: var(--dfx-letter-spacing-heading-small);
}

[data-inherit-heading-family="h5"] {
	font-family: var(--dfx-font-heading);
	font-weight: var(--dfx-weight-heading-light);
	font-size: var(--dfx-size-h5);
	line-height: var(--dfx-line-height-heading-small);
	letter-spacing: var(--dfx-letter-spacing-heading-small);
}

/* The label step, mirroring the h6 block above — 600 not 500, and --dfx-label-ink
   rather than a flat Pewter, which would be unreadable on a dark band. */
[data-inherit-heading-family="h6"] {
	font-family: var(--dfx-font-heading);
	font-weight: var(--dfx-weight-emphasis);
	font-size: var(--dfx-size-h6);
	line-height: var(--dfx-line-height-heading-small);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--dfx-label-ink);
}

/* NO RULE UNDER THE PAGE TITLE. Salient draws
   `.row .col.section-title { border-bottom: 1px solid rgba(0, 0, 0, .15) }` at 0-3-0,
   which put a hairline across the full container width under every page header — a
   divider the brand system does not use anywhere else, and one that reads as the top of
   the content rather than the bottom of the header.

   `html body` ties it at 0-3-2 and wins on load order, the same tie the title size above
   relies on.

   THE PADDING STAYS BUT SHRINKS. Salient sets `padding: 0 0 36px 0` in the same rule as
   the border, and with the border gone that padding is part of what separates the header
   from the first band — remove it entirely and the subtitle sits directly on top of the
   ladder. 24px is what makes the gap 64px: the first `.container-wrap` contributes 40px
   of its own padding-top, which is a global band value and is not touched here.

   64 BELOW AGAINST 80 ABOVE IS DELIBERATELY NOT SYMMETRIC. Space reads as belonging to
   whichever element it is nearer, and a header should own more of its space above than
   below — that is what groups the title with the page rather than with the band under it.
   Even so this is the one number in the pair that is a judgment call: 16px here (56px
   total) is the tighter alternative and nothing else needs to change with it.

   ALL WIDTHS, unlike the 80px above. That one is desktop-only because 80px of a phone
   viewport is a quarter of the screen; this is a reduction, so it is welcome at every
   size, and scoping it to desktop would leave mobile with less room above the title than
   below it. */
html body .row .col.section-title {
	border-bottom: none;
	padding-bottom: 1.5rem;   /* 24px; 40px of container-wrap padding follows it */
}

/* MORE AIR ABOVE THE PAGE TITLE, DESKTOP ONLY.
   Salient ships `.page-header-no-bg { padding-top: 35px; padding-bottom: 0 }` in
   style.css, and the material skin restates the same 35px at
   `body.material .page-header-no-bg` (0-2-1) — so a bare `.page-header-no-bg` rule
   here would lose to the skin. `html body.material` ties the skin's specificity and
   wins on load order, the same tie the title size and the border removal above rely
   on. Both selectors are listed because the skin is a Theme Option and a future
   change to Ascend or another skin would drop the `.material` class off the body.

   WHY 80, AND WHY IT IS NOT LOPSIDED. The header's own padding-bottom is 0; what
   separates it from the first band is 36px on `.col.section-title` (see above) plus
   40px of `container-wrap` padding — 76px. At Salient's 35px the title sat almost
   against the nav with more than twice that below it. 80px reads as a band with a
   title in it rather than a title that happens to be at the top of the page.

   DESKTOP ONLY because 80px of an ~700px-tall phone viewport is a quarter of the
   screen spent on nothing, and the h1 drops from 48px to 2.8rem below 1000px anyway
   (see the type section) — a smaller title needs less room above it. 1000px is
   Salient's own breakpoint, used here rather than a brand value so this changes at
   the same width the theme's layout does. */
@media (min-width: 1000px) {

	html body .page-header-no-bg,
	html body.material .page-header-no-bg {
		padding-top: 80px;
	}

	/* UNLESS A BREADCRUMB IS ALREADY DOING THAT JOB, which the rule above predates.
	   Its whole justification is the sentence "at Salient's 35px the title sat almost
	   against the nav" — 80px buys separation from the NAVIGATION. Once
	   dfx_page_breadcrumbs() prints a trail above the page header, that separation is
	   already there (40px above the trail plus its own 20px below), and the 80px stops
	   separating the title from the nav and starts separating it from the breadcrumb.
	   Stacked, they read as a hole: measured at 100px+ between trail and title.

	   1.5em RATHER THAN 0, WHICH WAS THE FIRST CUT. Zero made the trail's own
	   margin-bottom the only gap, on the reasoning that it would match a page where an
	   element supplies the h1. It matches too well: the page header is a `.row` and the
	   title sits at the very top of it, so at zero the h1 butts against the breadcrumb
	   with no acknowledgement that a band started. 1.5em adds a title's worth of air
	   without returning to a hole — and being em, it tracks the header's own font size,
	   so it stays proportionate at the 1000px breakpoint where the h1 steps down.

	   `body:has()` RATHER THAN AN ADJACENT SIBLING, because `.page-header-no-bg` is a
	   `.row` whose position relative to the trail depends on the template, and `:has()`
	   does not care how deeply it sits. Specificity ties the `.material` rule above at
	   0-2-2 — `:has()` contributes only its argument's weight — so this wins on source
	   order, which is why it lives here rather than in components.css. */
	html body:has( .dfx-page-breadcrumbs ) .page-header-no-bg,
	html body.material:has( .dfx-page-breadcrumbs ) .page-header-no-bg {
		padding-top: 1.5em;
	}
}

/* --- Non-heading text utility classes -------------------------------------- */
/* Reusable text roles for prose (not headings). Apply via WPBakery's
   "Extra class name" field on a Text element, or as an HTML class. */

/* Lead / subtitle — large intro copy. Color inherits so it stays readable on
   both dark and light sections. */
.dfx-lead {
	font-family: var(--dfx-font-body);
	font-size: 1.25rem;   /* 20px */
	line-height: 1.6;
	font-weight: var(--dfx-weight-body);
	max-width: 66ch;      /* readable measure (see readability section below) */
}

/* Eyebrow / overline — small uppercase label above a heading. Brass accent;
   override the color on light backgrounds (low contrast there). */
/* SIZE COMES FROM --dfx-size-label, shared with the "Trusted by" separator and the
   footer widget titles in components.css. It was a hardcoded 0.8125rem, which meant
   the three chrome labels on the site were three independent numbers — and when the
   h6 step moved to 16px the other two followed it while this one did not. Reading
   the token is what makes them one decision. */
.dfx-eyebrow {
	font-family: var(--dfx-font-body);
	font-size: var(--dfx-size-label);   /* 13px */
	font-weight: var(--dfx-weight-emphasis);
	line-height: 1.2;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	/* THE GAP BELONGS TO THE UTILITY, and its absence was the whole reason eyebrows sat
	   at different distances from their headings. Every caller was inventing a number,
	   so the inconsistency was structural rather than anyone's mistake.

	   NOW A TOKEN, --dfx-eyebrow-gap, declared below with the rule that makes it stick.
	   Writing the number here twice — once in this rule and once in the override that
	   has to beat WPBakery — is how it would drift again.

	   1.25rem, up from 0.75rem. That 0.75rem was itself chosen as a near no-op against
	   the 0.625rem the Custom Heading element emits, which was a migration argument
	   rather than a typographic one, and it kept coming back as "eyebrows need more
	   room". 20px under a 13px label reads as attached to the heading below it without
	   being cramped against it. */
	margin: 0 0 var(--dfx-eyebrow-gap);
	/* PADDING ZEROED BECAUSE THE HOST ELEMENT'S DEFAULT LEAKS IN OTHERWISE. Salient
	   gives every <p> a padding-bottom of 1.5em, which is 19.5px at this size — so the
	   same class on a <p> stood 19.5px taller than on the <h6> the Custom Heading
	   element emits, with an identical margin. A utility has to neutralise whatever it
	   is applied to; leaving one box property to the tag is what made this look like a
	   margin problem twice. */
	padding: 0;
	/* MUTED BY DEFAULT, BRASS BY REQUEST. This declared var(--dfx-brass) outright,
	   which made a 2.29:1 contrast failure the default state of a label utility and
	   handed out a color the brand says to use at "maximum restraint" to anyone who
	   did not choose. Reading --dfx-label-ink puts it on the same ink as the other two
	   chrome labels, and gets the dark-band re-pointing for free — which matters here
	   more than anywhere, because eyebrows mostly live in hero sections. Add
	   .dfx-accent alongside for Brass; see that rule for why it is dark-surface only. */
	color: var(--dfx-label-ink);
}

/* WHY THE CLASS ALONE WAS NOT ENOUGH, AND WHY THIS KEPT COMING BACK.
   The eyebrows in page content already carry .dfx-eyebrow — measured on staging,
   the Custom Heading element renders `vc_custom_heading vc_do_custom_heading
   dfx-eyebrow` — and still sat at 10px. WPBakery's own `.vc_custom_heading`
   margin is a single class, same specificity as .dfx-eyebrow, and its stylesheet
   is enqueued after this file, so source order handed it the win. Adding the
   class looked like it did nothing, so the next fix went back to being a
   per-section number in the builder. That is the loop this rule closes.

   The h6 half is deliberately broader than the class: it catches builder eyebrows
   nobody remembered to label, and it sets only the gap, so a Custom Heading that
   is not an eyebrow inherits nothing it shouldn't. One property, one token — if
   the gap needs to move again, --dfx-eyebrow-gap is the only edit. */
:root {
	--dfx-eyebrow-gap: 1.25rem;
}

.wpb_wrapper .dfx-eyebrow,
.wpb_wrapper h6.vc_custom_heading {
	margin-bottom: var(--dfx-eyebrow-gap);
}

/* Caption — small muted meta / footnote text (tuned for dark surfaces). */
.dfx-caption {
	font-family: var(--dfx-font-body);
	/* 12px → 13px, TO STOP THE UTILITY AND THE TOKEN DISAGREEING ABOUT WHAT "CAPTION"
	   MEANS. The body-side scale added --dfx-size-caption at 13px; leaving this class
	   hardcoded at 12px would have shipped a `.dfx-caption` that was not caption size,
	   which is the kind of near-miss that costs an afternoon later. The pixel is worth
	   less than the two names agreeing. 12px still exists as --dfx-size-micro, which is
	   what this was really using. */
	font-size: var(--dfx-size-caption);
	line-height: var(--dfx-line-height-ui);
	font-weight: var(--dfx-weight-body);
	color: var(--dfx-steel-gray);
}

/* --- Readability: measure (line length) — automatic, theme-wide ------------ */
/* Caps every content paragraph to ~66 characters for readability. `ch` scales
   with font size, so the reading rhythm stays consistent. Applied globally by
   the theme — no per-element classes required.

   LIST ITEMS TAKE THE SAME MEASURE, because a bullet is a line of prose and reads
   like one. This used to name `p` alone, which was invisible until a page put real
   body copy in a list: the privacy policy's accordion runs 14 lists, and its
   paragraphs sat at 66ch while the bullets beside them ran the full 1067px column.
   The mismatch was the tell — two kinds of prose, the same voice, different measures,
   stacked directly on top of each other.

   NO EXEMPTION LIST HERE, unlike the headline measure below, and that is a finding
   rather than an omission. Every ul and ol inside .container-wrap on this site today
   is author-written prose in a `.wpb_text_column` — 14 on /privacy, 1 on /iso-27001,
   none anywhere else. Navigation, footer and mega-menu lists are outside
   .container-wrap entirely, and the component lists (workflow grid, feature list,
   product cards) are divs and grids rather than lists. There is nothing to exempt, so
   nothing is exempted; a speculative list of Salient list elements that this site does
   not use would only be read and puzzled over later.

   IF A COMPONENT LIST EVER LANDS INSIDE .container-wrap and comes out clipped —
   Salient's Fancy Unordered List, a pricing table's feature list, a tab nav — this is
   the rule doing it, and it takes the `.dfx-full` hatch below or its own max-width.
   That is the same trade the paragraph cap has always made.

   `li` AND NOT `ul`/`ol`, so the measure lands on the text of each item the way it
   does on a paragraph, leaving the markers where the list put them. Nested lists need
   no special handling: the parent item's 66ch box already contains them, so a
   sub-bullet is narrower than its parent rather than measured afresh. */
.container-wrap p,
.container-wrap li { max-width: 66ch; }

/* Keep centered columns centered: center the capped block (not just its text).
   `.col.center` is the parent theme's class for center-aligned columns. Covers a bare
   paragraph and the capped text wrappers (.dfx-lead / .dfx-measure), which is
   where the class often lands when applied via WPBakery's Extra class name. */
.container-wrap .col.center p,
.container-wrap .col.center .dfx-lead,
.container-wrap .col.center .dfx-measure { margin-inline: auto; }

/* The same fix for the OTHER way a column gets centered. `.col.center` above is set by the
   row's alignment; Salient's per-column Text Align control writes
   `force-desktop-text-align-center` instead, which the rule above cannot see — so a centered
   column built that way left the capped block hugging the left. Measured on the Pam band:
   a 583px paragraph in an 867px column, 0px slack left, 284px right. */
.container-wrap .force-desktop-text-align-center p,
.container-wrap .force-desktop-text-align-center .dfx-lead,
.container-wrap .force-desktop-text-align-center .dfx-measure,
.container-wrap .force-desktop-text-align-center h1,
.container-wrap .force-desktop-text-align-center h2,
.container-wrap .force-desktop-text-align-center h3 { margin-inline: auto; }

/* Escape hatch: add the `.dfx-full` class to a block that must stay full width.
   Covers the class on the block itself or on a wrapper, for paragraphs and list items
   alike — on a list it is the useful place, since it releases every item at once. */
.container-wrap .dfx-full p,
.container-wrap .dfx-full li,
.container-wrap p.dfx-full,
.container-wrap li.dfx-full { max-width: none; }

/* Headline measure — cap large headings so they wrap rather than span full
   width (~36 characters; tighter than body because headings are large).
   Centered columns center the capped headline via the parent theme's `.col.center`.

   NOT THE WHOLE OF THE RULE, and the missing half is the page title. Salient prints the
   page header BEFORE `.container-wrap` opens — see page.php — so the site's most-read h1 is
   a sibling of this scope rather than a descendant, and this selector cannot reach it. The
   same 36ch is applied to it up in the page-header block, on that element's own mirrored
   selectors. Two rules, one value: if this figure changes, change it there too. */
.container-wrap h1,
.container-wrap h2,
.container-wrap h3 { max-width: 36ch; }
.container-wrap .col.center h1,
.container-wrap .col.center h2,
.container-wrap .col.center h3 { margin-inline: auto; }

/* THE MEASURE IS FOR AUTHOR-WRITTEN PROSE, NOT FOR COMPONENT HEADINGS, and this is
   where that distinction gets made rather than in each component's own file.

   The cap above is deliberately blanket, so authors get the reading measure without
   remembering a class. But it lands on every heading a component renders too, and a
   component heading is a labeled part of a UI rather than a line of prose. It gets
   clipped in ways that have nothing to do with reading: a section title that draws a
   full-width hairline had the rule cropped to the width of its words, and a Salient
   toggle title wrapped at 36ch and took its click target with it.

   BOTH USED TO BE FIXED DOWNSTREAM, which is the actual problem being solved here.
   components.css carried an eight-selector `max-width: none` block for the section
   titles, and the accordion would have needed a ninth. That list could only ever grow,
   because the cap keeps applying to things it was never written for and each new
   component discovers the same clipping from scratch. Exempting by convention here ends
   it: a dfx component named to the `__title` pattern is covered the day it is written
   and nothing has to be added below.

   COMPONENTS THAT WANT A MEASURE DECLARE THEIR OWN, which is the existing habit rather
   than a new rule — .dfx-hero--measure-constrained sets 36ch, .dfx-display sets 24ch.
   An explicit number beside the component beats one inherited from three files away.

   [class*="__title"] is the theme's BEM component-title convention and matches nothing
   else in this codebase, including the `--sub` variants whose class still contains
   `__title`. The Salient classes are its own component headings: toggle and accordion
   titles, blog and archive entry titles, the author box. Prose headings written in
   builder text carry no class, so they keep the cap.

   SCOPED TO h1–h3 rather than left as a bare class list, because `.title` in
   particular is broad in Salient and the cap it lifts only ever applied to headings.
   A `<p class="title">` keeps its own 66ch.

   NO display CHANGE HERE, on purpose. Salient's Inline Small toggle style makes the
   title an inline-block so its underline hugs the words, which is that style's design
   and not a bug to correct. Lifting the cap alone stops the early wrap; forcing
   display: block would stretch the rule to the full column and quietly restyle every
   toggle on the site.

   0-2-1, one step above the cap's 0-1-1, so it wins without !important and stays
   overridable by anything that genuinely needs a narrower measure. */
.container-wrap :is(h1, h2, h3):is(
	[class*="__title"],
	.toggle-title,
	.entry-title,
	.title,
	.nectar-author-info-title
) { max-width: none; }

/* Display heading — opt-in large headline. Apply via WPBakery "Extra class
   name" (and clear any inline font-size on the element). Responsive size,
   with balanced wrapping so it needs no manual line breaks. Overrides the
   default heading measure with a tighter one suited to large type. */
/* ESCALATED TO `html body …` AND !important ON THE TWO NEW PROPERTIES, and this was
   found by measuring rather than by reading — the homepage display headline rendered
   1.2/-0.02em while this rule said 1.05/-0.035em.

   WHAT BEATS 0-2-0. Two things, and neither is Salient being unusual:
     `.row .col h2`                      0-2-1   this file's own leading + tracking bands
     `.wpb_wrapper h2.vc_custom_heading` 0-2-2 ! Salient's generated line-height
   The band rules inherit `.row .col h*` from the selector list the tracking and leading
   blocks have always used, so the display class was outranked by the very rules it is
   supposed to sit above. `html body .container-wrap .dfx-display` is 0-2-3, which clears
   the first; !important is required for the second because Salient declares it and
   nothing short of !important reaches an !important.

   WHY THIS WAS INVISIBLE UNTIL NOW — and it is the same shape as the h2 tracking
   coincidence recorded in the tracking block. This rule used to declare a hardcoded
   line-height: 1.25, which was exactly the value the h1–h3 band held, and no tracking at
   all, which meant the flat -0.01em it inherited was also right. Two coincidences, so a
   rule that had been losing the cascade for its whole life looked correct. The moment
   display got values of its own, the loss became visible. Any property added here in
   future is losing too until it is escalated — do not add one to the 0-2-0 form.

   font-size RIDES THE ESCALATION but did not need it: the size scale's h2 rule is
   `body h2, .dfx-h2` with no `.row .col` variant, so 0-2-0 was already enough and 64px
   always rendered. It stays in this rule because splitting one property out to a weaker
   selector is how the next coincidence gets built. */
html body .container-wrap .dfx-display {
	font-size: var(--dfx-size-display);   /* 52px → 64px, one step above h1 */
	/* WAS A HARDCODED 1.25 — the single largest type on the site running the same leading
	   as a 24px h3, which is what "no display CHANGE" above was quietly also true of.
	   Now the display band: 1.05, which is 0.037em clear of Poppins' 1.013 collision
	   floor. See the token for the measurement and for why 1.07 is the fallback if a
	   two-line headline ever looks tight. */
	line-height: var(--dfx-line-height-display) !important;
	/* TRACKING WAS NEVER SET HERE AT ALL, so display inherited -0.01em from the
	   all-levels rule — the loosest type on the site at the size that needs it tightest.
	   -0.035em is the measured ceiling for Poppins, consuming 51% of the 0.068em an 'oo'
	   pair has to give. This is the property that does most of the work in this pass. */
	letter-spacing: var(--dfx-letter-spacing-display) !important;
	/* No font-weight: this inherits its element's, and the homepage puts .dfx-display on
	   an h2 — so if h1 ever goes heavier than h2, the 64px display renders lighter than
	   the 48px h1. Fix then is one line here reading --dfx-weight-display. */
	max-width: 24ch;
	text-wrap: balance;
}

/* Optional opt-in utility for non-paragraph text elements (rarely needed now). */
.dfx-measure { max-width: 66ch; }
.dfx-measure-center { max-width: 66ch; margin-inline: auto; }

/* H1–H3 family and weight, pinned with !important against the parent theme's
   generated per-heading typography CSS.

   The comment that used to sit here described Nexa at Heavy 800 for H1 and Extra
   Bold 700 for H2/H3, with H4–H6 in Inter. None of that has been true since the
   move to Poppins: --dfx-font-display now resolves to --dfx-font-heading (Poppins)
   for every heading level, and every weight token is 600 as of 2026-08-14 — so the
   weight split the old Nexa comment described is gone entirely, in one family at one
   weight. Corrected rather than deleted, because the stale version had a reader
   looking for a Nexa file that is not loaded.

   POPPINS IS NOT A BRAND FACE, and this is the place a reader will notice. The brand
   guidelines name Inter for all digital and product work and Nexa for display only;
   Poppins is in neither list. It is recorded here rather than fixed because changing
   --dfx-font-heading is a one-line edit with a site-wide result and belongs in its own
   decision, not inside a size and weight change. */
h1, h2, h3,
.wpb_wrapper h1,
.wpb_wrapper h2,
.wpb_wrapper h3 {
	font-family: var(--dfx-font-display) !important;
}

h1,
.wpb_wrapper h1 {
	font-weight: var(--dfx-weight-display) !important;
}

h2, h3,
.wpb_wrapper h2,
.wpb_wrapper h3 {
	font-weight: var(--dfx-weight-display-2) !important;
}

/* --- Links ------------------------------------------------------------------ */
a {
	color: var(--dfx-color-link);
}

a:hover,
a:focus {
	color: var(--dfx-color-link-hover);
}

/* --- CTA color by context ---------------------------------------------------- */
/* ONE PAIR OF TOKENS, TWO SURFACES. The primary button is Obsidian on a light page and
   Brass on a dark one, and this rule is the whole of the switch — no component names a
   color, and no button needs a modifier class.

   IT WORKS BY RE-POINTING, NOT BY OVERRIDING. Custom properties inherit, so a dark
   context re-points --dfx-color-cta for everything beneath it and every button rule below
   picks that up without knowing the context exists. The same mechanism --dfx-heading-ink
   uses, for the same reason: the alternative is a `.light .nectar-button` twin of every
   button rule in this file, and they drift.

   WHY BRASS STILL EXISTS HERE. An Obsidian button on an Obsidian band is invisible; Brass
   on Obsidian is 6.78:1 and is the one place the brand's premium accent is doing load-
   bearing work rather than decoration. Light surfaces get 15.5:1 white-on-Obsidian.

   THE MARKER LIST, AND IT IS LONGER THAN THE HEADING RULE'S BY ONE THAT MATTERS. `.light`
   is Salient's dark-band class (it describes the TEXT, not the background), `.dfx-band` is
   this theme's own, `.dfx-card--product` is the dark card — which is how the tier table's
   dark tier cards keep their Brass buttons for free — and `#page-header-bg` covers a page
   header with a background image, where Salient forces the text white.

   `.dfx-product-band` IS THE ONE THE HEADING RULE DOES NOT LIST, and leaving it out is how
   the first version of this shipped an invisible button: every product hero renders inside
   it, and the CTA came out Obsidian on Obsidian. The heading rule gets away without it
   because the band sets `color` on itself and headings inherit; a BUTTON does not inherit
   its fill from anything, so a dark context that is invisible to this list is a button
   that disappears. Caught by rendering a product page rather than by reading the CSS.

   THE LESSON FOR THE NEXT DARK SURFACE: adding one means adding it here. A component that
   paints itself dark and forgets this rule gets a black button on a black band, and it
   will look like a missing button rather than a missing selector.

   `[style*="color"]` IS DELIBERATELY EXCLUDED, unlike in the heading rule. There it
   over-matches on purpose because a false positive means "inherit", which is safe. Here a
   false positive means a Brass button on a light builder column — not a contrast failure,
   but a visible inconsistency on any page whose columns carry inline color, which is most
   of the builder pages. Explicit dark markers only. */
.light,
.dfx-band,
.dfx-product-band,
.dfx-card--product,
#page-header-bg {
	--dfx-color-cta:       var(--dfx-color-cta-on-dark);
	--dfx-color-cta-hover: var(--dfx-color-cta-on-dark-hover);
	--dfx-color-cta-text:  var(--dfx-color-cta-on-dark-text);

	/* FORM VALIDATION RIDES THE SAME LIST rather than declaring a second one, which
	   is the point of putting it here instead of next to the rules it feeds. The
	   marker list is the site's definition of "dark surface"; two copies of it drift,
	   and a drifted copy means red-500 error text at 2.77:1 on an Obsidian band —
	   a contrast failure that only appears once a visitor submits an empty form,
	   which is the last state anyone reviews. /book-a-demo is exactly that case: the
	   form sits in a full-width Obsidian row. See the field validation block below
	   and the token comments in brand-tokens.css for the measured ramp swap. */
	--dfx-field-border-error: var(--dfx-field-border-error-on-dark);
	--dfx-field-fill-error:   var(--dfx-field-fill-error-on-dark);
	--dfx-field-helper-error: var(--dfx-field-helper-error-on-dark);
	--dfx-field-icon-error:   var(--dfx-field-icon-error-on-dark);

	--dfx-alert-error-fill:   var(--dfx-alert-error-fill-on-dark);
	--dfx-alert-error-accent: var(--dfx-alert-error-accent-on-dark);
	--dfx-alert-error-title:  var(--dfx-alert-error-title-on-dark);

	/* AND THE WHOLE CONTROL TIER RIDES IT TOO, for the same reason and now covering
	   the resting state rather than only the error one. Every value is justified
	   against its SUI dark-mode token in brand-tokens.css; this is only the swap.

	   THE RESTING STATE IS THE POINT. Before this, the error tokens above were the
	   only thing that knew a dark band existed — so a field on /book-a-demo turned
	   red correctly when you submitted it empty, and at rest had no border at all,
	   because Salient's `.span_12.light input[type="email"]` (0-3-1) was setting the
	   fill and border and the theme's 0-2-1 field rule could not reach it. Both
	   halves are fixed here: the tokens flip on the marker, and the field rules
	   below carry the class count to actually land. */
	--dfx-control-border:          var(--dfx-control-border-on-dark);
	--dfx-control-border-hover:    var(--dfx-control-border-hover-on-dark);
	--dfx-control-border-disabled: var(--dfx-control-border-disabled-on-dark);
	--dfx-control-border-focus:    var(--dfx-control-border-focus-on-dark);
	--dfx-control-focus-ring:      var(--dfx-control-focus-ring-on-dark);

	--dfx-control-selected:        var(--dfx-control-selected-on-dark);
	--dfx-control-selected-hover:  var(--dfx-control-selected-hover-on-dark);
	--dfx-control-on-selected:     var(--dfx-control-on-selected-on-dark);

	--dfx-control-disabled-fill:   var(--dfx-control-disabled-fill-on-dark);
	--dfx-control-disabled-text:   var(--dfx-control-disabled-text-on-dark);

	--dfx-field-fill:              var(--dfx-field-fill-on-dark);
	--dfx-field-text:              var(--dfx-field-text-on-dark);
	--dfx-field-placeholder:       var(--dfx-field-placeholder-on-dark);

	/* THESE TWO ARE ALIASES OF THE CONTROL TIER ABOVE AND THEY STILL HAVE TO BE
	   RESTATED, WHICH IS THE OPPOSITE OF WHAT IT LOOKS LIKE. The first version of
	   this block left them out on the reasoning that re-pointing --dfx-control-border
	   would carry --dfx-field-border with it. It does not, and the reason is a rule
	   about custom properties worth knowing before writing another token alias:

	   A `var()` IS SUBSTITUTED WHERE THE DECLARATION LIVES, NOT WHERE IT IS USED.
	   `--dfx-field-border: var(--dfx-control-border)` is declared on `:root`, so it
	   resolves once, against `:root`, to the light value — and what `.light` then
	   inherits is that resolved colour, not the indirection. Re-pointing the control
	   tier here cannot reach backwards into a declaration made higher up the tree.

	   MEASURED, NOT REASONED ABOUT, AND THE TWO DISAGREED. On a dark band the field
	   reported `--dfx-control-border: rgba(255,255,255,0.25)` and
	   `--dfx-field-border: rgba(0,0,0,0.25)` at the same time, on the same element —
	   the swap had worked and the alias had not moved with it. The fill and the text
	   flipped correctly all along because they are declared with literal values, so
	   nothing about the render hinted that the border was on a different mechanism.
	   Same lesson as the OneTrust block and the validation pseudo-elements: the
	   inspection agreed with the intent and the pixels did not.

	   So an alias that needs a dark variant gets restated. An alias that has no dark
	   variant — --dfx-field-radius, --dfx-field-pad-x — does not need to appear here
	   at all, because there is nothing to swap. */
	--dfx-field-border:            var(--dfx-control-border-on-dark);
	--dfx-field-border-hover:      var(--dfx-control-border-hover-on-dark);

	/* The tick's stroke is baked into its data URI, so the alias moves rather than a
	   color — see the note on the icon tokens in brand-tokens.css for why this one
	   glyph cannot be a mask like the error one. */
	--dfx-icon-check:              var(--dfx-icon-check-on-dark);

}

/* --- Primary call-to-action buttons (the parent theme .nectar-button + regular) ------ */
/* Obsidian fill, Signal White label — or Brass on a dark surface, via the context rule
   directly above. Change --dfx-color-cta in brand-tokens.css to move every primary CTA on
   the site; nothing here names a color.

   `data-color-override="false"` is the attribute Salient puts on a button that is
   taking its color from the theme's accent option, so matching on it is what
   claims those buttons for the brand layer instead of the parent theme. Salient's
   accent is still blue in theme options — it is simply never seen, which is worth
   knowing before someone changes it there and wonders why nothing moves. */
.nectar-button.regular-button,
.nectar-button.see-through-yet-solid,
a.nectar-button[data-color-override="false"],
.wpb_button,
input[type="submit"],
button[type="submit"] {
	background-color: var(--dfx-color-cta);
	border-color: var(--dfx-color-cta);
	color: var(--dfx-color-cta-text);
	font-family: var(--dfx-font-body);
	font-weight: var(--dfx-weight-emphasis);
}

/* THE LABEL COLOR IS RESTATED ON HOVER, and the reason survives the color change.
   Salient ships `data-hover-text-color-override="#fff"` on these buttons and swaps the
   color from script, so the span is targeted directly rather than left to inherit. That
   mattered acutely when the button was Brass — white on Brass is 2.4:1, a failure on hover
   only, which is the kind of thing that ships — and it still matters now: on a dark surface
   the button is Brass and the script would put white on it again. */
.nectar-button.regular-button:hover,
a.nectar-button[data-color-override="false"]:hover,
a.nectar-button[data-color-override="false"]:focus,
.wpb_button:hover,
input[type="submit"]:hover,
button[type="submit"]:hover {
	background-color: var(--dfx-color-cta-hover);
	border-color: var(--dfx-color-cta-hover);
	color: var(--dfx-color-cta-text);
}

.nectar-button.regular-button:hover span,
a.nectar-button[data-color-override="false"]:hover span,
a.nectar-button[data-color-override="false"]:focus span {
	color: var(--dfx-color-cta-text);
}

/* --- Button size scale ------------------------------------------------------- */
/* WHAT A BUTTON'S SIZE CLASS MEANS, STATED ONCE. Until this block existed, size was set
   by WHERE a button was rather than by WHICH SIZE IT WAS, and the results disagreed: the
   same class list — `nectar-button small regular dfx-card__cta-button` — rendered 14px on
   a 34px box in the product cards and the comparison table, and 12px on a 48px box in the
   tier ladder, because the first two sit inside components that restate the type and the
   third does not. Meanwhile the hero's `large` button rendered 14px — the same label size
   as a `small` one. A scale that returns three different answers for one input is not a
   scale.

   WHAT SALIENT ACTUALLY SHIPS, since this was mis-audited twice and the notes in
   components.css recorded the wrong version of it. css/build/style.css declares font-size
   and padding on exactly three selectors:

     .nectar-button          12px   13px 18px
     .nectar-button.large    14px   15px 22px
     .nectar-button.jumbo    18px   25px 40px

   There is NO `.small` and no `.medium` rule, in style.css or in the generated
   uploads/salient/salient-dynamic-styles.css — checked in the browser against every
   loaded sheet, not by grepping one file. So `size="small"` is inert markup that lands on
   the 12px base, which is where the ladder's 12px came from. An earlier note here claimed
   the generated file set `.nectar-button.small { font-size: 1rem }`; if it ever did, Theme
   Options no longer emits it, and the components that relied on that claim were reading a
   value they were setting themselves.

   THE SCALE. Three steps, because Salient's markup offers three and a fourth would need
   a class nothing emits:

     small / unsuffixed   14px   0.5rem 0.875rem     ~36px box
     large                16px   0.8125rem 1.625rem  ~50px box
     jumbo                18px   1.125rem 2.25rem    ~63px box

   The small step is deliberately the value the cards and the comparison table already
   used, so the majority of the site does not move; large keeps the hero's 50px box and
   only corrects its label from 14px to 16px. The type sizes are the body tokens, so a
   button label sits on the same scale as the text around it.

   THE TRANSPARENT BORDER IS THE OTHER HALF OF "CONSISTENT". Salient's buttons are
   border-box with no border, so any variant that adds one — the ladder's outline CTA —
   grows 2px taller than the solid button beside it. That was the visible mismatch in the
   ladder: three 48px outline buttons and one 46px filled one in the same row. Reserving
   the border here means a variant recolors it instead of adding it, and no variant can
   change the box again.

   SPECIFICITY: `html body` puts these at 0-3-1 for the size classes, which clears
   Salient's own 0-1-0 and 0-2-0 rules and also the `body.material .nectar-button.jumbo`
   padding override at 0-3-1 on load order. Not decoration — the material skin restates
   jumbo's padding and a plain `.nectar-button.jumbo` here would lose to it. */
/* Reserved, not drawn. See above. `see-through` is excluded because those variants are
   Salient's own outline buttons and set their own border-width — 2px, 4px on jumbo — at a
   lower specificity than this, so a blanket 1px would quietly flatten them. */
html body .nectar-button:not([class*="see-through"]) {
	border: 1px solid transparent;
}

html body .nectar-button,
html body .nectar-button.small,
html body .nectar-button.medium {
	font-size: var(--dfx-size-body-sm);   /* 14px */
	line-height: 1.3;
}

html body .nectar-button.large {
	font-size: var(--dfx-size-body);      /* 16px */
	line-height: 1.35;
}

html body .nectar-button.jumbo {
	font-size: var(--dfx-size-h5);        /* 18px */
	line-height: 1.4;
}

/* PADDING SKIPS ICON BUTTONS, AND THAT SPLIT IS THE WHOLE REASON IT IS THREE MORE RULES.
   Salient reserves label room for the icon it absolutely-positions inside the button —
   `.nectar-button.has-icon { padding-left: 42px; padding-right: 42px }` at 0-2-0, with
   wider variants for jumbo in the material skin. A blanket `html body .nectar-button`
   padding at 0-3-1 outranks all of them, so an icon button would keep the icon and lose
   the space it sits in, with the label running underneath it.

   Nothing on the site emits `has-icon` today — Hero::cta_button() passes no icon — so this
   costs nothing now and is the difference between the scale being safe and being a trap
   the first time an editor picks an icon in the builder. Type and leading above are
   unconditional because an icon does not care what size the label is. */
html body .nectar-button:not(.has-icon),
html body .nectar-button.small:not(.has-icon),
html body .nectar-button.medium:not(.has-icon) {
	padding: 0.5rem 0.875rem;
}

html body .nectar-button.large:not(.has-icon) {
	padding: 0.8125rem 1.625rem;
}

html body .nectar-button.jumbo:not(.has-icon) {
	padding: 1.125rem 2.25rem;
}

/* --- Accessible focus states (never remove; meaning not by color alone) -----
   SPLIT IN TWO, BECAUSE A CONTROL AND A LINK NO LONGER FOCUS IN THE SAME COLOUR.
   Navigation focuses in Arc, the brand's own accent; controls focus in SUI's
   blue-500, from the control tier. Both are 2px with a 2px gap, so the geometry is
   shared and only the hue differs.

   THE SECOND RULE IS A FLOOR, NOT THE REAL ONE. Every control the site actually
   ships sits inside `.container-wrap` and is caught by the field and selection
   rules further down at 0-2-1 or heavier. This exists so a control rendered outside
   that wrapper — a header search, anything a plugin injects — still focuses like a
   control rather than falling back to the link colour. Keeping the element
   selectors here and pointing them at the control token is what makes the policy
   findable: the previous version listed input/textarea/select alongside the links
   and quietly handed them Arc. */
a:focus-visible,
button:focus-visible,
.nectar-button:focus-visible {
	outline: 2px solid var(--dfx-color-focus);
	outline-offset: 2px;
}

input:focus-visible,
textarea:focus-visible,
select:focus-visible {
	outline: var(--dfx-control-focus-ring-width) solid var(--dfx-control-focus-ring);
	outline-offset: var(--dfx-control-focus-ring-gap);
}

/* --- Reduced motion respect ------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* ============================================================================
   FORM FIELDS — ONE SYSTEM FOR EVERY CONTROL ON THE SITE
   ============================================================================

   Measured on /support before this existed: 48px text inputs with a 2px
   TRANSPARENT border, a 40px select with a square 1px #777771 border, and a 22px
   browser-default submit. Three heights, three border treatments, one column.

   THE INVISIBLE BORDER IS SALIENT'S, and it is the headline fix. Salient sets
   `border: 2px solid transparent` on inputs, so the border was never missing —
   it was being drawn in nothing. On a Signal White panel a white field with no
   border has almost no edge at all.

   SPECIFICITY: Salient's heaviest input rule is `.container-wrap input[type=…]`
   at 0-2-1. The selectors below sit at 0-2-1 (tying, and winning on load order,
   since this file is enqueued at priority 100) or 0-3-0 where a class is
   available. The frame, colour, height and padding all land that way.

   THREE PLACES USE `!important`, EACH AGAINST A DIFFERENT MECHANISM, and each is
   noted where it appears: Salient declares `!important` on the submit's padding
   and font size; select2's height needs it (measured — without it the box came
   out 66px); and intl-tel-input sets the flag's offset as an INLINE STYLE from
   JavaScript, which no selector can outrank.

   That last one is worth reading before debugging anything here. If a declaration
   in this block appears to do nothing AND a stylesheet scan finds no competing
   rule, look for an inline style before adding specificity — that combination cost
   two failed attempts on the flag. Also check whether Forminator's per-form Design
   setting has been turned back on: it styles the same properties from ID-scoped
   rules and will win.

   ---------------------------------------------------------------------------
   NOW ON SUI 2.0's CONTROL SPEC (2026-09-03, Dave). The geometry and states below
   come from SUI's `control/*` and `field/*` tiers rather than from values invented
   here; every one is justified against its token in brand-tokens.css. What moved:

     radius     4px → 8px  (`control/radius` → `sizing/radius/lg`)
     border     Soft Stone → black/25, white/25 on a dark surface
     hover      nothing    → black/33, a state the fields did not have at all
     focus      Arc ring, 1px offset → blue-600 border + blue-500 ring, 2px gap
     disabled   page surface → grey-200 fill, grey-500 label at 2.81:1
     motion     none       → 130ms on color and border

   THE HEIGHTS DID NOT MOVE, AND THAT IS THE INTERESTING PART. 48px and 40px look
   nothing like the 32/36/40 in `product-ui.md`, so they were checked rather than
   corrected: that table is SUI's DEFAULT density, and in the `comfortable` mode the
   export also ships, 48 is `component/xl` and 40 is `component/md`. The site was
   already a comfortable-density consumer. See the token comment for the detail.

   THE DARK-BAND FIELD WAS BROKEN AND THIS IS WHERE IT GETS FIXED. Measured on
   Kinsta's /book-a-demo before the change: `border-color: rgba(255,255,255,0)` and
   `background: rgba(255,255,255,0.07)`, both from Salient's
   `.span_12.light input[type="text"]` at 0-3-1, against which the base rule below
   at 0-2-1 never stood a chance. So a form in a full-width Obsidian row had no
   field boundary at all. The reclaim block at the end of this section takes those
   properties back; read its comment before touching any selector in it.
   -------------------------------------------------------------------------- */

.container-wrap input[type="text"],
.container-wrap input[type="email"],
.container-wrap input[type="tel"],
.container-wrap input[type="url"],
.container-wrap input[type="number"],
.container-wrap input[type="password"],
.container-wrap input[type="search"],
.container-wrap input[type="date"],
.container-wrap .forminator-input,
.container-wrap .forminator-textarea,
.container-wrap textarea,
.container-wrap select {
	box-sizing: border-box;
	width: 100%;
	margin: 0;
	padding: 0 var(--dfx-field-pad-x);
	border: 1px solid var(--dfx-field-border);
	border-radius: var(--dfx-field-radius);
	background-color: var(--dfx-field-fill);
	color: var(--dfx-field-text);
	font-family: inherit;
	font-size: var(--dfx-size-body-sm);
	/* SUI's 120–140ms band. COLOR AND BORDER ONLY, and the exclusions are the
	   design: no transition on height, padding or radius, because a control that
	   changes shape under the cursor reads as moving rather than as responding.
	   `outline` is left out too — a focus ring has to arrive instantly to be useful
	   to someone tabbing through, and animating it makes the field feel laggy at the
	   one moment precision matters. The global prefers-reduced-motion block further
	   up this file already flattens this to 0.01ms. */
	transition: border-color var(--dfx-control-transition),
	            background-color var(--dfx-control-transition),
	            color var(--dfx-control-transition);
}

/* HOVER, WHICH THE FIELDS SIMPLY DID NOT HAVE. SUI gives the field family its own
   `field/border/hover` one step down the border ramp (black/25 → black/33), and
   before this there was no hover state on any control on the site — a field looked
   identical whether or not the cursor was on it. 1.84:1 → 2.30:1 is a small move in
   absolute terms and a clear one in practice, because it is a change rather than a
   value.

   `:not(:focus-visible)` SO HOVER NEVER OVERWRITES FOCUS. Both rules set
   border-color and hover is declared second, so without the guard moving the mouse
   over the field you are typing in would drop the blue focus border back to grey.
   Salient makes the same mistake in the other direction on dark bands — see the
   reclaim block at the end of this section. */
.container-wrap input[type="text"]:hover:not(:focus-visible),
.container-wrap input[type="email"]:hover:not(:focus-visible),
.container-wrap input[type="tel"]:hover:not(:focus-visible),
.container-wrap input[type="url"]:hover:not(:focus-visible),
.container-wrap input[type="number"]:hover:not(:focus-visible),
.container-wrap input[type="password"]:hover:not(:focus-visible),
.container-wrap input[type="search"]:hover:not(:focus-visible),
.container-wrap input[type="date"]:hover:not(:focus-visible),
.container-wrap .forminator-input:hover:not(:focus-visible),
.container-wrap .forminator-textarea:hover:not(:focus-visible),
.container-wrap textarea:hover:not(:focus-visible),
.container-wrap select:hover:not(:focus-visible),
.container-wrap .select2-container:hover .select2-selection {
	border-color: var(--dfx-field-border-hover);
}

/* Height is separated from the box above so a textarea can opt out of it
   without restating the frame. */
.container-wrap input[type="text"],
.container-wrap input[type="email"],
.container-wrap input[type="tel"],
.container-wrap input[type="url"],
.container-wrap input[type="number"],
.container-wrap input[type="password"],
.container-wrap input[type="search"],
.container-wrap input[type="date"],
.container-wrap .forminator-input,
.container-wrap select {
	height: var(--dfx-field-height);
	line-height: 1;
}

/* A textarea is the one field whose height is content, not a token. Two field
   heights is the floor, and vertical padding replaces the centred line-height.

   THE FLOOR IS A FALLBACK, NOT A FIXED VALUE, so Forminator's per-field Default
   Height still works. Forminator emits that setting as an inline custom property
   on the field — `--forminator-textarea-min-height: 480px` — and its own rule
   reads it as a min-height. A flat value here outranked that rule, so the
   setting silently did nothing on every form. */
.container-wrap .forminator-textarea,
.container-wrap textarea {
	min-height: var( --forminator-textarea-min-height, calc( var(--dfx-field-height) * 2 ) );
	padding-block: 0.625rem;
	line-height: var(--dfx-line-height-body);
	resize: vertical;
}

.container-wrap input::placeholder,
.container-wrap textarea::placeholder {
	color: var(--dfx-field-placeholder);
	opacity: 1; /* Firefox dims placeholders by default; the token already is muted. */
}

/* ONE FOCUS TREATMENT FOR EVERY CONTROL. A ring rather than a border-colour
   change alone, so the state survives Windows High Contrast.

   NOW SUI's RING, WHICH IS TWO BLUES AND A GAP RATHER THAN ONE BLUE AND A NUDGE.
   Three things changed and each is a SUI value, not a preference:

     The border goes blue-600 and the ring blue-500 — `control/border/focus` binds
     to action/default and `control/focus-ring` one step lighter. Two values, so the
     border reads as the control changing and the ring as a halo around it. It was
     Arc for both, which is the marketing CTA colour doing a control's job.

     The gap goes 1px → 2px. SUI's focus ring is specified as a separate rectangle
     offset 2px outside the control, and 2px is what makes it read as a halo rather
     than as a thicker border.

     The ring's corner radius is DERIVED, not declared, and that is deliberate.
     `outline` follows the element's own radius, so 8px field + 2px offset gives a
     10px ring that stays parallel all the way round. Dave ruled specifically
     against snapping it to a token (`sui2-decisions.md`, June 17): the 10px
     `control/focus-ring-radius` in the export would stop tracking the control and
     the gap would pinch at the corners. Browsers do the right thing for free here,
     which is why there is nothing to state.

   ARC IS NOT GONE, IT MOVED BACK TO WHERE IT BELONGS. --dfx-color-focus still
   paints link and button focus; only controls take the SUI blue. Worth knowing
   before someone "unifies" the two: a control and a CTA are different things, and
   SUI is the only one of the two systems that has an opinion about controls. */
.container-wrap input:focus-visible,
.container-wrap textarea:focus-visible,
.container-wrap select:focus-visible,
.container-wrap .forminator-input:focus-visible,
.container-wrap .forminator-textarea:focus-visible,
.container-wrap .select2-container--focus .select2-selection {
	border-color: var(--dfx-control-border-focus);
	outline: var(--dfx-control-focus-ring-width) solid var(--dfx-control-focus-ring);
	outline-offset: var(--dfx-control-focus-ring-gap);
}

/* DISABLED NOW READS AS A FIELD, WHICH IT PREVIOUSLY DID NOT. The old fill was
   --dfx-color-surface — Signal White, the same value as the panel the contact form
   sits on — so a disabled field dissolved into its container and the control
   disappeared rather than looking unavailable. SUI's grey-200 fill with a grey-500
   label is 2.81:1, which is its documented disabled target, and the box survives on
   both white and Signal White. The border drops to black/10 so the frame recedes
   with the rest of it instead of staying at full strength around a dead control. */
.container-wrap input:disabled,
.container-wrap textarea:disabled,
.container-wrap select:disabled {
	border-color: var(--dfx-control-border-disabled);
	background-color: var(--dfx-control-disabled-fill);
	color: var(--dfx-control-disabled-text);
	cursor: not-allowed;
}

/* A disabled field's placeholder has to move too, or it stays at the resting
   token's strength and ends up the most legible thing in a control nobody can
   use. */
.container-wrap input:disabled::placeholder,
.container-wrap textarea:disabled::placeholder {
	color: var(--dfx-control-disabled-text);
}

/* --- The select2 replacement ------------------------------------------------
   Forminator swaps every select for a select2 widget and hides the real element
   as screen-reader-only, so `select {}` rules never reach what a visitor sees.
   The visible control is `.select2-selection`, and it has to be pulled onto the
   same frame by hand.

   `!important` ON THE TWO BOX PROPERTIES IS EMPIRICAL, not diagnosed: without it
   the box measured 66px instead of 48px. Something in select2's own styling wins
   and it was not worth tracing further. Everything else here lands normally, so
   the escalation is kept to the two properties that needed it. */
.container-wrap .select2-container .select2-selection--single {
	box-sizing: border-box !important;
	height: var(--dfx-field-height) !important;
	padding: 0;
	border: 1px solid var(--dfx-field-border);
	border-radius: var(--dfx-field-radius);
	background-color: var(--dfx-field-fill);
}

.container-wrap .select2-container .select2-selection--single .select2-selection__rendered {
	padding-left: var(--dfx-field-pad-x);
	color: var(--dfx-field-text);
	font-size: var(--dfx-size-body-sm);
	line-height: calc( var(--dfx-field-height) - 2px );
}

.container-wrap .select2-container .select2-selection--single .select2-selection__arrow {
	height: calc( var(--dfx-field-height) - 2px );
}

/* --- Submitting state -------------------------------------------------------
   FORMINATOR'S LOADER IS UNSTYLED ON THIS SITE, for the same reason its
   horizontal checkbox layout was: every rule for
   `.forminator-response-message.forminator-loading` is scoped to
   `[data-design=default|bold|flat|material]` and these forms run
   `data-design="none"`. So the class lands and nothing matches it — the message
   appears with no indicator, at body colour, easy to miss entirely on a dark
   band while a submission is in flight.

   That matters more now than it did: with AJAX submission on, this is the only
   feedback between pressing the button and the confirmation, and a form that
   looks inert invites a second click and a duplicate lead.

   Colour is inherited and the track is currentColor at low alpha, so it reads on
   a light or dark band without a variant — same reasoning as the code blocks
   below. Motion is disabled under `prefers-reduced-motion`, where the ring stays
   as a static indicator rather than vanishing. */
/* THE SPINNER IS UNGATED, so it exists even where the overlay cannot. */
.container-wrap .forminator-response-message.forminator-loading {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	color: inherit;
}

.container-wrap .forminator-response-message.forminator-loading::before {
	content: "";
	flex: 0 0 auto;
	width: 1.125rem;
	height: 1.125rem;
	border: 2px solid currentColor;
	border-color: color-mix( in srgb, currentColor 25%, transparent );
	border-top-color: currentColor;
	border-radius: 50%;
	animation: dfx-spin 0.7s linear infinite;
}

/* THE OVERLAY RULES ARE GATED BEHIND `:has()`, WHICH IS THE FALLBACK STRATEGY.
   Without `:has()` the form never becomes a positioning context, no scrim is
   drawn, and the block above still gives the message an inline spinner in normal
   flow — degraded, not broken. Nothing here can strand a visitor behind an
   overlay that will not lift.

   The scrim is the FORM's own `::before` rather than a wrapper, so no markup
   changes and nothing to clean up if the plugin's own loader is ever restored. A
   pseudo-element receives pointer events, so it is what actually blocks clicks
   on the fields underneath. */
.container-wrap .forminator-custom-form:has( .forminator-response-message.forminator-loading ) {
	position: relative;
}

.container-wrap .forminator-custom-form:has( .forminator-response-message.forminator-loading )::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 20;

	/* NO COLOUR AT ALL, WHICH IS THE ONLY THING THAT WORKS ON EVERY FORM. These
	   rules are not scoped to one form — the Contact Support form sits on Signal
	   White, and a dark fill there would read as an error state rather than a
	   dimmed form. CSS cannot ask what colour its backdrop is, and
	   `--dfx-band-bg` is no help: `Band_Theme` only emits it inline on product and
	   OEM bands, so on a plain builder row it is unset.

	   `brightness()` needs no colour. It scales whatever is behind it, so it
	   darkens a dark band and greys a light one, with one value for both:

	     Obsidian band       rgb(20,30,44)    -> rgb(14,21,31)
	     with the #0000004d wash              -> rgb(10,15,22)
	     Signal White        rgb(245,246,247) -> rgb(172,172,173)

	   0.7 also happens to land the dark case exactly on rgb(14,21,31) — the
	   composite of the container's own wash, which is the value three earlier
	   passes were trying to reach by hand. Those failed because a fill has to
	   guess the backdrop: translucent Obsidian was a no-op on an Obsidian band,
	   and black at 55% overshot. A filter does not guess.

	   NO FALLBACK FILL, DELIBERATELY. Any fixed colour is wrong on one surface or
	   the other, so where `backdrop-filter` is unsupported the card simply floats
	   over an undimmed form. Clicks are still blocked by this pseudo-element and
	   focus by the `inert` attribute, so the behaviour holds and only the dimming
	   is missing. */
	backdrop-filter: brightness( 0.7 ) blur( 1px );
	-webkit-backdrop-filter: brightness( 0.7 ) blur( 1px );
	border-radius: inherit;
}

/* The message itself becomes the dialog, centred over the scrim. */
.container-wrap .forminator-custom-form:has( .forminator-response-message.forminator-loading ) .forminator-response-message.forminator-loading {
	position: absolute;
	top: 50%;
	left: 50%;
	z-index: 21;
	transform: translate( -50%, -50% );
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin: 0;
	max-width: min( 22rem, 80% );
	padding: 1rem 1.25rem;
	background: var(--dfx-signal-white, #f5f6f7);
	color: var(--dfx-color-heading, #2b2f36);
	border-radius: var(--dfx-alert-radius, 8px);
	box-shadow: 0 8px 28px rgba( 20, 30, 44, 0.28 );
	text-align: left;
}

/* THE LABEL IS A NESTED `<p>`, WHICH IS WHY COLOURING THE CARD WAS NOT ENOUGH.
   Forminator writes the loading state as `html( '<p>' + loader_label + '</p>' )`
   — see `Send:` in build/front/front.multi.min.js — so on a dark band the
   paragraph took the band's own white and the card's colour never reached it,
   giving white text on a light card. Inheritance has to be asked for explicitly
   because the band rule is more specific than it. */
.container-wrap .forminator-custom-form:has( .forminator-response-message.forminator-loading ) .forminator-response-message.forminator-loading p {
	margin: 0;
	color: inherit;
	font-size: var(--dfx-size-body-sm);
	line-height: var(--dfx-line-height-body);
}

/* Motion is handled by the global prefers-reduced-motion reset near the top of
   this file, which already caps every animation — no rule needed here. */
@keyframes dfx-spin {
	to { transform: rotate( 360deg ); }
}

/* The button says so too, since that is where the cursor already is. */
.container-wrap .forminator-button-submit[disabled],
.container-wrap .forminator-button-submit.forminator-button-onload {
	opacity: 0.65;
	cursor: progress;
}

/* --- Preformatted and inline code -------------------------------------------
   COLOUR IS INHERITED, NOT SET, AND THAT IS THE FIX. These turn up inside form
   confirmation messages, which sit on light and dark bands alike — a band sets
   its own text colour, so anything that inherits is already right on both and
   any fixed value is wrong on one. The reported symptom was a confirmation on a
   dark band with near-invisible text, which is what breaking that inheritance
   looks like.

   The fill is translucent for the same reason: it darkens or lightens whichever
   surface it lands on instead of asserting a colour of its own, so there is no
   light-band and dark-band variant to keep in step. Border likewise, via
   currentColor at low alpha.

   Worth asking whether these belong in a confirmation at all — showing someone
   their own name and email in monospace reads like debug output. This makes it
   legible; it does not make it a good idea. */
.container-wrap pre,
.container-wrap code {
	color: inherit;
	background: rgba( 128, 128, 128, 0.14 );
	border: 1px solid currentColor;
	border-color: color-mix( in srgb, currentColor 22%, transparent );
	border-radius: 4px; /* Literal: there is no small-radius token, and 8px is the control tier. */
}

.container-wrap pre {
	padding: 1rem;
	overflow: auto;
	white-space: pre-wrap; /* An email in a narrow band should wrap, not scroll. */
	word-break: break-word;
}

.container-wrap code {
	padding: 0.125em 0.375em;
}

/* --- Checkboxes and radios --------------------------------------------------
   Sized off the type scale rather than the field height: these sit inline with a
   label, so they follow the text, not the input frame.

   ============================================================================
   THE OLD RULE HERE WAS DEAD CODE, IN TWO SEPARATE WAYS, AND BOTH WERE MEASURED
   ON KINSTA'S /book-a-demo RATHER THAN REASONED ABOUT.

   FIRST: IT WAS OUTRANKED. `salient-dynamic-styles.css` draws its own checkbox
   from

     label:has(input[type="checkbox"]) input[type="checkbox"]              0-2-3

   and the rule this replaces sat at 0-2-1. `:has()` takes the specificity of its
   most specific argument, so the `input[type="checkbox"]` inside it donates a class
   AND a type — which is what makes an innocuous-looking selector heavier than a
   `.container-wrap` one. Every checkbox on the site is inside a `<label>`
   (Forminator wraps them, and so does the integrations filter), so Salient won
   everywhere. Measured: 18.9px box, `border-radius: 0`, currentColor border.

   SECOND: `accent-color` CANNOT WORK ON A CONTROL THAT IS `appearance: none`.
   Salient sets `appearance: none` in that same rule, which switches the control off
   the platform painter — and `accent-color` only instructs the platform painter. So
   the declaration resolved (`accent-color: rgb(46, 159, 212)`, confirmed in the
   computed styles) and painted nothing. A property that reads correctly in devtools
   and has no effect is the worst kind of dead code, which is why this note is long.

   NET RESULT BEFORE THIS CHANGE — five treatments where the brief asks for one:
   Salient's square box on every label-wrapped checkbox; the platform's own control
   on every radio, because Salient styles checkboxes only; a dead
   `.forminator-checkbox-box` span beside each Forminator checkbox, because the
   plugin's Design setting is off and its stylesheet never loads; the theme's
   unreachable 18px rule; and Arc in an `accent-color` nobody sees.

   THE NATIVE INPUT IS STYLED DIRECTLY, NOT HIDDEN BEHIND A SPAN, and that is a
   decision worth defending because SUI's own base layer does the opposite (it
   visually hides the input and paints a `.sui-choice__box` sibling). Three reasons
   the sibling approach is wrong *here*:

     Forminator's markup is asymmetric. A checkbox gets
     `<span class="forminator-checkbox-box">`; a radio gets no equivalent — checked
     in radio.php, there is no `-box` span at all. So there is no element both
     controls share to paint, and a system that needs different markup per control
     is the inconsistency this work exists to remove.

     The focus ring lands on the real control. With a hidden input the ring has to
     be forwarded to the sibling with `:focus-visible + .box`, which works right up
     until markup order changes.

     Windows High Contrast still gets a real control. A hidden input painted by a
     span disappears in forced-colours mode; a styled native input does not.

   SO: `appearance: none` on the input, everything drawn with border, background and
   a background-image glyph. No pseudo-elements — see the icon token comment in
   brand-tokens.css for why Salient's `input::before` tick is invisible in Firefox.
   -------------------------------------------------------------------------- */

/* The box. TWO SELECTOR SHAPES IN ONE RULE, and they are doing different jobs:
   the bare `.container-wrap` pair is 0-2-1 and covers a checkbox that is not inside
   a label (nothing on the site today, but it costs a line); the `label:has()` pair
   is 0-3-3 and is what actually beats Salient's 0-2-3 on the controls that exist.
   Each selector in a group carries its own weight, so one declaration block serves
   both without the weak selectors dragging the strong ones down. */
.container-wrap input[type="checkbox"],
.container-wrap input[type="radio"],
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"],
.container-wrap label:has(input[type="radio"]) input[type="radio"] {
	box-sizing: border-box;
	appearance: none;
	flex: 0 0 auto;              /* the label is a flex row; never let the box squash */
	width: var(--dfx-control-box-size);
	height: var(--dfx-control-box-size);
	/* The gap to the label is the label's `gap`, not a margin here — one value
	   instead of two that can disagree. See the label rule below. */
	margin: 0;
	/* Optical, not structural: 18px box on a 20px line box, nudged so it sits on the
	   first line of a label that wraps rather than on the block's centre. */
	margin-top: 0.0625rem;
	border: var(--dfx-control-border-width) solid var(--dfx-control-border);
	background-color: var(--dfx-field-fill);
	background-repeat: no-repeat;
	background-position: center;
	background-size: contain;
	cursor: pointer;
	transition: border-color var(--dfx-control-transition),
	            background-color var(--dfx-control-transition);
}

/* The one place the two controls disagree, and SUI is explicit about both:
   `sizing/radius/md` (4) for the checkbox box, `sizing/radius/full` for the radio.
   A checkbox is a mark and a radio is a point. */
.container-wrap input[type="checkbox"],
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"] {
	border-radius: var(--dfx-control-radius-box);
}

.container-wrap input[type="radio"],
.container-wrap label:has(input[type="radio"]) input[type="radio"] {
	border-radius: 999px;
}

/* SALIENT'S TICK IS REMOVED, NOT RESTYLED. It is a rotated box built from two
   borders on `input::before` — which no browser has to support, because an input is
   a replaced element, and Firefox does not. `content: none` takes the pseudo-element
   out rather than trying to hide a thing that is already inconsistent across
   engines; at 0-3-4 it clears Salient's 0-2-4 base rule, and its `:checked::before`
   at 0-3-4 only sets `transform`, so there is nothing left for it to transform. */
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"]::before {
	content: none;
}

/* Checked. Fill goes to SUI's action blue and the glyph is a background image, so
   the same declaration serves both controls with only the image differing.

   THE TICK IS AN SVG AND THE DOT IS A GRADIENT, which is not inconsistency for its
   own sake. A tick is a shape that has to be drawn; a dot is a filled circle, which
   `radial-gradient` produces natively — and because the gradient reads the colour
   token directly, the radio needs no second data URI for the dark surface. The tick
   cannot do that (a data URI cannot read a custom property), which is exactly why
   --dfx-icon-check is an alias with two definitions. */
.container-wrap input[type="checkbox"]:checked,
.container-wrap input[type="radio"]:checked,
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"]:checked,
.container-wrap label:has(input[type="radio"]) input[type="radio"]:checked {
	border-color: var(--dfx-control-selected);
	background-color: var(--dfx-control-selected);
}

.container-wrap input[type="checkbox"]:checked,
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"]:checked {
	background-image: var(--dfx-icon-check);
}

/* 3px radius on a 16px content box — a 6px dot, SUI's proportion. Two positions in
   one colour stop rather than a second stop, so the edge is hard instead of
   antialiased into a halo. */
.container-wrap input[type="radio"]:checked,
.container-wrap label:has(input[type="radio"]) input[type="radio"]:checked {
	background-image: radial-gradient(
		circle at center,
		var(--dfx-control-on-selected) 0 0.1875rem,
		transparent 0.1875rem
	);
}

/* Hover. Unchecked follows the field family down the border ramp; checked darkens
   one step within the action ramp, per SUI's rule that a state is a step and never
   a tint or an opacity change. */
.container-wrap input[type="checkbox"]:hover:not(:checked):not(:disabled),
.container-wrap input[type="radio"]:hover:not(:checked):not(:disabled),
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"]:hover:not(:checked):not(:disabled),
.container-wrap label:has(input[type="radio"]) input[type="radio"]:hover:not(:checked):not(:disabled) {
	border-color: var(--dfx-control-border-hover);
}

.container-wrap input[type="checkbox"]:hover:checked:not(:disabled),
.container-wrap input[type="radio"]:hover:checked:not(:disabled),
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"]:hover:checked:not(:disabled),
.container-wrap label:has(input[type="radio"]) input[type="radio"]:hover:checked:not(:disabled) {
	border-color: var(--dfx-control-selected-hover);
	background-color: var(--dfx-control-selected-hover);
}

/* Focus — the same 2px ring with a 2px gap the fields use, from the same tokens.
   THE RADIUS DERIVES CORRECTLY FOR BOTH SHAPES WITHOUT BEING STATED, which is the
   payoff for leaving it to `outline`: the checkbox gets a 6px ring around its 4px
   box and the radio gets a round one, both staying parallel to their own edge. A
   tokenized ring radius would have needed a per-shape override. */
.container-wrap input[type="checkbox"]:focus-visible,
.container-wrap input[type="radio"]:focus-visible,
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"]:focus-visible,
.container-wrap label:has(input[type="radio"]) input[type="radio"]:focus-visible {
	outline: var(--dfx-control-focus-ring-width) solid var(--dfx-control-focus-ring);
	outline-offset: var(--dfx-control-focus-ring-gap);
}

/* Disabled. 0-4-3, because Salient's `label:has(…) input:disabled` is 0-3-3 and
   sets `opacity: 0.5` — which would dim a control that is already handled by real
   tokens, and dim its focus ring with it. Opacity is reset rather than left to
   compound: SUI treats disabled as its own colour pair, not as a faded version of
   the resting one. */
.container-wrap input[type="checkbox"]:disabled,
.container-wrap input[type="radio"]:disabled,
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"]:disabled,
.container-wrap label:has(input[type="radio"]) input[type="radio"]:disabled {
	border-color: var(--dfx-control-border-disabled);
	background-color: var(--dfx-control-disabled-fill);
	opacity: 1;
	cursor: not-allowed;
}

/* A DISABLED CONTROL THAT IS ALSO CHECKED HAS TO STILL LOOK CHECKED, or a form
   with a locked pre-set option reads as an empty one. So the state survives and only
   the colour drops out of the action ramp: the fill becomes grey-500 — the disabled
   TEXT token, used here as a fill — and the glyph stays white on it at 3.87:1, past
   the 3:1 threshold a non-text indicator needs.

   grey-500 RATHER THAN THE grey-200 DISABLED FILL, which is the choice worth
   recording. A tick has to sit on something darker than itself; grey-200 with a
   white tick is 1.37:1 and the mark simply vanishes, so the control would read as
   unchecked-and-disabled — the wrong state, stated confidently. Using the darker of
   the two disabled tokens keeps "checked" legible while "not available" is still
   carried by the colour being grey at all, plus the cursor and the label. */
.container-wrap input[type="checkbox"]:disabled:checked,
.container-wrap input[type="radio"]:disabled:checked,
.container-wrap label:has(input[type="checkbox"]) input[type="checkbox"]:disabled:checked,
.container-wrap label:has(input[type="radio"]) input[type="radio"]:disabled:checked {
	border-color: var(--dfx-control-disabled-text);
	background-color: var(--dfx-control-disabled-text);
}

/* The label. ONE LAYOUT FOR BOTH CONTROLS, which it was not before: Salient's
   `body[data-responsive] label:has(input[type="checkbox"])` flexes CHECKBOX labels
   only, so a radio label beside a checkbox label was a different layout for no
   reason anybody chose. 0-2-4 against Salient's 0-2-3 — `html body` buys the type
   count, the same trick and the same justification as the dark-band block below.

   `flex-start`, NOT `center`, AND THIS WAS ALREADY KNOWN. The integrations filter
   discovered it and fixed it locally: centring floats the box halfway down a label
   that wraps to two lines instead of sitting it on the first one. Promoted here so
   every checkbox on the site gets the right answer instead of the one component
   that hit the problem. Its local override is now redundant and removed.

   `gap`, NOT A MARGIN ON THE BOX. 0.5rem is SUI's control gap and Salient's 0.5em
   agreed with it; owning it on the label means the spacing cannot disagree with
   itself when a box rule and a label rule are edited months apart. */
html body .container-wrap label:has(input[type="checkbox"]),
html body .container-wrap label:has(input[type="radio"]) {
	display: flex;
	align-items: flex-start;
	gap: 0.5rem;
	cursor: pointer;
}

/* THE LABEL TEXT WAS OBSIDIAN ON AN OBSIDIAN BAND, WHICH IS TO SAY IT WAS NOT
   THERE. Found by rendering the controls on a dark row, not by reading anything:
   the `<label>` inherited Signal White correctly and the `<span>` inside it
   computed to `rgb(20, 30, 44)`. Traced to one grouped rule in
   `salient-dynamic-styles.css`:

     .nectar-color-accent-color, body a, label span, … { color: <accent> }   0-0-2

   `label span` — every span inside every label on the site, painted with Salient's
   Theme Options accent colour, which on this install is Obsidian. Forminator wraps
   its option text in `<span class="forminator-checkbox-label">`, so it matched.

   IT WAS ALSO WRONG ON LIGHT SURFACES, WHICH IS THE PART THAT WOULD HAVE SURVIVED
   A REVIEW. Obsidian on Signal White is perfectly legible, so nobody would flag it
   — but Obsidian is the heading ink and every other piece of form copy on the page
   uses Slate Graphite. So checkbox labels have been one step darker than the
   description text beside them, everywhere, since the forms were built.

   `inherit`, NOT A TOKEN, AND THAT IS THE WHOLE FIX. The label already resolves to
   the right ink on both surfaces — body colour on light, Signal White inside a
   `.light` row — so the span only has to stop opting out. Naming a token here would
   mean a second dark-surface swap to maintain; inheriting means the marker list
   keeps working for free. Same reasoning as the CTA context rule near the top of
   this file: re-point, do not override.

   MATCHED STRUCTURALLY RATHER THAN BY CLASS NAME, and the first attempt at this
   rule got it wrong in a way worth keeping on the record. It listed Forminator's
   class names — `.forminator-checkbox-label` and friends — which fixed the forms
   and missed the integrations filter, whose labels are a bare `<span>` with no
   class at all (`wpbakery-integrations-grid.php`). Salient's rule is `label span`;
   anything narrower than "a span inside a control label" leaves some of it behind.

   So the first selector below is the real fix, at 0-2-3 against Salient's 0-0-2,
   and it covers every current and future checkbox label regardless of markup. The
   Forminator class names stay for the ONE case the structural selector cannot
   reach: the consent field puts its description in a `<div>` that is a SIBLING of
   the label, not a child (consent.php), so no `label span` selector sees it. */
.container-wrap label:has(input[type="checkbox"]) span,
.container-wrap label:has(input[type="radio"]) span,
.container-wrap .forminator-checkbox__label,
.container-wrap .forminator-radio__label {
	color: inherit;
}

/* FORMINATOR'S BOX SPAN IS DEAD MARKUP AND IS HIDDEN EXPLICITLY. The plugin emits
   `<span class="forminator-checkbox-box" aria-hidden="true">` for its own drawn
   control, but the per-form Design setting is off, so forminator-forms.css never
   enqueues and the span arrives with no styling — an empty inline element with no
   dimensions. Harmless today and a second visible box the moment anyone turns that
   setting on. `display: none` rather than leaving it to luck; it is already
   aria-hidden, so nothing is lost. Radio has no equivalent span, which is the
   asymmetry that decided the approach above. */
.container-wrap .forminator-checkbox-box {
	display: none;
}

/* FORMINATOR'S HORIZONTAL LAYOUT IS INERT ON THIS SITE, so it is implemented
   here. Setting a checkbox or radio field to Horizontal adds
   `.forminator-checkbox-inline` (multivalue.php:241), but every rule that gives
   that class a layout is scoped to `[data-design=default|bold|flat|material]`
   and our forms run `data-design="none"` — so the class lands and nothing reads
   it. Same shape as an inert data attribute: not out-specified, just unmatched.

   A GRID RATHER THAN `inline-flex`, which is what Forminator would have done.
   Thirteen workflow labels wrapping as inline items give ragged rows that are
   hard to scan; equal columns stay aligned.

   A CONTAINER QUERY, NOT A VIEWPORT ONE, BECAUSE THE CONTAINER IS WHAT DECIDES.
   This started as `@media (min-width: 768px)`, which was wrong in a band nobody
   had tested: the form sits in a half-width builder column, so between roughly
   768 and 1000px of viewport the column is still narrow while the media query has
   already switched to two tracks — two ~180px columns, and "Repair Order
   Management" wraps. Querying the field's own width cannot disagree with itself
   that way.

   `auto-fit` was tried first and was also wrong: a floor wide enough to keep the
   labels on one line needs ~472px for its second track, and the form renders
   around 445px, so it silently collapsed to a single column at exactly the width
   that mattered. Hence an explicit count at a measured threshold.

   `minmax(0, …)` so a long label wraps instead of widening its track. */
.container-wrap .forminator-field-checkbox,
.container-wrap .forminator-field-radio {
	container-type: inline-size;
}

.container-wrap .forminator-field-checkbox .forminator-field:has(.forminator-checkbox-inline),
.container-wrap .forminator-field-radio .forminator-field:has(.forminator-radio-inline) {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	column-gap: 1.5rem;
	row-gap: 0.25rem;
}

/* 26rem ~= 416px: two 196px tracks plus the gap, which fits the longest label in
   this set. Below it one column is correct rather than cramped. */
@container (min-width: 26rem) {

	.container-wrap .forminator-field-checkbox .forminator-field:has(.forminator-checkbox-inline),
	.container-wrap .forminator-field-radio .forminator-field:has(.forminator-radio-inline) {
		grid-template-columns: repeat( 2, minmax(0, 1fr) );
	}
}

/* The option itself keeps its own flex so the box and label stay centred; the
   inline margins Forminator would have added are the grid's gaps instead. */
.container-wrap .forminator-checkbox-inline,
.container-wrap .forminator-radio-inline {
	margin-right: 0;
	margin-bottom: 0;
}

/* --- Submit buttons --------------------------------------------------------
   FORMINATOR'S SUBMIT CARRIES NO `type` ATTRIBUTE, which is the whole reason it
   rendered as a 22px browser-default button: every rule in this theme and in
   Salient targets `button[type="submit"]`, and it matches none of them. Styled by
   class instead. `appearance: none` so the platform stops painting its own chrome
   over the fill. */
.container-wrap .forminator-button-submit,
.container-wrap input[type="submit"] {
	box-sizing: border-box;
	appearance: none;
	width: auto;
	height: var(--dfx-field-height);
	padding: 0 1.5rem;
	border: 0;
	border-radius: 999px;
	background-color: var(--dfx-color-cta);
	color: var(--dfx-color-cta-text);
	font-family: inherit;
	font-size: var(--dfx-size-body-sm);
	font-weight: 600;
	line-height: 1;
	cursor: pointer;
}

.container-wrap .forminator-button-submit:hover,
.container-wrap .forminator-button-submit:focus-visible,
.container-wrap input[type="submit"]:hover,
.container-wrap input[type="submit"]:focus-visible {
	background-color: var(--dfx-color-cta-hover);
	color: var(--dfx-color-cta-text);
}

/* --- Forminator's admin-only edit link -------------------------------------
   Forminator prints "Edit form" under the submit for users who can edit the
   form. It is capability-gated, so it never reached a dealer — but it is chrome
   inside the page rather than in the admin bar, and it makes the front end read
   as unfinished when the team reviews it. Hidden rather than filtered out so
   nothing depends on a plugin hook that could be renamed. */
.forminator-edit-module {
	display: none;
}

/* Salient sets `padding: 16px 23px !important` and a font size on
   `.container-wrap input[type="submit"]` from rules keyed on its own body data
   attributes, heaviest 0-3-2. So the block above lands everything except those
   two, leaving a 16px submit beside two 14px ones — the exact inconsistency this
   system exists to remove. Escalated to 0-3-3 to clear it.

   Both skin branches are listed because `.material` is a Theme Option, and Theme
   Options are database values that differ between dealerfx.test and Kinsta.
   `.forminator-button-submit` needs none of this: it carries no `type`
   attribute, so none of Salient's `[type="submit"]` rules ever matched it. */
html body .container-wrap input[type="submit"],
html body.material .container-wrap input[type="submit"] {
	padding: 0 1.5rem !important;
	font-size: var(--dfx-size-body-sm) !important;
}

/* --- Icons that live inside a field ----------------------------------------
   Two of these, and both were sitting hard against the border because the
   padding above only moves TEXT.

   A NATIVE SELECT'S CHEVRON CANNOT BE MOVED. The browser draws it in the
   padding box at the edge, so no amount of padding-right shifts it. Replaced
   with a drawn one, which is the same treatment the forms filter already used —
   promoting it here means a native select and a select2 finally agree, and the
   chevron's right edge lands on --dfx-field-pad-x like the text does. */
.container-wrap select,
.container-wrap .forminator-select--field {
	appearance: none;
	padding-right: calc( var(--dfx-field-icon-slot) + var(--dfx-field-pad-x) );
	background-image:
		linear-gradient( 45deg, transparent 50%, currentColor 50% ),
		linear-gradient( 135deg, currentColor 50%, transparent 50% );
	background-position:
		right calc( var(--dfx-field-pad-x) + 5px ) center,
		right var(--dfx-field-pad-x) center;
	background-size: 5px 5px;
	background-repeat: no-repeat;
}

/* THE PHONE FIELD WAS A REAL DEFECT, NOT JUST CROWDING. intl-tel-input puts a
   32px country container at `right: 0` and reserves room for it with its own
   padding-right — which the blanket field padding above overwrote with 12px, so
   a typed number would have run underneath the flag. The slot is given back
   here, and the flag moved in to sit on the same edge as the text. */
.container-wrap .forminator-iti-input .forminator-input {
	padding-right: calc( var(--dfx-field-icon-slot) + var(--dfx-field-pad-x) * 2 );
}

/* INTL-TEL-INPUT SETS `right: 0` AS AN INLINE STYLE FROM JAVASCRIPT, which is the
   only reason this needs `!important`. An inline style outranks every selector, so
   no amount of specificity reaches it — two earlier attempts here failed for that
   reason, and a stylesheet scan finds nothing because there is no rule to find.

   `!important` on a declaration competing with a non-important inline style is the
   documented way to win, not an escalation against another author's stylesheet. */
.forminator-iti-input.iti .iti__country-container {
	right: 6px !important;
}

/* ============================================================================
   RECLAIMING THE DARK-BAND FIELD FROM SALIENT
   ============================================================================

   THE FIELD ON AN OBSIDIAN BAND HAD NO BORDER, AND THIS BLOCK IS THE WHOLE FIX.
   Measured on Kinsta's /book-a-demo, on the live form, before the change:

     border-color  rgba(255, 255, 255, 0)   ← drawn in nothing
     background    rgba(255, 255, 255, 0.07)
     border-radius 4px

   All three from Salient's `.span_12.light input[type="text"]` at 0-3-1, against
   which the base field rule at the top of this section — 0-2-1, no class to spare —
   could never land. So the site's one dark-band form has been rendering a fill with
   no edge, and nothing in the theme could see it: the rule was correct, applied, and
   outranked. Same shape as the error state's four-class selector, and the same
   lesson as the OneTrust block — the stylesheet said one thing and the pixels
   another, and the pixels were right.

   WHY `.span_12.light` AND NOT THE MARKER LIST. The marker list re-points the
   tokens (see the top of this file); this block is only about specificity, so it
   names the exact selector it has to beat. `.dfx-band` and `.dfx-product-band` are
   deliberately absent: Salient's rules are keyed on `.span_12.light`, so a field
   inside a theme-owned dark band has no competition and the base rule already
   reaches it. Adding them here would be dead weight that reads as necessary.

   `html body` IS BUYING TYPE COUNT, AND IT IS BUYING IT FROM A SPECIFIC RULE.
   Salient's hover rule is the heaviest thing in this contest:

     [data-form-style="default"] .span_12.light input[type="text"]:hover:not(:focus)
       { border-color: transparent }                                        0-6-1

   `data-form-style="default"` is confirmed set on body on Kinsta, so it matches.
   Three type selectors take the rules below to 0-6-3, which clears 0-6-1 on type
   count without an `!important` — worth the two redundant words, because the
   alternative was a fourth escalation in a file that documents three.

   THE ATTRIBUTE IS SALIENT'S THEME OPTION, AND THAT CUTS BOTH WAYS ON PURPOSE. If
   the Form Style option is ever changed, Salient's transparent-hover rule stops
   matching — and so does the need for the weight here, because the base hover rule
   then wins on its own. The two are gated on the same switch, so this cannot rot
   into a rule that is quietly load-bearing for a reason that no longer exists.
   -------------------------------------------------------------------------- */

/* Resting: fill, border and value colour, all three taken back at 0-4-x. */
.container-wrap .span_12.light input[type="text"],
.container-wrap .span_12.light input[type="email"],
.container-wrap .span_12.light input[type="tel"],
.container-wrap .span_12.light input[type="url"],
.container-wrap .span_12.light input[type="number"],
.container-wrap .span_12.light input[type="password"],
.container-wrap .span_12.light input[type="search"],
.container-wrap .span_12.light input[type="date"],
.container-wrap .span_12.light .forminator-input,
.container-wrap .span_12.light .forminator-textarea,
.container-wrap .span_12.light textarea,
.container-wrap .span_12.light select,
.container-wrap .span_12.light .select2-container .select2-selection--single {
	border: var(--dfx-control-border-width) solid var(--dfx-field-border);
	background-color: var(--dfx-field-fill);
	color: var(--dfx-field-text);
}

/* THE FILL IS OURS NOW, AND IT IS THE BAND'S OWN COLOUR. Salient was painting
   `rgba(255,255,255,0.07)`, which composites over Obsidian to about #23303D — a lift
   nobody chose, arriving as a side effect of a parent theme's skin option. The token
   is now flush Obsidian (Dave, 2026-09-03), so the white/25 border defines the
   control on its own at 2.27:1 and every value sitting on the fill gains contrast.
   See the token comment in brand-tokens.css for the measured set and for why
   obsidian-900 is the wrong-looking-right answer. */

/* Placeholder. Salient sets its own colour AND `opacity: .5` here, so resetting
   the colour alone leaves the value half-transparent and short of the contrast the
   token was picked for. Both are restated. */
.container-wrap .span_12.light input[type="text"]::placeholder,
.container-wrap .span_12.light input[type="email"]::placeholder,
.container-wrap .span_12.light input[type="tel"]::placeholder,
.container-wrap .span_12.light input[type="url"]::placeholder,
.container-wrap .span_12.light input[type="search"]::placeholder,
.container-wrap .span_12.light .forminator-input::placeholder,
.container-wrap .span_12.light .forminator-textarea::placeholder,
.container-wrap .span_12.light textarea::placeholder {
	color: var(--dfx-field-placeholder);
	opacity: 1;
}

/* Hover — the 0-6-3 rule the block comment is about. Salient actively sets
   `transparent` here, so without this the border vanishes on hover on exactly the
   surface where it had just been fixed. */
html body .container-wrap .span_12.light input[type="text"]:hover:not(:focus-visible),
html body .container-wrap .span_12.light input[type="email"]:hover:not(:focus-visible),
html body .container-wrap .span_12.light input[type="tel"]:hover:not(:focus-visible),
html body .container-wrap .span_12.light input[type="url"]:hover:not(:focus-visible),
html body .container-wrap .span_12.light input[type="number"]:hover:not(:focus-visible),
html body .container-wrap .span_12.light input[type="password"]:hover:not(:focus-visible),
html body .container-wrap .span_12.light input[type="search"]:hover:not(:focus-visible),
html body .container-wrap .span_12.light input[type="date"]:hover:not(:focus-visible),
html body .container-wrap .span_12.light .forminator-input:hover:not(:focus-visible),
html body .container-wrap .span_12.light .forminator-textarea:hover:not(:focus-visible),
html body .container-wrap .span_12.light textarea:hover:not(:focus-visible),
html body .container-wrap .span_12.light select:hover:not(:focus-visible) {
	border-color: var(--dfx-field-border-hover);
}

/* Focus. Salient's own focus rule is 0-4-1 and sets `border-color` plus
   `box-shadow: none`; a second, `.ascend`-scoped copy reaches 0-5-1 but `.ascend`
   is NOT on body here — checked on Kinsta rather than assumed, because that class
   is a Salient skin option and skin options are database values. The `html body`
   prefix covers it either way. `box-shadow` is not restated: the ring is an
   `outline`, so Salient zeroing a shadow costs nothing. */
html body .container-wrap .span_12.light input:focus-visible,
html body .container-wrap .span_12.light textarea:focus-visible,
html body .container-wrap .span_12.light select:focus-visible,
html body .container-wrap .span_12.light .forminator-input:focus-visible,
html body .container-wrap .span_12.light .forminator-textarea:focus-visible,
html body .container-wrap .span_12.light .select2-container--focus .select2-selection {
	border-color: var(--dfx-control-border-focus);
	outline: var(--dfx-control-focus-ring-width) solid var(--dfx-control-focus-ring);
	outline-offset: var(--dfx-control-focus-ring-gap);
}

/* Disabled, restated at the same weight for the same reason. On a dark band SUI
   inverts it — the fill goes LIGHT (grey-300) and the label dark (grey-600) — so an
   inactive control on Obsidian goes flat and pale rather than darker, which is the
   only way it reads as unavailable against a dark surround. 3.61:1. */
html body .container-wrap .span_12.light input:disabled,
html body .container-wrap .span_12.light textarea:disabled,
html body .container-wrap .span_12.light select:disabled {
	border-color: var(--dfx-control-border-disabled);
	background-color: var(--dfx-control-disabled-fill);
	color: var(--dfx-control-disabled-text);
}

/* ============================================================================
   FIELD VALIDATION — THE ERROR STATE
   ============================================================================

   Measured on /book-a-demo before this existed: submitting the form empty
   produced SEVEN messages in Signal White at 14px/400 — the same color, size and
   weight as the labels and the field descriptions sitting next to them — plus an
   unstyled bulleted summary list at the top of the form, and NO change of any
   kind to the fields themselves. Forminator marks the wrapper and sets
   aria-invalid correctly; nothing was listening. The state was announced to a
   screen reader and invisible to everyone else.

   IT LOOKED UNSTYLED BECAUSE IT WAS. Forminator's per-form Design setting is off
   (deliberately — see the note in the field block above), and with it off the
   plugin ships a 226-byte generated stylesheet and never enqueues
   forminator-forms.css. So there was no error styling to override, from anywhere.
   Worth knowing before hunting for a competing rule: there is none.

   THE PATTERN IS SUI 2.0'S, NOT INVENTED HERE. Two components do the work, and
   both already exist in the design system:

     Field error   `field/border-error` + `field/background-error` on the control,
                   `field/helper-error` on the message, `field/icon-error` on a
                   leading status glyph. Message type is SUI Subtle (13/18).
     Alert         The form-level summary becomes SUI's subtle Alert — lightest
                   fill, a 6px left accent stroke at `sizing/stroke/accent`, a
                   leading glyph, 8px surface radius, 16px inset.

   Colors are tokens and every ramp step is justified in brand-tokens.css. The
   short version: SUI red-500 on light surfaces, red-300 on dark, flipped by the
   marker list further up this file. Nothing here names a hex.

   THE GLYPH IS NOT DECORATION. It is what keeps the state off color alone, which
   the brand system requires and WCAG 1.4.1 requires. Drawn as an SVG mask rather
   than a background image so it takes its fill from the same token as the text —
   one value moves both, and the dark-surface flip reaches the icon for free.
   Solid rather than outline: SUI reserves Solid for small inline status glyphs,
   which is exactly this.
   -------------------------------------------------------------------------- */

/* --- The control ------------------------------------------------------------
   FOUR CLASSES IS NOT DECORATION EITHER. Salient's skin-material sets both the
   fill and the border color on a field inside a `.light` row from
   `.span_12.light input[type="email"]` — 0-3-1, heavier than this theme's own
   0-2-1 field rule, which is why a dark-band field has a transparent border in
   the first place. At 0-4-0 the selectors below clear it on class count, so the
   error border lands on a light band and a dark one from the same declaration.

   The select2 branch needs a fifth class for the same reason: Salient's
   `.span_12.light .select2-container--default .select2-selection--single` is
   0-4-0, a tie, and a tie decided by load order is a rule waiting to break the
   next time a stylesheet moves. */
/* ESCALATED TO 0-4-3 ON 2026-09-03, AND THE REGRESSION THAT FORCED IT IS WORTH
   RECORDING BECAUSE IT WAS SELF-INFLICTED. The dark-band reclaim block added above
   carries selectors like

     .container-wrap .span_12.light input[type="text"]                     0-4-1

   which is FOUR classes and one type — heavier than this rule was at 0-4-0. So on
   an Obsidian band an invalid field went back to the resting border and the resting
   fill: the message was red, the summary was red, and the field itself was not
   marked at all. Exactly the defect the validation work was written to fix, brought
   back by a block that had nothing to do with validation.

   THE TRAP IS THAT SOURCE ORDER LOOKED LIKE ENOUGH. The reclaim block sits earlier
   in this file than this rule, so a tie would have resolved here — and the two
   `.forminator-input` selectors DO tie at 0-4-0. It was the `input[type="text"]`
   variants in the same group that won, on a type count nobody counts. Reading the
   file top to bottom gives the wrong answer.

   `html body` FIXES IT WITHOUT DEPENDING ON ORDER AT ALL, which is the point. The
   error state should outrank the resting state on every surface, permanently,
   rather than because of where two blocks happen to sit. Any future rule that
   restates a resting border at 0-4-x now has to be deliberate about beating this. */
html body .container-wrap .forminator-field.forminator-has_error .forminator-input,
html body .container-wrap .forminator-field.forminator-has_error .forminator-textarea,
html body .container-wrap .forminator-field.forminator-has_error textarea,
html body .container-wrap .forminator-field.forminator-has_error select,
html body .container-wrap .forminator-field.forminator-has_error .select2-container .select2-selection {
	border-color: var(--dfx-field-border-error);
	background-color: var(--dfx-field-fill-error);
}

/* THE SELECTION CONTROLS WERE MISSING FROM THIS AND IT SHOWED ON A REAL FORM. A
   required checkbox group — a consent box, or the workflow picker — got the error
   message and the summary but no change to the control, because the rule above
   lists only the field family. So the one field the visitor had to go back and fix
   was the one the page did not mark.

   ITS OWN RULE RATHER THAN FOUR MORE SELECTORS ABOVE, because the declarations
   differ: an unchecked box takes the error border but must NOT take the error fill.
   The tinted red-50 wash reads as a filled-in state on an 18px box — it is the
   same problem as a checked control, in the wrong colour — so the box keeps the
   field fill and the border carries the state. `:not(:checked)` because a box the
   visitor has already ticked is not the field in error.

   0-4-4 — four classes clears Salient's 0-2-3 checkbox rule outright, and the
   `html body` is there for the same reason as the rule above it: so the error state
   cannot be undercut by a resting rule that happens to carry a type selector. */
html body .container-wrap .forminator-field.forminator-has_error input[type="checkbox"]:not(:checked),
html body .container-wrap .forminator-field.forminator-has_error input[type="radio"]:not(:checked) {
	border-color: var(--dfx-field-border-error);
}

/* THE RED BORDER DELIBERATELY SURVIVES FOCUS, and it survives it by accident of
   specificity, so it is restated here to make the behavior intentional rather
   than emergent. The focus rule above is 0-2-0 and sets border-color AND an
   outline; the error rule is 0-4-0 and wins the border. The result — red border,
   blue focus ring — is the correct one: the field is still invalid while you are
   typing in it, and dropping either signal loses information. Do not "fix" the
   two-color look by removing one.

   RESTATED RATHER THAN LEFT ALONE because a future refactor that raises the focus
   rule's weight would silently turn the border blue and hide the error on the one
   field the visitor is looking at. */
.container-wrap .forminator-field.forminator-has_error .forminator-input:focus-visible,
.container-wrap .forminator-field.forminator-has_error .forminator-textarea:focus-visible,
.container-wrap .forminator-field.forminator-has_error textarea:focus-visible,
.container-wrap .forminator-field.forminator-has_error select:focus-visible,
.container-wrap .forminator-field.forminator-has_error input[type="checkbox"]:not(:checked):focus-visible,
.container-wrap .forminator-field.forminator-has_error input[type="radio"]:not(:checked):focus-visible {
	border-color: var(--dfx-field-border-error);
	outline: var(--dfx-control-focus-ring-width) solid var(--dfx-control-focus-ring);
	outline-offset: var(--dfx-control-focus-ring-gap);
}

/* --- The message -----------------------------------------------------------
   Forminator appends `<span class="forminator-error-message">` after the field
   description. A span, so it needs a box before it can carry a leading glyph —
   flex rather than a text-indent so a message that wraps to two lines keeps its
   second line off the icon column.

   NO SPECIFICITY CONTEST HERE. The white text this replaces was INHERITED from
   the `.light` row, and an inherited value loses to any declaration at any
   weight. The 0-2-0 selector is for consistency with the rest of the block. */
.container-wrap .forminator-error-message {
	display: flex;
	align-items: flex-start;
	gap: 0.375rem;                          /* SUI control gap */
	margin-top: 0.375rem;
	color: var(--dfx-field-helper-error);
	font-size: var(--dfx-size-caption);     /* SUI Subtle — 13px */
	font-weight: var(--dfx-weight-body);
	line-height: 1.385;                     /* 18px on 13px, SUI's Subtle leading */
}

.container-wrap .forminator-error-message::before {
	content: "";
	flex: 0 0 auto;
	width: 1rem;
	height: 1rem;
	margin-top: 0.125rem;                   /* optical: 16px glyph on an 18px line */
	background-color: var(--dfx-field-icon-error);
	/* Both properties: Safari wanted the prefix for mask-image well past the point
	   the unprefixed one shipped, and the OEM carousel in components.css carries
	   the same pair for the same reason. */
	-webkit-mask: var(--dfx-icon-error) center / contain no-repeat;
	        mask: var(--dfx-icon-error) center / contain no-repeat;
}

/* --- The form-level summary ------------------------------------------------
   Forminator prints every message TWICE: once under its field, and again as a
   bulleted list in the response region at the top of the form. On /book-a-demo
   that is seven duplicated lines pushing the first field below the fold.

   THE LIST IS HIDDEN BUT NOT REMOVED, and that distinction is the whole design.
   The region is `role="alert"`, and it is the only thing that tells a screen-
   reader user the submit failed at all — `display: none` would take it out of the
   accessibility tree and the announcement with it. Clipped to 1px instead: still
   read, no longer occupying seven lines.

   THE VISIBLE LINE IS GENERATED CONTENT, WHICH IS A COMPROMISE WITH A REASON.
   Forminator's showErrors() does `.html('<ul></ul>')` on this div, wiping
   anything PHP put inside it, so a translatable server-rendered string cannot
   survive here. The `content` value reads a custom property so the string can
   still be overridden per-context — set --dfx-form-error-summary on the form or
   the row. WPML is installed with three languages configured but only English
   published; if a second language goes live, this line needs a real string,
   which means a small script on the `validation:showError` event rather than CSS.

   THE :not() GUARDS ARE LOAD-BEARING, AND THEY HAVE TO REPEAT ON THE PSEUDOS.
   The div is present and EMPTY on page load with aria-hidden="true"; Forminator
   drops the attribute when it fills the region. A pseudo-element is selected by
   its own compound, so guarding the box alone guards only the box: the first
   version of this block did exactly that, and /support rendered "Check the
   fields marked below." in red above a form nobody had submitted.

   THE PIXELS CAUGHT IT AND THE INSPECTION DID NOT, which is the reusable part.
   `matches()` on the guarded selector returned false and the box measured no
   fill and no padding — both correct, both irrelevant, because neither question
   was about the pseudos. The screenshot of an untouched page was the only thing
   that showed it. Same lesson as the OneTrust block: trust the render. */
.container-wrap .forminator-response-message.forminator-error:not([aria-hidden="true"]):not(.forminator-hidden) {
	position: relative;                     /* contains the clipped list */
	display: flex;
	align-items: flex-start;
	gap: 0.625rem;
	margin-bottom: 1.5rem;
	padding: var(--dfx-alert-inset);
	border-left: var(--dfx-alert-accent-width) solid var(--dfx-alert-error-accent);
	border-radius: var(--dfx-alert-radius);
	background-color: var(--dfx-alert-error-fill);
}

.container-wrap .forminator-response-message.forminator-error:not([aria-hidden="true"]):not(.forminator-hidden)::before {
	content: "";
	flex: 0 0 auto;
	width: 1.25rem;
	height: 1.25rem;
	background-color: var(--dfx-alert-error-title);
	-webkit-mask: var(--dfx-icon-error) center / contain no-repeat;
	        mask: var(--dfx-icon-error) center / contain no-repeat;
}

.container-wrap .forminator-response-message.forminator-error:not([aria-hidden="true"]):not(.forminator-hidden)::after {
	content: var(--dfx-form-error-summary, "Check the fields marked below.");
	color: var(--dfx-alert-error-title);
	font-size: var(--dfx-size-body-sm);
	font-weight: var(--dfx-weight-emphasis);
	line-height: 1.25rem;                   /* matches the glyph, so both sit on one line */
}

.container-wrap .forminator-response-message.forminator-error ul {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	list-style: none;
}
