/* =====================================================================
   styles.css - the TENANT BRAND LAYER. lumos.css is brand-agnostic and
   consumes the seeds defined here. Load order: lumos.css THEN styles.css.

   This is the STARTER DEFAULT: the seed values are the Lumos 2.2.3 export
   defaults, so the framework renders identically to the published template
   out of the box. A tenant duplicates the starter and replaces these.

   To brand a tenant, edit only:
     1. the 5 swatch seeds + font family below (the derived ramp follows),
     2. (optional) the .u-theme-* role mappings, if the defaults need changing,
     3. add @font-face via fonts.css and point --_typography---font--primary-family at it,
     4. add the per-site components at the bottom.
   Do NOT edit lumos.css - it stays byte-identical across every tenant.
   ===================================================================== */

:root {
  /* --- Brand seeds — yogdog, "V1 PRESS" (PRD-yogdog-onepager §4b; mock =
     studio-brutalist-mockups.html, V1 tokens). MONO: the signal IS the ink —
     the brand ramp derives greys, and the light theme overrides below keep the
     accent at full ink. Dark = the S6 QUIET-dark grammar (V2 TERMINAL values:
     warm off-black, bone ink, never pure white on black). --- */
  --swatch--brand-500: #111111;   /* mono signal = ink (ramp + brand-text derive from it) */
  --swatch--dark-900:  #111111;   /* ink */
  --swatch--dark-800:  #3a3a3a;   /* muted */
  --swatch--light-100: #ffffff;   /* paper */
  --swatch--light-200: #dddddd;   /* soft (hairlines; hovers are theme-relative ink washes since r7/r8) */
  /* extra swatches (the kyoom white-linen precedent) */
  --swatch--faint:     #777772;   /* mock --faint: quiet mono labels — a11y A2
    (25 Aug 2026): minimum-passing on white (4.50:1; was #8b8b85 = 3.42:1).
    Dark mode HOLDS the old value below — 8b8b85 on #121211 passes at 5.3 and
    darkening it there would fail; the faint pair is theme-split on purpose. */
  --swatch--carbon:    #121211;   /* quiet-dark paper */
  --swatch--carbon-2:  #1a1a18;   /* quiet-dark raised surface */
  --swatch--bone:      #e4e2db;   /* quiet-dark ink */
  --swatch--soft-dark: #242422;   /* quiet-dark hairline/soft */

  /* --- Fonts (self-hosted variable woff2, fonts.css) ---
     ONE text face: Archivo — display tiers take 900 via the tier weight
     channels below. Trim pair stays at the framework default (.33/.38em,
     tuned for a similar grotesque) — calibrate against the mock at the Y2b/Y3'
     visual parity pass, not here. The MONO (Spline Sans Mono) is NOT the
     secondary family: it rides the CO-27 label-family third slot below, so
     labels + chrome (nav links, footer) go mono while headings stay Archivo. */
  --_typography---font--primary-family: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --_typography---font--label-family: "Spline Sans Mono", ui-monospace, Menlo, monospace;

  /* --- Type voices: brutalist heading grammar (mock h1.hero-h / .blk-h) ---
     Heavy grotesque, ALL heading tiers uppercase; tight tracking on the big
     tiers. Sizes deliberately NOT seeded at Y2a — the starter's fluid ramp
     stands until the mock-parity pass sets endpoints (parametric clamp form). */
  --_typography---display--weight: 900;
  --_typography---display--text-transform: uppercase;
  --_typography---display--letter-spacing: -0.015em;
  --_typography---display--line-height: 0.92;
  --_typography---h1--weight: 900;
  --_typography---h1--text-transform: uppercase;
  --_typography---h1--letter-spacing: -0.015em;
  --_typography---h1--line-height: 0.95;
  --_typography---h2--weight: 900;
  --_typography---h2--text-transform: uppercase;
  --_typography---h2--letter-spacing: -0.01em;
  --_typography---h2--line-height: 1.05;
  --_typography---h3--weight: 900;
  --_typography---h3--text-transform: uppercase;
  --_typography---h3--letter-spacing: -0.01em;
  --_typography---h3--line-height: 1.1;
  --_typography---h4--weight: 900;
  --_typography---h4--text-transform: uppercase;
  --_typography---h5--weight: 900;
  --_typography---h5--text-transform: uppercase;
  /* CO-35 legal: the kit's .legal_body h2 rule falls back to
     secondary-family at 300 — restate the heading identity through the
     kit's own --_legal slots (Y3'a r4; transform/tracking already arrive
     via the bare-h2 tier channels above) */
  --_legal---h2--family: var(--_typography---font--primary-family);
  --_legal---h2--weight: 900;
  /* the sticky bar clips the ToC's default space--5 offset — clear it (r5) */
  --_legal---toc--top: 6rem;
  /* The print run begins only after a section reaches the upper half of the
     viewport. Slots are read by wireReveal's observer (CO-38). */
  --_reveal---io-threshold: 0.12;
  --_reveal---io-root-margin: 0px 0px -50% 0px;
  --_typography---h6--weight: 900;
  --_typography---h6--text-transform: uppercase;

  /* --- Radius: ZERO across every scale (no radius, anywhere) --- */
  --radius--small: 0rem;
  --radius--main: 0rem;
  --radius--large: 0rem;
  --radius--round: 0rem;

  /* --- Buttons: hard press physics via the CO-50 channels. steps(), never
     ease; the hover is a hard offset plate (Y-lift only — the X half of the
     mock's translate is the tx addon's job at Y3'). Shadow colour is seeded
     per THEME below, never here (a :root var(--_theme---*) resolves at the
     root and freezes — the CO-50(a) trap). --- */
  --button--radius: 0rem;
  --button--transition-duration: 0.08s;
  --button--transition-timing: steps(2, end);
  --button--hover-lift: -0.125rem;
}

/* Hard-plate hover shadow: declared ON the theme scopes so --_theme---text
   resolves per theme (ink plate on paper, bone plate on carbon). */
.u-theme-light, .u-theme-dark {
  --button--hover-shadow: 0.25rem 0.25rem 0 var(--_theme---text);
}

/* The starter nav re-declares its own button-primary--text (toward brand-text,
   which derives DARK from a mono #111 seed → invisible label on the ink slab —
   the kyoom-recorded trap, same fix). White label on the nav CTA too.
   AND the nav scope pins the HOVER slots (text-hover: background,
   background-hover: text) — in light mode the drawer CTA hovered to an ink
   slab instead of the tenant's paper-invert (Kris caught it, 25 Aug 2026;
   dark already worked because the r8 dark nav mirror re-pins its hovers).
   Mirror the LIGHT hovers here the same way — the dark block's higher
   specificity keeps dark intact. */
.nav_component {
  --_theme---button-primary--text: var(--swatch--light-100);
  --_theme---button-primary--background-hover: var(--swatch--light-100);
  --_theme---button-primary--text-hover: var(--swatch--dark-900);
  --_theme---button-primary--border-hover: var(--swatch--dark-900);
}

/* ---------------------------------------------------------------------
   Theme role overrides — PRESS light + QUIET dark (only roles that differ
   from stock; everything else inherits stock, which reads the seeds above).
   --------------------------------------------------------------------- */
.u-theme-light {
  /* CO-6 re-point (r7b): the nav logo role defaults to brand-500 = ink on
     EVERY theme; follow the theme text so the wordmark (fill=currentColor)
     switches — ink on paper, bone on carbon. The Mark's fills are hardcoded
     in the markup and ignore this. */
  --_theme---nav--logo: var(--_theme---text);
  --_theme---background: var(--swatch--light-100);        /* paper canvas (stock: light-200 grey) */
  --_theme---button-primary--background-hover: var(--swatch--light-100); /* mock tx hover: slab inverts to paper... */
  --_theme---button-primary--text-hover: var(--swatch--dark-900);        /* ...with ink label... */
  --_theme---button-primary--border-hover: var(--swatch--dark-900);      /* ...and an ink edge (with the seeded lift+plate) */
  --_theme---heading-accent: var(--swatch--dark-900);     /* mono: the accent IS the ink (stock: brand-600 mid-grey) */
  --_theme---border: var(--swatch--light-200);            /* soft hairline #ddd (stock: ink-o20) */
  --_theme---button-primary--text: var(--swatch--light-100); /* white label on the ink slab */
}
.u-theme-dark {                                           /* S6 quiet dark — V2 TERMINAL */
  --swatch--faint: #8b8b85;                               /* a11y A2: HOLD the pre-A2 faint here — on carbon it reads 5.3:1; the light side's #777772 would drop it to ~4.0 (the faint pair is theme-split, see :root) */
  --_theme---nav--logo: var(--_theme---text);             /* CO-6 re-point — see light block */
  --_theme---background: var(--swatch--carbon);           /* warm off-black (stock: ink) */
  --_theme---background-2: var(--swatch--carbon-2);       /* raised dark surface (stock: dark-800) */
  --_theme---text: var(--swatch--bone);                   /* bone, never pure white (stock: light-100) */
  --_theme---heading-accent: var(--swatch--bone);         /* mono dark: accent = bone (stock: brand-500 = ink, invisible here) */
  --_theme---border: var(--swatch--soft-dark);            /* quiet hairline (stock: light-100-o20) */
  --_theme---button-primary--background: var(--swatch--bone); /* sig inverts in quiet dark (stock: brand-500 = ink-on-carbon, invisible) */
  --_theme---button-primary--text: var(--swatch--carbon);
  /* r8 hover invert (Kris): dark ground + bone border on hover; the heavyset
     bottom/right shadow already rides --button--hover-shadow -> theme text
     (= bone here), shared with the light block above. */
  --_theme---button-primary--background-hover: var(--swatch--carbon);
  --_theme---button-primary--text-hover: var(--swatch--bone);
  --_theme---button-primary--border-hover: var(--swatch--bone);
}
/* r8: form fields ride the PAGE ground (the injected skin paints #fff — light
   slabs in the dark band); the skin's rgba(0,0,0,.25) edge vanishes on
   carbon, so the border mirrors the same 25% weight off the theme text. The
   skin rule is :where() zero-specificity by design — this outranks. */
[data-yd-form-rendered] .form_field {
  background: var(--_theme---background);
  border-color: color-mix(in srgb, var(--_theme---text) 25%, transparent);
}

/* ---------------------------------------------------------------------
   Y2b mint seeds — PRESS values for the four starter mints (CO-55 drawer,
   CO-56 ruled grid, CO-57 spec card, v-form-label-float). Metrics at :root;
   COLOUR slots on the theme scopes so --_theme---* resolves per theme (the
   CO-50(a) trap, same as the hover-shadow above). Rule/soft colours are NOT
   seeded where the neutral fallback is already --_theme---border (= soft /
   soft-dark here). Float-label insets stay at kit defaults — calibrate at
   the Y3' mock-parity pass against the real field metrics, not here.
   --------------------------------------------------------------------- */
:root {
  /* CO-55 drawer: mock 520px sheet, 3px ink edge, snap steps(3), 900 items */
  --_nav-drawer---width: min(32.5rem, 100vw);
  --_nav-drawer---border-width: 3px;
  --_nav-drawer---duration: 0.18s;
  --_nav-drawer---ease: steps(3, end);
  --_nav-drawer---link--size: clamp(2.2rem, 6vh, 3.6rem);
  --_nav-drawer---link--weight: 900;
  --_nav-drawer---link--leading: 1.05;
  --_nav-drawer---link--family: var(--_typography---font--primary-family); /* display face — the drawer items are headings, not chrome labels */
  --_nav-drawer---link--transform: uppercase;
  --_nav-drawer---link--tracking: -0.01em;
  --_nav-drawer---link-hover--indent: 1rem;
  --_nav-drawer---index--color: var(--swatch--faint);
  /* CO-56 ruled grid: 1px rules in a 2px frame (colour on the theme scopes) */
  --_grid-ruled---frame-width: 2px;
  /* CO-57 spec card: 2px frame, faint terms (family = the label-family chain) */
  --_spec---frame-width: 2px;
  --_spec---term--color: var(--swatch--faint);
  /* --- container + nav width: the mock shell (1280px cap, clamp margins).
     The stock container is UNCAPPED (--max-width--main = the full 90rem) and
     the nav bar ran viewport-wide (Kris, parity round 1) --- */
  --site--max-width: 80rem;
  --site--margin: clamp(1rem, 4vw, 3rem);
  --max-width--main: calc(var(--site--max-width) - var(--site--margin) * 2);
  --nav--max-width-outer: var(--max-width--main);
  --nav--spacing-outer-horizontal: var(--site--margin);
  /* the lockup is wide (3.8:1) — the stock 6rem logo box squashed it, and
     `.u-icon svg` is width:100%/height:auto !important, so the BOX is the only
     size knob: width = 3.773 x the mock's 30->38px height band (parity r1) */
  --nav--logo-width: clamp(7.0625rem, 15.1vw, 8.9375rem);
  /* (the lockup embed is .nav_logo_embed, NOT .u-icon — u-icon flattens every
     path fill to currentColor !important and painted the mark all black, r2) */
  /* --- micro-type: the label voice at the mock's 10.5px mono (tracking keeps
     the .14em default); body copy at the mock base --- */
  --_typography---label--font-size: 0.65625rem;
  --_typography---text-main--font-size: 1rem;
  --_typography---text-main--line-height: 1.55;
  --_typography---text-large--font-size: clamp(0.9375 * 1rem, ((0.9375 - ((1.125 - 0.9375) / (var(--site--viewport-max)  - var(--site--viewport-min)) * var(--site--viewport-min))) * 1rem + ((1.125 - 0.9375) / (var(--site--viewport-max)  - var(--site--viewport-min))) * 100vw), 1.125 * 1rem);
  /* --- buttons: the mock slab metrics (mono voice is an element rule below —
     CO-50 ships no family channel) --- */
  --button--regular--font-size: 0.8125rem;
  --button--regular--padding-block: 1.125rem;
  --button--regular--padding-inline: 1.625rem;
  /* v-form-label-float: snap label, faint at rest */
  --_form-label---float--transition: 0.09s steps(2, end);
  --_form-label---float--color: var(--swatch--faint);
}
.u-theme-light, .u-theme-dark {
  --_nav-drawer---border-color: var(--_theme---text);          /* hard ink/bone edge */
  --_nav-drawer---link-hover--background: var(--_theme---text); /* row inverts on hover */
  --_nav-drawer---link-hover--color: var(--_theme---background);
  --_nav-drawer---link-hover--index-color: var(--_theme---background); /* the faint index inverts too */
  --_grid-ruled---rule-color: var(--_theme---text);            /* hard mono rules, both widths */
  --_spec---frame-color: var(--_theme---text);                 /* row rules stay soft (fallback) */
  --_form-label---float--raised-color: var(--_theme---text);   /* label sharpens once raised */
}

/* ---------------------------------------------------------------------
   Secondary font routing (optional dual-font). Routes the secondary family
   (set above) to ONE target by re-pointing the primary-* font tokens the
   text-styles read - exactly what CO-8's .u-typography-secondary does, but
   applied as a SELECTOR so the page markup stays clean (no per-heading class).

   Keyed off the typography VARIANT classes, NOT the .u-heading wrapper: those
   survive the typography normalisation (.u-heading merges into .u-text) and
   identify headings vs body regardless of wrapper. Headings = display + h1..h6
   (authored pages carry v-typography-*; the styleguide + direct usage carry the
   raw u-text-style-*, so both are listed). Body = text-main + text-small (+ prose).

   Default target = HEADINGS. No-op until --_typography---font--secondary-family
   is set above. To route to BODY instead, comment the headings block and
   uncomment the body one. Bold/weight on headings is a separate tenant choice
   (set --_text-style---font-weight on the same selectors). ----------------- */
.v-typography-display,
.v-typography-h1, .v-typography-h2, .v-typography-h3,
.v-typography-h4, .v-typography-h5, .v-typography-h6,
.u-text-style-display,
.u-text-style-h1, .u-text-style-h2, .u-text-style-h3,
.u-text-style-h4, .u-text-style-h5, .u-text-style-h6 {
  --_typography---font--primary-family:      var(--_typography---font--secondary-family);
  --_typography---font--primary-trim-top:    var(--_typography---font--secondary-trim-top);
  --_typography---font--primary-trim-bottom: var(--_typography---font--secondary-trim-bottom);
}
/* Route to BODY instead (comment the headings block above, uncomment this):
.v-typography-text-main, .v-typography-text-small, .u-text-style-main, .u-text-style-small, .u-rich-text {
  --_typography---font--primary-family:      var(--_typography---font--secondary-family);
  --_typography---font--primary-trim-top:    var(--_typography---font--secondary-trim-top);
  --_typography---font--primary-trim-bottom: var(--_typography---font--secondary-trim-bottom);
} */

/* ---------------------------------------------------------------------
   Theme role overrides (optional) - the light/dark/brand swatch->role
   mappings live in lumos.css and already reference the seeds above. Only
   override here if a tenant needs a different mapping, e.g.:
     .u-theme-dark { --_theme---text: var(--swatch--brand-500); }
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Per-site components go below this line.
   --------------------------------------------------------------------- */

/* Testimonial author row + avatar (starter component; used by the Home testimonial
   slider and the components-page kit). The avatar is optional - a quote card without
   `.testimonial_author` is the no-avatar variant. */
.testimonial_author { display: flex; align-items: center; }  /* gap + top spacing = u-gap-2 + u-margin-top-3 utilities in the markup */
.testimonial_avatar { width: 3rem; height: 3rem; border-radius: 100vw; object-fit: cover; flex: 0 0 auto; background-color: var(--_theme---background-2); }
/* name->role spacing is a u-margin-top-* utility on the role in the markup (not custom
   CSS here); .testimonial_role only carries the muted styling. */
.testimonial_role { opacity: 0.65; }

/* Logo marquee slot - fixed-size placeholder logos a tenant swaps for real ones.
   Width is what makes the strip overflow the container, so the marquee actually scrolls. */
.marquee_logo { width: 7.5rem; height: 2.5rem; object-fit: contain; opacity: 0.5; flex: 0 0 auto; }

/* Card variants + surface controls (starter) - all attributes on .card_primary_wrap.
   The VARIANT says what the card holds; BACKGROUND + PADDING are independent controls
   shared across every variant, so they compose freely (e.g. a stat with no fill/pad =
   bare numbers that still get card_primary_header's spacing). A variant is an attribute
   swap on the same shell, mirroring Lumos's own card-type attribute.

   Content variants:
     data-card-variant="no-button" - hide the action button + click overlay
     data-card-variant="stat"      - big number + small caption (no image/button)
     data-card-variant="quote"     - quote-first card with an author row (.testimonial_*)
   Surface controls (default = filled + padded, no static border — the standard card):
     data-card-background="false"  - drop the fill; the card sits on the section
     data-card-padding="false"     - drop the inner padding
     data-card-border="true"       - paint a STATIC theme hairline (H1). The base card only
       carries the trigger-driven border that fades in/out on hover (transparent at rest), so an
       at-rest outline needs this. Compose with background="false" for the OUTLINE card (stats #8):
       no fill + a calm --_theme---border rule, padding kept. Independent of fill/pad, so a filled
       card can also carry a border. Specificity (attr+class) out-points the base longhands and is
       not hover-scoped, so the outline stays put (these cards aren't clickable).
     data-card-accent="true" (CO-31) - paint a STATIC accent bar on the TOP edge (the editorial
       "coded card" treatment: margin notes, credential cards, pathway cards). Two slot-token
       channels, set at/below the card (they inherit): --_card---accent--width (default 0.125rem)
       and --_card---accent--color (default the --_theme---heading-accent role) — a page CODES a
       card by re-pointing the color channel per instance (an is-* class or an inline custom
       property), so ONE axis serves every coded family. Composes with every variant + surface
       control; declared AFTER border="true" so the accent wins the top edge at equal
       specificity. Top edge only; the base border-top-style is already solid. */
[data-card-variant="no-button"] .button_main_wrap,
[data-card-variant="no-button"] .clickable_wrap { display: none; }
[data-card-variant="stat"] .card_primary_title { line-height: 1; }
[data-card-background="false"] .card_primary_element { background-color: transparent; }
[data-card-padding="false"] .card_primary_element { padding: 0; }
[data-card-border="true"] .card_primary_element { border-color: var(--_theme---border); }
[data-card-accent="true"] .card_primary_element {
  border-top-width: var(--_card---accent--width, 0.125rem);
  border-top-color: var(--_card---accent--color, var(--_theme---heading-accent));
}

/* Figure component (starter, CO-32) - the documentary figure treatment: a media
   <figure class="figure_component"> + a small baseline-flex caption with a bold lead-in
   (<figcaption class="figure_caption"><b>Fig. 01</b> <span>...</span>). Modes on the shell:
     (default)                       - plain documentary photo: hairline border on the img
     data-figure-frame="plate"       - framed specimen plate: the media sits in a
       .figure_frame child carrying a surface fill + hairline + fluid padding + an inset
       hairline (::after), and the art multiplies onto the plate so line-art takes the tint
     data-figure-treatment="grayscale" - desaturate the media (documentary/headshot)
     data-figure-treatment="multiply"  - multiply-blend the media onto the page ground
       (soft-plate hero media without the framed plate — line-art/photo takes the ground tint)
   Values ride inheriting --_figure---* slot tokens with neutral starter fallbacks (theme
   roles / type tokens); a tenant re-points them at/below the component. Caption gap + top
   spacing = u-gap / u-margin-top utilities in the markup (the testimonial precedent). The
   caption mutes via a color-mix channel, NOT opacity - opacity would dim the <b> lead-in,
   which re-declares its own full-strength color. */
.figure_component { margin: 0; }
.figure_component img { width: 100%; height: auto; display: block; }
.figure_component:not([data-figure-frame="plate"]) img {
  border: var(--border-width--main) solid var(--_figure---border, var(--_theme---border));
}
[data-figure-frame="plate"] .figure_frame {
  position: relative;
  background-color: var(--_figure---plate--background, var(--_theme---background-2));
  border: var(--border-width--main) solid var(--_figure---border, var(--_theme---border));
  padding: var(--_figure---plate--padding, clamp(1rem, 0.6rem + 1.6vw, 2rem));
}
[data-figure-frame="plate"] .figure_frame::after {
  content: "";
  position: absolute;
  inset: var(--_figure---plate--inset, 0.6rem);
  border: var(--border-width--main) solid var(--_figure---border, var(--_theme---border));
  opacity: var(--_figure---plate--inset-opacity, 0.55);
  pointer-events: none;
}
[data-figure-frame="plate"] img { mix-blend-mode: var(--_figure---plate--blend, multiply); }
[data-figure-treatment="grayscale"] img { filter: grayscale(1); }
[data-figure-treatment="multiply"] img { mix-blend-mode: multiply; }



/* Process steps (starter, CO-33) - ordered step items in a grid. The SHELL is the stock
   grid (u-grid-wrapper + data-*-columns) - no new grid here; reveal stagger = the existing
   .u-reveal. Each item = a coded top bar + a display numeral + heading + copy. The numeral
   is a probe-visible text tag (<p class="steps_number">01</p>); headings/copy inside items
   are NOT styled by the component - the markup composes u-text-style-* / typography
   variants. The bar shares the coded-channel idea with CO-31 but mints its own slots (a
   step bar and a card accent diverge independently). data-steps-layout="row" on an ITEM
   seats the numeral beside the content (the home-rail shape); default stacks it above. */
.steps_item {
  padding-top: var(--_steps---bar--space, var(--_spacing---space--4));
  border-top: var(--_steps---bar--width, var(--border-width--main)) solid var(--_steps---bar--color, var(--_theme---border));
}
.steps_number {
  font-family: var(--_steps---number--family, var(--_typography---font--secondary-family, var(--_typography---font--primary-family)));
  font-size: var(--_steps---number--size, var(--_typography---font-size--h3));
  line-height: var(--_steps---number--line-height, 1);
  font-weight: var(--_steps---number--weight, var(--_typography---font--primary-light, 300));
  color: var(--_steps---number--color, var(--_theme---heading-accent));
  margin-bottom: var(--_steps---number--space, var(--_spacing---space--3));
}
.steps_item[data-steps-layout="row"] {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--_steps---row--gap, var(--_spacing---space--3));
  align-items: start;
  padding-block: var(--_steps---bar--space, var(--_spacing---space--4));
}
.steps_item[data-steps-layout="row"] .steps_number { margin-bottom: 0; }

/* Before/after compare (starter, CO-34) - a clip-path reveal slider: two stacked media
   layers, the TOP layer clipped from the left by the --pos custom property, plus a vertical
   handle + round grip + corner labels that track --pos. The BASE mechanism is CSS-only:
   --pos defaults to 50%, so both halves render meaningfully with no JS (set a rest position
   inline, e.g. style="--pos:86%"); scripts.js wireCompare adds the pointer drag. Handle,
   grip and labels are pointer-events:none spans (probe-invisible) - the component is the
   sole pointer target. Values ride --_compare---* slots with neutral fallbacks; the label
   chip mutes its backdrop via color-mix (the theme surface at partial strength). */
.compare_component {
  position: relative;
  width: 100%;
  aspect-ratio: var(--_compare---ratio, 1);
  overflow: hidden;
  background-color: var(--_compare---background, var(--_theme---background-2));
  border: var(--border-width--main) solid var(--_theme---border);
  user-select: none;
  touch-action: none;
  cursor: ew-resize;
}
.compare_component img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: var(--_compare---fit, cover);
  display: block;
  pointer-events: none;
}
.compare_top { clip-path: inset(0 0 0 var(--pos, 50%)); }
.compare_handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--pos, 50%);
  width: var(--_compare---handle--width, 0.125rem);
  background-color: var(--_compare---handle--color, var(--_theme---text));
  transform: translateX(-50%);
  pointer-events: none;
}
.compare_grip {
  position: absolute;
  top: 50%;
  left: var(--pos, 50%);
  width: var(--_compare---grip--size, 2.5rem);
  height: var(--_compare---grip--size, 2.5rem);
  border-radius: 100vw;
  background-color: var(--_compare---background, var(--_theme---background-2));
  border: var(--_compare---handle--width, 0.125rem) solid var(--_compare---handle--color, var(--_theme---text));
  color: var(--_compare---handle--color, var(--_theme---text));
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.compare_label {
  position: absolute;
  bottom: var(--_compare---label--offset, 0.6rem);
  font-family: var(--_compare---label--family, var(--_typography---font--primary-family));
  font-size: var(--_compare---label--size, 0.65rem);
  letter-spacing: var(--_compare---label--letter-spacing, 0.14em);
  text-transform: uppercase;
  color: var(--_theme---text);
  background-color: color-mix(in srgb, var(--_compare---background, var(--_theme---background-2)) 82%, transparent);
  padding: var(--_compare---label--padding, 0.25rem 0.5rem);
  pointer-events: none;
}
.compare_label.is-l { left: var(--_compare---label--offset, 0.6rem); }
.compare_label.is-r { right: var(--_compare---label--offset, 0.6rem); }

/* CO-34 caption axis (P5.C3) - the annotated compare: a caption row under the compare's
   <figure> wrapper. .compare_caption on the <figcaption> (the wrapper itself never takes
   .figure_component - the CO-32 img rules leak onto the stacked layers); .compare_caption_hint
   is the small tracked action cue ("drag to reveal"), .compare_caption_text the annotation
   prose. Every value rides a --_compare---caption*--* slot with neutral fallbacks; the hint
   family chains to the label-family contract slot (the v-typography-label precedent), the
   text family to the primary. Lives with the component so every compare consumer shares one
   caption grammar (G&A dev_cap family = first consumer). */
.compare_caption { display: flex; flex-direction: column; gap: var(--_compare---caption--gap, 0.4em); margin: var(--_compare---caption--space, var(--_spacing---space--3)) 0 0; }
.compare_caption_hint { display: inline-flex; align-items: center; gap: var(--_compare---caption-hint--gap, 0.45em); align-self: flex-start;
  font-family: var(--_compare---caption-hint--family, var(--_typography---font--label-family, var(--_typography---font--primary-family)));
  font-size: var(--_compare---caption-hint--size, 0.68rem); letter-spacing: var(--_compare---caption-hint--tracking, 0.14em);
  text-transform: uppercase; font-weight: var(--_compare---caption-hint--weight, 600);
  color: var(--_compare---caption-hint--color, color-mix(in srgb, var(--_theme---text) 65%, transparent)); }
.compare_caption_text { font-family: var(--_compare---caption-text--family, var(--_typography---font--primary-family));
  font-style: var(--_compare---caption-text--style, normal); font-size: var(--_compare---caption-text--size, 0.9rem);
  line-height: var(--_compare---caption-text--leading, 1.5); color: var(--_compare---caption-text--color, color-mix(in srgb, var(--_theme---text) 65%, transparent));
  max-width: var(--_compare---caption-text--measure, none); }

/* Inline newsletter form (starter) - email field + submit on one row, capped width,
   centred. Wraps to stacked on narrow screens (the field's flex-basis triggers it). */
.newsletter_form { display: flex; flex-direction: row; flex-wrap: wrap; align-items: flex-end; max-width: 34rem; text-align: left; }  /* gap + centring = u-gap-2 + u-margin-inline-auto utilities in the markup */
.newsletter_field { flex: 1 1 16rem; }
.newsletter_field .form_field { width: 100%; }

/* Article reading column (single-post template) - constrain the rich-text body to a
   comfortable reading measure. SIZING only; centring is u-margin-inline-auto in the
   markup, vertical rhythm is the w-richtext defaults + u-margin-* utilities. */
/* width:100% so every section shares ONE box inside the centring container - without it
   the container shrink-wraps each .article_body to its content, so a short header (24ch h1)
   and a wide body centre at DIFFERENT left edges (caught on the Kyoom posts + legal pages) */
.article_body { max-width: 46rem; width: 100%; }

/* CMS article body - rich-text headings + media. The de-Webflowed u-rich-text only carries
   margins (type normally comes from the typography component), but CMS post bodies arrive
   as raw h2-h4 with no variant class - give them the heading recipes at the h4/h5/h6 scale.
   Headings route to the SECONDARY typography mode (CO-8), which defaults to primary in a
   single-font brand (no-op) and picks up the heading font in a dual-font brand (e.g. Kyoom). */
.article_body .u-rich-text h2, .article_body .u-rich-text h3, .article_body .u-rich-text h4 {
  font-family: var(--_typography---font--secondary-family);
  line-height: var(--_typography---line-height--medium);
  font-weight: var(--_typography---font--primary-bold);
  letter-spacing: var(--_typography---letter-spacing--normal);
  text-wrap: balance;
  margin-top: var(--_spacing---space--7);
  margin-bottom: var(--_spacing---space--4);
}
.article_body .u-rich-text h2 { font-size: var(--_typography---font-size--h4); }
.article_body .u-rich-text h3 { font-size: var(--_typography---font-size--h5); }
.article_body .u-rich-text h4 { font-size: var(--_typography---font-size--h6); }
.article_body .u-rich-text img { height: auto; border-radius: var(--radius--main); }
.article_body .u-rich-text img:not([class*="rt-img--"]) { width: 100%; margin-block: var(--_spacing---space--5); }
/* RT v2 Phase 5 (PRD-richtext) - the image-layout vocabulary. FIXED classes the
   editor writes: rt-img--left/right float with text wrap, rt-img--center is an
   inline block centred at its width, rt-img--full spans the column. Width rides
   preset classes (rt-img--w25/w33/w50/w75) or, for a CUSTOM width, the img's
   width attribute in px with marker class rt-img--wc (no width rule on purpose -
   the attribute sizes it, max-width caps it). Floats collapse to full-width on
   small screens: a wrap needs room to read. */
.u-rich-text img.rt-img--left, .u-rich-text img.rt-img--right {
  height: auto; max-width: 50%; border-radius: var(--radius--main);
  margin-block: .35em var(--_spacing---space--3);
}
.u-rich-text img.rt-img--left { float: left; clear: left; margin-right: var(--_spacing---space--4); }
.u-rich-text img.rt-img--right { float: right; clear: right; margin-left: var(--_spacing---space--4); }
.u-rich-text img.rt-img--center {
  display: block; height: auto; max-width: 100%; border-radius: var(--radius--main);
  margin-block: var(--_spacing---space--5); margin-inline: auto;
}
.u-rich-text img.rt-img--full {
  display: block; width: 100%; height: auto; border-radius: var(--radius--main);
  margin-block: var(--_spacing---space--5);
}
.u-rich-text img.rt-img--w25 { width: 25%; max-width: none; }
.u-rich-text img.rt-img--w33 { width: 33.333%; max-width: none; }
.u-rich-text img.rt-img--w50 { width: 50%; max-width: none; }
.u-rich-text img.rt-img--w75 { width: 75%; max-width: none; }
.u-rich-text img.rt-img--wc { max-width: 100%; }
/* RT v2 Phase 5b (PRD-richtext) - captions. A captioned image is
   figure > img + figcaption; layout classes stay ON THE IMG (one vocabulary
   location) and the figure follows them via :has(), so the caption travels
   with the float/centre/width. Inside a vocab figure the FIGURE owns
   geometry: the img resets and fills it - except rt-img--wc, which keeps NO
   width rule on purpose (the width attribute sizes it; the figure
   shrink-wraps). Bare legacy figures (no vocab classes) are untouched. */
.u-rich-text figure:has(> img.rt-img--left),
.u-rich-text figure:has(> img.rt-img--right) { max-width: 50%; margin-block: .35em var(--_spacing---space--3); }
.u-rich-text figure:has(> img.rt-img--left) { float: left; clear: left; margin-right: var(--_spacing---space--4); }
.u-rich-text figure:has(> img.rt-img--right) { float: right; clear: right; margin-left: var(--_spacing---space--4); }
.u-rich-text figure:has(> img.rt-img--center) { margin-block: var(--_spacing---space--5); margin-inline: auto; width: fit-content; max-width: 100%; }
.u-rich-text figure:has(> img.rt-img--full) { margin-block: var(--_spacing---space--5); }
.u-rich-text figure:has(> img.rt-img--w25) { width: 25%; }
.u-rich-text figure:has(> img.rt-img--w33) { width: 33.333%; }
.u-rich-text figure:has(> img.rt-img--w50) { width: 50%; }
.u-rich-text figure:has(> img.rt-img--w75) { width: 75%; }
.u-rich-text figure:has(> img.rt-img--wc) { width: fit-content; max-width: 100%; }
.u-rich-text figure > img[class*="rt-img--"] { float: none; margin: 0; }
.u-rich-text figure > img[class*="rt-img--"]:not(.rt-img--wc) { width: 100%; max-width: 100%; }

/* Contain floats: a floated image at the end of a body must grow its container,
   not spill into whatever follows. Clearfix (no display change - Lumos owns the
   container's display; verified no competing ::after on u-rich-text). */
.u-rich-text::after { content: ""; display: block; clear: both; }
/* RT v2 Phase 4 (PRD-richtext) - tables. The editor emits bare semantic
   table/tr/th/td (header row = th cells); presentation is wholly tenant CSS.
   Authored column widths ride col style widths (the one sanctioned inline
   style). Small screens scroll the table instead of squashing it. */
.u-rich-text table { width: 100%; border-collapse: collapse; margin-block: var(--_spacing---space--5); table-layout: fixed; }
.u-rich-text th, .u-rich-text td {
  border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
  padding: var(--_spacing---space--2) var(--_spacing---space--3);
  text-align: left; vertical-align: top; overflow-wrap: break-word;
}
.u-rich-text th { font-weight: var(--_typography---font--primary-bold, 600); background: color-mix(in srgb, currentColor 6%, transparent); }
@media (max-width: 640px) { .u-rich-text table { display: block; overflow-x: auto; } }

/* RT v2 Phase 4b (PRD-richtext) - toggle + callout blocks. The editor emits
   details.rt-toggle > summary + div.rt-toggle__body (native disclosure -
   zero-JS published output) and div.rt-callout.rt-callout--{info|warning|
   success|note} (closed variant enum). Accents ride a custom property so a
   tenant can rebrand the variants without touching structure. */
.u-rich-text details.rt-toggle {
  border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
  border-radius: var(--radius--main);
  padding: var(--_spacing---space--3) var(--_spacing---space--4);
  margin-block: var(--_spacing---space--4);
}
.u-rich-text details.rt-toggle summary {
  cursor: pointer; font-weight: var(--_typography---font--primary-bold, 600);
}
.u-rich-text details.rt-toggle .rt-toggle__body { padding-top: var(--_spacing---space--2); }
.u-rich-text details.rt-toggle .rt-toggle__body > :last-child { margin-bottom: 0; }
.u-rich-text .rt-callout {
  border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
  border-inline-start: 4px solid var(--rt-callout-accent, currentColor);
  border-radius: var(--radius--main);
  padding: var(--_spacing---space--3) var(--_spacing---space--4);
  margin-block: var(--_spacing---space--4);
  background: color-mix(in srgb, var(--rt-callout-accent, currentColor) 6%, transparent);
}
.u-rich-text .rt-callout > :last-child { margin-bottom: 0; }
.u-rich-text .rt-callout--info    { --rt-callout-accent: #4a7fb5; }
.u-rich-text .rt-callout--warning { --rt-callout-accent: #b5802a; }
.u-rich-text .rt-callout--success { --rt-callout-accent: #3a8a5f; }
.u-rich-text .rt-callout--note    { --rt-callout-accent: #7a63a8; }

/* RT v2 Phase 4b-2 - the TOC block. Filled at save from the document's h2-h4
   anchors (write-time stamp); plain list styling, tenant themes refine.
   scroll-margin keeps jumped-to headings clear of the fixed pill nav (same
   treatment as the footnote targets below). */
.u-rich-text nav.rt-toc {
  border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
  border-radius: var(--radius--main);
  padding: var(--_spacing---space--3) var(--_spacing---space--4);
  margin-block: var(--_spacing---space--4);
}
.u-rich-text nav.rt-toc ol { margin: 0; padding-left: 1.2em; }
.u-rich-text nav.rt-toc li.rt-toc__l3 { margin-left: 1em; }
.u-rich-text nav.rt-toc li.rt-toc__l4 { margin-left: 2em; }
.u-rich-text :is(h2, h3, h4)[id] { scroll-margin-top: calc(var(--nav--height-total, 5rem) + var(--_spacing---space--3, 1rem)); }

/* RT v2 CR-FN (PRD-link-health §5) - footnotes. The editor emits
   sup.rt-fn-ref > a#fnref-N (inline reference) and ol.rt-footnotes >
   li.rt-fn#fn-N (the end-of-document list, back-link a.rt-fn-back appended at
   serialise). Numbers ride the ol's native counter - item order always
   matches reference order (the editor enforces it).
   The li's paragraphs render inline so the back-link sits on the text line;
   Lumos cap-trim puts display:table ::before/::after on paragraphs, which
   inside an inline p become block boxes and force the break, so they're
   turned off here. scroll-margin keeps both jump targets (#fn-N and the
   #fnref-N back-jump) clear of the fixed pill nav. */
.u-rich-text sup.rt-fn-ref a { text-decoration: none; font-weight: var(--_typography---font--primary-bold, 600); padding: 0 0.12em; }
.u-rich-text ol.rt-footnotes {
  border-top: 1px solid color-mix(in srgb, currentColor 25%, transparent);
  margin-top: var(--_spacing---space--6);
  padding-top: var(--_spacing---space--3);
  font-size: 0.875em;
  color: color-mix(in srgb, currentColor 78%, transparent);
}
.u-rich-text ol.rt-footnotes li.rt-fn { margin-block: 0.25em; }
.u-rich-text ol.rt-footnotes li.rt-fn > p { display: inline; margin: 0; }
.u-rich-text ol.rt-footnotes li.rt-fn > p::before,
.u-rich-text ol.rt-footnotes li.rt-fn > p::after { content: none; }
.u-rich-text ol.rt-footnotes li.rt-fn > p + p::before { content: " "; display: inline; }
.u-rich-text .rt-fn-back { margin-inline-start: 0.35em; text-decoration: none; }
.u-rich-text li.rt-fn,
.u-rich-text sup.rt-fn-ref a { scroll-margin-top: calc(var(--nav--height-total, 5rem) + var(--_spacing---space--3, 1rem)); }


@media (max-width: 640px) {
  .u-rich-text img.rt-img--left, .u-rich-text img.rt-img--right, .u-rich-text img.rt-img--center {
    float: none; display: block; width: 100%; max-width: 100%; margin-inline: 0;
  }
  .u-rich-text figure:has(> img.rt-img--left), .u-rich-text figure:has(> img.rt-img--right), .u-rich-text figure:has(> img.rt-img--center) {
    float: none; width: 100%; max-width: 100%; margin-inline: 0;
  }
}
/* RT v2 (PRD-richtext): the editor's canonical form for MULTI-BLOCK list items is
   <li><p>...</p></li> (single-paragraph items are unwrapped at serialise). Trim the
   outer paragraph margins so list rhythm matches plain <li>text</li>; paragraphs
   WITHIN an item keep their spacing. */
.u-rich-text li > p:first-child { margin-top: 0; }
.u-rich-text li > p:last-child { margin-bottom: 0; }

/* ===== CUSTOM OVERRIDE [CO-7]: editorial timeline - dated rail (layout from Growth & Airway) =====
   A chronological rail: the year sits in the left gutter, a hollow node dot rides the line, and the
   content (title + bullets/prose + sources) runs to the right. Net-new starter component (ADDITION).
   This CSS is STRUCTURE ONLY (rail line, node dot, gutter positioning) - ALL text is authored with
   real Lumos typography components (u-text + v-typography-* / u-rich-text), never bespoke font CSS,
   so it themes + brands automatically. In a tenant the entries are CMS-driven; the starter ships a
   static demo (pages-src/timeline.html). --rail-x is the line's x. Markup:
     .tl_rail > .tl_entry ( .tl_year > (u-text v-typography-h5 | u-text v-typography-text-small)
       | .tl_node | .tl_body > (u-text v-typography-h5 | u-text u-rich-text) + .tl_srcs > a.tl_src )
   On narrow viewports the gutter collapses (year above content). Tracked in CO-7. ===== */
/* --timeline--fill is a DISTINCT, overridable token for the scroll-drawn thread + dot + active node.
   It defaults to the brand swatch - which is exactly the colour the button-main-link hover underline
   (--_theme---text-link--border-hover) resolves to on brand/dark themes - so the thread reads as the
   brand accent on EVERY theme (the stock link-hover token is ink on LIGHT themes, which made the
   thread render dark). A tenant can override --timeline--fill (on .tl_rail or a theme) to decouple. */
.tl_rail { --rail-x: 7.25rem; --timeline--fill: var(--swatch--brand-500); position: relative; }
.tl_rail::before { content: ""; position: absolute; left: var(--rail-x); top: 0.6rem; bottom: 0.6rem; width: 1px; background: var(--_theme---border); }
.tl_entry { position: relative; padding-left: calc(var(--rail-x) + 2.25rem); padding-bottom: var(--_spacing---space--6); min-height: 2.6rem; }
.tl_entry:last-child { padding-bottom: 0; }
.tl_year { position: absolute; left: 0; top: 0.05rem; width: calc(var(--rail-x) - 1.5rem); display: flex; flex-direction: column; align-items: flex-end; gap: var(--_spacing---space--1); text-align: right; }
.tl_node { position: absolute; left: var(--rail-x); top: 0.5rem; width: 0.72rem; height: 0.72rem; border-radius: 50%; background: var(--_theme---background); border: 1.5px solid var(--_theme---text); transform: translate(-50%, -50%); z-index: 2; transition: background .35s, border-color .35s, transform .35s; }
/* scroll-drawn thread: a fill line + a travelling dot that soft-snaps to each node (driven by scripts.js wireTimeline; both stay 0/hidden under prefers-reduced-motion or no-JS) */
.tl_fill { position: absolute; left: var(--rail-x); top: 0.6rem; width: 2px; height: 0; transform: translateX(-0.5px); background: var(--timeline--fill); opacity: .5; pointer-events: none; transition: height .22s cubic-bezier(.2,.7,.3,1); z-index: 1; }
.tl_dot { position: absolute; left: var(--rail-x); top: 0; width: 0.85rem; height: 0.85rem; border-radius: 50%; background: var(--timeline--fill); transform: translate(-50%, -50%); box-shadow: 0 0 0 0.3rem color-mix(in srgb, var(--timeline--fill) 15%, transparent); opacity: 0; z-index: 3; pointer-events: none; transition: top .22s cubic-bezier(.2,.7,.3,1), opacity .3s; }
.tl_dot.is-connected { animation: tl-pulse 1.4s ease-out infinite; }
@keyframes tl-pulse { 0% { box-shadow: 0 0 0 0.3rem color-mix(in srgb, var(--timeline--fill) 22%, transparent); } 70%, 100% { box-shadow: 0 0 0 0.75rem color-mix(in srgb, var(--timeline--fill) 0%, transparent); } }
.tl_node.is-active { background: var(--timeline--fill); border-color: var(--timeline--fill); transform: translate(-50%, -50%) scale(1.08); }
.tl_body { display: flex; flex-direction: column; gap: var(--_spacing---space--3); }
.tl_srcs { display: flex; flex-wrap: wrap; gap: 0.35rem 0.9rem; }
.tl_src { font-size: 0.72rem; color: color-mix(in srgb, var(--_theme---text) 55%, transparent); text-decoration: none; display: inline-flex; align-items: center; gap: 0.3rem; transition: color 0.2s; }
.tl_src::before { content: "Source:"; letter-spacing: 0.1em; text-transform: uppercase; font-size: 0.62rem; }
.tl_src:hover { color: var(--_theme---heading-accent); }
@media (max-width: 47.9375rem) {
  .tl_rail { --rail-x: 0.5rem; }
  .tl_entry { padding-left: 1.85rem; }
  .tl_year { position: static; width: auto; align-items: flex-start; text-align: left; margin-bottom: var(--_spacing---space--2); }
  .tl_node { top: 0.55rem; }
}

/* ===== CUSTOM OVERRIDE [CO-26]: evidence record - sourced-entry list (tag-teams CO-7) =====
   The flat companion to the CO-7 timeline: a bordered row per sourced claim - inline year
   kicker in the heading, description, and the SAME .tl_srcs/.tl_src source-link treatment
   (one hover/"Source:" recipe across both surfaces). Net-new starter component (ADDITION,
   from the Kyoom /evidence page). STRUCTURE ONLY - all text is real Lumos typography.
   In a tenant the entries are CMS-driven (evidences + categories collections); the starter
   ships a static demo (pages-src/timeline.html, after the rail). Markup:
     ul.ev_list[role=list] > li.ev_item ( u-text v-typography-h5 > h3 > span.ev_year + title
       | u-text u-rich-text > p | .tl_srcs > a.tl_src[target=_blank rel="noopener nofollow"] )
   External links carry an &#8599; arrow in a aria-hidden span. Tracked in CO-26. ===== */
.ev_list { list-style: none; margin: 0; padding: 0; }
.ev_item { border-top: var(--border-width--main) solid var(--_theme---border); padding-block: var(--_spacing---space--4); display: grid; gap: var(--_spacing---space--2); }
.ev_item:last-child { border-bottom: var(--border-width--main) solid var(--_theme---border); }
.ev_year { font-size: 0.72rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--swatch--brand-500); margin-right: 0.6rem; vertical-align: 0.12em; }

/* CO-26 vocabulary (P2.B3b) - the sourced-list extras that ride an evidence record:
   dated publication rows, dot-bulleted lists, tag pills, small notes and inline source
   links. Shared across CO-26/CO-7 surfaces exactly like .tl_srcs/.tl_src (a bullets
   list on a timeline page and an evidence page is ONE vocabulary). .ev_pubs is the
   pub-row wrapper hook (contract name, no CSS of its own). Values ride --_ev---*
   inheriting slots with neutral fallbacks; muted text is a color-mix channel, NOT
   opacity - links inside keep their own full-strength colour. */
.ev_pub { border-top: var(--border-width--main) solid var(--_theme---border); padding-block: var(--_spacing---space--3); display: grid; grid-template-columns: var(--_ev---pub--date-track, 7.5rem) 1fr; gap: var(--_spacing---space--3); align-items: baseline; }
.ev_pub time { font-family: var(--_ev---pub--time-family, inherit); font-size: var(--_ev---pub--time-size, 0.74rem); letter-spacing: 0.08em; text-transform: uppercase; color: var(--_ev---muted--color, color-mix(in srgb, var(--_theme---text) 65%, transparent)); }
.ev_pub a { color: var(--_theme---text); text-decoration: none; transition: color .2s; }
.ev_pub a:hover { color: var(--_theme---heading-accent); }
.ev_bullets { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--_spacing---space--2); }
.ev_bullets li { position: relative; padding-left: 1.2rem; }
.ev_bullets li::before { content: ""; position: absolute; left: 0; top: 0.62em; width: 0.34rem; height: 0.34rem; border-radius: 100vw; background: var(--_ev---bullet--color, var(--_theme---heading-accent)); }
.ev_tags { display: flex; flex-wrap: wrap; gap: var(--_spacing---space--2); margin: 0; padding: 0; list-style: none; }
/* P5.C3: span children join li — the pill row also serves running-text form
   (p.ev_tags > span, the G&A pathway tags; a ul/li recast would add probe
   landmarks the conversion doctrine forbids). Same values, either anatomy. */
.ev_tags li, .ev_tags span { font-family: var(--_ev---tag--family, inherit); font-size: var(--_ev---tag--size, 0.78rem); border: var(--border-width--main) solid var(--_theme---border); border-radius: 100vw; padding: 0.35rem 0.9rem; }
.ev_note { font-size: var(--_ev---note--size, 0.85rem); color: var(--_ev---muted--color, color-mix(in srgb, var(--_theme---text) 65%, transparent)); max-width: 60ch; }
.ev_inline_src { font-family: var(--_ev---inline-src--family, inherit); font-size: var(--_ev---inline-src--size, 0.7rem); color: var(--_ev---muted--color, color-mix(in srgb, var(--_theme---text) 65%, transparent)); text-decoration: none; transition: color .2s; }
.ev_inline_src:hover { color: var(--_theme---heading-accent); }

/* ---------------------------------------------------------------------
   Nav — active / current-page indicator. Desktop pill: a small brand dot centred under the
   current item. Mobile drawer (full-width, text-left rows) would float the dot mid-row, so
   there the current row's text takes the brand colour instead. JS (wireActiveNav, scripts.js)
   adds .is-current to the matching top-level link / dropdown parent — works for both the
   deployed clean URLs (/about) and the flat static preview (/pages/about.html). */
/* Dot anchors on the LINK, not .nav_links_text — the text carries u-text-style-* whose cap-height
   trim owns ::after, so a dot there collides (squished box + shifts the label). The link is
   height:100% + align-items:center, so offset from its centre: text-bottom is ~0.6rem below centre,
   place the dot just under it. */
.nav_links_wrap.v-menu-desktop .nav_links_link.is-current::after {
  content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, 0.85rem);
  width: 0.3rem; height: 0.3rem; border-radius: 100vw; background-color: var(--swatch--brand-500);
}
.nav_mobile_menu_wrap .nav_links_link.is-current .nav_links_text { color: var(--swatch--brand-500); }
/* Current page inside an open dropdown (a Services/Industries child): tint + keep it lit.
   The parent toggle also gets .is-current (the dot above), so the section reads as active
   even while the dropdown is closed. wireActiveNav sets both. */
.nav_dropdown_link.is-current .nav_dropdown_text { color: var(--swatch--brand-500); opacity: 1; }

/* ---------------------------------------------------------------------
   Pill-expand (starter component) — a title chip that reveals a description.
   Desktop (hover-capable): description collapsed, reveals on hover/focus (grid-rows 0fr->1fr).
   Base (no hover + no JS) = OPEN, so it's progressive-enhancement safe (content always readable).
   Touch: wirePillExpand (scripts.js) adds .pill_expand--tap (collapse) + toggles .is-open on tap,
   with aria-expanded. Mirrors the live Kyoom hover pill and adds the mobile tap. */
.pill_expand {
  flex: 0 0 auto; align-self: start; text-align: left; cursor: pointer; border-radius: var(--radius--main);
  background-color: color-mix(in hsl, currentColor 10%, transparent);
  padding: var(--_spacing---space--4);
}
/* mask width:0 + min-width:100% ties the chip's width to its HEADING (the description
   contributes 0 to the chip's intrinsic width, then fills the heading-width box + wraps). */
.pill_expand_mask { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 220ms ease; width: 0; min-width: 100%; }
.pill_expand_clip { overflow: hidden; }
/* Even padding: zero the title's trailing .u-text margin, and let the description carry the
   title->body gap (space-4). Collapsed, the description + its margin are clipped away, so the
   chip's box stays symmetric — no per-instance u-margin utilities needed in the markup. */
.pill_expand > .u-text { margin-bottom: 0; }
.pill_expand_clip > .u-text { margin-top: var(--_spacing---space--4); margin-bottom: 0; }
@media (hover: hover) {
  .pill_expand .pill_expand_mask { grid-template-rows: 0fr; }
  .pill_expand:is(:hover, :focus-within) .pill_expand_mask { grid-template-rows: 1fr; }
}
.pill_expand--tap .pill_expand_mask { grid-template-rows: 0fr; }
.pill_expand--tap.is-open .pill_expand_mask { grid-template-rows: 1fr; }

/* Legal / ToC component (starter, CO-35, P2.B5) - the legal-page pattern: a sticky
   scrollspy contents rail + a measured prose body + a bordered data table + a
   dateline. Generic for every tenant's policy pages (G&A privacy is the first
   consumer; class-map §4 REC). Parts: .legal_wrap (grid shell), .legal_toc (the
   rail; links gain .is-active + aria-current from the scripts.js wireLegalToc PE),
   .legal_body (prose + section bars on h2), .legal_table (the starter's ONLY table
   styling - promoting it to a general table variant is a named post-cutover
   question), .legal_meta (dateline). Values ride --_legal---* inheriting slots
   with neutral fallbacks (theme roles / typography tokens); a tenant re-points
   them at/below the component. The h2 section bar mints its own slots (a legal
   bar, a card accent and a step bar diverge independently - the CO-31/CO-33
   coded-channel idea). The 60em stack boundary is structural, not tokenable. */
.legal_wrap {
  display: grid;
  grid-template-columns: var(--_legal---toc--width, 16rem) 1fr;
  gap: var(--_legal---grid--row-gap, var(--_spacing---space--6)) var(--_legal---grid--column-gap, var(--_spacing---space--7));
  align-items: start;
}
.legal_toc {
  position: sticky;
  top: var(--_legal---toc--top, var(--_spacing---space--5));
  font-family: var(--_legal---toc--family, var(--_typography---font--label-family, var(--_typography---font--primary-family)));
  font-size: var(--_legal---toc--size, 0.85rem);
  line-height: var(--_legal---toc--line-height, 1.5);
}
@container (width < 60em) {
  .legal_wrap { grid-template-columns: 1fr; gap: var(--_legal---grid--stack-gap, var(--_spacing---space--5)); }
  .legal_toc { position: static; }
}
.legal_toc p {
  text-transform: uppercase;
  letter-spacing: var(--_legal---toc-label--letter-spacing, 0.1em);
  font-size: var(--_legal---toc-label--size, 0.7rem);
  color: var(--_legal---toc-label--color, color-mix(in srgb, var(--_theme---text) 65%, transparent));
  margin: 0 0 var(--_legal---toc-label--space, var(--_spacing---space--3));
}
.legal_toc a {
  display: block;
  padding: var(--_legal---toc-link--padding, 0.35rem 0 0.35rem 0.7rem);
  color: var(--_legal---toc-link--color, var(--_theme---text));
  text-decoration: none;
  border-bottom: var(--border-width--main) solid var(--_legal---toc-link--border, var(--_theme---border));
  box-shadow: inset var(--_legal---toc--active-bar, 0.125rem) 0 0 transparent;
  transition: color .2s, box-shadow .2s;
}
.legal_toc a:hover { color: var(--_legal---toc--active-color, var(--_theme---heading-accent)); }
.legal_toc a.is-active {
  color: var(--_legal---toc--active-color, var(--_theme---heading-accent));
  font-weight: var(--_legal---toc--active-weight, 500);
  box-shadow: inset var(--_legal---toc--active-bar, 0.125rem) 0 0 var(--_legal---toc--active-color, var(--_theme---heading-accent));
}
.legal_body { max-width: var(--_legal---body--measure, 68ch); }
.legal_body h2 {
  font-family: var(--_legal---h2--family, var(--_typography---font--secondary-family, var(--_typography---font--primary-family)));
  font-weight: var(--_legal---h2--weight, var(--_typography---font--primary-light, 300));
  font-size: var(--_legal---h2--size, var(--_typography---font-size--h5));
  line-height: var(--_legal---h2--line-height, 1.2);
  letter-spacing: var(--_legal---h2--letter-spacing, -0.01em);
  color: var(--_legal---h2--color, var(--_theme---heading));
  margin: var(--_legal---h2--space-above, var(--_spacing---space--6)) 0 var(--_legal---h2--space-below, var(--_spacing---space--3));
  padding-top: var(--_legal---bar--space, var(--_spacing---space--3));
  border-top: var(--_legal---bar--width, 0.125rem) solid var(--_legal---bar--color, var(--_theme---heading-accent));
  scroll-margin-top: var(--_legal---h2--scroll-margin, var(--_spacing---space--5));
}
.legal_body h2:first-child { margin-top: 0; }
.legal_body h3 {
  font-size: var(--_legal---h3--size, 1.05rem);
  font-weight: var(--_legal---h3--weight, 600);
  color: var(--_legal---h3--color, var(--_theme---heading));
  margin: var(--_legal---h3--space-above, var(--_spacing---space--4)) 0 var(--_legal---h3--space-below, var(--_spacing---space--2));
}
.legal_body p, .legal_body li {
  font-size: var(--_legal---body--size, 1rem);
  line-height: var(--_legal---body--line-height, 1.75);
  color: var(--_legal---body--color, var(--_theme---text));
}
.legal_body p { margin: 0 0 var(--_legal---body--space, var(--_spacing---space--3)); }
/* the framework list reset (lumos:2491 — ul,ol{list-style:""} + li{display:block})
   strips markers in every authored context; the kit part restores them. Promoted
   from G&A's is-complaints scope at the privacy/accessibility rebuild (P4 utility
   tail) — legal prose renders disc + decimal. */
.legal_body ul, .legal_body ol { margin: 0 0 var(--_legal---body--space, var(--_spacing---space--3)); padding-left: var(--_legal---list--indent, 1.2rem); }
.legal_body ul { list-style: disc; }
.legal_body ol { list-style: decimal; }
.legal_body :is(ul, ol) > li { display: list-item; }
.legal_body li { margin: 0 0 var(--_legal---list--gap, 0.4rem); }
.legal_body a { color: var(--_legal---link--color, var(--_theme---heading-accent)); }
.legal_body strong { color: var(--_legal---strong--color, var(--_theme---heading)); font-weight: var(--_legal---strong--weight, 600); }
.legal_table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 var(--_legal---body--space, var(--_spacing---space--3));
  font-size: var(--_legal---table--size, 0.92rem);
}
.legal_table th, .legal_table td {
  text-align: left;
  vertical-align: top;
  padding: var(--_legal---table--cell-padding, 0.6rem 0.8rem 0.6rem 0);
  border-bottom: var(--border-width--main) solid var(--_legal---table--border, var(--_theme---border));
  line-height: var(--_legal---table--line-height, 1.6);
}
.legal_table th {
  font-family: var(--_legal---table-th--family, var(--_typography---font--label-family, var(--_typography---font--primary-family)));
  font-weight: var(--_legal---table-th--weight, 600);
  color: var(--_legal---table-th--color, var(--_theme---heading));
}
.legal_meta {
  font-family: var(--_legal---meta--family, var(--_typography---font--label-family, var(--_typography---font--primary-family)));
  font-size: var(--_legal---meta--size, 0.82rem);
  letter-spacing: var(--_legal---meta--letter-spacing, 0.04em);
  color: var(--_legal---meta--color, color-mix(in srgb, var(--_theme---text) 65%, transparent));
  margin: var(--_legal---meta--space, var(--_spacing---space--3)) 0 0;
}

/* CUSTOM OVERRIDE [CO-36] Breakout-panel furniture — the part classes, media fill
   and load stagger for v-layout-breakout-panel (the layout variant lives in
   build-lumos.sh block 7; PRD-ga-componentisation s12 C1c — recast from the earlier
   split component onto the layout variant axis). .layout_panel_content = the
   content cap inside column-1 (max-width/padding channels, pointer re-enable over
   the pointer-inert container mirror); .layout_panel_ticks = ruler gradient on the
   panel's leading edge; .layout_panel_fig = rotated vertical fig-caption (a <b>
   lead-in keeps its own color channel). Media children FILL the panel (img/
   picture/video/.compare_component). data-layout-rise on the WRAPPER staggers the
   content children then the panel (owns its keyframe; reduced-motion reveals in
   place). Values ride --_layout---panel--* inheriting slots, neutral fallbacks. */
.layout_panel_content{ max-width:var(--_layout---panel--content-max, 30rem); padding:var(--_layout---panel--content-pad, var(--_spacing---space--7)) 0; pointer-events:auto; }
.u-layout-column-1:where(.v-layout-breakout-panel){ pointer-events:none; }
.u-layout-column-1:where(.v-layout-breakout-panel) .layout_panel_content{ pointer-events:auto; }
.u-layout-column-2:where(.v-layout-breakout-panel) > img,
.u-layout-column-2:where(.v-layout-breakout-panel) > picture,
.u-layout-column-2:where(.v-layout-breakout-panel) > video,
.u-layout-column-2:where(.v-layout-breakout-panel) > .compare_component{ width:100%; height:100%; aspect-ratio:auto; border:none; object-fit:cover; }
.layout_panel_ticks{ position:absolute; left:-1px; top:var(--_layout---panel--ticks-inset, 8%); bottom:var(--_layout---panel--ticks-inset, 8%); width:var(--_layout---panel--ticks-width, 9px); z-index:3;
  background:repeating-linear-gradient(to bottom, var(--_layout---panel--ticks-color, var(--_theme---border)) 0 1px, transparent 1px var(--_layout---panel--ticks-pitch, 26px)); }
.layout_panel_fig{ position:absolute; right:calc(100% + var(--_layout---panel--fig-gap, 0.55rem)); top:50%; transform:translateY(-50%) rotate(180deg);
  writing-mode:vertical-rl; z-index:3; white-space:nowrap;
  font-family:var(--_layout---panel--fig-font, inherit); font-size:var(--_layout---panel--fig-size, 0.75em);
  letter-spacing:var(--_layout---panel--fig-tracking, 0.12em); text-transform:uppercase;
  color:var(--_layout---panel--fig-color, color-mix(in srgb, var(--_theme---text) 65%, transparent)); }
.layout_panel_fig b{ color:var(--_layout---panel--fig-lead-color, inherit); }
@keyframes layout-panel-rise{ from{ opacity:0; transform:translateY(1.4rem); } to{ opacity:1; transform:none; } }
.u-layout-wrapper[data-layout-rise]:where(.v-layout-breakout-panel) .layout_panel_content > *{ opacity:0; animation:layout-panel-rise .9s cubic-bezier(.2,.7,.3,1) forwards; }
.u-layout-wrapper[data-layout-rise]:where(.v-layout-breakout-panel) .layout_panel_content > :nth-child(2){ animation-delay:.06s; }
.u-layout-wrapper[data-layout-rise]:where(.v-layout-breakout-panel) .layout_panel_content > :nth-child(3){ animation-delay:.16s; }
.u-layout-wrapper[data-layout-rise]:where(.v-layout-breakout-panel) .layout_panel_content > :nth-child(4){ animation-delay:.26s; }
.u-layout-wrapper[data-layout-rise]:where(.v-layout-breakout-panel) .layout_panel_content > :nth-child(5){ animation-delay:.36s; }
.u-layout-wrapper[data-layout-rise]:where(.v-layout-breakout-panel) .u-layout-column-2{ opacity:0; animation:layout-panel-rise .9s cubic-bezier(.2,.7,.3,1) .3s forwards; }
@media (prefers-reduced-motion:reduce){
  .u-layout-wrapper[data-layout-rise]:where(.v-layout-breakout-panel) .layout_panel_content > *,
  .u-layout-wrapper[data-layout-rise]:where(.v-layout-breakout-panel) .u-layout-column-2{ opacity:1; animation:none; }
}
@media (max-width:54em){
  .layout_panel_content{ max-width:none; }
  .u-layout-column-2:where(.v-layout-breakout-panel) > img,
  .u-layout-column-2:where(.v-layout-breakout-panel) > picture,
  .u-layout-column-2:where(.v-layout-breakout-panel) > video,
  .u-layout-column-2:where(.v-layout-breakout-panel) > .compare_component{ height:auto; aspect-ratio:var(--_layout---panel--mobile-ratio, 4/5); }
  .layout_panel_ticks{ display:none; }
  .layout_panel_fig{ position:static; transform:none; writing-mode:horizontal-tb; white-space:normal; display:flex; align-items:baseline; gap:0.5rem; margin-top:var(--_spacing---space--3); }
}

/* CUSTOM OVERRIDE [CO-37] Taglist — dot-separated inline tag row (PRD-ga-componentisation
   §12 C1; G&A hero symptom tags are the first consumer). Plain <span> children, a dot
   separator between items via ::after (never trailing). Distinct from the pill tag
   component (.tag_wrap): this is running-text furniture, not a badge. Values ride
   --_taglist---* slots with neutral fallbacks; typography composes in markup or rides
   the font channels. */
.taglist_wrap{ display:flex; flex-wrap:wrap; max-width:var(--_taglist---max-width, none); }
.taglist_wrap > span{ font-family:var(--_taglist---font-family, inherit); font-size:var(--_taglist---font-size, 0.75em);
  color:var(--_taglist---color, var(--_theme---text)); opacity:var(--_taglist---opacity, 1); position:relative;
  padding:var(--_taglist---pad-block, 0.3em) var(--_taglist---gap, 1em) var(--_taglist---pad-block, 0.3em) 0; margin-right:var(--_taglist---gap, 1em); }
.taglist_wrap > span:not(:last-child)::after{ content:""; position:absolute; right:0; top:50%;
  width:var(--_taglist---dot--size, 0.25em); height:var(--_taglist---dot--size, 0.25em); border-radius:50%;
  background:var(--_taglist---dot--color, currentColor); transform:translateY(-50%); }

/* CUSTOM OVERRIDE [CO-38] Scroll-reveal engine — the GSAP-free tier of the starter motion
   layer (PRD-ga-componentisation §12 C3 rider; reconciled from G&A's tenant .u-reveal io
   convention, the framework-cutover collision's proper fix). MARKUP: data-reveal on an
   element hides it until it scrolls in (scripts.js wireReveal adds .is-in via one shared
   IntersectionObserver); data-reveal-group on a container staggers its DIRECT [data-reveal]
   children (optional value = per-child step in seconds, default .07 — wireReveal sets
   transition-delay). Variants: fade, left, right, scale and clip. Per-carrier controls:
   data-reveal-delay/duration (milliseconds), data-reveal-ease and data-reveal-rise. The
   hidden state accepts both html.js and html.js-enabled PE flags, so no-JS renders in
   place; reduced-motion reveals in place. Values ride
   --_reveal---* slots. DISTINCT from lumos-native's .u-reveal (a FILLED load-time entrance
   animation — filled animations override normal declarations, which is why the two
   conventions could never share a class name) and from motion.js's GSAP sequence engines
   (data-animate-order / data-seq-*): this tier needs no library. */
[data-reveal]{ --_reveal---from-x:0px; --_reveal---from-y:var(--_reveal---rise, 1.1rem); --_reveal---from-scale:1; --_reveal---from-clip:inset(0 0 0 0); --_reveal---to-clip:inset(0 0 0 0); transition:opacity var(--_reveal---duration, .8s) ease var(--_reveal---delay, 0s), transform var(--_reveal---duration, .8s) var(--_reveal---ease, cubic-bezier(.2,.7,.3,1)) var(--_reveal---delay, 0s), clip-path var(--_reveal---duration, .8s) var(--_reveal---ease, cubic-bezier(.2,.7,.3,1)) var(--_reveal---delay, 0s); }
[data-reveal="fade"]{ --_reveal---from-y:0px; }
[data-reveal="left"]{ --_reveal---from-x:calc(var(--_reveal---rise, 1.1rem) * -1); --_reveal---from-y:0px; }
[data-reveal="right"]{ --_reveal---from-x:var(--_reveal---rise, 1.1rem); --_reveal---from-y:0px; }
[data-reveal="scale"]{ --_reveal---from-y:0px; --_reveal---from-scale:.96; }
[data-reveal="clip"]{ --_reveal---from-y:0px; --_reveal---from-clip:inset(0 0 100% 0); }
:is(html.js,html.js-enabled) [data-reveal]:not(.is-in){ opacity:0; clip-path:var(--_reveal---from-clip); transform:translate(var(--_reveal---from-x), var(--_reveal---from-y)) scale(var(--_reveal---from-scale)); }
:is(html.js,html.js-enabled) [data-reveal].is-in{ opacity:1; clip-path:var(--_reveal---to-clip); transform:translate(0,0) scale(1); }
@media (prefers-reduced-motion: reduce){
  :is(html.js,html.js-enabled) [data-reveal]:not(.is-in){ opacity:1; clip-path:var(--_reveal---to-clip); transform:none; }
  [data-reveal]{ transition:none; }
}

/* CUSTOM OVERRIDE [CO-39] Growth thread — a scroll-drawn organic SVG line down a zone of
   sections, with a tip dot (PRD-ga-componentisation §12 C3; G&A home/your-journey/homeoblock
   science zones = the two-consumer promotion the class-map §7a flagged). .thread_zone wraps
   the sections (they stack above the line at z-index:1); .thread_svg is the absolute
   full-zone canvas; .thread_track the faint full path, .thread_prog the drawn portion,
   .thread_dot the tip. scripts.js wireThread builds the path (anchor/rail via
   data-thread-anchor / data-thread-rail descendant markers, or fractional
   data-thread-start-x/-y on the zone; data-thread-approach tunes how soon the line settles
   to the rail) and draws it with scroll. Zones with NO thread config are left untouched
   (tenant-addon stand-down window, the CO-35 spy precedent). Paint rides --_thread---*
   slots with neutral theme-role fallbacks; a tenant may point --_thread---prog--stroke at
   a url(#gradient) paint server defined in its page SVG. */
.thread_zone{ position:relative; }
.thread_zone > section{ position:relative; z-index:1; }
.thread_svg{ position:absolute; inset:0; width:100%; height:100%; z-index:0; overflow:visible; pointer-events:none; }
.thread_track{ fill:none; stroke:var(--_thread---track--color, var(--_theme---border)); stroke-width:var(--_thread---track--width, 2px); opacity:var(--_thread---track--opacity, .18); stroke-linecap:round; }
.thread_prog{ fill:none; stroke:var(--_thread---prog--stroke, var(--_theme---text)); stroke-width:var(--_thread---prog--width, 2.5px); stroke-linecap:round; }
.thread_dot{ position:absolute; width:var(--_thread---dot--size, 0.95rem); height:var(--_thread---dot--size, 0.95rem); border-radius:50%;
  background:var(--_thread---dot--color, var(--_theme---text));
  box-shadow:0 0 0 var(--_thread---dot--ring, 0.4rem) color-mix(in srgb, var(--_thread---dot--color, var(--_theme---text)) 16%, transparent);
  transform:translate(-50%,-50%); opacity:0; transition:opacity .3s; z-index:1; }

/* CUSTOM OVERRIDE [CO-40] Price list — label/amount fee rows (PRD-ga-componentisation §12
   C3; G&A fees page = first consumer; the class-map's pricing-list promote-candidate). A
   plain <ul class="price_list"> of <li> rows: content span + .price_amount, baseline-
   aligned space-between, hairline top border per row + a closing bottom border (the CO-26
   border grammar on a simpler anatomy — a fee row is label↔value, not a sourced claim,
   and keeping <li> preserves the probe tag set where a <dl> recast would not). Values
   ride --_price---* slots with neutral fallbacks; the amount family chains to the
   label-family contract slot. */
.price_list{ list-style:none; margin:var(--_price---list--space, var(--_spacing---space--4)) 0 0; padding:0; }
.price_list li{ display:flex; justify-content:space-between; align-items:baseline; gap:var(--_price---row--gap, var(--_spacing---space--4));
  padding:var(--_price---row--pad, var(--_spacing---space--3)) 0; border-top:var(--border-width--main) solid var(--_price---row--border, var(--_theme---border));
  font-size:var(--_price---row--size, inherit); line-height:var(--_price---row--leading, 1.5); color:var(--_price---row--color, var(--_theme---text)); }
.price_list li:last-child{ border-bottom:var(--border-width--main) solid var(--_price---row--border, var(--_theme---border)); }
.price_list .price_amount{ font-family:var(--_price---amount--family, var(--_typography---font--label-family, var(--_typography---font--primary-family)));
  font-size:var(--_price---amount--size, 0.95em); color:var(--_price---amount--color, var(--_theme---text)); white-space:nowrap; }

/* CUSTOM OVERRIDE [CO-57] Spec card - ruled key/value document card (PRD-yogdog-onepager
   4a-result mint #4; the yogdog registry/pedigree card is the first consumer - the neutral
   shape serves spec/fact/pricing boxes). <aside class="spec_wrap"> holding a .spec_head row
   (title span + aside span, framed) over <dl class="spec_list"> - an auto/1fr grid of
   hairline-ruled dt/dd rows: dt muted (color-mix, never opacity - the CO-26 grammar) with a
   right rule, dd emphasised, last row borderless. The whole card rides the label-family
   contract chain. Values via --_spec---* inheriting slots, neutral fallbacks. Drag/motion
   is tenant-addon business, not the component's. */
.spec_wrap{ border:var(--_spec---frame-width, var(--border-width--main)) solid var(--_spec---frame-color, var(--_theme---border));
  background-color:var(--_spec---background, var(--_theme---background));
  font-family:var(--_spec---family, var(--_typography---font--label-family, var(--_typography---font--primary-family))); }
.spec_head{ display:flex; justify-content:space-between; align-items:center; gap:var(--_spacing---space--2);
  padding:var(--_spec---head--pad, var(--_spacing---space--2)) var(--_spec---pad-inline, var(--_spacing---space--3));
  border-bottom:var(--_spec---frame-width, var(--border-width--main)) solid var(--_spec---frame-color, var(--_theme---border));
  font-size:var(--_spec---head--size, 0.625rem); font-weight:var(--_spec---head--weight, var(--_typography---font--primary-bold, 700));
  letter-spacing:var(--_spec---head--tracking, .2em); text-transform:var(--_spec---head--transform, var(--_typography---text-transform--uppercase, uppercase)); }
.spec_list{ display:grid; grid-template-columns:auto 1fr; margin:0; }
.spec_list dt,.spec_list dd{ padding:var(--_spec---row--pad, var(--_spacing---space--2)) var(--_spec---pad-inline, var(--_spacing---space--3));
  font-size:var(--_spec---row--size, 0.6875rem); letter-spacing:var(--_spec---row--tracking, .06em);
  text-transform:var(--_spec---row--transform, var(--_typography---text-transform--uppercase, uppercase));
  border-bottom:var(--_spec---rule-width, var(--border-width--main)) solid var(--_spec---rule-color, var(--_theme---border)); margin:0; }
.spec_list dt{ color:var(--_spec---term--color, color-mix(in srgb, var(--_theme---text) 65%, transparent));
  border-right:var(--_spec---rule-width, var(--border-width--main)) solid var(--_spec---rule-color, var(--_theme---border)); }
.spec_list dd{ font-weight:var(--_spec---value--weight, var(--_typography---font--primary-bold, 700)); }
.spec_list > dt:nth-last-of-type(1),.spec_list > dd:last-child{ border-bottom:0; }

/* =====================================================================
   Y3'a — one-pager page layer (PRD-yogdog-onepager §4e; mock =
   studio-brutalist-mockups.html, T·ALIVE config). Tenant-side page
   components in the kyoom pattern; values read the theme roles/swatches.
   The T·ALIVE motion CSS lives in scripts.addon.js (injected under
   html.js only — no-JS renders everything in place).  [Y3a ONE-PAGER]
   ===================================================================== */

/* --- chrome: the drawer summary is the word MENU ⇄ CLOSE ✕ (burger retired) --- */
.nav_component[data-nav-menu="drawer"] .nav_button_layout { display: none; }
/* a11y (WCAG 2.5.8, A0 audit 25 Aug): the summary's visual box is the bare word
   (~32x7px at 1440w) — extend the HIT AREA without moving any geometry. The
   ::after is absolutely positioned (not a flex item, buys no gap — the trim-pair
   trap) and rides the summary in BOTH states: in-flow closed, r3/r11-pinned open.
   Horizontal inset stays modest so it cannot reach the theme toggle beside it. */
.nav_component[data-nav-menu="drawer"] summary.nav_button_wrap { position: relative; }
.nav_component[data-nav-menu="drawer"] summary.nav_button_wrap::after { content: ""; position: absolute; inset: -0.75rem -0.5rem; }
.nav_button_word_close { display: none; }
details[data-nav-drawer][open] .nav_button_word_menu { display: none; }
details[data-nav-drawer][open] .nav_button_word_close { display: inline; }
.nav_button_word { font-weight: 700; white-space: nowrap; text-wrap: nowrap; } /* the label voice's text-wrap:pretty re-wraps CLOSE ✕ (element beats inherited nowrap, r3) */
/* r11 (mobile round): at ≤520px the sheet is 100vw, so the pinned CLOSE can sit
   exactly where MENU sits — same right edge (the site margin, which the bar's
   capped layout row uses) and centred in the bar's 4rem row, instead of the r3
   sheet-corner pin (space--3 insets read 14.1px vs MENU's 28.3/377 at 393w).
   Wider screens keep the r3 pin: the 520px sheet has its own top-right corner. */
@media (max-width: 32.5em) {
  .nav_component[data-nav-menu="drawer"] details[data-nav-drawer][open] > summary {
    top: 0;
    right: var(--site--margin);
    height: var(--nav--height);
    display: flex;
    align-items: center;
  }
}
/* r11: the drawer's 01–04 index kickers top-align to the link headings (Kris;
   the framework's baseline alignment also diverges per engine — the cap-trim
   ::before inside the block-ified span gives Chrome and Safari different
   synthesized baselines). margin-top ink-aligns index cap to heading cap,
   probe-fitted at link sizes 51.1/54px (linear in the link-size clamp). */
.nav_component[data-nav-menu="drawer"] .nav_mobile_wrap .nav_links_link { align-items: flex-start; }
.nav_link_index { margin-top: calc(var(--_nav-drawer---link--size) * 0.17 - 4.1px); }
/* r8c: opening the drawer sets body overflow:hidden (framework scroll-lock),
   which drops the scrollbar and widens the viewport — centered content
   shifted right by the scrollbar width. Reserve the gutter permanently: the
   page always overflows vertically, so at rest the real scrollbar occupies
   it (zero visual change) and the lock stops reflowing. Overlay-scrollbar
   setups (width 0) are unaffected either way. */
html { scrollbar-gutter: stable; }
/* r6: the bar paints the PAGE ground, full-bleed. The framework pill paints
   --_theme---nav--background (default resolves paper, diverging from the
   ground in dark mode) on the LAYOUT row, which r2 capped to the content
   column — full-width components (the marquee) showed past its edges.
   Re-point the slot at the ground and move the paint up to the full-bleed
   contain. The drawer sheet consumes the same slot and follows. */
.nav_component { --_theme---nav--background: var(--_theme---background); }
/* …but lumos pins a STOCK light theme ON .nav_component itself (own scope:
   background light-200, text dark-900, …), so the bar ignored the page theme
   — the ground read as hardcoded and dark mode could not move it. Mirror the
   tenant's theme roles into the nav per wrapping theme; the slot above then
   resolves the LOCAL (correct) ground. */
.u-theme-light .nav_component {
  --_theme---background: var(--swatch--light-100);
  --_theme---heading-accent: var(--swatch--dark-900);
  --_theme---border: var(--swatch--light-200);
}
.u-theme-dark .nav_component {
  --_theme---background: var(--swatch--carbon);
  --_theme---background-2: var(--swatch--carbon-2);
  --_theme---text: var(--swatch--bone);
  --_theme---heading-accent: var(--swatch--bone);
  --_theme---border: var(--swatch--soft-dark);
  /* r8: lumos pins nav button-primary at brand-500 (= ink) + the brand seed's
     light label — on the dark drawer sheet that is an invisible slab with
     bare white text (Kris's drawer CTA report). Quiet-dark button identity,
     matching the theme block. */
  --_theme---button-primary--background: var(--swatch--bone);
  --_theme---button-primary--text: var(--swatch--carbon);
  --_theme---button-primary--background-hover: var(--swatch--carbon);
  --_theme---button-primary--text-hover: var(--swatch--bone);
  --_theme---button-primary--border-hover: var(--swatch--bone);
}
/* r7b: lockup theming moved into the MARKUP (Kris's call): the wordmark
   groups (YogDog/Studios) ride fill="currentColor" and follow the theme
   text; the Mark group's fills are HARDCODED in the svg and never shift.
   No CSS path-repaint here — a fill rule would drag the Mark with it. */
.nav_component[data-nav-layout="bar"] .nav_mobile_contain { background-color: var(--_theme---nav--background); }
.nav_component[data-nav-layout="bar"] .nav_mobile_layout { background-color: transparent; }
/* bar mode is full-bleed by design ([data-nav-layout="bar"] .nav_mobile_contain
   max-width:none) — cap the LAYOUT row to the content column (parity r2) */
.nav_component[data-nav-layout="bar"] .nav_mobile_layout {
  max-width: var(--max-width--main);
  width: calc(100% - var(--site--margin) * 2);
  margin-inline: auto;
  padding-inline: 0;
}
.nav_logo_embed svg { height: clamp(1.875rem, 4vw, 2.375rem); width: auto; display: block; }
/* r9d: colour-mode SWITCH — pill track + sliding circular knob, the
   current mode's icon cut out inside the knob (sun on light, moon on dark).
   Colour = the MENU label's chain; knob motion = the site's steps(2) micro.
   JS-only control: ships [hidden], the addon reveals it. */
.theme_toggle {
  display: flex; align-items: center; margin-left: auto;  /* r9e: left of MENU, pair hugs right */
  background: none; border: 0; padding: 0.25rem; cursor: pointer;
  color: var(--_typography---label--color, color-mix(in srgb, var(--_theme---text) 65%, transparent));
}
.theme_toggle[hidden] { display: none; }
.theme_toggle_track {
  display: flex; align-items: center; box-sizing: border-box;
  width: 40px; height: 22px; padding: 2px;
  border: 2px solid currentColor;  /* r9f: hard-edged, radius 0 — the PRESS plate grammar */
}
.theme_toggle_knob {
  display: flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; background: currentColor;  /* square knob (r9f) */
  transform: translateX(0); transition: transform .08s steps(2, end);
}
.theme_toggle_ico {
  width: 10px; height: 10px; fill: none;
  stroke: var(--_theme---background); stroke-width: 2.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.theme_toggle .is-moon { display: none; }
.u-theme-dark .theme_toggle .theme_toggle_knob { transform: translateX(18px); }
.u-theme-dark .theme_toggle .is-sun { display: none; }
.u-theme-dark .theme_toggle .is-moon { display: block; }
/* no scrim on the open drawer (Kris, 24 Aug: sticky bar, no scrim) */
.nav_component:has(details[data-nav-drawer][open]) .nav_menu_backdrop { opacity: 0; }
/* anchors land clear of the sticky bar */
main [id] { scroll-margin-top: 6rem; }

/* --- hero --- */
/* mock-parity type endpoints, SCOPED (a :root seed would resize the legal pages'
   h1/h2 too) - Lumos parametric clamp form, endpoints are the only knobs */
main .u-layout.is-hero {
  --_typography---h1--font-size: clamp(2.6 * 1rem, ((2.6 - ((6.2 - 2.6) / (var(--site--viewport-max)  - var(--site--viewport-min)) * var(--site--viewport-min))) * 1rem + ((6.2 - 2.6) / (var(--site--viewport-max)  - var(--site--viewport-min))) * 100vw), 6.2 * 1rem);
  --_typography---h1--line-height: 0.92;
}
.close_h {
  --_typography---h2--font-size: clamp(2 * 1rem, ((2 - ((4.4 - 2) / (var(--site--viewport-max)  - var(--site--viewport-min)) * var(--site--viewport-min))) * 1rem + ((4.4 - 2) / (var(--site--viewport-max)  - var(--site--viewport-min))) * 100vw), 4.4 * 1rem);
  --_typography---h2--line-height: 0.95;
}
main .u-layout.v-layout-columns-set.is-hero {
  --_layout---columns--template: minmax(0, 1.6fr) minmax(0, 0.9fr); /* R4d: minmax(0,fr) — content can never grow a track */
  align-items: end;
}
/* the framework centres grid columns (align-self:center) — the explicit align
   decision every two-column family needs: hero baselines sit at the END */
main .u-layout.v-layout-columns-set.is-hero > .u-layout-column-1,
main .u-layout.v-layout-columns-set.is-hero > .u-layout-column-2 { align-self: end; }
@container (width < 48em) {
  main .u-layout.v-layout-columns-set.is-hero { --_layout---columns--template: 1fr; align-items: start; }
}
.hero_coords p { display: flex; gap: 1.125rem; flex-wrap: wrap; align-items: baseline; }
/* r5: the framework cap-trim's p::before becomes a zero-width FLEX ITEM inside
   this flex p — the leading one buys a phantom 18px gap before the first span
   (read as an eyebrow indent). The kit's own exemption carries the fix: the p
   wears u-text-trim-off (markup), which the trim rule's :not() excludes — an
   unlayered CSS kill here loses to the trim's (0,2,2). */
.hero_coord_sep { font-size: 0.5625rem; transform: translateY(-1px); }
/* r11c: the eyebrow needs 361.5px at the label's .14em tracking — one line
   only on ≥402pt phones (borderline even there per engine), and the wrapped
   state showed the 18px flex gap as an oversized row gap (Kris). .07em +
   12px gaps = 318.6px, one line down to 360pt with slack; the 0.5rem
   row-gap tidies any residual wrap on narrower devices. */
@media (max-width: 40em) {
  .hero_coords { letter-spacing: 0.07em; }
  .hero_coords p { gap: 0.5rem 0.75rem; }
}
.hero_cycle { display: block; width: max-content; max-width: 100%; } /* cycling word owns its line and its ink bar stays word-sized */
.hero_cta_row { display: flex; gap: 0.875rem; align-items: center; flex-wrap: wrap; }
.hero_cta_row .u-text { margin-block: 0; }  /* the note's voice margin skewed the centring (r2) */
.hero_reg { cursor: grab; user-select: none; -webkit-user-select: none; }
.hero_reg.is-lift { cursor: grabbing; box-shadow: 0.5rem 0.5rem 0 var(--_theme---text); z-index: 40; position: relative; }
.hero_reg_mark { color: var(--_theme---heading-accent); }
.spec_live { color: var(--_theme---heading-accent); }

/* --- ticker (seamless: gap 0 so the -50% loop lands on an identical frame) --- */
.ticker_strip { border-block: var(--border-width--main) solid var(--_theme---border); }
.ticker_strip .u-marquee_track { gap: 0; padding-block: 0.75rem; }
.ticker_strip .u-marquee_track b { margin: 0 1.125rem; font-weight: var(--_typography---font--primary-bold, 700); }

/* --- section heads (01 · WORK · side note) --- */
/* r11: the labels TOP-align to the heading (Kris, all widths). baseline was
   also engine-divergent: the cap-trim ::before inside each block-ified span
   synthesizes different flex baselines in Chrome vs iOS Safari (bench read
   top-ish, Kris's phone read bottom). flex-start is deterministic; margin-top
   ink-aligns label cap to heading cap — probe-fitted at 393w (Δ2.83px, h3
   36.8px) and 1440w (Δ5.29px, h3 48px), linear in vw like the h3 clamp. */
.sec_head { display: flex; align-items: flex-start; gap: 1rem; margin-bottom: clamp(1.375rem, 3.5vw, 2.5rem); }
.sec_no { color: var(--_theme---heading-accent); font-weight: 700; }
/* the heading wrapper's voice applier carries margin-top = space--7 (measured
   equal at 393/1440); the labels ride the same token so the row keeps its
   height and the ink-nudge stays the only free variable */
.sec_no, .sec_side { margin-top: calc(var(--_spacing---space--7) + clamp(2.6px, calc(1.9px + 0.235vw), 5.3px)); }
.sec_side { margin-left: auto; color: var(--swatch--faint); }
@media (max-width: 40em) { .sec_side { display: none; } }

/* --- work rows --- */
.work_list { border-top: 2px solid var(--_theme---text); }
.work_row {
  display: grid; grid-template-columns: 3.5rem 1.2fr 1fr 11.25rem; gap: 0.875rem; align-items: center;
  padding: 1rem 0; border-bottom: var(--border-width--main) solid var(--_theme---border);
}
/* r7: the row swatches were theme-blind (light-200 hover painted a light slab
   in the dark band; faint/dark-800 pinned light-mode greys). Theme-relative
   ink washes read from the wrapping theme — an 8% text wash is a subtle
   darken on light and a subtle lighten on dark (the CO-26 color-mix grammar,
   never opacity). */
.work_row:hover { background: color-mix(in srgb, var(--_theme---text) 8%, transparent); }
.work_ix { color: color-mix(in srgb, var(--_theme---text) 58%, transparent); } /* a11y A2: 45%→58% — renders #757575 light (4.61:1) / #8b8a86 dark (~5.2:1); 45% failed both (3.03 / 3.78) */
.work_name { font-weight: 900; text-transform: uppercase; font-size: clamp(1.05rem, 2.2vw, 1.5rem); letter-spacing: -0.01em; }
.work_what { color: color-mix(in srgb, var(--_theme---text) 65%, transparent); }
.work_st { font-weight: 700; text-align: right; }
.work_st.is-live { color: var(--_theme---heading-accent); }
@media (max-width: 43.75em) { .work_row { grid-template-columns: 2.5rem 1fr 7.5rem; } .work_what { display: none; } }

/* --- services lanes + platform band --- */
.lane_cell { display: flex; flex-direction: column; }
/* the h4/main tier margins (CO-45 top applier + tier bottoms) blew the lane rhythm
   apart (parity r1) — zero the blocks, state the mock's 10px steps explicitly */
.lane_cell > .u-text { margin-block: 0; }
.lane_cell > .u-text + .u-text { margin-top: 1rem; }  /* r2: more air heading->para */
.lane_cell {
  --_typography---text-main--font-size: 0.90625rem;   /* the mock's 14.5px lane copy (r2) */
  --_typography---text-main--line-height: 1.55;
}
.lane_k { color: var(--_theme---heading-accent); font-weight: 700; letter-spacing: 0.2em; margin-bottom: 1.125rem; }  /* r5: more air label->heading */
.lane_list { list-style: none; margin: auto 0 0; padding: 2.125rem 0 0; }
.lane_list li { padding: 0.4375rem 0; border-top: var(--border-width--main) solid var(--_theme---border); }
/* r10 (Kris): the band is an INVERSION component — its ground is the wrapping
   theme's TEXT token and its text the theme's BACKGROUND token (the u-theme-dark
   markup wrap + paper pins are gone: those blended into the carbon page in dark
   mode). Light: ink plate, paper type (the mock, unchanged). Dark: bone plate,
   carbon type. */
.platform_band {
  border: 2px solid var(--_theme---text); border-top: 0; /* attaches under the ruled lanes */
  background: var(--_theme---text); color: var(--_theme---background);
  /* the label VOICE colours over the theme text — invisible on this inverted
     plate; re-seed its channel to the inverted ground for every band label */
  --_typography---label--color: color-mix(in srgb, var(--_theme---background) 75%, transparent);
  padding: clamp(1.125rem, 3vw, 1.625rem) clamp(1.25rem, 3vw, 2rem);
  display: flex; gap: 1.125rem; align-items: center; flex-wrap: wrap;
}
/* r10d (Kris): the full inversion is too bright in dark — there the band is
   a lifted grey plate with the normal light text, matching the card family;
   light keeps the mock's ink/paper inversion. */
.u-theme-dark .platform_band {
  background: color-mix(in srgb, var(--_theme---text) 8%, var(--_theme---background));
  color: var(--_theme---text);
  --_typography---label--color: color-mix(in srgb, var(--_theme---text) 65%, transparent);
}
.u-theme-dark .platform_band_stats b { color: var(--_theme---text); }
.platform_band_h { font-weight: 900; text-transform: uppercase; font-size: clamp(1rem, 2vw, 1.3rem); letter-spacing: 0.01em; margin-bottom: 0.5rem; } /* r11: air under the heading (Kris) */
.platform_band_stats { margin-left: auto; display: flex; gap: 1.375rem; }
.platform_band_stats b { color: var(--_theme---background); display: block; font-size: clamp(1.2rem, 2.4vw, 1.7rem); font-weight: 700; letter-spacing: 0; }
@media (max-width: 51.25em) { .platform_band_stats { margin-left: 0; width: 100%; } }
/* r11: the sub-label carries NO max-width (measured none) — the early wrap Kris
   read as one is the label voice's text-wrap:pretty balancing a forced wrap
   (10.5px mono at .14em tracking = ~342px, band content 317px at 393w) into a
   short first line. Natural wrap + .09em tracking (~319px) = one line on
   ≥390pt phones; the stat's left indent was the cap-trim ::before phantom flex
   item eating one 22px gap (u-text-trim-off in the markup kills it, hero r5). */
@media (max-width: 40em) {
  .platform_band_p { letter-spacing: 0.09em; text-wrap: normal; }
}

/* --- process / platform cells (CO-33 items inside CO-56 ruled grids) --- */
:root {
  /* the mock cells carry no top bar (the ruled grid draws the rules) and the
     numeral is a small mono kicker, not the CO-33 display numeral */
  --_steps---bar--width: 0px;
  /* bar--space stays at its default: it IS the cell's padding-top, so zeroing it
     un-squares the u-padding-4 cells (measured Y3'a) */
  --_steps---number--size: 0.6875rem;
  --_steps---number--weight: var(--_typography---font--primary-bold, 700);
  --_steps---number--family: var(--_typography---font--label-family, var(--_typography---font--primary-family));
  --_steps---number--line-height: 1.4;
  --_steps---number--space: 0.75rem;
}
.u-grid[data-grid-ruled] .steps_item { position: relative; transition: background-color .08s steps(1, end); }
.u-grid[data-grid-ruled] .steps_item:hover { background-color: color-mix(in srgb, var(--_theme---text) 8%, var(--_theme---background)); }  /* r10c: OPAQUE wash — the cell's ground paint is what hides the grid's text-coloured rule frame, so a transparent wash REVEALED it as a solid slab (r8's miss); mixing into the ground keeps the cell opaque in both modes */
.u-grid[data-grid-ruled] .steps_item h3 { margin-bottom: 0.75rem; }  /* r2: more air */
.u-grid[data-grid-ruled] .steps_item {
  --_typography---text-small--font-size: 0.84375rem;  /* the mock's 13.5px step copy (r2) */
  --_typography---text-small--line-height: 1.5;
}
.plat_ico {
  position: absolute; top: clamp(1rem, 2.6vw, 1.625rem); right: clamp(1rem, 2.6vw, 1.625rem);
  width: 1.625rem; height: 1.625rem; fill: none;
  stroke: color-mix(in srgb, var(--_theme---text) 30%, transparent);
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

/* --- buttons: the mock's mono slab voice (no CO-50 family channel exists) --- */
.button_main_text {
  font-family: var(--_typography---font--label-family, var(--_typography---font--primary-family));
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* --- consent surfaces (Y3'b): the CMP mounts at body end, OUTSIDE the wrapper
   that sets the fonts, so its defaults render UA serif + a generic button
   voice. Re-dress in the page vocabulary — Archivo ground, 900 uppercase
   headings, mono slab buttons, hard 2px plate edges. Colour already rides the
   theme roles (correct in both modes); typography + edges only here. The CMP
   sheet injects AFTER this one, so every override is id-scoped to outrank it. */
#yd-consent .yd-consent-inner, #yd-consent-modal .yd-consent-box {
  font-family: var(--_typography---font--primary-family);
}
#yd-consent.yd-consent--bar-bottom .yd-consent-inner {
  border-top: 2px solid var(--_theme---text);   /* the hard-rule grammar, not the role proxy */
  box-shadow: none;
}
#yd-consent .yd-consent-title, #yd-consent-modal .yd-consent-title {
  font-weight: 900; text-transform: uppercase;
  font-size: clamp(1rem, 2vw, 1.3rem); letter-spacing: 0.01em;   /* .platform_band_h scale */
}
#yd-consent .yd-consent-btn, #yd-consent-modal .yd-consent-btn {
  font-family: var(--_typography---font--label-family, var(--_typography---font--primary-family));
  font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  font-size: var(--button--regular--font-size, 0.8125rem);
  border-radius: 0; padding: 0.7em 1.2em;
}
#yd-consent .yd-consent-btn--secondary, #yd-consent-modal .yd-consent-btn--secondary {
  border: 2px solid var(--_theme---text);       /* enquiry hard-plate edge */
  background: var(--_theme---background); color: var(--_theme---text);
}
#yd-consent .yd-consent-btn--secondary:hover, #yd-consent-modal .yd-consent-btn--secondary:hover {
  background: var(--_theme---text); color: var(--_theme---background);
  border-color: var(--_theme---text);           /* press inversion */
}
#yd-consent .yd-consent-inlinecat {
  font-family: var(--_typography---font--label-family, var(--_typography---font--primary-family));
  font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
}
#yd-consent-modal .yd-consent-cat-name { font-weight: 700; }

/* --- contact + footer strip --- */
.enquiry_wrap { max-width: 45rem; }
/* mock form grammar (parity r1): 18px gaps both axes (the raised 10.5px label
   fits the row gap exactly), 2px ink fields in the mono voice, hard-plate focus */
.enquiry_wrap .form_fieldset_list { grid-column-gap: 1.125rem; grid-row-gap: 1.125rem; }
.enquiry_wrap .form_field {
  /* r8: theme-relative (were dark-900/light-100 swatches — white slabs +
     invisible edges in the dark band). Fields ride the PAGE ground; the hard
     2px plate edge is the theme text, matching the PRESS hard-plate grammar. */
  border: 2px solid var(--_theme---text);
  border-radius: 0;
  background-color: var(--_theme---background);
  color: var(--_theme---text);
  font-family: var(--_typography---font--label-family, var(--_typography---font--primary-family));
  font-size: 0.875rem;
  height: auto;
  padding: 0.75rem 0.875rem;
  transition: transform .08s steps(2, end), box-shadow .08s steps(2, end);
}
.enquiry_wrap .form_field:focus {
  outline: none;
  transform: translate(-2px, -2px);
  box-shadow: 4px 4px 0 var(--_theme---text);
}
.enquiry_wrap .form_label_text {
  font-family: var(--_typography---font--label-family, var(--_typography---font--primary-family));
  font-size: 0.65625rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
/* submit row: button + the mailto note on ONE row (the addon seats the note
   inside the form's content wrapper; no-JS keeps it below the form) */
/* r11: one input per row on phones (Kris) — the kit's 2-col fieldset grid stays
   for ≥40em. r11d: the reset must be the grid-column SHORTHAND — the SSR form
   renderer's injected sheet sets `grid-column: span 2` (a START-longhand span)
   on the textarea, so an end-only reset left the span alive and it minted an
   IMPLICIT second track that auto-placed every field 2-up on the 1-column
   grid. The injected sheet is :where()-zero and lives OUTSIDE the form markup
   — a bench graft of the form_component alone never carries it. */
@media (max-width: 40em) {
  .enquiry_wrap .form_fieldset_list { grid-template-columns: minmax(0, 1fr); }
  .enquiry_wrap .form_fieldset_list > .u-column-span-2 { grid-column: auto; }
}
.enquiry_wrap .u-content-wrapper { display: flex; align-items: center; gap: 0.875rem; flex-wrap: wrap; margin-top: 1.5rem; }
.enquiry_wrap .u-content-wrapper .u-button-wrapper { margin-top: 0; }
.enquiry_wrap .u-content-wrapper .u-text { margin-block: 0; }  /* the seated note sheds its stacked-context margin */
/* r11: the row's FIRST item is the interaction-only Turnstile box — a
   zero-size flex item that still buys one 14px gap, indenting the button at
   EVERY width, and at phone widths the wrap point decides whether the seated
   note lands beside the slab (393) or under it (402). Turnstile goes last on
   its own line (drops below the button if a challenge ever materialises);
   with it off the line, button+gap+note = 356px and the pair sits on ONE
   line down to ~390pt (Kris, r11b — the column stack's 1rem read too big);
   narrower phones wrap the note under the slab at the 14px row gap. */
.enquiry_wrap .u-content-wrapper > .form_turnstile { order: 1; width: 100%; }
/* the no-JS lane keeps the note below the form component — the u-margin
   utility loses to the voice applier, so re-point the voice's own channel
   (the label voice pins it to space--0, the measured 2px hug) */
.enquiry_wrap > .u-text { --_text-style---margin-top: var(--_spacing---space--3); }
/* Y3'a r4 — form outcome pages (thanks/sorry): tighter stack. The voice
   wrapper applier owns margin-top (u-margin utilities lose to it), so the
   voice CHANNELS are seeded under the page scope; the button wrapper's own
   space--6 margin has no channel and is set directly. */
.outcome_stack .u-text-style-h2 { --_typography---h2--margin-top: var(--_spacing---space--2); }
.outcome_stack > .u-text-style-main { --_typography---text-main--margin-top: var(--_spacing---space--2); }
.outcome_stack .u-button-wrapper { margin-top: var(--_spacing---space--3); }
/* the u-container is a centered flex column, and kit wrappers carrying
   justify-content:inherit (eyebrow, button wrapper) read that center as
   HORIZONTAL when they sit directly in it. The page grammar is left-set. */
.u-container > .u-eyebrow-wrapper, .u-container > .u-button-wrapper { justify-content: flex-start; }
/* legal hero (r5): the eyebrow is the HOME shape — a plain label block, no
   eyebrow component; the h2 channel is stepped down so the label sits tight
   over the heading (the thanks/sorry stack rhythm, probe-verified there) */
.legal_hero .u-text-style-h2 { --_typography---h2--margin-top: var(--_spacing---space--2); }
/* this project runs NO eyebrow markers (Kris, r5) — belt for any future
   eyebrow-component use; current pages carry none */
.u-eyebrow-marker { display: none; }
.mail_link { border-bottom: 2px solid currentColor; padding-bottom: 1px; }
.mail_link:hover { background: var(--_theme---text); color: var(--_theme---background); }
.footer_strip { display: flex; gap: 1.125rem; flex-wrap: wrap; align-items: baseline; padding: 1.625rem 0 3.75rem; }
.footer_strip_co { color: var(--_theme---text); font-weight: 700; }
.footer_strip a { color: var(--swatch--faint); }
.footer_strip a:hover { color: var(--_theme---text); text-decoration: underline; text-decoration-thickness: 2px; }
.footer_strip_tag { margin-left: auto; color: var(--swatch--faint); }
/* r11: the strip's 18px left "space" was the cap-trim ::before phantom flex item
   (one flex gap — the hero r5 mechanism; u-text-trim-off in the markup kills
   it). On phones the strip wraps as three deliberate lines: company / legal
   links / tag (Kris: legal links on their own line). */
@media (max-width: 40em) { .footer_strip_co, .footer_strip_tag { margin-left: 0; width: 100%; } }

/* =====================================================================
   NAV INK-BLEND (r3, SHIPPED 25 Aug 2026 — Kris-approved on the bench): transparent
   bar; contents painted OPAQUE WHITE; mix-blend-mode: difference carried by
   .nav_component ITSELF — the sticky bar is z-index:1000, a STACKING CONTEXT,
   so a blend on anything INSIDE it forms an isolated group and never reaches
   the page (r1/r2 both failed this way: computed style said difference, the
   pixels never blended — the blend must live on the element whose PARENT
   chain is context-free: page_wrap is static, overflow:clip doesn't isolate).
   White difference white ground = ink (normal look); over a dark element it
   stays white (inverts) — the flip happens only under occlusion, both themes
   with one rule. 65%-alpha label inks wash under blending, so bar inks are
   forced opaque; the multi-grey Bernese mark alone pre-inverts (child filter)
   so difference restores it over the ground. Drawer sheet EXCLUDED: blend +
   white paint suspend while open, bar ground returns.
   ===================================================================== */
.nav_component[data-nav-menu="drawer"][data-nav-layout="bar"] .nav_mobile_contain { background-color: transparent; }
.nav_component[data-nav-menu="drawer"] { mix-blend-mode: difference; }
.nav_component[data-nav-menu="drawer"] .nav_mobile_wrap { color: #ffffff; --_theme---nav--logo: #ffffff; }
.nav_component[data-nav-menu="drawer"] .nav_mobile_wrap .nav_button_word,
.nav_component[data-nav-menu="drawer"] .nav_mobile_wrap .theme_toggle { color: #ffffff; }
/* the toggle icon is a CUT-OUT (stroke = theme background). Inside the
   white-painted blend group that stroke must paint BLACK: difference then
   renders it as the ground colour in BOTH themes (light |0-1| = white sun on
   the ink-rendered knob; dark |0-.07| = carbon moon on the bone knob). The
   theme-var stroke was WHITE in light mode - identical to the knob's white
   paint, so the sun vanished (Kris caught it live, 25 Aug 2026). */
.nav_component[data-nav-menu="drawer"] .nav_mobile_wrap .theme_toggle_ico { stroke: #000000; }
.nav_component[data-nav-menu="drawer"]:has(details[data-nav-drawer][open]) .nav_mobile_wrap .theme_toggle_ico { stroke: var(--_theme---background); }
/* the Mark's hardcoded artwork must survive the blend in BOTH themes, so the
   pre-compensation is per-theme: light pre-inverts (difference vs the white
   ground restores it exactly); dark LIFTS each channel by the carbon value
   (sRGB feComponentTransfer: |C+carbon - carbon| = C, the artwork verbatim
   at rest — and over a light element it still flips, which is the point).
   DELIVERY MUST BE SAME-DOCUMENT SVG FILTERS (defs live in the lockup,
   _chrome.html): WebKit ignores CSS filter FUNCTIONS and data-URI filter
   references on inner SVG elements like this g, so the invert(1)/url(data:)
   form shipped a negative Mark on every iOS browser while Blink rendered it
   correctly (measured via sim repro, 25 Aug 2026). url(#id) works in both. */
.nav_component[data-nav-menu="drawer"] .nav_mobile_wrap .lockup g[data-name="Mark"] { filter: url(#yd-preinvert); }
.u-theme-dark .nav_component[data-nav-menu="drawer"] .nav_mobile_wrap .lockup g[data-name="Mark"] { filter: url(#ydlift); }
/* drawer open: everything back to normal paint + no blend */
.nav_component[data-nav-menu="drawer"]:has(details[data-nav-drawer][open]) { mix-blend-mode: normal; }
.nav_component[data-nav-menu="drawer"]:has(details[data-nav-drawer][open]) .nav_mobile_wrap { color: var(--_theme---text); --_theme---nav--logo: var(--_theme---text); }
.nav_component[data-nav-menu="drawer"]:has(details[data-nav-drawer][open]) .nav_mobile_wrap .nav_button_word,
.nav_component[data-nav-menu="drawer"]:has(details[data-nav-drawer][open]) .nav_mobile_wrap .theme_toggle { color: var(--_theme---text); }
.nav_component[data-nav-menu="drawer"]:has(details[data-nav-drawer][open]) .nav_mobile_wrap .lockup g[data-name="Mark"] { filter: none; }
.nav_component[data-nav-menu="drawer"][data-nav-layout="bar"]:has(details[data-nav-drawer][open]) .nav_mobile_contain { background-color: var(--_theme---nav--background); }

/* =====================================================================
   L2 LOAD COVER (25 Aug 2026, lab round L1-L4 -> L2 chosen): five ink
   bands slicing one massive Mark, exiting top-to-bottom over the bar
   press-run. Markup is emitted pre-paint by build-page.py (loader_html —
   no flash before JS); scripts.addon.js runLoader drives the exits.
   Ground: carbon in BOTH themes by Kris's call ("the loader should be
   dark/black no matter what theme"), except dark mode lifts to the dark
   platform band's own mix so the band exits READ against the carbon page
   (carbon-on-carbon made the mark look like it was glitching out). The
   Mark's blacks are restyled at build to var(--yd-ld-ground) so the head
   knocks out into the bands. Gated to html.js (no-JS never covers) and
   display:none under prefers-reduced-motion.
   ===================================================================== */
#yd-loader { display: none; }
html.js #yd-loader { display: block; position: fixed; inset: 0; z-index: 12000; pointer-events: none; --yd-ld-ground: var(--swatch--carbon); }
.u-theme-dark #yd-loader { --yd-ld-ground: color-mix(in srgb, var(--_theme---text) 8%, var(--_theme---background)); }
#yd-loader .yd-loader_defs { position: absolute; width: 0; height: 0; }
/* A 1px overdraw at both horizontal seams absorbs viewport-unit rounding and
   clip-path antialiasing. It is overlap, not a visible border. */
#yd-loader .yd-loader_band { position: absolute; left: 0; right: 0; height: calc(20vh + 2px); margin-top: -1px; overflow: hidden; background: var(--yd-ld-ground); clip-path: inset(0); transform-origin: 50% 50%; will-change: clip-path, transform; }
#yd-loader .yd-loader_band.is-out { animation: yd-loader-release-a .52s steps(1, end) both; }
#yd-loader .yd-loader_band:nth-of-type(even).is-out { animation-name: yd-loader-release-b; }
#yd-loader .yd-loader_mark { position: absolute; left: 50%; top: calc(50vh - var(--i) * 20vh); transform: translate(-50%, -50%); height: 58vh; width: auto; }
#yd-loader .yd-loader_mark_fault { opacity: 0; }
#yd-loader .yd-loader_melt_canvas { position: fixed; z-index: 2; opacity: 0; pointer-events: none; }
#yd-loader[data-melt-version="1"] .yd-loader_melt_canvas { left: 50%; top: calc(50% + 7.25vh); transform: translate(-50%,-50%); height: 72.5vh; width: auto; aspect-ratio: 163.1 / 225; }
#yd-loader[data-melt-version="2"] .yd-loader_melt_canvas,
#yd-loader[data-melt-version="3"] .yd-loader_melt_canvas,
#yd-loader[data-melt-version="4"] .yd-loader_melt_canvas { inset: 0; width: 100vw; height: 100vh; transform: none; }
#yd-loader.has-webgl-melt .yd-loader_mark { visibility: hidden; }
#yd-loader.has-webgl-melt .yd-loader_melt_canvas { opacity: 1; }
#yd-loader.has-webgl-melt.is-vortex .yd-loader_band { visibility: hidden; }
#yd-loader.has-webgl-melt.is-devouring .yd-loader_band { animation: yd-loader-devour 1.65s cubic-bezier(.72,.02,.92,.48) var(--pull-lag) both, yd-loader-devour-fault-a 1.65s steps(1,end) var(--pull-lag) both; }
#yd-loader.has-webgl-melt.is-devouring .yd-loader_band:nth-of-type(even) { animation-name: yd-loader-devour, yd-loader-devour-fault-b; }
/* The cover releases through five horizontal centre seams, echoing the hero's
   ink press rather than simply being removed in strips. */
#yd-loader.is-pressing .yd-loader_mark_core { animation: yd-loader-mark-core 3s steps(1, end) both; }
#yd-loader.is-pressing .yd-loader_mark_fault_a { animation: yd-loader-mark-fault-a 3s steps(1, end) both; }
#yd-loader.is-pressing .yd-loader_mark_fault_b { animation: yd-loader-mark-fault-b 3s steps(1, end) both; }
/* The core never changes size or position. Its uneven edge losses are paired
   with two clipped copies that flash offset raster fragments beside it. */
@keyframes yd-loader-mark-core {
  0%, 7.1% { clip-path: none; }
  7.2%, 10.6% { clip-path: polygon(0 0,100% 0,100% 17%,78% 17%,78% 31%,100% 31%,100% 100%,0 100%); }
  10.7%, 18.3% { clip-path: none; }
  18.4%, 22.1% { clip-path: polygon(0 0,63% 0,63% 13%,84% 13%,84% 38%,100% 38%,100% 100%,0 100%); }
  22.2%, 31.7% { clip-path: none; }
  31.8%, 36.9% { clip-path: polygon(0 0,100% 0,100% 100%,21% 100%,21% 83%,0 83%); }
  37%, 46.2% { clip-path: none; }
  46.3%, 49.4% { clip-path: polygon(0 0,100% 0,100% 57%,73% 57%,73% 72%,44% 72%,44% 88%,0 88%); }
  49.5%, 52.8% { clip-path: polygon(0 0,54% 0,54% 21%,69% 21%,69% 43%,100% 43%,100% 100%,0 100%); }
  52.9%, 65.6% { clip-path: none; }
  65.7%, 71.5% { clip-path: polygon(0 9%,29% 9%,29% 0,100% 0,100% 100%,0 100%); }
  71.6%, 82.4% { clip-path: none; }
  82.5%, 87.9% { clip-path: polygon(0 0,100% 0,100% 26%,87% 26%,87% 49%,66% 49%,66% 63%,100% 63%,100% 100%,0 100%); }
  88%, 100% { clip-path: none; }
}
@keyframes yd-loader-mark-fault-a {
  0%, 7.1% { opacity: 0; clip-path: inset(0); transform: translate(-50%, -50%); }
  7.2%, 10.6% { opacity: 1; clip-path: polygon(0 17%,100% 17%,100% 31%,0 31%); transform: translate(calc(-50% + 12px), calc(-50% - 3px)); }
  10.7%, 31.7% { opacity: 0; }
  31.8%, 36.9% { opacity: 1; clip-path: polygon(0 71%,46% 71%,46% 83%,100% 83%,100% 100%,0 100%); transform: translate(calc(-50% - 9px), calc(-50% + 4px)); }
  37%, 46.2% { opacity: 0; }
  46.3%, 49.4% { opacity: 1; clip-path: polygon(42% 54%,100% 54%,100% 73%,71% 73%,71% 88%,42% 88%); transform: translate(calc(-50% + 15px), calc(-50% + 2px)); }
  49.5%, 65.6% { opacity: 0; }
  65.7%, 71.5% { opacity: 1; clip-path: polygon(0 0,32% 0,32% 14%,21% 14%,21% 25%,0 25%); transform: translate(calc(-50% - 13px), calc(-50% - 5px)); }
  71.6%, 100% { opacity: 0; }
}
@keyframes yd-loader-mark-fault-b {
  0%, 18.3% { opacity: 0; clip-path: inset(0); transform: translate(-50%, -50%); }
  18.4%, 22.1% { opacity: 1; clip-path: polygon(51% 0,85% 0,85% 16%,100% 16%,100% 40%,63% 40%,63% 24%,51% 24%); transform: translate(calc(-50% + 7px), calc(-50% + 8px)); }
  22.2%, 49.4% { opacity: 0; }
  49.5%, 52.8% { opacity: 1; clip-path: polygon(0 0,57% 0,57% 24%,72% 24%,72% 45%,42% 45%,42% 33%,0 33%); transform: translate(calc(-50% - 11px), calc(-50% - 2px)); }
  52.9%, 82.4% { opacity: 0; }
  82.5%, 87.9% { opacity: 1; clip-path: polygon(64% 23%,100% 23%,100% 66%,79% 66%,79% 52%,64% 52%); transform: translate(calc(-50% + 14px), calc(-50% - 6px)); }
  88%, 100% { opacity: 0; }
}
/* Review hybrid: V3's raster temperament rebuilt with the authored SVG only.
   The core never scales or deforms. Irregular missing rows are paired with two
   translated fragment copies, then Original Bars owns the cover release. */
#yd-loader.is-v3-svg.is-pressing .yd-loader_mark_core { animation: yd-loader-v3svg-core 3s steps(1,end) both; }
#yd-loader.is-v3-svg.is-pressing .yd-loader_mark_fault_a { animation: yd-loader-v3svg-fault-a 3s steps(1,end) both; }
#yd-loader.is-v3-svg.is-pressing .yd-loader_mark_fault_b { animation: yd-loader-v3svg-fault-b 3s steps(1,end) both; }
@keyframes yd-loader-v3svg-core {
  0%,5.2% { clip-path:none; }
  5.3%,7.6% { clip-path:polygon(0 0,100% 0,100% 18%,72% 18%,72% 27%,100% 27%,100% 100%,0 100%); }
  7.7%,13.8% { clip-path:none; }
  13.9%,16.1% { clip-path:polygon(0 0,58% 0,58% 12%,84% 12%,84% 22%,100% 22%,100% 100%,0 100%); }
  16.2%,24.4% { clip-path:none; }
  24.5%,28.2% { clip-path:polygon(0 0,100% 0,100% 47%,82% 47%,82% 61%,55% 61%,55% 70%,100% 70%,100% 100%,0 100%); }
  28.3%,38.1% { clip-path:none; }
  38.2%,41.4% { clip-path:polygon(0 0,100% 0,100% 100%,38% 100%,38% 87%,12% 87%,12% 76%,0 76%); }
  41.5%,56.8% { clip-path:none; }
  56.9%,61.7% { clip-path:polygon(0 0,77% 0,77% 9%,100% 9%,100% 100%,0 100%); }
  61.8%,72.4% { clip-path:none; }
  72.5%,75.2% { clip-path:polygon(0 0,100% 0,100% 31%,87% 31%,87% 44%,100% 44%,100% 100%,0 100%); }
  75.3%,83.1% { clip-path:none; }
  83.2%,87.6% { clip-path:polygon(0 0,100% 0,100% 72%,69% 72%,69% 84%,42% 84%,42% 100%,0 100%); }
  87.7%,100% { clip-path:none; }
}
@keyframes yd-loader-v3svg-fault-a {
  0%,5.2% { opacity:0;clip-path:inset(0);transform:translate(-50%,-50%); }
  5.3%,7.6% { opacity:1;clip-path:polygon(0 17%,100% 17%,100% 28%,0 28%);transform:translate(calc(-50% + 16px),calc(-50% - 2px)); }
  7.7%,24.4% { opacity:0; }
  24.5%,28.2% { opacity:1;clip-path:polygon(35% 46%,100% 46%,100% 70%,58% 70%,58% 61%,35% 61%);transform:translate(calc(-50% - 13px),calc(-50% + 3px)); }
  28.3%,56.8% { opacity:0; }
  56.9%,61.7% { opacity:1;clip-path:polygon(52% 0,100% 0,100% 19%,72% 19%,72% 29%,52% 29%);transform:translate(calc(-50% + 11px),calc(-50% + 7px)); }
  61.8%,83.1% { opacity:0; }
  83.2%,87.6% { opacity:1;clip-path:polygon(24% 70%,100% 70%,100% 86%,63% 86%,63% 100%,24% 100%);transform:translate(calc(-50% + 18px),calc(-50% - 5px)); }
  87.7%,100% { opacity:0; }
}
@keyframes yd-loader-v3svg-fault-b {
  0%,13.8% { opacity:0;clip-path:inset(0);transform:translate(-50%,-50%); }
  13.9%,16.1% { opacity:1;clip-path:polygon(0 5%,70% 5%,70% 17%,100% 17%,100% 25%,34% 25%,34% 35%,0 35%);transform:translate(calc(-50% - 9px),calc(-50% + 5px)); }
  16.2%,38.1% { opacity:0; }
  38.2%,41.4% { opacity:1;clip-path:polygon(0 73%,48% 73%,48% 85%,78% 85%,78% 100%,0 100%);transform:translate(calc(-50% - 17px),calc(-50% - 3px)); }
  41.5%,72.4% { opacity:0; }
  72.5%,75.2% { opacity:1;clip-path:polygon(61% 25%,100% 25%,100% 49%,79% 49%,79% 61%,61% 61%);transform:translate(calc(-50% + 14px),calc(-50% + 2px)); }
  75.3%,90.1% { opacity:0; }
  90.2%,93.4% { opacity:1;clip-path:polygon(0 42%,44% 42%,44% 55%,67% 55%,67% 68%,0 68%);transform:translate(calc(-50% - 12px),calc(-50% + 8px)); }
  93.5%,100% { opacity:0; }
}
/* Organic review pass: fault locations are deliberately non-sequential. The
   same regions recur, bursts overlap, and quiet gaps vary so the mark never
   reads as a sweep around a clock face. Translation only: no scale or warp. */
#yd-loader.is-organic-svg.is-pressing .yd-loader_mark_core { animation: yd-loader-organic-core 3s steps(1,end) both; }
#yd-loader.is-organic-svg.is-pressing .yd-loader_mark_fault_a { animation: yd-loader-organic-fault-a 3s steps(1,end) both; }
#yd-loader.is-organic-svg.is-pressing .yd-loader_mark_fault_b { animation: yd-loader-organic-fault-b 3s steps(1,end) both; }
@keyframes yd-loader-organic-core {
  0%,8.7% { clip-path:none; }
  8.8%,11.6% { clip-path:polygon(0 0,100% 0,100% 43%,79% 43%,79% 57%,100% 57%,100% 100%,0 100%); }
  11.7%,24.1% { clip-path:none; }
  24.2%,27.8% { clip-path:polygon(0 0,100% 0,100% 100%,47% 100%,47% 86%,18% 86%,18% 76%,0 76%); }
  27.9%,35.6% { clip-path:none; }
  35.7%,37.4% { clip-path:polygon(0 0,62% 0,62% 12%,86% 12%,86% 25%,100% 25%,100% 100%,0 100%); }
  37.5%,48.2% { clip-path:none; }
  48.3%,53.1% { clip-path:polygon(0 0,100% 0,100% 39%,86% 39%,86% 51%,57% 51%,57% 62%,100% 62%,100% 100%,0 100%); }
  53.2%,70.4% { clip-path:none; }
  70.5%,73.2% { clip-path:polygon(0 0,100% 0,100% 100%,64% 100%,64% 89%,27% 89%,27% 100%,0 100%); }
  73.3%,82.8% { clip-path:none; }
  82.9%,87.7% { clip-path:polygon(0 0,73% 0,73% 9%,100% 9%,100% 100%,0 100%); }
  87.8%,91.2% { clip-path:none; }
  91.3%,94.1% { clip-path:polygon(0 0,100% 0,100% 46%,80% 46%,80% 59%,100% 59%,100% 100%,0 100%); }
  94.2%,100% { clip-path:none; }
}
@keyframes yd-loader-organic-fault-a {
  0%,8.7% { opacity:0;clip-path:inset(0);transform:translate(-50%,-50%); }
  8.8%,11.6% { opacity:1;clip-path:polygon(43% 39%,100% 39%,100% 58%,72% 58%,72% 67%,43% 67%);transform:translate(calc(-50% + 17px),calc(-50% - 4px)); }
  11.7%,18.3% { opacity:0; }
  18.4%,20.2% { opacity:.82;clip-path:polygon(0 66%,54% 66%,54% 81%,76% 81%,76% 100%,0 100%);transform:translate(calc(-50% - 8px),calc(-50% + 6px)); }
  20.3%,35.6% { opacity:0; }
  35.7%,37.4% { opacity:1;clip-path:polygon(0 5%,68% 5%,68% 18%,100% 18%,100% 28%,31% 28%,31% 37%,0 37%);transform:translate(calc(-50% - 14px),calc(-50% + 3px)); }
  37.5%,48.2% { opacity:0; }
  48.3%,53.1% { opacity:1;clip-path:polygon(35% 38%,100% 38%,100% 54%,77% 54%,77% 67%,35% 67%);transform:translate(calc(-50% + 20px),calc(-50% + 5px)); }
  53.2%,62.6% { opacity:0; }
  62.7%,65.1% { opacity:.86;clip-path:polygon(12% 72%,72% 72%,72% 86%,100% 86%,100% 100%,12% 100%);transform:translate(calc(-50% - 11px),calc(-50% - 7px)); }
  65.2%,82.8% { opacity:0; }
  82.9%,87.7% { opacity:1;clip-path:polygon(0 0,71% 0,71% 15%,100% 15%,100% 29%,46% 29%,46% 38%,0 38%);transform:translate(calc(-50% + 9px),calc(-50% + 8px)); }
  87.8%,100% { opacity:0; }
}
@keyframes yd-loader-organic-fault-b {
  0%,8.7% { opacity:0;clip-path:inset(0);transform:translate(-50%,-50%); }
  8.8%,11.6% { opacity:.72;clip-path:polygon(0 72%,49% 72%,49% 86%,79% 86%,79% 100%,0 100%);transform:translate(calc(-50% - 10px),calc(-50% + 7px)); }
  11.7%,24.1% { opacity:0; }
  24.2%,27.8% { opacity:1;clip-path:polygon(0 68%,61% 68%,61% 82%,91% 82%,91% 100%,0 100%);transform:translate(calc(-50% + 13px),calc(-50% - 5px)); }
  27.9%,42.5% { opacity:0; }
  42.6%,44.2% { opacity:.8;clip-path:polygon(51% 8%,100% 8%,100% 27%,77% 27%,77% 39%,51% 39%);transform:translate(calc(-50% + 7px),calc(-50% - 8px)); }
  44.3%,48.2% { opacity:0; }
  48.3%,53.1% { opacity:.88;clip-path:polygon(0 7%,57% 7%,57% 20%,87% 20%,87% 34%,0 34%);transform:translate(calc(-50% - 16px),calc(-50% - 2px)); }
  53.2%,70.4% { opacity:0; }
  70.5%,73.2% { opacity:1;clip-path:polygon(47% 43%,100% 43%,100% 61%,73% 61%,73% 73%,47% 73%);transform:translate(calc(-50% + 16px),calc(-50% + 3px)); }
  73.3%,91.2% { opacity:0; }
  91.3%,94.1% { opacity:1;clip-path:polygon(24% 41%,100% 41%,100% 58%,69% 58%,69% 70%,24% 70%);transform:translate(calc(-50% - 18px),calc(-50% + 6px)); }
  94.2%,100% { opacity:0; }
}
/* WebGL v2 turns the release into one implosion. Each band is pulled to the
   mark's centre while stepped clip faults shear large rectangular bites from
   its surface. The shader carries matching loose debris above these DOM
   surfaces, so the cover appears to break up rather than merely scale away. */
@keyframes yd-loader-devour {
  0% { transform: translateY(0) scale(1); }
  100% { transform: translateY(var(--pull-y)) scale(.004); visibility: hidden; }
}
@keyframes yd-loader-devour-fault-a {
  0%, 10% { clip-path: inset(0); }
  10.1%, 18% { clip-path: polygon(0 0,64% 0,64% 11%,100% 11%,100% 100%,19% 100%,19% 88%,0 88%); }
  18.1%, 29% { clip-path: polygon(0 7%,27% 7%,27% 0,100% 0,100% 78%,73% 78%,73% 93%,0 93%); }
  29.1%, 41% { clip-path: polygon(0 0,100% 0,100% 18%,81% 18%,81% 33%,100% 33%,100% 100%,35% 100%,35% 84%,0 84%); }
  41.1%, 56% { clip-path: polygon(0 13%,42% 13%,42% 4%,100% 4%,100% 72%,69% 72%,69% 91%,0 91%); }
  56.1%, 70% { clip-path: polygon(0 0,76% 0,76% 16%,100% 16%,100% 82%,54% 82%,54% 100%,0 100%); }
  70.1%, 84% { clip-path: polygon(0 19%,31% 19%,31% 5%,100% 5%,100% 71%,83% 71%,83% 89%,0 89%); }
  84.1%, 100% { clip-path: inset(7% 4% 9% 3%); }
}
@keyframes yd-loader-devour-fault-b {
  0%, 8% { clip-path: inset(0); }
  8.1%, 16% { clip-path: polygon(0 9%,38% 9%,38% 0,100% 0,100% 87%,77% 87%,77% 100%,0 100%); }
  16.1%, 27% { clip-path: polygon(0 0,82% 0,82% 14%,100% 14%,100% 93%,46% 93%,46% 78%,0 78%); }
  27.1%, 39% { clip-path: polygon(0 16%,21% 16%,21% 3%,100% 3%,100% 81%,66% 81%,66% 100%,0 100%); }
  39.1%, 53% { clip-path: polygon(0 0,58% 0,58% 12%,100% 12%,100% 91%,28% 91%,28% 72%,0 72%); }
  53.1%, 68% { clip-path: polygon(0 8%,35% 8%,35% 0,100% 0,100% 77%,88% 77%,88% 94%,0 94%); }
  68.1%, 82% { clip-path: polygon(0 0,72% 0,72% 17%,100% 17%,100% 84%,43% 84%,43% 100%,0 100%); }
  82.1%, 100% { clip-path: inset(9% 3% 6% 5%); }
}
/* Loader bands speak the hero ink language at viewport scale. Their outer
   envelope always closes towards the centre, while alternating raster faults
   leave short-lived blocks behind or remove them early. No transform touches
   the band, so the mark fragments inside are clipped rather than distorted. */
@keyframes yd-loader-release-a {
  0%, 18% { clip-path: inset(0); visibility: visible; }
  18.1%, 27% { clip-path: polygon(0 8%,62% 8%,62% 16%,100% 16%,100% 100%,38% 100%,38% 91%,0 91%); visibility: visible; }
  27.1%, 38% { clip-path: polygon(0 18%,27% 18%,27% 11%,100% 11%,100% 84%,74% 84%,74% 91%,0 91%); visibility: visible; }
  38.1%, 49% { clip-path: polygon(0 20%,100% 20%,100% 74%,66% 74%,66% 82%,0 82%); visibility: visible; }
  49.1%, 61% { clip-path: polygon(0 31%,43% 31%,43% 23%,100% 23%,100% 72%,18% 72%,18% 66%,0 66%); visibility: visible; }
  61.1%, 72% { clip-path: polygon(0 36%,76% 36%,76% 30%,100% 30%,100% 64%,52% 64%,52% 71%,0 71%); visibility: visible; }
  72.1%, 82% { clip-path: inset(37% 0); visibility: visible; }
  82.1%, 91% { clip-path: polygon(0 45%,31% 45%,31% 41%,100% 41%,100% 55%,69% 55%,69% 59%,0 59%); visibility: visible; }
  91.1%, 100% { clip-path: inset(50% 0); visibility: hidden; }
}
@keyframes yd-loader-release-b {
  0%, 18% { clip-path: inset(0); visibility: visible; }
  18.1%, 27% { clip-path: polygon(0 16%,38% 16%,38% 8%,100% 8%,100% 91%,62% 91%,62% 100%,0 100%); visibility: visible; }
  27.1%, 38% { clip-path: polygon(0 11%,73% 11%,73% 18%,100% 18%,100% 91%,26% 91%,26% 84%,0 84%); visibility: visible; }
  38.1%, 49% { clip-path: polygon(0 20%,34% 20%,34% 27%,100% 27%,100% 82%,0 82%); visibility: visible; }
  49.1%, 61% { clip-path: polygon(0 23%,57% 23%,57% 31%,100% 31%,100% 66%,82% 66%,82% 72%,0 72%); visibility: visible; }
  61.1%, 72% { clip-path: polygon(0 30%,24% 30%,24% 36%,100% 36%,100% 71%,48% 71%,48% 64%,0 64%); visibility: visible; }
  72.1%, 82% { clip-path: inset(37% 0); visibility: visible; }
  82.1%, 91% { clip-path: polygon(0 41%,69% 41%,69% 45%,100% 45%,100% 59%,31% 59%,31% 55%,0 55%); visibility: visible; }
  91.1%, 100% { clip-path: inset(50% 0); visibility: hidden; }
}
@media (prefers-reduced-motion: reduce) { #yd-loader { display: none !important; } }
