/* COLOR SCHEME → ui/shared/css/design-system.css (single source of truth).
   No raw hex/rgb here: use var(--brand) / var(--accent) / var(--accent-hover) for
   the brand accent, rgba(var(--brand-rgb), a) for any accent opacity, var(--success|
   warning|danger) (or rgba(var(--*-rgb), a)) for status, var(--purple) for the 2nd hue.
   Need a colour that doesn't exist yet? Add a token to the palette in design-system.css
   (the dark :root block AND its body.light-mode twin), then reference it here.
   Re-theming the whole app (e.g. peach → green) is then a one-file edit. */
    top: 0;
    border-bottom: var(--border-width) solid var(--border);
    white-space: nowrap;
  }
.db-table {
    table-layout: fixed;
    border-collapse: collapse;
  }
.db-table td {
    padding: 4px 10px;
    border-bottom: var(--border-width) solid var(--border-soft);
    border-right: var(--border-width) solid var(--files-border-strong);
    color: var(--fg-1);
    max-width: 300px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
.db-table td.db-row-delete-td,
.db-table th.db-row-delete-th { border-right: none; }
.db-table td:last-child,
.db-table th:last-child { border-right: none; }
.db-table tr:hover td { background: rgba(var(--ink-rgb), 0.04); }
.db-row-even td { background: var(--bg-2); }
.db-table tr[style*="height:100px"] .db-cell-pre { max-height: 50px; }

/* Slim scrollbars matching the rest of the app (agents page baseline:
   width 5px, thumb var(--bg-tint), transparent track). Applied to the table area,
   per-cell overflow scrollbars, and the sidebar table list. */
#db-table-data,
#db-table-data *,
#db-table-list {
    scrollbar-width: thin;
    scrollbar-color: var(--bg-tint) transparent;
  }
#db-table-data::-webkit-scrollbar,
#db-table-data *::-webkit-scrollbar,
#db-table-list::-webkit-scrollbar {
    width: 5px;
    height: 5px;
  }
#db-table-data::-webkit-scrollbar-track,
#db-table-data *::-webkit-scrollbar-track,
#db-table-list::-webkit-scrollbar-track {
    background: transparent;
  }
#db-table-data::-webkit-scrollbar-thumb,
#db-table-data *::-webkit-scrollbar-thumb,
#db-table-list::-webkit-scrollbar-thumb {
    background: var(--bg-tint);
    border-radius: 3px;
  }
#db-table-data::-webkit-scrollbar-thumb:hover,
#db-table-data *::-webkit-scrollbar-thumb:hover,
#db-table-list::-webkit-scrollbar-thumb:hover {
    background: var(--bg-tint);
  }
#db-table-data::-webkit-scrollbar-corner,
#db-table-data *::-webkit-scrollbar-corner {
    background: transparent;
  }

/* Light-mode override — same selectors as the dark-mode block above so the
   `#db-table-data *` rule (descendant scrollbars like .db-cell) doesn't keep
   the dark thumb color on a light background. Uses the warm peach used by
   the rest of the light theme. */
body.light-mode #db-table-data,
body.light-mode #db-table-data *,
body.light-mode #db-table-list {
    scrollbar-color: var(--bg-tint) transparent;
  }
body.light-mode #db-table-data::-webkit-scrollbar-thumb,
body.light-mode #db-table-data *::-webkit-scrollbar-thumb,
body.light-mode #db-table-list::-webkit-scrollbar-thumb {
    background: var(--bg-tint);
  }
body.light-mode #db-table-data::-webkit-scrollbar-thumb:hover,
body.light-mode #db-table-data *::-webkit-scrollbar-thumb:hover,
body.light-mode #db-table-list::-webkit-scrollbar-thumb:hover {
    background: var(--bg-tint);
  }
.db-table .col-pk { color: var(--warning); }
.db-table .col-null { color: var(--fg-3); font-style: italic; }
.db-cell-pre {
    margin: 0;
    white-space: pre-wrap;
    overflow-wrap: break-word;
    word-break: break-word;
    font-family: 'JetBrains Mono', 'Fira Code', monospace;
    font-size: 11px;
    line-height: 1.4;
    max-height: 6em;
    max-width: 100%;
    overflow: hidden;
  }
.db-row-resize-td {
    padding: 0 !important;
    line-height: 0;
  }
.db-row-resize-handle {
    height: 4px;
    cursor: row-resize;
    background: transparent;
  }
.db-row-resize-handle:hover,
.db-row-resize-handle.resizing {
    background: rgba(var(--brand-rgb), 0.19);
  }
.db-cell { overflow: auto; position: relative; }
.db-cell:hover { outline: 1px solid rgba(var(--brand-rgb), 0.33); }
.db-cell-overlay { display: contents; }
.db-cell-expand {
    position: absolute;
    top: 1px;
    right: 1px;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    color: var(--fg-3);
    width: 18px;
    height: 18px;
    line-height: 16px;
    text-align: center;
    font-size: 11px;
    cursor: pointer;
    border-radius: 3px;
    opacity: 0.35;
    z-index: 5;
    padding: 0;
    transition: opacity 0.12s;
  }
.db-cell:hover .db-cell-expand {
    opacity: 1;
  }
.db-cell-expand:hover {
    color: var(--brand);
    border-color: var(--brand);
  }

.db-cell-edit {
    position: absolute;
    top: 1px;
    left: 1px;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    color: var(--fg-3);
    width: 18px;
    height: 18px;
    line-height: 16px;
    text-align: center;
    font-size: 11px;
    cursor: pointer;
    border-radius: 3px;
    opacity: 0.35;
    z-index: 5;
    padding: 0;
    transition: opacity 0.12s;
  }
.db-cell:hover .db-cell-edit {
    opacity: 1;
  }
.db-cell-edit:hover {
    color: var(--warning);
    border-color: var(--warning);
  }

.db-row-delete-th,
.db-row-delete-td {
    width: 28px;
    min-width: 28px;
    max-width: 28px;
    padding: 0;
    text-align: center;
    background: transparent;
  }
.db-row-delete {
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    color: var(--fg-3);
    width: 22px;
    height: 22px;
    line-height: 18px;
    text-align: center;
    font-size: 12px;
    cursor: pointer;
    border-radius: 3px;
    opacity: 0.45;
    padding: 0;
    transition: opacity 0.12s, color 0.12s, border-color 0.12s;
  }
.db-row:hover .db-row-delete,
.db-row-delete:hover {
    opacity: 1;
  }
.db-row-delete:hover {
    color: var(--danger);
    border-color: var(--danger);
  }

/* Non-admin view: keep button visible so the hover tooltip can explain why
 * it's disabled, but dim it and override hover affordances. */
.db-cell-edit.restricted,
.db-row-delete.restricted,
.db-table-reset-btn.restricted,
#db-reset.restricted {
    opacity: 0.35;
    cursor: not-allowed;
    filter: grayscale(100%);
  }
.db-cell-edit.restricted:hover,
.db-row-delete.restricted:hover,
.db-table-reset-btn.restricted:hover,
#db-reset.restricted:hover {
    color: inherit;
    border-color: inherit;
    opacity: 0.35;
  }

.db-table td,
.db-table th {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
.db-th { position: relative; user-select: none; }
.th-text { 
    overflow: hidden; 
    text-overflow: ellipsis; 
    display: inline-block; 
    cursor: pointer; 
    border-radius: 3px; 
    padding: 1px 4px; 
    white-space: nowrap;
    max-width: 100%;
    box-sizing: border-box;
  }
.th-text:hover { background: var(--bg-elev-2); }
.th-name { display: inline; }
.th-sort { color: var(--fg-3); display: inline; font-size: 10px; margin-left: 2px; }
.th-resize {
    position: absolute;
    top: 0; right: 0;
    width: 5px;
    height: 100%;
    cursor: col-resize;
    z-index: 10;
  }
.th-resize:hover,
.th-resize.resizing { background: rgba(var(--brand-rgb), 0.33); }
.db-table .resize-line {
    position: absolute;
    top: 0; bottom: 0;
    width: 1px;
    background: var(--brand);
    z-index: 20;
    pointer-events: none;
  }
.db-edit-input {
    background: var(--bg-0);
    border: 1px solid var(--brand);
    color: var(--fg-1);
    padding: 4px 6px;
    font-size: 12px;
    font-family: 'Fira Code', Menlo, monospace;
    position: absolute;
    top: 4px;
    left: 10px;
    right: 10px;
    bottom: 4px;
    width: auto;
    height: auto;
    min-height: auto;
    box-sizing: border-box;
    outline: none;
    border-radius: 2px;
    resize: none;
  }

/* ── JSON editor modal ── */
  #cell-modal { display: none; position: fixed; inset: 0; z-index: 999; }
#cell-modal.open { display: flex; align-items: center; justify-content: center; }
#cell-modal-backdrop {
    position: absolute; inset: 0;
    background: rgba(var(--shadow-rgb), 0.6);
    backdrop-filter: blur(4px);
  }
#cell-modal-panel {
    position: relative;
    width: 80vw;
    max-width: 900px;
    height: 70vh;
    background: var(--bg-0);
    border: var(--border-width) solid var(--border);
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    z-index: 10;
    overflow: hidden;
    box-shadow: 0 10px 40px rgba(var(--shadow-rgb), 0.5);
  }
#cell-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 16px;
    border-bottom: var(--border-width) solid var(--border);
    flex-shrink: 0;
  }
#cell-modal-title {
    color: var(--brand);
    font-size: 13px;
    font-weight: 600;
  }
#cell-modal-close {
    background: none;
    border: none;
    color: var(--fg-3);
    font-size: 20px;
    cursor: pointer;
    line-height: 1;
  }
#cell-modal-close:hover { color: var(--danger); }
#cell-modal-editor {
    flex: 1;
    background: var(--bg-0);
    border: none;
    border-bottom: var(--border-width) solid var(--border);
    color: var(--fg-1);
    font-family: 'Fira Code', Menlo, monospace;
    font-size: 12px;
    line-height: 1.5;
    padding: 14px 16px;
    resize: none;
    outline: none;
    tab-size: 2;
    white-space: pre-wrap;
    word-break: break-word;
    overflow: auto;
  }
#cell-modal-editor:focus { border-bottom-color: var(--brand); }
#cell-modal-viewer {
    flex: 1;
    overflow: auto;
    padding: 16px;
    font-family: 'Fira Code', Menlo, monospace;
    font-size: 12px;
    line-height: 1.5;
    background: var(--bg-0);
  }
#cell-modal-viewer .json-root {
    font-size: 12px;
  }
#cell-modal-viewer .json-content-md {
    font-size: 13px;
    max-width: 800px;
  }

/* Column filter indicator */
  .db-th.filtered .th-name { color: var(--brand); }
.th-funnel {
    display: inline;
    margin-left: 2px;
    font-size: 9px;
    color: var(--brand);
  }
.th-hide-btn {
    position: absolute;
    top: 4px;
    left: 4px;
    background: transparent;
    border: none;
    color: var(--fg-3);
    font-size: 11px;
    cursor: pointer;
    z-index: 5;
    padding: 0;
    line-height: 1;
    opacity: 0.5;
    transition: all 0.15s;
  }
.db-th:hover .th-hide-btn {
    opacity: 1;
  }
.th-hide-btn:hover {
    color: var(--fg-1);
  }
.th-hide-btn.hidden-col {
    color: var(--danger);
    opacity: 1;
  }

/* ── Column filter/sort popup (Google Sheets style) ── */
  .db-col-popup {
    position: absolute;
    z-index: 100;
    background: var(--bg-2);
    border: var(--border-width) solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 32px rgba(var(--shadow-rgb), 0.6);
    padding: 6px 0;
    max-height: 320px;
    display: flex;
    flex-direction: column;
    min-width: 180px;
    max-width: 300px;
  }
.db-popup-sort {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 4px 6px;
    border-bottom: var(--border-width) solid var(--border);
  }
.db-popup-sort-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    color: var(--fg-1);
    font-size: 12px;
    font-family: inherit;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 4px;
    text-align: left;
    width: 100%;
  }
.db-popup-sort-btn:hover { background: var(--bg-elev-2); }
.db-popup-sort-btn.active { color: var(--brand); }
.db-popup-sort-arrow { font-size: 10px; display: inline-block; width: 14px; text-align: center; }
.db-popup-unsort-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    color: var(--fg-3);
    font-size: 11px;
    font-family: inherit;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 4px;
    text-align: left;
    width: 100%;
  }
.db-popup-unsort-btn:hover { background: var(--bg-elev-2); color: var(--fg-1); }
.db-popup-search-wrap {
    padding: 6px 6px 4px;
    border-bottom: var(--border-width) solid var(--border);
  }
.db-popup-search {
    width: 100%;
    background: var(--bg-0);
    border: var(--border-width) solid var(--border);
    border-radius: 4px;
    padding: 4px 8px;
    color: var(--fg-1);
    font-size: 11px;
    font-family: inherit;
    box-sizing: border-box;
    outline: none;
  }
.db-popup-search:focus { border-color: var(--brand); }
.db-popup-search::placeholder { color: var(--fg-4); }
.db-popup-select-header {
    display: flex;
    justify-content: space-between;
    padding: 4px 10px 2px;
    font-size: 11px;
  }
.db-popup-select-header a {
    color: var(--brand);
    text-decoration: none;
    cursor: pointer;
  }
.db-popup-select-header a:hover { text-decoration: underline; }
.db-popup-items {
    flex: 1;
    overflow-y: auto;
    max-height: 180px;
    padding: 2px 0;
  }
.db-popup-items::-webkit-scrollbar { width: 4px; }
.db-popup-items::-webkit-scrollbar-thumb { background: var(--bg-tint); border-radius: 2px; }
.db-popup-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    font-size: 12px;
    cursor: pointer;
    user-select: none;
    max-width: 100%;
  }
.db-popup-item:hover { background: var(--bg-elev-2); }
.db-popup-item.unchecked .db-popup-val { opacity: 0.4; }
.db-popup-check { accent-color: var(--brand); cursor: pointer; margin: 0; flex-shrink: 0; }
.db-popup-val { 
    overflow: hidden; 
    text-overflow: ellipsis; 
    white-space: nowrap; 
    flex: 1; 
  }
.db-popup-empty { padding: 10px; color: var(--fg-3); font-size: 11px; text-align: center; }
.db-popup-status {
    padding: 4px 10px;
    font-size: 10px;
    color: var(--fg-3);
    border-top: var(--border-width) solid var(--border);
  }
.db-popup-actions {
    padding: 6px 10px;
    border-top: var(--border-width) solid var(--border);
    display: flex;
    justify-content: flex-end;
  }
.db-popup-hide-row {
    padding: 2px 10px;
    border-top: var(--border-width) solid var(--border);
  }
.db-popup-hide-btn {
    background: none;
    border: none;
    color: var(--fg-3);
    font-size: 11px;
    font-family: inherit;
    cursor: pointer;
    padding: 4px 4px;
    border-radius: 4px;
    width: 100%;
    text-align: left;
  }
.db-popup-hide-btn:hover {
    background: var(--bg-elev-2);
    color: var(--danger);
  }
.db-popup-apply-btn {
    background: var(--brand);
    color: var(--bg-0);
    border: none;
    border-radius: 4px;
    padding: 4px 16px;
    font-size: 12px;
    font-family: inherit;
    font-weight: 600;
    cursor: pointer;
  }
.db-popup-apply-btn:hover { background: var(--brand-strong); }

/* Drag-to-reorder */
  .db-th.drop-target {
    border-left: 2px solid var(--brand) !important;
  }
.db-th[draggable="true"] {
    cursor: grab;
  }
.db-th[draggable="true"]:active {
    cursor: grabbing;
  }

/* ═══════════════════════════════════════════════
   LIGHT MODE OVERRIDES
   ═══════════════════════════════════════════════ */
body.light-mode .db-col-popup {
  background: var(--bg-2);
  border-color: var(--border);
  box-shadow: 0 8px 32px rgba(var(--shadow-rgb), 0.1);
}
body.light-mode .db-popup-sort {
  border-bottom-color: var(--border);
}
body.light-mode .db-popup-sort-btn {
  color: var(--fg-1);
}
body.light-mode .db-popup-sort-btn:hover {
  background: var(--bg-elev-2);
}
body.light-mode .db-popup-sort-btn.active {
  color: var(--brand);
}
body.light-mode .db-popup-unsort-btn {
  color: var(--fg-3);
}
body.light-mode .db-popup-unsort-btn:hover {
  background: var(--bg-elev-2);
  color: var(--fg-1);
}
body.light-mode .db-popup-search {
  background: var(--bg-0);
  border-color: var(--border);
  color: var(--fg-1);
}
body.light-mode .db-popup-search:focus {
  border-color: var(--brand);
}
body.light-mode .db-popup-search::placeholder {
  color: var(--fg-4);
}
body.light-mode .db-popup-select-header a {
  color: var(--brand);
}
body.light-mode .db-popup-items::-webkit-scrollbar-thumb {
  background: var(--bg-tint);
}
body.light-mode .db-popup-item {
  color: var(--fg-1);
}
body.light-mode .db-popup-item:hover {
  background: var(--bg-elev-2);
}
body.light-mode .db-popup-item.unchecked .db-popup-val {
  opacity: 0.3;
}
body.light-mode .db-popup-check {
  accent-color: var(--brand);
}
body.light-mode .db-popup-empty {
  color: var(--fg-3);
}
body.light-mode .db-popup-status {
  color: var(--fg-3);
  border-top-color: var(--border);
}
body.light-mode .db-popup-actions {
  border-top-color: var(--border);
}
body.light-mode .db-popup-apply-btn {
  background: var(--brand);
  color: #ffffff; /* KEEP (intentional): contrast ink on the brand-filled button */
}
body.light-mode .db-popup-apply-btn:hover {
  background: var(--brand-strong);
}
body.light-mode .th-text:hover {
  background: var(--bg-elev-2);
}
body.light-mode .db-popup-hide-row {
  border-top-color: var(--border);
}
body.light-mode .db-popup-hide-btn {
  color: var(--fg-3);
}
body.light-mode .db-popup-hide-btn:hover {
  background: var(--bg-elev-2);
  color: var(--danger);
}
body.light-mode .th-hide-btn {
  color: var(--fg-4);
}
body.light-mode .th-hide-btn:hover {
  color: var(--fg-1);
}
body.light-mode .th-hide-btn.hidden-col {
  color: var(--danger);
}
body.light-mode .db-cell-expand {
  background: var(--bg-0);
  border-color: var(--border);
  color: var(--fg-4);
}
body.light-mode .db-cell-expand:hover {
  color: var(--brand);
  border-color: var(--brand);
}
body.light-mode .db-cell-edit {
  background: var(--bg-0);
  border-color: var(--border);
  color: var(--fg-4);
}
body.light-mode .db-cell-edit:hover {
  color: var(--warning);
  border-color: var(--warning);
}
body.light-mode .db-row-delete {
  background: var(--bg-0);
  border-color: var(--border);
  color: var(--fg-4);
}
body.light-mode .db-row-delete:hover {
  color: var(--danger);
  border-color: var(--danger);
}
body.light-mode .db-edit-input {
  background: var(--bg-0);
  border-color: var(--brand);
  color: var(--fg-1);
}
body.light-mode #cell-modal-backdrop {
  background: rgba(255, 250, 245, 0.7); /* KEEP (intentional): light-mode warm-white veil scrim — deliberately lightens, no neutral/shadow token fits */
  backdrop-filter: blur(3px);
}
body.light-mode #cell-modal-close {
  color: var(--fg-4);
}
body.light-mode #cell-modal-close:hover {
  color: var(--danger);
}

body.light-mode .db-th.filtered .th-name {
  color: var(--brand);
}
body.light-mode .th-funnel {
  color: var(--brand);
}

/* ═══════════════════════════════════════════
   App Config UI — hosted inside Admin Tools' Settings view
═══════════════════════════════════════════ */

#app-config-container {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  /* Single source of truth for the scroll-area side inset (the gap between the
     panel edge and the settings content column). Used by the scroller's side
     margins AND the debug corner-frame below so they can never drift apart.
     Narrowed to 10px on mobile via the @media (max-width: 800px) override near
     the bottom of this block. */
  --ac-content-inset: 20px;
  /* Transparent so the host admin-tools panes (made transparent while the
     settings view is active — see "App Config transparency" block below) let
     the floating #main-panel's dynamic glass + spotlight show through, exactly
     like the Agents page. No solid fill of our own. */
  background: transparent;
}
#app-config-container[hidden] { display: none; }
/* Mobile / narrow screens: halve the App Settings side inset (20px → 10px) so the
   settings column gets back ~20px of width on small viewports. One override drives
   both the scroller margins and the debug corner-frame (both read
   --ac-content-inset). 800px matches the app-wide mobile breakpoint. */
@media (max-width: 800px) {
  #app-config-container { --ac-content-inset: 10px; }
}

/* ── App Config transparency ──
   The App Configuration page lives inside the Admin-Tools "Files" workspace,
   whose panes (#tab-admin-tools → .admin-tools → #files-settings-main) paint an
   opaque flat grey (--files-bg) for the code-editor / terminal views. While the
   *settings* view is showing, drop that grey on all three so the floating
   #main-panel's dynamic glass + cursor spotlight shows through — giving the App
   Configuration page the same living background as the Agents page, instead of a
   flat fill. Scoped via :has() to the settings pane being visible, so the Files
   editor, terminal, git, etc. keep their grey. */
#files-settings-main { background: transparent; }
.admin-tools:has(> #files-settings-main:not([hidden])),
#tab-admin-tools:has(#files-settings-main:not([hidden])) { background: transparent; }

/* Top header */
#app-config-header {
	  flex-shrink: 0;
	  padding: 18px 20px 12px;
	  background: transparent;
	}
.ac-page-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--fg-1, var(--fg-1));
  letter-spacing: 0.2px;
}

/* Tab strip with chevron-scroll carousel (mirrors agent-detail-tabs) */
#app-config-tabs-wrap {
	  position: relative;
	  display: block;
	  border-bottom: var(--border-width) solid var(--border);
	  flex-shrink: 0;
	  background: transparent;
	}
#app-config-tabs {
  display: flex;
  min-width: 0;
  padding: 0 18px;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
#app-config-tabs::-webkit-scrollbar { display: none; }
/* SISTER-CAROUSEL: ACTIVE-TAB-GROW — shared formatting with the agent-card tab
   carousel (`.agent-card-tabs .agents-detail-tab` in ui/main-panel/agents/agents.css).
   Both are horizontal chevron-scroll tab strips where the ACTIVE tab renders a
   couple px larger than the rest, with `font-size` in the transition so the
   change animates as you switch tabs. Keep the two in sync: any change to the
   active-tab grow/animation here must be mirrored there (and vice-versa). */
.ac-tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--fg-muted, var(--fg-3));
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  transition: color 0.15s, border-color 0.15s, font-size 0.15s;
  margin-bottom: -1px;
  white-space: nowrap;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.ac-tab:hover { color: var(--fg-1, var(--fg-1)); }
.ac-tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-size: 14px;
}
.ac-tab-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}
.ac-tab-count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 14px;
  padding: 0 4px;
  margin-left: 4px;
  border-radius: 7px;
  background: var(--bg-tint);
  color: var(--fg-2);
  font-size: 9px;
  font-weight: 700;
  line-height: 14px;
  vertical-align: middle;
}
.ac-tab.active .ac-tab-count-badge {
  background: var(--fg-4);
  color: var(--fg-1);
}
body.light-mode .ac-tab-count-badge {
  background: var(--bg-tint);
  color: var(--fg-2);
}
body.light-mode .ac-tab.active .ac-tab-count-badge {
  background: var(--border);
  color: var(--fg-1);
}
/* Masked edge-fade carousel — same technique as the agents carousel
   (.agents-carousel-chev / .agent-card-tabs-chev in ui/main-panel/agents/agents.css):
   never paint a colour to fake an edge fade. The scroller (#app-config-tabs) gets
   an alpha mask that fades the tabs to transparent at a scrollable edge, so it
   blends into whatever panel background sits behind it in either theme; the
   chevron button itself is transparent and just sits over the faded edge. */
#app-config-tabs-wrap.can-scroll-right #app-config-tabs {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent 100%);
          mask-image: linear-gradient(to right, #000 calc(100% - 44px), transparent 100%);
}
#app-config-tabs-wrap.can-scroll-left #app-config-tabs {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 44px);
          mask-image: linear-gradient(to right, transparent 0, #000 44px);
}
#app-config-tabs-wrap.can-scroll-left.can-scroll-right #app-config-tabs {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 44px, #000 calc(100% - 44px), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 44px, #000 calc(100% - 44px), transparent 100%);
}
.ac-tabs-chev {
  display: none;
  position: absolute;
  top: 0;
  bottom: 0;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--accent);
  cursor: pointer;
  font-size: 14px;
  width: 36px;
  padding: 0;
  font-family: inherit;
  z-index: 2;
}
.ac-tabs-chev.left {
	  left: 0;
	  justify-content: flex-start;
	  padding-left: 4px;
	}
	.ac-tabs-chev.right {
	  right: 0;
	  justify-content: flex-end;
	  padding-right: 4px;
	}
.ac-tabs-chev.visible { display: flex; }
.ac-tabs-chev:hover { color: var(--fg-1, var(--fg-1)); }

/* Right scrollable content */
#app-config-content {
  flex: 1;
  overflow-y: auto;
  /* `.rounded-scroll-corners` (design-system.css) is on this element in the
     markup, but kept INERT here — this scroller is shared by all 10 App-Config
     tabs, so a 0 radius makes the mask a full-opaque no-op. Only the App Settings
     gate below switches it on (raises --rsc-r + sets the centred-column inset). */
  --rsc-r: 0px;
  /* ROUNDED SCROLL VIEWPORT — the scroller clips its content to rounded corners,
     so WHATEVER sits at the top/bottom edge of the view (a sticky section heading
     OR a scrolling content row) is cut to the rounded shape instead of poking out
     square. overflow-y:auto already makes this a clip context, so the radius
     rounds the corners. The horizontal inset is a MARGIN, not padding: padding
     would hold the content away from the scroller's rounded edge (so the rounding
     would never touch it), whereas a margin shrinks the scroller box itself to
     where the content sits, so the content reaches — and is clipped by — the
     rounded corners. Keep the 28px top / 60px bottom as padding (breathing room +
     the bottom clearance the sticky-heading math is keyed to). */
  margin: 0 var(--ac-content-inset);
  /* No bottom padding: the content must reach the scroller's rounded BOTTOM edge
     so it gets clipped to the rounded corner at max scroll (the old 60px was
     clearance for the pill back when it floated INSIDE the scroller — the pill is
     now a sibling below, so it's no longer needed). Top keeps 28px breathing
     under the tab strip; sticky-nav cancels it for the first heading via
     `.ac-stickynav { margin-top: -28px }`. */
  padding: 28px 0 0;
  border-radius: var(--r-lg);
  display: flex;
  flex-direction: column;
  gap: 40px;
  scrollbar-width: thin;
  scrollbar-color: var(--border, var(--bg-tint)) transparent;
}
#app-config-content::-webkit-scrollbar { width: 5px; }
#app-config-content::-webkit-scrollbar-thumb {
  background: var(--border, var(--bg-tint));
  border-radius: 3px;
}
/* Round the scroll-viewport corners on the App Settings, Agent Settings AND Data
   Management tabs — via the reusable `.rounded-scroll-corners` mask
   (design-system.css), fed a CENTRED-COLUMN inset so the rounding hugs the 750px
   column, not the full panel width. The class is on #app-config-content (markup)
   but inert by default (--rsc-r: 0 in the base rule, since the scroller is shared
   by all 10 tabs); here — only while one of these standard 750px-column config
   sections is active — we switch it on by handing it a radius + the per-side inset.
   All three pages share the same centred-column layout (each is an `ac-stickynav`
   section whose `.ac-list` tables cap at 750px), so the rounded TOP corners read
   identically. All three now also have a bottom page-assistant bar below the
   scroller (App Settings' #ac-pa-bar, Agent Settings' #ac-as-pa-bar, Data
   Settings' #ac-ds-pa-bar), so the scroller shrinks to sit above the pill and
   its rounded BOTTOM corners pin just above it — the same convex shape on every
   tab.
   scrollbar-gutter keeps a stable strip on both sides so the column math (in
   #app-config-scroll-wrap below) floors onto the real gutter when the panel is
   narrow. The corners reveal the panel's own dynamic gradient, so they stay
   theme-correct. Doc: ui-guidance "Pinned rounded scroll corners". */
#app-config-container:has(#ac-section-data-settings.active) #app-config-content,
#app-config-container:has(#ac-section-app-settings.active) #app-config-content,
#app-config-container:has(#ac-section-agent-settings.active) #app-config-content {
  scrollbar-gutter: stable both-edges;
  --rsc-r: var(--ac-rnd-r);
  --rsc-side: var(--ac-rnd-side);
}
/* Mobile: drop the painted-on (masked) rounded scroll corners ENTIRELY — the
   centred-column mask inset only makes sense on a wide screen (where the 750px
   column sits inside a fat gutter). On a narrow phone panel the column is
   full-width, so the mask just doubled up on the scroller's OWN `border-radius`.
   A 0 mask radius makes the mask a full-opaque no-op, so the panel falls back to
   plain, normal border-radius corners like every other panel. Desktop keeps the
   mask (its whole point is the column inset). */
@media (max-width: 800px) {
  #app-config-container:has(#ac-section-data-settings.active) #app-config-content,
  #app-config-container:has(#ac-section-app-settings.active) #app-config-content,
  #app-config-container:has(#ac-section-agent-settings.active) #app-config-content {
    --rsc-r: 0px;
  }
}
/* Non-scrolling wrapper (markup in app-config.html) whose sole job is to be the
   query CONTAINER that sizes the centred-column corner mask. It takes over the
   scroller's flex:1 role; the scroller keeps its --ac-content-inset side margins
   inside it, so the scroller's box is unchanged. `container-type: inline-size`
   lets the rounding mask on the scroller resolve the 750px column from this
   wrapper's real width via cqw units. */
#app-config-scroll-wrap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  container-type: inline-size;
  /* Distance from the scroller's border edge IN to the 750px column edge.
     100cqw = wrapper width; scroller width = wrapper minus its two
     --ac-content-inset margins, so the wide "centring gutter" is
     (scrollerWidth - 750)/2. Floored at --ac-rnd-gutter (the per-side scrollbar
     gutter the scroller reserves both-edges, ~10px) so when the panel is narrower
     than the column the corners still hug the actual gutter-inset content edge. */
  --ac-rnd-gutter: 10px;
  --ac-rnd-side: max(var(--ac-rnd-gutter),
                     calc((100cqw - (2 * var(--ac-content-inset)) - 750px) / 2));
  --ac-rnd-r: var(--r-lg);
}
/* Section partials are injected into always-present wrapper slots
   (#ac-section-slot-*). Those wrappers must NOT be flex items, or the
   parent's `gap: 40px` fires between all 10 empty slots and stacks ~360px of
   phantom space above the active section. `display: contents` collapses the
   wrappers so the real `.ac-section` elements are the flex items again — and
   inactive sections (display:none) contribute no gap, as before the refactor. */
#app-config-content > [id^="ac-section-slot-"] { display: contents; }

/* Section */
.ac-section {
  display: none;
  flex-direction: column;
  gap: 16px;
}
.ac-section.active {
  display: flex;
}

/* ── App Config page-assistant pill (App Settings) ──────────────────────────
   The "advanced chat pill" docked at the bottom of the App Config panel. It is a
   SIBLING of the scroller (#app-config-content), NOT a child of it — see the
   markup note in app-config.html. #app-config-container is a flex column, so the
   scroller (flex:1) shrinks to sit ABOVE this pill; the scroll viewport therefore
   ENDS above the pill and clips its content there, so page rows can never scroll
   behind the pill. The pill area itself is transparent, so the floating panel's
   page-background gradient shows through the breathing-room gap around the pill
   (no opaque fill needed). It is capped to the same 750px column as the tables
   and centred to share their left/right edges. Geometry + the floating-glass
   skin come from the shared .chat-pill* classes (app1.css + the glass re-skin in
   index.css).

   SCOPED TO APP SETTINGS: the pill lives at the container level (always in the
   DOM), so it is hidden by default and revealed only while the App Settings
   section is the active one — the `:has()` rule below checks for it. */
/* Shared floating-bar layout for BOTH per-page assistant bars — App Settings'
   #ac-pa-bar and Agent Settings' #ac-as-pa-bar. Each is hidden by default and
   revealed only on its own active tab by the SEPARATE :has() rules below. */
#ac-pa-bar,
#ac-as-pa-bar,
#ac-ds-pa-bar {
  display: none;
  flex-shrink: 0;
  align-self: center;
  width: 100%;
  max-width: 750px;
  box-sizing: border-box;
  /* Breathing room: a gap above (between the scroller's floor / lowest heading
     and the pill) and below (between the pill and the panel floor). Both gaps are
     transparent, so the page gradient shows through them. */
  padding: 12px 20px 14px;
  background: transparent;
}
/* Reveal the pill only while App Settings is the active section. */
#app-config-container:has(#ac-section-app-settings.active) #ac-pa-bar {
  display: flex;
  flex-direction: column;
}
/* Agent Settings has its OWN bottom bar, revealed only on its own tab. Keep this a
   SEPARATE rule — comma-merging the two selectors would reveal both bars on either
   tab. */
#app-config-container:has(#ac-section-agent-settings.active) #ac-as-pa-bar {
  display: flex;
  flex-direction: column;
}
/* Data Settings has its OWN bottom bar (a composer twin of #ac-pa-bar), revealed
   only on its own tab. Kept a SEPARATE rule for the same reason as above. */
#app-config-container:has(#ac-section-data-settings.active) #ac-ds-pa-bar {
  display: flex;
  flex-direction: column;
}
#ac-pa-bar > .chat-pill,
#ac-ds-pa-bar > .chat-pill { width: 100%; }
/* The Agent Settings bar hosts the shared SEARCH/CHAT pill, whose wrapper is
   .conn-search-wrap.ability-search-pill-wrap (not a bare .chat-pill). Make it fill
   the bar and drop the inline-flow bottom margin it carried in its old top
   placement (spacing now comes from the bar's own padding). */
#ac-as-pa-bar > .conn-search-wrap { width: 100%; }
#ac-as-pa-bar .ability-search-pill-wrap { margin-bottom: 0; }
#ac-pa-input,
#ac-ds-pa-input { max-height: 160px; }
#ac-pa-preview-bar,
#ac-ds-pa-preview-bar {
  pointer-events: auto;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}

/* ── Mobile keyboard: clear the bottom clutter off a focused field ───────────
   When the on-screen keyboard is up (`body.kb-open`, set mobile-only by the
   viewport tracker in index.html), the App Config pages would otherwise crowd a
   focused field — e.g. the Models table "Search models…" input — behind two
   bottom-docked elements: the page-assistant chat pill and the BOTTOM-pinned
   sticky section headings. While the keyboard is open we get both out of the way
   so the field (and its model dropdown) own the shrunken viewport:
     • the chat pills (#ac-pa-bar / #ac-as-pa-bar) hide outright — `!important`
       beats their id-heavy `:has()` reveal rule above;
     • the sticky headings drop to `position: relative` so they stop stacking at
       the top/bottom edges and just scroll with the page (the higher-specificity
       selector wins over the base `position: sticky`; restored on keyboard close,
       inline top/bottom offsets from initStickyNav stay valid). `relative` (not
       `static`) keeps the row a containing block for the absolutely-positioned
       `.ac-theme-switch` in the Design header, so the switch stays pinned to its
       heading row instead of floating loose over the page. */
body.kb-open #ac-pa-bar,
body.kb-open #ac-as-pa-bar,
body.kb-open #ac-ds-pa-bar { display: none !important; }
body.kb-open .ac-stickynav .ac-category-summary { position: relative; }

.ac-section-header {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.ac-section-icon {
  width: 20px;
  height: 20px;
  color: var(--accent);
  flex-shrink: 0;
  margin-top: 3px;
}
.ac-section-title {
  margin: 0 0 4px;
  font-size: 16px;
  font-weight: 700;
  color: var(--fg-1, var(--fg-1));
}
.ac-section-desc {
  margin: 0;
  font-size: 12px;
  color: var(--fg-muted, var(--fg-3));
}

/* Card */
.ac-card {
  background: var(--bg-elev, var(--bg-elev));
  border: var(--border-width) solid var(--border);
  border-radius: 10px;
  padding: 18px 20px;
}
.ac-card-label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--fg-2, var(--fg-2));
}
.ac-card-sublabel {
  font-size: 11px;
  color: var(--fg-muted, var(--fg-3));
  margin-top: 2px;
}

/* Collapsible card (<details class="ac-card">). The default disclosure
   triangle is hidden; the card label gets its own chevron that rotates
   on open/close. Default state is `open` so behavior matches non-collapsible
   cards for first-time visitors. */
details.ac-card > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
}
details.ac-card > summary::-webkit-details-marker { display: none; }
details.ac-card > summary::after {
  content: "";
  display: inline-block;
  margin-left: auto;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--fg-3, var(--fg-3));
  border-bottom: 2px solid var(--fg-3, var(--fg-3));
  transform: rotate(45deg) translateY(-2px);
  transition: transform 0.18s ease;
  flex-shrink: 0;
}
details.ac-card[open] > summary::after {
  transform: rotate(-135deg) translateY(2px);
}
details.ac-card > summary.ac-card-label {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Hide everything that is not the summary when collapsed, restore when open. */
details.ac-card:not([open]) > *:not(summary) { display: none; }

/* Field */
.ac-field {
  margin-top: 14px;
}
.ac-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-2, var(--fg-2));
  margin-bottom: 6px;
}
.ac-input {
  width: 100%;
  padding: 9px 12px;
  background: var(--bg-base, var(--bg-0));
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
  color: var(--fg-1, var(--fg-1));
  font-size: 13px;
  font-family: inherit;
  box-sizing: border-box;
  transition: border-color 0.15s;
}
.ac-input:focus {
  outline: none;
  border-color: var(--accent);
}
.ac-hint {
  margin-top: 4px;
  font-size: 11px;
  color: var(--fg-muted, var(--fg-3));
}

/* Actions row */
.ac-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  flex-wrap: wrap;
}
.ac-btn {
  padding: 8px 18px;
  border-radius: 6px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  transition: opacity 0.15s, background 0.15s;
  border: none;
}
.ac-btn:hover { opacity: 0.85; }
.ac-btn-primary {
  background: var(--accent);
  color: var(--bg-0);
}
.ac-btn-ghost {
  background: transparent;
  border: 1px solid var(--fg-muted, var(--fg-3));
  color: var(--fg-muted, var(--fg-3));
}
.ac-status {
  font-size: 12px;
}

/* Model dropdown */
.ac-model-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  max-height: 260px;
  overflow-y: auto;
  background: var(--bg-elev);
  border: var(--border-width) solid var(--border);
  border-radius: 0 0 6px 6px;
  z-index: 20;
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb), 0.4);
}
.ac-model-item {
  padding: 8px 12px;
  cursor: pointer;
  font-size: 12px;
  color: var(--fg-1, var(--fg-1));
}
.ac-model-item:hover { background: rgba(var(--brand-rgb),0.08); }
.ac-model-item-row { display: flex; align-items: baseline; gap: 4px; min-width: 0; }
.ac-model-item-badges { display: flex; gap: 6px; margin-top: 4px; flex-wrap: wrap; }

/* Context / cost badges (model rows + selected-model panel) */
.ac-model-badge {
  display: inline-block;
  padding: 1px 7px;
  font-size: 10.5px;
  font-weight: 500;
  line-height: 1.6;
  color: var(--fg-2);
  background: var(--bg-base, var(--bg-0));
  border: var(--border-width) solid var(--border);
  border-radius: 999px;
  white-space: nowrap;
}

/* Selected-model metadata panel (chips + description) */
.ac-model-meta {
  margin-top: 8px;
  padding: 10px 12px;
  background: var(--bg-elev);
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
}
.ac-model-meta-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; }
.ac-model-meta-chips:last-child { margin-bottom: 0; }
.ac-model-meta-desc {
  font-size: 12px;
  line-height: 1.5;
  color: var(--fg-2);
}

/* Segment buttons (cloud/local,
live/scheduled) */
.ac-seg-btn {
  text-align: center;
  padding: 14px 12px;
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
  background: var(--bg-base, var(--bg-0));
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  color: var(--fg-1, var(--fg-1));
  font-family: inherit;
  transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.ac-seg-btn.active {
  border-color: var(--accent);
  background: rgba(var(--brand-rgb),0.08);
  color: var(--accent);
}
.ac-seg-sub {
  font-weight: 400;
  font-size: 11px;
  color: var(--fg-muted, var(--fg-3));
  display: block;
  margin-top: 4px;
}
.ac-seg-btn.active .ac-seg-sub { color: var(--accent); opacity: 0.7; }

/* Info banner */
.ac-info-banner {
  padding: 12px 14px;
  background: var(--bg-base, var(--bg-0));
  border-radius: 8px;
  border-left: 3px solid var(--accent);
  font-size: 12px;
  color: var(--fg-2, var(--fg-2));
  line-height: 1.5;
}
.ac-tab-link {
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
}

/* Stats table */
.ac-stats-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}
.ac-stats-table th {
  padding: 8px 10px;
  text-align: left;
  color: var(--fg-2, var(--fg-2));
  font-weight: 600;
  white-space: nowrap;
  background: var(--bg-base, var(--bg-0));
  border-bottom: var(--border-width) solid var(--border);
}
.ac-stats-table td {
  padding: 7px 10px;
  color: var(--fg-1, var(--fg-1));
  border-bottom: var(--border-width) solid var(--border);
  white-space: nowrap;
}
.ac-stats-table tr:last-child td { border-bottom: none; }
.ac-table-empty {
  text-align: center;
  color: var(--fg-muted, var(--fg-3));
  padding: 20px !important;
}

/* ── Optimizer Runs dashboard ───────────────────────────────────────
   Sortable / filterable runs table + expandable per-stage detail. Built on
   .ac-stats-table; all colours derive from the design-system palette. Markup:
   ui/admin-tools/app-config/optimizer/optimizer.html · logic: optimizer-stats.js */
.ac-opt-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; margin-bottom: 10px;
}
.ac-opt-toolbar-left { display: flex; align-items: center; gap: 8px; }
.ac-opt-toolbar-right { display: flex; align-items: center; gap: 8px; }
.ac-opt-count { font-size: 11px; color: var(--fg-muted, var(--fg-3)); font-weight: 400; }
.ac-opt-search { width: 160px; }
.ac-opt-filter { width: auto; }
.ac-opt-icon-btn { padding: 6px 8px; }
.ac-opt-table-wrap {
  overflow-x: auto;
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
}
.ac-opt-runs-table { margin: 0; }

/* Sortable headers */
.ac-opt-sort { cursor: pointer; user-select: none; position: relative; }
.ac-opt-sort:hover { color: var(--accent); }
.ac-opt-sort.sort-asc::after  { content: ' ▲'; font-size: 9px; color: var(--accent); }
.ac-opt-sort.sort-desc::after { content: ' ▼'; font-size: 9px; color: var(--accent); }
.ac-opt-num { text-align: right; }

/* Run rows */
.ac-opt-run-row { cursor: pointer; }
.ac-opt-run-row:hover td { background: rgba(var(--brand-rgb), 0.05); }
.ac-opt-run-row.open td { background: rgba(var(--brand-rgb), 0.07); }
.ac-opt-target { display: flex; align-items: center; gap: 6px; max-width: 220px; }
.ac-opt-target span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-opt-caret { width: 13px; height: 13px; flex: 0 0 auto; color: var(--fg-muted, var(--fg-3)); }
.ac-opt-arrow { color: var(--fg-muted, var(--fg-3)); }
.ac-opt-date { font-size: 10px; color: var(--fg-muted, var(--fg-3)); white-space: nowrap; }

/* Metric value + signed delta */
.ac-opt-metric-val { font-weight: 600; }
.ac-opt-delta { font-size: 10px; font-weight: 600; }
.ac-opt-delta.good { color: var(--success); }
.ac-opt-delta.bad  { color: var(--danger); }

/* Status / stage badges */
.ac-opt-badge {
  display: inline-block; padding: 2px 8px; border-radius: 10px;
  font-size: 10px; font-weight: 600; white-space: nowrap;
}
.ac-opt-badge.status-running  { background: rgba(var(--brand-rgb), 0.15);   color: var(--brand); }
.ac-opt-badge.status-success  { background: rgba(var(--success-rgb), 0.15); color: var(--success); }
.ac-opt-badge.status-failed   { background: rgba(var(--danger-rgb), 0.15);  color: var(--danger); }
.ac-opt-badge.stage-planner   { background: rgba(var(--purple-rgb), 0.15);  color: var(--purple); }
.ac-opt-badge.stage-worker    { background: rgba(var(--brand-rgb), 0.15);   color: var(--brand); }
.ac-opt-badge.stage-closer    { background: rgba(var(--warning-rgb), 0.15); color: var(--warning); }
.ac-opt-badge.stage-deployed  { background: rgba(var(--success-rgb), 0.15); color: var(--success); }

/* Expanded per-stage detail row */
.ac-opt-detail-row td { background: rgba(var(--brand-rgb), 0.03); padding: 0 !important; }
.ac-opt-detail-loading {
  display: flex; align-items: center; gap: 6px;
  padding: 14px; font-size: 11px; color: var(--fg-muted, var(--fg-3));
}
.ac-opt-stages { display: flex; gap: 10px; flex-wrap: wrap; padding: 12px 14px; }
.ac-opt-stage {
  flex: 1 1 180px; min-width: 170px;
  border: var(--border-width) solid var(--border);
  border-radius: 6px; padding: 10px; background: var(--bg-1, var(--bg-0));
  display: flex; flex-direction: column; gap: 6px;
}
.ac-opt-stage-head { display: flex; align-items: center; gap: 6px; }
.ac-opt-stage-icon { width: 14px; height: 14px; color: var(--accent); flex: 0 0 auto; }
.ac-opt-stage-label { font-size: 12px; font-weight: 600; color: var(--fg-1); }
.ac-opt-stage-meta { margin-left: auto; font-size: 10px; color: var(--fg-muted, var(--fg-3)); }
.ac-opt-stage-detail { font-size: 11px; color: var(--fg-2); line-height: 1.4; }
.ac-opt-stage-action { margin-top: auto; }
.ac-opt-open-btn { padding: 4px 10px; font-size: 11px; display: inline-flex; align-items: center; gap: 5px; }
.ac-opt-stage-noopen { font-size: 10px; color: var(--fg-muted, var(--fg-3)); font-style: italic; }

/* User Management — access mode radios */
.ac-um-radio {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  background: var(--bg-elev, var(--bg-elev));
  transition: border-color 0.15s, background 0.15s;
}
.ac-um-radio:hover {
  border-color: var(--accent);
}
.ac-um-radio input[type=radio] {
  margin-top: 3px;
  accent-color: var(--accent);
  flex-shrink: 0;
}
.ac-um-radio:has(input[type=radio]:checked) {
  border-color: var(--accent);
  background: rgba(var(--brand-rgb), 0.06);
}
.ac-um-radio-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-1, var(--fg-1));
}
.ac-um-radio-desc {
  font-size: 12px;
  color: var(--fg-muted, var(--fg-3));
  margin-top: 2px;
  line-height: 1.4;
}

/* App Access (Data Settings) — radio VARIANT of the shared toggle-list. Each
   access mode is a standard `.ac-ability-row` inside `.ac-list`, but the whole
   row is a <label> so clicking anywhere selects that mode, and the right-hand
   control is a native radio (accent-tinted) instead of a toggle. The selected
   row gets the same subtle accent wash the abilities tree uses for an enabled
   row, so the current choice reads at a glance. See app-access.js + ui-guidance
   "Toggle-list" (a row's control can be a radio). */
.ac-access-row { cursor: pointer; }
.ac-access-row:has(.ac-access-radio:checked) {
  background: rgba(var(--brand-rgb), 0.06);
}
.ac-access-radio-cell {
  /* Keep the radio vertically centred on the row's first (title) line and give
     the on-top save overlay a box to cover — _markSaving sets this relative. */
  display: flex;
  align-items: center;
  padding-top: 1px;
}
.ac-access-radio {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}

/* Danger Zone (Data Settings) — a checkbox VARIANT of the shared toggle-list.
   Each reset target is a `.ac-ability-row` wrapped in a <label> (so the whole row
   toggles its checkbox), with a danger-tinted native checkbox on the right. A
   ticked row gets a subtle danger wash so the current selection reads at a glance.
   The card heading + icons are danger-coloured to signal the destructive intent.
   See danger-zone.js + the #ac-danger-card block in data-settings.html. */
.ac-dz-row { cursor: pointer; }
.ac-dz-row:has(.ac-dz-check:checked) {
  background: rgba(var(--danger-rgb, 220, 80, 80), 0.07);
}
.ac-dz-check-cell {
  display: flex;
  align-items: center;
  padding-top: 1px;
}
.ac-dz-check {
  width: 16px;
  height: 16px;
  accent-color: var(--danger);
  cursor: pointer;
  flex-shrink: 0;
}

/* Hold-to-fire buttons (Reset / Delete installation). Press and HOLD: a danger
   fill sweeps left→right beneath the label; hold to the end and the action fires
   (the hold IS the confirmation — no dialog). Same affordance as the deploy card's
   hold-to-restart button. The fill's transition duration is driven from JS via the
   `--dz-hold` custom property so CSS + the JS fire-timer can never drift.
   See danger-zone.js `_beginHold`. */
.ac-dz-hold {
  position: relative;
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;           /* a touch-hold shouldn't scroll the page */
}
.ac-dz-hold .ac-dz-fill {
  position: absolute;
  inset: 0;
  z-index: 0;
  transform: scaleX(0);
  transform-origin: left center;
  background: rgba(var(--danger-rgb, 220, 80, 80), 0.30);
  transition: transform 150ms ease;    /* quick snap-back on release / abort */
  pointer-events: none;
}
.ac-dz-hold.holding .ac-dz-fill {
  transform: scaleX(1);
  transition: transform var(--dz-hold, 1500ms) linear;   /* == the JS fire timer */
}
.ac-dz-hold .ac-dz-hold-label {
  position: relative;
  z-index: 1;
}
/* Armed (past the arm threshold): intensify the danger tint so it reads as "committed". */
.ac-dz-hold.warning {
  color: var(--danger);
  border-color: rgba(var(--danger-rgb, 220, 80, 80), 0.75);
}
.ac-dz-hold[disabled] .ac-dz-fill { transform: scaleX(0); }

/* ── App Access · Social sign-in (data-settings.html) ──
   The social providers live in the SAME `.ac-list` tree as the access-mode
   radios, styled like the ability tree: a "Sign in with" group-header row over
   individual ability-style provider rows. The list itself draws the radio↔header
   and header↔first-provider hairlines; only the provider↔provider hairlines need
   a rule (the providers are wrapped in the `#ac-social-list` block). */
#ac-social-list > * + * { border-top: var(--border-width) solid var(--border); }

/* "Sign in options" header row — reads like an ability-group head (chevron +
   icon + title + count) and is now an EXPAND target: clicking it toggles the
   provider list below (collapsed by default). */
.ac-access-subhead { cursor: pointer; }
.ac-access-subhead .ac-category-count { align-self: center; margin-left: 8px; }
/* Collapse the provider list under the "Sign in options" head; the adjacent
   sibling reveal opens it when the head carries `.expanded` (comments between
   the two elements are ignored by the sibling combinator). */
#ac-social-list { display: none; }
.ac-access-subhead.expanded + #ac-social-list { display: block; }
.ac-access-subhead.expanded .ac-row-chevron--left { transform: rotate(90deg); }

/* Provider brand mark sits in the shared 28px icon column; scale the 18px brand
   SVG down to the 16px the ability rows use. Google / Microsoft carry their own
   brand colours inline; the monochrome marks (GitHub, Apple, generic) use
   currentColor, so tint them with the foreground for correct dark + light. */
.ac-social-brand { color: var(--fg-1); }
.ac-social-brand svg { width: 16px; height: 16px; display: block; }

/* User Management — pill badges in the table */
.ac-um-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: 10px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  white-space: nowrap;
}
.ac-um-badge-admin { background: rgba(var(--purple-rgb),0.18); color: var(--purple); }
.ac-um-badge-member { background: rgba(var(--brand-rgb),0.14); color: var(--brand); }
.ac-um-badge-approved { background: rgba(var(--success-rgb),0.16); color: var(--success); }
.ac-um-badge-pending { background: rgba(var(--warning-rgb),0.18); color: var(--warning); }
.ac-um-badge-locked { background: rgba(var(--danger-rgb),0.18); color: var(--danger); }

.ac-um-action-btn {
  background: transparent;
  border: var(--border-width) solid var(--border);
  color: var(--fg-1, var(--fg-1));
  padding: 3px 9px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 10px;
  font-family: inherit;
  margin: 0 2px;
}
.ac-um-action-btn:hover { border-color: var(--accent); color: var(--accent); }
.ac-um-action-btn.danger:hover { border-color: var(--danger); color: var(--danger); }

/* Social Sign-In providers (drop-in) — the expandable body content. The rows
   themselves reuse the shared ability-row / `.conn-toggle` look (see the App
   Access · Social sign-in block above); these rules only style what's INSIDE an
   expanded provider's `.ac-ability-body`: the keys form, redirect URI + save.
   Theme via tokens only (dark + light correct). */
.ac-social-check.saved { color: var(--success); }
.ac-social-check.error { color: var(--danger); }
.ac-social-detail {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ac-social-flabel {
  font-size: 11px;
  font-weight: 600;
  color: var(--fg-muted, var(--fg-3));
  margin-top: 6px;
}
.ac-social-field {
  background: var(--bg-1, var(--bg));
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
  color: var(--fg-1);
  padding: 7px 9px;
  font-size: 12px;
  font-family: inherit;
  width: 100%;
  box-sizing: border-box;
}
.ac-social-field:focus { outline: none; border-color: var(--accent); }
.ac-social-redirect { display: flex; align-items: center; gap: 8px; }
.ac-social-redirect code {
  flex: 1;
  background: rgba(var(--brand-rgb), 0.08);
  border-radius: 6px;
  padding: 7px 9px;
  font-size: 11px;
  color: var(--fg-1);
  overflow-x: auto;
  white-space: nowrap;
}
.ac-social-req, .ac-social-hint {
  font-size: 11px;
  color: var(--fg-muted, var(--fg-3));
  line-height: 1.45;
}
.ac-social-setup { font-size: 11px; color: var(--accent); text-decoration: none; }
.ac-social-setup:hover { text-decoration: underline; }
.ac-social-actions { margin-top: 8px; }

/* (Removed: .ac-gh-* GitHub status grid — was only used by the deleted
   App Configuration ▸ Git Providers tab; repo status now lives in the
   Source Control panel.) */

/* Details / summary */
.ac-details summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--accent);
  font-weight: 500;
}
.ac-details-body {
  margin-top: 10px;
  font-size: 12px;
  color: var(--fg-2, var(--fg-2));
  line-height: 1.7;
}
.ac-details-body ol { padding-left: 18px; margin: 6px 0; }
.ac-details-body p { margin: 6px 0; }

/* ── Saved Models table (Text / Image-in / Image-out) ── */
/* Header and rows share one column template so the angled labels line up
   over their checkbox columns. The header padding (11px) matches the row's
   10px padding + 1px border so the content boxes are the same width. */
.ac-prov-head,
.ac-provider-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 34px 34px 34px 22px;
  align-items: center;
  column-gap: 4px;
}

/* 45°-angled column header above the first row */
.ac-prov-head {
  align-items: end;
  min-height: 40px;
  margin-bottom: 4px;
  padding: 0 11px;
  overflow: visible;
}
.ac-prov-th {
  display: inline-block;
  justify-self: center;
  align-self: end;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--fg-3);
  white-space: nowrap;
  transform: rotate(-45deg);
  transform-origin: bottom center;
  pointer-events: none;
}

.ac-provider-row {
  background: var(--bg-0);
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  margin-bottom: 6px;
}
.ac-provider-row:last-child { margin-bottom: 0; }

.ac-prov-cell {
  display: flex;
  align-items: center;
  justify-content: center;
}
.ac-cap-dot {
  color: var(--fg-4);
  opacity: 0.5;
  font-size: 15px;
  line-height: 1;
}
.ac-prov-del {
  justify-self: center;
  background: none;
  border: none;
  color: var(--fg-4);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 0;
}
.ac-prov-del:hover { color: var(--danger); }

/* ══ Models — configurator table (4 rows, each field auto-saves) ══════════════
   A flush `.ac-list` table where each row is a label + its input/select on the
   right, plus the shared green auto-save check. overflow:visible so the model
   search dropdown can escape the table. */
.ac-list.ac-model-config { overflow: visible; }   /* beat .ac-list's overflow:hidden */
/* First/last child round the table corners — the last row may now be an injected
   saved-model `.ac-row` wrapper, so match any element, not just `.ac-ability-row`. */
.ac-model-config > :first-child { border-top-left-radius: 9px; border-top-right-radius: 9px; }
.ac-model-config > :last-child { border-bottom-left-radius: 9px; border-bottom-right-radius: 9px; }
.ac-model-cfg-row { align-items: center; padding-top: 7px; padding-bottom: 7px; }
/* display:block opts this label OUT of the shared `display:contents` stacked-row
   promotion — here the label is a fixed-width form column, not a title+desc. */
.ac-model-cfg-row > .ac-ability-label { display: block; flex: 0 0 84px; }
.ac-model-cfg-control {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  position: relative;   /* anchor for the overlaid auto-save check */
}
.ac-model-cfg-control > .ac-input { flex: 1; min-width: 0; }
.ac-model-search-wrap { position: relative; flex: 1; min-width: 0; }
.ac-model-search-wrap > .ac-input { width: 100%; }
.ac-input-sm { padding: 6px 9px; font-size: 12px; }

/* ── Config / settings control slot (SHARED) ──────────────────────────────────
   The right-hand control slot for a row in the unified `.ac-config-list` — used
   by config-setting rows AND tool rows alike (so a setting's field/menu and a
   tool's toggles land on the same edge). Read identically by THREE sites: the
   agent card's Config tab (tab-config.js `_cfgRow`) AND both ability tables'
   expanded settings+tools list (agent/admin-ability-table.js → dom-utils.js
   `_buildSettingsRowsHtml` / `_buildToolRow`). Lives here (not agents.css) so the
   admin Agent Settings table — which doesn't load agents.css — styles its rows
   the same neat way. Only the right-hand control needs bespoke rules; the row /
   border / divider / name / description come from the shared `.ac-list` /
   `.ac-ability-*` component. */
.ac-config-control {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  margin-left: auto;
  align-self: center;
}
/* Right-aligned controls keep their natural width inside the slot (the shared
   `.ac-input` is width:100%, which would blow out the row). */
.ac-config-control > .ac-input { width: auto; }
/* Number setting → a horizontal STEPPER: a back (left) + forward (right) Lucide
   arrow sit INSIDE the field at each edge (replacing the browser's native up/down
   spin-buttons), with the value centred between them so the arrows never overlap
   it. Clicking an arrow steps the value by the field's `step` (clamped to min/max)
   and fires the same input/change events, so existing autosave stays untouched.
   Shared by the ability config rows AND the agent Config tab (wiring in
   dom-utils.js `_wrapNumberStepper` / `_renderSettingInput`). */
.ac-num-stepper { position: relative; display: inline-flex; align-items: center; }
.ac-config-control > .ac-num-stepper { width: 96px; }
.ac-num-stepper > input.ac-config-num {
  width: 100%;
  box-sizing: border-box;
  text-align: center;
  padding-left: 20px;
  padding-right: 20px;
}
/* Strip the native number spin-buttons (the arrows replace them). */
.ac-config-num::-webkit-inner-spin-button,
.ac-config-num::-webkit-outer-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }
.ac-config-num { -moz-appearance: textfield; appearance: textfield; }
/* The two in-field arrow buttons, pinned to the field's left / right edge. */
.ac-num-step {
  position: absolute; top: 0; bottom: 0;
  display: flex; align-items: center; justify-content: center;
  width: 18px; padding: 0;
  border: none; background: transparent;
  color: var(--fg-3); cursor: pointer;
  transition: color .12s ease;
}
.ac-num-step.ac-num-down { left: 0; }
.ac-num-step.ac-num-up { right: 0; }
.ac-num-step:hover,
.ac-num-step:active { color: var(--accent); }
.ac-num-step:disabled { color: var(--fg-3); opacity: .3; cursor: default; }
.ac-num-step svg { display: block; pointer-events: none; }
/* Inline config dropdown — kept COMPACT (shorter than the two-line label) so a
   dropdown row sits at the exact same height as a toggle row. Without this the
   default `.ac-input` (9px/13px) is taller than the label and makes choice rows
   stand taller than on/off rows, breaking the uniform table rhythm. */
.ac-config-control > select.ac-config-sel { min-width: 132px; padding: 5px 10px; font-size: 12px; }
.ac-config-control > input.ac-config-key { width: 168px; }
/* Optional schema note rendered under an ability's settings list. */
.ac-config-note { font-size: 11px; color: var(--fg-muted, var(--fg-3)); margin: 8px 2px 2px; }
/* The transient auto-save check OVERLAYS the control's right edge rather than
   reserving layout width — so the Provider / Base URL / API Key inputs and the
   Add / Clear buttons all reach the same full-width right edge as the model
   field above them, instead of stopping short by the check's old slot. It only
   appears briefly after a save, so the overlap is momentary. */
.ac-model-cfg-control > .agents-autosave-check {
  position: absolute;
  right: 6px;
  top: 0;
  bottom: 0;
  margin: auto 0;
  flex: none;
  pointer-events: none;
}

/* ── Model row — the model field sits full-width on top; a wrapping bar below
   holds the detected caps on the LEFT and Add / Clear on the RIGHT. No divider
   above the field: the field + bar read as one "add a model" composer. The bar
   wraps so caps + buttons are inline when wide and stack when narrow. ── */
.ac-model-config > .ac-model-cfg-model { border-top: none; }
.ac-model-cfg-model > .ac-ability-label {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.ac-model-cfg-model .ac-model-search-wrap { width: 100%; flex: none; }
.ac-model-cfg-modelbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.ac-model-cfg-modelbar > .ac-model-caps { flex: 1 1 auto; margin: 0; }
.ac-model-cfg-modelbar > .ac-model-add-control { margin-left: auto; flex: 0 0 auto; }
.ac-btn-sm { padding: 6px 14px; font-size: 12px; }

/* Detected-capability badges (read-only — from the model catalog, not boxes).
   nowrap keeps Text / Image-in / Image-out inline on their own line; if the bar
   is too narrow it's the Add/Clear group (later flex item) that wraps below. */
.ac-model-caps { display: flex; gap: 5px; margin-top: 4px; flex-wrap: nowrap; }

/* Action row: two icon buttons pinned to the right; the row is not clickable
   (beat the generic `.ac-row > .ac-ability-row { cursor: pointer }` rule). */
.ac-model-advanced-head { align-items: center; }
.ac-row > .ac-ability-row.ac-model-advanced-head { cursor: default; }
.ac-model-row-actions {
  margin-left: auto;          /* push both buttons to the right edge */
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ── Add-new-model form: COLLAPSED by default. The 4 configurator rows
   (provider / base-url / key / model) are hidden until the `+` button removes
   `ac-cfg-collapsed` from the table. Saved-model rows + the action row stay
   visible regardless. ── */
.ac-model-config.ac-cfg-collapsed .ac-model-cfg-row { display: none; }
/* The model-load status line is part of the configurator — hide it (along with
   the search box) while the form is collapsed. !important beats the inline
   display the JS sets when it has a message to show. */
.ac-model-config.ac-cfg-collapsed .ac-model-cfg-status { display: none !important; }

/* Shared bare icon button (Advanced toggle) — no border, just the glyph. */
.ac-model-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  flex: none;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--fg-3);
  cursor: pointer;
  transition: color 0.15s ease;
}
.ac-model-icon-btn:hover { color: var(--accent); }
/* Advanced button reads as "on" while its options body is open. */
.ac-row.expanded > .ac-ability-row #ac-model-advanced-toggle { color: var(--accent); }

/* The `+` toggle: borderless `+` when the form is CLOSED. When the form is OPEN
   a square border appears and ONLY the glyph rotates 45° (+ → ×) — the box stays
   square, never a diamond. */
.ac-model-add-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  flex: none;
  border: 1px solid transparent;   /* no visible border while collapsed */
  border-radius: 6px;
  background: transparent;
  color: var(--fg-3);
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.ac-model-add-toggle > svg { transition: transform 0.15s ease; }
.ac-model-add-toggle:hover { color: var(--accent); }
.ac-model-config:not(.ac-cfg-collapsed) .ac-model-add-toggle {
  color: var(--accent);
  border-color: var(--accent);
}
.ac-model-config:not(.ac-cfg-collapsed) .ac-model-add-toggle > svg {
  transform: rotate(45deg);
}
.ac-cap-badge {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 999px;
  border: var(--border-width) solid var(--border);
  color: var(--fg-4);
  opacity: 0.55;
}
.ac-cap-badge.on { color: var(--accent); border-color: var(--accent); opacity: 1; }

/* ══ Saved Models — expandable rows injected into the unified model table.
   Single fixed layout: model name · Text / In / Out capability checkboxes ·
   stacked usage block (tokens-in / out / cost). The model name is the expand
   trigger — there is no chevron and no × (deletion lives in the expanded
   panel). JS sets the same grid inline; this is the fallback. */
.ac-model-config .ac-saved-row {
  display: grid;
  /* Model · Def · Text · In · Out · Eff · Usage (JS sets the same grid inline). */
  grid-template-columns: minmax(0, 1fr) 28px 28px 28px 28px 28px 38px;
  align-items: center;
  column-gap: 2px;
}

.ac-saved-head:hover { background: transparent; }
.ac-saved-th {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--fg-3);
  white-space: nowrap;
}
.ac-saved-usage-th { text-align: right; }
.ac-saved-cap { display: flex; align-items: center; justify-content: center; position: relative; }
/* Save-status overlay for a saved-model box (capability checkbox / Default radio):
   the shared `.ac-save-overlay` fills its container, but a cap cell is a wide
   28×15 grid slot, so a stretched overlay reads as a wide rectangle around a 15px
   box. Pin it to a small CENTRED SQUARE (just larger than the box) so the spinner
   / ✓ / ⚠ chip hugs the control instead of filling the column. */
.ac-saved-cap .ac-save-overlay {
  inset: auto;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 18px;
  transform: translate(-50%, -50%);
  border-radius: 4px;
}
/* Hold-to-cancel hazard cover for a capability checkbox. The instant a box is
   ticked it LOCKS (the underlying checkbox is disabled) and this cover drops on
   top so an accidental double-click can't flip it back. The change has already
   been sent to the server; the ONLY way to reverse it is a deliberate long-press
   of this triangle (the same hold-to-confirm gesture the delete buttons use — a
   rising fill tracks the press), which reverts the box and saves the reverted
   value. It then settles to the shared green ✓ / orange ⚠ save overlay. */
.ac-cap-hazard {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 5px;
  color: var(--warning);
  background: var(--bg-elev);
  box-shadow: inset 0 0 0 1px rgba(var(--warning-rgb), 0.55);
  cursor: pointer;
  overflow: hidden;
  z-index: 7;
  opacity: 0;
  transition: opacity 0.12s ease;
  -webkit-user-select: none;
  user-select: none;
  touch-action: none;
}
.ac-cap-hazard.show { opacity: 1; }
.ac-cap-hazard-fill {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 0%;
  background: rgba(var(--warning-rgb), 0.38);
  pointer-events: none;
}
.ac-cap-hazard-icon {
  position: relative;
  z-index: 1;
  display: inline-flex;
  line-height: 0;
}
/* RTL flow so an over-long name truncates at the START — the ellipsis sits on
   the left and the END of the name (the distinguishing version suffix) stays
   visible. `text-align: left` keeps a SHORT (non-truncated) name flush-left,
   sitting directly above the provider line rather than pushed to the right edge.
   The name itself is strong-LTR text, so it still reads left-to-right. */
.ac-saved-model-name {
  display: block;
  direction: rtl;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Clicking the name expands the row; show it as interactive. Both the model
   name and the provider line are left-aligned; the RTL flow above only governs
   which END truncates, not the resting alignment. */
.ac-saved-name-click { cursor: pointer; }
.ac-saved-name-click:hover .ac-saved-model-name { color: var(--accent); }

/* ── Stacked usage block — three tiny labelled lines that sit inside the row
   height (the row never grows taller than the two-line name cell). ── */
.ac-saved-usage {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-width: 0;
}
.ac-saved-usage-line {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  font-size: 9px;
  line-height: 1.15;
  white-space: nowrap;
}
.ac-saved-usage-v {
  font-weight: 600;
  color: var(--fg-2);
  font-variant-numeric: tabular-nums;
}

/* ── Delete control inside the expanded panel ── */
.ac-saved-del-row { margin-top: 10px; }
.ac-saved-del-btn {
  color: var(--danger);
  border-color: var(--border);
}
.ac-saved-del-btn:hover {
  color: var(--danger);
  border-color: var(--danger);
}

body.light-mode .ac-cap-badge { color: var(--fg-3); border-color: var(--border-strong); }
body.light-mode .ac-cap-badge.on { color: var(--brand); border-color: var(--brand); }
body.light-mode .ac-saved-th { color: var(--fg-3); }
/* The meta chips (context / out / price) fall back to a dark pill because
   --bg-base is undefined; give light mode a warm pill so they don't read as
   dark blobs on the cream panel. */
body.light-mode .ac-model-badge {
  background: var(--bg-tint);
  border-color: var(--border);
  color: var(--fg-2);
}

/* ── Spinning loader for the models panel ── */
@keyframes ac-spin { to { transform: rotate(360deg); } }
/* The + (add-a-model) button shows a spinner IN PLACE OF its glyph while the
   provider's available-model list loads — that fetch is the one genuinely slow
   part of the panel (it powers the "Search models…" box), so its indicator sits
   right on the control that opens the add-a-model form. */
.ac-model-add-spinner {
  display: none;
  width: 13px;
  height: 13px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: ac-spin 0.7s linear infinite;
}
.ac-model-add-toggle.loading > svg { display: none; }
.ac-model-add-toggle.loading > .ac-model-add-spinner { display: block; }

/* ── Light mode overrides ── */
body.light-mode #app-config-header { background: transparent; }
body.light-mode .ac-page-title { color: var(--fg-1); }
body.light-mode #app-config-tabs-wrap {
	  background: transparent;
	  border-bottom-color: var(--border);
	}
body.light-mode .ac-tab { color: var(--fg-3); }
body.light-mode .ac-tab:hover { color: var(--fg-2); }
body.light-mode .ac-tab.active { color: var(--brand); border-bottom-color: var(--brand); }
/* Edge fade comes from the alpha mask on #app-config-tabs (theme-agnostic), so
   the chevron stays a transparent button in light mode too — only its glyph
   colour changes. */
body.light-mode .ac-tabs-chev { color: var(--brand); }
body.light-mode .ac-tabs-chev:hover { color: var(--brand); }
body.light-mode .ac-card { background: var(--bg-elev); border-color: var(--border); }
body.light-mode .ac-section-title { color: var(--fg-1); }
body.light-mode .ac-section-desc { color: var(--fg-3); }
body.light-mode .ac-card-label { color: var(--fg-2); }
body.light-mode .ac-hint { color: var(--fg-3); }
body.light-mode .ac-label { color: var(--fg-2); }
body.light-mode .ac-input { background: var(--bg-1); border-color: var(--border-strong); color: var(--fg-1); }
body.light-mode .ac-input:focus { border-color: var(--brand); }
body.light-mode .ac-btn-primary { background: var(--brand); color: #fff; /* KEEP (intentional): contrast ink on the brand-filled button */ }
body.light-mode .ac-btn-ghost { border-color: var(--fg-3); color: var(--fg-3); }
body.light-mode .ac-seg-btn { background: var(--bg-1); border-color: var(--border-strong); color: var(--fg-1); }
body.light-mode .ac-seg-btn.active { border-color: var(--brand); background: rgba(var(--brand-rgb),0.06); color: var(--brand); }
body.light-mode .ac-info-banner { background: var(--bg-1); border-left-color: var(--brand); }
body.light-mode .ac-stats-table th { background: var(--bg-1); color: var(--fg-2); border-bottom-color: var(--border); }
body.light-mode .ac-stats-table td { color: var(--fg-1); border-bottom-color: var(--border-soft); }
body.light-mode .ac-table-empty { color: var(--fg-3); }

/* ── Integrations ── */
.ac-int-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 12px;
  white-space: nowrap;
}
.ac-int-badge-on {
  background: rgba(var(--success-rgb), 0.15);
  color: var(--success);
}
.ac-int-badge-off {
  background: rgba(var(--ink-rgb), 0.12);
  color: var(--fg-muted, var(--fg-3));
}
.ac-int-badge-soon {
  background: rgba(var(--warning-rgb), 0.15);
  color: var(--warning);
}
.ac-int-coming-soon {
  opacity: 0.6;
}
.ac-int-account-info {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-base, var(--bg-0));
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
}
body.light-mode .ac-int-badge-on { background: rgba(var(--success-rgb), 0.12); color: var(--success); }
body.light-mode .ac-int-badge-off { background: rgba(var(--ink-rgb), 0.08); color: var(--fg-3); }
body.light-mode .ac-int-badge-soon { background: rgba(var(--warning-rgb), 0.12); color: var(--warning); }
body.light-mode .ac-int-account-info { background: var(--bg-1); border-color: var(--border-strong); }
body.light-mode .ac-provider-row { background: var(--bg-1); border-color: var(--border-strong); }

/* ── Communications channel grid ── */
.ac-comm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 12px;
  margin-top: 4px;
}
.ac-comm-card {
  background: var(--bg-0);
  border: var(--border-width) solid var(--border);
  border-radius: 10px;
  position: relative;       /* anchor for the floating panel */
  transition: border-color 0.15s;
}
.ac-comm-card:hover { border-color: rgba(var(--brand-rgb),0.35); }
.ac-comm-card.panel-open { border-color: var(--accent); z-index: 10; }
.ac-comm-card-dim {
  opacity: 0.6;
  pointer-events: none;
}
.ac-comm-card-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 14px 14px 14px;
}
.ac-comm-logo {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: rgba(var(--ink-rgb),0.04);
}
.ac-comm-meta {
  flex: 1;
  min-width: 0;
}
.ac-comm-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-1, var(--fg-1));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ac-comm-desc {
  font-size: 11px;
  color: var(--fg-muted, var(--fg-3));
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ac-comm-badge {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 3px 8px;
  border-radius: 10px;
  white-space: nowrap;
  flex-shrink: 0;
}
.ac-comm-badge-active {
  background: rgba(var(--success-rgb),0.15);
  color: var(--success);
}
.ac-comm-badge-inactive {
  background: rgba(var(--danger-rgb),0.12);
  color: var(--danger);
}
.ac-comm-badge-unconfigured {
  background: rgba(var(--ink-rgb),0.1);
  color: var(--fg-muted, var(--fg-3));
}
.ac-comm-badge-dim {
  background: rgba(var(--ink-rgb),0.08);
  color: var(--fg-muted, var(--fg-3));
  font-size: 10px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 10px;
}
.ac-comm-toggle-btn {
  font-size: 11px;
  font-weight: 600;
  padding: 5px 11px;
  border-radius: 6px;
  border: var(--border-width) solid var(--border);
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
  font-family: inherit;
  transition: background 0.12s, border-color 0.12s;
}
.ac-comm-toggle-btn:hover { background: rgba(var(--brand-rgb),0.08); border-color: var(--accent); }
.ac-comm-toggle-btn.open { background: rgba(var(--brand-rgb),0.1); border-color: var(--accent); }
.ac-comm-panel {
  display: none;
  position: absolute;
  top: calc(100% - 14px);  /* overlap upward into the card, hiding its bottom corners */
  left: -1px;
  right: -1px;
  z-index: 200;
  padding: 24px 14px 16px; /* extra top padding to clear the overlap zone */
  border: 1px solid var(--accent);
  border-top: none;
  border-radius: 0 0 10px 10px;
  background: var(--bg-0);
  box-shadow: 0 12px 32px rgba(var(--shadow-rgb), 0.55);
}
.ac-comm-panel.open { display: block; }
.ac-comm-panel-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  flex-wrap: wrap;
}
.ac-comm-webhook-url {
  margin-top: 10px;
  padding: 8px 10px;
  background: var(--bg-base, var(--bg-0));
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
  font-size: 11px;
  color: var(--fg-2, var(--fg-2));
  word-break: break-all;
}

/* Light mode overrides for comm grid */
body.light-mode .ac-comm-card { background: var(--bg-elev); border-color: var(--border); }
body.light-mode .ac-comm-card:hover { border-color: rgba(var(--brand-rgb),0.4); }
body.light-mode .ac-comm-name { color: var(--fg-1); }
body.light-mode .ac-comm-desc { color: var(--fg-3); }
body.light-mode .ac-comm-toggle-btn { border-color: var(--border-strong); color: var(--brand); }
body.light-mode .ac-comm-toggle-btn:hover { background: rgba(var(--brand-rgb),0.06); border-color: var(--brand); }
body.light-mode .ac-comm-panel { background: var(--bg-elev); border-color: var(--brand); box-shadow: 0 12px 32px rgba(var(--shadow-rgb), 0.12); }
body.light-mode .ac-comm-webhook-url { background: var(--bg-1); border-color: var(--border-strong); }
body.light-mode .ac-comm-badge-active { background: rgba(var(--success-rgb),0.12); color: var(--success); }
body.light-mode .ac-comm-badge-inactive { background: rgba(var(--danger-rgb),0.1); color: var(--danger); }
body.light-mode .ac-comm-badge-unconfigured { background: rgba(var(--ink-rgb),0.08); color: var(--fg-3); }

/* ── Inline dropdown login form (light mode) ── */
body.light-mode #dd-login-email,
body.light-mode #dd-login-pass { background: var(--bg-1) !important; border-color: var(--border-strong) !important; color: var(--fg-1) !important; }
body.light-mode #dd-login-email:focus,
body.light-mode #dd-login-pass:focus { border-color: var(--brand) !important; }
body.light-mode #dd-login-remember { accent-color: var(--brand) !important; }
body.light-mode #dd-login-btn { background: var(--brand) !important; color: #ffffff !important; /* KEEP (intentional): contrast ink on the brand-filled button */ }

/* ── Admin Tools restricted overlay (light mode) ── */
body.light-mode #files-restricted-overlay { background: var(--bg-0) !important; }
body.light-mode .files-restricted-card { background: var(--bg-elev) !important; border-color: var(--border-strong) !important; box-shadow: 0 12px 32px rgba(var(--shadow-rgb), 0.10) !important; }

/* ── Agent Tools — compact ability rows ── */
.ac-abilities-compact,
.ac-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  border: var(--border-width) solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-elev, var(--bg-elev));
}
/* Hairline dividers are drawn by the CONTAINER between items — so an item can be
   a bare row OR an expandable `.ac-row` wrapper and the dividers always line up. */
.ac-abilities-compact > * + *,
.ac-list > * + * {
  border-top: var(--border-width) solid var(--border);
}

/* Admin Agent Tools wrapper (#ac-abilities-compact) is a BARE passthrough — the
   only visible frame is the inner `.ac-list` table it holds, exactly like the
   sister agent-card Abilities tab (which builds `.ac-list` straight into a plain
   div with no wrapper). So strip the wrapper's own border / background / radius
   and the pill↔table divider, letting the search pill + table sit directly on
   the panel. (SISTER-PANEL: AGENT-ABILITY-TABLE.) */
#ac-abilities-compact {
  border: none;
  border-radius: 0;
  background: transparent;
  overflow: visible;
}
#ac-abilities-compact > * + * { border-top: none; }

/* ── Ability-tree loading + staggered group reveal ──────────────────────────
   While the catalog (and, for the admin panel, the global tool defaults) load,
   the container shows a centered spinner. Once the groups are built they are
   revealed one-by-one, each unfurling vertically from its top edge with a small
   per-group delay applied inline by `_staggerRevealGroups` (dom-utils.js). The
   `both` fill keeps each group invisible until its delay elapses, so nothing
   flashes in before its turn. Mirrors across BOTH ability panels (admin +
   agent) because both call the shared loader/reveal helpers. Reuses the
   `ac-spin` keyframe defined for the models panel. */
.ac-tree-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 40px 0;
  color: var(--fg-3);
}
.ac-tree-spinner {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid var(--border, var(--border));
  border-top-color: var(--accent);
  animation: ac-spin 0.7s linear infinite;
}
.ac-tree-loading-label { font-size: 12px; }

/* ── Whole-section build gate ───────────────────────────────────────────────
   A config section whose JS rearranges static markup into formatted tables
   (e.g. Agent Settings: it builds the ability table, relocates integration
   cards, mounts the model table) would otherwise flash its raw half-built
   markup — search box, AI box, un-formatted collapsible groups — before the
   build finishes. The section's template ships with `is-building` on the
   `.ac-section` so, from the FIRST paint, every real child is hidden and a
   single centred spinner shows instead; the page's `init()` removes
   `is-building` once the async build resolves, revealing the finished tables in
   one step. Reusable on any config section — purely CSS + one class flip. */
.ac-section.is-building > *:not(.ac-section-loader) { display: none !important; }
.ac-section-loader {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 340px;
}
.ac-section:not(.is-building) > .ac-section-loader { display: none; }
.ac-section-loader-label { font-size: 12px; color: var(--fg-muted, var(--fg-3)); }

@keyframes ac-group-reveal {
  from { opacity: 0; transform: scaleY(0.4) translateY(-4px); }
  to   { opacity: 1; transform: scaleY(1) translateY(0); }
}
.ac-list .ac-group.ac-group-reveal {
  transform-origin: top center;
  animation: ac-group-reveal 0.34s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
@media (prefers-reduced-motion: reduce) {
  .ac-tree-spinner { animation-duration: 1.4s; }
  .ac-list .ac-group.ac-group-reveal { animation: none; }
}
.ac-ability-row {
  display: flex;
  /* Wrap so the description drops onto its OWN full-width line below the
     icon · title · control line, instead of being trapped in a narrow middle
     column that cramps on narrow screens (see `.ac-ability-label` /
     `.ac-ability-desc` below for the rest of the stacked-row mechanism). */
  flex-wrap: wrap;
  align-items: flex-start;
  column-gap: 10px;
  row-gap: 3px;
  padding: 8px 14px;
  background: transparent;
  transition: background 0.15s;
}
.ac-ability-row:hover {
  background: rgba(var(--brand-rgb), 0.04);
}
.ac-ability-row.ac-ability-enabled {
  background: rgba(var(--brand-rgb), 0.06);
}
.ac-ability-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  flex-shrink: 0;
  /* No fixed height — let it be natural so the icon aligns with the name
     text when the row uses align-items: flex-start. */
  padding-top: 1px;
}
.ac-ability-label {
  /* `display: contents` promotes the name + description to be flex children of
     the ROW itself, so the description can drop to its own full-width line
     below the `icon · title · control` line (rather than stacking inside a
     narrow middle column). The two form-column variants that reuse this class
     as a fixed-width label — `.ac-model-cfg-row` and `.ac-main-panel-item` —
     opt back out by restoring their own `display` below. */
  display: contents;
  flex: 1;
  min-width: 0;
}
.ac-ability-name {
  /* Sits on the title line; the control after it is pushed to the right edge. */
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-1, var(--fg-1));
  line-height: 1.3;
}
.ac-ability-desc {
  /* `order: 1` places the description after the control, and `flex-basis: 100%`
     forces it onto its own line, full row width, below the title line. The
     left indent aligns it under the title (icon column 28px + 10px gap); rows
     with no leading icon reset it to 0 just below. */
  order: 1;
  flex: 0 1 100%;
  margin-left: 38px;
  font-size: 10.5px;
  color: var(--fg-muted, var(--fg-3));
  line-height: 1.4;
  margin-top: 1px;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Claude target-device readiness warning (claude-agent.js `_deviceRow`): the pinned
   device can't run a Claude agent (no `claude` CLI). Coloured with the theme-aware
   --warning token (reads in both themes) and un-clamped so the full notice shows —
   the base desc clamps to one line, which would truncate this. */
.ac-ability-desc.ac-desc-warn {
  color: var(--warning);
  font-weight: 500;
  display: block;
  -webkit-line-clamp: unset;
  overflow: visible;
}
/* Icon-less rows (e.g. the config / settings rows built by dom-utils) put the
   description flush under the title, since there's no icon column to clear. */
.ac-ability-row:not(:has(> .ac-ability-icon)) .ac-ability-desc { margin-left: 0; }
/* When the row is expanded its description is no longer clamped — the title row
   grows vertically to show the full text (admin members carry `.ac-row-open`;
   agent cards / group heads use `.ac-row.expanded`). */
.ac-ability-row.ac-row-open .ac-ability-desc,
.ac-row.expanded > .ac-ability-row .ac-ability-desc {
  -webkit-line-clamp: unset;
  overflow: visible;
}
/* NOTE: there used to be a `@media (max-width: 640px)` block here that un-clamped
   the description on mobile, because the OLD layout crammed it into a narrow
   middle column where one line showed only a few words. The stacked-row layout
   (description on its own full-width line) fixed that cramping, so the mobile
   override is gone — the 1-line clamp now holds on mobile exactly like desktop,
   and the full text only appears when the row is expanded (the rule above). */
.ac-ability-toggle-wrap {
  flex-shrink: 0;
  margin-left: auto;
  padding-top: 1px;
}
.ac-ability-status {
  /* Sits left of the toggle, in line with the ability title. It owns the
     right-push (margin-left:auto) so the status + toggle read as one group at
     the right edge; the toggle that follows then takes a fixed small gap (see
     the `+ .ac-ability-toggle-wrap` rule below) instead of a second auto margin
     that would split the free space and open a gap between them. Empty = no
     text = invisible, but the auto margin still positions the toggle. */
  margin-left: auto;
  align-self: flex-start;     /* align to the title's first line, not centred */
  padding-top: 2px;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.02em;
  white-space: nowrap;
  flex-shrink: 0;
}
.ac-ability-status.tone-ok { color: var(--success); }
.ac-ability-status.tone-warn { color: var(--warning); }
.ac-ability-status + .ac-ability-toggle-wrap { margin-left: 8px; }

/* ── "Available to" caller-access dropdown (PER-AGENT ability table only) ──────
   A compact select sitting just left of the ability's visibility tri choosing
   who may trigger the ability (Everyone / Registered / Admins). Theme-safe — all
   colours come from the design-system palette. (SISTER-PANEL: AGENT-ABILITY-TABLE
   — agent-side only, so there is no admin-table mirror.) */
.ac-access-wrap {
  flex-shrink: 0;
  align-self: flex-start;     /* align with the ability title's first line */
  margin-left: 8px;
  display: inline-flex;
  align-items: center;
}
.ac-access-select {
  font-family: inherit;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.2;
  padding: 2px 4px;
  max-width: 108px;
  color: var(--fg-2);
  background: var(--bg-1);
  border: var(--border-width) solid var(--border);
  border-radius: 5px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.ac-access-select:hover:not(:disabled) { border-color: var(--border-strong); }
.ac-access-select:focus-visible {
  outline: none;
  border-color: var(--accent);
}
.ac-access-select:disabled { opacity: 0.5; cursor: default; }
/* A genuine restriction (Registered / Admins) tints the control so a locked-down
   ability reads at a glance. Uses the design-system's ready-made accent tints
   (`--accent-soft` fill + `--accent-line` border, both built off `--brand-rgb`)
   — NOT a `--accent-rgb` triple, which is not a real token (the accent hue's
   triple is `--brand-rgb`; `--accent` is only the solid alias). */
.ac-access-restricted .ac-access-select {
  color: var(--accent);
  border-color: var(--accent-line);
  background: var(--accent-soft);
}

/* ── Unified save-status indicator — OVERLAY ON TOP of the control ────────────
   (SISTER-PANEL: AGENT-ABILITY-TABLE) ONE save-status language for every config
   control in the ability tree. The indicator is centered DIRECTLY OVER the
   control it confirms (toggle / field / dropdown / permission segment), not in a
   slot beside it: a spinner while the save is in flight, then a green check ✓
   once the backend confirms, or an orange warning triangle ⚠ on failure (the
   caller then reverts the control to its prior value). Driven by `_markSaving` /
   `_flashSaveCheck` in dom-utils.js, which make the control's container
   position:relative and drop this overlay into it. Colours come from the
   design-system palette so it's theme-safe and matches the `.agents-autosave-check`
   tick the model + appearance tables use. */
.ac-save-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: inherit;
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: 6;
}
.ac-save-overlay.show { opacity: 1; }
/* In-flight: a dimming backdrop (theme-aware) so the spinner reads over the
   control underneath. */
.ac-save-overlay:has(.agents-spinner) {
  color: var(--brand);
  background: color-mix(in srgb, var(--bg-elev) 72%, transparent);
}
.ac-save-overlay.ok  { color: var(--success); background: var(--success-soft); }
.ac-save-overlay.err { color: var(--warning); background: var(--warning-soft); }
/* Over a toggle the wrap is square, so the overlay's `border-radius: inherit`
   comes out square and its corners poke past the pill-shaped switch underneath.
   Give it the toggle's own pill radius (matching `.conn-toggle-track`) so the
   green/orange confirm box hugs the switch shape with no square corners showing. */
.ac-ability-toggle-wrap .ac-save-overlay { border-radius: 10px; }

/* Tiny 1–2 word warning popup that floats just above the ⚠ triangle on a failed
   save, so the failure is unmissable even at a glance. Anchored to the overlay's
   centre; small entrance lift + fade. (SISTER-PANEL: AGENT-ABILITY-TABLE.) */
.ac-save-pop {
  position: absolute;
  bottom: calc(100% + 5px);
  left: 50%;
  transform: translate(-50%, 4px);
  white-space: nowrap;
  padding: 2px 7px;
  border-radius: calc(var(--radius-sm, 6px) * 0.8);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--warning);
  background: var(--bg-elev);
  border: var(--border-width) solid var(--warning);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: 7;
}
.ac-save-pop.show { opacity: 1; transform: translate(-50%, 0); }
/* Little downward-pointing nub. */
.ac-save-pop::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 4px solid transparent;
  border-top-color: var(--warning);
}

/* ── SHARED-HAZARD-CONFIRM ───────────────────────────────────────────────────
   The in-app replacement for the native confirm() popup (shared/js/confirm-dialog.js):
   a centred notification panel with a hazard badge + Cancel/Confirm. Theme-safe —
   all colours are design-system tokens; the tone (warning/danger) recolours the
   badge + confirm button. */
.wa-confirm-overlay {
  position: fixed;
  inset: 0;
  z-index: 4000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(var(--bg-0-rgb), 0.55);
  backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity 0.16s ease;
}
.wa-confirm-overlay.show { opacity: 1; }
.wa-confirm-panel {
  width: 100%;
  max-width: 420px;
  background: var(--bg-elev);
  border: var(--border-width) solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-modal, 0 30px 80px -20px rgba(var(--shadow-rgb), 0.6));
  padding: 20px 20px 16px;
  transform: translateY(8px) scale(0.985);
  transition: transform 0.16s ease;
}
.wa-confirm-overlay.show .wa-confirm-panel { transform: translateY(0) scale(1); }
.wa-confirm-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
.wa-confirm-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  border-radius: 50%;
}
.wa-confirm-warning .wa-confirm-icon { color: var(--warning); background: var(--warning-soft); }
.wa-confirm-danger  .wa-confirm-icon { color: var(--danger);  background: var(--danger-soft); }
.wa-confirm-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--fg-1);
  line-height: 1.3;
}
.wa-confirm-msg {
  font-size: 13px;
  line-height: 1.55;
  color: var(--fg-2, var(--muted));
  white-space: pre-wrap;
  margin-bottom: 18px;
}
.wa-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.wa-confirm-actions .ac-btn { padding: 7px 16px; }
.wa-confirm-warning .wa-confirm-go { background: var(--warning); color: var(--bg-0); }
.wa-confirm-danger  .wa-confirm-go { background: var(--danger);  color: var(--bg-0); }
@media (prefers-reduced-motion: reduce) {
  .wa-confirm-overlay,
  .wa-confirm-panel { transition: none; }
}

/* In-flight control spinner — mirrors agents.css `.agents-spinner` but lives HERE
   in the globally-loaded sheet so the ability-tree's saving spinners animate on
   EVERY page, including the admin Agent Settings page (which doesn't load
   agents.css). (SISTER-PANEL: AGENT-ABILITY-TABLE — toggle/eye/permission/config
   saving feedback.) The `.ac-save-overlay` selector covers the overlay spinner on
   controls (e.g. the toggle wrap) that aren't inside `.ac-config-control`. */
.ac-ability-status .agents-spinner,
.ac-config-control .agents-spinner,
.ac-save-overlay .agents-spinner,
.agents-eye-toggle .agents-spinner {
  display: inline-block;
  width: 11px;
  height: 11px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: agents-spin 0.6s linear infinite;
  vertical-align: middle;
}
@keyframes agents-spin { to { transform: rotate(360deg); } }
/* Locked-on safety ability (e.g. Context Control): the toggle is fixed in the
   ON position and cannot be turned off. The switch still renders as ON; we only
   signal that it is fixed (no-drop cursor) — clicking it reports "Cannot be
   deactivated" rather than changing. Theme-safe (inherits the ON track colour). */
.ac-ability-toggle-locked { cursor: not-allowed; }
.ac-ability-toggle-locked .conn-toggle { cursor: not-allowed; }

/* ── Toggle switch (SISTER-PANEL: AGENT-ABILITY-TABLE) ──────────────────────
   The on/off switch used by every ability-table row and config-list row, AND
   by the standalone config rows on the admin config pages (App Settings →
   Startup & Boot, etc.). Lives HERE in the globally-loaded sheet — not in the
   per-page agents.css — because the admin App Settings / Agent Settings pages
   don't load agents.css, so their toggles need the style from a global sheet
   (same precedent as the `.ac-config-*` widths above). Markup: a
   `.conn-toggle-wrap` label wrapping a visually-hidden `.conn-toggle` checkbox
   + a `.conn-toggle-track` span. */
.conn-toggle-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  flex-shrink: 0;
}
.conn-toggle {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.conn-toggle-track {
  display: block;
  width: 36px;
  height: 20px;
  background: var(--border-soft);
  border-radius: 10px;
  border: var(--border-width) solid var(--border);
  position: relative;
  transition: background 0.2s, border-color 0.2s;
}
.conn-toggle-track::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  background: var(--fg-3);
  border-radius: 50%;
  transition: left 0.2s, background 0.2s;
}
.conn-toggle:checked + .conn-toggle-track {
  background: rgba(var(--brand-rgb), 0.4);
  border-color: var(--accent);
}
.conn-toggle:checked + .conn-toggle-track::after {
  left: 18px;
  background: var(--accent);
}
/* (The toggle's saving feedback is now the shared `.ac-save-overlay` centered ON
   TOP of the toggle — see the unified save-status indicator block above — so the
   old knob-morph spinner here was removed.) */
/* Toggle track colours (off + checked) are now driven entirely by design-system
   tokens in the base rules above, so they already adapt to light mode — the
   former body.light-mode .conn-toggle-track overrides were redundant and removed. */

body.light-mode .ac-abilities-compact {
  background: var(--bg-elev);
  border-color: var(--border);
}
body.light-mode .ac-ability-row {
  border-bottom-color: var(--border);
}
body.light-mode .ac-ability-row:hover {
  background: rgba(var(--brand-rgb), 0.04);
}
body.light-mode .ac-ability-row.ac-ability-enabled {
  background: rgba(var(--brand-rgb), 0.06);
}
body.light-mode .ac-ability-name {
  color: var(--fg-1);
}
body.light-mode .ac-ability-desc {
  color: var(--fg-3);
}

/* ── Expandable row (click-to-open info / config) ─────────────────────────
   An item that reveals a body when clicked. Wrap the head + body in `.ac-row`
   and JS adds `.expanded` to open it. Use `.ac-row-static` for a non-expanding
   item. The body holds additional info and/or config fields (OAuth, scopes…). */
.ac-row {
  display: flex;
  flex-direction: column;
}
.ac-row > .ac-ability-row {
  cursor: pointer;
}
.ac-row-static > .ac-ability-row {
  cursor: default;
}
.ac-ability-body {
  display: none;
  /* Left padding matches the member-row indent so the settings / tools line up
     directly under the ability they belong to. */
  padding: 8px 14px 10px 20px;
  border-top: var(--border-width) solid var(--border);
  background: var(--bg-1);
}
/* `.ac-row.expanded` opens agent-panel card bodies; `.ac-body-open` opens admin
   member bodies (the admin member row is not wrapped in its own `.ac-row`). */
.ac-row.expanded > .ac-ability-body,
.ac-ability-body.ac-body-open {
  display: block;
}
/* Nothing to configure → no empty padded strip. The row has still grown to show
   its full description; only the settings/tools area is suppressed. */
.ac-row.expanded > .ac-ability-body:empty,
.ac-ability-body.ac-body-open:empty {
  display: none;
}
.ac-row-chevron {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  margin-left: 8px;
  color: var(--fg-muted, var(--fg-3));
  transition: transform 0.2s;
}
.ac-row.expanded > .ac-ability-row .ac-row-chevron {
  transform: rotate(90deg);
}
/* Left-positioned expand chevron (sits BEFORE the icon, after the row indent).
   Fixed width so the icons line up whether or not a chevron is present. */
.ac-row-chevron.ac-row-chevron--left {
  margin-left: 0;
  margin-right: -2px;
  width: 14px;
  justify-content: center;
}
body.light-mode .ac-list,
body.light-mode .ac-abilities-compact {
  background: var(--bg-elev);
  border-color: var(--border);
}
body.light-mode .ac-list > * + *,
body.light-mode .ac-abilities-compact > * + * {
  border-top-color: var(--border);
}
body.light-mode .ac-ability-body {
  border-top-color: var(--border);
  background: var(--bg-1);
}

/* ── Per-ability tool rows (SISTER-PANEL: AGENT-ABILITY-TABLE) ──────────────
   Tool rows now render as the SAME stacked `.ac-ability-row` as a config-setting
   row (built by `_buildToolRow` in shared/dom-utils.js): the tool NAME + its
   controls share the title line, and the description drops to its own full-width
   line below. Config knobs and tools sit in ONE unified list, so they read as one
   continuous run of rows — there is no separate boxed/flat `.agents-tool-*`
   styling any more. The only tool-specific rule is the monospace name below; the
   PERMISSION control is the shared `.ac-tri` (styled in the "3-position toggle"
   block) and the row / dividers come from the shared `.ac-list` /
   `.ac-ability-row` rules above — all theme-safe via design-system tokens. */
.ac-ability-name.ac-tool-name {
  /* A tool name is an identifier, so keep it monospace + a touch smaller than a
     human-worded config label, while the row layout stays identical. */
  font-family: var(--font-mono, monospace);
  font-weight: 500;
  font-size: 12px;
}

/* ── Unified settings + tools list — INLINE in the expanded ability body ───────
   The config + tool rows are NOT wrapped in their own bordered "sub-table" box.
   They flow directly inside the expanded ability body (`.ac-ability-body`), just
   like the skill row above them, so the panel never nests a box-in-a-box (ability
   table → ability → boxed sub-table). The list is still built with the shared
   `.ac-list` class so its hairline ROW DIVIDERS (and their light-mode colour)
   come for free; here we strip only the list's FRAME — border, rounded corners,
   elevated fill, and corner clip — from THIS inner list (`.ac-config-settings`),
   leaving the outer ability table's frame untouched. Each row also drops its own
   left/right padding so its title and right-aligned control line up with the
   skill row's edges (the body's own padding does the inset). (SISTER-PANEL:
   AGENT-ABILITY-TABLE — the class lives on the shared `_buildAbilitySettingsList`
   list, so both ability panels match automatically.) */
.ac-list.ac-config-settings {
  border: none;
  border-radius: 0;
  overflow: visible;
  background: transparent;
  margin-top: 4px;
}
.ac-config-settings > .ac-ability-row {
  padding-left: 0;
  padding-right: 0;
}

/* ── Even up the expanded body's left/right edges ──────────────────────────────
   With the sub-table box gone, the expanded body looked lopsided: its content
   (and, on the agent card, its dark background) sat indented on the LEFT while
   the right reached the edge. Two stacked causes, fixed per panel.

   (1) Content inset — the body pads its LEFT more than its right (20px base, or
   32px for a grouped member, vs 14px right) to tuck settings under the indented
   ability name. Pull the left back to the same 14px the right uses so the rows
   sit symmetrically, flush to both edges. Scoped to the ability tables (admin
   `#ac-abilities-compact` + the agent card's `.conn-fields` bodies) so the shared
   Appearance theme editor — which also reuses `.ac-ability-body` — keeps its own
   indent. (SISTER-PANEL: AGENT-ABILITY-TABLE.) */
#ac-abilities-compact .ac-ability-body,
.conn-fields.ac-ability-body {
  padding-left: 14px;
}

/* (2) Agent card only — the member card is shifted right 18px by its
   `.ac-group-body > .ac-row` wrapper (to align the header under the group like an
   admin member). That drag also pulled the expanded body's dark background in on
   the left while the right stayed flush. Cancel the shift for the BODY alone (the
   header keeps its member indent) so the dark area reaches both edges — matching
   the admin table, whose body already spans the full width. The -18px mirrors the
   wrapper's padding-left; keep them in step. */
.ac-group-body .conn-fields.ac-ability-body {
  margin-left: -18px;
}

/* ── Grouped abilities (Administrator / Basic / Web) ──────────────────────
   Each group is an expandable `.ac-row` whose head carries a 3-position
   toggle; expanding reveals its member abilities as plain rows in `.ac-group-
   body` (NOT a nested bordered box — the members read as continuation rows of
   the same flush list). */
.ac-group > .ac-ability-row.ac-group-head {
  cursor: pointer;
  padding-top: 11px;
  padding-bottom: 11px;
}
.ac-group-head .ac-ability-name { font-size: 13.5px; }
.ac-group-body {
  display: none;
  /* No own background / box — the members are flush rows revealed below the
     head. A faint inset on the left edge marks them as children. */
  border-top: var(--border-width) solid var(--border);
}
.ac-row.expanded > .ac-group-body { display: block; }
/* Hairlines between members. */
.ac-group-body > .ac-ability-row + .ac-ability-row,
.ac-group-body > .ac-card.ac-card-compact + .ac-ability-row,
.ac-group-body > .ac-ability-row + .ac-card.ac-card-compact,
.ac-group-body > .ac-card.ac-card-compact + .ac-card.ac-card-compact {
  border-top: var(--border-width) solid var(--border);
}
/* Admin table: member rows have .ac-ability-body siblings between them so the
   adjacent-sibling rule above never fires. Cover them with :not(:first-child). */
.ac-group-body > .ac-ability-row:not(:first-child) {
  border-top: var(--border-width) solid var(--border);
}
/* Agent table: member rows are wrapped in .ac-row; cover those too. */
.ac-group-body > .ac-row + .ac-row {
  border-top: var(--border-width) solid var(--border);
}
/* Member rows sit clearly indented under the group, on a faintly recessed
   surface so the group reads as a parent without becoming a separate table.
   The group head's content starts at 14px (its base `.ac-ability-row` pad); a
   32px member indent is a deliberate one-level step in from that. The agent
   table's wrapped cards (`.ac-group-body > .ac-row` below) reach the same
   effective indent via the card wrapper instead. */
.ac-group-body > .ac-ability-row,
.ac-group-body > .ac-card.ac-card-compact .ac-int-row {
  padding-left: 32px;
  background: rgba(var(--ink-rgb), 0.12);
}
/* The expanded settings/tools body lines up under its (now indented) member
   row — override the base `.ac-ability-body` 20px so admin members and their
   bodies share the same left edge. (Agent cards indent via the wrapper, so
   their inner body is excluded by the `> ` direct-child combinator.) */
.ac-group-body > .ac-ability-body {
  padding-left: 32px;
}
/* Agent table: each member is a `.conn-card.ac-row` wrapper holding a header
   row + expandable body. Indent the whole wrapper so the header AND its body
   shift right together, landing the header content at the same 32px (18px
   wrapper + 14px base header pad) as the admin member rows above. Full-width
   hairline dividers are unaffected (padding sits inside the card's border). */
.ac-group-body > .ac-row {
  padding-left: 18px;
}
.ac-group-body > .ac-ability-row:hover,
.ac-group-body > .ac-card.ac-card-compact .ac-int-row:hover {
  background: rgba(var(--brand-rgb), 0.06);
}
/* Credential cards (Web Scraper / Browser Cookies) relocated into the Web
   group shed their own box so they line up with the toggle rows. */
.ac-group-body > .ac-card.ac-card-compact {
  margin: 0;
  border: none;
  border-radius: 0;
  background: transparent;
}
.ac-group-body > .ac-card.ac-card-compact > div[id$="-body"] { padding-left: 32px; }
body.light-mode .ac-group-body { border-top-color: var(--border); }
body.light-mode .ac-group-body > .ac-ability-row + .ac-ability-row,
body.light-mode .ac-group-body > .ac-card.ac-card-compact + .ac-ability-row,
body.light-mode .ac-group-body > .ac-ability-row + .ac-card.ac-card-compact,
body.light-mode .ac-group-body > .ac-card.ac-card-compact + .ac-card.ac-card-compact {
  border-top-color: var(--border);
}
body.light-mode .ac-group-body > .ac-ability-row:not(:first-child),
body.light-mode .ac-group-body > .ac-row + .ac-row {
  border-top-color: var(--border);
}
body.light-mode .ac-group-body > .ac-ability-row,
body.light-mode .ac-group-body > .ac-card.ac-card-compact .ac-int-row {
  background: var(--bg-1);
}
body.light-mode .ac-group-body > .ac-ability-row:hover,
body.light-mode .ac-group-body > .ac-card.ac-card-compact .ac-int-row:hover {
  background: rgba(var(--brand-rgb), 0.06);
}

/* ── 3-position toggle (Off · Mixed · On) ─────────────────────────────────
   A wider sliding switch with three detents. The left/right halves are click
   targets (all-off / all-on); the centre "mixed" detent is set by the code
   when members are partially on — you reach it by toggling members, not by
   clicking. Mirrors `.conn-toggle-track` colours so it sits with the app's
   switches. */
.ac-tri {
  position: relative;
  display: block;
  width: 54px;
  height: 20px;
  flex-shrink: 0;
  border-radius: 10px;
  border: var(--border-width) solid var(--border);
  background: var(--border-soft);
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}
.ac-tri-knob {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--fg-3);
  transition: left 0.2s, background 0.2s;
}
.ac-tri[data-state="mixed"] .ac-tri-knob { left: 20px; background: var(--warning); }
.ac-tri[data-state="on"] { background: rgba(var(--brand-rgb), 0.4); border-color: var(--accent); }
.ac-tri[data-state="on"] .ac-tri-knob { left: 38px; background: var(--accent); }
/* Tri-toggle off/mixed/on colours are all design-system tokens above, so they
   adapt to light mode — the former body.light-mode .ac-tri overrides were
   redundant and removed. */
/* Inactive group toggle — same footprint as `.ac-tri`, shown for groups whose
   members are all placeholders / coming-soon (nothing to toggle). Greyed and
   non-interactive so every group row still carries a same-size toggle. */
.ac-tri.ac-tri-disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

/* ── Permission tri ceiling (admin global limit) ──────────────────────────────
   The admin's per-tool global default is the LOOSEST a single agent may pick
   (Auto < Ask < Deny). On the PER-AGENT tree the looser-than-ceiling side of the
   tri is dimmed with a hatch so it reads as locked; the JS clamps any looser
   click up to the ceiling (see `_buildPermissionTri` in dom-utils.js). Only the
   per-agent tree sets `data-ceiling` — the admin tree IS the ceiling, so its tris
   are never hatched. Hatch colour is a `color-mix` of `--fg-3`, so it tracks both
   themes (no hard-coded neutral). */
.ac-tri-perm[data-ceiling]::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; right: 0;
  border-radius: 0 10px 10px 0;
  background: repeating-linear-gradient(-45deg,
    color-mix(in oklab, var(--fg-3) 38%, transparent) 0 3px,
    transparent 3px 6px);
  pointer-events: none;
}
/* Ceiling = Ask → lock the Auto third (rightmost ~1/3). */
.ac-tri-perm[data-ceiling="ask"]::after { left: 66%; }
/* Ceiling = Deny → lock the Ask + Auto two-thirds (rightmost ~2/3). */
.ac-tri-perm[data-ceiling="deny"]::after { left: 33%; }

/* ── Group head delete button (admin) ────────────────────────────────────────
   The group row's trash button sits as the LAST child of the head, mirroring
   the per-ability delete button (`.ac-ability-delete-btn`) so the two line up
   on the same right edge. Both share the same inline style set in JS; this rule
   only guarantees the group head reserves room for it at the same offset. */
.ac-group-head .ac-ability-delete-btn { margin-left: 0; }

/* ── Compactified integration cards ──────────────────────────────────────
   Every OAuth / channel / generic card (agent-settings/agent-settings.html) is turned
   into a slim row that matches the Agent Tools table: the original card header
   is hidden and a `.ac-int-row` is prepended; the config form lives in the
   collapsible `…-body` revealed on expand. See _compactifyCard() in
   app-config.js. */
.ac-card.ac-card-compact {
  padding: 0;
  margin: 0 0 6px;
  border: var(--border-width) solid var(--border);
  border-radius: 10px;
  background: var(--bg-elev);
  overflow: hidden;
}
.ac-card-compact .ac-int-row {
  border-bottom: none;
  cursor: pointer;
}
.ac-card-compact > div[id$="-body"] {
  padding: 12px 14px 14px;
  border-top: var(--border-width) solid var(--border);
}
.ac-int-row-chevron {
  display: flex;
  align-items: center;
  margin-left: 8px;
  flex-shrink: 0;
  color: var(--fg-muted);
  transition: transform 0.2s;
}
.ac-int-row .ac-ability-toggle-wrap { margin-left: auto; }
.ac-int-row-chevron + * { margin-left: 0; }

/* Integration cards grouped into ONE flush `.ac-list` (see _groupIntegrationLists
   in app-config.js). The list draws the border / radius / dividers; each card
   sheds its own box so the section reads as a single compact option-table —
   the same look as the Agent Tools reference. */
.ac-list > .ac-card.ac-card-compact {
  margin: 0;
  border: none;
  border-radius: 0;
  background: transparent;
}
/* Divider between cards — needs higher specificity than the `border:none` above,
   so the generic `.ac-list > * + *` rule (lower specificity) can't supply it. */
.ac-list > .ac-card.ac-card-compact + .ac-card.ac-card-compact {
  border-top: var(--border-width) solid var(--border);
}
body.light-mode .ac-list > .ac-card.ac-card-compact + .ac-card.ac-card-compact {
  border-top-color: var(--border);
}

/* ── Coming-soon rows (greyed, non-interactive) ──────────────────────────── */
.ac-soon-rows {
  margin-top: 8px;
}
.ac-soon-row { opacity: 0.7; }
.ac-soon-row .ac-ability-icon { color: var(--fg-muted); }
.ac-soon-pill {
  flex-shrink: 0;
  margin-left: auto;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--bg-2);
  color: var(--fg-muted);
  border: var(--border-width) solid var(--border-soft);
  white-space: nowrap;
}
body.light-mode .ac-card.ac-card-compact { background: var(--bg-elev); border-color: var(--border); }
body.light-mode .ac-card-compact > div[id$="-body"] { border-top-color: var(--border); }
body.light-mode .ac-soon-pill { background: rgba(var(--ink-rgb), 0.08); border-color: var(--border); }

/* ╔══════════════════════════════════════════════════════════════════════╗
   ║  SHARED COLLAPSIBLE SECTION PATTERN  —  KEEP IN SYNC ACROSS PAGES      ║
   ╠══════════════════════════════════════════════════════════════════════╣
   ║  `.ac-category-group` is the ONE collapsible-section look used by every ║
   ║  Admin Config page: Agent Settings (Agent Tools / Channels / Providers ║
   ║  groups) AND App Settings (Agent LLM Defaults / Main Panel Pages /      ║
   ║  Startup & Boot / Remote Access), and any pages added later.            ║
   ║                                                                        ║
   ║  The group is a PLAIN TEXT HEADER (chevron + icon + title), collapsed   ║
   ║  by default, that opens to reveal ONE full-width `.ac-card` (or         ║
   ║  `.ac-abilities-compact`) of options below — never a card-within-a-     ║
   ║  card. Page margins come from the parent panel, not these rows.         ║
   ║                                                                        ║
   ║  >>> If you restyle these groups, change them HERE only. Do not fork    ║
   ║  >>> per-page copies — every page reads from this single block so the   ║
   ║  >>> whole app stays visually coherent. The matching markup contract    ║
   ║  >>> is documented in ui/admin-tools/app-config/agent-settings/agent-settings.html (search   ║
   ║  >>> "COLLAPSIBLE SECTION PATTERN").                                    ║
   ║                                                                        ║
   ║  STICKY-NAV PAGES (App/Agent Settings · Data Management · Features —    ║
   ║  the `.ac-stickynav` sections, see the "ADVANCED SCROLL" block below):  ║
   ║  there a collapsible group is a PLAIN `<div class="ac-category-group    ║
   ║  ac-collapsible">` driven by an `.ac-open` class, NOT a native          ║
   ║  `<details>` — display:contents (the flatten that lets headings stack)  ║
   ║  breaks a <details>'s collapse AND its flow layout. Non-sticky pages    ║
   ║  may still use native `<details>` here.                                 ║
   ╚══════════════════════════════════════════════════════════════════════╝ */
.ac-category-group {
  margin-top: 14px;
  border: none;
  border-radius: 0;
  background: transparent;
  overflow: visible;
}
.ac-category-group[open] {
  background: transparent;
}
.ac-category-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 2px;
  cursor: pointer;
  user-select: none;
  list-style: none;
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-1, var(--fg-1));
  border-radius: 8px;
  transition: background 0.15s;
}
.ac-category-summary::-webkit-details-marker {
  display: none;
}
.ac-category-summary:hover {
  background: rgba(var(--brand-rgb), 0.05);
}
/* ── Inline LIVE theme switch in a category header (App Settings → Design) ──
   A compact pill of shared `.theme-option` buttons pushed to the right edge of
   the `.ac-category-summary` flex row. The button look (idle/hover/active +
   light-mode) is the shared `.theme-option` style (app1.css + design-system.css);
   this block only sets the compact pill container + sizing, and overrides the
   dropdown's flex:1 stretch so the three buttons stay tight in the header.
   SISTER-CONTROL of .theme-toggle-group (user dropdown). */
.ac-theme-switch {
  /* ABSOLUTE so the switch can't grow the header row — the heading keeps the
     exact same height as the other category headers (driven only by the title
     line). Anchored to the right edge, vertically centred. Containing block =
     the sticky `.ac-category-summary` (position:sticky), which always wraps this
     switch (Design header lives in an `.ac-stickynav` section). */
  position: absolute;
  right: 2px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--bg-0);
  border: var(--border-width) solid var(--border);
  border-radius: 999px;
}
.ac-theme-switch .theme-option {
  flex: 0 0 auto;                  /* override the dropdown's flex:1 stretch */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 3px 8px;
  font-size: 13px;
}
/* Tunnel "How do I set up my Cloudflare tunnel?" collapsible guide */
.ac-tl-guide-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  cursor: pointer;
  user-select: none;
  list-style: none;
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-1);
  transition: background 0.15s;
}
.ac-tl-guide-summary::-webkit-details-marker { display: none; }
.ac-tl-guide-summary:hover { background: rgba(var(--brand-rgb), 0.05); }
.ac-tl-guide-chev { transition: transform 0.18s ease; }
.ac-tl-guide[open] .ac-tl-guide-chev { transform: rotate(180deg); }

/* ── Remote Access row bodies (App Settings → Remote Access) ────────────────
   Small layout helpers for the content INSIDE the three expandable rows
   (same-network · internet method · on-your-phone). The table/row look itself
   is the shared .ac-list / .ac-row component; these only tidy the form bits
   that used to be ad-hoc inline-styled boxes across two separate panels. All
   colours are design-system vars, so the bodies are correct in dark AND light.
   Element ids inside are read by ui/shared/js/remote-access.js +
   ui/shared/js/tunnel-link.js. */
.ac-ra-url-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ac-ra-code {
  font-size: 13px;
  padding: 4px 8px;
  background: var(--bg-0);
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
  color: var(--fg-1);
}
.ac-ra-code-long { font-size: 12px; word-break: break-all; max-width: 100%; }

/* Same-network: every LAN address stacked, each a uniform code + Copy + QR row. */
.ac-ra-sn-urls { display: flex; flex-direction: column; gap: 8px; }
.ac-ra-sn-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ac-ra-sn-row .ac-ra-code { flex: 1 1 auto; min-width: 0; word-break: break-all; }
/* Compact square icon button (Copy / QR) — Lucide glyph, no text. */
.ac-ra-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
  width: 30px; height: 30px; padding: 0;
  background: var(--bg-0);
  border: var(--border-width) solid var(--border);
  color: var(--fg-2);
}
.ac-ra-icon-btn:hover { color: var(--accent); border-color: var(--accent); opacity: 1; }
.ac-ra-icon-btn .lucide-icon,
.ac-ra-icon-btn svg { width: 15px; height: 15px; }
/* Clone-current-repo (Repo-to-deploy header) busy state — dim + gently spin the
   icon while it reads the current repo. See deploy.js _cloneCurrentRepo. */
.ac-deploy-repo-clone-busy { opacity: 0.55; pointer-events: none; }
.ac-deploy-repo-clone-busy svg { animation: ac-deploy-spin 0.8s linear infinite; }
@keyframes ac-deploy-spin { to { transform: rotate(360deg); } }
/* Clone toggle ON: the button is a two-state toggle (clone the current repo ↔ back
   to the production repo). While a clone is in effect it reads as active/filled so
   it's clear a second click reverts. See deploy.js _toggleCloneCurrentRepo. */
.ac-deploy-repo-clone-on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg-0);
}
.ac-deploy-repo-clone-on:hover { color: var(--bg-0); opacity: 0.9; }
/* Primary icon button (Start) — accent-filled to stand out from the rest. */
.ac-ra-icon-btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg-0);
}
.ac-ra-icon-btn-primary:hover { color: var(--bg-0); opacity: 0.85; }

/* QR popover: a small clickable hover-card anchored to the QR button (placed by
   _placeQrPopup in remote-access.js). Click-outside / Escape closes it. */
.ac-ra-qr-pop {
  position: fixed; z-index: 1000;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 10px;
  min-width: 132px;            /* keep the QR a usable size for short URLs */
  background: var(--bg-elev, var(--bg-0));
  border: var(--border-width) solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb), 0.45);
}
/* Functional white plate so the QR scans dark-on-light in either theme. The
   plate stretches to the panel width (driven by the one-line URL), so the QR
   square is at least as wide as the address. */
.ac-ra-qr-plate { background: #ffffff; padding: 8px; border-radius: 6px; align-self: stretch; }
.ac-ra-qr-pop-url {
  font-size: 11px;
  font-family: var(--font-mono, monospace);
  color: var(--fg-2);
  white-space: nowrap;         /* the address stays on a single line */
  text-align: center;
}
.ac-ra-code-inline {
  font-size: 11px;
  padding: 1px 5px;
  background: var(--bg-0);
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  color: var(--fg-1);
}
.ac-ra-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-top: 10px;
}
.ac-ra-fields:empty { margin-top: 0; }
.ac-ra-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 12px;
}
/* Deploy card — the local-deployments list (this app + sibling checkouts),
   deploy.js `_renderInstances`. Each instance is an expandable `.ac-row` inside
   #ac-deploy-instances; the list reuses the shared `.ac-ability-row` /
   `.ac-ability-body` look, so these rules only add the status dot, badges, the
   "Port N · folder" meta line and the sibling action buttons. Theme-safe. */
.ac-deploy-insts > .ac-row + .ac-row {
  border-top: var(--border-width) solid var(--border);
}
/* Status dot in the icon column (green running / amber busy / grey stopped). */
.ac-deploy-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--fg-muted, var(--fg-3));
}
.ac-deploy-dot-run {
  background: var(--success, #22c55e);
  box-shadow: 0 0 0 3px rgba(var(--success-rgb, 34, 197, 94), 0.18);
}
.ac-deploy-dot-busy { background: var(--warning, #d1a01e); }
.ac-deploy-dot-stop { background: var(--fg-muted, var(--fg-3)); opacity: 0.55; }
/* Right-hand status word on the title line. */
.ac-deploy-inst-status {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 500;
  color: var(--fg-muted, var(--fg-3));
}
/* Hold-to-restart button on the "This app" hub row (deploy.js _beginResetHold).
   Press and hold: after a beat it arms to the hazard triangle + tints red — the
   same cue the app's delete buttons use — while the fill sweeps left→right; hold
   to the end and the server restarts (no confirm dialog; the hold is the confirm).
   The .holding fill duration MUST match _RESET_HOLD_MS in deploy.js. */
.ac-deploy-reset-btn {
  position: relative;
  align-self: center;          /* centre on the title line (row is flex-start) */
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: var(--border-width) solid var(--border);
  border-radius: 7px;
  background: transparent;
  color: var(--fg-muted, var(--fg-3));
  cursor: pointer;
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;          /* let a touch-hold work without triggering scroll */
}
.ac-deploy-reset-btn:hover { color: var(--fg-1); border-color: var(--border-strong, var(--border)); }
.ac-deploy-reset-btn.warning { color: var(--danger); border-color: rgba(var(--danger-rgb, 220, 80, 80), 0.6); }
.ac-deploy-reset-btn .ac-deploy-reset-ico { position: relative; z-index: 1; display: inline-flex; }
.ac-deploy-reset-btn .ac-deploy-reset-ico svg { width: 14px; height: 14px; }
.ac-deploy-reset-fill {
  position: absolute;
  inset: 0;
  z-index: 0;
  transform: scaleX(0);
  transform-origin: left center;
  background: rgba(var(--danger-rgb, 220, 80, 80), 0.22);
  transition: transform 150ms ease;   /* quick snap-back on release / abort */
}
.ac-deploy-reset-btn.holding .ac-deploy-reset-fill {
  transform: scaleX(1);
  transition: transform 1200ms linear; /* == _RESET_HOLD_MS in deploy.js */
}
/* Small pill badges after the name ("This app", "Needs update"). */
.ac-deploy-badge {
  display: inline-block;
  margin-left: 7px;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 600;
  vertical-align: middle;
}
.ac-deploy-badge-app {
  background: rgba(var(--accent-rgb, 224, 163, 94), 0.16);
  color: var(--accent);
}
.ac-deploy-badge-warn {
  background: rgba(var(--warning-rgb, 209, 160, 30), 0.18);
  color: var(--warning, #b8860b);
}
/* The "Port N · folder" line. The port is fixed-width; the folder is monospace
   and dims slightly. A middle dot separates them. */
.ac-deploy-meta-port { font-weight: 600; color: var(--fg-2, var(--fg-1)); }
.ac-deploy-meta-folder {
  margin-left: 14px;
  font-family: var(--font-mono);
  color: var(--fg-muted, var(--fg-3));
  word-break: break-all;
}
.ac-deploy-meta-folder::before {
  content: "·";
  margin-right: 14px;
  color: var(--fg-muted, var(--fg-3));
}
/* Danger action (Remove) reuses the base `.ac-btn` but tints red. */
.ac-deploy-danger-btn { color: var(--danger); border-color: rgba(var(--danger-rgb, 220, 80, 80), 0.5); }
.ac-deploy-danger-btn:hover { background: rgba(var(--danger-rgb, 220, 80, 80), 0.1); }
/* Reused for the "reopen at" link after a hub-port relaunch. */
.ac-deploy-current-link {
  color: var(--accent);
  text-decoration: none;
  font-family: var(--font-mono);
}
.ac-deploy-current-link:hover { text-decoration: underline; }

/* Setup-bundle rows (data-settings.html #ac-deploy-export-row /
   #ac-deploy-import-row): two static top-level rows in #ac-deploy-list, siblings
   of "+ New deployment". They reuse the shared expandable `.ac-row` /
   `.ac-ability-row` / `.ac-ability-body` look, so these rules only add the
   password + Verify row and the import preview lines. Behaviour wired by
   deploy.js _initBootRows; expand by data-settings.js _wireBootRow. Theme-safe. */
/* Password + inline Verify button. The field grows; the button hugs its label
   and matches the input height so they read as one control. The Verify button
   reveals the Copy (export) / Accept (import) action once it succeeds. */
.ac-boot-pw-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
}
.ac-boot-pw-row .ac-input { flex: 1 1 auto; min-width: 0; }
.ac-boot-verify { flex: 0 0 auto; white-space: nowrap; }
/* One "what the code carries" line in the import preview — summary text with an
   optional "already set" badge, divided by the same hairline the list uses. */
.ac-boot-preview-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  font-size: 12px;
  border-top: var(--border-width) solid var(--border);
}
.ac-boot-preview-row > span:first-child { flex: 1 1 auto; min-width: 0; }
.ac-boot-preview-row .ac-deploy-badge { margin-left: 0; }

/* Deploy card — primary "Deploy now" button + the live NDJSON log.
   Reuses the Remote Access form grid (.ac-ra-fields) + actions row. */
.ac-deploy-go-btn {
  background: var(--accent);
  color: var(--bg-0);
  border-color: var(--accent);
  font-weight: 600;
}
.ac-deploy-go-btn:hover { opacity: 0.85; }
.ac-deploy-log {
  margin-top: 10px;
  max-height: 220px;
  overflow: auto;
  padding: 10px 12px;
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
  background: var(--bg-tint);
  color: var(--fg-2);
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Generated copy-paste command box — shown by "manual" deploy targets (e.g.
   Termux on a phone) that produce a command the admin runs themselves, rather
   than creating a cloud server. The command sits in a monospace box with a Copy
   button; the note below explains what to do with it. */
.ac-deploy-cmd-wrap { margin-top: 12px; }
.ac-deploy-cmd-head {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--fg-3);
  margin-bottom: 5px;
}
.ac-deploy-cmd-row {
  display: flex;
  align-items: stretch;
  gap: 8px;
}
.ac-deploy-cmd {
  flex: 1 1 auto;
  min-width: 0;
  padding: 9px 11px;
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
  background: var(--bg-0);
  color: var(--fg-1);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.5;
  white-space: pre;
  overflow-x: auto;
  user-select: all;            /* one tap/drag selects the whole command */
}
.ac-deploy-cmd-copy {
  flex: 0 0 auto;
  align-self: flex-start;
  white-space: nowrap;
}
.ac-deploy-cmd-note {
  margin-top: 7px;
  padding: 8px 11px;
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  background: var(--bg-tint);
  color: var(--fg-2);
  font-size: 11.5px;
  line-height: 1.55;
}
/* Manual install rows (Linux/Termux, Windows, macOS) — the command box's button
   column (Copy + QR stacked) and the click-to-show QR popover. Shared by all
   three rows (kept the `ac-tx-` names from the original Termux-only row). The
   command is shown LIVE (built by deploy.js as the fields change); the QR button
   toggles a popover (same code as a scannable QR), mirroring Remote Access →
   Same network. */
.ac-tx-cmd-btns {
  flex: 0 0 auto;
  align-self: center;
  display: flex;
  gap: 6px;
}
/* The command QR is denser than a short URL, so give its popover plate more room
   so it stays comfortably scannable. Builds on .ac-ra-qr-pop. */
.ac-tx-qr-pop { min-width: 212px; max-width: 260px; }
.ac-tx-steps {
  margin: 12px 0 0;
  padding-left: 20px;
  color: var(--fg-2);
  font-size: 12px;
  line-height: 1.6;
}
.ac-tx-steps li { margin-bottom: 4px; }

/* Field help: a small circled "?" beside a label. CLICK / TAP opens the help
   bubble (.ac-tip-pop below); hover also previews it on a mouse. Used by the
   Deploy form so each field can explain where to find the value or how to
   choose. (cursor: help = the little "?" pointer, signalling it's clickable.) */
.ac-field-tip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  margin-left: 6px;
  border: var(--border-width) solid var(--border-strong, var(--border));
  border-radius: 50%;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  color: var(--fg-3);
  cursor: help;
  vertical-align: middle;
  user-select: none;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.ac-field-tip:hover,
.ac-field-tip:focus-visible,
.ac-field-tip.is-open {
  color: var(--bg-0);
  background: var(--accent);
  border-color: var(--accent);
  outline: none;
}
/* The floating help bubble itself — one shared element appended to <body> so it
   floats above the panel and never gets clipped by a scroll box. JS positions
   it next to the clicked "?" badge. */
.ac-tip-pop {
  position: fixed;
  z-index: 9999;
  max-width: 300px;
  /* JS caps max-height to the room above/below the badge, so a tall image bubble
     scrolls inside itself instead of running off the top/bottom of the screen. */
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 9px 11px;
  border: var(--border-width) solid var(--border-strong, var(--border));
  border-radius: 8px;
  background: var(--bg-0);
  color: var(--fg-2);
  font-size: 12px;
  line-height: 1.5;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.28);
  pointer-events: none;          /* purely informational — clicks pass through */
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity .12s ease, transform .12s ease;
}
.ac-tip-pop.show { opacity: 1; transform: translateY(0); }
.ac-tip-pop[hidden] { display: none; }
/* Live "who is this size for" note shown under the machine-size menu. */
.ac-field-note {
  margin-top: 6px;
  padding: 7px 10px;
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  background: var(--bg-tint);
  color: var(--fg-2);
  font-size: 11.5px;
  line-height: 1.5;
}
.ac-field-note:empty { display: none; }

/* A clickable helper link rendered directly under a Deploy field's TITLE, above
   the input (e.g. "Open the Google Cloud console"). Dynamic ones (built from the
   project ID) hide themselves until the value they depend on is filled in — see
   deploy.js _applyDynLink. `display:block` keeps it on its own line between the
   label and the field; the bottom margin separates it from the input. */
.ac-field-link {
  display: block;
  margin-top: 4px;
  margin-bottom: 7px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  word-break: break-word;
}
.ac-field-link:hover { text-decoration: underline; }

/* Inline "how to get this" help under a form field — the field's `tip` spelled
   out visibly (not behind the hover ⓘ badge) while a provider is being set up.
   Used by dns.js `_buildField(..., showTip=true)`; collapses back to the badge
   once the provider is connected. Muted, small, its own line under the input. */
.ac-field-help {
  margin-top: 5px;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--fg-3);
  word-break: break-word;
}

/* Rich help bubble — carries a paragraph, an optional (project-ID-driven) link,
   and screenshot thumbnails, not just one line of text. Made clickable (the plain
   bubble is pointer-events:none) so the link inside can be followed. */
.ac-tip-pop.is-rich { pointer-events: auto; }
.ac-tip-pop .ac-tip-text { margin-bottom: 8px; }
.ac-tip-pop .ac-tip-link {
  display: inline-block;
  margin-bottom: 8px;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  word-break: break-all;
}
.ac-tip-pop .ac-tip-link:hover { text-decoration: underline; }
.ac-tip-pop .ac-tip-gallery { display: flex; flex-direction: column; gap: 8px; }
.ac-tip-pop .ac-tip-gallery img {
  display: block;
  width: 100%;
  height: auto;
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
  cursor: zoom-in;                /* click to open full-size in the lightbox */
  transition: border-color .12s ease;
}
.ac-tip-pop .ac-tip-gallery img:hover { border-color: var(--accent); }

/* Full-screen image viewer opened by clicking a screenshot inside a help bubble.
   One shared element appended to <body>, above every panel and the tip bubble
   itself (z-index over .ac-tip-pop). Click anywhere (or Escape) to close. */
.ac-tip-lightbox {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.8);
  cursor: zoom-out;
  opacity: 0;
  transition: opacity .12s ease;
}
.ac-tip-lightbox.show { opacity: 1; }
.ac-tip-lightbox[hidden] { display: none; }
.ac-tip-lightbox img {
  max-width: 92vw;
  max-height: 92vh;
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
  box-shadow: 0 10px 44px rgba(0, 0, 0, 0.55);
}

/* Advanced-actions panel revealed by the Deploy form's "More" button — the
   less-common actions (test / save / tear-down) tucked away so the completion
   step shows only the primary "Activate" button. */
.ac-deploy-more-panel {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
  padding: 10px;
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
  background: var(--bg-tint);
}
.ac-deploy-more-panel[hidden] { display: none; }

/* Drag-and-drop affordance on the Service-account JSON box: dropping the
   downloaded key file fills the box as if pasted (deploy.js _wireDropzone). */
textarea.ac-dropzone.is-dragover {
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: 0 0 0 2px var(--accent-line);
}
.ac-ra-check-lbl {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--fg-3);
  cursor: pointer;
}
.ac-ra-subhead {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-1);
  margin-bottom: 4px;
}
.ac-ra-subhead .lucide-icon { width: 14px; height: 14px; color: var(--accent); flex-shrink: 0; }
.ac-ra-disclosure > summary {
  cursor: pointer;
  font-size: 11px;
  color: var(--fg-3);
  list-style: none;
}
.ac-ra-disclosure > summary::-webkit-details-marker { display: none; }
.ac-category-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  font-size: 10px;
  color: var(--fg-3, var(--fg-3));
  transition: transform 0.2s;
  flex-shrink: 0;
}
.ac-category-group[open] .ac-category-chevron {
  transform: rotate(90deg);
}
.ac-category-title {
  flex: 1;
}
.ac-category-count {
  font-size: 10px;
  color: var(--fg-muted, var(--fg-3));
  font-weight: 400;
}
.ac-category-body {
  padding: 4px 0 0;
  border-top: none;
}
/* Standard config-page width. Each group on the App Settings page — its
   subheading AND its `.ac-list` table together — is capped and centred so rows
   stay a comfortable reading width instead of stretching across the whole panel,
   and the heading's left edge lines up with the table below it. This is the
   reference width for the standardised config-page layout (see
   docs/claude/ui-guidance.md). */
/* `width:100%` is REQUIRED, not just max-width: the group is a flex item of the
   column-flex `.ac-section`, and the centring `margin:0 auto` (auto cross-axis
   margins) cancels the default stretch — without an explicit width each group
   would then size to its OWN content, so the short-label Main/Admin Panel tables
   collapsed to ~130px while the long-text Design/Remote tables filled ~750px.
   width:100% gives a definite size so every group resolves to min(panel,750px)
   regardless of content, and the auto margins still centre it once the panel is
   wider than 750. */
/* ── ADVANCED SCROLL: sticky section navigator (opt-in via .ac-stickynav) ────
   A config page opts in by adding `ac-stickynav` to its `<section>` and calling
   initStickyNav() from its module (shared code in
   ui/admin-tools/app-config/sticky-nav.js). On those pages the section headings
   stay on screen the WHOLE time you scroll: a heading whose table you've
   scrolled past pins to the TOP of the list, a heading whose table is still
   coming up pins to the BOTTOM, and the one you're reading sits between them
   with its table. Every heading is always visible and is click-to-jump to its
   table. Pages that DON'T add the class keep their normal native layout
   (e.g. a `<details>` group there still uses native collapse) — this whole
   block is gated on `.ac-stickynav`.

   WHY `display:contents` — a `position:sticky` element can only travel inside
   its own containing block. Each heading normally lives inside its own
   `.ac-category-group`, so it could only stay stuck while THAT one group was on
   screen. Collapsing the group with `display:contents` removes the group's box,
   so the heading and its table become direct children of the whole section —
   now the heading's containing block is the ENTIRE page, which is what lets it
   stay stuck across every other section. The per-heading stacked top/bottom
   offsets (so headings pile up instead of overlapping) and the click-to-jump
   are set by initStickyNav() in sticky-nav.js.

   COLLAPSIBLE GROUPS — display:contents on a `<details>` breaks BOTH its
   native collapse AND its flow layout (the details' content box won't take part
   in the section's flex column, so the open bodies overlap). So a collapsible
   group on a sticky-nav page is authored as a PLAIN `<div class="ac-category-
   group ac-collapsible">` (same element App Settings' always-open groups use,
   which flows correctly under display:contents) and is opened/closed via the
   `.ac-open` CLASS below — never a `<details>`. A `.ac-category-group` WITHOUT
   `.ac-collapsible` stays always-open (App Settings' page lists). */
.ac-stickynav { gap: 0; }
/* Pull the sticky section flush up under the tab strip: cancel #app-config-content's
   top padding (28px — keep in sync with that rule) so the FIRST heading sits
   directly beneath the tabs with no gap. It already PINS flush there when
   scrolled; this matches it at rest. Scoped to .ac-stickynav, so non-sticky
   config tabs keep the scroller's normal top breathing room. */
.ac-stickynav { margin-top: -28px; }
/* No inter-section space before the very first heading (that 14px margin is only
   meant to separate one section's heading from the previous section's table). */
.ac-stickynav .ac-category-group:first-of-type > .ac-category-summary { margin-top: 0; }
.ac-stickynav .ac-category-group { display: contents; }

/* A top-of-page header or banner on a sticky-nav page (e.g. Data Management's
   env-lock banner) is capped + centred to the same 750px column as the group
   headings/tables below, so everything shares one width and left edge. (No
   sticky-nav page currently uses .ac-section-header — they follow the standard
   "no page-title header" layout — but the rule is kept for any that adds one.) */
.ac-stickynav > .ac-section-header,
.ac-stickynav > .ac-info-banner {
  width: 100%;
  max-width: 750px;
  margin-left: auto;
  margin-right: auto;
}

/* Heading banner: capped + centred to the same 750px as the table so their left
   edges line up (this cap used to live on the now-boxless `.ac-category-group`),
   plus the sticky-banner look. `top`/`bottom` (the stacked offsets) are written
   per-heading inline by initStickyNav. The translucent blurred backing keeps
   the living-glass page background showing through while the table rows scroll
   cleanly underneath it. */
.ac-stickynav .ac-category-summary {
  width: 100%;
  max-width: 750px;
  margin-left: auto;
  margin-right: auto;
  margin-top: 14px;                 /* inter-section spacing (was the group's margin) */
  position: sticky;
  z-index: 6;
  cursor: pointer;
  /* FULLY OPAQUE, with a dynamic glow gradient that APPROXIMATES the panel
     background (not an exact match — the same family of colours). Why opaque and
     not the panel's translucent glass: the panel gets its living glow from
     `backdrop-filter` (blur+saturate), but some mobile browsers (e.g. the Corona
     browser) don't support backdrop-filter and silently drop it — so a translucent
     heading there let scrolled rows read straight through. A fully opaque fill
     hides scrolled content on EVERY browser with no backdrop-filter needed.

     The fill is the opaque page base (--bg-0) under two soft radial glows wired to
     the palette (ambient hue top-centre, brand hue lower-right — the same hues the
     animated background uses), so it reads as a dim version of the panel glow and
     tracks the theme/preset + live Appearance edits. background-attachment:fixed
     positions the glows against the viewport so they line up with the page glow
     instead of each heading carrying its own blob. Square corners (kept) — the
     scroll-area edges are rounded by the SCROLLER itself (#app-config-content has
     border-radius + overflow clip), which clips whatever sits at the top/bottom
     edge (heading OR a content row), so the headings don't need their own
     rounding. The image list lives in --ac-head-bg so the base
     .ac-category-summary:hover `background` shorthand can't wipe it. */
  --ac-head-bg:
    radial-gradient(90vmax at 50% 30%, rgba(var(--ambient-2-rgb, 187,154,247), 0.16), transparent 60%),
    radial-gradient(60vmax at 82% 78%, rgba(var(--brand-rgb, 125,207,255), 0.14), transparent 60%);
  /* Opaque base, lifted a touch off the page base (--bg-0) toward the text colour
     so the bar reads a little lighter than the raw page background. */
  --ac-head-base: color-mix(in oklab, var(--bg-0) 90%, var(--fg-1));
  border-radius: 0;
  background-color: var(--ac-head-base);
  background-image: var(--ac-head-bg);
  background-attachment: fixed, fixed;
  background-repeat: no-repeat;
  box-shadow: none;
  transition: color 0.15s;
}
/* Hover keeps the identical opaque fill (the base .ac-category-summary:hover
   `background` shorthand would otherwise wipe the glow image + base colour). */
.ac-stickynav .ac-category-summary:hover {
  background-color: var(--ac-head-base);
  background-image: var(--ac-head-bg);
}
/* Cursor-follow spotlight on the headings — the SAME `.spot` system the cards /
   bubbles / main panel use (cursor-effects.js adds the `.spot` class and tracks
   --mx/--my as the pointer moves over the heading; the glow shows while hovered).
   Tuned for a thin wide bar: a tighter radius + a slightly stronger brand tint
   than the default since it sits on the opaque dark bar. The shared .spot::before
   paints at z-index:0, which on a heading would land the glow OVER the inline
   label text — so we drop it to z-index:-1 so the glow rides over the opaque
   background but UNDER the text. (position:sticky on the heading makes its own
   stacking context, so -1 stays above the bar's own background, not behind it.) */
.ac-stickynav .ac-category-summary.spot {
  --spot-radius: 260px;
  --spot-color: rgba(var(--brand-rgb), 0.16);
}
.ac-stickynav .ac-category-summary.spot::before { z-index: -1; }
/* The heading whose table you're currently reading. */
.ac-stickynav .ac-category-summary.ac-navh-active {
  color: var(--accent);
}
/* Cap the BODY (not just the `.ac-list`) at 750px centred too, so everything a
   group body holds — the table AND any banners/notes/cards beside it (e.g. the
   Remote Access security banner) — lines up to the same width as the heading
   instead of stretching the full section width. */
.ac-stickynav .ac-category-body {
  width: 100%;
  max-width: 750px;
  margin-left: auto;
  margin-right: auto;
}
.ac-stickynav .ac-list { width: 100%; max-width: 750px; margin-left: auto; margin-right: auto; }

/* Collapsible groups on a sticky-nav page are class-driven (a plain <div> with
   `.ac-collapsible`, opened via `.ac-open`): hide the body unless `.ac-open`,
   and rotate the chevron when open. A group WITHOUT `.ac-collapsible` is never
   hidden, so it stays always-open. */
.ac-stickynav .ac-category-group.ac-collapsible:not(.ac-open) > .ac-category-body { display: none; }
.ac-stickynav .ac-category-group.ac-collapsible.ac-open > .ac-category-summary .ac-category-chevron { transform: rotate(90deg); }
.ac-category-desc {
  font-size: 11px;
  color: var(--fg-muted, var(--fg-3));
  margin: 0 2px 12px;
  line-height: 1.5;
}
body.light-mode .ac-category-group {
  background: transparent;
  border-color: transparent;
}
body.light-mode .ac-category-summary {
  color: var(--fg-1);
}
body.light-mode .ac-category-summary:hover {
  background: rgba(var(--brand-rgb), 0.04);
}
/* …but a STICKY heading must stay OPAQUE on hover, or scrolled rows read through
   it (the translucent shorthand above otherwise wipes the opaque fill + glow,
   and outranks the dark `.ac-stickynav …:hover` rule in light mode). Re-assert
   the opaque fill for the sticky headers — visible most on colored light themes. */
body.light-mode .ac-stickynav .ac-category-summary:hover {
  background-color: var(--ac-head-base);
  background-image: var(--ac-head-bg);
}
body.light-mode .ac-category-chevron {
  color: var(--fg-3);
}
body.light-mode .ac-category-desc {
  color: var(--fg-3);
}

/* ── MOBILE sub-header tab strip (Data / App / Agent Settings) ──────────────
   Opt-in via initStickyNav(id,{mobileCarousel}) — JS in
   ui/admin-tools/app-config/sticky-nav.js (`_buildCarousel`/`_syncMode`). On a
   mobile-width viewport an opted-in section (Data Settings, App Settings, Agent
   Settings) swaps its STACKED sticky headings for a horizontal, swipeable strip
   of its section
   headings. That strip is a GENUINE SUB-HEADER: `_buildCarousel` inserts the
   `.ac-mnav` into #app-config-subnav — a sibling directly beneath
   #app-config-tabs-wrap and OUTSIDE the content scroller — so it spans the full
   panel width, sits flush under the app-config tabs and mirrors their look
   (transparent bar, bottom rule, underline tabs), instead of a rounded card
   clipped inside the scroller. Tapping a tab jumps the scroll to that section;
   the tab for the section you're reading highlights and scrolls itself into view.
   The section's own headings stay (just un-stuck) so each block is still labelled
   as you scroll. Desktop keeps the stacked sticky headings — the strip is removed
   from the DOM there and the host is hidden. */

/* The sub-header host: hidden on desktop / on any tab without a carousel. Shown
   only on mobile while a carousel-enabled section (Data / App / Agent Settings) is
   active (and once its strip is built, so an empty host adds no border). */
#app-config-subnav { display: none; }
.ac-mnav { display: block; }

@media (max-width: 800px) {
  #app-config-container:has(#ac-section-data-settings.active) #app-config-subnav:not(:empty),
  #app-config-container:has(#ac-section-app-settings.active) #app-config-subnav:not(:empty),
  #app-config-container:has(#ac-section-agent-settings.active) #app-config-subnav:not(:empty) {
    display: block;
    flex-shrink: 0;                   /* a fixed header row in the flex column, never squeezed */
  }
  /* Each opted-in section parks its OWN strip in the shared host (tagged with
     data-section), and a strip built earlier stays parked when you switch tabs —
     so show only the strip whose section is currently active, hide the rest. */
  #app-config-subnav > .ac-mnav { display: none; }
  #app-config-container:has(#ac-section-data-settings.active) #app-config-subnav > .ac-mnav[data-section="data-settings"],
  #app-config-container:has(#ac-section-app-settings.active) #app-config-subnav > .ac-mnav[data-section="app-settings"],
  #app-config-container:has(#ac-section-agent-settings.active) #app-config-subnav > .ac-mnav[data-section="agent-settings"] {
    display: block;
  }
  /* Un-stack the in-flow headings for a carousel section so they scroll naturally
     instead of pinning top/bottom. Higher specificity than the base
     `.ac-stickynav .ac-category-summary { position: sticky }`.
     `relative` (not `static`) so the row still un-sticks BUT stays a containing
     block for the absolutely-positioned `.ac-theme-switch` in the Design header —
     `static` here let the switch escape to the viewport and float loose over the
     mobile page instead of pinning to the right of its heading row. */
  .ac-section.ac-mnav-on .ac-category-summary { position: relative; }

  #app-config-subnav > .ac-mnav {
    /* Full-width bar with a bottom rule, mirroring #app-config-tabs-wrap — a
       second row of tabs attached directly under the app-config tabs. It lives
       outside the scroller, so (like the tab wrap) nothing scrolls behind it and
       it can be transparent, letting the panel gradient show through. */
    width: 100%;
    border-bottom: var(--border-width) solid var(--border);
    background: transparent;
  }
  .ac-mnav-strip {
    display: flex;
    gap: 0;
    overflow-x: auto;
    padding: 0 18px;                  /* match #app-config-tabs */
    scroll-behavior: smooth;
    scroll-snap-type: x proximity;
    scrollbar-width: none;            /* hide the horizontal scrollbar */
    -ms-overflow-style: none;
  }
  .ac-mnav-strip::-webkit-scrollbar { width: 0; height: 0; display: none; }
  /* Mirror .ac-tab (the app-config tab bar): underline tabs — the active one
     accent-coloured with the underline + a touch larger, animated. Keep in sync
     with `.ac-tab` above. */
  .ac-mnav-chip {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;              /* sit the underline ON the bar's bottom rule */
    padding: 10px 14px;
    color: var(--fg-muted, var(--fg-3));
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    font-family: inherit;
    cursor: pointer;
    scroll-snap-align: start;
    transition: color 0.15s, border-color 0.15s, font-size 0.15s;
  }
  /* Icon inherits the tab colour (muted when idle, accent when active) — same as
     .ac-tab-icon, which sets no colour of its own. */
  .ac-mnav-chip .ac-mnav-ico { width: 14px; height: 14px; flex: 0 0 auto; }
  .ac-mnav-chip:hover { color: var(--fg-1); }
  .ac-mnav-chip.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
    font-size: 14px;
  }
  /* Light-mode mirror of `body.light-mode .ac-tab*` (active uses --brand there). */
  body.light-mode .ac-mnav-chip { color: var(--fg-3); }
  body.light-mode .ac-mnav-chip:hover { color: var(--fg-2); }
  body.light-mode .ac-mnav-chip.active { color: var(--brand); border-bottom-color: var(--brand); }
  /* Edge-fade on the side(s) that can still scroll — an ALPHA MASK only, so the
     opaque bar (never a guessed colour) shows through the fade. Same technique as
     the main-tab carousel; the both-sides rule outranks the single-side ones. */
  .ac-mnav.can-left .ac-mnav-strip {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 26px);
            mask-image: linear-gradient(to right, transparent 0, #000 26px);
  }
  .ac-mnav.can-right .ac-mnav-strip {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent 100%);
            mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent 100%);
  }
  .ac-mnav.can-left.can-right .ac-mnav-strip {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
  }
}

/* ── Main Panel Pages (drag-reorderable list) ── */
/* Main Panel Pages — the shared `.ac-list` flush table plus a drag handle so the
   rows double as a drag-to-reorder list. The list/row look comes from `.ac-list`
   / `.ac-ability-row`; only the drag affordance, drop indicator and the small
   visibility toggle are defined here. */
.ac-main-panel-list {
  /* container look comes from `.ac-list` */
}
.ac-main-panel-item {
  cursor: grab;
  user-select: none;
}
.ac-main-panel-item.dragging {
  opacity: 0.5;
  cursor: grabbing;
}
.ac-main-panel-item.drag-over {
  box-shadow: inset 0 2px 0 0 var(--accent);
  background: rgba(var(--brand-rgb), 0.08);
}
.ac-main-panel-item .ac-mp-drag-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  color: var(--fg-3, var(--fg-3));
  flex-shrink: 0;
  cursor: grab;
}
.ac-main-panel-item .ac-mp-drag-handle svg {
  width: 14px;
  height: 14px;
}
/* 3-state page-visibility toggle (Off · Signed-in only · Always visible). Mirrors
   the abilities table's `.ac-tri` (same 54px footprint, knob positions and token
   colours) so the two read as the same control: grey knob (left) = off, amber
   knob (middle) = signed-in only, accent knob on a filled track (right) = always
   visible. All colours are design-system tokens, so it adapts to light mode with
   no per-theme override. */
.ac-main-panel-item .ac-mp-tri {
  position: relative;
  width: 54px;
  height: 20px;
  flex-shrink: 0;
  margin-left: auto;
  padding: 0;
  border-radius: 10px;
  /* crisp 1px outline — must be a whole pixel; a sub-pixel width (e.g. 0.6px)
     anti-aliases into a fuzzy ~2px smear on hi-dpi. Tracks the global token (1px). */
  border: var(--border-width) solid var(--border);
  background: var(--border-soft);
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}
.ac-main-panel-item .ac-mp-tri-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--fg-3);
  transition: left 0.2s, background 0.2s;
}
.ac-main-panel-item .ac-mp-tri[data-state="auth"] .ac-mp-tri-knob {
  left: 20px;
  background: var(--warning);
}
.ac-main-panel-item .ac-mp-tri[data-state="all"] {
  /* brand fill marks the active state; the outer border stays the neutral 1px
     var(--border) from the base rule — no teal ring around the pill. */
  background: rgba(var(--brand-rgb), 0.4);
}
.ac-main-panel-item .ac-mp-tri[data-state="all"] .ac-mp-tri-knob {
  left: 38px;
  background: var(--accent);
}
.ac-main-panel-item .ac-mp-tri:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
/* A locked page (Admin Tools / Admin Configuration) can't be turned Off: its tri
   skips the leftmost "off" slot and cycles only Signed-in only ⇄ Always visible.
   It reads like a normal toggle — the full border runs across the whole pill and
   the "Always visible" state carries the same brand fill as the other toggles
   (inherited from the base `[data-state="all"]` rule above) — except the blocked
   "off" slot on the left is blanked out with a dark zone, so part of the inside
   reads as dark / out of range. The knob always sits to its right (auth: left
   20px, all: left 38px), so it's never covered.
   Curve faces the toggle: the dead zone is rounded on its LEFT corners (to hug
   the pill's left end), and its INNER (right) edge is carved into a concave
   socket — a radial-gradient hole the same radius as the knob (7px) — so the
   round knob nests against a mirror of its own curve, like a ball in a socket.
   The slot the knob travels in reads as the right-hand portion of the control;
   the leftmost blocked position is just the remaining untogglable area. */
.ac-main-panel-item .ac-mp-tri-nolock::before {
  content: '';
  position: absolute;
  left: 0;                          /* flush to the inner border edge — no track/fill peeking around the dead zone */
  top: 0;
  bottom: 0;
  width: 25px;                      /* right edge just left of the signed-in knob's centre, cradling it with a hair of clearance */
  border-radius: 9px 0 0 9px;       /* hugs the pill's inner radius (10px outer − 1px border); right edge shaped by the socket below */
  /* concave socket on the right edge. Radius is larger than the knob (~10px vs
     7px) so the arc is gentle — a tight curve shows stair-step aliasing at this
     size. A 2px feather (transparent 9px → bg-1 11px) anti-aliases the edge. */
  background: radial-gradient(circle at right center,
              transparent 9px, var(--bg-1) 11px);
  pointer-events: none;
}
.ac-main-panel-item.ac-mp-locked {
  cursor: default;
  background: rgba(var(--brand-rgb), 0.06);
}
.ac-main-panel-item.ac-mp-locked .ac-ability-name {
  color: var(--accent);
}
body.light-mode .ac-main-panel-item.ac-mp-locked {
  background: rgba(var(--brand-rgb), 0.08);
}

/* Icon button — opens the shared searchable icon picker to change a page icon. */
.ac-main-panel-item .ac-mp-icon-btn {
  background: none;
  border: none;
  padding: 0;
  width: 26px;
  height: 26px;
  cursor: pointer;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--fg-2, var(--fg-2));
  transition: background 0.15s, color 0.15s;
}
.ac-main-panel-item .ac-mp-icon-btn:hover {
  background: var(--bg-2);
  color: var(--accent);
}
body.light-mode .ac-main-panel-item .ac-mp-icon-btn:hover {
  background: var(--bg-2);
}

/* Editable page-name field — reads as plain text until hovered/focused.
   display:block opts out of the shared `display:contents` stacked-row promotion
   (this label wraps an editable input, not a title + description). */
.ac-main-panel-item .ac-ability-label {
  display: block;
  flex: 1;
  min-width: 0;
}
.ac-main-panel-item .ac-mp-name-input {
  width: 100%;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--fg-1, var(--fg-1));
  font: inherit;
  font-size: 13.5px;
  padding: 3px 6px;
  cursor: text;
  transition: border-color 0.15s, background 0.15s;
}
.ac-main-panel-item .ac-mp-name-input:hover {
  border-color: var(--border);
}
.ac-main-panel-item .ac-mp-name-input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-1);
}
.ac-main-panel-item.ac-mp-locked .ac-mp-name-input {
  color: var(--accent);
}
body.light-mode .ac-main-panel-item .ac-mp-name-input:focus {
  background: var(--bg-1);
}

/* ── Terminal tunnel (user drives a terminal through chat) ──────────────────
   The overlay covers the chat message area with a live read-only terminal; the
   banner (above the pill) carries the key palette + Hand-back. Theme-aware via
   design-system variables — correct in dark and light. See ui/shared/js/chatTunnel.js
   and app/agent/terminal_tunnel.py. */
.chat-tunnel-overlay {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: flex;
  flex-direction: column;
  background: var(--bg-0, var(--bg-0));
  border-top: var(--border-width) solid var(--border);
  overflow: hidden;
}
.chat-tunnel-overlay .xterm { padding: 8px; height: 100%; }
.chat-tunnel-error {
  padding: 16px;
  color: var(--danger);
  font-family: var(--font-mono, monospace);
  font-size: 13px;
}

/* ── Terminal Chat engine overlay ───────────────────────────────────────────
   When the active agent runs on the terminal_chat engine, the chat bubble area
   is replaced by a live xterm.js terminal. Same visual contract as the tunnel
   overlay above. See ui/chat-side-panel/js/chat-terminal-engine.js and
   plugins/engines/terminal_chat/. */
#chat-terminal-overlay {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: var(--bg-0);
  border-top: var(--border-width) solid var(--border);
  overflow: hidden;
}
#chat-terminal-overlay .xterm { padding: 8px; height: 100%; }

.chat-tunnel-banner {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 8px;
  padding: 10px 12px;
  border: 1px solid var(--accent);
  border-radius: 12px;
  background: var(--bg-elev);
  box-shadow: 0 0 0 1px rgba(var(--brand-rgb), 0.12) inset;
}
.chat-tunnel-banner-top {
  display: flex;
  align-items: center;
  gap: 8px;
}
.chat-tunnel-label {
  flex: 1 1 auto;
  font-size: 13px;
  color: var(--fg-2, var(--fg-1));
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-tunnel-label .chat-tunnel-cmd {
  color: var(--fg-1);
  font-family: var(--font-mono, monospace);
}
.chat-tunnel-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex: 0 0 auto;
  animation: chat-tunnel-pulse 1.6s ease-in-out infinite;
}
@keyframes chat-tunnel-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.7); }
}
.chat-tunnel-handback {
  flex: 0 0 auto;
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: 8px;
  cursor: pointer;
}
.chat-tunnel-handback:hover {
  color: var(--bg-0, var(--bg-0));
  background: var(--accent);
}
.chat-tunnel-keys {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.chat-tunnel-key {
  min-width: 34px;
  padding: 4px 9px;
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  color: var(--fg-2, var(--fg-1));
  background: var(--bg-1);
  border: var(--border-width) solid var(--border);
  border-radius: 7px;
  cursor: pointer;
}
.chat-tunnel-key:hover {
  color: var(--fg-1);
  border-color: var(--accent);
  background: var(--bg-tint);
}
.chat-tunnel-key:active { transform: translateY(1px); }

/* ── Debug Console Log Panel ─────────────────────────────────────────────── */
.debug-console-panel {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 9999;
  height: 250px;
  background: var(--bg-0, var(--bg-0));
  border-top: var(--border-width) solid var(--border);
  display: none;
  flex-direction: column;
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  color: var(--fg-2);
  box-shadow: 0 -4px 24px rgba(var(--shadow-rgb), 0.5);
}
.debug-console-panel.open {
  display: flex;
}
.debug-console-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 10px;
  background: var(--bg-1);
  border-bottom: var(--border-width) solid var(--border);
  flex-shrink: 0;
}
.debug-console-title {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-1);
}
.debug-console-header-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}
.debug-console-filter-btn {
  padding: 1px 7px;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  background: transparent;
  color: var(--fg-3);
  font-family: inherit;
  font-size: 10px;
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.debug-console-filter-btn.active {
  background: rgba(var(--brand-rgb),0.15);
  color: var(--accent);
  border-color: var(--accent);
}
.debug-console-filter-btn:hover {
  color: var(--fg-1);
}
.debug-console-clear-btn,
.debug-console-close-btn {
  background: none;
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  color: var(--fg-3);
  cursor: pointer;
  padding: 1px 7px;
  font-size: 11px;
  font-family: inherit;
}
.debug-console-clear-btn:hover,
.debug-console-close-btn:hover {
  color: var(--fg-1);
  border-color: var(--accent);
}
.debug-console-content {
  flex: 1;
  overflow-y: auto;
  padding: 4px 0;
  background: var(--bg-0, var(--bg-0));
}
.debug-console-content .debug-console-empty {
  padding: 20px;
  text-align: center;
  color: var(--fg-4);
  font-style: italic;
}
.dc-entry {
  padding: 2px 10px;
  border-bottom: var(--border-width) solid var(--border);
  line-height: 1.5;
  word-break: break-all;
}
.dc-entry:hover {
  background: rgba(var(--brand-rgb),0.05);
}
.dc-entry .dc-tag {
  display: inline-block;
  width: 42px;
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0 4px;
  border-radius: 3px;
  margin-right: 6px;
  text-align: center;
}
.dc-entry .dc-tag.log { color: var(--accent); background: rgba(var(--brand-rgb),0.12); }
.dc-entry .dc-tag.warn { color: var(--warning); background: rgba(var(--warning-rgb),0.12); }
.dc-entry .dc-tag.error { color: var(--danger); background: rgba(var(--danger-rgb),0.12); }
.dc-entry .dc-tag.debug { color: #73daca; background: rgba(115,218,202,0.12); } /* KEEP (intentional): teal is the fixed debug console-level colour; no status token maps to it */
.dc-entry .dc-msg {
  color: var(--fg-2);
}
.dc-entry.warn .dc-msg { color: var(--warning); }
.dc-entry.error .dc-msg { color: var(--danger); }
.dc-entry .dc-stack {
  display: block;
  font-size: 10px;
  color: var(--fg-4);
  padding-left: 50px;
  white-space: pre-wrap;
  max-height: 80px;
  overflow: hidden;
}
.dc-entry .dc-stack.expanded {
  max-height: none;
}
.debug-console-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 3px 10px;
  background: var(--bg-1);
  border-top: var(--border-width) solid var(--border);
  flex-shrink: 0;
  font-size: 10px;
}
.debug-console-preserve {
  display: flex;
  align-items: center;
  gap: 4px;
  color: var(--fg-4);
  cursor: pointer;
}
.debug-console-preserve input[type="checkbox"] {
  accent-color: var(--accent);
}
.debug-console-count {
  color: var(--fg-4);
}

/* Debug console toggle button in header */
.debug-console-toggle-btn {
  background: none;
  border: var(--border-width) solid var(--border);
  border-radius: 6px;
  color: var(--fg-3);
  cursor: pointer;
  padding: 4px 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s ease;
}
.debug-console-toggle-btn:hover {
  color: var(--fg-1);
  border-color: var(--accent);
  background: rgba(var(--brand-rgb),0.08);
}
.debug-console-toggle-btn.active {
  color: var(--accent);
  border-color: var(--accent);
  background: rgba(var(--brand-rgb),0.12);
}
/* When adopted into the Admin Tools group (header-build.js), the bug toggle sits
   right of the server health dot as a compact, borderless icon so it reads as
   part of the group rather than a detached control. Its own border/hover would
   double up with the group's pill, so we drop them here. */
.main-tab-group .debug-console-toggle-btn {
  border-color: transparent;
  border-radius: 999px;
  padding: 3px;
  margin-left: 1px;
}
.main-tab-group .debug-console-toggle-btn:hover {
  border-color: transparent;
  background: rgba(var(--brand-rgb),0.12);
}

/* ── Ability-table hybrid search / chat pill ──────────────────────────────────
   The pill above BOTH ability tables (per-agent Abilities tab + admin Agent
   Settings). Built by buildAbilitySearchPill in ui/shared/js/dom-utils.js; it
   opts into the shared .chat-pill .chat-pill-1line geometry (app1.css) and the
   floating-glass skin .ability-search-pill (index.css). Only the wrapper spacing
   and this pill's OWN attachment preview bar live here — and they live in app3.css
   (not agents.css) because the admin table renders on a page that doesn't load
   agents.css. The pill is inline (above the table, normal flow), so its preview
   bar sits BELOW it in flow — unlike the floating composer's absolute
   #chat-preview-bar. */
.ability-search-pill-wrap { margin-bottom: 12px; }
.ability-search-pill-wrap .ability-search-preview {
  gap: 6px;
  flex-wrap: wrap;
  padding: 0;
  margin-top: 6px;
}

/* ── Appearance: theme palette editor (App Settings → Appearance) ──────────────
   A two-row expandable table (Light + Dark) built on the shared ability-table
   component (.ac-list / .ac-row / .ac-ability-body). Each row expands to reveal
   that theme's preset chips, background select and palette swatches — these
   styles cover the CONTENT inside the expandable .ac-ability-body (the row/
   table/expand look itself comes from the shared component). A shared Fonts &
   borders card sits below. Built by app-settings.js from the token map in
   ui/shared/js/appearance.js; every control auto-saves with the shared
   .agents-autosave-check green tick (same as the ability tables / model table).
   Colours come from design-system variables, so it tracks dark + light itself. */

/* Preset chips — one-click full-palette themes for this mode (top of the body). */
.ac-theme-presets {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
}
.ac-preset-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--fg-2);
  background: var(--bg-tint);
  border: var(--border-width) solid var(--border);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.ac-preset-chip:hover { color: var(--fg-1); border-color: var(--accent-line); }
.ac-preset-chip.active {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}
.ac-preset-chip .ac-preset-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: 0 0 0 1px rgba(var(--shadow-rgb), 0.18) inset;
}
/* Delete "×" on a custom-theme chip. */
.ac-preset-chip .ac-preset-del {
  margin-left: 1px;
  margin-right: -2px;
  width: 15px;
  height: 15px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 14px;
  line-height: 1;
  color: var(--fg-3);
  opacity: 0.7;
}
.ac-preset-chip .ac-preset-del:hover {
  color: var(--danger);
  background: rgba(var(--danger-rgb), 0.14);
  opacity: 1;
}
/* "Add theme" button — a dashed sibling of the preset chips. */
.ac-preset-add {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--fg-3);
  background: transparent;
  border: var(--border-width) dashed var(--border-strong);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.ac-preset-add:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}

/* A row in a theme card: the background select + each colour swatch. */
.ac-theme-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 30px;
  padding: 5px 0;
}
.ac-theme-row + .ac-theme-row { border-top: var(--border-width) solid var(--border-soft); }
.ac-theme-row-label {
  font-size: 12.5px;
  color: var(--fg-2);
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}
.ac-theme-row-ctl { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* Colour swatch input — square, rounded, no native chrome. */
.ac-swatch {
  width: 30px;
  height: 26px;
  padding: 0;
  border: var(--border-width) solid var(--border-strong);
  border-radius: var(--r-md);
  background: none;
  cursor: pointer;
}
.ac-swatch::-webkit-color-swatch-wrapper { padding: 2px; }
.ac-swatch::-webkit-color-swatch { border: none; border-radius: 5px; }
.ac-swatch::-moz-color-swatch { border: none; border-radius: 5px; }
/* Opacity slider — sits left of the colour swatch on alpha-capable tokens
   (surfaces + bubbles). A checker hint behind the track reads as "transparency".
   Accent thumb so it matches the rest of the controls. */
.ac-swatch-alpha {
  -webkit-appearance: none;
  appearance: none;
  width: 64px;
  height: 6px;
  border-radius: var(--r-pill);
  background:
    linear-gradient(90deg, transparent, var(--fg-2)),
    repeating-conic-gradient(var(--border-soft) 0% 25%, transparent 0% 50%) 0 / 8px 8px;
  cursor: pointer;
  flex-shrink: 0;
}
.ac-swatch-alpha::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg-elev);
  box-shadow: 0 1px 3px rgba(var(--shadow-rgb), 0.4);
  cursor: pointer;
}
.ac-swatch-alpha::-moz-range-thumb {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg-elev);
  cursor: pointer;
}
/* The select inside a theme row (e.g. the in-body Background select) sizes itself;
   cap it so the row breathes. */
.ac-theme-row .ac-input { max-width: 190px; width: 100%; }

/* ── Skeleton loading placeholders (shared) ─────────────────────────────────
   Phantom "shimmer" shapes shown while a page's data loads, so the layout has
   form from the first paint instead of a blank area or a lone spinner. Lives in
   this globally-loaded sheet so EVERY page (including fragment-loaded pages with
   no sheet of their own) can compose skeletons from the same primitives.

   Usage: give an element `sk-shimmer` for the animated fill, plus a shape helper
   (`sk-line` for a text row, `sk-block` for a rounded panel, `sk-circle` for an
   avatar/icon). Size via inline style or a page-local class. Theme-aware — the
   gradient is built from design-system elevation vars, so it works in dark and
   light with no per-page colours.

   The Agents page (ui/main-panel/agents/agents.css) was the original home of
   `.sk-shimmer`; its `.agent-skeleton .sk-icon/.sk-lines/.sk-line` LAYOUT stays
   page-local, but the shimmer paint + keyframe were promoted HERE. */
.sk-shimmer {
  background: linear-gradient(
    90deg,
    var(--bg-elev) 25%,
    var(--bg-elev-2) 37%,
    var(--bg-elev) 63%
  );
  background-size: 400% 100%;
  animation: skShimmer 1.4s ease-in-out infinite;
}
@keyframes skShimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: 0 0; }
}
/* Generic shape helpers — combine with `sk-shimmer`. */
.sk-line   { height: 11px; border-radius: 6px; }
.sk-block  { border-radius: 8px; }
.sk-circle { border-radius: 50%; }
@media (prefers-reduced-motion: reduce) {
  .sk-shimmer { animation: none; }
}
