/* ═══════════════════════════════════════════════════════════════════════
   40-states.css — STATE MODIFIER CLASSES
   ═══════════════════════════════════════════════════════════════════════
   Universal modifiers applied via JS when an element transitions state.
   Prefix: .is-* (following BEM state convention).

   Example:
     <button class="btn btn-primary is-loading">Save</button>
     <li class="nav-link is-active">Dashboard</li>
     <input class="input is-error">
   ═══════════════════════════════════════════════════════════════════════ */

@layer states {

/* ─── Visibility ───────────────────────────────────────────────────── */
.is-hidden       { display: none !important; }
.is-invisible    { visibility: hidden; }
.is-visible      { display: block; }
.is-sr-only      { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* ─── g24.bar — universal progress / score / sparkline bar ────────────
   Reads width from CSS custom property (set by g24.hydrateBars or via JS).
   Replaces 40+ inline style="width:Xpct%" innerHTML patterns
   (Bible §11 anti-pattern #1 → killed by g24.bar helper, 17 May 2026).
─────────────────────────────────────────────────────────────────── */
.g24-bar {
  position: relative;
  width: 100%;
  height: 8px;
  background: var(--color-surface-2, #e5e7eb);
  border-radius: 999px;
  overflow: hidden;
}
.g24-bar-fill {
  width: var(--w, 0%);
  height: 100%;
  background: var(--color-brand-solid, #4f46e5);
  border-radius: 999px;
  transition: width var(--motion-duration-1, .25s) var(--motion-ease-out, ease-out);
}
.g24-bar-good .g24-bar-fill { background: var(--color-success-solid, #16a34a); }
.g24-bar-warn .g24-bar-fill { background: var(--color-warning-solid, #f59e0b); }
.g24-bar-bad  .g24-bar-fill { background: var(--color-danger-solid,  #dc2626); }
.g24-bar-label {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600;
  color: var(--color-text-strong, #111827);
}

/* ─── Active / selected / current ──────────────────────────────────── */
.is-active       { /* consumed per-component; see .btn, .nav-link, .tab-item */ }
.is-current      { font-weight: var(--weight-semibold); }
.is-selected     { background: var(--color-brand-soft); color: var(--color-text-brand); }

/* ─── Disabled ─────────────────────────────────────────────────────── */
.is-disabled {
  opacity: var(--opacity-disabled);
  cursor: not-allowed;
  pointer-events: none;
}

/* ─── Dimmed (visual fade without disabling clicks) ───────────────────
   Use when a row/item should fade for context (e.g., dismissed, marked,
   already-handled) but remain interactive. Reimbursement.js Wave 6
   pattern; promoted to universal Wave 7 (Msir review pass).

   Difference vs .is-disabled: .is-dimmed keeps pointer-events; cursor
   unchanged; no transparent text. Just opacity fade.
─────────────────────────────────────────────────────────────────── */
.is-dimmed {
  opacity: var(--opacity-dimmed, 0.4);
  transition: opacity var(--motion-duration-1, .2s) var(--motion-ease-out, ease-out);
}

/* ─── Loading (spinner friendly; works with ::after) ───────────────── */
.is-loading {
  position: relative;
  pointer-events: none;
  color: transparent !important;
}
.is-loading::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 14px; height: 14px;
  margin: -7px 0 0 -7px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 600ms linear infinite;
  color: var(--color-text-muted);
}

/* ─── Validation states ────────────────────────────────────────────── */
.is-error, .input.is-error, .select.is-error, .textarea.is-error {
  border-color: var(--color-danger-border);
  color: var(--color-danger-fg);
}
.is-error:focus { box-shadow: 0 0 0 var(--color-focus-ring-width) var(--color-danger-bg); }

.is-success, .input.is-success {
  border-color: var(--color-success-border);
}
.is-warning, .input.is-warning {
  border-color: var(--color-warn-border);
}

/* ─── Focused (for programmatic focus rings) ───────────────────────── */
.is-focused       { box-shadow: var(--shadow-focus); outline: none; }

/* ─── Collapsed / expanded (accordion etc.) ────────────────────────── */
.is-collapsed     { max-height: 0; overflow: hidden; opacity: 0; }
.is-expanded      { max-height: 9999px; opacity: 1; transition: max-height var(--transition-slow) var(--ease-out), opacity var(--transition-base) var(--ease-out); }

/* ─── Sticky element (in-scroll header etc.) ───────────────────────── */
.is-sticky {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--color-surface-card);
}

/* ─── Pressed / active (for tappable rows) ─────────────────────────── */
.is-pressed       { background: var(--color-surface-active); transform: translateY(1px); }

/* ─── Empty state ──────────────────────────────────────────────────── */
.is-empty {
  padding: var(--space-8);
  text-align: center;
  color: var(--color-text-muted);
  font-style: italic;
}

/* ─── Read-only ────────────────────────────────────────────────────── */
.is-readonly {
  opacity: 0.85;
  background: var(--color-surface-disabled);
  cursor: text;
}

/* ─── Dragging (future drag-n-drop) ────────────────────────────────── */
.is-dragging      { opacity: 0.55; cursor: grabbing; }
.is-drop-target   { outline: 2px dashed var(--color-brand); outline-offset: 4px; }


/* ─── Loading Skeleton (UX-1 · 9 May 2026) ─────────────────────────── */
/* Usage:
     <div class="g24-skeleton g24-skeleton-text"></div>      ← single line
     <div class="g24-skeleton g24-skeleton-text short"></div> ← 60% width
     <div class="g24-skeleton g24-skeleton-avatar"></div>     ← circle
     <div class="g24-skeleton g24-skeleton-card"></div>       ← card block
     <div class="g24-skeleton-row"></div>                     ← table row
   JS API: g24.skeleton.show(container, {rows, type})
           g24.skeleton.hide(container)                                   */
.g24-skeleton {
  background: linear-gradient(90deg,
    var(--color-surface-hover) 0%,
    var(--color-bg-alt) 40%,
    var(--color-surface-hover) 100%);
  background-size: 800px 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: var(--radius-md);
  display: block;
}
html[data-ui-reduce-motion="1"] .g24-skeleton { animation: none; opacity: .5; }

.g24-skeleton-text       { height: 14px; width: 100%; margin-bottom: var(--space-2); }
.g24-skeleton-text.short { width: 60%; }
.g24-skeleton-text.xs    { height: 10px; width: 40%; }
.g24-skeleton-avatar     { width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0; }
.g24-skeleton-card       { height: 80px; width: 100%; }
.g24-skeleton-btn        { height: 32px; width: 100px; border-radius: var(--radius-md); }

/* skeleton row — mimics one table row */
.g24-skeleton-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--card-border, rgba(255,255,255,.06));
}
.g24-skeleton-row .g24-skeleton-text { margin: 0; flex: 1; }
.g24-skeleton-row .g24-skeleton-text.short { flex: 0 0 30%; }
.g24-skeleton-row .g24-skeleton-text.xs    { flex: 0 0 18%; }

/* skeleton section wrapper — use to wrap skeleton inside a card */
.g24-skeleton-wrap {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* skeleton text stack — vertical stack used by g24.skeleton(type:'text') */
.g24-skeleton-text-stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* ─── Empty State Component (UX-2 · 9 May 2026) ────────────────────── */
/* Replaces the minimal .is-empty class with a full card component.
   Usage:
     <div class="g24-empty-state">
       <div class="g24-empty-icon">📭</div>
       <div class="g24-empty-title">No results found</div>
       <div class="g24-empty-msg">Try adjusting your filters.</div>
       <button class="btn btn-primary g24-empty-cta">Run Scan</button>
     </div>
   JS shortcut: g24.emptyState(containerEl, {icon, title, message, cta, onCta}) */
.g24-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--space-10) var(--space-6);
  gap: var(--space-2);
  color: var(--color-text-muted);
  min-height: 180px;
}
.g24-empty-icon {
  font-size: 2.4rem;
  line-height: 1;
  margin-bottom: var(--space-1);
  opacity: .85;
}
.g24-empty-title {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--color-text-body);
  margin: 0;
}
.g24-empty-msg {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  max-width: 340px;
  line-height: var(--leading-relaxed);
  margin: 0;
}
.g24-empty-cta {
  margin-top: var(--space-3);
}

/* compact variant — for inside table cells / small panels */
.g24-empty-state.compact {
  padding: var(--space-5) var(--space-4);
  min-height: 100px;
}
.g24-empty-state.compact .g24-empty-icon { font-size: 1.6rem; }
.g24-empty-state.compact .g24-empty-title { font-size: var(--text-base); }

/* ─── .status-msg — universal save/result status flash ───────────────
   Replaces 30+ `el.style.color = 'var(--tone-…-fg)'` JS sites across
   /settings, /pending-actions, /clients, etc. (UX Bible §11 #23 banned).
   Apply tone via `.is-success` / `.is-error` / `.is-warn` / `.is-muted`.
   Reset by removing all four modifiers.

   Example:
     <span class="status-msg is-success">Saved ✓</span>
     el.classList.add('status-msg', 'is-success');
─────────────────────────────────────────────────────────────────── */
.status-msg              { font-size: var(--text-sm); transition: color var(--motion-duration-1, .15s) var(--motion-ease-out, ease-out); }
.status-msg.is-success   { color: var(--color-success-fg, var(--tone-success-fg)); }
.status-msg.is-error     { color: var(--color-danger-fg,  var(--tone-danger-fg)); }
.status-msg.is-warn      { color: var(--color-warn-fg,    var(--tone-warn-fg)); }
.status-msg.is-muted     { color: var(--color-text-muted); }

/* ─── .is-success-flash — transient success colour pulse ─────────────
   Use on a button or chip to briefly indicate "Copied!" / "Saved!" /
   "Sent!" — JS adds the class for ~1.2s then removes.
   Pairs with btn/button base; respects reduce-motion.

   Example:
     btn.classList.add('is-success-flash');
     setTimeout(() => btn.classList.remove('is-success-flash'), 1200);
─────────────────────────────────────────────────────────────────── */
.is-success-flash        { color: var(--color-success-fg, var(--tone-success-fg)) !important; transition: color var(--motion-duration-1, .2s) var(--motion-ease-out, ease-out); }

/* ─── .is-row-selected — generic selected-row tint ─────────────────────
   Universal selected-row indicator for admin tables (Feature Flags bulk
   select, Users list, Pending Actions queue, etc). Replaces page-prefixed
   variants like `.settings-ff-row.is-selected`.

   Example:
     <tr class="is-row-selected">…</tr>
─────────────────────────────────────────────────────────────────── */
.is-row-selected         { background: var(--color-brand-soft, var(--color-info-bg)); outline: 1px solid var(--color-brand-solid, var(--color-info-border)); outline-offset: -1px; }

/* ─── .empty-hint — compact one-liner inline empty state (Wave 13, 18 May 2026) ───
   Dashed-border faint card for "no items yet" hints inside form/list panels.
   Use instead of full .g24-empty-state when only one line of muted text is needed. */
.empty-hint {
  padding: var(--space-4, 14px);
  text-align: center;
  color: var(--color-text-muted, var(--text-muted));
  font-size: var(--text-sm, 12px);
  font-style: italic;
  background: var(--color-surface-2, var(--input-bg, rgba(100,116,139,0.05)));
  border: 1px dashed var(--card-border, var(--color-border-default, #e5e7eb));
  border-radius: var(--radius-md, 6px);
}
.empty-hint.is-plain {
  background: transparent;
  border: none;
  padding: var(--space-2, 8px) 0;
}

/* ─── .notice — centered loading / error / success messages (Wave 13, 18 May 2026) ───
   Replaces inline `padding:14-20px;text-align:center;color:var(--tone-…-fg)`
   pattern used 20+ times across fba.js for ⏳ loading / ❌ error / ✅ success
   one-shot status messages inside dynamic <div>s. Apply tone via .is-*. */
.notice {
  padding: var(--space-4, 14px) var(--space-3, 12px);
  text-align: center;
  font-size: var(--text-sm, 13px);
  color: var(--color-text-muted, var(--text-muted));
}
.notice.is-info     { color: var(--color-info-fg, var(--brand-accent, var(--tone-info-fg))); }
.notice.is-success  { color: var(--color-success-fg, var(--tone-success-fg)); }
.notice.is-warn     { color: var(--color-warn-fg, var(--tone-warn-fg)); }
.notice.is-danger   { color: var(--color-danger-fg, var(--tone-danger-fg)); }
.notice.is-loading  { color: var(--color-info-fg, var(--brand-accent, var(--tone-info-fg))); }
.notice.is-comfortable { padding: var(--space-5, 20px); }

/* ─── .pre-output — small monospace output block (Wave 13, 18 May 2026) ───
   Used for task subprocess stdout. Tokenised colours/sizes; scroll on overflow. */
.pre-output {
  margin-top: var(--space-2, 8px);
  font-size: var(--text-xs, 11px);
  color: var(--color-text-muted, var(--text-muted));
  max-height: 200px;
  overflow: auto;
  background: var(--input-bg, var(--color-surface-2, #f3f4f6));
  padding: var(--space-2, 8px);
  border-radius: var(--radius-sm, 4px);
}

} /* @layer states */

/* ═══════════════════════════════════════════════════════════════════════
   Universal [hidden] guard (Wave 13-C · 18 May 2026)
   ═══════════════════════════════════════════════════════════════════════
   The HTML5 spec contract says any element with the `hidden` attribute MUST
   render as display:none. Component rules like `.g24-drawer { display:flex }`
   or `.modal { display:grid }` in per-page CSS silently break that contract —
   element stays visible even when JS sets `el.hidden = true`.

   Real bugs caught by this class of override:
     · #ceDetailDrawer stuck-open ghost panel on /home (18 May 2026)
     · .alert[hidden] showing when toast dismissed (Wave 7-N)
     · fba.js Bulk Edit Grid error banner stuck visible (Wave R7-D era)

   CRITICAL CASCADE NOTE: this rule is INTENTIONALLY OUTSIDE the @layer states
   block above. Rules inside a @layer LOSE to rules outside any layer, even
   when the layered rule has !important. Per-page blueprint CSS is unlayered,
   so a layered [hidden] !important wouldn't beat `.g24-drawer { display:flex }`.
   Keep this rule at file-bottom, outside @layer. DO NOT move it back inside.

   The `!important` IS justified — [hidden] is an HTML spec contract.
   Targeted `.ClassName[hidden] { display:none !important }` rules in
   component files remain (for readability + intent docs); this universal
   rule is the sledgehammer net beneath them.
   ═══════════════════════════════════════════════════════════════════════ */
[hidden] { display: none !important; }
