/*
 * Plus Jakarta Sans is the brand typeface defined in the design handoff
 * (release `design-handoff-2026-05-18`). Loaded from Google Fonts so we don't
 * need to ship the TTFs in the admin bundle. Weights 400/500/600/700 cover
 * the admin's typographic scale.
 */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');

/*
 * Material Symbols Outlined — the icon family used by the design handoff.
 * The font's ligature table turns icon names (e.g., "search", "check_circle")
 * into glyphs, so callsites read as `<Ms name="search"/>` instead of carrying
 * raw SVG paths. Variation axes (FILL, wght, GRAD, opsz) drive style; defaults
 * applied on the `.ms` class below match the handoff's outlined-light look.
 */
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,400..700,0..1,-50..200&display=swap');

:root {
  /* Color palette — mirrors AdminPalette from the canvas implementation */
  --qf-primary: #0F766E;
  --qf-primary-container: #CCFBF1;
  --qf-on-primary: #FFFFFF;
  --qf-on-primary-container: #134E4A;
  --qf-background: #F8FAFC;
  --qf-surface: #FFFFFF;
  --qf-surface-variant: #F1F5F9;
  --qf-on-background: #0F172A;
  --qf-on-surface: #0F172A;
  --qf-on-surface-variant: #64748B;
  --qf-outline: #E2E8F0;
  --qf-error: #B91C1C;
  --qf-on-error: #FFFFFF;
  --qf-error-container: #FEE2E2;
  --qf-on-error-container: #7F1D1D;

  /* Density tokens — mirror AdminDensity */
  --qf-space-1: 2px;
  --qf-space-2: 4px;
  --qf-space-3: 6px;
  --qf-space-4: 8px;
  --qf-space-5: 12px;
  --qf-space-6: 16px;
  --qf-space-7: 24px;
  --qf-row-height: 40px;
  --qf-input-height: 36px;
  --qf-corner-sm: 6px;
  --qf-corner-md: 10px;

  /* Typography */
  --qf-font-family: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --qf-text-body: 14px;
  --qf-text-label: 12px;
  --qf-text-title: 16px;
  --qf-text-headline: 20px;

  /*
   * ---------- Admin design-handoff tokens (--adm-*) ----------
   *
   * These tokens come from the design handoff pinned in release
   * `design-handoff-2026-05-18`. They are an additive overlay on top of the
   * existing `--qf-*` tokens; nothing in this file applies them yet. The
   * per-slice UI audit tracked in #267 will gradually adopt them on
   * individual screens. Until a screen is audited, the existing `--qf-*`
   * tokens remain its source of truth.
   *
   * Source of truth for the values: `quemfaz-admin-web-ui-design-system/
   * project/ui_kits/admin/admin.css` inside the design handoff tarball.
   */

  /* Admin density spacing (2-64px scale, tighter than mobile) */
  --adm-space-1: 2px;
  --adm-space-2: 4px;
  --adm-space-3: 6px;
  --adm-space-4: 8px;
  --adm-space-5: 12px;
  --adm-space-6: 16px;
  --adm-space-7: 20px;
  --adm-space-8: 24px;
  --adm-space-10: 32px;
  --adm-space-12: 40px;
  --adm-space-16: 64px;

  /* Admin density type — 11/12/13/14/16/18/22/28 px scale */
  --adm-text-2xs: 11px;
  --adm-text-xs: 12px;
  --adm-text-sm: 13px;
  --adm-text-md: 14px;
  --adm-text-lg: 16px;
  --adm-text-xl: 18px;
  --adm-text-2xl: 22px;
  --adm-text-3xl: 28px;

  --adm-lh-tight: 1.25;
  --adm-lh-snug: 1.4;
  --adm-lh-base: 1.5;

  /* Control / row heights */
  --adm-h-input: 32px;
  --adm-h-input-lg: 40px;
  --adm-h-button: 32px;
  --adm-h-button-lg: 36px;
  --adm-h-row: 40px;
  --adm-h-row-dense: 32px;
  --adm-h-sidebar-item: 30px;
  --adm-h-topbar: 48px;
  --adm-h-toolbar: 44px;

  /* Layout dimensions */
  --adm-sidebar-w: 248px;
  --adm-sidebar-w-collapsed: 56px;
  --adm-drawer-w: 640px;
  --adm-drawer-w-wide: 880px;
  --adm-content-max: 1440px;

  /* Surfaces */
  --adm-bg-app: var(--qf-background);
  --adm-bg-canvas: var(--qf-surface);
  --adm-bg-sidebar: #FBFCFC;
  --adm-bg-row-hover: rgba(15, 118, 110, 0.04);
  --adm-bg-row-active: rgba(15, 118, 110, 0.08);
  --adm-bg-row-selected: rgba(15, 118, 110, 0.06);
  --adm-bg-banner-sandbox: #FEF3C7;
  --adm-bg-banner-sandbox-fg: #78350F;
  --adm-bg-overlay: rgba(15, 23, 42, 0.40);

  /* Borders */
  --adm-border: var(--qf-outline);
  --adm-border-strong: #CBD5E1;
  --adm-border-focus: var(--qf-primary);

  /* Status pill colors (light backgrounds, readable foreground) */
  --adm-pill-info-bg: #DBEAFE;       --adm-pill-info-fg: #1E40AF;
  --adm-pill-warn-bg: #FEF3C7;       --adm-pill-warn-fg: #92400E;
  --adm-pill-danger-bg: #FEE2E2;     --adm-pill-danger-fg: #991B1B;
  --adm-pill-success-bg: #DCFCE7;    --adm-pill-success-fg: #166534;
  --adm-pill-neutral-bg: #F1F5F9;    --adm-pill-neutral-fg: #475569;
  --adm-pill-mod-bg: #CCFBF1;        --adm-pill-mod-fg: #115E59;
  --adm-pill-purple-bg: #EDE9FE;     --adm-pill-purple-fg: #5B21B6;

  /* Diff highlighting in destructive confirm modals */
  --adm-diff-add-bg: rgba(22, 163, 74, 0.10);
  --adm-diff-add-fg: #14532D;
  --adm-diff-remove-bg: rgba(220, 38, 38, 0.10);
  --adm-diff-remove-fg: #7F1D1D;

  /* Radii — tighter than mobile */
  --adm-radius-xs: 4px;
  --adm-radius-sm: 6px;
  --adm-radius-md: 8px;
  --adm-radius-lg: 12px;

  /* Shadows — lighter and more functional than mobile */
  --adm-shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.04);
  --adm-shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
  --adm-shadow-md: 0 4px 8px rgba(15, 23, 42, 0.06), 0 2px 4px rgba(15, 23, 42, 0.04);
  --adm-shadow-drawer: -16px 0 40px rgba(15, 23, 42, 0.12);
  --adm-shadow-modal: 0 24px 48px rgba(15, 23, 42, 0.20), 0 8px 16px rgba(15, 23, 42, 0.10);
  --adm-shadow-palette: 0 24px 64px rgba(15, 23, 42, 0.24);

  /* Keyboard hint chip */
  --adm-kbd-bg: #F8FAFA;
  --adm-kbd-fg: #475569;
  --adm-kbd-border: #CBD5E1;

  /*
   * Decorative background blobs used behind auth + error screens (handoff
   * `screens-auth.jsx` + `screens-dashboard-reports.jsx` 404/Forbidden panels).
   * Two soft blurred shapes — greenish top-left, warm peach bottom-right —
   * give brand polish to the unauthenticated/empty surfaces without affecting
   * the rest of the chrome.
   *
   * Tokenized against the existing brand palette so dark-mode is a single-pass
   * override (OQ-1 decided: tokenize, not pin design hex codes). The
   * `--qf-decoration-peach-*` tokens are new; if a future palette refactor
   * promotes "peach" to a first-class brand color, these become aliases.
   */
  --qf-decoration-peach-200: #FED7AA;
  --qf-decoration-peach-300: #FDBA74;
  --qf-decoration-blob-1: var(--qf-primary-container);
  --qf-decoration-blob-2: var(--qf-decoration-peach-200);
}

/* ============ QuemFaz Q brand mark (A.2 custom-glyph half) ============ */
/*
 * Square with rounded corners, primary fill, white centered "Q". Sized by
 * the inline `width`/`height`/`font-size` set in the `QBadge` composable
 * so multiple placements (auth card top, sidebar header) share styling.
 *
 * Display logic: `inline-flex` with both axes centered keeps the Q glyph
 * optically centered inside the square at any size.
 */
.qf-q-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--qf-primary);
  color: var(--qf-on-primary);
  font-family: var(--qf-font-family);
  font-weight: 700;
  border-radius: var(--adm-radius-sm);
  line-height: 1;
  user-select: none;
}

/* ============ Material Symbols glyph atom (A.2) ============ */
/*
 * Applied to `<span class="ms">name</span>`. The span's text content is the
 * glyph ligature; the font does the rendering. Defaults match the handoff's
 * outlined-light treatment (wght 400, no fill). The `Ms` composable overrides
 * `font-size` and `font-variation-settings` per callsite when the defaults
 * don't fit.
 *
 * `vertical-align: middle` keeps inline icons centered with adjacent text.
 * `user-select: none` prevents accidental selection when the operator drags
 * across a row.
 */
.ms {
  font-family: 'Material Symbols Outlined', system-ui, sans-serif;
  font-weight: normal;
  font-style: normal;
  font-size: 16px;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-feature-settings: 'liga';
  font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
  vertical-align: middle;
  user-select: none;
}

/* Reset */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; min-height: 100vh; }
body {
  font-family: var(--qf-font-family);
  font-size: var(--qf-text-body);
  line-height: 1.5;
  color: var(--qf-on-background);
  background-color: var(--qf-background);
  -webkit-font-smoothing: antialiased;
}

/* Mount point fills the viewport */
#root {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Phase B+ adds component-specific rules below. */

/* ============ Buttons (admin density per #267 handoff) ============ */
.qf-btn {
  font-family: inherit;
  font-size: var(--adm-text-sm);
  font-weight: 500;
  min-height: var(--adm-h-button);
  padding: 0 var(--adm-space-6);
  border-radius: var(--adm-radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--adm-space-2);
  text-decoration: none;
  transition: background-color 120ms, border-color 120ms, color 120ms;
}
.qf-btn:disabled { cursor: not-allowed; opacity: 0.5; }
.qf-btn-small { min-height: 24px; padding: 0 var(--adm-space-4); font-size: var(--adm-text-xs); }

/*
 * Icon-only buttons (label="" + leadingIcon or trailingIcon set, A.3).
 * Tighter padding so the glyph sits in a square hit-target rather than
 * a wide pill. Used by close-`×` affordances, refresh icon-only triggers,
 * and the upcoming copy-icon button (A.5).
 */
.qf-btn-icon-only { padding: 0 var(--adm-space-3); aspect-ratio: 1; min-width: var(--adm-h-button); }
.qf-btn-small.qf-btn-icon-only { min-width: 24px; padding: 0 var(--adm-space-2); }

.qf-btn-primary { background: var(--qf-primary); color: var(--qf-on-primary); }
.qf-btn-primary:hover:not(:disabled) { filter: brightness(0.95); }

.qf-btn-secondary { background: var(--adm-bg-canvas); color: var(--qf-primary); border-color: var(--adm-border); }
.qf-btn-secondary:hover:not(:disabled) { background: var(--qf-surface-variant); }

.qf-btn-tertiary { background: var(--qf-primary-container); color: var(--qf-on-primary-container); }
.qf-btn-tertiary:hover:not(:disabled) { filter: brightness(0.95); }

.qf-btn-danger { background: var(--qf-error); color: var(--qf-on-error); }
.qf-btn-danger:hover:not(:disabled) { filter: brightness(0.95); }

.qf-btn-link { background: transparent; color: var(--qf-primary); padding: 0 var(--adm-space-3); min-height: auto; border: none; }
.qf-btn-link:hover:not(:disabled) { text-decoration: underline; }

.qf-spinner {
  display: inline-block;
  width: 14px; height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: qf-spin 0.7s linear infinite;
}
@keyframes qf-spin { to { transform: rotate(360deg); } }

/* ============ Inputs (admin density) ============ */
.qf-input {
  font-family: inherit;
  font-size: var(--adm-text-sm);
  height: var(--adm-h-input);
  padding: 0 var(--adm-space-4);
  border-radius: var(--adm-radius-sm);
  border: 1px solid var(--adm-border);
  background: var(--adm-bg-canvas);
  color: var(--qf-on-surface);
  outline: none;
  transition: border-color 120ms, box-shadow 120ms;
  width: 100%;
}
.qf-input:focus { border-color: var(--adm-border-focus); box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.12); }
.qf-input:disabled { background: var(--qf-surface-variant); color: var(--qf-on-surface-variant); cursor: not-allowed; }
.qf-input-error { border-color: var(--qf-error); }

/*
 * Input with leading or trailing icon slots (A.4 / OQ-2). The wrapper sets up
 * `position: relative` so the icons can sit absolutely positioned over the
 * input edges. The input itself is padded on whichever edge has an icon so
 * the text never collides with the glyph.
 *
 * Pointer-events convention:
 *  - Leading icon = decoration. `pointer-events: none` so clicks fall through
 *    to the input (focusing it). Color is muted (--qf-on-surface-variant).
 *  - Trailing icon = action. `pointer-events: auto`. The composable mounted
 *    inside the slot (typically a Btn) handles its own click target.
 */
.qf-input-wrapper { position: relative; width: 100%; display: inline-block; }
.qf-input-leading-icon {
  position: absolute;
  left: var(--adm-space-4);
  top: 50%;
  transform: translateY(-50%);
  color: var(--qf-on-surface-variant);
  pointer-events: none;
  display: inline-flex;
}
.qf-input-trailing-icon {
  position: absolute;
  right: var(--adm-space-3);
  top: 50%;
  transform: translateY(-50%);
  pointer-events: auto;
  display: inline-flex;
  color: var(--qf-on-surface-variant);
}
.qf-input-with-leading { padding-left: 32px; }
.qf-input-with-trailing { padding-right: 32px; }

/* OTP keeps a larger control height — accessibility on the auth flow. */
.qf-otp-input { letter-spacing: 8px; text-align: center; font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; font-size: 18px; height: var(--adm-h-input-lg); }

/* ============ Field (label + input + error) — admin density ============ */
.qf-field { display: flex; flex-direction: column; gap: var(--adm-space-2); padding: var(--adm-space-3) 0; width: 100%; }
.qf-field-label { font-size: var(--adm-text-xs); font-weight: 500; color: var(--qf-on-surface); }
.qf-field-error { font-size: var(--adm-text-xs); color: var(--qf-error); margin-top: var(--adm-space-2); }

/* ============ Inline error banner — admin density ============ */
.qf-inline-error {
  background: var(--qf-error-container);
  color: var(--qf-on-error-container);
  border-radius: var(--adm-radius-sm);
  padding: var(--adm-space-4) var(--adm-space-5);
}
.qf-inline-error-message { font-size: var(--adm-text-sm); }
.qf-inline-error-correlation {
  font-size: var(--adm-text-xs);
  margin-top: var(--adm-space-2);
  display: flex;
  align-items: center;
  gap: var(--adm-space-2);
  font-variant-numeric: tabular-nums;
}

/* ============ Inline warning banner — for non-fatal "be aware" copy ============ */
/* Same shape as .qf-inline-error so layout assumptions hold; distinct tint   */
/* so the operator scans it as a heads-up, not a hard failure.               */
.qf-inline-warning {
  background: var(--adm-warning-container, #fff4e0);
  color: var(--adm-on-warning-container, #6b3a00);
  border-radius: var(--adm-radius-sm);
  padding: var(--adm-space-4) var(--adm-space-5);
  font-size: var(--adm-text-sm);
  margin-bottom: var(--adm-space-3);
}

/* ============ Pill (admin pill tokens per handoff) ============ */
.qf-pill {
  display: inline-block;
  font-size: var(--adm-text-2xs);
  font-weight: 500;
  padding: 2px var(--adm-space-3);
  border-radius: var(--adm-radius-xs);
  line-height: var(--adm-lh-tight);
}
.qf-pill-default  { background: var(--adm-pill-neutral-bg); color: var(--adm-pill-neutral-fg); }
.qf-pill-info     { background: var(--adm-pill-info-bg);    color: var(--adm-pill-info-fg); }
.qf-pill-success  { background: var(--adm-pill-success-bg); color: var(--adm-pill-success-fg); }
.qf-pill-warning  { background: var(--adm-pill-warn-bg);    color: var(--adm-pill-warn-fg); }
.qf-pill-danger   { background: var(--adm-pill-danger-bg);  color: var(--adm-pill-danger-fg); }
.qf-pill-pending  { background: var(--adm-pill-purple-bg);  color: var(--adm-pill-purple-fg); }
.qf-pill-neutral  { background: #F1F5F9; color: #334155; }

/* ============ Copyable text ============ */
.qf-copyable {
  user-select: text;
  cursor: text;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  background: var(--qf-surface-variant);
  padding: 0 var(--qf-space-2);
  border-radius: var(--qf-corner-sm);
}

/*
 * Copy-button success state (A.5 / OQ-3). When the operator clicks the
 * icon-only "Copiar" button, the glyph swaps to a green `check` for ~2s.
 * The `.qf-copy-success` modifier tints the icon green; the icon swap
 * itself happens in CopyableText.kt by switching the glyph name.
 *
 * The button also gets `.qf-copy-btn` unconditionally so it can pick up
 * an `aspect-ratio: 1` square treatment from `.qf-btn-icon-only` (Btn
 * already applies that when label is blank).
 */
.qf-copy-btn .ms { color: var(--qf-on-surface-variant); }
.qf-copy-success .ms { color: #166534; /* mirrors --adm-pill-success-fg */ }

/* ============ HeroIcon (A.8) ============ */
/*
 * Tinted rounded-square holding a single Material Symbols glyph. Tones
 * pull background + foreground from the same admin pill tokens so a
 * future palette shift uniformly hits HeroIcon + Pill (consistent
 * meaning of "info / warn / danger" across the UI).
 */
.qf-hero-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--adm-radius-lg);
  flex-shrink: 0;
}
.qf-hero-icon--info { background: var(--adm-pill-info-bg); color: var(--adm-pill-info-fg); }
.qf-hero-icon--warn { background: var(--adm-pill-warn-bg); color: var(--adm-pill-warn-fg); }
.qf-hero-icon--danger { background: var(--adm-pill-danger-bg); color: var(--adm-pill-danger-fg); }

.qf-hero-icon--sm { width: 40px; height: 40px; border-radius: var(--adm-radius-md); }
.qf-hero-icon--lg { width: 96px; height: 96px; }

/* ============ AuthBrand / AuthShell ============ */
/*
 * AuthBrand structure (A.7): Q badge on the left, wordmark + "Admin"
 * subtitle stacked on the right. Two size variants:
 *  - `--lg` (auth card) — 28px badge, 18px wordmark, 12px subtitle.
 *  - `--sm` (sidebar) — 24px badge, 14px wordmark, 11px subtitle.
 *
 * Both placements share the same DOM structure so a future visual
 * refresh hits one component, not two.
 */
.qf-auth-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--adm-space-4);
  color: var(--qf-on-surface);
}
.qf-auth-brand-text { display: flex; flex-direction: column; }
.qf-auth-brand-wordmark { font-weight: 700; line-height: 1.1; color: var(--qf-on-background); }
.qf-auth-brand-sub {
  font-weight: 500;
  color: var(--qf-on-surface-variant);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.qf-auth-brand--lg .qf-auth-brand-wordmark { font-size: var(--adm-text-xl); }
.qf-auth-brand--lg .qf-auth-brand-sub { font-size: var(--adm-text-xs); margin-top: 2px; }
.qf-auth-brand--sm .qf-auth-brand-wordmark { font-size: var(--adm-text-md); }
.qf-auth-brand--sm .qf-auth-brand-sub { font-size: var(--adm-text-2xs); margin-top: 1px; }
.qf-auth-shell {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--qf-background);
  padding: var(--qf-space-6);
  /* `relative` + `overflow: hidden` contain the absolutely-positioned
     decorative blobs below so they don't bleed into siblings outside
     the shell (no-op when there are no siblings, but principled). */
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

/*
 * Decorative background blobs (A.6). Two large soft-blurred circles, one
 * tinted greenish from the brand palette (top-left), one warm peach
 * (bottom-right). Pure CSS pseudo-elements so AuthShell + every error
 * screen that uses it (NotFound, Forbidden) picks them up uniformly with
 * zero composable changes.
 *
 * `z-index: -1` keeps the blobs behind the auth card. `pointer-events:
 * none` keeps them out of click targeting. `filter: blur(...)` is what
 * makes the shapes soft — a flat-color circle alone reads as a button.
 * Colors come from the tokens added in A.1 so dark-mode flips with the
 * palette later.
 *
 * Disabled in `prefers-reduced-motion` (no animation today; if any
 * future enhancement adds drifting motion, the toggle is already there).
 */
.qf-auth-shell::before,
.qf-auth-shell::after {
  content: "";
  position: absolute;
  width: 360px;
  height: 360px;
  border-radius: 50%;
  filter: blur(64px);
  opacity: 0.55;
  pointer-events: none;
  z-index: -1;
}
.qf-auth-shell::before {
  top: -120px;
  left: -120px;
  background: var(--qf-decoration-blob-1);
}
.qf-auth-shell::after {
  bottom: -120px;
  right: -120px;
  background: var(--qf-decoration-blob-2);
}
.qf-auth-card {
  width: 100%;
  max-width: 420px;
  min-width: 320px;
  background: var(--qf-surface);
  border-radius: var(--qf-corner-md);
  padding: var(--qf-space-7);
  box-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
}
.qf-auth-title {
  margin: var(--qf-space-5) 0 var(--qf-space-5) 0;
  font-size: var(--qf-text-headline);
  font-weight: 500;
}
/* Hero icon slot used by error screens (404, Forbidden) where the
   AuthShell substitutes the brand for a tinted glyph. Centered so the
   square sits clearly above the title. Phase H of
   admin-ui-handoff-alignment. */
.qf-auth-hero {
  display: flex;
  justify-content: center;
  margin-bottom: var(--qf-space-2);
}

/* ============ SandboxBanner — admin tokens, top-of-page yellow strip ============ */
.qf-sandbox-banner {
  background: var(--adm-bg-banner-sandbox);
  color: var(--adm-bg-banner-sandbox-fg);
  padding: 6px var(--adm-space-6);
  text-align: center;
  font-weight: 600;
  font-size: var(--adm-text-xs);
  line-height: var(--adm-lh-snug);
  border-bottom: 1px solid rgba(146, 64, 14, 0.20);
}

/* ============ Modal — admin tokens, generic confirm/content dialog ============ */
.qf-modal-backdrop {
  position: fixed;
  inset: 0;
  background: var(--adm-bg-overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: var(--adm-space-6);
}
.qf-modal {
  background: var(--adm-bg-canvas);
  border-radius: var(--adm-radius-lg);
  padding: var(--adm-space-6);
  width: 100%;
  max-width: 480px;
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  box-shadow: var(--adm-shadow-modal);
  display: flex;
  flex-direction: column;
  gap: var(--adm-space-5);
}
.qf-modal-title {
  margin: 0;
  font: 700 var(--adm-text-lg) / var(--adm-lh-tight) var(--qf-font-display);
  letter-spacing: -0.01em;
}
/*
 * #267 audit item 3 — confirm hierarchy. The icon-coded header is opt-in: a
 * caller that passes `kind = ModalKind.X` to `<Modal>` gets a colored
 * circular badge next to the title-block. The header layout below mirrors
 * the handoff's `.adm-modal__header` + `.adm-modal__icon` pair, adapted to
 * our existing `qf-*` class prefix; pill-color tokens drive the badge fills
 * so an Info/Warn/Danger/Success badge tracks the same palette as the
 * status pills used elsewhere in the admin.
 */
.qf-modal-header {
  display: flex;
  align-items: flex-start;
  gap: var(--adm-space-4);
}
.qf-modal-icon {
  width: 36px;
  height: 36px;
  border-radius: 9999px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  font: 700 16px / 1 var(--qf-font-body);
}
.qf-modal-icon-info    { background: var(--adm-pill-info-bg);    color: var(--adm-pill-info-fg); }
.qf-modal-icon-warn    { background: var(--adm-pill-warn-bg);    color: var(--adm-pill-warn-fg); }
.qf-modal-icon-danger  { background: var(--adm-pill-danger-bg);  color: var(--adm-pill-danger-fg); }
.qf-modal-icon-success { background: var(--adm-pill-success-bg); color: var(--adm-pill-success-fg); }
.qf-modal-title-block {
  flex: 1;
  min-width: 0;
  padding-top: 2px;
}
.qf-modal-title-block .qf-modal-title { margin: 0; }
.qf-modal-subtitle {
  font: 400 var(--adm-text-sm) / var(--adm-lh-snug) var(--qf-font-body);
  color: var(--qf-fg-2);
  margin: 6px 0 0;
}
.qf-modal-content {
  font: 400 var(--adm-text-sm) / var(--adm-lh-base) var(--qf-font-body);
  color: var(--qf-fg-1);
}
/*
 * #267 audit item 9 — audit-trail annotation. Subtle note under the body
 * showing the `AuditAction.name` (server enum) that this confirm writes on
 * commit. Operator gets one-glance visibility into the audit-log string a
 * downstream reviewer will see in `/audit-log`. Monospaced inline code uses
 * a tighter font scale than the body so it doesn't compete with the title.
 */
.qf-modal-audit-note {
  font: 500 var(--adm-text-xs) / var(--adm-lh-snug) var(--qf-font-body);
  color: var(--qf-fg-2);
  display: flex;
  align-items: center;
  gap: var(--adm-space-2);
  flex-wrap: wrap;
}
.qf-modal-audit-action {
  font-family: var(--qf-font-tabular, ui-monospace, "SF Mono", "Roboto Mono", Menlo, monospace);
  font-size: var(--adm-text-2xs);
  background: var(--adm-pill-neutral-bg);
  color: var(--adm-pill-neutral-fg);
  border-radius: var(--adm-radius-xs);
  padding: 2px var(--adm-space-3);
  letter-spacing: 0;
}
.qf-modal-actions {
  display: flex;
  gap: var(--adm-space-3);
  justify-content: flex-end;
}
.qf-typed-hint {
  display: flex;
  align-items: center;
  gap: var(--adm-space-3);
  font: 500 var(--adm-text-xs) / var(--adm-lh-snug) var(--qf-font-body);
  color: var(--qf-fg-2);
  margin: var(--adm-space-3) 0;
}

/* ============ Drawer — admin right-side detail panel (640px / 880px wide) ============
   Matches design handoff `adm-drawer*` contract: list+detail screens push the
   detail into this panel instead of full-page navigation. Two width variants:
   default 640px, `qf-drawer-wide` 880px (audit-log entries, pending-service
   detail, etc. — see #267 drift table). Overlay is click-to-dismiss; ESC
   handled by the caller via onClose. */
.qf-drawer-overlay {
  position: fixed;
  inset: 0;
  background: var(--adm-bg-overlay);
  z-index: 900;
}
.qf-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--adm-drawer-w);
  max-width: 100vw;
  background: var(--adm-bg-canvas);
  box-shadow: var(--adm-shadow-drawer);
  z-index: 901;
  display: flex;
  flex-direction: column;
}
.qf-drawer.qf-drawer-wide { width: var(--adm-drawer-w-wide); }
.qf-drawer-header {
  display: flex;
  align-items: center;
  gap: var(--adm-space-4);
  padding: var(--adm-space-5) var(--adm-space-6);
  border-bottom: 1px solid var(--adm-border);
}
.qf-drawer-header-titles {
  flex: 1;
  min-width: 0;
}
.qf-drawer-title {
  font: 700 var(--adm-text-xl) / var(--adm-lh-tight) var(--qf-font-display);
  letter-spacing: -0.01em;
  margin: 0;
}
.qf-drawer-subtitle {
  font: 500 var(--adm-text-xs) / 1 var(--qf-font-body);
  color: var(--qf-fg-2);
  margin-top: 4px;
}
.qf-drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--adm-space-6);
  display: flex;
  flex-direction: column;
  gap: var(--adm-space-6);
}
.qf-drawer-footer {
  display: flex;
  align-items: center;
  gap: var(--adm-space-3);
  padding: var(--adm-space-4) var(--adm-space-6);
  border-top: 1px solid var(--adm-border);
  background: var(--adm-bg-app);
  flex-wrap: wrap;
}
.qf-drawer-footer-grow { flex: 1; }

/* ============ DrawerSection (A.10 / Admins-3 canonical body block) ============ */
/*
 * Section grouping inside `.qf-drawer-body`. The small-caps title is the
 * visual signature — operator reads it as "this is a group of related
 * fields", not as another title. Uniform spacing across the section keeps
 * every drawer's body cadence the same.
 *
 * Matches `.adm-section` + `.adm-section__title` in the design handoff.
 */
.qf-drawer-section {
  display: flex;
  flex-direction: column;
  gap: var(--adm-space-3);
}
.qf-drawer-section-title {
  font-size: var(--adm-text-2xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--qf-on-surface-variant);
}
.qf-drawer-section-body {
  display: flex;
  flex-direction: column;
  gap: var(--adm-space-3);
}

/* ============ PageHeader (A.11 / Admins-9 cross-screen sweep) ============ */
/*
 * Real `<h1>` + subtitle paragraph + right-aligned action cluster. Renders
 * directly under the breadcrumb, above the toolbar/filter row. Every list
 * screen adopts this for "what is this page for" affordance — breadcrumb
 * alone is a nav crumb, not a content title.
 */
.qf-page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--adm-space-6);
  margin: 0 0 var(--adm-space-6) 0;
  flex-wrap: wrap;
}
.qf-page-header-titles { display: flex; flex-direction: column; gap: var(--adm-space-2); flex: 1; min-width: 0; }
.qf-page-title {
  font-size: var(--adm-text-2xl);
  font-weight: 700;
  color: var(--qf-on-background);
  margin: 0;
  line-height: var(--adm-lh-tight);
}
.qf-page-subtitle {
  font-size: var(--adm-text-sm);
  color: var(--qf-on-surface-variant);
  margin: 0;
  line-height: var(--adm-lh-snug);
  max-width: 72ch;
}
.qf-page-header-actions {
  display: flex;
  align-items: center;
  gap: var(--adm-space-3);
  flex-shrink: 0;
}
.qf-drawer-keyboard {
  display: flex;
  align-items: center;
  gap: var(--adm-space-3);
  font: 500 var(--adm-text-2xs) / 1 var(--qf-font-body);
  color: var(--qf-fg-2);
}
.qf-drawer-close {
  width: 28px;
  height: 28px;
  border: 0;
  background: transparent;
  border-radius: var(--adm-radius-sm);
  cursor: pointer;
  color: var(--qf-fg-2);
  font: 600 20px / 1 var(--qf-font-body);
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
.qf-drawer-close:hover {
  background: var(--adm-bg-row-hover);
  color: var(--qf-fg-1);
}
.qf-drawer-close:focus-visible {
  outline: 2px solid var(--adm-border-focus);
  outline-offset: 1px;
}
/*
 * Body-section grouping helper used by Step 3 detail-screen composables
 * (Reports / PendingService / AuditLogEntry). Their migrated content tree
 * sits in a single `.qf-drawer-body-stack` Div so the column gap matches
 * what the drawer body itself uses one level up — a section-to-section
 * 16px rhythm that mirrors the handoff `.adm-drawer__body` profile.
 */
.qf-drawer-body-stack {
  display: flex;
  flex-direction: column;
  gap: var(--adm-space-6);
  width: 100%;
}

/* ============ Inline confirm (A.16) ============ */
.qf-inline-confirm {
  display: inline-flex;
  align-items: center;
  gap: var(--qf-space-3);
}

.qf-inline-confirm-prompt {
  font-size: var(--qf-text-label);
  color: var(--qf-on-surface-variant);
}

/* ============ Empty state — admin density ============ */
.qf-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--adm-space-12) var(--adm-space-6);
  gap: var(--adm-space-4);
}
.qf-empty-state h3 {
  margin: 0;
  font-size: var(--adm-text-lg);
  font-weight: 600;
}
.qf-empty-state-description {
  margin: 0;
  font-size: var(--adm-text-sm);
  color: var(--qf-on-surface-variant);
}
.qf-empty-state-cta {
  margin-top: var(--adm-space-3);
}

/* ============ Stat card — admin density ============ */
.qf-stat-card {
  background: var(--adm-bg-canvas);
  border-radius: var(--adm-radius-md);
  padding: var(--adm-space-6);
  border: 1px solid var(--adm-border);
  box-shadow: var(--adm-shadow-xs);
}
.qf-stat-card-title {
  font-size: var(--adm-text-xs);
  color: var(--qf-on-surface-variant);
  margin-bottom: var(--adm-space-3);
}
.qf-stat-card-value {
  font-size: var(--adm-text-3xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--qf-on-surface);
}
.qf-stat-card-delta {
  margin-top: var(--adm-space-2);
  font-size: var(--adm-text-xs);
  color: var(--qf-on-surface-variant);
}

/*
 * StatCard extensions (A.12): head row (icon + title) and tone variants
 * on the delta line. Tones use the same palette as Pill / HeroIcon so
 * "success / warn / danger" mean the same thing visually across atoms.
 */
.qf-stat-card-head {
  display: inline-flex;
  align-items: center;
  gap: var(--adm-space-2);
  margin-bottom: var(--adm-space-3);
  color: var(--qf-on-surface-variant);
}
.qf-stat-card-head .qf-stat-card-title { margin-bottom: 0; }
.qf-stat-card-skeleton {
  height: var(--adm-text-3xl);
  width: 60%;
  border-radius: var(--adm-radius-xs);
  background: linear-gradient(90deg, var(--adm-bg-app), var(--qf-surface-variant), var(--adm-bg-app));
  background-size: 200% 100%;
  animation: qf-skel-shimmer 1.4s ease-in-out infinite;
}
@keyframes qf-skel-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.qf-stat-card--success .qf-stat-card-delta { color: var(--adm-pill-success-fg); }
.qf-stat-card--warn .qf-stat-card-delta { color: var(--adm-pill-warn-fg); }
.qf-stat-card--danger .qf-stat-card-delta { color: var(--adm-pill-danger-fg); }

/* ============ Sparkline (A.13 — pending full implementation) ============ */
/* See Sparkline.kt — the SVG rendering is deferred to Phase E (Analytics-7).
 * This stub just reserves the vertical slot so neighboring widgets don't
 * reflow when the renderer lands. */
/*
 * A.13 Sparkline — host div + child SVG. The child is built imperatively
 * via document.createElementNS (compose-html doesn't expose SVG
 * composables), so the host's only job is to reserve vertical space
 * and let the SVG inside scale to 100% width.
 */
.qf-sparkline {
  display: block;
  width: 100%;
  position: relative;
}
.qf-sparkline > svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ============ Keyboard glyph — admin kbd tokens ============ */
.qf-kbd {
  display: inline-block;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: var(--adm-text-2xs);
  background: var(--adm-kbd-bg);
  color: var(--adm-kbd-fg);
  border: 1px solid var(--adm-kbd-border);
  border-radius: var(--adm-radius-xs);
  padding: 1px var(--adm-space-2);
  line-height: var(--adm-lh-tight);
}

/* ============ Shell (authed chassis) — admin layout dims ============ */
.qf-shell {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
.qf-shell-body {
  flex: 1;
  display: grid;
  grid-template-columns: var(--adm-sidebar-w) 1fr;
  min-height: 0;
}
.qf-shell-main-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow-y: auto;
  background: var(--adm-bg-app);
}
.qf-shell-content {
  padding: var(--adm-space-7) var(--adm-space-8);
  flex: 1;
  max-width: var(--adm-content-max);
  width: 100%;
}

/* ============ Sidebar — admin density (slimmer items, off-white bg) ============ */
.qf-sidebar {
  background: var(--adm-bg-sidebar);
  border-right: 1px solid var(--adm-border);
  padding: var(--adm-space-5) var(--adm-space-4);
  display: flex;
  flex-direction: column;
  gap: var(--adm-space-5);
  overflow-y: auto;
}
.qf-sidebar-brand {
  padding-bottom: var(--adm-space-4);
  border-bottom: 1px solid var(--adm-border);
  display: flex;
  align-items: center;
  gap: var(--adm-space-3);
}
.qf-sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/*
 * Sidebar nav item. Indented (+adm-space-4 of left padding on top of the
 * shared horizontal padding) so the section header reads as a flush-left
 * label and items sit visibly nested under it. The Btn-link variant
 * defaults to the primary color — we override to a neutral on-surface
 * here so idle items don't compete with the active highlight.
 */
.qf-sidebar-item {
  justify-content: flex-start;
  min-height: var(--adm-h-sidebar-item);
  font-size: var(--adm-text-md);
  font-weight: 400;
  padding: 0 var(--adm-space-4) 0 var(--adm-space-6);
  border-radius: var(--adm-radius-sm);
  color: var(--qf-on-surface);
  position: relative;
}
.qf-sidebar-item:hover:not(:disabled) {
  background: var(--adm-bg-row-hover);
  color: var(--qf-on-surface);
}

/*
 * Active sidebar item. Primary-container tint + bold weight + 3px
 * left-edge accent in primary so the operator's location reads at a
 * glance. The accent is rendered as a ::before pseudo so we don't have
 * to mutate the inner Btn DOM.
 */
.qf-sidebar-item--active,
.qf-sidebar-item--active:hover:not(:disabled) {
  background: var(--qf-primary-container);
  color: var(--qf-on-primary-container);
  font-weight: 600;
}
.qf-sidebar-item--active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  background: var(--qf-primary);
  border-radius: 0 2px 2px 0;
}

/*
 * Sidebar section header (Phase I.1 / OQ-5). Smaller font + much wider
 * letter-spacing + muted color so it reads as a label, not as another
 * link. Extra padding-top separates groups visually.
 */
.qf-sidebar-section-title {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--qf-on-surface-variant);
  opacity: 0.72;
  padding: var(--adm-space-6) var(--adm-space-4) var(--adm-space-2);
}
/* Trim the leading padding when a section header is the very first nav child. */
.qf-sidebar-nav > .qf-sidebar-section-title:first-child {
  padding-top: 0;
}

/* ============ TopBar — admin 48px height + admin density ============ */
.qf-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--adm-space-4);
  padding: 0 var(--adm-space-6);
  min-height: var(--adm-h-topbar);
  background: var(--adm-bg-canvas);
  border-bottom: 1px solid var(--adm-border);
}
.qf-topbar-breadcrumbs {
  display: flex;
  align-items: center;
  gap: var(--adm-space-2);
  font-size: var(--adm-text-sm);
}
.qf-breadcrumb {
  font-size: var(--adm-text-sm);
  color: var(--qf-on-surface-variant);
}
.qf-breadcrumb-current {
  font-weight: 600;
  color: var(--qf-on-background);
}
.qf-breadcrumb-separator {
  color: var(--qf-on-surface-variant);
}
.qf-topbar-actions {
  display: flex;
  align-items: center;
  gap: var(--adm-space-3);
}

/*
 * Account dropdown (Phase I.2). Trigger is a small button showing the
 * signed-in admin's email and a chevron; clicking toggles a panel that
 * repeats the email (full text) and a "Sair" action. Panel is absolutely
 * positioned so it floats over the page content; small enough that a
 * click-anywhere-to-dismiss isn't required for the lean version.
 */
.qf-account-menu {
  position: relative;
}
.qf-account-menu-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--adm-space-2);
  background: transparent;
  border: 1px solid var(--adm-border);
  border-radius: var(--adm-radius-sm);
  padding: 4px var(--adm-space-3);
  font-family: inherit;
  font-size: var(--adm-text-sm);
  color: var(--qf-on-surface);
  cursor: pointer;
  max-width: 220px;
}
.qf-account-menu-trigger:hover {
  background: var(--adm-bg-row-hover);
}
.qf-account-menu-email {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 140px;
}
.qf-account-menu-panel {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 220px;
  background: var(--adm-bg-canvas);
  border: 1px solid var(--adm-border);
  border-radius: var(--adm-radius-sm);
  box-shadow: var(--adm-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.08));
  padding: var(--adm-space-2);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.qf-account-menu-email-row {
  padding: var(--adm-space-2) var(--adm-space-3);
  font-size: var(--adm-text-xs);
  color: var(--qf-on-surface-variant);
  border-bottom: 1px solid var(--adm-border);
  margin-bottom: 2px;
  word-break: break-all;
}
.qf-account-menu-action {
  display: inline-flex;
  align-items: center;
  gap: var(--adm-space-2);
  background: transparent;
  border: 0;
  border-radius: var(--adm-radius-xs);
  padding: var(--adm-space-2) var(--adm-space-3);
  font-family: inherit;
  font-size: var(--adm-text-sm);
  color: var(--qf-on-surface);
  cursor: pointer;
  text-align: left;
}
.qf-account-menu-action:hover {
  background: var(--adm-bg-row-hover);
}

/* ============ CommandPalette ============ */
.qf-command-list {
  max-height: 320px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  margin-top: var(--qf-space-4);
  outline: none;
}
.qf-command-row {
  background: var(--qf-surface);
  border: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--qf-space-3) var(--qf-space-5);
  font-family: inherit;
  font-size: var(--qf-text-body);
  cursor: pointer;
  text-align: left;
}
.qf-command-row-focused { background: var(--qf-surface-variant); }
.qf-command-row:hover { background: var(--qf-surface-variant); }
.qf-command-label { color: var(--qf-on-surface); }
.qf-command-keys {
  display: flex;
  gap: var(--qf-space-2);
}
.qf-command-empty {
  padding: var(--qf-space-4);
  color: var(--qf-on-surface-variant);
  font-size: var(--qf-text-label);
  margin: 0;
}
/*
 * Command palette section header (Phase I.5 / OQ-7). Mirrors the sidebar
 * section header styling for visual consistency across the chrome.
 */
.qf-palette-section-title {
  font-size: var(--adm-text-2xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--qf-on-surface-variant);
  padding: var(--adm-space-3) var(--adm-space-5) var(--adm-space-2);
}
/* Strip the leading padding when a section header is the first list child. */
.qf-command-list > .qf-palette-section-title:first-child {
  padding-top: 0;
}
/*
 * Fixed-width icon slot keeps labels aligned across rows regardless of
 * whether each command supplies an icon. Width is intentionally wider
 * than the 16px glyph so the gap between icon and label stays visually
 * uniform.
 */
.qf-palette-row-icon {
  width: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--qf-on-surface-variant);
  margin-right: var(--adm-space-3);
  flex-shrink: 0;
}

/* ============ AutocompletePicker ============ */
.qf-autocomplete {
  position: relative;
  width: 100%;
}
.qf-autocomplete-list {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 10;
  max-height: 240px;
  overflow-y: auto;
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-sm);
  margin-top: var(--qf-space-1);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  display: flex;
  flex-direction: column;
}
.qf-autocomplete-row {
  background: var(--qf-surface);
  border: none;
  width: 100%;
  padding: var(--qf-space-2) var(--qf-space-4);
  font-family: inherit;
  font-size: var(--qf-text-body);
  color: var(--qf-on-surface);
  cursor: pointer;
  text-align: left;
}
.qf-autocomplete-row-focused { background: var(--qf-surface-variant); }
.qf-autocomplete-row:hover { background: var(--qf-surface-variant); }
.qf-autocomplete-empty {
  padding: var(--qf-space-3) var(--qf-space-4);
  color: var(--qf-on-surface-variant);
  font-size: var(--qf-text-label);
  margin: 0;
}

/* ============ CopyableList ============ */
.qf-copyable-list {
  display: flex;
  flex-direction: column;
  gap: var(--qf-space-3);
}
.qf-copyable-list-items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--qf-space-2);
}
.qf-copyable-list-item {
  display: flex;
  align-items: center;
  gap: var(--qf-space-3);
}
.qf-copyable-list-bulk {
  margin-top: var(--qf-space-2);
}
.qf-copyable-list-empty {
  color: var(--qf-on-surface-variant);
}

/* ============ QR rendering ============ */
.qf-qr {
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-md);
  padding: var(--qf-space-3);
  display: inline-block;
}
.qf-qr svg { display: block; }
.qf-qr-fallback {
  background: var(--qf-surface-variant);
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-5);
}

/* ============ Screens ============ */
.qf-screen-submit {
  margin-top: var(--qf-space-5);
}
.qf-screen-submit > .qf-btn {
  width: 100%;
}
.qf-screen-alt-action {
  margin-top: var(--qf-space-3);
  display: flex;
  justify-content: center;
}
.qf-screen-alt-action > .qf-btn-link {
  width: 100%;
  justify-content: center;
}
.qf-screen-qr {
  display: flex;
  justify-content: center;
  margin: var(--qf-space-5) 0;
}
.qf-screen-secret {
  font-size: var(--qf-text-label);
  color: var(--qf-on-surface-variant);
  display: flex;
  align-items: center;
  gap: var(--qf-space-2);
  flex-wrap: wrap;
}
.qf-home-permissions {
  margin-top: var(--qf-space-6);
  background: var(--qf-surface);
  border-radius: var(--qf-corner-md);
  padding: var(--qf-space-6);
  border: 1px solid var(--qf-outline);
}
.qf-home-permissions > p {
  margin: 0 0 var(--qf-space-4) 0;
  font-weight: 500;
  font-size: var(--qf-text-label);
  color: var(--qf-on-surface-variant);
}
.qf-home-logout {
  margin-top: var(--qf-space-6);
}

/* ============ Success state (used by Forgot + Reset screens) ============ */
.qf-screen-success {
  background: #D1FAE5;
  color: #065F46;
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-5);
  margin-bottom: var(--qf-space-5);
}
.qf-screen-success p {
  margin: 0;
}

/* ============ Dashboard ============ */
.qf-dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--qf-space-5);
}
.qf-stat-card-button {
  display: block;
  text-align: left;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}
.qf-stat-card-button:hover .qf-stat-card {
  border-color: var(--qf-primary);
}
.qf-dashboard-loading {
  padding: var(--qf-space-6);
  color: var(--qf-on-surface-variant);
}

/* ============ Reports (Slice 1) ============ */
.qf-reports-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--qf-space-4);
  align-items: flex-end;
  margin-bottom: var(--qf-space-6);
  padding: var(--qf-space-4) var(--qf-space-5);
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-md);
}

/*
 * Compact in-row filters (filter-row context).
 *
 * `.qf-field` defaults to `width: 100%` so it stacks vertically on a
 * normal form. Inside `.qf-reports-filters` that override would force
 * every filter onto its own line (despite the parent being `flex-wrap`).
 * Override `width` to auto + smaller paddings + smaller label so the
 * filter row reads as a single compact strip across the top of every
 * list screen, matching the design handoff.
 *
 * Follow-up #294 will replace this row entirely with the FilterChipBar
 * atom (B.2) — until then the in-row Fields stay but render tightly.
 */
.qf-reports-filters .qf-field {
  width: auto;
  flex: 0 1 180px;
  padding: 0;
  gap: 4px;
  min-width: 140px;
}
.qf-reports-filters .qf-field-label {
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--qf-on-surface-variant);
  line-height: 1;
}
.qf-reports-filters .qf-input,
.qf-reports-filters select.qf-input,
.qf-reports-filters input.qf-input {
  height: 32px;
  padding: 0 var(--qf-space-4);
  font-size: var(--qf-text-label);
}
.qf-reports-filters .qf-btn {
  align-self: flex-end;
  height: 32px;
  padding: 0 var(--qf-space-5);
}

.qf-reports-bulk-bar {
  position: sticky;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--qf-space-5);
  padding: var(--qf-space-4) var(--qf-space-5);
  background: var(--qf-primary-container);
  color: var(--qf-on-primary-container);
  border-radius: var(--qf-corner-sm);
  margin-bottom: var(--qf-space-5);
  z-index: 1;
}

.qf-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-md);
  overflow: hidden;
}
.qf-table thead {
  background: var(--qf-surface-variant);
}
.qf-table th,
.qf-table td {
  text-align: left;
  padding: var(--qf-space-4) var(--qf-space-5);
  border-bottom: 1px solid var(--qf-outline);
  font-size: var(--qf-text-body);
}
.qf-table th {
  font-weight: 600;
  color: var(--qf-on-surface-variant);
  font-size: var(--qf-text-label);
}
.qf-table tbody tr:last-child td {
  border-bottom: none;
}
.qf-table-row-selected td {
  background: var(--qf-primary-container);
}
.qf-table-row-clickable {
  cursor: pointer;
}
.qf-table-row-clickable:hover td {
  background: var(--qf-surface-variant);
}

.qf-reports-load-more {
  display: flex;
  justify-content: center;
  padding: var(--qf-space-6) 0;
}

.qf-report-detail-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--qf-space-4);
  margin: var(--qf-space-5) 0 var(--qf-space-6) 0;
  color: var(--qf-on-surface-variant);
}

.qf-report-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--qf-space-5);
  margin-bottom: var(--qf-space-6);
}

.qf-report-detail-card {
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-md);
  padding: var(--qf-space-6);
}
.qf-report-detail-card h3 {
  margin: 0 0 var(--qf-space-5) 0;
  font-size: var(--qf-text-title);
}
.qf-report-detail-card p {
  margin: 0 0 var(--qf-space-3) 0;
}
.qf-report-detail-card .qf-label {
  color: var(--qf-on-surface-variant);
  font-weight: 500;
}

.qf-report-detail-section {
  margin-bottom: var(--qf-space-6);
}

.qf-action-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--qf-space-4);
  margin-top: var(--qf-space-6);
}

.qf-target-preview-deleted {
  background: var(--qf-error-container);
  color: var(--qf-on-error-container);
  padding: var(--qf-space-5);
  border-radius: var(--qf-corner-sm);
  font-weight: 500;
}

.qf-textarea {
  min-height: 80px;
  height: auto;
  padding: var(--qf-space-4) var(--qf-space-5);
  resize: vertical;
}

/* ============ Profiles (Slice 5) ============ */
.qf-profile-photo {
  width: 96px;
  height: 96px;
  border-radius: var(--qf-corner-md);
  object-fit: cover;
  margin-bottom: var(--qf-space-4);
  display: block;
}

.qf-profile-status-history ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.qf-profile-status-history li {
  padding: var(--qf-space-3) 0;
  border-bottom: 1px solid var(--qf-outline);
  font-size: var(--qf-text-body);
}
.qf-profile-status-history li:last-child {
  border-bottom: none;
}

/* ============ Audit log (Slice 6) ============ */
/* Pretty-printed JSON snapshot. Monospace + light bg + scrollable so a
   large snapshot doesn't push the rest of the card off-screen. */
.qf-json {
  background: var(--qf-surface-variant);
  color: var(--qf-on-surface-variant);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-5);
  margin: 0;
  max-height: 480px;
  overflow: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 13px;
  line-height: 1.5;
  white-space: pre;
}

.qf-json code {
  font-family: inherit;
  background: transparent;
  padding: 0;
  white-space: pre;
}

/* Visual tightening for the audit-log list: relative-time + actor + action
   + targetSummary make for narrow columns, so reduce vertical padding. */
.qf-audit-list-row td {
  padding-top: var(--qf-space-3);
  padding-bottom: var(--qf-space-3);
}

/* ============ Search analytics (Slice 9) ============ */

/* "Data is synthetic in sandbox" callout, sits above the LLM-fallback
   placeholder banner. Same warm warn tone as the top-of-shell sandbox
   ribbon but spans the content column instead of the viewport. */
.qf-sandbox-data-banner {
  background: #FEF3C7;
  color: #92400E;
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-5);
  margin-bottom: var(--qf-space-5);
  font-size: var(--qf-text-body);
}

/* "LLM fallback rate em desenvolvimento" placeholder banner — neutral
   info chrome so an operator doesn't mistake it for an error. */
.qf-analytics-info-banner {
  background: var(--qf-surface-variant);
  color: var(--qf-on-surface-variant);
  border: 1px dashed var(--qf-outline);
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-5);
  margin-bottom: var(--qf-space-5);
  font-size: var(--qf-text-body);
}

/* 7d / 30d / 90d radio row above the widget grid. */
.qf-analytics-window-selector {
  display: flex;
  gap: var(--qf-space-6);
  margin-bottom: var(--qf-space-6);
  font-size: var(--qf-text-body);
}
.qf-analytics-window-option {
  display: inline-flex;
  align-items: center;
  gap: var(--qf-space-3);
  cursor: pointer;
}

/*
 * Analytics KPI strip — four StatCards side-by-side above the widget
 * grid. Collapses to a 2-column grid under 720px (cards stay readable
 * on a narrow tab) and one column under 480px.
 */
.qf-analytics-kpi-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--qf-space-5);
  margin-bottom: var(--qf-space-6);
}
@media (max-width: 720px) {
  .qf-analytics-kpi-strip { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  .qf-analytics-kpi-strip { grid-template-columns: 1fr; }
}

/* Two-column grid of widget cards, collapses to one column under 960px. */
.qf-analytics-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--qf-space-6);
}
@media (max-width: 960px) {
  .qf-analytics-grid {
    grid-template-columns: 1fr;
  }
}

.qf-analytics-card {
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-md);
  padding: var(--qf-space-6);
}
.qf-analytics-card-title {
  margin: 0 0 var(--qf-space-5) 0;
  font-size: var(--qf-text-title);
  color: var(--qf-on-surface);
}

/* Per-card empty state — same gentle copy as the spec. */
.qf-analytics-empty {
  color: var(--qf-on-surface-variant);
  font-size: var(--qf-text-body);
  padding: var(--qf-space-5) 0;
  text-align: center;
}

/* Compact table inside a card. Tighter padding than the global .qf-table
   because cards are narrower than the full content column. */
.qf-analytics-table th,
.qf-analytics-table td {
  padding: var(--qf-space-3) var(--qf-space-4);
  font-size: var(--qf-text-body);
}

/* CSS-bar widget row — label | track | count. The fill's width is the
   only thing screens set inline; everything else is here. */
.qf-analytics-bar {
  display: grid;
  grid-template-columns: 140px 1fr 80px;
  gap: var(--qf-space-4);
  align-items: center;
  padding: var(--qf-space-3) 0;
  font-size: var(--qf-text-body);
}
.qf-analytics-bar-label {
  color: var(--qf-on-surface);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.qf-analytics-bar-track {
  background: var(--qf-surface-variant);
  border-radius: var(--qf-corner-sm);
  height: 10px;
  overflow: hidden;
}
.qf-analytics-bar-fill {
  background: var(--qf-primary);
  height: 100%;
  border-radius: var(--qf-corner-sm);
}
.qf-analytics-bar-count {
  color: var(--qf-on-surface-variant);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Animated skeleton placeholder for loading widgets — keyframes share
   the same gentle shimmer used elsewhere in the admin app. */
.qf-skeleton {
  background: linear-gradient(
    90deg,
    var(--qf-surface-variant) 0%,
    rgba(255, 255, 255, 0.6) 50%,
    var(--qf-surface-variant) 100%
  );
  background-size: 200% 100%;
  border-radius: var(--qf-corner-sm);
  animation: qf-skeleton-shimmer 1.4s ease-in-out infinite;
}
.qf-skeleton-row {
  height: 18px;
  margin: var(--qf-space-3) 0;
}
@keyframes qf-skeleton-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ============ App-config editor (Slice 2) ============ */
.qf-appconfig-header {
  display: flex;
  align-items: baseline;
  gap: var(--qf-space-6);
  margin-bottom: var(--qf-space-6);
}
.qf-appconfig-header h1 {
  margin: 0;
  font-size: var(--qf-text-headline);
}
.qf-appconfig-version {
  color: var(--qf-on-surface-variant);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--qf-text-label);
}

.qf-appconfig-dirty {
  background: #FEF3C7;
  color: #78350F;
  border: 1px solid #FDE68A;
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-4) var(--qf-space-5);
  margin-bottom: var(--qf-space-5);
  font-weight: 500;
}

.qf-appconfig-conflict {
  background: var(--qf-error-container);
  color: var(--qf-on-error-container);
  border: 1px solid var(--qf-error);
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-5);
  margin-bottom: var(--qf-space-5);
  display: flex;
  flex-direction: column;
  gap: var(--qf-space-4);
}

.qf-appconfig-section {
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-md);
  padding: var(--qf-space-6);
  margin-bottom: var(--qf-space-6);
}
.qf-appconfig-section h3 {
  margin: 0 0 var(--qf-space-5) 0;
  font-size: var(--qf-text-title);
}

.qf-appconfig-helper {
  color: var(--qf-on-surface-variant);
  font-size: var(--qf-text-label);
  margin: 0 0 var(--qf-space-5) 0;
}

.qf-appconfig-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--qf-space-5);
}

.qf-appconfig-warning {
  color: #92400E;
  background: #FEF3C7;
  border: 1px solid #FDE68A;
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-3) var(--qf-space-4);
  margin-top: var(--qf-space-3);
  font-size: var(--qf-text-label);
}

/* Generic warning banner for in-modal / in-form callouts where the
   operator needs to read a non-blocking "this is irreversible" message
   before continuing. Used by the Enroll backup-codes modal (Phase G.7
   of admin-ui-handoff-alignment) and intended as a reusable atom for
   any future "warn before commit" surface. Distinct from
   .qf-appconfig-warning (one-shot after-save banner) and .qf-plan-warning
   (server-returned validation warnings on a Plans save). */
.qf-warning {
  color: #92400E;
  background: #FEF3C7;
  border: 1px solid #FDE68A;
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-3) var(--qf-space-4);
  margin: var(--qf-space-3) 0;
  font-size: var(--qf-text-body);
  font-weight: 500;
}

/* Inline checkbox + label pair, used by the Enroll backup-codes modal
   gate so the operator must affirm they saved the codes before the
   primary CTA enables. Kept as a separate class so the spacing can
   evolve independently of in-form checkboxes. */
.qf-checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--qf-space-2);
  margin: var(--qf-space-4) 0 var(--qf-space-2);
}

.qf-checkbox-row label {
  cursor: pointer;
  user-select: none;
  font-size: var(--qf-text-body);
}

.qf-appconfig-switch-row {
  display: flex;
  align-items: center;
  gap: var(--qf-space-4);
  margin-bottom: var(--qf-space-5);
}

.qf-appconfig-actions {
  position: sticky;
  bottom: 0;
  background: var(--qf-background);
  border-top: 1px solid var(--qf-outline);
  padding: var(--qf-space-5) 0;
  margin-top: var(--qf-space-6);
  display: flex;
  justify-content: flex-end;
  gap: var(--qf-space-4);
}

/* Diff modal rows — stacked old→new per spec (side-by-side deferred to Phase 3). */
.qf-appconfig-diff-row {
  border-bottom: 1px solid var(--qf-outline);
  padding: var(--qf-space-4) 0;
}
.qf-appconfig-diff-row:last-child {
  border-bottom: none;
}
.qf-appconfig-diff-field {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-weight: 600;
  margin: 0 0 var(--qf-space-3) 0;
}
.qf-appconfig-diff-old {
  color: var(--qf-error);
  text-decoration: line-through;
}
.qf-appconfig-diff-new {
  color: #15803D;
  font-weight: 500;
}

/* ============ Pending jobs viewer (Slice 10) ============ */
/* Inline success banner after a singular retry/cancel succeeds. Mirrors
   the InlineError shape but uses the success container colors so the
   operator immediately sees the positive feedback without a separate
   modal step. */
.qf-job-feedback {
  background: var(--qf-success-container, var(--qf-primary-container));
  color: var(--qf-on-success-container, var(--qf-on-primary-container));
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-4) var(--qf-space-5);
  margin: var(--qf-space-4) 0;
  font-size: var(--qf-text-body);
}

/* Placeholder text for the "per-attempt history not implemented yet"
   note in the job detail screen. Visually de-emphasized so it reads as
   meta-information, not as a missing piece of data. */
.qf-job-history-placeholder {
  color: var(--qf-on-surface-variant);
  font-style: italic;
  margin-top: var(--qf-space-4);
}

/* ============ Plans CRUD (Slice 8) ============ */
/* Header row above the plans table — holds the "Novo plano" button
   right-aligned so it sits visually distinct from the table body. */
.qf-plans-header {
  display: flex;
  justify-content: flex-end;
  padding-bottom: var(--qf-space-3);
}

/* Form grid for the create/edit screen — single column of fields with
   vertical rhythm. Mirrors the field layout used elsewhere; isolated
   class so plans-specific spacing tweaks don't bleed into other forms. */
.qf-plan-form {
  display: flex;
  flex-direction: column;
  gap: var(--qf-space-4);
  max-width: 640px;
  padding: var(--qf-space-4) 0;
}

/* Inline success banner reused across the plans list and edit screens
   (after save, set-default, delete). Same shape as the job feedback
   pattern so visual language stays consistent. */
.qf-plan-feedback {
  background: var(--qf-success-container, var(--qf-primary-container));
  color: var(--qf-on-success-container, var(--qf-on-primary-container));
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-4) var(--qf-space-5);
  margin: var(--qf-space-4) 0;
  font-size: var(--qf-text-body);
}

/* Yellow informational banner used to surface server-side warnings
   after a successful save (e.g. "5 usuários têm mais serviços que o
   novo limite"). Not blocking — the change has already landed. */
.qf-plan-warning {
  background: var(--qf-warning-container, #fff7e6);
  color: var(--qf-on-warning-container, #6b3a00);
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-4) var(--qf-space-5);
  margin: var(--qf-space-4) 0;
  font-size: var(--qf-text-body);
}

/* ============ Catalog (Slice 7) ============ */
.qf-catalog-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--qf-space-5);
  margin-top: var(--qf-space-4);
  margin-bottom: var(--qf-space-4);
}

.qf-catalog-form .qf-field {
  margin-bottom: 0;
}

/* ============ Pending services review (Slice 3) ============ */
/* Per-signal meta line (source · city · relative time) attached after the
   raw_description on the detail screen. Visually de-emphasized so the
   raw text reads as the primary content. */
.qf-pending-source-meta {
  color: var(--qf-on-surface-variant);
  font-size: var(--qf-text-caption);
}

/* Stacked list of merge-target candidates in the merge modal. Each row
   shows the candidate's name + id with a "Usar" link that fills the
   target-id input. */
.qf-pending-merge-candidates {
  display: flex;
  flex-direction: column;
  gap: var(--qf-space-2);
  margin-top: var(--qf-space-2);
}

.qf-pending-merge-candidate {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--qf-space-3);
  padding: var(--qf-space-2) var(--qf-space-3);
  background: var(--qf-surface-variant);
  border-radius: var(--qf-corner-sm);
}

.qf-pending-merge-candidate-name {
  font-weight: 500;
}

.qf-pending-merge-candidate-id {
  color: var(--qf-on-surface-variant);
  font-family: var(--qf-mono);
  font-size: var(--qf-text-caption);
}

/* ============ Admins management (Slice 11) ============ */
.qf-admins-permission-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--qf-space-5);
  margin: var(--qf-space-5) 0;
}

.qf-admins-permission-category {
  background: var(--qf-surface-variant);
  border-radius: var(--qf-corner-sm);
  padding: var(--qf-space-4) var(--qf-space-5);
}

.qf-admins-permission-category h3 {
  font-size: var(--qf-text-caption);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 0 var(--qf-space-3) 0;
  color: var(--qf-on-surface-variant);
}

.qf-admins-permission-row {
  display: flex;
  align-items: center;
  gap: var(--qf-space-3);
  padding: var(--qf-space-2) 0;
  cursor: pointer;
}

.qf-admins-totp-block {
  margin-top: var(--qf-space-6);
  padding: var(--qf-space-4) var(--qf-space-5);
  background: var(--qf-surface-variant);
  border-radius: var(--qf-corner-sm);
}

.qf-admins-totp-block h3 {
  margin: 0 0 var(--qf-space-3) 0;
}

/* ============ A.14 SegmentedControl ============ */
.qf-segmented {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--qf-surface-variant);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-md);
}

.qf-segmented-option {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--qf-on-surface-variant);
  font: inherit;
  font-size: var(--qf-text-label);
  padding: var(--qf-space-3) var(--qf-space-5);
  border-radius: var(--qf-corner-sm);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}

.qf-segmented-option:hover:not(.qf-segmented-option--active) {
  background: rgba(15, 118, 110, 0.06);
  color: var(--qf-on-surface);
}

.qf-segmented-option--active {
  background: var(--qf-surface);
  color: var(--qf-primary);
  font-weight: 600;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
  cursor: default;
}

/* ============ A.15 InlinePercentBar ============ */
.qf-percent-bar {
  display: inline-flex;
  align-items: center;
  gap: var(--qf-space-4);
  vertical-align: middle;
}

.qf-percent-bar-track {
  position: relative;
  height: 6px;
  background: var(--qf-surface-variant);
  border-radius: 999px;
  overflow: hidden;
}

.qf-percent-bar-fill {
  height: 100%;
  border-radius: inherit;
  transition: width 200ms ease;
}

.qf-percent-bar-track--neutral .qf-percent-bar-fill { background: var(--qf-primary); }
.qf-percent-bar-track--success .qf-percent-bar-fill { background: #16A34A; }
.qf-percent-bar-track--warn    .qf-percent-bar-fill { background: #D97706; }
.qf-percent-bar-track--danger  .qf-percent-bar-fill { background: var(--qf-error); }

.qf-percent-bar-label {
  font-size: var(--qf-text-label);
  color: var(--qf-on-surface-variant);
  font-variant-numeric: tabular-nums;
  min-width: 28px;
  text-align: right;
}

/* ============ A.17 EntityLink ============ */
.qf-entity-link {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  font: inherit;
  color: var(--qf-primary);
  display: inline-flex;
  align-items: center;
  gap: var(--qf-space-2);
  cursor: pointer;
  text-align: inherit;
}

.qf-entity-link:hover .qf-entity-link-label,
.qf-entity-link:focus-visible .qf-entity-link-label {
  text-decoration: underline;
}

.qf-entity-link:focus-visible {
  outline: 2px solid var(--qf-primary);
  outline-offset: 2px;
  border-radius: 2px;
}

.qf-entity-link--muted {
  color: var(--qf-on-surface-variant);
}

.qf-entity-link--muted:hover,
.qf-entity-link--muted:focus-visible {
  color: var(--qf-primary);
}

/* ============ A.18 FieldRow ============ */
.qf-field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--qf-space-5);
  align-items: start;
}

@media (max-width: 480px) {
  .qf-field-row {
    grid-template-columns: 1fr;
  }
}

/* ============ A.19 Toast ============ */
.qf-toast-host {
  position: fixed;
  top: var(--qf-space-6);
  right: var(--qf-space-6);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: var(--qf-space-4);
  pointer-events: none;
  max-width: 360px;
}

.qf-toast {
  display: flex;
  align-items: flex-start;
  gap: var(--qf-space-4);
  padding: var(--qf-space-5) var(--qf-space-6);
  background: var(--qf-surface);
  color: var(--qf-on-surface);
  border-radius: var(--qf-corner-md);
  border: 1px solid var(--qf-outline);
  border-left-width: 4px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
  pointer-events: auto;
  animation: qf-toast-enter 180ms ease-out;
}

@keyframes qf-toast-enter {
  from { opacity: 0; transform: translateX(8px); }
  to   { opacity: 1; transform: translateX(0); }
}

.qf-toast--info    { border-left-color: var(--qf-primary); }
.qf-toast--success { border-left-color: #16A34A; }
.qf-toast--warn    { border-left-color: #D97706; }
.qf-toast--danger  { border-left-color: var(--qf-error); }

.qf-toast--info    .qf-toast-icon { color: var(--qf-primary); }
.qf-toast--success .qf-toast-icon { color: #16A34A; }
.qf-toast--warn    .qf-toast-icon { color: #D97706; }
.qf-toast--danger  .qf-toast-icon { color: var(--qf-error); }

.qf-toast-icon {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  margin-top: 1px;
}

.qf-toast-text {
  flex: 1 1 auto;
  font-size: var(--qf-text-body);
  line-height: 1.4;
}

.qf-toast-close {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--qf-on-surface-variant);
  padding: 2px;
  cursor: pointer;
  border-radius: var(--qf-corner-sm);
}

.qf-toast-close:hover {
  background: var(--qf-surface-variant);
  color: var(--qf-on-surface);
}

/* ============ A.20 PermPill ============ */
.qf-perm-pill {
  display: inline-flex;
  align-items: center;
  font-family: var(--qf-mono, ui-monospace, "SF Mono", Menlo, Consolas, monospace);
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--qf-on-surface-variant);
  background: var(--qf-surface-variant);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-sm);
  padding: 1px 6px;
  vertical-align: middle;
}

.qf-perm-pill::before { content: "["; opacity: 0.5; margin-right: 1px; }
.qf-perm-pill::after  { content: "]"; opacity: 0.5; margin-left: 1px; }

/* ============ B.3 Pagination ============ */
.qf-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--qf-space-6);
  padding: var(--qf-space-5) 0;
  font-size: var(--qf-text-label);
  color: var(--qf-on-surface-variant);
  flex-wrap: wrap;
}

.qf-pagination-range {
  font-variant-numeric: tabular-nums;
}

.qf-pagination-pagesize {
  display: inline-flex;
  align-items: center;
  gap: var(--qf-space-3);
}

.qf-pagination-label {
  color: var(--qf-on-surface-variant);
}

.qf-pagination-select {
  font: inherit;
  font-size: var(--qf-text-label);
  padding: var(--qf-space-2) var(--qf-space-3);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-sm);
  background: var(--qf-surface);
  color: var(--qf-on-surface);
  cursor: pointer;
}

.qf-pagination-nav {
  display: inline-flex;
  align-items: center;
  gap: var(--qf-space-3);
}

.qf-pagination-page-indicator {
  font-variant-numeric: tabular-nums;
  min-width: 56px;
  text-align: center;
  color: var(--qf-on-surface);
}

/* ============ B.2 FilterChipBar ============ */
.qf-filter-chip-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--qf-space-3);
  padding: var(--qf-space-4) 0;
}

.qf-filter-chip {
  display: inline-flex;
  align-items: stretch;
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: 999px;
  overflow: hidden;
  font-size: var(--qf-text-label);
  height: 28px;
}

.qf-filter-chip--active {
  background: var(--qf-primary-container);
  border-color: var(--qf-primary);
}

.qf-filter-chip-body {
  appearance: none;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: inherit;
  color: var(--qf-on-surface);
  padding: 0 var(--qf-space-4);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
}

.qf-filter-chip-body:hover {
  background: rgba(15, 23, 42, 0.04);
}

.qf-filter-chip--active .qf-filter-chip-body {
  color: var(--qf-on-primary-container);
}

.qf-filter-chip-label {
  color: var(--qf-on-surface-variant);
}

.qf-filter-chip--active .qf-filter-chip-label {
  color: var(--qf-on-primary-container);
}

.qf-filter-chip-separator {
  color: var(--qf-on-surface-variant);
  margin: 0 2px 0 0;
}

.qf-filter-chip-value {
  font-weight: 600;
}

.qf-filter-chip-remove {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--qf-on-surface-variant);
  cursor: pointer;
  padding: 0 var(--qf-space-3);
  display: inline-flex;
  align-items: center;
  border-left: 1px solid var(--qf-outline);
}

.qf-filter-chip-remove:hover {
  background: var(--qf-error-container);
  color: var(--qf-on-error-container);
}

.qf-filter-chip-add {
  appearance: none;
  background: transparent;
  border: 1px dashed var(--qf-outline);
  border-radius: 999px;
  color: var(--qf-on-surface-variant);
  font: inherit;
  font-size: var(--qf-text-label);
  padding: 0 var(--qf-space-4) 0 var(--qf-space-3);
  height: 28px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
}

.qf-filter-chip-add:hover {
  border-color: var(--qf-primary);
  color: var(--qf-primary);
  background: rgba(15, 118, 110, 0.04);
}

/* ============ B.8 ExportMenu ============ */
.qf-export-menu {
  position: relative;
  display: inline-block;
}

.qf-export-menu-popover {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 10;
  min-width: 220px;
  background: var(--qf-surface);
  border: 1px solid var(--qf-outline);
  border-radius: var(--qf-corner-md);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
  padding: var(--qf-space-3);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.qf-export-menu-item {
  appearance: none;
  border: 0;
  background: transparent;
  font: inherit;
  text-align: left;
  display: flex;
  align-items: center;
  gap: var(--qf-space-4);
  padding: var(--qf-space-3) var(--qf-space-4);
  border-radius: var(--qf-corner-sm);
  cursor: pointer;
  color: var(--qf-on-surface);
}

.qf-export-menu-item:hover {
  background: var(--qf-surface-variant);
}

.qf-export-menu-item-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.qf-export-menu-item-label {
  font-size: var(--qf-text-body);
  font-weight: 500;
}

.qf-export-menu-item-description {
  font-size: var(--qf-text-label);
  color: var(--qf-on-surface-variant);
}

/* ============ B.1 DataTable + B.4 SortableHeader ============ */
.qf-datatable-shell {
  display: flex;
  flex-direction: column;
  gap: var(--qf-space-3);
}

.qf-datatable-th {
  /* The base .qf-table th already has padding + border-bottom; this
     class adds alignment hooks. Keep th cosmetics in :where so per-
     screen overrides don't have to fight specificity. */
}

.qf-datatable-th--start,
.qf-datatable-td--start  { text-align: left; }
.qf-datatable-th--center,
.qf-datatable-td--center { text-align: center; }
.qf-datatable-th--end,
.qf-datatable-td--end    { text-align: right; }

/* When the header cell is right-aligned, the inner sort button needs
   to be right-aligned too — it's a flex container, so we flip it. */
.qf-datatable-th--end .qf-datatable-sort-btn { justify-content: flex-end; }
.qf-datatable-th--center .qf-datatable-sort-btn { justify-content: center; }

.qf-datatable-sort-btn {
  appearance: none;
  border: 0;
  background: transparent;
  font: inherit;
  font-weight: inherit;
  color: inherit;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--qf-space-2);
  cursor: pointer;
  text-align: inherit;
  width: 100%;
}

.qf-datatable-sort-btn:hover {
  color: var(--qf-primary);
}

.qf-datatable-sort-btn--active {
  color: var(--qf-primary);
}

.qf-datatable-row-skeleton td {
  padding-top: var(--qf-space-5);
  padding-bottom: var(--qf-space-5);
}

.qf-datatable-skeleton-cell {
  height: 14px;
  border-radius: 4px;
  background: linear-gradient(
    90deg,
    var(--qf-surface-variant) 0%,
    rgba(255, 255, 255, 0.6) 50%,
    var(--qf-surface-variant) 100%
  );
  background-size: 200% 100%;
  animation: qf-skel-shimmer 1.4s linear infinite;
}

.qf-datatable-empty-cell {
  padding: var(--qf-space-7) var(--qf-space-5) !important;
  text-align: center;
  color: var(--qf-on-surface-variant);
}

