/* ─────────────────────────────────────────────────────────────
   Swivel Design System — components.css
   Component-level styles. Load after tokens.css.
   ───────────────────────────────────────────────────────────── */

/* Buttons */
.btn {
  font-family: var(--font-body);
  font-size: var(--fs-ui-caps);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.85;
  padding: 0 var(--space-xl);
  /* Canonical control height. Every control that sits in a row with a button
     (.bg-btn, .bs-action, .field-input) resolves to this same token. */
  height: var(--control-h);
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  transition: background var(--motion-base), border-color var(--motion-base), color var(--motion-base);
  white-space: nowrap;
}
.btn-primary {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-fg);
  box-shadow: var(--elev-1);
}
.btn-primary:hover { background: var(--btn-primary-bg-hover); box-shadow: var(--elev-2); }
.btn-secondary {
  background: transparent;
  color: var(--btn-outline-fg);
  border-color: var(--btn-outline-fg);
}
.btn-secondary:hover { background: var(--graphite-10); }
.btn-tertiary {
  background: transparent;
  color: var(--btn-outline-fg);
}
.btn-tertiary:hover { background: var(--graphite-10); }
.btn-tonal {
  background: var(--slate-bg);
  color: var(--slate-100);
}
.btn-tonal:hover { background: var(--btn-tonal-hover); }
.btn-accent {
  background: var(--switch-100);
  /* Switch cyan is the same bright value in both themes, so the label needs ink
     that stays dark — --graphite-90 inverts to near-white in dark mode and drops
     this CTA to ~1.3:1. --graphite-contrast is near-black under both themes. */
  color: var(--graphite-contrast);
}
.btn-accent:hover { background: var(--btn-accent-hover); }
.btn-danger {
  background: var(--error);
  color: var(--white);
}
.btn-danger:hover { background: var(--btn-danger-hover); }
.btn-tonal-error {
  background: var(--error-bg);
  color: var(--error);
}
.btn-tonal-error:hover { background: var(--btn-tonal-error-hover); }
/* IconButton — standalone square icon-only button, all variants */
.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--control-h); height: var(--control-h);
  padding: 0;
  border: none; border-radius: var(--radius-sm);
  cursor: pointer;
  flex-shrink: 0;
  transition: background 120ms ease, color 120ms ease;
}
.btn-icon .material-symbols-outlined { font-size: var(--icon-md); line-height: 1; }
.btn-icon.btn-sm { width: var(--control-h-sm); height: var(--control-h-sm); }
.btn-icon.btn-sm .material-symbols-outlined { font-size: var(--icon-sm); }
.btn-icon.btn-lg { width: var(--control-h-lg); height: var(--control-h-lg); }
.btn-icon.btn-lg .material-symbols-outlined { font-size: var(--icon-lg); }
/* Variants — reuse the same color contract as .btn */
.btn-icon.btn-primary   { background: var(--btn-primary-bg);    color: var(--btn-primary-fg); }
.btn-icon.btn-primary:hover { background: var(--btn-primary-bg-hover); }
.btn-icon.btn-secondary { background: transparent; color: var(--btn-outline-fg); border: 1px solid var(--btn-outline-fg); }
.btn-icon.btn-secondary:hover { background: var(--graphite-10); }
.btn-icon.btn-tertiary  { background: transparent; color: var(--btn-outline-fg); }
.btn-icon.btn-tertiary:hover { background: var(--graphite-10); }
/* Tonal icon button matches its text sibling: --slate-bg fill, --slate-100 glyph. */
.btn-icon.btn-tonal     { background: var(--slate-bg); color: var(--slate-100); }
.btn-icon.btn-tonal:hover { background: var(--btn-tonal-hover); }
.btn-icon.btn-danger    { background: var(--error); color: var(--white); }
.btn-icon.btn-danger:hover { background: var(--btn-danger-hover); }
.btn-icon:disabled, .btn-icon.is-disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }

.btn:disabled, .btn.is-disabled {
  opacity: 0.4;
  pointer-events: none;
}
.btn-sm { height: 24px; font-size: var(--fs-ui-xs); padding: 0 var(--space-md); }
.btn-lg { height: 40px; font-size: var(--fs-ui-caps); padding: 0 var(--space-xxl); }
/* Icons inside text buttons scale with the button */
.btn:not(.btn-icon) .material-symbols-outlined { font-size: var(--icon-md); line-height: 1; }
.btn-sm:not(.btn-icon) .material-symbols-outlined { font-size: var(--icon-xs); }
.btn-lg:not(.btn-icon) .material-symbols-outlined { font-size: var(--icon-lg); }

/* Button group */
.btn-group {
  display: inline-flex;
  align-items: stretch;
  height: 34px;
  border-radius: var(--radius-sm);
}
.bg-group-label {
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.71;
  color: var(--graphite-bg);
}
.bg-btn {
  appearance: none;
  background: transparent;
  /* Unselected segments sit in slate; the selected one takes the slate fill. */
  color: var(--slate-100);
  border: 1px solid var(--graphite-20);
  height: 34px;
  padding: 0 var(--space-xl);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  cursor: pointer;
  box-sizing: border-box;
  font-family: var(--font-body);
  font-size: var(--fs-ui-caps);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
  position: relative;
}
.bg-btn:hover:not(.is-selected) { background: var(--surface-muted); color: var(--slate-contrast); }
/* Selected segment hover deepens the slate fill and the label together, so the
   label never loses contrast against the new fill. */
.bg-btn.is-selected:hover { background: color-mix(in srgb, var(--slate-bg) 82%, var(--slate-100)); color: var(--slate-contrast); }
.bg-btn + .bg-btn { margin-left: -1px; }
.btn-group .bg-btn:first-child { border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm); }
.btn-group .bg-btn:last-child  { border-top-right-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm); }
.bg-btn .material-symbols-outlined { font-size: var(--icon-md); line-height: 1; }
.bg-btn.is-selected {
  background: var(--slate-bg);
  color: var(--slate-100);
  border-color: var(--slate-100);
  z-index: 1;
}
.bg-btn.icon-only { width: 50px; padding: 0; }
/* Status-toned glyph inside a segment (e.g. a status filter group) */
.bg-btn .material-symbols-outlined.is-success { color: var(--success); }
.bg-btn .material-symbols-outlined.is-warning { color: var(--warning); }
.bg-btn .material-symbols-outlined.is-error { color: var(--error); }
.bg-btn .material-symbols-outlined.is-neutral { color: var(--graphite-bg); }
.btn-group.set-a .bg-btn { color: var(--slate-100); }
.btn-group.set-a .bg-btn:not(.is-selected) { background: transparent; }
.btn-group.set-b .bg-btn { color: var(--slate-100); }
.btn-group.set-b .bg-btn:not(.is-selected) { background: transparent; }
.btn-group.set-a .bg-btn.is-selected { color: var(--slate-100); border-color: var(--graphite-20); }
.btn-group.set-b .bg-btn.is-selected { color: var(--graphite-bg); border-color: var(--graphite-20); }
.btn-group.set-b.text-muted .bg-btn.is-selected { color: var(--graphite-bg); }
.btn-group:not(.is-disabled).set-a .bg-btn:not(.is-selected):hover,
.btn-group:not(.is-disabled).set-b .bg-btn:not(.is-selected):hover { background: var(--surface-muted); color: var(--slate-contrast); }
.btn-group:not(.is-disabled).set-a .bg-btn.is-selected:hover,
.btn-group:not(.is-disabled).set-b .bg-btn.is-selected:hover { background: color-mix(in srgb, var(--slate-bg) 82%, var(--slate-100)); color: var(--slate-contrast); }
/* Disabled variant — selected fills with disabled-state color, rest sit on surface/muted */
.btn-group.is-disabled { pointer-events: none; }
.btn-group.is-disabled .bg-btn,
.btn-group.is-disabled .bg-btn:not(.is-selected) {
  background: var(--surface-muted);
  color: var(--graphite-40);
  border-color: var(--graphite-20);
}
.btn-group.is-disabled .bg-btn.is-selected {
  background: var(--graphite-20);
  color: var(--graphite-50);
  border-color: var(--graphite-20);
}
.bg-btn[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(2px);
  background: var(--graphite-base);
  color: var(--surface-canvas);
  border: 1px solid var(--graphite-contrast);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  z-index: 100;
  transition: opacity 120ms ease, transform 120ms ease;
}
.bg-btn[data-tip]:hover::after, .bg-btn[data-tip]:focus-visible::after { opacity: 1; transform: translateX(-50%) translateY(0); }

/* IconButton tooltip — required on every .btn-icon (enforced below by JS lint) */
.btn-icon { position: relative; }
.btn-icon[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(2px);
  background: var(--graphite-base);
  color: var(--surface-canvas);
  border: 1px solid var(--graphite-contrast);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  z-index: 100;
  transition: opacity 120ms ease, transform 120ms ease;
}
.btn-icon[data-tooltip]:hover::after,
.btn-icon[data-tooltip]:focus-visible::after { opacity: 1; }
/* Missing-tooltip lint highlight — dev-only visual */
.btn-icon:not([data-tooltip]) { outline: 2px dashed var(--error) !important; }

/* ── Split Button ────────────────────────────────────────────
   Two-segment control: left = primary action, right = dropdown
   trigger. Clicking the divider segment opens a context menu;
   clicking outside closes it. */
.btn-split {
  display: inline-flex;
  align-items: stretch;
  border-radius: var(--radius-sm);
  position: relative;
  flex-shrink: 0;
}
.btn-split .bs-action,
.btn-split .bs-trigger {
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  /* Button label type is one contract across .btn, .bg-btn and .bs-action:
     body family, 500, --fs-ui-caps, 0.04em, uppercase. Do not re-specify a
     display font or a heavier weight here — a split button must be
     indistinguishable from the Button next to it. */
  font-family: var(--font-body);
  font-size: var(--fs-ui-caps);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transition: background 120ms ease;
  white-space: nowrap;
}
.btn-split .bs-action {
  padding: 0 var(--space-xl);
  height: var(--control-h);
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}
.btn-split .bs-trigger {
  width: 28px;
  height: var(--control-h);
  padding: 0;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  position: relative;
}
.btn-split .bs-trigger .material-symbols-outlined { font-size: var(--icon-lg); line-height: 1; }

/* Primary (filled) — the same tokens as .btn-primary and the selected
   button-group segment: --btn-primary-bg (slate light, switch-cyan dark) with
   matching ink, so the CTA never diverges from .btn-primary per theme. */
.btn-split-primary .bs-action,
.btn-split-primary .bs-trigger { background: var(--btn-primary-bg); color: var(--btn-primary-fg); }
.btn-split-primary .bs-action  { border-right: 1px solid color-mix(in srgb, var(--btn-primary-fg) 25%, transparent); }
.btn-split-primary .bs-action:hover  { background: var(--btn-primary-bg-hover); }
.btn-split-primary .bs-trigger:hover { background: var(--btn-primary-bg-hover); }
.btn-split-primary.is-open .bs-trigger { background: var(--btn-primary-bg-hover); }

/* Compact — icon-only action segment, space-premium situations */
.btn-split-compact .bs-action { padding: 0; width: var(--control-h); }
.btn-split-compact .bs-action .material-symbols-outlined { font-size: var(--icon-md); }

/* Ghost / text — no fill or stroke; dropdown arrow hidden until hover */
.btn-split-ghost .bs-action,
.btn-split-ghost .bs-trigger {
  background: transparent;
  color: var(--graphite-90);
  font-family: var(--font-body);
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--fs-ui-caps);
}
.btn-split-ghost .bs-action  { padding: 0 var(--space-md); border-right: 1px solid transparent; }
.btn-split-ghost .bs-trigger { width: 22px; }
.btn-split-ghost .bs-action:hover  { background: var(--graphite-10); }
.btn-split-ghost .bs-trigger:hover { background: var(--graphite-10); }
/* Arrow hidden at rest; revealed on container hover or when open */
.btn-split-ghost .bs-trigger .material-symbols-outlined { opacity: 0; font-size: var(--icon-md); transition: opacity 100ms ease; }
.btn-split-ghost:hover .bs-trigger .material-symbols-outlined,
.btn-split-ghost.is-open .bs-trigger .material-symbols-outlined { opacity: 1; }

/* Dropdown menu */
.bs-menu {
  display: none;
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 160px;
  background: var(--surface-base);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-sm);
  box-shadow: 0 4px 16px rgba(19,21,20,0.12);
  z-index: 300;
  padding: 4px 0;
  animation: bs-in 100ms ease;
}
@keyframes bs-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.btn-split.is-open .bs-menu { display: block; }
.bs-item {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: transparent;
  padding: 10px 16px;
  font-family: var(--font-body);
  font-size: var(--fs-ui-dense);
  color: var(--graphite-90);
  cursor: pointer;
  line-height: 1;
}
.bs-item:hover { background: var(--surface-muted, var(--graphite-10)); }
[data-theme="dark"] .bs-menu { box-shadow: 0 4px 16px rgba(0,0,0,0.4); }

.icon { width: 14px; height: 14px; display: inline-block; }
.icon svg { display: block; width: 100%; height: 100%; }

/* Fields */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  min-width: 240px;
}
/* Narrow-field host — a wrapper that opts its field out of the 240px floor.
   Use for fields packed into popover rows (sort / filter builders). */
.field-narrow .field,
.field-narrow .field-inset,
.field-narrow .tag-input { min-width: 0; }
.field-label {
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.71;
  letter-spacing: 0;
  color: var(--graphite-bg);
}
.field-label .req { color: var(--error); margin-left: var(--space-sm); }
.field-input {
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.71;
  height: 34px;
  padding: 0 var(--space-lg);
  border-radius: var(--radius-sm);
  border: 1px solid var(--graphite-20);
  background: var(--surface-input);
  color: var(--graphite-90);
  outline: none;
  transition: border-color var(--motion-base), box-shadow var(--motion-base);
}
.field-input:focus,
.field-input.is-focused {
  border-width: 2px;
  border-color: var(--switch-contrast);
  padding: 0 calc(var(--space-lg) - 1px);
  box-shadow: var(--focus-ring);
}
/* Error + focus — keep the 2px stroke + halo, but switch palette to error
   so the field still reads as invalid rather than "OK, just selected". */
.field.is-error .field-input:focus,
.field.is-error .field-input.is-focused {
  border-color: var(--error);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--error) 32%, transparent);
}
.field-input::placeholder { color: var(--graphite-40); }
.field.is-error .field-input { border-color: var(--error); }
.field.is-error .field-help { color: var(--error); }
.field-help {
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  color: var(--graphite-bg);
}
.field-input-icon {
  position: relative;
  display: flex;
  align-items: center;
}
.field-input-icon .icon-l, .field-input-icon .icon-r {
  position: absolute;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  /* Material Symbols default to 24px, which overflows this slot. Pin the glyph
     to the slot size so adornments match the select arrow. */
  font-size: var(--icon-md);
  line-height: 1;
  color: var(--graphite-bg);
}
/* A date field's calendar glyph is interactive when it opens a picker. */
.field-input-icon .icon-r[role="button"] { cursor: pointer; pointer-events: auto; }
.field-input-icon .icon-r[role="button"]:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.field-input-icon .icon-l { left: 10px; }
.field-input-icon .icon-r { right: 10px; }
.field-input-icon .field-input { width: 100%; }
/* Padding follows the adornments that are actually present. The sibling
   selector covers markup that omits the has-l / has-r hint classes. */
.field-input-icon.has-l .field-input,
.field-input-icon .icon-l ~ .field-input { padding-left: 32px; }
.field-input-icon.has-r .field-input { padding-right: 32px; }
/* Static text adornment — a currency symbol or unit. Shares the leading-icon
   slot and its padding, so a field carries a prefix or an icon, not both. */
.field-prefix {
  position: absolute; left: 10px; z-index: 1;
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  color: var(--graphite-bg); pointer-events: none;
}
.field-inset .field-row .field-prefix { position: static; flex: 0 0 auto; }

/* Textarea — .field-input plus the rules a multi-line control needs. Same
   stroke, focus, and error treatment; the height contract is rows, not
   --control-h, so it never resolves to 34px. */
textarea.field-input {
  min-height: 76px;
  height: auto;
  padding: 8px var(--space-lg);
  line-height: 1.5;
  resize: vertical;
  display: block;
  width: 100%;
  box-sizing: border-box;
}
/* Autosizing textarea — JS sets the height, so the grabber would lie. */
textarea.field-input.is-autosize { resize: none; overflow: hidden; }
/* Inset textarea: the notch label straddles the top border, and a textarea's
   first line starts at the very top edge — so it needs its own top padding to
   clear the label. A single-line inset field is centred in its row and doesn't.
   The gutter moves from the wrapper onto the textarea so the control fills the
   field box edge to edge (a big scrollable region inset by 14px reads as a box
   inside a box) while the text keeps the same 14px gutter. */
.field-inset:has(> textarea.field-input) { padding: 0; }
.field-inset textarea.field-input { min-height: 62px; padding: 11px 14px 7px; }
.field-inset:has(> textarea.field-input):focus-within textarea.field-input,
.field-inset.is-focused:has(> textarea.field-input) textarea.field-input { padding-left: 13px; padding-right: 13px; }
/* Character counter, right-aligned under the field, beside any help text. */
.field-foot {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-md);
}
.field-foot .field-help { flex: 1 1 auto; }
.field-count {
  font-family: var(--font-mono);
  font-size: var(--fs-ui-small);
  color: var(--graphite-bg);
  flex: 0 0 auto;
  /* Right-aligns whether or not help text is present — space-between alone
     leaves a lone counter at flex-start. */
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}
.field-count.is-over { color: var(--error); }

/* Inset-label field — label sits in a notch on the top stroke */
.field-inset {
  position: relative;
  border: 1px solid var(--graphite-20);
  border-radius: var(--radius-sm);
  background: var(--surface-input);
  padding: 0 14px;
  min-width: 240px;
  transition: border-color var(--motion-base), box-shadow var(--motion-base);
}
.field-inset:focus-within,
.field-inset.is-focused {
  border-width: 2px;
  border-color: var(--switch-contrast);
  padding: 0 13px;
  box-shadow: var(--focus-ring);
}
.field-inset.is-error:focus-within,
.field-inset.is-error.is-focused {
  border-color: var(--error);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--error) 32%, transparent);
}
.field-inset .field-label {
  position: absolute;
  top: 0;
  left: 10px;
  transform: translateY(calc(-50% - 2px));
  background: var(--surface-input);
  padding: 0 var(--space-sm);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.02em;
  color: var(--graphite-bg);
  margin: 0;
  z-index: 2;
}
.field-inset .field-input {
  border: none;
  background: transparent;
  height: 34px;
  padding: 0;
  width: 100%;
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.71;
  color: var(--graphite-90);
}
.field-inset .field-input:focus { box-shadow: none; border: none; }
.field-inset .field-row {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}
.field-inset .field-row .icon-l,
.field-inset .field-row .icon-r {
  flex: 0 0 auto;
  font-size: var(--icon-md);
  line-height: 1;
  color: var(--graphite-bg);
  cursor: default;
}
.field-inset .field-row .icon-r { cursor: pointer; }
.field-inset.is-disabled {
  background: var(--graphite-10);
  border-color: var(--graphite-20);
}
.field-inset.is-disabled .field-label { background: var(--graphite-10); }
.field-input:disabled { color: var(--graphite-50); }
.field-inset.is-disabled .field-input { color: var(--graphite-50); }
.field-inset.is-error { border-color: var(--error); }
.field-inset.is-error .field-label { color: var(--error); }

/* Floating variant — modal fields only (see adherence rule label_placement).
   Empty + unfocused: label hidden, the input's own placeholder carries the
   hint. Focused or has a value: label floats to the notch, same spot the
   plain inset label sits permanently. :has() lets this react to real input
   value with no JS state. */
.field-inset.is-floating .field-label {
  top: 50%; left: 14px;
  font-family: var(--font-body); font-size: var(--fs-ui-default); font-weight: 400;
  line-height: 1; letter-spacing: 0; color: var(--graphite-40);
  background: transparent; padding: 0;
  opacity: 0; pointer-events: none;
  transition: top var(--motion-fast), font-size var(--motion-fast), color var(--motion-fast), background var(--motion-fast), padding var(--motion-fast), opacity var(--motion-fast);
}
.field-inset.is-floating:has(.field-input:focus) .field-label,
.field-inset.is-floating:has(.field-input:not(:placeholder-shown)) .field-label,
.field-inset.is-floating.has-value .field-label {
  top: 0; left: 10px;
  transform: translateY(calc(-50% - 2px));
  font-family: var(--font-display); font-size: var(--fs-ui-small); font-weight: 600;
  line-height: 1; letter-spacing: 0.02em; color: var(--graphite-bg);
  background: var(--surface-input); padding: 0 var(--space-sm);
  opacity: 1;
}
.field-inset.is-floating.is-error:has(.field-input:focus) .field-label,
.field-inset.is-floating.is-error:has(.field-input:not(:placeholder-shown)) .field-label,
.field-inset.is-floating.is-error.has-value .field-label { color: var(--error); }

/* ────────────────────────────────────────────────────────────
   Select menu — popover surface for select fields
   ──────────────────────────────────────────────────────────── */
.select-menu {
  width: 260px;
  background: var(--surface-base);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-2);
  padding: var(--space-sm) 0;
  display: flex;
  flex-direction: column;
  font-family: var(--font-body);
}
.sm-search {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  height: 36px;
  padding: 0 var(--space-lg);
  margin: 0 0 var(--space-sm) 0;
}
.sm-search .material-symbols-outlined { font-size: var(--icon-md); color: var(--graphite-bg); }
.sm-search input {
  flex: 1; min-width: 0;
  border: none; background: transparent; outline: none;
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  color: var(--graphite-90);
  padding: 0;
}
.sm-search:focus-within { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.sm-search input::placeholder { color: var(--graphite-40); }

.sm-item {
  display: flex; align-items: center; gap: var(--space-lg);
  height: 34px;
  padding: 0 var(--space-lg);
  color: var(--graphite-90);
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  cursor: pointer;
  background: var(--surface-base);
  transition: background var(--motion-fast);
  user-select: none;
}
.sm-item:hover { background: var(--graphite-10); }
.sm-item.is-selected { background: var(--slate-bg); }
.sm-item.is-disabled { color: var(--graphite-40); pointer-events: none; }

/* Leading platform icon (image or glyph) — keeps row geometry stable */
.sm-icon-l {
  width: 18px; height: 18px; flex: 0 0 18px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-xs);
  overflow: hidden;
  color: var(--graphite-bg);
}
.sm-icon-l img { width: 100%; height: 100%; object-fit: contain; }
.sm-icon-l .material-symbols-outlined { font-size: var(--icon-md); }

/* Section header — non-interactive, sits between groups of items */
.sm-header {
  display: flex; align-items: center;
  height: 34px;
  padding: 0 var(--space-lg);
  color: var(--graphite-bg);
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 500;
  cursor: default;
}

/* Create-new row — pinned at end, plus-glyph + label */
.sm-item.sm-create { color: var(--graphite-90); }
.sm-item.sm-create .material-symbols-outlined { font-size: var(--icon-md); color: var(--graphite-bg); }

/* M3-style checkbox, tinted with Swivel slate */
.sm-check {
  position: relative;
  width: 18px; height: 18px; flex: 0 0 18px;
  border-radius: 2px;
  border: 2px solid var(--graphite-bg);
  background: transparent;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--white);
  transition: background var(--motion-fast), border-color var(--motion-fast);
}
.sm-check .material-symbols-outlined {
  font-size: var(--icon-sm);
  font-variation-settings: 'wght' 700;
  opacity: 0;
}
.sm-check.is-checked {
  background: var(--slate-100);
  border-color: var(--slate-100);
}
.sm-check.is-checked .material-symbols-outlined { opacity: 1; }

/* Indeterminate (some but not all children checked) — used by Select All */
.sm-check.is-indeterminate {
  background: var(--slate-100);
  border-color: var(--slate-100);
}
.sm-check.is-indeterminate::after {
  content: '';
  position: absolute;
  width: 10px; height: 2px;
  background: var(--white);
  border-radius: 1px;
}

/* When a check sits inside a selected row, the slate-bg behind it is fine —
   the check fill is darker slate so contrast holds. */
.sm-item.is-selected .sm-check { border-color: var(--slate-100); }

/* "Only" affordance — appears on hover of a checkbox row, replaces selection
   with just this row. Pill chip, anchored to the right edge of the row. */
.sm-item { position: relative; }
.sm-only {
  position: absolute;
  right: var(--space-lg);
  top: 50%;
  transform: translateY(-50%);
  display: none;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  background: var(--surface-base);
  border: 1px solid var(--graphite-30);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: var(--fs-ui-xs);
  line-height: 1;
  color: var(--graphite-90);
  cursor: pointer;
  transition: background var(--motion-fast);
}
.sm-item:hover .sm-only { display: inline-flex; }
.sm-only:hover { background: var(--graphite-10); }

/* Thin divider between the select-all row and the rest of the list */
.sm-rule { height: 1px; background: var(--surface-stroke); margin: var(--space-sm) 0; }

/* Tag input — multi-value chip input */
.ti-chip {
  display: inline-flex; align-items: center; gap: var(--space-sm);
  height: 22px; padding: 0 var(--space-sm) 0 10px;
  background: var(--graphite-10);
  border-radius: var(--radius-full);
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-90);
  white-space: nowrap;
}
.ti-chip.has-icon { padding-left: var(--space-sm); }
.ti-chip .ti-chip-icon {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--icon-2xs); color: var(--graphite-bg);
}
.ti-chip-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--graphite-bg); color: white;
  font-size: 9px; cursor: pointer; font-family: var(--font-body);
}
.tag-input {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--graphite-20);
  border-radius: var(--radius-sm);
  background: var(--surface-input);
  min-height: 34px;
  box-sizing: border-box;
}
.tag-input.has-l { padding-left: 36px; }
/* A chips trigger nested in an inset field must not draw its own box —
   the .field-inset already provides the stroke and fill. */
.field-inset .tag-input { border: 0; background: transparent; padding: 0; min-height: 34px; width: 100%; align-items: center; }
.field-inset .tag-input.has-r { padding-right: 22px; }
.tag-input.has-r { padding-right: 36px; }
.tag-input .ti-icon-l, .tag-input .ti-icon-r {
  position: absolute; top: 50%; transform: translateY(-50%);
  color: var(--graphite-bg); font-size: var(--icon-md);
  opacity: 0; transition: opacity .12s; cursor: pointer;
}
.tag-input:hover .ti-icon-l, .tag-input:hover .ti-icon-r { opacity: 1; }
.tag-input .ti-icon-l { left: 10px; }
.tag-input .ti-icon-r { right: 10px; }
.tag-input.is-disabled:hover .ti-icon-r { opacity: 0; }
.field.is-error .tag-input { border-color: var(--error); }
.field.is-error .tag-input .ti-icon-r { color: var(--error); }
.tag-input.is-disabled { background: var(--graphite-10); }
.tag-input.is-disabled .ti-chip-x { display: none; }
.tag-input.is-disabled .ti-chip { background: var(--graphite-20); }
.field-inset.tag-inset.is-disabled .ti-chip { background: var(--graphite-20); }

/* Inset tag input — chips inside .field-inset notch border */
.field-inset.tag-inset { padding: 0; min-height: 34px; }
.field-inset.tag-inset .tag-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm);
  padding: 3px var(--space-lg);
  position: relative;
  min-height: 32px;
  box-sizing: border-box;
}
.field-inset.tag-inset.has-l .tag-row { padding-left: 36px; }
.field-inset.tag-inset.has-r .tag-row { padding-right: 36px; }
.field-inset.tag-inset .ti-icon-l, .field-inset.tag-inset .ti-icon-r {
  position: absolute; top: 50%; transform: translateY(-50%);
  color: var(--graphite-bg); font-size: var(--icon-md);
  opacity: 0; transition: opacity .12s; cursor: pointer;
}
.field-inset.tag-inset:hover .ti-icon-l,
.field-inset.tag-inset:hover .ti-icon-r { opacity: 1; }
.field-inset.tag-inset .ti-icon-l { left: 10px; }
.field-inset.tag-inset .ti-icon-r { right: 10px; }
.field-inset.tag-inset.is-disabled:hover .ti-icon-r { opacity: 0; }
.field-inset.tag-inset.is-error .ti-icon-r { color: var(--error); }
.field-inset.tag-inset.is-disabled .ti-chip-x { display: none; }

.tag-input-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(240px, 1fr));
  gap: 14px 18px;
}
/* Canonical chips inside tag input */
.tag-input .chip, .field-inset.tag-inset .chip { font-size: var(--fs-ui-small); }
.tag-input.is-disabled .chip,
.field-inset.tag-inset.is-disabled .chip { background: var(--graphite-20); }
.tag-input.is-disabled .chip-dismiss,
.field-inset.tag-inset.is-disabled .chip-dismiss { display: none; }

/* Chips */
.chip {
  --chip-fill: var(--graphite-10);
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  font-family: var(--font-body);
  font-size: var(--fs-ui-xs);
  font-weight: 500;
  letter-spacing: 0.02em;
  height: 24px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-full);
  background: var(--graphite-10);
  color: var(--graphite-80);
  border: 1px solid transparent;
}
.chip.has-dismiss { padding-right: var(--space-sm); }
.chip.is-selected { background: var(--slate-bg); color: var(--slate-contrast); --chip-fill: var(--slate-bg); }
.chip.is-outlined { background: transparent; border-color: var(--graphite-20); color: var(--graphite-80); --chip-fill: var(--graphite-20); }
.chip.is-accent { background: var(--switch-100); color: var(--graphite-contrast); --chip-fill: var(--switch-100); }
.chip-sm { height: 20px; font-size: var(--fs-ui-tag); padding: 0 var(--space-md); gap: var(--space-sm); }
.chip-lg { height: 32px; font-size: var(--fs-ui-caps); padding: 0 14px; gap: var(--space-md); }
.chip .chip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--icon-xs);
  line-height: 1;
  margin-left: -2px;
}
.chip-sm .chip-icon { font-size: var(--icon-2xs); }
.chip-lg .chip-icon { font-size: var(--icon-sm); }
.chip-square { border-radius: var(--radius-sm); }
.chip.is-icon-only { width: 24px; padding: 0; justify-content: center; }
.chip.is-icon-only.chip-sm { width: 20px; }
.chip.is-icon-only.chip-lg { width: 32px; }
.chip.is-icon-only .chip-icon { margin-left: 0; }
.chip-stacked {
  flex-direction: column;
  height: auto;
  padding: var(--space-md) var(--space-lg);
  gap: var(--space-sm);
  line-height: 1;
}
.chip-stacked.chip-sm { padding: var(--space-sm) var(--space-md); gap: 3px; }
.chip-stacked.chip-lg { padding: 10px var(--space-xl); gap: var(--space-sm); }
.chip-stacked .chip-icon { margin-left: 0; font-size: var(--icon-md); }
.chip-stacked.chip-sm .chip-icon { font-size: var(--icon-xs); }
.chip-stacked.chip-lg .chip-icon { font-size: var(--icon-xl); }
/* Scoped under .chip so it outranks the Material Symbols stylesheet's own
   .material-symbols-outlined { font-size: var(--icon-2xl) }, which Google serves after
   ours at equal specificity — an unscoped .chip-dismiss loses that tie. */
.chip .chip-dismiss {
  display: inline-flex; align-items: center; justify-content: center;
  /* Derived from the chip's own FILL (not its ink). Default/outlined fills
     are near-white in light mode, so "lighter" would vanish — always shift
     toward the readable direction: darker in light mode, lighter in dark,
     deepening further on hover. */
  color: color-mix(in srgb, var(--chip-fill) 82%, black 18%);
  font-size: var(--icon-sm);
  line-height: 1;
  cursor: pointer;
  flex: 0 0 auto;
  font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 20;
}
.chip .chip-dismiss:hover { color: color-mix(in srgb, var(--chip-fill) 68%, black 32%); opacity: 1; }
[data-theme="dark"] .chip .chip-dismiss,
:root:not([data-theme="light"]) .chip .chip-dismiss { color: color-mix(in srgb, var(--chip-fill) 82%, white 18%); }
[data-theme="dark"] .chip .chip-dismiss:hover,
:root:not([data-theme="light"]) .chip .chip-dismiss:hover { color: color-mix(in srgb, var(--chip-fill) 68%, white 32%); }

/* Badges (status) */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  font-family: var(--font-body);
  font-size: var(--fs-ui-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  height: 22px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-sm);
}
.badge-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge .material-symbols-outlined { font-size: var(--icon-xs); line-height: 1; font-variation-settings: 'opsz' 20, 'wght' 500, 'FILL' 1, 'GRAD' 0; }
.badge-success { background: var(--success-bg); color: var(--success-fg); }
.badge-warning { background: var(--warning-bg); color: var(--warning-fg); }
.badge-info { background: var(--info-bg); color: var(--info-fg); }
.badge-error { background: var(--error-bg); color: var(--error-fg); }
.badge-neutral { background: var(--graphite-10); color: var(--graphite-80); }
.badge-pending { background: var(--graphite-10); color: var(--graphite-90); }

/* Bare status icon (no chip) — used in tables, headers, and the status row */
.status-ico {
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  outline: none;
}
.status-ico .material-symbols-outlined { font-size: var(--icon-xl); line-height: 1; }
.status-ico:focus-visible { box-shadow: 0 0 0 2px var(--switch-100); border-radius: var(--radius-sm); }

/* Tooltip on hover/focus — used by status badges */
[data-tip]:not(.tooltip-host) { position: relative; }
[data-tip]:not(.tooltip-host)::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--graphite-base);
  color: var(--surface-canvas);
  border: 1px solid var(--graphite-contrast);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-sm);
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease, transform .12s ease;
  z-index: 100;
}
[data-tip]:hover::after,
[data-tip]:focus-visible::after { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Checkbox — the canonical binary form control.
   A real <input type="checkbox">, styled to match .sm-check (the same treatment
   inside select-menu rows). Use for multi-select lists, table selection columns,
   and form opt-ins. For a setting that takes effect immediately, use .switch. */
.checkbox,
.table-card .cell-select input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  margin: 0;
  width: 18px; height: 18px; flex: 0 0 18px;
  border-radius: 2px;
  border: 2px solid var(--graphite-bg);
  background-color: transparent;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  cursor: pointer;
  display: inline-block;
  transition: background-color var(--motion-fast), border-color var(--motion-fast);
}
.checkbox:checked,
.table-card .cell-select input[type="checkbox"]:checked {
  background-color: var(--slate-100);
  border-color: var(--slate-100);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23ffffff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.4l3.2 3.2L13 4.8'/%3E%3C/svg%3E");
}
.checkbox:indeterminate,
.table-card .cell-select input[type="checkbox"]:indeterminate {
  background-color: var(--slate-100);
  border-color: var(--slate-100);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3' y='7' width='10' height='2' rx='1' fill='%23ffffff'/%3E%3C/svg%3E");
}
.checkbox:hover:not(:disabled) { border-color: var(--slate-100); }
.checkbox:focus-visible,
.table-card .cell-select input[type="checkbox"]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}
.checkbox:disabled { opacity: 0.4; cursor: default; }
/* Small — table selection columns and dense lists */
.checkbox-sm { width: 15px; height: 15px; flex-basis: 15px; border-width: 1.5px; }
/* Label row. The whole row is the hit target, so the label wraps the input. */
.checkbox-row {
  display: flex; align-items: center; gap: 10px;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  color: var(--graphite-90);
}
.checkbox-row.is-disabled { color: var(--graphite-30); cursor: default; }
/* Secondary line under the label. */
.checkbox-row .cb-text { display: flex; flex-direction: column; gap: 2px; }
.checkbox-row .cb-help { font-size: var(--fs-ui-small); color: var(--slate-100); }

/* Breadcrumb — the small trail above a page heading.
   Always sits above the page's own <h1>; it never IS the heading. Parents in
   --graphite-bg, the current segment in --graphite-90. On an object page the
   object's ID appears as a copyable .id-chip. */
.breadcrumb {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 6px;
  font-family: var(--font-body);
  font-size: var(--fs-ui-small);
  font-weight: 500;
  line-height: 1.4;
  color: var(--graphite-90);
  margin: 0;
}
/* Segments are their own flex boxes so a leading glyph aligns to its label. */
.breadcrumb .bc-seg,
.breadcrumb a,
.breadcrumb .bc-link,
.breadcrumb .bc-current,
.breadcrumb > span:not(.bc-sep) {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.breadcrumb a,
.breadcrumb .bc-link {
  color: var(--link);
  text-decoration: none;
  border-radius: var(--radius-sm);
}
.breadcrumb a:hover,
.breadcrumb .bc-link:hover { color: var(--link-hover); text-decoration: underline; }
.breadcrumb a:focus-visible,
.breadcrumb .bc-link:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* Current page — never a link */
.breadcrumb .bc-current { color: var(--graphite-90); font-weight: 600; }
.breadcrumb .bc-sep { color: var(--graphite-20); user-select: none; }
/* Leading object glyph */
.breadcrumb .material-symbols-outlined { font-size: var(--icon-sm); color: var(--graphite-bg); }

/* Toast — a transient confirmation, not a message about the page.
   Reuses the .alert body wholesale (.alert-icon, .alert-body, .alert-title,
   .alert-dismiss) and adds only the stack, elevation, and enter/exit motion.
   Anything the user must read or act on stays an inline Alert. */
.toast-region {
  position: fixed;
  z-index: 900;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  pointer-events: none;
  max-width: 420px;
}
.toast-region.pos-bottom-right { right: var(--space-xl); bottom: var(--space-xl); align-items: flex-end; }
.toast-region.pos-bottom-left { left: var(--space-xl); bottom: var(--space-xl); align-items: flex-start; }
.toast-region.pos-top-right { right: var(--space-xl); top: var(--space-xl); align-items: flex-end; }
.toast-region.pos-top-center { left: 50%; transform: translateX(-50%); top: var(--space-xl); align-items: center; }
.toast {
  pointer-events: auto;
  box-shadow: var(--elev-3);
  min-width: 260px;
  animation: toast-in var(--motion-base) cubic-bezier(0.2, 0, 0, 1) both;
}
.toast.is-leaving { animation: toast-out var(--motion-fast) ease-in both; }
/* An action sits inside the body, under the message — never in place of dismiss. */
.toast-action {
  appearance: none; background: transparent; border: 0; padding: 0;
  margin-top: var(--space-sm);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
  align-self: flex-start;
}
.toast-action:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes toast-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(4px); } }
@media (prefers-reduced-motion: reduce) {
  .toast, .toast.is-leaving { animation-duration: 1ms; }
}

/* ────────────────────────────────────────────────────────────
   Charts — see design-system/charts.rules.yaml for the full rule
   registry and design-system/Charts.html for the rendered showcase.
   The plot is SVG; these classes own the chrome around it.
   ──────────────────────────────────────────────────────────── */
.chart { position: relative; width: 100%; font-family: var(--font-body); }
.chart-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-lg); margin-bottom: var(--space-lg);
}
.chart-title {
  font-family: var(--font-display); font-size: var(--fs-ui-default);
  font-weight: 600; color: var(--graphite-90); margin: 0;
}
.chart-sub { font-size: var(--fs-ui-small); color: var(--graphite-bg); margin: 0; }
.chart-plot { display: block; width: 100%; overflow: visible; }
/* Axis type. Tabular numerals so tick labels and tooltip values line up. */
.chart-axis-label {
  font-family: var(--font-body); font-size: var(--fs-axis-tick);
  fill: var(--graphite-bg); font-variant-numeric: tabular-nums;
}
.chart-grid-line { stroke: var(--surface-stroke); stroke-width: 1; }
.chart-baseline { stroke: var(--graphite-20); stroke-width: 1; }
/* A target or reference line is dashed and labelled — never a solid series. */
.chart-target-line { stroke: var(--graphite-bg); stroke-width: 1; stroke-dasharray: 4 3; }
.chart-series-line { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* Missing data connects through as a dashed segment, never a drop to zero. */
.chart-series-line.is-gap { stroke-dasharray: 3 3; }
.chart-bar { transition: opacity var(--motion-fast); }
.chart-bar.is-muted { opacity: 0.35; }
.chart-point { stroke: var(--surface-base); stroke-width: 2; }
.chart-crosshair { stroke: var(--graphite-30); stroke-width: 1; stroke-dasharray: 3 3; }

/* Tooltip — always --surface-base on a 1px --surface-stroke hairline, in both
   themes. The tokens adapt; never invert this per theme. */
.chart-tooltip {
  position: absolute; z-index: 20; pointer-events: none;
  min-width: 172px; padding: var(--space-lg);
  background: var(--surface-base);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-2);
}
.chart-tooltip-title {
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  color: var(--graphite-90); margin: 0 0 var(--space-md);
}
.chart-tooltip-row {
  display: flex; align-items: center; gap: var(--space-md);
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  color: var(--graphite-bg); padding: 1px 0;
}
.chart-tooltip-row .ct-swatch {
  flex: 0 0 auto; width: 16px; height: 3px; border-radius: 2px;
}
.chart-tooltip-row .ct-name { flex: 1 1 auto; }
.chart-tooltip-row .ct-value {
  font-family: var(--font-display); font-size: var(--fs-ui-small);
  font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--graphite-bg); margin-left: auto;
}
/* Only one row promotes at a time, and rows never reorder on hover. */
.chart-tooltip-row.is-active { color: var(--graphite-90); }
.chart-tooltip-row.is-active .ct-value { color: var(--graphite-90); }
.chart-tooltip-row.is-other .ct-swatch { background: var(--graphite-20); }

/* Legend — the swatch matches the shape it describes, never a coloured icon. */
.chart-legend {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-lg); margin-top: var(--space-lg);
}
.chart-legend-item {
  display: inline-flex; align-items: center; gap: var(--space-md);
  font-size: var(--fs-ui-small); color: var(--graphite-bg);
}
.chart-legend-item .cl-swatch { width: 16px; height: 3px; border-radius: 2px; flex: 0 0 auto; }
.chart-legend-item .cl-swatch.is-block { height: 10px; border-radius: 2px; }

/* Donut — the centre anchor is what justifies the format. */
.chart-donut-wrap { position: relative; display: inline-flex; align-items: center; justify-content: center; }
.chart-donut-anchor {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; gap: 2px;
}
.chart-donut-value {
  font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 600;
  line-height: 1; color: var(--graphite-90); font-variant-numeric: tabular-nums;
}
.chart-donut-label { font-size: var(--fs-ui-small); color: var(--graphite-bg); }

/* Sparkline — inline context for one metric, no axes. */
.chart-sparkline { display: inline-block; vertical-align: middle; line-height: 0; }

/* KPI card — one number, one label, one delta. */
.kpi-card {
  display: flex; flex-direction: column; gap: var(--space-sm);
  padding: var(--space-xl);
  background: var(--surface-base);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-md);
}
.kpi-label {
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-bg);
}
/* UI/Metric token — Antarctica 700 / --fs-ui-metric / 0.95, in
   --switch-contrast. The token colours the value so KPI readouts anchor to the
   brand accent without a separate accent treatment. Not Avantt: the display
   face is for headings and titles, never for a metric readout. */
.kpi-value {
  font-family: var(--font-body); font-size: var(--fs-ui-metric); font-weight: 700;
  line-height: 0.95; color: var(--switch-contrast); font-variant-numeric: tabular-nums;
}
.kpi-foot { display: flex; align-items: center; gap: var(--space-md); }
.kpi-delta {
  display: inline-flex; align-items: center; gap: 2px;
  font-family: var(--font-mono); font-size: var(--fs-ui-xs); font-weight: 500;
  letter-spacing: 0.02em; font-variant-numeric: tabular-nums;
  color: var(--graphite-bg);
}
.kpi-delta.is-up { color: var(--success); }
.kpi-delta.is-down { color: var(--error); }
.kpi-delta .material-symbols-outlined { font-size: var(--icon-sm); }
.kpi-window { font-size: var(--fs-ui-small); color: var(--graphite-bg); }
.kpi-card .chart-sparkline { margin-top: var(--space-sm); }

/* Bullet — actual vs target with qualitative bands. Replaces gauges. */
.chart-bullet-band { fill: var(--graphite-10); }
.chart-bullet-band.is-mid { fill: var(--graphite-20); }
.chart-bullet-measure { fill: var(--viz-slate); }
.chart-bullet-target { stroke: var(--graphite-90); stroke-width: 2; }

/* ─────────────────────────────────────────────────────────────
   DATE PICKER (modal-style, MD3 docked layout recolored)
   ───────────────────────────────────────────────────────────── */

.md-datepicker {
  background: var(--md-surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--elev-3);
  border: 1px solid var(--md-outline);
  width: 360px;
  overflow: hidden;
  font-family: var(--font-body);
}
.md-dp-header {
  background: var(--md-surface-container);
  padding: 20px 24px 16px;
  border-bottom: 1px solid var(--md-outline-variant);
}
.md-dp-eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-ui-small);
  letter-spacing: 0.04em;
  color: var(--md-on-surface-variant);
  text-transform: uppercase;
  margin-bottom: 8px;
}
.md-dp-headline {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.1;
  color: var(--md-on-surface);
  margin: 0;
}

.md-dp-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 16px 8px;
}
.md-dp-month {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-ui-default);
  color: var(--md-on-surface);
  letter-spacing: 0.02em;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.md-dp-month .material-symbols-outlined { font-size: var(--icon-md); color: var(--md-on-surface-variant); }
.md-dp-arrows { display: flex; gap: 4px; }
.md-icon-btn {
  all: unset;
  cursor: pointer;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--md-on-surface-variant);
  transition: background var(--motion-fast);
}
.md-icon-btn:hover { background: var(--md-state-hover); }
.md-icon-btn .material-symbols-outlined { font-size: var(--icon-md); }

.md-dp-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  padding: 0 12px 12px;
}
.md-dp-dow {
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-ui-small);
  letter-spacing: 0.02em;
  color: var(--md-on-surface-variant);
  padding: 8px 0;
}
.md-dp-cell {
  all: unset;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-ui-caps);
  color: var(--md-on-surface);
  margin: 1px auto;
  transition: background var(--motion-fast);
}
.md-dp-cell.is-muted { color: var(--md-on-surface-variant); opacity: 0.4; }
/* Range grids leave adjacent-month cells inert — the same date is a real cell
   in the other month's grid, so any fill here would double-draw the ribbon. */
.md-range-grid .md-dp-cell.is-inert { cursor: default; pointer-events: none; }
.md-range-grid .md-dp-cell.is-inert::before,
.md-range-grid .md-dp-cell.is-inert::after { content: none; }
.md-dp-cell:hover { background: var(--md-state-hover); }
.md-dp-cell.is-today {
  border: 1px solid var(--md-primary);
  color: var(--md-primary);
}
.md-dp-cell.is-selected {
  background: var(--md-primary);
  color: var(--md-on-primary);
  border: 1px solid var(--md-primary);
}
.md-dp-cell.is-selected:hover { background: var(--md-primary); }

.md-dp-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 16px 16px;
}
.md-text-btn {
  all: unset;
  cursor: pointer;
  padding: 8px 16px;
  border-radius: var(--radius-full);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-ui-caps);
  letter-spacing: 0.04em;
  color: var(--md-primary);
  transition: background var(--motion-fast);
}
.md-text-btn:hover { background: var(--md-state-hover); }

/* ─────────────────────────────────────────────────────────────
   DATE RANGE PICKER (two months, range fill)
   ───────────────────────────────────────────────────────────── */

.md-range {
  background: var(--md-surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--elev-3);
  border: 1px solid var(--md-outline);
  width: 680px;
  overflow: hidden;
  font-family: var(--font-body);
}
/* Preset rail — a left column of relative ranges. Almost every range an
   operator wants is relative ("last 7 days"), and reaching one by clicking two
   cells means counting backwards from today. Presets are the primary path; the
   grid is the fallback for an arbitrary span. */
.md-range-presets {
  /* Chips hug their text rather than stretching to a column width — a chip is a
     chip, and a stretched one reads as a list of buttons. */
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  padding: var(--space-lg);
  border-right: 1px solid var(--md-outline);
}
.md-preset {
  appearance: none; cursor: pointer;
  padding: 7px 14px;
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  background: var(--md-surface-container);
  font-family: var(--font-body); font-size: var(--fs-ui-small); font-weight: 500;
  color: var(--md-on-surface);
  text-align: left;
  white-space: nowrap;
}
.md-preset:hover { background: var(--md-surface-container-hi); }
.md-preset:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* Tonal when active — the same --switch-bg the range ribbon uses, so the chip
   and the span it produced read as one thing. Not --md-primary: a solid chip
   competes with the selected endpoints in the grid. */
.md-preset.is-active {
  background: var(--switch-bg);
  color: var(--md-on-primary-container);
  border-color: var(--md-primary);
}
/* Reset clears rather than selects, so it sits apart and stays quiet. */
.md-preset.is-reset {
  margin-top: var(--space-md);
  background: transparent;
  border-color: var(--md-outline);
  color: var(--md-on-surface-variant);
}
.md-preset.is-reset:hover { background: var(--md-state-hover); color: var(--md-on-surface); }
/* With a rail the dialog becomes a two-column shell. Every non-rail child is
   pinned to column 2 explicitly — auto-placement would fill row by row and drop
   the month nav and the action row into the rail's column. The rail spans the
   rows rather than using `1 / -1`, which resolves against the EXPLICIT grid and
   would collapse to row 1 with no grid-template-rows declared. */
.md-range.has-presets { display: grid; grid-template-columns: auto 1fr; align-items: start; }
.md-range.has-presets > .md-range-presets {
  grid-column: 1;
  grid-row: 1 / span 99;
  /* align-items: start is right for the content column; the rail's divider has
     to reach the bottom of the dialog. */
  align-self: stretch;
}
.md-range.has-presets > :not(.md-range-presets) { grid-column: 2; }

.md-range-header {
  background: var(--md-surface-container);
  padding: 20px 24px 16px;
  border-bottom: 1px solid var(--md-outline-variant);
}
.md-range-headline {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-h4);
  color: var(--md-on-surface);
  margin: 0;
  display: flex;
  align-items: center;
  gap: 12px;
}
.md-range-headline .arrow { color: var(--md-on-surface-variant); font-weight: 400; }
.md-range-months {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  padding: 8px 4px 4px;
}
.md-range-month { padding: 8px 12px; }
.md-range-month-label {
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-ui-default);
  color: var(--md-on-surface);
  padding: 8px 0 4px;
}

/* Range cells are a 3-layer stack:
   · ::before  = ribbon fill between endpoints (z-index 1)
   · ::after   = solid endpoint circle (z-index 2)
   · .md-day-num = day text, raised above both (z-index 3) */
.md-range-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}
.md-range-grid .md-dp-dow { padding: 8px 0; }
.md-range-grid .md-dp-cell {
  position: relative;
  width: 36px;
  height: 36px;
  background: transparent;
  border: 0;
  isolation: isolate;       /* each cell becomes its own stacking context */
  z-index: 0;
}
.md-range-grid .md-day-num {
  position: relative;
  z-index: 3;
}

/* Ribbon — horizontal fill between range endpoints. */
.md-range-grid .in-range::before {
  content: "";
  position: absolute;
  inset: 4px -4px 4px -4px;
  background: var(--md-primary-container);
  border-radius: 0;
  z-index: 1;
}
.md-range-grid .range-start::before {
  inset: 4px -4px 4px 4px;
  border-top-left-radius: 999px;
  border-bottom-left-radius: 999px;
}
.md-range-grid .range-end::before {
  inset: 4px 4px 4px -4px;
  border-top-right-radius: 999px;
  border-bottom-right-radius: 999px;
}
.md-range-grid .range-start.range-end::before {
  inset: 4px;
  border-radius: 999px;
}

/* Endpoint circle — solid primary fill painted ON TOP of the ribbon. */
.md-range-grid .is-selected::after {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  background: var(--md-primary);
  z-index: 2;
}
.md-range-grid .is-selected { color: var(--md-on-primary); }

/* ─────────────────────────────────────────────────────────────
   TIME PICKER (dial)
   ───────────────────────────────────────────────────────────── */

.md-timepicker {
  background: var(--md-surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--elev-3);
  border: 1px solid var(--md-outline);
  width: 360px;
  overflow: hidden;
  font-family: var(--font-body);
}
.md-tp-header {
  background: var(--md-surface-container);
  padding: 20px 24px 16px;
  border-bottom: 1px solid var(--md-outline-variant);
  text-align: left;
}
.md-tp-eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-ui-small);
  letter-spacing: 0.04em;
  color: var(--md-on-surface-variant);
  text-transform: uppercase;
  margin-bottom: 12px;
}
.md-tp-time-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
  font-family: var(--font-display);
}
/* The time readout is a metric, not a heading — 40px sits between --fs-h2 and
   --fs-ui-metric with no token behind it, so it uses the metric token at the M3
   dial's proportions. */
.md-tp-num {
  flex: 1;
  background: var(--md-surface);
  border: 1px solid var(--md-outline);
  border-radius: var(--radius-md);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-ui-metric);
  line-height: 1;
  color: var(--md-on-surface);
  display: flex;
  align-items: center;
  justify-content: center;
  height: 72px;
}
.md-tp-num.is-active {
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
  border-color: var(--md-primary);
}
.md-tp-colon {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-ui-metric);
  color: var(--md-on-surface);
  display: flex;
  align-items: center;
}
.md-tp-ampm {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--md-outline);
  border-radius: var(--radius-md);
  overflow: hidden;
  width: 56px;
}
.md-tp-ampm button {
  all: unset;
  cursor: pointer;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-ui-caps);
  color: var(--md-on-surface-variant);
  letter-spacing: 0.04em;
}
.md-tp-ampm button.is-active {
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
}
.md-tp-ampm button + button { border-top: 1px solid var(--md-outline); }

/* Numeric mode — M3's input time picker. The hour and minute become real text
   inputs; the field keeps the same 72px box as the dial mode's readout so the
   toggle does not resize the card. */
.md-tp-num input {
  all: unset;
  width: 100%; height: 100%;
  text-align: center;
  font-family: inherit; font-size: inherit; font-weight: inherit;
  color: inherit;
  font-variant-numeric: tabular-nums;
}
.md-tp-num.is-input { cursor: text; }
.md-tp-num.is-input:focus-within {
  border-color: var(--md-primary);
  box-shadow: var(--focus-ring);
}
.md-tp-labels {
  display: flex; gap: var(--space-md);
  padding: 6px 0 0;
  font-family: var(--font-body);
  font-size: var(--fs-ui-small);
  color: var(--md-on-surface-variant);
}
.md-tp-labels span:first-child { flex: 1; text-align: center; }
.md-tp-labels span:last-child { flex: 1; text-align: center; }
/* Mode toggle sits on the action row, left of Cancel/OK, as M3 places it. */
.md-tp-mode {
  margin-right: auto;
  display: inline-flex; align-items: center; gap: var(--space-sm);
}

.md-tp-dial-wrap {
  padding: 20px;
  display: flex;
  justify-content: center;
}
.md-tp-dial {
  width: 256px;
  height: 256px;
  position: relative;
}
.md-tp-dial-bg {
  position: absolute;
  inset: 0;
  background: var(--md-surface-container);
  border-radius: 50%;
}
.md-tp-num-mark {
  position: absolute;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--fs-ui-default);
  color: var(--md-on-surface);
  transform: translate(-50%, -50%);
  user-select: none;
}
.md-tp-num-mark.is-selected {
  background: var(--md-primary);
  color: var(--md-on-primary);
}
.md-tp-hand {
  position: absolute;
  background: var(--md-primary);
  border-radius: 999px;
  transform-origin: 50% 100%;
}
/* The dial marks are real buttons — reset the chrome without disturbing the
   .is-selected fill, which wins on specificity. */
button.md-tp-num-mark {
  appearance: none; -webkit-appearance: none;
  border: 0; padding: 0; background: transparent; cursor: pointer;
}
button.md-tp-num-mark:hover:not(.is-selected) { background: var(--md-state-hover); }
button.md-tp-num-mark:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.md-dp-cell:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.md-text-btn:focus-visible, .md-icon-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* ─────────────────────────────────────────────────────────────
   DATE INPUT FIELD (MD3 outlined text field with calendar icon)
   ───────────────────────────────────────────────────────────── */

.md-input {
  position: relative;
  width: 280px;
  height: var(--control-h);            /* 34px — matches Swivel field density */
  border: 1px solid var(--md-outline);
  border-radius: var(--radius-sm);
  background: var(--md-surface);
  display: flex;
  align-items: center;
  padding: 0 14px;
  gap: 8px;
  transition: border-color var(--motion-base);
  font-family: var(--font-body);
}
.md-input:hover { border-color: var(--md-on-surface); }
.md-input:focus-within,
.md-input.is-focused {
  border: 2px solid var(--switch-contrast);
  padding: 0 13px;
  box-shadow: var(--focus-ring);
}
.md-input.is-error { border-color: var(--error); }
/* Error + focus — keep the 2px stroke + halo but flip to error palette so
   the field still reads invalid, not "OK, just selected". Matches the
   Component Library focus rule. */
.md-input.is-error:focus-within,
.md-input.is-error.is-focused {
  border: 2px solid var(--error);
  padding: 0 13px;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--error) 32%, transparent);
}
.md-input input {
  all: unset;
  flex: 1;
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  line-height: 1;
  color: var(--md-on-surface);
}
/* Inset label sits on the top border, always \u2014 UI/Small (Avantt SemiBold 12px) */
.md-input .label {
  position: absolute;
  left: 10px;
  top: 0;
  transform: translateY(-50%);
  background: var(--md-surface);
  padding: 0 4px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-ui-small);
  line-height: 1;
  letter-spacing: 0;
  color: var(--md-on-surface-variant);
  pointer-events: none;
  transition: color var(--motion-base);
}
/* The inset label is .label, not .field-label, so the required marker needs its
   own rule — .field-label .req does not reach it. */
.md-input .label .req { color: var(--error); margin-left: var(--space-sm); }
.md-input.is-focused .label { color: var(--md-primary); }
.md-input.is-error .label { color: var(--error); }
.md-input .trailing {
  color: var(--md-on-surface-variant);
  font-size: var(--icon-md);
  cursor: pointer;
}
.md-input .supporting {
  position: absolute;
  left: 14px;
  bottom: -18px;
  font-family: var(--font-body);
  font-size: var(--fs-ui-small);
  color: var(--md-on-surface-variant);
}
.md-input.is-error .supporting { color: var(--error); }

/* ────────────────────────────────────────────────────────────
   Progress — completion of a known whole. Not Bullet (actual vs
   target with qualitative bands) and not Skeleton (content that
   has not arrived). See adherence.rules.yaml.
   ──────────────────────────────────────────────────────────── */
.progress { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.progress-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-md);
  font-family: var(--font-body); font-size: var(--fs-ui-small); color: var(--graphite-90);
}
.progress-head .pr-value {
  font-family: var(--font-mono); font-size: var(--fs-ui-small);
  color: var(--graphite-bg); font-variant-numeric: tabular-nums;
  margin-left: auto; flex: 0 0 auto;
}
.progress-track {
  position: relative;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--graphite-10);
}
/* The clip belongs to the sweep, not the track. A determinate fill rounds its
   own end via border-radius, so clipping here served only the indeterminate
   animation — and it also contained .progress-target, flattening the reference
   line into a notch inside the fill. */
.progress-track.is-indeterminate { overflow: hidden; }
.progress-fill {
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--slate-100);
  transition: width var(--motion-base) cubic-bezier(0.2, 0, 0, 1);
}
/* Tones mark a delivery state, not decoration — same reservation as Badge. */
.progress-fill.is-success { background: var(--success); }
.progress-fill.is-warning { background: var(--warning); }
.progress-fill.is-error   { background: var(--error); }
/* Sizes. sm for a table cell, lg where progress is the page's subject. */
.progress-sm .progress-track { height: 4px; }
.progress-lg .progress-track { height: 12px; }
/* A target marker — the only case where a progress bar carries a reference.
   Anything more qualitative than one line is a Bullet. */
.progress-target {
  position: absolute; top: -2px; bottom: -2px; width: 2px;
  background: var(--graphite-90);
  border-radius: 1px;
}
/* Indeterminate — activity of unknown duration. A fixed-width sliver sweeps
   the track; the fill width means nothing, so no value is shown. */
.progress-track.is-indeterminate .progress-fill {
  width: 40%;
  transition: none;
  animation: progress-sweep 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes progress-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
@media (prefers-reduced-motion: reduce) {
  .progress-track.is-indeterminate .progress-fill {
    animation: none; width: 100%; opacity: 0.4;
  }
}
/* Flight progress — elapsed time across a flight window, with dark days
   blocked out. Progress is slate (time spent); dark days are graphite (time
   the flight was not delivering). The two encodings must not share a hue,
   because a dark day inside the elapsed portion overlays the fill. */
.progress-flight .progress-track { height: 10px; }
.progress-flight .progress-fill { background: var(--slate-100); }
.flight-dark {
  position: absolute;
  top: 0; bottom: 0;
  /* --graphite-80, not --graphite-bg: bg is a mid grey in both themes and read
     as a soft smudge over the slate fill.
     The hairline is what actually separates a dark day from the elapsed fill it
     paints over — hue alone measures 2.9:1 in dark mode, under the 3:1 non-text
     threshold, so the boundary is carried by shape instead. OUTSET, like the
     today marker's ring: an inset hairline is a fixed 1px on all four sides, so
     on a single-day stretch it consumed half the mark and on the 3px clamp
     nearly three quarters, turning a dark day into a pale tick. */
  background: var(--graphite-80);
  box-shadow: 0 0 0 1px var(--surface-base);
  cursor: default;
  outline: none;
  /* Above the fill, so a dark day inside elapsed time still reads as dark. */
  z-index: 2;
}
/* A single dark day is narrow — give it a floor so it stays hittable. */
.flight-dark { min-width: 3px; }
/* Caps follow position in the flight WINDOW, not position in the list. The
   today marker is also a span, so :last-of-type never matched a dark stretch,
   and :first-of-type capped whichever stretch came first even mid-flight. */
.flight-dark.is-at-start { border-top-left-radius: var(--radius-full); border-bottom-left-radius: var(--radius-full); }
.flight-dark.is-at-end { border-top-right-radius: var(--radius-full); border-bottom-right-radius: var(--radius-full); }
.flight-dark:hover, .flight-dark:focus-visible { background: var(--graphite-90); }
/* Today marker sits above both — and must survive landing ON a dark day, which
   is a normal state (paused today). --graphite-90 against --graphite-80 is
   1.3:1, so the marker carries a --surface-base ring and reads on any ground
   rather than relying on the ramp. */
.progress-flight .progress-target {
  z-index: 3;
  box-shadow: 0 0 0 1px var(--surface-base);
}
/* Flight bounds, under the bar. */
.flight-bounds {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-md);
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-bg);
}
.flight-bounds .fb-mid { color: var(--graphite-90); }
/* Legend for the two encodings — needed the moment dark days are present. */
.flight-legend {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-lg);
  font-family: var(--font-body); font-size: var(--fs-ui-small); color: var(--graphite-bg);
}
.flight-legend span { display: inline-flex; align-items: center; gap: 6px; }
.flight-legend i { width: 14px; height: 8px; border-radius: 2px; flex: 0 0 auto; }
.flight-legend i.is-elapsed { background: var(--slate-100); }
.flight-legend i.is-dark { background: var(--graphite-80); }

/* In a table cell the bar sits inline with its figure. The cell carries the
   full reading on [data-tip] — the column only has room for the percentage. */
.progress-cell {
  display: inline-flex; align-items: center; gap: var(--space-md);
  min-width: 120px; cursor: default; outline: none;
}
.progress-cell:focus-visible { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.progress-cell .progress { width: 72px; }
.progress-cell .pr-cell-value {
  font-family: var(--font-mono); font-size: var(--fs-ui-small);
  color: var(--graphite-bg); font-variant-numeric: tabular-nums;
}
/* The tip must clear the row above without widening the column. */
.progress-cell[data-tip]::after { white-space: nowrap; }

/* ────────────────────────────────────────────────────────────
   Activity panel — a reverse-chronological stream of what users and
   agents did, each row expandable into the record it wrote.
   Day dividers chunk the stream; the detail table shows either the
   values written (create) or a before/after diff (update).
   ──────────────────────────────────────────────────────────── */
.activity { display: flex; flex-direction: column; gap: 0; --act-line-x: calc(var(--space-lg) + 32px); }
/* Day divider — a chip, not a full-width rule: the stream is the content,
   and a rule across it reads as a section break rather than a date change. */
.act-day {
  align-self: flex-start;
  margin-left: calc(var(--act-line-x) - 28px);
  display: inline-flex; align-items: center;
  padding: 4px 12px; margin-top: var(--space-lg); margin-bottom: var(--space-sm);
  background: var(--surface-base);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-full);
  font-family: var(--font-body); font-size: var(--fs-ui-small); font-weight: 600;
  color: var(--graphite-90);
}
.act-day:first-child { margin-top: 0; }
/* The connector between two consecutive events in the same day group — its
   height stands in for the old flex gap, and its bar continues the row-level
   line through the space between cards, so the thread reads unbroken. */
.act-link { display: block; height: var(--space-md); margin-left: calc(var(--act-line-x) - 1px); width: 2px; background: var(--surface-stroke); }
.act-event:has(+ .act-day) { margin-bottom: var(--space-md); }
.act-event {
  position: relative;
  background: var(--surface-base);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.act-head {
  display: grid;
  grid-template-columns: 64px 36px 1fr auto;
  align-items: start;
  gap: var(--space-lg);
  padding: var(--space-lg);
}
.act-time {
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  font-variant-numeric: tabular-nums;
  color: var(--graphite-bg);
  text-align: right; padding-top: 8px;
}
/* Avatar carries the ENTITY glyph; the badge carries the ACTION. Two facts in
   one mark, so the row's text does not have to repeat either. */
.act-avatar {
  position: relative;
  width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-md);
  background: var(--slate-bg);
  color: var(--slate-base);
  flex: 0 0 auto;
}
.act-avatar .material-symbols-outlined { font-size: var(--icon-lg); }
.act-badge {
  position: absolute; right: -3px; bottom: -3px;
  width: 17px; height: 17px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  /* Outset ring against the row, so the badge stays legible on any avatar. */
  box-shadow: 0 0 0 2px var(--surface-base);
}
.act-badge .material-symbols-outlined { font-size: var(--icon-2xs); }
/* Tonal, the same --bg / --fg pairing Badge and Alert use: at 17px a saturated
   fill is a bright dot that pulls the eye off the sentence, which is what the
   row is actually for. The pair also inverts correctly per theme on its own.
   Sync uses switch cyan rather than --info — the accent already means "the
   system did this" — and pairs with --switch-contrast, since there is no
   --switch-fg. */
.act-badge.is-create { background: var(--success-bg); color: var(--success-fg); }
.act-badge.is-update { background: var(--warning-bg); color: var(--warning-fg); }
.act-badge.is-sync { background: var(--switch-bg); color: var(--switch-contrast); }
.act-badge.is-delete { background: var(--error-bg); color: var(--error-fg); }
.act-badge.is-neutral { background: var(--graphite-10); color: var(--graphite-90); }
.act-body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.act-line {
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  line-height: 1.45; color: var(--graphite-bg);
  text-wrap: pretty;
}
/* The actor and the object are the two things a reader scans for, so they take
   full strength; the verb and the entity type stay secondary. */
.act-line .act-actor { font-weight: 600; color: var(--graphite-90); }
.act-line .act-object { font-weight: 600; color: var(--graphite-90); }
.act-line a.act-object { color: var(--link); text-decoration: none; }
.act-line a.act-object:hover { color: var(--link-hover); text-decoration: underline; }
.act-line .id-chip { margin-right: var(--space-sm); vertical-align: baseline; }
.act-meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-bg);
}
.act-meta .material-symbols-outlined { font-size: var(--icon-xs); }
.act-meta .am-sep { color: var(--graphite-30); }
.act-toggle {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  color: var(--graphite-bg);
}
.act-toggle:hover { background: var(--surface-muted); color: var(--graphite-90); }
.act-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.act-toggle .material-symbols-outlined { font-size: var(--icon-lg); }

/* Detail table. Not a .table: these are the fields of one record, so there is
   no sorting, no selection, and no pagination to inherit. */
.act-detail { width: 100%; border-collapse: collapse; border-top: 1px solid var(--surface-stroke); }
.act-detail th {
  text-align: left; padding: 10px var(--space-lg);
  font-family: var(--font-body); font-size: var(--fs-ui-small); font-weight: 600;
  color: var(--graphite-90);
  border-bottom: 1px solid var(--surface-stroke);
}
.act-detail td {
  padding: 9px var(--space-lg);
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  color: var(--graphite-90);
  border-bottom: 1px solid var(--graphite-10);
  vertical-align: top;
}
.act-detail tr:last-child td { border-bottom: 0; }
/* Nested fields tint, so depth reads without a tree line. */
.act-detail tr.is-nested td { background: var(--surface-muted); }
.act-detail .ad-field { color: var(--graphite-90); }
/* A field that was never set is absent data, not an empty string — italic and
   muted so it cannot be misread as the literal text "Not Set". */
.act-detail .ad-unset { font-style: italic; color: var(--graphite-bg); }
/* Inactive object name — muted and italic, so a disabled record reads
   differently from an active one at a glance without needing a badge or
   column. Applies to the name wherever it appears: table cells, object
   cards, breadcrumbs, headers. */
.is-inactive-name { font-style: italic; color: var(--graphite-bg); }
.act-detail .ad-before { color: var(--graphite-bg); }
.act-detail .ad-group td { font-weight: 600; }

/* Filter rail — the stream's own filters, stacked in a card beside it. Grouped
   checkbox lists rather than a bar: an activity feed filters on several axes at
   once and the page is tall enough to carry them vertically. */
.filter-rail {
  display: flex; flex-direction: column; gap: var(--space-lg);
  padding: var(--space-xl);
  background: var(--surface-base);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-md);
}
/* Clear sits in the rail's header, beside the title — it acts on the whole rail,
   so it belongs to the rail rather than to the toolbar over the content. */
.filter-rail .fr-head { display: flex; align-items: center; gap: var(--space-md); }
.filter-rail .fr-head .fr-title { flex: 1 1 auto; }
.filter-rail .fr-title {
  font-family: var(--font-display); font-size: var(--fs-ui-default); font-weight: 600;
  color: var(--graphite-90); margin: 0;
}
.filter-rail .fr-group { display: flex; flex-direction: column; gap: 2px; }
.filter-rail .fr-legend {
  font-family: var(--font-body); font-size: var(--fs-ui-small); font-weight: 600;
  color: var(--graphite-90); margin: 0 0 var(--space-sm);
}
.filter-rail .fr-row {
  display: flex; align-items: center; gap: var(--space-md);
  padding: 5px 0;
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  color: var(--graphite-90);
}
.filter-rail .fr-row .material-symbols-outlined { font-size: var(--icon-md); color: var(--graphite-bg); flex: 0 0 auto; }
.filter-rail .fr-row .fr-label { flex: 1 1 auto; min-width: 0; }
.filter-rail .fr-row .checkbox { flex: 0 0 auto; }
/* "Only" narrows the facet to this one value — the common case in a list of
   seven, where the alternative is unchecking six. Hidden until the row is
   hovered or the chip itself takes focus: a column of always-on chips competes
   with the labels, which are what the user is reading. */
.filter-rail .fr-only {
  flex: 0 0 auto;
  opacity: 0;
  appearance: none; cursor: pointer;
  padding: 1px 8px;
  border: 1px solid var(--graphite-20);
  border-radius: var(--radius-full);
  background: var(--surface-base);
  font-family: var(--font-body); font-size: var(--fs-ui-xs); font-weight: 600;
  color: var(--graphite-bg);
  transition: opacity var(--motion-fast);
}
.filter-rail .fr-row:hover .fr-only,
.filter-rail .fr-only:focus-visible { opacity: 1; }
.filter-rail .fr-only:hover { border-color: var(--graphite-30); color: var(--graphite-90); }
.filter-rail .fr-only:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* A range trigger with an inset label, matching the fields beside it — the
   notch sits on the trigger's own top border, so no wrapper border is added. */
.range-field { position: relative; display: flex; }
.range-field .rt-inset {
  position: absolute; top: 0; left: 10px;
  transform: translateY(-50%);
  z-index: 2; pointer-events: none;
  padding: 0 var(--space-sm);
  background: var(--surface-input);
  font-family: var(--font-display); font-size: var(--fs-ui-small); font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--graphite-bg);
}

/* ────────────────────────────────────────────────────────────
   Agent workspace — a two-panel shell: the conversation on the
   left, the artifacts the agent produced on the right. The split
   is the whole point: the transcript stays readable while the
   work it describes stays open beside it, so neither is a modal
   over the other.
   ──────────────────────────────────────────────────────────── */
.agent-shell {
  display: grid; grid-template-columns: minmax(360px, 1fr) minmax(0, 1.35fr);
  height: 100%; min-height: 0;
  background: var(--surface-canvas);
}
.agent-shell > .as-chat { border-right: 1px solid var(--surface-stroke); }
.as-chat, .as-panel { display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--surface-base); }
/* Alpha / advisory banner above both panes. */
.as-banner {
  grid-column: 1 / -1;
  padding: 10px var(--space-xl);
  background: var(--warning-bg); color: var(--warning-fg);
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  text-align: center;
}
/* Fixed height rather than padding alone: the tab buttons' own vertical padding
   makes .as-panel-head taller than .as-chat-head at equal outer padding, so the
   two headers need an explicit shared height to actually match. */
.as-chat-head, .as-panel-head {
  display: flex; align-items: center; gap: var(--space-md);
  height: 57px;
  padding: 0 var(--space-xl);
  border-bottom: 1px solid var(--surface-stroke);
  flex: 0 0 auto;
}
.as-wordmark {
  display: inline-flex; align-items: baseline; gap: var(--space-sm);
  font-family: var(--font-display); font-size: 15px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--graphite-90);
}
.as-wordmark .as-alpha {
  font-size: var(--fs-ui-tag); letter-spacing: 0.08em;
  color: var(--slate-100);
}
/* Transcript. Scrolls independently of the composer pinned below it. */
.as-stream {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--space-xl);
  padding: var(--space-xl);
}
/* A user turn is a bubble; an agent turn is not. The asymmetry is what makes
   the transcript scannable — two bubble styles read as two speakers of equal
   weight, when the agent's output is the content and the prompt is the aside. */
.as-turn { display: flex; flex-direction: column; gap: var(--space-sm); }
.as-turn.is-user { align-items: flex-end; }
.as-who { font-family: var(--font-body); font-size: var(--fs-ui-small); color: var(--graphite-bg); }
.as-bubble {
  max-width: 82%;
  padding: 12px var(--space-xl);
  border-radius: var(--radius-lg);
  /* --slate-120, not --slate-100: the ramp steps are theme-fixed while --white
     inverts to #E0E0E0 in dark, so a --slate-100 fill gets WORSE in dark rather
     than better — 4.25:1 light / 3.22:1 dark, under AA at 14px/400. --slate-120
     is the shallowest step that clears both: 6.32:1 light / 4.79:1 dark.
     --slate-110 is not enough; dark lands at 3.91:1. */
  background: var(--slate-120); color: var(--white);
  font-family: var(--font-body); font-size: var(--fs-ui-default); line-height: 1.5;
  text-wrap: pretty;
}
.as-time { font-family: var(--font-body); font-size: var(--fs-ui-xs); color: var(--graphite-bg); }
/* "Thought" label above a reasoning step — quiet, because it is provenance. */
.as-thought-label {
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-bg);
}
/* A resolved reasoning step: what the agent asked itself and what it settled on. */
.as-step {
  display: flex; align-items: flex-start; gap: var(--space-md);
  padding: 14px var(--space-lg);
  background: var(--success-bg);
  border: 1px solid color-mix(in srgb, var(--success) 24%, transparent);
  border-radius: var(--radius-md);
}
.as-step .as-step-icon { flex: 0 0 auto; color: var(--success); }
.as-step .as-step-icon .material-symbols-outlined {
  font-size: var(--icon-md); font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 20;
}
.as-step-body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.as-step-q { font-family: var(--font-body); font-size: var(--fs-ui-default); color: var(--graphite-90); }
.as-step-a { font-family: var(--font-body); font-size: var(--fs-ui-default); font-weight: 600; color: var(--graphite-90); }
/* A completed action, one line, no container — a box per action would turn the
   transcript into a stack of cards. */
.as-done {
  display: flex; align-items: center; gap: var(--space-md);
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-70);
}
.as-done .material-symbols-outlined { font-size: var(--icon-sm); color: var(--success); }
.as-prose {
  font-family: var(--font-body); font-size: var(--fs-ui-default); line-height: 1.65;
  color: var(--graphite-90); margin: 0; text-wrap: pretty; max-width: 62ch;
}
.as-feedback { display: flex; align-items: center; gap: var(--space-sm); }
.as-feedback button {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  width: 24px; height: 24px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  color: var(--graphite-bg);
}
.as-feedback button:hover { background: var(--surface-muted); color: var(--graphite-90); }
.as-feedback button:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.as-feedback .material-symbols-outlined { font-size: var(--icon-md); }
/* Working state. Italic and unadorned: a spinner would imply a determinate
   wait, and the agent does not know how long it will take. */
.as-working {
  display: flex; align-items: center; gap: var(--space-md);
  font-family: var(--font-body); font-size: var(--fs-ui-default); font-style: italic;
  color: var(--graphite-bg);
}
/* The brand sticker stands in for the agent while it works — it is the agent's
   own mark, and a generic glyph beside its own transcript reads as a system
   status rather than the agent speaking. */
/* The sticker IS the <img> — .chat-sticker sits on it, not on a wrapper.
   No size override here: reaction is 48px per ChatSticker's own contract, and
   this row is that context, not a reason to shrink it. */
.as-working img.chat-sticker { flex: 0 0 auto; }
/* Composer, pinned below the stream. */
.as-composer { flex: 0 0 auto; padding: var(--space-lg) var(--space-xl); }
.as-composer-row {
  display: flex; align-items: center; gap: var(--space-md);
  padding: 0 var(--space-lg);
  min-height: 48px;
  background: var(--surface-input);
  border: 1px solid var(--graphite-20);
  border-radius: var(--radius-md);
}
.as-composer-row:focus-within { border-color: var(--switch-contrast); box-shadow: var(--focus-ring); }
.as-composer-row input {
  flex: 1 1 auto; min-width: 0;
  border: 0; outline: none; background: transparent;
  font-family: var(--font-body); font-size: var(--fs-ui-default); color: var(--graphite-90);
}
.as-composer-row:focus-within { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.as-composer-row button {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  color: var(--graphite-bg);
}
.as-composer-row button:hover { color: var(--graphite-90); background: var(--surface-muted); }
.as-composer-row button:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.as-chat-foot {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: var(--space-lg);
  padding: 0 var(--space-xl) var(--space-lg);
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-bg);
}
.as-chat-foot .as-context { margin-left: auto; display: inline-flex; align-items: center; gap: var(--space-md); }
/* Session controls. Slate, so they read as the actions they are rather than as
   footer chrome, with a glyph each — "New" and "History" are the two things a
   user looks for here and a word alone makes them scan. */
.as-chat-foot .as-session {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--link); text-decoration: none;
  font-family: var(--font-body); font-size: var(--fs-ui-small); font-weight: 500;
}
.as-chat-foot .as-session:hover { color: var(--link-hover); text-decoration: underline; }
.as-chat-foot .as-session:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.as-chat-foot .as-session .material-symbols-outlined { font-size: var(--icon-sm); }

/* An unanswered question. Neutral rather than the resolved step's success tint:
   it is a prompt awaiting the user, not something the agent settled. */
.as-ask {
  display: flex; flex-direction: column; gap: var(--space-lg);
  padding: var(--space-xl);
  background: var(--surface-muted);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-md);
}
.as-ask-q {
  font-family: var(--font-body); font-size: var(--fs-ui-default); font-weight: 600;
  color: var(--graphite-90); margin: 0;
}
.as-choices { display: flex; flex-direction: column; gap: 2px; }
/* A choice row, not a radio: this system has no radio control, and five options
   carrying descriptions cannot be a button group. The whole row is the target,
   with a filled dot marking the selection. */
.as-choice {
  appearance: none; border: 0; cursor: pointer; text-align: left;
  display: flex; align-items: flex-start; gap: var(--space-lg);
  padding: 9px var(--space-md);
  border-radius: var(--radius-sm);
  background: transparent;
}
.as-choice:hover { background: var(--surface-base); }
.as-choice:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.as-choice .ac-dot {
  flex: 0 0 auto; margin-top: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  border: 1.5px solid var(--graphite-30);
  display: flex; align-items: center; justify-content: center;
}
.as-choice[aria-checked="true"] .ac-dot { border-color: var(--switch-contrast); }
.as-choice[aria-checked="true"] .ac-dot::after {
  content: ''; width: 8px; height: 8px; border-radius: 50%;
  background: var(--switch-contrast);
}
.as-choice .ac-body { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.as-choice .ac-label {
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  color: var(--graphite-90);
}
.as-choice[aria-checked="true"] .ac-label { font-weight: 600; }
.as-choice .ac-hint {
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-70);
}
.as-ask-foot { display: flex; align-items: center; justify-content: flex-end; }

/* Workspace tabs. A dot marks a pane holding unseen output — the count in
   parentheses is for panes that hold a countable set. */
.as-tabs { display: flex; align-items: stretch; gap: 2px; flex: 1 1 auto; height: 100%; }
.as-tab {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  display: inline-flex; align-items: center; gap: var(--space-md);
  padding: 0 14px;
  border-bottom: 2px solid transparent;
  font-family: var(--font-body); font-size: var(--fs-ui-small); font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--graphite-bg);
}
.as-tab:hover { color: var(--graphite-90); }
.as-tab:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.as-tab.is-active { color: var(--graphite-90); border-bottom-color: var(--switch-contrast); }
.as-tab .as-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--switch-contrast); }
/* Canvas and Focus sit on --surface-canvas, distinct from the chat and Sandbox
   surfaces — a workbench backdrop under the cards/table it holds, rather than
   the same plate as the content itself. */
.as-panel-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: var(--space-xl); background: var(--surface-canvas); }
.as-panel-body.is-sandbox { background: var(--surface-base); }
.as-panel-title {
  display: flex; align-items: center; gap: var(--space-md);
  margin: 0 0 var(--space-xl);
  font-family: var(--font-display); font-size: var(--fs-h4); font-weight: 600;
  color: var(--graphite-90);
}
/* Inline-editable object name in a panel header — looks like plain text until
   focused, when the ghost border and hover fill signal it's a field. Matches
   .as-panel-title's type directly rather than inheriting, since it sits at the
   same spot in the header row but isn't wrapped by that class. */
.as-title-input {
  all: unset; flex: 1; min-width: 0;
  font-family: var(--font-display); font-size: var(--fs-h4); font-weight: 600; color: var(--graphite-90);
  padding: 2px 6px; margin: -2px -6px; border-radius: var(--radius-sm);
  border: 1px solid transparent;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.as-title-input:hover { background: var(--graphite-10); }
.as-title-input:focus { background: var(--surface-base); border-color: var(--md-outline, var(--graphite-20)); box-shadow: var(--focus-ring); }
.as-panel-controls {
  display: flex; align-items: center; gap: var(--space-md);
  margin: 0 0 var(--space-lg);
}

/* ────────────────────────────────────────────────────────────
   Object card — a lifecycle object scanned as a card rather than a
   table row. Use when each object carries a status, a few named
   attributes, and a count or two: a table row flattens those into
   columns that only make sense read together.
   ──────────────────────────────────────────────────────────── */
.object-stack { display: flex; flex-direction: column; gap: var(--space-md); }
/* The whole card opens the detail page — not just the name — so it needs a
   pointer cursor and a hover/focus treatment of its own. Interactive children
   (the kebab, any custom action) stop propagation so they do not also navigate. */
.object-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-xl);
  padding: 14px var(--space-xl);
  background: var(--surface-base);
  border: 1px solid var(--surface-stroke);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.object-card:hover { border-color: var(--graphite-20); background: var(--graphite-10); }
.object-card:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* Badge + optional rate chip, as ONE grid child. Grid auto-placement gives
   every direct child of .object-card its own track — with the badge and rate
   as separate children, the rate consumed the body's flexible 1fr column and
   pushed the name into the stats column, overlapping it. */
.oc-lead { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
/* isPlatform stacks the rate under the chip rather than beside it — the short
   horizontal chip is already as wide as the platform mark needs, so a second
   chip beside it reads as two separate facts instead of one badge. */
.oc-lead.is-stacked { flex-direction: column; align-items: center; gap: 4px; }

/* Row action menu — an icon button, not a filled control: no background at
   rest, round on hover/focus, icon in --graphite-bg. A kebab is a secondary
   affordance next to the primary Switch, so it should recede until touched. */
.oc-kebab {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: var(--radius-full);
  color: var(--graphite-bg);
  transition: background var(--motion-fast), color var(--motion-fast);
}
.oc-kebab .material-symbols-outlined { font-size: var(--icon-md); font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 20; }
.oc-kebab:hover { background: var(--graphite-10); color: var(--graphite-90); }
.oc-kebab:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* The ID badge carries two facts: the identifier, and the lifecycle state as
   its hue plus a corner glyph. Tonal --bg/--fg pairs, the same as Badge and the
   activity panel — a saturated plate at this size is a colour block that pulls
   the eye off the name. */
/* Icon on top, ID below — one stacked badge, not an icon with a corner overlay.
   The icon names whatever property tells this object apart from its neighbours
   in THIS list: a lifecycle state in one list, a type in another. tone carries
   the colour family; it is not lifecycle-specific. */
/* Square, icon over ID — the default: the icon carries the object's type or
   lifecycle stage, so it needs room to read as a symbol rather than a small
   mark beside text. */
.oc-badge {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  width: 52px; height: 52px; flex: 0 0 auto;
  border-radius: var(--radius-md);
  /* Slate by default — the badge identifies the object, and slate is this
     system's identifying/informational tint (see the ID chip and row-avatar).
     isPlatform stays graphite: a partner mark is neutral by definition, not
     part of Swivel's own visual vocabulary. */
  background: var(--slate-bg);
  cursor: default;
}
.oc-badge .material-symbols-outlined {
  font-size: var(--icon-md); color: var(--slate-100);
  font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 24;
}
.oc-badge .oc-id {
  font-family: var(--font-mono); font-size: var(--fs-ui-code); font-weight: 500;
  color: var(--slate-100);
}
/* Horizontal — ONLY when the leading icon is a platform mark (the ID is a
   third-party system's own identifier, e.g. a SpringServe ID). A platform
   glyph is a small logo-like mark, not a type/lifecycle symbol, so it reads
   fine beside the ID rather than needing the square treatment. */
.oc-badge.is-platform {
  flex-direction: row; gap: 6px;
  width: auto; height: 26px; padding: 0 10px;
  border-radius: var(--radius-sm);
  background: var(--graphite-10);
}
.oc-badge.is-platform .material-symbols-outlined {
  font-size: var(--icon-sm); color: var(--graphite-70);
  font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 20;
}
.oc-badge.is-platform .oc-id { font-size: var(--fs-ui-xs); color: var(--graphite-70); }
/* Lifecycle — tone follows the object's actual stage, the same tones the
   status/state cell uses (Table.cell_kinds "state"). Overrides the slate
   default: a lifecycle stage is a real differentiator, not a neutral
   identifier, so it earns its own colour. */
.oc-badge.is-lifecycle.is-success { background: var(--success-bg); }
.oc-badge.is-lifecycle.is-success .material-symbols-outlined,
.oc-badge.is-lifecycle.is-success .oc-id { color: var(--success-fg); }
.oc-badge.is-lifecycle.is-warning { background: var(--warning-bg); }
.oc-badge.is-lifecycle.is-warning .material-symbols-outlined,
.oc-badge.is-lifecycle.is-warning .oc-id { color: var(--warning-fg); }
.oc-badge.is-lifecycle.is-error { background: var(--error-bg); }
.oc-badge.is-lifecycle.is-error .material-symbols-outlined,
.oc-badge.is-lifecycle.is-error .oc-id { color: var(--error-fg); }
.oc-badge.is-lifecycle.is-neutral { background: var(--graphite-10); }
.oc-badge.is-lifecycle.is-neutral .material-symbols-outlined,
.oc-badge.is-lifecycle.is-neutral .oc-id { color: var(--graphite-70); }
/* The real partner mark, not a generic Material glyph — same source PlatformIcon
   uses (assets/partners/<platform>.png), sized to sit inline in the chip. */
.oc-badge .oc-platform-mark { height: 14px; width: auto; max-width: 20px; display: block; flex: 0 0 auto; object-fit: contain; }
.oc-badge.is-copied { background: var(--success-bg); }
.oc-badge.is-copied .material-symbols-outlined, .oc-badge.is-copied .oc-id { color: var(--success-fg); }
.oc-badge.is-manual { background: var(--warning-bg); }
.oc-badge.is-manual .material-symbols-outlined, .oc-badge.is-manual .oc-id { color: var(--warning-fg); }
.oc-badge::selection, .oc-badge *::selection { background: var(--slate-bg); }
.oc-badge:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* A rate or platform detail beside the ID chip — its own small chip, not a
   second line stacked inside the badge, so the badge stays a single-row
   identifier and the rate reads as a distinct fact. */
.oc-rate-chip {
  display: inline-flex; align-items: center;
  height: 26px; padding: 0 10px;
  border-radius: var(--radius-full);
  background: var(--graphite-10);
  font-family: var(--font-body); font-size: var(--fs-ui-xs); font-weight: 500;
  color: var(--graphite-70);
}
.oc-body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.oc-name {
  font-family: var(--font-body); font-size: var(--fs-ui-default); font-weight: 600;
  color: var(--graphite-90);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The name no longer carries its own link — the whole card does — so it reads
   in the link colour without being its own separate target. */
.oc-name.is-link { color: var(--link); }
.object-card:hover .oc-name.is-link { color: var(--link-hover); }
.oc-meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-family: var(--font-body); font-size: var(--fs-ui-small);
  color: var(--graphite-70);
}
.oc-meta .om-sep { color: var(--graphite-20); }
/* Attribute readouts on the right. Labelled, because a bare number on a card
   has no column header to explain it. */
.oc-stats { display: flex; align-items: center; gap: var(--space-xl); }
.oc-stat { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
.oc-stat .os-label {
  font-family: var(--font-body); font-size: var(--fs-ui-section); font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--graphite-bg);
}
.oc-stat .os-value {
  font-family: var(--font-body); font-size: var(--fs-ui-dense); font-weight: 400;
  color: var(--graphite-90); font-variant-numeric: tabular-nums;
}
.oc-actions { display: flex; align-items: center; gap: var(--space-sm); }
/* Kebab and any custom control (a Switch) live here and must stop the click
   from reaching the card, or using them would also open the detail page. */
.oc-actions button, .oc-actions .switch { position: relative; z-index: 1; }
/* State label beside a switch. A class rather than an inline colour, so the
   token paints from the stylesheet instead of waiting on the logic class. */
.oc-state-label {
  min-width: 62px; text-align: right;
  font-family: var(--font-body); font-size: var(--fs-ui-small); font-weight: 600;
  color: var(--graphite-bg);
}
.oc-state-label.is-on { color: var(--success); }
.oc-state-label.is-dry { color: var(--warning-text); }

/* Lifecycle — a fixed set of named stages as equal segments, filled up to the
   one reached. Not a ProgressBar: the stages are discrete and countable, so a
   continuous rail would invite reading a percentage that does not exist. The
   tooltip names the stage, so the marks need no legend. */
.lifecycle { display: inline-flex; align-items: center; gap: 2px; }
.lifecycle .lc-bar {
  width: 10px; height: 6px;
  border-radius: 1px;
  background: var(--graphite-10);
}
.lifecycle .lc-bar.is-done { background: var(--slate-100); }
.table .cell-lifecycle { width: 1%; white-space: nowrap; }
.table.is-compact .lifecycle .lc-bar { width: 8px; height: 5px; }

/* Range trigger — a filter-bar button that opens a picker popover and shows the
   chosen span as its label. One control, one popover: two single pickers would
   hide the relationship between the ends. Matches --control-h so it lines up
   with the fields beside it. */
.range-trigger {
  display: inline-flex; align-items: center; gap: var(--space-md);
  width: 100%; height: var(--control-h);
  padding: 0 10px;
  appearance: none; cursor: pointer;
  background: var(--surface-input);
  border: 1px solid var(--graphite-20);
  border-radius: var(--radius-sm);
  font-family: var(--font-body); font-size: var(--fs-ui-default);
  color: var(--graphite-bg);
  text-align: left;
}
.range-trigger:hover { border-color: var(--graphite-30); }
.range-trigger:focus-visible { outline: none; border-color: var(--switch-contrast); box-shadow: var(--focus-ring); }
/* Trailing calendar, matching DateField's .trailing slot — a date control's
   affordance sits on the right in this system. Ordered rather than reordered in
   markup, so every existing consumer picks it up. The glyph is date_range
   rather than calendar_today because this control holds two dates. */
.range-trigger .material-symbols-outlined { order: 2; flex: 0 0 auto; font-size: var(--icon-md); color: var(--graphite-bg); }
.range-trigger .rt-label { order: 1; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A set range is real data, so it reads at full strength. */
.range-trigger.is-set { color: var(--graphite-90); border-color: var(--graphite-30); }
.range-trigger.is-set .material-symbols-outlined { color: var(--slate-100); }

/* Switch */
.switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-md);
  font-family: var(--font-body);
  font-size: var(--fs-ui-small);
  color: var(--graphite-90);
}
.switch-track {
  /* Element-agnostic: the track may be a div or a span, so give it a box. */
  display: block;
  flex: 0 0 auto;
  width: 32px;
  height: 18px;
  border-radius: var(--radius-full);
  background: var(--graphite-30);
  position: relative;
  transition: background var(--motion-base);
}
.switch-track::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--white);
  transition: left var(--motion-base);
  box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
/* On, default tone: --slate-bg, not --graphite-90 — graphite-90 inverts to
   near-white in dark mode (#F2F3F2), which swallows the white knob. slate-bg
   inverts to a real mid-tone in both themes, so the knob stays visible. */
.switch.is-on .switch-track { background: var(--slate-bg); }
.switch.is-on .switch-track::after { left: 16px; }
.switch.is-on.accent .switch-track { background: var(--switch-100); }

/* ─── Theme switch ───────────────────────────────────────────
   The canonical light/dark control: sun · track · moon, no text label.
   The icon on the active side tints slate; the other stays muted. Drive it
   from aria-checked (true = dark) so the control is its own state.
   Add .is-fixed to pin it top-right on a specimen or showcase page. */
.theme-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-lg);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  line-height: 1;
}
.theme-switch .ts-ico {
  font-size: var(--icon-md);
  line-height: 1;
  color: var(--graphite-40);
  transition: color var(--motion-base);
}
.theme-switch[aria-checked="false"] .ts-sun,
.theme-switch[aria-checked="true"] .ts-moon { color: var(--ts-active-fg); }
.ts-track {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: 40px;
  height: 18px;
  border-radius: var(--radius-full);
  background: var(--ts-track-on);
  transition: background var(--motion-base);
}
.ts-track::after {
  content: "";
  position: absolute;
  top: -3px;
  left: -2px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--white);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.28);
  transition: left var(--motion-base);
}
.theme-switch[aria-checked="true"] .ts-track::after { left: 18px; }
.theme-switch:focus-visible { outline: none; }
.theme-switch:focus-visible .ts-track { box-shadow: var(--focus-ring); }
.theme-switch.is-fixed {
  position: fixed;
  top: 24px;
  right: 24px;
  z-index: 50;
  padding: 6px 14px;
  background: var(--surface-base);
  border: 1px solid var(--graphite-20);
  border-radius: var(--radius-full);
  box-shadow: var(--elev-1);
}

/* Alert */
.alert {
  display: flex;
  gap: var(--space-lg);
  padding: 14px var(--space-xl);
  border-radius: var(--radius-md);
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.71;
  border: 1px solid transparent;
  --alert-color: var(--info);
  --alert-bg: var(--info-bg);
}
.alert .alert-body { line-height: 1.71; flex: 1; color: var(--graphite-90); }
.alert .alert-title {
  font-family: var(--font-display);
  font-size: var(--fs-ui-title);
  font-weight: 600;
  line-height: 0.95;
  margin-bottom: var(--space-sm);
  display: block;
  color: var(--alert-text, var(--alert-color));
}
.alert .alert-icon {
  flex: 0 0 auto;
  font-size: var(--icon-xl);
  line-height: 1;
  margin-top: 1px;
  font-variation-settings: 'FILL' 1;
  color: var(--alert-color);
}
.alert .alert-dismiss {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  border-radius: var(--radius-sm);
  color: var(--graphite-bg);
  opacity: 0.8;
  cursor: pointer;
  font-size: var(--icon-md);
  line-height: 1;
  margin-top: -2px;
  margin-right: -4px;
}
.alert .alert-dismiss:hover { opacity: 1; background: rgba(0,0,0,0.06); }

/* Severity variables */
.alert-info     { --alert-color: var(--info);    --alert-bg: var(--info-bg); }
.alert-success  { --alert-color: var(--success); --alert-bg: var(--success-bg); }
.alert-warning  { --alert-color: var(--warning); --alert-text: var(--warning-text); --alert-bg: var(--warning-bg); }
.alert-error    { --alert-color: var(--error);   --alert-bg: var(--error-bg); }

/* Filled variant (default) */
.alert-filled    { background: var(--alert-bg); border-color: transparent; }
.alert,
.alert-filled    { background: var(--alert-bg); }

/* Outlined variant */
.alert-outlined  { background: transparent; border-color: var(--alert-color); }

/* Table */
.table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-body);
  font-size: var(--fs-ui-small);
}
.table th, .table td {
  padding: var(--space-lg) var(--space-xl);
  text-align: left;
  border-bottom: 1px solid var(--graphite-10);
}
.table th {
  font-weight: 600;
  font-size: var(--fs-ui-section);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--graphite-bg);
  background: var(--graphite-10);
}
.table tbody tr:hover { background: var(--graphite-10); }
.table tbody td a { color: var(--link); text-decoration: none; }
.table tbody td a:hover { color: var(--link-hover); text-decoration: underline; }
.table .cell-id { font-family: var(--font-mono); font-size: var(--fs-ui-xs); color: var(--slate-100); }
.table .cell-num { font-family: var(--font-mono); text-align: right; }
/* Row actions: hugs its content at the end of the row. The icon button appears
   on row hover or keyboard focus — a column of always-on glyphs competes with
   the data for attention, and the action is per-row, not a page-level control. */
.table .cell-actions { width: 1%; white-space: nowrap; text-align: center; }
.table tbody td.cell-actions .tt-btn { opacity: 0; transition: opacity var(--motion-fast); }
.table tbody tr:hover td.cell-actions .tt-btn,
.table tbody td.cell-actions .tt-btn:focus-visible { opacity: 1; }

/* Table — variants (elevation / outline) + empty state */
.table-card {
  background: transparent;
  border-radius: var(--radius-md);
  /* Visible so toolbar tooltips can escape the card's top edge; the scrolling
     body clips itself instead. */
  overflow: visible;
}
.table-card-elevation {
  border: 1px solid var(--graphite-10);
  box-shadow: var(--elev-2);
}
.table-card-outline {
  border: 1px solid var(--graphite-20);
  box-shadow: none;
}
.table-card .table { font-family: var(--font-body); font-size: var(--fs-ui-default); }
.table-card .table thead th {
  background: var(--graphite-10);
  font-family: var(--font-body);
  font-size: var(--fs-ui-caps);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--graphite-bg);
  padding: var(--space-lg) 14px;
  border-bottom: 1px solid var(--graphite-10);
  white-space: nowrap;
}
.table-card .table tbody td {
  padding: var(--space-md) 14px;
  border-bottom: 1px solid var(--graphite-10);
  vertical-align: middle;
  white-space: nowrap;
  color: var(--graphite-90);
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.71;
}
.table-card .table tbody tr:last-child td { border-bottom: 0; }
.table-card .table tbody tr:hover { background: var(--graphite-10); }

.table-card .cell-id,
.table-card .cell-metric { font-family: var(--font-body); font-size: var(--fs-ui-default); font-weight: 400; line-height: 1.71; color: var(--graphite-90); }
.table-card .cell-id { padding-right: var(--space-sm); width: 1%; white-space: nowrap; }
.id-chip,
.table-card .id-chip {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-sm);
  /* IDs are hyphenated (BYR-4471) and the pill cannot grow — never let the
     hyphen become a break opportunity, and never let flex squeeze it. */
  white-space: nowrap; flex: 0 0 auto;
  height: 22px; padding: 0 var(--space-md);
  border-radius: var(--radius-full);
  background: var(--graphite-10);
  color: var(--graphite-90);
  font-family: var(--font-mono);
  font-size: var(--fs-ui-code, 10px);
  font-weight: 500;
  line-height: 23px;
  font-feature-settings: "ss01", "cv11";
  letter-spacing: 0.02em;
}
.id-chip .id-chip-icon { width: 12px; height: 12px; border-radius: 2px; flex: 0 0 auto; object-fit: contain; }
/* Copyable ID chip — a real button. Used in the breadcrumb on object pages and
   anywhere an ID is the thing an operator needs on their clipboard. */
button.id-chip {
  appearance: none; border: 1px solid transparent; cursor: pointer;
  transition: background var(--motion-fast), border-color var(--motion-fast);
}
button.id-chip:hover { background: var(--graphite-20); border-color: var(--graphite-30); }
button.id-chip:active { background: var(--graphite-30); }
button.id-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
button.id-chip.is-copied { background: var(--success-bg); border-color: var(--success); color: var(--success-fg); }
/* Clipboard write refused (sandboxed frame, denied permission) — the ID is
   selected instead and the tooltip tells the operator to finish by keyboard. */
button.id-chip.is-manual { background: var(--warning-bg); border-color: var(--warning); color: var(--warning-fg); }
button.id-chip::selection, button.id-chip *::selection { background: var(--slate-bg); }

.table-card .cell-metric { text-align: right; }
/* Range compare — percent change vs the compare range, after the value in the
   metric's own cell. The slot has a fixed width and renders even when empty
   (no prior value), so values stay aligned down the column. Colour follows
   good/bad, the arrow follows direction; the *-text tokens clear 4.5:1 on the
   row-hover tint at this size. */
.table-card .has-delta { white-space: nowrap; }
.table-card .has-delta .delta { display: inline-flex; align-items: baseline; justify-content: flex-start; width: 64px; flex: none; box-sizing: border-box; margin-left: var(--space-sm); vertical-align: baseline; font-family: var(--font-body); font-size: var(--fs-ui-small); font-variant-numeric: tabular-nums; color: var(--graphite-70); }
.table-card .has-delta .delta .material-symbols-outlined { font-size: var(--icon-md); line-height: 1; height: 1em; align-self: center; margin: 0 -3px 0 -5px; }
.table-card .has-delta .delta.is-good { color: var(--success-text); }
.table-card .has-delta .delta.is-bad { color: var(--error-text); }
/* The name cell is the most-scanned column in the table, so it takes the
   inverting --link token rather than the theme-fixed --slate-100. */
.table-card .table tbody td.cell-name-slate { color: var(--link); }
.table-card .table tbody td.cell-children-slate { color: var(--slate-100); }
.table-card .cell-children-slate .children-stamp { color: var(--slate-100); border-color: var(--slate-100); }
/* Specificity has to beat `.table-card .table tbody td`, which sets the row's
   own colour — at two classes this rule lost, so the documented "date recedes"
   treatment silently never applied.
   --graphite-70, not --graphite-bg: a date is read on the row-hover tint as
   often as on the card, and --graphite-bg lands at 4.00:1 there in light, under
   AA at 13px. The graphite ramp inverts, so one value serves both themes. */
.table-card .table tbody td.cell-date { color: var(--graphite-70); font-size: var(--fs-ui-dense); }
.table-card .cell-state .material-symbols-outlined {
  font-size: var(--icon-lg);
  font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 24;
}
.table-card .cell-state.is-success .material-symbols-outlined { color: var(--success); }
.table-card .cell-state.is-error   .material-symbols-outlined { color: var(--error); font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 24; }
.table-card .cell-state.is-warning .material-symbols-outlined { color: var(--warning); }
.table-card .cell-state.is-neutral .material-symbols-outlined { color: var(--graphite-bg); }
/* Outline (unfilled) state glyph */
.table-card .cell-state.is-outline .material-symbols-outlined { font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 24; }
.table-card .cell-state [data-tip] { cursor: default; outline: none; }
/* Every tabbable tooltip icon shares one focus ring, by ATTRIBUTE rather than
   a per-class list — cell-state, row-avatar, lifecycle, verified-mark, and any
   future one all match automatically. Without this a keyboard user tabs
   through invisible stops: the icon has a tabIndex and an aria-label but no
   visible indicator it is focused. */
[data-tip][tabindex]:focus-visible {
  outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm);
}

.table-card .row-avatar {
  width: 22px; height: 22px;
  background: var(--slate-bg); color: var(--slate-100);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--icon-xs);
}
.table-card .name-cell .row-avatar { border-radius: 50%; }
.table-card td > .row-avatar:not(.name-cell .row-avatar) { border-radius: var(--radius-sm); }
.table-card .row-avatar .material-symbols-outlined { font-size: var(--icon-md); }
.btn-group .bg-btn .material-symbols-outlined { font-size: var(--icon-md); }
.pp-cta .material-symbols-outlined { font-size: var(--icon-md); }
.table-card .name-cell { display: inline-flex; align-items: center; gap: var(--space-md); }

.table-card .children-stamp {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; height: 22px; padding: 0 var(--space-sm);
  border: 1px solid var(--graphite-20); border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: var(--fs-ui-xs); color: var(--graphite-90);
  background: transparent;
}
.table-card .table th.cell-id,
.table-card .table td.cell-id,
.table-card .table th.cell-icon,
.table-card .table td.cell-icon,
.table-card .table th.cell-state,
.table-card .table td.cell-state,
.table-card .table th.cell-children,
.table-card .table td.cell-children-slate { text-align: center; }
.table-card .label-stack { display: inline-flex; align-items: center; gap: var(--space-sm); flex-wrap: nowrap; }
/* Overflow count after a truncated chip list. Text, not another chip — a chip
   would read as one more value of the same kind rather than a count of the
   hidden ones. Carries the full list in its tooltip. */
.table-card .chip-more {
  font-family: var(--font-body); font-size: var(--fs-ui-xs); font-weight: 500;
  color: var(--graphite-bg); white-space: nowrap; cursor: default;
}

/* Number button — a count that opens the records behind it. Same geometry as
   .children-stamp, but interactive: a count the user can act on has to look
   pressable, and a stamp does not. */
.table-card .count-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 22px; padding: 0 var(--space-sm);
  appearance: none; cursor: pointer;
  border: 1px solid var(--graphite-20); border-radius: var(--radius-sm);
  background: transparent;
  font-family: var(--font-body); font-size: var(--fs-ui-xs); color: var(--link);
  transition: background var(--motion-fast), border-color var(--motion-fast);
}
.table-card .count-btn:hover { background: var(--surface-muted); border-color: var(--graphite-30); color: var(--link-hover); }
.table-card .count-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* Zero is not a link — there is nothing behind it. */
.table-card .count-btn.is-zero {
  cursor: default; color: var(--graphite-bg); border-color: var(--graphite-10);
}
.table-card .count-btn.is-zero:hover { background: transparent; border-color: var(--graphite-10); color: var(--graphite-bg); }
.table-card .table th.cell-count-btn,
.table-card .table td.cell-count-btn { width: 1%; white-space: nowrap; text-align: center; }

/* Count expander — a .count-btn that opens the records behind it as a sub-table
   directly beneath its row. The open count reads as pressed: inset fill, a 2px
   ring, and the label drops the link colour because it is now the thing in view,
   not a way to it. State lives on aria-expanded only; never mirror it in a class. */
.table-card .count-btn[aria-expanded="true"],
.table-card .count-btn[aria-expanded="true"]:hover {
  background: var(--surface-canvas);
  border-color: var(--slate-base);
  box-shadow: 0 0 0 1px var(--slate-base);
  color: var(--graphite-90);
}
.table-card .count-btn[aria-expanded="true"]:focus-visible {
  box-shadow: 0 0 0 1px var(--slate-base), var(--focus-ring);
}
/* The parent row stays lit while its sub-table is open, so the two read as one
   unit when the user scrolls away and back. */
.table-card .table tbody tr.is-expanded,
.table-card .table tbody tr.is-expanded:hover { background: var(--graphite-10); }
.table-card .table tbody tr.is-expanded td { border-bottom-color: transparent; }

/* The sub-table row: one cell spanning every column, lit like its parent row so
   the two read as one block. The sub-table sits in that tray inset past the ID
   column on the left and by the cell padding on the right, with a short gap
   under the parent and a slightly longer one before the next row. No hover on
   this row; its own rows carry hover. */
.table-card .table tbody tr.subtable-row,
.table-card .table tbody tr.subtable-row:hover { background: var(--graphite-10); }
.table-card .table tbody tr.subtable-row > td {
  padding: var(--space-md) 14px var(--space-xl) var(--space-5xl);
  white-space: normal;
}
/* Fills that rest on --graphite-10 vanish into a lit row, which is that same
   colour on hover and while expanded: the ID pill loses its edge, a neutral
   badge or plain chip reads as bare text, an empty bar track disappears. Lift
   each one step while its row is lit. The sub-table row is excluded because
   it is lit only as a tray; its own rows light themselves on hover. Chip
   variants with their own fill (selected, outlined, accent) keep it. */
.table-card .table tbody tr:not(.subtable-row):is(:hover, .is-expanded) > td :is(
  .id-chip,
  .chip:not(.is-selected, .is-outlined, .is-accent),
  .badge-neutral,
  .badge-pending,
  .progress-track,
  .lc-bar:not(.is-done)
) { background: var(--graphite-20); }
.table-card .table tbody tr:not(.subtable-row):is(:hover, .is-expanded) > td button.id-chip:hover {
  background: var(--graphite-30);
}
.table-card .subtable {
  background: var(--surface-base);
  border: 1px solid var(--graphite-20);
  border-radius: var(--radius-md);
  overflow: hidden;
  animation: subtable-in var(--motion-slow) var(--ease-enter);
}
@keyframes subtable-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .table-card .subtable { animation: none; } }
.table-card .subtable .table { width: 100%; border-collapse: collapse; }
.table-card .subtable .table thead th { background: var(--surface-muted); }
.table-card .subtable .table tbody tr:last-child td { border-bottom: 0; }
/* Child names are long and machine-generated; let them breathe on one line and
   truncate rather than wrap the row to three lines. */
.table-card .subtable .table td.cell-name-slate {
  max-width: 0; width: 100%; overflow: hidden; text-overflow: ellipsis;
}
/* Capped list: the footer link takes the user to the full, filterable list. */
.table-card .subtable-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg);
  padding: var(--space-md) 14px;
  border-top: 1px solid var(--graphite-10);
  font-family: var(--font-body); font-size: var(--fs-ui-small); color: var(--graphite-bg);
}
.table-card .subtable-foot a { color: var(--link); text-decoration: none; }
.table-card .subtable-foot a:hover { color: var(--link-hover); text-decoration: underline; }

/* An icon column can carry a bare glyph instead of a .row-avatar plate: the
   plate reads as a badge, which implies a state rather than a property. */
.table-card .table td.cell-icon > .material-symbols-outlined {
  font-size: var(--icon-lg); color: var(--graphite-bg); cursor: default;
  font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.table-card .table td.cell-icon > .material-symbols-outlined:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

/* Skip link: visually hidden until focused, then pinned top-left above
   everything — the first tab stop on any app-shell page. */
.skip-link {
  position: absolute; top: -48px; left: 8px; z-index: 1000;
  padding: 10px 16px; border-radius: var(--radius-sm);
  background: var(--graphite-90); color: var(--graphite-contrast);
  font-family: var(--font-body); font-size: var(--fs-ui-default); font-weight: 600;
  text-decoration: none;
  transition: top var(--motion-fast);
}
.skip-link:focus-visible { top: 8px; box-shadow: var(--focus-ring); outline: none; }

/* Guaranteed-delivery mark before an object name. An icon-only badge: the word
   would repeat down every row, and the tooltip carries the meaning. */
.table-card .name-cell .verified-mark {
  display: inline-flex; align-items: center; justify-content: center;
  /* Slate, not --success: guaranteed delivery is a property of the product, not
     a healthy state. Semantic green here would compete with the status column,
     which is where good and bad actually live.
     --slate-base rather than --slate-100 because the ramp steps are theme-fixed
     — the mark would sit at 3.82:1 in dark, technically passing the 3:1 non-text
     threshold but visibly dim beside the name it qualifies. */
  flex: 0 0 auto; color: var(--slate-base); cursor: default;
}
.table-card .name-cell .verified-mark .material-symbols-outlined {
  font-size: var(--icon-sm);
  font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 20;
}

.table-card-empty {
  padding: 56px var(--space-xl);
  text-align: center;
  color: var(--graphite-bg);
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
}

/* Scrolling table body with a pinned header. Wrap the table in
   .table-scroll inside the card and cap its height. */
.table-card .table-scroll { overflow: auto; }
.table-card > .table-scroll:last-child { border-bottom-left-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); }
.table-card .table-scroll .table thead th { position: sticky; top: 0; z-index: 2; }
/* Checkbox / selection column */
.table-card .cell-select { width: 44px; }
.table-card .cell-select input[type="checkbox"] {
  width: 15px; height: 15px; flex-basis: 15px; border-width: 1.5px; display: block;
}
/* Sort-rules popover rows (table toolbar) — drag to reorder, remove on hover */
.sort-rule { cursor: grab; }
.sort-rule.is-dragging { opacity: 0.45; }
.sort-rule.is-over { box-shadow: 0 -2px 0 0 var(--slate-100); }
.sort-rule .sort-rule-x { opacity: 0; transition: opacity 120ms ease; }
.sort-rule:hover .sort-rule-x,
.sort-rule:focus-within .sort-rule-x { opacity: 1; }
/* Text action disabled in place (column picker Reset) */
[data-off="true"]:disabled { color: var(--graphite-40); cursor: default; }
/* Table component popovers — hover states the inline styles can't carry */
.table-card .tbl-col-opt:hover,
.table-card [role="listbox"][aria-label="Density"] [role="option"]:hover { background: var(--surface-muted); }
/* Column visibility — set by a toolbar column picker */
.table-card .table .col-hidden { display: none; }
/* Sortable column header — the button carries the label and the caret */
.table-card .th-sort {
  all: unset;
  display: inline-flex; align-items: center; gap: 4px;
  cursor: pointer; color: inherit; font: inherit; letter-spacing: inherit; text-transform: inherit;
}
.table-card .th-sort .material-symbols-outlined { font-size: var(--icon-xs); opacity: 0.7; }
.table-card .th-sort:focus-visible { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
/* Compact density — driven by the toolbar's density control */
.table-card.is-compact .table thead th { padding: var(--space-sm) 14px; }
.table-card.is-compact .table tbody td { padding: var(--space-sm) 14px; line-height: 1.5; }
/* Footer strip under the table — result count and pagination */
.table-card .table-foot {
  display: flex; align-items: center; gap: var(--space-lg);
  padding: 10px 14px;
  border-top: 1px solid var(--graphite-10);
  font-size: var(--fs-ui-small);
  color: var(--slate-100);
}

/* Table toolbar — sits above the table */
.table-toolbar .tt-btn[data-tip] { position: relative; }
.table-toolbar .tt-btn[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  /* Above the button — below it the table body paints over the tooltip. */
  left: 50%; bottom: calc(100% + 8px);
  transform: translateX(-50%);
  padding: var(--space-sm) var(--space-md);
  background: var(--graphite-base); color: var(--surface-canvas);
  border: 1px solid var(--graphite-contrast);
  font-family: var(--font-display); font-size: var(--fs-ui-small); font-weight: 600;
  line-height: 1; letter-spacing: 0;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity 100ms ease;
  z-index: 12;
}
.table-toolbar .tt-btn[data-tip]::before {
  content: ''; position: absolute;
  left: 50%; bottom: calc(100% + 1px);
  transform: translateX(-50%);
  border: 4px solid transparent;
  border-top-color: var(--graphite-base);
  opacity: 0; transition: opacity 100ms ease;
  z-index: 12;
}
.table-toolbar .tt-btn[data-tip]:hover::after,
.table-toolbar .tt-btn[data-tip]:focus-visible::after,
.table-toolbar .tt-btn[data-tip]:hover::before,
.table-toolbar .tt-btn[data-tip]:focus-visible::before { opacity: 1; }

.table-toolbar {
  position: relative;
  z-index: 10;
  display: inline-flex;
  align-items: center;
  gap: 28px;
  padding: 10px var(--space-sm);
}
.table-toolbar .tt-btn {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--slate-100);
  cursor: pointer;
  transition: background 120ms;
}
.table-toolbar .tt-btn:hover { background: var(--sn-selected-bg); color: var(--sn-selected-fg); }
.table-toolbar .tt-btn .material-symbols-outlined { font-size: var(--icon-lg); }
.table-toolbar .tt-btn .tt-badge {
  position: absolute;
  top: -4px;
  right: -6px;
  min-width: 16px;
  height: 16px;
  padding: 0 var(--space-sm);
  background: var(--slate-100);
  color: var(--white);
  font-family: var(--font-display);
  font-size: var(--fs-ui-tag);
  font-weight: 600;
  border-radius: var(--radius-full);
  display: grid;
  place-items: center;
  line-height: 1;
}

/* Table pagination — sits below the table */
.table-pagination {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-xxl);
  padding: 10px var(--space-sm);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  color: var(--graphite-90);
}
.table-pagination .tp-rpp {
  display: inline-flex;
  align-items: center;
  gap: var(--space-lg);
}
/* Rows-per-page control is a button in real use */
.table-pagination .tp-rpp-value {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  color: var(--graphite-90);
  background: none;
  border: 0;
  padding: 2px 4px;
  border-radius: var(--radius-sm);
  font: inherit;
  cursor: pointer;
}
.table-pagination .tp-rpp-value:hover { background: var(--slate-bg); }
/* Current row in a small inline listbox (rows-per-page, etc.) */
[role="listbox"] [role="option"].is-selected { background: var(--slate-bg); color: var(--slate-contrast); }
.table-pagination .tp-range {
  color: var(--graphite-90);
}
.table-pagination .tp-nav {
  display: inline-flex;
  gap: var(--space-lg);
}
.table-pagination .tp-nav button {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--slate-100);
  cursor: pointer;
}
.table-pagination .tp-nav button:hover { background: var(--slate-bg); }
.table-pagination .tp-nav button:disabled { color: var(--graphite-30); cursor: default; }
.table-pagination .tp-nav .material-symbols-outlined { font-size: var(--icon-md); }

/* Modal */
.modal-shell {
  background: rgba(19, 21, 20, 0.6);
  border-radius: var(--radius-md);
  padding: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal {
  background: var(--surface-base);
  border-radius: var(--radius-lg);
  padding: 28px;
  width: 100%;
  max-width: 440px;
  box-shadow: 0 20px 50px rgba(0,0,0,0.3);
}
.modal-title {
  font-family: var(--font-display);
  font-size: var(--fs-ui-title);
  font-weight: 600;
  line-height: 0.95;
  color: var(--graphite-90);
  margin: 0 0 var(--space-md);
}
.modal-body {
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.71;
  color: var(--graphite-90);
  margin: 0 0 var(--space-xxl);
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-md);
}

/* Modal */
.modal-shell {
  background: rgba(19, 21, 20, 0.6);
  border-radius: var(--radius-md);
  padding: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal {
  background: var(--surface-base);
  border-radius: var(--radius-lg);
  padding: 28px;
  width: 100%;
  max-width: 440px;
  box-shadow: 0 20px 50px rgba(0,0,0,0.3);
}
.modal-title {
  font-family: var(--font-display);
  font-size: var(--fs-ui-title);
  font-weight: 600;
  line-height: 0.95;
  color: var(--graphite-90);
  margin: 0 0 var(--space-md);
}
.modal-body {
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.71;
  color: var(--graphite-90);
  margin: 0 0 var(--space-xxl);
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-md);
}

/* ─── App shell layout ───────────────────────────────────────
   The frame TopHeader + Sidenav sit in: full-height column, header pinned,
   nav and content sharing the remaining row. Squares off the header and nav
   so they read as chrome rather than floating cards. */
.app-shell {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background: var(--surface-canvas);
  color: var(--graphite-90);
  font-family: var(--font-body);
}
.app-shell > .top-header { flex: 0 0 56px; border-radius: 0; }
.app-shell-body { display: flex; flex: 1; min-height: 0; }
.app-shell-body > .sidenav {
  border-radius: 0;
  border: none;
  border-right: 1px solid var(--graphite-10);
  overflow: hidden;
}
.app-shell-main { display: flex; flex-direction: column; min-width: 0; flex: 1; min-height: 0; }
.app-shell-main > main { flex: 1; min-height: 0; overflow: auto; padding: 28px 24px 40px; }

/* Sidebar / side navigation.
   .sidenav is the production root; .sidenav-demo is the showcase alias. */
.nav-demo {
  display: grid;
  grid-template-columns: 64px 220px 1fr;
  gap: var(--space-xl);
  height: 360px;
}
.sidenav,
.sidenav-demo {
  /* Production width. The showcase grid sizes its own columns, so the demo
     alias opts out below. */
  width: 232px;
  flex: 0 0 auto;
  background: var(--surface-base);
  border-radius: var(--radius-md);
  padding: var(--space-lg) var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  border: 1px solid var(--graphite-10);
  position: relative;
}
.sidenav .sn-state-label,
.sidenav-demo .sn-state-label {
  position: absolute;
  top: -22px;
  left: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-ui-section);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--slate-100);
}
.sidenav .sn-section,
.sidenav-demo .sn-section {
  font-family: var(--font-body);
  font-size: var(--fs-ui-section);
  font-weight: 600;
  line-height: 0.95;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--graphite-bg);
  padding: var(--space-lg) var(--space-lg) var(--space-sm);
}
.sidenav .sn-item,
.sidenav-demo .sn-item {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 7px var(--space-lg);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--fs-ui-default);
  font-weight: 400;
  line-height: 1.43;
  color: var(--graphite-bg);
  cursor: pointer;
}
.sidenav .sn-item:hover,
.sidenav-demo .sn-item:hover { background: var(--graphite-10); color: var(--graphite-90); }
.sidenav .sn-item.is-selected,
.sidenav-demo .sn-item.is-selected {
  background: var(--sn-selected-bg);
  color: var(--sn-selected-fg);
  font-weight: 600;
}
.sidenav .sn-item .icon,
.sidenav-demo .sn-item .icon { color: currentColor; }
/* Scroll body + pinned footer. Utility rows (Settings) stay reachable at the
   bottom no matter how long the route list gets. */
.sidenav .sn-scroll,
.sidenav-demo .sn-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.sidenav .sn-footer,
.sidenav-demo .sn-footer {
  flex: 0 0 auto;
  padding-top: var(--space-md);
  border-top: 1px solid var(--graphite-10);
}
/* Glyphs sit at 18px in both states — the Material default of 24px inflates the row. */
.sidenav .sn-item .material-symbols-outlined,
.sidenav-demo .sn-item .material-symbols-outlined { font-size: var(--icon-md); }
.sidenav .sn-item.is-danger,
.sidenav-demo .sn-item.is-danger { color: var(--error); }
.sidenav .sn-item.is-danger:hover,
.sidenav-demo .sn-item.is-danger:hover { background: var(--error-bg, rgba(211,47,47,0.08)); color: var(--error); }

.nav-demo .sidenav-demo { width: auto; }

/* Collapsed sidebar variant — icon-only rail */
.sidenav.is-collapsed,
.sidenav-demo.is-collapsed { width: 64px; padding: var(--space-lg) var(--space-sm); }
.sidenav.is-collapsed .sn-item,
.sidenav-demo.is-collapsed .sn-item {
  justify-content: center;
  padding: 9px 0;
  gap: 0;
  font-size: 0;
}
.sidenav.is-collapsed .sn-item .material-symbols-outlined,
.sidenav-demo.is-collapsed .sn-item .material-symbols-outlined { font-size: var(--icon-md); }
.sidenav.is-collapsed .sn-item .icon,
.sidenav-demo.is-collapsed .sn-item .icon { font-size: initial; }
.sidenav.is-collapsed .sn-section,
.sidenav-demo.is-collapsed .sn-section {
  height: 1px;
  padding: 0;
  margin: var(--space-md) var(--space-sm);
  background: var(--graphite-10);
  font-size: 0;
  color: transparent;
  overflow: hidden;
}

.tabs-demo {
  background: var(--surface-base);
  border-radius: var(--radius-md);
  border: 1px solid var(--graphite-10);
  padding: 0;
  display: flex;
  flex-direction: column;
}
.tabs-bar {
  display: flex;
  border-bottom: 1px solid var(--graphite-10);
  padding: 0 var(--space-xl);
  gap: var(--space-sm);
}
.tab {
  font-family: var(--font-body);
  font-size: var(--fs-ui-caps);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--graphite-bg);
  height: 56px;
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-xl);
  cursor: pointer;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  /* A tab is a shrinkable flex item, so without this any multi-word label wraps
     and the underline indicator sits under a two-line box. */
  white-space: nowrap;
  flex: 0 0 auto;
  gap: 6px;
}
/* Subset size, its own element rather than part of the label string. */
.tab .tab-count {
  font-family: var(--font-mono);
  font-size: var(--fs-ui-small);
  font-variant-numeric: tabular-nums;
  color: var(--graphite-bg);
  letter-spacing: 0;
  text-transform: none;
}
.tab.is-selected .tab-count { color: inherit; }
.tab:hover { color: var(--graphite-90); }
.tab.is-selected { color: var(--link); border-bottom-color: var(--link); font-weight: 600; }
.tab.is-selected:hover { color: var(--link-hover); border-bottom-color: var(--link-hover); }
.tabs-content {
  padding: var(--space-xxl);
  font-family: var(--font-body);
  font-size: var(--fs-ui-small);
  color: var(--slate-100);
  line-height: 1.5;
}




/* ── Scoped demo / component overrides ─────── */
.pf-grid { display: flex; flex-wrap: wrap; gap: var(--space-lg); }
      .pf-tile { display: flex; flex-direction: column; align-items: center; gap: var(--space-sm); width: 96px; }
      /* Logo plate is a CONSTANT light canvas in both themes: brand logos
         carry their own fixed colors (and some are dark wordmarks), so a
         neutral white plate is the only surface that keeps every mark legible
         without recoloring it. Deliberate per-asset decision, not a token. */
      .pf-plate {
        width: 64px; height: 64px;
        display: flex; align-items: center; justify-content: center;
        background: #FFFFFF;
        border: 1px solid var(--surface-stroke);
        border-radius: var(--radius-md);
        box-shadow: var(--elev-1);
      }
      .pf-plate.wide { width: 116px; }
      .pf-plate img { max-width: 40px; max-height: 40px; width: auto; height: auto; display: block; }
      .pf-plate.wide img { max-width: 92px; max-height: 34px; }
      .pf-name { font-family: var(--font-body); font-size: var(--fs-ui-small); font-weight: 500; color: var(--graphite-90); text-align: center; line-height: 1.2; }
      .pf-key { font-family: var(--font-mono); font-size: var(--fs-ui-code); color: var(--slate-100); }
      .pf-tile.is-missing .pf-plate { background: transparent; border-style: dashed; box-shadow: none; }
      .pf-tile.is-missing .pf-plate .material-symbols-outlined { color: var(--graphite-bg); font-size: var(--icon-xl); }
      .pf-tile.is-missing .pf-name { color: var(--graphite-bg); }

/* The Beacon chat vocabulary is .as-* (see "Agent workspace" above). A second
   .beacon-* demo block used to live here; the ChatSticker showcase now uses the
   real contract, so there is one vocabulary rather than two. */

.th-demo { display: flex; flex-direction: column; gap: 16px; }
      .top-header {
        display: flex;
        align-items: center;
        gap: 20px;
        height: 56px;
        padding: 0 16px;
        font-family: var(--font-body);
        border-radius: var(--radius-md);
      }
      /* Default (production): tracks the page theme through tokens. Use this
         in a real app shell. */
      .top-header {
        background: var(--surface-base);
        color: var(--graphite-90);
        border-bottom: 1px solid var(--graphite-10);
      }
      /* Locked variants — these document the dark and light TopHeader side by
         side on the showcase page, so they must NOT track the page theme
         toggle. Hardcoded literals, specimen use only. */
      .top-header.is-dark  { background: #131514; color: #FFFFFF; border-bottom: 0; }
      .top-header.is-light { background: #FFFFFF; color: #131514; border: 1px solid #F0F1F0; }

      .th-menu {
        display: inline-flex; align-items: center; justify-content: center;
        width: 36px; height: 36px; border: 0; border-radius: var(--radius-sm);
        background: transparent; color: currentColor; cursor: pointer;
      }
      .th-menu .material-symbols-outlined { font-size: var(--icon-xl); }
      /* Brand lockup swaps by theme with no JS and no src interpolation:
         both marks ship in the markup, CSS picks one. */
      .th-brand .th-mark-dark { display: none; }
      [data-theme="dark"] .th-brand .th-mark-dark { display: block; }
      [data-theme="dark"] .th-brand .th-mark-light { display: none; }
      .th-menu:hover { background: var(--graphite-10); }
      .top-header.is-dark .th-menu:hover { background: rgba(255,255,255,0.06); }
      .top-header.is-light .th-menu:hover { background: #F0F1F0; }

      /* Logout — trailing icon action, sits after the user indicator */
      .th-logout {
        display: inline-flex; align-items: center; justify-content: center;
        width: 36px; height: 36px; border: 0; border-radius: var(--radius-sm);
        background: transparent; color: currentColor; cursor: pointer;
        opacity: 0.75;
      }
      .th-logout .material-symbols-outlined { font-size: var(--icon-lg); }
      .th-logout:hover { opacity: 1; background: var(--graphite-10); }
      .top-header.is-dark .th-logout:hover { background: rgba(255,255,255,0.06); }

      .th-brand { display: inline-flex; align-items: center; height: 36px; }
      .th-brand img { height: 22px; width: auto; display: block; }

      .th-org {
        display: inline-flex; align-items: center; gap: 10px;
        height: 36px; padding: 0 12px 0 16px;
        background: transparent;
        border-radius: var(--radius-sm);
        font-family: var(--font-body); font-size: var(--fs-ui-default);
        font-weight: 400; line-height: 1.71; color: currentColor;
        min-width: 260px; justify-content: space-between;
      }
      .th-org { border: 1px solid var(--graphite-20); cursor: pointer; }
      .th-org:hover { background: var(--graphite-10); }
      .top-header.is-dark .th-org  { border: 1px solid #484C4B; }
      .top-header.is-light .th-org { border: 1px solid #D4D6D5; }
      .th-org .material-symbols-outlined { font-size: var(--icon-xl); color: currentColor; opacity: 0.9; }

      .th-spacer { flex: 1; }

      /* DEPRECATED — the canonical light/dark control is .theme-switch. These
         rules remain only for the locked specimen rows on the showcase page. */
      .th-theme { display: inline-flex; align-items: center; gap: 10px; }
      .th-theme .material-symbols-outlined { font-size: var(--icon-md); opacity: 0.9; }
      .th-theme .th-moon { color: var(--slate-100); opacity: 1; }
      .th-theme .th-switch {
        width: 36px; height: 20px; border-radius: var(--radius-full);
        background: var(--slate-100); position: relative; cursor: pointer;
      }
      .th-theme .th-switch::after {
        content: ""; position: absolute; top: 2px; left: 2px;
        width: 16px; height: 16px; border-radius: 50%;
        background: var(--white);
        box-shadow: 0 1px 2px rgba(0,0,0,0.25);
        transition: left .15s;
      }
      .th-theme.is-on .th-switch::after { left: 18px; }

      .th-user {
        display: inline-flex; align-items: center; gap: 10px;
        height: 32px; padding: 0 14px 0 4px;
        border-radius: var(--radius-full);
        font-family: var(--font-body); font-size: var(--fs-ui-default); line-height: 1;
      }
      .th-user { background: var(--sn-selected-bg); color: var(--sn-selected-fg); }
      .top-header.is-dark  .th-user { background: var(--sn-selected-bg); color: var(--sn-selected-fg); }
      .top-header.is-light .th-user { background: var(--sn-selected-bg); color: var(--sn-selected-fg); }
      .th-user-wrap { position: relative; }
      .th-user { border: none; cursor: default; font-family: inherit; }
      .th-user.is-clickable { cursor: pointer; }
      .th-user.is-clickable:hover { filter: brightness(1.08); }
      .th-user.is-clickable:focus-visible { outline: none; box-shadow: var(--focus-ring); }
      .th-user-chev { font-size: var(--icon-lg); opacity: 0.8; }

      /* Popover — profile block, then labeled groups, then a footer row set
         off by its own divider. Anchored to the header, right-aligned so it
         never overhangs the viewport edge regardless of screen width. */
      .th-user-menu {
        position: absolute; top: calc(100% + 8px); right: 0;
        width: 280px; z-index: 60;
        background: var(--surface-base);
        border: 1px solid var(--surface-stroke);
        border-radius: var(--radius-lg);
        box-shadow: var(--elev-3);
        padding: var(--space-lg) 0;
      }
      .thm-profile { display: flex; align-items: center; gap: var(--space-lg); padding: var(--space-md) var(--space-xl) var(--space-lg); }
      .thm-profile-avatar { width: 40px; height: 40px; flex: 0 0 auto; }
      .thm-profile-avatar .material-symbols-outlined { font-size: var(--icon-xl); }
      .thm-profile-text { min-width: 0; }
      .thm-profile-name { margin: 0; font-family: var(--font-display); font-weight: 600; font-size: var(--fs-ui-default); color: var(--graphite-90); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
      .thm-profile-email { margin: 2px 0 0; font-size: var(--fs-ui-small); color: var(--graphite-bg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
      .thm-group { padding: var(--space-sm) 0; border-top: 1px solid var(--surface-stroke); }
      .thm-group:first-of-type { border-top: 1px solid var(--surface-stroke); }
      .thm-group-label { padding: var(--space-sm) var(--space-xl) 4px; font-family: var(--font-body); font-size: var(--fs-ui-xs); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--graphite-bg); }
      .thm-item {
        display: flex; align-items: center; gap: var(--space-lg); width: 100%;
        padding: var(--space-md) var(--space-xl);
        background: transparent; border: none; cursor: pointer; text-align: left;
        font-family: var(--font-body); font-size: var(--fs-ui-default); color: var(--graphite-90);
      }
      .thm-item .material-symbols-outlined { font-size: var(--icon-lg); color: var(--graphite-bg); }
      .thm-item:hover { background: var(--graphite-10); }
      .thm-item:focus-visible { outline: none; box-shadow: inset var(--focus-ring); }
      .thm-footer { padding-bottom: 0; }
      .th-user .th-avatar {
        width: 26px; height: 26px; border-radius: 50%;
        display: inline-flex; align-items: center; justify-content: center;
        background: #FFFFFF; color: #637D9C;
      }
      .th-user .th-avatar .material-symbols-outlined {
        font-size: var(--icon-lg);
        font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
      }

.fb-skeleton {
        background: linear-gradient(90deg, var(--graphite-10) 0%, var(--graphite-20) 50%, var(--graphite-10) 100%);
        background-size: 200% 100%;
        border-radius: var(--radius-sm);
        animation: fb-shimmer 1.4s var(--ease-standard, ease) infinite;
      }
      @keyframes fb-shimmer { to { background-position: -200% 0; } }
      .fb-line { height: 12px; }
      .fb-line.lg { height: 16px; }
      .fb-line.sm { height: 10px; }
      .fb-circle { border-radius: 50%; }
      .fb-skel-row {
        display: flex; align-items: center; gap: var(--space-lg);
        padding: var(--space-lg) var(--space-xl);
        border-bottom: 1px solid var(--graphite-10);
      }
      .fb-skel-row:last-child { border-bottom: 0; }
      .fb-skel-table {
        border: 1px solid var(--graphite-10);
        border-radius: var(--radius-md);
        background: var(--surface-base);
        overflow: hidden;
        flex: 1; min-width: 0;
      }
      .fb-skel-card {
        display: flex; flex-direction: column; gap: var(--space-md);
        padding: var(--space-xl);
        border: 1px solid var(--graphite-10);
        border-radius: var(--radius-md);
        background: var(--surface-base);
        flex: 1; min-width: 0;
      }

      .fb-frame {
        display: flex; flex-direction: column; align-items: center; justify-content: center;
        gap: var(--space-md); padding: var(--space-xxl);
        border: 1px solid var(--graphite-10); border-radius: var(--radius-md);
        background: var(--surface-base); text-align: center;
        flex: 1; min-width: 0;
      }
      .fb-mark {
        display: inline-grid; place-items: center;
        width: 40px; height: 40px; border-radius: 50%;
        background: var(--slate-bg); color: var(--slate-100);
      }
      .fb-mark .material-symbols-outlined { font-size: var(--icon-xl); }
      .fb-frame.is-error .fb-mark { background: var(--error-bg); color: var(--error); }
      .fb-frame.is-warning .fb-mark { background: var(--warning-bg); color: var(--warning); }
      .fb-title {
        font-family: var(--font-display); font-weight: 600;
        font-size: var(--fs-ui-title); line-height: 0.95;
        color: var(--graphite-90); margin: var(--space-sm) 0 0;
      }
      .fb-body {
        font-family: var(--font-body); font-size: var(--fs-ui-default);
        line-height: 1.5; color: var(--graphite-bg);
        max-width: 420px; margin: 0;
      }
      .fb-actions { display: inline-flex; gap: var(--space-md); margin-top: var(--space-md); }
      .fb-error-detail {
        font-family: var(--font-mono); font-size: var(--fs-ui-xs);
        color: var(--graphite-bg);
        background: var(--graphite-10);
        padding: var(--space-sm) var(--space-md);
        border-radius: var(--radius-sm);
        max-width: 420px;
      }

/* ── Tooltip ─────────────────────────────────────────────────────────────
   Fill contrasts the page background: graphite/base (dark in light mode,
   light in dark mode), graphite/contrast stroke, surface/canvas text.
   Typography: UI/Small (12px Avantt SemiBold).

   Two usage patterns:
   1. [data-tooltip] attribute — pure CSS, top-placement, no tail (default).
      Add [data-tooltip-placement] for a tail: top | bottom | left | right.
   2. .tooltip class — for positioned JS overlays. Add .tooltip-top etc.
      for the tail variant matching the placement. */

/* ── Base shape ── */
.tooltip {
  display: inline-flex;
  align-items: center;
  padding: 5px 10px;
  background: var(--graphite-base);
  border: 1px solid var(--graphite-contrast);
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  color: var(--surface-canvas);
  white-space: nowrap;
  position: relative;
  pointer-events: none;
  line-height: 1.4;
  letter-spacing: 0.02em;
}

/* Tail — top (tooltip above anchor, tail points down) */
.tooltip-top:not(.tooltip-host)::after,
[data-tooltip][data-tooltip-placement="top"]::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: var(--graphite-base);
  pointer-events: none;
}

/* Tail — bottom (tooltip below anchor, tail points up) */
.tooltip-bottom:not(.tooltip-host)::after,
[data-tooltip][data-tooltip-placement="bottom"]::after {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-bottom-color: var(--graphite-base);
  pointer-events: none;
}

/* Tail — right (tooltip to right of anchor, tail points left toward anchor) */
.tooltip-right:not(.tooltip-host)::after,
[data-tooltip][data-tooltip-placement="right"]::after {
  content: '';
  position: absolute;
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
  border: 6px solid transparent;
  border-right-color: var(--graphite-base);
  pointer-events: none;
}

/* Tail — left (tooltip to left of anchor, tail points right toward anchor) */
.tooltip-left:not(.tooltip-host)::after,
[data-tooltip][data-tooltip-placement="left"]::after {
  content: '';
  position: absolute;
  left: 100%;
  top: 50%;
  transform: translateY(-50%);
  border: 6px solid transparent;
  border-left-color: var(--graphite-base);
  pointer-events: none;
}

/* [data-tooltip] auto-CSS — no tail by default (top placement implied) */
[data-tooltip] { position: relative; }
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(2px);
  background: var(--graphite-base);
  border: 1px solid var(--graphite-contrast);
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  color: var(--surface-canvas);
  letter-spacing: 0.02em;
  white-space: nowrap;
  padding: 5px 10px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease, transform 120ms ease;
  z-index: 100;
}
[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
/* Right/left placements need their own position + transform — the base rule
   above only covers top (the implied default). */
[data-tooltip][data-tooltip-placement="right"]::after {
  bottom: auto; left: calc(100% + 8px); top: 50%;
  transform: translateY(-50%) translateX(-2px);
}
[data-tooltip][data-tooltip-placement="right"]:hover::after,
[data-tooltip][data-tooltip-placement="right"]:focus-visible::after {
  transform: translateY(-50%) translateX(0);
}
[data-tooltip][data-tooltip-placement="left"]::after {
  bottom: auto; left: auto; right: calc(100% + 8px); top: 50%;
  transform: translateY(-50%) translateX(2px);
}
[data-tooltip][data-tooltip-placement="left"]:hover::after,
[data-tooltip][data-tooltip-placement="left"]:focus-visible::after {
  transform: translateY(-50%) translateX(0);
}
[data-tooltip][data-tooltip-placement="bottom"]::after {
  bottom: auto; top: calc(100% + 8px); left: 50%;
  transform: translateX(-50%) translateY(-2px);
}
[data-tooltip][data-tooltip-placement="bottom"]:hover::after,
[data-tooltip][data-tooltip-placement="bottom"]:focus-visible::after {
  transform: translateX(-50%) translateY(0);
}

/* ── Tooltip host — the actual .tooltip-host + data-tip contract Tooltip.jsx
   renders. (Distinct from the [data-tooltip] auto-CSS above, which is for
   plain HTML that wants a tooltip with no React.) ── */
.tooltip-host { cursor: default; }
.tooltip-host::after {
  content: attr(data-tip);
  position: absolute;
  display: inline-flex; align-items: center;
  padding: var(--space-sm) var(--space-md);
  background: var(--graphite-base);
  border: 1px solid var(--graphite-contrast);
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-size: var(--fs-ui-small);
  font-weight: 600;
  color: var(--surface-canvas);
  white-space: nowrap;
  letter-spacing: 0.02em;
  line-height: 1.4;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease, transform 120ms ease;
  z-index: 100;
}
.tooltip-host:hover::after,
.tooltip-host:focus-visible::after { opacity: 1; }

.tooltip-host.tooltip-top::after {
  bottom: calc(100% + 8px); left: 50%;
  transform: translateX(-50%) translateY(2px);
}
.tooltip-host.tooltip-top:hover::after,
.tooltip-host.tooltip-top:focus-visible::after { transform: translateX(-50%) translateY(0); }

.tooltip-host.tooltip-bottom::after {
  top: calc(100% + 8px); left: 50%;
  transform: translateX(-50%) translateY(-2px);
}
.tooltip-host.tooltip-bottom:hover::after,
.tooltip-host.tooltip-bottom:focus-visible::after { transform: translateX(-50%) translateY(0); }

.tooltip-host.tooltip-right::after {
  left: calc(100% + 8px); top: 50%;
  transform: translateY(-50%) translateX(-2px);
}
.tooltip-host.tooltip-right:hover::after,
.tooltip-host.tooltip-right:focus-visible::after { transform: translateY(-50%) translateX(0); }

.tooltip-host.tooltip-left::after {
  right: calc(100% + 8px); top: 50%;
  transform: translateY(-50%) translateX(2px);
}
.tooltip-host.tooltip-left:hover::after,
.tooltip-host.tooltip-left:focus-visible::after { transform: translateY(-50%) translateX(0); }

/* ── Drawer ───────────────────────────────────────────────────────────────
   Slide-in panel anchored to the viewport edge. Right placement default;
   .drawer-left for secondary/left panels. Three width sizes: sm/default/lg.
   Open by adding .is-open to .drawer-overlay. */

.drawer-overlay {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: flex;
  justify-content: flex-end;
  background: rgba(19, 21, 20, 0.5);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
}
.drawer-overlay.is-open {
  opacity: 1;
  pointer-events: all;
}

/* Panel */
.drawer {
  position: relative;
  height: 100%;
  width: 480px;
  max-width: calc(100vw - 48px);
  background: var(--surface-base);
  border-left: 1px solid var(--surface-stroke);
  box-shadow: -8px 0 40px rgba(19, 21, 20, 0.16);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 260ms cubic-bezier(0.4, 0, 0.2, 1);
  overflow: hidden;
}
.drawer-overlay.is-open .drawer {
  transform: translateX(0);
}

/* Sizes */
.drawer-sm { width: 360px; }
.drawer-lg { width: 640px; }

/* Left placement */
.drawer-overlay.drawer-left { justify-content: flex-start; }
.drawer-overlay.drawer-left .drawer {
  border-left: none;
  border-right: 1px solid var(--surface-stroke);
  box-shadow: 8px 0 40px rgba(19, 21, 20, 0.16);
  transform: translateX(-100%);
}
.drawer-overlay.drawer-left.is-open .drawer { transform: translateX(0); }

/* Header */
.drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 24px;
  border-bottom: 1px solid var(--surface-stroke);
  flex-shrink: 0;
}
.drawer-title {
  font-family: var(--font-display);
  font-size: var(--fs-ui-title);
  font-weight: 600;
  line-height: 0.95;
  color: var(--graphite-90);
  margin: 0;
}

/* Body */
.drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: 24px;
  scrollbar-width: thin;
}
.drawer-body::-webkit-scrollbar { width: 4px; }
.drawer-body::-webkit-scrollbar-thumb { background: var(--graphite-20); border-radius: 999px; }

/* Footer */
.drawer-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-md);
  padding: 16px 24px;
  border-top: 1px solid var(--surface-stroke);
  flex-shrink: 0;
}

/* Dark mode */
[data-theme="dark"] .drawer-overlay { background: rgba(0, 0, 0, 0.6); }
[data-theme="dark"] .drawer { box-shadow: -8px 0 40px rgba(0, 0, 0, 0.4); }
[data-theme="dark"] .drawer-overlay.drawer-left .drawer { box-shadow: 8px 0 40px rgba(0, 0, 0, 0.4); }

/* ─── Card ───────────────────────────────────────────────────
   Surface container: --surface-base fill, hairline stroke, radius 4–8px.
   One of .card-outline (default look) / .card-elevation / .card-flat. */
.card { background: var(--surface-base); border-radius: var(--radius-lg); padding: var(--space-3xl); box-sizing: border-box; }
.card-outline { border: 1px solid var(--surface-stroke); }
.card-elevation { border: 1px solid var(--graphite-10); box-shadow: var(--elev-2); }
.card-flat { border: 0; box-shadow: none; }
.card-sm { padding: var(--space-lg); border-radius: var(--radius-md); }
.card-lg { padding: 40px; }
.card-header { display: flex; align-items: flex-start; gap: var(--space-lg); margin-bottom: var(--space-lg); }
.card-title { font-family: var(--font-display); font-size: var(--fs-ui-title); font-weight: 600; line-height: 0.95; color: var(--graphite-90); margin: 0; }
.card-subtitle { font-family: var(--font-body); font-size: var(--fs-ui-dense); color: var(--graphite-bg); margin: var(--space-sm) 0 0; }
.card-actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-md); }
.card-body { font-family: var(--font-body); font-size: var(--fs-ui-default); line-height: 1.71; color: var(--graphite-90); }
.card-footer { display: flex; align-items: center; gap: var(--space-md); margin-top: var(--space-xxl); padding-top: var(--space-lg); border-top: 1px solid var(--surface-stroke); }

/* ─── Skeleton ───────────────────────────────────────────────
   Loading placeholder whose shape matches the eventual content. */
@keyframes skeleton-sheen { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
.skeleton {
  display: block; border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--surface-muted) 25%, var(--graphite-10) 37%, var(--surface-muted) 63%);
  background-size: 200% 100%;
  animation: skeleton-sheen 1.4s ease-in-out infinite;
}
.skeleton-text { height: 12px; }
.skeleton-title { height: 20px; border-radius: var(--radius-sm); }
.skeleton-block { height: 96px; border-radius: var(--radius-md); }
.skeleton-circle { border-radius: 50%; }
.skeleton-stack { display: flex; flex-direction: column; gap: var(--space-md); }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; background: var(--surface-muted); } }

/* ─── State panel (empty / error) ────────────────────────────
   Centered icon + headline + body + actions. .is-error tints the icon.
   Inside a table use .table-card-empty for the in-card variant. */
.state-panel { display: flex; flex-direction: column; align-items: center; gap: var(--space-md); padding: 64px var(--space-xxl); text-align: center; }
.state-panel .sp-icon { display: inline-flex; align-items: center; justify-content: center; width: 56px; height: 56px; margin-bottom: var(--space-sm); border-radius: var(--radius-md); background: var(--slate-bg); color: var(--slate-base); font-size: var(--icon-3xl); }
.state-panel.is-error .sp-icon { background: var(--error-bg); color: var(--error-fg); }
.state-panel .sp-title { font-family: var(--font-display); font-size: 16px; font-weight: 600; color: var(--graphite-90); margin: 0; }
.state-panel .sp-body { font-family: var(--font-body); font-size: var(--fs-ui-dense); color: var(--graphite-bg); max-width: 42ch; line-height: 1.6; text-wrap: pretty; margin: 0; }
.state-panel .sp-actions { display: flex; align-items: center; gap: var(--space-md); margin-top: var(--space-sm); }
.state-panel-compact { padding: var(--space-xxl) var(--space-lg); }

/* ─── Chat sticker ───────────────────────────────────────────
   Animated brand GIF as a standalone chat message. No plate, no shadow. */
.chat-sticker { display: block; background: none; border: 0; box-shadow: none; }
/* Smallest size — inline beside a line of working-state text, where reaction's
   48px would crowd the text and mismatch its line-height. */
.chat-sticker-inline { width: 32px; height: 32px; }
.chat-sticker-reaction { width: 48px; height: 48px; }
.chat-sticker-thread { width: 80px; height: 80px; }
.chat-sticker-emphasis { width: 128px; height: 128px; }

/* ── Section — a titled, optionally collapsible block within a card stack.
   Reconciles two prior ad hoc implementations (beacon-workspace's card-header
   icon swap, Data Viz Dashboard's rotating chevron) into one contract: a
   single chevron that rotates -90deg when collapsed, and the body collapses
   via max-height + opacity so the motion is smooth rather than a hard cut. */
.section { background: var(--surface-base); border-radius: var(--radius-lg); }
.section.is-outline { border: 1px solid var(--surface-stroke); }
.section:not(.is-outline) { box-shadow: var(--elev-1); }
.section-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-lg); padding: var(--space-xl) var(--space-xl) var(--space-lg);
  border-bottom: 1px solid var(--surface-stroke);
}
.section.is-collapsed .section-header { padding-bottom: var(--space-xl); border-bottom-color: transparent; }
.section-header.is-clickable { cursor: pointer; }
.section-title-block { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.section-title { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-ui-title); color: var(--graphite-90); margin: 0; }
.section-meta { font-family: var(--font-body); font-size: var(--fs-ui-small); color: var(--graphite-bg); margin: 0; }
.section-chev {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: var(--radius-sm);
  color: var(--graphite-bg); font-size: var(--icon-lg); cursor: pointer;
  transition: transform var(--motion-base) var(--ease-standard), background var(--motion-fast);
}
.section-chev:hover { background: var(--graphite-10); }
.section-chev:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.section.is-collapsed .section-chev { transform: rotate(180deg); }
.section-body {
  overflow: hidden; padding: var(--space-xl);
  max-height: 2000px; opacity: 1;
  transition: max-height var(--motion-base) var(--ease-standard), opacity var(--motion-fast), padding var(--motion-fast);
}
.section.is-collapsed .section-body { max-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0; }

/* Editable page title — click-to-rename h1/h2 on object detail pages. */
.editable-title.et-static {
  display: inline-flex; align-items: center; gap: var(--space-sm);
  cursor: text; border-radius: var(--radius-sm);
  margin: -4px -6px; padding: 4px 6px;
}
.editable-title.et-static:hover, .editable-title.et-static:focus-visible {
  background: var(--graphite-10);
  outline: none;
}
.editable-title .et-pencil {
  font-size: var(--icon-sm); color: var(--slate-100);
  opacity: 0; transition: opacity 0.1s ease;
}
.editable-title.et-static:hover .et-pencil, .editable-title.et-static:focus-visible .et-pencil { opacity: 1; }
.editable-title.is-h1 .et-text, input.editable-title.is-h1 { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 600; line-height: 1.05; letter-spacing: -0.01em; }
.editable-title.is-h2 .et-text, input.editable-title.is-h2 { font-family: var(--font-display); font-size: var(--fs-h4); font-weight: 600; line-height: 1.1; }
input.editable-title.et-input {
  color: var(--graphite-90); background: var(--surface-base);
  border: 1px solid var(--btn-primary-bg); border-radius: var(--radius-sm);
  padding: 3px 6px; margin: -4px -6px; min-width: 240px;
  box-shadow: var(--focus-ring);
}
