/* NetiChecker: modern, classy, calm.
   Restrained slate palette + indigo accent + generous whitespace.
   Material Symbols + Inter for type.
*/

/* Elevation reads the same way in both themes: --bg is the recessed page,
   --panel-2 a sunken surface inside it (section frames, lists, canvases) and
   --panel the bright content that sits on top. A box framed inside a box
   stays on --panel and is told apart by its border; --panel-2 is for content
   surfaces, the lists and previews that items float on. */
:root {
  --bg: #e8ebf2;
  --panel: #ffffff;
  --panel-2: #eef1f7;
  --border: #e5e7eb;
  --border-strong: #d1d5db;
  --text: #0f172a;
  --text-2: #334155;
  --muted: #64748b;
  /* the page's ink, resolved here once: a surface that re-sets --text for its
     own ground (the run card) hands these to the controls standing on it */
  --page-text: var(--text);
  --page-muted: var(--muted);
  --accent: #4f46e5;        /* indigo 600 */
  --accent-soft: #eef2ff;
  --accent-hover: #4338ca;
  --ok: #059669;
  --ok-soft: #ecfdf5;
  --warn: #b45309;
  --warn-soft: #fffbeb;
  --danger: #b91c1c;
  --danger-soft: #fef2f2;
  --shadow-sm: 0 1px 2px rgba(15,23,42,0.04), 0 1px 2px rgba(15,23,42,0.03);
  --shadow-md: 0 2px 6px rgba(15,23,42,0.05), 0 8px 24px rgba(15,23,42,0.04);
  --radius-sm: 8px;
  --radius: 12px;
  --radius-lg: 16px;
}

[data-theme="dark"] {
  --bg: #060a13;
  --panel: #1a2436;
  --panel-2: #0f1727;
  --border: #26324a;
  --border-strong: #3d4d6b;
  --text: #e5e7eb;
  --text-2: #cbd5e1;
  --muted: #94a3b8;
  --accent: #818cf8;
  --accent-soft: rgba(129, 140, 248, 0.12);
  --accent-hover: #a5b4fc;
  --ok: #34d399;
  --ok-soft: rgba(52, 211, 153, 0.12);
  --warn: #f59e0b;
  --warn-soft: rgba(245, 158, 11, 0.12);
  --danger: #f87171;
  --danger-soft: rgba(248, 113, 113, 0.12);
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);
  --shadow-md: 0 2px 6px rgba(0,0,0,0.4), 0 16px 40px rgba(0,0,0,0.35);
}

* { box-sizing: border-box; }
/* The hidden attribute must always win, even over display rules like
   .row { display:flex } (author styles override the UA [hidden] rule). */
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
html { font-size: 16px; }

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.55;
  font-size: 16px;
  min-height: 100vh;
  /* left column: collapsible sidenav; right: main (no footer: the project line
     sits at the end of the Übersicht) */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
body.app-fixed { height: 100vh; overflow: hidden; }
body > main { grid-column: 2; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); }

code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; background: var(--panel-2); padding: 1px 6px; border-radius: 6px; }

.material-symbols-outlined {
  font-variation-settings: 'wght' 400, 'opsz' 24;
  vertical-align: middle;
  font-size: 20px;
  line-height: 1;
}

/* collapsible left navigation */
.sidenav {
  grid-row: 1 / -1;
  position: sticky; top: 0; z-index: 60;
  height: 100vh; width: 228px;
  display: flex; flex-direction: column;
  padding: 14px 12px;
  background: var(--panel);
  border-right: 1px solid var(--border);
  transition: width .15s ease;
  /* the width animates: labels must clip instead of spilling onto the page */
  overflow: hidden;
}
/* Collapsed is an icon rail, not gone: the panel, its border and every icon
   stay, only the labels disappear. The toggle sits in the head row (a panel
   symbol, the same spot in both states), so nothing protrudes onto the page. */
.sidenav.collapsed { width: 64px; padding: 14px 8px; }
.sidenav.collapsed .brand,
.sidenav.collapsed .nav a > span:not(.material-symbols-outlined),
.sidenav.collapsed .conn-chip > span:not(.material-symbols-outlined),
.sidenav.collapsed .account-text,
.sidenav.collapsed .account-caret { display: none; }
.sidenav.collapsed .account-btn { justify-content: center; padding: 6px 0; }
.sidenav.collapsed .sidenav-head { justify-content: center; }
.sidenav.collapsed .nav a { justify-content: center; padding: 9px 0; }
/* collapse control: a panel-toggle icon button in the head row, next to the
   brand while expanded, alone and centered while collapsed */
.sidenav-toggle {
  width: 34px; height: 34px; padding: 0; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: transparent; color: var(--text-2);
  border: none; border-radius: var(--radius-sm);
  cursor: pointer;
  transition: color .12s ease, background .12s ease;
}
.sidenav-toggle:hover { color: var(--accent); background: var(--panel-2); }
.sidenav-toggle:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px;
}
.sidenav-toggle .material-symbols-outlined { font-size: 22px; }
.sidenav-head {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  padding: 2px 4px 12px;
  border-bottom: 1px solid var(--border);
}
.sidenav .brand { min-width: 0; }
.sidenav-foot {
  display: flex; flex-direction: column; align-items: stretch; gap: 10px;
  padding: 12px 4px 2px;
  border-top: 1px solid var(--border);
}
.sidenav-foot .conn-chip { justify-content: center; }
.sidenav-foot .icon-btn { width: 100%; }
/* two lines: the plain word NetiChecker, the product's second word under it */
.brand {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  color: var(--text); font-weight: 700; font-size: 17px; line-height: 1.2;
  text-decoration: none;
}
.brand:hover { color: var(--text); }
.brand-name { letter-spacing: -0.01em; }
.brand-sub { font-size: 12px; font-weight: 500; color: var(--muted); letter-spacing: 0.02em; }

.nav {
  display: flex; flex-direction: column; gap: 3px; flex: 1;
  /* spread the links over the full height between head and foot */
  justify-content: space-evenly;
  padding: 12px 0; min-height: 0; overflow-y: auto;
}
.nav a {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: var(--radius-sm);
  color: var(--text-2); font-weight: 500; font-size: 14.5px;
  white-space: nowrap;
  transition: background .12s ease, color .12s ease;
}
.nav a .material-symbols-outlined { font-size: 19px; flex: none; }
.nav a:hover { background: var(--panel-2); color: var(--text); }
.nav a.active { background: var(--accent-soft); color: var(--accent); }
/* the account button in the foot (common.js initAccount): who is here, with
   which role; it opens .account-menu */
.account-btn {
  width: 100%; min-width: 0;
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-2); text-align: left; font-size: 13px;
}
.account-btn:hover,
.account-btn[aria-expanded="true"] { background: var(--panel-2); border-color: var(--border); }
.account-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.account-btn > .material-symbols-outlined { font-size: 22px; color: var(--muted); flex: none; }
.account-btn > .account-caret { font-size: 18px; margin-left: auto; }
.account-text { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 1.25; }
.account-name, .account-role { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-name { font-weight: 600; color: var(--text); }
.account-role { font-size: 11.5px; color: var(--muted); }
.account-role.read { color: var(--warn); }
/* the panel: fixed on the viewport, because the sidenav clips what leaves it;
   common.js places it above the button, or beside it on the collapsed rail */
.account-menu {
  position: fixed; z-index: 150;
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px; overflow: auto;
  background: var(--panel); color: var(--text); font-size: 13px;
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}
.account-head { display: flex; align-items: center; gap: 10px; }
.account-head > .material-symbols-outlined { font-size: 32px; color: var(--muted); flex: none; }
.account-head-text { display: flex; flex-direction: column; min-width: 0; }
.account-head-text strong { font-size: 14.5px; overflow-wrap: anywhere; }
.account-sub { color: var(--muted); font-size: 12.5px; overflow-wrap: anywhere; }
.account-facts {
  display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 12px;
  margin: 0; padding: 10px 0;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.account-facts dt { color: var(--muted); font-weight: 600; }
.account-facts dd {
  margin: 0; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
}
.account-facts code { overflow-wrap: anywhere; }
.account-fact-text { color: var(--text-2); }
.account-warn {
  margin: 0; padding: 8px 10px; border-radius: var(--radius-sm);
  background: var(--warn-soft); color: var(--warn);
}
.account-items { display: flex; flex-direction: column; gap: 2px; }
.account-item {
  width: 100%;
  display: flex; align-items: center; gap: 10px;
  padding: 7px 8px; border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-2); font-size: 13px; text-align: left;
  text-decoration: none; cursor: pointer;
}
.account-item:hover,
.account-item:focus-visible { background: var(--panel-2); border-color: transparent; color: var(--text); }
.account-item > .material-symbols-outlined { font-size: 19px; color: var(--muted); flex: none; }
.account-item > .account-item-ext { font-size: 15px; }
.account-item-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.account-item-sub { font-size: 11.5px; color: var(--muted); }
.account-note { margin: 0; font-size: 12px; color: var(--muted); }
.role {
  font-size: 10.5px; padding: 2px 8px; border-radius: 999px;
  background: var(--panel-2); color: var(--muted); text-transform: uppercase; font-weight: 700; letter-spacing: 0.04em;
  border: 1px solid var(--border);
}
.role-admin { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.role.role-read { background: var(--warn-soft); color: var(--warn); border-color: transparent; }

.conn-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 999px;
  font-size: 12.5px; font-weight: 600; text-decoration: none;
  border: 1px solid var(--border);
}
.conn-chip .material-symbols-outlined { font-size: 17px; }
.conn-chip.on { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.conn-chip.off { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.conn-chip:hover { filter: brightness(0.97); text-decoration: none; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid var(--border);
  width: 36px; height: 36px; border-radius: var(--radius-sm);
  color: var(--text-2); cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
  padding: 0;
}
.icon-btn:hover { background: var(--panel-2); color: var(--text); border-color: var(--border-strong); }
.icon-btn.danger { color: var(--danger); border-color: transparent; }
.icon-btn.danger:hover { background: var(--danger-soft); border-color: transparent; }

/* page */
.page { flex: 1; padding: 32px 32px 56px; max-width: 1240px; width: 100%; margin: 0 auto; }
.page.page-fluid { padding: 0; max-width: none; width: 100%; display: flex; min-height: 0; overflow: hidden; }
/* Wide pages (settings/jobs/account): use the full viewport width so the
   SSH console docks to the right edge instead of a centered stripe. */
.page.page-wide { max-width: none; }

/* Themed, slim scrollbars for in-app scroll containers. */
.chat-log, .console-stream, .chat-sidebar, .console-entry pre.code {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.chat-log::-webkit-scrollbar,
.console-stream::-webkit-scrollbar,
.chat-sidebar::-webkit-scrollbar { width: 10px; height: 10px; }
.chat-log::-webkit-scrollbar-track,
.console-stream::-webkit-scrollbar-track,
.chat-sidebar::-webkit-scrollbar-track { background: transparent; }
.chat-log::-webkit-scrollbar-thumb,
.console-stream::-webkit-scrollbar-thumb,
.chat-sidebar::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
.chat-log::-webkit-scrollbar-thumb:hover,
.console-stream::-webkit-scrollbar-thumb:hover,
.chat-sidebar::-webkit-scrollbar-thumb:hover { background: var(--muted); background-clip: padding-box; }
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 8px; }

/* typography */
h1 { font-size: 26px; line-height: 1.25; margin: 0 0 6px; letter-spacing: -0.01em; display: inline-flex; align-items: center; gap: 8px; }
h1 .material-symbols-outlined { color: var(--accent); font-size: 26px !important; }
h2 { font-size: 17px; margin: 0 0 8px; display: inline-flex; align-items: center; gap: 6px; }
h2 .material-symbols-outlined { color: var(--muted); }
h2.section { font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin: 32px 0 14px; font-weight: 600; display: block; }
/* section label plus its own controls on one line (Infrastruktur, Alex section) */
.section-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 8px; }
h3 { font-size: 15px; margin: 0 0 12px; color: var(--text-2); font-weight: 600; }
.muted { color: var(--muted); }
.small { font-size: 13px; }

/* cards */
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px 24px;
  box-shadow: var(--shadow-sm);
}
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }

/* minmax(0, …): a column may not grow past its share because of a long model
   id or a link-card grid, or a narrow window scrolls sideways */
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; align-items: start; }
@media (max-width: 980px) { .grid-2 { grid-template-columns: minmax(0, 1fr); } }

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }

/* metric */
.metric { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow-sm); }
.metric-head { font-weight: 600; margin-bottom: 10px; font-size: 14px; letter-spacing: 0.02em; text-transform: uppercase; color: var(--muted); }
.metric-body { display: flex; justify-content: space-between; font-size: 14px; margin-top: 10px; }
.metric-body strong { font-size: 18px; font-weight: 600; color: var(--text); }
.bar { background: var(--panel-2); border: 1px solid var(--border); border-radius: 999px; height: 8px; overflow: hidden; }
.bar-fill { background: var(--accent); height: 100%; transition: width .3s ease; }

/* charts */
.charts-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.charts-grid .wide { grid-column: 1 / -1; }
@media (max-width: 980px) { .charts-grid { grid-template-columns: 1fr; } .charts-grid .wide { grid-column: auto; } }
.metrics-wrap { overflow-x: auto; }
.metrics-table { min-width: 720px; }
.metrics-table th { white-space: nowrap; }

.chart-card { min-height: 280px; }
.chart-card canvas { display: block; width: 100% !important; height: 240px !important; }
.chart-card.wide canvas { height: 320px !important; }

/* forms */
.form { display: flex; flex-direction: column; gap: 16px; margin-top: 14px; }
.row { display: flex; flex-direction: column; gap: 6px; }
.row.two { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.row.actions { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 4px; }
.row-head { display: flex; justify-content: space-between; align-items: center; font-weight: 500; }

label { font-size: 14px; color: var(--text-2); font-weight: 500; }

input, select, textarea {
  font: inherit; font-size: 15px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--text);
  width: 100%;
  transition: border-color .12s, box-shadow .12s;
}
input::placeholder, textarea::placeholder { color: var(--muted); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input:disabled, select:disabled, textarea:disabled {
  background: var(--panel-2);
  color: var(--muted);
  cursor: not-allowed;
}
textarea { resize: vertical; }

/* custom select */
.select-wrap { position: relative; }
.select-wrap select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 36px;
  cursor: pointer;
}
.select-wrap .select-chevron {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  pointer-events: none; color: var(--muted); font-size: 20px !important;
}
.select-wrap.tight select { padding: 6px 30px 6px 10px; font-size: 13px; }

/* Custom dropdown popup (Chromium's customizable select; other browsers
   keep the native list). Matches the panel/card design of the UI. */
@supports (appearance: base-select) {
  .select-wrap select,
  .select-wrap select::picker(select) {
    appearance: base-select;
  }
  .select-wrap select::picker-icon {
    display: none; /* the wrap already provides its own chevron */
  }
  .select-wrap select::picker(select) {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    padding: 4px;
    margin-top: 4px;
  }
  .select-wrap select option {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 14px;
    color: var(--text);
    background: var(--panel);
    cursor: pointer;
    transition: background .1s ease;
  }
  .select-wrap select option:hover,
  .select-wrap select option:focus-visible {
    background: var(--panel-2);
    outline: none;
  }
  .select-wrap select option:checked {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
  }
  .select-wrap select option::checkmark {
    color: var(--accent);
  }
}

/* buttons */
button {
  font: inherit; font-size: 14.5px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 9px 14px;
  background: var(--panel); color: var(--text);
  display: inline-flex; align-items: center; gap: 6px;
  transition: background .12s, border-color .12s, color .12s;
}
button:hover { background: var(--panel-2); border-color: var(--border-strong); }
button .material-symbols-outlined { font-size: 18px; }

button.primary {
  background: var(--accent); color: #fff; border-color: var(--accent);
}
button.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
button.primary .material-symbols-outlined { color: #fff; }

button.link {
  background: transparent; border: none; color: var(--accent); padding: 4px 6px;
}
button.link:hover { background: transparent; color: var(--accent-hover); text-decoration: underline; }
button.link.danger { color: var(--danger); }

a.ghost {
  padding: 9px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-2); background: var(--panel);
  display: inline-flex; align-items: center; gap: 6px; font-size: 14.5px;
}
a.ghost:hover { background: var(--panel-2); border-color: var(--border-strong); color: var(--text); }
a.ghost.small { padding: 6px 10px; font-size: 13px; }

.inline { display: inline-flex; align-items: center; }

/* toggles */
.toggles { flex-direction: row; gap: 22px; align-items: center; flex-wrap: wrap; }
.switch { display: inline-flex; align-items: center; gap: 10px; font-size: 14px; cursor: pointer; user-select: none; }
.switch input { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }
.switch-slider {
  position: relative; display: inline-block;
  width: 36px; height: 20px; border-radius: 999px;
  background: var(--border-strong); transition: background .14s;
}
.switch-slider::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 999px;
  background: #fff; transition: transform .14s;
  box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.switch input:checked + .switch-slider { background: var(--accent); }
.switch input:checked + .switch-slider::after { transform: translateX(16px); }

/* tables */
.table { width: 100%; border-collapse: collapse; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm); }
.table th, .table td { padding: 11px 14px; text-align: left; border-bottom: 1px solid var(--border); font-size: 14px; vertical-align: middle; }
.table th { background: var(--panel-2); font-weight: 600; color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
.table tr:last-child td { border-bottom: none; }
.table.compact th, .table.compact td { padding: 8px 12px; }

.pill { display: inline-block; padding: 3px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.pill.ok { background: var(--ok-soft); color: var(--ok); }
.pill.warn { background: var(--warn-soft); color: var(--warn); }
.pill.muted { background: var(--panel-2); color: var(--muted); border: 1px solid var(--border); }

/* code */
pre.code, .code {
  background: #0f172a; color: #e2e8f0;
  padding: 12px 14px; border-radius: var(--radius-sm);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; overflow-x: auto; white-space: pre-wrap; word-break: break-word;
  border: 1px solid #1e293b;
}
[data-theme="dark"] pre.code { background: #020617; }
/* an ASCII diagram scrolls inside its block instead of wrapping: a wrapped
   line breaks the columns it is drawn in */
pre.code.diagram { white-space: pre; word-break: normal; }

/* JSON colours. Two sets, because JSON shows up on two surfaces: the dark
   code block (which is dark in both themes) and the bright panel a schema is
   edited on. The block sets the variables on itself, so its children inherit
   the right set wherever they are rendered. */
:root {
  --json-key: #0f766e; --json-str: #a16207; --json-num: #1d4ed8;
  --json-bool: #7c3aed; --json-null: #64748b; --json-punct: #94a3b8;
  --json-var: #7c3aed;
}
[data-theme="dark"] {
  --json-key: #5eead4; --json-str: #fbbf24; --json-num: #93c5fd;
  --json-bool: #d8b4fe; --json-null: #94a3b8; --json-punct: #64748b;
  --json-var: #d8b4fe;
}
pre.code, .code {
  --json-key: #7dd3fc; --json-str: #fdba74; --json-num: #a5b4fc;
  --json-bool: #f0abfc; --json-null: #94a3b8; --json-punct: #64748b;
  --json-var: #fca5a5;
}
.j-key { color: var(--json-key); }
.j-str { color: var(--json-str); }
.j-num { color: var(--json-num); }
.j-bool { color: var(--json-bool); font-weight: 600; }
.j-null { color: var(--json-null); font-style: italic; }
.j-punct { color: var(--json-punct); }
/* a ${variable} in a prompt: the same treatment on both surfaces, so a token
   dragged into a prompt is recognisable wherever that prompt is shown */
.j-var { color: var(--json-var); font-weight: 600; }

/* The JSON editor (a textarea over its own coloured copy) lives at the very
   end of this file: its metrics have to beat the per-page textarea rules, or
   the two layers drift apart. */

/* auth card */
.auth-card {
  max-width: 460px; margin: 80px auto; padding: 36px 36px 28px;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-md);
}
.auth-card h1 { font-size: 24px; }
.auth-brand {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--accent); font-weight: 700; font-size: 18px;
  margin-bottom: 18px;
}
.auth-brand .material-symbols-outlined { font-size: 28px !important; }

/* banner */
.banner {
  background: var(--ok-soft); border: 1px solid transparent;
  padding: 14px 18px; border-radius: var(--radius);
  margin: 0 0 22px;
}
.banner-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; color: var(--ok); }
.banner pre.code { margin-top: 6px; }
.banner.banner-error { background: var(--danger-soft); }
.banner.banner-error .banner-head { color: var(--danger); }
.banner.banner-dry { background: var(--warn-soft); }
.banner.banner-dry .banner-head { color: var(--warn); }

.ssh-status {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; border-radius: var(--radius); margin: 4px 0 22px;
  font-size: 14px; border: 1px solid var(--border);
}
.ssh-status.on { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.ssh-status.off { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.ssh-status code { background: rgba(0,0,0,0.06); color: inherit; }
.ssh-status strong { color: inherit; }
.action-card details { margin-top: 10px; font-size: 13px; }
.action-card summary { cursor: pointer; color: var(--muted); }

.action-result {
  margin-top: 14px; padding: 12px 14px;
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--radius-sm); background: var(--panel-2);
}
.action-result.is-ok { border-left-color: var(--ok); }
.action-result.is-error { border-left-color: var(--danger); }
.action-result.is-dry { border-left-color: var(--muted); }
.action-result-head { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 13.5px; }
.action-result-head .material-symbols-outlined { font-size: 18px; }
.action-result-head .muted { margin-left: auto; font-weight: 400; }
.action-result pre.code { margin: 8px 0 0; max-height: 260px; }
.action-result-foot { margin: 8px 0 0; }

/* ---------------- inference (chat, full-bleed) ---------------- */
.chat-app {
  flex: 1;
  display: grid;
  grid-template-columns: 420px 1fr;
  grid-template-rows: minmax(0, 1fr);   /* row never grows past the viewport */
  width: 100%;
  min-height: 0;
  height: 100%;
}
@media (max-width: 1100px) { .chat-app { grid-template-columns: 360px 1fr; } }
@media (max-width: 900px) {
  .chat-app { grid-template-columns: 1fr; grid-template-rows: auto minmax(60vh, 1fr); height: auto; }
  body.app-fixed { height: auto; overflow: auto; }
}

.chat-sidebar {
  display: flex; flex-direction: column;
  background: var(--panel);
  border-right: 1px solid var(--border);
  min-height: 0;
  overflow-y: auto;
}
/* The settings scroll on their own once they need more room than the history leaves them:
   `.sidebar-history` keeps its min-height, so expanded blocks can never squeeze the
   Verlauf down to a single row again. */
.sidebar-config {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 10px;
  font-size: 12.5px;
  flex: 0 1 auto; min-height: 0;
  overflow-y: auto;
}
.sidebar-head { display: flex; align-items: center; gap: 8px; }
.sidebar-head.between { justify-content: space-between; }
.sidebar-head h2 { font-size: 14px; margin: 0; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); font-weight: 600; }
.sidebar-head h2 .material-symbols-outlined { color: var(--muted); font-size: 18px !important; }
.sidebar-config .ghost.small { align-self: flex-start; }
/* The settings read one step smaller than the page (the owner asked for it): every
   control here sets its own size, so the step is taken per element, not on the box. */
.sidebar-config label { font-size: 13px; }
.sidebar-config select, .sidebar-config input { font-size: 14px; }
.sidebar-config .small { font-size: 12.5px; }
.sidebar-config .switch { font-size: 13px; }
.sidebar-config .config-block summary { font-size: 12.5px; }
.sidebar-config button { font-size: 13.5px; }

.server-status {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: var(--radius-sm);
  font-size: 12px; border: 1px solid var(--border);
}
.server-status .material-symbols-outlined { font-size: 18px; }
.server-status a { color: inherit; text-decoration: underline; }
.server-status.checking { background: var(--panel-2); color: var(--muted); }
.server-status.on { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.server-status.pending { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.server-status.off { background: var(--panel-2); color: var(--text-2); }

.sidebar-history {
  flex: 1 0 auto; min-height: max(280px, 40%);
  display: flex; flex-direction: column;
  padding: 14px 14px 16px;
  gap: 10px;
}
.history-list {
  display: flex; flex-direction: column; gap: 2px;
  overflow-y: auto;
  min-height: 0;
  flex: 1;
}
.history-empty { padding: 12px 6px; }
.history-item {
  display: flex; flex-direction: column; gap: 2px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  position: relative;
  transition: background .12s;
}
.history-item:hover { background: var(--panel-2); }
.history-item.active { background: var(--accent-soft); }
.history-item.active .history-item-title { color: var(--accent); font-weight: 600; }
.history-item-title { font-size: 14px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-right: 22px; }
.history-item-meta { font-size: 11.5px; color: var(--muted); }
.history-item-delete {
  position: absolute; top: 6px; right: 6px;
  border: none; background: transparent; cursor: pointer;
  color: var(--muted); opacity: 0;
  border-radius: 6px; padding: 2px;
  display: inline-flex; align-items: center;
}
.history-item:hover .history-item-delete { opacity: 1; }
.history-item-delete:hover { color: var(--danger); background: var(--danger-soft); }
.history-item-delete .material-symbols-outlined { font-size: 16px; }

.chat-main {
  display: flex; flex-direction: column;
  min-width: 0; min-height: 0;
  overflow: hidden;
  background: var(--bg);
}
.chat-log {
  flex: 1; min-height: 0;
  padding: 28px 32px 12px;
  display: flex; flex-direction: column; gap: 18px;
  overflow-y: auto;
  align-items: stretch;
}
.chat-log .bubble { max-width: 760px; width: max-content; }
.chat-log .bubble.user { margin-left: auto; }
.chat-log .bubble.bot { margin-right: auto; }
.chat-empty {
  margin: auto; text-align: center; color: var(--muted);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.chat-empty .material-symbols-outlined { font-size: 36px !important; color: var(--border-strong); }

.bubble { display: flex; flex-direction: column; gap: 6px; max-width: 92%; }
.bubble.user { align-self: flex-end; }
.bubble.bot { align-self: flex-start; }
.bubble-meta { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.bubble-meta .material-symbols-outlined { font-size: 16px; }

.bubble.user .bubble-body {
  background: var(--accent); color: #fff;
  padding: 12px 14px; border-radius: 14px 14px 4px 14px;
  font-size: 14.5px; white-space: pre-wrap; word-break: break-word;
}
.bubble.user .bubble-body.file {
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid var(--accent); border-style: dashed;
}
.bubble.bot .bubble-body {
  background: var(--panel-2); color: var(--text);
  padding: 14px 16px; border-radius: 14px 14px 14px 4px;
  border: 1px solid var(--border);
  font-size: 14.5px;
}
.bubble.bot.error .bubble-body { background: var(--danger-soft); border-color: transparent; color: var(--danger); }
.bubble-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.entry-card {
  padding: 12px 14px; border-radius: var(--radius-sm);
  background: var(--panel); border: 1px solid var(--border);
  margin-top: 10px;
}
.entry-card + .entry-card { margin-top: 8px; }
.entry-card .entry-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; gap: 8px; }
.entry-quote { color: var(--text-2); font-style: italic; font-size: 13.5px; margin: 0 0 8px; max-height: 84px; overflow: hidden; }
/* a hint box inside a card is a box in a box: bright surface, dashed frame */
.config-hint { padding: 10px 12px; background: var(--panel); border: 1px dashed var(--border-strong); border-radius: var(--radius-sm); }
.acts { list-style: none; margin: 2px 0 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.acts li { font-size: 13px; }
.acts .act-present { color: var(--text); font-weight: 500; }
.acts .act-absent { color: var(--muted); }

.entry-raw { margin-top: 10px; }
.entry-raw summary { cursor: pointer; color: var(--muted); font-size: 13px; }
.entry-raw pre.code { margin-top: 8px; max-height: 320px; }

.kv { display: grid; grid-template-columns: 130px 1fr; gap: 4px 12px; font-size: 13.5px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; }

.chat-composer {
  border-top: 1px solid var(--border);
  padding: 14px 32px 22px;
  background: var(--panel);
  display: flex; flex-direction: column; gap: 10px;
}
.chat-composer .composer-row textarea {
  background: var(--panel-2);
}
.composer-row textarea { font-size: 15px; min-height: 56px; }
.composer-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.composer-actions .icon-btn { width: 38px; height: 38px; }
/* The send button itself carries the auto margin so it stays pinned to the
   lower right in every mode, even when other row items are hidden or wrap. */
#send-btn { margin-left: auto; }

.composer-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px; font-size: 13.5px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--text-2); cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.composer-btn:hover { background: var(--panel-2); border-color: var(--border-strong); color: var(--text); }
.composer-btn .material-symbols-outlined { font-size: 18px; color: var(--muted); }

.composer-hint {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin: 2px 0 0;
}
.composer-hint .material-symbols-outlined { font-size: 16px; vertical-align: middle; }

/* a box in a box (the Playground's System-Prompt, Format and Sampling blocks,
   the Test-Suite's dataset box): bright like the card around it, delimited by
   its border; its textarea and JSON editor are --panel too */
.config-block { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px; background: var(--panel); }
.config-block summary { cursor: pointer; font-size: 13.5px; font-weight: 500; color: var(--text-2); }
.config-block textarea {
  margin-top: 8px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
}
.format-actions { display: flex; align-items: center; gap: 8px; margin-top: 8px; flex-wrap: wrap; }

.cell-clip { max-width: 360px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.staged-entries { display: flex; flex-wrap: wrap; gap: 8px; padding: 4px 0 0; }
.staged-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--accent-soft); color: var(--accent);
  border-radius: 999px; padding: 4px 10px 4px 12px; font-size: 13px;
  max-width: min(360px, 100%);
}
.staged-chip .material-symbols-outlined { font-size: 16px; }
.staged-chip-text { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* a model that is not ready (Infrastruktur, Übersicht) recedes */
.staged-chip.is-down { background: var(--panel-2); color: var(--muted); border: 1px dashed var(--border-strong); }
.staged-chip button { background: transparent; border: none; color: var(--accent); padding: 0; margin-left: 2px; cursor: pointer; }

/* ---------------- console layout (Alex section, connection.html) ---------------- */
/* The section lives on a normal-width page (max 1240px) next to the sidenav,
   so the console takes a share of the row instead of a fixed 480-640px that
   squeezed the cards; below 1180px the two stack (the rule further down). */
.console-layout {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(320px, 2fr);
  gap: 22px;
  align-items: start;
}
@media (max-width: 1180px) { .console-layout { grid-template-columns: minmax(0, 1fr); } }
.console-main { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.console-main .cards { margin-top: 4px; }

/* Beside the cards the console starts on the same line as the first of them
   (both are grid items aligned to the start); while the section scrolls it
   sticks at the page's own top padding and fills the rest of the viewport.
   Stacked under the cards it is an ordinary box of bounded height: a sticky
   viewport-tall console there would cover the section below it. */
.console-side {
  position: sticky; top: 32px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px 10px;
  box-shadow: var(--shadow-sm);
  height: calc(100vh - 52px);
  min-width: 0;
  display: flex; flex-direction: column;
}
@media (max-width: 1180px) {
  .console-side { position: static; height: auto; max-height: 520px; }
}
.console-head { display: flex; align-items: center; justify-content: space-between; }
.console-head h2 { margin: 0; }
.console-stream { overflow-y: auto; margin-top: 8px; display: flex; flex-direction: column; gap: 12px; padding-bottom: 10px; }
.console-empty { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; padding: 28px 8px; }
.console-empty .material-symbols-outlined { font-size: 28px !important; color: var(--border-strong); }

.console-entry {
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: var(--radius-sm); padding: 10px 12px; background: var(--panel-2);
}
.console-entry.is-ok { border-left-color: var(--ok); }
.console-entry.is-error { border-left-color: var(--danger); }
.console-entry.is-dry { border-left-color: var(--muted); }
.console-entry-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.console-label { font-weight: 600; font-size: 13.5px; }
.console-meta { margin: 2px 0 6px; }
.console-cmd { background: #0f172a; }
.console-out { background: #0b1220; }
.console-err { background: #2a1212; color: #fecaca; }
.console-entry pre.code { margin: 6px 0 0; font-size: 12.5px; max-height: 460px; }

.mode-tabs { display: inline-flex; gap: 4px; margin-top: 12px; padding: 4px; background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.mode-tab { background: transparent; border: none; padding: 7px 16px; border-radius: 6px; font-size: 14px; color: var(--text-2); }
.mode-tab:hover { background: var(--panel); transform: none; }
.mode-tab.active { background: var(--accent); color: #fff; }
.mode-pane { margin-top: 4px; }

/* Header of Konfiguration and Test-Suite that stays put while scrolling: a
   rail of the sections of whichever view is visible (the view itself is
   picked in the sidebar). The rail is a progress bar (the section being read
   fills from left to right) and each segment jumps to its section on click.
   Filled by analyze.js from the DOM, so a new section needs no change here. */
.az-sticky {
  position: sticky; top: 0; z-index: 30;
  /* bleed over the page padding so scrolled content never shows beside it.
     The bottom padding is the strip of page colour under the step rail, so
     scrolled content passes below the bar instead of touching it. */
  margin: -32px -32px 30px; padding: 8px 32px 10px;
  /* the bar sits on the recessed page colour (the owner asked for a grey
     strip): the bright cards scroll under it */
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
/* scrolls sideways when the sections do not fit, but without a scrollbar:
   a 16px track would add its height to a bar that is already page chrome */
.az-steps {
  display: flex; gap: 4px;
  overflow-x: auto; scrollbar-width: none;
}
.az-steps::-webkit-scrollbar { display: none; }
.az-steps:empty { display: none; }
.az-step {
  --fill: 0%;
  /* auto basis, not 0: the segments still fill the bar, but a long section
     name takes the room it needs instead of every segment being cut to the
     same width and truncating the longest label first */
  flex: 1 1 auto; min-width: 64px;
  display: flex; flex-direction: column; gap: 6px; align-items: stretch;
  padding: 6px 8px 0; margin: 0;
  background: none; border: none; border-radius: 0;
  font-size: 12.5px; color: var(--muted); text-align: left; cursor: pointer;
}
.az-step:hover { background: none; border: none; color: var(--text-2); }
.az-step.active { color: var(--accent); font-weight: 600; }
.az-step-top { display: flex; align-items: center; gap: 6px; min-width: 0; }
.az-step .material-symbols-outlined { font-size: 16px; flex: none; }
.az-step-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the rail itself: accent up to --fill, neutral after it */
.az-step::after {
  content: ""; height: 3px; border-radius: 2px;
  background: linear-gradient(to right,
    var(--accent) var(--fill), var(--border-strong) var(--fill));
}
@media (max-width: 720px) {
  .az-sticky { margin: -20px -16px 14px; padding: 6px 16px 8px; }
}

.row-actions { display: flex; align-items: center; gap: 6px; }
.row-actions .icon-btn { width: 32px; height: 32px; }

.cancel-form { display: inline-flex; align-items: center; gap: 6px; }
.cancel-form input { width: 120px; }
.danger-btn { color: var(--danger); border-color: var(--danger); }
.danger-btn:hover { background: var(--danger-soft); }
.danger-btn .material-symbols-outlined { color: var(--danger); }

/* saved/unsaved chips + schema validity (inference sidebar) */
.save-state {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
  padding: 2px 8px; border-radius: 999px; margin-left: 8px; vertical-align: middle;
}
.save-state.saved { background: var(--ok-soft); color: var(--ok); }
.save-state.dirty { background: var(--warn-soft); color: var(--warn); }
.schema-valid { font-size: 12.5px; font-weight: 600; color: var(--muted); }
.schema-valid.ok { color: var(--ok); }
.schema-valid.warn { color: var(--warn); }
.schema-valid.bad { color: var(--danger); }
/* "gültig" is the verdict and stays green; the count of portability hints is
   advisory and reads grey, like the hint boxes under the editor. */
.schema-valid .schema-valid-note { color: var(--muted); font-weight: 400; }
#bk-schema {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; width: 100%;
}
.bk-schema-library {
  display: grid; grid-template-columns: minmax(220px, 1fr) minmax(220px, 1fr);
  gap: 10px; margin-bottom: 8px;
}
.bk-schema-library label { display: block; margin-bottom: 3px; }
.bk-schema-library input, .bk-schema-library select { width: 100%; }
.bk-schema-actions { flex-wrap: wrap; margin-top: 8px; }
.schema-diagnostics { display: flex; flex-direction: column; gap: 5px; margin-top: 8px; }
.schema-diagnostics:empty { display: none; }
.schema-diagnostic {
  display: flex; align-items: flex-start; gap: 6px; padding: 6px 8px;
  border-radius: 6px; font-size: 12.5px;
}
.schema-diagnostic .material-symbols-outlined { flex: none; font-size: 17px; }
/* Portability hints are advisory, not defects: they stay neutral grey so a
   real compatibility error (.bad) is the only coloured line in the list. */
.schema-diagnostic.warn { color: var(--muted); background: var(--bg); }
.schema-diagnostic.bad { color: var(--danger); background: var(--danger-soft); }
@media (max-width: 760px) {
  .bk-schema-library { grid-template-columns: 1fr; }
}

/* pre-inference checks */
.az-rule-flag { color: var(--accent); font-size: 12px; cursor: help; }
.bk-precheck-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
/* A rule is one line, read left to right: what is checked, with which values,
   what a hit does and (for a raising rule) how far and when. Nothing here
   wraps onto a second row, which is why every part is a flex item of the same
   line instead of a grid cell plus full-width extras underneath. */
.bk-pc-row { display: flex; align-items: flex-start; gap: 8px; }
.bk-pc-row > .bk-pc-type { flex: 0 0 190px; }
.bk-pc-row > .bk-pc-action { flex: 0 0 285px; }
.bk-pc-row > .icon-btn { flex: none; margin-top: 3px; }
.bk-pc-param { flex: 1 1 200px; min-width: 0; }
.bk-pc-param input[type="text"] { width: 100%; }
.bk-pc-text { display: flex; flex-direction: column; gap: 2px; }
/* how many points a raising rule adds, and whether they stand before the
   request or come on top of the answer; the sentence explaining the two is
   the group's tooltip, so it costs the line no width */
.bk-pc-boost { display: flex; align-items: center; gap: 8px; flex: 0 1 300px; }
.bk-pc-boost .bk-pc-threshold input { width: 68px; }
.bk-pc-boost .select-wrap { flex: 1 1 auto; min-width: 0; }
/* the entries of a list rule are its parameter, so they sit in the parameter
   cell: the chips (scrolling after three rows) over the field that adds one */
.bk-pc-list { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.bk-pc-list .bk-pc-chips { margin-top: 0; }
.bk-pc-list .bk-feed-addrow { flex-wrap: wrap; }
/* the shared stop word base and the configuration's own checks are two
   different things: each gets its own framed block. A box inside a box stays
   on the bright card surface and is delimited by its border alone, at every
   depth (so the schema editor inside Strukturierte Ausgabe looks the same as
   the stop word filter inside Vorprüfungen). Only content surfaces recess. */
.bk-sub-box {
  display: block; margin-top: 14px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel);
}
.bk-sub-box + .bk-sub-box { margin-top: 18px; }
.bk-sub-box .row-head { margin-top: 0; }
/* An unframed block: the two halves of Vorprüfungen. The section box around
   them is the only frame there; a border per half framed the same content
   twice, so they keep the spacing and the title look of a sub-box and lose
   the box itself. */
.bk-plain-box { display: block; }
.bk-plain-box + .bk-plain-box { margin-top: 22px; }
.bk-plain-box .row-head { margin-top: 0; }
/* the numbered sub-box titles label their block, they are not headlines:
   they take the same --muted as a section heading, so a box does not read as
   several competing titles set in near-black bold */
.bk-sub-box > .row-head > label,
.bk-sub-box > .row-head > label strong,
.bk-sub-box > label.switch strong,
.bk-plain-box > .row-head > label,
.bk-plain-box > .row-head > label strong,
.bk-plain-box > .row-head > label.switch strong { color: var(--muted); font-weight: 600; }
/* the info symbol belongs to the label it explains, not to the far edge of
   the space-between row */
.row-head > .bk-info { margin-right: auto; margin-left: 2px; }
/* the schema editor is the content of Strukturierte Ausgabe, not a second
   block inside it: no frame of its own, only the spacing */
.bk-schema-block { display: block; margin-top: 14px; }
/* boxed entry list, scrolling once it is more than three rows tall
   (3 x 28px chips + 2 x 6px gaps + 2 x 8px padding). Same dotted board as the
   pipeline canvas: the chips are items placed on it (see the .bk-canvas rule
   for the surface). */
.bk-sw-list {
  display: flex; flex-wrap: wrap; gap: 6px; align-content: flex-start;
  max-height: 112px; overflow-y: auto; margin-top: 10px; padding: 8px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.bk-pc-threshold { display: flex; align-items: center; gap: 8px; }
.bk-pc-threshold input { width: 84px; }
/* only where the line genuinely has no room left does it give up and wrap */
@media (max-width: 1100px) { .bk-pc-row { flex-wrap: wrap; } }

/* verdict: score terms and moderation levels */
.bk-vd-term {
  display: grid; grid-template-columns: minmax(200px, 1fr) auto auto auto auto;
  gap: 10px; align-items: center;
}
.bk-vd-path { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bk-vd-path input { width: 100%; }
.bk-vd-level {
  display: grid; grid-template-columns: minmax(160px, 1fr) auto auto auto;
  gap: 10px; align-items: center;
}
@media (max-width: 1100px) {
  .bk-vd-term, .bk-vd-level { grid-template-columns: 1fr 1fr; }
}
.bk-vd-formula {
  margin: 10px 0 0; padding: 10px 12px; white-space: pre-wrap;
  font-size: 12px; line-height: 1.6;
}
.bk-vd-formula.bk-vd-empty { color: var(--muted); font-family: inherit; }
.bk-vd-probe-out:not(:empty) { margin-top: 10px; }
.bk-vd-result { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.bk-vd-parts { margin: 8px 0 0; padding-left: 18px; }
.bk-vd-parts li { margin: 2px 0; }

/* context preparation */
.bk-prep-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.bk-prep-set { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.bk-prep-field { display: flex; align-items: center; gap: 8px; }
.bk-prep-field input[type="number"] { width: 90px; }
.bk-prep-endpoint { min-width: 280px; }
.config-block .row.two { gap: 10px; }

/* structured output toggled off: block stays visible but inactive */
.config-block.disabled { opacity: 0.55; }
.config-block.disabled textarea { background: var(--panel-2); cursor: not-allowed; }
/* a JSON editor's text is transparent, so the surface behind it has to carry
   the disabled look */
.config-block.disabled .code-edit-hl { background: var(--panel-2); color: var(--muted); }
.config-block.disabled button { cursor: not-allowed; }

/* composer feedback why Send is disabled; own line so it can never push the
   Send button onto a wrapped row */
.send-hint-row { text-align: right; }
.send-hint { color: var(--warn); font-weight: 500; }
button.primary:disabled {
  background: var(--border-strong); border-color: var(--border-strong);
  cursor: not-allowed;
}
.danger-btn:disabled {
  color: var(--muted); border-color: var(--border);
  background: var(--panel); cursor: not-allowed;
}
.danger-btn:disabled .material-symbols-outlined { color: var(--muted); }
.danger-btn:disabled:hover { background: var(--panel); }

/* cluster query buttons (index.html): plain-language label + command */
.query-btn {
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  padding: 10px 16px;
  text-align: left;
}
.query-btn .query-label {
  display: inline-flex; align-items: center; gap: 7px;
  font-weight: 600; font-size: 14.5px; color: var(--text);
}
.query-btn .query-label .material-symbols-outlined { font-size: 19px; color: var(--accent); }
.query-btn .query-cmd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px; color: var(--muted);
  padding-left: 26px; /* aligns under the label text, past the icon */
}

/* confirmation dialog (NC.confirm) */
.modal-overlay {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(15, 23, 42, 0.45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 12vh 16px 0;
}
[data-theme="dark"] .modal-overlay { background: rgba(0, 0, 0, 0.6); }
.modal {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 22px 24px 18px;
  width: 100%; max-width: 440px;
  animation: modal-in .14s ease-out;
}
/* A window that is only rebuilt (the node editor redraws itself after every
   edit) must not replay the entry animation: it would read as the window
   closing and a new one opening. */
.modal.no-anim { animation: none; }
@keyframes modal-in {
  from { opacity: 0; transform: translateY(-8px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
.modal-head {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 8px;
}
.modal-head .material-symbols-outlined { color: var(--warn); font-size: 24px !important; }
.modal-head.danger .material-symbols-outlined { color: var(--danger); }
.modal-head.ask .material-symbols-outlined { color: var(--accent); }
.modal-head h3 { margin: 0; font-size: 16px; color: var(--text); }
.modal-msg { margin: 0 0 18px; color: var(--text-2); font-size: 14.5px; white-space: pre-wrap; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; }
.modal-actions.wrap { flex-wrap: wrap; }

/* --- GitLab login (common.js) --- */
/* While the page asks who is here its content stays hidden, so a page that
   gives way to the login does not flash first (revealed after 2.5 s anyway). */
html.auth-wait main { visibility: hidden; }
/* the login ran out while the page was open: one notice, bottom right */
.auth-banner {
  position: fixed; right: 20px; bottom: 20px; z-index: 300;
  display: flex; align-items: center; gap: 10px;
  max-width: min(520px, calc(100vw - 40px));
  padding: 12px 12px 12px 16px;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--warn); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); font-size: 14px;
}
.auth-banner > .material-symbols-outlined { color: var(--warn); flex: none; }
.auth-banner a { font-weight: 600; white-space: nowrap; }
.auth-banner .icon-btn { flex: none; width: 30px; height: 30px; }
/* a control the GitLab role does not allow (NC.applyNeeds) */
.needs-note {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 14px; margin: 0 0 14px;
  background: var(--panel); border: 1px solid var(--border);
  border-left: 3px solid var(--warn); border-radius: var(--radius-sm);
  color: var(--text-2); font-size: 13.5px;
}
.needs-note .material-symbols-outlined { color: var(--warn); font-size: 19px; flex: none; }

/* --- public pages: login.html and datenschutz.html (body[data-public]) --- */
/* no sidenav there: one column with the page centred in it */
body[data-public] { display: block; }
body[data-public] > main { grid-column: auto; }
.public-page { max-width: 880px; margin: 0 auto; padding: 32px 20px 56px; }
.public-page.login-page { max-width: 580px; }
.public-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 22px; }
.public-brand { font-size: 19px; }
.login-card h1 { margin-bottom: 10px; }
.login-state {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 14px; margin: 14px 0 0;
  background: var(--panel); border: 1px solid var(--border);
  border-left: 3px solid var(--accent); border-radius: var(--radius-sm);
  color: var(--text-2); font-size: 14px;
}
.login-state > .material-symbols-outlined { color: var(--accent); font-size: 19px; flex: none; }
.login-state.bad { border-left-color: var(--danger); }
.login-state.bad > .material-symbols-outlined { color: var(--danger); }
/* an <a>, not a button: the login is a plain navigation to the controller
   (form-action 'self' would refuse a form posting there) */
.login-btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: 18px 0 4px; padding: 11px 18px;
  background: var(--accent); color: #fff; border: 1px solid var(--accent);
  border-radius: var(--radius-sm); font-size: 15px; font-weight: 600;
  transition: background .12s, border-color .12s;
}
.login-btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; }
.login-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.login-btn .material-symbols-outlined { color: #fff; font-size: 19px; }
/* the quieter ways on beside it: another account, the approvals in GitLab */
.login-alts { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 18px; margin: 10px 0 0; }
.login-alts:not(:has(> :not([hidden]))) { display: none; }
.login-alt { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 500; }
.login-alt .material-symbols-outlined { font-size: 18px; }
.login-sub { margin: 22px 0 8px; }
.login-facts { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 8px; color: var(--text-2); font-size: 14px; }
.login-foot { margin: 20px 0 0; padding-top: 14px; border-top: 1px solid var(--border); }
.privacy-card { padding: 28px 32px 24px; }
.privacy-card .back { display: inline-flex; align-items: center; gap: 4px; margin-bottom: 14px; font-size: 14px; }
.privacy-card .back .material-symbols-outlined { font-size: 18px; }
.privacy-card h1 { margin-bottom: 10px; }
.privacy-card h2 { display: block; font-size: 18px; margin: 30px 0 10px; }
.privacy-card h3 { font-size: 15.5px; margin: 20px 0 8px; color: var(--text); }
.privacy-card p, .privacy-card li { color: var(--text-2); font-size: 15px; }
.privacy-card p { margin: 0 0 10px; }
.privacy-card ul { margin: 0 0 12px; padding-left: 22px; display: flex; flex-direction: column; gap: 4px; }
.privacy-card .lead { color: var(--text); font-size: 16px; margin-bottom: 18px; }
.privacy-note {
  margin: 14px 0; padding: 12px 16px;
  background: var(--warn-soft); border-left: 3px solid var(--warn); border-radius: var(--radius-sm);
}
.privacy-card .privacy-note p { margin: 0; color: var(--text); }
.privacy-table-wrap { overflow-x: auto; margin: 6px 0 14px; }
.privacy-table { box-shadow: none; min-width: 560px; }
.privacy-table td { vertical-align: top; }
.privacy-foot { margin-top: 28px; padding-top: 14px; border-top: 1px solid var(--border); }
.privacy-card .privacy-foot p { margin: 0; color: var(--muted); font-size: 13px; }
@media (max-width: 640px) {
  .public-page { padding: 20px 16px 40px; }
  .privacy-card { padding: 20px 18px; }
  .privacy-table th, .privacy-table td { padding: 8px 10px; }
}

/* serve-profile and provider cards (Infrastruktur page) */
.profiles-list { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
.profile-card {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; background: var(--panel-2);
}
.profile-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.profile-card .acts { margin-top: 6px; }
.profile-card .acts li { color: var(--text-2); }

/* ---------------- analyze dashboard (benchmark charts) ----------------
   Validated categorical palette; dark values are the same hues re-stepped
   for the dark surface. Charts read these at render time. */
:root {
  --viz-1: #2a78d6; --viz-2: #1baf7a; --viz-3: #eda100; --viz-4: #008300;
  --viz-5: #4a3aa7; --viz-6: #e34948; --viz-7: #e87ba4; --viz-8: #eb6834;
  --viz-seq-1: #cde2fb; --viz-seq-2: #9ec5f4; --viz-seq-3: #5598e7; --viz-seq-4: #2a78d6;
  --viz-grid: #e1e0d9; --viz-axis: #c3c2b7; --viz-muted: #898781; --viz-ink: #52514e;
  --viz-surface: #ffffff;
}
[data-theme="dark"] {
  --viz-1: #3987e5; --viz-2: #199e70; --viz-3: #c98500; --viz-4: #008300;
  --viz-5: #9085e9; --viz-6: #e66767; --viz-7: #d55181; --viz-8: #d95926;
  --viz-seq-1: #b7d3f6; --viz-seq-2: #6da7ec; --viz-seq-3: #2a78d6; --viz-seq-4: #184f95;
  --viz-grid: #2c2c2a; --viz-axis: #383835; --viz-muted: #898781; --viz-ink: #c3c2b7;
  --viz-surface: #111827;
}

/* Tester: run configuration cards side by side. Wide enough that a card's
   own lines (the setting rows, the switch labels) stay on one line instead of
   wrapping after every second control. */
.az-run-cards {
  display: grid; gap: 12px; margin: 10px 0 0;
  /* min() so a pane narrower than a card does not overflow sideways */
  grid-template-columns: repeat(auto-fill, minmax(min(460px, 100%), 1fr));
}
/* the setup card and whatever follows it are separate stations, not one
   stack of borders */
#az-view-tester > .card + .card { margin-top: 24px; }
/* A run card is a live station, so it carries a deep, saturated violet to
   blue gradient with white text (the owner asked for it strong and dark, not
   a tint), which sets it apart from the grey history entries. --text and
   --muted are re-set on the card so everything written straight on the
   gradient is white; a control (select, chip, details box) keeps the --panel
   surface every control has and sets the page's own text colour back on
   itself, which is what makes it stand out from the ground. */
.az-run-card {
  display: flex; flex-direction: column; gap: 8px;
  border: 1px solid rgba(255, 255, 255, 0.22); border-radius: var(--radius-sm);
  padding: 12px 14px; box-shadow: var(--shadow-sm);
  --text: #fff; --text-2: rgba(255, 255, 255, 0.92); --muted: rgba(255, 255, 255, 0.78);
  color: var(--text);
  background: linear-gradient(135deg, #7c3aed, #4338ca 50%, #1d4ed8);
}
[data-theme="dark"] .az-run-card {
  border-color: rgba(255, 255, 255, 0.16);
  background: linear-gradient(135deg, #5b21b6, #3730a3 50%, #1e40af);
}
.az-run-card select, .az-run-card input, .az-run-card .az-run-details {
  --text: var(--page-text); --text-2: var(--page-text); --muted: var(--page-muted);
  color: var(--text);
}
.az-run-card select { padding: 7px 30px 7px 10px; font-size: 14px; }
.az-run-card .select-chevron { color: var(--page-muted); }
.az-run-card a { color: #fff; text-decoration: underline; }
/* the card a hand-over from the Konfiguration focuses: white on the tint */
.az-run-card:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.az-run-card .az-swatch { outline: 2px solid rgba(255, 255, 255, 0.85); outline-offset: 1px; }
.az-run-card .icon-btn { color: #fff; background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.28); }
.az-run-card .icon-btn:hover { background: rgba(255, 255, 255, 0.24); color: #fff; }
.az-run-title { flex: 1; min-width: 0; font-weight: 600; font-size: 14.5px;
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.az-run-note { font-size: 12.5px; color: var(--muted); line-height: 1.45; margin: 0; }
.az-run-note.warn { color: #fde68a; }
.az-run-note.bad { color: #fecaca; font-weight: 600; }
.az-run-card-head { display: flex; align-items: center; gap: 8px; }
.az-run-card-head .icon-btn { margin-left: auto; width: 28px; height: 28px; }
.az-run-card-head .icon-btn .material-symbols-outlined { font-size: 16px; }
.az-run-chip {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  border: 1px solid transparent; border-radius: 999px; padding: 4px 12px 4px 8px;
  background: var(--accent-soft); color: var(--accent);
  font-size: 13px; font-weight: 500; cursor: pointer;
  transition: border-color .12s;
}
.az-run-chip:hover { border-color: var(--accent); background: var(--accent-soft); }
.az-run-chip .material-symbols-outlined { font-size: 16px; color: var(--accent); }
/* on the run card the accent-soft chip would melt into the gradient: there
   it sits on --panel like the selects, and the hover keeps its border (the
   grid search's chips are on a plain card and stay as they are) */
.az-run-card .az-run-chip { background: var(--panel); }
.az-run-card .az-run-chip:hover { border-color: #fff; }
.az-run-details {
  display: flex; flex-direction: column; gap: 4px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); padding: 10px 12px;
}
.az-run-details pre.code { margin: 4px 0 0; max-height: 200px; font-size: 12px; }
.az-run-aspect { display: flex; align-items: center; gap: 8px; min-height: 28px; }
.az-run-aspect-label {
  flex: none; min-width: 62px;
  font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
/* The facts under the board are one table: the label column is as wide as
   its widest label, so every value starts at the same x and a wrapped value
   stays in its own column. */
.az-run-facts {
  display: grid; grid-template-columns: max-content minmax(0, 1fr);
  gap: 8px 14px; align-items: baseline; margin: 2px 0 0;
}
.az-run-facts > dt, .az-run-facts > dd { margin: 0; padding: 4px 0; }
.az-run-facts > dd { overflow-wrap: anywhere; }
/* The pipeline of a run card, always open and read-only: a small copy of
   the board (baukasten.js miniBoard) in the card's own white, the executing
   Baustein filled, the pins dashed. Nothing in it can be clicked. */
.az-run-board {
  margin-top: 2px; padding: 8px;
  border: 1px solid rgba(255, 255, 255, 0.28); border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.06);
}
.bk-mini { display: block; width: 100%; height: auto; color: var(--text); }
.bk-mini-wire { fill: none; stroke: currentColor; stroke-opacity: .6; stroke-width: 2.5; }
.bk-mini-wire.agent { stroke-dasharray: 7 5; }
.bk-mini-card rect {
  fill: currentColor; fill-opacity: .12;
  stroke: currentColor; stroke-opacity: .5; stroke-width: 1.5;
}
.bk-mini-card.pin rect { fill-opacity: .04; stroke-dasharray: 5 4; }
.bk-mini-card.exec rect { fill-opacity: .28; stroke-opacity: 1; stroke-width: 2.5; }
/* 18px is MINI_FONT in baukasten.js, which measures a label with it */
.bk-mini-card text {
  fill: currentColor; font: 600 18px Inter, sans-serif; dominant-baseline: central;
}
.az-run-add {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; min-height: 150px;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm);
  background: transparent; color: var(--muted); font-size: 14px; cursor: pointer;
  transition: border-color .12s, color .12s, background .12s;
}
.az-run-add:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.az-run-add .material-symbols-outlined { font-size: 22px; }
.az-grid-config-row { display: flex; align-items: center; gap: 6px; }
.az-grid-config-row .az-check input { width: auto; }
.az-dash-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Test history: read-only review entries in an archival gray, clearly set
   apart from the live (violet to blue) run cards; only "Load configuration"
   acts. */
.az-hist-entry {
  border: 1px solid var(--border); border-left: 3px solid var(--border-strong);
  border-radius: var(--radius-sm); background: var(--panel-2);
  padding: 10px 12px; margin-top: 10px;
}
.az-hist-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.az-hist-when { font-weight: 600; font-size: 13.5px; color: var(--text-2); }
.az-hist-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.az-hist-chip {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  background: var(--panel); border: 1px dashed var(--border-strong);
  color: var(--muted); font-size: 12.5px; cursor: default; user-select: text;
}

/* run log: what happened to every item, step by step ---------------------- */
.az-log-controls {
  display: flex; flex-wrap: wrap; gap: 12px 16px; align-items: flex-end;
  margin-top: 12px;
}
.az-log-ctl { display: flex; flex-direction: column; gap: 4px; }
.az-log-ctl select, .az-log-ctl input { width: auto; min-width: 190px; }
.az-log-ctl.grow { flex: 1 1 220px; }
.az-log-ctl.grow input { width: 100%; }

.az-log-facts { margin-top: 10px; }
.az-log-fact {
  display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 4px 16px;
  padding: 7px 0; border-bottom: 1px dashed var(--border); font-size: 13.5px;
}
.az-log-fact:last-child { border-bottom: none; }
.az-log-fact-key { color: var(--muted); }
.az-log-fact-val { color: var(--text-2); word-break: break-word; }

.az-log-batches { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.az-log-batch {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--panel-2);
}
.az-log-batch-name { font-weight: 600; font-size: 13px; min-width: 74px; }
.az-log-batch-err { color: var(--danger); }

.az-log-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.az-log-item {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel-2);
}
.az-log-item > summary {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  cursor: pointer; list-style: none; font-size: 13.5px;
}
.az-log-item > summary::-webkit-details-marker { display: none; }
.az-log-item > summary::before {
  content: ""; flex: none; width: 0; height: 0;
  border-left: 5px solid var(--muted);
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform 0.12s ease;
}
.az-log-item[open] > summary::before { transform: rotate(90deg); }
.az-log-item > summary:hover { background: var(--accent-soft); }
.az-log-num {
  flex: none; min-width: 42px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.az-log-text {
  flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; color: var(--text-2);
}
.az-log-route { flex: none; color: var(--muted); font-size: 12.5px; white-space: nowrap; }
.az-log-total {
  flex: none; min-width: 74px; text-align: right; font-weight: 600;
  font-variant-numeric: tabular-nums; font-size: 12.5px;
}
.az-log-body { padding: 2px 14px 14px; border-top: 1px dashed var(--border); }
.az-log-full {
  margin: 10px 0 0; padding: 8px 10px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 13px; color: var(--text-2); white-space: pre-wrap;
  word-break: break-word; max-height: 170px; overflow: auto;
}
.az-log-steps { margin-top: 12px; }
.az-log-step { display: flex; gap: 12px; position: relative; padding: 7px 0; }
.az-log-step:not(:last-child)::before {
  content: ""; position: absolute; left: 4px; top: 20px; bottom: -4px;
  width: 1px; background: var(--border-strong);
}
.az-log-dot {
  flex: none; width: 9px; height: 9px; margin-top: 5px; border-radius: 999px;
  background: var(--border-strong); position: relative; z-index: 1;
}
.az-log-dot-precheck, .az-log-dot-prompt, .az-log-dot-request { background: var(--accent); }
.az-log-dot-answer, .az-log-dot-parse, .az-log-dot-score { background: var(--ok); }
.az-log-dot-notice, .az-log-dot-post { background: var(--warn); }
.az-log-dot-block, .az-log-dot-error { background: var(--danger); }
.az-log-dot-verdict { background: var(--text); }
.az-log-step-main { flex: 1 1 auto; min-width: 0; }
.az-log-step-head { display: flex; align-items: baseline; gap: 10px; }
.az-log-step-label { font-weight: 600; font-size: 13px; }
.az-log-step-ms {
  margin-left: auto; flex: none; color: var(--muted); font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.az-log-step-detail {
  margin: 2px 0 0; font-size: 13px; color: var(--text-2); word-break: break-word;
}
.az-log-bar {
  display: block; height: 4px; max-width: 340px; margin-top: 6px;
  background: var(--border); border-radius: 999px; overflow: hidden;
}
.az-log-bar-fill { display: block; height: 100%; background: var(--accent); border-radius: 999px; }

@media (max-width: 720px) {
  .az-log-fact { grid-template-columns: 1fr; }
  .az-log-ctl select, .az-log-ctl input { min-width: 0; width: 100%; }
  .az-log-ctl { flex: 1 1 160px; }
  /* the message moves to its own line instead of squeezing the row */
  .az-log-item > summary { flex-wrap: wrap; }
  .az-log-text { flex: 1 1 100%; order: 1; }
  .az-log-total { min-width: 0; }
}

/* Builder sections stacked underneath each other, each in its own framed box
   so it is clear where a section starts and ends. A section box is styled like
   a .card on Infrastruktur: the bright --panel surface on the recessed page,
   same border, radius and shadow. A box nested inside it stays on the same
   bright surface and is delimited by its border (see .bk-sub-box); only
   content surfaces (canvas, lists, previews) recess. The gap between two
   sections is wider than the padding inside one, so the steps read as
   separate stations rather than as one long column. */
.bk-section-box {
  margin-top: 28px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  box-shadow: var(--shadow-sm);
  /* not clipped: the heading's info tooltip drops out of the head row into
     the box, and on a short section (Konfiguration) past its bottom edge */
}
.bk-section {
  display: flex; align-items: center; gap: 7px;
  margin: 0; padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  font-size: 12.5px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.bk-section .material-symbols-outlined { font-size: 17px; color: var(--accent); }
/* a control may sit in the head row (the configuration picker): it must not
   inherit the heading's uppercase small-caps look */
.bk-section .select-wrap select {
  text-transform: none; letter-spacing: normal;
  font-size: 13.5px; font-weight: 500; color: var(--text);
  padding: 6px 30px 6px 10px;
}
.bk-section .bk-config-pick { min-width: 220px; margin-left: 4px; }
.bk-section .bk-head-btn {
  text-transform: none; letter-spacing: normal;
  font-size: 12.5px; font-weight: 500; color: var(--text);
  padding: 6px 10px;
}
/* the hand-over to the Test-Suite sits in the head's top right corner */
.bk-section #bk-to-suite { margin-left: auto; }

/* Long explanations sit behind the info symbol next to a heading instead of
   as a wall of text above the content: the box says what it is, the tooltip
   says how it works. The text comes from data-tip and is rendered as text,
   so it can never carry markup. */
.bk-info {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; padding: 0; margin: 0;
  border: none; background: none; color: var(--muted); cursor: help;
}
.bk-info:hover, .bk-info:focus-visible { color: var(--accent); }
.bk-info:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 50%; }
.bk-info .material-symbols-outlined { font-size: 17px; color: inherit; }
/* display, not opacity: a hidden absolutely positioned box still counts
   towards its ancestors' scrollable width, and a 520px tooltip next to a
   heading would give the whole page a horizontal scrollbar */
.bk-info::after {
  content: attr(data-tip);
  display: none;
  position: absolute; top: calc(100% + 8px); left: -10px; z-index: 80;
  width: max-content; max-width: min(520px, 70vw);
  padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--text-2);
  box-shadow: var(--shadow-md);
  font-size: 13px; font-weight: 400; line-height: 1.55;
  text-transform: none; letter-spacing: normal; white-space: pre-line;
  text-align: left; pointer-events: none;
}
.bk-info:hover::after, .bk-info:focus-visible::after { display: block; }
.bk-section-box > .mode-pane { padding: 6px 16px 18px; }
.az-check-list {
  display: flex; flex-direction: column; gap: 6px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; background: var(--panel-2);
}
.az-check { display: inline-flex; align-items: center; gap: 8px; font-weight: 400; cursor: pointer; }
.az-check input { width: auto; }
.az-grid-vars { display: flex; flex-direction: column; gap: 8px; }
.az-grid-var {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 8px 12px; background: var(--panel);
}
.az-grid-var-head { display: flex; align-items: center; gap: 8px; font-weight: 400; cursor: pointer; }
.az-grid-var-head input { width: auto; }
/* the values of a dimension sit under its switch, indented to its label */
.az-grid-var-body { margin: 8px 0 2px 24px; display: flex; flex-direction: column; gap: 8px; }
.az-grid-var-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* a gateway offers dozens of models: the list scrolls instead of pushing the
   remaining dimensions off the page */
.az-grid-models { max-height: 220px; overflow-y: auto; }
.az-grid-model-meta { color: var(--muted); font-size: 12px; }
.az-swatch {
  display: inline-block; width: 12px; height: 12px;
  border-radius: 3px; vertical-align: middle; flex: none;
}
.az-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; }
.az-legend-chip { display: inline-flex; align-items: center; gap: 7px; }
.az-tile-value {
  font-size: 26px; font-weight: 600; color: var(--text);
  line-height: 1.2; margin-top: 2px;
}
.az-chart { position: relative; min-height: 0; }
.az-chart h3 { margin-bottom: 2px; }
.az-chart svg { display: block; max-width: 100%; }
.az-axis-text { font-size: 11px; fill: var(--viz-muted); font-variant-numeric: tabular-nums; }
.az-value-text { font-size: 12px; font-weight: 600; fill: var(--viz-ink);
                 font-variant-numeric: tabular-nums; }
.az-bar:hover { filter: brightness(1.08); }
.az-facet-title { font-size: 12.5px; color: var(--muted); margin: 10px 0 4px; font-weight: 600; }
.az-tooltip {
  position: absolute; z-index: 40; pointer-events: none;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
  padding: 8px 10px; font-size: 12.5px; max-width: 320px;
}
.az-tip-row { display: flex; align-items: center; gap: 6px; white-space: nowrap;
              overflow: hidden; text-overflow: ellipsis; }
.az-tip-key { display: inline-block; width: 10px; height: 3px; border-radius: 2px; flex: none; }
.az-confusion { display: flex; flex-wrap: wrap; gap: 16px; }
.az-conf-card { display: flex; flex-direction: column; gap: 6px; }
.az-conf-title { display: flex; align-items: center; gap: 7px; }
.az-conf-grid {
  display: grid; grid-template-columns: 64px 64px; gap: 2px;
}
.az-conf-cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  height: 52px; border-radius: 6px; font-size: 11px;
}
.az-conf-cell strong { font-size: 15px; font-variant-numeric: tabular-nums; }

/* ---------------- NetiChecker Baukasten (analyze) ----------------
   Pipeline designer: palette + canvas of agent Bausteine, context level
   switches and the Prompt Baustein composer. Reuses the viz palette for
   the Baustein type colors so light/dark theming stays consistent. */
/* The Baukasten card is a grouping wrapper, not a surface of its own: the
   framed section boxes inside it already carry the chrome, so a second frame
   around them only nests a box in a box and steals 24px on each side. It
   keeps its spacing and loses everything visible, which lets the sections
   use the full page width. */
.bk-card {
  margin-bottom: 22px; padding: 0;
  background: transparent; border: none; box-shadow: none;
}
/* the first section is the top of the view: the sticky bar already spaces it */
.bk-card > .bk-section-box:first-child { margin-top: 0; }
.bk-config-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 10px 0 4px;
}
.bk-config-pick { min-width: 220px; }
.bk-config-pick select { padding: 8px 34px 8px 12px; font-size: 14px; }
.bk-config-name-label { flex: none; margin: 0; }
.bk-config-bar input[type="text"] { max-width: 240px; padding: 8px 12px; font-size: 14px; }
/* the id is an address fragment, so it reads like code */
.bk-config-bar #bk-config-id { max-width: 200px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; }
.bk-config-bar #bk-config-id.bad { border-color: var(--danger); }
/* The tags: chips, their add field and the counter inside one input-like
   frame of a fixed width, because a configuration carries at most three tags
   (MAX_TAGS in baukasten.js) and the bar must not change width while they
   are typed. The strip inside stays one line high and scrolls sideways
   instead of wrapping, should three long tags not fit; paintTags scrolls to
   the end, where the add field sits. The scrollbar is hidden like the section
   rail's (.az-steps), because a bar inside a 30px box would eat the chips. */
.bk-tags-frame {
  display: flex; align-items: center; flex: none; width: 380px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); cursor: text;
  transition: border-color .12s, box-shadow .12s;
}
.bk-tags-frame:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.bk-tags {
  flex: 1; min-width: 0;
  display: flex; flex-wrap: nowrap; align-items: center; gap: 4px; padding: 3px 6px;
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
}
.bk-tags::-webkit-scrollbar { display: none; }
.bk-config-bar .bk-tags input[type="text"] {
  flex: 1 1 80px; min-width: 80px; max-width: none; width: auto;
  padding: 5px 4px; font-size: 13px; border: none; background: transparent; box-shadow: none;
}
/* full: the field gives its room to the chips instead of standing empty */
.bk-config-bar .bk-tags input[type="text"]:disabled {
  flex: 0 0 0; min-width: 0; width: 0; padding: 0; background: transparent;
}
.bk-tag-count {
  flex: none; padding: 0 8px;
  font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.bk-tag-count.full { color: var(--accent); font-weight: 600; }
.bk-tag, .sx-tag {
  display: inline-flex; align-items: center; gap: 2px; max-width: 100%;
  padding: 2px 8px; border-radius: 999px; border: 1px solid transparent;
  background: var(--accent-soft); color: var(--accent);
  font-size: 12px; font-weight: 600; line-height: 1.5; overflow-wrap: anywhere;
}
.bk-tag { padding-right: 3px; flex: none; max-width: none; white-space: nowrap; }
.bk-tag-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; padding: 0; border: none; border-radius: 50%;
  background: transparent; color: inherit; font-size: 15px; line-height: 1; cursor: pointer;
}
.bk-tag-x:hover, .bk-tag-x:focus-visible { background: var(--accent); color: #fff; outline: none; }
.bk-config-bar .save-state { margin-left: 0; }
.bk-spacer { flex: 1; }

/* Baustein icons follow the app palette (indigo accent), like the topbar
   and nav icons, instead of introducing chart colors into the chrome. */
.bk-type-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 7px; flex: none;
  background: var(--accent-soft);
}
.bk-type-icon .material-symbols-outlined { font-size: 16px; color: var(--accent); }

/* palette */
.bk-palette { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 16px; }
.bk-pal-item {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px 6px 8px; font-size: 13.5px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); cursor: grab; user-select: none;
  transition: border-color .12s, background .12s;
}
.bk-pal-item:hover { border-color: var(--border-strong); background: var(--panel-2); }
.bk-pal-item.used { opacity: 0.4; cursor: default; }

/* The pipeline board, n8n-style: a surface the cards sit on at their own
   coordinates, with the wires drawn between their ports. There is no row and
   no grid any more, so two Bausteine can hang off the same card and really
   run beside each other. It never scrolls sideways: the board is exactly as
   wide as this pane and a flow that does not fit folds into the next band of
   columns (layoutBoard, re-folded on resize by fitToPane in baukasten.js), so
   the whole pipeline is always visible. */
.bk-canvas {
  position: relative; height: 460px; min-height: 240px;
  overflow-x: hidden; overflow-y: auto; resize: vertical;
  /* The gutter is reserved whether or not the board is currently taller than
     the pane: without it clientWidth changes by a scrollbar the moment a
     second row appears, and the board would be laid out for a width it no
     longer has (and then need a horizontal scrollbar to show it). */
  scrollbar-gutter: stable;
  border: 1px dashed var(--border-strong); border-radius: var(--radius);
  background-color: var(--panel);
}
.bk-board { position: relative; min-width: 100%; min-height: 100%; }
/* the standing note under the board and the button that lays the board out
   again (Karten anordnen) sit on one line */
.bk-board-foot { display: flex; align-items: center; gap: 12px; margin-top: 6px; }
.bk-board-foot p { flex: 1; min-width: 0; }
/* n8n-style drawing board, shared by the pipeline board and the prompt
   composition: the bright surface with a dot grid on it, so it reads as a
   board the cards are placed on. The cards cover the dots, which is what
   separates them from it in both themes. The grid is a background-image, so
   anything overriding the surface must set background-color, never the
   background shorthand. */
.bk-board, .bk-comp-list, .bk-sw-list {
  background-color: var(--panel);
  background-image: radial-gradient(var(--border-strong) 1px, transparent 1.2px);
  background-size: 18px 18px;
  background-position: 9px 9px;
}
.bk-board.drag-over { background-color: var(--accent-soft); }
.bk-empty {
  position: absolute; left: 50%; top: 24px; transform: translateX(-50%);
  color: var(--muted); font-size: 13.5px; text-align: center; max-width: 420px;
}

/* the wires: one layer under the cards, each with an invisible fat copy that
   catches the pointer so a 2px line is still easy to hover. The router in
   baukasten.js rounds every turn into a curve, so a wire needs no line join. */
.bk-wires { position: absolute; left: 0; top: 0; pointer-events: none; }
.bk-wire { fill: none; stroke: var(--border-strong); stroke-width: 2; }
.bk-wire.agent { stroke: var(--accent); stroke-dasharray: 5 4; opacity: 0.8; }
.bk-wire.hover { stroke: var(--accent); stroke-width: 2.5; opacity: 1; }
.bk-wire.temp { stroke: var(--accent); stroke-width: 2; stroke-dasharray: 4 4; }
.bk-wire-hit { fill: none; stroke: transparent; stroke-width: 18; pointer-events: stroke; }
/* the controls of a wire appear only while it (or they) are hovered, so an
   idle board stays a board */
.bk-edge-tools {
  position: absolute; z-index: 3; display: none; gap: 4px;
  transform: translate(-50%, -50%);
}
.bk-edge-tools.show { display: flex; }
.bk-edge-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; border-radius: 50%;
  border: 1px solid var(--accent); background: var(--panel); color: var(--accent);
  box-shadow: var(--shadow-sm);
}
.bk-edge-btn:hover { background: var(--accent-soft); transform: none; }
.bk-edge-btn.danger { border-color: var(--danger); color: var(--danger); }
.bk-edge-btn.danger:hover { background: var(--danger-soft, var(--accent-soft)); }
.bk-edge-btn .material-symbols-outlined { font-size: 14px; }
.bk-edge-pop, .bk-agent-pop { position: absolute; z-index: 4; margin: 0; }
.bk-edge-pick {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 8px; font-size: 13px; text-align: left; font-weight: 400;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--text-2);
}
.bk-edge-pick:hover { border-color: var(--accent); color: var(--accent); }
.bk-edge-pick .bk-type-icon .material-symbols-outlined { font-size: 16px; }

/* the two fixed pins of the board: where a request enters and where it ends.
   Width and height are PIN_W/NODE_H in baukasten.js: change them together. */
.bk-pin {
  position: absolute; z-index: 1; display: flex; align-items: center; gap: 7px;
  width: 176px; height: 48px; padding: 8px 12px;
  font-size: 12.5px; color: var(--muted); cursor: grab;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm);
  background: var(--panel);
}
.bk-pin.dragging { opacity: 0.6; }
.bk-pin .material-symbols-outlined { font-size: 17px; }
.bk-pin-text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.bk-pin-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the result pin is a station that is configured, not just an end marker:
   clicking it opens the Urteilsfindung, and its subtitle says what is set */
.bk-pin-result { border-style: solid; }
.bk-pin-result:hover { border-color: var(--accent); color: var(--accent); }
.bk-pin-sub {
  font-size: 10.5px; line-height: 1.25; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* a card: width and height are NODE_W/NODE_H in baukasten.js, which every
   port, wire and the board layout compute from; change them together */
.bk-node {
  position: absolute; z-index: 2;
  display: flex; flex-direction: column; justify-content: center;
  padding: 10px 12px; width: 190px; height: 48px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); box-shadow: var(--shadow-sm); cursor: grab;
  transition: border-color .12s, box-shadow .12s;
}
.bk-node:hover { border-color: var(--border-strong); }
.bk-node.selected { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.bk-node.dragging { opacity: 0.75; box-shadow: var(--shadow-md); cursor: grabbing; }
.bk-node-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
.bk-node-head .bk-node-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* connection points: drag one to draw a wire, click the lower one to pick
   agents from a list instead */
.bk-port {
  position: absolute; width: 11px; height: 11px; border-radius: 50%;
  background: var(--panel); border: 2px solid var(--border-strong);
}
.bk-port-in { left: -6px; top: 50%; margin-top: -5.5px; }
.bk-port-out { right: -6px; top: 50%; margin-top: -5.5px; cursor: crosshair; }
.bk-port-agent {
  left: 50%; margin-left: -5.5px; bottom: -6px;
  border-color: var(--accent); cursor: crosshair;
}
.bk-port-out:hover, .bk-port-agent:hover { background: var(--accent); border-color: var(--accent); }
.bk-node:hover .bk-port-out, .bk-node:hover .bk-port-agent { border-color: var(--accent); }
/* the "+" under a card, the way an agent node carries one in n8n: the dot
   above it draws a wire by hand, this opens the list of Bausteine. The dashed
   stem connects the two so the pair reads as one connection point. Its 15px
   gap plus 20px height is CHROME_BOTTOM (and part of TRACK_GAP) in
   baukasten.js: the room no other card or wire may take. */
.bk-node-plus {
  position: absolute; left: 50%; top: calc(100% + 15px);
  transform: translateX(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; padding: 0; border-radius: 5px; cursor: pointer;
  border: 1px solid var(--border-strong); background: var(--panel);
  color: var(--muted); box-shadow: var(--shadow-sm);
}
.bk-node-plus::before {
  content: ""; position: absolute; left: 50%; bottom: 100%;
  height: 10px; border-left: 1px dashed var(--border-strong);
}
.bk-node-plus:hover {
  color: var(--accent); border-color: var(--accent); background: var(--accent-soft);
  transform: translateX(-50%);
}
.bk-node-plus .material-symbols-outlined { font-size: 14px; }
.bk-node:hover .bk-node-plus, .bk-node.orchestrator .bk-node-plus { color: var(--accent); }
.bk-node:hover .bk-node-plus::before { border-color: var(--accent); }
/* the count of a Baustein already on the board: it stays addable, so the
   palette entry only says how often it is in use */
.bk-pal-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 17px; padding: 0 4px; margin-left: 2px;
  font-size: 11px; font-weight: 600; border-radius: 999px;
  background: var(--accent); color: #fff;
}
.bk-node-title { font-size: 13.5px; font-weight: 600; line-height: 1.25; }
.bk-node-remove {
  position: absolute; top: 4px; right: 4px;
  width: 24px; height: 24px; opacity: 0; transition: opacity .12s;
}
.bk-node-remove .material-symbols-outlined { font-size: 15px; }
.bk-node:hover .bk-node-remove { opacity: 1; }
.bk-node-remove:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }

.bk-tool-pop {
  display: flex; flex-direction: column; gap: 4px; width: 260px; margin-top: 8px;
  padding: 10px; border: 1px solid var(--accent); border-radius: var(--radius-sm);
  background: var(--panel); box-shadow: var(--shadow-sm);
  /* every Baustein is offered, so the list is longer than the board is tall */
  max-height: 380px; overflow-y: auto;
}
.bk-tool-pop-head { display: flex; align-items: center; gap: 6px; font-size: 13px; }
/* second half of the agent picker: what is attached already, under the list
   of Bausteine that a click would newly create */
.bk-tool-pop-sub {
  margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--border);
  font-size: 12px; font-weight: 600; color: var(--muted);
}
.bk-tool-pop-head strong { flex: 1; font-size: 13px; }
.bk-tool-pop-head .material-symbols-outlined { font-size: 16px; color: var(--accent); }
.bk-tool-pop-head .icon-btn { width: 24px; height: 24px; flex: none; }
.bk-tool-opt {
  display: flex; align-items: center; gap: 4px;
  padding: 5px 6px 5px 8px; font-size: 13px; text-align: left;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel);
}
/* the name half of an attached-agent row; the buttons next to it edit and
   detach, so the name itself is not a control */
.bk-tool-pick {
  display: flex; align-items: center; gap: 7px;
  flex: 1; min-width: 0; font-weight: 400;
}
.bk-tool-opt > .icon-btn { width: 26px; height: 26px; flex: none; }
.bk-tool-opt > .icon-btn .material-symbols-outlined { font-size: 15px; }
.bk-tool-opt .bk-type-icon .material-symbols-outlined { font-size: 16px; }
/* the form-wide "inputs fill their row" rule would squeeze the label out */
.bk-tool-opt input[type="checkbox"] { width: auto; flex: none; margin: 0; }
.bk-tool-opt .bk-block-name { flex: 1; }
/* The Baustein editor is a window over the board, n8n-style: the wiring
   behind it darkens and stays put, the window sits in the middle instead of
   filling the screen, and inside it the input sits left of the configuration
   and the output right of it. The same window carries the Urteilsfindung of
   the result pin (.bk-vdv). */
#bk-node-editor, #bk-verdict-editor { align-items: center; padding: 24px 16px; }
/* The window carries three columns of settings, so its type is one step
   smaller than the page's throughout: the labels, notes and controls inside
   it all read off these two rules rather than the page defaults. */
.bk-ndv {
  max-width: 1580px; max-height: calc(100vh - 48px);
  display: flex; flex-direction: column; padding: 18px 22px 16px;
  font-size: 13px;
}
.bk-ndv .small, .bk-ndv label, .bk-ndv input, .bk-ndv select,
.bk-ndv button, .bk-ndv p, .bk-ndv li { font-size: 12.5px; }
.bk-ndv-head {
  display: flex; align-items: center; gap: 10px;
  padding-bottom: 12px; border-bottom: 1px solid var(--border);
}
.bk-ndv-head strong { font-size: 14px; color: var(--text); }
.bk-ndv-head .muted { flex: 1; }
.bk-ndv-head .icon-btn { flex: none; }
/* Eingang | Konfiguration | Ausgabe, the three columns of an n8n node view.
   The outer two are fixed so the configuration keeps the room it needs. */
.bk-ndv-body {
  display: grid; grid-template-columns: 260px minmax(0, 1fr) 320px; gap: 20px;
  flex: 1; min-height: 0; padding-top: 14px;
}
.bk-ndv-pane { min-height: 0; overflow-y: auto; padding-right: 4px; }
.bk-ndv-input { border-right: 1px solid var(--border); padding-right: 18px; }
.bk-ndv-output { border-left: 1px solid var(--border); padding-left: 18px; }
.bk-ndv-pane-head {
  display: flex; align-items: center; gap: 7px; margin-bottom: 6px;
  color: var(--text-2);
}
.bk-ndv-pane-head strong { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; }
.bk-ndv-pane-head .material-symbols-outlined { font-size: 16px; }
.bk-ndv-pane .bk-sub-box { margin-top: 18px; }
.bk-ndv textarea { width: 100%; margin-top: 8px; font-size: 12.5px; }
.bk-ndv .modal-actions {
  padding-top: 14px; margin-top: 14px; border-top: 1px solid var(--border);
  justify-content: space-between;
}
.bk-ndv-remove { color: var(--danger); }
.bk-ndv-remove:hover { border-color: var(--danger); }
/* one source of the flow, its fields underneath: the same shape n8n's input
   panel has, so a field can be dragged from here onto the configuration */
.bk-ndv-src { margin-top: 10px; }
.bk-ndv-src-head {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--muted); font-weight: 600;
}
.bk-ndv-src-head .material-symbols-outlined { font-size: 15px; }
.bk-ndv-field {
  display: flex; align-items: center; gap: 6px; margin-top: 5px;
  padding: 5px 8px 5px 5px; font-size: 12px; cursor: grab;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--text-2);
}
.bk-ndv-field:hover { border-color: var(--border-strong); }
.bk-ndv-field.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.bk-ndv-field .material-symbols-outlined { font-size: 15px; color: var(--muted); flex: none; }
.bk-ndv-field.on .material-symbols-outlined { color: var(--accent); }
.bk-ndv-field-name {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The two prompts of a Baustein: picked above, edited here. A field from the
   input column is dropped straight into the text, which is what wires it, so
   the textarea is the drop target and says so while something hovers it. */
.bk-ndv-prompt-box { display: flex; flex-direction: column; min-width: 0; }
/* Thinking, Temperatur, top_p and Seed of a Baustein (ndvSamplingRow): one
   line under API connection and model, wrapping only when the column is narrow */
.bk-sampling-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 16px; margin: 10px 0 4px; }
.bk-sampling-row .switch { padding-bottom: 8px; }
.bk-sampling-field { display: flex; flex-direction: column; gap: 3px; }
.bk-sampling-field input { width: 86px; }
.bk-ndv-prompt-box .row-head { margin-bottom: 6px; }
.bk-ndv-prompt-box .row-head label { color: var(--muted); font-weight: 600; }
/* The surface a prompt is written on (the same dark block a prompt is
   previewed in) belongs to the coloured-overlay pair around it: see
   .code-edit.var-edit at the end of this file, which is where the textarea
   and the copy under it are kept on identical metrics. */
.bk-ndv-prompt-box .ghost { align-self: flex-start; margin-top: 6px; }
/* a token that names nothing (any more): the text keeps it, the line says so */
.bk-ndv-stale { color: var(--warn); }
/* the Urteilsfindung window: its middle column carries the whole calculation,
   its output column three lines, so the outer columns give room back */
.bk-vdv .bk-ndv-body { grid-template-columns: 280px minmax(0, 1fr) 250px; }
/* nothing to remove here - the result pin is part of the board */
.bk-vdv .modal-actions { justify-content: flex-end; }
.bk-vdv .bk-ndv-field { cursor: pointer; }
/* declared, not selectable: the output column and the flow's own fields */
.bk-vdv .bk-ndv-output .bk-ndv-field,
.bk-vdv .bk-vd-flow .bk-ndv-field { cursor: default; }
/* the output column is the narrowest (250px) and holds three fixed names:
   let them wrap instead of truncating "Moderationsstufe" to an ellipsis */
.bk-vdv .bk-ndv-output .bk-ndv-field { flex-wrap: wrap; }
.bk-vdv .bk-ndv-output .bk-ndv-field-name {
  flex: none; max-width: 100%; overflow: visible; white-space: normal;
}
/* the one action that turns a Baustein's schema into the format a run really
   asks for; it sits under that Baustein's fields, inside its box */
.bk-vd-adopt { align-self: flex-start; margin-top: 8px; }
/* too narrow for three columns: the output moves under the configuration,
   then everything stacks */
@media (max-width: 1180px) {
  .bk-ndv-body, .bk-vdv .bk-ndv-body { grid-template-columns: 240px minmax(0, 1fr); }
  .bk-ndv-output {
    grid-column: 1 / -1; border-left: none; padding-left: 0;
    border-top: 1px solid var(--border); padding-top: 14px;
  }
}
@media (max-width: 900px) {
  .bk-ndv-body, .bk-vdv .bk-ndv-body { grid-template-columns: minmax(0, 1fr); }
  .bk-ndv-input { border-right: none; padding-right: 0; }
}
.bk-tool-opt:hover { border-color: var(--border-strong); }
.bk-tool-opt.on { border-color: var(--accent); background: var(--accent-soft); }
.bk-tool-pop .bk-loop-limit { flex-wrap: wrap; margin-top: 6px; font-size: 13px; }
.bk-tool-pop .bk-loop-limit input { width: 64px; }
.bk-tool-pop .bk-loop-limit .muted { flex-basis: 100%; }
.bk-node.orchestrator { border-color: var(--accent); background: var(--accent-soft); }
/* The role rides on the card's top edge, not in the title row: it labels the
   whole card, and sitting outside the flow keeps every card exactly NODE_H
   high, which is what the port geometry in baukasten.js assumes. Its 9px
   above the card is CHROME_TOP there, and TOP_PAD keeps it on the board. */
.bk-node-role {
  position: absolute; top: -9px; left: 10px; right: 30px;
  display: flex; pointer-events: none;
}
.bk-role-badge {
  display: inline-flex; align-items: center; gap: 3px; flex: none;
  padding: 1px 7px 1px 5px; font-size: 10.5px; font-weight: 600;
  border-radius: 999px; background: var(--accent); color: var(--panel);
}
.bk-role-badge .material-symbols-outlined { font-size: 12px; }
.bk-node-flag { color: var(--muted); display: inline-flex; }
.bk-node-flag .material-symbols-outlined { font-size: 15px; }

/* Baustein settings (inside the editor window) */
.bk-field-label { display: block; margin-top: 14px; }
/* the role is shown, not chosen: it follows the tools wired on the canvas */
.bk-role-line { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 6px; }
.bk-role-state {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 12px; font-size: 12.5px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--text-2);
}
.bk-role-state.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.bk-role-state .material-symbols-outlined { font-size: 17px; }

/* ${variables} rendered as variables, the way n8n shows them */
.bk-var {
  display: inline-block; padding: 0 5px; border-radius: 4px;
  font-weight: 600; background: var(--panel-2); color: var(--text-2);
  border: 1px solid var(--border-strong);
}
.bk-var.live { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.bk-var.unknown { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }
/* On the dark preview block a variable is coloured text, not a light chip
   punched into it: the same colour the prompt editor paints, so a token reads
   the same wherever the prompt is shown. Only a live one - really substituted
   at run time - keeps the filled chip. */
.code .bk-var {
  background: transparent; border-color: transparent; padding: 0;
  color: var(--json-var);
}
.code .bk-var.live {
  padding: 0 5px; color: #c7d2fe;
  background: rgba(129, 140, 248, 0.22); border-color: #818cf8;
}
.code .bk-var.unknown { color: #fbbf24; }
.bk-var-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; }
.bk-var-chip {
  padding: 2px 8px; font-size: 11.5px; cursor: pointer;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--panel); color: var(--text-2);
}
.bk-var-chip:hover { border-color: var(--accent); color: var(--accent); }
.bk-var-chip.live { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.bk-var-preview { max-height: 120px; overflow: auto; margin: 6px 0 0; white-space: pre-wrap; }
.bk-loop-limit { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.bk-loop-limit label { margin: 0; }
.bk-loop-limit input { width: 80px; }
.bk-returns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.bk-return-chip {
  position: relative; display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; font-size: 13px; cursor: pointer; user-select: none;
  border: 1px solid var(--border); border-radius: 999px; background: var(--panel);
  transition: background .12s, border-color .12s, color .12s;
}
.bk-return-chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.bk-return-chip:hover { border-color: var(--border-strong); }
.bk-return-chip:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.bk-return-chip.on {
  background: var(--accent-soft); border-color: var(--accent);
  color: var(--accent); font-weight: 500;
}
/* a tool another orchestrator already owns, or one that would close a circle */
.bk-return-chip.blocked { opacity: 0.5; cursor: not-allowed; }
.bk-return-chip.blocked:hover { border-color: var(--border); }

/* context pane */
.bk-presets { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
/* the combination the switches below currently add up to, so a preset is not
   a fire-and-forget button: it shows which one is in effect */
.bk-preset.active {
  background: var(--accent-soft); border-color: var(--accent);
  color: var(--accent); font-weight: 500;
}
.bk-preset.active:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.bk-preset.active .material-symbols-outlined { color: var(--accent); }
.bk-ctx-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 28px; margin-top: 14px; }
@media (max-width: 980px) { .bk-ctx-grid { grid-template-columns: 1fr; } }
/* No box and no switch: each side is a plain list of what can be picked, the
   thread as radios (exactly one), the feeds as checkboxes (any number). A
   frame around a list of five checkboxes only nested a box in a box. */
.bk-ctx-col { display: flex; flex-direction: column; gap: 8px; }
.bk-ctx-legend { font-size: 13px; font-weight: 600; color: var(--muted); }
.bk-ctx-list { display: flex; flex-direction: column; gap: 7px; }
.bk-ctx-list .az-check { align-items: center; }
.bk-feed-chip {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  padding: 3px 4px 3px 12px; font-size: 13px;
  background: var(--accent-soft); color: var(--accent); border-radius: 999px;
}
.bk-feed-chip .icon-btn { width: 22px; height: 22px; border: none; color: inherit; }
.bk-feed-chip .icon-btn .material-symbols-outlined { font-size: 14px; }
/* regex entries of the stop word base read as code, not as prose */
.bk-feed-chip.regex {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; background: var(--panel-2); color: var(--text-2);
  border: 1px solid var(--border-strong);
}
.bk-feed-chip.regex.bad {
  border-color: var(--danger); color: var(--danger); background: var(--danger-soft);
}
/* wraps: the stop word row carries a mode select and two buttons besides the
   input, which would otherwise be squeezed to a few pixels in a narrow pane */
.bk-feed-addrow { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.bk-feed-addrow input { flex: 1 1 200px; min-width: 0; padding: 7px 10px; font-size: 13.5px; }
.bk-sw-mode { flex: none; width: 168px; }
.bk-sw-mode select { padding: 7px 30px 7px 10px; font-size: 13.5px; }

/* prompt Bausteine pane: library | composition | composed prompt, then a
   full-width row for the structured output settings */
.bk-prompt-grid {
  /* one height for all three columns: the library sets it, composition and
     composed prompt fill it instead of ending wherever their content does */
  --bk-col-h: 460px;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px; align-items: stretch;
}
@media (max-width: 1240px) { .bk-prompt-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 860px) { .bk-prompt-grid { grid-template-columns: 1fr; } }
.bk-prompt-col {
  display: flex; flex-direction: column; min-width: 0; height: var(--bk-col-h);
}
.bk-prompt-col textarea { width: 100%; font-size: 13.5px; }
.bk-structured-row {
  margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--border);
}

/* The library is one framed panel of fixed height: category heading, its
   Bausteine indented underneath, next heading - no box per category. */
.bk-block-list {
  display: flex; flex-direction: column; gap: 10px; margin-top: 8px;
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 10px; border: 1px solid var(--border);
  /* the bright surface, like every other framed block of the builder: the
     category headings and the indented rows carry the structure here, so a
     recessed panel behind them only darkened the column */
  border-radius: var(--radius-sm); background: var(--panel);
}
.bk-lib-cat { display: flex; flex-direction: column; gap: 6px; }
.bk-cat-body {
  display: flex; flex-direction: column; gap: 6px;
  padding-left: 14px; border-left: 1px solid var(--border);
}
.bk-cat-body:empty { display: none; }
.bk-block-cat {
  display: flex; align-items: center; gap: 6px; width: 100%;
  font-size: 11.5px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.05em;
  margin: 0; padding: 2px 0; text-align: left;
  background: none; border: none; cursor: pointer;
}
.bk-block-cat.open { color: var(--text-2); }
.bk-block-cat:hover { color: var(--text-2); }
.bk-block-cat .bk-cat-name { flex: 1; }
.bk-block-cat .material-symbols-outlined { font-size: 16px; }
.bk-cat-empty { margin: 0; font-style: italic; }
/* every save button of the Baukasten carries the same colour, and the ones
   whose target changed since the last save stand out */
.bk-save-btn {
  color: var(--accent); border-color: var(--accent); background: var(--panel);
}
.bk-save-btn .material-symbols-outlined { color: inherit; }
.bk-save-btn:hover:not(:disabled) { background: var(--accent-soft); }
.bk-save-btn.attn {
  background: var(--accent); border-color: var(--accent); color: #fff;
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.bk-save-btn.attn:hover:not(:disabled) { background: var(--accent); }
.bk-save-btn:disabled {
  color: var(--muted); border-color: var(--border);
  background: var(--panel); box-shadow: none; cursor: not-allowed;
}
.bk-save-full-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.bk-save-full-row input { flex: 1 1 150px; min-width: 0; padding: 7px 10px; font-size: 13.5px; }
.bk-save-full-row button { flex: none; }
.bk-prompt-role { margin-bottom: 18px; }
.bk-block-row.in-use { border-color: var(--ok); }
.bk-block-row {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 8px 6px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--panel);
}
.bk-block-name { flex: 1; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bk-block-row .icon-btn, .bk-comp-row .icon-btn { width: 28px; height: 28px; flex: none; }
.bk-block-row .icon-btn .material-symbols-outlined,
.bk-comp-row .icon-btn .material-symbols-outlined { font-size: 16px; }
.bk-block-row .icon-btn:disabled { color: var(--ok); cursor: default; background: transparent; border-color: transparent; }
.bk-block-editor {
  display: flex; flex-direction: column; gap: 8px;
  padding: 12px; border: 1px solid var(--accent);
  border-radius: var(--radius-sm); background: var(--panel);
}
.bk-block-editor textarea { font-size: 13.5px; }
.bk-comp-list {
  display: flex; flex-direction: column; gap: 6px;
  flex: 1; min-height: 64px; overflow-y: auto;
  margin-top: 8px; padding: 10px;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm);
  /* same dotted board as the pipeline canvas: it is the same gesture, cards
     dragged into an order (see the .bk-canvas rule for the surface) */
}
.bk-comp-row {
  position: relative; display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--panel); cursor: grab;
}
.bk-comp-row > .material-symbols-outlined { font-size: 18px; color: var(--muted); }
.bk-comp-row.dragging { opacity: 0.45; }
.bk-comp-row.drop-above::before, .bk-comp-row.drop-below::after {
  content: ""; position: absolute; left: 4px; right: 4px; height: 3px;
  border-radius: 2px; background: var(--accent);
}
.bk-comp-row.drop-above::before { top: -6px; }
.bk-comp-row.drop-below::after { bottom: -6px; }
/* fills the column so the save row sits at its bottom edge, level with the
   library and the composition next to it */
.bk-preview { flex: 1; min-height: 0; overflow: auto; margin: 0; }

/* embedded Swagger frame (kept for any page that wants to inline the docs;
   the Übersicht links out to them instead) */
.swagger-frame {
  width: 100%;
  height: calc(100vh - 190px);
  min-height: 480px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  box-shadow: var(--shadow-sm);
}

/* overview & guide page (index.html) */
.guide-steps {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}
.guide-steps li {
  counter-increment: step;
  position: relative;
  padding: 6px 0 6px 38px;
}
.guide-steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 7px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.link-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
  gap: 12px;
  margin-top: 12px;
}
a.link-card {
  display: block;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  text-decoration: none;
  color: var(--text);
  transition: border-color 0.15s, box-shadow 0.15s;
}
a.link-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
}
.link-card-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
}
.link-card-title .material-symbols-outlined {
  color: var(--accent);
  font-size: 20px;
}
.link-card-sub {
  display: block;
  margin-top: 4px;
  color: var(--muted);
  font-size: 13px;
}

/* NC.dropdown: a button with a short menu of actions ("Exportieren ▾") */
.dropdown { position: relative; display: inline-flex; }
.dropdown-caret { margin-left: -2px; }
.dropdown-menu {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 90;
  min-width: 100%; padding: 4px;
  display: flex; flex-direction: column; gap: 2px;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
}
.dropdown-item {
  justify-content: flex-start; white-space: nowrap;
  border-color: transparent; background: transparent; font-size: 13px;
}
.dropdown-item:hover, .dropdown-item:focus-visible { background: var(--panel-2); }

/* Schnittstelle page (schnittstelle.html, schnittstelle.js): the Controller-API
   card and the Katalog of saved configurations side by side. A configuration is
   a bordered block on the bright card surface (a box in a box stays bright);
   its try form is folded away in a details. Long URLs, ids, curl lines and model
   names break anywhere, so a phone never scrolls sideways. */
.sx-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; align-items: stretch; }
/* The Katalog is exactly as tall as the Controller-API card beside it:
   `contain: size` keeps its content out of the row height, so the API card
   alone sets it, and the list scrolls inside the card. */
#sx-catalog-card { contain: size; display: flex; flex-direction: column; }
#sx-catalog-card > * { flex: none; }
#sx-catalog-card > .sx-catalog {
  flex: 1 1 0; min-height: 160px; overflow-y: auto; overflow-x: hidden;
  scrollbar-gutter: stable; padding-right: 4px;
}
@media (max-width: 1100px) {
  .sx-cols { grid-template-columns: minmax(0, 1fr); }
  #sx-catalog-card { contain: none; }
  #sx-catalog-card > .sx-catalog { flex: none; min-height: 0; overflow: visible; padding-right: 0; }
}
.sx-filter { display: flex; flex-direction: column; gap: 8px; margin: 2px 0 10px; }
.sx-filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.sx-filter-row input[type="search"] { flex: 2 1 220px; width: auto; min-width: 0; padding: 8px 12px; font-size: 14px; }
.sx-filter-model { flex: 1 1 180px; min-width: 0; }
.sx-filter-model select { padding-top: 8px; padding-bottom: 8px; font-size: 14px; }
.sx-tag-filter { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.sx-tag-filter-label { margin-right: 2px; }
.sx-no-match { margin: 0 0 10px; }
/* a tag of the Katalog is a filter switch; its look is the Baukasten's chip */
button.sx-tag { cursor: pointer; font: inherit; font-size: 12px; font-weight: 600; line-height: 1.5; padding: 1px 9px; }
button.sx-tag:hover { background: var(--accent-soft); border-color: var(--accent); }
button.sx-tag.on, button.sx-tag.on:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.sx-config-tags { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.sx-docs-note { margin: 12px 0 0; }
.sx-sub-title { margin: 22px 0 8px; }
/* a method tag in front of an address */
.sx-call { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; margin: 0 0 8px; min-width: 0; }
.sx-method { flex: none; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; color: var(--accent); background: var(--accent-soft); border-radius: 6px; padding: 2px 6px; }
.sx-url { min-width: 0; overflow-wrap: anywhere; }
/* a copyable block: the text takes the row, the copy button keeps its size */
.sx-copy-row { display: flex; align-items: flex-start; gap: 8px; min-width: 0; }
.sx-copy-row > pre.code { flex: 1 1 auto; min-width: 0; margin: 0; }
.sx-copy { flex: none; width: 32px; height: 32px; }
.sx-copy-row.sx-call { align-items: center; margin: 8px 0; }
.sx-copy-row.sx-call .sx-url { flex: 1 1 auto; }
/* a curl line has no spaces where it matters, so it breaks anywhere */
.sx-curl { word-break: break-all; }
/* the answer fields: a narrower name column than the default .kv */
.sx-fields { grid-template-columns: 110px 1fr; margin: 0 0 8px; }
.sx-note.is-error, .sx-row-note.is-error { color: var(--danger); }
.sx-empty { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; margin-bottom: 8px; }
.sx-empty p { margin: 0; }
.sx-catalog { display: flex; flex-direction: column; gap: 14px; }
.sx-catalog > * { flex: none; }
/* inside the scrolling list a tooltip spans its facts instead of reaching
   past the list's edge, where the scroll box would clip it */
.sx-facts { position: relative; }
.sx-facts .bk-info { position: static; }
.sx-facts .bk-info::after { left: 0; right: 0; top: calc(100% + 6px); width: auto; max-width: none; }
/* the try fold closes the block, so the bottom padding matches the top */
.sx-config { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel); padding: 12px 14px; min-width: 0; }
.sx-config-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; margin-bottom: 8px; }
.sx-config-name { font-size: 15px; overflow-wrap: anywhere; }
.sx-config-head code { overflow-wrap: anywhere; }
.sx-facts { display: flex; flex-wrap: wrap; gap: 2px 16px; margin-top: 4px; color: var(--text-2); }
.sx-fact { min-width: 0; overflow-wrap: anywhere; }
.sx-model { overflow-wrap: anywhere; }
.sx-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.sx-config > .sx-actions { margin: 4px 0 8px; }
/* a button and a link side by side: one height, like a.ghost.small */
.sx-actions > button, .sx-actions > .dropdown > button { padding: 6px 10px; font-size: 13px; }
.sx-row-note { flex-basis: 100%; margin: 0; }
.sx-config > .sx-row-note { margin-bottom: 6px; }
/* Ausprobieren: a fold under a hairline, closed until the operator opens it */
.sx-try { border-top: 1px solid var(--border); margin-top: 6px; padding-top: 8px; min-width: 0; }
.sx-try > summary { cursor: pointer; font-size: 13.5px; font-weight: 500; color: var(--text-2); }
.sx-try[open] > summary { margin-bottom: 10px; }
.sx-try textarea { width: 100%; min-height: 72px; margin-top: 10px; }
.sx-try-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-top: 8px; }
.sx-try-actions .sx-row-note { flex: 1 1 200px; }
.sx-try-result { margin-top: 12px; min-width: 0; }
.sx-try-facts { margin: 0 0 10px; }
.sx-try-facts dd { overflow-wrap: anywhere; }
.sx-try-checks { margin: 0; padding-left: 18px; }
.sx-try-json-label { margin: 0 0 6px; }
.sx-try-result pre.code { margin: 0; max-height: 360px; overflow: auto; }

/* Übersicht model card (index.html, guide.js): the default provider's models
   grouped by role, then one collapsible per external provider and one for the
   Alex models, each asked only on its first expand (Alex costs an SSH call).
   A collapsible is a box in a box, so it stays on the bright --panel and is
   delimited by its border. A model id has no spaces to wrap at, hence
   overflow-wrap. */
.guide-source-head { margin: 4px 0 6px; }
.guide-model-note { overflow-wrap: anywhere; }
.guide-model-group + .guide-model-group { margin-top: 10px; }
.guide-model-group-title { font-size: 12.5px; font-weight: 600; color: var(--muted); }
.guide-details {
  margin-top: 16px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel);
}
.guide-details > summary { cursor: pointer; font-size: 13.5px; font-weight: 500; color: var(--text-2); }
.guide-details > summary .muted { margin-left: 4px; font-weight: 400; }
.guide-details[open] > summary { margin-bottom: 6px; }

/* Übersicht layout (index.html): two independent columns, left the live state
   of this installation, right the guide. Each column stacks its own cards, so
   a tall card on one side leaves no hole on the other (a .grid-2 row would
   stretch to its tallest cell). One column below 1000px. */
.ov-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; align-items: start; }
.ov-col { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
@media (max-width: 1000px) { .ov-cols { grid-template-columns: minmax(0, 1fr); } }
/* Aktuelle Einstellungen: one row per area, the name left, the value and the
   links to where it is changed right; a hairline between rows. The value takes
   the room and the links wrap under it before the value gets squeezed. */
.ov-settings { margin: 12px 0 0; display: flex; flex-direction: column; }
.ov-set-row { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 4px 14px; padding: 10px 0; border-top: 1px solid var(--border); font-size: 13.5px; }
.ov-set-row:first-child { border-top: none; padding-top: 0; }
.ov-set-row dt { color: var(--muted); font-weight: 500; }
.ov-set-row dd { margin: 0; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px 12px; min-width: 0; }
.ov-set-value { flex: 1 1 220px; min-width: 0; overflow-wrap: anywhere; }
.ov-set-line { display: block; }
.ov-set-line + .ov-set-line { margin-top: 2px; }
.ov-set-link { flex: none; white-space: nowrap; font-size: 13px; }
@media (max-width: 560px) { .ov-set-row { grid-template-columns: minmax(0, 1fr); } }
/* Modelle dieser Installation: one block per source */
.ov-source + .ov-source { margin-top: 18px; }
.ov-ext-list .guide-details { margin-top: 8px; }
.ov-model-foot { margin: 14px 0 0; }
/* a model chip is a link when its id is a Hugging Face repo; the default model
   carries the star and a heavier weight */
a.staged-chip { text-decoration: none; }
a.staged-chip:hover { background: var(--accent); color: #fff; }
.staged-chip.is-default { font-weight: 600; }
/* the project line closes the Übersicht (there is no page footer any more) */
.ov-project { margin: 28px 0 0; text-align: center; }

/* Auswahl & Export box (analyze.html, baukasten.js): the Exportieren menu sits
   at the right end of the configuration bar, so it opens towards the left and
   stays inside the section box; once the bar wraps on a narrow screen the
   button may start a line, and the menu opens to the right again. The status
   line under the bar (#bk-export-note) turns --danger only for a real error: a
   controller that cannot be reached (the configurations wait in this browser
   for the next sync) is a plain note. */
.bk-config-bar .dropdown-menu { left: auto; right: 0; }
@media (max-width: 720px) {
  .bk-config-bar .dropdown-menu { left: 0; right: auto; }
}
#bk-export-note.bad { color: var(--danger); }

/* The Wissenspool's status line (NC.poolBar in common.js), under Auswahl &
   Export on Konfiguration and in the Katalog on Schnittstelle. */
.pool-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
  margin: 10px 0 0; font-size: 13px; color: var(--muted);
}
.pool-bar > .material-symbols-outlined { font-size: 18px; }
.pool-bar-text { flex: 1 1 16rem; min-width: 0; }
.pool-bar.bad { color: var(--danger); }

/* Playground (inference.html): the page head is a band over the full-bleed
   chat app, so the fluid page stacks as a column instead of the row the chat
   app alone used; the chat app then takes the rest of the viewport height
   (flex, not height: 100%, which would add the band's height on top). */
.page.page-fluid.playground-page { flex-direction: column; }
.playground-head {
  margin: 0; padding: 18px 32px 14px;
  background: var(--panel); border-bottom: 1px solid var(--border);
}
.playground-page > .chat-app { flex: 1; min-height: 0; height: auto; }
@media (max-width: 720px) {
  .playground-head { padding: 16px 16px 12px; }
}
/* a page title with its info symbol beside it: the explanation sits behind
   the symbol instead of as a paragraph under the heading */
.page-title { display: flex; align-items: center; gap: 8px; }
.page-title h1 { margin: 0; }

/* ---------------- Infrastruktur page (connection.html) ---------------- */
/* LLM gateway card (#llmaas): facts, key/error banners and the models by role. */
.llmaas-facts { margin: 12px 0 0; }
.llmaas-facts dd { overflow-wrap: anywhere; }
.llmaas-card .banner { margin: 14px 0 0; }
.llmaas-models-head { margin: 18px 0 8px; }
.llmaas-models { display: flex; flex-direction: column; gap: 12px; }
.llmaas-models > p { margin: 0; }
.llmaas-role-head { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--text-2); }
/* Gateway ids are long (vendor/model-size-variant): give the chip the row. */
.llmaas-models .staged-chip { max-width: 100%; }
.llmaas-models .staged-chip-text { max-width: 320px; }
.llmaas-note { display: flex; align-items: center; gap: 6px; margin: 16px 0 0; }
.llmaas-note .material-symbols-outlined { font-size: 16px; }

/* One tile for every model provider (#backends): the gateway and the external
   providers side by side as framed entries, the optional Alex part folded
   away under them. Everything inside stays on the bright surface and is
   delimited by its border alone (a box in a box), so the nested Alex cards
   drop their shadow too. The Controller-API card stands on its own below. */
.backends-card > h2 { margin-bottom: 6px; }
.backends-grid { margin-top: 16px; }
.backend-entry {
  min-width: 0;
  padding: 18px 20px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.backend-entry h3 { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 0; }
.backend-entry h3 .material-symbols-outlined,
.alex-section > summary h3 .material-symbols-outlined { color: var(--muted); }
/* a sub-box title, not a headline: muted at 600 */
.backend-sub { font-size: 13px; font-weight: 600; color: var(--muted); margin: 18px 0 8px; }
.backends-card .alex-section { margin-top: 22px; }
.backends-card .alex-section > summary,
.backends-card .card { box-shadow: none; }
.controller-card { margin-top: 24px; }

/* The optional Alex part is one <details>: its summary is the whole bar, a
   card-like button (native <summary>, so Enter/Space and focus come for free)
   with our own triangle, because the default marker differs per browser. The
   triangle spans the three lines (title, description, the SSH target); the
   spacing between them is a margin instead of row-gap, so the target line,
   hidden until connection.js has filled it, leaves no gap behind. */
.alex-section { margin-top: 32px; }
.alex-section > summary {
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 12px; row-gap: 0;
  align-items: center;
  padding: 16px 20px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  list-style: none;
}
.alex-section > summary::-webkit-details-marker { display: none; }
.alex-section > summary::before {
  content: ""; grid-row: 1 / span 3;
  width: 0; height: 0;
  border-style: solid; border-width: 6px 0 6px 9px;
  border-color: transparent transparent transparent var(--muted);
  transition: transform 0.15s ease;
}
.alex-section[open] > summary::before { transform: rotate(90deg); }
.alex-section > summary > * { grid-column: 2; }
.alex-section > summary > * + * { margin-top: 4px; }
.alex-target { overflow-wrap: anywhere; }
.alex-section > summary h3 { margin: 0; display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.alex-section > summary:hover { border-color: var(--border-strong); }
.alex-section > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.alex-body { margin-top: 22px; }

/* responsive tweaks */
@media (max-width: 720px) {
  .page { padding: 20px 16px 40px; }
  .grid-2, .ov-cols, .ov-col, .sx-cols { gap: 16px; }
}

/* A JSON textarea with its colours behind it: the highlighted copy fills the
   box, the textarea sits on top with transparent text and a visible caret.
   Both need identical metrics (font, padding, border, wrapping) or the two
   layers drift apart line by line - which is why this block sits last in the
   file, after the page rules that style a textarea by their own class. */
.code-edit { position: relative; margin-top: 8px; }
.code-edit > textarea, .code-edit > .code-edit-hl {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; line-height: 1.5; letter-spacing: normal;
  padding: 10px 12px; border: 1px solid transparent; border-radius: var(--radius-sm);
  white-space: pre-wrap; overflow-wrap: break-word; word-break: break-word;
  margin: 0; tab-size: 2;
  /* Both layers reserve the scrollbar column, always. Without it the textarea
     loses ~15px to its scrollbar as soon as the schema is long enough while
     the copy behind it keeps the full width, and every wrapped line lands one
     row apart from its colours. */
  scrollbar-gutter: stable;
}
.code-edit > .code-edit-hl {
  position: absolute; inset: 0; overflow: hidden; pointer-events: none;
  border-color: var(--border); background: var(--panel); color: var(--text);
}
.code-edit > textarea {
  position: relative; display: block; width: 100%;
  background: transparent; color: transparent; caret-color: var(--text);
  resize: vertical;
}
/* the disabled look belongs to the layer that is actually visible; the
   textarea on top has to stay transparent whatever a page rule says */
.config-block.disabled .code-edit > textarea { background: transparent; }
/* translucent, or the selection would paint over the coloured copy below */
.code-edit > textarea::selection { background: rgba(99, 102, 241, 0.28); color: transparent; }
.code-edit > textarea:focus { box-shadow: none; }
.code-edit:focus-within > .code-edit-hl {
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
/* The same pair for a prompt instead of a schema (the two prompts of a
   Baustein): the dark surface a prompt is shown on everywhere, with its
   ${variables} coloured. The textarea on top has to stay fully transparent,
   whatever .bk-ndv says about a prompt textarea - hence these rules, which
   sit after it. */
.code-edit.var-edit > textarea, .code-edit.var-edit > .code-edit-hl {
  font-size: 12.5px; min-height: 190px;
}
.code-edit.var-edit > .code-edit-hl {
  background: #0f172a; color: #e2e8f0; border-color: #1e293b;
  --json-var: #fca5a5;
}
[data-theme="dark"] .code-edit.var-edit > .code-edit-hl { background: #020617; }
.code-edit.var-edit > textarea {
  background: transparent; color: transparent; border-color: transparent;
  caret-color: #e2e8f0;
}
.code-edit.var-edit > textarea::placeholder { color: #94a3b8; }
.code-edit.var-edit > textarea::selection {
  background: rgba(148, 163, 184, 0.35); color: transparent;
}
/* a field from the input column is dropped straight into the text: the frame
   that says so is the copy underneath, which is the layer with the border */
.code-edit.var-edit.drag-over > .code-edit-hl {
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
