/* ============================================================ */
/*  COREUCX BRANDBOOK LIBRARY - coreucx-lib.css                  */
/*  THE single source of truth: tokens, themes and global        */
/*  component classes for portal, docs and marketing.            */
/*  Documented live at Portal > Documentation > Brandbook.       */
/*  Load FIRST, before page-layer stylesheets.                   */
/* ============================================================ */

/* ============================================================ */
/*  Reset / base                                                 */
/* ============================================================ */

*, *::before, *::after { box-sizing: border-box; }
html, body, #app { height: 100%; }
/* #app clips the up-scaled shell so its larger layout box never spawns scrollbars */
#app { position: fixed; inset: 0; overflow: hidden; }
body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 11px;
  line-height: 1.45;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-feature-settings: "cv11", "ss01";
}
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; }
a { color: inherit; text-decoration: none; }
/* in-app record links (ticket numbers etc): quiet until hover, accent underline on hover */
.cx-ticket-link { cursor: pointer; }
.cx-ticket-link:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

::selection { background: var(--accent-soft); color: var(--accent-soft-fg); }

/* scrollbar - modern standard properties (2026): thin bar, transparent track */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 6px; height: 6px; background: transparent; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-track-piece { background: transparent; }
*::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
*::-webkit-scrollbar-corner { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
*:hover::-webkit-scrollbar-thumb { background: var(--text-faint); }


/* ============================================================ */
/*  Primitives                                                   */
/* ============================================================ */

.btn {
  display: inline-flex; align-items: center; gap: 5px;
  height: 24px;
  padding: calc(1px + var(--optics-text, 0px)) 10px 0; /* floored-baseline correction */
  border-radius: var(--r-md);
  font-size: 10px;
  font-weight: 500;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  transition: all 120ms ease;
  white-space: nowrap;
}
.btn:hover { border-color: var(--border-strong); background: var(--bg-2); }
.btn:focus-visible { outline: 0; box-shadow: 0 0 0 2px var(--focus-ring); border-color: var(--accent); }
.btn.primary { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.btn.primary:hover { filter: brightness(0.95); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--text-muted); }
.btn.ghost:hover { background: var(--bg-2); color: var(--text); }
.btn.danger { color: var(--c-danger); }
.btn.danger:hover { background: var(--c-danger-soft); }
.btn.success { background: var(--c-success); border-color: var(--c-success); color: #fff; }
.btn.lg { height: 29px; padding: calc(0px + var(--optics-text, 0px)) 13px 0; font-size: 10.5px; } /* symmetric: bitmap-audit found the old 1px BOTTOM pad (discredited-scanner era) lifted the label 1px */
.btn.sm { height: 19px; padding: calc(1px + var(--optics-text, 0px)) 6px 0; font-size: 9px; } /* +1px top: integer baseline at 13 */
.btn.icon-only { padding: 0; width: 24px; justify-content: center; }
.btn.icon-only.sm { width: 19px; }
/* Icon button: a SHARED object (46 uses across 19 files - nav chrome, widgets, feature pages and
   the shared table), so it belongs here. Its base rule previously lived in app.css, which owns
   portal chrome only; app.css and portal.css now carry scoped modifiers on it and nothing else. */
.icon-btn {
  width: 24px; height: 24px;
  display: grid; place-items: center;
  background: transparent; border: 1px solid transparent; border-radius: var(--r-md);
  color: var(--text-muted);
  transition: all 120ms ease;
}
.icon-btn:hover { background: var(--bg-2); color: var(--text); border-color: var(--border); }
.icon-btn.active { background: var(--accent-soft); color: var(--accent-soft-fg); border-color: transparent; }
/* Icon optical compensation (baseline law): .btn vertical padding is asymmetric on purpose
   (+1px top centers the LABEL's floored baseline), but a leading svg is a geometric glyph,
   not baseline ink - flex-centering it in the padding-shifted content box left it ~0.5px
   low in every button. Shift it back up by half the top padding - the same compensation
   .card-header .label .ico and .pill .dot already carry. icon-only buttons have no
   padding (true center already); .lg's top pad is optics-only. */
.btn > svg { position: relative; top: calc((1px + var(--optics-text, 0px)) / -2); }
.btn.lg > svg { top: calc(var(--optics-text, 0px) / -2); }
.btn.icon-only > svg { top: 0; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.card-header {
  display: flex; align-items: center; gap: var(--sp-2);
  height: 33px; min-height: 33px; flex-shrink: 0; box-sizing: border-box;
  padding: 0 11px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--text);
}
.card-header .label {
  display: flex; align-items: center; gap: 5px;
  /* caps-only text: the line box reserves descender space the caps never use, so the
     ink sits high when the box is flex-centered. MEASURED live (canvas ink metrics vs
     header center): raw error ~2px at 9px GeistMono caps - +2px top padding zeroes it
     (verified against .cx-meta-lbl, which measures 0.00 offset). */
  padding-top: calc(2px + var(--optics-text, 0px));
  font-family: var(--font-mono);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  font-weight: 500;
}
.card-header .label .dot { width: 4px; height: 4px; background: var(--accent); border-radius: 50%; position: relative; top: calc(-1px - var(--optics-text, 0px) / 2); /* re-center against the label's baseline padding */ }
/* leading icon in a widget-header label - THE object (never hand-roll inline-flex spans).
   Geometric glyph: compensate the label's text-baseline padding like .pill .dot. */
.card-header .label .ico { display: inline-flex; color: var(--accent); position: relative; top: calc(-1px - var(--optics-text, 0px) / 2); }
.card-header .actions { margin-left: auto; display: flex; gap: 3px; align-items: center; }
.card-body { padding: 11px; }
.card-body.flush { padding: 0; }

/* hover tip (CXHoverTip in common.jsx): portal-rendered popup listing the detail behind a
   summarised cell (e.g. a count pill expanding to its items on hover). */
.cx-hovertip-pop { position: fixed; z-index: 1000; background: var(--elevated); border: 1px solid var(--border-strong); border-radius: var(--r-md); box-shadow: var(--shadow-lg); padding: 8px 10px; display: flex; flex-wrap: wrap; gap: 4px; max-width: 260px; pointer-events: none; }

/* status pill */
.pill {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  padding: calc(2px + var(--optics-text, 0px)) 6px 0;
  /* BASELINE LAW (empirical, bitmap-scanned): Chrome FLOORS painted baselines. +2px top
     = 4px/4px ink gaps in the 17px pill. --optics-text = user calibration (Brandbook). */
  line-height: 1;
  height: 17px;
  font-size: 9px;
  font-family: var(--font-mono);
  font-weight: 500;
  border-radius: 799px;
  background: var(--bg-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
  white-space: nowrap;
  letter-spacing: 0.01em;
  text-transform: uppercase;
}
.pill .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; position: relative; top: calc(-1px - var(--optics-text, 0px) / 2); /* 5px = ODD size: lands on integer pixel rows (6..11) in the 17px pill - a 4px dot straddles half-pixels and renders blurry-high. */ }
.pill.success { background: var(--c-success-soft); color: var(--c-success); border-color: color-mix(in oklab, var(--c-success), transparent 75%); }
.pill.warning { background: var(--c-warning-soft); color: oklch(0.55 0.16 75); border-color: color-mix(in oklab, var(--c-warning), transparent 75%); }
:root[data-theme="dark"] .pill.warning { color: var(--c-warning); }
.pill.danger { background: var(--c-danger-soft); color: var(--c-danger); border-color: color-mix(in oklab, var(--c-danger), transparent 75%); }
.pill.info { background: var(--c-info-soft); color: var(--c-info); border-color: color-mix(in oklab, var(--c-info), transparent 75%); }
.pill.ai { background: var(--c-ai-soft); color: var(--c-ai); border-color: color-mix(in oklab, var(--c-ai), transparent 75%); }
.pill.accent { background: var(--accent-soft); color: var(--accent-soft-fg); border-color: transparent; }
.pill.solid.success { background: var(--c-success); color: white; border-color: transparent; }
.pill.solid.danger { background: var(--c-danger); color: white; border-color: transparent; }

/* mono */
.mono { font-family: var(--font-mono); font-feature-settings: "tnum"; letter-spacing: -0.01em; }
.tnum { font-variant-numeric: tabular-nums; }


/* ============================================================ */
/*  Table - unified styles for ALL .tbl                          */
/* ============================================================ */
.tbl {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 10.5px;
  line-height: 1.4;
}
.tbl thead th {
  position: sticky; top: 0;
  text-align: center; /* header-label law: centered in every column, all tables (v2.89) */
  font-weight: 500;
  font-size: 8.5px;
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-faint);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  /* mono-caps baseline law (same as .card-header .label): 8.5px GeistMono uppercase ink
     sits ~2px high when centered - +2px top padding lands the label on the cell's
     vertical center; consumes the user optics calibration. */
  padding: calc(2px + var(--optics-text, 0px)) 11px 0;
  height: 30px;
  white-space: nowrap;
  z-index: 1;
}
/* header cell anatomy: label + sort caret center as a unit on the FULL cell width;
   the filter funnel is out of flow, justified right against the padding edge (v2.91) */
.tbl th .th-wrap { position: relative; display: flex; align-items: center; gap: 3px; width: 100%; }
.tbl th .th-label { display: inline-flex; align-items: center; justify-content: center; gap: 3px; flex: 1 1 auto; min-width: 0; }
.tbl th .th-label .th-sort { margin-left: 1px; }
.tbl th .th-wrap .th-filter-wrap { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
/* funnel columns reserve a symmetric 16px inset so label ink never runs under the
   out-of-flow funnel and the center axis is preserved */
.tbl th:has(.th-filter-wrap) .th-label { padding: 0 16px; }
.tbl tbody td {
  padding: 8px 11px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  vertical-align: middle;
  font-size: 10.5px;
}
.tbl tbody tr {
  height: 38px; /* minimum row height; cell padding expands as needed */
}
.tbl tbody tr:hover td { background: color-mix(in oklab, var(--accent), transparent 92%); } /* accent tint (folded from portal.css override) */
.tbl tbody tr:hover td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
.tbl tbody tr:hover td:hover { background: color-mix(in oklab, var(--accent), transparent 80%); }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl tbody tr.selected td { background: var(--accent-soft); }

/* numeric cells: same font scale, tabular */
.tbl .num,
.tbl .mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 10px;
}
.tbl .muted { color: var(--text-muted); }

/* text-colour utilities - THE classes for the two secondary ink levels.
   Replaces the portal's most-repeated inline style (58 occurrences found
   by the round-9 census). Inline color:var(--text-muted/faint) is drift. */
.t-muted { color: var(--text-muted); }
.t-faint { color: var(--text-faint); }

/* normalize sub-line beneath a primary value (used in name+subtitle pattern) */
.tbl tbody td > div:not(.pill) > div:last-child:not(:only-child) {
  font-size: 9px;
  color: var(--text-faint);
  margin-top: 2px;
  font-family: var(--font-mono);
}

/* primary value above sub-line */
.tbl tbody td > div > strong {
  font-weight: 500;
  font-size: 10.5px;
}

/* normalize pills inside table cells */
.tbl .pill {
  height: 16px;
  font-size: 8.5px;
  padding: calc(0px + var(--optics-text, 0px)) 6px 0; /* 16px box: integer baseline at 11; calibration applies */
}
.tbl .pill .dot { width: 4px; height: 4px; top: 0; /* 16px box is even: 4px dot is already integer-crisp */ }

/* normalize action buttons inside tables (row-end menus, play, etc.) */
.tbl .icon-btn { width: 21px; height: 21px; }
.tbl .btn.sm { height: 21px; padding: calc(1px + var(--optics-text, 0px)) 8px 0; font-size: 9px; } /* integer baseline at 14 */
.tbl .btn.sm.icon-only { width: 21px; padding: 0; }

/* normalize svg icons inside table cells */
.tbl tbody td svg { vertical-align: middle; }
/* Fixed-layout grid tables reserve a width per column; a cell whose content exceeds it must CLIP,
   never paint across its neighbour (td overflow defaults to visible, which produced overlapping
   text on any table that had not declared column widths). Full values stay available via title. */
.tbl.tbl-grid tbody td { overflow: hidden; }
/* Inset surface: a recessed panel INSIDE a card - stat tiles, summary strips, meta rows. Distinct
   from .card, which is a raised surface on the page background: this one sits on --bg-2 so it reads
   as carved into its parent. Added because eight files had independently inlined the same three
   declarations with drifting values (radius 7 and 8, six different paddings, none matching a token).
   The object owns the SURFACE only; layout (flex, minWidth, gap) stays at the call site because it
   genuinely differs per use. .cx-inset-p gives the common tile padding. */
.cx-inset { background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--r-lg); }
.cx-inset-p { padding: 9px 11px; }
.cx-inset-pt { padding: 6px 9px; }
/* Borderless variant: the same recessed surface without the hairline, for panels stacked directly
   inside a card where a border would double up with the card edge. Three files had inlined it. */
.cx-inset.flat { border-color: transparent; }
/* KPI tile: the .card object with a compact stat modifier. Shared by portal pages and the docs,
   so it belongs here rather than in app.css. */
.card.kpi {
  padding: 11px 13px;
  display: flex; flex-direction: column; gap: 3px;
  min-width: 0;
  position: relative;
}
.card.kpi .sparkline { position: absolute; right: 10px; bottom: 10px; opacity: 0.4; }

.kpi.accent { border-color: var(--accent); background: color-mix(in oklab, var(--accent-soft), transparent 50%); }

/* small inline progress bars in cells */
.tbl tbody td span[style*="background: var(--border)"][style*="height: 3px"] { /* targeting inline progress bars in code */
  flex-shrink: 0;
}


/* ============================================================ */
/*  COREUCX BRANDBOOK - Canonical Token Library                  */
/*  v1.0 · 09 Jul 2026                                           */
/*                                                               */
/*  SINGLE SOURCE OF TRUTH for every color, radius, shadow and   */
/*  font in the COREUCX portal + marketing site.                 */
/*  Documented live at: Portal → Documentation → Brandbook.      */
/*  Phase 2 (approved refactor) moves component rules here too.  */
/* ============================================================ */

:root {
  /* font stack */
  --font-sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono: "Geist Mono", "JetBrains Mono", "SF Mono", Menlo, monospace;

  /* radii */
  --r-sm: 3px;
  --r-md: 5px;
  --r-lg: 8px;
  --r-xl: 11px;

  /* shadows */
  --shadow-sm: 0 1px 0 rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.06);
  --shadow-md: 0 3px 10px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06);
  --shadow-lg: 0 10px 26px rgba(0,0,0,0.12), 0 3px 6px rgba(0,0,0,0.08);

  /* status - shared between themes; -soft = translucent mix so it adapts to
     the theme's background (the old opaque light-lightness softs rendered
     near-white surfaces in dark mode) */
  --c-success: oklch(0.72 0.16 155);
  --c-success-soft: color-mix(in oklab, var(--c-success), transparent 88%);
  --c-warning: oklch(0.78 0.16 80);
  --c-warning-soft: color-mix(in oklab, var(--c-warning), transparent 88%);
  --c-danger: oklch(0.65 0.21 25);
  --c-danger-soft: color-mix(in oklab, var(--c-danger), transparent 88%);
  --c-info: oklch(0.70 0.15 230);
  --c-info-soft: color-mix(in oklab, var(--c-info), transparent 88%);
  --c-ai: oklch(0.72 0.18 290);
  --c-ai-soft: color-mix(in oklab, var(--c-ai), transparent 88%);
}

/* ---- light theme ---- */
:root[data-theme="light"], .bb-light {
  --bg: oklch(0.985 0.003 60);
  --bg-2: oklch(0.97 0.005 60);
  --surface: #ffffff;
  --surface-2: oklch(0.985 0.003 60);
  --elevated: #ffffff;
  --border: oklch(0.91 0.008 60);
  --border-strong: oklch(0.85 0.01 60);
  --text: oklch(0.22 0.012 250);
  --text-muted: oklch(0.50 0.012 250);
  --text-faint: oklch(0.65 0.01 250);
  --accent: oklch(0.62 0.21 45);
  --accent-fg: #ffffff;
  --accent-soft: oklch(0.95 0.05 45);
  --accent-soft-fg: oklch(0.45 0.18 45);
  --focus-ring: oklch(0.62 0.21 45 / 0.35);
  --grid-line: oklch(0.93 0.005 60);
}

/* ---- dark theme ---- */
:root[data-theme="dark"], .bb-dark {
  --bg: oklch(0.16 0.008 250);
  --bg-2: oklch(0.19 0.008 250);
  --surface: oklch(0.20 0.009 250);
  --surface-2: oklch(0.23 0.010 250);
  --elevated: oklch(0.24 0.011 250);
  --border: oklch(0.30 0.012 250);
  --border-strong: oklch(0.38 0.014 250);
  --text: oklch(0.96 0.004 250);
  --text-muted: oklch(0.68 0.012 250);
  --text-faint: oklch(0.50 0.010 250);
  --accent: oklch(0.74 0.18 50);
  --accent-fg: oklch(0.18 0.01 50);
  --accent-soft: oklch(0.30 0.08 50);
  --accent-soft-fg: oklch(0.85 0.15 50);
  --focus-ring: oklch(0.74 0.18 50 / 0.40);
  --grid-line: oklch(0.24 0.01 250);
}

/* .bb-light / .bb-dark: scoped theme previews inside the Brandbook page.
   Values are IDENTICAL to the attribute-selector themes above - same source. */

/* marketing CTA - bitmap-verified centered (ink offset 0.00); consumes --optics-text calibration */
.btn-xl {
  height: 46px; padding: calc(0px + var(--optics-text, 0px)) 22px 0; font-size: 14.5px; line-height: 1; border-radius: var(--r-md);
  display: inline-flex; align-items: center; gap: 8px; font-weight: 500;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  transition: all 140ms ease;
}
.btn-xl.primary { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.btn-xl.primary:hover { filter: brightness(1.06); transform: translateY(-1px); box-shadow: 0 8px 24px color-mix(in oklab, var(--accent), transparent 70%); }
.btn-xl:hover { border-color: var(--border-strong); background: var(--bg-2); }
.btn-xl.ghost { background: transparent; }

/* ============================================================ */
/*  FORM CONTROLS - THE unified text law                        */
/*  ALL inputs, selects and textareas share ONE text style:     */
/*  Geist (sans) · 9px · 500 · normal tracking - identical to    */
/*  .btn label text. Enforced with !important inside .app-shell */
/*  so per-instance inline styles are VOID by design.           */
/* ============================================================ */
.input, .select, .textarea {
  height: 24px;
  padding: 0 8px;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: normal;
  /* NOTE: never set a px line-height on single-line fields - Chrome forces
     ::placeholder to line-height:normal (UA !important), so text and placeholder
     would center differently. Fixed height + natural line-height centers both. */
  line-height: normal;
  outline: 0;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.textarea { padding: 6px 8px; height: auto; min-height: 48px; resize: vertical; line-height: 1.5; }
.input:focus, .select:focus, .textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--focus-ring); }
.input:hover:not(:disabled), .select:hover:not(:disabled) { border-color: var(--accent); }
/* portal enforcement: catches every field, including inline-styled and classless ones.
   TWO documented exceptions: .bb-code (Brandbook code editor - its transparent textarea
   must match the highlighted <pre> underneath glyph-for-glyph) and .num (numeric ident
   fields - numbers are mono per the type law; the .num rule below owns their metrics so
   locked/editable twins stay identical).
   line-height locked to normal: with the font constant portal-wide, geometry is then a
   pure function of control height - bitmap-verified centered at 19px and 24px. */
.app-shell input:not(.num), .app-shell select:not(.num), .app-shell textarea:not(.bb-code) {
  font-family: var(--font-sans) !important;
  font-size: 9px !important;
  font-weight: 500 !important;
  letter-spacing: normal !important;
}
.app-shell input, .app-shell select { line-height: normal !important; }
/* placeholders: one style everywhere - same size as typed text (no metric jump), lighter voice */
input::placeholder, textarea::placeholder {
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: 400;
  font-style: normal;
  letter-spacing: normal;
  color: var(--text-faint);
  opacity: 1;
}
.select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%), linear-gradient(135deg, var(--text-muted) 50%, transparent 50%); background-position: calc(100% - 11px) center, calc(100% - 8px) center; background-size: 3px 3px, 3px 3px; background-repeat: no-repeat; padding-right: 21px; }

/* Brand wordmark - THE two-colour object (Brandbook §02). ONE weight (600);
   the colour split alone carries the contrast: CORE --text / UCX --text-muted,
   contiguous. Consumers add their own size/tracking (topbar, marketing nav, footer). */
.cx-wordmark { font-weight: 600; color: var(--text); }
.cx-wordmark .dim { color: var(--text-muted); font-weight: inherit; }

/* Generic layout objects - .cx-stack (column) / .cx-row (centered row) / .cx-2col.
   Default gap --sp-2 (5); .tight = --sp-1 (3), .loose = --sp-3 (8). Use these instead
   of hand-rolled display:flex divs; compose one-off padding inline from the scale. */
.cx-stack { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.cx-row { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.cx-stack.tight, .cx-row.tight { gap: var(--sp-1); }
.cx-stack.loose, .cx-row.loose { gap: var(--sp-3); }
.cx-2col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
/* form section divider: full-width eyebrow with a rule, groups fields inside .cx-2col forms */
.cx-form-sec { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px; margin-top: var(--sp-2); font-family: var(--font-mono); font-size: 8px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.07em; color: var(--accent); }
.cx-form-sec::after { content: ""; flex: 1; height: 1px; background: color-mix(in oklab, var(--accent), transparent 55%); }
.cx-form-sec:first-child { margin-top: 0; }
/* full-width form row: N fields share one row inside a .cx-2col form;
   a .cx-fit field (CXField width="fit") shrinks to its content - the rest share the slack */
.cx-form-row { grid-column: 1 / -1; display: flex; gap: var(--sp-3); }
.cx-form-row > * { flex: 1 1 0; min-width: 0; }
.cx-form-row > .cx-fit { flex: 0 0 auto; }

/* Header field row: THE object for label+field pairs in widget headers.
   ONE gap law: label hugs its field at --sp-2 (5px) - same as the ident/slot clusters,
   so every label-to-box gap in the portal is identical. ONE width law: the label
   column is sized in ch to the longest label INCLUDING colon (default 8 = "CHANNEL:" /
   "SUBJECT:"), so no label can overflow its box and eat its own gap - that overflow
   (44px of text in a 40px box) is exactly what made gaps read as non-uniform. */
.cx-hdr-field { display: inline-flex; gap: var(--sp-2); align-items: center; min-width: 0; --lbl-n: 8; /* label column width in ch; a real token (was a fallback-only knob nothing ever set) */ }
.cx-hdr-field > .cx-hdr-lbl, .cx-slot-field > .cx-hdr-lbl { display: inline-block; box-sizing: border-box; width: calc(var(--lbl-n, 8) * (1ch + 0.05em)); flex-shrink: 0; text-align: right; padding-top: calc(0.5px + var(--optics-text, 0px)); /* caps-only baseline law (measured: ink 0.66px high vs the 19px field) */ }

/* ticket-header layout objects: ALL spacing in the ticket header flows from these -
   never per-row numbers. Rows wrap with --sp-1 row gap and --sp-4 column gap. */
.cx-tkt-head { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4) var(--sp-3); border-bottom: 1px solid var(--border); flex-shrink: 0; }
.cx-tkt-rows { display: flex; flex-direction: column; gap: var(--sp-2); }
.cx-hdr-row { display: flex; align-items: center; gap: var(--sp-1) var(--sp-4); flex-wrap: wrap; min-width: 0; }
.cx-hdr-row.grow { flex: 1; }
.cx-actions-row { display: flex; align-items: center; align-content: center; gap: var(--sp-1) var(--sp-3); flex-wrap: wrap; justify-content: flex-end; }

/* ticket-header GRID - ONE column system for all four header rows.
   colA: label+field pair (40 lbl + 4 + 192 field = 236)
   colB: channel-ident zone (18 icon slot + 5 + 40 lbl + 5 + 192 field); .cx-slot-field
         + .cx-ident-slot give non-ident cells (Comp) the same internal geometry, so
         every colB field's left edge is identical
   colC: flexible meta zone - clusters right-aligned (.cx-colC), pairs left (.start)
   Rows living in different containers (the card-header bar and .cx-tkt-rows) still
   align because the template and horizontal padding (--sp-4) are identical. */
.cx-tkt-grid { display: grid; grid-template-columns: max-content max-content minmax(0, 1fr); column-gap: var(--sp-4); row-gap: var(--sp-2); align-items: center; }
/* Track law: colA/colB are max-content, NEVER fixed px. Labels are uniform-width per
   column, fields are fixed 192 - so max-content is deterministic and identical across
   both header grids. (A hard 236px track survived the v2.17 label widening and silently
   squeezed shrinkable fields to 186.8px while the Channel select overflowed: same row,
   three different right edges.) */
.cx-slot-field { display: inline-flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.cx-ident-slot { width: 18px; flex-shrink: 0; }
/* Middle-column label width: ONE width shared by ident labels and the ghost-slot
   (COMP:) label so every col-B field shares a left edge. --midlbl-n = char count of
   the longest middle label incl colon (set per-ticket from JSX; 5 = "COMP:").
   1ch + 0.05em per char mirrors .cx-meta-lbl letter-spacing = exact painted width.
   Right-aligned: any slack (e.g. SMS: in a 5ch box) pools left, reading as column
   structure - labels always hug their field at the cluster gap. */
.cx-tkt-grid { --midlbl-n: 5; }
.cx-tkt-grid .cx-chan-ident > .cx-meta-lbl,
.cx-tkt-grid .cx-slot-field > .cx-hdr-lbl { width: calc(var(--midlbl-n) * (1ch + 0.05em)); min-width: 0; box-sizing: border-box; text-align: right; flex-shrink: 0; padding-top: calc(0.5px + var(--optics-text, 0px)); /* caps-only baseline law - keeps ident labels on the same optical line as .cx-hdr-lbl */ }
.cx-tkt-grid > .cx-colC { justify-self: end; }
/* colC cluster taller than one row (stacked SLA cells): span 2 rows so its height
   distributes instead of inflating row 1 - which centered the 19px fields ~5px down
   and made the header->Name gap read as 10px instead of the structural --sp-2. */
.cx-tkt-grid > .cx-colC.tall { grid-row: span 2; align-self: center; }
.cx-tkt-grid > .cx-colC.start { justify-self: start; }
/* colC start cluster (DEP:) is the only left-aligned colC item - there is no label
   column above/below it to align with, so its label is natural-width: the 8ch floor
   pooled ~22px of slack between the Comp field and the DEP: text. With it gone, the
   only gap there is the grid's own column gap. */
.cx-tkt-grid > .cx-colC.start > .cx-hdr-lbl { width: auto; min-width: 0; }
.cx-tkt-grid > .cx-span-all { grid-column: 1 / -1; }
/* Narrow floor (fit-zoom layouts down to 1280): the rigid 3-column grid would clip
   colC (Dep invisible, SLA/pills cut). Degrade to wrapping flex - whole cells wrap
   to new lines, same 5/13 rhythm, nothing hidden. Column starts realign at >=1380. */
@media (max-width: 1379px) {
  .cx-tkt-grid { display: flex; flex-flow: row wrap; gap: var(--sp-2) var(--sp-4); align-items: center; } /* direction EXPLICIT: .cx-tkt-rows base flex-direction:column must not leak through */
  .cx-tkt-grid > .cx-colC { margin-left: auto; }
  .cx-tkt-grid > .cx-colC.start { margin-left: 0; }
  .cx-tkt-grid > .cx-span-all { flex: 1 1 100%; }
}
/* participant chip (conversation/call header strips) */
.cx-conf-chip { display: inline-flex; align-items: center; gap: 5px; padding: 2px 7px 2px 2px; border-radius: 999px; background: var(--surface); border: 1px solid var(--border); font-size: 9px; }
/* channel-identity composite: [icon] [CHANNEL:] [value] - THE object for channel+ident
   rows in ticket headers. Value slot is .input.hdr (editable recipient) or
   .cx-hdr-valbox (read-only identity). Defined once, used by header AND body. */
.cx-chan-ident { display: inline-flex; gap: var(--sp-2); align-items: center; min-width: 0; }
.cx-chan-icon { width: 18px; height: 18px; border-radius: 5px; color: #fff; display: grid; place-items: center; flex-shrink: 0; }
.cx-hdr-valbox { border: 1px solid var(--border); border-radius: var(--r-md); padding: 0 6px; height: 19px; width: 192px; display: inline-flex; align-items: center; box-sizing: border-box; }
/* numeric ident (phone-like values) - shared .num treatment for BOTH twin states so
   the field never changes typeface when a ticket flips locked/editable. Numbers are
   mono per the type law (same as .cx-clock and table numerics). The span twin also
   needs the digits-only baseline law (cap-height ink, no descenders, measured 1.03px
   high); the input twin centers its own text, so no padding correction there. */
.cx-hdr-valbox.num, .input.hdr.num { font-family: var(--font-mono); font-size: 8.8px; letter-spacing: -0.01em; font-weight: 500; }
.cx-hdr-valbox.num { padding-top: calc(1px + var(--optics-text, 0px)); }
/* meta label: uppercase mono microcopy used before header/meta values */
.cx-meta-lbl { font-size: 8.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-faint); font-family: var(--font-mono); flex-shrink: 0; }
/* read-only header VALUE text: same voice as field text (9px Geist 500) so
   displays and inputs sitting side-by-side read as one system */
.cx-hdr-val { font-family: var(--font-sans); font-size: 9px; font-weight: 500; letter-spacing: normal; color: var(--text); }

/* slim 19px header controls (ticket-header fields; heights from the control scale) */
.input.hdr, .select.hdr { height: 19px; padding: 0 6px; }
.select.hdr { padding: 0 5px; }

:root, [data-theme="light"] {
  /* SPACING TOKENS - the named gap scale (fibonacci 2/3/5/8/13/21). Gaps and
     composition padding come from these steps; off-scale numbers are drift. */
  --sp-0: 2px; --sp-1: 3px; --sp-2: 5px; --sp-3: 8px; --sp-4: 13px; --sp-5: 21px;
  /* ink-on-color: text/icon ink painted over tone- or data-colored fills (channel
     icons, tone pills, chart bars). Library tokens - literal #fff/#000 in markup
     is a Conformance violation. */
  --ink-on-color: #fff;
  --ink-on-accent: #000;
  --c-vip: oklch(0.58 0.19 300);
  --c-flow: oklch(0.64 0.15 95);
}

/* ============================================================ */
/*  TEXT UTILITIES - the named text scale                       */
/*  The ONLY sanctioned way to size text in markup. Inline      */
/*  font styles are a Conformance violation (Brandbook §19).    */
/*  Terminal declarations (!important): a utility must ALWAYS   */
/*  win, exactly like the inline styles it replaced - otherwise */
/*  later-loaded or higher-specificity page rules re-size text. */
/* ============================================================ */
.tx-7 { font-size: 7px !important; }
.tx-75 { font-size: 7.5px !important; }
.tx-8 { font-size: 8px !important; }
.tx-85 { font-size: 8.5px !important; }
.tx-9 { font-size: 9px !important; }
.tx-95 { font-size: 9.5px !important; }
.tx-10 { font-size: 10px !important; }
.tx-105 { font-size: 10.5px !important; }
.tx-11 { font-size: 11px !important; }
.tx-12 { font-size: 12px !important; }
/* numeric readout (call clocks, countdowns): digits are cap-height with no descenders,
   so the line box reserves descender space the ink never uses - same law as .pill /
   .card-header .label. +1.5px top padding centers the ink at 12px mono digits. */
.cx-clock { font-family: var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; padding-top: calc(1.5px + var(--optics-text, 0px)); }
/* row-embedded mono microcopy (toolbar result counters etc): 9px GeistMono ink sits ~2px
   high when flex-centered (same measured law as the card-header label) - +2px top padding
   centers the ink in a 24px control row. */
.cx-row-meta { font-family: var(--font-mono); font-feature-settings: "tnum"; letter-spacing: -0.01em; font-size: 9px; color: var(--text-faint); padding-top: calc(2px + var(--optics-text, 0px)); }
/* call bar (live call / conference strips) - THE object for in-widget call chrome.
   Geometry law: the 1px border-bottom sits INSIDE the 33px box, so a naive flex-center
   lands children on the content center - 0.5px above the visible bar's center. The
   asymmetric padding (4 top / 3 bottom) + 1px border restores true visual centering:
   4 + (25-h)/2 + h/2 = 16.5 = box center for every child height. */
.cx-callbar { box-sizing: border-box; display: flex; align-items: center; flex-wrap: wrap; gap: 3px var(--sp-3); min-height: 33px; padding: 4px 11px 3px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
/* popover anchor - THE wrapper for a trigger button + absolutely-positioned popover.
   Must be inline-flex, never block: a block wrapper wraps its button in an inline
   line box whose baseline reserves ~1px of descender room below the button, making
   the wrapper taller than the button and mis-centering the whole row. */
.cx-popanchor { position: relative; display: inline-flex; align-items: center; }
.tx-13 { font-size: 13px !important; }
.tx-14 { font-size: 14px !important; }
.tx-145 { font-size: 14.5px !important; }
.tx-17 { font-size: 17px !important; }
.tx-19 { font-size: 19px !important; }
.tx-21 { font-size: 21px !important; }
.tx-38 { font-size: 38px !important; }
.w-400 { font-weight: 400 !important; }
.w-500 { font-weight: 500 !important; }
.w-600 { font-weight: 600 !important; }
.w-700 { font-weight: 700 !important; }
.u-up { text-transform: uppercase !important; letter-spacing: 0.05em !important; }
.u-uc { text-transform: uppercase !important; }
.u-none { text-transform: none !important; }
.ls-0 { letter-spacing: 0 !important; }
.ls-t1 { letter-spacing: -0.01em !important; } /* display tightening, mild */
.ls-t2 { letter-spacing: -0.02em !important; } /* display tightening, standard (KPIs, headings) */
.ls-t3 { letter-spacing: -0.03em !important; } /* display tightening, big numerals */
.ls-w6 { letter-spacing: 0.06em !important; }  /* wide microcopy (annotations above u-uc default) */
