/* ============================================
   EnrichmentHelper — Design System
   DaisyUI 5, re-skinned with the EH2 teal-console
   palette. Two theme blocks — [data-theme="dim"]
   (dark, the default) + [data-theme="light"]. The
   <html data-theme> attribute is owned by the
   pre-paint theme script in base.html; default dark.
   Load AFTER daisyui.css so our overrides win the
   cascade.
   ============================================ */

/* -----------------------------------------
   1. FONT + BASE
   ----------------------------------------- */
:root {
  --mono: 'IBM Plex Mono', ui-monospace, Menlo, Consolas, monospace;

  /* Motion system (ported from the EH2 prototype). Theme-agnostic.
     --motion-fast/-med drive micro-transitions on interactive chrome
     (nav, buttons, rows, toggles, chips); --motion-card is the ceiling
     for the heavier hover affordances. --ease-out is the entrance curve
     reserved for a later slice. Nothing here should exceed .22s. */
  --motion-fast: 0.12s;
  --motion-med:  0.15s;
  --motion-card: 0.22s;
  --ease-out: cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* Accessibility floor: honor the OS "reduce motion" setting globally.
   Collapses every animation/transition to a near-zero duration so the
   UI snaps to its end state instead of moving. Kept here, before any
   component rule, so it can only be beaten by an explicit !important
   elsewhere (there are none). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  font-family: 'IBM Plex Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: -0.005em;
  /* Fixed radial wash behind the shell — the EH2 console look. The grad
     stop colors differ per theme (set below); body paints base-200 under
     it so a no-grad fallback still reads as the surface. */
  background: var(--bg-grad), var(--color-base-200);
  background-attachment: fixed;
}

code, kbd, samp, pre {
  font-family: var(--mono);
}

/* Tailwind's CDN font-mono utility falls back to the system monospace stack;
   route it through our IBM Plex Mono token so SKU/EAN/stock and other tabular
   cells match the rest of the type system. The html prefix lifts specificity
   above Tailwind's utility, which the browser build injects after app.css. */
html .font-mono { font-family: var(--mono); }

/* Re-skin DaisyUI with the EH2 teal-console palette. We override every
   DaisyUI semantic variable so .btn / .card / .input / .badge / .modal
   read in the new colors automatically. The dark block also seeds :root
   so non-DaisyUI rules and the no-JS fallback resolve to dark.

   Token map (prototype → DaisyUI):
     surface / surface-2 / surface-3 → base-100 / base-200 / base-300
     text                            → base-content
     brand / brand-ink               → primary / primary-content (+ accent)
   The prototype's raw --surface-*, --border-*, --text-* and --brand-*
   tokens are also exposed verbatim for rules ported from EH2 that
   reference them directly. */
:root,
[data-theme="dim"] {
  color-scheme: dark;

  /* Prototype raw tokens — dark */
  --bg: #0c1015;
  --surface: #11171f;
  --surface-2: #151c26;
  --surface-3: #1b2531;
  --surface-hover: #19222e;
  --eh-line: #212b37;
  --border-soft: #1a232e;
  --border-strong: #2c3947;
  --text: #e7edf3;
  --text-muted: #93a2b2;
  --text-faint: #5e6e7e;
  --text-ghost: #43505d;
  --brand: #2fbfa8;
  --brand-ink: #061513;
  --brand-soft: #143029;
  --brand-line: #1f5046;
  --bg-grad: radial-gradient(1200px 600px at 78% -8%, #11202a 0%, #0c1015 60%);

  /* DaisyUI surface ramp ← prototype surfaces */
  --color-base-100: var(--surface);
  --color-base-200: var(--surface-2);
  --color-base-300: var(--surface-3);
  --color-base-content: var(--text);

  /* Brand teal primary + accent; warm secondary kept for legacy spots */
  --color-primary: var(--brand);
  --color-primary-content: var(--brand-ink);
  --color-secondary: #e6a94e;
  --color-secondary-content: #1a1206;
  --color-accent: var(--brand);
  --color-accent-content: var(--brand-ink);
  --color-neutral: var(--surface-2);
  --color-neutral-content: var(--text);

  /* Semantic ← prototype */
  --color-info:    #4ea7e6;
  --color-success: #46c98a;
  --color-warning: #e6a94e;
  --color-error:   #e76b7a;
  --color-violet:  #9b8cf0;

  /* Elevation scrim — black reads on both themes, but tuned per theme so
     light surfaces still get a perceptible (softer) shadow. */
  --shadow-ink: 0, 0, 0;
  --shadow-strength-sm: 0.18;
  --shadow-strength-md: 0.28;
  --shadow-strength-lg: 0.35;

  /* Utility aliases used by the page-level CSS below */
  --bc-90: color-mix(in oklch, var(--color-base-content) 92%, transparent);
  --bc-75: color-mix(in oklch, var(--color-base-content) 78%, transparent);
  --bc-60: color-mix(in oklch, var(--color-base-content) 62%, transparent);
  --bc-50: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  --bc-40: color-mix(in oklch, var(--color-base-content) 36%, transparent);
  --bc-25: color-mix(in oklch, var(--color-base-content) 20%, transparent);
  --bc-12: color-mix(in oklch, var(--color-base-content) 10%, transparent);

  --sidebar-w: 232px;
  --topbar-h: 56px;

  /* Avatar palette — deterministic per-user chip colours (see
     dashboard/_shared.py:avatar_color). 9 muted hues kept around the
     teal-console family at one consistent saturation/lightness so the
     people panel reads as a harmonised set, not a rainbow. All carry
     white initials at >=4.5:1. --eh-avatar-system is the neutral chip
     for non-person actors. */
  --eh-avatar-0: #2f9e8f;   /* teal */
  --eh-avatar-1: #3d92c4;   /* blue */
  --eh-avatar-2: #5a7fd6;   /* indigo */
  --eh-avatar-3: #8169d1;   /* violet */
  --eh-avatar-4: #a85fb0;   /* plum */
  --eh-avatar-5: #c75f86;   /* rose */
  --eh-avatar-6: #c08144;   /* amber */
  --eh-avatar-7: #4fa46a;   /* green */
  --eh-avatar-8: #3a9aae;   /* cyan */
  --eh-avatar-system: var(--surface-3);
}

[data-theme="light"] {
  color-scheme: light;

  /* Prototype raw tokens — light */
  --bg: #eef1f5;
  --surface: #ffffff;
  --surface-2: #f4f7fa;
  --surface-3: #eaeff4;
  --surface-hover: #f0f4f8;
  --eh-line: #dde4ec;
  --border-soft: #e8edf3;
  --border-strong: #c6d1dd;
  --text: #16202b;
  --text-muted: #586675;
  --text-faint: #8b98a6;
  --text-ghost: #aab5c1;
  --brand: #138b78;
  --brand-ink: #ffffff;
  --brand-soft: #dcf2ec;
  --brand-line: #a9ddd1;
  --bg-grad: radial-gradient(1100px 560px at 80% -10%, #e2eef0 0%, #eef1f5 55%);

  --color-base-100: var(--surface);
  --color-base-200: var(--surface-2);
  --color-base-300: var(--surface-3);
  --color-base-content: var(--text);

  --color-primary: var(--brand);
  --color-primary-content: var(--brand-ink);
  --color-secondary: #b9791a;
  --color-secondary-content: #ffffff;
  --color-accent: var(--brand);
  --color-accent-content: var(--brand-ink);
  --color-neutral: var(--surface-3);
  --color-neutral-content: var(--text);

  --color-info:    #2479c4;
  --color-success: #1f9d63;
  --color-warning: #b9791a;
  --color-error:   #cf3d52;
  --color-violet:  #6a52d6;

  /* Lighter surfaces need a softer-but-present scrim. */
  --shadow-ink: 30, 41, 59;
  --shadow-strength-sm: 0.08;
  --shadow-strength-md: 0.12;
  --shadow-strength-lg: 0.16;

  /* Avatar palette — deeper than the dim set so white initials keep
     >=4.5:1 on the lighter surface. Same hue order, so a given user's
     chip reads as "the same colour" across themes. */
  --eh-avatar-0: #1d8576;   /* teal */
  --eh-avatar-1: #2476a8;   /* blue */
  --eh-avatar-2: #3f5fc0;   /* indigo */
  --eh-avatar-3: #6748c2;   /* violet */
  --eh-avatar-4: #97419f;   /* plum */
  --eh-avatar-5: #b53e6b;   /* rose */
  --eh-avatar-6: #a5662a;   /* amber */
  --eh-avatar-7: #2f8a51;   /* green */
  --eh-avatar-8: #21808f;   /* cyan */
  --eh-avatar-system: var(--surface-3);
}

/* -----------------------------------------
   1b. ICON SPRITE
   Shared <symbol> defs live in the global sprite partial
   (templates/partials/_icon_sprite.html, included from base.html).
   Inline SVG <use> references read currentColor + carry the
   1.6px stroke baked into each <symbol>. The .icon class is
   the universal sizing surface; ad-hoc sizes override width/height
   inline. The sprite host itself is display:none so it never
   reflows the page even if the partial is included mid-body.
   ----------------------------------------- */
.icon-sprite-host { display: none; }

.icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  display: inline-block;
  vertical-align: middle;
  color: currentColor;
  /* Presentation attributes on the sprite host don't inherit through
     <use> per SVG spec, so the marks read as filled black blobs unless
     we re-apply fill:none + stroke here. Stroke-width 1.6 matches the
     lucide style baked into each <symbol>. */
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon-sm { width: 13px; height: 13px; }
.icon-xs { width: 11px; height: 11px; }
.icon-lg { width: 18px; height: 18px; }

/* -----------------------------------------
   2. APP SHELL LAYOUT (sidebar + topbar)
   Layout spec matches Claude Design prototype:
   232px sidebar, brand → scrollable nav, no
   footer. User moved to top-right of topbar,
   which carries a "Synced Xm ago" live chip.
   ----------------------------------------- */
.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
  background: var(--color-base-200);
}

/* Collapsed icon-rail variant. State is set on <html> (pre-paint script
   in base.html reads localStorage['eh.sidebar.collapsed'] before first
   paint). Above the 900px mobile breakpoint only — below that the
   sidebar is a drawer and ignores the collapse state entirely. */
:root[data-sidebar="collapsed"] .app-shell {
  grid-template-columns: 56px 1fr;
}

@media (max-width: 900px) {
  .app-shell { grid-template-columns: 1fr; }
  /* On mobile, ignore the desktop collapse state — the sidebar is a
     drawer toggled by the topbar hamburger. */
  :root[data-sidebar="collapsed"] .app-shell { grid-template-columns: 1fr; }
  .app-sidebar { display: none; }
  .app-sidebar.is-open { display: flex; position: fixed; inset: 0 auto 0 0; width: 260px; z-index: 60; box-shadow: 0 0 0 100vw rgba(15, 20, 34, 0.42); }
}

.app-sidebar {
  background: var(--color-base-300);
  border-right: 1px solid var(--bc-12);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  z-index: 10;
  /* No padding — children carry their own: brand (header height),
     scroll (its own inset). Keeps the brand bar flush with the topbar. */
  padding: 0;
}

/* Brand — gradient square mark + "EnrichmentHelper" wordmark.
   Height matches .app-topbar so the top edge reads as a single band. */
.app-sidebar__brand {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0 1.125rem;
  height: var(--topbar-h);
  border-bottom: 1px solid var(--bc-12);
  color: var(--color-base-content);
  text-decoration: none;
  flex-shrink: 0;
}

/* Original brand logo — rendered as-is (no gradient tint box). */
.app-sidebar__logo {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  display: block;
}

/* Theme-swapped brand logos — the cube's pale top facet washes out on a
   light sidebar, so each theme shows its own recolored variant. Element+class
   specificity so the toggle wins over .app-sidebar__logo's display. */
img.themed-logo--light { display: none; }
[data-theme="light"] img.themed-logo--dark { display: none; }
[data-theme="light"] img.themed-logo--light { display: block; }

/* Products table selection — EH2 square. The DaisyUI default checkbox border
   washes out on the teal-console surfaces; draw a clear bordered square that
   fills brand on check. Scoped to the products table so other checkboxes are
   untouched. Templates pair this with checkbox-xs — the sole sanctioned
   size exception, justified by the dense ~50-row product grid; all other
   form checkboxes use the canonical checkbox-sm. */
.table-products input[type="checkbox"].checkbox {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  border: 1.5px solid var(--border-strong);
  background-color: var(--surface);
  display: inline-grid;
  place-items: center;
  cursor: pointer;
  transition: background-color .12s, border-color .12s;
  flex: none;
}
.table-products input[type="checkbox"].checkbox:hover { border-color: var(--color-primary); }
.table-products input[type="checkbox"].checkbox:checked,
.table-products input[type="checkbox"].checkbox:indeterminate {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
}
.table-products input[type="checkbox"].checkbox:checked::after {
  content: "";
  width: 4px;
  height: 8px;
  border: solid var(--color-primary-content);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translateY(-1px);
}
.table-products input[type="checkbox"].checkbox:indeterminate::after {
  content: "";
  width: 8px;
  height: 2px;
  background-color: var(--color-primary-content);
}

/* Visible checkbox for forms outside the products table (settings cockpit
   hide-brand toggle, alias scope, etc.). The bare DaisyUI .checkbox is
   near-invisible on the dark surface; this mirrors the products-table
   treatment so a checkbox reads as a checkbox everywhere. */
input[type="checkbox"].checkbox-visible {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  border: 1.5px solid var(--border-strong);
  background-color: var(--surface);
  display: inline-grid;
  place-items: center;
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
  flex: none;
}
input[type="checkbox"].checkbox-visible:hover { border-color: var(--color-primary); }
input[type="checkbox"].checkbox-visible:checked {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
}
input[type="checkbox"].checkbox-visible:checked::after {
  content: "";
  width: 4px;
  height: 8px;
  border: solid var(--color-primary-content);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translateY(-1px);
}

.app-sidebar__wordmark {
  font-weight: 600;
  font-size: 0.86rem;
  letter-spacing: -0.015em;
}
.app-sidebar__wordmark em {
  font-style: normal;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  font-weight: 500;
}

.app-sidebar__scroll {
  flex: 1;
  overflow-y: auto;
  padding: 0.75rem 0.625rem 1rem;
}

.app-sidebar__section + .app-sidebar__section { margin-top: 0.875rem; }

.section-label {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
  padding: 0.375rem 0.625rem 0.25rem;
}

.app-nav-link {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.45rem 0.625rem;
  border-radius: 0.45rem;
  font-size: 0.82rem;
  line-height: 1.3;
  color: color-mix(in oklch, var(--color-base-content) 78%, transparent);
  transition: background-color 0.12s ease, color 0.12s ease;
  text-decoration: none;
  font-weight: 500;
  border: 0;
  background: transparent;
  width: 100%;
  text-align: left;
  cursor: pointer;
}

.app-nav-link:hover {
  background: color-mix(in oklch, var(--color-base-content) 5%, transparent);
  color: var(--color-base-content);
}

.app-nav-link__icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
}

.app-nav-link.is-active {
  background: color-mix(in oklch, var(--color-primary) 14%, transparent);
  color: var(--color-primary);
  font-weight: 600;
}
.app-nav-link.is-active .app-nav-link__icon { color: var(--color-primary); }

.app-nav-link__count {
  margin-left: auto;
  font-size: 0.69rem;
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in oklch, var(--color-base-content) 10%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
}
.app-nav-link.is-active .app-nav-link__count {
  background: color-mix(in oklch, var(--color-primary) 18%, transparent);
  color: var(--color-primary);
}

/* Sidebar collapse toggle — pinned to the bottom of the rail. Slim
   chevron button using the same icon/text muted-content tone the nav
   links use. Two icon variants live inside the button; CSS swaps which
   one is visible based on collapsed state. */
.app-sidebar__toggle {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.4rem;
  height: 2.25rem;
  padding: 0 0.75rem;
  margin: 0.25rem 0.5rem 0.5rem;
  border: 0;
  border-radius: 0.4rem;
  background: transparent;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.app-sidebar__toggle:hover {
  background: color-mix(in oklch, var(--color-base-content) 5%, transparent);
  color: var(--color-base-content);
}
.app-sidebar__toggle-icon { display: block; }
.app-sidebar__toggle-icon--expand { display: none; }

/* ---------- Collapsed state ---------- */
:root[data-sidebar="collapsed"] .app-sidebar {
  /* Width follows the grid, but constrain children that escape the
     56px rail (e.g. text spans) by clipping. */
  overflow-x: hidden;
}

/* Brand row: hide wordmark, center the logo. */
:root[data-sidebar="collapsed"] .app-sidebar__brand {
  padding: 0;
  justify-content: center;
}
:root[data-sidebar="collapsed"] .app-sidebar__wordmark { display: none; }

/* Section labels disappear; the Workspace/System divider survives as a
   1px line between the two sections (silent visual break). */
:root[data-sidebar="collapsed"] .section-label { display: none; }
:root[data-sidebar="collapsed"] .app-sidebar__section + .app-sidebar__section {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--color-base-300);
}

/* Tighten scroll inset so 16px icons sit centred in the 56px rail. */
:root[data-sidebar="collapsed"] .app-sidebar__scroll {
  padding: 0.5rem 0.375rem 0.75rem;
}

/* Nav links: icon-only, centred. Hide the label span. Keep relative
   positioning so the hover tooltip can flyout to the right. */
:root[data-sidebar="collapsed"] .app-nav-link {
  position: relative;
  justify-content: center;
  padding: 0.5rem 0;
  gap: 0;
}
:root[data-sidebar="collapsed"] .app-nav-link > span { display: none; }
:root[data-sidebar="collapsed"] .app-nav-link__count { display: none; }

/* Pure-CSS hover flyout tooltip. Reads data-label from the link and
   parks itself just to the right of the rail. 200ms delay-in keeps
   accidental sweeps from flashing labels everywhere. */
:root[data-sidebar="collapsed"] .app-nav-link::after {
  content: attr(data-label);
  position: absolute;
  left: calc(100% + 0.375rem);
  top: 50%;
  transform: translateY(-50%);
  background: var(--color-base-100);
  color: var(--color-base-content);
  border: 1px solid var(--color-base-300);
  border-radius: 0.35rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.75rem;
  font-weight: 500;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease 0s;
  z-index: 30;
  box-shadow: 0 4px 12px rgba(var(--shadow-ink), var(--shadow-strength-md));
}
:root[data-sidebar="collapsed"] .app-nav-link:hover::after {
  opacity: 1;
  transition-delay: 0.2s;
}

/* Toggle button in collapsed mode: center it, swap which chevron shows. */
:root[data-sidebar="collapsed"] .app-sidebar__toggle {
  justify-content: center;
  padding: 0;
  margin: 0.25rem 0.375rem 0.5rem;
}
:root[data-sidebar="collapsed"] .app-sidebar__toggle-icon--collapse { display: none; }
:root[data-sidebar="collapsed"] .app-sidebar__toggle-icon--expand { display: block; }

.app-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Topbar — sticky band with blur. Layout:
   [mobile toggle] [breadcrumbs] [synced chip] [enrichment status] [user] */
.app-topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--topbar-h);
  background: color-mix(in oklch, var(--color-base-100) 82%, transparent);
  backdrop-filter: saturate(150%) blur(10px);
  -webkit-backdrop-filter: saturate(150%) blur(10px);
  border-bottom: 1px solid var(--bc-12);
  padding: 0 1.25rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.app-topbar__breadcrumbs {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  font-size: 0.82rem;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
}
.app-topbar__breadcrumbs .crumb-current {
  color: var(--color-base-content);
  font-weight: 500;
}
.app-topbar__breadcrumbs a {
  color: inherit;
  text-decoration: none;
}
.app-topbar__breadcrumbs a:hover {
  color: var(--color-base-content);
  text-decoration: underline;
}
.app-topbar__breadcrumbs svg { opacity: 0.55; flex-shrink: 0; }

/* Synced chip — live-dot pulse + "Synced Xm ago" text. Sits on the
   right side of the topbar so it stays visible while the breadcrumbs
   take any overflow on narrow viewports. */
/* Slot wrapper — pushes the chip to the right even when HTMX hasn't
   yet populated it, so the topbar layout doesn't jump when the
   fragment arrives. */
.app-topbar__sync-slot {
  display: flex;
  align-items: center;
  min-height: 24px;
  flex-shrink: 0;
}

/* Theme toggle anchors the right-aligned topbar cluster (it's the first
   element after the breadcrumb, so the auto margin lives here now). The
   two glyphs are stacked; only the one offering the *other* mode shows. */
.app-topbar__theme-toggle {
  margin-left: auto;
  flex-shrink: 0;
  color: var(--bc-60);
  transition: color var(--motion-fast) var(--ease-out);
}
.app-topbar__theme-toggle:hover { color: var(--color-base-content); }
.app-topbar__theme-toggle .app-theme-toggle__sun { display: none; }
.app-topbar__theme-toggle .app-theme-toggle__moon { display: inline-block; }
[data-theme="light"] .app-topbar__theme-toggle .app-theme-toggle__sun { display: inline-block; }
[data-theme="light"] .app-topbar__theme-toggle .app-theme-toggle__moon { display: none; }

.app-topbar__sync {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  padding: 0.25rem 0.625rem 0.25rem 0.5rem;
  border-radius: 999px;
  border: 1px solid var(--color-base-300);
  background: var(--color-base-200);
  flex-shrink: 0;
}

/* Never-synced variant: amber icon+text signals "no Akeneo pull has
   completed in this workspace yet — trigger one from /products". */
.app-topbar__sync--warn {
  color: var(--color-warning);
  border-color: color-mix(in oklch, var(--color-warning) 35%, transparent);
  background: color-mix(in oklch, var(--color-warning) 10%, var(--color-base-200));
}

/* Green pulsing dot — shared with the Activity Feed "Live" meta label. */
@keyframes live-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(87, 178, 138, 0.55); }
  70%  { box-shadow: 0 0 0 6px rgba(87, 178, 138, 0); }
  100% { box-shadow: 0 0 0 0 rgba(87, 178, 138, 0); }
}
.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-success);
  display: inline-block;
  animation: live-pulse 2s infinite;
  flex-shrink: 0;
}

/* Enriching-batch pill — sits next to the Synced chip in the topbar.
   Matches the canonical .app-topbar__live design: rounded pill, info
   pulse-dot on the left, "Enriching N/M" text. Reuses .app-topbar__sync
   chrome for parity (border, background, padding) so the two chips
   read as a row, not as competing widgets. */
@keyframes enrich-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.7); }
}
.app-topbar__live {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
  padding: 0.25rem 0.625rem 0.25rem 0.5rem;
  border-radius: 999px;
  border: 1px solid var(--color-base-300);
  background: var(--color-base-200);
  text-decoration: none;
  flex-shrink: 0;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.app-topbar__live:hover {
  background: color-mix(in oklch, var(--color-base-content) 5%, transparent);
  color: var(--color-base-content);
}
.app-topbar__live .pulse {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-info);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-info) 22%, transparent);
  animation: enrich-pulse 1.6s ease-in-out infinite;
  flex-shrink: 0;
}
.app-topbar__live b {
  color: color-mix(in oklch, var(--color-base-content) 92%, transparent);
  font-weight: 600;
  margin-left: 2px;
  font-variant-numeric: tabular-nums;
}
.app-topbar__live--failed {
  color: color-mix(in oklch, var(--color-warning) 90%, var(--color-base-content));
  border-color: color-mix(in oklch, var(--color-warning) 35%, var(--color-base-300));
}
.app-topbar__live--failed .pulse {
  background: var(--color-warning);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-warning) 22%, transparent);
}

/* User pill in topbar: email + avatar; clicking opens the dropdown. */
.app-topbar__user {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.2rem 0.25rem 0.2rem 0.6rem;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
  cursor: pointer;
  color: var(--color-base-content);
  line-height: 1;
  flex-shrink: 0;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.app-topbar__user:hover {
  background: color-mix(in oklch, var(--color-base-content) 5%, transparent);
  border-color: var(--color-base-300);
}
.app-topbar__user-email {
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Hide the inline email below 1366px — full email + display name still
   live in the avatar dropdown, so identity is one click away. The 1366
   breakpoint matches common 13-14" laptop widths. */
@media (max-width: 1365px) {
  .app-topbar__user-email { display: none; }
}
.app-topbar__avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  --eh-avatar: var(--brand);
  background: linear-gradient(135deg, color-mix(in oklch, var(--eh-avatar) 82%, black), var(--eh-avatar));
  color: white;
  display: grid;
  place-items: center;
  font-size: 0.68rem;
  font-weight: 600;
  flex-shrink: 0;
  letter-spacing: 0;
}

.app-content {
  /* Canonical was 1480px; nudged to 1520px (+40px) so dense tables
     (products, sources) get one extra column of breathing room without
     pulling the shell off-center on standard laptop displays. Bottom
     inset stays at 4rem so a long form's last row clears the action-
     bar's drop-shadow; validate page overrides bottom to 0 below so
     the sticky action-bar can flush against the viewport edge. */
  padding: 1.375rem 1.75rem 2.5rem;
  width: 100%;
  max-width: 95rem;               /* 1520px */
}

/* On the validate page the sticky .action-bar must reach the viewport
   bottom flush. App-content's bottom padding would otherwise push the
   sticky's containing block up by that amount, leaving a visible gap.
   Drop the bottom inset to zero when a validate-shell is present and
   let the action-bar's own padding be the only spacer. */
.app-content:has(.validate-shell) { padding-bottom: 0; }

/* Opt-in narrow column for text-heavy pages (login, profile) that
   shouldn't stretch edge-to-edge on ultrawide displays. Wrap the page
   body in <div class="app-content-narrow"> to get the old 1400px cap. */
.app-content-narrow {
  max-width: 1400px;
  margin: 0 auto;
}

.app-mobile-toggle {
  display: none;
}

@media (max-width: 900px) {
  .app-mobile-toggle { display: inline-flex; }
  .app-content { padding: 1rem 1rem 2.5rem; }
  /* On narrow viewports collapse the sync chip label — keep the dot so
     the page still signals that it's live. */
  .app-topbar__sync span:not(.live-dot) { display: none; }
}

/* -----------------------------------------
   3. TABLE POLISH
   ----------------------------------------- */
.table-zebra tbody tr {
  transition: background-color 0.15s ease;
}

.table-zebra tbody tr:hover {
  background-color: color-mix(in oklch, var(--color-primary) 8%, transparent);
}

.table thead th {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
  background: color-mix(in oklch, var(--color-base-200) 50%, var(--color-base-100));
  border-bottom: 1px solid var(--color-base-300);
}

/* Memory-viewer scroll container — sticky thead so column meaning
   stays visible while scrolling through hundreds of memory entries.
   The opaque background on each <th> matters: without it the
   scrolled rows show through and make the header unreadable. */
.memory-viewer__sticky-head th {
  position: sticky;
  top: 0;
  z-index: 1;
  /* Same background used for normal table headers so the sticky row
     looks identical to the static one until you start scrolling. */
  background: color-mix(in oklch, var(--color-base-200) 92%, var(--color-base-100));
}

/* -----------------------------------------
   4. GLOBAL TRANSITIONS + COMPONENT RESETS
   ----------------------------------------- */
.btn, .card, .badge, .input, .select, .textarea, .toggle, .checkbox {
  transition: all var(--motion-med) var(--ease-out);
}

/* Modern PIMs use flat cards — no lift. Keep a subtle shadow only on
   explicit .card-elevated. The old global lift caused jitter on dense
   review screens. */
.card {
  box-shadow: none;
  border: 1px solid var(--color-base-300);
  background: var(--color-base-100);
  border-radius: 0.625rem;
}

.card:hover { transform: none; box-shadow: none; }

.card-elevated {
  box-shadow: 0 1px 2px rgba(15, 20, 34, 0.04), 0 2px 6px rgba(15, 20, 34, 0.04);
}

/* Buttons: tighten radius, flatten default */
.btn {
  border-radius: 0.5rem;
  font-weight: 500;
  letter-spacing: 0;
}

.btn-primary {
  box-shadow: 0 1px 0 color-mix(in oklch, var(--color-primary) 60%, transparent);
}

/* Inputs / selects: slimmer, more PIM-like */
.input, .select, .textarea {
  border-radius: 0.5rem;
  background: var(--color-base-100);
}

.input-bordered, .select-bordered, .textarea-bordered {
  border-color: var(--color-base-300);
}

.input-bordered:focus, .select-bordered:focus, .textarea-bordered:focus {
  outline: 2px solid color-mix(in oklch, var(--color-primary) 35%, transparent);
  outline-offset: 1px;
  border-color: color-mix(in oklch, var(--color-primary) 40%, var(--color-base-300));
}

/* Smooth link underlines */
a:not(.btn):not(.nav-active):not([class*="menu"]):not(.app-nav-link):not(.app-sidebar__brand) {
  transition: color 0.15s ease;
}

/* -----------------------------------------
   5. HTMX SWAP TRANSITIONS
   ----------------------------------------- */
.htmx-added { animation: htmxFadeIn 0.25s ease-out both; }
.htmx-swapping { opacity: 0; transition: opacity 0.15s ease-out; }
.htmx-settling { animation: htmxFadeIn 0.2s ease-out both; }

@keyframes htmxFadeIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* -----------------------------------------
   6. MICRO-INTERACTION KEYFRAMES
   ----------------------------------------- */
@keyframes slideInRight {
  from { transform: translateX(100%); opacity: 0; }
  to   { transform: translateX(0); opacity: 1; }
}
@keyframes slideUp {
  from { transform: translateY(100%); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes skeletonPulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 1; }
}

.anim-slide-in-right   { animation: slideInRight 0.3s ease-out both; }
.anim-slide-up         { animation: slideUp 0.3s ease-out both; }
.anim-fade-in          { animation: fadeIn 0.25s ease-in both; }
.anim-skeleton-pulse   { animation: skeletonPulse 1.5s ease-in-out infinite; }

/* -----------------------------------------
   7. VERTICAL TIMELINE (Trace Viewer)
   ----------------------------------------- */
.trace-timeline {
  position: relative;
  padding-left: 2rem;
  margin-left: 0.75rem;
}

.trace-timeline::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0.75rem;
  width: 2px;
  background-color: var(--color-base-300);
}

.trace-step { position: relative; padding-bottom: 1.25rem; }

.trace-step::before {
  content: "";
  position: absolute;
  left: -1.6rem;
  top: 0.35rem;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  background-color: var(--color-primary);
  border: 2px solid var(--color-base-100);
  z-index: 1;
}

.trace-step:last-child { padding-bottom: 0; }

.trace-step.active::before {
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 30%, transparent);
}

.trace-step--reasoning::before { background-color: var(--color-base-content); opacity: 0.4; }
.trace-step--tool::before      { background-color: var(--color-primary); }
.trace-step--decision::before  { background-color: var(--color-success); }

/* -----------------------------------------
   7b. RAIL TIMELINE (V2 search-loop trace)
   Ported from the EH2 prototype's .tr-*/.trs-* system. EH2's raw
   --*-soft/-line tokens don't exist here; soft fills are derived from
   our semantic --color-* tokens via color-mix (the established pattern).
   ----------------------------------------- */
.tr-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem; }
.tr-title { font-size: 0.8125rem; font-weight: 600; }
.tr-count { font-size: 0.6875rem; color: var(--text-faint); }

.tr-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: 0.5rem;
  margin-bottom: 0.875rem;
}
.tr-stat {
  border: 1px solid var(--eh-line);
  border-radius: 0.5rem;
  background: var(--surface-2);
  padding: 0.625rem 0.5rem;
}
.tr-stat-v { font-size: 1.25rem; font-weight: 600; line-height: 1.1; }
.tr-stat-v.ok   { color: var(--color-success); }
.tr-stat-v.warn { color: var(--color-warning); }
.tr-stat-v.err  { color: var(--color-error); }
.tr-stat-l {
  font-size: 0.625rem;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  margin-top: 0.1875rem;
}

.tr-steps { padding: 0.25rem 0 0.5rem; }
.tr-step { display: flex; gap: 0.75rem; }
.tr-step.is-skipped { opacity: 0.55; }

.trs-rail { display: flex; flex-direction: column; align-items: center; flex: none; }
.trs-dot {
  width: 24px; height: 24px;
  border-radius: 7px;
  display: grid; place-items: center;
  flex: none; margin-top: 0.25rem;
}
.trs-dot .icon { width: 13px; height: 13px; }
.trs-line { width: 2px; flex: 1; background: var(--eh-line); margin: 0.1875rem 0; min-height: 8px; }

.trs-dot.seed    { background: var(--surface-3); color: var(--text-muted); }
.trs-dot.tool    { background: color-mix(in oklch, var(--color-info) 16%, var(--surface)); color: var(--color-info); }
.trs-dot.extract { background: var(--brand-soft); color: var(--color-primary); }
.trs-dot.score   { background: var(--brand-soft); color: var(--color-primary); }
.trs-dot.gate    { background: color-mix(in oklch, var(--color-warning) 16%, var(--surface)); color: var(--color-warning); }
.trs-dot.memory  { background: color-mix(in oklch, var(--color-info) 16%, var(--surface)); color: var(--color-info); }
.trs-dot.ok      { background: color-mix(in oklch, var(--color-success) 16%, var(--surface)); color: var(--color-success); }
.trs-dot.warn    { background: color-mix(in oklch, var(--color-warning) 16%, var(--surface)); color: var(--color-warning); }
.trs-dot.err     { background: color-mix(in oklch, var(--color-error) 16%, var(--surface)); color: var(--color-error); }

.trs-body { flex: 1; min-width: 0; padding: 0.25rem 0 0.625rem; }
.trs-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.trs-tool { font-size: 0.78125rem; font-weight: 600; }
.trs-domain { font-size: 0.6875rem; color: var(--text-faint); }
.trs-tier {
  font-size: 0.5625rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.3px;
  color: var(--text-muted);
  background: var(--surface-2); border: 1px solid var(--eh-line);
  border-radius: 4px; padding: 0 5px;
}
.trs-meta { display: flex; align-items: center; gap: 0.625rem; margin-left: auto; }
.trs-score { font-size: 0.71875rem; font-weight: 600; color: var(--color-primary); }
.trs-faint { font-size: 0.6875rem; color: var(--text-faint); }

.trs-res {
  font-size: 0.625rem; font-weight: 600;
  padding: 1px 7px; border-radius: 99px;
  text-transform: uppercase; letter-spacing: 0.3px;
}
.trs-res.ok      { color: var(--color-success); background: color-mix(in oklch, var(--color-success) 16%, var(--surface)); }
.trs-res.extract { color: var(--color-primary); background: var(--brand-soft); }
.trs-res.weak    { color: var(--color-warning); background: color-mix(in oklch, var(--color-warning) 16%, var(--surface)); }
.trs-res.skip    { color: var(--text-faint); background: var(--surface-3); }
.trs-res.miss,
.trs-res.err     { color: var(--color-error); background: color-mix(in oklch, var(--color-error) 16%, var(--surface)); }

.trs-msg { font-size: 0.78125rem; color: var(--text-muted); margin-top: 0.25rem; line-height: 1.45; }

.trs-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem; margin-top: 0.375rem; }
.trs-src { font-size: 0.65625rem; color: var(--text-faint); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trs-fchip {
  font-size: 0.65625rem; color: var(--color-primary);
  background: var(--brand-soft); border: 1px solid var(--brand-line);
  border-radius: 4px; padding: 1px 6px;
}
.trs-query {
  font-size: 0.65625rem; color: var(--text-faint);
  background: var(--surface-2); border: 1px solid var(--eh-line);
  border-radius: 4px; padding: 1px 6px;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.tr-stopline {
  margin-top: 0.5rem; font-size: 0.6875rem; color: var(--text-faint);
}
.tr-stopline.warn { color: var(--color-warning); }

.tr-sources { margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid var(--border-soft); }
.tr-sources-h {
  font-size: 0.6875rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.4px;
  color: var(--text-faint); margin-bottom: 0.375rem;
}
.tr-sources-row { display: flex; flex-wrap: wrap; gap: 0.375rem; }

/* -----------------------------------------
   8. ACCENT BORDER HELPERS
   ----------------------------------------- */
.border-l-primary { border-left: 4px solid var(--color-primary); }

/* -----------------------------------------
   9. DESIGN SYSTEM — page chrome
   ----------------------------------------- */

/* Avatar circle (user initial) */
.avatar-initial {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: 9999px;
  background: color-mix(in oklch, var(--color-primary) 18%, transparent);
  color: var(--color-primary);
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.01em;
}

/* Content card: shared container for product list table, etc.
   ``overflow: visible`` lets header-anchored dropdowns (brand/supplier
   typeahead) escape the card boundary; the body keeps its own clip so
   the table still respects the rounded bottom corners. */
.content-card {
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.625rem;
  overflow: visible;
}

.content-card-header {
  padding: 0.875rem 1rem;
  border-bottom: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 50%, var(--color-base-100));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.content-card-header:first-child {
  border-top-left-radius: inherit;
  border-top-right-radius: inherit;
}

.content-card-body {
  padding: 0;
  overflow: hidden;
  border-bottom-left-radius: inherit;
  border-bottom-right-radius: inherit;
}
.content-card-body:first-child {
  border-top-left-radius: inherit;
  border-top-right-radius: inherit;
}

/* Attention panel — clickable alert rows. Severity sets the left border
   accent and the icon-square tint. The row links to the relevant
   workspace; the chevron + hover shading sells "click me".
   Canonical: 11px 18px padding, 30×30 icon, 13px label, 11.5px hint. */
.attention-row {
  display: flex;
  align-items: stretch;
  border-left: 3px solid transparent;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-content) 10%, transparent);
  position: relative;
  transition: background 0.12s ease;
}
.attention-row:last-child { border-bottom: 0; }
.attention-row:hover {
  background: color-mix(in oklch, var(--color-base-content) 6%, transparent);
}
.attention-row__main {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 0.75rem;            /* 12px */
  padding: 0.6875rem 0.5rem 0.6875rem 1.125rem;
  text-decoration: none;
  color: inherit;
  min-width: 0;
}
.attention-row__dismiss {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;          /* 28px */
  margin: 0.5rem 0.5rem 0.5rem 0;
  border-radius: 0.375rem;
  border: 0;
  background: transparent;
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.12s ease, color 0.12s ease;
}
.attention-row__dismiss:hover {
  background: color-mix(in oklch, var(--color-base-content) 10%, transparent);
  color: var(--color-base-content);
}
.attention-row__dismiss svg { width: 14px; height: 14px; }
.attention-row__icon {
  width: 1.875rem;         /* 30px */
  height: 1.875rem;
  border-radius: 0.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.attention-row__icon svg {
  width: 16px;
  height: 16px;
}
.attention-row__body { flex: 1; min-width: 0; }
.attention-row__label {
  font-size: 0.8125rem;    /* 13px */
  font-weight: 500;
  color: var(--color-base-content);
  line-height: 1.35;
}
.attention-row__hint {
  font-size: 0.71875rem;   /* 11.5px */
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-top: 0.125rem;
  line-height: 1.4;
}
.attention-row__chevron {
  color: color-mix(in oklch, var(--color-base-content) 40%, transparent);
  margin-left: auto;
  flex-shrink: 0;
  width: 14px;
  height: 14px;
}
.attention-row--error {
  border-left-color: var(--color-error);
}
.attention-row--error .attention-row__icon {
  background: color-mix(in oklch, var(--color-error) 14%, transparent);
  color: var(--color-error);
}
.attention-row--warning {
  border-left-color: var(--color-warning);
}
.attention-row--warning .attention-row__icon {
  background: color-mix(in oklch, var(--color-warning) 18%, transparent);
  color: var(--color-warning);
}
.attention-row--info {
  border-left-color: var(--color-info);
}
.attention-row--info .attention-row__icon {
  background: color-mix(in oklch, var(--color-info) 14%, transparent);
  color: var(--color-info);
}

/* Attention empty state — green-checkmark + "Nothing needs attention".
   Tightened to ~11px vertical so it matches the canonical row rhythm
   instead of feeling like a half-height blank panel. */
.attention-empty {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.6875rem 1.125rem;  /* 11px 18px — matches .attention-row */
  font-size: 0.8125rem;
  color: color-mix(in oklch, var(--color-base-content) 78%, transparent);
}
.attention-empty__icon {
  width: 1.25rem;          /* 20px */
  height: 1.25rem;
  border-radius: 9999px;
  background: color-mix(in oklch, var(--color-success) 16%, transparent);
  color: var(--color-success);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.attention-empty__icon svg { width: 12px; height: 12px; }
.attention-empty__meta {
  margin-left: auto;
  font-size: 0.71875rem;
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
}

/* ============================ HEALTH CARD ==============================
   Canonical structure (Downloads/EnrichtmentHelper/app/styles.css L1220):
     .health-row         row container, 10px 0 padding, hairline divider
     .health-row__main   name + detail block, grows
     .health-row__name   13px / 500 / bc-90
     .health-row__detail 11.5px / muted
     .health-row__bar    140px utilization bar (storage-headline shape)
     .health-row__count  11.5px tnum, 48px min-width, right aligned
   Status pill sits at the very end of the row. */
.health-row {
  display: flex;
  align-items: center;
  gap: 0.875rem;           /* 14px */
  padding: 0.625rem 0;     /* 10px 0 — card-body provides 18px sides */
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-content) 8%, transparent);
}
.health-row:last-child { border-bottom: 0; }
.health-row__main { flex: 1; min-width: 0; }
.health-row__name {
  font-size: 0.8125rem;    /* 13px */
  font-weight: 500;
  color: color-mix(in oklch, var(--color-base-content) 92%, transparent);
  line-height: 1.35;
}
.health-row__detail {
  font-size: 0.71875rem;   /* 11.5px */
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-top: 0.125rem;
  line-height: 1.4;
}
.health-row__bar {
  width: 140px;
  flex-shrink: 0;
}
.health-row__count {
  font-size: 0.71875rem;   /* 11.5px */
  color: color-mix(in oklch, var(--color-base-content) 65%, transparent);
  min-width: 48px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.health-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.625rem;
  padding: 0.625rem 0 0;
  margin-top: 0.5rem;
  border-top: 1px solid color-mix(in oklch, var(--color-base-300) 70%, transparent);
}

/* Active-now bubble on the People panel — small green dot + label */
.active-now-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--color-success);
  padding: 0.1rem 0.5rem;
  border-radius: 9999px;
  background: color-mix(in oklch, var(--color-success) 14%, transparent);
  white-space: nowrap;
}
.active-now-pill::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 9999px;
  background: var(--color-success);
  box-shadow: 0 0 0 2px color-mix(in oklch, var(--color-success) 24%, transparent);
  animation: active-now-pulse 1.8s ease-in-out infinite;
}
@keyframes active-now-pulse {
  0%, 100% { box-shadow: 0 0 0 2px color-mix(in oklch, var(--color-success) 24%, transparent); }
  50%      { box-shadow: 0 0 0 4px color-mix(in oklch, var(--color-success) 12%, transparent); }
}

/* Section label (validate review, etc) */
.section-label {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: color-mix(in oklch, var(--color-base-content) 65%, transparent);
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--color-base-300);
  margin-bottom: 0.75rem;
  width: 100%;
}

.section-label-hint {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 40%, transparent);
  font-style: italic;
}

/* Side-by-side field comparison */
.compare-current {
  border-left: 3px solid color-mix(in oklch, var(--color-info) 45%, transparent);
  padding-left: 0.75rem;
}

.compare-enriched {
  border-left: 3px solid color-mix(in oklch, var(--color-primary) 55%, transparent);
  padding-left: 0.75rem;
}

.compare-arrow {
  display: none;
  align-items: center;
  justify-content: center;
  color: color-mix(in oklch, var(--color-base-content) 35%, transparent);
}

@media (min-width: 1024px) {
  .compare-arrow { display: flex; }
}

/* Hero Approve button */
.btn-approve-hero {
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0 1.25rem;
  height: 2.5rem;
  box-shadow: 0 2px 0 color-mix(in oklch, var(--color-primary) 60%, transparent);
}

/* -----------------------------------------
   9b. SETTINGS / INSIGHTS SUBNAV
   In-page secondary rail rendered inside .app-content. Not a second
   sidebar — sticks to the top of the scroll area at topbar height + 16px
   so it never overlaps the sticky topbar. The host page wraps content
   in .settings-layout (200px rail | content) and pulls in the
   partials/_settings_subnav.html partial.

   Math at 1280px laptop minimum:
     sidebar 240px (or 56px collapsed) + subnav 200px + .app-content
     gutters 1.75rem × 2 (56px) + grid gap 1.5rem (24px) = 520px chrome
     when sidebar is expanded → 760px usable content. Workable for the
     forms we put in here; tighter sidebar (collapsed) lifts that to
     944px.
   ----------------------------------------- */
.settings-layout {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}

.subnav {
  /* Sticky offset = topbar height + 16px breathing room so the section
     label doesn't kiss the topbar's bottom border. */
  position: sticky;
  top: calc(var(--topbar-h) + 16px);
  width: 200px;
  flex-shrink: 0;
  padding: 0.25rem 0.75rem 1rem 0;
  border-right: 1px solid var(--color-base-300);
  /* Sit slightly above the page content so a long form scrolling past
     can't poke through the right border on subpixel layouts. */
  z-index: 1;
}

.subnav__section + .subnav__section { margin-top: 0.875rem; }

.subnav__label {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
  padding: 0.375rem 0.625rem 0.5rem;
}

.subnav__link {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.45rem 0.625rem;
  border-radius: 0.45rem;
  font-size: 0.82rem;
  line-height: 1.3;
  color: color-mix(in oklch, var(--color-base-content) 78%, transparent);
  transition: background-color 0.12s ease, color 0.12s ease;
  text-decoration: none;
  font-weight: 500;
}

.subnav__link:hover {
  background: color-mix(in oklch, var(--color-base-content) 5%, transparent);
  color: var(--color-base-content);
}

.subnav__link:focus-visible {
  outline: 2px solid color-mix(in oklch, var(--color-primary) 45%, transparent);
  outline-offset: 1px;
}

.subnav__link-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
}

.subnav__link.is-active {
  background: color-mix(in oklch, var(--color-primary) 14%, transparent);
  color: var(--color-primary);
  font-weight: 600;
}
.subnav__link.is-active .subnav__link-icon { color: var(--color-primary); }

/* Scrollspy variant of the subnav: identical visual, different active
   semantics. The active state is toggled by the IntersectionObserver in
   templates/settings/index.html instead of `request.url.path`. Adding
   .subnav__link.is-spy hint allows future style divergence without
   touching the partial. */
.subnav__link.is-spy {
  cursor: pointer;
}

/* -----------------------------------------
   9b. SETTINGS LONG PAGE — anchored sections + scrollspy
   The /settings route is a long single-column document divided into
   anchored sections (#integrations, #brands, …). The .subnav above
   highlights the section currently crossing the top 30% of the viewport;
   anchor jumps via the subnav links scroll-smoothly to each section.
   ----------------------------------------- */
.settings-section {
  /* Anchor-jump offset so #brands lands below the sticky topbar instead
     of being hidden under it. Mirrors the .subnav sticky offset. */
  scroll-margin-top: calc(var(--topbar-h) + 16px);
  padding-top: 0.5rem;
  margin-top: 1.75rem;
}

.settings-section:first-of-type {
  margin-top: 0;
}

.settings-section__title {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-bottom: 0.75rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--color-base-300);
}

/* Smooth-scroll for the page when subnav anchor links are clicked.
   Scoped to the long page so other routes don't pick up the behaviour
   (e.g. validate's "scroll to error" jumps should be instant). */
html:has(.settings-long-page) {
  scroll-behavior: smooth;
}

/* Lazy-load skeleton inside a settings-section while its HTMX swap is
   pending. Same look the rest of the app uses, just constrained so the
   long page doesn't reflow once each section's real content lands. */
.settings-section-skeleton {
  min-height: 8rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1rem 0;
}

/* -----------------------------------------
   9c. SETTINGS — EH2 SECTION RESTYLES (R1a/b/c)
   Ported from the EH2 prototype (conn-card / tpl-panel / dtable),
   re-expressed in our token system so both themes resolve. The
   prototype's raw --surface/--border/--text/--brand tokens are
   already aliased in the theme blocks above, so most rules read
   through them directly.
   ----------------------------------------- */

/* ---- R1a: API connections as cubes ---- */
.conn-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
  gap: 0.75rem;
}

.conn-card {
  display: flex;
  align-items: center;
  gap: 0.8125rem;
  padding: 0.875rem 1rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.625rem;
  text-align: left;
  width: 100%;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.conn-card:hover {
  border-color: var(--border-strong);
  background: var(--surface-hover);
}
.conn-card.is-open {
  border-color: color-mix(in oklch, var(--color-primary) 45%, var(--color-base-300));
}

/* Coloured logo tile — the per-service mark. Background colour is set
   inline per service (data-driven brand tint); text stays white so the
   initial reads on any tint in either theme. */
.conn-logo {
  width: 40px;
  height: 40px;
  border-radius: 9px;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 15px;
  color: #fff;
  flex: none;
  letter-spacing: -0.01em;
}

.conn-card__body { flex: 1; min-width: 0; }
.conn-card__name {
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--color-base-content);
  line-height: 1.3;
}
.conn-card__kind {
  font-size: 0.75rem;
  color: var(--bc-60);
  margin-top: 1px;
}
.conn-card__meta {
  font-family: var(--mono);
  font-size: 0.71875rem;
  color: var(--bc-50);
  margin-top: 0.375rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.conn-card__gear {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 0.4rem;
  border: 0;
  background: transparent;
  color: var(--bc-50);
  transition: transform 0.18s ease, color 0.12s ease;
  pointer-events: none;
}
.conn-card.is-open .conn-card__gear {
  color: var(--color-primary);
  transform: rotate(90deg);
}

/* Expanded credential form panel — sits below the card grid, full width
   of the section so wide forms (Akeneo, GS1) get room. */
.conn-panel {
  margin-top: 0.75rem;
  border: 1px solid var(--color-base-300);
  border-radius: 0.625rem;
  background: var(--color-base-100);
  padding: 1rem 1.125rem 1.125rem;
}
.conn-panel__head {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding-bottom: 0.75rem;
  margin-bottom: 0.875rem;
  border-bottom: 1px solid var(--color-base-300);
}
.conn-panel__head .conn-logo { width: 30px; height: 30px; border-radius: 7px; font-size: 12px; }
.conn-panel__title { font-weight: 600; font-size: 0.9rem; }

/* ---- R1c: Prompts two-pane (stage list + editor) ---- */
.prompt-shell {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
}

.prompt-stages {
  border: 1px solid var(--color-base-300);
  border-radius: 0.625rem;
  background: var(--color-base-100);
  overflow: hidden;
}
.prompt-stage {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  border-bottom: 1px solid var(--border-soft);
  background: transparent;
  padding: 0.8125rem 1rem;
  cursor: pointer;
  transition: background 0.12s ease;
}
.prompt-stage:last-child { border-bottom: 0; }
.prompt-stage:hover { background: var(--surface-hover); }
.prompt-stage.is-active { background: var(--brand-soft); }
.prompt-stage__name {
  font-size: 0.84rem;
  font-weight: 500;
  color: var(--color-base-content);
}
.prompt-stage.is-active .prompt-stage__name { color: var(--color-primary); }
.prompt-stage__meta {
  font-size: 0.72rem;
  color: var(--bc-60);
  margin-top: 2px;
}

.prompt-editor {
  border: 1px solid var(--color-base-300);
  border-radius: 0.625rem;
  background: var(--color-base-100);
  overflow: hidden;
}
.prompt-editor__head {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.8125rem 1rem;
  border-bottom: 1px solid var(--color-base-300);
}
.prompt-editor__head h3 { margin: 0; font-size: 0.875rem; font-weight: 600; }

/* Template chooser panel (set-default rows) — sits between the editor
   head and the textarea. Tinted surface to separate it from the editor
   body the way the prototype's .tpl-panel does. */
.tpl-panel {
  border-bottom: 1px solid var(--color-base-300);
  background: var(--color-base-200);
}
.tpl-panel__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6875rem 1rem 0.5rem;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--bc-50);
  font-weight: 600;
}
.tpl-list {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: 0 0.75rem 0.75rem;
}
.tpl-row {
  display: flex;
  align-items: flex-start;
  gap: 0.6875rem;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  background: var(--color-base-100);
  cursor: pointer;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.tpl-row:hover { border-color: var(--border-strong); }
.tpl-row.is-selected {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

/* Set-default radio — clicking it activates the template (one active per
   kind). Filled teal + check when this row is the default. */
.tpl-radio {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid var(--border-strong);
  flex: none;
  display: grid;
  place-items: center;
  color: var(--color-primary-content);
  margin-top: 1px;
  background: transparent;
  padding: 0;
  cursor: pointer;
}
.tpl-radio.is-on {
  background: var(--color-primary);
  border-color: var(--color-primary);
}
.tpl-radio .icon { width: 11px; height: 11px; stroke-width: 3; }

.tpl-row__main { min-width: 0; flex: 1; }
.tpl-row__name {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-base-content);
}
.tpl-row__desc {
  font-size: 0.75rem;
  color: var(--bc-50);
  margin-top: 2px;
}
.tpl-badge-default {
  font-size: 0.625rem;
  font-weight: 600;
  line-height: 1;
  padding: 0.1875rem 0.375rem;
  border-radius: 0.3rem;
  color: var(--color-success);
  background: color-mix(in oklch, var(--color-success) 16%, transparent);
}
.tpl-setdefault {
  flex: none;
  align-self: center;
  font-size: 0.71875rem;
  font-weight: 600;
  color: var(--color-primary);
  padding: 0.1875rem 0.5625rem;
  border: 1px solid var(--brand-line);
  border-radius: 0.4rem;
  background: var(--brand-soft);
  cursor: pointer;
}
.tpl-setdefault:hover {
  background: color-mix(in oklch, var(--color-primary) 22%, transparent);
}

/* ---- R1a/b: dense data table (dtable) ---- */
.dtable { width: 100%; border-collapse: collapse; }
.dtable thead th {
  text-align: left;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  color: var(--bc-50);
  padding: 0.625rem 0.875rem;
  border-bottom: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 50%, var(--color-base-100));
}
.dtable tbody td {
  padding: 0.6875rem 0.875rem;
  border-bottom: 1px solid var(--border-soft);
  vertical-align: middle;
  font-size: 0.8125rem;
}
.dtable tbody tr { transition: background 0.1s ease; }
.dtable tbody tr:hover { background: var(--surface-hover); }
.dtable tbody tr:last-child td { border-bottom: 0; }
.dtable .cell-mono {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--bc-75);
}

/* Verified status cell — green dot + label, reads in both themes. */
.dtable-verified {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.78rem;
  color: var(--color-success);
}
.dtable-verified::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-success);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-success) 18%, transparent);
  flex: none;
}

/* -----------------------------------------
   10. LANDING / HOME TILES
   ----------------------------------------- */
.launch-tile {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.25rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.75rem;
  text-decoration: none;
  color: var(--color-base-content);
  transition: border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
  position: relative;
  overflow: hidden;
}

.launch-tile::after {
  content: "";
  position: absolute;
  inset: auto -2rem -2rem auto;
  width: 6rem;
  height: 6rem;
  background: color-mix(in oklch, var(--color-primary) 10%, transparent);
  border-radius: 9999px;
  opacity: 0.6;
  transition: transform 0.3s ease, opacity 0.3s ease;
  pointer-events: none;
}

.launch-tile:hover {
  border-color: color-mix(in oklch, var(--color-primary) 45%, var(--color-base-300));
  transform: translateY(-1px);
  box-shadow: 0 6px 24px rgba(15, 20, 34, 0.06);
}

.launch-tile:hover::after {
  transform: scale(1.15);
  opacity: 0.8;
}

.launch-tile-icon {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 0.5rem;
  background: color-mix(in oklch, var(--color-primary) 14%, transparent);
  color: var(--color-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
}

.launch-tile-title {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  position: relative;
  z-index: 1;
}

.launch-tile-desc {
  font-size: 0.8rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  line-height: 1.4;
  position: relative;
  z-index: 1;
}

.launch-tile-meta {
  margin-top: auto;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: color-mix(in oklch, var(--color-primary) 70%, var(--color-base-content));
  font-weight: 600;
  position: relative;
  z-index: 1;
}

/* Status-pill variant of the meta row. Used on /settings/index where
   each tile shows a chip ("5/8 providers configured") instead of a
   call-to-action caret. The pill carries its own typography so we
   neutralise the uppercase + tracking the default meta applies. */
.launch-tile-meta--status {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  color: inherit;
  display: flex;
  align-items: center;
  padding-top: 0.25rem;
}

/* -----------------------------------------
   11. STATUS TAB STRIP (page-level filter)
   ----------------------------------------- */
.status-tabs {
  display: flex;
  align-items: stretch;
  gap: 0.25rem;
  border-bottom: 1px solid var(--color-base-300);
  margin-bottom: 0.875rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.status-tabs::-webkit-scrollbar { display: none; }

.status-tab {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.875rem;
  font-size: 0.82rem;
  font-weight: 500;
  color: color-mix(in oklch, var(--color-base-content) 65%, transparent);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  text-decoration: none;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.status-tab:hover {
  color: var(--color-base-content);
}

.status-tab.is-active {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
  font-weight: 600;
}

.status-tab-count {
  display: inline-flex;
  align-items: center;
  padding: 0 0.45rem;
  min-width: 1.4rem;
  height: 1.15rem;
  border-radius: 9999px;
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
  font-size: 0.68rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.status-tab.is-active .status-tab-count {
  background: color-mix(in oklch, var(--color-primary) 18%, transparent);
  color: var(--color-primary);
}

/* -----------------------------------------
   12. COMPACT FILTER BAR — claude-design tokens
   Flat chip-style controls on a base-100 band with hairline borders.
   Drives the product-list filters; select elements are styled to match
   the chip look so they visually line up with the search box.
   ----------------------------------------- */
.filter-bar {
  /* Canonical (EnrichmentHelper.html line 848): single-line filter row
     at 1280px+ — flex / gap 10px / wrap as a fallback under laptop
     width. nowrap above 1280px keeps every filter chip in one band so
     the user's eye doesn't have to walk a second row. */
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
  width: 100%;
  min-width: 0;
}
@media (max-width: 1279px) {
  .filter-bar { flex-wrap: wrap; }
}

.filter-bar .search-wrap {
  position: relative;
  flex: 0 1 16rem;              /* shrinkable, capped at 256px */
  min-width: 12rem;
  max-width: 22rem;
  height: 32px;
  display: flex;
  align-items: center;
  background: var(--color-base-100);
  border: 1px solid color-mix(in oklch, var(--color-base-300) 85%, transparent);
  border-radius: 7px;
  padding: 0 10px 0 30px;
  transition: border-color 120ms ease;
}

.filter-bar .search-wrap:focus-within {
  border-color: color-mix(in oklch, var(--color-primary) 55%, transparent);
}

.filter-bar .search-wrap svg {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  width: 14px;
  height: 14px;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  pointer-events: none;
}

.filter-bar .search-wrap input {
  background: transparent;
  border: 0;
  outline: 0;
  flex: 1;
  font-size: 13px;
  height: 100%;
  padding: 0;
  color: var(--color-base-content);
}

.filter-bar .search-wrap input::placeholder {
  color: color-mix(in oklch, var(--color-base-content) 40%, transparent);
}

/* Selects: flatten daisyUI select look into the chip family. Width is
   ``auto`` (set inline on combobox triggers) so the chip sizes to its
   selected label — matches canonical's per-select sizing. */
.filter-bar select.select,
.filter-bar button.select {
  height: 32px;
  min-height: 32px;
  padding: 0 28px 0 10px;
  font-size: 12.5px;
  font-weight: 500;
  background-color: var(--color-base-100);
  border: 1px solid color-mix(in oklch, var(--color-base-300) 85%, transparent);
  border-radius: 7px;
  color: color-mix(in oklch, var(--color-base-content) 80%, transparent);
  transition: border-color 120ms ease, color 120ms ease;
  flex-shrink: 0;
}
.filter-bar select.select { width: auto; }

.filter-bar select.select:hover {
  border-color: color-mix(in oklch, var(--color-base-content) 22%, transparent);
}

.filter-bar select.select:focus {
  outline: none;
  border-color: color-mix(in oklch, var(--color-primary) 55%, transparent);
  color: var(--color-base-content);
}

.filter-bar .spacer { flex: 1; }

/* -----------------------------------------
   13. PRODUCT ROW THUMBNAIL
   ----------------------------------------- */
.product-thumb {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 0.45rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  flex-shrink: 0;
  text-transform: uppercase;
  border: 1px solid transparent;
}

.product-thumb-img {
  object-fit: cover;
  background: var(--color-base-200);
  border: 1px solid var(--color-base-300);
}

/* Eight-color palette cycled by SKU length % 8 */
.thumb-c0 { background: color-mix(in oklch, #2f6fd3 18%, transparent); color: #2f6fd3; }
.thumb-c1 { background: color-mix(in oklch, #6c5ce7 18%, transparent); color: #6c5ce7; }
.thumb-c2 { background: color-mix(in oklch, #00a884 20%, transparent); color: #008a6d; }
.thumb-c3 { background: color-mix(in oklch, #e27d3b 20%, transparent); color: #c95f1a; }
.thumb-c4 { background: color-mix(in oklch, #d4487e 18%, transparent); color: #bb2d65; }
.thumb-c5 { background: color-mix(in oklch, #2d8ab0 20%, transparent); color: #1f6f91; }
.thumb-c6 { background: color-mix(in oklch, #7a6042 22%, transparent); color: #7a6042; }
.thumb-c7 { background: color-mix(in oklch, #4a5568 18%, transparent); color: #4a5568; }

[data-theme="dim"] .thumb-c0,
[data-theme="dim"] .thumb-c1,
[data-theme="dim"] .thumb-c2,
[data-theme="dim"] .thumb-c3,
[data-theme="dim"] .thumb-c4,
[data-theme="dim"] .thumb-c5,
[data-theme="dim"] .thumb-c6,
[data-theme="dim"] .thumb-c7 {
  color-scheme: dark;
  filter: brightness(1.15) saturate(1.1);
}

.product-cell {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-width: 0;
}

.product-cell-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 0.1rem;
}

.product-cell-primary {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--color-base-content);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 320px;
}

.product-cell-secondary {
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-family: var(--mono);
  line-height: 1.1;
}

.product-cell-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 65%, transparent);
  font-weight: 500;
}

/* Enabled/disabled indicator on product thumbnails. Uses a real child
   <span class="product-thumb-dot"> rather than ::after — because ::after
   doesn't render on <img> tags, and using a real element sidesteps any
   pseudo-element quirks entirely. Green = live in Akeneo, red = disabled. */
.product-thumb-box {
  position: relative;
  display: inline-block;
  flex-shrink: 0;
  line-height: 0;
}
.product-thumb-dot {
  position: absolute;
  right: -3px;
  top: -3px;
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 9999px;
  border: 1.5px solid var(--color-base-100);
  pointer-events: none;
  z-index: 1;
}
.product-thumb-box.is-enabled  .product-thumb-dot { background: var(--color-success); }
.product-thumb-box.is-disabled .product-thumb-dot { background: var(--color-error); }
.product-thumb-box.is-disabled .product-thumb {
  border-color: color-mix(in oklch, var(--color-error) 45%, transparent);
}

/* -----------------------------------------
   14. FLOATING SELECTION BAR
   ----------------------------------------- */
.selection-bar {
  position: fixed;
  bottom: 1.25rem;
  left: 50%;
  transform: translateX(-50%) translateY(0);
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.5rem 0.5rem 0.5rem 1rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 9999px;
  box-shadow: 0 10px 36px rgba(15, 20, 34, 0.14), 0 2px 8px rgba(15, 20, 34, 0.08);
  z-index: 40;
  max-width: calc(100vw - 2rem);
  min-width: min(560px, 92vw);
  transition: transform 0.25s ease, opacity 0.25s ease;
}

.selection-bar.is-hidden {
  transform: translateX(-50%) translateY(200%);
  opacity: 0;
  pointer-events: none;
}

.selection-bar-count {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: var(--color-base-content);
  font-weight: 500;
}

.selection-bar-count strong {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.selection-bar-divider {
  width: 1px;
  height: 1.5rem;
  background: var(--color-base-300);
}

/* Data-density table tweaks so product-cell rows breathe right.
   Styling derives from the claude-design tokens: uppercase micro-type
   headers on a subtle tinted band, hairline rows, primary-tinted hover. */
.table-products {
  font-size: 13px;
}

.table-products thead th {
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 10px 14px;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  background: color-mix(in oklch, var(--color-base-200) 50%, var(--color-base-100));
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 65%, transparent);
}

/* Sortable column headers — rendered by the sort_header macro in
   _table.html. The whole <th> is clickable via the nested <a.sort-link>;
   we keep the link block-level so the hover target matches the cell. */
.sortable-th { padding: 0 !important; }

.sortable-th .sort-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 10px 14px;
  width: 100%;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
  user-select: none;
}

.sortable-th.text-right .sort-link {
  justify-content: flex-end;
}

.sortable-th .sort-link:hover {
  background: color-mix(in oklch, var(--color-base-content) 6%, transparent);
  color: var(--color-base-content);
}

.sortable-th.is-sorted {
  color: var(--color-primary);
}

.sortable-th.is-sorted .sort-link {
  color: var(--color-primary);
  background: color-mix(in oklch, var(--color-primary) 7%, transparent);
}

.sort-icon {
  width: 0.8rem;
  height: 0.8rem;
  flex-shrink: 0;
}

.sort-icon.is-idle {
  opacity: 0.35;
}

.sortable-th .sort-link:hover .sort-icon.is-idle {
  opacity: 0.7;
}

.table-products tbody td {
  padding: 10px 14px;
  vertical-align: middle;
}
.table-products tbody td.status-cell {
  /* Wider right padding so the status pill stops hugging the boundary
     with the (often empty) action column. Higher specificity than a
     bare Tailwind utility so the override doesn't get silently lost. */
  padding-right: 1.75rem;
}

.table-products tbody tr {
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 65%, transparent);
  transition: background 80ms ease;
}

.table-products tbody tr:last-child { border-bottom: none; }

.table-products tbody tr:hover {
  background: color-mix(in oklch, var(--color-primary) 6%, transparent);
}

/* Docs column — two small icon chips matching the claude-design filter
   bar Docs row. Bolt icon = large energy label, doc icon = datasheet.
   Neutral square by default, success-tinted when the artefact is present. */
.docs-cell {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
}
.doc-dot {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  color: color-mix(in oklch, var(--color-base-content) 40%, transparent);
  background: color-mix(in oklch, var(--color-base-300) 85%, transparent);
}
.doc-dot svg { width: 11px; height: 11px; }
.doc-dot.is-on {
  color: var(--color-success);
  background: color-mix(in oklch, var(--color-success) 14%, transparent);
}

/* Legacy horizontal-nav active indicator (retained for any sub-menus) */
.nav-active {
  background-color: color-mix(in oklch, var(--color-primary) 12%, transparent);
  color: var(--color-primary);
  border-radius: 0.375rem;
  font-weight: 600;
}

/* Keep .navbar component usable on auth pages (login form) */
.navbar {
  background: var(--color-base-100);
  border-bottom: 1px solid var(--color-base-300);
}

/* -----------------------------------------
   15. DASHBOARD OPS — stat tiles, pills,
       source rows, section dividers.
   Feeds the Activity Feed / People Panel /
   Pipeline Health / System Health cards.
   ----------------------------------------- */

.tnum { font-variant-numeric: tabular-nums; }
.text-muted { color: color-mix(in oklch, var(--color-base-content) 65%, transparent); }
.text-mute2 { color: color-mix(in oklch, var(--color-base-content) 40%, transparent); }

/* Page header — matches the prototype's design-system spec.
   Canonical (app/styles.css line 149-152): title 22px / sub 13px /
   18px margin-bottom. We keep rem units so the user's browser zoom
   scales the page coherently, but the values trace 1:1 to canonical. */
.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.125rem;     /* 18px — canon */
  flex-wrap: wrap;
}
.page-header__title {
  font-size: 1.375rem;          /* 22px — canon */
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin: 0;
}
.page-header__subtitle {
  margin: 0.1875rem 0 0;        /* 3px — canon */
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  font-size: 0.8125rem;         /* 13px — canon */
  line-height: 1.5;
  max-width: 40rem;             /* 640px — canon */
}
.page-header__actions {
  display: flex;
  gap: 0.5rem;
  flex-shrink: 0;
}

/* Dashboard multi-column layouts — defined in pure CSS (no Tailwind
   utility dependency) because the browser-JIT for Tailwind has a
   visible first-paint delay and we want these to be right on initial
   render. Shared breakpoint matches the app-shell mobile cutover at
   900px; the `lg` variant jumps to a 2-col grid at ≥1024px. */
.dash-stack { display: flex; flex-direction: column; gap: 1.25rem; }

/* Paired widgets in .dash-2col — keep both halves the same height so
   the cost donut card and the trend chart card line up at the bottom
   even when one has a longer legend. The taller half drives the row
   height; the shorter card flex-fills its grid cell to match.
   Targets `> div` only — leaving the trend chart's trailing <script>
   tag at its browser-default display:none. */
.dash-2col > div {
  display: flex;
  flex-direction: column;
}
.dash-2col > div > .card,
.dash-2col > .card {
  flex: 1 1 auto;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.dash-2col {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
}
@media (min-width: 1024px) {
  .dash-2col { grid-template-columns: 1fr 1fr; }
  .dash-2col--wide-left { grid-template-columns: 1.1fr 1fr; }
}

/* Stat tiles — compact KPI row under the page header. 4 across at
   wide widths, auto-fit-stack below 720px. Explicit media query
   beats auto-fit when the container briefly gets very narrow during
   sidebar drawer animations. */
.stat-tile-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.85rem;
  margin-bottom: 1.25rem;
}
@media (min-width: 640px) {
  .stat-tile-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .stat-tile-grid { grid-template-columns: repeat(4, 1fr); }
}
.stat-tile {
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 10px;
  padding: 0.9rem 1rem;
  position: relative;
  overflow: hidden;
}
.stat-tile::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--tile-accent, var(--color-primary));
  opacity: 0.75;
}
.stat-tile--info    { --tile-accent: var(--color-info); }
.stat-tile--accent  { --tile-accent: var(--color-accent); }
.stat-tile--primary { --tile-accent: var(--color-primary); }
.stat-tile--success { --tile-accent: var(--color-success); }
.stat-tile--warning { --tile-accent: var(--color-warning); }
.stat-tile--error   { --tile-accent: var(--color-error); }
a.stat-tile--link {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color 120ms, transform 120ms;
}
a.stat-tile--link:hover {
  border-color: var(--tile-accent);
}
a.stat-tile--link:hover::before { opacity: 1; }

.stat-tile__label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  margin-bottom: 0.3rem;
  font-weight: 600;
}
.stat-tile__value {
  font-size: 1.55rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.stat-tile__hint {
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-top: 0.25rem;
}

/* Status pill — THE canonical badge primitive (dot + lowercase label +
   semantic tint). DaisyUI has .badge but we want the dot-pill, tighter radii,
   and the neutral/accent vocabulary the design uses. Templates use
   .status-pill + a .status-pill--{success|warning|error|info|accent|neutral}
   variant; never DaisyUI .badge. */
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1.45;
  letter-spacing: 0.01em;
  text-transform: lowercase;
  border: 1px solid transparent;
  white-space: nowrap;
  transition: background-color var(--motion-fast) var(--ease-out),
              color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
}
.status-pill::before {
  content: "";
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 9999px;
  background: currentColor;
  flex: none;
}
.status-pill--success { background: color-mix(in oklch, var(--color-success) 18%, transparent); color: var(--color-success); border-color: color-mix(in oklch, var(--color-success) 30%, transparent); }
.status-pill--error   { background: color-mix(in oklch, var(--color-error) 18%, transparent); color: var(--color-error); border-color: color-mix(in oklch, var(--color-error) 30%, transparent); }
.status-pill--warning { background: color-mix(in oklch, var(--color-warning) 20%, transparent); color: var(--color-warning); border-color: color-mix(in oklch, var(--color-warning) 30%, transparent); }
.status-pill--info    { background: color-mix(in oklch, var(--color-info) 18%, transparent); color: var(--color-info); border-color: color-mix(in oklch, var(--color-info) 30%, transparent); }
.status-pill--accent  { background: color-mix(in oklch, var(--color-accent) 18%, transparent); color: var(--color-accent); border-color: color-mix(in oklch, var(--color-accent) 30%, transparent); }
.status-pill--neutral { background: color-mix(in oklch, var(--color-base-content) 10%, transparent); color: color-mix(in oklch, var(--color-base-content) 75%, transparent); border-color: var(--color-base-300); }

/* Labeled hairline rule inside cards (used between sections of a card). */
.section-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.1rem 0 0.55rem;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
}
.section-divider__rule {
  flex: 1;
  height: 1px;
  background: var(--color-base-300);
}

/* Supplier performance — left-rail accent for high-reject suppliers so
   the problem rows stand out without saturating the table in red. */
.supplier-row--attention td:first-child {
  border-left: 3px solid var(--color-error);
  padding-left: calc(var(--cell-pad-x, 0.6rem) - 3px);
}

/* Source-row bar — reused by Pipeline Health for rejection reasons +
   most-edited fields (same shape the dashboard already uses for the
   Source Coverage card). */
.source-row {
  display: grid;
  grid-template-columns: 1fr 2fr 56px;
  align-items: center;
  gap: 0.65rem;
}
.source-row + .source-row { margin-top: 0.3rem; }
.source-row__bar-track {
  height: 6px;
  background: var(--color-base-300);
  border-radius: 999px;
  overflow: hidden;
}
.source-row__bar-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--color-primary);
  transition: width 0.2s ease;
}
.source-row__pct {
  text-align: right;
  font-size: 0.78rem;
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
}

/* Avatar bubble used in the activity feed + people panel. */
.avatar-bubble {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  --eh-avatar: var(--brand);
  background: linear-gradient(135deg, color-mix(in oklch, var(--eh-avatar) 82%, black), var(--eh-avatar));
  color: white;
  display: grid;
  place-items: center;
  font-size: 0.66rem;
  font-weight: 600;
  flex-shrink: 0;
  letter-spacing: 0;
}
.avatar-bubble--system {
  background: var(--color-base-300);
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
}

/* Activity-feed row shell — grid layout for when / who+action / drill-in. */
.activity-row {
  display: grid;
  grid-template-columns: 72px 1fr auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 1rem;
  font-size: 0.82rem;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 65%, transparent);
}
.activity-row:last-child { border-bottom: none; }
.activity-row__when { font-size: 0.72rem; color: color-mix(in oklch, var(--color-base-content) 55%, transparent); }
.activity-row__body { display: flex; align-items: center; gap: 0.6rem; min-width: 0; }
.activity-row__headline {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.activity-row__who { font-size: 0.84rem; font-weight: 500; }
.activity-row__meta { font-size: 0.72rem; color: color-mix(in oklch, var(--color-base-content) 60%, transparent); margin-top: 0.15rem; }

/* Attention-source rows synthesized from get_attention_signals — same
   shell as a normal audit row, with a left accent so the operator can
   visually separate "what an alert announced" from "what someone did". */
.activity-row--attention {
  background: color-mix(in oklch, var(--color-warning) 6%, transparent);
  border-left: 3px solid color-mix(in oklch, var(--color-warning) 65%, transparent);
  padding-left: calc(1rem - 3px);
}

/* Error-log tail — monospace with level coloring. */
.error-log {
  background: var(--color-base-200);
  border: 1px solid var(--color-base-300);
  border-radius: 7px;
  padding: 0.6rem 0.7rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.55;
  overflow: auto;
  max-height: 280px;
}
.error-log__row {
  display: grid;
  grid-template-columns: 54px 58px 1fr;
  gap: 0.5rem;
  padding: 0.25rem 0;
  border-bottom: 1px dashed var(--color-base-300);
}
.error-log__row:last-child { border-bottom: none; }
.error-log__level--error { color: var(--color-error); font-weight: 600; }
.error-log__level--warn  { color: var(--color-warning); font-weight: 600; }
.error-log__level--info  { color: color-mix(in oklch, var(--color-base-content) 70%, transparent); font-weight: 600; }

/* Cost anomaly callout — warning-tinted band above the two-column System
   Health content. */
.cost-anomaly-banner {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid color-mix(in oklch, var(--color-warning) 30%, transparent);
  background: color-mix(in oklch, var(--color-warning) 14%, transparent);
  border-radius: 8px;
  margin-bottom: 1rem;
}
.cost-anomaly-banner__spark { flex-shrink: 0; }

/* Cost breakdown card — donut on the left, legend + total on the
   right, optional stats strip below. Matches the design's
   Cost-breakdown tile layout. */
.cost-breakdown__body {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 1.5rem;
  align-items: center;
}
@media (max-width: 720px) {
  .cost-breakdown__body { grid-template-columns: 1fr; justify-items: center; }
}

.cost-breakdown__donut {
  width: 180px;
  height: 180px;
  flex-shrink: 0;
}

.cost-breakdown__legend {
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  min-width: 0;
}

.cost-breakdown__row {
  display: grid;
  grid-template-columns: 12px 1fr auto auto;
  gap: 0.75rem;
  align-items: center;
  padding: 0.3rem 0;
  font-size: 0.82rem;
}
.cost-breakdown__row--total { padding-top: 0.5rem; }

.cost-breakdown__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-primary);
  flex-shrink: 0;
}

.cost-breakdown__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cost-breakdown__calls {
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-size: 0.75rem;
}

.cost-breakdown__cost {
  font-weight: 600;
  text-align: right;
  min-width: 4.5rem;
}

.cost-breakdown__divider {
  height: 1px;
  background: var(--color-base-300);
  margin: 0.35rem 0 0.25rem;
  grid-column: 1 / -1;
}

/* Secondary stats strip — three compact cells below the donut. */
.cost-breakdown__stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--color-base-300);
}
.cost-breakdown__stat-label {
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
  margin-bottom: 0.2rem;
}
.cost-breakdown__stat-value {
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.1;
}
.cost-breakdown__stat-hint {
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  margin-top: 0.15rem;
}

/* Tight table density used by People panel + System Health. */
.table-ops {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.82rem;
}
.table-ops thead th {
  text-align: left;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.5rem 0.6rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  border-bottom: 1px solid var(--color-base-300);
  font-weight: 600;
}
.table-ops tbody td {
  padding: 0.55rem 0.6rem;
  vertical-align: middle;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 65%, transparent);
}
.table-ops tbody tr:last-child td { border-bottom: none; }
.table-ops .text-right { text-align: right; }

/* -----------------------------------------
   Storage panel — dashboard system row.
   Headline progress bar, per-bucket stacked
   bar, legend swatches. All tinted via the
   existing semantic palette so the panel
   stays single-theme.
   ----------------------------------------- */
.storage-headline {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  margin-top: 0.5rem;
}
.storage-headline--text {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  font-size: 0.86rem;
  align-items: baseline;
}
.storage-headline__bar {
  flex: 1 1 auto;
  height: 0.5rem;
  border-radius: 999px;
  background: color-mix(in oklch, var(--color-base-300) 80%, transparent);
  overflow: hidden;
  min-width: 0;
}
.storage-headline__fill {
  height: 100%;
  border-radius: inherit;
  transition: width 0.3s ease;
}
.storage-headline__bar--success .storage-headline__fill { background: var(--color-success); }
.storage-headline__bar--warning .storage-headline__fill { background: var(--color-warning); }
.storage-headline__bar--error   .storage-headline__fill { background: var(--color-error); }
.storage-headline__bar--neutral .storage-headline__fill { background: color-mix(in oklch, var(--color-base-content) 35%, transparent); }

.storage-headline__meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.82rem;
  flex-shrink: 0;
}
.storage-pct-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.storage-pct-chip--success { background: color-mix(in oklch, var(--color-success) 14%, transparent); color: var(--color-success); }
.storage-pct-chip--warning { background: color-mix(in oklch, var(--color-warning) 18%, transparent); color: var(--color-warning); }
.storage-pct-chip--error   { background: color-mix(in oklch, var(--color-error) 16%, transparent);   color: var(--color-error); }

/* Stacked bar — buckets as side-by-side flex segments. Each segment's
   width is set inline via style="width: <pct>%". Hairline divider
   between segments via box-shadow inset so we don't need a real gap. */
.storage-stack {
  display: flex;
  width: 100%;
  height: 0.65rem;
  border-radius: 0.35rem;
  overflow: hidden;
  background: color-mix(in oklch, var(--color-base-300) 60%, transparent);
}
.storage-stack__seg {
  height: 100%;
  min-width: 2px;
  box-shadow: inset -1px 0 0 0 color-mix(in oklch, var(--color-base-100) 35%, transparent);
  transition: filter 0.12s ease;
}
.storage-stack__seg:last-child { box-shadow: none; }
.storage-stack__seg:hover { filter: brightness(1.08); }
.storage-stack__seg--info      { background: var(--color-info); }
.storage-stack__seg--success   { background: var(--color-success); }
.storage-stack__seg--warning   { background: var(--color-warning); }
.storage-stack__seg--error     { background: var(--color-error); }
.storage-stack__seg--accent    { background: var(--color-accent); }
.storage-stack__seg--primary   { background: var(--color-primary); }
.storage-stack__seg--secondary { background: var(--color-secondary); }
.storage-stack__seg--neutral   { background: color-mix(in oklch, var(--color-base-content) 35%, transparent); }

/* Legend grid — 2 columns at laptop width, swatches small + flat. */
.storage-legend {
  margin-top: 0.85rem;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 1.5rem;
  row-gap: 0.35rem;
}
@media (max-width: 720px) {
  .storage-legend { grid-template-columns: 1fr; }
}
.storage-legend__row {
  display: grid;
  grid-template-columns: 0.6rem 1fr auto auto;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.8rem;
  line-height: 1.3;
  padding: 0.15rem 0;
}
.storage-legend__label {
  color: color-mix(in oklch, var(--color-base-content) 80%, transparent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.storage-legend__bytes {
  color: var(--color-base-content);
  font-weight: 500;
}
.storage-legend__count {
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
  font-size: 0.72rem;
  min-width: 3.5rem;
  text-align: right;
}

.storage-swatch {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 0.18rem;
  display: inline-block;
  flex-shrink: 0;
}
.storage-swatch--info      { background: var(--color-info); }
.storage-swatch--success   { background: var(--color-success); }
.storage-swatch--warning   { background: var(--color-warning); }
.storage-swatch--error     { background: var(--color-error); }
.storage-swatch--accent    { background: var(--color-accent); }
.storage-swatch--primary   { background: var(--color-primary); }
.storage-swatch--secondary { background: var(--color-secondary); }
.storage-swatch--neutral   { background: color-mix(in oklch, var(--color-base-content) 35%, transparent); }

/* Coverage badges (Step 4 provenance/coverage UI). Three variants for
   "good" / "warn" / "muted (legacy)". Reuses the neutral token palette
   so this stays single-theme per the design rule. */
.coverage-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.15rem 0.55rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  border-radius: 999px;
  border: 1px solid color-mix(in oklch, var(--color-base-300) 80%, transparent);
  background: color-mix(in oklch, var(--color-base-200) 50%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
  white-space: nowrap;
}
.coverage-badge--good {
  border-color: color-mix(in oklch, var(--color-success) 40%, transparent);
  background: color-mix(in oklch, var(--color-success) 15%, transparent);
  color: color-mix(in oklch, var(--color-success) 90%, var(--color-base-content));
}
.coverage-badge--warn {
  border-color: color-mix(in oklch, var(--color-warning) 40%, transparent);
  background: color-mix(in oklch, var(--color-warning) 15%, transparent);
  color: color-mix(in oklch, var(--color-warning) 90%, var(--color-base-content));
}
.coverage-badge--muted {
  opacity: 0.6;
}

/* Inline source pill placed after each spec value so the reviewer can
   see "Voltage = 230V (Icecat)" at a glance. Hover shows the full
   source string via the title= attribute. */
.source-pill {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0 0.4rem;
  font-size: 0.62rem;
  font-weight: 500;
  border-radius: 4px;
  background: color-mix(in oklch, var(--color-base-300) 60%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  vertical-align: middle;
}

/* Per-field match/extractor confidence badge (Track B) — sits after the
   source pill, tinted with the teal accent and tabular numerics. */
.source-conf {
  display: inline-block;
  margin-left: 0.3rem;
  padding: 0 0.35rem;
  font-size: 0.6rem;
  font-weight: 600;
  border-radius: 4px;
  font-variant-numeric: tabular-nums;
  background: color-mix(in oklch, var(--color-primary) 18%, transparent);
  color: color-mix(in oklch, var(--color-primary) 90%, var(--color-base-content));
  vertical-align: middle;
}

/* -----------------------------------------
   16. IMAGE BULK IMPORT — conflict review grid
   4-column compare strip per row: existing thumb · arrow ·
   imported thumb · metadata + decision radios. Density target is
   ~6 rows visible at 1280 px (sidebar collapsed: 1224 px content).
   ----------------------------------------- */
.conflict-grid {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.conflict-row {
  display: grid;
  grid-template-columns: 92px 28px 92px 1fr;
  gap: 0.85rem;
  align-items: stretch;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--color-base-300);
  border-radius: 8px;
  background: var(--color-base-100);
  cursor: pointer;
  outline: 0;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.conflict-row:hover {
  border-color: color-mix(in oklch, var(--color-primary) 35%, var(--color-base-300));
}
.conflict-row.is-focused {
  border-color: color-mix(in oklch, var(--color-primary) 55%, var(--color-base-300));
  box-shadow: 0 0 0 2px color-mix(in oklch, var(--color-primary) 18%, transparent);
}

.conflict-row__current,
.conflict-row__import {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  /* compare-current / compare-enriched apply the left accent border;
     we tighten the inner padding so the thumb sits flush. */
  padding-left: 0.5rem;
}
.conflict-row__label {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: 600;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
}

.conflict-row__arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  color: color-mix(in oklch, var(--color-base-content) 35%, transparent);
}

.conflict-thumb {
  width: 76px;
  height: 76px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--color-base-300);
  background: var(--color-base-200);
  cursor: zoom-in;
  display: block;
}
.conflict-thumb--empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
  font-size: 0.65rem;
  color: color-mix(in oklch, var(--color-base-content) 40%, transparent);
  background: color-mix(in oklch, var(--color-base-200) 70%, var(--color-base-300));
  cursor: default;
}

.conflict-row__meta {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 0.5rem;
  min-width: 0;
}
.conflict-row__product { min-width: 0; }
.conflict-row__sku {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--color-primary);
  text-decoration: none;
}
.conflict-row__sku:hover { text-decoration: underline; }
.conflict-row__name {
  font-size: 0.82rem;
  color: var(--color-base-content);
  line-height: 1.25;
  margin-top: 0.1rem;
  /* clamp at two lines so a long Dutch name doesn't force the row taller
     than its 76 px thumb. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
.conflict-row__detail {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-top: 0.25rem;
}
.conflict-row__dim { font-variant-numeric: tabular-nums; }

.conflict-row__radios {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
}

/* Radio "chip" — looks like a chip with a hidden native input. The
   ``is-checked`` state pulls in a tinted accent matching the decision
   semantic (replace = warning, keep = neutral, append = info). */
.radio-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--color-base-300);
  background: var(--color-base-100);
  font-size: 0.78rem;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.1s ease, background 0.1s ease, color 0.1s ease;
}
.radio-chip input { display: none; }
.radio-chip:hover {
  border-color: color-mix(in oklch, var(--color-base-content) 22%, transparent);
}
.radio-chip.is-checked {
  font-weight: 600;
}
.radio-chip.is-keep.is-checked {
  border-color: color-mix(in oklch, var(--color-base-content) 35%, transparent);
  background: color-mix(in oklch, var(--color-base-content) 7%, transparent);
  color: var(--color-base-content);
}
.radio-chip.is-replace.is-checked {
  border-color: color-mix(in oklch, var(--color-warning) 55%, transparent);
  background: color-mix(in oklch, var(--color-warning) 16%, transparent);
  color: var(--color-warning);
}
.radio-chip.is-append.is-checked {
  border-color: color-mix(in oklch, var(--color-info) 55%, transparent);
  background: color-mix(in oklch, var(--color-info) 16%, transparent);
  color: var(--color-info);
}
.radio-chip__warn {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  margin-left: 0.3rem;
  padding: 0 0.35rem;
  border-radius: 4px;
  background: color-mix(in oklch, var(--color-warning) 22%, transparent);
  color: var(--color-warning);
  font-size: 0.62rem;
  font-weight: 600;
}

/* Truncation helper for table cells. */
.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* On the cramped collapsed-sidebar laptop width (~1224 px content)
   the meta column starts to crowd. Drop the thumb to 64 px so the
   metadata column has room to breathe without dropping the radios
   below it. */
@media (max-width: 1340px) {
  .conflict-row {
    grid-template-columns: 80px 24px 80px 1fr;
    gap: 0.6rem;
  }
  .conflict-thumb { width: 64px; height: 64px; }
}

/* -----------------------------------------
   Product peek drawer (right-side slide-out)
   Triggered from /products list and the
   /documents/imports/<batch> matched panel.
   Reuses the same partial in both surfaces;
   the wrapper is mounted globally in base.html.
   ----------------------------------------- */

/* Make the products-list product cell behave like a clickable card
   without changing the row layout. The thumb + name + brand combo are
   the trigger; the row chrome (checkbox, action button, status pill)
   are siblings of the cell so they keep their independent click areas. */
.product-cell--peek {
  cursor: pointer;
  border-radius: 6px;
  transition: background-color 0.12s ease;
  padding: 2px 4px;
  margin: -2px -4px;
}
.product-cell--peek:hover {
  background: color-mix(in oklch, var(--color-primary) 8%, transparent);
}
.product-cell--peek:focus-visible {
  outline: 2px solid color-mix(in oklch, var(--color-primary) 45%, transparent);
  outline-offset: 1px;
}

/* Matched-panel peek trigger — minimal restyle of the old <a> link
   shape so the click target reads the same to the reviewer but doesn't
   navigate. The "↗" affordance next to it preserves new-tab access. */
.matched-peek-trigger {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.matched-peek-trigger:hover { text-decoration: underline; }
.matched-peek-newtab {
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
  display: inline-flex;
  align-items: center;
  padding: 2px;
  border-radius: 3px;
  transition: color 0.12s ease, background 0.12s ease;
}
.matched-peek-newtab:hover {
  color: var(--color-base-content);
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
}

/* Wrapper. Fixed full-viewport so the panel can anchor right and the
   backdrop can dim the rest of the page. Pointer-events shimmed off
   the wrapper itself so the panel + backdrop opt back in. */
.peek-drawer {
  position: fixed;
  inset: 0;
  z-index: 70;
  pointer-events: none;
}
.peek-drawer.is-open { pointer-events: auto; }

.peek-drawer__backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in oklch, #000 38%, transparent);
  opacity: 0;
  transition: opacity 0.15s ease-out;
}
.peek-drawer.is-open .peek-drawer__backdrop { opacity: 1; }

.peek-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 480px;
  max-width: 100vw;
  background: var(--color-base-100);
  border-left: 1px solid var(--color-base-300);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform var(--motion-med) ease-out;
  box-shadow: -16px 0 32px -16px rgba(var(--shadow-ink), var(--shadow-strength-lg));
}
.peek-drawer.is-open .peek-drawer__panel { transform: translateX(0); }

@media (max-width: 1439px) {
  .peek-drawer__panel { width: 360px; }
}

/* Header — SKU + status pill on the left, open-link + close on the right. */
.peek-drawer__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 50%, var(--color-base-100));
}
.peek-drawer__header-left,
.peek-drawer__header-right {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.peek-drawer__sku {
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--color-base-content);
}
.peek-drawer__open-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.72rem;
  font-weight: 500;
  border-radius: 0.35rem;
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
  background: color-mix(in oklch, var(--color-base-content) 6%, transparent);
  text-decoration: none;
  transition: background 0.12s ease, color 0.12s ease;
}
.peek-drawer__open-link:hover {
  background: color-mix(in oklch, var(--color-primary) 14%, transparent);
  color: var(--color-primary);
}
.peek-drawer__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 0.35rem;
  background: transparent;
  border: 0;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.peek-drawer__close:hover {
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
  color: var(--color-base-content);
}

/* Title strip — thumbnail + product name + meta. Sits between header
   and body; visually anchors the drawer's identity. */
.peek-drawer__title {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--color-base-300);
}
.peek-drawer__thumb {
  width: 48px;
  height: 48px;
  flex-shrink: 0;
  border-radius: 0.5rem;
  overflow: hidden;
  background: color-mix(in oklch, var(--color-base-200) 70%, var(--color-base-100));
  border: 1px solid var(--color-base-300);
  display: flex;
  align-items: center;
  justify-content: center;
}
.peek-drawer__thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.peek-drawer__thumb .product-thumb {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  font-weight: 600;
}
.peek-drawer__title-body { min-width: 0; flex: 1; }
.peek-drawer__name {
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-base-content);
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.peek-drawer__meta {
  font-size: 0.78rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-top: 0.15rem;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex-wrap: wrap;
}
.peek-drawer__meta-sep { opacity: 0.45; }

/* Body — vertical scroll, dense section spacing. */
.peek-drawer__body {
  flex: 1;
  overflow-y: auto;
  padding: 0.5rem 0;
}
.peek-drawer__empty {
  padding: 1.5rem 1rem;
  text-align: left;
}

/* Each of the three groups (Identifiers / ERP / Web) reads as a distinct
   block: generous vertical padding + a stronger divider than a hairline, so
   the sections separate clearly without needing a heavier treatment. */
.peek-section {
  padding: 1.1rem 1.15rem;
  border-bottom: 2px solid color-mix(in oklch, var(--color-base-300) 85%, transparent);
}
.peek-section:last-child { border-bottom: 0; }
.peek-section--inline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}
.peek-section__label {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin: 0 0 0.5rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.peek-section--inline .peek-section__label { margin: 0; }
.peek-section__count {
  font-size: 0.66rem;
  font-weight: 500;
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  text-transform: none;
}

/* Key-value list — tight 2-column grid, dt mutes, dd reads as content. */
.peek-kv {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 0.3rem 0.75rem;
  margin: 0;
  font-size: 0.8rem;
}
.peek-kv dt {
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  font-weight: 500;
}
.peek-kv dd {
  margin: 0;
  color: var(--color-base-content);
  word-break: break-word;
}
.peek-kv .font-mono {
  font-size: 0.78rem;
}
/* Category breadcrumbs stack one full path per line inside the ERP dd. */
.peek-cat-paths {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

/* Compact presence flags — energy label + data sheet. Muted when absent,
   brand-tinted (and a link, for the data sheet) when present. */
.peek-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.peek-flag {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.72rem;
  font-weight: 500;
  padding: 0.2rem 0.55rem;
  border-radius: 0.4rem;
  border: 1px solid color-mix(in oklch, var(--color-base-300) 70%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
}
.peek-flag.is-on {
  color: var(--color-base-content);
  border-color: color-mix(in oklch, var(--brand) 40%, var(--color-base-300));
  background: color-mix(in oklch, var(--brand) 6%, transparent);
}
a.peek-flag.is-on:hover {
  background: color-mix(in oklch, var(--brand) 12%, transparent);
}
.peek-stock {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.peek-empty {
  color: color-mix(in oklch, var(--color-base-content) 30%, transparent);
}
.peek-empty-block {
  font-size: 0.78rem;
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
  font-style: italic;
  margin: 0;
}

/* Category chips — wrap onto multiple rows, hairline borders. */
.peek-chip-list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0;
  padding: 0;
}
/* Tag chip (rounded-rect, no dot) for value lists like categories — the
   canonical "tag", distinct from the dot .status-pill used for status. */
.peek-chip {
  font-size: 0.72rem;
  padding: 0.15rem 0.5rem;
  border-radius: 0.35rem;
  background: color-mix(in oklch, var(--color-base-200) 80%, var(--color-base-100));
  border: 1px solid var(--color-base-300);
  color: color-mix(in oklch, var(--color-base-content) 80%, transparent);
}

/* Image grid — 4 columns on the wide drawer, 3 on the narrow. */
.peek-image-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.4rem;
}
@media (max-width: 1439px) {
  .peek-image-grid { grid-template-columns: repeat(3, 1fr); }
}
.peek-image-cell {
  aspect-ratio: 1 / 1;
  position: relative;
  border-radius: 0.4rem;
  overflow: hidden;
  border: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 60%, var(--color-base-100));
}
.peek-image-cell img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.peek-image-tag {
  position: absolute;
  font-size: 0.6rem;
  font-weight: 600;
  padding: 0.05rem 0.3rem;
  border-radius: 0.25rem;
  letter-spacing: 0.02em;
  line-height: 1.4;
}
.peek-image-tag--main {
  bottom: 0.2rem;
  left: 0.2rem;
  background: color-mix(in oklch, var(--color-primary) 85%, transparent);
  color: white;
}
.peek-image-tag--lock {
  top: 0.2rem;
  right: 0.2rem;
  background: color-mix(in oklch, var(--color-accent) 22%, transparent);
  color: var(--color-accent);
  padding: 0.15rem;
  display: inline-flex;
}
.peek-image-tag--slot {
  top: 0.2rem;
  left: 0.2rem;
  background: color-mix(in oklch, var(--color-base-content) 65%, transparent);
  color: var(--color-base-100);
}

/* Generated content — title bigger, description html-rendered. */
.peek-generated + .peek-generated { margin-top: 0.75rem; }
.peek-generated__label {
  display: block;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: 600;
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
  margin-bottom: 0.2rem;
}
.peek-generated__title {
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--color-base-content);
  margin: 0;
  line-height: 1.4;
}
.peek-generated__desc {
  font-size: 0.78rem;
  line-height: 1.5;
  color: color-mix(in oklch, var(--color-base-content) 85%, transparent);
}
.peek-generated__desc :where(p, ul, ol) { margin: 0 0 0.4rem; }
.peek-generated__desc :where(p, ul, ol):last-child { margin-bottom: 0; }
.peek-generated__desc :where(li) { margin-left: 1rem; }

/* Footer — quick action row. Primary action sits on the right. */
.peek-drawer__footer {
  border-top: 1px solid var(--color-base-300);
  padding: 0.65rem 0.85rem;
  background: color-mix(in oklch, var(--color-base-200) 40%, var(--color-base-100));
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.peek-action {
  flex: 1;
  font-size: 0.75rem;
  gap: 0.35rem;
}
.peek-action--primary {
  flex: 1.2;
}
@media (max-width: 1439px) {
  .peek-action span { font-size: 0.72rem; }
}

/* -----------------------------------------
   Health banner — top-of-page Redis/DB/worker alert
   ----------------------------------------- */
.health-banner {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 1rem;
  font-size: 0.85rem;
  font-weight: 500;
  border-bottom: 1px solid var(--color-base-300);
  position: relative;
  z-index: 40;
}
.health-banner--warning {
  background: color-mix(in oklch, var(--color-warning) 18%, var(--color-base-100));
  color: color-mix(in oklch, var(--color-warning) 70%, var(--color-base-content));
  border-bottom-color: color-mix(in oklch, var(--color-warning) 35%, transparent);
}
.health-banner--critical {
  background: color-mix(in oklch, var(--color-error) 22%, var(--color-base-100));
  color: color-mix(in oklch, var(--color-error) 75%, var(--color-base-content));
  border-bottom-color: color-mix(in oklch, var(--color-error) 40%, transparent);
}
.health-banner__msg { flex: 1; min-width: 0; }
.health-banner__link {
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.health-banner__link:hover { opacity: 0.85; }
.health-banner__dismiss {
  background: transparent;
  border: 0;
  color: inherit;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0 0.25rem;
  cursor: pointer;
  opacity: 0.7;
}
.health-banner__dismiss:hover { opacity: 1; }

/* -----------------------------------------
   Insights — KPI tiles, filter bar, per-run table, info tooltip
   ----------------------------------------- */

/* Filter bar sits between the page-header and the KPI row. Visually
   distinct from the header so the active period reads as filter state,
   not page chrome. */
.insights-filter-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.625rem 0.875rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  margin-bottom: 1.25rem;
  font-size: 0.85rem;
}
.insights-filter-bar__label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
}

/* KPI tile — denser variant of .stat-tile tuned for the 4-up insights row.
   Includes a "delta" affordance under the value for ↑/↓ percentage. */
.kpi-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.85rem;
  margin-bottom: 1.25rem;
}
@media (min-width: 640px) { .kpi-row { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .kpi-row { grid-template-columns: repeat(4, 1fr); } }

.kpi-tile {
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 10px;
  padding: 0.85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.kpi-tile__label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  font-weight: 600;
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
}
.kpi-tile__value {
  font-size: 1.55rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.kpi-tile__sub {
  font-size: 0.78rem;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  font-variant-numeric: tabular-nums;
}
.kpi-tile__delta--up { color: var(--color-warning); font-weight: 600; }
.kpi-tile__delta--down { color: var(--color-success); font-weight: 600; }
.kpi-tile__delta--flat { color: color-mix(in oklch, var(--color-base-content) 55%, transparent); }

/* Provider breakdown — left card pairs the donut with a flat table. */
.provider-breakdown__table {
  width: 100%;
  font-size: 0.82rem;
  border-collapse: collapse;
  margin-top: 0.75rem;
}
.provider-breakdown__table th {
  text-align: left;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--color-base-300);
}
.provider-breakdown__table td {
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 60%, transparent);
}
.provider-breakdown__swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  display: inline-block;
  margin-right: 0.45rem;
  vertical-align: middle;
}
.provider-breakdown__table tr:last-child td { border-bottom: 0; }
.provider-breakdown__swatch--muted {
  background: color-mix(in oklch, var(--color-base-content) 22%, transparent);
}
.provider-breakdown__row--zero td {
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
}
.provider-breakdown__tag {
  margin-left: 0.45rem;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  vertical-align: middle;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  background: color-mix(in oklch, var(--color-base-content) 9%, transparent);
}

/* Chart card header — title left, period chip group right. Used on
   /insights/costs to scope each chart independently. */
.chart-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

.chart-period-tabs {
  display: inline-flex;
  gap: 2px;
  background: color-mix(in oklch, var(--color-base-content) 6%, transparent);
  border-radius: 0.4rem;
  padding: 2px;
}
.chart-period-tabs button {
  font-size: 0.72rem;
  padding: 0.25rem 0.6rem;
  border-radius: 0.3rem;
  border: 0;
  background: transparent;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  cursor: pointer;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  transition: background 0.12s ease, color 0.12s ease;
}
.chart-period-tabs button:hover {
  color: var(--color-base-content);
}
.chart-period-tabs button.is-active {
  background: var(--color-base-100);
  color: var(--color-base-content);
  box-shadow: 0 1px 2px rgba(var(--shadow-ink), var(--shadow-strength-sm));
}

/* Empty-state placeholder shown inside a chart's wrapper when the
   selected period yields no data. Sits absolutely on top of the
   (hidden) canvas so the wrapper height stays stable. */
.chart-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 0.85rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  pointer-events: none;
}


/* Cost breakdown table — dense, one row per calendar period
   (month default / day toggle). Period | Products | Cost | Cost/product. */
.runs-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}
.runs-table th {
  text-align: left;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 50%, transparent);
}
/* The blanket left-align above is more specific than the .text-right
   utility on the numeric headers (Products / Cost / Cost-per-product),
   so without this row the column headers sit on the left edge while
   the data cells right-align to the column end — visually misaligned.
   Re-state the alignment at matching specificity so the utility wins. */
.runs-table th.text-right { text-align: right; }
.runs-table td {
  padding: 0.55rem 0.6rem;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 50%, transparent);
  font-variant-numeric: tabular-nums;
}
.runs-table tr:last-child td { border-bottom: 0; }
.runs-table__empty {
  padding: 1.5rem;
  text-align: center;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-size: 0.85rem;
}

/* Info tooltip — 14px outlined "i" glyph that toggles a 240px popover.
   Anchored top-left of the parent .kpi-tile__label baseline. Pure CSS
   for the chrome; Alpine x-data="{open:false}" handles the toggle. */
.info-tip {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid color-mix(in oklch, var(--color-base-content) 40%, transparent);
  background: transparent;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-size: 9px;
  font-weight: 700;
  font-family: ui-sans-serif, system-ui, sans-serif;
  line-height: 1;
  cursor: help;
  padding: 0;
  vertical-align: -1px;
  transition: color 0.12s ease, border-color 0.12s ease;
}
.info-tip:hover,
.info-tip:focus-visible {
  color: var(--color-primary);
  border-color: var(--color-primary);
  outline: none;
}
.info-tip__pop {
  position: absolute;
  top: calc(100% + 6px);
  left: -6px;
  width: 240px;
  z-index: 30;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.4rem;
  padding: 0.55rem 0.7rem;
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.4;
  text-transform: none;
  letter-spacing: normal;
  color: var(--color-base-content);
  box-shadow: 0 6px 24px rgba(15, 20, 34, 0.08);
}
.info-tip__pop::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 8px;
  width: 8px;
  height: 8px;
  background: var(--color-base-100);
  border-top: 1px solid var(--color-base-300);
  border-left: 1px solid var(--color-base-300);
  transform: rotate(45deg);
}

/* /insights/activity — filter bar + chips + event list.
   Re-uses .activity-row from the dashboard panel so per-row styling
   stays in lockstep across surfaces. */
.activity-filter-bar {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.625rem 0.875rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  margin-bottom: 0.75rem;
  font-size: 0.85rem;
  flex-wrap: wrap;
}
.activity-filter-bar__spacer { flex: 1; }
.activity-filter-bar__label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
}

.activity-chips {
  display: inline-flex;
  gap: 0.35rem;
  flex-wrap: wrap;
}
.activity-chip {
  border: 1px solid var(--color-base-300);
  background: var(--color-base-100);
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.activity-chip:hover {
  background: color-mix(in oklch, var(--color-primary) 8%, var(--color-base-100));
  border-color: color-mix(in oklch, var(--color-primary) 25%, var(--color-base-300));
}
.activity-chip.is-active {
  background: color-mix(in oklch, var(--color-primary) 14%, transparent);
  color: var(--color-primary);
  border-color: color-mix(in oklch, var(--color-primary) 35%, transparent);
}

.activity-meta {
  font-size: 0.78rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-bottom: 0.5rem;
}

.activity-events {
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  overflow: hidden;
}
.activity-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.activity-empty {
  padding: 1.5rem;
  text-align: center;
  font-size: 0.85rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
}
.activity-show-more {
  display: flex;
  justify-content: center;
  padding: 0.75rem;
  border-top: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 35%, transparent);
}
.activity-show-more:empty { display: none; }

/* -----------------------------------------------------------------
   17. SOURCE PILLS — per-source variants for spec/field origin tables
   The base .source-pill is the neutral chrome (margin, font, radius).
   Variants tint the chip to its source family so reviewers scan a
   long specs table by source at a glance:
     icecat (info-blue) · eprel (success-green) · vendor (warning-amber)
     vision (accent-teal) · web (secondary-coral) · ai/openai (primary-mint)
   Used in: validate/_structured_specs.html, validate/_provenance.html.
   ----------------------------------------------------------------- */
.source-pill { font-variant-numeric: tabular-nums; letter-spacing: 0.01em; }
.source-pill--icecat {
  background: color-mix(in oklch, var(--color-info) 16%, transparent);
  color: color-mix(in oklch, var(--color-info) 80%, var(--color-base-content));
}
.source-pill--eprel,
.source-pill--epreL {
  background: color-mix(in oklch, var(--color-success) 18%, transparent);
  color: color-mix(in oklch, var(--color-success) 80%, var(--color-base-content));
}
.source-pill--vendor,
.source-pill--erp {
  background: color-mix(in oklch, var(--color-warning) 16%, transparent);
  color: color-mix(in oklch, var(--color-warning) 80%, var(--color-base-content));
}
.source-pill--vision {
  background: color-mix(in oklch, var(--color-accent) 18%, transparent);
  color: color-mix(in oklch, var(--color-accent) 85%, var(--color-base-content));
}
.source-pill--web,
.source-pill--gs1 {
  background: color-mix(in oklch, var(--color-secondary) 16%, transparent);
  color: color-mix(in oklch, var(--color-secondary) 80%, var(--color-base-content));
}
.source-pill--ai,
.source-pill--openai,
.source-pill--category_suggester {
  background: color-mix(in oklch, var(--color-primary) 18%, transparent);
  color: color-mix(in oklch, var(--color-primary) 92%, var(--color-base-content));
}
.source-pill--doc,
.source-pill--doc_search {
  background: color-mix(in oklch, var(--color-base-content) 12%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
}

/* -----------------------------------------------------------------
   18. DATA-SOURCES STRIP — design's right-rail summary chip strip
   Compact pill row above the Field origins / Debug cards. Each
   .ds-pill carries an --ok (source contributed) or --miss (tried,
   no match) modifier.
   ----------------------------------------------------------------- */
.ds-strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0.55rem 0.7rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.55rem;
}
.ds-strip__title {
  font-size: 0.62rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 700;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-right: 0.2rem;
}
.ds-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.12rem 0.5rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 500;
  border: 1px solid transparent;
  cursor: default;
  user-select: none;
}
.ds-pill::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.85;
}
.ds-pill--ok {
  background: color-mix(in oklch, var(--color-success) 12%, transparent);
  color: color-mix(in oklch, var(--color-success) 90%, var(--color-base-content));
  border-color: color-mix(in oklch, var(--color-success) 32%, transparent);
}
.ds-pill--miss {
  background: color-mix(in oklch, var(--color-base-200) 60%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  border-color: color-mix(in oklch, var(--color-base-content) 18%, transparent);
}
.ds-pill--miss::before { background: color-mix(in oklch, var(--color-base-content) 35%, transparent); opacity: 1; }
.ds-pill--link {
  cursor: pointer;
  text-decoration: none;
  transition: filter 120ms ease-out;
}
.ds-pill--link:hover {
  filter: brightness(1.08);
  text-decoration: underline;
}

/* -----------------------------------------------------------------
   19. ENERGY-CARD — large hero label + stacked class / datasheet column
   Used in validate/_energy_labels.html. 1-1-1: three equal square tiles
   (sized like the image grid) — class, label, datasheet. The EU-label
   image scales like a product photo (object-fit: contain on a white
   canvas → shown full, scaled down inside the box). .energy-class-badge
   tints by class letter; the green-gradient idiom is reserved for A-class.
   ----------------------------------------------------------------- */
.energy-card__body {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.625rem;
  align-items: start;
}
@media (max-width: 900px) {
  .energy-card__body { grid-template-columns: 1fr; }
}
.energy-class-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
}
.energy-class-badge {
  width: 60%;
  aspect-ratio: 1 / 1;
  border-radius: 14px;
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: clamp(38px, 6vw, 64px);
  letter-spacing: -0.02em;
  background: var(--color-base-200);
  color: var(--color-base-content);
}
/* Multi-char labels (legacy A+/A++/A+++) shrink so they fit the square. */
.energy-class-badge--compound { font-size: clamp(22px, 3.4vw, 36px); }
/* Legacy-scale marker under the badge — keeps old A+++ from reading as new A. */
.energy-slot__legacy-tag {
  margin-top: 6px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: center;
  color: var(--color-warning, #b4790f);
}
.energy-class-badge--A { background: linear-gradient(135deg, #9fe88d, #5eb46f); color: #0b1810; }
.energy-class-badge--B { background: linear-gradient(135deg, #c0e88d, #8db46f); color: #131a08; }
.energy-class-badge--C { background: linear-gradient(135deg, #e8de8d, #b49d6f); color: #1a1408; }
.energy-class-badge--D { background: linear-gradient(135deg, #e8c98d, #b48d6f); color: #1a1308; }
.energy-class-badge--E { background: linear-gradient(135deg, #e8a88d, #b4796f); color: #1a0d08; }
.energy-class-badge--F { background: linear-gradient(135deg, #e8908d, #b46f6f); color: #1a0a08; }
.energy-class-badge--G { background: linear-gradient(135deg, #e8758d, #b46f88); color: #1a0810; }

.energy-slot {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.energy-slot__label {
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
}
.energy-slot__frame {
  /* Real EU energy labels are tall portraits (~689 × 1739 ≈ 2:5). The
     slot honours that ratio so the label renders at full readable
     size without forcing a lightbox click — operators verify A↔G
     letter + scale at a glance. */
  aspect-ratio: 2 / 5;
  width: 100%;
  min-height: 22rem;
  border-radius: 8px;
  background: var(--color-base-200);
  border: 1px solid var(--color-base-300);
  display: grid;
  place-items: center;
  overflow: hidden;
  position: relative;
}
.energy-slot__frame img {
  /* Lock the img to the frame box explicitly so ``object-fit: contain``
     engages — without this the browser renders at intrinsic pixel size
     and blows past the frame (caught on the 2026-05-15 review). */
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  cursor: zoom-in;
}
.energy-slot__frame--square {
  /* Class-letter badge tile and datasheet tile — square so the letter
     badge sits centred without the tall stretch the EU-label slots use. */
  aspect-ratio: 1 / 1;
  min-height: 0;
}
.energy-slot__frame--image {
  /* The large EU label tile: a white canvas with padding, exactly like
     the product image tiles, so the portrait label letterboxes on white
     (object-fit: contain on .energy-slot__frame img) and reads as a
     scaled-down product photo rather than a stretched hero. */
  background: #fff;
  padding: 0.25rem;
}
.energy-slot__frame--filled {
  background: linear-gradient(180deg,
    color-mix(in oklch, var(--color-success) 14%, var(--color-base-200)),
    color-mix(in oklch, var(--color-success) 4%, var(--color-base-300)));
  border-color: color-mix(in oklch, var(--color-success) 28%, var(--color-base-300));
}
.energy-slot__frame--empty {
  background: var(--color-base-200);
  border-style: dashed;
  color: color-mix(in oklch, var(--color-base-content) 40%, transparent);
}
.energy-slot__actions {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin-top: 0.25rem;
  flex-wrap: wrap;
}
.energy-slot__remove { color: color-mix(in oklch, var(--color-error) 78%, var(--color-base-content)); }
.energy-slot__remove:hover { color: var(--color-error); }

/* -----------------------------------------------------------------
   20. SPECS-TABLE-FLAT — design's structured-specs table layout
   Hairline borders, b3 header background, source pill column.
   Sticky head supports scrolling when "Show all" is toggled open.
   ----------------------------------------------------------------- */
.specs-table-flat {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
}
.specs-table-flat thead th {
  text-align: left;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  padding: 0.45rem 0.65rem;
  background: color-mix(in oklch, var(--color-base-300) 35%, transparent);
  border-bottom: 1px solid var(--color-base-300);
  position: sticky;
  top: 0;
  z-index: 1;
}
.specs-table-flat tbody td {
  padding: 0.45rem 0.65rem;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 55%, transparent);
}
.specs-table-flat tbody tr:hover td {
  background: color-mix(in oklch, var(--color-base-200) 55%, transparent);
}
.specs-table-flat tbody tr:last-child td { border-bottom: 0; }
.specs-table-flat__key {
  color: color-mix(in oklch, var(--color-base-content) 62%, transparent);
}
.specs-table-flat__val {
  color: var(--color-base-content);
  font-weight: 500;
}
.specs-table-flat__src {
  width: 90px;
  text-align: right;
}
.specs-table-flat__actions {
  width: 2rem;
  text-align: right;
  padding-right: 0.45rem !important;
}
.specs-table-flat__val[contenteditable="true"] {
  outline: none;
  border-radius: 0.25rem;
}
.specs-table-flat__val[contenteditable="true"]:focus {
  background: color-mix(in oklch, var(--color-primary) 8%, var(--color-base-100));
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--color-primary) 40%, transparent);
}
.spec-row-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border: 0;
  background: transparent;
  border-radius: 0.3rem;
  color: color-mix(in oklch, var(--color-base-content) 40%, transparent);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.specs-table-flat tbody tr:hover .spec-row-remove { opacity: 1; }
.spec-row-remove:hover {
  background: color-mix(in oklch, var(--color-error) 12%, transparent);
  color: var(--color-error);
}
.spec-row-remove svg { width: 12px; height: 12px; }

/* -----------------------------------------------------------------
   21. BRAND-MATCH ROW — the "No match in Akeneo / Create" inline row
   Shown under the two-up Brand input grid on validate. Keeps the
   message + CTA on one line at ≥720px content width.
   ----------------------------------------------------------------- */
.brand-match-row {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex-wrap: wrap;
  padding: 0.55rem 0.65rem;
  background: color-mix(in oklch, var(--color-base-200) 55%, transparent);
  border: 1px dashed color-mix(in oklch, var(--color-base-content) 18%, transparent);
  border-radius: 0.45rem;
  font-size: 0.78rem;
  color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
}
.brand-match-row strong { color: var(--color-base-content); font-weight: 600; }
.brand-match-row__spacer { flex: 1; min-width: 0.5rem; }

/* -----------------------------------------------------------------
   22. DEBUG-CARD — collapsible "Debug · pipeline trace" drawer
   Two flavours: <details> native disclosure (zero-JS) and Alpine
   x-show usage. Tightens header padding and adds a caret rotation
   so the disclosure animation matches the rest of the app.
   ----------------------------------------------------------------- */
.debug-card { background: var(--color-base-100); }
.debug-card > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
}
.debug-card > summary::-webkit-details-marker { display: none; }
.debug-card .debug-card__caret {
  transition: transform 0.18s ease;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
}
.debug-card[open] .debug-card__caret { transform: rotate(180deg); }

.debug-trace {
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}
.debug-trace li {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.3rem 0;
  border-bottom: 1px dashed color-mix(in oklch, var(--color-base-300) 60%, transparent);
}
.debug-trace li:last-child { border-bottom: 0; }
.debug-trace__stage {
  flex: 1;
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
}
.debug-trace__t {
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-size: 0.72rem;
}
.debug-trace__cost {
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-size: 0.7rem;
  letter-spacing: 0.02em;
}

/* -----------------------------------------------------------------
   23. COST DONUT CENTER OVERLAY — total + period label sit in the
   middle of the Chart.js doughnut. Anchored absolutely inside the
   .cost-donut-wrap (position:relative).
   ----------------------------------------------------------------- */
.cost-donut-wrap {
  position: relative;
  height: 200px;
  max-width: 100%;
}
.cost-donut-center {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  text-align: center;
}
.cost-donut-center__value {
  font-size: 1.45rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--color-base-content);
  line-height: 1.1;
}
.cost-donut-center__label {
  display: block;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  margin-top: 0.2rem;
}

/* -----------------------------------------------------------------
   24. ACTIVITY-ROW — design's 84px time column anatomy
   The dashboard activity feed + /insights/activity rows share this
   shell. Bump from 72px → 84px to match the design, and add the
   .activity-row__sku pill chrome.
   ----------------------------------------------------------------- */
.activity-row { grid-template-columns: 84px 1fr auto; }
.activity-row__sku {
  font-family: var(--mono);
  font-size: 0.7rem;
  padding: 0.05rem 0.4rem;
  border-radius: 4px;
  background: color-mix(in oklch, var(--color-base-200) 70%, transparent);
  border: 1px solid color-mix(in oklch, var(--color-base-300) 60%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
  letter-spacing: 0.01em;
}

/* -----------------------------------------------------------------
   25. KBD CHIP — monospace shortcut tag for action-bar buttons
   Styling parallels DaisyUI .kbd but tighter for the validate bar.
   ----------------------------------------------------------------- */
.kbd-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 0.32rem;
  border-radius: 4px;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 600;
  background: color-mix(in oklch, var(--color-base-content) 12%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
  border: 1px solid color-mix(in oklch, var(--color-base-content) 18%, transparent);
  margin-left: 0.35rem;
  vertical-align: middle;
}

/* -----------------------------------------------------------------
   Unified Documents table — folder/child row treatment.
   The table itself reuses .table-products chrome (uppercase thead,
   hairline rows, primary-tinted hover). These classes layer the
   chevron, indent rail, and hover-revealed actions on top.
   ----------------------------------------------------------------- */
.doc-table { font-size: 13px; width: 100%; border-collapse: collapse; table-layout: fixed; }

/* Fixed layout so the DOCUMENT column truncates instead of stretching the
   table past the card. Column widths are declared on the header row; the
   first column is left unsized to absorb all remaining width. */
.doc-table th:not(:first-child),
.doc-row > td:not(:first-child) { white-space: nowrap; }
.doc-table th:nth-child(2) { width: 120px; }  /* Type */
.doc-table th:nth-child(3) { width: 92px; }   /* Size */
.doc-table th:nth-child(4) { width: 126px; }  /* Parse */
.doc-table th:nth-child(5) { width: 114px; }  /* Index */
.doc-table th:nth-child(6) { width: 140px; }  /* Attached SKUs */
.doc-table th:nth-child(7) { width: 132px; }  /* Uploaded */
.doc-table th:nth-child(8) { width: 120px; }  /* Uploader */
.doc-table th:nth-child(9) { width: 96px; }   /* actions */

.doc-table thead th {
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 10px 14px;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  background: color-mix(in oklch, var(--color-base-200) 50%, var(--color-base-100));
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 65%, transparent);
  text-align: left;
  white-space: nowrap;
}
.doc-table thead th.text-right { text-align: right; }

.doc-row > td {
  padding: 9px 14px;
  vertical-align: middle;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 55%, transparent);
}
.doc-row { transition: background 80ms ease; }
.doc-row:hover { background: color-mix(in oklch, var(--color-primary) 5%, transparent); }
.doc-row:last-child > td { border-bottom: none; }

.doc-row__name {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  min-width: 0;
}
.doc-row__name-text {
  font-weight: 500;
  color: color-mix(in oklch, var(--color-base-content) 92%, transparent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.doc-row__name-sub {
  display: block;
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  font-weight: 400;
  margin-top: 1px;
}

/* Square file icon — 28px tile, neutral with a subtle accent border.
   Variant hooks let documents/images carry distinct tints without
   re-defining the box. */
.doc-row__icon {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  background: color-mix(in oklch, var(--color-base-300) 65%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
  flex-shrink: 0;
  border: 1px solid color-mix(in oklch, var(--color-base-300) 80%, transparent);
}
.doc-row__icon svg { width: 14px; height: 14px; }
.doc-row__icon--pdf    { color: var(--color-error); background: color-mix(in oklch, var(--color-error) 12%, var(--color-base-100)); border-color: color-mix(in oklch, var(--color-error) 25%, transparent); }
.doc-row__icon--excel  { color: var(--color-success); background: color-mix(in oklch, var(--color-success) 12%, var(--color-base-100)); border-color: color-mix(in oklch, var(--color-success) 25%, transparent); }
.doc-row__icon--image  { color: var(--color-info); background: color-mix(in oklch, var(--color-info) 12%, var(--color-base-100)); border-color: color-mix(in oklch, var(--color-info) 25%, transparent); }
.doc-row__icon--folder { color: var(--color-accent); background: color-mix(in oklch, var(--color-accent) 12%, var(--color-base-100)); border-color: color-mix(in oklch, var(--color-accent) 25%, transparent); }

/* Chevron toggle sits to the left of the icon on folder rows; takes
   the same 16px footprint on flat rows (rendered as a placeholder)
   so the document name column stays aligned across kinds. */
.doc-row__chevron {
  width: 16px;
  height: 16px;
  display: inline-grid;
  place-items: center;
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  transition: transform 120ms ease, color 120ms ease;
}
.doc-row__chevron svg { width: 12px; height: 12px; }
.doc-row__chevron:hover { color: var(--color-base-content); }
.doc-row__chevron.is-open { transform: rotate(90deg); }
.doc-row__chevron--spacer { cursor: default; pointer-events: none; visibility: hidden; }

/* Child row indent + left rail. The indent runs from the table edge
   to the row's content; the 2px rail draws on the first cell so it
   visually nests under the folder. */
.doc-row--child > td:first-child {
  padding-left: 38px;
  position: relative;
}
.doc-row--child > td:first-child::before {
  content: "";
  position: absolute;
  left: 22px;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--color-base-300);
  border-radius: 1px;
}
.doc-row--child > td {
  background: color-mix(in oklch, var(--color-base-200) 35%, transparent);
}
.doc-row--child .doc-row__icon { width: 24px; height: 24px; border-radius: 5px; }
.doc-row--child .doc-row__icon svg { width: 12px; height: 12px; }

/* Action cluster — sits in the last column. Folder/document rows show
   their buttons by default; child rows reveal on row hover only so
   the indent rail stays uncluttered while scanning. */
/* Fixed 3-column grid (download | open | delete), right-anchored, so each
   action lands in the same column across every row type — rows missing an
   action emit an empty .doc-row__action-slot to hold its column. */
.doc-row__actions {
  display: grid;
  grid-template-columns: repeat(3, 26px);
  align-items: center;
  gap: 0.25rem;
  justify-content: end;
}
.doc-row__actions > * { justify-self: center; }
.doc-row__action-slot { width: 26px; height: 26px; }
/* Canonical icon-only button. One vocabulary for square icon affordances
   (table-row actions, toolbar toggles). Base = 30px ghost square; --sm
   (26px) for dense table rows, --danger for destructive hover. Use real
   .btn for anything with a text label; reserve .icon-btn for icon-only. */
.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 6px;
  background: transparent;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  border: 1px solid transparent;
  cursor: pointer;
  flex: none;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.icon-btn:hover {
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
  color: var(--color-base-content);
  border-color: var(--color-base-300);
}
.icon-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.icon-btn:disabled:hover {
  background: transparent;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  border-color: transparent;
}
.icon-btn svg, .icon-btn .icon { width: 15px; height: 15px; }
.icon-btn--sm { width: 26px; height: 26px; border-radius: 5px; }
.icon-btn--sm svg, .icon-btn--sm .icon { width: 13px; height: 13px; }
.icon-btn--danger:hover {
  color: var(--color-error);
  border-color: color-mix(in oklch, var(--color-error) 35%, transparent);
  background: color-mix(in oklch, var(--color-error) 10%, transparent);
}

.doc-row__child-actions { opacity: 0; transition: opacity 120ms ease; }
.doc-row--child:hover .doc-row__child-actions,
.doc-row--child:focus-within .doc-row__child-actions { opacity: 1; }

/* Inline pencil shown next to the filename on row hover. Pure presentational
   button — the click handler toggles Alpine state on the row scope. */
.doc-row__rename {
  width: 20px; height: 20px;
  border-radius: 4px;
  display: inline-grid;
  place-items: center;
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
  background: transparent;
  border: 0;
  cursor: pointer;
  opacity: 0;
  transition: opacity 120ms ease, color 120ms ease, background 120ms ease;
  margin-left: 0.2rem;
  flex-shrink: 0;
}
.doc-row__rename svg { width: 11px; height: 11px; }
.doc-row:hover .doc-row__rename { opacity: 1; }
.doc-row__rename:hover {
  color: var(--color-base-content);
  background: color-mix(in oklch, var(--color-base-content) 9%, transparent);
}

.doc-row__rename-input {
  font-size: 13px;
  padding: 2px 6px;
  border-radius: 4px;
  border: 1px solid var(--color-primary);
  background: var(--color-base-100);
  color: var(--color-base-content);
  outline: none;
  min-width: 220px;
  font-family: inherit;
}

/* Inline filename tag list under the name (e.g. "specs · dimensions").
   Sub-meta row, low-weight, never wraps to a third line. */
.doc-row__tags {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.55rem;
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
}
.doc-row__tags-sep { color: color-mix(in oklch, var(--color-base-content) 25%, transparent); }

/* Folder summary badge — "Batch · 50" pill. Slightly tighter than
   status-pill so it reads as meta, not a status signal. */
.doc-row__batch-meta {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.7rem;
  font-weight: 500;
  color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  border: 1px solid var(--color-base-300);
  background: var(--color-base-200);
}

/* In-flight strip — slim row above the table; one line per active
   batch. Border-left rail in info-blue so it reads as "happening now"
   without screaming for attention. */
.import-strip {
  display: flex;
  flex-direction: column;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-left: 3px solid var(--color-info);
  border-radius: 8px;
  padding: 0.55rem 0.85rem;
  margin-bottom: 1rem;
  gap: 0.35rem;
}
.import-strip__row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.84rem;
}
.import-strip__row + .import-strip__row {
  padding-top: 0.35rem;
  border-top: 1px solid color-mix(in oklch, var(--color-base-300) 60%, transparent);
}
.import-strip__label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--color-info);
}
.import-strip__name { font-weight: 500; }
.import-strip__meta { color: color-mix(in oklch, var(--color-base-content) 55%, transparent); font-size: 0.78rem; }
.import-strip__link { margin-left: auto; }

/* Empty state below the table — flat, no card. */
.doc-empty {
  text-align: center;
  padding: 3rem 1rem;
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  font-size: 0.88rem;
}
.doc-empty__title { font-size: 1rem; color: color-mix(in oklch, var(--color-base-content) 75%, transparent); margin-bottom: 0.35rem; }


/* -----------------------------------------------------------------
   28. VALIDATE — page shell, identity strip, progress, cards
   The reviewer's primary screen. Three-column grid (category tree |
   center stack | right rail) with sticky asides. The identity strip
   sits between the page header and the grid as a single horizontal
   row carrying SKU / name / brand / EAN / status / Akeneo toggle.
   ----------------------------------------------------------------- */

.validate-progress {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.8125rem;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
}
.validate-progress b { color: var(--color-base-content); font-weight: 600; }
.validate-progress__bar {
  width: 128px;
  height: 4px;
  background: var(--color-base-300);
  border-radius: 999px;
  overflow: hidden;
}
.validate-progress__fill {
  height: 100%;
  background: var(--color-primary);
  border-radius: inherit;
  transition: width 300ms ease;
}

.validate-ident {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  flex-wrap: wrap;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.625rem;
  padding: 0.7rem 1rem;
  margin-bottom: 0.875rem;
}
.validate-ident__sku {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: color-mix(in oklch, var(--color-base-content) 72%, transparent);
  padding: 0.18rem 0.5rem;
  background: color-mix(in oklch, var(--color-base-content) 7%, transparent);
  border-radius: 4px;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.validate-ident__name {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--color-base-content);
  letter-spacing: -0.015em;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 32rem;
}
.validate-ident__ean {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  letter-spacing: 0.01em;
}
.validate-ident__spacer { flex: 1; min-width: 0.5rem; }
.validate-ident__toggle {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.72rem;
}
.validate-ident__toggle .label-akeneo {
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 500;
  letter-spacing: 0;
}

/* Data-quality grade chip — A–E badge sitting in the identity strip as a
   top-level quality signal. Letter + word ("Grade B"), colour-coded by
   grade. Variants set a single ``--q`` colour var that the base rule mixes
   into background / border / text so all five share one ramp. */
.quality-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  padding: 0.16rem 0.5rem 0.16rem 0.4rem;
  border-radius: 6px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0;
  white-space: nowrap;
  --q: var(--color-base-content);
  border: 1px solid color-mix(in oklch, var(--q) 35%, transparent);
  background: color-mix(in oklch, var(--q) 14%, transparent);
  color: color-mix(in oklch, var(--q) 92%, var(--color-base-content));
}
.quality-chip__letter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.1rem;
  height: 1.1rem;
  border-radius: 4px;
  font-size: 0.78rem;
  font-weight: 800;
  line-height: 1;
  background: color-mix(in oklch, var(--q) 88%, transparent);
  color: var(--color-base-100);
}
.quality-chip__label {
  font-weight: 600;
  color: color-mix(in oklch, var(--color-base-content) 78%, transparent);
}
.quality-chip--a { --q: var(--color-success); }
.quality-chip--b { --q: var(--color-accent); }
.quality-chip--c { --q: color-mix(in oklch, var(--color-base-content) 60%, transparent); }
.quality-chip--d { --q: var(--color-warning); }
.quality-chip--e { --q: var(--color-error); }

/* Right-rail "Data quality" panel: two 0–100 axis meters + a missing[]
   worklist of chips. Reuses .v-card chrome from the rail. */
.quality-axes {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.quality-axis {
  display: grid;
  grid-template-columns: 5.5rem 1fr 2.2rem;
  align-items: center;
  gap: 0.5rem;
}
.quality-axis__label {
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 62%, transparent);
}
.quality-axis__track {
  height: 5px;
  border-radius: 999px;
  background: color-mix(in oklch, var(--color-base-content) 10%, transparent);
  overflow: hidden;
}
.quality-axis__fill {
  height: 100%;
  border-radius: 999px;
  background: var(--color-accent);
}
.quality-axis__fill--warn { background: var(--color-warning); }
.quality-axis__fill--low  { background: var(--color-error); }
.quality-axis__val {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-align: right;
  color: color-mix(in oklch, var(--color-base-content) 78%, transparent);
}
.quality-missing {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: 0.65rem;
}
.quality-missing__chip {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  font-size: 0.68rem;
  font-weight: 500;
  border-radius: 4px;
  border: 1px solid color-mix(in oklch, var(--color-warning) 32%, transparent);
  background: color-mix(in oklch, var(--color-warning) 12%, transparent);
  color: color-mix(in oklch, var(--color-warning) 92%, var(--color-base-content));
}
.quality-complete {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.4rem;
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-success) 92%, var(--color-base-content));
}

.validate-shell {
  display: grid;
  grid-template-columns: 288px minmax(0, 1fr) 320px;
  gap: 1rem;
  align-items: start;
}
@media (max-width: 1440px) {
  .validate-shell { grid-template-columns: 248px minmax(0, 1fr) 296px; }
}
@media (max-width: 1100px) {
  .validate-shell { grid-template-columns: 1fr; }
}
.validate-shell > aside {
  position: sticky;
  top: calc(var(--topbar-h) + 16px);
  align-self: start;
  max-height: calc(100vh - var(--topbar-h) - 28px);
  overflow-y: auto;
  overflow-x: visible;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
@media (max-width: 1100px) {
  .validate-shell > aside { position: static; max-height: none; overflow: visible; }
}

/* Center-column card: thin header (title + sub) + flat body. Different
   from the dashboard's .content-card pattern — tighter, no shadow,
   per-card right slot for accessory pills/buttons.
   Spacing trace to canonical (EnrichmentHelper/app/styles.css):
     .card-body          → 16px 18px (1rem 1.125rem)   [tight 12 14]
     .card-header        → 12px 18px (0.75rem 1.125rem)
     .card-title         → 13-14px / 600
     .mb-3 between cards → 12px (0.75rem) */
.v-card {
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.625rem;
  margin-bottom: 0.75rem;
  overflow: visible;
}
.v-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.625rem 0.875rem;   /* 10px 14px — canon header-tight */
  background: color-mix(in oklch, var(--color-base-200) 35%, var(--color-base-100));
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 70%, transparent);
  border-radius: 0.625rem 0.625rem 0 0;
}
.v-card__header-main { min-width: 0; }
.v-card__title {
  font-size: 0.8125rem;         /* 13px — canon card-title */
  font-weight: 600;
  color: var(--color-base-content);
  letter-spacing: -0.005em;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.v-card__title svg.icon { color: color-mix(in oklch, var(--color-base-content) 55%, transparent); width: 14px; height: 14px; }
.v-card__sub {
  font-size: 0.71875rem;        /* 11.5px — canon card-header__sub */
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  margin-top: 0.125rem;
}
.v-card__sub--link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  text-decoration: none;
  transition: color 0.12s ease;
}
.v-card__sub--link code {
  font-size: 0.69rem;
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
  background: color-mix(in oklch, var(--color-base-300) 60%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
  font-family: var(--mono);
}
.v-card__sub--link:hover {
  color: var(--color-primary);
}
.v-card__sub--link:hover code {
  color: var(--color-primary);
  background: color-mix(in oklch, var(--color-primary) 12%, transparent);
}
.v-card__body { padding: 0.75rem 0.875rem; }    /* 12px 14px — tight body */
.v-card__body--tight { padding: 0.5rem 0.75rem; }

/* External-file drop affordance. Activated only for OS file drags (the JS
   gates on dataTransfer.types including 'Files') so internal SortableJS
   tile reorders never trigger it. Scoped per card / per energy slot. */
.eh-dropzone { position: relative; }
.eh-dropzone.is-drop-active {
  outline: 2px dashed var(--color-primary);
  outline-offset: -3px;
  border-radius: 0.625rem;
  background: color-mix(in oklch, var(--color-primary) 6%, transparent);
}
.eh-dropzone__hint {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: none;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  pointer-events: none;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-primary);
  background: color-mix(in oklch, var(--color-base-100) 72%, transparent);
  border-radius: inherit;
}
.eh-dropzone.is-drop-active .eh-dropzone__hint { display: flex; }
/* Energy-slot variant: the dashed ring hugs the square frame, not the
   whole slot column, so it lines up with the tile being targeted. */
.energy-slot.eh-dropzone.is-drop-active {
  outline: none;
  background: transparent;
}
.energy-slot.eh-dropzone.is-drop-active .energy-slot__frame {
  outline: 2px dashed var(--color-primary);
  outline-offset: -3px;
  background: color-mix(in oklch, var(--color-primary) 8%, transparent);
}

/* Category-tree sub-card variants (sit inside an aside .v-card) */
.tree-suggestion {
  padding: 0.55rem 0.85rem;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 70%, transparent);
  background: color-mix(in oklch, var(--color-primary) 6%, transparent);
}
.tree-suggestion__pill {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  padding: 0.1rem 0.45rem;
  border-radius: 4px;
  background: color-mix(in oklch, var(--color-primary) 18%, transparent);
  color: color-mix(in oklch, var(--color-primary) 95%, var(--color-base-content));
}
.tree-suggestion__crumb {
  font-size: 0.75rem;
  margin-top: 0.35rem;
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
  line-height: 1.4;
}
.tree-suggestion__crumb b { color: var(--color-base-content); font-weight: 600; }
.tree-suggestion__crumb a {
  color: color-mix(in oklch, var(--color-primary) 95%, var(--color-base-content));
  text-decoration: none;
  font-weight: 600;
}
.tree-suggestion__crumb a:hover { text-decoration: underline; }
.tree-search {
  padding: 0.45rem 0.6rem;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 70%, transparent);
}
.tree-list {
  flex: 1;
  overflow-y: auto;
  padding: 0.25rem 0 0.4rem;
  min-height: 0;
}

/* Image grid (validate-page variant — design's 4-col tile grid).
   Canonical (app/styles.css line 521): grid-template-columns: repeat(4,1fr);
   gap: 10px; .image-tile aspect-ratio: 1/1.
   The 1fr × 4 sizing stretches tiles to fill the center column width;
   on a 1440px viewport that yields ~220px squares which dominate the
   page. Cap the grid at a sensible max so tiles read as thumbnails,
   not hero images. The 6-of-10 product image set looks balanced at
   ~140px per tile. */
.image-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.625rem;
}
@media (max-width: 1100px) {
  .image-grid { grid-template-columns: repeat(3, 1fr); }
}
.image-tile {
  position: relative;
  aspect-ratio: 1 / 1;
  /* White background — product photos almost universally ship on
     white, so a #fff tile makes the contained image read as a
     continuous canvas. NOTE: any embedded whitespace in the source
     (e.g. a portrait product on a 1080×1080 white canvas with the
     subject in the top 70%) WILL render — object-fit: contain is
     working — but the whitespace pixels become invisible against
     this background. That's expected. The lightbox sits on dark
     base-100 so those same whitespace pixels read as visible padding
     there; not a bug, just contrast. A slate tile bg was tried
     (commit 8f5e78b) and rejected. */
  background: #fff;
  border: 1px solid var(--color-base-300);
  border-radius: 0.375rem;
  overflow: hidden;
  /* Plain block container. An earlier `display: grid; place-items:
     center` made the img's height: 100% resolve against an auto-sized
     grid cell — for a 790×1400 portrait tile, the img rendered at
     142.5×252.5 inside a 152×152 tile, overflowed, was clipped, and
     looked like object-fit: cover (no letterboxing). With block-flow
     the img's percent height resolves against the tile's intrinsic
     height set by aspect-ratio, and contain letterboxes correctly. */
  display: block;
  cursor: grab;
  padding: 0.25rem;
}
.image-tile img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.image-tile__main {
  position: absolute;
  left: 6px;
  bottom: 6px;
  background: var(--color-primary);
  color: var(--color-primary-content);
  font-size: 0.6rem;
  font-weight: 700;
  padding: 0.1rem 0.4rem;
  border-radius: 3px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  z-index: 2;
}
.image-tile__overlay-btn {
  position: absolute;
  top: 4px;
  right: 4px;
  opacity: 0;
  transition: opacity 0.12s;
  z-index: 2;
}
.image-tile:hover .image-tile__overlay-btn { opacity: 1; }

/* Description preview pane — readable serif-safe leading inside cards */
.html-preview {
  font-size: 0.85rem;
  line-height: 1.55;
  color: color-mix(in oklch, var(--color-base-content) 92%, transparent);
  max-height: 18rem;
  overflow-y: auto;
}
.html-preview p { margin: 0 0 0.6rem; }
.html-preview p:last-child { margin-bottom: 0; }
.html-preview ul { margin: 0.4rem 0 0.6rem 1.25rem; padding: 0; }
.html-preview li { margin: 0.15rem 0; color: color-mix(in oklch, var(--color-base-content) 75%, transparent); }
.html-preview b, .html-preview strong { color: var(--color-base-content); font-weight: 600; }

/* Inline input with trailing unit (EUR, kg, etc.) */
.input-suffix {
  position: relative;
  display: flex;
  align-items: stretch;
  border: 1px solid color-mix(in oklch, var(--color-base-content) 18%, transparent);
  background: color-mix(in oklch, var(--color-base-200) 35%, transparent);
  border-radius: 0.4rem;
  overflow: hidden;
}
.input-suffix .input,
.input-suffix input {
  flex: 1;
  border: none;
  background: transparent;
  padding: 0.4rem 0.55rem;
  font-size: 0.85rem;
  color: var(--color-base-content);
  min-width: 0;
}
.input-suffix .input:focus,
.input-suffix input:focus { outline: none; }
.input-suffix:focus-within {
  border-color: color-mix(in oklch, var(--color-primary) 60%, transparent);
}
.input-suffix__unit {
  display: inline-flex;
  align-items: center;
  padding: 0 0.55rem;
  font-size: 0.7rem;
  font-weight: 500;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  background: color-mix(in oklch, var(--color-base-300) 50%, transparent);
  border-left: 1px solid color-mix(in oklch, var(--color-base-content) 12%, transparent);
}

/* Logistics card: bottom hint row under the 3-col grid */
.logistics-hint {
  margin-top: 0.6rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  flex-wrap: wrap;
}
.logistics-hint b { color: color-mix(in oklch, var(--color-base-content) 88%, transparent); font-weight: 600; }
.logistics-hint__measure {
  font-variant-numeric: tabular-nums;
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
}
/* Mint star next to a field label, indicating "AI-picked this value".
   Same idiom as the categories tree's suggested-leaf marker. */
.ai-suggest-mark {
  color: var(--color-success);
  font-size: 0.75rem;
  margin-left: 0.25rem;
  cursor: help;
}

/* Brand card 2-col + full-width match row underneath */
.brand-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.8rem;
  align-items: end;
}
@media (max-width: 1100px) {
  .brand-grid { grid-template-columns: 1fr; }
}
.brand-grid__row { grid-column: 1 / -1; }
.brand-grid__label {
  font-size: 0.7rem;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 500;
  margin-bottom: 0.25rem;
  display: block;
}
.brand-grid__label span.muted { color: color-mix(in oklch, var(--color-base-content) 35%, transparent); }
.brand-grid__readonly {
  width: 100%;
  border: 1px solid color-mix(in oklch, var(--color-base-content) 14%, transparent);
  background: color-mix(in oklch, var(--color-base-300) 35%, transparent);
  border-radius: 0.4rem;
  padding: 0.45rem 0.6rem;
  font-size: 0.85rem;
  color: color-mix(in oklch, var(--color-base-content) 78%, transparent);
}

/* Action bar — sticky flat bar at the bottom of the center stack.
   Shadow is intentionally softer than the original — the design's
   floating shells use a similar two-stop drop, not a single dark
   slab. Heavy shadows compete with the green Approve glow. */
.action-bar {
  position: sticky;
  /* Flush against the viewport bottom — no gap. Negative bottom +
     equal padding-bottom on .app-content was the canonical pattern,
     but a flat 0 reads cleaner with our rounded card corners and
     keeps the bar locked even when /app-content has its own padding
     bottom. */
  bottom: 0;
  z-index: 30;
  margin-top: 0.85rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  /* Square the bottom corners so the bar reads as glued to the
     viewport edge rather than floating with rounded skirts. */
  border-radius: 0.65rem 0.65rem 0 0;
  padding: 0.6rem 0.85rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: nowrap;
  box-shadow: 0 -8px 24px rgba(var(--shadow-ink), var(--shadow-strength-md)),
              0 -1px 0 rgba(var(--shadow-ink), var(--shadow-strength-sm));
}
.action-bar__shortcut {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  font-size: 0.62rem;
  font-family: var(--mono);
  padding: 0 0.34rem;
  border-radius: 4px;
  background: var(--color-base-200);
  color: color-mix(in oklch, var(--color-base-content) 78%, transparent);
  border: 1px solid var(--color-base-300);
  margin-left: 0.4rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  line-height: 1;
}
.action-bar__hint {
  font-size: 0.72rem;
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
}
.action-bar__nav {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  flex-shrink: 0;
  /* Group Prev + Next in a single tinted pill so the pair reads as a
     unit and matches the visual weight of the action buttons. */
  background: color-mix(in oklch, var(--color-base-content) 6%, transparent);
  border: 1px solid color-mix(in oklch, var(--color-base-content) 10%, transparent);
  border-radius: 0.4rem;
  padding: 2px;
}
.action-bar__nav .btn { white-space: nowrap; }

/* Hero approve button — flat mint, kbd chip inverted. Single-line to
   keep the action bar a single horizontal row at laptop widths. */
.btn-approve {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.38rem 0.7rem;
  border-radius: 0.45rem;
  background: var(--color-primary);
  color: var(--color-primary-content);
  font-weight: 700;
  font-size: 0.78rem;
  line-height: 1.1;
  letter-spacing: -0.005em;
  white-space: nowrap;
  border: 1px solid color-mix(in oklch, var(--color-primary) 70%, white);
  box-shadow: 0 6px 20px color-mix(in oklch, var(--color-primary) 22%, transparent),
              0 1px 2px color-mix(in oklch, var(--color-primary) 30%, transparent);
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.12s ease;
}
.btn-approve:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 24px color-mix(in oklch, var(--color-primary) 30%, transparent),
              0 2px 4px color-mix(in oklch, var(--color-primary) 40%, transparent);
}
.btn-approve:active { transform: translateY(0); }
.btn-approve:disabled,
.btn-approve.is-disabled {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
.btn-approve svg.icon { color: var(--color-primary-content); width: 16px; height: 16px; opacity: 0.92; }
.btn-approve .action-bar__shortcut {
  background: color-mix(in oklch, var(--color-primary-content) 20%, transparent);
  color: var(--color-primary-content);
  border-color: color-mix(in oklch, var(--color-primary-content) 32%, transparent);
  box-shadow: none;
}

/* Setting rows — label + description on the left, a control on the right.
   Used by the Alerts section's per-signal enable toggles. */
.set-rows {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  overflow: hidden;
}
.set-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.6rem 0.85rem;
  background: var(--color-base-100);
}
.set-row + .set-row { border-top: 1px solid var(--color-base-300); }
.set-row__text { min-width: 0; flex: 1; }
.set-row__label {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--color-base-content);
  line-height: 1.3;
}
.set-row__desc {
  font-size: 0.75rem;
  line-height: 1.35;
  margin-top: 1px;
  color: color-mix(in oklch, var(--color-base-content) 52%, transparent);
}

/* Validate-page input — softer than DaisyUI .input-bordered, with the
   design's primary-tint hover + focus-ring. Used for the editable
   single-line product title field on review. */
.v-input {
  width: 100%;
  background: var(--color-base-200);
  border: 1px solid var(--color-base-300);
  border-radius: 0.4rem;
  padding: 0.5rem 0.7rem;
  font-size: 0.9rem;
  color: var(--color-base-content);
  font-family: inherit;
  line-height: 1.4;
  transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.v-input:hover { border-color: color-mix(in oklch, var(--color-base-300) 60%, var(--color-primary)); }
.v-input:focus {
  outline: none;
  background: var(--color-base-100);
  border-color: color-mix(in oklch, var(--color-primary) 60%, transparent);
  box-shadow: 0 0 0 2px color-mix(in oklch, var(--color-primary) 35%, transparent);
}

/* Inline alert — softer than DaisyUI .alert, tuned for the validate
   page's "Already handled" / "No enrichment" notices. Tinted background
   at 14% with a colored hairline; text reads at full saturation. */
.alert-inline {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.55rem 0.85rem;
  border-radius: 0.5rem;
  font-size: 0.8rem;
  line-height: 1.5;
  border: 1px solid;
  margin-bottom: 0.85rem;
}
.alert-inline svg.icon { width: 14px; height: 14px; margin-top: 2px; flex-shrink: 0; }
.alert-inline strong, .alert-inline b { font-weight: 600; }
.alert-inline p { margin: 0; }
.alert-inline__detail { color: color-mix(in oklch, currentColor 70%, var(--color-base-content)); font-size: 0.75rem; margin-top: 1px; }
.alert-inline--error {
  background: color-mix(in oklch, var(--color-error) 12%, transparent);
  border-color: color-mix(in oklch, var(--color-error) 32%, transparent);
  color: color-mix(in oklch, var(--color-error) 92%, var(--color-base-content));
}
.alert-inline--warning {
  background: color-mix(in oklch, var(--color-warning) 12%, transparent);
  border-color: color-mix(in oklch, var(--color-warning) 32%, transparent);
  color: color-mix(in oklch, var(--color-warning) 92%, var(--color-base-content));
}
.alert-inline--info {
  background: color-mix(in oklch, var(--color-info) 12%, transparent);
  border-color: color-mix(in oklch, var(--color-info) 32%, transparent);
  color: color-mix(in oklch, var(--color-info) 92%, var(--color-base-content));
}

/* Segmented control — Preview / HTML toggle on the description card.
   Mirrors the design's tweaks-seg pattern: pill of muted bg containing
   1-px-padded buttons; the active one floats up with surface bg + lift.
   Sized down for in-card use. */
.seg {
  display: inline-flex;
  background: var(--color-base-200);
  border: 1px solid var(--color-base-300);
  border-radius: 6px;
  padding: 2px;
  gap: 2px;
}
.seg__btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  padding: 3px 9px;
  font-size: 11.5px;
  font-weight: 500;
  border-radius: 4px;
  cursor: pointer;
  line-height: 1.2;
  transition: background 0.12s ease, color 0.12s ease;
}
.seg__btn:hover { color: var(--color-base-content); }
.seg__btn.is-active {
  background: var(--color-base-100);
  color: var(--color-base-content);
  box-shadow: 0 1px 2px rgba(var(--shadow-ink), var(--shadow-strength-md));
}

/* Field-origin compact rows for the right rail */
.field-origins {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.72rem;
}
.field-origins td {
  padding: 0.35rem 0.6rem;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 50%, transparent);
}
.field-origins tr:last-child td { border-bottom: 0; }
.field-origins td.label {
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
}
.field-origins td.val { text-align: right; }

/* ---- Validate v2: category tree rows (Akeneo-mockup-aligned) ---- */
.tree-row {
  display: grid;
  grid-template-columns: 16px 14px 1fr auto;
  align-items: center;
  gap: 6px;
  padding-top: 2px;
  padding-bottom: 2px;
  padding-right: 8px;
  font-size: 0.78rem;
  cursor: default;
}
.tree-row:hover { background: color-mix(in oklch, var(--color-base-content) 5%, transparent); }
.tree-row.is-selected {
  background: color-mix(in oklch, var(--color-success) 10%, transparent);
  box-shadow: inset 2px 0 0 var(--color-success);
}
.tree-row__chevron {
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: color-mix(in oklch, var(--color-base-content) 50%, transparent);
  background: transparent;
  border: 0;
  cursor: pointer;
  flex-shrink: 0;
}
.tree-row__chevron:hover { color: var(--color-base-content); }
.tree-row__type-icon {
  color: color-mix(in oklch, var(--color-base-content) 45%, transparent);
}
.tree-row.is-selected .tree-row__type-icon { color: color-mix(in oklch, var(--color-success) 75%, var(--color-base-content)); }
.tree-row__label {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  color: color-mix(in oklch, var(--color-base-content) 88%, transparent);
}
.tree-row.is-selected .tree-row__label {
  color: color-mix(in oklch, var(--color-success) 92%, var(--color-base-content));
  font-weight: 500;
}
.tree-row__suggest {
  font-size: 0.72rem;
  color: var(--color-primary);
  line-height: 1;
}
.tree-row__select {
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  padding: 0;
  margin-left: 6px;
  cursor: pointer;
  flex-shrink: 0;
  color: var(--color-success);
}
.tree-row__box {
  display: inline-block;
  width: 13px;
  height: 13px;
  border-radius: 3px;
  border: 1.5px solid color-mix(in oklch, var(--color-base-content) 30%, transparent);
  background: transparent;
  transition: border-color 0.12s ease;
}
.tree-row__select:hover .tree-row__box {
  border-color: color-mix(in oklch, var(--color-success) 70%, transparent);
}
.tree-row__check {
  width: 14px;
  height: 14px;
  stroke-width: 3;
}

/* ---- Validate v2: tertiary action buttons (subtle ghost, semantic tints) ---- */
.action-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  height: 30px;
  padding: 0 0.55rem;
  border-radius: 0.4rem;
  font-size: 0.74rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.action-btn svg.icon { width: 12px; height: 12px; opacity: 0.95; }

/* EH2 restraint: Approve is the only saturated CTA. Reject keeps danger
   identity but as an outline (transparent fill, danger border+text, danger
   tint only on hover); Insufficient + Re-enrich drop their semantic tint
   entirely and read as neutral ghosts so the bar resolves to one scheme. */
.action-btn--danger {
  background: transparent;
  border: 1px solid color-mix(in oklch, var(--color-error) 40%, transparent);
  color: var(--color-error);
}
.action-btn--danger:hover {
  background: color-mix(in oklch, var(--color-error) 12%, transparent);
  border-color: color-mix(in oklch, var(--color-error) 55%, transparent);
}
.action-btn--warning,
.action-btn--info {
  background: transparent;
  border: 1px solid color-mix(in oklch, var(--color-base-content) 14%, transparent);
  color: color-mix(in oklch, var(--color-base-content) 68%, transparent);
}
.action-btn--warning:hover,
.action-btn--info:hover {
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
  border-color: color-mix(in oklch, var(--color-base-content) 22%, transparent);
  color: var(--color-base-content);
}
.action-btn .action-bar__shortcut {
  background: color-mix(in oklch, currentColor 14%, transparent);
  border-color: color-mix(in oklch, currentColor 32%, transparent);
  color: currentColor;
  box-shadow: none;
  opacity: 0.95;
}

.action-nav-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  height: 26px;
  padding: 0 0.5rem;
  border-radius: 0.3rem;
  font-size: 0.72rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  background: transparent;
  border: 1px solid transparent;
  color: color-mix(in oklch, var(--color-base-content) 65%, transparent);
  transition: background 0.12s ease, color 0.12s ease;
}
.action-nav-btn:hover:not(:disabled) {
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
  color: var(--color-base-content);
}
.action-nav-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.action-nav-btn svg.icon { width: 12px; height: 12px; opacity: 0.8; }
.action-nav-btn .action-bar__shortcut {
  margin-left: 0.3rem;
  min-width: 15px;
  height: 15px;
  font-size: 0.58rem;
}

/* Empty queue celebration — soft primary radial behind a flat panel.
   Mirrors the design's empty-celebrate idiom; replaces the older
   DaisyUI .card chrome that competed with everything around it. */
.empty-celebrate {
  position: relative;
  padding: 4rem 3rem;
  text-align: center;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.875rem;
  overflow: hidden;
  max-width: 640px;
  margin: 4rem auto;
}
.empty-celebrate::before {
  content: '';
  position: absolute;
  left: 50%; top: -20%;
  width: 420px; height: 420px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in oklch, var(--color-primary) 22%, transparent) 0%,
    transparent 60%);
  pointer-events: none;
}
.empty-celebrate > * { position: relative; }
.empty-celebrate__title {
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.022em;
  margin: 0 0 0.5rem;
}
.empty-celebrate__sub {
  color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
  font-size: 0.875rem;
  margin: 0 auto 1.75rem;
  max-width: 28rem;
  line-height: 1.5;
}
.empty-celebrate__stats {
  display: flex;
  gap: 1.25rem;
  justify-content: center;
  margin-bottom: 1.75rem;
}
.empty-celebrate__stat-divider { width: 1px; background: var(--color-base-300); }
.empty-celebrate__stat-value {
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.022em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.empty-celebrate__stat-label {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  font-weight: 600;
  margin-top: 0.2rem;
}
.empty-celebrate__actions {
  display: flex;
  gap: 0.625rem;
  justify-content: center;
}

/* ---- Sidebar auto-collapse on scroll (validate page only — gated by JS) ---- */
/* The sidebar grid column transitions are owned by .app-shell above. We
   add a transition on the sidebar's children so wordmark/labels fade
   rather than pop when auto-collapse fires. The transform-based topbar
   hide pattern was tried and rejected — header context (breadcrumb,
   sync, run indicator) is too valuable to lose on scroll. */
.app-shell { transition: grid-template-columns 0.15s ease; }
.app-sidebar__wordmark,
.app-nav-link span,
.section-label { transition: opacity 0.12s ease; }

/* -----------------------------------------------------------------
   BRAND MAPPING (S6 — operator queue)
   Templates: src/app/templates/brand_mapping/{index,_row}.html
   Composition rules: existing tokens only — primary mint, semantic
   success/warning/info, --bc-* opacity stops, --color-base-100/200/300.
   No new palette entries.

   Layout shape:
     .bm-page                  page wrapper (lives inside .settings-layout)
     .bm-summary               pending-count chip in the header actions slot
     .bm-queue                 content-card wrapping the queue table
     .bm-table                 dense queue table (pair | suggestions | actions)
     .bm-row                   one <tr> per pair; outerHTML swap target
       .bm-pair                col 1 — supplier + brand_text + meta
       .bm-sugs                col 2 — ordered suggestion list (top 3 visible)
       .bm-actions             col 3 — split-button match / new brand /
                                use supplier / overflow
     .bm-row--confirmed        2s confirmation banner before row deletes itself
     .bm-empty                 empty-state panel (most common state today)
   ----------------------------------------------------------------- */

.bm-page { min-width: 0; }

/* ---- Summary chip in the page-header actions slot ---- */
.bm-summary {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.3rem 0.75rem;
  border-radius: 9999px;
  background: color-mix(in oklch, var(--color-primary) 12%, transparent);
  border: 1px solid color-mix(in oklch, var(--color-primary) 25%, transparent);
  color: var(--color-primary);
  font-size: 0.78rem;
  font-weight: 500;
  white-space: nowrap;
}
.bm-summary__count {
  font-size: 0.95rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.bm-summary__label {
  color: color-mix(in oklch, var(--color-primary) 75%, var(--color-base-content));
}

/* ---- Queue card ---- */
.bm-queue { overflow: hidden; }
.bm-queue__title {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 600;
  font-size: 0.88rem;
  color: var(--bc-90);
}
.bm-queue__title svg {
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
}
.bm-queue__hint {
  font-size: 0.72rem;
  color: var(--bc-50);
  font-style: italic;
  text-align: right;
  flex: 1 1 auto;
  min-width: 0;
}
.bm-queue__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 1rem;
  border-top: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 50%, var(--color-base-100));
  font-size: 0.78rem;
  color: var(--bc-60);
}
.bm-queue__page-info strong {
  color: var(--bc-90);
  font-variant-numeric: tabular-nums;
}
.bm-queue__pager { display: flex; align-items: center; gap: 0.5rem; }
.bm-queue__page-label {
  font-size: 0.75rem;
  color: var(--bc-60);
  padding: 0 0.5rem;
  font-variant-numeric: tabular-nums;
}

/* ---- Queue table ---- */
.bm-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}
.bm-table__th {
  text-align: left;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  color: var(--bc-50);
  padding: 0.625rem 1rem;
  border-bottom: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 50%, var(--color-base-100));
}
.bm-table__th--pair    { width: 22%; }
.bm-table__th--sugs    { width: auto; }
.bm-table__th--actions { width: 17rem; }

/* ---- Row (the load-bearing swap target) ---- */
.bm-row {
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-content) 7%, transparent);
  transition: background-color 0.12s ease;
}
.bm-row:last-child { border-bottom: 0; }
.bm-row > td {
  padding: 0.9rem 1rem;
  vertical-align: top;
}
.bm-row:hover { background: color-mix(in oklch, var(--color-primary) 4%, transparent); }

/* Confirmation banner replacing a row briefly. The row is removed
   from the DOM after .bm-row__confirm's fade completes; the inline
   <script> in the partial owns the timing. */
.bm-row--confirmed {
  background: color-mix(in oklch, var(--color-success) 10%, transparent);
  transition: opacity 0.5s ease, background 0.4s ease;
}
.bm-row--confirmed.is-fading { opacity: 0; }
.bm-row--confirmed:hover     { background: color-mix(in oklch, var(--color-success) 10%, transparent); }
.bm-row__confirm {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 1rem;
}
.bm-row__confirm-icon {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 9999px;
  display: inline-grid;
  place-items: center;
  background: color-mix(in oklch, var(--color-success) 20%, transparent);
  color: var(--color-success);
  flex-shrink: 0;
}
.bm-row__confirm-body {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}
.bm-row__confirm-body strong { font-weight: 600; color: var(--bc-90); font-size: 0.86rem; }
.bm-row__confirm-meta {
  font-size: 0.72rem;
  color: var(--bc-55, color-mix(in oklch, var(--color-base-content) 55%, transparent));
}

/* ---- Column 1 — pair identity ---- */
.bm-pair {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  min-width: 0;
}
.bm-pair__tag {
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--bc-50);
  margin-bottom: 0.15rem;
}
.bm-pair__supplier-name {
  display: block;
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--bc-75);
  font-family: var(--mono);
  word-break: break-word;
  line-height: 1.3;
}
.bm-pair__brand-text {
  display: block;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--bc-90);
  letter-spacing: -0.01em;
  line-height: 1.25;
  word-break: break-word;
}
.bm-pair__brand-text--empty {
  font-style: italic;
  color: var(--bc-40);
  font-weight: 400;
}
.bm-pair__meta {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  font-size: 0.7rem;
  color: var(--bc-50);
  margin-top: 0.1rem;
}
.bm-pair__count,
.bm-pair__when {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-variant-numeric: tabular-nums;
}
.bm-pair__count svg,
.bm-pair__when svg { opacity: 0.7; }

/* ---- Column 2 — suggestion stack ---- */
.bm-sugs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.bm-sug {
  display: grid;
  grid-template-columns: 1.25rem minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  grid-column-gap: 0.55rem;
  grid-row-gap: 0.1rem;
  align-items: center;
  padding: 0.35rem 0.55rem;
  border-radius: 0.4rem;
  border: 1px solid transparent;
  background: color-mix(in oklch, var(--color-base-200) 55%, var(--color-base-100));
  transition: background 0.12s ease, border-color 0.12s ease;
}
.bm-sug--top {
  background: color-mix(in oklch, var(--color-primary) 7%, var(--color-base-100));
  border-color: color-mix(in oklch, var(--color-primary) 22%, transparent);
}
.bm-sug__rank {
  grid-row: 1 / span 2;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 9999px;
  display: inline-grid;
  place-items: center;
  font-size: 0.65rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--bc-60);
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
}
.bm-sug--top .bm-sug__rank {
  color: var(--color-primary);
  background: color-mix(in oklch, var(--color-primary) 18%, transparent);
}
.bm-sug__brand {
  grid-row: 1;
  grid-column: 2;
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  min-width: 0;
}
.bm-sug__label {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--bc-90);
  letter-spacing: -0.005em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bm-sug__code {
  font-size: 0.66rem;
  font-weight: 500;
  color: var(--bc-50);
  padding: 0.05rem 0.35rem;
  border-radius: 0.25rem;
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
  font-family: var(--mono);
  flex-shrink: 0;
}
.bm-sug__score {
  grid-row: 1;
  grid-column: 3;
  font-size: 0.78rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: 0.05rem 0.45rem;
  border-radius: 0.3rem;
  letter-spacing: -0.01em;
}
.bm-sug--high .bm-sug__score {
  color: var(--color-success);
  background: color-mix(in oklch, var(--color-success) 14%, transparent);
}
.bm-sug--mid .bm-sug__score {
  color: var(--bc-75);
  background: color-mix(in oklch, var(--color-base-content) 10%, transparent);
}
.bm-sug--low .bm-sug__score {
  color: var(--color-warning);
  background: color-mix(in oklch, var(--color-warning) 16%, transparent);
}
.bm-sug__reason {
  grid-row: 2;
  grid-column: 2 / span 2;
  font-size: 0.68rem;
  color: var(--bc-50);
  font-family: var(--mono);
  letter-spacing: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bm-sugs__more {
  margin-top: 0.4rem;
  font-size: 0.68rem;
  color: var(--bc-50);
  font-style: italic;
  padding-left: 1.75rem;
}
.bm-sugs__none {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.625rem;
  font-size: 0.78rem;
  color: var(--bc-60);
  background: color-mix(in oklch, var(--color-base-200) 60%, var(--color-base-100));
  border: 1px dashed color-mix(in oklch, var(--color-base-content) 18%, transparent);
  border-radius: 0.4rem;
  font-style: italic;
}
.bm-sugs__none svg { color: var(--bc-50); flex-shrink: 0; }

/* ---- Column 3 — action cluster ---- */
.bm-actions {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.bm-actions .btn {
  white-space: nowrap;
  height: 1.85rem;
  min-height: 1.85rem;
  font-size: 0.78rem;
  padding-left: 0.55rem;
  padding-right: 0.55rem;
  gap: 0.3rem;
}
.bm-actions .btn-square { width: 1.85rem; padding: 0; }

/* Split button (Match + chevron) */
.bm-action-split {
  display: inline-flex;
  align-items: stretch;
  flex-shrink: 0;
}
.bm-action-split__main {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  padding-left: 0.6rem;
  padding-right: 0.55rem;
}
.bm-action-split__more {
  position: relative;
}
.bm-action-split__chevron {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  padding: 0 0.35rem;
  margin-left: 1px;
  border-left: 1px solid color-mix(in oklch, var(--color-primary-content) 25%, transparent);
  list-style: none;
  cursor: pointer;
  width: auto;
  min-width: 1.6rem;
}
.bm-action-split__more > summary { list-style: none; }
.bm-action-split__more > summary::-webkit-details-marker { display: none; }
.bm-action-split__more > summary::marker { content: ""; }
.bm-action-split__menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 18rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  box-shadow: 0 8px 24px rgba(var(--shadow-ink), var(--shadow-strength-md));
  padding: 0.3rem;
  z-index: 30;
  animation: htmxFadeIn 0.12s ease-out both;
}
.bm-action-split__menu-header {
  padding: 0.35rem 0.5rem 0.4rem;
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--bc-50);
}
.bm-action-split__menu-item {
  display: flex;
  align-items: center;
  width: 100%;
  gap: 0.625rem;
  padding: 0.45rem 0.55rem;
  background: transparent;
  border: 0;
  border-radius: 0.35rem;
  font-size: 0.8rem;
  cursor: pointer;
  text-align: left;
  color: var(--bc-90);
  transition: background 0.1s ease;
}
.bm-action-split__menu-item:hover {
  background: color-mix(in oklch, var(--color-primary) 10%, transparent);
}
.bm-action-split__menu-item.is-default {
  background: color-mix(in oklch, var(--color-primary) 7%, transparent);
}
.bm-action-split__menu-brand {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}
.bm-action-split__menu-label {
  font-weight: 600;
  color: var(--bc-90);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bm-action-split__menu-code {
  font-size: 0.67rem;
  color: var(--bc-50);
  font-family: var(--mono);
}
.bm-action-split__menu-score {
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--color-primary);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

/* "New brand" inline form (collapsible <details>) */
.bm-action-new { position: relative; }
.bm-action-new > summary { list-style: none; cursor: pointer; }
.bm-action-new > summary::-webkit-details-marker { display: none; }
.bm-action-new > summary::marker { content: ""; }
.bm-action-new__form {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  width: 20rem;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  padding: 0.875rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  box-shadow: 0 8px 24px rgba(var(--shadow-ink), var(--shadow-strength-md));
  z-index: 30;
  animation: htmxFadeIn 0.12s ease-out both;
}
.bm-action-new__field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.bm-action-new__field label {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--bc-75);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.bm-action-new__optional {
  font-size: 0.65rem;
  font-weight: 400;
  color: var(--bc-50);
  text-transform: lowercase;
  letter-spacing: 0;
  font-style: italic;
}
.bm-action-new__buttons {
  display: flex;
  justify-content: flex-end;
  gap: 0.4rem;
  margin-top: 0.15rem;
}

/* "Use supplier" — neutral button; nothing extra. */
.bm-action-supplier {
  border: 1px solid var(--color-base-300);
}

/* Overflow kebab */
.bm-action-overflow { position: relative; }
.bm-action-overflow > summary { list-style: none; cursor: pointer; }
.bm-action-overflow > summary::-webkit-details-marker { display: none; }
.bm-action-overflow > summary::marker { content: ""; }
.bm-action-overflow__trigger {
  border: 1px solid var(--color-base-300);
}
.bm-action-overflow__menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 10rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  box-shadow: 0 8px 24px rgba(var(--shadow-ink), var(--shadow-strength-md));
  padding: 0.3rem;
  z-index: 30;
  animation: htmxFadeIn 0.12s ease-out both;
}
.bm-action-overflow__item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.45rem 0.55rem;
  background: transparent;
  border: 0;
  border-radius: 0.35rem;
  font-size: 0.78rem;
  color: var(--bc-90);
  cursor: pointer;
  text-align: left;
  transition: background 0.1s ease;
}
.bm-action-overflow__item:hover {
  background: color-mix(in oklch, var(--color-base-content) 6%, transparent);
}

/* ---- Empty state ---- */
.bm-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding: 3rem 2rem;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0.75rem;
  text-align: center;
}
.bm-empty__icon {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 9999px;
  display: inline-grid;
  place-items: center;
  background: color-mix(in oklch, var(--color-success) 16%, transparent);
  color: var(--color-success);
  margin-bottom: 0.25rem;
}
.bm-empty__icon svg { width: 24px; height: 24px; stroke-width: 1.8; }
.bm-empty__title {
  margin: 0;
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--bc-90);
  letter-spacing: -0.015em;
}
.bm-empty__body {
  margin: 0;
  max-width: 32rem;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--bc-60);
}
.bm-empty__how {
  width: 100%;
  max-width: 28rem;
  margin-top: 0.5rem;
  padding: 0.875rem 1rem 0.9rem;
  background: color-mix(in oklch, var(--color-base-200) 60%, var(--color-base-100));
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  text-align: left;
}
.bm-empty__how-title {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--bc-50);
  margin-bottom: 0.5rem;
}
.bm-empty__how-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.bm-empty__how-list li {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.82rem;
  color: var(--bc-75);
}
.bm-empty__how-list strong { color: var(--bc-90); font-weight: 600; }
.bm-empty__how-icon {
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 0.3rem;
  display: inline-grid;
  place-items: center;
  background: color-mix(in oklch, var(--color-primary) 14%, transparent);
  color: var(--color-primary);
  flex-shrink: 0;
}
.bm-empty__meta {
  margin-top: 0.5rem;
  font-size: 0.73rem;
  color: var(--bc-50);
}
.bm-empty__meta a {
  color: var(--color-primary);
  text-decoration: none;
}
.bm-empty__meta a:hover { text-decoration: underline; }

/* ---- Narrow-laptop safety (1280-1380px) — drop the "New brand" trigger
   label so only its icon shows; "Supplier" becomes icon-only too.
   Actions still all reachable, suggestion column gets to breathe. ---- */
@media (max-width: 1380px) {
  .bm-table__th--actions { width: 14rem; }
  .bm-action-new__form { width: 18rem; }
  .bm-action-new__trigger span,
  .bm-action-supplier span { display: none; }
  .bm-action-new__trigger,
  .bm-action-supplier {
    width: 1.85rem;
    padding-left: 0;
    padding-right: 0;
    gap: 0;
  }
}

/* ============================================
   ENRICHMENT PROGRESS — slim sticky strip + right-side drawer.
   Replaces the prior three-zone scaffold (#enrichment-batch-header /
   #enrichment-active grid / #enrichment-completed pills); see
   templates/products/_enrichment_progress.html for the DOM.

   States the markup carries:
     .progress-strip                — running, drawer closed (default)
     .progress-strip + .is-open     — running, drawer open
     .progress-strip--done          — post-completion recap
     #enrichment-progress .is-minimized
                                    — strip hidden, .enrichment-restore-handle
                                      shown instead; topbar pill is the
                                      authoritative progress signal.
   ============================================ */

#enrichment-progress.is-minimized .progress-strip,
#enrichment-progress.is-minimized .enrichment-drawer,
#enrichment-progress.is-minimized .enrichment-drawer__backdrop {
  display: none;
}

.progress-strip {
  position: relative;
  height: 36px;
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 7px;
  display: flex;
  align-items: center;
  padding: 0 8px 0 12px;
  margin-bottom: 12px;
  overflow: hidden;
  font-size: 12.5px;
  gap: 14px;
}
.progress-strip__fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  background: linear-gradient(90deg,
    color-mix(in oklch, var(--color-info) 9%, transparent) 0%,
    color-mix(in oklch, var(--color-info) 5%, transparent) 100%);
  border-right: 1px solid color-mix(in oklch, var(--color-info) 28%, transparent);
  pointer-events: none;
  transition: width 0.35s ease-out;
}
.progress-strip > *:not(.progress-strip__fill) {
  position: relative;
  z-index: 1;
}

.progress-strip .ps-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: 600;
  color: var(--bc-90);
  white-space: nowrap;
}
.ps-spinner {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 1.7px solid color-mix(in oklch, var(--color-info) 35%, transparent);
  border-top-color: var(--color-info);
  animation: ps-spin 0.85s linear infinite;
  flex-shrink: 0;
}
.ps-spinner--sm { width: 10px; height: 10px; border-width: 1.5px; }
@keyframes ps-spin { to { transform: rotate(360deg); } }

.ps-count {
  font-variant-numeric: tabular-nums;
  color: var(--bc-90);
  font-weight: 600;
  white-space: nowrap;
}
.ps-count b { font-weight: 700; color: var(--color-base-content); }
.ps-count__sep { color: var(--bc-50); margin: 0 1px; }

.ps-meta {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  color: var(--bc-60);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ps-meta .text-success { color: var(--color-success); font-weight: 600; }
.ps-meta .text-info    { color: var(--color-info);    font-weight: 600; }
.ps-meta .text-warning { color: var(--color-warning); font-weight: 600; }
.ps-meta .text-error   { color: var(--color-error);   font-weight: 600; }

.ps-divider {
  width: 1px;
  height: 16px;
  background: var(--bc-12);
  flex-shrink: 0;
}

.ps-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 500;
  cursor: pointer;
  border: 0;
  white-space: nowrap;
}
.ps-chip .icon { width: 11px; height: 11px; }
.ps-chip--warn {
  color: var(--color-warning);
  background: color-mix(in oklch, var(--color-warning) 14%, transparent);
}
.ps-chip--warn:hover {
  background: color-mix(in oklch, var(--color-warning) 22%, transparent);
}
.ps-chip--hidden { display: none; }

/* When the failed count is 0 we hide the wrap entirely so the strip stays
   visually tidy; JS toggles .ps-failed-wrap--hidden. */
.ps-failed-wrap--hidden { display: none; }

.ps-spacer { flex: 1; min-width: 8px; }
.ps-actions {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.ps-btn {
  height: 24px;
  padding: 0 9px;
  font-size: 11.5px;
  font-weight: 500;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 5px;
  color: var(--bc-75);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  white-space: nowrap;
}
.ps-btn:hover {
  background: color-mix(in oklch, var(--color-base-content) 6%, transparent);
  color: var(--bc-90);
}
.ps-btn .icon { width: 11px; height: 11px; stroke-width: 1.8; }
.ps-btn--icon {
  width: 24px;
  padding: 0;
  justify-content: center;
}
.ps-btn--primary {
  background: color-mix(in oklch, var(--color-info) 14%, transparent);
  color: var(--color-info);
  font-weight: 600;
}
.ps-btn--primary:hover {
  background: color-mix(in oklch, var(--color-info) 22%, transparent);
}
.ps-btn--success {
  background: color-mix(in oklch, var(--color-success) 18%, transparent);
  color: var(--color-success);
  font-weight: 600;
}
.ps-btn--success:hover {
  background: color-mix(in oklch, var(--color-success) 26%, transparent);
}
.ps-btn--danger { color: var(--color-error); }
.ps-btn--danger:hover {
  background: color-mix(in oklch, var(--color-error) 12%, transparent);
}
.ps-btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Post-completion variant — green-tinted fill, status reads "Enriched". */
.progress-strip--done .progress-strip__fill {
  width: 100% !important;
  background: linear-gradient(90deg,
    color-mix(in oklch, var(--color-success) 12%, transparent) 0%,
    color-mix(in oklch, var(--color-success) 5%, transparent) 100%);
  border-right: 0;
}
.progress-strip--done .ps-status { color: var(--color-success); }

/* Cancelled / failed variants reuse the same chassis but recolor. */
.progress-strip--cancelled .progress-strip__fill {
  background: linear-gradient(90deg,
    color-mix(in oklch, var(--color-warning) 12%, transparent) 0%,
    color-mix(in oklch, var(--color-warning) 5%, transparent) 100%);
  border-right: 0;
}
.progress-strip--cancelled .ps-status { color: var(--color-warning); }
.progress-strip--errored .progress-strip__fill {
  background: linear-gradient(90deg,
    color-mix(in oklch, var(--color-error) 12%, transparent) 0%,
    color-mix(in oklch, var(--color-error) 5%, transparent) 100%);
  border-right: 0;
}
.progress-strip--errored .ps-status { color: var(--color-error); }

/* Restore handle — shown when the user has minimized the strip. Tiny
   chevron-down chip flush under the topbar so they can re-expand without
   trekking to the navbar pill. */
.enrichment-restore-handle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 9px 0 7px;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--bc-75);
  background: var(--color-base-100);
  border: 1px solid var(--color-base-300);
  border-radius: 0 0 6px 6px;
  border-top: 0;
  cursor: pointer;
  margin: -1px 0 10px 12px;
}
.enrichment-restore-handle:hover {
  color: var(--bc-90);
  background: color-mix(in oklch, var(--color-base-content) 4%, var(--color-base-100));
}
.enrichment-restore-handle svg {
  color: var(--color-info);
  flex-shrink: 0;
}
.enrichment-restore-handle.hidden { display: none; }

/* ---- Drawer (right side). Anchored to the viewport so it floats over
   the table without pushing layout, and the table dims slightly so the
   eye lands on the drawer first. ---- */
.enrichment-drawer {
  position: fixed;
  top: var(--topbar-h, 56px);
  right: 0;
  bottom: 0;
  width: 440px;
  max-width: 92vw;
  background: var(--color-base-100);
  border-left: 1px solid var(--color-base-300);
  box-shadow: -8px 0 28px rgba(var(--shadow-ink), var(--shadow-strength-lg));
  display: flex;
  flex-direction: column;
  z-index: 40;
  transform: translateX(100%);
  transition: transform 0.22s ease-out;
  font-size: 12.5px;
}
.enrichment-drawer.is-open {
  transform: translateX(0);
}
.enrichment-drawer.is-closed,
.enrichment-drawer.is-closed * {
  pointer-events: none;
}
.enrichment-drawer__backdrop {
  position: fixed;
  inset: 0;
  background: transparent;
  z-index: 39;
  pointer-events: none;
}
.enrichment-drawer.is-open + .enrichment-drawer__backdrop,
.enrichment-drawer.is-open ~ .enrichment-drawer__backdrop {
  pointer-events: auto;
}

/* Dim the table behind the drawer (subtly — power users hate jumpy
   contrast). Body gets the class via JS while .is-open is set. */
body.enrichment-drawer-open #product-table-wrapper {
  filter: brightness(0.94);
  transition: filter 0.2s ease-out;
}

.enrichment-drawer__header {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 60%, var(--color-base-100));
  flex-shrink: 0;
}
.enrichment-drawer__heading {
  flex: 1;
  min-width: 0;
}
.enrichment-drawer__title {
  font-size: 13px;
  font-weight: 600;
  color: var(--bc-90);
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.enrichment-drawer__sub {
  font-size: 11.5px;
  color: var(--bc-50);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.enrichment-drawer__close {
  margin-left: auto;
  width: 24px;
  height: 24px;
  border: 0;
  background: transparent;
  border-radius: 4px;
  color: var(--bc-60);
  cursor: pointer;
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
.enrichment-drawer__close:hover { background: var(--bc-12); color: var(--bc-90); }
.enrichment-drawer__close .icon { width: 12px; height: 12px; stroke-width: 2; }

.enrichment-drawer__tabs {
  display: flex;
  gap: 2px;
  padding: 0 14px;
  border-bottom: 1px solid var(--color-base-300);
  background: var(--color-base-100);
  flex-shrink: 0;
}
.enrichment-drawer__tab {
  padding: 9px 10px;
  font-size: 12px;
  color: var(--bc-60);
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  cursor: pointer;
  font-weight: 500;
  background: transparent;
}
.enrichment-drawer__tab:hover { color: var(--bc-90); }
.enrichment-drawer__tab.is-active {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
  font-weight: 600;
}
.enrichment-drawer__tab-count {
  font-size: 10.5px;
  padding: 1px 6px;
  border-radius: 999px;
  background: color-mix(in oklch, var(--color-base-content) 10%, transparent);
  color: var(--bc-75);
  margin-left: 4px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.enrichment-drawer__tab.is-active .enrichment-drawer__tab-count {
  background: color-mix(in oklch, var(--color-primary) 18%, transparent);
  color: var(--color-primary);
}

.enrichment-drawer__list {
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}
.enrichment-drawer__section {
  display: flex;
  flex-direction: column;
}
.enrichment-drawer__empty {
  padding: 24px 14px;
  text-align: center;
  font-size: 12px;
  color: var(--bc-50);
}

.drawer-row {
  display: grid;
  grid-template-columns: 18px 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 9px 14px;
  border-bottom: 1px solid color-mix(in oklch, var(--color-base-300) 60%, transparent);
  font-size: 12.5px;
}
.drawer-row:hover { background: color-mix(in oklch, var(--color-primary) 6%, transparent); }
.drawer-row--active { background: color-mix(in oklch, var(--color-info) 8%, transparent); }
.drawer-row--hidden { display: none; }

.drawer-row__icon {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  display: grid;
  place-items: center;
}
.drawer-row__icon svg {
  width: 9px;
  height: 9px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.drawer-row__icon--ok {
  background: color-mix(in oklch, var(--color-success) 22%, transparent);
  color: var(--color-success);
}
.drawer-row__icon--warn {
  background: color-mix(in oklch, var(--color-warning) 22%, transparent);
  color: var(--color-warning);
}
.drawer-row__icon--fail {
  background: color-mix(in oklch, var(--color-error) 22%, transparent);
  color: var(--color-error);
}
.drawer-row__icon--run {
  background: color-mix(in oklch, var(--color-info) 22%, transparent);
  color: var(--color-info);
}
.drawer-row__icon--idle {
  background: color-mix(in oklch, var(--color-base-content) 10%, transparent);
  color: var(--bc-50);
}
.drawer-row__icon--run .ps-spinner {
  border-color: color-mix(in oklch, var(--color-info) 40%, transparent);
  border-top-color: var(--color-info);
  width: 9px;
  height: 9px;
  border-width: 1.5px;
}

.drawer-row__main { min-width: 0; }
.drawer-row__name {
  font-size: 12.5px;
  color: var(--bc-90);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}
.drawer-row__sub {
  font-size: 11px;
  color: var(--bc-50);
  margin-top: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.drawer-row__sub b { color: var(--bc-75); font-weight: 500; }
.drawer-row__time {
  font-size: 11px;
  color: var(--bc-50);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
  flex-shrink: 0;
}
.drawer-row__review {
  margin-left: 8px;
  font-size: 11px;
  color: var(--color-info);
  text-decoration: none;
  border: 1px solid color-mix(in oklch, var(--color-info) 30%, transparent);
  padding: 1px 8px;
  border-radius: 4px;
  background: color-mix(in oklch, var(--color-info) 10%, transparent);
  white-space: nowrap;
}
.drawer-row__review:hover {
  background: color-mix(in oklch, var(--color-info) 18%, transparent);
}
.drawer-row__re-enrich {
  margin-left: 8px;
  font-size: 11px;
  color: var(--bc-75);
  background: transparent;
  border: 1px solid var(--color-base-300);
  padding: 1px 8px;
  border-radius: 4px;
  cursor: pointer;
}
.drawer-row__re-enrich:hover { background: var(--bc-12); color: var(--bc-90); }
.drawer-row__right {
  display: inline-flex;
  align-items: center;
  gap: 0;
}

.enrichment-drawer__footer {
  padding: 9px 14px;
  border-top: 1px solid var(--color-base-300);
  background: color-mix(in oklch, var(--color-base-200) 60%, var(--color-base-100));
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 11.5px;
  color: var(--bc-50);
  flex-shrink: 0;
}

/* Narrow-laptop safety — drop secondary counters and let the strip
   breathe at 1280–1380px. Per-product detail still reachable via Details. */
@media (max-width: 1380px) {
  .progress-strip { gap: 10px; }
  .progress-strip .ps-divider:nth-of-type(2) { display: none; }
  .progress-strip .ps-meta:nth-of-type(2) { display: none; }
}

/* ============================================================
   Validation overview (R2) — two-zone live progress + ready queue.
   Tokens only (--surface / --border / --brand / --color-*); reads in
   both [data-theme="dim"] and [data-theme="light"].
   ============================================================ */
.ov-zone { margin-bottom: 1.5rem; }
.ov-zone__head {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-bottom: 0.75rem;
  color: var(--text-muted);
}
.ov-zone__head .icon { color: var(--text-muted); }
.ov-zone__title {
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
}
.ov-zone__count {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text-faint);
  background: color-mix(in oklch, var(--color-base-content) 8%, transparent);
  border-radius: 999px;
  padding: 0.08rem 0.5rem;
}
.ov-zone__meta {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text-faint);
}
.ov-zone__pulse {
  width: 8px; height: 8px; border-radius: 999px;
  background: var(--brand);
  box-shadow: 0 0 0 0 color-mix(in oklch, var(--brand) 60%, transparent);
  animation: ov-pulse 1.6s ease-out infinite;
}
.ov-zone--done .ov-zone__pulse {
  background: var(--color-success);
  animation: none;
}
@keyframes ov-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in oklch, var(--brand) 55%, transparent); }
  70%  { box-shadow: 0 0 0 7px color-mix(in oklch, var(--brand) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--brand) 0%, transparent); }
}

.ov-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 0.75rem;
}

/* Per-card rule (no JS hook) so it also fires on cards
   validation-overview.js prepends live on enrichment-complete. No
   stagger — over the dense grid + live prepends it reads as jitter. */
@keyframes cardIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.ov-card {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 0.85rem;
  background: var(--surface);
  border: 1px solid var(--eh-line);
  border-radius: 0.625rem;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s, background 0.15s, transform 0.15s;
  min-width: 0;
  animation: cardIn var(--motion-card) var(--ease-out) both;
}
.ov-card--ready { cursor: pointer; }
.ov-card--ready:hover {
  border-color: color-mix(in oklch, var(--brand) 45%, var(--eh-line));
  background: color-mix(in oklch, var(--brand) 5%, var(--surface));
}
.ov-card--enriching { cursor: pointer; }
.ov-card--enriching:hover { border-color: color-mix(in oklch, var(--brand) 40%, var(--eh-line)); }
.ov-card--warn { border-left: 3px solid var(--color-warning); }

.ov-card__thumb {
  position: relative;
  width: 48px; height: 48px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 0.5rem;
  background: var(--surface-2);
  color: var(--text-faint);
  overflow: hidden;
}
.ov-card__thumb img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}

.ov-card__body { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 0.3rem; }
.ov-card__name {
  font-size: 0.84rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--color-base-content);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ov-card__meta { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; }
.ov-card__brand { font-size: 0.72rem; color: var(--text-muted); }
.ov-card__sku { font-family: var(--mono); font-size: 0.68rem; color: var(--text-faint); }
.ov-card__phase {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--brand);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ov-card__go { flex: none; color: var(--text-ghost); transition: color 0.15s, transform 0.15s; }
.ov-card--ready:hover .ov-card__go { color: var(--brand); transform: translateX(2px); }

.ov-dot {
  width: 6px; height: 6px; border-radius: 999px;
  background: currentColor;
  animation: ov-pulse 1.6s ease-out infinite;
}

/* progress ring */
.ov-ring { position: relative; width: 40px; height: 40px; flex: none; }
.ov-ring svg { transform: rotate(-90deg); }
.ov-ring__bg { stroke: var(--surface-3); }
.ov-ring__fg { stroke: var(--brand); transition: stroke-dashoffset 0.4s ease; }
.ov-ring__pct {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 600;
  color: var(--text-muted);
}

/* empty states */
.ov-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 2.25rem 1rem;
  text-align: center;
  color: var(--text-muted);
}
.ov-empty .icon { color: var(--color-success); }
.ov-empty__busy { display: none; margin: 0; font-size: 0.84rem; }
.ov-empty__clear { margin: 0; font-size: 0.84rem; }
.ov-empty[data-enriching="1"] .ov-empty__busy { display: block; }
.ov-empty[data-enriching="1"] .ov-empty__clear { display: none; }
.ov-empty[data-enriching="1"] .icon { color: var(--brand); }

.ov-waiting {
  display: flex; align-items: center; gap: 0.6rem;
  margin-top: 0.75rem; padding: 0.6rem 0.85rem;
  background: var(--surface-2);
  border: 1px dashed var(--eh-line);
  border-radius: 0.625rem;
  font-size: 0.78rem; color: var(--text-muted);
}
.ov-waiting__dot { width: 6px; height: 6px; border-radius: 999px; background: var(--text-faint); flex: none; }
.ov-waiting__count b { color: var(--color-base-content); font-weight: 600; }
.ov-waiting__next { font-size: 0.72rem; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.ov-waiting__next:not(:empty)::before { content: "next up: "; color: var(--text-ghost); }

/* triage zone — products that didn't enrich */
.ov-triage__selectall {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin-left: auto; font-size: 0.72rem; color: var(--text-faint);
  cursor: pointer;
}
.ov-triage {
  display: flex; flex-direction: column;
  border: 1px solid var(--eh-line); border-radius: 0.75rem;
  overflow: hidden; background: var(--color-base-100);
}
.ov-triage__row {
  display: flex; align-items: center; gap: 0.85rem;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--eh-line);
}
.ov-triage__row:last-child { border-bottom: none; }
.ov-triage__row:hover { background: var(--surface-2); }
.ov-triage__check { flex: none; display: inline-flex; }
.ov-triage__main { flex: 0 0 32%; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.ov-triage__name {
  font-size: 0.84rem; font-weight: 500; color: var(--color-base-content);
  text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-triage__name:hover { color: var(--brand); }
.ov-triage__sub { display: flex; align-items: center; gap: 0.5rem; }
.ov-triage__sku { font-family: var(--mono); font-size: 0.68rem; color: var(--text-faint); }
.ov-triage__brand { font-size: 0.7rem; color: var(--text-muted); }
.ov-triage__row .status-pill { flex: none; }
.ov-triage__reason {
  flex: 1 1 auto; min-width: 0;
  font-size: 0.74rem; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* trace drawer */
.ov-drawer { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
.ov-drawer[data-state="closed"] { visibility: hidden; }
.ov-drawer[data-state="open"] { visibility: visible; pointer-events: auto; }
.ov-drawer__overlay {
  position: absolute; inset: 0;
  background: color-mix(in oklch, #000 50%, transparent);
  opacity: 0;
  transition: opacity 0.2s;
}
.ov-drawer[data-state="open"] .ov-drawer__overlay { opacity: 1; }
.ov-drawer__panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(560px, 94vw);
  background: var(--surface);
  border-left: 1px solid var(--eh-line);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform var(--motion-card) ease;
  box-shadow: -16px 0 40px color-mix(in oklch, #000 22%, transparent);
}
.ov-drawer[data-state="open"] .ov-drawer__panel { transform: translateX(0); }
.ov-drawer__head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--eh-line);
}
.ov-drawer__thumb {
  width: 40px; height: 40px; flex: none;
  display: grid; place-items: center;
  border-radius: 0.5rem;
  background: var(--surface-2);
  color: var(--text-faint);
}
.ov-drawer__id { min-width: 0; flex: 1; }
.ov-drawer__name {
  font-size: 0.9rem; font-weight: 600; margin: 0;
  color: var(--color-base-content);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-drawer__sub { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.2rem; }
.ov-drawer__sub code { font-family: var(--mono); font-size: 0.7rem; color: var(--text-faint); }
.ov-drawer__close {
  flex: none;
  background: none; border: 1px solid var(--eh-line);
  border-radius: 0.4rem;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  color: var(--text-muted);
  cursor: pointer;
}
.ov-drawer__close:hover { background: var(--surface-2); color: var(--color-base-content); }
.ov-drawer__body { padding: 1rem; overflow-y: auto; display: flex; flex-direction: column; gap: 1.25rem; }
.ov-trace__label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  margin-bottom: 0.55rem;
}
.ov-checklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.ov-check {
  display: flex; align-items: center; gap: 0.55rem;
  font-size: 0.8rem;
  padding: 0.4rem 0.55rem;
  border-radius: 0.4rem;
  color: var(--text-faint);
}
.ov-check .icon { flex: none; }
.ov-check--done { color: var(--color-base-content); }
.ov-check--done .icon { color: var(--color-success); }
.ov-check--active {
  color: var(--brand);
  background: color-mix(in oklch, var(--brand) 8%, transparent);
}
.ov-check--active .icon { color: var(--brand); animation: ov-spin 1s linear infinite; }
@keyframes ov-spin { to { transform: rotate(360deg); } }
.ov-log {
  max-height: 280px;
  overflow-y: auto;
  border: 1px solid var(--eh-line);
  border-radius: 0.5rem;
  background: var(--surface-2);
  padding: 0.4rem;
}
.ov-log__line {
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--text-muted);
  padding: 0.15rem 0.35rem;
  border-bottom: 1px solid var(--border-soft);
}
.ov-log__line:last-child { border-bottom: 0; }
.ov-log__line--muted { color: var(--text-faint); }

/* -----------------------------------------------------------------
   BRANDS COCKPIT (Track 1 — settings/_sources_table.html, brand kind)
   Two-pane console: a scrollable brand list (label + count + reject-rate
   pill) on the left, the reused edit form in a detail card on the right.
   Plus the observed-alias learn-queue banner that links to /brand-mapping.
   Tokens only: --color-primary / semantic success/warning/error /
   --color-base-* / --bc-* opacity stops. Laptop-width (1280px+); no
   responsive collapse needed below that floor.
   ----------------------------------------------------------------- */

/* ---- Reject-rate pill (shared: list rows + detail header) ---- */
.reject-pill {
  flex: none;
  display: inline-flex;
  align-items: center;
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1;
  padding: 0.22rem 0.5rem;
  border-radius: 9999px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  white-space: nowrap;
}
.reject-pill--low {
  color: var(--color-success);
  background: color-mix(in oklch, var(--color-success) 14%, transparent);
  border-color: color-mix(in oklch, var(--color-success) 30%, transparent);
}
.reject-pill--mid {
  color: var(--color-warning);
  background: color-mix(in oklch, var(--color-warning) 16%, transparent);
  border-color: color-mix(in oklch, var(--color-warning) 32%, transparent);
}
.reject-pill--high {
  color: var(--color-error);
  background: color-mix(in oklch, var(--color-error) 16%, transparent);
  border-color: color-mix(in oklch, var(--color-error) 34%, transparent);
}

/* ---- Learn-queue banner ---- */
.learn-queue-banner {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.9rem;
  margin-bottom: 0.75rem;
  border-radius: 0.55rem;
  font-size: 0.82rem;
  text-decoration: none;
  transition: background-color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
}
.learn-queue-banner--active {
  background: color-mix(in oklch, var(--color-primary) 10%, var(--color-base-100));
  border: 1px solid color-mix(in oklch, var(--color-primary) 28%, transparent);
  color: var(--color-base-content);
  cursor: pointer;
}
.learn-queue-banner--active:hover {
  background: color-mix(in oklch, var(--color-primary) 16%, var(--color-base-100));
  border-color: color-mix(in oklch, var(--color-primary) 42%, transparent);
}
.learn-queue-banner__icon { color: var(--color-primary); display: inline-flex; }
.learn-queue-banner__count {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--color-primary);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.learn-queue-banner__text { color: var(--bc-75); }
.learn-queue-banner__cta {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-weight: 600;
  font-size: 0.78rem;
  color: var(--color-primary);
}
.learn-queue-banner--clear {
  background: color-mix(in oklch, var(--color-success) 8%, var(--color-base-100));
  border: 1px solid color-mix(in oklch, var(--color-success) 22%, transparent);
  color: color-mix(in oklch, var(--color-success) 85%, var(--color-base-content));
}
.learn-queue-banner--clear svg { color: var(--color-success); flex: none; }

/* ---- Two-pane grid ---- */
.brand-cockpit {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
}

/* ---- Left list ---- */
.brand-cockpit__list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  max-height: 70vh;
  overflow-y: auto;
  padding-right: 0.15rem;
}
.brand-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  text-align: left;
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--color-base-300);
  border-radius: 0.5rem;
  background: var(--color-base-100);
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--ease-out),
              background-color var(--motion-fast) var(--ease-out);
}
.brand-row:hover {
  border-color: color-mix(in oklch, var(--color-base-content) 22%, transparent);
}
.brand-row.is-active {
  border-color: color-mix(in oklch, var(--color-primary) 55%, transparent);
  background: color-mix(in oklch, var(--color-primary) 8%, var(--color-base-100));
}
.brand-row__main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 0.1rem; }
.brand-row__name {
  font-size: 0.84rem;
  font-weight: 500;
  color: var(--bc-90);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.brand-row__sub {
  font-size: 0.7rem;
  color: var(--bc-50);
  font-variant-numeric: tabular-nums;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.brand-row__flag {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--bc-40);
  border: 1px solid var(--color-base-300);
  border-radius: 9999px;
  padding: 0.05rem 0.35rem;
}
.brand-cockpit__empty {
  padding: 1.5rem 0.75rem;
  text-align: center;
  font-size: 0.8rem;
  color: var(--bc-50);
}

/* ---- Right detail pane ---- */
.brand-cockpit__detail { min-width: 0; }
.brand-cockpit__detail-loading,
.brand-cockpit__placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 12rem;
  color: var(--bc-50);
  font-size: 0.82rem;
}
.brand-panel { display: flex; flex-direction: column; }
.brand-panel__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 1rem;
  border-bottom: 1px solid var(--color-base-300);
}
.brand-panel__title { min-width: 0; display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.brand-panel__name { font-size: 1.05rem; font-weight: 600; color: var(--bc-90); }
.brand-panel__code {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--bc-40);
}
.brand-panel__meta { display: flex; align-items: center; gap: 0.6rem; flex: none; }
.brand-panel__count { font-size: 0.78rem; color: var(--bc-60); font-variant-numeric: tabular-nums; }
.brand-panel__note {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin: 0.9rem 1rem 0;
  padding: 0.6rem 0.8rem;
  border-radius: 0.5rem;
  font-size: 0.78rem;
  line-height: 1.4;
}
.brand-panel__carried {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--color-base-300);
}
.brand-panel__carried-label {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--bc-50);
  flex: none;
}
.brand-panel__note svg { flex: none; margin-top: 0.05rem; }
.brand-panel__note--warn {
  color: color-mix(in oklch, var(--color-warning) 88%, var(--color-base-content));
  background: color-mix(in oklch, var(--color-warning) 12%, var(--color-base-100));
  border: 1px solid color-mix(in oklch, var(--color-warning) 28%, transparent);
}

/* ---- Subnav badge (Brand-mapping pending count) ---- */
.subnav__badge {
  margin-left: auto;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1;
  padding: 0.18rem 0.4rem;
  border-radius: 9999px;
  color: var(--color-primary);
  background: color-mix(in oklch, var(--color-primary) 16%, transparent);
  font-variant-numeric: tabular-nums;
}
