/**
 * Dealer-FX Brand Tokens
 * -----------------------------------------------------------------------------
 * Single source of truth for the Dealer-FX marketing brand system, exposed as
 * CSS custom properties. Do not hardcode hex values elsewhere in the child
 * theme — reference these tokens instead.
 *
 * Palette and typography per the Dealer-FX Brand System (marketing context).
 * Arc is the primary action/CTA color on marketing surfaces.
 */

:root {
	/* --- Primary palette --------------------------------------------------- */
	--dfx-obsidian:        #141E2C; /* Foundation, text, anchoring */
	--dfx-baltic:          #234F7C; /* Primary brand color */
	--dfx-powder:          #A9BDD1; /* Hierarchy support */
	--dfx-stone:           #D6D2C4; /* Neutral balance */
	--dfx-brass:           #B5A46A; /* Premium accent — maximum restraint */
	/* One step darker, same hue, for hover/press. Chosen so an Obsidian label
	   still clears WCAG AA on it — 5.5:1 — because a brass button carries dark
	   text, and the usual "just darken it" hover walks that contrast down until
	   the label goes soft. */
	--dfx-brass-hover:     #AB9754;

	/* --- Neutrals ---------------------------------------------------------- */
	/* Identical hex to --dfx-obsidian, and currently referenced nowhere. Kept
	   because the brand doc names both, but it is a second name for one color:
	   prefer --dfx-obsidian (or the semantic aliases) so grep finds every use. */
	--dfx-ink-black:       #141E2C; /* Shared anchor tone with Obsidian */
	--dfx-graphite-black:  #2B2F36;
	--dfx-slate-graphite:  #424752;
	/* ADDED TO CLOSE A GAP IN THE RAMP, not for a one-off component. Moving body
	   copy to Slate Graphite left --dfx-color-text-muted pointing at the same hex
	   as body, so "muted" stopped being muted in ten components at once. The next
	   step up the ramp was Steel Graphite #9AA0A6 at 2.44:1 on Signal White, which
	   is not usable for text of any size, so there was nothing between the two.
	   Same 221deg slate hue, sitting between them: 4.72:1 on Signal White (clears
	   AA) and 1.82:1 against body, which is enough to read as a second tier. */
	--dfx-pewter:          #676E7E;
	/* THE AA FLOOR, AND THAT IS ITS ENTIRE DEFINITION. Same 221deg slate hue as
	   Pewter and Slate Graphite, at the lightest 8-bit step on that hue that still
	   clears 4.5:1 on white — 4.54:1. One step lighter is #6F7789 at 4.49:1, which
	   fails, so this is not a round number and cannot be nudged: it is the edge.

	   ADDED ON REQUEST FOR THE FOOTER LINKS, which is worth stating plainly because
	   it is the second color in this ramp that the brand doc does not name. Pewter
	   was added to close a gap between two usable values; this one exists to sit ON
	   a threshold, which is a different and more fragile kind of reason. It buys
	   about half a ramp step of lightness over Pewter (4.54 against 5.11) and has
	   no headroom left for antialiasing, a non-sRGB display, or a footer tinted a
	   shade off white — any of which move the real ratio down, not up.

	   SO USE IT WHERE THE LIGHTNESS IS THE POINT AND THE SURFACE IS KNOWN WHITE.
	   For anything that needs to stay readable across surfaces, --dfx-color-text-muted
	   (Pewter) is the safer muted role and is still the default. Do not repoint
	   --dfx-color-text-muted at this — it would take ten components to the threshold
	   at once, several of them sitting on Signal White rather than white, where this
	   resolves to 4.20:1 and fails. */
	--dfx-nickel:          #6F7688;
	--dfx-steel-gray:      #9AA0A6;
	--dfx-soft-stone:      #C8CBCF; /* Borders, dividers */
	--dfx-signal-white:    #F5F6F7; /* Base surface */

	/* --- Interactive & action --------------------------------------------- */
	--dfx-arc:             #2E9FD4; /* Primary CTA / focus — primary action */
	--dfx-arc-hover:       #2185B4; /* One step darker for hover/press */
	--dfx-cyan:            #0BBCD4; /* Secondary actions. No longer links — see --dfx-color-link */
	--dfx-cyan-hover:      #09A0B5;
	--dfx-signal:          #14C4B5; /* In-flight motion ONLY */

	/* --- Operational status (editorial) ------------------------------------ */
	--dfx-info:            #5B8FD4;
	--dfx-saffron:         #E8A030; /* Warning / review */
	--dfx-ember:           #E05C5C; /* Error / critical */
	--dfx-success:         #27BE69; /* Terminal positive ONLY */
	--dfx-forest:          #4A7A55; /* Sustained health ONLY */

	/* --- Brand elevation (premium, sparing) -------------------------------- */
	--dfx-gold:            #C5A84D;
	--dfx-amber:           #C47E3A;
	--dfx-sienna:          #A0522D;

	/* --- Data viz / distinction (never status) ----------------------------- */
	--dfx-verdigris:       #3DA89F;
	--dfx-teal:            #1F6B64;
	--dfx-seafoam:         #8ECDC7;
	--dfx-mist:            #E2F3F1;
	--dfx-violet:          #6258A8;
	--dfx-indigo:          #3A3270;
	--dfx-thistle:         #A9A3D0;
	--dfx-dusk:            #EAE8F4;
	--dfx-iris:            #7B5EA7; /* AI features */
	--dfx-rose:            #C45E7A; /* Human / support moments */

	/* --- Earth family (warm editorial) ------------------------------------- */
	--dfx-cream:           #F3EDE4;
	--dfx-pebble:          #DDD0BC;
	--dfx-sand:            #B09878;
	--dfx-umber:           #6E5840;

	/* --- Semantic role aliases (use these in components) ------------------- */
	/* TWO AXES, and keeping them apart is the point.

	   ELEMENT ROLE  --dfx-color-heading / --dfx-color-body. What an h2 is, versus
	                 what a paragraph is. Applied once each in brand-base.css and
	                 nowhere else; a component should never need to name either.
	   EMPHASIS ROLE --dfx-color-text / --dfx-color-text-muted. Primary versus
	                 secondary WITHIN a block — a card's title against its summary.
	                 This is what components reach for.

	   They are separate because the questions are separate. A muted caption is
	   muted whether it sits under a heading or in a footer, and a heading is a
	   heading whether or not anything near it is muted. Collapsing the two is what
	   produces the "why is this gray" archaeology later. */
	--dfx-color-heading:         var(--dfx-graphite-black);   /* #2B2F36 — 12.4:1 on Signal White */
	--dfx-color-body:            var(--dfx-slate-graphite);   /* #424752 — 8.6:1 on Signal White */

	/* NOTE ON THE HEADING/BODY SPLIT: #2B2F36 against #424752 is 1.44:1, so the two
	   are all but indistinguishable side by side — the hierarchy is carried by size
	   and by weight 600, not by this. #2B2F36 is also LIGHTER than the #141E2C
	   headings previously inherited from body, so headings got softer, not stronger.
	   Both were reviewed and chosen deliberately. If the split should read as a real
	   distinction, point --dfx-color-heading at --dfx-obsidian (1.80:1 against body)
	   — one line, here, and every heading on the site moves. */

	--dfx-color-text:            var(--dfx-obsidian);
	/* Pewter, not Slate Graphite. Slate Graphite is now --dfx-color-body, and a
	   muted role that resolves to the same hex as body is not a role. */
	--dfx-color-text-muted:      var(--dfx-pewter);
	--dfx-color-surface:         var(--dfx-signal-white);
	--dfx-color-brand:           var(--dfx-baltic);
	--dfx-color-action:          var(--dfx-arc);
	--dfx-color-action-hover:    var(--dfx-arc-hover);

	/* Primary CTA — THE ONE PLACE TO CHANGE BUTTON COLOR.
	   Obsidian fill, Signal White label, at 15.5:1. Its own role rather than reusing
	   --dfx-color-action: that alias also paints the hero's eyebrow icon chip and a
	   badge in components.css, both of which carry WHITE glyphs, so the two cannot
	   share a value safely.

	   IT WAS BRASS, AND BRASS IS WHY IT CHANGED. The brand system reserves Brass for
	   premium moments and caps it at roughly one per surface; making it the global
	   primary button broke that everywhere at once. The tier ladder made it
	   unarguable — four Brass buttons and a Brass "Most popular" badge on one screen,
	   where the badge is the thing meant to draw the eye and could not, because four
	   buttons were wearing its color.

	   So Brass goes back to being an accent, and the dark-context override in
	   brand-base.css is where it still paints buttons — on an Obsidian band an
	   Obsidian button would disappear, and Brass on Obsidian is 6.78:1. One rule,
	   two surfaces: light gets Obsidian, dark gets Brass.

	   THE HOVER GOES TO BALTIC, WHICH BREAKS THE USUAL RULE ON PURPOSE. Interaction
	   states darken one step within a color's ramp, and Obsidian is the end of the
	   ramp — there is no darker step. Baltic is the primary brand color, it is a real
	   shift rather than a tint, and the label still clears AA at 7.82:1. */
	--dfx-color-cta:             var(--dfx-obsidian);
	--dfx-color-cta-hover:       var(--dfx-baltic);
	--dfx-color-cta-text:        var(--dfx-signal-white);

	/* The same pair for a dark surface, named so the override in brand-base.css reads
	   as a role swap rather than as two hardcoded colors. */
	--dfx-color-cta-on-dark:       var(--dfx-brass);
	--dfx-color-cta-on-dark-hover: var(--dfx-brass-hover);
	--dfx-color-cta-on-dark-text:  var(--dfx-obsidian);
	/* Socket UI's text-link-default, replacing #0BBCD4 (2.29:1). AA ON WHITE ONLY:
	   4.68:1 on #FFFFFF, 4.33 on Signal White (--dfx-color-surface), 4.07 on Mist —
	   the last two under 4.5. Accepted white-only on DSGN-618 rather than diverging
	   from Socket UI, so a resting link on any surface but --dfx-color-surface-raised
	   is a known, deliberate AA exception. Hover clears everywhere (5.04 on Signal
	   White) and stays PROVISIONAL pending Socket UI's own text-link-hover. */
	--dfx-color-link:            #0072E0;
	--dfx-color-link-hover:      #0068CC;
	--dfx-color-border:          var(--dfx-soft-stone);
	/* Soft Stone lightened toward the surface. Full-strength Soft Stone is right
	   for a single divider or an input outline, but repeated across thirty table
	   rows it reads as a grid rather than a guide. Same hue, so the two sit
	   together without looking like different decisions. */
	--dfx-color-border-subtle:   #E3E5E7;

	/* A surface that sits ABOVE the page. Pure white against Signal White (#F5F6F7) is
	   the whole of the separation where it is used.

	   NOTE THE PRODUCT CARD NO LONGER USES THIS, despite the name coming from it. The
	   card went dark to match the homepage cards; the footer took this token over on the
	   reasoning that it now stands in the same relationship to the page. See the footer
	   block in components.css. */
	--dfx-color-surface-raised:  #FFFFFF;

	/* The inverse: a surface that sits above the page by going DARK rather than light.
	   The product card, matching the homepage's dark tiles. Semantic rather than reaching
	   for --dfx-obsidian in a component, so the two can diverge if a second dark surface
	   ever needs a different value. */
	--dfx-color-surface-inverse: var(--dfx-obsidian);
	--dfx-color-focus:           var(--dfx-arc);

	/* --- Interactive controls — SUI 2.0's `control/*` tier -------------------
	   THE CHROME EVERY CONTROL SHARES, AND THE TIER EXISTS BECAUSE SUI'S DOES.
	   SUI 2.0 splits control tokens three ways (`sui2-decisions.md`, June 15):
	   `control/*` is what is identical across every control except Button — radius,
	   border, focus ring, disabled; `field/*` is the palette only Input, Select and
	   Textarea share — fill, placeholder, helper; `{component}/*` is the thin
	   override layer. The block below is `control/*`, the one after it is `field/*`.

	   THE SPLIT IS NOT BUREAUCRACY, IT PREVENTS FALSE SHARING. A checkbox has no
	   placeholder and no fill of its own; putting those at the control tier would
	   force an override on every selection control. Keeping them one tier down is
	   what lets a checkbox and a text input agree on their border and focus ring
	   while disagreeing about everything else — which is the whole ask.

	   SUI VALUES WIN INSIDE A CONTROL, WHICH IS A DELIBERATE PRECEDENCE CALL.
	   `product-ui.md` scopes SUI to product UI and hands marketing pages to
	   `applications.md` — but the brand system defines no field, no checkbox and no
	   focus ring, so on a form there is nothing for it to hand over. SUI is the only
	   place those values exist and the only place they have been contrast-tested
	   against a control. So: Arc stays the CTA and the marketing accent, and every
	   control on the site takes SUI's action blue. Dave, 2026-09-03.

	   The primitives are declared here rather than in the palette at the top of the
	   file, matching how the red ramp arrived with the validation block below — a SUI
	   ramp step is not a brand color and should not read as one in the palette.

	   TWO OF THESE ARE ALREADY BRAND VALUES, which is worth knowing before anyone
	   "de-duplicates" them: SUI grey-300 is #C8CBCF, the same hex as Soft Stone, and
	   SUI grey-900 is #141E2C, the same hex as Obsidian. The ramps genuinely meet.
	   They are still written as SUI names where a SUI role reads them, because the
	   two happening to agree today is not a reason to make one depend on the other. */
	--dfx-blue-400:              #1A8EFF; /* SUI blue-400 — action/default on dark */
	--dfx-blue-500:              #0072E0; /* SUI blue-500 — focus ring, and already --dfx-color-link */
	--dfx-blue-600:              #005BB3; /* SUI blue-600 — action/default */
	--dfx-blue-700:              #004385; /* SUI blue-700 — action/hover */
	--dfx-grey-200:              #DADCDF; /* SUI grey-200 — disabled fill */
	--dfx-grey-500:              #7D828A; /* SUI grey-500 — text/secondary, disabled label */

	/* BLACK-ALPHA BORDERS, NOT SOFT STONE, AND THE ALPHA IS THE POINT. SUI's
	   `color/border/*` family is black at 10/15/25/33% rather than a grey hex, so one
	   value composites correctly on white, on Signal White and on a tinted panel. The
	   site needs exactly that: the same field renders on a white card, a Signal White
	   panel and an Obsidian band, and a fixed grey is only ever right on one of them.
	   Dark surfaces flip to white-alpha via the marker list in brand-base.css, which
	   is SUI's own dark-mode answer (`color/grey/white-25`).

	   THIS ALSO FIXES A REAL DEFECT RATHER THAN JUST RENAMING ONE. Salient paints
	   `border: 2px solid transparent` on inputs and sets the border color again from
	   `.span_12.light input[type="email"]` at 0-3-1, so a field on a dark band had no
	   visible border at all — the old 0-2-1 field rule could not reach it. See the
	   field block in brand-base.css for the specificity that fixes it.

	   Contrast: black/25 on white is 1.84:1, which is BELOW WCAG 1.4.11's 3:1 for a
	   non-text control boundary. That is SUI's own documented, accepted exception
	   (`sui2-decisions.md`: "an accepted, documented WCAG 1.4.11 non-compliance
	   prioritising aesthetics") and it is inherited here knowingly, not overlooked.
	   The compensations are real: focus is a 2px ring in blue-500, the error state
	   moves the border to red-500 at 5.59:1, and no control communicates by border
	   alone. If that trade is ever revisited, it moves here and moves everywhere.

	   AND IT IS A STEP UP, NOT DOWN, WHICH SURPRISED ME ENOUGH TO MEASURE IT TWICE.
	   Soft Stone — the value this replaces — is 1.63:1 on white. Black/25 is 1.84:1
	   and black/33 on hover is 2.30:1, so the resting border gets slightly stronger
	   and hover becomes a real step rather than the nothing it was before. On
	   Obsidian, white/25 is 2.27:1 and white/33 is 2.99:1, against a border that
	   currently is not drawn at all. */
	--dfx-control-border:          rgba(0, 0, 0, 0.25); /* SUI control/border/default — black/25 */
	--dfx-control-border-hover:    rgba(0, 0, 0, 0.33); /* SUI field/border/hover — black/33 */
	--dfx-control-border-disabled: rgba(0, 0, 0, 0.10); /* SUI control/border/disabled — black/10 */
	--dfx-control-border-focus:    var(--dfx-blue-600); /* SUI control/border/focus */
	--dfx-control-border-width:    1px;                 /* SUI control/border-width */

	/* THE RING AND THE BORDER ARE DIFFERENT BLUES ON PURPOSE. SUI binds
	   `control/border/focus` to action/default (blue-600) and `control/focus-ring` to
	   state/focus-ring → border/focus (blue-500), one step lighter. The border reads
	   as the control changing state; the ring reads as a halo around it. Collapsing
	   them to one value loses the separation at the exact moment it is doing work.

	   THE GAP IS 2px AND THE RADIUS IS DERIVED, NOT TOKENIZED. SUI's ring sits 2px
	   outside the control (`outline-offset`), and its corner radius is computed as the
	   control's own radius plus the gap so the ring stays parallel to the edge all the
	   way round. Do not snap it to the radius scale — `control/focus-ring-radius` is
	   10 in the export and using it would make the gap vary at the corners. Dave ruled
	   on this directly (`sui2-decisions.md`, June 17). Browsers derive it for free
	   from `outline`, so nothing here has to state it. */
	--dfx-control-focus-ring:       var(--dfx-blue-500); /* SUI control/focus-ring */
	--dfx-control-focus-ring-width: 2px;                 /* SUI control/focus-ring-width */
	--dfx-control-focus-ring-gap:   2px;

	/* DISABLED TARGETS 2.8:1, WHICH IS A DECISION AND NOT A MISS. WCAG 1.4.3 exempts
	   inactive controls from contrast; SUI aims at ~2.8:1 deliberately — muted enough
	   to read as unavailable, legible enough to still say what the control does.

	   THE TOKEN CHAIN AND THE DECISION DISAGREE, AND THE DECISION WINS. Following the
	   export literally gives `control/disabled/background` → grey-100 and
	   `control/disabled/text` → `text/disabled` → grey-300, which measures 1.39:1 —
	   not a muted control, an unreadable one. `sui2-decisions.md` states the target
	   and the pair that meets it: #dadcdf with #7d828a, i.e. grey-200 with grey-500
	   (`text/secondary`), measured here at 2.81:1. That pair is used.

	   grey-200 rather than the chain's grey-100 for a second, independent reason: a
	   disabled field has to still look like a field. grey-100 is 1.08:1 against a
	   Signal White panel — invisible on the exact surface the contact form sits on,
	   which is the same failure the previous value here had (it used the page surface
	   as the fill outright). grey-200 is 1.27:1 against Signal White and 1.37:1
	   against white, so the box survives on both. Worth raising with the SUI
	   maintainers as an export bug rather than carrying the workaround forever. */
	--dfx-control-disabled-fill:  var(--dfx-grey-200); /* SUI grey-200 — the measured pair */
	--dfx-control-disabled-text:  var(--dfx-grey-500); /* SUI text/secondary — 2.81:1 on the fill */

	/* Selection fill for a checked checkbox or radio. SUI's action pair, darkening one
	   step on hover per the ramp rule — never a lightened tint, never opacity. */
	--dfx-control-selected:        var(--dfx-blue-600);
	--dfx-control-selected-hover:  var(--dfx-blue-700);
	--dfx-control-on-selected:     #FFFFFF; /* SUI text/on-action */

	/* Two radii, and they are different in SUI on purpose: a field is a surface (8px,
	   `control/radius` → `sizing/radius/lg`) and a checkbox is a mark (4px,
	   `sizing/radius/md`). A radio is `sizing/radius/full`, stated where it is used. */
	--dfx-control-radius:      8px;  /* SUI control/radius — inputs, selects, textareas */
	--dfx-control-radius-box:  4px;  /* SUI checkbox box radius */

	/* NO SIZE TOKEN EXISTS IN SUI FOR A CHECKBOX, so this one is the theme's and is
	   labelled as such. 18px sits with 14px body copy and is the value this theme
	   already declared before the control was drawn — it was simply unreachable
	   behind Salient's rule, so keeping it changes nothing anybody has seen. The hit
	   area is the whole <label>, so the box being under 44px costs no touch target. */
	--dfx-control-box-size:    1.125rem; /* 18px — theme value, not SUI */

	/* 130ms, the middle of SUI's 120–140ms band, on color and border only. No
	   transition on layout or size: a control that changes shape on hover reads as
	   moving rather than responding. `prefers-reduced-motion` is honored globally in
	   brand-base.css, so nothing here needs to repeat it. */
	--dfx-control-transition:  130ms ease;

	/* --- Controls on a dark surface -------------------------------------------
	   THE SECOND HALF OF EVERY CONTROL TOKEN, swapped in by the marker list in
	   brand-base.css — the same list the CTA colors and the validation ramp already
	   ride, for the reason recorded there: two copies of "what counts as a dark
	   surface" drift, and a drifted copy is a control nobody can see on a band
	   nobody reviews. /book-a-demo's form sits in a full-width Obsidian row, so this
	   is the live case and not a hypothetical.

	   THIS IS SUI'S OWN DARK MODE, WITH ONE CORRECTION. `[data-theme="dark"]` in the
	   export moves the borders to white-alpha and the action color to blue-400, and
	   both are taken as-is. But it also points the dark surfaces at the PEWTER ramp
	   (pewter-800, #233D4D — a blue-teal), and `sui2-decisions.md` records that
	   palette being replaced in place: the dark surface tokens were re-pointed onto
	   the OBSIDIAN ramp. The export is stale on that one family, the decision is not,
	   and Obsidian is what the site's bands are actually painted in. So the fills
	   below come from the obsidian ramp.

	   THE DARK FIELD IS DARK, NOT WHITE, AND THAT IS DELIBERATE — this is the token
	   that decides it. SUI's `field/background/default` follows `surface/default`,
	   which in dark mode is a dark surface; a white field punched into an Obsidian
	   band reads as a hole.

	   AND IT IS FLUSH WITH THE BAND — THE BORDER IS THE FIELD. Dave, 2026-09-03,
	   after comparing the candidates rendered on the band. The first version of this
	   token used obsidian-800 #253346, chosen because it sat within a point of the
	   `rgba(255,255,255,0.07)` Salient had been painting there by accident, and it
	   was too light. So the fill is now the band's own value and the white/25 border
	   carries the whole definition of the control at 2.27:1.

	   THE NUMBERS AGREE WITH THE EYE, WHICH IS NOT ALWAYS TRUE AND IS WHY THEY WERE
	   CHECKED. The border is the thing at risk when the fill stops contrasting — and
	   it is not at risk at all: white/25 measures 2.23–2.28:1 across every candidate
	   from #0F141A to #253346, essentially flat, because it composites over near-
	   identical darks. Everything that sits ON the fill improves by going darker.
	   Against the #253346 this replaces:

	     value text (grey-100)      10.93 → 14.32
	     placeholder (grey-400)      4.85 →  6.35
	     focus border (blue-400)     3.87 →  5.08
	     border white/25              2.20 →  2.27
	     hover white/33               2.79 →  2.99

	   So obsidian-800 was the worst option in the set for every value except the one
	   it was picked for. Worth stating plainly, because "a visible lift" sounds like
	   the safe choice and it cost contrast on all five.

	   OBSIDIAN-900 (#18202A) IS THE TRAP HERE, and it is the one a future reader will
	   reach for. It looks like the principled answer — a real step on the documented
	   obsidian ramp, one below the band — but it is 1.02:1 against #141E2C, which is
	   not a perceptible difference on any screen. It would be a token doing nothing,
	   named as though it were doing something. Considered and rejected on that basis,
	   not overlooked. If this fill ever needs to separate from the band, the honest
	   step is a 4–5% white wash (#1D2734 / #202937, 1.11–1.15:1) — perceptible, and
	   the same mechanism the dark error fill already uses.

	   PLACEHOLDER GOES TO grey-400, NOT PEWTER, and it is the same contrast-first
	   call as the light side made in the other direction. Pewter is 5.11:1 on white
	   but only 2.50:1 on this fill, so carrying it across would fail the very
	   threshold it was chosen to clear. grey-400 is SUI's dark `text/secondary`.

	   THE CHECK GLYPH TURNS OBSIDIAN ON DARK. White on blue-400 is 3.30:1 and
	   Obsidian on blue-400 is 5.08:1 — and a checkmark is a small shape carrying the
	   whole state of the control, so it takes the stronger of the two. */
	--dfx-control-border-on-dark:          rgba(255, 255, 255, 0.25); /* SUI grey/white-25 */
	--dfx-control-border-hover-on-dark:    rgba(255, 255, 255, 0.33); /* SUI grey/white-33 */
	--dfx-control-border-disabled-on-dark: rgba(255, 255, 255, 0.10);
	--dfx-control-border-focus-on-dark:    var(--dfx-blue-400); /* SUI dark action/default */
	--dfx-control-focus-ring-on-dark:      var(--dfx-blue-400); /* SUI dark border/focus */

	/* The two collapse to one value on dark, which is SUI's choice and not an
	   oversight here: in dark mode `action/default` and `border/focus` both resolve to
	   blue-400. So a focused control on a dark band gets border and ring in the same
	   blue and reads as one thicker edge — acceptable, because the ring's 2px gap
	   still separates them. */
	--dfx-control-selected-on-dark:        var(--dfx-blue-400);
	--dfx-control-selected-hover-on-dark:  #52AAFF; /* SUI blue-300 — dark action/hover, 6.83:1 on Obsidian */
	--dfx-control-on-selected-on-dark:     var(--dfx-obsidian);

	/* Inverted from the light side: on dark, SUI's `state/disabled/subtle` is grey-300
	   and `text/disabled` is grey-600, so an inactive control goes flat and LIGHT
	   rather than darker. 3.61:1, comfortably past the 2.8:1 target. */
	--dfx-control-disabled-fill-on-dark:   var(--dfx-soft-stone); /* grey-300 */
	--dfx-control-disabled-text-on-dark:   #5F656E;               /* SUI grey-600 */

	--dfx-field-fill-on-dark:              var(--dfx-obsidian); /* flush with the band */
	--dfx-field-text-on-dark:              #ECEDEF; /* SUI grey-100 — dark text/primary */
	--dfx-field-placeholder-on-dark:       #9AA0A6; /* SUI grey-400 — dark text/secondary */

	/* --- Form fields — SUI 2.0's `field/*` tier ------------------------------
	   ONE SET OF FIELD VALUES FOR THE WHOLE SITE. Before these existed the contact
	   form rendered three heights in one column — 48px text inputs, a 40px select and
	   a 22px submit — because each came from a different source and none knew about
	   the others. See the field block in brand-base.css.

	   TWO HEIGHTS, NOT ONE. The comfortable height is for forms somebody fills in;
	   the compact one is for filter bars, where controls sit beside content rather
	   than being the content. 48px also clears the 44px minimum touch target, which
	   matters because a service manager fills these in on a phone in the drive.

	   AND THE TWO HEIGHTS ARE ALREADY SUI VALUES — this was checked rather than
	   assumed, because 48 and 40 look nothing like the 32/36/40 in `product-ui.md`.
	   That table is SUI's DEFAULT density. The export also ships a `comfortable`
	   mode (`:root[data-mode="comfortable"]`), and there 48 is `sizing/component/xl`,
	   40 is `sizing/component/md`, and `sizing/touch-target-min` is 48. So the site
	   is a comfortable-density consumer and the heights need no change; a marketing
	   page filled in on a phone in the service drive is exactly what that mode is
	   for. Recording it so the next reader does not "correct" 48px down to 36. */
	--dfx-field-height:          3rem;    /* 48px — forms; SUI comfortable component/xl */
	--dfx-field-height-compact:  2.5rem;  /* 40px — filter bars; SUI comfortable component/md */

	/* 12px, SUI's `field/inset/md` → `spacing/inset/sm`. Unchanged in value; now
	   named against the token it was already matching. */
	--dfx-field-pad-x:           0.75rem;

	/* An alias, so every `--dfx-field-radius` already in the stylesheets picks up the
	   control tier without being rewritten. It was 4px; SUI's field radius is 8. */
	--dfx-field-radius:          var(--dfx-control-radius);

	/* Width reserved at the end of a field for an icon that sits inside it — a select's
	   chevron, the phone field's country flag. 2rem matches intl-tel-input's own country
	   container, so text clears the flag instead of running under it. */
	--dfx-field-icon-slot:       2rem;   /* 32px */

	/* PURE WHITE, NOT SIGNAL WHITE, and this is the fix for the invisible field. A
	   field that shares its container's fill stops reading as a field, and the contact
	   form sits on a Signal White panel. White + a real border works on both.
	   Also SUI's own value: `field/background/default` → `control/background/default`
	   → `surface/default` → grey/white. */
	--dfx-field-fill:            #FFFFFF;
	--dfx-field-border:          var(--dfx-control-border);
	--dfx-field-border-hover:    var(--dfx-control-border-hover);
	--dfx-field-text:            var(--dfx-color-body);

	/* PEWTER, NOT SUI'S grey-300, AND THIS IS THE ONE PLACE THE FIELD SYSTEM
	   DELIBERATELY LEAVES SUI. `field/placeholder/default` resolves to
	   `text/disabled` (grey-300, #C8CBCF) — 1.71:1 on white. SUI justifies it as
	   exempt because a placeholder is non-essential, and inside the product that
	   holds: every field there carries a real label.

	   It does not hold here. On this site a placeholder frequently carries the format
	   hint — and the theme has now made the same call three times in a row, each time
	   toward contrast: Ember → red-500 for validation text, success → `success/dark`
	   for a validation border, and this. Pewter is 5.06:1 on white. If a placeholder
	   ever becomes purely decorative, this is the token to reconsider. */
	--dfx-field-placeholder:     var(--dfx-color-text-muted);

	/* --- Field validation — error ---------------------------------------------
	   Values come from SUI 2.0's `field/*` and `color/feedback/error` families
	   (references/product-ui.md + the Figma token export), because those are the
	   only error values in the system that have been contrast-tested against a
	   field. The names below mirror SUI's: border / fill / helper / icon.

	   EMBER IS NOT USED HERE, and it is the token you would reach for first.
	   --dfx-ember (#E05C5C) is the brand's editorial error color and it is 3.31:1
	   on Signal White — a fail for the 4.5:1 body-text threshold that a validation
	   message has to clear, since the message IS the instruction. SUI's red-500
	   is 5.59:1 on Signal White and 6.05:1 on white. Ember stays defined for
	   editorial use; a form never reads it.

	   THE RAMP STEPS FLIP BY SURFACE, WHICH IS THE WHOLE REASON THE -on-dark PAIR
	   EXISTS. red-500 on Obsidian is 2.77:1 — invisible. SUI's own dark theme
	   answers this by moving `feedback/error/default` from red-500 to red-300, and
	   red-300 on Obsidian is 5.53:1. The pair below is that swap, nothing more. */
	--dfx-red-50:                #FFF0EF; /* SUI red-50  — subtle error fill */
	--dfx-red-300:               #E77069; /* SUI red-300 — error on dark surfaces */
	--dfx-red-500:               #BD271E; /* SUI red-500 — error on light surfaces */
	--dfx-red-600:               #961F18; /* SUI red-600 — one step darker, icons */

	--dfx-field-border-error:    var(--dfx-red-500);
	--dfx-field-fill-error:      var(--dfx-red-50);
	--dfx-field-helper-error:    var(--dfx-red-500);
	--dfx-field-icon-error:      var(--dfx-red-600);

	/* THE DARK FILL IS A HUE SHIFT, NOT A VALUE SHIFT, and it is worth knowing
	   before someone "fixes" it. Composited over Obsidian this wash lands on
	   #30222D: 1.11:1 against the band and 1.10:1 against the default dark field
	   fill Salient paints (#242E3B). Measured, not estimated. So on a dark band the
	   fill is a supporting cue only — the border, the icon and the message carry
	   the state, which is what the never-color-alone rule asks for anyway.
	   SUI's dark `feedback/error/lightest` (red-900) was tried first and is worse:
	   1.11:1, and darker than the band it sits on. */
	--dfx-field-border-error-on-dark: var(--dfx-red-300);
	--dfx-field-fill-error-on-dark:   rgba(223, 62, 53, 0.14); /* SUI red-400 wash */
	--dfx-field-helper-error-on-dark: var(--dfx-red-300);
	--dfx-field-icon-error-on-dark:   var(--dfx-red-300);

	/* --- Feedback alert — error ------------------------------------------------
	   SUI 2.0's subtle Alert: a `feedback/<intent>/lightest` fill, a left accent
	   stroke at `sizing/stroke/accent`, a leading status glyph, and the surface
	   radius. The form-level validation summary is the site's first consumer; the
	   token names are intent-agnostic so a success or warning alert can be added
	   later without renaming anything.

	   The title steps one further down the ramp than the accent (red-600 rather
	   than red-500) because it sits ON the tinted fill rather than on the page:
	   7.57:1 on red-50, where red-500 would be 5.46:1. Same reasoning as the
	   field icon. */
	--dfx-alert-error-fill:      var(--dfx-red-50);
	--dfx-alert-error-accent:    var(--dfx-red-500);
	--dfx-alert-error-title:     var(--dfx-red-600);

	--dfx-alert-error-fill-on-dark:   rgba(223, 62, 53, 0.14);
	--dfx-alert-error-accent-on-dark: var(--dfx-red-300);
	--dfx-alert-error-title-on-dark:  var(--dfx-red-300);

	--dfx-alert-accent-width:    6px;    /* SUI sizing/stroke/accent */
	--dfx-alert-radius:          8px;    /* SUI surface radius */
	--dfx-alert-inset:           1rem;   /* SUI spacing/inset/md */

	/* The error status glyph — SUI's circle-exclamation, Solid weight, on the 16px
	   grid. A TOKEN RATHER THAN AN INLINE VALUE because it is used at two sizes in
	   two components (the field message and the alert) and a data URI copied twice
	   is a data URI that gets edited once.

	   CARRIES NO FILL. It is consumed as a `mask`, so the shape comes from here and
	   the color comes from whichever error token the component is using — which is
	   what lets the light/dark ramp swap reach the icon without a second URI.
	   The exclamation is knocked out of the disc by `fill-rule="evenodd"`; a mask
	   reads alpha only, so a two-tone glyph is not available. */
	--dfx-icon-error: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0ZM7.1 4h1.8v5.2H7.1zM7.1 10.6h1.8v1.8H7.1z'/%3E%3C/svg%3E");

	/* The checkbox tick — SUI's `check` glyph on the same 16px grid.
	   TWO URIS AND AN ALIAS, WHICH IS NOT HOW THE ERROR GLYPH ABOVE WORKS, and the
	   difference is worth explaining because the obvious move is to copy that one.
	   The error glyph is consumed as a `mask`, so its color comes from whatever token
	   the component hands it and one URI covers both surfaces. A tick cannot be a
	   mask: it has to sit ON a filled box, and masking the input would take the fill
	   with it. So the stroke color is baked in, which means one URI per surface and
	   the alias below is what the marker list re-points. Change the tick shape in
	   both or the dark bands quietly keep the old one.

	   STROKED RATHER THAN FILLED, AND THE ::before TRICK IS DELIBERATELY NOT USED.
	   Salient draws its tick as a rotated, border-built `input::before` — and
	   pseudo-elements on an `<input>` are not supported in Firefox, because an input
	   is a replaced element. So Salient's checkbox has no visible tick in Firefox at
	   all. A background image on the input itself works in every browser, which is
	   the whole reason for the detour. */
	--dfx-icon-check-on-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23FFFFFF' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.4l3.1 3.1 5.9-6'/%3E%3C/svg%3E");
	--dfx-icon-check-on-dark:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23141E2C' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.4l3.1 3.1 5.9-6'/%3E%3C/svg%3E");
	--dfx-icon-check:          var(--dfx-icon-check-on-light);

	/* Footer links — its own pair rather than reusing the muted text role, for the
	   same reason the CTA pair exists: --dfx-color-text-muted paints card subtitles
	   and eyebrows on Signal White, where Nickel fails, so the footer's threshold
	   value must not be able to leak into them. Hover goes to Slate Graphite, one
	   step down the ramp — 2.05:1 against the resting color, which is a real shift
	   rather than a tint, and it is the same color the footer's body copy uses so
	   a hovered link lands ON the surrounding text weight instead of past it. */
	--dfx-footer-link:           var(--dfx-nickel);
	--dfx-footer-link-hover:     var(--dfx-slate-graphite);

	/* Navigation (top-level links sit on the dark Obsidian header) */
	--dfx-nav-link:              rgba(255, 255, 255, 0.8);
	--dfx-nav-link-hover:        #FFFFFF;
	--dfx-nav-logo-gap:          1.5rem; /* extra space between logo and main nav */
	--dfx-utility-nav-pad:       15px;   /* L/R padding on utility text links (was 20px) */

	/* Secondary button (outline/ghost) — values tuned for the dark header */
	--dfx-btn-secondary-fill:         transparent;
	--dfx-btn-secondary-fill-hover:   rgba(255, 255, 255, 0.10);
	--dfx-btn-secondary-border:       rgba(255, 255, 255, 0.8);
	--dfx-btn-secondary-border-hover: #FFFFFF;
	--dfx-btn-secondary-text:         rgba(255, 255, 255, 0.8);
	--dfx-btn-secondary-text-hover:   #FFFFFF;

	/* Inverted primary button — light pill + Obsidian text, for dark surfaces */
	--dfx-btn-inverted-fill:          var(--dfx-signal-white);
	--dfx-btn-inverted-fill-hover:    var(--dfx-soft-stone); /* one step darker */
	--dfx-btn-inverted-text:          var(--dfx-obsidian);

	/* --- Typography -------------------------------------------------------- */
	--dfx-font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	--dfx-font-heading: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	/* All headings use Poppins. Display token retained for the H1–H3 weight
	   split and for a future display face if one is reintroduced. */
	--dfx-font-display: var(--dfx-font-heading);
	/* Tried at 700 and reverted 2026-08-12. Poppins Bold is real, not synthesized, and
	   leading is unaffected — but the 'oo' gap tightens to 0.057em, so display tracking
	   would need easing to -0.03em. */
	--dfx-weight-display: 600;   /* Poppins SemiBold — H1 */
	--dfx-weight-display-2: 600; /* Poppins SemiBold — H2/H3 */

	--dfx-weight-body:        400;
	--dfx-weight-emphasis:    600;
	--dfx-weight-heading:     600;
	/* H4–H5, AND THE 500 SPLIT IS OFF AGAIN AT DAVE'S DIRECTION 2026-08-14. Every
	   heading level is 600 now. Pointed at --dfx-weight-heading rather than set to a
	   literal 600 so the two cannot drift apart, and so the eight consumers in
	   brand-base.css and components.css keep reading a token instead of being rewritten.

	   KEPT AS A SEPARATE TOKEN, not merged, precisely because it has been 500 once and
	   may be again — it turns the split back on in one line. It also fixes the pairing
	   inconsistency the 500 version accepted: h3.dfx-h5 and h4.dfx-h5 now both render
	   18px/600, since weight follows the LEVEL and the levels no longer disagree.

	   WATCH THE h5/BODY GAP IF ANYTHING MOVES. A real h5 is 18px/600 against body's
	   16px/400, so weight is doing the work again at only a 2px size step — the
	   relationship the h5 re-anchor to 18px was meant to carry on size alone.

	   STILL LOAD-BEARING AGAINST THE PARENT THEME: Salient's generated CSS sets h4 to
	   500 and h5/h6 to 600 regardless of this token, so brand-base.css has to win on
	   specificity at all three for any value here to render. It does. */
	--dfx-weight-heading-light: var(--dfx-weight-heading);

	/* --- Raised surfaces: radius and shadow -------------------------------------
	   Taken from Socket UI's card, which is the agreed style direction for this
	   component. Its two-layer shadow is the part worth copying rather than
	   approximating: a wide soft layer for the lift and a tight dark one for the
	   contact edge. One layer alone reads as either a blur or a smudge.

	   NOT CALLED "elevation", DESPITE THAT BEING SOCKET'S NAME FOR IT. There is
	   already a "Brand elevation" block in this file and it holds gold, amber and
	   sienna — a color family for premium emphasis, nothing to do with depth. Reusing
	   the word here would put two unrelated meanings under one term in one file.

	   20px, AND THE DIRECTION CHANGED TO GET THERE. This was 8px, taken from Socket UI's
	   card; the product card now follows the homepage's dark tiles instead, which are
	   20px. Socket's shadow values below survive the change because the homepage tiles
	   carry no shadow at all — being dark on dark, they do not need one, and a dark card
	   on a light page still reads better grounded.

	   Every other radius in the theme is 2px, with one 4px, so badges and buttons stay
	   sharp inside a 20px card. That is a larger gap than it was at 8px; if the softer
	   corner is wanted site-wide it is a brand decision, and this token is where it
	   starts. */
	--dfx-radius-raised:         1.25rem; /* 20px */
	--dfx-shadow-raised:         0 2px 8px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.05);
	--dfx-shadow-raised-hover:   0 8px 24px rgba(0, 0, 0, 0.10), 0 3px 6px rgba(0, 0, 0, 0.07);

	--dfx-line-height-body:    1.6;

	/* FIVE HEADING LEADINGS, BECAUSE LEADING HAS TO MOVE WITH SIZE.
	   Was two. The split below the h4 boundary is unchanged and its reasoning still
	   stands; what follows it is the same argument applied ABOVE the boundary, which
	   the two-token version left alone. 1.25 covered 24px through 64px — a 2.7x size
	   range on one value, so it was right at one point on the ramp and wrong at both
	   ends. Now: display 1.2, h1 fluid, h2 1.3, h3 1.35, h4-h6 1.4.

	   RE-BANDED 2026-08-14 ON THE AIR, NOT THE RATIO, and that is the useful lesson.
	   Ratio bands made ink air SHRINK as size fell — display 8.8px, h1 6.6, h2 6.0,
	   h3 5.7 — then jump to 7.7 at h4, so h2 and h3 were the tightest steps on the site
	   and a wrapping 32px h2 read as one block. Air = line-height − 1.013em of Poppins
	   ink, and it is the quantity the eye reads. Now monotonic: 12.0 / 11.4 / 9.2 /
	   8.1 / 7.7 / 7.0. Check air, not ratio, if a level ever moves again.

	   POPPINS SETS THE FLOOR, AND IT IS NOT THE SAME FLOOR INTER WOULD SET. Measured
	   in the browser rather than read off a spec sheet: Poppins' ink ascender is
	   0.740em and its ink descender 0.273em, so two lines touch at a line-height of
	   1.013 and nothing below that is available at any size. Inter's equivalent is
	   0.943. That 0.07em difference is why a leading value copied from an Inter-set
	   page is not transferable here, and it is the same root cause as the tracking
	   note further down. Poppins' full content box is 1.400em, so at these values the
	   LINE BOXES overlap even though the ink does not — visible only if a display
	   heading ever gets a background, border or outline. */

/* --- The two original heading leadings, now the middle and bottom of five ------- */
/* LEADING HAS TO MOVE WITH SIZE.
	   1.25 was applied to every heading level from the 48px hero down to a 16px item
	   title. That is right at display sizes and wrong at small ones: leading is a
	   ratio, so the same number gives a 48px headline 60px of line box — generous —
	   and a 16px wrapped title 20px, which is four pixels of air between two lines of
	   600-weight type and reads as one block of text.

	   It showed up first on the product feature grid, where names like "Online &
	   In-dealer Appointments" wrap to two lines in a quarter-width column. It was never
	   specific to that component; every small heading on the site had it.

	   The split is at the h4 boundary, which is also where --dfx-weight-heading-light
	   already switches, so the scale changes character in one place rather than two.
	   1.4 at the 18px h5 gives 25.2px against the 25.6px of the body copy beneath it —
	   a title set slightly tighter than its own description, which is the relationship
	   wanted. That margin is 0.4px now rather than the 1.6px it was at a 16px h5, so
	   the relationship survives the re-anchor but with less room; if h5 rises again,
	   check this pair rather than assuming it still holds. */
	/* 1.35 as of the air re-band, up from 1.25 — h3's 24px had 5.7px of air, the tightest
	   step on the ramp. Now 8.1px, still under h4's 7.7px... which it must not be: h3 is
	   LARGER, so its air has to be larger. 8.1 > 7.7 holds. */
	--dfx-line-height-heading: 1.35;   /* h3 — 24px. Also the default for unstyled contexts. */
	--dfx-line-height-heading-small: 1.4; /* h4–h6, 20px and below. Unchanged. */

	/* --dfx-line-height-heading is h3 alone in the scale block, but it is ALSO the value
	   the all-levels rule at the top of brand-base.css uses for unstyled contexts, so
	   moving it to 1.35 moved that default too. Intended: 1.35 is the safer default of
	   the two.

	   --dfx-line-height-heading-small has three consumers in components.css
	   (.dfx-hero__eyebrow, .dfx-feature__name, .dfx-feature-index__name), all of them
	   elements standing in for a real h4/h5. Untouched on purpose. */

	/* --- The three new leadings, above the h4 boundary ---------------------------- */

	/* DISPLAY, 52–64px. 1.05 → 1.15 → 1.2; the second move was to stay clear of h1's new
	   1.25 ceiling, since display is larger and must stay tighter. 12.0px of air at 64px.
	   For reference Notion's 1.00 on Inter is 0.057em, about 1.07 here. */
	--dfx-line-height-display: 1.2;

	/* H1, AND IT IS THE ONE LEADING THAT IS A LENGTH RATHER THAN A RATIO.
	   h1 is clamp(2.125rem, 3.9vw, 3rem) = 34→48px, and a single ratio cannot loosen as
	   that comes down: 1.10 is right at 48px and cramped at 34px, where a headline wraps
	   to three lines on a phone. A unitless line-height cannot be made fluid, so this is
	   absolute and fluid instead, interpolating on the same vw axis as the size.

	   THE TWO ANCHORS ARE THE ONLY NUMBERS THAT WERE CHOSEN:
	     at 48px (the h1 ceiling)  →  60px = 3.75rem   = ratio 1.25   (11.4px air)
	     at 34px (the h1 floor)    →  44px = 2.75rem   = ratio 1.294  ( 9.6px air)
	   The middle term is then forced, not picked. h1 reaches its ceiling at 1230.8px of
	   viewport (48/0.039) and its floor at 871.8px (34/0.039), so the leading travels
	   44→60px across those 359px: slope 4.457vw, intercept 0.322rem.

	   THE FLOOR RATIO IS CAPPED BY h2, which is the constraint that is easy to miss. The
	   floor is 34px against h2's 32px — only 2px apart — so the floor ratio must stay
	   BELOW h2's 1.3 or a 34px h1 would be more loosely led than the 32px h2 under it.
	   1.294 is that ceiling-on-the-floor, not a preference.

	   SO IF EITHER h1 ENDPOINT MOVES, ALL FOUR NUMBERS HERE ARE STALE. That coupling is
	   the cost of fluid leading and there is no way to express it in CSS that avoids it.
	   It is also the only place in this file where one token's value depends on another's
	   arithmetic, which is why it is spelled out rather than left to be re-derived. */
	--dfx-line-height-h1: clamp(2.75rem, calc(0.322rem + 4.457vw), 3.75rem);

	/* H2, 32px fixed. 1.2 → 1.3: at 1.2 a wrapping 32px h2 had 6.0px of air, the
	   complaint that triggered the re-band. 9.2px now. Also the cap on h1's floor ratio,
	   see above. */
	--dfx-line-height-h2: 1.3;

	/* --- Heading tracking, four bands ---------------------------------------------
	   WAS ONE FLAT -0.01em ON ALL SIX LEVELS, which is optically wrong in the same way
	   one flat leading was: tracking has to tighten as size rises. The block in
	   brand-base.css that escalated this token to all six levels anticipated the split
	   in as many words — "if a future value is ever split by size the way leading is,
	   split this rule then" — and this is that.

	   THE POPPINS MEASUREMENT THAT SET THESE NUMBERS, and it is the most useful thing
	   found in this pass. Against Inter at the same size:
	     Poppins is 5.3% WIDER — a 38-character headline sets 20.75em vs Inter's 19.70em,
	       so -0.0275em of tracking is the cost of merely reaching Inter's set width.
	     Poppins has 28% LESS SIDEBEARING to give it back — its 'o' carries 0.034em per
	       side against Inter's 0.043em, so an 'oo' pair has 0.068em of ink gap where
	       Inter has 0.087em, and the round forms collide at -0.068em.
	   Both at once. Poppins is set wide AND spaced tight, so it LOOKS loose and cannot
	   be tightened much — matching Inter's density would need -0.0625em, which leaves an
	   'oo' pair 0.0055em of gap (0.35px at 64px), i.e. touching. THERE IS NO TRACKING
	   VALUE THAT MAKES POPPINS AS DENSE AS INTER. That is a property of the face, and it
	   belongs in the Poppins-vs-Inter decision still open in the heading-scale notes —
	   the guidelines say Inter for digital, and this is the first hard number arguing it.

	   -0.035em AT DISPLAY IS THEREFORE A CEILING, NOT A PREFERENCE: it consumes 51% of
	   the available 0.068em gap and leaves 2.1px of air at 64px. Do not go past -0.045em
	   on this face at any size. */
	--dfx-letter-spacing-display: -0.035em;      /* .dfx-display, 52–64px */
	--dfx-letter-spacing-h1: -0.03em;            /* h1, 34–48px */
	--dfx-letter-spacing-heading: -0.02em;       /* h2 + h3, 32/24px — WAS -0.01em */
	--dfx-letter-spacing-heading-small: -0.01em; /* h4 + h5, 20/18px — the old value */

	/* --- Heading size scale ------------------------------------------------ */
	/* SIX HAND-SET SIZES, AND THAT IS A CHANGE OF KIND FROM WHAT WAS HERE. This block
	   used to hold a strict 1.25 modular scale and the comment argued the ratio was
	   the point: six sizes as one decision. These are specified values — 48 / 32 / 24
	   / 20 / 18 / 16 — so read the ratio as a description rather than a rule:

	     h6 → h5   1.125      h3 → h2   1.333
	     h5 → h4   1.111      h2 → h1   1.500
	     h4 → h3   1.200

	   READ IT AS INCREMENTS RATHER THAN RATIOS AND IT SNAPS INTO FOCUS: +2, +2, +4, +8,
	   +16. The steps double. That is the pattern the ramp is actually built on, and it is
	   why 16 is the right bottom rung — at 14 the sequence read +4, +2, +4, +8, +16, and
	   that 4-then-2 stumble was the thing that made the ramp feel arbitrary.

	   h6 IS 16px, THE SAME AS BODY, AND THAT IS DELIBERATE — BUT ONLY BECAUSE h6 IS NOT
	   A HEADING. This file has been here before and got it wrong once, so the distinction
	   matters. h5 used to sit at 16px on the argument that "a card title at body size
	   carrying weight 600 is a real distinction". It was not: a 16px/600 sentence-case
	   title above 16px/400 sentence-case copy reads as one block, and that was the most
	   common note on the type.

	   WHAT MAKES h6 DIFFERENT IS THAT IT IS NOT SENTENCE CASE. h6 is the LABEL step, and
	   it now carries uppercase, 0.14em tracking, weight 600 and the muted ink in
	   brand-base.css rather than leaving them to each component. Uppercase at 0.14em
	   reads nothing like a paragraph, so sharing a font-size with body copy costs
	   nothing — the size was never what separated them.

	   THIS WAS NOT AN INVENTION, IT WAS A PROMOTION. Every one of the four places the h6
	   step was used already wrote uppercase + 0.14em + 600 + muted by hand: the product
	   group titles, the feature-list sub-headings, the "Trusted by" separator and the
	   footer widget titles. Four usages, four identical treatments, four copies. The role
	   existed; only the declaration of it was scattered.

	   AND IT SETTLES THE EYEBROW. .dfx-eyebrow is 13px and was 1px off the old 14px h6,
	   close enough to read as a typo. At 16 the two are 3px apart, which reads as two
	   different steps — which they are: the eyebrow is Brass brand accent, h6 is muted
	   secondary.

	   h5 → h4 IS STILL THE NARROW STEP at 1.111 — 18 to 20, same weight, same leading,
	   same tracking, so two pixels is the entire difference between a real h4 and a real
	   h5. h6 no longer competes with them now it is a caps label, but that pair still
	   does with each other. Left at 20 deliberately; if it ever needs air, h4 is the
	   number to move, not h5, which is boxed in between body and h4.

	   h1 breaks out at 1.5, which is deliberate and matches what every previous version
	   of this ramp did — a page headline is not the next rung of a ladder.

	   WHAT WAS TRADED AWAY, so nobody looks for it later: changing one step no longer
	   implies the others. Under the old ratio, moving the anchor moved all six and the
	   file could promise they stayed related. Now the relationship is a description of
	   these numbers, and editing one is a local edit. If the ramp ever wants to be
	   generative again, that is a real decision to re-make, not a regression to fix.

	   WHAT WAS GAINED, and it is not only tidiness: every step is a clean rem at a
	   16px root — 3 / 2 / 1.5 / 1.25 / 1.125 / 1. The derived values were
	   1.40625rem and 2.1875rem and 0.9rem, exact but unreadable, and two of them were
	   already rounded away from the true ratio. Values a person can hold in their head
	   are worth something in a file that gets hand-edited.

	   rem, NOT em, AT EVERY STEP. em compounds against the parent, so a heading inside
	   a sized container silently changes size and a nested one changes again. rem
	   resolves against the root, so a step is the same pixel everywhere it appears —
	   which is the whole premise of a ramp. It also still tracks a reader's browser
	   font-size setting, which a px value would not.

	   APPLIED IN brand-base.css AND NOWHERE ELSE. That file's size-scale block is
	   still the only place a heading gets a font-size; these tokens exist so tuning
	   the ramp is an edit here rather than a rewrite of six selectors, matching how
	   color and weight already work.

	   THE ONE INVARIANT WORTH GUARDING: h1's FLOOR must stay above h2. An earlier ramp
	   broke it — h1 was clamp(36px … 56px) against a fixed 40px h2, so below a ~782px
	   viewport h1 resolved to 36px and every phone rendered an h1 SMALLER than the h2
	   under it. h2 is 32px, so the floor is 34px: 2px of headroom.

	   THE FLOOR IS THE ONE NUMBER HERE NOT SPECIFIED, and it is worth knowing it is
	   derived rather than chosen. It is the MOBILE h1, squeezed from both sides: it has
	   to clear a fixed 32px h2, so it cannot go below 33. A phone therefore renders a
	   34px h1 above a 32px h2 — a 2px gap, where the desktop pair is 16px. The hierarchy
	   is carried by weight and position at that width, not size. The fix if that ever
	   reads badly is making h2 fluid too, rather than pushing this floor up past the h1
	   it is meant to be a smaller version of.

	   THE vw MIDDLE TERM IS 3.9, DERIVED NOT PICKED. At 3.9vw the clamp reaches its
	   34px floor below ~872px and its 48px ceiling above ~1231px. It is the same 3.9vw
	   the very first version of this ramp used at 32→48, which is not a coincidence: the
	   ceiling is the same 48px, so the curve that reaches it is the same curve. Change an
	   endpoint and re-derive this rather than leaving it, or the breakpoints drift
	   silently.

	   h5 IS THE SMALLEST THING THAT IS STILL A HEADING, at 18px — one step above body,
	   sentence case, and the bottom of the ramp proper. h6 sits below it ON body size
	   because it stepped out of the ramp and became a label. Read the six as five
	   heading sizes plus a label, not six headings. */
	--dfx-size-h1: clamp(2.125rem, 3.9vw, 3rem); /* 34 → 48px */
	--dfx-size-h2: 2rem;                     /* 32px */
	--dfx-size-h3: 1.5rem;                   /* 24px */
	--dfx-size-h4: 1.25rem;                  /* 20px — the section-heading step */
	--dfx-size-h5: 1.125rem;                 /* 18px — the smallest real heading */
	--dfx-size-h6: 1rem;                     /* 16px — body size; the LABEL step, see
	                                            the uppercase treatment in brand-base */

	/* One step ABOVE h1, for the opt-in .dfx-display headline. 52 → 64px, and this pair
	   is the easiest version of this token there has been to reason about.

	   THE INVARIANT IS NOW UNCONDITIONAL. Display's FLOOR (52px) exceeds h1's CEILING
	   (48px), so display is larger than h1 at every viewport width by construction —
	   there is no window to check and no crossover analysis to redo. Previous versions
	   overlapped the two clamps and needed an argument about which was flat while the
	   other scaled; this one needs none. Keep that property if either endpoint moves: as
	   long as the display floor stays above the h1 ceiling, the check is a comparison of
	   two numbers rather than a sweep across widths.

	   THE RANGE IS DELIBERATELY NARROW, 52 → 64 against h1's 34 → 48. Display is an
	   opt-in headline for one or two places, not a level, so it does not need to
	   collapse as far on a phone as an h1 that appears on every page.

	   5.2vw puts the ceiling above ~1231px, the same crossover as h1's, so the two reach
	   their maximum together. The floor is reached below ~1000px, earlier than h1's
	   ~872px, which is only a difference in where each stops scaling. */
	--dfx-size-display: clamp(3.25rem, 5.2vw, 4rem); /* 52 → 64px */

	/* --- The chrome-label size, DELIBERATELY OUTSIDE THE HEADING RAMP ---------
	   14px for labels that are furniture rather than document structure: the
	   eyebrow above a hero headline, the "Trusted by" separator, the footer widget
	   titles. Not a rung of the ramp and not meant to become one.

	   WHY THIS TOKEN EXISTS. There are two label jobs and only one of them belongs
	   to h6:

	     IN THE OUTLINE   Product group titles, feature-list sub-headings. Real
	                      h2/h3 elements doing structural work. 16px, from h6.
	     CHROME           Eyebrow, separator, footer widget titles, megamenu column
	                      heads. Not in the outline at all. 14px, from here.

	   Before this token the second group had no size of its own. The eyebrow
	   hardcoded 13px; the separator and footer titles read --dfx-size-h6, so when
	   h6 moved to 16px they followed it — by accident rather than by decision, and
	   they came out visibly loud. Three label sizes and nothing tying them.

	   14 RATHER THAN 13, and 13 is a live option. 0.875rem is a clean rem where
	   13px is 0.8125rem, and 14 keeps clear of the 12px caption. The eyebrow's old
	   13px was never argued for. To go back, set 0.8125rem here — one edit now
	   reaches all three, which is the whole point of the token.

	   IT DOES NOT CARRY THE INK. All three consumers share this size plus uppercase
	   and 0.14em tracking, and differ on color: the eyebrow is Brass because it is
	   a brand accent, the other two are muted because they are secondary. Color is
	   the axis that separates them, so it stays local.

	   SALIENT'S MEGAMENU COLUMN HEADS ARE THE FOURTH CONSUMER AND ARE NOT WIRED UP.
	   They render 11.2px from Salient's own h6 Theme Options fields. components.css
	   has long carried a note saying the fix, if it ever matters, is pointing both
	   at one value — this is that value, and it is a better target than h6 was. */
	/* 13px, DROPPED FROM 14px 2026-08-12 — the reduction this token was created to make
	   cheap. Dave said "14 for now, but I might reduce to 13 if I don't like it"; he
	   didn't like it, and the whole point of the token is that this is one edit.

	   IT REACHES FOUR CONSUMERS NOW, NOT THE THREE ORIGINALLY RECORDED: .dfx-eyebrow,
	   the "Trusted by" separator, the footer widget titles, and — added the same day —
	   the megamenu column heads. So the caps labels in the header and the footer move
	   together by construction, which is what was asked for.

	   IT IS NOW THE SAME VALUE AS --dfx-size-caption, AND STILL NOT THE SAME TOKEN. The
	   collision moved rather than went away: label used to share 14px with body-sm, now
	   it shares 13px with caption. Same reasoning as before — this one is uppercase, 600,
	   0.14em chrome; caption is sentence-case 400 running text. They are one edit away
	   from diverging again and merging them would couple two unrelated roles.

	   .dfx-hero__eyebrow IS NOT AFFECTED. Different component, reads --dfx-size-h4 at
	   20px, deliberately matched to a real h4. */
	--dfx-size-label: 0.8125rem;             /* 13px */

	/* --- The body-side scale, FOUR STEPS, NEW IN THIS PASS -------------------------
	   THE HEADING SCALE OWNED h1–h6 AND STOPPED, so everything below h4 was hand-set at
	   the point of use. An audit of all four CSS files found ELEVEN distinct values
	   between 11px and 16.8px:

	     11 · 12 · 12.8 · 13 · 13.6 · 14 · 14.4 · 15 · 15.2 · 16 · 16.8

	   None of them tokenised, roughly thirty declarations, and no two components
	   agreeing on what "small" meant. This is the actual reason the site read as
	   oversized: the working text that fills every card, list and table had drifted
	   upward one un-owned notch at a time, while the headings — which everybody looks at
	   when the complaint is "too big" — were already correct. h2 at 32px and body at 16px
	   match Notion's pricing page exactly; 15.2px feature-list items against their 14px
	   did not.

	   FOUR STEPS, AND THE ENDPOINTS ARE DELIBERATELY THEIRS: 16 / 14 / 13 / 12 against
	   Notion's 16 / 15 / 14 / 12. Ratios to body land on 1.0 / 0.875 / 0.8125 / 0.75.

	   WHY FOUR AND NOT SIX. The eleven values collapse into four ROLES, not eleven
	   intentions — prose, dense UI, annotation, fine print. 13.6 and 14.4 were never
	   chosen; they are 0.85rem and 0.9rem written by different hands on different days.

	   13px AND 14px ARE ONE PIXEL APART AND THAT IS THE RISK IN THIS BLOCK. It is the
	   same narrow-step problem the heading ramp has at h5→h4. They are distinguishable
	   here only because they differ in role and usually in ink too — body-sm is text you
	   read, caption is text you glance at. If they ever sit adjacent in the same
	   component at the same color, that pair is the thing to look at, and caption is
	   the one to move.

	   --dfx-size-label (14px, above) IS NOT THE SAME TOKEN AS --dfx-size-body-sm, though
	   they share a value today. Label is uppercase chrome with 0.14em tracking; body-sm
	   is sentence-case running text. They are one edit away from diverging — the
	   heading-scale notes already record that Dave may drop the label to 13px — and
	   merging them now would make that edit silently move card copy. */
	--dfx-size-body:    1rem;        /* 16px — prose, paragraph measure */
	--dfx-size-body-sm: 0.875rem;    /* 14px — cards, feature lists, nav. The workhorse. */
	--dfx-size-caption: 0.8125rem;   /* 13px — captions, meta, annotation */
	--dfx-size-micro:   0.75rem;     /* 12px — table cells, fine print, legal */

	/* UI LEADING, BECAUSE 1.6 IS A PROSE VALUE AND IT WAS ON EVERYTHING.
	   --dfx-line-height-body: 1.6 is correct for a paragraph at a 66ch measure and too
	   loose for a three-word list item in a card column, where it reads as a gap between
	   unrelated lines rather than a set. Notion runs 1.43 on card body and 1.33 on table
	   micro against the same 1.5 they use for prose. 1.45 here covers body-sm and
	   caption; micro takes 1.35 inline where it is used, being both smaller and shorter.

	   PAIR IT WITH THE SIZE, ALWAYS. Every hand-set size this replaces sat next to a
	   hand-set or inherited leading, and half the perceived weight of this change comes
	   from the leading rather than the size. A component that takes --dfx-size-body-sm
	   and leaves 1.6 behind has banked about half the fix. */
	--dfx-line-height-ui: 1.45;

	/* POSITIVE TRACKING AT 12px, THE ONLY PLACE THIS SCALE GOES POSITIVE.
	   Below about 13px the counters close up and letterfits that are correct at reading
	   size read as clumps; opening them is what keeps fine print legible rather than
	   merely small. Poppins is not in play here — this is body copy, so Inter — but
	   Inter's own optical guidance points the same way. Notion uses +0.01em at 12px on
	   their comparison table, which is the same call. */
	--dfx-letter-spacing-micro: 0.01em;

	/* --- Layout ------------------------------------------------------------ */
	/* Max width of the main content container — READ FROM SALIENT, NOT DECIDED HERE.
	   Theme Options › General Settings › Functionality › "Max Website Container
	   Width" owns the number, and its generated stylesheet authors BOTH the literal
	   `.container { max-width }` and this custom property, so consuming the property
	   tracks the option with nothing to keep in sync.

	   IT WAS A HARD 1280px, WHICH MADE THE THEME OPTION A DECOY. The reason was real
	   — Salient's slider floor is 1425px, so a narrower site cannot be set in the UI
	   at all — but the cost was that changing the field in Theme Options did nothing
	   to the content container, silently, with the override four files away. Anyone
	   reasonably concludes the setting is broken.

	   THE FALLBACK IS SALIENT'S OWN DEFAULT, not 1280. If the property ever goes
	   missing, matching what the parent theme would have done is the conservative
	   answer; 1280 would quietly reinstate the override this replaces.

	   To go narrower than 1425px again, set a length here instead of the var() — and
	   put a note in the Linear ticket, because the Theme Options field becomes inert
	   the moment you do. */
	--dfx-container-width: var(--container-width, 1425px);
}
