/* ==========================================================================
   components.css — the reusable UI kit.

   Several class names here are CONTRACTS with js/ and the Selenium suite:
     .pbar / .pbar-track / .pbar-fill / .pbar-label / .pbar-error
     .item / .grow / .title / .sub / .badge / .icon-btn / .btn-mini
     .retry-btn / .row-error / .staged / .staging / .chatbot-card
     .toast[.ok|.err] and .conn[.ok|.down]   (className is OVERWRITTEN by JS)
   Restyle them freely; never rename them.
   ========================================================================== */

/* ================================ buttons =============================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--rs-2);
  padding: 9px 16px;
  border: 1px solid transparent;
  border-radius: var(--rs-r-lg);
  font-family: var(--rs-font-sans);
  font-size: var(--rs-fs-base);
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--rs-dur-2) var(--rs-ease),
              border-color var(--rs-dur-2) var(--rs-ease),
              box-shadow var(--rs-dur-2) var(--rs-ease),
              color var(--rs-dur-2) var(--rs-ease);
}
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn .icon { width: 16px; height: 16px; }

.btn-primary { background: var(--rs-navy); color: #fff; box-shadow: var(--rs-sh-border); }
.btn-primary:hover:not(:disabled) { background: var(--rs-navy-hover); }
.btn-primary:active:not(:disabled) { background: var(--rs-navy-accent); }

.btn-outline {
  background: #fff;
  color: var(--rs-navy);
  border-color: var(--rs-p300);
  box-shadow: var(--rs-sh-border);
}
.btn-outline:hover:not(:disabled) { background: var(--rs-p100); border-color: var(--rs-p400); }

/* The brand's universal hover pattern: p100 fill, navy ink. */
.btn-ghost { background: transparent; color: var(--rs-ink-3); }
.btn-ghost:hover:not(:disabled) { background: var(--rs-p100); color: var(--rs-navy); }

.btn-danger { background: #fff; color: var(--rs-bad); border-color: #F3C4C4; }
.btn-danger:hover:not(:disabled) { background: var(--rs-bad-bg); border-color: var(--rs-bad); }
.btn-danger-solid { background: var(--rs-bad); color: #fff; border-color: var(--rs-bad); }
.btn-danger-solid:hover:not(:disabled) { background: #B91C1C; border-color: #B91C1C; }
.btn-danger:focus-visible,
.btn-danger-solid:focus-visible { box-shadow: var(--rs-focus-ring-danger); }

/* Sits on the navy top nav. */
.btn-onnavy {
  background: rgba(255, 255, 255, .09);
  border-color: rgba(255, 255, 255, .18);
  color: #fff;
}
.btn-onnavy:hover:not(:disabled) { background: rgba(255, 255, 255, .16); }

/* Small outline button — used for row Retry and the list/chat "Chat" action. */
.btn-mini {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border: 1px solid var(--rs-p300);
  border-radius: var(--rs-r-md);
  background: #fff;
  color: var(--rs-navy);
  font-family: var(--rs-font-sans);
  font-size: var(--rs-fs-sm);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--rs-dur-2) var(--rs-ease),
              border-color var(--rs-dur-2) var(--rs-ease),
              color var(--rs-dur-2) var(--rs-ease);
}
.btn-mini:hover:not(:disabled) { background: var(--rs-p100); border-color: var(--rs-p400); }
.btn-mini:disabled { opacity: .5; cursor: not-allowed; }
.btn-mini .icon { width: 13px; height: 13px; }
.btn-mini.retry-btn { border-color: var(--rs-warn); color: var(--rs-warn-fg); }
.btn-mini.retry-btn:hover:not(:disabled) { background: var(--rs-warn-bg); }

/* Icon-only button. Neutral by default; `.danger` for destructive removes.
   The e2e suite selects `button.icon-btn`, which a compound class still
   matches. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  flex: 0 0 auto;
  padding: 0;
  border: none;
  border-radius: var(--rs-r-md);
  background: transparent;
  color: var(--rs-p500);
  cursor: pointer;
  transition: background var(--rs-dur-2) var(--rs-ease), color var(--rs-dur-2) var(--rs-ease);
}
.icon-btn:hover { background: var(--rs-p100); color: var(--rs-navy); }
.icon-btn .icon { width: 16px; height: 16px; }
.icon-btn.danger:hover { background: var(--rs-bad-bg); color: var(--rs-bad); }

/* ================================ forms ================================= */

label { display: block; }

.field { display: block; margin-bottom: var(--rs-4); }
.field > .label,
label > .label {
  display: block;
  margin-bottom: 6px;
  font-size: var(--rs-fs-sm);
  font-weight: 600;
  color: var(--rs-ink-2);
}
.field > .help,
.help {
  display: block;
  margin-top: 5px;
  font-size: var(--rs-fs-sm);
  color: var(--rs-ink-4);
}
.help.error-text { color: var(--rs-bad); }

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 var(--rs-5);
}
.form-grid .field.span-2 { grid-column: 1 / -1; }

input, textarea, select {
  width: 100%;
  padding: 9px 12px;
  background: #fff;
  border: 1px solid var(--rs-p300);
  border-radius: var(--rs-r-lg);
  color: var(--rs-ink);
  font: inherit;
  font-size: var(--rs-fs-md);
  transition: border-color var(--rs-dur-2) var(--rs-ease),
              box-shadow var(--rs-dur-2) var(--rs-ease),
              background var(--rs-dur-2) var(--rs-ease);
}
input::placeholder, textarea::placeholder { color: var(--rs-p400); }
input:hover:not(:disabled), textarea:hover:not(:disabled) { border-color: var(--rs-p400); }
input:focus-visible, textarea:focus-visible, select:focus-visible {
  border-color: var(--rs-cyan);
  box-shadow: var(--rs-focus-ring);
}
input:disabled, textarea:disabled { background: var(--rs-p50); color: var(--rs-ink-4); cursor: not-allowed; }
textarea { resize: vertical; min-height: 76px; }

/* Checkboxes MUST keep real box dimensions: the Selenium suite calls
   `cb.click()` on #doc-public / #url-public directly, so `display:none` or
   `opacity:0;position:absolute` would make them non-interactable. */
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 18px; height: 18px;
  flex: 0 0 18px;
  margin: 0 6px 0 0;
  padding: 0;
  border: 1.5px solid var(--rs-p400);
  border-radius: var(--rs-r-sm);
  background: #fff;
  cursor: pointer;
  transition: background var(--rs-dur-1) var(--rs-ease), border-color var(--rs-dur-1) var(--rs-ease);
}
input[type="checkbox"]:hover { border-color: var(--rs-cyan); }
input[type="checkbox"]:checked { background: var(--rs-cyan); border-color: var(--rs-cyan); }
input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 5px; top: 1px;
  width: 5px; height: 10px;
  border: 2px solid #fff;
  border-top: 0;
  border-left: 0;
  transform: rotate(42deg);
}

.check {
  display: inline-flex;
  align-items: center;
  margin: 0;
  font-size: var(--rs-fs-sm);
  font-weight: 500;
  color: var(--rs-ink-2);
  white-space: nowrap;
  cursor: pointer;
}

/* File inputs stay REAL and visible — the e2e suite does `send_keys` straight
   onto them, so they must never be hidden behind a styled label. */
input[type="file"] {
  padding: 6px;
  background: var(--rs-p50);
  border: 1px dashed var(--rs-p300);
  color: var(--rs-ink-3);
  font-size: var(--rs-fs-sm);
  cursor: pointer;
}
input[type="file"]:hover { border-color: var(--rs-cyan); background: var(--rs-info-bg); }
input[type="file"]::file-selector-button {
  margin-right: var(--rs-3);
  padding: 7px 14px;
  border: 1px solid var(--rs-p300);
  border-radius: var(--rs-r-md);
  background: #fff;
  color: var(--rs-navy);
  font-family: var(--rs-font-sans);
  font-size: var(--rs-fs-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--rs-dur-2) var(--rs-ease);
}
input[type="file"]::file-selector-button:hover { background: var(--rs-p100); }

/* Horizontal "add something" form used by every ingestion panel. */
.inline-form {
  display: flex;
  align-items: center;
  gap: var(--rs-3);
  flex-wrap: wrap;
  margin-bottom: var(--rs-4);
}
.inline-form input[type="text"],
.inline-form input[type="url"] { flex: 1 1 220px; min-width: 160px; }
.inline-form input[type="file"] { flex: 1 1 260px; min-width: 200px; }
.inline-form .btn { flex: 0 0 auto; }

.form-actions { display: inline-flex; align-items: center; gap: var(--rs-3); }

/* A second form in the same panel, set apart from the one above it. */
.form-followup {
  margin-top: var(--rs-5);
  padding-top: var(--rs-5);
  border-top: 1px solid var(--rs-line);
}

/* =========================== segmented control ========================== */

.segmented {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--rs-p100);
  border: 1px solid var(--rs-p200);
  border-radius: var(--rs-r-lg);
}
.seg-btn {
  padding: 6px 14px;
  border: none;
  border-radius: var(--rs-r-md);
  background: transparent;
  font-family: var(--rs-font-sans);
  font-size: var(--rs-fs-sm);
  font-weight: 600;
  color: var(--rs-ink-3);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--rs-dur-2) var(--rs-ease),
              color var(--rs-dur-2) var(--rs-ease),
              box-shadow var(--rs-dur-2) var(--rs-ease);
}
.seg-btn:hover { color: var(--rs-navy); }
.seg-btn.is-active {
  background: #fff;
  color: var(--rs-navy);
  box-shadow: var(--rs-sh-border), 0 1px 2px rgba(13, 42, 78, .08);
}

/* ============================= toggle switch ============================ */
/* Safe to visually hide this input — it is never driven by Selenium. */

.switch {
  display: inline-flex;
  align-items: center;
  gap: var(--rs-2);
  font-size: var(--rs-fs-sm);
  font-weight: 500;
  color: var(--rs-ink-2);
  white-space: nowrap;
  cursor: pointer;
}
.switch input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
.switch-track {
  position: relative;
  width: 36px; height: 20px;
  flex: 0 0 36px;
  border-radius: var(--rs-r-pill);
  background: var(--rs-p300);
  transition: background var(--rs-dur-2) var(--rs-ease);
}
.switch-thumb {
  position: absolute;
  top: 2px; left: 2px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(13, 42, 78, .3);
  transition: transform var(--rs-dur-2) var(--rs-ease);
}
.switch input:checked + .switch-track { background: var(--rs-cyan); }
.switch input:checked + .switch-track .switch-thumb { transform: translateX(16px); }
.switch input:focus-visible + .switch-track { box-shadow: var(--rs-focus-ring); }

/* ============================== search box ============================== */

.search { position: relative; flex: 1 1 240px; min-width: 180px; }
.search .icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px; height: 16px;
  color: var(--rs-p400);
  pointer-events: none;
}
.search input { padding-left: 36px; background: var(--rs-p50); }
.search input:focus-visible { background: #fff; }
.search input::-webkit-search-cancel-button { cursor: pointer; }

/* ================================ panels ================================ */

.panel {
  background: var(--rs-surface);
  border: 1px solid var(--rs-line);
  border-radius: var(--rs-r-xl);
  padding: var(--rs-5);
  margin-bottom: var(--rs-5);
  box-shadow: var(--rs-sh-panel);
}
.panel-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--rs-4);
  padding-bottom: var(--rs-4);
  margin-bottom: var(--rs-4);
  border-bottom: 1px solid var(--rs-line);
}
.panel-head h2 { font-size: var(--rs-fs-h2); }
.panel-head .lead { font-size: var(--rs-fs-base); margin-top: 4px; }
.panel-flush { padding: 0; }
.panel-flush .toolbar { padding: var(--rs-4) var(--rs-5); }

.callout {
  display: flex;
  gap: var(--rs-3);
  padding: 14px 16px;
  margin: var(--rs-4) 0;
  background: var(--rs-info-bg);
  border: 1px solid var(--rs-info-line);
  border-left: 4px solid var(--rs-cyan);
  border-radius: var(--rs-r-lg);
  font-size: var(--rs-fs-base);
  color: var(--rs-info-fg);
}
.callout .icon { color: var(--rs-cyan-deep); margin-top: 2px; }
.callout p { margin: 0; }
.callout.warn {
  background: var(--rs-warn-bg);
  border-color: #F4DCA0;
  border-left-color: var(--rs-warn);
  color: var(--rs-warn-fg);
}
.callout.warn .icon { color: var(--rs-warn); }

/* =============================== toolbar =============================== */

.toolbar {
  display: flex;
  align-items: center;
  gap: var(--rs-3);
  flex-wrap: wrap;
  margin-bottom: var(--rs-4);
}

/* ================================ table ================================= */

.table-wrap {
  /* `position: relative` is load-bearing: without it an absolutely-positioned
     descendant (the .sr-only caption in the Actions header) is laid out against
     the initial containing block, escapes this clip entirely, and pushes the
     whole page into horizontal scroll on narrow viewports.
     overflow-x:auto (rather than hidden) means a table too wide for its column
     scrolls instead of silently clipping the Actions cell. */
  position: relative;
  border: 1px solid var(--rs-line);
  border-radius: var(--rs-r-lg);
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  background: #fff;
}
.rs-table {
  width: 100%;
  border-collapse: collapse;
  background: #fff;
  font-size: var(--rs-fs-base);
}
.rs-table th {
  text-align: left;
  padding: 11px 14px;
  background: var(--rs-surface-2);
  border-bottom: 1px solid var(--rs-line);
  font-family: var(--rs-font-brand);
  font-weight: 600;
  font-size: var(--rs-fs-sm);
  color: var(--rs-navy);
  white-space: nowrap;
}
.rs-table td {
  padding: 12px 14px;
  border-bottom: 1px solid var(--rs-line);
  vertical-align: middle;
}
.rs-table tbody tr:last-child td { border-bottom: none; }
.th-actions { width: 1%; }

/* `.chatbot-card` is now a <tr>. The old card styles (padding, radius and a
   translateY hover) must NOT be reused: `transform` on a table row creates a
   containing block and breaks table rendering. Hover is background only. */
.chatbot-card { cursor: pointer; transition: background var(--rs-dur-2) var(--rs-ease); }
.chatbot-card:hover { background: var(--rs-p50); }
.chatbot-card:focus-visible { outline: 2px solid var(--rs-cyan); outline-offset: -2px; }

.agent-cell { display: flex; align-items: center; gap: var(--rs-3); min-width: 0; }
.agent-id { min-width: 0; }
.agent-name {
  margin: 0;
  font-family: var(--rs-font-brand);
  font-weight: 600;
  font-size: var(--rs-fs-base);
  color: var(--rs-navy);
  line-height: 1.3;
}
.agent-sub {
  display: block;
  font-size: var(--rs-fs-sm);
  color: var(--rs-ink-4);
  max-width: 38ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cell-actions { white-space: nowrap; }
.cell-actions > * + * { margin-left: var(--rs-1); }
.cell-status { min-width: 150px; }

.cid {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--rs-r-md);
  background: var(--rs-p100);
  color: var(--rs-cyan-deep);
  font-family: var(--rs-font-mono);
  font-size: var(--rs-fs-xs);
}

/* A copyable code block (the widget embed snippet). Same mono/well family as
   .cid above, sized for several lines. It must stay visible and selectable:
   when navigator.clipboard is unavailable (an admin panel served over plain
   http), selecting this by hand is the fallback copy path. */
.snippet {
  margin: 8px 0;
  padding: 10px 12px;
  max-height: 8.5em;
  overflow: auto;
  border: 1px solid var(--rs-p300);
  border-radius: var(--rs-r-md);
  background: var(--rs-p50);
  color: var(--rs-ink-2);
  font-family: var(--rs-font-mono);
  font-size: var(--rs-fs-xs);
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-all;
  -webkit-user-select: all;
  user-select: all;
}
.snippet:focus-visible { outline: var(--rs-focus-ring); }
.table-foot { margin: 10px 14px 2px; font-size: var(--rs-fs-sm); color: var(--rs-ink-4); }

/* ========================= chips, pills, avatars ======================== */

.status-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: var(--rs-r-pill);
  font-size: var(--rs-fs-xs);
  font-weight: 600;
  white-space: nowrap;
}
.status-chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.status-chip.is-ready     { background: var(--rs-good-bg); color: var(--rs-good-fg); }
.status-chip.is-ingesting { background: var(--rs-info-bg); color: var(--rs-info-fg); }
.status-chip.is-ingesting::before { animation: rs-pulse 1.4s var(--rs-ease) infinite; }
.status-chip.is-failed    { background: var(--rs-bad-bg);  color: var(--rs-bad-fg); }
.status-chip.is-empty     { background: var(--rs-neutral-bg); color: var(--rs-p500); }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  margin: 2px 4px 2px 0;
  border-radius: var(--rs-r-md);
  background: var(--rs-p100);
  color: var(--rs-ink-2);
  font-size: var(--rs-fs-xs);
  font-weight: 500;
  white-space: nowrap;
}
.pill .icon { width: 12px; height: 12px; color: var(--rs-cyan-deep); }
.pill.is-none { background: transparent; color: var(--rs-p400); }

/* Visibility badge on file/url rows (public|private). */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: var(--rs-r-pill);
  background: var(--rs-neutral-bg);
  color: var(--rs-p500);
  font-size: var(--rs-fs-xs);
  font-weight: 600;
  white-space: nowrap;
}
.badge.public { background: var(--rs-good-bg); color: var(--rs-good-fg); }
/* Deployment lifecycle. `paused` is reversible (warn), `revoked` is terminal (bad). */
.badge.is-active { background: var(--rs-good-bg); color: var(--rs-good-fg); }
.badge.is-paused { background: var(--rs-warn-bg); color: var(--rs-warn-fg); }
.badge.is-revoked { background: var(--rs-bad-bg); color: var(--rs-bad-fg); }
/* Session archive state on the user-sessions screen. */
.badge.is-deleted { background: var(--rs-bad-bg); color: var(--rs-bad-fg); }

.avatar-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  flex: 0 0 36px;
  border-radius: var(--rs-r-lg);
  background: var(--rs-gradient-avatar);
  color: #fff;
  font-family: var(--rs-font-brand);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .3px;
}
.avatar-tile.sm { width: 28px; height: 28px; flex-basis: 28px; font-size: 11px; border-radius: var(--rs-r-md); }

/* =============================== stat tiles ============================= */

.stat-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--rs-4);
  margin-bottom: var(--rs-5);
}
.stat {
  position: relative;
  overflow: hidden;
  padding: 16px 18px;
  background: #fff;
  border: 1px solid var(--rs-line);
  border-radius: var(--rs-r-xl);
  box-shadow: var(--rs-sh-card);
}
.stat::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--rs-p300);
}
.stat.is-info::before { background: var(--rs-cyan); }
.stat.is-good::before { background: var(--rs-good); }
.stat.is-bad::before  { background: var(--rs-bad); }
.stat.is-clickable { cursor: pointer; transition: box-shadow var(--rs-dur-2) var(--rs-ease); }
.stat.is-clickable:hover { box-shadow: var(--rs-sh-raised); }
.stat-label {
  margin: 0 0 6px;
  font-size: var(--rs-fs-xs);
  font-weight: 700;
  letter-spacing: var(--rs-track-caps);
  text-transform: uppercase;
  color: var(--rs-p500);
}
.stat-value {
  margin: 0;
  font-family: var(--rs-font-brand);
  font-weight: 700;
  font-size: 26px;
  line-height: 1.1;
  color: var(--rs-navy);
}
.stat-sub { margin: 4px 0 0; font-size: var(--rs-fs-sm); color: var(--rs-ink-4); }
.stat .pbar { margin-top: 8px; }

/* =============================== item rows ============================== */

.items { list-style: none; margin: 0; padding: 0; }
.item {
  display: flex;
  align-items: center;
  gap: var(--rs-3);
  padding: 11px 14px;
  margin-bottom: var(--rs-2);
  background: #fff;
  border: 1px solid var(--rs-line);
  border-radius: var(--rs-r-lg);
  box-shadow: var(--rs-sh-border);
  transition: border-color var(--rs-dur-2) var(--rs-ease), background var(--rs-dur-2) var(--rs-ease);
}
.item:hover { border-color: var(--rs-p300); background: var(--rs-p50); }
.item .grow { flex: 1; min-width: 0; }
.item .title {
  font-size: var(--rs-fs-base);
  font-weight: 500;
  color: var(--rs-navy);
  word-break: break-word;
}
.item .sub { font-size: var(--rs-fs-sm); color: var(--rs-ink-4); word-break: break-all; }
.item .item-icon { color: var(--rs-p500); width: 20px; height: 20px; }
.item.row-error { border-color: var(--rs-bad); background: var(--rs-bad-bg); }
.item.row-error .item-icon { color: var(--rs-bad); }

/* Web-page rows put the address in the title as a real link. `break-all` keeps
   a long query string inside the row instead of stretching the panel. */
.item .title a {
  color: inherit;
  text-decoration: none;
  word-break: break-all;
  border-bottom: 1px solid transparent;
}
.item .title a:hover { color: var(--rs-cyan-deep); border-bottom-color: currentColor; }

/* FAQ rows are two-line (question + full answer), so they align to the top
   rather than centring like single-line file/URL rows. */
.item.is-qa { align-items: flex-start; }
.item.is-qa .item-icon { margin-top: 2px; }
.item.is-qa .icon-btn { margin-top: -2px; }

/* Deployment rows: a stacked row like .is-qa, with a snippet and a button
   cluster. Each control is bound by its own class in views.js, so the order
   here is free (unlike file/url rows, where the first .icon-btn is the remove
   handler). */
.item.is-deploy { align-items: flex-start; }
.item.is-deploy .item-icon { margin-top: 2px; }
.item.is-deploy .icon-btn { margin-top: -2px; }
.item.is-deploy .badge { margin-top: 1px; }
/* A paused or revoked deployment reads as inactive at a glance, but its snippet
   stays fully legible — an admin still needs to copy or compare it. */
.item.is-paused .title,
.item.is-revoked .title { color: var(--rs-ink-3); }
.dep-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rs-2);
  margin-top: 2px;
}
.qa-answer {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin-top: 5px;
  font-size: var(--rs-fs-sm);
  line-height: 1.5;
  color: var(--rs-ink-2);
}
.qa-tag {
  flex: 0 0 auto;
  padding: 0 6px;
  border-radius: var(--rs-r-pill);
  background: var(--rs-info-bg);
  color: var(--rs-cyan-deep);
  font-family: var(--rs-font-brand);
  font-size: var(--rs-fs-2xs);
  font-weight: 700;
  line-height: 18px;
}

/* Staged (selected but not yet uploaded) files. */
.staging { list-style: none; margin: 0 0 var(--rs-3); padding: 0; }
.staging .staged {
  display: flex;
  align-items: center;
  gap: var(--rs-3);
  padding: 7px 12px;
  margin-bottom: 6px;
  border: 1px dashed var(--rs-p300);
  border-radius: var(--rs-r-md);
  background: var(--rs-p50);
  color: var(--rs-ink-3);
}
.staging .staged .title {
  flex: 1;
  font-size: var(--rs-fs-sm);
  font-weight: 500;
  color: var(--rs-ink-2);
  word-break: break-all;
}
.staging .staged .sub { font-size: var(--rs-fs-xs); color: var(--rs-ink-4); white-space: nowrap; }
.staging .staged .icon { color: var(--rs-p500); width: 15px; height: 15px; }

/* ============================= progress bars ============================ */
/* Structure is fixed by views.js progressBarHtml():
     .pbar > .pbar-track > .pbar-fill   +   .pbar-label
   Stage ticks and the indeterminate state are added with pseudo-elements and
   a class only, so the emitted markup never has to change.
   The fill's width is set as an inline style by main.js. */

.pbar { display: flex; align-items: center; gap: var(--rs-2); }
.pbar-track {
  position: relative;
  flex: 1;
  height: 8px;
  min-width: 60px;
  background: var(--rs-p100);
  border: 1px solid var(--rs-p200);
  border-radius: var(--rs-r-pill);
  overflow: hidden;
}
/* Six ticks — one per ingestion stage:
   queued → loading → chunking → saving_chunks → embedding → storing_vectors */
.pbar-track::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(90deg,
    transparent 0 calc(100% / 6 - 1px),
    rgba(13, 42, 78, .16) calc(100% / 6 - 1px) calc(100% / 6));
}
.pbar-fill {
  height: 100%;
  width: 0;
  border-radius: var(--rs-r-pill);
  background: linear-gradient(90deg, var(--rs-cyan-deep), var(--rs-cyan));
  transition: width var(--rs-dur-4) var(--rs-ease);
}
.pbar-label {
  font-family: var(--rs-font-mono);
  font-size: var(--rs-fs-xs);
  font-weight: 600;
  color: var(--rs-ink-3);
  white-space: nowrap;
  min-width: 9ch;
  text-align: right;
}
.pbar.pbar-error .pbar-fill { background: var(--rs-bad); }
.pbar.pbar-error .pbar-label { color: var(--rs-bad); }

/* Queued / 0%: sweep instead of a stuck empty bar. */
.pbar.is-indeterminate .pbar-fill {
  width: 35% !important;
  background: linear-gradient(90deg, transparent, var(--rs-cyan), transparent);
  animation: rs-indet 1.15s var(--rs-ease) infinite;
}
@keyframes rs-indet {
  0%   { transform: translateX(-120%); }
  100% { transform: translateX(320%); }
}
@media (prefers-reduced-motion: reduce) {
  .pbar.is-indeterminate .pbar-fill { animation: none; width: 100% !important; opacity: .4; }
}

.row-pbar { margin-top: 8px; }
.card-pbar { margin-top: 8px; }

/* ============================== skeletons =============================== */
/* Skeleton rows must NEVER carry .chatbot-card or li.item[data-source-id] —
   the e2e suite counts those and would treat placeholders as real records. */

.skel {
  background: linear-gradient(90deg, var(--rs-p100) 25%, var(--rs-p200) 37%, var(--rs-p100) 63%);
  background-size: 400% 100%;
  border-radius: var(--rs-r-md);
  animation: rs-shimmer 1.4s ease-in-out infinite;
}
@keyframes rs-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) { .skel { animation: none; } }

.skel-line { display: block; height: 11px; margin: 5px 0; }
.skel-line.w-60 { width: 60%; }
.skel-line.w-40 { width: 40%; }
.skel-line.w-30 { width: 30%; }
.skel-tile { width: 36px; height: 36px; border-radius: var(--rs-r-lg); flex: 0 0 36px; }
.skel-dot { width: 20px; height: 20px; border-radius: var(--rs-r-md); flex: 0 0 20px; }
.skel-row td { padding: 12px 14px; border-bottom: 1px solid var(--rs-line); }

/* Source-list placeholder. Never `li.item` — the e2e suite counts those. */
.skel-item {
  display: flex;
  align-items: center;
  gap: var(--rs-3);
  padding: 11px 14px;
  margin-bottom: var(--rs-2);
  border: 1px solid var(--rs-line);
  border-radius: var(--rs-r-lg);
  background: #fff;
  list-style: none;
}
.skel-item .grow { flex: 1; min-width: 0; }
.skel-item .skel-line { display: block; }

/* ============================= empty states ============================= */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--rs-10) var(--rs-6);
  background: #fff;
  border: 1px dashed var(--rs-p300);
  border-radius: var(--rs-r-xl);
}
.empty-art { width: 96px; height: 96px; color: var(--rs-p300); margin-bottom: var(--rs-4); stroke-width: 1.25; }
.empty-state h2 { margin: 0 0 6px; font-size: 18px; }
.empty-state .lead { margin: 0 0 var(--rs-5); }

.list-empty-mini {
  padding: var(--rs-6);
  text-align: center;
  color: var(--rs-ink-4);
  font-size: var(--rs-fs-sm);
  border: 1px dashed var(--rs-p300);
  border-radius: var(--rs-r-lg);
  background: var(--rs-p50);
}

/* ================================ modal ================================= */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--rs-z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--rs-5);
  background: rgba(13, 42, 78, .45);
  backdrop-filter: blur(2px);
  animation: rs-fade var(--rs-dur-2) var(--rs-ease);
}
.modal {
  width: min(460px, 100%);
  max-height: calc(100vh - var(--rs-8));
  overflow-y: auto;
  padding: var(--rs-6);
  background: #fff;
  border-radius: var(--rs-r-2xl);
  box-shadow: var(--rs-sh-overlay);
  animation: rs-pop var(--rs-dur-3) var(--rs-ease);
}
.modal-title { margin: 0 0 8px; font-size: 19px; font-weight: 700; }
.modal-body { margin: 0 0 var(--rs-4); font-size: var(--rs-fs-md); color: var(--rs-ink-2); }
.modal-body b { color: var(--rs-navy); }
/* The crawl page list can run to a few hundred rows, so it scrolls inside the
   modal rather than pushing its buttons off the screen. */
.crawl-pages-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 46vh;
  overflow-y: auto;
}
.crawl-pages-list li {
  padding: 6px 0;
  border-bottom: 1px solid var(--rs-line);
  font-size: var(--rs-fs-sm);
  word-break: break-all;
}
.crawl-pages-list li:last-child { border-bottom: 0; }
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--rs-2);
  margin-top: var(--rs-5);
}
@keyframes rs-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes rs-pop {
  from { opacity: 0; transform: translateY(10px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* ================================ toast ================================= */
/* main.js:57 overwrites className with exactly "toast", "toast ok" or
   "toast err" — so every style must hang off those, and the icon has to be a
   pseudo-element. The entry animation must be short and END AT opacity:1,
   because WebDriver treats opacity:0 as "not displayed" when the e2e suite
   waits for the toast. */

.toast {
  position: fixed;
  left: 50%;
  bottom: var(--rs-6);
  transform: translateX(-50%);
  z-index: var(--rs-z-toast);
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(520px, calc(100vw - 40px));
  padding: 12px 18px;
  background: #fff;
  border: 1px solid var(--rs-line);
  border-left: 4px solid var(--rs-p400);
  border-radius: var(--rs-r-lg);
  box-shadow: var(--rs-sh-overlay);
  font-size: var(--rs-fs-md);
  color: var(--rs-ink-2);
  animation: rs-toast-in var(--rs-dur-3) var(--rs-ease);
}
.toast::before {
  content: "";
  flex: 0 0 8px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--rs-p400);
}
.toast.ok { border-left-color: var(--rs-good); }
.toast.ok::before { background: var(--rs-good); }
.toast.err { border-left-color: var(--rs-bad); }
.toast.err::before { background: var(--rs-bad); }
@keyframes rs-toast-in {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ============================== responsive ============================== */

@media (max-width: 1279px) {
  .stat-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 1023px) {
  .rs-table { min-width: 760px; }
  .form-grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 767px) {
  .stat-strip { grid-template-columns: minmax(0, 1fr); }

  /* Table collapses into stacked cards. Every <td> carries a data-label
     (emitted by views.js) which becomes the row caption. */
  .table-wrap { overflow-x: visible; border: none; background: transparent; }
  /* The table stops being a table: `display:block` is what lets width:100%
     actually constrain it once the rows become stacked cards. */
  .rs-table { display: block; width: 100%; min-width: 0; background: transparent; }
  .rs-table thead { display: none; }
  .rs-table tbody { display: block; }
  .rs-table tr {
    display: block;
    padding: var(--rs-3);
    margin-bottom: var(--rs-3);
    background: #fff;
    border: 1px solid var(--rs-line);
    border-radius: var(--rs-r-lg);
    box-shadow: var(--rs-sh-border);
  }
  .rs-table td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--rs-3);
    padding: 6px 4px;
    border: none;
  }
  .rs-table td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: var(--rs-fs-xs);
    font-weight: 700;
    letter-spacing: var(--rs-track-caps);
    text-transform: uppercase;
    color: var(--rs-p500);
  }
  .rs-table td.cell-agent { justify-content: flex-start; padding-bottom: var(--rs-3); }
  .rs-table td.cell-agent::before { display: none; }
  .rs-table td.cell-actions { justify-content: flex-end; padding-top: var(--rs-3); }
  .rs-table td.cell-actions::before { display: none; }
  .agent-sub { max-width: none; white-space: normal; }
  .cell-status { min-width: 0; }

  .item { flex-wrap: wrap; }
  .item .grow { flex: 1 1 100%; }
  .inline-form { flex-direction: column; align-items: stretch; }
  .inline-form .btn { width: 100%; }
  .toolbar { flex-direction: column; align-items: stretch; }
  .segmented { overflow-x: auto; }
}
