/* ============================================================================
   ELIVTECH — Design tokens (single source of truth)
   Typography (§1), spacing and density (§6), and surfaces (§7) — §7.1 asks for
   the surfaces to be defined alongside the type tokens, so one file owns all
   three. The type sections keep the name the brief gives them.
   ----------------------------------------------------------------------------
   Brief §1.1: every type decision on the site resolves through a custom property
   declared in this file. No other stylesheet may declare a raw font-family,
   font-size, font-weight, line-height or letter-spacing. `docs/check-typography.sh`
   enforces it; `docs/typography-guideline.md` records where every value came from.

   Brief §A: nothing here is invented. Every value below is one the Sandbox theme
   already establishes in style.css; this file only names it, consolidates the
   near-duplicates, and interpolates between the theme's own small-screen and
   large-screen endpoints. The right-hand comment on each token is its provenance.

   Load order: last, so these rules win over the theme's. Custom properties do not
   need to be declared before use, so custom.css / content-enhance.css /
   ck-content-style.css consume these tokens freely despite loading earlier.
   ========================================================================== */

:root {
  /* --- Families (§1.2: exactly one family; the theme ships one text face) ---
     Manrope is the only text face the theme loads and the only one that renders.
     Heading and body are the same family, differentiated by weight, size and
     letter-spacing tokens alone — never by a second typeface.
     The fallback stack is the theme's own `--bs-font-sans-serif` ("Manrope",
     sans-serif) extended with the system UI faces the theme already declares at
     style.css:33639, because a bare `sans-serif` fallback is thin. */
  /* "Manrope Fallback" is the metric-matched Arial defined in fonts.css: it
     occupies close to the same space as Manrope, so the swap when the real
     face arrives does not move the layout. */
  --font-body: "Manrope", "Manrope Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
  --font-heading: var(--font-body);

  /* Code only. This is the theme's own `--bs-font-monospace` stack — entirely
     system faces, so it downloads nothing and does not count against the §1.2
     two-family limit any more than Bootstrap Icons does. 38 CMS content rows
     render <code>/<pre>, so the role is real and cannot simply be dropped. */
  --font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
               "Courier New", monospace;

  /* --- Weights (§1.2: pruned to the roles actually rendered) ---------------
     The Google Fonts request loads Manrope 400;500;700 and style.css resolves to
     exactly those three. All three render on the finished site; there is no
     fourth. 600 is deliberately absent — see `.fw-semibold` below. */
  --fw-regular: 400;   /* style.css: body copy fallback, muted labels */
  --fw-medium: 500;    /* style.css:199 --bs-body-font-weight */
  --fw-bold: 700;      /* style.css:23927 all headings, .display-*, .lead emphasis */

  /* --- Size scale (§1.3: fluid, theme endpoints, only the interpolation new) --
     Each token is clamp(low, theme-formula, high) where:
       low   = the theme's own computed value at the narrowest verification
               width (320px), so nothing gets larger on small screens;
       mid   = the theme's own calc() formula, unchanged;
       high  = the theme's own ≥1200px value, so nothing gets larger on wide ones.
     Where the theme is already fixed across breakpoints the token is fixed too.
     All values are rem against the root, so browser zoom and the reader's own
     text-size setting scale the entire system (§1.3). */
  --fs-display-1: clamp(1.586rem, calc(1.365rem + 1.38vw), 2.4rem);  /* style.css:599  32→48px */
  --fs-display-2: clamp(1.527rem, calc(1.345rem + 1.14vw), 2.2rem);  /* style.css:609  31→44px */
  --fs-display-3: clamp(1.469rem, calc(1.325rem + 0.9vw),  2rem);    /* style.css:619  29→40px */
  --fs-display-4: clamp(1.411rem, calc(1.305rem + 0.66vw), 1.8rem);  /* style.css:629  28→36px */
  --fs-display-5: clamp(1.352rem, calc(1.285rem + 0.42vw), 1.6rem);  /* style.css:639  27→32px */
  --fs-display-6: clamp(1.294rem, calc(1.265rem + 0.18vw), 1.4rem);  /* style.css:649  26→28px */

  --fs-h1: clamp(1.308rem, calc(1.27rem + 0.24vw),  1.45rem);        /* style.css:293  26→29px */
  --fs-h2: clamp(1.265rem, calc(1.255rem + 0.06vw), 1.3rem);         /* style.css:303  25→26px */
  --fs-h3: 1.1rem;                                                    /* style.css:313  22px */
  --fs-h4: 0.95rem;                                                   /* style.css:317  19px */
  --fs-h5: 0.9rem;                                                    /* style.css:321  18px */
  --fs-h6: 0.85rem;                                                   /* style.css:325  17px */

  --fs-lead: 0.9rem;   /* style.css:596  .lead — 18px */
  --fs-lg:   1rem;     /* style.css:26328 .fs-lg / .fs-20 — 20px */
  --fs-body: 0.8rem;   /* style.css:198  --bs-body-font-size — 16px, the floor */
  --fs-sm:   0.75rem;  /* style.css .fs-15 — 15px, the eyebrow/meta size in use */
  --fs-xs:   0.7rem;   /* style.css:26325 .fs-sm / .fs-14 — 14px, smallest in use */

  /* The one very large figure the theme uses, for the 404 numeral and the
     contact page's display figure. Same clamp treatment. */
  --fs-figure: clamp(2.199rem, calc(1.575rem + 3.9vw), 4.5rem);      /* style.css .fs-90 */

  /* --- Line height (§1.3: a token per role) --------------------------------
     Consolidated from the theme's own ramp (1.2 / 1.25 / 1.3 / 1.35 / 1.4 /
     1.45 / 1.5 / 1.55 / 1.7) toward the four values it genuinely needs. Heading
     line-height tightens as size grows; body never drops below the theme's 1.7. */
  --lh-display: 1.2;      /* style.css:23981 .display-1 */
  --lh-heading: 1.3;      /* style.css:23957 h1, :23987 .display-4/5 */
  --lh-subheading: 1.5;   /* style.css:23974 h5 — for h4/h5/h6 at near-body size */
  --lh-body: 1.7;         /* style.css:200 --bs-body-line-height — the floor (§6.5) */
  --lh-none: 1;           /* style.css — icons, badges, single-line controls */

  /* --- Letter spacing ------------------------------------------------------ */
  --ls-heading: -0.01rem; /* style.css:23941 + .ls-md */
  --ls-eyebrow: 0.02rem;  /* style.css:23955 .text-uppercase headings + .ls-lg */
  --ls-normal: normal;

  /* --- Word spacing (the theme sets it globally; kept as a token) ---------- */
  --ws-body: 0.05rem;     /* style.css:23570 body */
  --ws-heading: 0.1rem;   /* style.css:23940 headings */

  /* --- Measure (§1.3: capped in ch so it follows the type token) -----------
     70ch at the body token is ~570px, which is exactly the theme's own
     `.col-lg-6` prose width inside a 1140px container — the widest line the
     theme itself sets a paragraph on. Not an invented number: the theme's
     half-column, restated so it follows the type rather than a pixel width. */
  --measure: 70ch;
  --measure-narrow: 56ch;

  /* --- Icon sizing (§3.2: a token, never a per-instance decision) ----------
     Every icon role renders at the same size on every page, and each size is
     proportioned to the heading token it sits beside so the glyph's optical
     weight matches its text. All three are fluid because the type they pair
     with is fluid. `--icon-inline` is relative on purpose: it belongs to the
     text it sits inside (a link arrow, a bullet), not to the page. */
  --icon-sm: var(--fs-display-5);   /* pairs with h3/h4 — 27→32px */
  --icon-md: var(--fs-display-3);   /* pairs with a card heading — 29→40px */
  --icon-lg: var(--fs-display-1);   /* pairs with a section heading — 32→48px */
  --icon-inline: 1.2em;             /* runs inside a line of text */

  /* One gradient definition, one angle, reused on every icon glyph on the site
     (§3.2). Both stops are theme colours: --bs-primary and the emphasis variant
     of the same hue. The fallback is the darker stop, which is the value that
     passes AA on both light surfaces. */
  --icon-gradient: linear-gradient(135deg, var(--bs-primary) 0%, var(--bs-primary-text-emphasis) 100%);
  --icon-gradient-fallback: var(--bs-primary-text-emphasis);

  /* --- Numerals (§1.2) ----------------------------------------------------
     Manrope ships tabular figures. Applied as a token via `.numeric`, never
     per instance, wherever figures sit in aligned sets. */
  --fvn-tabular: "tnum" 1, "lnum" 1;

  /* --- Bind the theme's own Bootstrap entry points to the tokens -----------
     These four custom properties are what style.css itself reads for body copy,
     buttons, badges, tooltips, form controls and every other component, so
     rebinding them here propagates the token system through the whole theme
     without editing a single theme rule. */
  --bs-font-sans-serif: var(--font-body);
  --bs-font-monospace: var(--font-mono);
  --bs-body-font-family: var(--font-body);
  --bs-body-font-size: var(--fs-body);
  --bs-body-font-weight: var(--fw-medium);
  --bs-body-line-height: var(--lh-body);
}

/* ---------------------------------------------------------------------------
   Root sizing (§1.3)
   The theme shipped `--bs-root-font-size: 20px`, which pins the root and
   silently discards the reader's own browser text-size setting. 125% of the
   browser default resolves to exactly the same 20px for a default reader — so
   every rem value in the theme's 39,564 lines renders identically — but it now
   scales with the reader instead of overriding them. See audit §11 D1.
   The declaration lives here rather than in style.css so the token file owns it.
   --------------------------------------------------------------------------- */
:root {
  --fs-root: 125%;
  font-size: var(--fs-root);
}

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  line-height: var(--lh-body);
  word-spacing: var(--ws-body);
}

/* ---------------------------------------------------------------------------
   Headings (§1.3)
   Semantic order is enforced in markup, never by picking a heading for its size.
   Where heading-sized text carries no heading meaning, use a .display-* token
   class on a non-heading element.
   --------------------------------------------------------------------------- */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-heading);
  word-spacing: var(--ws-heading);
}

h1, .h1 { font-size: var(--fs-h1); line-height: var(--lh-heading); }
h2, .h2 { font-size: var(--fs-h2); line-height: var(--lh-heading); }
h3, .h3 { font-size: var(--fs-h3); line-height: var(--lh-heading); }
h4, .h4 { font-size: var(--fs-h4); line-height: var(--lh-subheading); }
h5, .h5 { font-size: var(--fs-h5); line-height: var(--lh-subheading); }
h6, .h6 { font-size: var(--fs-h6); line-height: var(--lh-subheading); }

.display-1 { font-size: var(--fs-display-1); line-height: var(--lh-display); }
.display-2 { font-size: var(--fs-display-2); line-height: var(--lh-display); }
.display-3 { font-size: var(--fs-display-3); line-height: var(--lh-display); }
.display-4 { font-size: var(--fs-display-4); line-height: var(--lh-heading); }
.display-5 { font-size: var(--fs-display-5); line-height: var(--lh-heading); }
.display-6 { font-size: var(--fs-display-6); line-height: var(--lh-heading); }

.display-1, .display-2, .display-3,
.display-4, .display-5, .display-6 {
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-heading);
}

/* Uppercase is permitted on the eyebrow token only (§1.3). */
.text-uppercase { letter-spacing: var(--ls-eyebrow); }
.text-muted { color: var(--ink-muted) !important; }

/* ---------------------------------------------------------------------------
   Body-level roles
   --------------------------------------------------------------------------- */
.lead { font-size: var(--fs-lead); font-weight: var(--fw-medium); line-height: var(--lh-body); }
p, li, dd, dt, blockquote, figcaption, label, td, th { font-family: inherit; }

/* The eight size utilities the site actually uses, restated as tokens. The
   theme's other 192 `.fs-*` rules were dead CSS and are removed from style.css
   (§1.4). `.fs-lg` and `.fs-sm` keep their theme names; the numeric aliases stay
   so existing markup and any CMS-stored HTML keeps working (§0.4). */
.fs-xs, .fs-14, .fs-sm { font-size: var(--fs-xs) !important; }
.fs-15                  { font-size: var(--fs-sm) !important; }
.fs-16                  { font-size: var(--fs-body) !important; }
.fs-17                  { font-size: var(--fs-h6) !important; }
.fs-19                  { font-size: var(--fs-h4) !important; }
.fs-lg, .fs-20          { font-size: var(--fs-lg) !important; }
.fs-28                  { font-size: var(--fs-display-6) !important; }
.fs-90                  { font-size: var(--fs-figure) !important; }

/* Weight utilities. `.fw-semibold` is Bootstrap's 600, but Manrope 600 is not in
   the load, so the browser would synthesise it — §1.2 forbids faux bold. It
   resolves to the real 700 instead. (§1.2 also allows adding 600 to the request;
   we do not, because no design role needs a fourth weight.) */
.fw-light, .fw-normal    { font-weight: var(--fw-regular) !important; }
.fw-medium               { font-weight: var(--fw-medium) !important; }
.fw-semibold, .fw-bold,
.fw-bolder               { font-weight: var(--fw-bold) !important; }

/* Line-height utilities */
/* Line-height utilities. .lh-1 and .lh-base map exactly onto theme values
   (1 and 1.7); .lh-sm maps exactly onto --lh-subheading (1.5). The theme's
   .lh-lg (1.9) is unused site-wide and consolidates onto the body token
   rather than becoming a fifth line-height. */
.lh-1      { line-height: var(--lh-none) !important; }
.lh-sm     { line-height: var(--lh-subheading) !important; }
.lh-base   { line-height: var(--lh-body) !important; }
.lh-lg     { line-height: var(--lh-body) !important; }

/* Letter-spacing utilities, remapped onto the two spacing roles the system has.
   The theme shipped five (.ls-xs … .ls-xl); they collapse to heading and
   eyebrow, which are the only two the finished site renders. */
.ls-xs, .ls-sm, .ls-md      { letter-spacing: var(--ls-heading) !important; }
.ls-lg, .ls-xl              { letter-spacing: var(--ls-eyebrow) !important; }
.ls-normal                  { letter-spacing: var(--ls-normal) !important; }

/* ---------------------------------------------------------------------------
   Shared type roles used across components
   --------------------------------------------------------------------------- */

/* Eyebrow — the only uppercase text on the site (§1.3). */
.elx-eyebrow,
.eyebrow {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  line-height: var(--lh-subheading);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
}

/* Aligned figures — metric rails, tables, position indicators (§1.2). */
.numeric,
.elx-stat__num,
.elx-table td,
.elx-table th {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: var(--fvn-tabular);
}

/* Prose measure cap (§1.3, §6.4). Applied to prose containers deliberately —
   never to a centred column that would then sit beside emptiness. */
.measure        { max-width: var(--measure); }
.measure-narrow { max-width: var(--measure-narrow); }

/* ---------------------------------------------------------------------------
   Emphasis without italic (§1.2 "no faux styling")
   Manrope is a weight-axis-only family — Google Fonts ships no italic for it,
   and adding a second face to get one would break the one-family rule. So the
   site never asks for a slant it cannot render: <em>, <cite> and .fst-italic
   carry emphasis through weight, which Manrope does have. This covers the 8 CMS
   content rows that use <em> without editing them; the quote component's own
   `font-style: italic` was removed in content-enhance.css.
   --------------------------------------------------------------------------- */
em, cite, .fst-italic {
  font-style: normal;
  font-weight: var(--fw-bold);
}

/* No faux styling anywhere (§1.2). */
* {
  -webkit-text-stroke: 0;
  font-synthesis: none;
}

/* ===========================================================================
   SPACING AND DENSITY (§6)
   ---------------------------------------------------------------------------
   The theme already ships an ordered spacing scale (its .p-1 … .p-24 utilities).
   §6.1 promotes it to tokens rather than inventing one, and retires the top of
   it: steps 14–24 (90px–400px) are reserved for nothing and no longer appear on
   any section. Body sections use the middle of the scale.
   =========================================================================== */

:root {
  --space-1:  0.25rem;  /*  5px — theme .p-1  */
  --space-2:  0.5rem;   /* 10px — theme .p-2  */
  --space-3:  0.75rem;  /* 15px — theme .p-3  */
  --space-4:  1rem;     /* 20px — theme .p-4  */
  --space-5:  1.25rem;  /* 25px — theme .p-5  */
  --space-6:  1.5rem;   /* 30px — theme .p-6  */
  --space-7:  1.75rem;  /* 35px — theme .p-7  */
  --space-8:  2rem;     /* 40px — theme .p-8  */
  --space-10: 2.5rem;   /* 50px — theme .p-10 */
  --space-12: 3.5rem;   /* 70px — theme .p-12 */

  /* --- Section rhythm (§6.1, §6.2) --------------------------------------
     One convention owns section vertical padding site-wide: it sits on the
     section wrapper, and the container inside contributes none. The seam
     between two sections is therefore 2 x this value.

     Audited baseline was `py-7 py-md-9` on the inner container — 35px mobile
     and 45px desktop per side, so a 70px / 90px seam, doubled at every join.
     §6.1 says move down one step and review, then a further step. First pass
     took 7 -> 5 on mobile and 9 -> 7 on desktop; the measurement showed the
     page still reading airy, so both come down one further step to 4 and 6.
     Growth from mobile to desktop is two steps, the §6.1 cap, and stops there —
     no further growth at lg, xl or above.
     Seam: 40px mobile / 60px desktop, against an audited 70px / 90px. */
  --space-section: var(--space-4);      /* 20px per side -> 40px seam */

  /* The two moments per page allowed the top of the retained scale (§6.1):
     the hero and the closing CTA band. Nothing else may use it. */
  --space-section-lg: var(--space-6);   /* 30px per side */

  /* --- Proximity hierarchy (§6.3) ----------------------------------------
     card padding < grid gap < section seam, kept intact while tightening —
     all three come down together, never one alone. */
  --pad-card: var(--space-5);           /* 25px — was the theme's 2rem/40px */
  --gap-grid: var(--space-6);           /* 30px */

  /* --- Text rhythm (§6.3) -------------------------------------------------
     The gap above a heading is larger than the gap below it, so a heading
     binds to the text it introduces instead of floating between blocks. */
  --space-heading-above: var(--space-8);
  --space-heading-below: var(--space-3);
  --space-paragraph: var(--space-4);    /* one token, everywhere */

  /* Rebinding the theme's own card custom properties propagates the card
     padding token to every .card-body on the site without touching a rule. */
  --bs-card-spacer-y: var(--pad-card);
  --bs-card-spacer-x: var(--pad-card);
}

@media (min-width: 768px) {
  :root {
    --space-section:    var(--space-6);   /* 30px per side -> 60px seam */
    --space-section-lg: var(--space-8);   /* 40px per side */
    --pad-card:         var(--space-6);   /* 30px */
    --gap-grid:         var(--space-7);   /* 35px */
  }
}

/* --- One gap between sections, not two (§6.2) ------------------------------
   Section vertical padding is owned by the section wrapper alone. `section.`
   scopes this away from <header class="wrapper"> and <footer>, which manage
   their own chrome. Containers inside a section contribute no vertical padding,
   so a section's bottom padding meeting the next section's top padding is the
   only vertical space at a seam — never that plus a container's own. */
section.wrapper {
  padding-block: var(--space-section);
}
section.wrapper.elx-sec-lg {
  padding-block: var(--space-section-lg);
}
section.wrapper.elx-sec-flush {
  padding-block: 0;
}
section.wrapper > .container,
section.wrapper > .container-fluid {
  padding-top: 0;
  padding-bottom: 0;
}

/* --- Trailing margins never stack onto section padding (§6.2) -------------- */
.container > :last-child,
.card-body > :last-child,
.elx-content > :last-child,
[class*="col-"] > :last-child,
blockquote > :last-child,
figure > :last-child,
li > :last-child {
  margin-bottom: 0;
}

/* --- Heading rhythm (§6.3) -------------------------------------------------
   :where() carries zero specificity, so any explicit utility in the markup
   still wins; this only sets the default relationship. */
:where(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6,
       .display-1, .display-2, .display-3, .display-4, .display-5, .display-6) {
  margin-top: var(--space-heading-above);
  margin-bottom: var(--space-heading-below);
}
:where(section, .card-body, .container, [class*="col-"], .elx-content, header, aside)
  > :where(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6,
           .display-1, .display-2, .display-3, .display-4, .display-5, .display-6):first-child {
  margin-top: 0;
}
/* Chrome is not prose. A display token used as a wordmark, a nav label or a
   card eyebrow takes its spacing from its component, never from the prose
   rhythm above. */
:where(.navbar, .offcanvas, .dropdown-menu, .btn, .nav-link, .card-img-overlay)
  :where(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6,
         .display-1, .display-2, .display-3, .display-4, .display-5, .display-6) {
  margin-top: 0;
  margin-bottom: 0;
}
:where(p) { margin-bottom: var(--space-paragraph); }

/* --- Grid gaps resolve to the token (§6.3) --------------------------------- */
.elx-gap { --bs-gutter-x: var(--gap-grid); --bs-gutter-y: var(--gap-grid); }

/* ===========================================================================
   SURFACES (§7)
   ---------------------------------------------------------------------------
   Every value is one the theme already exposes in its Bootstrap palette
   (style.css :root). No colour is invented. At most three surfaces per page.
   =========================================================================== */

:root {
  --surface-base:   var(--bs-light);          /* #fefefe — theme --bs-light      */
  --surface-subtle: var(--bs-soft-primary);   /* #edf2fc — theme --bs-soft-primary */
  --surface-deep:   var(--bs-dark);           /* #262b32 — theme --bs-dark       */
  --surface-border: rgba(164, 174, 198, 0.2); /* the theme's own hairline rule   */

  /* Link ink (§7.2, §5.3: AA on every surface).
     The theme's --bs-primary #3f78e0 measures 4.19:1 on --surface-base and
     3.76:1 on --surface-subtle — large-text only, below the 4.5:1 AA floor for
     body copy. The theme already ships the answer: --bs-primary-text-emphasis
     #2c549d, Bootstrap's own emphasis variant of the same hue, at 7.28:1 and
     6.54:1. Nothing is invented; the brand colour itself is untouched and
     still fills buttons, icons and accents, where its contrast is against
     white and passes comfortably. */
  --link-on-light: var(--bs-primary-text-emphasis);   /* #2c549d */

  /* Muted text (§5.3: AA at every width). The theme's --bs-secondary-color is
     rgba(96,105,123,.75), which measures 3.26:1 on --surface-base and 3.05:1 on
     --surface-subtle — below the 4.5:1 floor, and it is used for every section
     eyebrow. It resolves to the same hue at full opacity, which is the theme's
     own --bs-gray-600 / --bs-body-color and measures 5.47:1 and 4.92:1. */
  --ink-muted: var(--bs-body-color);
  --bs-secondary-color: var(--ink-muted);
  --link-on-deep:  #fff;

  /* Ink for each surface, so contrast is a property of the surface, not of the
     component that happens to sit on it. */
  --ink-on-base:   var(--bs-body-color);      /* #60697b */
  --ink-on-deep:   rgba(255, 255, 255, 0.85);
  --head-on-base:  var(--bs-heading-color);   /* #343f52 */
  --head-on-deep:  #fff;
}

.elx-surface-base   { background-color: var(--surface-base); }
.elx-surface-subtle { background-color: var(--surface-subtle); }
.elx-surface-deep   { background-color: var(--surface-deep); color: var(--ink-on-deep); }

/* Body links resolve to the AA-passing ink on every light surface. Buttons keep
   the brand fill, where the text is white on #3f78e0.

   The :not() chain carries the specificity of its arguments — five classes —
   so any rule that needs to override it on a dark field must match or beat
   that. The deep-surface and footer rules below repeat the chain for exactly
   that reason; without it the light-surface ink lands on a near-black
   background at roughly 2:1. */
:where(body) a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.card),
.link-primary,
.elx-content a,
.elx-crumbs a,
.elx-toc a.is-active { color: var(--link-on-light); }

/* Deep surfaces: the page's one dark band, and the footer, which is
   `footer.bg-dark.text-inverse` rather than an .elx-surface-deep section. */
.elx-dial-overlay a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.card),
a.elx-link--inverse:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.card),
.elx-surface-deep a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.card),
.elx-footer a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.card),
footer.bg-dark a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.card),
.text-inverse a:not(.btn):not(.nav-link):not(.dropdown-item):not(.page-link):not(.card) {
  color: var(--link-on-deep);
}
.elx-footer a:not(.btn):hover,
.elx-footer a:not(.btn):focus-visible,
.elx-surface-deep a:not(.btn):hover,
.elx-surface-deep a:not(.btn):focus-visible { color: #fff; text-decoration: underline; }

/* Everything else in the footer reads on the deep field too. */
.elx-footer,
.elx-footer p,
.elx-footer li,
.elx-footer span,
.elx-footer .widget-title,
footer.bg-dark { color: var(--ink-on-deep); }
.elx-footer .widget-title,
.elx-footer h2, .elx-footer h3, .elx-footer h4 { color: var(--head-on-deep); }

/* A whole-card link must not tint its own body copy: the card takes the
   surface's ink and only the link component inside it takes the link colour. */
a.card { color: var(--ink-on-base); }
a.card h1, a.card h2, a.card h3, a.card h4, a.card h5, a.card h6 { color: var(--head-on-base); }

/* A hairline seam is the only way two identically coloured sections may sit
   adjacent (§7.2). */
.elx-surface-seam { border-top: 1px solid var(--surface-border); }

/* On the deep surface, text and links invert and the gradient icons swap to
   their light fallback so they stay visible (§7.2). */
.elx-surface-deep h1, .elx-surface-deep h2, .elx-surface-deep h3,
.elx-surface-deep h4, .elx-surface-deep h5, .elx-surface-deep h6,
.elx-surface-deep .display-1, .elx-surface-deep .display-2,
.elx-surface-deep .display-3, .elx-surface-deep .display-4,
.elx-surface-deep .display-5, .elx-surface-deep .display-6 { color: var(--head-on-deep); }
.elx-surface-deep p,
.elx-surface-deep li { color: var(--ink-on-deep); }
.elx-surface-deep a:not(.btn) { color: #fff; }
.elx-surface-deep .elx-grad-icon,
.elx-surface-deep .elx-gradient-text {
  background: none;
  -webkit-text-fill-color: #fff;
  color: #fff;
}

/* Cards take their surface from the band they sit on (§7.2): on a subtle band
   they lift to the base surface; on a base band they are defined by a hairline
   border rather than a fill.

   A card that declares its own variant is excluded. Without that exclusion this
   descendant selector (0,2,0) outranks the single-class variant rules (0,1,0),
   so .elx-card--gradient kept its white text but lost its dark fill and read as
   a blank card, and .elx-card--soft lost its tint on every CMS page. A variant
   is an explicit opt-out of the band default, so it has to win. */
.elx-surface-base .card,
.elx-surface-base .elx-card:not(.elx-card--gradient):not(.elx-card--soft),
.elx-surface-subtle .card,
.elx-surface-subtle .elx-card:not(.elx-card--gradient):not(.elx-card--soft) {
  background-color: var(--surface-base);
  border: 1px solid var(--surface-border);
}
.elx-surface-deep .card {
  background-color: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
}

/* --- Focus rings are never clipped by a tightened container (§6.5) --------- */
section.wrapper,
section.wrapper > .container { overflow: visible; }


/* --- Contrast repairs found by the site-wide crawl (§1.4, AA) -------------
   docs/contrast-crawl.mjs walks every page and compares each text node with
   its effective background. These three failed and are fixed at the token
   layer rather than in the vendor theme.
   -------------------------------------------------------------------------- */

/* Form labels and post meta inherited theme greys that read at 2.9:1 and
   2.16:1 on the base surface. Both are small text, so both need 4.5:1;
   --ink-muted is the darkest grey the type system already uses (5.6:1). */
.form-label,
.col-form-label,
.form-floating > label { color: var(--ink-muted); }

/* ...but only on a light surface. On a dark one the theme's light grey is
   already correct, and darkening it put the footer's newsletter label at
   1.4:1 dark-on-dark across all 111 pages — a fix that broke more than it
   repaired, caught by re-running the crawl rather than by reading the diff. */
footer .form-label,
footer .col-form-label,
footer .form-floating > label,
.elx-surface-deep .form-label,
.elx-surface-deep .col-form-label,
.elx-surface-deep .form-floating > label { color: rgba(255, 255, 255, 0.75); }
.meta,
.post-category { color: var(--ink-muted); }

/* Prism ships a DARK theme: every .token.* colour, and the #4ec9b0 base, is
   tuned for its #2d2d2d block. That background is only applied to
   `pre[class*="language-"]`, but CKEditor emits `<pre><code class="language-x">`
   — the class lands on the inner code, so the block stayed light and the dark
   palette rendered on it at 2.02:1. Give the block the background its palette
   was designed for; recolouring just the base text would leave every keyword
   and string token still unreadable. */
.elx-content pre:has(> code[class*="language-"]),
.ck-content pre:has(> code[class*="language-"]) {
  background: #2d2d2d;
  border-color: #2d2d2d;
  color: #ccc;
}
