/* ==========================================================================
   Design tokens. Everything below this block reads colors/radii/shadows from
   these custom properties instead of hardcoding hex values, so the whole app
   can be retheme'd from one place - which is exactly what the light/dark
   toggle (Connections tab, wired in renderer.js) does by swapping which of
   these two blocks is active via the <html data-theme="..."> attribute.
   theme-init.js sets that attribute before first paint (see index.html's
   <head>) so there's no flash of the wrong theme on launch.

   "Data" tokens (--bg-data*, --text-data*, --border-data*) are a separate
   axis from the chrome tokens above them - they theme the results grid,
   Object Browser/Comparison cards, Library, and AI chat, which used to be
   unconditionally white regardless of app theme. In dark mode they're now a
   genuinely dark surface (not just "the same dark as the chrome" - a
   slightly distinct shade for depth), and in light mode they're close to
   the app's original white-panel look.
   ========================================================================== */
:root {
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-pill: 999px;

  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.08);
  --shadow-dropdown: 0 4px 14px rgba(0, 0, 0, 0.35);
  --shadow-modal: 0 8px 24px rgba(0, 0, 0, 0.5);

  /* ---- Dark theme (default) ---- */
  --bg-app: #1e1e1e;
  --bg-panel: #262626;
  --bg-panel-2: #212121;
  --bg-input: #1b1b1b;
  --bg-btn: #3a3a3a;
  --bg-btn-hover: #474747;
  --bg-hover: #2a2a2a;
  --bg-elevated: #2a2a2a;
  --bg-elevated-2: #2d2d2d;
  --bg-code: #111111;

  --border: #3a3a3a;
  --border-soft: #333333;
  --border-strong: #4a4a4a;

  --text-primary: #eeeeee;
  --text-secondary: #cccccc;
  --text-muted: #999999;
  --text-faint: #777777;
  --text-on-accent: #ffffff;

  --accent: #3572e8;
  --accent-hover: #2f63cc;
  --accent-2: #2b6fd9;
  --accent-2-hover: #2760bd;
  --link: #6ab0f3;
  --link-hover: #8cc4ff;

  --success: #2b8a3e;
  --success-hover: #257234;
  --success-bg: #16331d;
  --success-text: #6bd97a;

  --danger: #a03030;
  --danger-hover: #872828;
  --danger-bg: #3a1c1c;
  --danger-text: #ff8080;

  --warning-text: #e0a95d;
  --code-text: #9fe89f;

  --bg-data: #191919;
  --bg-data-alt: #212121;
  --bg-data-hover: #22314a;
  --border-data: #3d3d3d;
  --border-data-strong: #4c4c4c;
  --text-data: #e6e6e6;
  --text-data-muted: #9a9a9a;
  --text-data-faint: #707070;

  --callout-warn-bg: rgba(224, 169, 93, 0.12);
  --callout-warn-border: rgba(224, 169, 93, 0.45);
  --callout-warn-text: #e0c079;

  --diff-added-bg: rgba(47, 158, 68, 0.18);
  --diff-added-text: #7fd996;
  --diff-removed-bg: rgba(217, 60, 60, 0.18);
  --diff-removed-text: #ff9c9c;
  --diff-mismatch-bg: rgba(224, 169, 93, 0.16);
  --diff-mismatch-text: #e0c079;
  --diff-row-onlyA-bg: rgba(217, 60, 60, 0.14);
  --diff-row-onlyB-bg: rgba(53, 114, 232, 0.14);
  --diff-row-changed-bg: rgba(224, 169, 93, 0.14);
  --diff-cell-mismatch-bg: rgba(224, 169, 93, 0.3);

  --mark-bg: #7a5c00;
  --mark-text: #ffe27a;

  --cm-active-line: rgba(255, 255, 255, 0.045);

  /* ---- eText Builder redesign: selection/focus system ----
     Exact RGB channels of --accent in each theme, diluted - "the same
     blue, translucent," not a new hue. Used ONLY for a hover/chosen wash
     on mutually-exclusive choice controls (radio tiles) and badges -
     never for "this task is done" (that stays --success family). */
  --accent-soft: rgba(53, 114, 232, 0.10);
  --accent-tint: rgba(53, 114, 232, 0.18);
  --focus-ring: 0 0 0 3px rgba(53, 114, 232, 0.45);
  /* Momentary hover-lift shadow for the one surface that becomes a real
     click target without permanently floating like --shadow-card. */
  --shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.14);
  /* 4px spacing scale - theme-agnostic, no light-mode override needed. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
}

:root[data-theme="light"] {
  --bg-app: #f3f4f6;
  --bg-panel: #ffffff;
  --bg-panel-2: #eceef2;
  --bg-input: #ffffff;
  --bg-btn: #eef0f3;
  --bg-btn-hover: #e2e5ea;
  --bg-hover: #f0f2f5;
  --bg-elevated: #ffffff;
  --bg-elevated-2: #ffffff;
  --bg-code: #1e1e1e;

  --border: #d7dbe1;
  --border-soft: #e3e6eb;
  --border-strong: #c2c7d0;

  --text-primary: #1b2028;
  --text-secondary: #3c4451;
  --text-muted: #6b7280;
  --text-faint: #8a94a3;
  --text-on-accent: #ffffff;

  --accent: #2f63cc;
  --accent-hover: #2554b0;
  --accent-2: #2b6fd9;
  --accent-2-hover: #2158b8;
  --link: #2f63cc;
  --link-hover: #1f4fa8;

  --success: #237233;
  --success-hover: #1c5e29;
  --success-bg: #e4f5e7;
  --success-text: #1c7430;

  --danger: #b53434;
  --danger-hover: #9c2b2b;
  --danger-bg: #fbeae9;
  --danger-text: #a92a2a;

  --warning-text: #935416;
  --code-text: #9fe89f;

  --bg-data: #ffffff;
  --bg-data-alt: #f6f7f9;
  --bg-data-hover: #eaf1ff;
  --border-data: #dfe3e8;
  --border-data-strong: #c7cdd6;
  --text-data: #1b2028;
  --text-data-muted: #5c6472;
  --text-data-faint: #8a94a3;

  --callout-warn-bg: #fff8e1;
  --callout-warn-border: #e8c468;
  --callout-warn-text: #6b5300;

  --diff-added-bg: #e3f9e5;
  --diff-added-text: #1a7f37;
  --diff-removed-bg: #ffeef0;
  --diff-removed-text: #b31d28;
  --diff-mismatch-bg: #fff6e0;
  --diff-mismatch-text: #8a5a00;
  --diff-row-onlyA-bg: #fff0f0;
  --diff-row-onlyB-bg: #eef3ff;
  --diff-row-changed-bg: #fffaeb;
  --diff-cell-mismatch-bg: #ffe9a8;

  --mark-bg: #ffe27a;
  --mark-text: #000000;

  --cm-active-line: rgba(0, 0, 0, 0.035);

  --accent-soft: rgba(47, 99, 204, 0.06);
  --accent-tint: rgba(47, 99, 204, 0.10);
  --focus-ring: 0 0 0 3px rgba(47, 99, 204, 0.35);
}

* { box-sizing: border-box; }
html, body {
  margin: 0;
  height: 100%;
  font-family: -apple-system, Segoe UI, Arial, sans-serif;
  background: var(--bg-app);
  color: var(--text-secondary);
  overflow-x: hidden;
}

/* App-wide scrollbar styling (Chromium/Electron-only, harmless no-op
   elsewhere) - a neutral semi-transparent gray reads fine against both
   theme's chrome and data panels, so one rule covers everything instead of
   needing a light/dark variant per panel. */
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(130, 130, 130, 0.45); border-radius: 6px; border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: rgba(150, 150, 150, 0.65); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

.screen { height: 100%; display: flex; flex-direction: column; }
.hidden { display: none !important; }

.icon { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; vertical-align: -2px; }
.icon-sm { width: 12px; height: 12px; }
.icon-lg { width: 18px; height: 18px; }
button .icon { margin-right: 5px; }
button.icon-only .icon { margin-right: 0; }

.checkbox-row { display: flex; align-items: center; gap: 8px; font-size: 12px; margin-top: 14px; }
.checkbox-row input { width: auto; margin: 0; }

.status { margin-top: 10px; font-size: 12px; white-space: pre-wrap; }
.status.error { color: var(--danger-text); }
.status.ok { color: var(--success-text); }
.status.warn { color: var(--callout-warn-text); }

.tabs { display: flex; flex-wrap: wrap; align-items: center; background: var(--bg-panel-2); border-bottom: 1px solid var(--border); }
.tab-btn {
  display: inline-flex;
  align-items: center;
  padding: 9px 16px;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 13px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
  white-space: nowrap;
}
.tab-btn:hover { color: var(--text-secondary); }
.tab-btn.active { color: var(--text-primary); border-bottom-color: var(--accent); }

.shortcuts-btn {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  align-self: center;
  padding: 6px 14px;
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: 12px;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.shortcuts-btn:hover { color: var(--text-primary); border-color: var(--border-strong); }

.shortcuts-box { max-width: 480px; }
.shortcuts-title { font-size: 14px; font-weight: 700; margin-bottom: 12px; }
.shortcuts-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.shortcuts-row { display: flex; align-items: baseline; gap: 12px; font-size: 12.5px; }
.shortcuts-keys {
  flex: 0 0 auto;
  min-width: 120px;
  font-family: 'Courier New', monospace;
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 2px 8px;
  color: var(--code-text);
  text-align: center;
}
.shortcuts-desc { color: var(--text-secondary); }
.shortcuts-section-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; color: var(--text-faint); margin: 10px 0 2px; }
.shortcuts-section-label:first-child { margin-top: 0; }

.tab-panel { display: none; flex: 1; flex-direction: column; min-height: 0; }
.tab-panel.active { display: flex; }

/* Connections tab */
#tab-connections { overflow: auto; }
.connections-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px 0;
}
.connections-header-title { font-size: 16px; font-weight: 700; color: var(--text-primary); }

/* Theme toggle - same visual language as the AI tab's on/off switch
   (.ai-toggle-*), kept as its own separately-named component rather than
   shared, so retheming this doesn't risk touching the AI toggle's working
   behavior. */
.theme-toggle-row { display: flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; font-size: 12.5px; color: var(--text-secondary); }
.theme-toggle-switch { position: relative; display: inline-block; width: 36px; height: 20px; flex: 0 0 auto; }
.theme-toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.theme-toggle-track { position: absolute; inset: 0; background: var(--border-strong); border-radius: 20px; transition: background 0.15s ease; }
.theme-toggle-track::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--bg-panel);
  transition: transform 0.15s ease;
}
.theme-toggle-switch input:checked + .theme-toggle-track { background: var(--accent); }
.theme-toggle-switch input:checked + .theme-toggle-track::before { transform: translateX(16px); }
.theme-toggle-switch input:focus-visible + .theme-toggle-track { outline: 2px solid var(--accent); outline-offset: 2px; }

.connections-layout {
  display: flex;
  gap: 20px;
  padding: 20px;
  align-items: flex-start;
}
.connections-form-card,
.connections-list-card {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 20px 24px;
}
.connections-form-card { width: 420px; flex: 0 0 auto; }
.connections-list-card { flex: 1; min-width: 320px; }
.connections-form-card h2,
.connections-list-card h2 { font-size: 15px; margin: 0 0 10px; color: var(--text-primary); }
.connections-form-card .hint { font-size: 12px; color: var(--text-muted); margin: 0 0 14px; line-height: 1.5; }
.connections-form-card .hint a { color: var(--link); }
.connections-form-card .hint a:hover { color: var(--link-hover); }
.connections-form-card label { display: block; font-size: 12px; color: var(--text-secondary); margin: 12px 0 4px; }
.connections-form-card input[type="text"],
.connections-form-card input[type="password"] {
  width: 100%;
  padding: 8px 10px;
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 13px;
  transition: border-color 0.15s ease;
}
.connections-form-card input[type="text"]:focus,
.connections-form-card input[type="password"]:focus { outline: none; border-color: var(--accent); }
.connections-form-card .advanced { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 10px; }
.connections-form-card .advanced summary { font-size: 12px; color: var(--link); cursor: pointer; }
.connections-form-card .advanced .hint { margin: 8px 0 4px; }
.conn-form-actions { display: flex; gap: 8px; margin-top: 18px; }
#btn-add-connection {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  padding: 10px;
  background: var(--accent);
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-on-accent);
  font-size: 14px;
  cursor: pointer;
  transition: background 0.15s ease;
}
#btn-add-connection:hover { background: var(--accent-hover); }
/* Only shown while editing an existing saved connection (see
   btn-connection-edit in renderer.js) - Save & Connect's own label changes
   to "Save Changes" for that same state, so this is the one visible way
   back out to a plain new-connection form without reloading the app. */
#btn-cancel-edit-connection {
  padding: 10px 16px;
  background: var(--bg-btn);
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 14px;
  cursor: pointer;
  transition: background 0.15s ease;
}
#btn-cancel-edit-connection:hover { background: var(--bg-btn-hover); }

.connection-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border-soft);
}
.connection-row:last-child { border-bottom: none; }
.connection-info { flex: 1; min-width: 0; }
.connection-nickname { display: block; font-size: 13px; font-weight: 600; color: var(--text-primary); }
.connection-host { display: block; font-size: 11px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.connection-badge { font-size: 11px; padding: 3px 8px; border-radius: var(--radius-pill); flex-shrink: 0; }
.connection-badge.active { background: var(--success-bg); color: var(--success-text); }
.connection-badge.inactive { background: var(--bg-btn); color: var(--text-muted); }
.connection-password-inline {
  width: 130px;
  padding: 6px 8px;
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 12px;
}
.connection-password-inline:focus { outline: none; border-color: var(--accent); }
.connection-row button {
  display: inline-flex;
  align-items: center;
  background: var(--bg-btn);
  border: none;
  color: var(--text-primary);
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s ease;
}
.connection-row button:hover { background: var(--bg-btn-hover); }
.connection-row button.btn-connect { background: var(--success); color: var(--text-on-accent); }
.connection-row button.btn-connect:hover { background: var(--success-hover); }
.connection-row button.btn-disconnect { background: var(--danger); color: var(--text-on-accent); }
.connection-row button.btn-disconnect:hover { background: var(--danger-hover); }
.connections-empty { color: var(--text-faint); font-size: 12px; padding: 8px 0; }

/* SQL / Text editor tabs */
.toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: var(--bg-panel);
  border-bottom: 1px solid var(--border-soft);
  flex-wrap: wrap;
}
.toolbar-label { font-size: 12px; color: var(--text-muted); }
/* Thin vertical rule separating logical clusters of controls within a
   toolbar (file actions / language+format / connection / analysis) once a
   tab's toolbar has grown past a handful of controls - purely visual, no
   behavior. */
.toolbar-divider { width: 1px; align-self: stretch; background: var(--border-soft); margin: 2px 2px; flex-shrink: 0; }
/* Shown next to a connection <select> only when it has no active
   connections to offer - a one-click shortcut back to the Connections tab
   instead of making the user go find it themselves. */
.goto-connections-link { font-size: 12px; color: var(--link); text-decoration: none; white-space: nowrap; }
.goto-connections-link:hover { color: var(--link-hover); text-decoration: underline; }
#btn-cancel-sql { background: var(--danger-bg); color: var(--danger-text); }
#btn-cancel-sql:hover:not(:disabled) { background: var(--danger); color: var(--text-on-accent); }
.toolbar select,
.toolbar .maxrows-input {
  padding: 6px 8px;
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 12px;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}
.toolbar select:focus,
.toolbar .maxrows-input:focus { outline: none; border-color: var(--accent); }
.toolbar select:disabled,
.toolbar .maxrows-input:disabled { opacity: 0.5; }
.toolbar .maxrows-input { width: 90px; }
.toolbar button {
  display: inline-flex;
  align-items: center;
  background: var(--bg-btn);
  border: none;
  color: var(--text-primary);
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.1s ease;
}
.toolbar button:hover:not(:disabled) { background: var(--bg-btn-hover); }
.toolbar button:active:not(:disabled) { transform: scale(0.97); }
.toolbar button:disabled { opacity: 0.5; cursor: default; }
.toolbar button:focus-visible,
button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
#btn-run-sql { background: var(--success); color: var(--text-on-accent); }
#btn-run-sql:hover:not(:disabled) { background: var(--success-hover); }
#btn-ai-send { background: var(--accent-2); color: var(--text-on-accent); }
#btn-ai-send:hover:not(:disabled) { background: var(--accent-2-hover); }
.run-status { margin-left: 8px; font-size: 12px; color: var(--text-muted); }

.toolbar-menu-wrap { position: relative; }
.history-panel {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 4px;
  width: 420px;
  max-height: 360px;
  overflow-y: auto;
  background: var(--bg-elevated-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-dropdown);
  z-index: 20;
}
.history-item {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-soft);
  cursor: pointer;
  font-size: 12px;
  color: var(--text-primary);
}
.history-item:hover { background: var(--bg-hover); }
.history-item-sql {
  font-family: monospace;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 4.2em;
  overflow: hidden;
}
.history-item-meta { color: var(--text-muted); font-size: 11px; margin-top: 4px; }
.history-empty { padding: 12px; color: var(--text-faint); font-size: 12px; }
.history-clear-row { padding: 6px 10px; text-align: right; border-bottom: 1px solid var(--border-soft); }
.history-clear-row button {
  background: none;
  border: none;
  color: var(--danger-text);
  font-size: 11px;
  cursor: pointer;
  padding: 2px 6px;
}
.history-clear-row button:hover { text-decoration: underline; }

.editor-host {
  flex: 0 0 40%;
  min-height: 120px;
  position: relative;
  border: 1px solid transparent;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.editor-host.tall { flex: 1; }
.editor-host .CodeMirror {
  height: 100%;
  font-size: 13px;
  font-family: 'Cascadia Code', 'Cascadia Mono', Consolas, Menlo, Monaco, monospace;
}
/* Focus ring - toggled via cm.on('focus'/'blur') in renderer.js, since
   CodeMirror's own textarea-backed input isn't a real :focus-within target
   in every Chromium version this app has been tested against. */
.editor-host.cm-focused { border-color: var(--accent-2); box-shadow: 0 0 0 1px rgba(43, 111, 217, 0.35); }
/* Drag-and-drop a .sql/.txt file onto the editor - toggled the same way. */
.editor-host.drag-over { border-color: var(--success); box-shadow: 0 0 0 1px rgba(47, 158, 68, 0.4); }
.editor-host.drag-over::after {
  content: 'Drop to open';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(30, 30, 30, 0.75);
  color: var(--code-text);
  font-size: 14px;
  font-weight: 700;
  pointer-events: none;
  z-index: 10;
}
/* Active-line highlight (addon/selection/active-line.js) - subtle enough
   not to fight either CodeMirror theme's own selection/cursor colors. A
   light overlay reads fine on monokai (dark); eclipse (light) needs a dark
   one instead - --cm-active-line flips with the app theme for that reason,
   not because CodeMirror's theme itself is themed via these tokens. */
.CodeMirror-activeline-background { background: var(--cm-active-line); }

/* Find/Replace widget - a VS Code-style floating panel anchored to the
   editor it's attached to (see attachFindReplace in renderer.js), replacing
   CodeMirror's stock dialog/search addons (a single tiny prompt bar per
   command, no match count, no case/whole-word toggles, separate commands
   for find vs replace instead of one combined panel). */
@keyframes frWidgetIn {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}
.fr-widget {
  position: absolute;
  top: 8px;
  right: 24px;
  z-index: 15;
  background: var(--bg-elevated-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-dropdown);
  padding: 6px;
  font-size: 12px;
  color: var(--text-primary);
  animation: frWidgetIn 0.15s ease;
  max-width: calc(100% - 32px);
}
.fr-row { display: flex; align-items: center; gap: 4px; }
.fr-row + .fr-row { margin-top: 4px; }
.fr-expand {
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 10px;
  padding: 4px 2px;
  transition: transform 0.15s ease, color 0.15s ease;
}
.fr-expand:hover { color: var(--text-primary); }
.fr-expand.expanded { transform: rotate(90deg); }
.fr-input, .fr-replace-input {
  width: 180px;
  padding: 5px 7px;
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 12px;
  transition: border-color 0.15s ease;
}
.fr-input:focus, .fr-replace-input:focus { outline: none; border-color: var(--accent-2); }
.fr-widget.fr-noresults .fr-input { border-color: var(--danger); }
.fr-count {
  min-width: 64px;
  text-align: center;
  font-size: 11px;
  color: var(--text-muted);
  padding: 0 4px;
  white-space: nowrap;
}
.fr-count.fr-noresults { color: var(--danger-text); font-weight: 700; }
.fr-toggle, .fr-icon-btn {
  background: var(--bg-btn);
  border: none;
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  padding: 5px 8px;
  font-size: 11px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.fr-toggle:hover, .fr-icon-btn:hover { background: var(--bg-btn-hover); }
.fr-toggle.active { background: var(--accent-2); color: var(--text-on-accent); }
.fr-icon-btn:disabled { opacity: 0.4; cursor: default; }
.fr-close { margin-left: 2px; font-size: 14px; line-height: 1; padding: 4px 8px; }
.fr-replace-row { padding-left: 18px; }
.fr-replace-one, .fr-replace-all {
  background: var(--bg-btn);
  border: none;
  color: var(--text-primary);
  border-radius: var(--radius-sm);
  padding: 5px 9px;
  font-size: 11px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease;
}
.fr-replace-one:hover, .fr-replace-all:hover { background: var(--bg-btn-hover); }

/* Match highlights - all matches dimmed, current match bright, both
   applied via CodeMirror markText rather than the stock addon's overlay
   mode, so case-sensitive/whole-word filtering can be computed in plain JS
   without fighting CodeMirror's own search-cursor semantics. */
.fr-match { background: rgba(255, 213, 0, 0.35); border-radius: 2px; }
.fr-match-current { background: #ff9d00; color: #111; border-radius: 2px; }

.split-handle {
  flex: 0 0 6px;
  background: var(--bg-panel-2);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  cursor: row-resize;
  transition: background 0.15s ease;
}
.split-handle:hover, .split-handle.dragging { background: var(--border-strong); }

.results-wrap { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.results-meta { padding: 6px 12px; font-size: 12px; color: var(--text-data-muted); background: var(--bg-data); border-bottom: 1px solid var(--border-data); }
.results-container { flex: 1; overflow: auto; background: var(--bg-data); }

/* min-width (not width) - fills the container's width when there are few
   columns, same as before, but lets the table grow wider than the
   container when it has many columns (cells are white-space: nowrap below),
   so .results-container/.detail-group's existing overflow:auto can scroll
   to the rest instead of squeezing/clipping every column to fit. */
table.results-table { border-collapse: collapse; min-width: 100%; font-size: 12px; color: var(--text-data); }
table.results-table th, table.results-table td {
  border: 1px solid var(--border-data);
  padding: 5px 10px;
  text-align: left;
  white-space: nowrap;
}
table.results-table th { background: var(--bg-data); font-weight: 700; position: sticky; top: 0; border-bottom: 2px solid var(--border-data-strong); color: var(--text-data); }
table.results-table tr:nth-child(even) td { background: var(--bg-data-alt); }
/* Row hover - written after the nth-child rule so it wins the cascade on
   ties, making it easy to track one row across a wide table. Excludes the
   virtual-scroll spacer rows (see renderVisibleRows in renderer.js), which
   have no real content to highlight. */
table.results-table tbody tr:not(.virtual-spacer):hover td { background: var(--bg-data-hover); }
table.results-table tr.virtual-spacer td { padding: 0; border: none; }

table.results-table th.sortable { cursor: pointer; user-select: none; }
table.results-table th.sortable:hover { background: var(--bg-data-hover); }
table.results-table th .sort-arrow { display: inline-flex; margin-left: 4px; color: var(--text-data-muted); }
table.results-table th .sort-arrow .sort-arrow-desc { transform: rotate(180deg); }

table.results-table td.copyable-cell { cursor: pointer; }
table.results-table td.copyable-cell.copied { background: var(--success-bg) !important; color: var(--success-text) !important; }

/* A blank cell could be a DB NULL or a genuine empty string - BI Publisher's
   XML output renders both identically, so there's no way to tell them apart
   here. This just marks "there's nothing here" distinctly from a real short
   value, rather than claiming to know which case it is. */
table.results-table td.cell-blank { color: var(--text-data-faint); font-style: italic; }

/* Softer borders when this table is nested inside the Object Browser /
   Dependent Object Detector card wrapper (.detail-group already supplies
   its own rounded border+shadow, so the table's own grid lines would
   otherwise double up against it). */
.detail-group table.results-table th,
.detail-group table.results-table td {
  border-color: var(--border-data);
}
.detail-group table.results-table th { border-bottom: 2px solid var(--border-data-strong); }
.detail-group table.results-table { margin: 0; }

/* CodeMirror search/replace dialog + autocomplete hint popup - restyled to
   track the app's own theme tokens instead of a hardcoded palette, so they
   stay legible whichever CodeMirror theme (monokai/eclipse) is active. */
.CodeMirror-dialog {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border-bottom: 1px solid var(--border-strong);
}
.CodeMirror-dialog input {
  background: var(--bg-input);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
}
.CodeMirror-hints {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  font-family: monospace;
  box-shadow: var(--shadow-dropdown);
}
.CodeMirror-hint { color: var(--text-primary); }
li.CodeMirror-hint-active { background: var(--accent-2); color: var(--text-on-accent); }

/* Table Browser */
.tables-hint { padding: 8px 12px 0; margin: 0; color: var(--text-muted); }
.tables-search-input {
  flex: 1;
  max-width: 320px;
  padding: 6px 8px;
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 12px;
  transition: border-color 0.15s ease;
}
.tables-search-input:focus { outline: none; border-color: var(--accent); }
.tables-layout { flex: 1; display: flex; min-height: 0; }
.tables-list-panel {
  flex: 0 0 280px;
  overflow: auto;
  border-right: 1px solid var(--border-soft);
  background: var(--bg-app);
}
.tables-detail-panel { flex: 1; overflow: auto; background: var(--bg-data); color: var(--text-data); display: flex; flex-direction: column; }
.table-row-item {
  padding: 7px 12px;
  font-size: 12px;
  color: var(--text-secondary);
  cursor: pointer;
  border-bottom: 1px solid var(--bg-panel-2);
}
.table-row-item:hover { background: var(--bg-hover); }
.table-row-item.selected { background: var(--accent); color: var(--text-on-accent); }
.tables-empty { color: var(--text-faint); font-size: 12px; padding: 10px 12px; }
/* Same class, but shown against the data detail panel instead of the dark
   chrome list panel - needs the data-panel muted color to stay readable
   there regardless of theme. */
#table-columns-container .tables-empty { color: var(--text-data-muted); }

/* Library */
.library-item {
  padding: 10px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--bg-panel-2);
}
.library-item:hover { background: var(--bg-hover); }
.library-item.selected { background: var(--accent); }
.library-item.selected .library-item-preview,
.library-item.selected .library-item-tag { color: var(--text-on-accent); }
.library-item-title { font-size: 12.5px; font-weight: 700; color: var(--text-primary); margin-bottom: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.library-item-tags { margin-bottom: 4px; }
.library-item-tag {
  display: inline-block;
  font-size: 10px;
  color: var(--text-secondary);
  background: var(--bg-btn);
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  margin-right: 4px;
}
.library-item-preview {
  font-size: 11px;
  color: var(--text-muted);
  font-family: 'Courier New', monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.library-item mark, .library-detail-header mark { background: var(--mark-bg); color: var(--mark-text); border-radius: 2px; }

.library-detail-header {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-data);
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-data);
}
.library-detail-title { font-size: 14px; font-weight: 700; flex: 1; color: var(--text-data); }
.library-detail-meta { padding: 8px 18px; font-size: 11px; color: var(--text-data-muted); border-bottom: 1px solid var(--border-data); background: var(--bg-data); }
.library-detail-body {
  flex: 1;
  overflow: auto;
  margin: 14px 18px;
  padding: 12px;
  background: var(--bg-data-alt);
  color: var(--text-data);
  border: 1px solid var(--border-data);
  border-radius: var(--radius-md);
  font-family: 'Courier New', monospace;
  font-size: 12px;
  white-space: pre-wrap;
  word-break: break-word;
}

/* DBI Search */
.dbi-item {
  padding: 10px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--bg-panel-2);
}
.dbi-item:hover { background: var(--bg-hover); }
.dbi-item.selected { background: var(--accent); }
.dbi-item.selected .dbi-item-meta { color: var(--text-on-accent); }
.dbi-item-title { font-size: 12.5px; font-weight: 700; color: var(--text-primary); margin-bottom: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dbi-item-meta {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dbi-cap-note { color: var(--text-faint); font-size: 12px; padding: 8px 12px 0; }

.dbi-detail-header {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-data);
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-data);
}
.dbi-detail-title { font-size: 14px; font-weight: 700; flex: 1; color: var(--text-data); }
.dbi-detail-row {
  display: flex;
  gap: 8px;
  padding: 5px 18px;
  font-size: 12px;
  color: var(--text-data);
  border-bottom: 1px solid var(--bg-panel-2);
  background: var(--bg-data);
}
.dbi-detail-row-label { flex: 0 0 170px; font-weight: 700; color: var(--text-data-muted); }
.dbi-detail-row-value { flex: 1; word-break: break-word; }
.dbi-detail-section-title {
  padding: 12px 18px 0;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-data-muted);
  background: var(--bg-data);
}
.dbi-detail-body {
  flex: 1;
  overflow: auto;
  margin: 8px 18px 14px;
  padding: 12px;
  background: var(--bg-data-alt);
  color: var(--text-data);
  border: 1px solid var(--border-data);
  border-radius: var(--radius-md);
  font-family: 'Courier New', monospace;
  font-size: 12px;
  white-space: pre-wrap;
  word-break: break-word;
}

.library-form { padding: 18px; display: flex; flex-direction: column; gap: 10px; max-width: 640px; background: var(--bg-data); flex: 1; }
.library-form label { font-size: 12px; font-weight: 700; color: var(--text-data-muted); }
.library-form input, .library-form textarea {
  padding: 8px 10px;
  border: 1px solid var(--border-data-strong);
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  font-family: inherit;
  background: var(--bg-data);
  color: var(--text-data);
}
.library-form input:focus, .library-form textarea:focus { outline: none; border-color: var(--accent); }
.library-form textarea { font-family: 'Courier New', monospace; resize: vertical; }
.library-form-actions { display: flex; gap: 8px; margin-top: 4px; }

/* HDL Load history (left nav list + detail-panel header) */
.hdl-history-item {
  padding: 10px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--bg-panel-2);
}
.hdl-history-item:hover { background: var(--bg-hover); }
.hdl-history-item.selected { background: var(--accent); color: var(--text-on-accent); }
.hdl-history-item-title {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hdl-history-item.selected .hdl-history-item-title { color: var(--text-on-accent); }
.hdl-history-item-meta { font-size: 11px; color: var(--text-muted); margin-bottom: 4px; }
.hdl-history-item.selected .hdl-history-item-meta { color: var(--text-on-accent); opacity: 0.85; }
.hdl-history-item-watching { font-size: 10.5px; font-style: italic; }

.hdl-history-badge {
  display: inline-block;
  font-size: 11px;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
}
.hdl-history-badge.success { background: var(--success-bg); color: var(--success-text); }
.hdl-history-badge.error { background: var(--danger-bg); color: var(--danger-text); }
.hdl-history-badge.in-progress { background: var(--callout-warn-bg); color: var(--callout-warn-text); }
.hdl-history-badge.stopped { background: var(--bg-btn); color: var(--text-muted); }

/* Extracts & Reports - saved config list (left nav) + run-history badges,
   cloned from .hdl-history-item/-badge above (same per-tab-class
   convention as .dbi-item was cloned from .library-item). */
.extract-item {
  padding: 10px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--bg-panel-2);
  transition: background-color 120ms ease, transform 120ms ease;
}
.extract-item:hover { background: var(--bg-hover); transform: translateX(2px); }
.extract-item.selected { background: var(--accent); color: var(--text-on-accent); }
.extract-item.selected:hover { transform: none; }
.extract-item-top-row { display: flex; align-items: center; gap: 8px; margin-bottom: 3px; }
.extract-item-title {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}
.extract-item.selected .extract-item-title { color: var(--text-on-accent); }
.extract-item-meta-row { display: flex; align-items: baseline; gap: 6px; }
.extract-item-meta {
  font-size: 11px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}
.extract-item.selected .extract-item-meta { color: var(--text-on-accent); opacity: 0.85; }
.extract-item-recency { font-size: 10px; color: var(--text-faint); flex-shrink: 0; }
.extract-item.selected .extract-item-recency { color: var(--text-on-accent); opacity: 0.7; }

.extract-badge {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}
.extract-badge.success { background: var(--success-bg); color: var(--success-text); }
.extract-badge.error { background: var(--danger-bg); color: var(--danger-text); }
.extract-badge.in-progress { background: var(--callout-warn-bg); color: var(--callout-warn-text); }
.extract-badge.neutral { background: var(--bg-btn); color: var(--text-muted); }
.extract-badge.in-progress::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  margin-right: 5px;
  animation: extract-pulse 1.4s ease-in-out infinite;
}
@keyframes extract-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* Detail panel - cloned from .dbi-detail-header/-title/-row/-section-title/-body above. */
.extract-detail-header {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-data);
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-data);
}
.extract-detail-title { font-size: 14px; font-weight: 700; flex: 1; color: var(--text-data); }
.extract-detail-section-title {
  padding: 12px 18px 0;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-data-muted);
  background: var(--bg-data);
}

/* Config-editing form (name/path/flowName/etc.) - mirrors .library-form. */
.extract-form { padding: 18px; display: flex; flex-direction: column; gap: 10px; background: var(--bg-data); }
.extract-form label { font-size: 12px; font-weight: 700; color: var(--text-data-muted); }
.extract-form input, .extract-form select, .extract-form textarea {
  padding: 8px 10px;
  border: 1px solid var(--border-data-strong);
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  font-family: inherit;
  background: var(--bg-data);
  color: var(--text-data);
}
.extract-form input:focus, .extract-form select:focus, .extract-form textarea:focus { outline: none; border-color: var(--accent); }
.extract-form-actions { display: flex; gap: 8px; margin-top: 4px; }

/* One row per report/extract parameter - label + its value input/select,
   plus (Extract params only) a toggle to attach a SQL-backed LOV. */
.extract-param-row {
  padding: 10px 18px;
  border-bottom: 1px solid var(--bg-panel-2);
  background: var(--bg-data);
}
.extract-param-row-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.extract-param-name { font-size: 12.5px; font-weight: 700; color: var(--text-data); flex: 1; }
.extract-param-row input, .extract-param-row select {
  width: 100%;
  box-sizing: border-box;
  padding: 6px 8px;
  border: 1px solid var(--border-data-strong);
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  background: var(--bg-data-alt);
  color: var(--text-data);
}
.extract-param-row input:focus, .extract-param-row select:focus { outline: none; border-color: var(--accent); }
.extract-lov-sql {
  width: 100%;
  box-sizing: border-box;
  margin-top: 6px;
  padding: 6px 8px;
  border: 1px solid var(--border-data-strong);
  border-radius: var(--radius-sm);
  font-size: 11.5px;
  font-family: 'Courier New', monospace;
  background: var(--bg-data-alt);
  color: var(--text-data);
  resize: vertical;
}
.extract-lov-sql:focus { outline: none; border-color: var(--accent); }
.extract-lov-hint { font-size: 10.5px; color: var(--text-data-muted); margin-top: 4px; }

.extract-progress-bar {
  position: relative;
  height: 16px;
  margin-top: 6px;
  border-radius: 4px;
  background: var(--border-data);
  overflow: hidden;
  max-width: 320px;
}
.extract-progress-bar-fill {
  height: 100%;
  background: var(--accent);
  transition: width 0.4s ease;
}
.extract-progress-bar-fill.errored { background: var(--danger); }
.extract-progress-bar-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--text-data);
  text-shadow: 0 0 3px var(--bg-data);
}

/* Collapsible one-line summary replacing what used to be 2-3 always-visible
   makeRow key/value blocks (report path/output format, or flow name/LDG/
   flow task instance name) - click to expand into the real rows. */
.extract-config-summary {
  margin: 10px 18px 0;
  padding: 7px 10px;
  font-family: 'Courier New', monospace;
  font-size: 11.5px;
  color: var(--text-data-muted);
  background: var(--bg-data-alt);
  border: 1px solid var(--border-data);
  border-radius: var(--radius-md);
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 8px;
}
.extract-config-summary:hover { border-color: var(--border-data-strong); color: var(--text-data); }
.extract-config-summary::after { content: '▸'; margin-left: auto; transition: transform 150ms ease; flex-shrink: 0; }
.extract-config-summary.expanded::after { transform: rotate(90deg); }
.extract-config-summary-details { display: flex; flex-direction: column; }
.extract-config-summary-details.hidden { display: none; }

/* Small (i) affordance replacing a permanent full-sentence caption under
   every parameter field - the same info is one hover/focus away via the
   native title tooltip instead of always being on screen. */
.extract-param-info-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1px solid var(--border-data-strong);
  color: var(--text-data-faint);
  font-size: 10px;
  line-height: 1;
  cursor: help;
  flex-shrink: 0;
}
.extract-param-info-btn:hover { color: var(--accent); border-color: var(--accent); }

/* Run History - one collapsed card per run (status + time + one contextual
   action visible at a glance; everything else - full error text, the
   parameter dump, saved file path, Remove - lives behind the expand
   toggle instead of being permanently on screen for every run). */
.extract-run-card {
  margin: 0 18px 8px;
  border: 1px solid var(--border-data);
  border-left: 3px solid var(--border-data);
  border-radius: var(--radius-md);
  background: var(--bg-data-alt);
  overflow: hidden;
}
.extract-run-card.status-success { border-left-color: var(--success); }
.extract-run-card.status-error { border-left-color: var(--danger); }
.extract-run-card.status-in-progress { border-left-color: var(--callout-warn-text); }
.extract-run-card-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  cursor: pointer;
  transition: background-color 120ms ease;
}
.extract-run-card-summary:hover { background: var(--bg-data-hover); }
.extract-run-time { font-size: 11px; color: var(--text-data-muted); flex-shrink: 0; }
.extract-run-error-snippet {
  font-size: 11px;
  color: var(--danger-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
  min-width: 0;
}
.extract-run-spacer { flex: 1; }
.extract-run-chevron { font-size: 10px; color: var(--text-data-faint); transition: transform 150ms ease; flex-shrink: 0; }
.extract-run-card.expanded .extract-run-chevron { transform: rotate(90deg); }
.extract-run-card-body {
  max-height: 0;
  overflow: hidden;
  transition: max-height 200ms ease;
  padding: 0 12px;
}
.extract-run-card.expanded .extract-run-card-body { max-height: 600px; overflow-y: auto; padding: 0 12px 10px; border-top: 1px solid var(--border-data); }
.extract-run-card-body > * { margin-top: 8px; }
.extract-run-card .extract-progress-bar { margin: 0 12px 9px; }
.extract-run-error-callout {
  border-left: 3px solid var(--danger);
  background: var(--danger-bg);
  color: var(--danger-text);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 8px 10px;
  font-size: 11.5px;
  white-space: pre-wrap;
}
.extract-run-params-grid {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 3px 12px;
  font-size: 11px;
  font-family: 'Courier New', monospace;
  color: var(--text-data-muted);
}
.extract-run-params-grid .val { color: var(--text-data); overflow-wrap: anywhere; }
.extract-run-blank-toggle, .extract-run-actions button {
  background: transparent;
  border: none;
  color: var(--text-data-faint);
  font-size: 10.5px;
  cursor: pointer;
  padding: 0;
}
.extract-run-blank-toggle:hover { color: var(--accent); }
.extract-run-actions { display: flex; gap: 12px; align-items: center; }

.hdlload-history-meta {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-data);
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-data);
}
.hdlload-history-meta-title { font-size: 14px; font-weight: 700; flex: 1; color: var(--text-data); }
.hdlload-history-meta-line { font-size: 11px; color: var(--text-data-muted); }

#table-detail-header {
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-data);
  border-bottom: 1px solid var(--border-data);
  background: var(--bg-data);
}
#table-columns-container { flex: 1; overflow: auto; }

/* Object Browser / Dependent Object Detector - shared "card" look. Scoped
   to these two tabs' result containers so the SQL Editor's own results
   grid (which reuses .results-table/.results-container for a very
   different, high-density use case) is unaffected. */
#objects-result-container,
#depobj-result-container,
#compare-result-container {
  padding: 10px 0 20px;
  background: var(--bg-app);
}

.object-detail {
  margin: 0 14px 14px;
  padding: 16px 18px;
  background: var(--bg-data);
  color: var(--text-data);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  border: 1px solid var(--border-data);
}
.object-detail-field { display: flex; gap: 10px; padding: 6px 2px; font-size: 12.5px; border-bottom: 1px solid var(--border-data); }
.object-detail-field:last-child { border-bottom: none; }
.object-detail-label { flex: 0 0 170px; font-weight: 700; color: var(--text-data-muted); }
.object-detail-value { flex: 1; color: var(--text-data); word-break: break-word; }
.object-detail-body {
  margin-top: 12px;
  padding: 12px;
  background: var(--bg-data-alt);
  color: var(--text-data);
  border: 1px solid var(--border-data);
  border-radius: var(--radius-md);
  font-family: 'Courier New', monospace;
  font-size: 12px;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Object Browser editing - see the "Object Browser editing" comment above
   lookupObject in renderer.js for the REST-vs-generated-SQL split this
   styles both flows for. */
.obj-edit-btn {
  float: right;
  display: inline-flex;
  align-items: center;
  background: var(--accent-2);
  border: none;
  color: var(--text-on-accent);
  padding: 5px 12px;
  border-radius: var(--radius-sm);
  font-size: 11px;
  cursor: pointer;
  transition: background 0.15s ease;
}
.obj-edit-btn:hover { background: var(--accent-2-hover); }
.obj-edit-btn.hidden { display: none; }
.obj-edit-textarea {
  width: 100%;
  min-height: 220px;
  margin-top: 12px;
  padding: 12px;
  background: var(--bg-data-alt);
  color: var(--text-data);
  border: 1px solid var(--accent-2);
  border-radius: var(--radius-md);
  font-family: 'Courier New', monospace;
  font-size: 12px;
  resize: vertical;
  box-sizing: border-box;
}
.obj-edit-actions { margin-top: 8px; display: flex; gap: 8px; }
.obj-edit-actions button {
  font-size: 12px;
  padding: 6px 12px;
  background: var(--bg-btn);
  color: var(--text-primary);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s ease;
}
.obj-edit-actions button:hover { background: var(--bg-btn-hover); }
.obj-edit-actions button:first-child { background: var(--accent-2); color: var(--text-on-accent); }
.obj-edit-actions button:first-child:hover { background: var(--accent-2-hover); }

.obj-generated-sql {
  margin-top: 12px;
  padding: 12px;
  background: var(--callout-warn-bg);
  border: 1px solid var(--callout-warn-border);
  border-radius: var(--radius-md);
}
.obj-generated-sql-label { font-size: 11.5px; font-weight: 700; color: var(--callout-warn-text); margin-bottom: 8px; }
.obj-generated-sql pre {
  background: var(--bg-code);
  color: var(--code-text);
  padding: 10px;
  border-radius: var(--radius-md);
  overflow-x: auto;
  font-size: 12px;
  font-family: 'Courier New', monospace;
  white-space: pre-wrap;
  margin: 0 0 8px;
}
.obj-generated-sql button {
  font-size: 11px;
  padding: 5px 10px;
  background: var(--bg-btn);
  color: var(--text-primary);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  margin-right: 8px;
  transition: background 0.15s ease;
}
.obj-generated-sql button:hover { background: var(--bg-btn-hover); }

.obj-row-edit-btn {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  padding: 4px 10px;
  background: var(--accent-2);
  color: var(--text-on-accent);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease;
}
.obj-row-edit-btn:hover { background: var(--accent-2-hover); }
.obj-row-edit-btn.hidden { display: none; }
.obj-row-edit-input {
  width: 100%;
  box-sizing: border-box;
  padding: 3px 5px;
  font-size: 12px;
  border: 1px solid var(--accent-2);
  border-radius: 3px;
  color: var(--text-data);
  background: var(--bg-data);
}
.results-table td .obj-generated-sql { min-width: 320px; white-space: normal; }
.results-table td button {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  padding: 4px 9px;
  background: var(--bg-btn);
  color: var(--text-primary);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  margin-right: 4px;
  white-space: nowrap;
  transition: background 0.15s ease;
}
.results-table td button:hover { background: var(--bg-btn-hover); }
.obj-row-edit-status {
  font-size: 11px;
  color: var(--success-text);
  margin-left: 4px;
  white-space: normal;
  word-break: break-word;
  display: inline-block;
  max-width: 420px;
  vertical-align: middle;
}
.obj-row-edit-status.obj-row-edit-error { color: var(--danger-text); font-weight: 700; }
.obj-row-delete-btn { background: var(--danger); color: var(--text-on-accent); }
.obj-row-delete-btn:hover { background: var(--danger-hover); }
.obj-add-row-bar { margin: 0 0 8px; }
.obj-add-row-form {
  margin-top: 8px;
  padding: 10px;
  background: var(--bg-data-alt);
  border: 1px solid var(--border-data);
  border-radius: var(--radius-sm);
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px;
}
.obj-add-row-field {
  display: flex;
  flex-direction: column;
  font-size: 11px;
  color: var(--text-data-muted);
  gap: 3px;
}
.obj-add-row-field input {
  padding: 4px 6px;
  font-size: 12px;
  border: 1px solid var(--border-data-strong);
  border-radius: 3px;
  color: var(--text-data);
  background: var(--bg-data);
}
.obj-add-row-field input:focus { outline: none; border-color: var(--accent); }

/* Comparison tab - field/table diffs between two connections. */
.diff-header {
  margin: 0 14px 14px;
  padding: 16px 18px;
  background: var(--bg-data);
  color: var(--text-data);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  border: 1px solid var(--border-data);
}
.diff-field {
  display: flex;
  gap: 10px;
  padding: 6px 2px;
  font-size: 12.5px;
  border-bottom: 1px solid var(--border-data);
}
.diff-field:last-child { border-bottom: none; }
.diff-field-label { flex: 0 0 170px; font-weight: 700; color: var(--text-data-muted); }
.diff-field-value { flex: 1; color: var(--text-data); word-break: break-word; }
.diff-field-value:first-of-type { border-right: 1px dashed var(--border-data-strong); padding-right: 10px; }
.diff-field-mismatch { background: var(--diff-mismatch-bg); margin: 0 -8px; padding-left: 8px; padding-right: 8px; border-radius: var(--radius-sm); }
.diff-field-mismatch .diff-field-value { color: var(--diff-mismatch-text); font-weight: 700; }

.diff-text {
  margin: 0 14px 14px;
  padding: 4px 0;
  background: var(--bg-data);
  border: 1px solid var(--border-data);
  border-radius: var(--radius-lg);
  overflow: auto;
  font-family: 'Courier New', monospace;
  font-size: 12px;
}
.diff-line { padding: 1px 12px; white-space: pre-wrap; word-break: break-word; }
.diff-line-same { color: var(--text-data-muted); }
.diff-line-added { background: var(--diff-added-bg); color: var(--diff-added-text); }
.diff-line-removed { background: var(--diff-removed-bg); color: var(--diff-removed-text); }

.diff-summary { padding: 8px 14px; font-size: 12px; color: var(--text-secondary); }

table.diff-table th, table.diff-table td { white-space: nowrap; }
tr.diff-row-onlyA td:first-child, tr.diff-row-onlyB td:first-child, tr.diff-row-changed td:first-child { border-left: 3px solid transparent; }
tr.diff-row-onlyA { background: var(--diff-row-onlyA-bg) !important; }
tr.diff-row-onlyB { background: var(--diff-row-onlyB-bg) !important; }
tr.diff-row-changed { background: var(--diff-row-changed-bg) !important; }
td.diff-cell-mismatch { background: var(--diff-cell-mismatch-bg) !important; font-weight: 700; }

.diff-status-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  white-space: nowrap;
}
.diff-status-same { background: #6b7280; }
.diff-status-changed { background: #d9822b; }
.diff-status-onlyA { background: #c92a2a; }
.diff-status-onlyB { background: #2f5fc4; }

/* Section headers - group headings ("VALUE_SET (3)", "Formula: X") sit on
   the dark background between white detail cards, so they read as
   navigation/structure rather than another content card. */
.section-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 20px 14px 8px;
  padding: 0 2px 8px;
  border-bottom: 1px solid var(--border);
}
.section-header:first-child { margin-top: 8px; }
.section-header-title { font-size: 12.5px; font-weight: 700; color: var(--text-primary); letter-spacing: 0.2px; }
.section-header-count {
  font-size: 11px;
  color: var(--text-secondary);
  background: var(--bg-btn);
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  margin-left: auto;
}

/* Type badges - consistent color per object type across both tabs, so the
   kind of thing you're looking at is recognizable at a glance. Saturated,
   opaque chips with white text read fine against either theme, so these
   colors are deliberately NOT theme tokens. */
.type-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.4px;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  text-transform: uppercase;
  color: #fff;
  white-space: nowrap;
}
/* Separates each item's results when the Dependent Object Detector analyzes
   several extracts/formulas at once (comma-separated names) - only
   inserted when there's more than one, so a single-item analysis looks
   exactly as it always has. */
.batch-item-header {
  margin: 24px 14px 4px;
  padding-bottom: 6px;
  border-bottom: 2px solid var(--border-strong);
  font-size: 15px;
  font-weight: 700;
  color: var(--text-primary);
}
.batch-item-header:first-child { margin-top: 10px; }

.type-badge.type-value_set { background: #3572e8; }
.type-badge.type-lookup { background: #8250df; }
.type-badge.type-udt { background: #0f9d8c; }
.type-badge.type-message { background: #d9822b; }
.type-badge.type-balance_group { background: #2f9e44; }
.type-badge.type-formula { background: #a61e4d; }
.type-badge.type-extract { background: #495057; }
.type-badge.type-table { background: #364fc7; }

/* Text Editor's "Detected Sub-Objects" panel - same results-wrap/
   results-container split used by the SQL Editor, but with a small header
   bar (label + close button) instead of a plain status line, since closing
   it hands the editor its full height back. */
.subobj-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-data);
  background: var(--bg-data);
  border-bottom: 1px solid var(--border-data);
}
.subobj-close-btn {
  border: none;
  background: transparent;
  color: var(--text-data-muted);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}
.subobj-close-btn:hover { color: var(--text-data); }

/* Grouped, expandable rows (Dependent Object Detector's detected
   sub-objects) - a bordered card containing one row per item, each
   expandable in place with a rotating chevron and a smooth reveal instead
   of an instant display:none/block toggle. */
.detail-group {
  margin: 0 14px 14px;
  background: var(--bg-data);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  border: 1px solid var(--border-data);
  /* auto, not hidden - still clips content to the rounded corners (any
     overflow value other than visible does), but a wrapped table wider
     than the card (renderPlainTable's usage - HDL Errors/HDL Load/etc.)
     now scrolls horizontally instead of being hard-clipped with no way to
     see the rest of its columns. No visual difference for content that
     doesn't overflow (the expandable-rows usage below), since auto shows
     no scrollbar when nothing overflows. */
  overflow: auto;
}
.expandable-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 16px;
  font-size: 12.5px;
  color: var(--text-data);
  cursor: pointer;
  border-bottom: 1px solid var(--border-data);
  transition: background 0.12s ease;
}
.expandable-row:last-child { border-bottom: none; }
.expandable-row:hover { background: var(--bg-data-hover); }
.expandable-row .chevron {
  display: inline-flex;
  width: 12px;
  color: var(--text-data-muted);
  transition: transform 0.15s ease, color 0.15s ease;
}
.expandable-row .chevron .icon { width: 12px; height: 12px; }
.expandable-row.open .chevron { transform: rotate(90deg); color: var(--accent); }
.expandable-row-name { flex: 1; font-family: 'Courier New', monospace; }
.detail-area {
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  background: var(--bg-data-alt);
  transition: max-height 0.25s ease, opacity 0.2s ease;
}
.detail-area.open {
  max-height: 6000px;
  opacity: 1;
  border-bottom: 1px solid var(--border-data);
}
.detail-area:last-child.open { border-bottom: none; }
.detail-loading { padding: 12px 16px; color: var(--text-data-faint); font-size: 12px; font-style: italic; }

/* Empty/error states - a dashed placeholder for "nothing here", a plain
   tinted box for an error, both replacing ad hoc inline-styled divs. */
.empty-state {
  margin: 14px;
  padding: 26px 18px;
  text-align: center;
  color: var(--text-data-faint);
  background: var(--bg-data);
  border: 1px dashed var(--border-data-strong);
  border-radius: var(--radius-lg);
  font-size: 12.5px;
}
.empty-state.is-error {
  color: var(--danger-text);
  background: var(--danger-bg);
  border: 1px solid var(--danger);
  text-align: left;
  white-space: pre-wrap;
}

.sql-used { margin: 10px 14px 14px; font-size: 11px; color: var(--text-data-muted); }
.sql-used summary { cursor: pointer; color: var(--accent); }
.sql-used summary:hover { color: var(--accent-hover); }
.sql-used pre {
  margin-top: 6px;
  padding: 10px;
  color: var(--text-data);
  background: var(--bg-data-alt);
  border: 1px solid var(--border-data);
  border-radius: var(--radius-sm);
  white-space: pre-wrap;
  word-break: break-word;
}

/* Floating "Look up in Oracle Docs" button shown on text selection */
.docs-lookup-btn {
  display: inline-flex;
  align-items: center;
  position: fixed;
  z-index: 1000;
  background: var(--accent);
  color: var(--text-on-accent);
  border: none;
  border-radius: var(--radius-md);
  padding: 6px 12px;
  font-size: 12px;
  cursor: pointer;
  box-shadow: var(--shadow-dropdown);
  transition: background 0.15s ease;
}
.docs-lookup-btn:hover { background: var(--accent-hover); }

/* In-page confirm dialog - replaces native window.confirm(), which is a
   known Electron/Windows trouble spot for leaving keyboard focus broken
   after the dialog closes. */
.confirm-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.confirm-box {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
  padding: 20px 22px;
  max-width: 420px;
  width: 90%;
}
.confirm-message { font-size: 13px; line-height: 1.5; white-space: pre-wrap; margin-bottom: 16px; }
.confirm-prompt-input {
  width: 100%;
  box-sizing: border-box;
  background: var(--bg-input);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-size: 13px;
  margin-bottom: 16px;
}
.confirm-prompt-input:focus { outline: none; border-color: var(--accent); }
.confirm-actions { display: flex; justify-content: flex-end; gap: 8px; }
.confirm-actions button {
  background: var(--bg-btn);
  border: none;
  color: var(--text-primary);
  padding: 7px 14px;
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  cursor: pointer;
  transition: background 0.15s ease;
}
.confirm-actions button:hover { background: var(--bg-btn-hover); }
.confirm-actions button.confirm-ok { background: var(--accent-2); color: var(--text-on-accent); }
.confirm-actions button.confirm-ok:hover { background: var(--accent-2-hover); }
.confirm-actions button:disabled { opacity: 0.5; cursor: not-allowed; }
.confirm-actions button:disabled:hover { background: var(--bg-btn); }
.confirm-actions button.confirm-ok:disabled:hover { background: var(--accent-2); }

/* AI Assistant */
@keyframes aiFadeSlideIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

.ai-toolbar { row-gap: 10px; }
.ai-status-group { display: flex; align-items: center; gap: 6px; }
.ai-quick-actions { padding-top: 4px; padding-bottom: 4px; }

.ai-pull-row { align-items: center; }
.ai-pull-row input[type='text'] {
  flex: 1;
  min-width: 180px;
  padding: 6px 10px;
  background: var(--bg-input);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 12px;
  transition: border-color 0.15s ease;
}
.ai-pull-row input[type='text']:focus {
  outline: none;
  border-color: var(--accent-2);
}

.ai-pull-progress-wrap { padding: 0 12px 10px; background: var(--bg-panel); }
.ai-pull-progress-track {
  height: 6px;
  border-radius: 3px;
  background: var(--bg-input);
  overflow: hidden;
}
.ai-pull-progress-fill {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--accent-2), var(--accent));
  border-radius: 3px;
  transition: width 0.3s ease;
}
.ai-pull-progress-fill.indeterminate {
  background: linear-gradient(90deg, transparent, var(--accent-2), transparent);
  background-size: 200% 100%;
  animation: aiIndeterminateSweep 1.2s ease-in-out infinite;
}
@keyframes aiIndeterminateSweep {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

.local-ai-panel { border-bottom: 1px solid var(--border-soft); }
.local-ai-header {
  padding: 10px 12px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
  background: var(--bg-panel);
}
.local-ai-header strong { color: var(--text-primary); }
.local-ai-transcript {
  max-height: 260px;
  overflow: auto;
  padding: 12px;
  background: var(--bg-data);
}

.local-ai-web-result { padding: 8px 0; border-bottom: 1px solid var(--border-data); }
.local-ai-web-result:last-child { border-bottom: none; }
.local-ai-web-result-title {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--accent);
  text-decoration: none;
  cursor: pointer;
}
.local-ai-web-result-title:hover { text-decoration: underline; }
.local-ai-web-result-url { font-size: 11px; color: var(--text-data-muted); margin-top: 1px; word-break: break-all; }
.local-ai-web-result-snippet { font-size: 12.5px; color: var(--text-data); margin-top: 4px; line-height: 1.5; }
.local-ai-web-result-save {
  margin-top: 6px;
  font-size: 11.5px;
  padding: 3px 9px;
}

.local-ai-hit-content.collapsed {
  max-height: 140px;
  overflow: hidden;
  position: relative;
}
.local-ai-hit-content.collapsed::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 36px;
  background: linear-gradient(to bottom, transparent, var(--bg-data));
}
.local-ai-hit-toggle {
  margin: 2px 0 12px;
  font-size: 11.5px;
  padding: 3px 9px;
}

.local-ai-placeholder { color: var(--text-data-muted); font-style: italic; }

.local-ai-rule-form label {
  display: block;
  font-size: 11px;
  color: var(--text-data-muted);
  margin-top: 8px;
}
.local-ai-rule-form input {
  width: 100%;
  box-sizing: border-box;
  margin-top: 2px;
}
.local-ai-rule-form button { margin-top: 10px; }

.ai-info-panel {
  margin: 16px;
  padding: 18px 20px;
  background: var(--bg-data);
  color: var(--text-data);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  border: 1px solid var(--border-data);
  font-size: 13px;
  line-height: 1.6;
  max-width: 640px;
  animation: aiFadeSlideIn 0.25s ease;
}
.ai-info-panel p { margin: 0 0 12px; }
.ai-info-panel p:last-child { margin-bottom: 0; }
.ai-info-panel code {
  background: var(--bg-data-alt);
  padding: 1px 5px;
  border-radius: 3px;
  font-family: 'Courier New', monospace;
}
.ai-inline-link-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
  font: inherit;
}
.ai-inline-link-btn:hover { color: var(--accent-hover, var(--accent)); }

.status-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--text-faint);
  flex: 0 0 auto;
}
.status-dot.ok { background: var(--success); }
.status-dot.bad { background: var(--danger); }

.ai-toggle-row {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  font-size: 12.5px;
  color: var(--text-secondary);
}
.ai-toggle-switch {
  position: relative;
  display: inline-block;
  width: 36px;
  height: 20px;
  flex: 0 0 auto;
}
.ai-toggle-switch input {
  opacity: 0;
  width: 0;
  height: 0;
  position: absolute;
}
.ai-toggle-track {
  position: absolute;
  inset: 0;
  background: var(--border-strong);
  border-radius: 20px;
  transition: background 0.15s ease;
}
.ai-toggle-track::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--bg-panel);
  transition: transform 0.15s ease;
}
.ai-toggle-switch input:checked + .ai-toggle-track {
  background: var(--success);
}
.ai-toggle-switch input:checked + .ai-toggle-track::before {
  transform: translateX(16px);
}
.ai-toggle-switch input:focus-visible + .ai-toggle-track {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}

.ai-chat-transcript {
  flex: 1;
  overflow: auto;
  padding: 16px;
  background: var(--bg-data);
  animation: aiFadeSlideIn 0.25s ease;
}
.ai-chat-empty { color: var(--text-data-faint); font-size: 12.5px; text-align: center; padding: 40px 20px; }
.ai-message { margin-bottom: 16px; max-width: 82%; animation: aiFadeSlideIn 0.2s ease; }
.ai-message.user { margin-left: auto; }
.ai-message-role { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; color: var(--text-data-muted); margin-bottom: 4px; }
.ai-message.user .ai-message-role { text-align: right; }
.ai-message-bubble {
  padding: 10px 14px;
  border-radius: 12px;
  font-size: 13px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--text-data);
  background: var(--bg-data-alt);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}
.ai-message.user .ai-message-bubble { background: var(--accent-2); color: var(--text-on-accent); border-bottom-right-radius: 4px; }
.ai-message:not(.user) .ai-message-bubble { border-bottom-left-radius: 4px; }
.ai-message.error .ai-message-bubble { background: var(--danger-bg); border: 1px solid var(--danger); color: var(--danger-text); box-shadow: none; }
.ai-message-bubble pre {
  background: var(--bg-code);
  color: var(--code-text);
  padding: 10px;
  border-radius: var(--radius-md);
  overflow-x: auto;
  font-size: 12px;
  font-family: 'Courier New', monospace;
  white-space: pre-wrap;
  margin: 8px 0;
}
.ai-message-actions { margin-top: 6px; display: flex; gap: 8px; }
.ai-message-actions button {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  padding: 4px 10px;
  background: var(--bg-btn);
  color: var(--text-primary);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.1s ease;
}
.ai-message-actions button:hover { background: var(--bg-btn-hover); }
.ai-message-actions button:active { transform: scale(0.96); }

/* Three bouncing dots shown in the Assistant's bubble while waiting on a
   reply, instead of a static "Thinking…" label with no sense of progress. */
.ai-typing-indicator { display: inline-flex; align-items: center; gap: 4px; padding: 2px 0; }
.ai-typing-indicator span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-data-muted);
  animation: aiTypingBounce 1.1s ease-in-out infinite;
}
.ai-typing-indicator span:nth-child(2) { animation-delay: 0.15s; }
.ai-typing-indicator span:nth-child(3) { animation-delay: 0.3s; }
@keyframes aiTypingBounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-4px); opacity: 1; }
}

.ai-chat-input-row { display: flex; gap: 8px; padding: 12px; background: var(--bg-panel); border-top: 1px solid var(--border-soft); }
.ai-chat-input {
  flex: 1;
  padding: 8px 10px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--bg-input);
  color: var(--text-primary);
  font-family: 'Courier New', monospace;
  font-size: 12.5px;
  resize: vertical;
  transition: border-color 0.15s ease;
}
.ai-chat-input:focus { outline: none; border-color: var(--accent-2); }

/* HDL Generator tab */
.hdl-layout { flex: 1; display: flex; min-height: 0; }
.hdl-fields-panel {
  flex: 0 0 340px;
  overflow: auto;
  border-right: 1px solid var(--border-soft);
  background: var(--bg-app);
  padding: 10px 14px;
}
.hdl-field-preset-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.hdl-preset-btn {
  font-size: 11.5px;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-soft);
  background: var(--bg-btn);
  color: var(--text-secondary);
  cursor: pointer;
}
.hdl-preset-btn:hover { background: var(--bg-btn-hover); }
.hdl-preset-btn.active {
  border-color: var(--accent);
  color: var(--text-on-accent);
  background: var(--accent);
}
.hdl-file-panel {
  flex: 1;
  overflow: auto;
  background: var(--bg-app);
  padding: 10px 14px;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.hdl-file-panel h3 { margin: 4px 0 8px; font-size: 13px; color: var(--text-secondary); }
.hdl-field-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 0;
  font-size: 12px;
  color: var(--text-primary);
}
.hdl-field-row input[type="checkbox"] { margin: 0; }
.hdl-field-row .hdl-dff-required { color: var(--warning-text); font-size: 10px; }
.hdl-field-row .hdl-dff-segment-meta { color: var(--text-muted); font-size: 10.5px; }
/* Clickable variant of the same badge (lookup-type only) - a real <button>
   under the hood so it's keyboard-focusable, but visually just the badge
   text plus an underline-on-hover affordance rather than looking like a
   distinct control that would clutter this already-dense checklist. */
button.hdl-lookup-values-btn {
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  color: var(--text-muted); font-size: 10.5px; font-family: inherit;
}
button.hdl-lookup-values-btn:hover { color: var(--accent-2); text-decoration: underline; }
.hdl-lookup-values-list {
  margin: 0 0 4px 18px;
  padding: 6px 8px;
  border-left: 2px solid var(--border-soft);
  font-size: 11px;
  color: var(--text-secondary);
  max-height: 160px;
  overflow: auto;
}
.hdl-lookup-value-row { display: flex; gap: 8px; padding: 1px 0; }
.hdl-lookup-value-code { flex: 0 0 auto; min-width: 90px; font-family: 'Courier New', monospace; color: var(--text-data); }
.hdl-lookup-value-meaning { color: var(--text-muted); }

/* "Supported unique keys" panel - see loadUserKeyGroups in renderer.js.
   Sits above the attribute checklist since it explains a prerequisite
   (which attributes must ALL have real values together) that the
   per-attribute "(user key)" badges below can only hint at individually. */
.hdl-userkeys-panel { margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--border-soft); }
.hdl-userkeys-panel h3 { margin: 0 0 6px; font-size: 13px; color: var(--text-secondary); }
.hdl-userkeys-panel .hint { font-size: 11px; margin: 0 0 8px; color: var(--text-muted); }
.hdl-userkey-row { display: flex; flex-direction: column; gap: 1px; padding: 4px 0; font-size: 12px; }
.hdl-userkey-row:not(:last-child) { border-bottom: 1px dashed var(--border-soft); }
.hdl-userkey-label { color: var(--text-secondary); font-weight: 600; }
.hdl-userkey-attrs { color: var(--text-data); font-family: 'Courier New', monospace; font-size: 11.5px; }
.hdl-batch-list { flex: 0 0 auto; max-height: 160px; overflow: auto; margin-bottom: 8px; }
.hdl-batch-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  margin-bottom: 6px;
  background: var(--bg-panel);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  font-size: 12px;
  color: var(--text-primary);
}
.hdl-batch-item button {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  padding: 3px 8px;
  background: var(--danger);
  color: var(--text-on-accent);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s ease;
}
.hdl-batch-item button:hover { background: var(--danger-hover); }
.hdl-generate-row { display: flex; gap: 8px; margin-bottom: 12px; }

.hdl-spreadsheet-toolbar { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.hdl-spreadsheet-toolbar select {
  padding: 5px 7px;
  font-size: 12px;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  background: var(--bg-input);
  color: var(--text-primary);
}
#hdl-spreadsheet-paste-wrap { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
#hdl-spreadsheet-paste {
  width: 100%;
  box-sizing: border-box;
  font-family: 'Courier New', monospace;
  font-size: 11.5px;
  padding: 6px 8px;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  background: var(--bg-input);
  color: var(--text-primary);
  resize: vertical;
}
#hdl-spreadsheet-summary { font-size: 11px; margin: 0 0 12px; color: var(--text-muted); }

/* Sits directly under its attribute's .hdl-field-row, indented to read as
   that row's own sub-control rather than a new checklist entry. */
.hdl-field-source-row {
  display: flex;
  gap: 6px;
  margin: 0 0 4px 22px;
}
.hdl-field-source-row select,
.hdl-field-source-row input {
  padding: 3px 6px;
  font-size: 11px;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  background: var(--bg-input);
  color: var(--text-primary);
}
.hdl-field-source-row select { flex: 0 0 auto; }
.hdl-field-source-row input { flex: 1; }
.hdl-field-source-row input:disabled { color: var(--text-muted); font-style: italic; }
.hdl-preview-legend {
  display: flex;
  gap: 14px;
  font-size: 10.5px;
  margin-bottom: 4px;
}
.hdl-preview-legend .hdl-preview-required::before,
.hdl-preview-legend .hdl-preview-flex::before {
  content: '\25A0';
  margin-right: 4px;
}
.hdl-preview {
  flex: 1;
  overflow: auto;
  background: var(--bg-code);
  color: var(--code-text);
  font-family: 'Courier New', monospace;
  font-size: 11.5px;
  padding: 10px;
  border-radius: var(--radius-sm);
  white-space: pre;
  margin: 0;
}
.hdl-preview-required { color: var(--warning-text); font-weight: 700; }
.hdl-preview-flex { color: var(--accent-2); }

.hdl-dff-panel { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border-soft); }
.hdl-dff-panel h3 { margin: 0 0 6px; font-size: 13px; color: var(--text-secondary); }
.hdl-dff-panel .hint { font-size: 11px; margin: 0 0 8px; color: var(--text-muted); }
.hdl-dff-toolbar { display: flex; gap: 6px; margin-bottom: 6px; }
.hdl-dff-toolbar input {
  flex: 1;
  padding: 5px 7px;
  font-size: 12px;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  background: var(--bg-input);
  color: var(--text-primary);
}
.hdl-dff-toolbar input:focus { outline: none; border-color: var(--accent); }
#hdl-dff-object-hint { font-size: 11px; margin: 0 0 6px; color: var(--warning-text); }
#hdl-dff-suggestions { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
#hdl-dff-suggestions button { font-size: 11px; }
.hdl-dff-context-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 0;
  font-size: 12px;
  color: var(--text-primary);
  cursor: pointer;
}
.hdl-dff-context-row input[type="radio"] { margin: 0; }
.hdl-dff-segment-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 0 3px 16px;
  font-size: 11.5px;
  color: var(--text-secondary);
}
.hdl-dff-segment-row .hdl-dff-segment-meta { color: var(--text-muted); font-size: 10.5px; }
.hdl-dff-segment-row .hdl-dff-required { color: var(--warning-text); font-size: 10px; }

/* HDL Load tab */
.hdlload-card {
  margin: 0 12px 12px;
  padding: 12px;
  background: var(--bg-panel);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 480px;
}
.hdlload-card label { font-size: 11.5px; color: var(--text-secondary); }
.hdlload-card input[type="text"],
.hdlload-card input[type="number"] {
  padding: 5px 7px;
  font-size: 12px;
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  background: var(--bg-input);
  color: var(--text-primary);
}
.hdlload-card input[type="text"]:focus,
.hdlload-card input[type="number"]:focus { outline: none; border-color: var(--accent); }
.hdlload-advanced { font-size: 11.5px; color: var(--text-secondary); }
.hdlload-advanced summary { cursor: pointer; }
.hdlload-advanced label,
.hdlload-advanced input { margin-top: 6px; }
.hdlload-warning {
  font-size: 11px;
  color: var(--warning-text);
  padding: 6px 8px;
  border: 1px solid var(--warning-text);
  border-radius: var(--radius-sm);
}
.hdlload-phase-banner {
  margin: 12px;
  padding: 10px 14px;
  font-size: 12.5px;
  font-weight: 600;
  border-radius: var(--radius-sm);
  background: var(--callout-warn-bg);
  border: 1px solid var(--callout-warn-border);
  color: var(--callout-warn-text);
}
.hdlload-phase-banner.error { background: var(--danger-bg); border-color: var(--danger); color: var(--danger-text); }
.hdlload-phase-banner.done { background: var(--success-bg); border-color: var(--success-text); color: var(--success-text); }
.hdlload-actions { display: flex; gap: 8px; align-items: center; margin-top: 4px; }
.hdlload-progress-wrap {
  margin: 0 12px 12px;
  padding: 10px 12px;
  background: var(--bg-panel);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 480px;
}
.hdlload-progress-track {
  height: 6px;
  border-radius: 3px;
  background: var(--bg-input);
  overflow: hidden;
}
.hdlload-progress-fill {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--accent-2), var(--accent));
  border-radius: 3px;
  transition: width 0.3s ease;
}
#hdlload-status.hdlload-status-error { color: var(--danger-text); }
#btn-hdlload-load { background: var(--accent-2); color: var(--text-on-accent); }
#btn-hdlload-load:hover:not(:disabled) { background: var(--accent-2-hover); }
#btn-hdlload-rollback,
#btn-hdlload-stop { background: var(--danger-bg); color: var(--danger-text); }
#btn-hdlload-rollback:hover,
#btn-hdlload-stop:hover { background: var(--danger); color: var(--text-on-accent); }
.hdlload-confirm-input {
  width: 100%;
  box-sizing: border-box;
  padding: 6px 8px;
  margin-bottom: 12px;
  font-size: 13px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--text-primary);
}

/* ==========================================================================
   eText Builder - a 4-step wizard (Samples -> Sections -> Map Fields ->
   Review & Generate) for building real Oracle eText RTF templates without
   needing to know RTF or Oracle's directive syntax. Uses the "Data" token
   set for dense content (field chips, rulers, tables) and regular panel
   tokens for the wizard chrome (cards, stepper, buttons) so it reads as a
   guided flow rather than a raw data-editing surface.
   ========================================================================== */
.etext-panel { padding-bottom: 24px; overflow-y: auto; }

/* ---- Intro: icon badge + eyebrow + trust chip, a small branding moment
   instead of two plain lines of text. ---- */
.etext-intro { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-4) var(--space-5) var(--space-3); }
.etext-intro-icon {
  flex-shrink: 0; width: 36px; height: 36px; border-radius: var(--radius-lg);
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-data-hover); border: 1px solid var(--border-data); color: var(--accent-2);
}
.etext-intro-icon .icon { width: 19px; height: 19px; }
.etext-intro-body { min-width: 0; }
.etext-intro-eyebrow { font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-2); margin-bottom: 2px; }
.etext-intro-title { font-size: 17px; font-weight: 700; color: var(--text-primary); letter-spacing: -0.01em; }
.etext-intro-sub { font-size: 12.5px; color: var(--text-muted); line-height: 1.55; margin-top: 3px; max-width: 640px; }
.etext-trust-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 600; color: var(--text-muted);
  background: var(--bg-btn); border: 1px solid var(--border-soft);
  padding: 3px 9px 3px 7px; border-radius: var(--radius-pill); margin-top: var(--space-2);
}
.etext-trust-chip .icon { width: 12px; height: 12px; color: var(--text-faint); }

/* ---- Stepper: always visible, shows progress, completed steps are
   clickable to jump back and re-edit without losing later work. A held
   ring on the active node + a one-line subtitle per step + a gradient
   connector into the active step make this legibly bespoke instead of a
   generic tutorial-wizard component. ---- */
.etext-stepper {
  display: flex; align-items: flex-start; padding: var(--space-4) var(--space-5);
  background: var(--bg-panel-2); border-bottom: 1px solid var(--border);
  overflow-x: auto; flex-shrink: 0;
}
.etext-step-pill { display: flex; align-items: flex-start; gap: var(--space-2); padding: 4px 6px; border-radius: var(--radius-md); transition: background 0.12s ease; }
.etext-step-pill.clickable { cursor: pointer; }
.etext-step-pill.clickable:hover { background: var(--bg-hover); }
.etext-step-pill.clickable:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-step-number {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums;
  border: 2px solid var(--border-strong); color: var(--text-muted); background: var(--bg-panel);
  transition: background 0.16s ease, border-color 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
}
.etext-step-number .icon { width: 15px; height: 15px; stroke-width: 3; }
.etext-step-pill.active .etext-step-number {
  border-color: var(--accent); background: var(--accent); color: var(--text-on-accent);
  box-shadow: 0 0 0 3px var(--bg-panel-2), 0 0 0 5px var(--accent-tint);
}
.etext-step-pill.completed .etext-step-number { border-color: var(--success); background: var(--success); color: var(--text-on-accent); }
.etext-step-text { display: flex; flex-direction: column; gap: 1px; padding-top: 1px; }
.etext-step-label { font-size: 12.5px; font-weight: 700; color: var(--text-muted); white-space: nowrap; line-height: 1.3; }
.etext-step-pill.active .etext-step-label, .etext-step-pill.completed .etext-step-label { color: var(--text-primary); }
.etext-step-pill.clickable:hover .etext-step-label { color: var(--link); }
.etext-step-sub { font-size: 10.5px; color: var(--text-faint); white-space: nowrap; line-height: 1.3; }
.etext-step-pill.active .etext-step-sub { color: var(--text-muted); }
@media (max-width: 1000px) { .etext-step-sub { display: none; } }
.etext-step-connector { flex: 1; height: 2px; background: var(--border); margin: 15px 12px 0; min-width: 20px; border-radius: 1px; align-self: flex-start; }
.etext-step-connector.completed { background: var(--success); }
.etext-step-connector.completed:has(+ .etext-step-pill.active) { background: linear-gradient(to right, var(--success), var(--accent)); }

/* ---- Cards: the wizard's basic content unit. Title is now a real header
   bar (background + bottom border) instead of floating text, matching
   the idiom .etext-mapping-header/.etext-stepper already use. ---- */
.etext-card { position: relative; background: var(--bg-panel); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-card); margin: 0 var(--space-5) var(--space-5); overflow: hidden; }
.etext-card-title {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: 14px; font-weight: 700; color: var(--text-primary);
  padding: var(--space-3) var(--space-4); margin: 0;
  background: var(--bg-panel-2); border-bottom: 1px solid var(--border-soft);
}
.etext-card-title .etext-card-title-sub { font-size: 11.5px; font-weight: 400; color: var(--text-muted); margin-left: 2px; }
.etext-card-help { font-size: 12px; color: var(--text-muted); line-height: 1.55; padding: var(--space-3) var(--space-4) var(--space-4); max-width: 900px; }
.etext-card-help strong { color: var(--text-secondary); font-weight: 600; }
.etext-required { color: var(--danger-text); font-weight: 700; }

.etext-field-group { padding: 0 var(--space-5) var(--space-4); }
.etext-field-group-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-faint); margin-bottom: var(--space-2); }

/* ---- Universal native control base - checkboxes & radios only. Nothing
   else in the file styles these, so this is safe as a blanket rule. The
   checked radio's dot is drawn as an independent foreground shape (not a
   backdrop-color mask) so it looks right on every surface it sits on -
   plain rows, tinted tiles, data rows - not just --bg-panel. ---- */
.etext-panel input[type="checkbox"],
.etext-panel input[type="radio"] {
  appearance: none; -webkit-appearance: none;
  width: 16px; height: 16px; margin: 0; flex-shrink: 0;
  border: 1.5px solid var(--border-strong); background-color: var(--bg-input); cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.etext-panel input[type="checkbox"] { border-radius: 4px; }
.etext-panel input[type="radio"] { border-radius: 50%; }
.etext-panel input[type="checkbox"]:hover, .etext-panel input[type="radio"]:hover { border-color: var(--accent); }
.etext-panel input[type="checkbox"]:checked, .etext-panel input[type="radio"]:checked { border-color: var(--accent); background-color: var(--accent); }
.etext-panel input[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20,6 9,17 4,12'/%3E%3C/svg%3E");
  background-size: 11px 11px; background-position: center; background-repeat: no-repeat;
}
.etext-panel input[type="radio"]:checked {
  background-image: radial-gradient(circle at center, var(--text-on-accent) 28%, transparent 30%);
}
.etext-panel input[type="checkbox"]:disabled, .etext-panel input[type="radio"]:disabled { opacity: 0.5; cursor: not-allowed; }
.etext-panel input[type="checkbox"]:focus-visible, .etext-panel input[type="radio"]:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* ---- Output-layout radio tiles. Hover gets a neutral border/background
   bump only - the accent border + tint fill is earned exclusively by the
   checked state, so "pointing at" and "picked" read as different things. ---- */
.etext-radio-tiles { display: flex; gap: var(--space-3); flex-wrap: wrap; }
.etext-radio-tile {
  position: relative; flex: 1; min-width: 200px; max-width: 320px;
  display: flex; flex-direction: column; gap: 4px;
  padding: var(--space-3) 36px var(--space-3) var(--space-4);
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  background: var(--bg-btn); cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, box-shadow 0.12s ease;
}
.etext-radio-tile:hover { border-color: var(--border-strong); background: var(--bg-btn-hover); }
.etext-radio-tile input { position: absolute; top: 14px; right: 14px; }
.etext-radio-tile:has(input:checked) { border-color: var(--accent); background: var(--accent-tint); }
.etext-radio-tile:has(input:focus-visible) { box-shadow: var(--focus-ring); }
.etext-radio-tile-title { font-size: 12.5px; font-weight: 700; color: var(--text-primary); }
.etext-radio-tile-desc { font-size: 11px; color: var(--text-muted); line-height: 1.45; }
.etext-delimiter-row { display: flex; align-items: center; gap: var(--space-2); font-size: 12.5px; color: var(--text-secondary); margin-top: var(--space-2); }
.etext-delimiter-row select, .etext-delimiter-row input[type="text"] {
  padding: 5px 8px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--bg-input); color: var(--text-primary); font-size: 12px;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.etext-delimiter-row select:focus, .etext-delimiter-row input[type="text"]:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }

/* ---- Buttons: primary/secondary keep their existing roles, a new ghost
   tier is used exclusively for "Back" so it never competes with a step's
   real action. All three get an icon slot + a real focus ring. ---- */
.etext-step-actions { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-1) var(--space-5) var(--space-4); flex-wrap: wrap; }
.etext-btn-primary, .etext-btn-secondary, .etext-btn-ghost {
  display: inline-flex; align-items: center; gap: 7px;
  border-radius: var(--radius-md); font-weight: 700; cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease, color 0.12s ease;
}
.etext-btn-primary .icon, .etext-btn-secondary .icon, .etext-btn-ghost .icon { width: 15px; height: 15px; flex-shrink: 0; }
.etext-btn-primary.back .icon, .etext-btn-secondary.back .icon, .etext-btn-ghost.back .icon { transform: scaleX(-1); }
.etext-btn-primary { background: var(--accent); color: var(--text-on-accent); border: 1px solid var(--accent); padding: 9px 20px; font-size: 13px; }
.etext-btn-primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }
.etext-btn-primary:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-btn-primary:disabled { opacity: 0.5; cursor: default; }
.etext-btn-secondary { background: var(--bg-btn); color: var(--text-primary); border: 1px solid var(--border-strong); padding: 8px 16px; font-size: 12.5px; }
.etext-btn-secondary:hover:not(:disabled) { background: var(--bg-btn-hover); }
.etext-btn-secondary:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-btn-secondary:disabled { opacity: 0.5; cursor: default; }
.etext-btn-ghost { background: transparent; border: 1px solid transparent; color: var(--text-muted); padding: 8px var(--space-3); font-size: 12.5px; }
.etext-btn-ghost:hover { background: var(--bg-hover); color: var(--text-primary); }
.etext-btn-ghost:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-inline-status { font-size: 12px; color: var(--text-muted); line-height: 1.4; display: flex; align-items: center; gap: 6px; }

/* ---- Dev Options: a quiet icon trigger + popover instead of a raw
   checkbox sitting on the primary form. The anchor is a SIBLING of
   .etext-card (not a descendant) so the popover escapes the card's own
   overflow:hidden entirely; #etext-step-1 (position:relative) is its
   positioning context. ---- */
.etext-devlog-anchor { position: absolute; top: 14px; right: var(--space-5); z-index: 5; }
.etext-devlog-trigger {
  width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: var(--radius-md);
  background: var(--bg-panel); color: var(--text-faint); cursor: pointer;
}
.etext-devlog-trigger .icon { width: 15px; height: 15px; }
.etext-devlog-trigger:hover { background: var(--bg-hover); color: var(--text-secondary); }
.etext-devlog-trigger[aria-expanded="true"] { background: var(--bg-btn); color: var(--text-primary); border-color: var(--border-strong); }
.etext-devlog-trigger:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-devlog-popover {
  position: absolute; top: calc(100% + var(--space-2)); right: 0; z-index: 20;
  width: 270px; padding: var(--space-4);
  background: var(--bg-elevated); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-dropdown);
}
.etext-devlog-popover.hidden { display: none; }
.etext-devlog-toggle { display: flex; align-items: center; gap: var(--space-2); font-size: 11.5px; color: var(--text-secondary); cursor: pointer; padding: 0; }
.etext-devlog-hint { font-size: 10.5px; color: var(--text-faint); line-height: 1.4; margin-top: 6px; }
.etext-devlog-actions { display: flex; flex-direction: column; gap: 6px; margin-top: var(--space-3); }
.etext-devlog-btn {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--text-muted); background: transparent;
  border: 1px solid var(--border-soft); border-radius: var(--radius-sm);
  padding: 6px 9px; cursor: pointer; transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
.etext-devlog-btn .icon { width: 12px; height: 12px; }
.etext-devlog-btn:hover { color: var(--text-secondary); border-color: var(--border-strong); background: var(--bg-hover); }
.etext-devlog-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* ---- Result / status banners: neutral card + colored left rule + icon
   chip, so a result reads by shape/icon first and color second - and no
   longer double-shadows since it always renders inside an already-
   shadowed .etext-card. ---- */
.etext-result-banner {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: 13px var(--space-4) 13px 14px; border-radius: var(--radius-md);
  font-size: 12.5px; line-height: 1.55; margin: var(--space-1) var(--space-5) var(--space-4);
  background: var(--bg-panel); border: 1px solid var(--border); border-left: 3px solid var(--border-strong);
}
.etext-result-banner-icon {
  flex-shrink: 0; width: 24px; height: 24px; border-radius: 50%; margin-top: 1px;
  display: flex; align-items: center; justify-content: center;
}
.etext-result-banner-icon .icon { width: 14px; height: 14px; stroke-width: 2.5; }
.etext-result-banner-body { flex: 1; min-width: 0; color: var(--text-secondary); }
.etext-result-banner.success { border-left-color: var(--success); }
.etext-result-banner.success .etext-result-banner-icon { background: var(--success-bg); color: var(--success-text); }
.etext-result-banner.error { border-left-color: var(--danger); }
.etext-result-banner.error .etext-result-banner-icon { background: var(--danger-bg); color: var(--danger-text); }
.etext-result-banner.info { border-left-color: var(--callout-warn-border); }
.etext-result-banner.info .etext-result-banner-icon { background: var(--callout-warn-bg); color: var(--callout-warn-text); }
.etext-result-banner-title { font-weight: 700; margin-bottom: 3px; color: var(--text-primary); }
.etext-result-banner-path {
  font-family: 'Courier New', monospace; font-size: 11.5px; word-break: break-all; margin-top: 6px;
  padding: 6px 8px; background: var(--bg-data); border: 1px solid var(--border-data); border-radius: var(--radius-sm);
  color: var(--text-data-muted);
}

.etext-input-row { display: flex; gap: var(--space-4); padding: var(--space-1) var(--space-5) var(--space-4); }
.etext-input-col { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.etext-input-label-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 5px; }
.etext-input-label-row label { font-size: 12px; font-weight: 700; color: var(--text-secondary); }
.etext-input-label-row-actions { display: flex; align-items: center; gap: var(--space-2); }

/* "+ Function" trigger next to Target Output - drag one of these onto the
   textarea like an XML tree leaf instead of typing an expression from
   memory into Step 3. position:fixed (with top/left computed in JS from
   the button's own rect on open) rather than an ancestor-relative popover,
   since Step 1's card has overflow:hidden for its rounded corners and
   would otherwise clip this the same way an earlier review caught for the
   Dev Options popover. */
.etext-function-anchor { position: relative; }
.etext-function-popover {
  position: fixed; z-index: 50;
  width: 320px; max-height: 70vh; overflow-y: auto;
  padding: var(--space-4);
  background: var(--bg-elevated); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-dropdown);
}
.etext-function-popover.hidden { display: none; }
.etext-function-popover .etext-cheatsheet-section { margin-top: var(--space-2); }
.etext-function-popover .etext-cheatsheet-section:first-child { margin-top: 0; }

/* Aggregate parameter picker (see openAggregateParamPicker) - a real
   overlay+modal (not a popover) since it needs its own drop target and a
   confirm/cancel step, unlike a simple insert-on-drag chip. */
.etext-modal-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0, 0, 0, 0.4);
  display: flex; align-items: center; justify-content: center;
}
.etext-modal {
  width: 380px; max-width: calc(100vw - 40px);
  padding: var(--space-5);
  background: var(--bg-elevated); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-dropdown);
}
.etext-modal-title { font-size: 14px; font-weight: 700; color: var(--text-primary); margin-bottom: var(--space-2); }
.etext-modal-help { font-size: 12px; color: var(--text-secondary); line-height: 1.4; margin-bottom: var(--space-3); }
.etext-modal-input {
  width: 100%; box-sizing: border-box; font-size: 13px; padding: 8px 10px;
  border: 1px solid var(--border-data-strong); border-radius: var(--radius-sm);
  background: var(--bg-data-alt); color: var(--text-data);
}
.etext-modal-input:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.etext-modal-preview { font-size: 11.5px; color: var(--text-faint); margin-top: var(--space-2); min-height: 15px; }
.etext-modal-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-4); }
#etext-xml-input, #etext-output-input {
  width: 100%;
  box-sizing: border-box;
  height: 168px;
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  font-size: 11.5px;
  line-height: 1.5;
  font-family: 'Courier New', monospace;
  background: var(--bg-input);
  color: var(--text-primary);
  resize: vertical;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
#etext-xml-input:focus, #etext-output-input:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
#etext-output-input.etext-drag-over { border-color: var(--accent); box-shadow: var(--focus-ring); background: var(--bg-data-hover); }

.etext-workspace { padding: 0; }
.etext-step-title {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-data-muted);
  margin: var(--space-5) 0 var(--space-2);
}

/* ---- Live Preview shell: the schema tree + output-lines panel share one
   collapsible container instead of competing as two separate boxes right
   under the primary textareas - both still auto-reveal the instant
   either gets real content (this is live feedback, never gated behind a
   click), only the MANUAL collapse (once already shown) is optional. ---- */
.etext-live-preview { margin: 0 var(--space-5) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg-panel-2); overflow: hidden; }
.etext-live-preview.hidden { display: none; }
/* The XML schema tree/error/size-notice box sits directly inside the
   Sample XML column (see #tab-etext markup and updateXmlColumnView in
   renderer.js) - in place of the textarea once a real tree is available
   and the user hasn't asked to see raw XML instead - so the draggable
   tree ends up right next to Target Output with nothing to scroll past
   in between. It's independent of the (separate, full-width) "Live
   preview" wrapper below, which now holds only the Output-lines-detected
   panel - bundling that panel into this same swap was a real bug: it made
   "Output lines detected" (needed to configure repeat/nesting per line,
   unrelated to whether Sample XML shows as text or a tree) disappear
   entirely whenever the user toggled back to viewing raw XML. */
.etext-input-col > .etext-xml-schema-preview { margin-top: var(--space-2); flex: 1; overflow-y: auto; min-height: 0; }
.etext-live-preview-toggle {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-2) var(--space-3); background: none; border: none; cursor: pointer;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-faint);
}
.etext-live-preview-toggle:hover { color: var(--text-secondary); }
.etext-live-preview-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-live-preview-toggle .icon { width: 14px; height: 14px; transition: transform .15s ease; }
.etext-live-preview-toggle.collapsed .icon { transform: rotate(-90deg); }
.etext-live-preview-body { padding: 0 var(--space-3) var(--space-3); display: flex; flex-direction: column; gap: var(--space-3); }
.etext-live-preview-body.collapsed { display: none; }

/* Live XML schema preview in Step 1 - parses whatever's currently pasted
   into Sample XML on the fly (no Analyze click needed) so a user can see
   the structure immediately, and offers drafting a starting sample output
   line straight from the XML's own values. */
.etext-xml-schema-preview, .etext-output-lines-preview {
  margin: 0; padding: var(--space-3) 14px;
  border: 1px solid var(--border-data); border-radius: var(--radius-md); background: var(--bg-data-alt);
}
.etext-xml-schema-preview-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-faint); margin-bottom: var(--space-2); }
.etext-xml-schema-preview-error { font-size: 12px; color: var(--danger-text); line-height: 1.4; }

/* Function chips in the "+ Function" popover (see .etext-function-popover
   above) - reuse the XML tree's own draggable-tag look (dashed underline,
   link color) so both read as the same "drag this onto your sample"
   affordance. */
.etext-function-chip { display: inline-block; font-size: 12px; padding: 2px 0; }

/* Step 1's live "Output lines detected" panel - one row per distinct
   output line shape, each with a plain "appears once" vs "repeats for
   each: [level]" choice instead of requiring the user to dig through the
   XML tree to find the right element to mark. */
.etext-line-repeat-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-5);
  padding: var(--space-2) var(--space-3); border: 1px solid var(--border-data); border-radius: var(--radius-sm);
  background: var(--bg-data); margin-top: var(--space-2); transition: border-color 0.12s ease;
}
.etext-line-repeat-row:hover { border-color: var(--border-data-strong); }
.etext-line-repeat-preview { flex: 1 1 260px; min-width: 200px; font-size: 11.5px; }
.etext-line-repeat-meta { color: var(--text-data-faint); margin-bottom: 2px; }
.etext-line-repeat-sample { font-family: 'Courier New', monospace; color: var(--text-data); white-space: pre; overflow-x: auto; }
.etext-line-repeat-controls { display: flex; flex-direction: column; gap: 5px; flex: 1 1 260px; min-width: 240px; }
.etext-line-repeat-option { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text-data); cursor: pointer; }
.etext-line-repeat-select { margin-left: 4px; font-size: 12px; padding: 3px 6px; border: 1px solid var(--border-data-strong); border-radius: var(--radius-sm); background: var(--bg-data-alt); color: var(--text-data); }
.etext-line-repeat-select:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }

/* Real expand/collapse XML tree (mirrors Oracle Template Viewer's own
   "XML Treeview" panel) - one node per element, a clickable chevron for
   containers, a small dot bullet for leaves, repeating sibling tags shown
   once with a xN badge instead of once per instance. */
.etext-xtree-toolbar { display: flex; gap: var(--space-4); margin-bottom: var(--space-2); }
.etext-xtree { font-family: 'Courier New', monospace; font-size: 12.5px; background: var(--bg-data-alt); border: 1px solid var(--border-data); border-radius: var(--radius-sm); padding: 6px 8px; max-height: 360px; overflow: auto; }
.etext-xtree-node { position: relative; }
.etext-xtree-row { display: flex; align-items: center; gap: 6px; padding: 3px 6px; margin: 0 -6px; border-radius: var(--radius-sm); white-space: nowrap; transition: background 0.1s ease; }
.etext-xtree-row:hover { background: var(--bg-hover); }
.etext-xtree-toggle {
  width: 16px; height: 16px; flex: 0 0 16px; display: flex; align-items: center; justify-content: center;
  border: none; background: none; color: var(--text-data-faint); cursor: pointer; padding: 0; border-radius: 3px;
  transition: color 0.12s ease, background 0.12s ease;
}
.etext-xtree-toggle .icon { width: 12px; height: 12px; transition: transform 0.12s ease; }
.etext-xtree-toggle:hover { color: var(--text-data); background: var(--bg-hover); }
.etext-xtree-toggle.expanded .icon { transform: rotate(90deg); }
.etext-xtree-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-xtree-bullet { width: 16px; flex: 0 0 16px; display: flex; align-items: center; justify-content: center; }
.etext-xtree-bullet::before { content: ""; width: 4px; height: 4px; border-radius: 50%; background: var(--text-data-faint); display: block; }
.etext-xtree-tag { color: var(--text-data); }
.etext-xtree-tag.etext-xtree-draggable { cursor: grab; color: var(--link); text-decoration: none; border-bottom: 1px dashed var(--link); }
.etext-xtree-tag.etext-xtree-draggable:hover { color: var(--link-hover); border-bottom-color: var(--link-hover); }
.etext-xtree-tag.etext-xtree-draggable:active { cursor: grabbing; }
.etext-xtree-drag-hint { font-size: 11px; color: var(--text-data-muted); margin-bottom: 6px; }
.etext-xtree-repeat-badge {
  display: inline-flex; align-items: center; font-size: 10px; font-weight: 700;
  padding: 1px 7px; border-radius: var(--radius-pill); margin-left: 2px;
  border: 1px solid var(--border-data-strong); color: var(--accent-2);
}
.etext-xtree-children { margin-left: 7px; padding-left: 12px; border-left: 1px dotted var(--border-data-strong); }
.etext-xtree-children.collapsed { display: none; }
.etext-xtree-mark-repeat {
  display: inline-flex; align-items: center; gap: 4px; border: none; background: none; cursor: pointer;
  padding: 2px 6px 2px 2px; border-radius: 3px; font-size: 10.5px; color: var(--text-data-faint);
  transition: color 0.12s ease, background 0.12s ease;
}
.etext-xtree-mark-repeat .icon { width: 12px; height: 12px; }
.etext-xtree-mark-repeat:hover { color: var(--accent-2); background: var(--bg-hover); }
.etext-xtree-mark-repeat.marked { color: var(--accent-2); font-weight: 700; }
.etext-xtree-marked-hint {
  display: flex; align-items: center; gap: var(--space-2); font-size: 11px;
  color: var(--accent-2); background: var(--bg-data-alt);
  border: 1px solid var(--border-data); border-radius: var(--radius-sm);
  padding: 5px 8px; margin-bottom: 6px;
}

/* Step 3's interactive mapping tree - same base look as the read-only
   Step 1 preview, plus states for "this section's own element",
   "already placed", "not reachable", and a small ref-path hint next to
   any leaf whose computed reference isn't just a bare field name. A
   small check icon (not just strikethrough) is the primary "used" cue -
   strikethrough alone reads as "disabled", not "used". A placed leaf
   stays fully interactive (same color/cursor/hover as an unplaced one)
   because the same source field is a legitimate, common thing to map
   into a section more than once (e.g. one date field formatted two
   different ways in two output columns) - it must never look or behave
   disabled, only "unreachable" (no valid path at all) actually is. */
.etext-mapping-tree-wrap { margin-bottom: var(--space-4); }
.etext-mapping-tree { max-height: 320px; }
.etext-xtree-current { font-weight: 700; color: var(--accent-2); }
.etext-xtree-leaf-clickable { cursor: pointer; border-radius: 3px; padding: 0 3px; color: var(--text-data); }
.etext-xtree-leaf-clickable:hover { background: var(--bg-data-hover); color: var(--text-data); }
.etext-xtree-leaf-clickable.placed { cursor: pointer; }
.etext-xtree-leaf-clickable.placed:hover { background: var(--bg-data-hover); }
.etext-xtree-leaf-clickable.unreachable { color: var(--text-data-faint); opacity: 0.5; font-style: italic; cursor: not-allowed; }
.etext-xtree-leaf-clickable.unreachable:hover { background: none; }
.etext-xtree-check { width: 12px; height: 12px; color: var(--success-text); flex-shrink: 0; margin-right: 1px; }
.etext-xtree-ref-hint { color: var(--text-data-faint); font-size: 10.5px; font-family: 'Courier New', monospace; }

/* Schema/output preview lists shown right after Analyze - a clear
   reference of exactly what fields are available and what the sample
   output looks like, before the user starts dragging anything. */
.etext-schema-list, .etext-output-list { display: flex; flex-direction: column; gap: 5px; }
.etext-schema-group, .etext-output-line-preview {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-data);
  border-radius: var(--radius-sm);
  background: var(--bg-data-alt);
  font-size: 11.5px;
}
.etext-schema-group-name { font-weight: 700; color: var(--text-data); font-family: 'Courier New', monospace; }
.etext-schema-group-count { color: var(--text-data-faint); font-weight: 400; }
.etext-schema-group-fields { color: var(--text-data-muted); font-family: 'Courier New', monospace; margin-top: 3px; line-height: 1.5; word-break: break-all; }
.etext-output-line-preview-meta { color: var(--text-data-muted); margin-bottom: 3px; }
.etext-output-line-preview-sample { font-family: 'Courier New', monospace; color: var(--text-data); white-space: pre; overflow-x: auto; }
.etext-collapsible-toggle {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11.5px; font-weight: 600; color: var(--link); cursor: pointer;
  background: none; border: none; padding: 0; margin-top: 6px;
}
.etext-collapsible-toggle .icon { width: 12px; height: 12px; transition: transform .12s ease; }
.etext-collapsible-toggle:hover { color: var(--link-hover); }
.etext-collapsible-toggle.expanded .icon { transform: rotate(180deg); }
.etext-collapsible-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: 3px; }

/* Delimited-mode mapping: a row of segment "slots" instead of a
   character-position ruler - dragging (or clicking, see .etext-slot-hint)
   a field onto a slot assigns it that position in field emission order
   (delimited output has no fixed character positions, only field
   sequence). Bigger + a visible "drop here" hint on empty slots so the
   interaction is obvious without having to be told about drag-and-drop. */
/* Delimited-mode "slots": occupied = success family (a completed micro-
   task), drag-over = accent (a live "drop here now" moment) - the two no
   longer look identical the way a single accent color used to make them. */
.etext-slots { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.etext-slot {
  min-width: 112px; padding: var(--space-2) var(--space-3); text-align: center;
  border: 2px dashed var(--border-data-strong); border-radius: var(--radius-md); background: var(--bg-data);
  cursor: pointer; transition: border-color 0.12s ease, background 0.12s ease, box-shadow 0.12s ease;
}
.etext-slot:hover { border-color: var(--accent); background: var(--accent-soft); }
.etext-slot.occupied { border-style: solid; border-color: var(--success); background: var(--success-bg); cursor: default; }
.etext-slot.drag-over { border-style: solid; border-color: var(--accent); background: var(--accent-tint); box-shadow: var(--focus-ring); }
.etext-slot-index { font-size: 9.5px; color: var(--text-data-faint); font-weight: 700; }
.etext-slot-sample { font-size: 10px; color: var(--text-data-faint); font-family: 'Courier New', monospace; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.etext-slot-field { font-size: 12.5px; font-weight: 700; color: var(--text-data); font-family: 'Courier New', monospace; }
.etext-slot-hint { font-size: 10.5px; color: var(--text-data-faint); font-style: italic; }

/* ---- Editable field-name label (see renderEditableFieldName in
   renderer.js) - used wherever a placed field's XML reference shows up in
   Step 3's visual mapping area (delimited slots, the ruler's legend), not
   just in the field table below it. The pencil button stays invisible
   until the label is hovered (or keyboard-focused) so plain browsing
   doesn't look cluttered with edit affordances everywhere. ---- */
.etext-field-editable { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
.etext-field-editable-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 200px; }
.etext-field-edit-btn {
  flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; padding: 0; border: none; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-data-faint); cursor: pointer;
  opacity: 0; transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.etext-field-edit-btn .icon { width: 12px; height: 12px; }
.etext-field-edit-btn:hover { background: var(--bg-hover); color: var(--text-data); }
.etext-field-editable:hover .etext-field-edit-btn, .etext-field-edit-btn:focus-visible { opacity: 1; }
.etext-field-edit-input {
  font-family: 'Courier New', monospace; font-size: 12px;
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  background: var(--bg-input); color: var(--text-primary);
  padding: 2px 4px; width: 100%; min-width: 90px; box-sizing: border-box;
}
.etext-field-edit-input:focus { outline: none; box-shadow: var(--focus-ring); }
.etext-ruler-legend-label { font-size: 11px; color: var(--text-data-muted); }

/* ---- Section cards - one per detected record type. Header bar carries
   the Include toggle/name/kind badge; "ignored" is a dashed border + dim
   content, not a flat opacity wash over the whole card (which used to
   make the checkbox itself half as clickable-looking as everything else). ---- */
.etext-section-card { border: 1px solid var(--border-data); border-radius: var(--radius-lg); background: var(--bg-data-alt); margin-bottom: var(--space-3); overflow: hidden; }
.etext-section-card.ignored { border-style: dashed; }
.etext-section-card.ignored .etext-section-card-top,
.etext-section-card.ignored .etext-section-fields-grid { opacity: 0.55; }
.etext-section-card-top { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4); background: var(--bg-elevated); border-bottom: 1px solid var(--border-data); }
.etext-section-card-top input[type="text"] {
  flex: 1; min-width: 0; font-weight: 700; font-size: 13px; color: var(--text-primary);
  border: 1px solid transparent; background: transparent; padding: 4px 8px; border-radius: var(--radius-sm);
  transition: border-color .12s ease, background .12s ease;
}
.etext-section-card-top input[type="text"]:hover { border-color: var(--border-data); background: var(--bg-data); }
.etext-section-card-top input[type="text"]:focus { outline: none; border-color: var(--accent); background: var(--bg-data); box-shadow: var(--focus-ring); }
.etext-section-fields-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--space-4); padding: var(--space-4); }
.etext-section-field-label { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-data-faint); margin-bottom: 4px; display: block; }
.etext-section-field-help { font-size: 10px; color: var(--text-data-faint); margin-top: 3px; line-height: 1.4; }
.etext-source-resolved-hint { font-family: 'Courier New', monospace; word-break: break-all; }
.etext-section-row select, .etext-section-card select, .etext-section-card input[type="text"] {
  width: 100%; box-sizing: border-box;
  padding: 5px 8px;
  border: 1px solid var(--border-data-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-data);
  color: var(--text-data);
  font-size: 11.5px;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.etext-section-row select:focus, .etext-section-card select:focus, .etext-section-card input[type="text"]:focus:not(.etext-section-card-top input) { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.etext-section-toggle { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--text-data-muted); cursor: pointer; user-select: none; flex-shrink: 0; }
/* A nested section's card renders physically inside its parent's card,
   indented under a connecting rail, instead of just referencing the
   parent by name in a dropdown - the layout itself shows the <LEVEL>
   hierarchy the generated template will actually have. */
.etext-section-children { margin: 0 0 var(--space-3) 24px; padding: var(--space-3) 0 0 16px; border-left: 2px solid var(--border-data-strong); display: flex; flex-direction: column; gap: var(--space-3); }
.etext-section-children .etext-section-card { background: var(--bg-data); margin-bottom: 0; }
.etext-section-nested-badge { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-data-muted); flex-shrink: 0; }
.etext-empty-msg { display: flex; align-items: center; gap: var(--space-2); font-size: 12px; color: var(--text-muted); line-height: 1.5; padding: var(--space-3) var(--space-4); border: 1px dashed var(--border-strong); border-radius: var(--radius-md); background: var(--bg-btn); }
.etext-empty-msg-icon { flex-shrink: 0; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--bg-panel-2); color: var(--text-faint); }
.etext-empty-msg-icon .icon { width: 16px; height: 16px; }
.etext-empty-msg-text { flex: 1; min-width: 0; }

/* NEW: section-kind badge, and role - only rendered for the exception
   cases (repeating/ignored) - the common "single" case stays unbadged. A
   quiet outlined pill (not a tinted fill) keeps its text at full contrast
   against every theme. */
.etext-kind-badge { display: inline-flex; align-items: center; font-size: 10px; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; padding: 2px 8px; border-radius: var(--radius-pill); flex-shrink: 0; border: 1px solid; }
.etext-kind-badge.kind-repeating { color: var(--accent-2); border-color: var(--accent-2); }
.etext-kind-badge.kind-ignore { color: var(--text-muted); border-color: var(--border-data-strong); font-style: italic; }

/* ---- NEW: Step 2 two-column layout - a wide "Confirm record types"
   column (needs everything visible at once to get nesting right) plus a
   narrow rail (recap + the rarely-used Defined Levels, both tucked behind
   a collapsed-by-default accordion so they don't compete for attention). ---- */
.etext-step2-layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--space-6); align-items: start; padding: 0 var(--space-5); }
@media (max-width: 900px) { .etext-step2-layout { grid-template-columns: 1fr; } }
.etext-step2-main { min-width: 0; display: flex; flex-direction: column; gap: var(--space-5); }
.etext-step2-rail { display: flex; flex-direction: column; gap: var(--space-4); }
.etext-level-budget { padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg-panel); font-size: 11.5px; color: var(--text-muted); }
.etext-level-budget.warn { border-color: var(--callout-warn-border); background: var(--callout-warn-bg); color: var(--callout-warn-text); }

/* ---- NEW: rail accordion - houses the recap + Defined Levels cards,
   collapsed by default. Re-parented .etext-card children are flattened
   (no shadow/border/background of their own) so there's no
   card-inside-a-card look. ---- */
.etext-accordion { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--bg-panel); overflow: hidden; }
.etext-accordion-item + .etext-accordion-item { border-top: 1px solid var(--border-soft); }
.etext-accordion-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; padding: var(--space-3) var(--space-4); background: none; border: none; cursor: pointer;
  font-size: 12px; font-weight: 700; color: var(--text-secondary); text-align: left;
}
.etext-accordion-header:hover { background: var(--bg-hover); }
.etext-accordion-header:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-accordion-header .icon { width: 14px; height: 14px; color: var(--text-faint); transition: transform .15s ease; flex-shrink: 0; }
.etext-accordion-item.open .etext-accordion-header .icon { transform: rotate(90deg); }
.etext-accordion-body { padding: 0 var(--space-4) var(--space-4); }
.etext-accordion-body.collapsed { display: none; }
.etext-accordion-body .etext-card { box-shadow: none; border: none; margin: 0; background: transparent; }
.etext-accordion-body .etext-card-title { background: transparent; border-bottom: none; padding: 0 0 var(--space-1); }
.etext-accordion-body .etext-card-help { padding: 0 0 var(--space-3); }

/* ---- Step 3's mapping section: clickable header that collapses once a
   section is fully mapped (so attention stays on what's left to do, not
   five identical-weight open boxes) - the one sanctioned resting-state
   shadow exception (a momentary hover lift signaling "you can click
   this"), plus a real keyboard path (role=button/tabIndex/aria-expanded,
   set in JS - see renderStep3). ---- */
.etext-mapping-section { border: 1px solid var(--border-data); border-radius: var(--radius-lg); margin-bottom: var(--space-4); background: var(--bg-data-alt); overflow: hidden; }
.etext-mapping-header {
  display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-data); font-size: 14px; font-weight: 700; color: var(--text-data);
  cursor: pointer; transition: background 0.12s ease, box-shadow 0.12s ease;
}
.etext-mapping-header:hover { background: var(--bg-data-hover); box-shadow: var(--shadow-hover); }
.etext-mapping-header:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-mapping-header-tag { font-size: 10.5px; font-weight: 600; color: var(--text-data-faint); font-family: 'Courier New', monospace; }
.etext-mapping-progress { margin-left: auto; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; color: var(--accent-2); background: none; border: 1px solid var(--border-data-strong); padding: 2px var(--space-2); border-radius: var(--radius-pill); flex-shrink: 0; }
.etext-mapping-collapse-icon { width: 14px; height: 14px; color: var(--text-data-faint); transition: transform .15s ease; flex-shrink: 0; }
.etext-mapping-section.collapsed { overflow: visible; }
.etext-mapping-section.collapsed .etext-condition-row, .etext-mapping-section.collapsed .etext-mapping-body { display: none; }
.etext-mapping-section.collapsed .etext-mapping-collapse-icon { transform: rotate(-90deg); }

.etext-condition-row { padding: var(--space-2) var(--space-4); border-bottom: 1px solid var(--border-data); font-size: 11.5px; color: var(--text-data-muted); }
.etext-condition-row-label-line { display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
.etext-condition-help-icon { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; background: transparent; border: 1px solid var(--border-data-strong); color: var(--text-data-faint); font-size: 9.5px; cursor: pointer; flex-shrink: 0; padding: 0; transition: background .12s ease, color .12s ease, border-color .12s ease; }
.etext-condition-help-icon .icon { width: 11px; height: 11px; }
.etext-condition-help-icon:hover { background: var(--bg-hover); color: var(--text-data); border-color: var(--border-strong); }
.etext-condition-help-icon:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-condition-cheatsheet { margin-top: var(--space-2); padding: var(--space-2) var(--space-3); border: 1px solid var(--border-data-strong); border-radius: var(--radius-md); background: var(--bg-data); font-size: 11px; color: var(--text-data-muted); line-height: 1.6; }
.etext-condition-cheatsheet .etext-cheatsheet-section { margin-top: 5px; }
.etext-condition-cheatsheet code { font-family: 'Courier New', monospace; background: var(--bg-data-alt); padding: 1px 4px; border-radius: 3px; color: var(--text-data); }
.etext-function-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-top: 3px; }
.etext-function-row code { flex: 1; word-break: break-word; }
.etext-function-use-btn {
  flex-shrink: 0; font-size: 10.5px; padding: 2px 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-data-strong); background: var(--bg-data-alt); color: var(--text-data); cursor: pointer;
}
.etext-function-use-btn:hover { background: var(--accent); color: var(--text-on-accent); border-color: var(--accent); }
.etext-panel .etext-condition-input { width: 100%; box-sizing: border-box; font-family: 'Courier New', monospace; font-size: 11.5px; padding: 6px 9px; border: 1px solid var(--border-data-strong); border-radius: var(--radius-sm); background: var(--bg-data); color: var(--text-data); transition: border-color .12s ease, box-shadow .12s ease; }
.etext-panel .etext-condition-input:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.etext-mapping-body { padding: var(--space-4); }
.etext-palette-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-data-faint); margin-bottom: 6px; }
.etext-palette { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--space-4); }
.etext-custom-field-row { display: flex; gap: var(--space-2); margin-bottom: var(--space-4); }
.etext-panel .etext-custom-field-input { flex: 1; font-family: 'Courier New', monospace; font-size: 11.5px; padding: 5px 9px; border: 1px solid var(--border-data-strong); border-radius: var(--radius-sm); background: var(--bg-data); color: var(--text-data); transition: border-color .12s ease, box-shadow .12s ease; }
.etext-panel .etext-custom-field-input:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.etext-field-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 12px;
  border: 1px solid var(--border-data-strong);
  border-radius: var(--radius-pill);
  background: var(--bg-data);
  color: var(--text-data);
  font-size: 11px;
  font-family: 'Courier New', monospace;
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease, opacity .12s ease;
}
.etext-field-chip:hover { border-color: var(--accent); background: var(--accent-soft); }
.etext-field-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.etext-field-chip.placed { opacity: 0.5; border-style: dashed; background: transparent; cursor: default; }
.etext-field-chip.placed:hover { border-color: var(--border-data-strong); background: transparent; }

.etext-ruler { overflow-x: auto; padding-bottom: 4px; }
.etext-ruler-numbers { display: flex; font-size: 9px; color: var(--text-data-faint); font-family: 'Courier New', monospace; user-select: none; }
.etext-ruler-cells { display: flex; }
.etext-ruler-cell {
  width: 15px;
  height: 24px;
  flex-shrink: 0;
  border: 1px solid var(--border-data);
  border-left: none;
  font-size: 10px;
  font-family: 'Courier New', monospace;
  color: var(--text-data-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-data);
  cursor: pointer;
  transition: background 0.1s ease, box-shadow 0.1s ease;
}
.etext-ruler-cell:first-child { border-left: 1px solid var(--border-data); }
.etext-ruler-cell:hover { background: var(--bg-hover); }
.etext-ruler-cell.drag-over { box-shadow: var(--focus-ring); background: var(--accent-tint); border-color: var(--accent); }
.etext-ruler-cell.occupied { color: var(--text-on-accent); cursor: default; font-weight: 700; }
.etext-ruler-cell.occupied.field-0 { background: var(--accent); }
.etext-ruler-cell.occupied.field-1 { background: var(--success); }
.etext-ruler-cell.occupied.field-2 { background: var(--callout-warn-text); color: #1b2028; }
.etext-ruler-cell.occupied.field-3 { background: var(--danger); }
.etext-ruler-cell.occupied.field-4 { background: var(--accent-2); }

/* NEW: legend row - one chip per currently-placed field (built dynamically,
   see renderStep3's fixed-width branch) - labels what each ruler color
   cycle actually means instead of leaving it an arbitrary-looking stripe. */
.etext-ruler-legend { display: flex; flex-wrap: wrap; gap: var(--space-3); padding: var(--space-2) var(--space-3); margin-bottom: var(--space-2); background: var(--bg-data); border: 1px solid var(--border-data); border-radius: var(--radius-sm); }
.etext-ruler-legend-item { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text-data-muted); }
.etext-ruler-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.etext-ruler-legend-item.field-0 .etext-ruler-legend-swatch { background: var(--accent); }
.etext-ruler-legend-item.field-1 .etext-ruler-legend-swatch { background: var(--success); }
.etext-ruler-legend-item.field-2 .etext-ruler-legend-swatch { background: var(--callout-warn-text); }
.etext-ruler-legend-item.field-3 .etext-ruler-legend-swatch { background: var(--danger); }
.etext-ruler-legend-item.field-4 .etext-ruler-legend-swatch { background: var(--accent-2); }

/* ---- Placed-fields / review tables: zebra + row hover on both, an
   icon-only Remove button instead of a text-only red link. ---- */
.etext-placed-table { width: 100%; border-collapse: collapse; margin-top: var(--space-3); font-size: 11px; }
.etext-placed-table th { text-align: left; color: var(--text-data-faint); font-weight: 700; text-transform: uppercase; font-size: 9.5px; letter-spacing: 0.03em; padding: 7px 10px; border-bottom: 1px solid var(--border-data); }
.etext-placed-table td { padding: 7px 10px; border-bottom: 1px solid var(--border-data); color: var(--text-data); }
.etext-placed-table tbody tr:nth-child(even) td { background: var(--bg-data); }
.etext-placed-table tbody tr:hover td { background: var(--bg-data-hover); }
.etext-placed-table input[type="number"] { width: 55px; padding: 3px 5px; border: 1px solid var(--border-data-strong); border-radius: var(--radius-sm); background: var(--bg-data); color: var(--text-data); }
.etext-placed-table select { padding: 3px 5px; border: 1px solid var(--border-data-strong); border-radius: var(--radius-sm); background: var(--bg-data); color: var(--text-data); font-size: 11px; }
.etext-placed-remove { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: var(--radius-sm); background: transparent; border: none; color: var(--danger-text); cursor: pointer; transition: background .12s ease; }
.etext-placed-remove .icon { width: 13px; height: 13px; }
.etext-placed-remove:hover { background: var(--danger-bg); }
.etext-placed-remove:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.etext-validate-line { display: flex; align-items: flex-start; gap: var(--space-2); font-family: 'Courier New', monospace; font-size: 11px; padding: 6px 10px; border-radius: var(--radius-sm); margin-bottom: 4px; white-space: pre; overflow-x: auto; }
.etext-validate-icon .icon { width: 13px; height: 13px; flex-shrink: 0; margin-top: 2px; }
.etext-validate-line.match { background: var(--success-bg); color: var(--success-text); }
.etext-validate-line.mismatch { background: var(--danger-bg); color: var(--danger-text); }

/* ---- Step 4: Review & Generate summary table + action row. Back stays
   a quiet ghost; Generate is the one action that writes a real file to
   disk, so it's the visually dominant one - size/color/weight earn that,
   not a second shadow stacked on top of an already-shadowed card. ---- */
.etext-review-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.etext-review-table th { text-align: left; color: var(--text-data-faint); font-weight: 700; text-transform: uppercase; font-size: 9.5px; letter-spacing: 0.03em; padding: 6px 10px; border-bottom: 1px solid var(--border-data); }
.etext-review-table td { padding: 7px 10px; border-bottom: 1px solid var(--border-data); color: var(--text-data); vertical-align: top; }
.etext-review-table tbody tr:nth-child(even) td { background: var(--bg-data); }
.etext-review-table tbody tr:hover td { background: var(--bg-data-hover); }
.etext-review-field-list { font-family: 'Courier New', monospace; font-size: 10.5px; color: var(--text-data-muted); line-height: 1.6; }
.etext-review-field-list.etext-review-empty { color: var(--warning-text); font-style: italic; }
.etext-review-condition { font-family: 'Courier New', monospace; font-size: 10.5px; color: var(--text-data-muted); }
#etext-validation-area:not(:empty) { padding: 0 var(--space-5) 4px; }
.etext-engine-status-line { font-size: 11px; color: var(--text-faint); padding: 0 var(--space-5) var(--space-3); display: flex; align-items: center; gap: 6px; }
.etext-generate-actions { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: var(--space-2) var(--space-5) var(--space-5); }
.etext-actions-left, .etext-actions-right { display: flex; align-items: center; gap: var(--space-3); }
#btn-etext-generate { background: var(--accent); color: var(--text-on-accent); font-weight: 700; font-size: 14px; padding: var(--space-3) var(--space-6); }
#btn-etext-generate:hover { background: var(--accent-hover); }
.hdlload-confirm-input:focus { outline: none; border-color: var(--accent); }
