/* =========================================================================
   VEIL BASE — type scale, links, buttons, shared primitives (System §3, §6)
   Scope: applied inside .veil-scope wrappers OR page-concept containers so
   we never fight Blocksy globally. Patterns emit .veil-scope on their root.
   ========================================================================= */

/* =========================================================================
   PATTERN-FACING CONTRACT — READ BEFORE EDITING OR DELETING BELOW
   Verified 2026-08-17 against the live DOM and against the pattern library.

   The design-system classes in this file (.veil-scope, .veil-on-dark,
   .veil-dark, .veil-btn, .veil-btn-ghost, .veil-textlink) render ZERO times
   on most already-ported pages, because those pages carry raw ported markup
   that never adopted the contract. That makes them LOOK like dead code.
   They are not.

   inc/veil-design-system.php registers every file in the patterns directory
   as a live block pattern (106 of them). Of those, 17 emit .veil-scope,
   5 emit .veil-on-dark, 2 emit .veil-btn. Those patterns are the mechanism
   for porting the six pages still outstanding, so deleting these rules would
   leave each pattern unstyled at the moment of insertion — a breakage that
   would not surface until mid-port.

   Two rules follow from this:
   1. Do NOT delete these rules as dead code. They are unused-but-required.
   2. Do NOT write NEW sitewide rules against these classes expecting them to
      hit ported pages. Measure the rendered DOM first and target the classes
      that actually exist (vh-hero, veil-banner, hub-hero, brf-hero, dv2-cap,
      vw-card, sv5-card, hubcard, fsx-stats, veil-rcard, vb-tile). Three of
      the ten THEME-45 rules were caught as no-ops for exactly this reason.
   ========================================================================= */

.veil-scope {
	font-family: var(--veil-font-body);
	color: var(--veil-ink);
	font-size: var(--veil-body);
	line-height: var(--veil-body-lh);
}

/* ----- Headings: Source Serif 4 for display only ----- */
.veil-scope h1, .veil-scope h2 {
	font-family: var(--veil-font-display);
	font-weight: 700;
	color: var(--veil-ink);
	line-height: 1.1;
	margin: 0;
}
.veil-scope h1 { font-size: var(--veil-h1); line-height: 1.05; }
.veil-scope h2 { font-size: var(--veil-h2); }
.veil-scope h3 {
	font-family: var(--veil-font-display);
	font-weight: 700;
	font-size: var(--veil-h3);
	color: var(--veil-ink);
	margin: 0;
}
/* One italic accent word max per H1 (System §3) */
.veil-scope h1 em { font-style: italic; color: var(--veil-accent-em); }
.veil-on-dark h1, .veil-on-dark h2, .veil-on-dark h3 { color: #fff; }
.veil-on-dark h1 em { color: var(--veil-mist); }

/* ----- Eyebrow ----- */
.veil-eyebrow {
	display: block;
	font-family: var(--veil-font-body);
	font-weight: 800;
	font-size: var(--veil-eyebrow-size);
	letter-spacing: var(--veil-eyebrow-track);
	text-transform: uppercase;
	color: var(--veil-eyebrow-light);
}
.veil-on-dark .veil-eyebrow { color: var(--veil-mist); }

/* ----- Accent rule (56–60 × 3–4px) ----- */
.veil-rule { width: 56px; height: 3px; background: var(--veil-accent); border: 0; margin: 0 0 22px; }

/* ----- Links ----- */
.veil-scope a:not([class]) {
	color: var(--veil-link);
	text-decoration: underline;
	text-underline-offset: 2px;
}
/* WCAG-02 Lane A guard. --veil-link was darkened from #39a751 to #0a5c36 to
 * clear SC 1.4.3 on cream/white (A-4). #0a5c36 is only 2.28:1 on the dark
 * surface, so any bare link inside a dark section is re-pointed at
 * --veil-mist (9.45:1 on #071712) — the same colour .veil-textlink already
 * uses on dark. Additive: this selector did not exist before, so it cannot
 * take anything away. */
.veil-on-dark a:not([class]),
.veil-dark a:not([class]) { color: var(--veil-mist); }

/* ----- Buttons (System §6). High specificity by design — Blocksy styles bare <a>. ----- */
.veil-scope a.veil-btn,
.veil-scope a.veil-btn:visited {
	display: inline-block;
	background: var(--veil-accent);
	color: var(--veil-on-accent);
	font-family: var(--veil-font-body);
	font-weight: 800;
	font-size: 15px;
	padding: 15px 28px;
	border-radius: var(--veil-radius-btn);
	border: none;
	text-decoration: none;
	transition: transform var(--veil-ease), opacity var(--veil-ease);
	white-space: nowrap;
}
.veil-scope a.veil-btn:hover { transform: translateY(-1px); opacity: .93; }

.veil-scope a.veil-btn-ghost,
.veil-scope a.veil-btn-ghost:visited {
	display: inline-block;
	background: transparent;
	color: #eafff4;
	/* WCAG-02 Lane A · SC 1.4.11 (A-7). Was alpha .45, which composites over
	 * #071712 to rgb(61,103,83) = 2.86:1 — the button's only boundary, just
	 * under 3:1. Alpha .50 composites to rgb(67,112,90) = 3.25:1. Alpha is
	 * the only value changed; hue, width and radius are untouched. */
	border: 1.4px solid rgba(127, 201, 162, .50);
	font-weight: 800;
	font-size: 14.5px;
	padding: 13px 24px;
	border-radius: var(--veil-radius-btn);
	text-decoration: none;
	transition: border-color var(--veil-ease);
}
.veil-scope a.veil-btn-ghost:hover { border-color: var(--veil-mist); }

.veil-scope a.veil-textlink,
.veil-scope a.veil-textlink:visited {
	color: var(--veil-accent-deep);
	font-weight: 800;
	font-size: 14.5px;
	text-decoration: underline;
	background: transparent;
}
.veil-on-dark a.veil-textlink, .veil-on-dark a.veil-textlink:visited { color: var(--veil-mist); }

/* ----- Layout primitives ----- */
.veil-wrap { max-width: var(--veil-max); margin: 0 auto; padding: 0 var(--veil-gutter); }
.veil-wrap-narrow { max-width: 1180px; margin: 0 auto; padding: 0 var(--veil-gutter); }

/* Full-bleed section escape (System §10; prefer alignfull in templates where possible) */
.veil-bleed {
	width: 100vw !important;
	max-width: 100vw !important;
	margin-left: calc(50% - 50vw) !important;
	margin-right: 0 !important;
	overflow-x: hidden;
}

/* Dark section wrapper — THE soft-glow standard */
.veil-dark {
	background: var(--veil-dark-surface);
	color: #fff;
}
.veil-dark p { color: var(--veil-dark-body); }

/* ----- THEME-36: container for core "constrained" blocks (additive) -----
 * WordPress core's layout stylesheet never emits on this install — zero
 * wp-container-core-group-is-layout-*{...} rules are printed on any page
 * (same root cause as THEME-13, which found core column CSS missing). So a
 * block carrying is-layout-constrained gets NO max-width and NO auto margins.
 * Blocksy still widths it (--theme-block-width = min(100%, 90vw)), so such a
 * block renders flush to the LEFT edge at near-full viewport width — the
 * owner-reported bug on /compliance/ ("Compliance Support for Organizations
 * Under Pressure", "Frameworks Veil Group Supports") and on the FAQ block of
 * the 5 service dossiers.
 * Restore the standard Veil measure. Metrics are copied verbatim from the
 * existing Veil container `.veil-lc .in` (pages/dossier.css): max-width
 * 1200px, centered, 32px side padding — so a restored section is visually
 * identical to every other Veil section.
 * Scoped to DIRECT-child chains only, which is what keeps the exclusions
 * honest: nested constrained blocks (the split hero's dark form card, the
 * inner heading/body group inside each restored section) never match, so
 * nothing is inset twice. Full-bleed roots are excluded by class:
 * .alignfull (compliance/dossier split hero), .veil-bleed and .veil-banner
 * (dark full-bleed panels + CTA bands), .veil-scope (pattern roots that
 * already carry their own container). No existing rule is modified. */
.entry-content > .wp-block-group-is-layout-constrained:not(.alignfull):not(.veil-bleed):not(.veil-banner):not(.veil-scope),
.entry-content > .veil-scope > .wp-block-group-is-layout-constrained:not(.alignfull):not(.veil-bleed):not(.veil-banner) {
	max-width: 1200px;
	margin-inline: auto;
	padding-inline: 32px;
}

/* ----- THEME-37: restore the auto margins core never emits (additive) -----
 * Same missing stylesheet as THEME-36, one level up. Core's layout CSS for a
 * constrained container emits BOTH a measure and `margin-left/right: auto` on
 * its children. Here only Blocksy's half survives:
 *   main.min.css  .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull))
 *                 { width: var(--theme-block-width) }   =  min(100%, 90vw)
 * so EVERY direct child of .entry-content is widthed but never centred: at
 * 1440 it is a 1296px box hard against the left edge, leaving a 144px dead
 * strip down the right of the whole page (45px at 375). That is the sitewide
 * off-centre defect and, on any section that paints a background, a band that
 * visibly stops short of the right edge.
 * Rule 1 supplies only the missing half — margin-inline: auto — with core's
 * own exclusions, and is deliberately held at (0,1,0) specificity via :where()
 * so that every page-level full-bleed section (.veil-wv, .dv2-prob, .dv2-cta,
 * .dv2-proc, .brf-*, .vh-hero, .vq, .vc, .hub-hero, .ct, .st-page, .kb-page,
 * .sv5-page …) still wins with its own `margin-left: calc(50% - 50vw)`,
 * because pages/*.css is enqueued after base.css. Transparent sections keep a
 * centred inner container either way, so their text measure is unchanged —
 * only the surrounding box is re-centred. .entry-content is qualified with
 * :where(.is-layout-constrained) so the header/off-canvas .entry-content
 * (is-layout-flow) is never touched. */
.entry-content:where(.is-layout-constrained) > :where(:not(.alignleft):not(.alignright):not(.alignfull)) {
	margin-inline: auto;
}

/* Rule 2 — .veil-banner is the Veil full-bleed banner root and every instance
 * in reference/*.html carries align="full". The /compliance/ readiness CTA is
 * the one instance that lost that class when its section was ported through a
 * wp:html block, so it alone renders as a 1296px dark band with cream showing
 * past its right edge (claude/qa/phase6/evidence-cta-band-short-right-edge.png)
 * while its sibling dark bands bleed correctly. Restore the bleed with the
 * same 100vw technique already used by .veil-wv / .dv2-* / .brf-*; instances
 * that still carry .alignfull are excluded and keep their existing box. Its
 * 8% side padding is a percentage of the containing block, so it resolves
 * identically before and after. */
.entry-content > .veil-banner:not(.alignfull) {
	width: 100vw;
	max-width: 100vw;
	margin-inline: calc(50% - 50vw) 0;
}

/* =========================================================================
   WCAG-02 LANE A — SITEWIDE FOCUS INDICATOR (SC 2.4.7 + SC 1.4.11)
   Closes findings A-3 (1.4.11) and R-1 (2.4.7) together, because they are one
   root cause. Blocksy's parent bundle opens with
       *, *::before, *::after { outline: none; box-sizing: border-box }
   and then restores an indicator for ONLY a:focus-visible, button:focus-visible
   and checkbox/radio inputs, in --theme-palette-color-1 (#39A751). Two
   consequences, both measured at rendered pixels by WCAG-01b:
     1. Anything not on that restore list paints ZERO pixels on focus — 39 FAQ
        <summary> controls across 6 pages and the reCAPTCHA <iframe> on 7.
     2. What it does restore measures 2.96:1 on cream, 2.80:1 on sage,
        3.08:1 on white and 3.85:1 in the footer, against 3:1.
   This block restores an indicator for every interactive element TYPE rather
   than an enumerated few, and paints it in the two-tone ring defined in
   tokens.css (--veil-focus-ring / --veil-focus-ring-inner = the existing
   --veil-ink / --veil-mist, the same tokens FIX-01 uses).
   Geometry: box-shadow spread 2px paints the mist band 0-2px outside the
   border box; outline-offset 2px puts the 2px ink band at 2-4px. They tile
   exactly, so on a light surface the ink band carries the contrast (>=10.7:1)
   and on a dark surface the mist band does (>=6.08:1). Neither surface can
   lose the ring, including page-specific dark sections that carry no
   .veil-on-dark class (the home bento feature tile, the services situation
   bar, the dossier dark bands).
   Specificity is a deliberate (0,1,1) — the same as Blocksy's own rule — and
   the child theme is enqueued after the parent bundle, so this wins on order
   while every later, more specific rule (FIX-01's form rings in
   pages/remaining.css and pages/dossier.css, dossier.css's .fw-toggle ring)
   still wins over this. Nothing here uses !important.
   [tabindex="-1"] is excluded on purpose: <main id="main" tabindex="-1"> is
   the skip-link target and GF's validation summary is tabindex="-1"; both are
   programmatic focus destinations, not tab stops, and ringing the whole <main>
   would be a visual defect, not an improvement.
   iframe — MEASURED BEHAVIOUR, read this before "simplifying" these three
   selectors. Chromium never matches :focus or :focus-visible on an <iframe>
   host element; focus is delegated into the child browsing context. Two
   distinct states were measured on /about/contact/:
     (a) focus INSIDE the frame  -> :focus-within matches, and this rule paints
         (verified: mist + ink bands, 8.99:1 against the #0a1d15 panel, 3,120
         pixels changed in a scroll-locked diff). This is the state a working
         reCAPTCHA produces, because its checkbox takes focus.
     (b) a genuine Tab traverse on THIS build -> the iframe becomes
         document.activeElement but matches NONE of :focus, :focus-visible or
         :focus-within, so nothing paints and no CSS rule can make it. That is
         because the local reCAPTCHA keys are production-registered and return
         "Invalid domain" (tracked in DEFERRED.md), so the frame holds an error
         page with nothing focusable inside it.
   The 2.4.7 half of R-1 for the CAPTCHA therefore has to be re-verified at the
   Phase-7 cutover on the real domain. If an indicator is ever required at that
   exact tab stop regardless, it needs JS or a focusable wrapper element — not
   a stylesheet change.
   ========================================================================= */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
details:focus-visible,
label:focus-visible,
area[href]:focus-visible,
object:focus-visible,
embed:focus-visible,
audio[controls]:focus-visible,
video[controls]:focus-visible,
[contenteditable]:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible,
iframe:focus,
iframe:focus-visible,
iframe:focus-within {
	outline: 2px solid var(--veil-focus-ring);
	outline-offset: 2px;
	box-shadow: 0 0 0 2px var(--veil-focus-ring-inner);
}

/* Native form controls get the outline half only. Gravity Forms controls
 * already carry FIX-01's verified 8.99:1 / 8.43:1 mist ring from
 * pages/remaining.css and pages/dossier.css, which is more specific and loads
 * later, so it still wins outright; withholding the box-shadow here means
 * this block cannot thicken, recolour or otherwise disturb that ring. Non-GF
 * inputs (Blocksy's search field) sit on light chrome, where the ink band is
 * >=10.7:1 on its own. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
	outline: 2px solid var(--veil-focus-ring);
	outline-offset: 2px;
}

/* Same protection for the two Gravity Forms controls that are NOT inputs —
 * the submit <button> and any anchor inside the form wrapper. Without this
 * they would pick up the mist box-shadow above and, since FIX-01's outline is
 * also mist, render as a 4px mist ring instead of the approved 2px one. */
.gform_wrapper button:focus-visible,
.gform_wrapper a:focus-visible {
	box-shadow: none;
}

/* =========================================================================
   WCAG-04 · SC 1.4.3 — Blocksy's own link colour, scoped to light surfaces
   -------------------------------------------------------------------------
   Blocksy's parent bundle paints every unclassed anchor with
   `a { color: var(--theme-link-initial-color) }`, and the Customizer palette
   sets that variable to #39A751 on `:root`. Measured at RENDERED pixels
   (full-page screenshot, modal painted background inside the anchor's own
   client rect, 1440x900, dsf 1):

       #39a751 on cream #fffaf1 .... 2.96:1   x36 nodes,  7 pages
       #39a751 on white #ffffff .... 3.08:1   x 3 nodes,  3 pages
       hover #2f9946 on cream ...... 3.42:1   (same anchors, :hover)

   All are body copy at 15-16.5px / 400, so the threshold is 4.5:1.

   WHY THIS IS SCOPED AND NOT A :root SWAP
   ---------------------------------------
   WCAG-02 Lane A flagged, correctly, that the SAME variable serves anchors
   that sit on the dark surface, where #0a5c36 measures only 2.28:1 — a
   global swap would trade 39 light failures for a worse set of dark ones.
   So the variable is redefined on `body`, not `:root` (custom properties
   inherit; the nearest ancestor declaration wins for every descendant),
   and every dark container that actually holds such an anchor puts it back
   to --veil-mist.

   The dark list is not guessed. A probe enumerated all 163 anchors that
   compute rgb(57,167,81) sitewide and measured each one's painted
   background: 127 land on light surfaces, 36 on dark, and those 36 live in
   exactly three places — the header CTA (.veil-hdr-contact__list, 24, all
   icon-only with no text), .veil-banner (11) and .dv2-cta (1). The rest of
   the list below is the remaining Veil dark-section roots, included so a
   future page that puts a bare link in one is covered pre-emptively.

   Blast radius is deliberately small: --theme-link-initial-color feeds
   nothing but link colour, and Blocksy already re-declares it locally for
   the site title, the main menu, the sub-menu, the mobile menu and the
   sidebar — all of which are deeper than `body` and therefore untouched.

   Measured after: cream 7.78:1, white 8.09:1, sage 7.35:1; hover
   --veil-ink 11.40 / 11.85 / 10.77. No new brand colour — --veil-link is
   the existing --veil-accent-deep value and --veil-ink is the body-copy ink.
   ========================================================================= */
body {
	--theme-link-initial-color: var(--veil-link);
	--theme-link-hover-color: var(--veil-ink);
}

/* Dark-surface guard — keep the light link colour where the canvas is dark. */
.veil-on-dark,
.veil-dark,
.veil-banner,
.dv2-cta,
.veil-hdr-contact__list,
.veil-wv,
.brf-hero,
.hub-hero,
.ct,
.vh-hero,
.vq,
.vc,
.sv-sit,
.sv5-cta,
.sv-p-dark,
.sv5-card.dark,
.kb-dcard {
	--theme-link-initial-color: var(--veil-mist);
	--theme-link-hover-color: var(--veil-mist);
}

/* =========================================================================
   THEME-41 — RESTORE THE FULL-BLEED ESCAPE (2026-08-17, consistency audit D-1)

   THEME-36/37 above were written for an install where WordPress core's layout
   stylesheet did NOT emit. It emits now. Core prints, inline in <head>:

     .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)) {
        max-width: var(--wp--style--global--content-size);   [resolves to 1290px]
        margin-left: auto !important;
        margin-right: auto !important;
     }

   The `!important` on the margins beats every page-level bleed rule (which are
   all un-flagged), and the max-width clamps their `width:100vw`. Measured live
   at 1440 before this fix: .vh-hero / .hub-hero / .brf-hero / .ct all computed
   x=75, width=1290, max-width=1290px — i.e. a 75px cream margin down both
   sides of bands the author explicitly asked to bleed.

   Sections that still bled were only those that escaped by force: .alignfull
   (excluded by core's own :where() negation) or their own !important
   (.sv5-page, .st-page, .kb-page, .veil-bleed). That is why /services/ ran
   edge-to-edge while / and /industries-served/ stopped short — the owner-
   reported symptom. It is desktop-only: below 1291px, 100vw < the clamp and
   the rule is inert, which is why it survived every prior review.

   Fix: re-assert the escape for every selector that already declares the
   bleed intent in pages/*.css. Each class is doubled (.x.x) purely to raise
   specificity to (0,2,0) so it outranks core's (0,1,0) rule irrespective of
   stylesheet order — inline <head> styles otherwise win ties on position.
   No new bleed is introduced: this list is exactly the set of selectors that
   already carry `margin-left: calc(50% - 50vw)` and were being defeated.
   margin-right:0 matches the proven .veil-bleed pattern above.

   Horizontal-overflow note: 100vw is ~15px wider than the client width on a
   real browser with a classic scrollbar. Verified in a headed browser that an
   ancestor computes overflow-x: clip and absorbs it — page overflow measured
   0 before and after this rule. Independently, 170 page/width combinations
   measured zero overflow. Not reproducible above 1290px on a 1265px machine;
   confirm once on a wide monitor.

   2026-08-17 addendum: .gva-light joins the list. On /governments/ the
   .gva-ans cards alternated 436 / 385px because their grandparent .brf-wrap
   alternated 1024 / 922px — the dark band bled while the light one sat at
   Blocksy's 88vw block width. The column gaps were identical (72px), so the
   audit's "mismatched gap" hypothesis was wrong and equalising them changed
   nothing; the container was the cause. Same light/dark inset zigzag as D-10,
   same rule resolves it.
   ========================================================================= */
.vh-hero.vh-hero,
.vh-marq.vh-marq,
.vq.vq,
.vc.vc,
.hub-hero.hub-hero,
.ct.ct,
.brf-hero.brf-hero,
.brf-svc.brf-svc,
.brf-press.brf-press,
.brf-cta.brf-cta,
.fsx-svc.fsx-svc,
.inx-svc.inx-svc,
.gva-dark.gva-dark,
.gva-light.gva-light,
.veil-wv.veil-wv,
.dv2-proc.dv2-proc,
.dv2-prob.dv2-prob,
.dv2-cta.dv2-cta {
	width: 100vw !important;
	max-width: 100vw !important;
	margin-left: calc(50% - 50vw) !important;
	margin-right: 0 !important;
}

/* =========================================================================
   THEME-42 — HOVER FEEDBACK ON VEIL BUTTONS AND GHOST LINKS
   (2026-08-17, consistency audit D-3)

   base.css:82 defines the Design System hover (translateY(-1px), opacity .93)
   on `.veil-btn`. Audit finding: NO production button carries that class, so
   the rule has never matched anything and every Veil-authored button and ghost
   link is completely inert under the cursor. The only elements that responded
   to hover sitewide were Blocksy nav/footer links and the two card components
   that define their own (.veil-rcard, .vb-tile).

   This applies the same DS motion to the classes actually shipped. Transform
   and opacity only, so it cannot fight the colour declarations (many of which
   are !important) and honours the DS motion budget (<=250ms, opacity/transform
   only). prefers-reduced-motion drops the translate and keeps the opacity cue.
   ========================================================================= */
.vh-b1, .vh-b2, .vw a.b1, .vc a.b1, .dv2-btn, .kb-cta,
.sv-sit a, .sv5-card a.go, .vb-all, .veil-rall,
.brf-card a, .hubcard a, .brf-cta a, .dv2-cta a, .sv5-cta a {
	transition: transform .18s ease, opacity .18s ease;
}
.vh-b1:hover, .vh-b2:hover, .vw a.b1:hover, .vc a.b1:hover,
.dv2-btn:hover, .kb-cta:hover, .sv5-cta a:hover,
.brf-cta a:hover, .dv2-cta a:hover {
	transform: translateY(-1px);
	opacity: .93;
}
.sv5-card a.go:hover, .vb-all:hover, .veil-rall:hover,
.brf-card a:hover, .hubcard a:hover {
	opacity: .78;
}
@media (prefers-reduced-motion: reduce) {
	.vh-b1:hover, .vh-b2:hover, .vw a.b1:hover, .vc a.b1:hover,
	.dv2-btn:hover, .kb-cta:hover, .sv5-cta a:hover,
	.brf-cta a:hover, .dv2-cta a:hover { transform: none; }
}

/* =========================================================================
   THEME-43 — TAP TARGETS ON GHOST / "EXPLORE" LINKS (WCAG 2.2 SC 2.5.8)
   (2026-08-17, consistency audit D-5)

   22 standalone links measured 18-21.4px tall at 390px, stacked with under
   24px spacing, so neither the Spacing nor the Inline exception applies:
   5 on /insurance/ (.inx-node, display:inline, 18px), 3 on /governments/
   (.gva-ans, inline, 18px), 3 on /financial-services/ (.fsx-cb, block,
   21.4px), 5 on /law-firms/ (.brf-card, block, 21.4px), 5 `.go` on
   /services/ (23.1px), 4 on /industries-served/ and 2 on /about/
   (.hubcard, 23.1px).

   First attempt used padding-block with a compensating negative margin. That
   is layout-neutral for INLINE anchors but not for BLOCK ones, and measurement
   showed 4-5 links shifting on every block-context page — so it was rejected.
   This uses the transparent ::after band already proven for the footer nav
   (B-24b, footer.css:390-402): an absolutely-positioned child of the anchor
   enlarges the POINTER target while contributing nothing to layout, at any
   display value. Height is pinned to 24px and centred on the link box.

   The band is inset 0 horizontally so it never widens past the link text, and
   these anchors carry no other ::after (their arrows are literal characters in
   the content), so nothing is overwritten.
   ========================================================================= */
.inx-node a, .gva-ans a, .fsx-cb a, .brf-card a,
.hubcard a, .sv5-card a.go, .veil-rall {
	position: relative;
}
.inx-node a::after, .gva-ans a::after, .fsx-cb a::after, .brf-card a::after,
.hubcard a::after, .sv5-card a.go::after, .veil-rall::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	top: 50%;
	height: 24px;
	transform: translateY(-50%);
}

/* =========================================================================
   THEME-45 (2026-08-17) — CRAFT POLISH (Bucket A of DESIGN-CRITIQUE.md)
   Five parallel audits found the theme's tokens exist but are bypassed, and
   that several baseline interaction affordances were simply never authored.
   Everything in this block is additive and layout-neutral: no box changes
   size or moves. A10 (optical tracking) is the one item with visible effect
   and is verified by screenshot before deploy.
   ========================================================================= */

/* --- A1. ::selection. Was undefined, so selecting text on a deep-green
   site painted the browser's default blue. --- */
::selection { background: var(--veil-accent); color: var(--veil-on-accent); }
/* Dark contexts. NOTE: .veil-on-dark / .veil-dark render ZERO times sitewide
 * (measured on 6 pages, 2026-08-17) — the ported page markup never carries
 * them, so the architecture's dark-context hook is dead. Selectors below are
 * the roots that actually exist in the DOM. The two legacy classes are kept
 * at the end so any future pattern that does use them still works. */
.vh-hero ::selection, .veil-banner ::selection, .hub-hero ::selection,
.brf-hero ::selection, .brf-cta ::selection, .dv2-cta ::selection,
.vc ::selection, .veil-footer ::selection,
.veil-on-dark ::selection, .veil-dark ::selection {
	background: var(--veil-mist);
	color: var(--veil-black-green);
}

/* --- A2. :active press states. Zero existed sitewide — every CTA lifted on
   hover and then did nothing when pressed, which reads as unwired. Returns
   the element to rest and shortens the transition so the press feels
   mechanical rather than animated. --- */
.veil-scope a.veil-btn:active,
.vh-b1:active, .vh-b2:active, .vw a.b1:active, .vc a.b1:active,
.dv2-btn:active, .kb-cta:active, .sv5-cta a:active,
.brf-cta a:active, .dv2-cta a:active {
	transform: translateY(0);
	opacity: .88;
	transition-duration: 60ms;
}

/* --- A6. Native control theming. Checkboxes and radios painted OS blue on
   the dark-green contact form. --- */
:root { accent-color: var(--veil-accent); }
.ct-form { accent-color: var(--veil-mist); caret-color: var(--veil-mist); }

/* --- A8. Underline weight. text-decoration-thickness was never set, so bare
   links inherited the body face's default and varied by weight. --- */
.veil-scope a:not([class]) { text-decoration-thickness: 1px; }
.veil-scope a:not([class]):hover { text-decoration-thickness: 2px; }

/* --- A10. Optical tracking on display type. Exactly one of ~14 H1 rules had
   any (pages/home.css:8). A serif at 40-70px with default tracking reads as
   "web font at a big size" rather than as set type. Negative tracking only
   above the size where it is warranted; H3 and below are left alone.
   Scoped to .entry-content, NOT .veil-scope: measurement on 6 live pages
   showed .veil-scope renders 0 times on 5 of them (1 on /services/data-mining/,
   the FAQ), so a .veil-scope rule would have been almost entirely dead. The
   footer's 16px nav <h2>s sit outside .entry-content and are verified
   unaffected — negative tracking on small uppercase labels would be wrong.
   Verified layout-neutral: 0 height change on home/datamining/about/finserv. */
.entry-content h1 { letter-spacing: -.015em; }
.entry-content h2 { letter-spacing: -.008em; }

@media (prefers-reduced-motion: reduce) {
	.veil-scope a.veil-btn:active,
	.vh-b1:active, .vh-b2:active, .vw a.b1:active, .vc a.b1:active,
	.dv2-btn:active, .kb-cta:active, .sv5-cta a:active,
	.brf-cta a:active, .dv2-cta a:active { transform: none; }
}

/* =========================================================================
   THEME-47 (2026-08-17) — ACTIVATE THE METRIC-MATCHED FALLBACK FACES
   inc/veil-design-system.php preloads three woff2 and declares six
   metric-matched @font-face fallbacks (size-adjust / ascent-override /
   descent-override) to kill swap-shift. None of it was reaching the page:
   Blocksy's main.min.css declares `body, h1..h6, ... { font-family: Nunito,
   sans-serif }` from its customizer option at specificity (0,0,1), tying
   base.css's own `body` rule and winning on load order. Measured before this
   block: body computed `Nunito, sans-serif` — the raw stack, with no
   'Veil Sans Fallback' in it, so the overrides were inert for every element
   that inherited from body.

   `html body` / `html :is(h1..h6)` is (0,0,2) and (0,0,2), so it outranks the
   parent theme's (0,0,1) irrespective of enqueue order. Purely a fallback-
   chain change: the primary family is unchanged, so once Nunito and Source
   Serif 4 load the rendering is byte-identical — the fallbacks only apply
   during the swap window, which is exactly the CLS this was built to fix.
   ========================================================================= */
html body { font-family: var(--veil-font-body); }
html :is(h1, h2, h3, h4, h5, h6) { font-family: var(--veil-font-display); }
