/* KIX component CSS. Every value resolves to a --kx- token.
   Square corners, hairline rules, mono caps labels, no blur shadows. */

/* ---------- Button ---------- */
.kx-btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--kx-space-2); font-family: var(--kx-font-mono); font-size: var(--kx-size-label); font-weight: var(--kx-weight-medium); text-transform: uppercase; letter-spacing: var(--kx-track-label); line-height: 1.3; min-height: var(--kx-control-h); padding-inline: var(--kx-space-5); padding-block: var(--kx-space-2); border-radius: var(--kx-radius-0); border: var(--kx-border); background: transparent; color: var(--kx-text); cursor: pointer; text-decoration: none; white-space: normal; max-width: 100%; min-width: 0; text-align: center; position: relative; transition: background-color var(--kx-dur-fast) var(--kx-ease), color var(--kx-dur-fast) var(--kx-ease), border-color var(--kx-dur-fast) var(--kx-ease), opacity var(--kx-dur-fast) var(--kx-ease); }
/* A control must never burst its container, whatever label it is given.
   - max-width/min-width: it cannot force a flex or grid track wider than its column.
   - white-space: normal: a label wraps ONLY when it genuinely will not fit, so a button with
     room still sets on one line and desktop is unchanged. This is deliberately not behind a
     breakpoint: the same defect reappeared at 821px, where a two-column grid squeezes
     .kx-empty to 284px against a 281px button, and chasing breakpoints would never end.
   - min-height, not height: a wrapped label grows the box instead of spilling out of it.
   Sizes below therefore override padding-inline only — a `padding` shorthand here would reset
   padding-block to 0 and clip the second line. */
.kx-btn--sm { min-height: var(--kx-control-h-sm); padding-inline: var(--kx-space-4); font-size: var(--kx-size-label-sm); }
.kx-btn--lg { min-height: var(--kx-control-h-lg); padding-inline: var(--kx-space-6); }
.kx-btn--block { display: flex; width: 100%; }
.kx-btn--primary { background: var(--kx-accent); color: var(--kx-on-accent); border-color: var(--kx-accent); }
.kx-btn--primary:hover { background: var(--kx-accent-press); border-color: var(--kx-accent-press); }
.kx-btn--primary:active { background: var(--kx-accent-press); transform: translateY(1px); }
.kx-btn--solid { background: var(--kx-bg-invert); color: var(--kx-text-invert); border-color: var(--kx-bg-invert); }
.kx-btn--solid:hover { background: var(--kx-accent); color: var(--kx-on-accent); border-color: var(--kx-accent); }
.kx-btn--solid:active { transform: translateY(1px); }
.kx-btn--outline:hover { background: var(--kx-bg-invert); color: var(--kx-text-invert); }
.kx-btn--outline:active { transform: translateY(1px); }
.kx-btn--ghost { border-color: transparent; padding-inline: var(--kx-space-2); }
.kx-btn--ghost:hover { color: var(--kx-text-accent); box-shadow: inset 0 -1px 0 0 var(--kx-accent); }
.kx-btn--danger { color: var(--kx-danger); border-color: var(--kx-danger); }
.kx-btn--danger:hover { background: var(--kx-danger); color: var(--kx-white); }
.kx-btn:focus-visible { outline: var(--kx-focus-ring); outline-offset: var(--kx-focus-offset); }
.kx-btn[disabled], .kx-btn[aria-disabled="true"] { cursor: not-allowed; background: var(--kx-field-disabled-bg); color: var(--kx-text-faint); border-color: var(--kx-rule-soft); transform: none; }
.kx-btn[data-loading="true"] { cursor: progress; color: transparent; }
.kx-btn--primary[data-loading="true"] { background: var(--kx-accent); border-color: var(--kx-accent); }
.kx-btn--solid[data-loading="true"] { background: var(--kx-bg-invert); border-color: var(--kx-bg-invert); }
.kx-btn--outline[data-loading="true"], .kx-btn--ghost[data-loading="true"], .kx-btn--danger[data-loading="true"] { background: transparent; border-color: var(--kx-rule); }
.kx-btn[data-loading="true"] .kx-btn__spinner { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--kx-text); }
/* Mobile: lift sm buttons to the 44px touch target. Wrapping is handled globally above. */
@media (max-width: 480px) {
  .kx-btn--sm { min-height: var(--kx-control-h); }
}
/* Loading spinner colour. A loading button is ALWAYS also disabled (Button.jsx sets
   disabled={disabled || loading}), so it matches .kx-btn[disabled] too. The previous
   `.kx-btn[disabled][data-loading="true"] .kx-btn__spinner` rule was written for a
   genuinely-disabled loading button, but at 4 class-level selectors it outranked every
   3-selector variant rule and therefore captured EVERY loading button — painting the spinner
   the muted step on an accent fill: 2.08:1 light, 1.32:1 dark, failing 1.4.11 in both. It is
   deleted rather than re-scoped, because "disabled and loading" is not a distinct state here.
   Each variant now names the ink of its OWN loading ground, per the marks-on-accent rule in
   tokens/colors.css. Measured: primary 5.95:1 (ink on accent, both themes), solid 18.94:1
   (paper on ink light / ink on paper dark), outline+ghost+danger 18.94:1 on the page ground. */
.kx-btn--primary[data-loading="true"] .kx-btn__spinner { color: var(--kx-on-accent); }
.kx-btn--solid[data-loading="true"] .kx-btn__spinner { color: var(--kx-text-invert); }
.kx-btn--outline[data-loading="true"] .kx-btn__spinner,
.kx-btn--ghost[data-loading="true"] .kx-btn__spinner,
.kx-btn--danger[data-loading="true"] .kx-btn__spinner { color: var(--kx-text); }

/* ---------- IconButton ---------- */
.kx-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: var(--kx-control-h); height: var(--kx-control-h); padding: 0; border: var(--kx-border); background: transparent; color: var(--kx-text); cursor: pointer; border-radius: var(--kx-radius-0); transition: background-color var(--kx-dur-fast) var(--kx-ease), color var(--kx-dur-fast) var(--kx-ease); }
.kx-iconbtn--sm { width: var(--kx-control-h-sm); height: var(--kx-control-h-sm); }
.kx-iconbtn--bare { border-color: transparent; }
.kx-iconbtn:hover { background: var(--kx-bg-invert); color: var(--kx-text-invert); }
.kx-iconbtn--accent:hover { background: var(--kx-accent); color: var(--kx-on-accent); border-color: var(--kx-accent); }
.kx-iconbtn:active { transform: translateY(1px); }
.kx-iconbtn:focus-visible { outline: var(--kx-focus-ring); outline-offset: var(--kx-focus-offset); }
.kx-iconbtn[disabled] { cursor: not-allowed; color: var(--kx-text-faint); border-color: var(--kx-rule-soft); background: transparent; }

/* ---------- Card ---------- */
.kx-card { position: relative; display: block; background: var(--kx-bg-raised); color: var(--kx-text); border: var(--kx-border); padding: var(--kx-space-6); border-radius: var(--kx-radius-0); transition: background-color var(--kx-dur-fast) var(--kx-ease), color var(--kx-dur-fast) var(--kx-ease), border-color var(--kx-dur-fast) var(--kx-ease); }
.kx-card--flat { background: transparent; }
.kx-card--sunken { background: var(--kx-bg-sunken); border-color: var(--kx-rule-soft); }
.kx-card--invert { background: var(--kx-bg-invert); color: var(--kx-text-invert); border-color: var(--kx-bg-invert); }
.kx-card--accent { background: var(--kx-accent); color: var(--kx-on-accent); border-color: var(--kx-accent); }
.kx-card--offset { box-shadow: var(--kx-offset-block); }
a.kx-card, .kx-card[role="button"], .kx-card[data-interactive="true"] { cursor: pointer; border-bottom: var(--kx-border); }
/* An interactive card inverts on hover, and it RE-SCOPES THE THEME rather than
   repainting the ground. Fixed 2026-09-15 — Section: "when I hover over the cards
   on the work page, the colour changes and some of the text becomes illegible.
   Black on Black and White on White."

   He was describing a real and total failure, not a near-miss. The rule used to
   set `background` and `color` only, so every descendant holding its OWN token —
   the display title on --kx-text, the discipline tags and the scope line on
   --kx-text-muted, the "Open case study" affordance — kept the UN-hovered theme's
   value and landed on the inverted ground. Measured on the live page before this
   change: the title and the affordance at 1.00:1 in BOTH themes (ink on ink,
   paper on paper — literally invisible), the tags at 2.86:1 light / 2.42:1 dark.

   This is the same failure the design system already documents for inverted
   bands: an inverted subtree must re-scope its tokens, because `color` only
   reaches text that does not name a colour of its own. The invert tokens below
   already exist and are already measured for exactly this ground — 7.83:1 and
   6.62:1 — so nothing new is introduced here, it is simply used. */
.kx-card[data-interactive="true"]:hover {
  background: var(--kx-bg-invert);
  border-color: var(--kx-bg-invert);
  color: var(--kx-text-invert);
}
/* The six token re-points, and the three border composites that depend on them, live in
   tokens/colors.css under [data-kx-ground="invert"] — a scope whose selector list includes
   this hover state, so the ground and its tokens can never desync. They are theme values,
   not component styles: declared here they were unregistered, invisible in the token list,
   and nothing stopped the next inverted surface reinventing the same six lines.

   *** SYNC WARNING — THIS FILE CANNOT SHIP WITHOUT tokens/colors.css ***
   This rule inverts the ground and relies ENTIRELY on the [data-kx-ground="invert"] scope
   in tokens/colors.css to re-point the type and rule tokens. Copy this file into a target
   whose colors.css has no [data-kx-ground] scope and every descendant that names its own
   colour keeps the un-hovered value: title and affordance at 1.00:1, invisible. That is
   the 2026-09-15 failure, restored in full.
   Order when syncing: tokens/colors.css FIRST (safe on its own — it merely duplicates
   re-points an older inline hover rule already makes), then this file. Never the reverse,
   and never this file alone. */
/* The meta line takes the accent on hover — but ONLY where the accent is legal.
   On the ink ground it measures 5.95:1. On the PAPER ground (which is what
   --kx-bg-invert is in the dark theme) it is 3.18:1, and colors.css names that
   exact pairing as forbidden in its own header. So the dark theme's hover uses
   the muted invert step instead, at 6.62:1. */
.kx-card[data-interactive="true"]:hover .kx-card__meta { color: var(--kx-accent); }
[data-kx-theme="dark"] .kx-card[data-interactive="true"]:hover .kx-card__meta { color: var(--kx-text-invert-muted); }
.kx-card[data-interactive="true"]:focus-visible { outline: var(--kx-focus-ring); outline-offset: var(--kx-focus-offset); }
.kx-card__meta { font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); text-transform: uppercase; letter-spacing: var(--kx-track-label); color: var(--kx-text-muted); }

/* ---------- Badge / Tag ---------- */
.kx-badge { display: inline-flex; align-items: center; gap: var(--kx-space-1); font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); text-transform: uppercase; letter-spacing: var(--kx-track-label); line-height: 1; padding: var(--kx-space-1) var(--kx-space-2); border: var(--kx-border); border-radius: var(--kx-radius-0); white-space: nowrap; }
.kx-badge--accent { background: var(--kx-accent); color: var(--kx-on-accent); border-color: var(--kx-accent); }
.kx-badge--solid { background: var(--kx-bg-invert); color: var(--kx-text-invert); border-color: var(--kx-bg-invert); }
.kx-badge--quiet { border-color: var(--kx-rule-mid); color: var(--kx-text-muted); }
.kx-badge--danger { border-color: var(--kx-danger); color: var(--kx-danger); }
.kx-tag { display: inline-flex; align-items: center; gap: var(--kx-space-2); font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); text-transform: uppercase; letter-spacing: var(--kx-track-label); padding: var(--kx-space-1) var(--kx-space-3); border: var(--kx-border-mid); border-radius: var(--kx-radius-pill); color: var(--kx-text-muted); background: transparent; white-space: nowrap; }
.kx-tag[data-selected="true"] { background: var(--kx-bg-invert); color: var(--kx-text-invert); border-color: var(--kx-bg-invert); }
button.kx-tag { cursor: pointer; transition: var(--kx-transition); }
button.kx-tag:hover { border-color: var(--kx-rule); color: var(--kx-text); }
button.kx-tag:focus-visible { outline: var(--kx-focus-ring); outline-offset: var(--kx-focus-offset); }
.kx-tag__x { display: inline-flex; opacity: 0.6; }
.kx-tag__x:hover { opacity: 1; color: var(--kx-text-accent); }

/* ---------- Brand ---------- */
.kx-wordmark { font-family: var(--kx-font-display); font-weight: var(--kx-weight-black); font-variation-settings: "wdth" var(--kx-display-width-max); text-transform: uppercase; letter-spacing: var(--kx-track-hero); line-height: 1; display: inline-flex; align-items: center; color: var(--kx-text); border: 0; }
.kx-eyebrow { display: inline-flex; align-items: center; gap: var(--kx-space-3); font-family: var(--kx-font-mono); font-size: var(--kx-size-label); font-weight: var(--kx-weight-medium); text-transform: uppercase; letter-spacing: var(--kx-track-label-wide); color: var(--kx-text-muted); }
.kx-eyebrow--accent { color: var(--kx-text-accent); }
.kx-eyebrow > span { white-space: nowrap; }
.kx-eyebrow__tick { display: block; width: var(--kx-space-5); height: var(--kx-hairline); background: var(--kx-accent); }
.kx-rule { border: 0; height: var(--kx-hairline); background: var(--kx-rule); margin: 0; }
.kx-rule--soft { background: var(--kx-rule-soft); }
.kx-rule--accent { background: var(--kx-accent); }
.kx-rule--thick { height: var(--kx-rule-thick); }
.kx-rule--heavy { height: var(--kx-rule-heavy); }

/* ---------- Icon ---------- */
.kx-icon { display: inline-block; flex: none; background-color: currentColor; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-position: center; mask-position: center; -webkit-mask-size: contain; mask-size: contain; }

/* ---------- Fields ---------- */
.kx-field { display: flex; flex-direction: column; gap: var(--kx-space-2); }
.kx-field__label { font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); text-transform: uppercase; letter-spacing: var(--kx-track-label); color: var(--kx-text-muted); display: flex; gap: var(--kx-space-2); }
.kx-field__req { color: var(--kx-accent); }
.kx-field__hint { font-size: var(--kx-size-caption); color: var(--kx-text-muted); }
.kx-field__error { font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); letter-spacing: var(--kx-track-label); text-transform: uppercase; color: var(--kx-danger); }
.kx-input, .kx-textarea, .kx-select { width: 100%; font-family: var(--kx-font-body); font-size: var(--kx-size-body); color: var(--kx-text); background: var(--kx-field-bg); border: var(--kx-border); border-radius: var(--kx-radius-0); padding: 0 var(--kx-space-4); height: var(--kx-control-h); transition: border-color var(--kx-dur-fast) var(--kx-ease), background-color var(--kx-dur-fast) var(--kx-ease); }
.kx-textarea { height: auto; padding: var(--kx-space-3) var(--kx-space-4); min-height: 120px; resize: vertical; line-height: var(--kx-leading-body); }
.kx-input::placeholder, .kx-textarea::placeholder { color: var(--kx-text-muted); } /* 6.62:1 — faint (3.23:1) fails 1.4.3 for placeholders */
.kx-input:hover, .kx-textarea:hover, .kx-select:hover { border-color: var(--kx-accent); }
.kx-input:focus-visible, .kx-textarea:focus-visible, .kx-select:focus-visible { outline: var(--kx-focus-ring); outline-offset: var(--kx-focus-offset); border-color: var(--kx-accent); }
.kx-input[aria-invalid="true"], .kx-textarea[aria-invalid="true"], .kx-select[aria-invalid="true"] { border-color: var(--kx-danger); box-shadow: inset 0 0 0 1px var(--kx-danger); }
.kx-input[disabled], .kx-textarea[disabled], .kx-select[disabled] { background: var(--kx-field-disabled-bg); color: var(--kx-text-faint); border-color: var(--kx-rule-soft); cursor: not-allowed; }
.kx-select__wrap { position: relative; display: block; }
.kx-select { appearance: none; padding-right: var(--kx-space-8); cursor: pointer; }
.kx-select__caret { position: absolute; right: var(--kx-space-4); top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--kx-text-muted); }

/* ---------- Choice controls ---------- */
.kx-choice { display: inline-flex; align-items: flex-start; gap: var(--kx-space-3); cursor: pointer; font-size: var(--kx-size-body-sm); line-height: 1.35; }
.kx-choice input { position: absolute; opacity: 0; width: 0; height: 0; }
.kx-choice__box { flex: none; width: 20px; height: 20px; border: var(--kx-border); background: var(--kx-field-bg); display: inline-flex; align-items: center; justify-content: center; transition: background-color var(--kx-dur-fast) var(--kx-ease), border-color var(--kx-dur-fast) var(--kx-ease); }
.kx-choice__box--radio { border-radius: var(--kx-radius-pill); }
.kx-choice:hover .kx-choice__box { border-color: var(--kx-accent); }
.kx-choice input:checked + .kx-choice__box { background: var(--kx-accent); border-color: var(--kx-accent); }
.kx-choice input:focus-visible + .kx-choice__box { outline: var(--kx-focus-ring); outline-offset: var(--kx-focus-offset); }
.kx-choice__mark { width: 10px; height: 10px; background: var(--kx-on-accent); opacity: 0; }
.kx-choice__mark--radio { border-radius: var(--kx-radius-pill); }
.kx-choice input:checked + .kx-choice__box .kx-choice__mark { opacity: 1; }
/* SCOPED TO CHECKBOXES 2026-09-15 — Select Ruling, adoption step 5. This was
   `.kx-choice input:indeterminate`, unscoped. A RADIO is :indeterminate whenever
   nothing in its group is checked, so an untouched radio group painted every one
   of its boxes accent. It also outbid the disabled ground two lines below: this
   selector is (0,3,1) against that rule's (0,3,0), so a disabled untouched radio
   drew accent instead of disabled. Found by CD while drawing the ruling and fixed
   on its own merits — the ledger's pre-selected first option only masks it at
   this one call site, so the next radio group anyone adds would have carried it. */
.kx-choice input[type="checkbox"]:indeterminate + .kx-choice__box { background: var(--kx-accent); border-color: var(--kx-accent); }
.kx-choice[data-disabled="true"] { cursor: not-allowed; color: var(--kx-text-faint); }
.kx-choice[data-disabled="true"] .kx-choice__box { background: var(--kx-field-disabled-bg); border-color: var(--kx-rule-soft); }

/* ---------- Choice ledger ---------- */
/* Select Ruling, 15 September 2026 — "the system does not rebuild platform widgets".
   Select is for lists that are long, ordered and familiar (country, month, year) or
   longer than about eight options. Below that, a short mutually-exclusive set is a
   LEDGER, not a dropdown: nothing opens, so there is no overlay, no focus to return
   and no trap to get wrong. The layout lives here and not at the call site.
   The gap reset needs !important because RadioGroup sets display and gap as an
   INLINE style, and no class rule can outrank an inline one — the same constraint
   that keeps Home's capability grid carrying only its one-column base inline. */
.kx-ledger { gap: 0 !important; border: var(--kx-border); background: var(--kx-field-bg); }
.kx-ledger > .kx-choice { display: flex; width: 100%; box-sizing: border-box; align-items: center; min-height: 48px; padding: 0 var(--kx-space-5); }
.kx-ledger > .kx-choice + .kx-choice { border-top: var(--kx-hairline) solid var(--kx-rule-soft); }
.kx-ledger > .kx-choice:hover { background: var(--kx-bg-sunken); }

/* ---------- Switch ---------- */
.kx-switch { display: inline-flex; align-items: center; gap: var(--kx-space-3); cursor: pointer; font-size: var(--kx-size-body-sm); }
.kx-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.kx-switch__track { flex: none; width: 48px; height: 24px; border: var(--kx-border); background: var(--kx-field-bg); position: relative; transition: background-color var(--kx-dur-fast) var(--kx-ease), border-color var(--kx-dur-fast) var(--kx-ease); }
.kx-switch__knob { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; background: var(--kx-text); transition: transform var(--kx-dur-fast) var(--kx-ease), background-color var(--kx-dur-fast) var(--kx-ease); }
.kx-switch input:checked + .kx-switch__track { background: var(--kx-accent); border-color: var(--kx-accent); }
.kx-switch input:checked + .kx-switch__track .kx-switch__knob { transform: translateX(24px); background: var(--kx-ink); }
.kx-switch input:focus-visible + .kx-switch__track { outline: var(--kx-focus-ring); outline-offset: var(--kx-focus-offset); }
.kx-switch[data-disabled="true"] { cursor: not-allowed; color: var(--kx-text-faint); }
.kx-switch[data-disabled="true"] .kx-switch__track { background: var(--kx-field-disabled-bg); border-color: var(--kx-rule-soft); }
.kx-switch[data-disabled="true"] .kx-switch__knob { background: var(--kx-text-faint); }

/* ---------- Tabs ---------- */
.kx-tabs { display: flex; gap: 0; border-bottom: var(--kx-border-soft); overflow-x: auto; scrollbar-width: none; }
.kx-tabs::-webkit-scrollbar { display: none; }
.kx-tab { font-family: var(--kx-font-mono); font-size: var(--kx-size-label); text-transform: uppercase; letter-spacing: var(--kx-track-label); background: transparent; border: 0; border-bottom: var(--kx-rule-thick) solid transparent; color: var(--kx-text-muted); padding: var(--kx-space-3) var(--kx-space-4); cursor: pointer; white-space: nowrap; transition: color var(--kx-dur-fast) var(--kx-ease), border-color var(--kx-dur-fast) var(--kx-ease); }
.kx-tab:hover { color: var(--kx-text); }
.kx-tab[aria-selected="true"] { color: var(--kx-text); border-bottom-color: var(--kx-accent); }
.kx-tab:focus-visible { outline: var(--kx-focus-ring); outline-offset: calc(var(--kx-focus-offset) * -1); }
.kx-tab[disabled] { color: var(--kx-text-faint); cursor: not-allowed; }

/* ---------- Dialog ---------- */
.kx-dialog__overlay { position: fixed; inset: 0; background: var(--kx-overlay); display: flex; align-items: center; justify-content: center; padding: var(--kx-space-5); z-index: 100; animation: kx-fade var(--kx-dur-base) var(--kx-ease); }
.kx-dialog { background: var(--kx-bg-raised); color: var(--kx-text); border: var(--kx-border); width: 100%; max-width: 520px; padding: var(--kx-space-6); }
.kx-dialog__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--kx-space-5); margin-bottom: var(--kx-space-4); }
.kx-dialog__title { font-family: var(--kx-font-display); font-weight: var(--kx-weight-black); font-variation-settings: "wdth" var(--kx-display-width); text-transform: uppercase; letter-spacing: var(--kx-track-display); line-height: var(--kx-leading-tight); font-size: var(--kx-size-d4); margin: 0; }
.kx-dialog__body { color: var(--kx-text-muted); font-size: var(--kx-size-body-sm); }
.kx-dialog__foot { display: flex; gap: var(--kx-space-3); justify-content: flex-end; margin-top: var(--kx-space-6); }
@keyframes kx-fade { from { opacity: 0; } to { opacity: 1; } }

/* ---------- Toast / Alert ---------- */
.kx-toast { display: flex; align-items: flex-start; gap: var(--kx-space-3); background: var(--kx-bg-invert); color: var(--kx-text-invert); border: var(--kx-hairline) solid var(--kx-bg-invert); padding: var(--kx-space-4); min-width: 280px; max-width: 420px; }
.kx-toast--accent { border-left: var(--kx-rule-heavy) solid var(--kx-accent); }
.kx-toast--error { border-left: var(--kx-rule-heavy) solid var(--kx-danger); }
.kx-toast__title { font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); text-transform: uppercase; letter-spacing: var(--kx-track-label); }
.kx-toast__body { font-size: var(--kx-size-caption); color: var(--kx-text-invert-muted); margin-top: var(--kx-space-1); }
.kx-alert { display: flex; gap: var(--kx-space-3); border: var(--kx-border); padding: var(--kx-space-4); background: var(--kx-bg-raised); }
.kx-alert--accent { border-left: var(--kx-rule-heavy) solid var(--kx-accent); }
.kx-alert--error { border-color: var(--kx-danger); border-left-width: var(--kx-rule-heavy); }
.kx-alert__title { font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); text-transform: uppercase; letter-spacing: var(--kx-track-label); margin-bottom: var(--kx-space-1); }
.kx-alert__body { font-size: var(--kx-size-body-sm); color: var(--kx-text-muted); }

/* ---------- Tooltip ---------- */
.kx-tooltip { position: relative; display: inline-flex; }
.kx-tooltip__bubble { position: absolute; bottom: calc(100% + var(--kx-space-2)); left: 50%; transform: translateX(-50%); background: var(--kx-bg-invert); color: var(--kx-text-invert); font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); letter-spacing: var(--kx-track-label); text-transform: uppercase; padding: var(--kx-space-2) var(--kx-space-3); white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity var(--kx-dur-fast) var(--kx-ease); z-index: 50; }
.kx-tooltip:hover .kx-tooltip__bubble, .kx-tooltip:focus-within .kx-tooltip__bubble { opacity: 1; }

/* ---------- Spinner / Empty ---------- */
.kx-spinner { display: inline-block; border: var(--kx-rule-thick) solid currentColor; border-right-color: transparent; border-radius: var(--kx-radius-pill); animation: kx-spin 700ms var(--kx-ease-linear) infinite; }
@keyframes kx-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .kx-spinner { animation-duration: 2s; } }
.kx-empty { display: flex; flex-direction: column; align-items: flex-start; gap: var(--kx-space-4); border: var(--kx-hairline) dashed var(--kx-rule-mid); padding: var(--kx-space-7) var(--kx-space-6); }
.kx-empty__title { font-family: var(--kx-font-display); font-variation-settings: "wdth" var(--kx-display-width); font-weight: var(--kx-weight-black); text-transform: uppercase; letter-spacing: var(--kx-track-display); font-size: var(--kx-size-d4); line-height: var(--kx-leading-tight); }
.kx-empty__body { font-size: var(--kx-size-body-sm); color: var(--kx-text-muted); max-width: var(--kx-measure-tight); }

/* ---------- Header / Footer ---------- */
.kx-header { position: sticky; top: 0; z-index: 40; background: var(--kx-bg); border-bottom: var(--kx-border); }
.kx-header__bar { max-width: var(--kx-page-max); margin: 0 auto; padding: var(--kx-space-4) var(--kx-gutter); display: flex; align-items: center; justify-content: space-between; gap: var(--kx-space-5); }
.kx-header__nav { display: flex; align-items: center; gap: var(--kx-space-6); }
.kx-header__link { font-family: var(--kx-font-mono); font-size: var(--kx-size-label); text-transform: uppercase; letter-spacing: var(--kx-track-label); color: var(--kx-text-muted); border: 0; padding: var(--kx-space-2) 0; box-shadow: inset 0 -1px 0 0 transparent; transition: box-shadow var(--kx-dur-fast) var(--kx-ease); }
.kx-header__link:hover { color: var(--kx-text); box-shadow: inset 0 -1px 0 0 var(--kx-accent); }
.kx-header__link[aria-current="page"] { color: var(--kx-text); box-shadow: inset 0 -2px 0 0 var(--kx-accent); }
.kx-header__actions { display: flex; align-items: center; gap: var(--kx-space-3); }
.kx-header__burger { display: none; }
.kx-header__drawer { display: none; }
@media (max-width: 820px) {
  .kx-header__nav, .kx-header__cta { display: none; }
  .kx-header__burger { display: inline-flex; }
  .kx-header__drawer { display: block; border-top: var(--kx-border-soft); padding: var(--kx-space-4) var(--kx-gutter) var(--kx-space-6); }
  .kx-header__drawer .kx-header__link { display: block; font-size: var(--kx-size-d4); font-family: var(--kx-font-display); font-variation-settings: "wdth" var(--kx-display-width); font-weight: var(--kx-weight-black); letter-spacing: var(--kx-track-display); padding: var(--kx-space-3) 0; border-bottom: var(--kx-border-soft); }
}
.kx-footer { border-top: var(--kx-border); background: var(--kx-bg); }
.kx-footer__inner { max-width: var(--kx-page-max); margin: 0 auto; padding: var(--kx-space-8) var(--kx-gutter) var(--kx-space-6); display: grid; grid-template-columns: 1.2fr 1fr 1fr; gap: var(--kx-space-7); }
.kx-footer__col { display: flex; flex-direction: column; gap: var(--kx-space-3); align-items: flex-start; }
.kx-footer__legal { max-width: var(--kx-page-max); margin: 0 auto; padding: var(--kx-space-5) var(--kx-gutter); border-top: var(--kx-border-soft); display: flex; justify-content: space-between; gap: var(--kx-space-4); flex-wrap: wrap; font-family: var(--kx-font-mono); font-size: var(--kx-size-label-sm); letter-spacing: var(--kx-track-label); text-transform: uppercase; color: var(--kx-text-muted); } /* 6.62:1 — faint (3.23:1) is disabled-only, never live text */
@media (max-width: 820px) { .kx-footer__inner { grid-template-columns: 1fr; gap: var(--kx-space-6); } }

/* ---- Dialog amendment · 14 September 2026 (CD) ------------------------------------------
   Adds size="wide" for the Work case-study modal. Every value resolves to a --kx- token;
   radius stays 0; no blur shadow. Nothing here changes the existing md dialog. */

/* head is a two-part block, so an eyebrow and a Badge can sit with the title */
.kx-dialog__headtext { min-width: 0; display: flex; flex-direction: column; gap: var(--kx-space-3); }
.kx-dialog__headaside { flex: none; display: flex; align-items: center; gap: var(--kx-space-4); }
.kx-dialog__eyebrow { font-size: var(--kx-size-label-sm); }

/* size="wide": head and foot hold their place, only the body scrolls, so the title of a
   tall dialog is always on screen. Padding moves to the three regions, which is what lets
   a body band run to the dialog's own edges. */
.kx-dialog--wide { max-width: 1280px; padding: 0; display: flex; flex-direction: column; max-height: calc(100vh - var(--kx-space-7)); overflow: hidden; }
.kx-dialog__overlay--wide { align-items: flex-start; overflow: auto; }
.kx-dialog--wide .kx-dialog__head { flex: none; margin: 0; padding: var(--kx-space-5) var(--kx-space-6); border-bottom: var(--kx-border); align-items: center; }
.kx-dialog--wide .kx-dialog__title { font-size: var(--kx-size-d3); }
.kx-dialog--wide .kx-dialog__body { flex: 1; min-height: 0; overflow: auto; padding: var(--kx-space-6); color: var(--kx-text); font-size: var(--kx-size-body); }

/* The dialog body's scrollbar. CD ruling, 2026-09-15 — Section asked for "a thin orange
   scrollbar" and the answer is thin, but NOT orange.
   The rail is on the left, so the well owns the body's right edge for its full height:
   that bar never once sits against KIX's own ground, it is our chrome hard against the
   client's screens down their whole length. Orange there is not "our chrome on our
   dialog", it is the exact adjacency the well exists to remove, drawn as a 3040px line.
   The focus ring earns its exception by being transient and keyboard-summoned; a
   scrollbar is present the entire time anyone looks at the collage.
   The complaint is still answered: the fault was that it was BROWSER DEFAULT, not that
   it was neutral. #5A5A58 reads on paper (6.62:1) and on the well's near-black alike.
   Track stays undrawn — a drawn track would add a second vertical line beside the well's
   hairline. Both syntaxes; the standard one has no hover form, so hover is webkit-only,
   which is degradation rather than defect. */
.kx-dialog__body { scrollbar-width: thin; scrollbar-color: var(--kx-ink-60) transparent; }
.kx-dialog__body::-webkit-scrollbar { width: var(--kx-space-2); }
.kx-dialog__body::-webkit-scrollbar-track { background: transparent; }
.kx-dialog__body::-webkit-scrollbar-thumb { background: var(--kx-ink-60); border-radius: 0; }
.kx-dialog__body::-webkit-scrollbar-thumb:hover { background: var(--kx-ink-40); }
.kx-dialog--wide .kx-dialog__body--flush { padding: 0; }
.kx-dialog--wide .kx-dialog__foot { flex: none; margin: 0; padding: var(--kx-space-4) var(--kx-space-6); border-top: var(--kx-border); justify-content: space-between; }

/* Under 1024px a wide dialog becomes a full-bleed sheet. The breakpoint is the width at
   which the system's two-column content stops fitting beside a 560px image column. */
@media (max-width: 1024px) {
  .kx-dialog__overlay--wide { padding: 0; }
  .kx-dialog--wide { max-width: 100%; height: 100vh; max-height: 100vh; border-width: 0; }
}
@media (max-width: 480px) {
  .kx-dialog--wide .kx-dialog__head { padding: var(--kx-space-4) var(--kx-space-5); }
  .kx-dialog--wide .kx-dialog__foot { padding: var(--kx-space-3) var(--kx-space-5); }
}
