/* ==========================================================================
 * Kulima Component Library — Phase 18
 * --------------------------------------------------------------------------
 * AgroDB / Kulima FMIS — multi-tenant farm management for Uganda's agriculture.
 *
 * A token-driven component layer that LAYERS ON TOP of Bootstrap 5 and the
 * Phase 18 foundation. Every value references a --kulima-* custom property from
 * kulima-tokens.css (no colour/size literals in component bodies — the single
 * exception is the spinner's rgba track, built from currentColor). Nothing here
 * restyles a bare Bootstrap class; every selector is a .kulima-* class, so the
 * two systems coexist and existing pages are untouched until they opt in.
 *
 * Load order (set in public/includes/head.php):
 *   Bootstrap/APEX  ->  kulima-tokens.css  ->  kulima-theme.css
 *   ->  kulima-a11y.css  ->  kulima-fonts.css  ->  kulima-components.css
 * This file loads LAST so its component rules win over the thin helpers, and it
 * deliberately EXTENDS (never destructively overrides) the button/card/badge
 * primitives already defined in kulima-theme.css.
 *
 * Interaction-state doctrine (chwezi design engine, component-states skill):
 * every interactive component fills the reachable cells of the state matrix —
 *   default · hover · focus-visible · active/pressed · disabled · loading ·
 *   selected/checked · error/invalid · read-only
 * — each with a token-backed treatment, never left to chance. State is carried
 * by TEXT + SHAPE + ICON, not colour alone (WCAG 1.4.1). Focus-visible is a
 * real >=3:1 ring (reuses --kulima-color-focus from the token layer; WCAG 2.4.7
 * / 2.4.11 / 1.4.11). Interactive targets are >=44px (field-use ergonomics,
 * WCAG 2.5.8). Dark theme is correct for free: components reference semantic
 * roles, which the [data-theme="dark"] block in kulima-tokens.css re-points.
 *
 * Transitions are scoped to tokenised durations/easing and collapsed under
 * prefers-reduced-motion by the guard already shipped in kulima-theme.css §9
 * (its selector [class^="kulima-"],[class*=" kulima-"] covers everything here);
 * a local guard at the end re-asserts it for keyframe animations (spinner,
 * button busy, toast entry).
 * ========================================================================== */

/* ==========================================================================
 * 1. BUTTONS  (.kulima-btn + variant + optional size/modifier)
 * --------------------------------------------------------------------------
 * The base .kulima-btn and the --primary / --secondary / --outline variants
 * are defined in kulima-theme.css §4. This section ADDS the rest of the library
 * contract: the subtle + danger variants, sizes, full-width + icon-only
 * modifiers, and the complete interaction-state matrix (active/pressed,
 * focus-visible, loading) that the thin helper layer did not carry.
 * Variants (orthogonal axis): primary | secondary | subtle | danger | outline.
 * Sizes (orthogonal axis):    sm | (default md) | lg.
 * ========================================================================== */

/* --- Variant: subtle (low-emphasis, tinted fill) --- */
.kulima-btn--subtle {
  background-color: var(--kulima-color-primary-subtle);
  color: var(--kulima-color-primary-hover);
  border-color: transparent;
}
.kulima-btn--subtle:hover {
  background-color: var(--kulima-color-primary-subtle);
  border-color: var(--kulima-color-primary);
}

/* --- Variant: danger (destructive action) --- */
.kulima-btn--danger {
  background-color: var(--kulima-color-danger);
  color: var(--kulima-color-on-danger);
  border-color: transparent;
}
.kulima-btn--danger:hover {
  background-color: var(--kulima-red-700);
}
/* Destructive controls get a distinct-but-still->=3:1 focus ring (doctrine). */
.kulima-btn--danger:focus-visible {
  outline-color: var(--kulima-color-danger);
}

/* --- State: active/pressed (all variants) — a real, intentional press move --- */
.kulima-btn:active {
  transform: translateY(1px);
}

/* --- State: focus-visible — defence-in-depth over the global ring in theme --- */
.kulima-btn:focus-visible {
  outline: 3px solid var(--kulima-color-focus);
  outline-offset: 2px;
}

/* --- Sizes (keep >=44px target even at --sm via min-height) --- */
.kulima-btn--sm {
  min-height: var(--kulima-touch-target-min);
  padding: var(--kulima-space-1) var(--kulima-space-3);
  font-size: var(--kulima-font-size-xs);
}
.kulima-btn--lg {
  min-height: var(--kulima-touch-target-comfy);
  padding: var(--kulima-space-3) var(--kulima-space-6);
  font-size: var(--kulima-font-size-base);
}

/* --- Modifiers --- */
.kulima-btn--block {
  display: flex;
  width: 100%;
}
.kulima-btn--icon {
  padding: var(--kulima-space-2);
  min-width: var(--kulima-touch-target-min);
}

/* --- State: loading --- *
 * Preserve label width + height (no layout shift), block duplicate activation,
 * expose busy via aria-busy="true". Spinner glyph is drawn from currentColor so
 * it inherits the variant's text colour and themes for free. The label stays in
 * the DOM (screen readers keep the name); it is just visually hidden. */
.kulima-btn[aria-busy="true"] {
  position: relative;
  color: transparent !important; /* hide label glyphs, keep box size */
  pointer-events: none;          /* block duplicate submits */
  cursor: progress;
}
.kulima-btn[aria-busy="true"]::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1.1em;
  height: 1.1em;
  margin: -0.55em 0 0 -0.55em;
  border: 2px solid currentColor;          /* transparent via color above... */
  border-color: var(--kulima-color-on-primary); /* ...so set an explicit visible colour */
  border-right-color: transparent;
  border-radius: var(--kulima-radius-pill);
  animation: kulima-spin 0.6s linear infinite;
}
/* On light-filled variants (subtle/outline) the busy spinner needs a dark ring. */
.kulima-btn--subtle[aria-busy="true"]::after,
.kulima-btn--outline[aria-busy="true"]::after {
  border-color: var(--kulima-color-primary);
  border-right-color: transparent;
}

/* ==========================================================================
 * 2. FORM CONTROLS  (.kulima-field wrapper → label · control · help · error)
 * --------------------------------------------------------------------------
 * Molecule anatomy: Label + Control(input|select|textarea) + HelpText + Error.
 * The full state matrix per control: default · hover · focus-visible · disabled
 * · read-only · error/invalid. State is never colour-only — the error state
 * pairs the red border with an error message element and expects aria-invalid +
 * aria-describedby on the control (documented in the component library doc).
 * ========================================================================== */
.kulima-field {
  display: block;
  margin-bottom: var(--kulima-space-4);
}
.kulima-field__label {
  display: block;
  margin-bottom: var(--kulima-space-1);
  font-family: var(--kulima-font-heading);
  font-size: var(--kulima-font-size-sm);
  font-weight: var(--kulima-font-weight-semibold);
  color: var(--kulima-color-text);
}
/* Required marker — shape + text, not colour alone. */
.kulima-field__label[data-required]::after {
  content: " *";
  color: var(--kulima-color-danger);
}

.kulima-input,
.kulima-select,
.kulima-textarea {
  display: block;
  width: 100%;
  min-height: var(--kulima-touch-target-min);
  padding: var(--kulima-space-2) var(--kulima-space-3);
  font-family: var(--kulima-font-body);
  font-size: var(--kulima-font-size-base);
  line-height: var(--kulima-line-height-snug);
  color: var(--kulima-color-text);
  background-color: var(--kulima-color-surface);
  border: 1px solid var(--kulima-color-border);
  border-radius: var(--kulima-radius-sm);
  transition: border-color var(--kulima-motion-fast) var(--kulima-easing-standard),
              box-shadow var(--kulima-motion-fast) var(--kulima-easing-standard);
}
.kulima-textarea {
  min-height: calc(var(--kulima-touch-target-min) * 2);
  line-height: var(--kulima-line-height-body);
  resize: vertical;
}
/* Native select keeps a usable caret + tap height. */
.kulima-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--kulima-space-6);
  background-image: linear-gradient(45deg, transparent 50%, var(--kulima-color-text-muted) 50%),
                    linear-gradient(135deg, var(--kulima-color-text-muted) 50%, transparent 50%);
  background-position: right var(--kulima-space-4) center, right calc(var(--kulima-space-4) - 5px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* --- State: placeholder --- */
.kulima-input::placeholder,
.kulima-textarea::placeholder {
  color: var(--kulima-color-text-muted);
  opacity: 1;
}

/* --- State: hover --- */
.kulima-input:hover,
.kulima-select:hover,
.kulima-textarea:hover {
  border-color: var(--kulima-color-border-strong);
}

/* --- State: focus-visible (keyboard) + focus (also show ring on click-into a
 * field, which is expected affordance for text entry) --- */
.kulima-input:focus,
.kulima-select:focus,
.kulima-textarea:focus {
  outline: none; /* replaced by the token ring below — never bare outline:none */
  border-color: var(--kulima-color-primary);
  box-shadow: var(--kulima-shadow-focus);
}
.kulima-input:focus-visible,
.kulima-select:focus-visible,
.kulima-textarea:focus-visible {
  outline: 3px solid var(--kulima-color-focus);
  outline-offset: 1px;
}

/* --- State: disabled --- */
.kulima-input:disabled,
.kulima-select:disabled,
.kulima-textarea:disabled,
.kulima-input[aria-disabled="true"],
.kulima-select[aria-disabled="true"],
.kulima-textarea[aria-disabled="true"] {
  background-color: var(--kulima-color-surface-sunken);
  color: var(--kulima-color-text-muted);
  cursor: not-allowed;
  opacity: 0.7;
}

/* --- State: read-only (distinct from disabled: value is meaningful, borderless-ish) --- */
.kulima-input[readonly],
.kulima-textarea[readonly] {
  background-color: var(--kulima-color-surface-sunken);
  border-color: transparent;
  border-bottom-color: var(--kulima-color-border);
  cursor: default;
}

/* --- State: error/invalid (border + shadow; paired with __error text + aria-invalid) --- */
.kulima-input[aria-invalid="true"],
.kulima-select[aria-invalid="true"],
.kulima-textarea[aria-invalid="true"],
.kulima-field--error .kulima-input,
.kulima-field--error .kulima-select,
.kulima-field--error .kulima-textarea {
  border-color: var(--kulima-color-danger);
}
.kulima-input[aria-invalid="true"]:focus,
.kulima-field--error .kulima-input:focus,
.kulima-field--error .kulima-select:focus,
.kulima-field--error .kulima-textarea:focus {
  box-shadow: 0 0 0 3px var(--kulima-color-danger-subtle);
}

/* --- Help text + error message --- */
.kulima-field__help {
  display: block;
  margin-top: var(--kulima-space-1);
  font-size: var(--kulima-font-size-sm);
  color: var(--kulima-color-text-muted);
}
.kulima-field__error {
  display: flex;
  align-items: center;
  gap: var(--kulima-space-1);
  margin-top: var(--kulima-space-1);
  font-size: var(--kulima-font-size-sm);
  font-weight: var(--kulima-font-weight-semibold);
  color: var(--kulima-color-danger);
}
[data-theme="dark"] .kulima-field__error {
  color: var(--kulima-red-500);
}

/* ==========================================================================
 * 3. CARDS  (.kulima-card extended with sub-parts; base is in theme §5)
 * --------------------------------------------------------------------------
 * Organism: Card.Header / Card.Body / Card.Footer as slot-like sub-elements.
 * --interactive makes a whole card a clickable/tabbable surface with the full
 * hover/focus-visible/active matrix. Base card visuals live in kulima-theme.css.
 * ========================================================================== */
.kulima-card__header {
  margin: calc(var(--kulima-space-5) * -1) calc(var(--kulima-space-5) * -1) var(--kulima-space-4);
  padding: var(--kulima-space-4) var(--kulima-space-5);
  border-bottom: 1px solid var(--kulima-color-border);
  font-family: var(--kulima-font-heading);
  font-size: var(--kulima-font-size-md);
  font-weight: var(--kulima-font-weight-semibold);
  color: var(--kulima-color-text);
}
.kulima-card__footer {
  margin: var(--kulima-space-4) calc(var(--kulima-space-5) * -1) calc(var(--kulima-space-5) * -1);
  padding: var(--kulima-space-4) var(--kulima-space-5);
  border-top: 1px solid var(--kulima-color-border);
  display: flex;
  gap: var(--kulima-space-2);
  justify-content: flex-end;
}
.kulima-card--interactive {
  cursor: pointer;
  transition: box-shadow var(--kulima-motion-base) var(--kulima-easing-standard),
              border-color var(--kulima-motion-base) var(--kulima-easing-standard),
              transform var(--kulima-motion-base) var(--kulima-easing-standard);
}
.kulima-card--interactive:hover {
  box-shadow: var(--kulima-shadow-md);
  border-color: var(--kulima-color-border-strong);
}
.kulima-card--interactive:active {
  transform: translateY(1px);
}
.kulima-card--interactive:focus-visible {
  outline: 3px solid var(--kulima-color-focus);
  outline-offset: 2px;
}

/* ==========================================================================
 * 4. STATUS PILLS  (.kulima-pill — state by text + shape, not colour alone)
 * --------------------------------------------------------------------------
 * Distinct from the .kulima-badge in theme.css (a generic count/tag chip). A
 * pill reports a RECORD STATUS (active / pending / overdue / draft) and always
 * carries a leading shape token (__dot) plus its label text, so meaning never
 * depends on hue alone (WCAG 1.4.1). Colour reinforces; it does not encode.
 * ========================================================================== */
.kulima-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--kulima-space-2);
  padding: var(--kulima-space-1) var(--kulima-space-3);
  font-family: var(--kulima-font-heading);
  font-size: var(--kulima-font-size-xs);
  font-weight: var(--kulima-font-weight-semibold);
  line-height: 1.3;
  border-radius: var(--kulima-radius-pill);
  border: 1px solid var(--kulima-color-border);
  background-color: var(--kulima-color-surface-raised);
  color: var(--kulima-color-text);
}
.kulima-pill__dot {
  flex: 0 0 auto;
  width: 0.55em;
  height: 0.55em;
  border-radius: var(--kulima-radius-pill);
  background-color: currentColor;
}
.kulima-pill--success { color: var(--kulima-green-800);  border-color: var(--kulima-color-success); background-color: var(--kulima-color-success-subtle); }
.kulima-pill--warning { color: var(--kulima-amber-900);  border-color: var(--kulima-color-warning); background-color: var(--kulima-color-warning-subtle); }
.kulima-pill--danger  { color: var(--kulima-red-700);    border-color: var(--kulima-color-danger);  background-color: var(--kulima-color-danger-subtle); }
.kulima-pill--info    { color: var(--kulima-blue-700);   border-color: var(--kulima-color-info);    background-color: var(--kulima-color-info-subtle); }
.kulima-pill--neutral { color: var(--kulima-color-text-muted); }

/* Dark theme: subtle backgrounds are dark — lift the label + keep the dot hue. */
[data-theme="dark"] .kulima-pill--success,
[data-theme="dark"] .kulima-pill--warning,
[data-theme="dark"] .kulima-pill--danger,
[data-theme="dark"] .kulima-pill--info {
  color: var(--kulima-neutral-50);
}

/* ==========================================================================
 * 5. TABLES  (.kulima-table + .kulima-table-wrap responsive wrapper)
 * --------------------------------------------------------------------------
 * Zebra striping via surface-sunken, sticky-friendly header, row hover, and a
 * horizontal-scroll wrapper so wide data tables never break page reflow at
 * narrow widths (the sanctioned 2D-content exception, WCAG 1.4.10). The wrapper
 * complements .kulima-table-scroll in kulima-a11y.css; this one also shows a
 * subtle edge shadow cue.
 * ========================================================================== */
.kulima-table-wrap {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--kulima-color-border);
  border-radius: var(--kulima-radius-md);
}
.kulima-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--kulima-font-body);
  font-size: var(--kulima-font-size-sm);
  color: var(--kulima-color-text);
  background-color: var(--kulima-color-surface);
}
.kulima-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--kulima-z-base);
  text-align: left;
  padding: var(--kulima-space-3) var(--kulima-space-4);
  font-family: var(--kulima-font-heading);
  font-size: var(--kulima-font-size-xs);
  font-weight: var(--kulima-font-weight-bold);
  letter-spacing: var(--kulima-tracking-label);
  text-transform: uppercase;
  color: var(--kulima-color-text-muted);
  background-color: var(--kulima-color-surface-raised);
  border-bottom: 2px solid var(--kulima-color-border);
}
.kulima-table tbody td {
  padding: var(--kulima-space-3) var(--kulima-space-4);
  border-bottom: 1px solid var(--kulima-color-border);
  vertical-align: middle;
}
/* Zebra */
.kulima-table tbody tr:nth-child(even) {
  background-color: var(--kulima-color-surface-sunken);
}
/* Row hover (pointer affordance for interactive rows) */
.kulima-table--hover tbody tr:hover {
  background-color: var(--kulima-color-primary-subtle);
}
/* Numeric alignment helper */
.kulima-table .kulima-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
 * 6. ALERTS  (.kulima-alert — inline, persistent, in-flow page messages)
 * --------------------------------------------------------------------------
 * Role banner. State by icon + heading text + colour (not colour alone). Four
 * intents reuse the semantic roles so dark theme is automatic. Dismiss button
 * is a real >=44px target.
 * ========================================================================== */
.kulima-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--kulima-space-3);
  padding: var(--kulima-space-4);
  border: 1px solid transparent;
  border-radius: var(--kulima-radius-md);
  font-family: var(--kulima-font-body);
  font-size: var(--kulima-font-size-sm);
  color: var(--kulima-color-text);
}
.kulima-alert__icon { flex: 0 0 auto; font-size: 1.2em; line-height: 1.4; }
.kulima-alert__body { flex: 1 1 auto; }
.kulima-alert__title {
  font-family: var(--kulima-font-heading);
  font-weight: var(--kulima-font-weight-bold);
  margin-bottom: var(--kulima-space-1);
}
.kulima-alert__close {
  flex: 0 0 auto;
  min-width: var(--kulima-touch-target-min);
  min-height: var(--kulima-touch-target-min);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  color: inherit;
  cursor: pointer;
  border-radius: var(--kulima-radius-sm);
  opacity: 0.7;
}
.kulima-alert__close:hover { opacity: 1; }
.kulima-alert__close:focus-visible { outline: 3px solid var(--kulima-color-focus); outline-offset: 2px; }

.kulima-alert--success { background: var(--kulima-color-success-subtle); border-color: var(--kulima-color-success); }
.kulima-alert--warning { background: var(--kulima-color-warning-subtle); border-color: var(--kulima-color-warning); }
.kulima-alert--danger  { background: var(--kulima-color-danger-subtle);  border-color: var(--kulima-color-danger); }
.kulima-alert--info    { background: var(--kulima-color-info-subtle);    border-color: var(--kulima-color-info); }

[data-theme="dark"] .kulima-alert--success,
[data-theme="dark"] .kulima-alert--warning,
[data-theme="dark"] .kulima-alert--danger,
[data-theme="dark"] .kulima-alert--info {
  color: var(--kulima-neutral-50);
}

/* ==========================================================================
 * 7. TOASTS  (.kulima-toast / .kulima-toast-region — transient notifications)
 * --------------------------------------------------------------------------
 * The region is a fixed aria-live container (set role="status"/"alert" +
 * aria-live in markup); each toast slides in, then JS removes it. State by
 * icon + text + colour. Entry animation respects reduced-motion (guard below).
 * ========================================================================== */
.kulima-toast-region {
  position: fixed;
  top: var(--kulima-space-4);
  right: var(--kulima-space-4);
  left: auto;
  z-index: var(--kulima-z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--kulima-space-2);
  max-width: min(92vw, 24rem);
  pointer-events: none; /* only toasts themselves are interactive */
}
.kulima-toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--kulima-space-2);
  padding: var(--kulima-space-3) var(--kulima-space-4);
  background-color: var(--kulima-color-surface-raised);
  color: var(--kulima-color-text);
  border: 1px solid var(--kulima-color-border);
  border-radius: var(--kulima-radius-md);
  box-shadow: var(--kulima-shadow-lg);
  font-size: var(--kulima-font-size-sm);
  animation: kulima-toast-in var(--kulima-motion-base) var(--kulima-easing-standard);
}
.kulima-toast--success { border-color: var(--kulima-color-success); }
.kulima-toast--warning { border-color: var(--kulima-color-warning); }
.kulima-toast--danger  { border-color: var(--kulima-color-danger); }
.kulima-toast--info    { border-color: var(--kulima-color-info); }

/* ==========================================================================
 * 8. MODALS  (classes only — JS/markup wiring is out of scope for this file)
 * --------------------------------------------------------------------------
 * .kulima-modal-backdrop (overlay) + .kulima-modal (dialog) + header/body/
 * footer parts. Visibility is driven by a page's own JS toggling [hidden] or a
 * .is-open class; this file only supplies the styling + the focus ring on the
 * close control. Dialog markup must set role="dialog" aria-modal="true" and
 * manage focus trap/return (documented in the component library doc).
 * ========================================================================== */
.kulima-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--kulima-z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--kulima-space-4);
  background-color: rgba(21, 26, 22, 0.55); /* neutral-950 @ 55% scrim */
}
.kulima-modal {
  width: 100%;
  max-width: 32rem;
  max-height: 90vh;
  overflow-y: auto;
  background-color: var(--kulima-color-surface);
  color: var(--kulima-color-text);
  border: 1px solid var(--kulima-color-border);
  border-radius: var(--kulima-radius-xl);
  box-shadow: var(--kulima-shadow-lg);
}
.kulima-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--kulima-space-3);
  padding: var(--kulima-space-5);
  border-bottom: 1px solid var(--kulima-color-border);
  font-family: var(--kulima-font-heading);
  font-size: var(--kulima-font-size-lg);
  font-weight: var(--kulima-font-weight-bold);
}
.kulima-modal__body { padding: var(--kulima-space-5); }
.kulima-modal__footer {
  display: flex;
  gap: var(--kulima-space-2);
  justify-content: flex-end;
  padding: var(--kulima-space-4) var(--kulima-space-5);
  border-top: 1px solid var(--kulima-color-border);
}
.kulima-modal__close {
  min-width: var(--kulima-touch-target-min);
  min-height: var(--kulima-touch-target-min);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  color: var(--kulima-color-text-muted);
  cursor: pointer;
  border-radius: var(--kulima-radius-sm);
}
.kulima-modal__close:hover { color: var(--kulima-color-text); }
.kulima-modal__close:focus-visible { outline: 3px solid var(--kulima-color-focus); outline-offset: 2px; }

/* ==========================================================================
 * 9. NAV / TABS  (.kulima-tabs → .kulima-tab, with selected + states)
 * --------------------------------------------------------------------------
 * Tab list. Selected tab carries aria-selected="true" AND a shape cue (the
 * underline bar) so selection is not colour-only. Full matrix: default · hover
 * · focus-visible · active · selected · disabled.
 * ========================================================================== */
.kulima-tabs {
  display: flex;
  gap: var(--kulima-space-1);
  border-bottom: 1px solid var(--kulima-color-border);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.kulima-tab {
  flex: 0 0 auto;
  min-height: var(--kulima-touch-target-min);
  display: inline-flex;
  align-items: center;
  gap: var(--kulima-space-2);
  padding: var(--kulima-space-2) var(--kulima-space-4);
  font-family: var(--kulima-font-heading);
  font-size: var(--kulima-font-size-sm);
  font-weight: var(--kulima-font-weight-semibold);
  color: var(--kulima-color-text-muted);
  background: transparent;
  border: 0;
  border-bottom: 3px solid transparent; /* reserved so selection never shifts layout */
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--kulima-motion-fast) var(--kulima-easing-standard),
              border-color var(--kulima-motion-fast) var(--kulima-easing-standard);
}
.kulima-tab:hover { color: var(--kulima-color-text); }
.kulima-tab:active { color: var(--kulima-color-primary-hover); }
.kulima-tab:focus-visible { outline: 3px solid var(--kulima-color-focus); outline-offset: -3px; }
.kulima-tab[aria-selected="true"] {
  color: var(--kulima-color-primary);
  border-bottom-color: var(--kulima-color-primary); /* the shape cue */
}
.kulima-tab:disabled,
.kulima-tab[aria-disabled="true"] {
  color: var(--kulima-color-text-muted);
  opacity: 0.5;
  cursor: not-allowed;
}

/* ==========================================================================
 * 10. EMPTY STATE  (.kulima-empty — zero-data blank slate with a next action)
 * --------------------------------------------------------------------------
 * Content-level state: an icon/illustration slot, a heading, an explanatory
 * line, and a primary action. Never a dead end — it always offers the next step.
 * ========================================================================== */
.kulima-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--kulima-space-3);
  padding: var(--kulima-space-8) var(--kulima-space-4);
  text-align: center;
  color: var(--kulima-color-text-muted);
}
.kulima-empty__icon {
  font-size: var(--kulima-font-size-2xl);
  color: var(--kulima-color-border-strong);
  line-height: 1;
}
.kulima-empty__title {
  font-family: var(--kulima-font-heading);
  font-size: var(--kulima-font-size-md);
  font-weight: var(--kulima-font-weight-bold);
  color: var(--kulima-color-text);
}
.kulima-empty__text {
  max-width: 42ch;
  font-size: var(--kulima-font-size-sm);
}

/* ==========================================================================
 * 11. SPINNER  (.kulima-spinner — standalone loading indicator)
 * --------------------------------------------------------------------------
 * Pair with role="status" + a visually-hidden "Loading…" label in markup so it
 * is announced (WCAG 4.1.3). Track is built from currentColor so it inherits
 * context colour and themes for free — the single sanctioned non-token colour
 * expression in this file, and it is derived, not a literal palette value.
 * ========================================================================== */
.kulima-spinner {
  display: inline-block;
  width: 1.5rem;
  height: 1.5rem;
  border: 3px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--kulima-radius-pill);
  color: var(--kulima-color-primary);
  opacity: 0.85;
  animation: kulima-spin 0.6s linear infinite;
}
.kulima-spinner--sm { width: 1rem; height: 1rem; border-width: 2px; }
.kulima-spinner--lg { width: 2.5rem; height: 2.5rem; border-width: 4px; }
.kulima-spinner--center {
  display: block;
  margin-inline: auto;
}

/* ==========================================================================
 * 12. KEYFRAMES + MOTION GUARD
 * --------------------------------------------------------------------------
 * kulima-theme.css §9 already collapses transitions for any .kulima-* element
 * under prefers-reduced-motion; this re-asserts the floor for the KEYFRAME
 * animations defined here (spinner / busy button / toast entry), where a
 * transition-duration override alone is not enough.
 * ========================================================================== */
@keyframes kulima-spin {
  to { transform: rotate(360deg); }
}
@keyframes kulima-toast-in {
  from { opacity: 0; transform: translateY(-0.5rem); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .kulima-spinner,
  .kulima-btn[aria-busy="true"]::after {
    animation-duration: 1.2s; /* keep a slow, non-vestibular spin so busy is still legible */
  }
  .kulima-toast {
    animation: none; /* appear instantly rather than slide */
  }
}

/* ==========================================================================
 * LEGACY THEME GLYPHS → BOOTSTRAP ICONS (D-13)
 * --------------------------------------------------------------------------
 * The Apex theme CSS (components.css, bootstrap-extended.css) draws a few
 * pseudo-element glyphs from the Feather font, which is no longer loaded.
 * These rules redraw them from Bootstrap Icons so no tofu box appears.
 * ========================================================================== */
table.dataTable thead th[class*="sorting"]:not(.sorting_disabled)::before,
table.dataTable thead th[class*="sorting"]:not(.sorting_disabled)::after,
.collapse-icon a[data-toggle="collapse"]::before,
.collapse-icon a[data-toggle="collapse"]::after,
.picker__button--close::before,
.app-sidebar .navigation li.has-sub > a::after {
  font-family: "bootstrap-icons";
}
table.dataTable thead th[class*="sorting"]:not(.sorting_disabled)::before { content: "\f148"; } /* arrow-up */
table.dataTable thead th[class*="sorting"]:not(.sorting_disabled)::after { content: "\f128"; } /* arrow-down */
.collapse-icon a[data-toggle="collapse"]::after { content: "\f63b"; } /* dash-lg */
.collapse-icon a[data-toggle="collapse"]::before { content: "\f64d"; } /* plus-lg */
.picker__button--close::before { content: "\f659"; } /* x-lg */
.app-sidebar .navigation li.has-sub > a::after { content: "\f285"; } /* chevron-right */
