/* Темы интерфейса (2026-09-29): цвета — переменные по роли (фон --bg-*, текст --fg-*,
   рамка --bd-*, тень --sh-*); значения светлой темы — прежние цвета. Тёмная тема
   (html[data-theme="dark"]) переопределяет их ниже в этом файле, как в VS Code Dark+. */
:root {
  --bd-234a85: #234a85;
  --bd-255_255_255_0p35: rgba(255, 255, 255, 0.35);
  --bd-255_255_255_0p4: rgba(255, 255, 255, 0.4);
  --bd-2b579a: #2b579a;
  --bd-2fa84f: #2fa84f;
  --bd-9fb6d6: #9fb6d6;
  --bd-aeb8c7: #aeb8c7;
  --bd-c7cedb: #c7cedb;
  --bd-cfd6e0: #cfd6e0;
  --bd-cfe0f2: #cfe0f2;
  --bd-d13b3b: #d13b3b;
  --bd-d6dbe4: #d6dbe4;
  --bd-d8cfa8: #d8cfa8;
  --bd-d8dee8: #d8dee8;
  --bd-dde3ea: #dde3ea;
  --bd-dfe3ea: #dfe3ea;
  --bd-e3b464: #e3b464;
  --bd-e3e7ec: #e3e7ec;
  --bd-e3e8ee: #e3e8ee;
  --bd-e3e8ef: #e3e8ef;
  --bd-eddca0: #eddca0;
  --bd-eeeeee: #eee;
  --bd-eef1f4: #eef1f4;
  --bd-ffffff: #fff;
  --bg-0_0_0_0p06: rgba(0, 0, 0, 0.06);
  --bg-0_0_0_0p85: rgba(0, 0, 0, 0.85);
  --bg-20_26_36_0p45: rgba(20, 26, 36, 0.45);
  --bg-234a85: #234a85;
  --bg-255_255_255_0p15: rgba(255, 255, 255, 0.15);
  --bg-255_255_255_0p3: rgba(255, 255, 255, 0.3);
  --bg-255_255_255_0p5: rgba(255, 255, 255, 0.5);
  --bg-255_255_255_0p6: rgba(255, 255, 255, 0.6);
  --bg-255_255_255_0p85: rgba(255, 255, 255, 0.85);
  --bg-2b579a: #2b579a;
  --bg-2fa84f: #2fa84f;
  --bg-43_108_176_0p35: rgba(43, 108, 176, 0.35);
  --bg-6fd88a: #6fd88a;
  --bg-8b96a5: #8b96a5;
  --bg-9aa7b8: #9aa7b8;
  --bg-c2d9f0: #c2d9f0;
  --bg-c7cedb: #c7cedb;
  --bg-c9daf3: #c9daf3;
  --bg-cfd6e0: #cfd6e0;
  --bg-cfe0f5: #cfe0f5;
  --bg-d13b3b: #d13b3b;
  --bg-d3392e: #d3392e;
  --bg-d5dbe4: #d5dbe4;
  --bg-d6e2f5: #d6e2f5;
  --bg-d9e8fb: #d9e8fb;
  --bg-dce8f7: #dce8f7;
  --bg-dde3ea: #dde3ea;
  --bg-dde7f7: #dde7f7;
  --bg-dde8fb: #dde8fb;
  --bg-dfe4ea: #dfe4ea;
  --bg-e0a52c: #e0a52c;
  --bg-e0e7f2: #e0e7f2;
  --bg-e2e6ee: #e2e6ee;
  --bg-e4e8ee: #e4e8ee;
  --bg-e58a8a: #e58a8a;
  --bg-e5e9ee: #e5e9ee;
  --bg-e74c3c: #e74c3c;
  --bg-e7ecf3: #e7ecf3;
  --bg-e8ecf2: #e8ecf2;
  --bg-e8f0fe: #e8f0fe;
  --bg-eef1f4: #eef1f4;
  --bg-eef3fa: #eef3fa;
  --bg-eef3fb: #eef3fb;
  --bg-eef4fb: #eef4fb;
  --bg-f0f0f0: #f0f0f0;
  --bg-f2f4f7: #f2f4f7;
  --bg-f2f5fa: #f2f5fa;
  --bg-f2f6fc: #f2f6fc;
  --bg-f4f6f9: #f4f6f9;
  --bg-f5c563: #f5c563;
  --bg-f5f7fa: #f5f7fa;
  --bg-f7f9fb: #f7f9fb;
  --bg-f9fafb: #f9fafb;
  --bg-fafbfc: #fafbfc;
  --bg-fbe0b0: #fbe0b0;
  --bg-fbfbfc: #fbfbfc;
  --bg-fdeacb: #fdeacb;
  --bg-fdecc8: #fdecc8;
  --bg-ffc83d: #ffc83d;
  --bg-fff4c7: #fff4c7;
  --bg-fff6dd: #fff6dd;
  --bg-ffffff: #fff;
  --fg-0e7c86: #0e7c86;
  --fg-111111: #111;
  --fg-15803d: #15803d;
  --fg-17325c: #17325c;
  --fg-1a7a3c: #1a7a3c;
  --fg-1a8a5f: #1a8a5f;
  --fg-1f8a5b: #1f8a5b;
  --fg-222222: #222;
  --fg-234a85: #234a85;
  --fg-2a2a2a: #2a2a2a;
  --fg-2b2b2b: #2b2b2b;
  --fg-2b579a: #2b579a;
  --fg-2ecc71: #2ecc71;
  --fg-333333: #333;
  --fg-3a4452: #3a4452;
  --fg-3a6bc7: #3a6bc7;
  --fg-444444: #444;
  --fg-444455: #445;
  --fg-555555: #555;
  --fg-555566: #556;
  --fg-5a6472: #5a6472;
  --fg-5b7bab: #5b7bab;
  --fg-666666: #666;
  --fg-666677: #667;
  --fg-6b6250: #6b6250;
  --fg-6b7684: #6b7684;
  --fg-6c86b3: #6c86b3;
  --fg-767676: #767676;
  --fg-777777: #777;
  --fg-7a4fb5: #7a4fb5;
  --fg-7a5c00: #7a5c00;
  --fg-7a8494: #7a8494;
  --fg-7a8699: #7a8699;
  --fg-888888: #888;
  --fg-8993a4: #8993a4;
  --fg-8a5a10: #8a5a10;
  --fg-8a6d1a: #8a6d1a;
  --fg-8a7f57: #8a7f57;
  --fg-8a8a8a: #8a8a8a;
  --fg-8a939e: #8a939e;
  --fg-8a93a3: #8a93a3;
  --fg-8a93a6: #8a93a6;
  --fg-8b95a5: #8b95a5;
  --fg-999999: #999;
  --fg-9aa3b1: #9aa3b1;
  --fg-9aa3b2: #9aa3b2;
  --fg-a626a4: #a626a4;
  --fg-a9a9a9: #a9a9a9;
  --fg-aa3333: #a33;
  --fg-aab2bd: #aab2bd;
  --fg-aeb8c7: #aeb8c7;
  --fg-b5651d: #b5651d;
  --fg-b97a00: #b97a00;
  --fg-c0392b: #c0392b;
  --fg-c45c26: #c45c26;
  --fg-c62828: #c62828;
  --fg-cccccc: #ccc;
  --fg-d18b1f: #d18b1f;
  --fg-d68a00: #d68a00;
  --fg-e0a800: #e0a800;
  --fg-e74c3c: #e74c3c;
  --fg-fbfbfc: #fbfbfc;
  --fg-ffffff: #fff;
  --sh-0_0_0_0p15: rgba(0, 0, 0, 0.15);
  --sh-0_0_0_0p18: rgba(0, 0, 0, 0.18);
  --sh-0_0_0_0p25: rgba(0, 0, 0, 0.25);
  --sh-0_0_0_0p5: rgba(0, 0, 0, 0.5);
  --sh-111_216_138_0p35: rgba(111, 216, 138, 0.35);
  --sh-209_59_59_0p2: rgba(209, 59, 59, 0.2);
  --sh-20_30_50_0p15: rgba(20, 30, 50, 0.15);
  --sh-20_40_80_p14: rgba(20, 40, 80, .14);
  --sh-224_165_44_0p25: rgba(224, 165, 44, 0.25);
  --sh-229_138_138_0p35: rgba(229, 138, 138, 0.35);
  --sh-245_197_99_0p4: rgba(245, 197, 99, 0.4);
  --sh-43_87_154_0p15: rgba(43, 87, 154, 0.15);
  --sh-47_168_79_0p2: rgba(47, 168, 79, 0.2);
  --sh-5b8dc9: #5b8dc9;
  --sh-e0b400: #e0b400;
  --sh-e67e22: #e67e22;
  --sh-f0b429: #f0b429;
}

html, body { height: 100%; margin: 0; }
body {
  font-family: -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  font-size: 14px;
  color: var(--fg-222222);
  background: var(--bg-eef1f4);
}

.ws-shell { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
/* Рабочий стол — на всё окно: у самой страницы полос прокрутки не бывает (прокручиваются области внутри). */
body:has(> .ws-shell) { overflow: hidden; }

/* ── Верхняя панель ── */
.ws-topbar {
  height: 48px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px;
  background: var(--bg-2b579a);
  color: var(--fg-ffffff);
}
.ws-topbar-title { font-weight: 600; font-size: 15px; }
/* Знак BIAS без подложки (2026-10-07): светлые штрихи = цвет текста шапки (тема), выделенная строка — фирменный оранжевый. */
.ws-topbar-title { display: inline-flex; align-items: center; gap: 7px; }
.ws-topbar-logo { width: 18px; height: 20px; flex: none; }
/* Переключатель темы: в светлой — луна (включить тёмную), в тёмной — солнце. */
.ws-theme-icon-sun { display: none; }
html[data-theme="dark"] .ws-theme-icon-sun { display: inline-block; }
html[data-theme="dark"] .ws-theme-icon-moon { display: none; }
/* Индикатор "идёт процесс" (2026-09-29) — в шапке, вне форм: бегущая полоса
   по нижнему краю шапки + крутящийся значок у заголовка. Анимируются только
   transform/opacity — это делает компоновщик браузера, поэтому анимация идёт
   и пока JS занят синхронным RunClientAction (WASM). Появляется с задержкой
   150 мс (animation-delay) — быстрые действия не мигают. */
.ws-topbar { position: relative; }
.ws-topbar-left { display: flex; align-items: center; gap: 10px; }
.ws-busy-spinner {
  width: 16px; height: 16px; flex: none;
  border: 2px solid var(--bd-255_255_255_0p35);
  border-top-color: var(--bd-ffffff);
  border-radius: 50%;
  opacity: 0;
  visibility: hidden;
}
.ws-busy-bar {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 3px; overflow: hidden;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.ws-busy-bar::before {
  content: "";
  position: absolute; top: 0; bottom: 0; left: 0;
  width: 35%;
  background: var(--bg-ffc83d);
  transform: translateX(-100%);
}
body.ws-busy .ws-busy-spinner {
  visibility: visible;
  animation: ws-busy-fade 0.2s 0.15s forwards, ws-busy-spin 0.8s linear infinite;
}
body.ws-busy .ws-busy-bar {
  visibility: visible;
  animation: ws-busy-fade 0.2s 0.15s forwards;
}
body.ws-busy .ws-busy-bar::before { animation: ws-busy-slide 1.1s ease-in-out infinite; }
body.ws-busy, body.ws-busy * { cursor: progress !important; }
@keyframes ws-busy-fade { to { opacity: 1; } }
@keyframes ws-busy-spin { to { transform: rotate(360deg); } }
@keyframes ws-busy-slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(290%); }
}
/* Без явного flex тут кнопки выравниваются по vertical-align: baseline
   (умолчание у inline-block) — а базовая линия у текстового глифа (⏻),
   эмодзи (🔔) и вложенного <span> (кружочек About) не совпадает, отсюда
   "разброс" по высоте. Flex + align-items центрирует все три по общей линии
   независимо от содержимого. */
.ws-topbar-actions { display: flex; align-items: center; gap: 2px; }
/* Текущий пользователь в шапке (2026-10-02): кружок с буквой + имя; узкий экран — только кружок. */
.ws-user-chip {
  display: inline-flex; align-items: center; gap: 8px; height: 32px; max-width: 240px;
  margin-right: 8px; padding: 0 12px 0 4px; border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 16px; background: rgba(255, 255, 255, .10); color: #fff; cursor: pointer;
  font: inherit; font-size: 13px;
}
.ws-user-chip:hover { background: rgba(255, 255, 255, .20); }
.ws-user-avatar {
  flex: none; width: 24px; height: 24px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-size: 12px; font-weight: 700; color: #fff;
  background: linear-gradient(135deg, #60a5fa, #2563eb); box-shadow: 0 0 0 1.5px rgba(255, 255, 255, .6);
}
.ws-user-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 760px) { .ws-user-name { display: none; } .ws-user-chip { padding-right: 4px; } }
/* Область подсветки hover/focus считается от размера контента — а он у трёх
   кнопок РАЗНЫЙ (текстовый глиф со своим line-height, SVG с фиксированной
   высотой, вложенный span-кружочек) — отсюда разная высота самой кнопки при
   выделении. Фиксируем кнопку квадратом и центрируем контент внутри — размер
   кнопки больше не зависит от того, что внутри. */
.ws-topbar-actions .ws-icon-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ws-icon-btn {
  background: transparent;
  border: none;
  color: var(--fg-ffffff);
  font-size: 18px;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 4px;
  text-decoration: none;
  display: inline-block;
}
.ws-icon-btn:hover { background: var(--bg-255_255_255_0p15); }

/* About — кружочек с "!" (версии Движка/Host/Workspace, см. Versioning.cs),
   клик кладёт три строки в табло сообщений (см. WS.showMessage). */
.ws-about-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}

/* Переключатель языка интерфейса (см. t()/applyTranslations в Shell.html) —
   тот же "пустой" монохромный стиль топбара, что у остальных кнопок. */
.ws-lang-select {
  background: transparent;
  color: var(--fg-ffffff);
  border: 1px solid var(--bd-255_255_255_0p4);
  border-radius: 4px;
  font-size: 12px;
  padding: 3px 4px;
  cursor: pointer;
  margin-right: 4px;
}
.ws-lang-select option { color: var(--fg-222222); }

/* Колокольчик (переключатель правой панели) — контур, не заливка (SVG
   stroke="currentColor", по аналогии с кружочком About — тот же "пустой"
   монохромный стиль, не цветной эмодзи-глиф). Точка в углу — если в чате
   есть непрочитанное (см. chat-target/loadSidebar в Shell.html, второй
   <script>). */
.ws-bell-btn { position: relative; }
.ws-bell-icon { display: block; }
.ws-bell-dot {
  position: absolute;
  top: 2px;
  right: 3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--bg-e74c3c);
  border: 1px solid var(--bd-2b579a);
}

/* ── Полоса вкладок ── */
/* Внизу над строкой состояния, ярлыки прямоугольные (2026-10-07). */
.ws-tabbar-row { height: 32px; flex: none; display: flex; background: var(--bg-eef1f4); border-top: 1px solid var(--bd-c7cedb); }
/* "Главная" стоит здесь, ДО кнопки прокрутки влево — вне прокручиваемой
   .ws-tabbar, не участвует в скролле/переполнении (см. renderTabbar в
   Shell.html — рендерит её в этот контейнер отдельно от остальных вкладок). */
.ws-tabbar-home { flex: none; display: flex; align-items: stretch; }
/* Ярлыки стоят вплотную, между ними — вертикальная черта (border-right у .ws-tab). */
.ws-tabbar {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: stretch;
  overflow-x: hidden;
  scroll-behavior: smooth;
}
/* Кнопки прокрутки — видны только когда вкладки не помещаются целиком (см.
   updateTabbarNavButtons в Shell.html), обычный горизонтальный скролл
   (колёсиком/полосой) убран сознательно — только этими кнопками. */
.ws-tabbar-nav {
  flex: none;
  width: 22px;
  border: none;
  background: transparent;
  color: var(--fg-555555);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
}
.ws-tabbar-nav:hover { background: var(--bg-0_0_0_0p06); }
/* Стрелка прокрутки вкладок не шире своей кнопки (2026-10-07: svg торчал на 2px за край окна — у страницы
   появлялись полосы прокрутки, когда вкладки не помещаются). */
.ws-tabbar-nav { padding: 0; overflow: hidden; display: inline-flex; align-items: center; justify-content: center; }
.ws-tabbar-nav .ws-icon { width: 16px; height: 16px; }
/* Пиктограмма на ярлыке закладки (wwwroot/icons, 2026-09-30). */
.ws-tab-icon { width: 16px; height: 16px; flex: none; }
.ws-tab-home-icon { display: inline-flex; color: var(--fg-555555); }
.ws-tab {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 14px;
  background: transparent;
  border-right: 1px solid var(--bd-c7cedb);
  cursor: pointer;
  color: var(--fg-555555);
  font-size: 13px;
  white-space: nowrap;
  /* user-select: none — клик по вкладке (особенно быстрый двойной клик, или
     чуть смещённый drag) иначе выделяет текст заголовка как обычный текст —
     виден мигающий текстовый курсор/выделение поверх UI-хрома, который
     должен вести себя как кнопка, не как текстовое поле (тот же приём, что
     уже у .ws-grid-cell-sortable — сортируемый заголовок таблицы). */
  user-select: none;
}
/* Активная — белая, с зелёной чертой снизу. */
.ws-tab.active { background: var(--bg-ffffff); color: var(--fg-111111); box-shadow: inset 0 -2px 0 var(--bd-2fa84f); }
/* "Главная" — ширина как у левой панели (.ws-nav) — конкретное значение
   проставляется инлайн из JS (см. renderTabbar/syncHomeTabWidth в Shell.html),
   т.к. nav можно тянуть сплиттером; тут только сброс nowrap-поведения обычных
   вкладок, чтобы инлайн-width не спорил с ним. */
.ws-tab-home { box-sizing: border-box; flex: none; }
.ws-tab-close {
  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 2px;
}
.ws-tab-close:hover { color: var(--fg-c0392b); }
/* Изменённая форма (2026-10-04): маленький кружок на месте крестика; наведение — крестик. */
.ws-tab-close { min-width: 14px; text-align: center; }
.ws-tab-close.is-dirty { font-size: 0; }
.ws-tab-close.is-dirty::before { content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--fg-2b579a); vertical-align: middle; }
.ws-tab-close.is-dirty:hover { font-size: 14px; }
.ws-tab-close.is-dirty:hover::before { display: none; }
/* Неактивные закладки (2026-10-04) — текст и пиктограмма приглушены, при наведении ярче. */
/* --fg-888888, не --fg-8a939e: в тёмной теме та переопределена зелёной. */
.ws-tab:not(.active) { color: var(--fg-888888); }
.ws-tab:not(.active) .ws-tab-icon { opacity: .5; filter: saturate(.5); }
.ws-tab:not(.active):hover { color: var(--fg-555555); }
.ws-tab:not(.active):hover .ws-tab-icon { opacity: .85; filter: none; }

/* ── Основная область: левое меню / контент / правая панель ── */
.ws-body { flex: 1; display: flex; min-height: 0; }

.ws-nav {
  width: 220px;
  flex: none;
  background: var(--bg-ffffff);
  border-right: 1px solid var(--bd-dde3ea);
  padding: 8px 0;
  overflow-y: auto;
}
.ws-nav-item {
  display: block;
  width: 100%;
  text-align: left;
  /* Отступ БОЛЬШЕ, чем у .ws-nav-section (16px) — команда выглядит вложенной
     ПОД заголовком раздела, а не вровень с ним. Вертикальный отступ — 5px
     (было 9px, запрос пользователя 2026-07-30: "сделай плотнее строки,
     интервал между строками" про левую панель). */
  padding: 5px 16px 5px 28px;
  background: none;
  border: none;
  font-size: 13.5px;
  color: var(--fg-333333);
  cursor: pointer;
}
.ws-nav-item:hover { background: var(--bg-eef1f4); }
/* Подсветка на время, пока открыто контекстное меню (см. showContextMenuAt в
   Shell.html) — без нужно, чтобы курсор оставался на кнопке, чтобы было
   видно, к чему относится меню, даже когда курсор уже увели к пункту меню. */
.ws-nav-item.ws-context-active { background: var(--bg-dce8f7); }
.ws-nav-section {
  padding: 12px 16px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--fg-999999);
}
.ws-nav-section:first-child { padding-top: 4px; }

/* Маленькие пиктограммы перед пунктами меню — по data-cmd (см. Shell.html,
   {{П}}-цикл) — атрибут ВСЕГДА на русском, независимо от текущего языка
   интерфейса (только видимый текст переводится, см. applyStaticTranslations),
   поэтому селектор по-русски безопасен при любой локали. Список пунктов
   фиксированный (см. Program.cs adminSection + "РазделКоманд: Конфигурация ="
   в Bias.Host.config) — новый пункт меню просто не получит иконку, не
   ошибка, ничего не сломается. */
/* Ширина = "+/−" (1.3em) + .ws-tree-icon (1.6em) у "Подсистемы" ниже — те
   пункты, у которых нет своей стрелки, всё равно занимают то же место под
   неё, поэтому иконки везде в панели визуально выравниваются по одной
   вертикали (запрос пользователя — "такую симметрию по всем разделам"). */
/* SVG data-URI, не эмодзи (запрос пользователя 2026-07-30: "надо все картинки
   пиктограмм передавать" + "эту картинку не перенёс" про кнопку "Выйти") —
   эмодзи-глиф в ::before рендерится ШРИФТОМ КЛИЕНТА, тот же класс проблем, что
   и у остальных иконок в этом файле (см. renderIcon/ICON_PATHS в Shell.html)
   — здесь JS не поможет (::before читает CSS, не DOM), поэтому свой SVG прямо
   в background-image, контуры — те же Feather Icons. "Сервисы" убран —
   вкладки такой в Админке больше нет (см. doc-комментарий HandleDesktopServicesData
   в Program.cs), правило било в несуществующий data-cmd. */
.ws-nav-item[data-cmd]::before {
  display: inline-block; width: 2.9em; height: 16px; text-align: center;
  background-repeat: no-repeat; background-position: center; background-size: 16px 16px;
  vertical-align: -3px;
}
/* Цветные плитки Админки (запрос пользователя, 2026-10-02: "красивые пиктограммки
   в админке цветные") — те же плитки, что wwwroot/icons у разделов рабочего стола
   (/icons/<Имя>.svg). Плитка 22px прижата к правому краю слота 2.9em — там же, где
   плитка "Подсистем" после стрелки "+/−" (см. .ws-tree-icon-tile), все на одной
   вертикали. Конструкторы (data-icon, графитовые "системные" плитки) — так же. */
.ws-nav-item[data-cmd="Роли"]::before,
.ws-nav-item[data-cmd="Регламентные задания"]::before,
.ws-nav-item[data-cmd="Общие настройки"]::before,
.ws-nav-item[data-cmd="Пользователи"]::before,
.ws-nav-item[data-cmd="Активные пользователи"]::before,
.ws-nav-item[data-cmd="Настройки чата"]::before,
.ws-nav-item[data-cmd="Обновление"]::before,
.ws-nav-item[data-cmd="Структура БД"]::before,
.ws-nav-item[data-cmd="Менеджер БД"]::before,
.ws-nav-item[data-cmd="Монитор"]::before,
.ws-nav-item[data-cmd="Параметры манифеста"]::before,
.ws-nav-item[data-cmd="Глобальный поиск"]::before,
.ws-nav-item[data-cmd="Конструктор макетов"][data-icon]::before,
.ws-nav-item[data-cmd="Конструктор схем компоновки"][data-icon]::before,
.ws-nav-item[data-cmd="Новое"][data-icon]::before,
.ws-nav-item[data-cmd="Помощь"][data-icon]::before,
.ws-nav-item[data-cmd="Синтакс-помощник"][data-icon]::before {
  content: ""; width: 2.9em; height: 22px; margin: 0 8px 0 0; vertical-align: -6px;
  background-repeat: no-repeat; background-position: right center; background-size: 22px 22px;
}
/* Длинная подпись переносится вровень с текстом, не под плитку. */
.ws-nav-item[data-cmd="Роли"],
.ws-nav-item[data-cmd="Регламентные задания"],
.ws-nav-item[data-cmd="Общие настройки"],
.ws-nav-item[data-cmd="Пользователи"],
.ws-nav-item[data-cmd="Активные пользователи"],
.ws-nav-item[data-cmd="Настройки чата"],
.ws-nav-item[data-cmd="Обновление"],
.ws-nav-item[data-cmd="Структура БД"],
.ws-nav-item[data-cmd="Менеджер БД"],
.ws-nav-item[data-cmd="Монитор"],
.ws-nav-item[data-cmd="Параметры манифеста"],
.ws-nav-item[data-cmd="Глобальный поиск"],
.ws-nav-item[data-cmd="Конструктор макетов"],
.ws-nav-item[data-cmd="Конструктор схем компоновки"],
.ws-nav-item[data-cmd="Новое"][data-icon],
.ws-nav-item[data-cmd="Помощь"][data-icon],
.ws-nav-item[data-cmd="Синтакс-помощник"][data-icon] { display: flex; align-items: center; }
.ws-nav-item[data-cmd]::before { flex: none; }
/* [data-icon] ниже по файлу ставит padding-left: 14px — здесь специфичнее. */
.ws-nav-item[data-cmd="Конструктор макетов"][data-icon],
.ws-nav-item[data-cmd="Конструктор схем компоновки"][data-icon],
.ws-nav-item[data-cmd="Новое"][data-icon],
.ws-nav-item[data-cmd="Помощь"][data-icon],
.ws-nav-item[data-cmd="Синтакс-помощник"][data-icon] { padding-left: 28px; }
.ws-nav-item[data-cmd="Роли"]::before { background-image: url("/icons/%D0%A0%D0%BE%D0%BB%D0%B8.svg"); }
.ws-nav-item[data-cmd="Регламентные задания"]::before { background-image: url("/icons/%D0%A0%D0%B5%D0%B3%D0%BB%D0%B0%D0%BC%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D0%B5%D0%97%D0%B0%D0%B4%D0%B0%D0%BD%D0%B8%D1%8F.svg"); }
.ws-nav-item[data-cmd="Общие настройки"]::before { background-image: url("/icons/%D0%9D%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B8.svg"); }
.ws-nav-item[data-cmd="Пользователи"]::before,
.ws-nav-item[data-cmd="Активные пользователи"]::before { background-image: url("/icons/%D0%9F%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D0%B8.svg"); }
.ws-nav-item[data-cmd="Настройки чата"]::before { background-image: url("/icons/%D0%9D%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B8%D0%A7%D0%B0%D1%82%D0%B0.svg"); }
.ws-nav-item[data-cmd="Обновление"]::before { background-image: url("/icons/%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5.svg"); }
.ws-nav-item[data-cmd="Структура БД"]::before { background-image: url("/icons/%D0%A1%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0%D0%91%D0%94.svg"); }
.ws-nav-item[data-cmd="Монитор"]::before { background-image: url("/icons/%D0%9C%D0%BE%D0%BD%D0%B8%D1%82%D0%BE%D1%80.svg"); }
.ws-nav-item[data-cmd="Менеджер БД"]::before { background-image: url("/icons/%D0%9C%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80%D0%91%D0%94.svg"); }
.ws-nav-item[data-cmd="Параметры манифеста"]::before { background-image: url("/icons/%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B%D0%9C%D0%B0%D0%BD%D0%B8%D1%84%D0%B5%D1%81%D1%82%D0%B0.svg"); }
.ws-nav-item[data-cmd="Глобальный поиск"]::before { background-image: url("/icons/%D0%9F%D0%BE%D0%B8%D1%81%D0%BA.svg"); }
/* Плитки узлов дерева подсистем (buildTreeNode/treeNodeTile в Shell.html) — 18px,
   у подсистемы — кружок состояния в углу (зелёный/красный). */
.ws-tree-icon.ws-tree-tile { position: relative; width: 1.6em; margin-right: 6px; text-align: center; }
.ws-tree-tile img { width: 18px; height: 18px; vertical-align: -4px; }
.ws-tree-tile-status {
  position: absolute; right: 0; bottom: -2px; width: 8px; height: 8px; border-radius: 50%;
  background: currentColor; box-shadow: 0 0 0 1.5px var(--bg-ffffff);
}
/* Плитка "Подсистем" — <img> в .ws-tree-icon (wireSubsystemTree в Shell.html). */
.ws-tree-icon.ws-tree-icon-tile { width: 1.6em; margin-right: 8px; text-align: right; }
.ws-tree-icon-tile img { width: 22px; height: 22px; vertical-align: -6px; }
/* "Подсистемы" — иконка НЕ через ::before (та всегда ПЕРЕД всем содержимым,
   включая стрелку "+/−" — а нужно ПОСЛЕ неё) — вставляется JS'ом в
   wireSubsystemTree (Shell.html), см. .ws-tree-icon там же. */

/* ── Дерево "Подсистемы" в левой панели (см. Shell.html, wireSubsystemTree/
   buildTreeNode) — узлы подгружаются по требованию, не всё дерево сразу. */
.ws-tree-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.3em;
  flex: none;
  text-align: center;
  color: var(--fg-767676);
  font-size: 13px;
  font-weight: 700;
}
.ws-tree-arrow:empty { visibility: hidden; }
/* Без явного margin — тот же принцип, что у дочерних строк дерева (.ws-tree-row):
   отступ получается ЕСТЕСТВЕННО из фиксированной ширины .ws-tree-arrow/
   .ws-tree-icon, отдельный margin делал разрыв визуально шире, чем у
   дочерних узлов (запрос пользователя — "сделай симметрично"). */
.ws-tree-row {
  display: flex;
  align-items: center;
  /* 3px (было 6px, запрос пользователя 2026-07-30: "сделай плотнее строки,
     интервал между строками"). */
  padding: 3px 16px 3px 0;
  font-size: 13px;
  color: var(--fg-333333);
  cursor: pointer;
}
.ws-tree-row:hover { background: var(--bg-eef1f4); }
.ws-tree-row.ws-context-active { background: var(--bg-dce8f7); }
.ws-tree-icon { display: inline-block; width: 1.6em; text-align: center; flex: none; }
/* Статус подсистемы (Активна/Отключена, см. buildTreeNode в Shell.html) —
   НАСТОЯЩИЙ CSS-кружок, не эмодзи (🟢/🔴) — эмодзи рендерится ШРИФТОМ
   КЛИЕНТА, на разных ОС/браузерах выглядит по-разному вплоть до "не тот
   символ вообще" (найдено живьём, 2026-07-30 — на одном хосте кружка не
   было видно совсем). CSS-фигура одинакова везде, где вообще работает CSS. */
.ws-tree-icon.ws-status-dot::before {
  content: ''; display: inline-block; width: 10px; height: 10px;
  border-radius: 50%; background: currentColor; vertical-align: middle;
}
.ws-status-active { color: var(--fg-2ecc71); }
.ws-status-inactive { color: var(--fg-e74c3c); }

/* ── Единый SVG-иконки (см. renderIcon/ICON_PATHS в Shell.html, запрос
   пользователя 2026-07-30: "надо все картинки пиктограмм передавать, не
   только кружок") — размер от font-size родителя (em), цвет от color
   (currentColor) — ведёт себя как символ текста, но рендерится как
   векторная фигура, а не шрифтовый глиф. -0.15em сдвиг компенсирует
   built-in отступ SVG под базовой линией текста (иначе иконка "прыгает"
   выше строки). */
.ws-icon { width: 1em; height: 1em; vertical-align: -0.15em; display: inline-block; flex: none; }
.ws-icon-filled { fill: currentColor; }
/* Длинное имя — переносится (2026-10-03), а не обрезается многоточием. */
.ws-tree-label { min-width: 0; white-space: normal; overflow-wrap: anywhere; line-height: 1.25; }
.ws-tree-children { display: block; }
/* См. .ws-user-form-field[hidden] выше по файлу — та же гоча: класс и атрибут
   [hidden] равны по специфичности, и правило автора (.ws-tree-children)
   побеждает встроенное браузерное [hidden]{display:none} по каскаду —
   без явного переопределения ниже childrenBox.hidden=true в Shell.html
   ничего бы не скрывало (разворот работал бы, сворачивание — нет). */
.ws-tree-children[hidden] { display: none; }

/* Выделение строки (только модули-файлы в таблице "Модули:", см.
   opts.moduleActions у buildTreeNode) — своё правило, ЕДИНСТВЕННОЕ место,
   где дерево вообще поддерживает выделение (обычное дерево левой панели
   его не использует). */
.ws-tree-row.selected { background: var(--bg-dce8f7); }
/* Карандаш «Переименовать» справа от названия (папки и файлы подсистемы, 2026-10-05):
   виден при наведении на строку и у выделенной. */
.ws-tree-rename { margin-left: 6px; padding: 0 3px; border: 0; background: none; color: inherit; cursor: pointer;
  opacity: 0; line-height: 1; border-radius: 3px; flex: none; }
.ws-tree-row:hover .ws-tree-rename, .ws-tree-row.selected .ws-tree-rename { opacity: .7; }
.ws-tree-rename:hover { opacity: 1; background: var(--bg-eef1f4); }
/* Панель действий над подсистемами в дереве Админки (2026-10-03): первой строкой
   раскрытого узла "Подсистемы", отступ как у дочерних узлов, кнопки компактнее. */
.ws-nav .ws-tree-toolbar {
  /* Сплошная полоса во всю ширину панели, вплотную к её верху: -8px — верхний отступ .ws-nav
     (иначе при прокрутке строки дерева видны над панелью и по её бокам). */
  width: auto;
  margin: -8px 0 6px 0;
  padding: 4px 8px;
  gap: 0;
  flex-wrap: wrap;
  border-radius: 0;
  /* Без рамки, свой фон (чуть темнее светлой панели / светлее тёмной) — выделяется. */
  border: none;
  position: sticky;
  top: -8px;
  z-index: 2;
  background: var(--bg-eef1f4);
}
.ws-nav .ws-tree-toolbar .ws-toolbar-btn { min-width: 24px; height: 24px; }
/* Фокус вне зоны "Подсистемы" — панель тусклая и неактивная. */
.ws-tree-toolbar-inactive .ws-toolbar-btn { opacity: .35; cursor: default; pointer-events: none; }
/* Флажок модуля (запрос пользователя 2026-07-28: "к строкам добавить
   пиктограммки" + множественное удаление, тот же чекбокс, что был у
   плоского грида раньше, см. .ws-module-flag — здесь просто в дереве). */
/* .ws-tree-module-flag ОДНИМ классом не хватает специфичности, чтобы
   победить ".ws-user-form-field input:not(.bias-field)" (см. ниже в файле,
   flex:1/min-width:0 для ЛЮБОГО обычного input в поле формы) — этот флажок
   лежит внутри как раз такого поля (.ws-user-form-field-multiline "Модули:")
   — та же гоча, что уже ловили на плоском гриде, здесь наступили на неё
   ЗАНОВО (специфичность в один класс была недостаточной). Три уровня
   селектора вместо одного — гарантированно побеждает. */
/* Флажок — .bias-checkbox-input библиотеки (2026-10-03); здесь только отступ в строке дерева. */
.ws-modules-tree .ws-tree-row input.ws-tree-module-flag { flex: none; margin: 0 6px 0 0; }

/* ── Список модулей в карточке подсистемы (см. Shell.html, loadSubsystemFormTab) —
   дерево (переиспользует buildTreeNode левой панели, см. opts.moduleActions),
   не таблица — своя рамка/скругление, тот же принцип, что у .ws-grid. ── */
.ws-modules-tree { border: 1px solid var(--bd-dde3ea); border-radius: 0 0 6px 6px; overflow: auto; max-height: 320px; background: var(--bg-ffffff); }


.ws-content { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; background: var(--bg-ffffff); }

/* ── Шапка формы (назад/вперёд/избранное — заголовок — ссылка/ещё/закрыть) ── */
.ws-formheader {
  flex: none;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 2px 14px;
  box-sizing: border-box;
  min-height: 33px; /* как у двухстрочного заголовка (путь + имя) — шапка одной высоты на всех вкладках */
  border-bottom: 1px solid var(--bd-eeeeee);
}
.ws-formheader .ws-icon-btn { color: var(--fg-666666); font-size: 15px; padding: 3px 7px; line-height: 1; }
.ws-formheader .ws-icon-btn:hover { background: var(--bg-eef1f4); }
.ws-formheader .ws-icon-btn:disabled { color: var(--fg-cccccc); cursor: default; background: none; }
.ws-fh-star.active { color: var(--fg-e0a800); }
/* user-select: none — тот же случай, что и у .ws-tab (см. комментарий там):
   заголовок вкладки в шапке — не текстовое поле, клик по нему не должен
   оставлять текстовый курсор/выделение. */
.ws-fh-title { font-size: 17px; font-weight: 600; color: var(--fg-222222); margin-left: 6px; user-select: none; }
.ws-fh-spacer { flex: 1; }
/* Кнопки табличного документа в заголовке вкладки (2026-10-06). */
.ws-fh-doc { display: flex; align-items: center; gap: 2px; margin-right: 6px; }
.ws-fh-doc[hidden] { display: none; }
.ws-fh-doc .ws-fh-doc-on { background: var(--bg-e1ecfa, #e1ecfa); }

/* min-height:0 — иначе flex-item не сжимается ниже высоты своего контента
   (запрос пользователя, 2026-08-04: "Табло появилось а страница не учитывает
   табло"): без этого #ws-pages не отдаёт место Табло, таблица/страница
   продолжают занимать высоту "как будто Табло нет". Сама закладка (область
   #ws-pages) должна сжиматься; таблица лишь заполняет оставшееся. */
.ws-pages { flex: 1; min-height: 0; overflow: auto; position: relative; }
/* Закрепление вкладки (2026-10-10, Shell.html applyDockLayout): две видимые формы, между ними сплиттер библиотеки.
   Каждая часть прокручивается сама; активная (последний щелчок) отмечена полосой сверху. */
.ws-pages.ws-pages-docked { overflow: hidden; }
.ws-pages.ws-pages-docked > .ws-page.ws-page-shown { display: block; }
.ws-pages.ws-pages-docked > .ws-page.bias-split-pane { min-height: 0; min-width: 0; height: auto; overflow: auto; }
.ws-pages.ws-pages-docked > .ws-page.active { box-shadow: inset 0 2px 0 var(--bd-2fa84f); }
.ws-tab.ws-tab-docked { box-shadow: inset 3px 0 0 var(--bd-2fa84f); }
.ws-tab.ws-tab-docked.active { box-shadow: inset 3px 0 0 var(--bd-2fa84f), inset 0 -2px 0 var(--bd-2fa84f); }
.ws-tab.ws-tab-shown { background: var(--bg-ffffff); }
/* Крестовина при перетаскивании ярлыка вкладки. */
.ws-dock-overlay { position: fixed; z-index: 1000; display: flex; align-items: center; justify-content: center; background: rgba(43, 87, 154, .06); }
.ws-dock-cross { display: grid; grid-template-columns: 64px 96px 64px; grid-template-rows: 56px 64px 56px; gap: 8px; padding: 16px;
  background: var(--bg-ffffff); border: 1px solid var(--bd-dde3ea); border-radius: 10px; box-shadow: 0 8px 28px rgba(0, 0, 0, .18); }
.ws-dock-target { display: flex; align-items: center; justify-content: center; border: 1px solid var(--bd-dde3ea); border-radius: 8px;
  color: var(--fg-555555); background: var(--bg-fafbfc); font-size: 12.5px; font-weight: 600; text-align: center; line-height: 1.2; }
.ws-dock-target .ws-icon { width: 28px; height: 28px; pointer-events: none; }
.ws-dock-target.hover { border-color: var(--bd-2fa84f); background: var(--bg-eef4fb); color: var(--fg-111111); box-shadow: 0 0 0 2px var(--bd-2fa84f) inset; }
.ws-dock-top { grid-column: 2; grid-row: 1; }
.ws-dock-left { grid-column: 1; grid-row: 2; }
.ws-dock-center { grid-column: 2; grid-row: 2; }
.ws-dock-right { grid-column: 3; grid-row: 2; }
.ws-dock-bottom { grid-column: 2; grid-row: 3; }
.ws-dock-ghost { position: fixed; z-index: 1001; pointer-events: none; padding: 4px 10px; font-size: 12.5px; white-space: nowrap;
  background: var(--bg-ffffff); border: 1px solid var(--bd-dde3ea); border-radius: 6px; box-shadow: 0 4px 12px rgba(0, 0, 0, .15); }
/* min-height:100% — вкладка не короче области страниц; height:auto —
   высота по содержимому (запрос пользователя, 2026-08-09: иначе пустая
   нижняя «дыра» под короткой таблицей выглядела как растянутый грид).
   Редактор модуля — отдельно (.ws-page-module-source { height:100% }). */
.ws-page { display: none; padding: 20px; min-height: 100%; height: auto; box-sizing: border-box; }
.ws-page.active { display: block; }
.ws-page-placeholder { color: var(--fg-888888); }
/* Начальная страница интерфейса (2026-10-07): колонки форм в закладке Home. */
.ws-home-columns { display: flex; gap: 16px; align-items: flex-start; padding: 8px; min-height: 100%; box-sizing: border-box; }
.ws-home-col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.ws-home-form { border: 1px solid var(--bd-dde3ea); border-radius: 4px; padding: 8px; background: var(--bg-ffffff); overflow: auto; max-height: 70vh; }
@media (max-width: 800px) { .ws-home-columns { flex-direction: column; } }
.ws-page-placeholder h2 { margin-top: 0; color: var(--fg-333333); }

/* ── "Что нового" на Home (см. loadHomeTab/ЧтоНового) — показывается один
   раз после смены версии хоста, поэтому просто заметная, не навязчивая. ── */
.ws-whats-new { max-width: 640px; margin-bottom: 20px; padding: 12px 16px; background: var(--bg-eef4fb); border: 1px solid var(--bd-cfe0f2); border-radius: 4px; }
.ws-whats-new h3 { margin: 0 0 8px; color: var(--fg-2b579a); }
.ws-whats-new ul { margin: 0; padding-left: 20px; }
.ws-whats-new li { margin-bottom: 4px; }

/* ── Анимация ожидания перезапуска службы (карточка "Обновление", см.
   pollForRestart в Shell.html) — вращающийся кружок + счётчик секунд, ЧИСТО
   визуальные, не зависят от того, отвечает ли сейчас сервер. ── */
.ws-spinner {
  display: inline-block; width: 14px; height: 14px; margin-right: 8px;
  border: 2px solid var(--bd-cfe0f2); border-top-color: var(--bd-2b579a); border-radius: 50%;
  vertical-align: middle; animation: ws-spin 0.8s linear infinite;
}
@keyframes ws-spin { to { transform: rotate(360deg); } }
.ws-update-timer { color: var(--fg-666677); font-variant-numeric: tabular-nums; }

/* ── Помощь (см. loadHelpTab/loadHelpChatTab/... в Shell.html) ── */
.ws-help-page { max-width: 720px; line-height: 1.5; }
.ws-help-page h2 { margin-top: 0; color: var(--fg-2b579a); }
.ws-help-page h3 { color: var(--fg-333333); margin-bottom: 4px; }
.ws-help-page ul { margin-top: 4px; padding-left: 22px; }
.ws-help-page li { margin-bottom: 4px; }
.ws-help-page code {
  background: var(--bg-eef1f4);
  border-radius: 3px;
  padding: 1px 5px;
  font-size: 13px;
}
.ws-help-table { border-collapse: collapse; width: 100%; margin: 8px 0 16px; font-size: 13px; }
.ws-help-table th, .ws-help-table td {
  border: 1px solid var(--bd-dde3ea);
  padding: 5px 8px;
  text-align: left;
  vertical-align: top;
}
.ws-help-table th { background: var(--bg-f4f6f9); }
.ws-help-comment { color: var(--fg-8a93a6); font-style: italic; }
.ws-help-badge {
  display: inline-block;
  vertical-align: middle;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 10px;
  background: var(--bg-fdecc8);
  color: var(--fg-8a6d1a);
  font-size: 11px;
  font-weight: 600;
}
.ws-help-link-inline {
  border: none;
  background: none;
  padding: 0;
  color: var(--fg-2b579a);
  text-decoration: underline;
  cursor: pointer;
  font-size: inherit;
  font-family: inherit;
}
.ws-help-links { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.ws-help-link {
  text-align: left;
  padding: 10px 12px;
  border: 1px solid var(--bd-c7cedb);
  border-radius: 6px;
  background: var(--bg-f7f9fb);
  color: var(--fg-2b579a);
  font-size: 14px;
  cursor: pointer;
}
.ws-help-link:hover { background: var(--bg-eef3fa); border-color: var(--bd-9fb6d6); }
/* Пароль без менеджера паролей браузера: обычное поле, символы скрыты точками
   (Login.html, карточка пользователя — noBrowserPasswordSave в Shell.html). */
.ws-masked-input { -webkit-text-security: disc; }

/* Пользователи этого браузера (слоты сеансов /u/N/): номер входа на кнопке в шапке. */
.ws-sessions-btn { position: relative; }
.ws-sessions-slot {
  position: absolute;
  right: -2px;
  bottom: -2px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  box-sizing: border-box;
  border-radius: 7px;
  background: var(--bg-fdecc8);
  color: var(--fg-8a6d1a);
  font-size: 10px;
  font-weight: 700;
  line-height: 14px;
  text-align: center;
}
/* Разделы из bias-help.js: шире (примеры кода), блоки кода, заметки, оглавление, поиск. */
.ws-help-page.ws-help-wide { max-width: 980px; }
.ws-help-page h4 { margin: 14px 0 4px; color: var(--fg-333333); }
.ws-help-page pre.ws-help-code {
  background: var(--bg-f4f6f9);
  border: 1px solid var(--bd-dde3ea);
  border-radius: 4px;
  padding: 8px 12px;
  margin: 6px 0 12px;
  font-size: 13px;
  line-height: 1.45;
  overflow-x: auto;
  white-space: pre;
}
.ws-help-note {
  border-left: 3px solid var(--bd-9fb6d6);
  background: var(--bg-f7f9fb);
  padding: 6px 12px;
  margin: 8px 0 12px;
}
.ws-help-toc { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 14px; }
.ws-help-toc-item {
  border: 1px solid var(--bd-c7cedb);
  border-radius: 12px;
  background: var(--bg-f7f9fb);
  color: var(--fg-2b579a);
  padding: 2px 10px;
  font-size: 12px;
  cursor: pointer;
}
.ws-help-toc-item:hover { background: var(--bg-eef3fa); border-color: var(--bd-9fb6d6); }
/* Поиск по справке — .bias-field библиотеки (2026-10-03); здесь только раскладка. */
.ws-help-search { max-width: none; margin: 6px 0 4px; }
.ws-help-search-results { display: flex; flex-direction: column; gap: 4px; margin-bottom: 6px; }
.ws-help-search-hit {
  display: flex;
  flex-direction: column;
  text-align: left;
  border: 1px solid var(--bd-dde3ea);
  border-radius: 4px;
  background: var(--bg-f7f9fb);
  padding: 5px 9px;
  cursor: pointer;
  font-family: inherit;
}
.ws-help-search-hit:hover { background: var(--bg-eef3fa); border-color: var(--bd-9fb6d6); }
.ws-help-search-where { color: var(--fg-2b579a); font-weight: 600; font-size: 13px; }
.ws-help-search-snippet { color: var(--fg-666677); font-size: 12px; }
.ws-help-flash { animation: ws-help-flash 1.6s ease-out; }
@keyframes ws-help-flash { 0% { background: var(--bg-fdecc8); } 100% { background: transparent; } }

.ws-status-ok { color: var(--fg-1a7a3c); font-weight: 600; }
.ws-status-error { color: var(--fg-c62828); font-weight: 600; cursor: help; }
/* Второстепенная пометка в ячейке (слот, давность), 2026-10-04. */
.ws-status-muted { opacity: .65; font-size: .92em; }
.ws-status-pending { color: var(--fg-8a6d1a); font-weight: 600; }

/* ── Тулбар над таблицей вкладки (Пользователи/Роли/Сервисы/Модули и т.п.,
   см. createBiasGrid в bias-grid.js) — компактная полоса кнопок-
   пиктограмм со своей рамкой/фоном и подсказкой по наведению (title), а не
   раскрашенные текстовые кнопки — образец приносил пользователь (тулбар
   списка референсной платформы), но со своими цветами/иконками, не её скином (запрос
   2026-07-28: "такой тулбар, только наш стиль"). Один общий компонент —
   правка меняет вид сразу везде, где есть Bias.Grid. ── */
.ws-toolbar {
  display: flex;
  align-items: center;
  gap: 2px;
  box-sizing: border-box;
  margin-bottom: 0;
  padding: 3px;
  /* Фон РОДИТЕЛЯ (обычно белая карточка/страница), не отдельный серый
     прямоугольник — запрос пользователя 2026-07-28: "сделай фон родителя".
     Ширина — во всю таблицу (было fit-content, тулбар занимал только
     ширину своих кнопок), без отступа снизу и без нижней рамки — тулбар и
     .ws-grid ниже (та же рамка/скругление, см. .ws-grid) сливаются в один
     блок без видимого шва, тулбар только "накрывает" его сверху. */
  background: transparent;
  width: 100%;
  border: 1px solid var(--bd-dde3ea);
  border-bottom: none;
  border-radius: 6px 6px 0 0;
}
/* Пустая командная панель (нет ни одной кнопки) не показывается, таблица — со скруглёнными верхними углами (2026-10-10). */
.ws-toolbar:empty { display: none; }
.ws-toolbar:empty + .ws-grid { border-radius: 6px; }
/* Таблица в пределах высоты формы (fitGridHeight в bias-grid.js): строки прокручиваются внутри, шапка закреплена. */
.ws-grid.ws-grid-fit { overflow-y: auto; }
.ws-grid.ws-grid-fit > .ws-grid-header { position: sticky; top: 0; z-index: 2; }
.ws-toolbar-btn {
  min-width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--fg-333333);
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  padding: 0 8px;
  white-space: nowrap;
}
.ws-toolbar-btn:hover { background: var(--bg-ffffff); border-color: var(--bd-c7cedb); }
/* Разделитель между группой базовых CRUD-кнопок и extraButtons конкретной
   таблицы (см. hasBaseButtons в createBiasGrid) — тонкая вертикальная
   линия, как группы кнопок на образце. */
.ws-toolbar-sep { width: 1px; align-self: stretch; margin: 2px 4px; background: var(--bg-dde3ea); }
/* Цветные пиктограммы тулбара грида (запрос пользователя, 2026-10-02: "сделай цветные
   кнопочки, а то страшные") — цвет по смыслу действия, значок рисуется currentColor;
   при наведении — лёгкая подложка того же цвета. Во всех таблицах (bias-grid.js). */
.ws-toolbar-btn[data-grid-add] { color: #16a34a; }
.ws-toolbar-btn[data-grid-add-group] { color: #d97706; }
.ws-toolbar-btn[data-grid-edit] { color: #2563eb; }
.ws-toolbar-btn[data-grid-mark] { color: #ea580c; }
.ws-toolbar-btn[data-grid-delete] { color: #dc2626; }
.ws-toolbar-btn[data-grid-copy] { color: #7c3aed; }
.ws-toolbar-btn[data-grid-refresh] { color: #0d9488; }
.ws-toolbar-btn[data-grid-list-settings] { color: #4f46e5; }
.ws-toolbar-btn[data-grid-add]:hover, .ws-toolbar-btn[data-grid-add-group]:hover, .ws-toolbar-btn[data-grid-edit]:hover,
.ws-toolbar-btn[data-grid-mark]:hover, .ws-toolbar-btn[data-grid-delete]:hover, .ws-toolbar-btn[data-grid-copy]:hover,
.ws-toolbar-btn[data-grid-refresh]:hover, .ws-toolbar-btn[data-grid-list-settings]:hover {
  background: color-mix(in srgb, currentColor 12%, transparent);
  border-color: color-mix(in srgb, currentColor 35%, transparent);
}
/* Сами значки действий — картинки wwwroot/icons (renderIcon, ACTION_ICON_FILES); цвет
   кнопки выше — только для подложки при наведении. */
img.ws-icon-img { object-fit: contain; }
html[data-theme="dark"] .ws-toolbar-btn[data-grid-add] { color: #4ade80; }
html[data-theme="dark"] .ws-toolbar-btn[data-grid-add-group] { color: #fbbf24; }
html[data-theme="dark"] .ws-toolbar-btn[data-grid-edit] { color: #60a5fa; }
html[data-theme="dark"] .ws-toolbar-btn[data-grid-mark] { color: #fb923c; }
html[data-theme="dark"] .ws-toolbar-btn[data-grid-delete] { color: #f87171; }
html[data-theme="dark"] .ws-toolbar-btn[data-grid-copy] { color: #a78bfa; }
html[data-theme="dark"] .ws-toolbar-btn[data-grid-refresh] { color: #2dd4bf; }
html[data-theme="dark"] .ws-toolbar-btn[data-grid-list-settings] { color: #818cf8; }

/* ── Кнопка страничного DSL (см. PageDslBuiltins.RenderButton — "Кнопка" в
   .bias, класс .bias-btn, атрибут data-action довязывается в Shell.html через
   wireDataActionButtons) — тот же визуальный стиль, что у .ws-toolbar-btn
   ("+ Добавить" и т.п.), чтобы DSL-кнопки, показанные внутри вкладки
   Workspace (см. loadSubsystemCommandTab), не выглядели браузерной кнопкой
   по умолчанию. ── */
/* Форма DSL (см. FormDslBuiltins.RenderForm — корневой .bias-form) —
   вертикальный зазор между соседними контролами минимум 7px (запрос
   пользователя, 2026-08-04). Форма заполняет вкладку; высоту таблиц
   считает layoutFormTables в Shell.html от clientHeight формы. */
.bias-form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  min-height: 0;
  flex: 1 1 auto;
  height: 100%;
  overflow: auto;
}
.ws-page:has(.bias-form) {
  height: 100%;
  min-height: 0;
  overflow: hidden;
  box-sizing: border-box;
}
.ws-page.active:has(.bias-form) {
  display: flex;
  flex-direction: column;
}
/* Глобальный поиск — та же схема: вкладка фиксированной высоты, таблица
   результатов дотягивается до низа (layoutPageFillGrids в Shell.html).
   Пикер-вкладка (запрос пользователя, 2026-09-18: кнопка "..." у
   ПолеСоСписком — см. openCatalogPicker) — ТА ЖЕ схема, один грид без
   .bias-form вокруг (в отличие от layoutFormTables — тому нужна форма с
   несколькими таблицами, тут ровно одна, делить место не с кем). */
.ws-page:has(#gsearch-results),
.ws-page:has(.bias-picker-page) {
  height: 100%;
  min-height: 0;
  overflow: hidden;
  box-sizing: border-box;
}
.ws-page.active:has(#gsearch-results),
.ws-page.active:has(.bias-picker-page) {
  display: flex;
  flex-direction: column;
}
.ws-page:has(#gsearch-results) > .ws-user-form { flex: none; }
#gsearch-results {
  width: 100%;
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
}
.bias-picker-page {
  display: flex;
  flex-direction: column;
  width: 100%;
  min-width: 0;
  min-height: 0;
  flex: 1 1 auto;
  box-sizing: border-box;
}
/* Кнопка "Выбор" вверху слева формы выбора (запрос пользователя,
   2026-09-18, как в исходной платформе) — своя полоска НАД гридом, flex:none — не участвует
   в дележе высоты, layoutPageFillGrids сам подтягивает [data-picker-grid]
   до низа вкладки вокруг неё (см. class-doc openCatalogPicker). */
.bias-picker-toolbar {
  flex: none;
  padding: 8px 10px;
  border-bottom: 1px solid var(--bd-d8dee8);
}
.ws-user-form:has(.bias-form) {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  overflow: hidden;
}
.bias-form > .bias-table-grid,
.bias-form > fieldset.bias-group,
.bias-form > .bias-pages,
.bias-form > .bias-command-bar,
.bias-form > .bias-table-splitter { align-self: stretch; width: 100%; box-sizing: border-box; }

/* Сплитер МЕЖДУ соседними Таблица (запрос пользователя, 2026-09-16) —
   перетаскиваемая полоса, отдаёт место одной таблице за счёт другой (см.
   wireTableSplitters/layoutFormTables в Shell.html). Тонкая "ручка"
   посередине — тот же визуальный язык, что у резайза колонок грида. */
.bias-table-splitter {
  flex: none;
  height: 9px;
  margin: -1px 0;
  cursor: row-resize;
  position: relative;
  z-index: 1;
}
.bias-table-splitter::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background: var(--bg-c7cedb);
}
.bias-table-splitter:hover::after,
.bias-table-splitter.bias-table-splitter-active::after { background: var(--bg-2b579a); }

/* КоманднаяПанель (запрос пользователя, 2026-09-15) — горизонтальная полоса
   кнопок-Команда, тот же принцип, что у .bias-group-horizontal ниже. */
.bias-command-bar {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
}
.bias-command-bar > .bias-btn { flex-shrink: 1; min-width: 0; max-width: 100%; }

.bias-btn {
  background: var(--bg-2b579a);
  color: var(--fg-ffffff);
  border: none;
  border-radius: 4px;
  padding: 7px 14px;
  font-size: 13px;
  cursor: pointer;
  flex: 0 0 auto;
  align-self: flex-start;
  max-width: 100%;
  box-sizing: border-box;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bias-group-horizontal > .bias-btn,
.bias-group-horizontal-always > .bias-btn,
.bias-group-horizontal > .bias-field,
.bias-group-horizontal-always > .bias-field,
.bias-group-horizontal > .bias-field-group,
.bias-group-horizontal-always > .bias-field-group,
.bias-group-horizontal > .bias-field-with-header,
.bias-group-horizontal-always > .bias-field-with-header,
.bias-group-horizontal > .bias-label,
.bias-group-horizontal-always > .bias-label {
  flex-shrink: 1;
  min-width: 0;
  max-width: 100%;
}
.bias-btn:hover { background: var(--bg-234a85); }
.bias-btn:disabled { background: var(--bg-9aa7b8); cursor: not-allowed; }

/* ── Группа страничного DSL (см. PageDslBuiltins.RenderGroup — "Группа" в
   .bias, рендерится как <fieldset>/<legend>, не div — браузер сам рисует
   заголовок, "вырезанный" в верхней границе рамки) — Группировка (запрос
   пользователя, 2026-08-04): "Вертикальная"/"ГоризонтальнаяВсегда"/
   "ГоризонтальнаяЕслиВозможно" переключает укладку дочерних элементов.
   ФОН — ПРОЗРАЧНЫЙ по умолчанию (никакого background: ... здесь) — ЦветФона
   задаёт его точечно через inline style на конкретном <fieldset> (см.
   RenderGroup), эта база его не перекрывает. ── */
fieldset.bias-group {
  border: 1px solid var(--bd-d8cfa8);
  border-radius: 8px;
  padding: 14px 16px 12px;
  margin: 0 0 12px;
  display: flex;
  gap: 8px;
}
.bias-group-vertical { flex-direction: column; align-items: flex-start; min-width: 0; }
/* Несжимаемые дети — по вертикали flex-shrink:0. Высоту таблиц задаёт JS. */
.bias-form > .bias-btn,
.bias-form > .bias-label,
.bias-form > .bias-field-with-header,
.bias-form > .bias-checkbox,
.bias-form > .bias-field,
.bias-form > .bias-field-group,
.bias-pages-panel > .bias-btn,
.bias-pages-panel > .bias-label,
.bias-pages-panel > .bias-field-with-header,
.bias-pages-panel > .bias-checkbox,
.bias-pages-panel > .bias-field,
.bias-pages-panel > .bias-field-group,
fieldset.bias-group > .bias-btn,
fieldset.bias-group > .bias-label,
fieldset.bias-group > .bias-field-with-header,
fieldset.bias-group > .bias-checkbox,
fieldset.bias-group > .bias-field,
fieldset.bias-group > .bias-field-group,
fieldset.bias-group > .bias-list,
fieldset.bias-group > .bias-chart {
  flex-shrink: 0;
}
/* Выравнивание заголовков Поля «Лево» внутри вертикальной группы
   (запрос пользователя, 2026-08-07): общая колонка подписей + общая линия
   начала полей. Ширину колонки подписей выставляет syncGroupFormLabels
   (--bias-form-label-width); без JS каждая строка сама по max-content. */
.bias-group-vertical > .bias-field-with-header.bias-field-header-left,
.bias-pages-panel > .bias-field-with-header.bias-field-header-left,
.bias-form > .bias-field-with-header.bias-field-header-left,
.ws-user-form > .bias-field-with-header.bias-field-header-left,
.ws-user-form .bias-form > .bias-field-with-header.bias-field-header-left {
  display: grid;
  /* Колонка подписей — общая ширина группы (syncFormLabelColumn), но НЕ уже
     самой подписи: minmax(общая, max-content) — не влезает — расширяется. */
  grid-template-columns: minmax(var(--bias-form-label-width, 0px), max-content) minmax(0, max-content);
  column-gap: 5px;
  align-items: center;
  align-self: flex-start;
  width: auto;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
.bias-group-vertical > .bias-field-with-header.bias-field-header-left > .bias-field-header-label,
.bias-pages-panel > .bias-field-with-header.bias-field-header-left > .bias-field-header-label,
.bias-form > .bias-field-with-header.bias-field-header-left > .bias-field-header-label,
.ws-user-form > .bias-field-with-header.bias-field-header-left > .bias-field-header-label,
.ws-user-form .bias-form > .bias-field-with-header.bias-field-header-left > .bias-field-header-label {
  justify-self: start;
  text-align: left;
}
.bias-group-vertical > .bias-field-with-header.bias-field-header-left > .bias-field,
.bias-group-vertical > .bias-field-with-header.bias-field-header-left > .bias-field-group,
.bias-pages-panel > .bias-field-with-header.bias-field-header-left > .bias-field,
.bias-pages-panel > .bias-field-with-header.bias-field-header-left > .bias-field-group,
.bias-form > .bias-field-with-header.bias-field-header-left > .bias-field,
.bias-form > .bias-field-with-header.bias-field-header-left > .bias-field-group,
.ws-user-form > .bias-field-with-header.bias-field-header-left > .bias-field,
.ws-user-form > .bias-field-with-header.bias-field-header-left > .bias-field-group,
.ws-user-form .bias-form > .bias-field-with-header.bias-field-header-left > .bias-field,
.ws-user-form .bias-form > .bias-field-with-header.bias-field-header-left > .bias-field-group {
  flex: none;
  /* Без Ширины — по содержимому; при Ширина > 0 inline width/max-width
     побеждает (см. InjectWidthStyle) и задаёт точный размер контрола.
     justify-self:start — иначе grid stretch выравнивает ВСЕ поля по
     ширине самой широкой колонки (баг: «День» с Ширина:30 выглядело
     так же широко, как «Должность»). */
  justify-self: start;
  width: auto;
  min-width: 0;
  box-sizing: border-box;
}
/* "Горизонтальная если возможно" — переносит лишние элементы на новую
   строку, когда не влезают (было единственным горизонтальным вариантом,
   имя класса сохранено — старые формы не задевает). */
.bias-group-horizontal { flex-direction: row; align-items: flex-start; flex-wrap: wrap; min-width: 0; }
/* "Горизонтальная всегда" — один ряд без переноса; по вертикали не
   растягиваем соседей (группа со Статусом не становится высоты таблицы). */
.bias-group-horizontal-always { flex-direction: row; align-items: flex-start; flex-wrap: nowrap; min-width: 0; overflow-x: auto; }
fieldset.bias-group > legend {
  padding: 0 6px;
  font-size: 12px;
  color: var(--fg-8a7f57);
  font-weight: 600;
}

/* ── Страницы страничного DSL (см. FormDslBuiltins.RenderPages — "Страницы"/
   "Страница" в .bias). По умолчанию растягиваются по горизонтали, если не
   задана Ширина / РастягиватьПоГоризонтали=Ложь (как Таблица; запрос
   пользователя, 2026-08-09). ── */
.bias-pages {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  min-width: 0;
  margin: 0 0 12px;
  /* В вертикальном стеке — на всю ширину родителя. */
  width: 100%;
  max-width: 100%;
  align-self: stretch;
}
/* В горизонтальной группе — забирают оставшуюся ширину (Статус слева
   по содержимому, Страницы справа на остаток). По вертикали не тянем
   (align-self:flex-start — иначе снова растянется до высоты Статуса). */
.bias-group-horizontal > .bias-pages,
.bias-group-horizontal-always > .bias-pages {
  flex: 1 1 0%;
  width: auto;
  align-self: flex-start;
}
.bias-form > .bias-pages {
  margin: 0;
}
.bias-pages-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  border-bottom: 1px solid var(--bd-d8cfa8);
  margin-bottom: 12px;
  flex: none;
  width: 100%;
  box-sizing: border-box;
}
.bias-pages-btn {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 7px 14px;
  font-size: 13px;
  color: var(--fg-6b6250);
  cursor: pointer;
  margin-bottom: -1px;
}
.bias-pages-btn:hover { color: var(--fg-2b579a); }
/* Поле переключателя (2026-10-08): радиокнопки или тумблер (кнопки рядом). */
.bias-radio-field { display: inline-flex; flex-wrap: wrap; gap: 4px 14px; align-items: center; }
.bias-radio-option { background: transparent; border: none; padding: 3px 0; font: inherit; color: inherit; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.bias-radio-dot { width: 12px; height: 12px; border-radius: 50%; border: 1px solid var(--bd-2b579a); box-sizing: border-box; }
.bias-radio-on .bias-radio-dot { background: radial-gradient(circle, var(--fg-2b579a) 0 3px, transparent 3.5px); }
.bias-tumbler { gap: 0; }
.bias-tumbler .bias-radio-option { border: 1px solid var(--bd-2b579a); padding: 3px 10px; margin-left: -1px; color: var(--fg-2b579a); }
.bias-tumbler .bias-radio-option:first-of-type { border-radius: 3px 0 0 3px; margin-left: 0; }
.bias-tumbler .bias-radio-option:last-of-type { border-radius: 0 3px 3px 0; }
.bias-tumbler .bias-radio-on { background: var(--bd-2b579a); color: #fff; }
/* Поле надписи — значение текстом, без рамки. */
.bias-field.bias-label-field { border-color: transparent; background: transparent; box-shadow: none; padding-left: 0; }
.bias-pages-btn-active {
  color: var(--fg-2b579a);
  border-bottom-color: var(--bd-2b579a);
  font-weight: 600;
}
.bias-pages-panel {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
  min-width: 0;
  width: 100%;
}
.bias-pages-panel[hidden] { display: none; }

/* ── Надпись формового DSL — одна строка; по горизонтали может сжаться. ── */
.bias-label {
  padding: 2px 0;
  font-size: 13px;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Таблица формового DSL — высоту/вьюпорт задаёт layoutFormTables
   (Shell.html) от размера формы; здесь только каркас. ── */
.bias-table-grid {
  --bias-grid-row-h: 38px;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-self: stretch;
  width: 100%;
  min-width: 0;
  overflow: hidden;
  box-sizing: border-box;
  flex: none;
}
.bias-table-grid > .ws-toolbar { flex: none; }
.bias-table-grid > .ws-grid {
  flex: none;
  min-height: 0;
  overflow: auto;
  box-sizing: border-box;
}
.bias-table-grid .ws-grid-windowed {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-sizing: border-box;
}
.bias-table-grid .ws-grid-windowed-viewport {
  /* Предел сжатия — заголовок + 1 строка; точную высоту пишет JS. */
  min-height: calc(var(--bias-grid-row-h) * 2);
  box-sizing: border-box;
}

/* ── Поле страничного DSL (см. PageDslBuiltins.RenderField — "Поле" в .bias,
   типы Текст/Многострочный/Пароль/Файл) — раньше вообще не было правила: рендерился
   голым браузерным input/textarea без единого стиля. Акцентный цвет фокуса — тот
   же синий var(--fg-2b579a), что и у .bias-btn, чтобы все контролы страничного DSL читались
   как один комплект, а не набор случайных виджетов. ── */
.bias-field {
  display: inline-block;
  box-sizing: border-box;
  width: 100%;
  max-width: min(320px, 100%);
  min-width: 0;
  padding: 8px 12px;
  font-size: 14px;
  font-family: inherit;
  color: var(--fg-333333);
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-cfd6e0);
  border-radius: 6px;
  transition: border-color .15s ease, box-shadow .15s ease;
  /* Однострочные поля — одна строка; при узкой форме сжимаются по ширине
     (запрос пользователя, 2026-08-09). */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bias-field:hover { border-color: var(--bd-aeb8c7); }
.bias-field:focus {
  outline: none;
  border-color: var(--bd-2b579a);
  box-shadow: 0 0 0 3px var(--sh-43_87_154_0p15);
}
.bias-field::placeholder { color: var(--fg-9aa3b1); }
.bias-field:disabled { background: var(--bg-f2f4f7); color: var(--fg-8a93a3); cursor: not-allowed; }
/* Нередактируемое, но копируемое поле (логин в карточке пользователя, 2026-10-02):
   readonly вместо disabled — текст выделяется и копируется; вид как у disabled. */
.bias-field.ws-field-copyable[readonly] { background: var(--bg-f2f4f7); color: var(--fg-8a93a3); cursor: text; }
/* Многострочное — не сжимаем до одной строки; перенос текста обычный. */
textarea.bias-field {
  min-height: 72px;
  max-width: min(480px, 100%);
  resize: vertical;
  line-height: 1.4;
  white-space: pre-wrap;
  text-overflow: clip;
  overflow: auto;
}
input.bias-field[type="file"] { max-width: min(320px, 100%); padding: 6px 8px; border-style: dashed; cursor: pointer; white-space: normal; text-overflow: clip; }

/* ── Заголовок Поля (запрос пользователя, 2026-08-04: ScriptField.Заголовок/
   ПоложениеЗаголовка, см. PageDslBuiltins.RenderField) — подпись ВСЕГДА
   идёт ПЕРЕД полем в DOM, визуальную позицию (слева/сверху/справа/снизу)
   даёт flex-direction на обёртке — .bias-field-with-header появляется в
   разметке ТОЛЬКО если ПоложениеЗаголовка реально задано, обычные Поля без
   этого свойства её вообще не получают (нулевой риск для существующих
   форм). .bias-field/.bias-field-group внутри — flex:1, чтобы сам виджет
   тянулся по доступной ширине независимо от того, как обёртка его уложила
   (row у Лево/Право не даёт ему браузерную ширину по умолчанию). ── */
.bias-field-with-header {
  display: flex;
  gap: 6px;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
/* Подпись НИКОГДА не обрезается (запрос пользователя, 2026-09-25: "если
   заголовок не влазит в заданном месте, то расширять пространство, чтобы
   влезло") — раньше overflow:hidden + ellipsis резали её ("Местонахожден…"),
   когда колонка подписей оказывалась уже текста. */
.bias-field-with-header .bias-field-header-label {
  flex: none;
  font-size: 13px;
  color: var(--fg-555555);
  white-space: nowrap;
  overflow: visible;
  text-overflow: clip;
  min-width: max-content;
}
.bias-field-with-header.bias-field-header-top { flex-direction: column; align-items: flex-start; gap: 3px; }
.bias-field-with-header.bias-field-header-bottom { flex-direction: column-reverse; align-items: flex-start; gap: 3px; }
.bias-field-with-header.bias-field-header-left { flex-direction: row; align-items: center; }
.bias-field-with-header.bias-field-header-right { flex-direction: row-reverse; align-items: center; }
.bias-field-with-header > .bias-field,
.bias-field-with-header > .bias-field-group { flex: 1; min-width: 0; max-width: none; }
/* Ширина (DSL) — inline width/max-width на виджете; flex:1 не должен
   растягивать поле шире заданного значения. */
.bias-field-with-header > .bias-field[style*="width:"],
.bias-field-with-header > .bias-field-group[style*="width:"] {
  flex: none;
}

/* ── Кнопки Поля (список/выбор/очистка/регулирование/открытие/создание) —
   запрос пользователя 2026-08-03, скриншот стандартного набора кнопок у
   поля ввода реквизита референсной платформы — по умолчанию скрыты,
   включаются DSL-свойствами КнопкаВыпадающегоСписка/КнопкаВыбора/
   КнопкаОчистки/КнопкаРегулирования/КнопкаОткрытия/КнопкаСоздания (см.
   ScriptField/PageDslBuiltins.RenderField) — .bias-field-group появляется в
   разметке ТОЛЬКО если хоть одна кнопка включена, обычные Поля без кнопок
   этот контейнер вообще не получают (нулевой риск для уже существующих
   форм).

   ИСПРАВЛЕНО 2026-08-03 (замечание пользователя на скриншоте: "почему
   кнопка справа, а не внутри поля") — раньше .bias-field-group была просто
   flex-строкой [input][кнопка], каждая своя рамка, зазор между ними; на
   референс-скриншоте кнопки ВНУТРИ той же самой рамки, что и сам input,
   одним нешовным виджетом. Теперь рамку/фон/фокус рисует САМА .bias-field-
   group (то есть класс .bias-field ВНУТРИ группы теряет свою рамку целиком,
   становится прозрачным — см. .bias-field-group .bias-field ниже), кнопки —
   без своей рамки, просто иконки в общем боксе, прижатые к правому краю. ── */
.bias-field-group {
  display: inline-flex;
  align-items: center;
  max-width: min(480px, 100%);
  min-width: 0;
  box-sizing: border-box;
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-cfd6e0);
  border-radius: 6px;
  transition: border-color .15s ease, box-shadow .15s ease;
  /* overflow:hidden — запрос пользователя, 2026-08-12: "вертикальные
     полосочки и фон темнее" у кнопок ниже — кнопки теперь растягиваются на
     всю высоту (align-self: stretch) и красят фон вплотную до самого края,
     без этого фон ПОСЛЕДНЕЙ кнопки торчал бы прямоугольным углом за
     скруглённой рамкой группы. */
  overflow: hidden;
}
.bias-field-group:hover { border-color: var(--bd-aeb8c7); }
.bias-field-group:focus-within {
  border-color: var(--bd-2b579a);
  box-shadow: 0 0 0 3px var(--sh-43_87_154_0p15);
}
/* Сам input/textarea теряет СВОЮ рамку/фон/тень фокуса целиком — рамку
   теперь рисует .bias-field-group вокруг всего widget'а разом. */
.bias-field-group .bias-field,
.bias-field-group .bias-field:hover,
.bias-field-group .bias-field:focus {
  max-width: none;
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: transparent;
  box-shadow: none;
}
/* Вертикальные разделители + тёмный фон между кнопками (запрос
   пользователя, 2026-08-12, скриншот поля "Действие" с "…"/"↗" рядом) —
   border-left на КАЖДОЙ кнопке рисует полоску И перед первой (между ней и
   input), и между соседними кнопками, без margin (margin создавал бы
   двойной зазор рядом с полоской) — align-self: stretch тянет кнопку (и
   её полоску) на всю высоту группы, не только на высоту иконки. */
.bias-field-btn {
  flex: none;
  align-self: stretch;
  width: 28px;
  margin: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-eef1f4);
  color: var(--fg-7a8494);
  border: none;
  border-left: 1px solid var(--bd-cfd6e0);
  border-radius: 0;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.bias-field-btn:hover { background: var(--bg-dfe4ea); color: var(--fg-333333); }
/* Регулирование — стрелки вверх/вниз СТОПКОЙ в одном узком слоте (не два
   отдельных .bias-field-btn рядом — визуально это ОДИН спиннер-контрол, как
   на скриншоте), тоже БЕЗ своей рамки — внутри общего бокса группы. */
.bias-field-spin {
  flex: none;
  display: flex;
  flex-direction: column;
  margin: 0 4px;
}
.bias-field-spin-up, .bias-field-spin-down {
  flex: 1 1 auto;
  width: 18px;
  height: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--fg-7a8494);
  border: none;
  font-size: 7px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.bias-field-spin-up:hover, .bias-field-spin-down:hover { color: var(--fg-333333); }

/* ── ПолеСоСписком страничного DSL (см. PageDslBuiltins.RenderComboBox —
   "ПолеСоСписком" в .bias) — обычный <select>, но с СОБСТВЕННОЙ стрелкой
   (appearance:none + фоновый SVG-шеврон) — родная стрелка браузера меняется от
   темы/ОС и не сочеталась бы с остальными стилизованными контролами. ── */
.bias-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23667085' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 12px 8px;
  cursor: pointer;
}
.bias-select:hover { background-position: right 12px center; }
/* ПолеСоСписком — теперь текстовый input с живой фильтрацией, не
   <select> (запрос пользователя, 2026-09-18: "поиск в строке... кнопка
   выбора '...'" — см. class-doc RenderComboBox) — .bias-select ВЫШЕ даёт
   тот же шеврон-фон (сигнализирует "тут список"), но cursor:pointer от
   него же не подходит РЕДАКТИРУЕМОМУ полю — переопределяем на text.
   .bias-combo-source — сырой список вариантов для JS-фильтра
   (wireComboInputs), НИКОГДА не показывается сам (уже [hidden] в
   разметке, здесь просто явно на случай будущего override). */
.bias-combo-input { cursor: text; }
.bias-combo-source { display: none; }
/* Список подсказок — position:fixed (см. wireComboInputs, ПОЧЕМУ не
   внутри .bias-field-group), координаты пишет JS при каждом показе. */
.bias-combo-suggestions {
  position: fixed;
  z-index: 500;
  overscroll-behavior: contain;
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-cfd6e0);
  border-radius: 6px;
  box-shadow: 0 6px 20px var(--sh-20_30_50_0p15);
  max-height: 260px;
  overflow-y: auto;
  box-sizing: border-box;
}
.bias-combo-suggestion {
  padding: 7px 12px;
  font-size: 13.5px;
  color: var(--fg-333333);
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bias-combo-suggestion:hover { background: var(--bg-eef3fb); }
/* Подсветка стрелками (ссылочное поле автоформы, 2026-09-25). */
.bias-combo-suggestion.active { background: var(--bg-dde7f7); }
.bias-combo-suggestion-empty { color: var(--fg-8a93a3); cursor: default; }
/* Кнопка "..." — .bias-field-btn уже несёт вид/hover (см. выше), только
   моноширинный шрифт для самих точек — по умолчанию тройной "..." в
   обычном шрифте выглядит слишком плотно/криво прижатым к низу строки. */
.bias-combo-picker-btn { font-family: monospace; letter-spacing: 1px; }

/* ── Флажок страничного DSL (см. PageDslBuiltins.RenderCheckbox — "Флажок" в
   .bias) — нативный input[type=checkbox], но полностью перерисованный через
   appearance:none + собственную ::after-галочку, а не голый браузерный квадратик:
   тот же синий var(--fg-2b579a), что и у .bias-btn/.bias-field, когда отмечен. ── */
.bias-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 14px;
  color: var(--fg-333333);
  -webkit-user-select: none;
  user-select: none;
}
.bias-checkbox-input {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  position: relative;
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1px solid var(--bd-cfd6e0);
  border-radius: 4px;
  background: var(--bg-ffffff);
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.bias-checkbox:hover .bias-checkbox-input { border-color: var(--bd-aeb8c7); }
.bias-checkbox-input:checked { background: var(--bg-2b579a); border-color: var(--bd-2b579a); }
.bias-checkbox-input:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid var(--bd-ffffff);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.bias-checkbox-input:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--sh-43_87_154_0p15);
}

/* ── Переключатель страничного DSL (см. PageDslBuiltins.RenderToggle —
   "Переключатель" в .bias) — тот же смысл, что Флажок, но pill-тумблер:
   input сам скрыт (opacity:0, поверх трека, кликабельная область), реальная
   картинка — .bias-toggle-track/.bias-toggle-thumb. Тот же синий var(--fg-2b579a), что и
   у отмеченного Флажка/.bias-btn/.bias-field. ── */
.bias-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 14px;
  color: var(--fg-333333);
  -webkit-user-select: none;
  user-select: none;
}
.bias-toggle-input {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  width: 34px;
  height: 20px;
  margin: 0;
  cursor: pointer;
}
.bias-toggle-track {
  position: relative;
  flex: none;
  width: 34px;
  height: 20px;
  border-radius: 10px;
  background: var(--bg-cfd6e0);
  transition: background .15s ease;
}
.bias-toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--bg-ffffff);
  transition: left .15s ease;
  box-shadow: 0 1px 2px var(--sh-0_0_0_0p25);
}
.bias-toggle-input:checked + .bias-toggle-track { background: var(--bg-2b579a); }
.bias-toggle-input:checked + .bias-toggle-track .bias-toggle-thumb { left: 16px; }
.bias-toggle-input:focus-visible + .bias-toggle-track {
  outline: none;
  box-shadow: 0 0 0 3px var(--sh-43_87_154_0p15);
}

/* ── Редактор скриптов (см. wwwroot/components/bias-editor.js,
   loadModuleSourceTab в Shell.html) — textarea ПОВЕРХ подсвеченного <pre>,
   оба слоя занимают одно и то же место (position:absolute, одинаковые
   шрифт/паддинги/line-height — иначе текст textarea и подсветка под ней
   разъедутся по буквам). textarea прозрачна (виден только курсор), реальный
   видимый текст — из <pre>. ── */
.bias-editor {
  display: flex;
  flex-direction: column;
  width: 100%;
  /* Высота по умолчанию — фиксированные 60vh (разумно там, где родитель НЕ
     растянут во всю вкладку). Редактор скриптов (см. .ws-module-source-form
     ниже, запрос пользователя 2026-07-28: "область до конца не
     растягивается") её переопределяет на 100% своего контейнера. */
  height: 60vh;
  min-height: 240px;
  border: 1px solid var(--bd-dde3ea);
  border-radius: 6px;
  overflow: hidden;
  background: var(--bg-fbfbfc);
}
/* ── Вкладка редактора скриптов (см. loadModuleSourceTab в Shell.html) —
   растянуть .bias-editor во всю оставшуюся высоту вкладки, а не 60vh с
   пустым местом под ним. .ws-user-form УЖЕ flex-column (см. общее правило
   выше) — здесь height:100% на странице (базовый .ws-page — height:auto)
   и обёртка редактора flex:1, чтобы она забрала всё, что осталось ПОСЛЕ
   кнопок/ошибки над ней. ── */
/* Во всю вкладку, без отступов по краям (2026-10-04, как у табличного документа). */
.ws-page.ws-page-module-source { height: 100%; padding: 0; }
.ws-module-source-form { height: 100%; gap: 0; }
.ws-module-source-form .ws-user-form-error:empty { display: none; }
.ws-module-source-editor { flex: 1; min-height: 0; display: flex; }
.ws-module-source-editor .bias-editor { height: 100%; border: 0; border-radius: 0; }
/* Меньше пустого места над тулбаром редактора (запрос пользователя
   2026-07-31: "подними редактор выше") — обычные .ws-page 20px сверху здесь
   не нужны, тулбар редактора и так плотный, свой отступ не требуется.
   padding-top — в правиле .ws-page.ws-page-module-source выше. */

/* Панель действий редактора (запрос пользователя 2026-07-28: переход по
   номеру строки, сдвиг выделения влево/вправо) — тот же визуальный язык,
   что у .ws-toolbar над таблицами (компактные квадратные кнопки-пиктограммы
   с подсказкой по наведению), но СВОИ классы: это панель ВНУТРИ одного
   редактора, а не общий компонент Bias.Grid. */
.bias-editor-toolbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  background: var(--bg-f5f7fa);
  border-bottom: 1px solid var(--bd-dde3ea);
}
.bias-editor-btn {
  min-width: 26px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--fg-333333);
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 13px;
  cursor: pointer;
  padding: 0 6px;
}
.bias-editor-btn:hover { background: var(--bg-ffffff); border-color: var(--bd-c7cedb); }
/* Кнопка "Сохранить" тулбара (запрос пользователя 2026-07-31: "кнопку
   сохранить перенеси в тулбар") — единственная кнопка тулбара с цветным
   фоном (тот же синий, что был у отдельной большой кнопки "Сохранить" под
   заголовком вкладки, см. .ws-user-form-actions button:first-child), чтобы
   не потеряться среди остальных монохромных иконок. */
.bias-editor-btn-save { background: var(--bg-2b579a); color: var(--fg-ffffff); border-color: var(--bd-2b579a); }
.bias-editor-btn-save:hover { background: var(--bg-234a85); border-color: var(--bd-234a85); }
/* Кнопка-тумблер маркера (data-ed-bookmark-toggle) — подсвечена, пока курсор
   стоит на строке, у которой уже есть маркер (см. syncBookmarkToggleState в
   bias-editor.js), тот же приём "активная кнопка = цветной фон", что и у
   .bias-editor-btn-save, но своим (не синим, тем же оранжевым, что у флажков
   в гутере) цветом, чтобы визуально не путать с "Сохранить". */
.bias-editor-btn.active { background: var(--bg-fdeacb); border-color: var(--bd-e3b464); color: var(--fg-8a5a10); }
.bias-editor-btn.active:hover { background: var(--bg-fbe0b0); }

/* Навигатор по символам (запрос пользователя, 2026-07-30; иконки f()/p()
   как у автодополнения — 2026-08-08) — кастомный дропдаун вместо <select>
   (нативный option не красит HTML). Высота кнопки = 24px как у остальных
   кнопок тулбара. */
.bias-editor-symbol-nav {
  position: relative;
  flex: 1 1 auto;
  min-width: 80px;
  max-width: 280px;
  margin: 0 4px;
}
.bias-editor-symbol-nav-btn {
  display: flex;
  align-items: center;
  width: 100%;
  height: 24px;
  padding: 0 22px 0 6px;
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
  background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath fill='%23666' d='M1 1l4 4 4-4'/%3E%3C/svg%3E") no-repeat right 8px center;
  font: inherit;
  font-size: 12px;
  color: var(--fg-2a2a2a);
  cursor: pointer;
  text-align: left;
  box-sizing: border-box;
}
.bias-editor-symbol-nav-btn:hover { border-color: var(--bd-aeb8c7); }
.bias-editor-symbol-nav-label {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.bias-editor-symbol-nav-outside {
  color: var(--fg-8a93a3);
  overflow: hidden;
  text-overflow: ellipsis;
}
.bias-editor-symbol-nav-menu {
  position: absolute;
  z-index: 30;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  min-width: 180px;
  max-height: 280px;
  overflow-y: auto;
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
  box-shadow: 0 4px 14px var(--sh-0_0_0_0p15);
  font-family: "Cascadia Code", "Consolas", "SFMono-Regular", Menlo, monospace;
  font-size: 12px;
}
.bias-editor-symbol-nav-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px;
  cursor: pointer;
  white-space: nowrap;
  color: var(--fg-2a2a2a);
  line-height: 1.5;
}
.bias-editor-symbol-nav-item.active,
.bias-editor-symbol-nav-item:hover { background: var(--bg-e8f0fe); }
.bias-editor-symbol-nav-item-name {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Диалог поиска по символам (см. bias-editor.js openSymbolSearch) — тот
   же .ws-modal-* каркас, что у showConfirmDialog/WS.prompt ("наш вопрос",
   запрос пользователя дословно), СВОЯ раскладка тела (колонка: поле поиска
   сверху, прокручиваемый список снизу — у обычных .ws-modal-body она
   строчная, для иконки+текста, здесь не подходит). */
.bias-editor-symbol-search-modal { width: 420px; max-width: 90vw; }
.bias-editor-symbol-search-modal .bias-field { width: 100%; margin-top: 4px; }
.bias-editor-symbol-search-list {
  max-height: 320px;
  overflow-y: auto;
  margin-top: 10px;
  border: 1px solid var(--bd-dde3ea);
  border-radius: 6px;
}
.bias-editor-symbol-item {
  padding: 4px 8px;
  cursor: pointer;
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 12px;
  font-family: "Cascadia Code", "Consolas", "SFMono-Regular", Menlo, monospace;
  border-bottom: 1px solid var(--bd-eef1f4);
}
.bias-editor-symbol-item:last-child { border-bottom: none; }
.bias-editor-symbol-item.active,
.bias-editor-symbol-item:hover { background: var(--bg-e8f0fe); }
.bias-editor-symbol-empty { padding: 16px; text-align: center; color: var(--fg-8a93a3); font-size: 13px; }

/* Тело редактора — textarea ПОВЕРХ подсвеченного <pre>, оба слоя занимают
   одно и то же место (position:absolute внутри этой обёртки, одинаковые
   шрифт/паддинги/line-height — иначе текст textarea и подсветка под ней
   разъедутся по буквам). textarea прозрачна (виден только курсор), реальный
   видимый текст — из <pre>. white-space:pre (НЕ pre-wrap) — длинные строки
   не переносятся, а скроллятся по горизонтали (запрос пользователя
   2026-07-28: "показывать как есть", плюс горизонтальный скролл). */
/* .bias-editor-body — теперь flex-строка [гутер][code-area] (запрос
   пользователя 2026-08-03: "добавь слева номера строк") — раньше сама была
   position:relative с textarea/pre/mirror поверх неё inset:0; эта роль
   переехала на .bias-editor-code-area ниже, body просто раскладывает гутер
   и code-area рядом. */
.bias-editor-body { display: flex; flex: 1; min-height: 0; }
.bias-editor-code-area { position: relative; flex: 1; min-width: 0; }
.bias-editor-highlight,
.bias-editor-input {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 10px 12px;
  border: none;
  font-family: "Cascadia Code", "Consolas", "SFMono-Regular", Menlo, monospace;
  font-size: 13px;
  line-height: 1.5;
  white-space: pre;
  overflow: auto;
  box-sizing: border-box;
}
/* Гутер номеров строк — СВОЙ flex-столбец слева от code-area (не поверх
   текста), builds заново на каждый render() (см. renderGutter в
   bias-editor.js). font/line-height/padding-top ТОЧНО как у
   .bias-editor-input рядом — иначе номера разъедутся со своими строками
   текста по вертикали (тот же принцип синхронности, что у highlight/
   textarea пары). overflow-y:hidden (не auto) — своей полосы прокрутки у
   гутера нет, вертикальный скролл только имитируется через gutterEl.
   scrollTop = textarea.scrollTop (JS, тот же приём, что уже синхронизирует
   .bias-editor-highlight). overflow-x:hidden — гутер никогда не скроллится
   горизонтально, даже если у него самого вдруг наберётся широкий контент. */
.bias-editor-gutter {
  flex: none;
  padding: 10px 0;
  font-family: "Cascadia Code", "Consolas", "SFMono-Regular", Menlo, monospace;
  font-size: 13px;
  line-height: 1.5;
  color: var(--fg-9aa3b2);
  background: var(--bg-f5f7fa);
  border-right: 1px solid var(--bd-dde3ea);
  overflow-x: hidden;
  overflow-y: hidden;
  user-select: none;
}
.bias-editor-gutter-line {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 2px;
  padding: 0 4px 0 6px;
  cursor: pointer;
  white-space: nowrap;
}
.bias-editor-gutter-line:hover { background: var(--bg-e8ecf2); color: var(--fg-555566); }
.bias-editor-gutter-line.bookmarked { color: var(--fg-b5651d); }
/* Флаг-слот ПОСТОЯННОЙ ширины у КАЖДОЙ строки гутера (даже без маркера) —
   иначе номера "прыгали" бы по горизонтали в зависимости от того, есть ли
   на конкретной строке маркер (см. renderGutter). */
.bias-editor-gutter-flag { flex: none; width: 11px; display: inline-flex; align-items: center; justify-content: center; }
.bias-editor-gutter-flag svg { width: 11px; height: 11px; color: var(--fg-d18b1f); }
/* Метка строки "Форма ..." (запрос пользователя, 2026-08-08) — В ГУТЕРЕ,
   не внутри подсвеченного текста (см. doc-комментарий у FORM_DECL_RE в
   bias-editor.js — вставка внутрь строки сдвинула бы курсор относительно
   textarea под ним). Та же ширина/вёрстка, что у .bias-editor-gutter-flag
   рядом — оба чисто информационные значки, не кнопки. */
.bias-editor-gutter-form { flex: none; width: 11px; display: inline-flex; align-items: center; justify-content: center; }
.bias-editor-gutter-form svg { width: 11px; height: 11px; color: var(--fg-0e7c86); }
.bias-editor-gutter-num { flex: none; min-width: 1.6em; text-align: right; }
/* Свёртка СПРАВА от номера (запрос пользователя, 2026-08-04) — SVG
   chevronDown (развёрнуто) / chevronRight (свёрнуто), см. ICON_PATHS. */
.bias-editor-gutter-fold {
  flex: none;
  width: 14px;
  height: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg-8b95a5);
  border-radius: 3px;
  cursor: pointer;
}
.bias-editor-gutter-fold .ws-icon { width: 12px; height: 12px; display: block; }
.bias-editor-gutter-fold:hover { color: var(--fg-2b579a); background: var(--bg-e0e7f2); }
.bias-editor-gutter-fold-collapsed { color: var(--fg-2b579a); }
.bias-editor-gutter-fold-collapsed:hover { background: var(--bg-d6e2f5); }
.bias-editor-gutter-fold-empty { cursor: default; pointer-events: none; }
.bias-editor-gutter-fold-empty:hover { background: transparent; color: inherit; }
.bias-editor-highlight {
  color: var(--fg-2a2a2a);
  pointer-events: none;
}
.bias-editor-highlight code { font: inherit; }
.bias-editor-input {
  background: transparent;
  color: transparent;
  caret-color: var(--fg-111111);
  resize: none;
  outline: none;
}
/* color:transparent на самой textarea (выше) прячет её РЕАЛЬНЫЙ (несвёрнутый)
   текст в обычном состоянии — видна только подсветка из .bias-editor-highlight
   ПОД ней. НО браузер красит выделение через ::selection ОТДЕЛЬНЫМ путём,
   который color:transparent игнорирует (известная особенность приёма
   "прозрачная textarea поверх подсвеченного pre" — без явного правила ниже
   выделенный текст становится ЧИТАЕМЫМ поверх подсветки). Реальный баг,
   найден пользователем живьём (2026-08-08, скриншот): при выделении строки
   со свёрнутым блоком наружу вылезал служебный "⋯ folded:N" — то, что
   highlight() сознательно НЕ рисует в обычном режиме (см. bias-editor.js).
   color:transparent здесь же, внутри ::selection, — вот и всё исправление. */
.bias-editor-input::selection { background: var(--bg-43_108_176_0p35); color: transparent; }
.bias-editor-input::-moz-selection { background: var(--bg-43_108_176_0p35); color: transparent; }

/* Автодополнение (запрос пользователя, 2026-07-30: "всплывающая подсказка
   после '.' или Ctrl+Space", см. bias-editor.js). bias-editor-mirror —
   невидимый клон textarea, ТОЛЬКО чтобы узнать пиксельные координаты
   курсора (у обычного textarea нет для этого API) — тот же класс
   .bias-editor-input даёт идентичные шрифт/паддинг/box-sizing.
   ТОЛЬКО height освобождён от inset:0 (растёт по содержимому — иначе
   многострочный текст обрезался бы по высоте textarea, а маркер на
   поздних строках получал бы неверную позицию). ШИРИНА НАМЕРЕННО
   ОСТАВЛЕНА как у textarea (НЕ auto) — реальный найденный баг (живой
   отчёт пользователя, скриншот: "при открытии ломает редактор"): при
   white-space:pre строки и так никогда не переносятся, поэтому
   горизонтальная позиция ЛЮБОГО символа не зависит от ширины бокса —
   width:auto был не нужен вообще, а на длинной строке реального скрипта
   раздувал mirror на сотни пикселей за пределы редактора, чего
   overflow:hidden у .bias-editor выше по дереву не всегда гасило
   визуально в реальной странице (в отличие от изолированного тестового
   стенда) — оттуда и "ломает редактор"/лишний горизонтальный скролл. */
.bias-editor-mirror {
  visibility: hidden;
  pointer-events: none;
  bottom: auto;
  height: auto;
  overflow: hidden;
}
.bias-editor-completions {
  position: absolute;
  z-index: 20;
  min-width: 140px;
  max-width: 320px;
  max-height: 220px;
  overflow-y: auto;
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
  box-shadow: 0 4px 14px var(--sh-0_0_0_0p15);
  font-family: "Cascadia Code", "Consolas", "SFMono-Regular", Menlo, monospace;
  font-size: 12px;
}
/* Пиктограммы слева от каждого пункта (запрос пользователя 2026-07-30,
   скриншот референсной платформы) — f() для вызываемых функций (GLOBAL_FUNC_LANG), {} для
   имён типов (TYPE_LANG, только после "Новый"), — для всего остального
   (ключевые слова/члены объектов) — см. iconKindForWord в bias-editor.js.
   Плотнее (2px 8px, было 3px 10px) и меньше шрифт (12px, было 13px) — тот
   же запрос: "строки в списке поплотнее и шрифт меньше". */
.bias-editor-completion-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 1px 8px;
  cursor: pointer;
  white-space: nowrap;
  color: var(--fg-2a2a2a);
  line-height: 1.5;
}
.bias-editor-completion-item.active,
.bias-editor-completion-item:hover {
  background: var(--bg-e8f0fe);
}
.bias-editor-completion-icon {
  flex: none;
  width: 28px;
  text-align: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--fg-8a93a6);
}
.bias-editor-completion-icon-func { color: var(--fg-2b579a); }
.bias-editor-completion-icon-proc { color: var(--fg-1a8a5f); }
.bias-editor-completion-icon-type { color: var(--fg-7a4fb5); }
.bias-editor-completion-icon-form { color: var(--fg-c45c26); }
.bias-editor-completion-icon-other { color: var(--fg-8a93a6); }
.bias-editor-completion-label { overflow: hidden; text-overflow: ellipsis; }

/* Своё контекстное меню редактора (запрос пользователя, 2026-08-03: "своё
   контекстное меню со стандартными командами") — position:fixed (координаты
   прямо из события клика мыши, см. openContextMenuAt в bias-editor.js), а
   не absolute внутри .bias-editor-code-area — так меню свободно от overflow:
   hidden/auto скроллящихся предков и всегда видно целиком, даже у самого
   края редактора. Визуальный язык — тот же, что у .bias-editor-completions
   рядом (белый фон, та же рамка/тень), но пункты крупнее (обычная команда
   меню, не построчный список автодополнения). */
.bias-editor-context-menu {
  position: fixed;
  z-index: 30;
  min-width: 180px;
  padding: 4px;
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-c7cedb);
  border-radius: 6px;
  box-shadow: 0 4px 14px var(--sh-0_0_0_0p18);
  font-size: 13px;
}
.bias-editor-context-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 4px;
  cursor: pointer;
  color: var(--fg-2a2a2a);
}
.bias-editor-context-item:hover { background: var(--bg-e8f0fe); }
.bias-editor-context-icon {
  flex: none;
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--fg-555566);
}
.bias-editor-context-icon .ws-icon { width: 14px; height: 14px; }
.bias-editor-context-sep {
  height: 1px;
  margin: 4px 6px;
  background: var(--bg-e2e6ee);
}

/* Статусная строка редактора (запрос пользователя 2026-07-28: "в статусной
   строке в левом углу показывать координаты курсора") — СВОЯ, внутри
   редактора, НЕ общий WS.setStatus в шапке Workspace (тот — для Статус()
   скрипта, другой смысл, см. его doc-комментарий ниже в этом же файле). */
.bias-editor-status {
  flex: none;
  padding: 3px 10px;
  font-size: 12px;
  color: var(--fg-666666);
  border-top: 1px solid var(--bd-dde3ea);
  background: var(--bg-f5f7fa);
}
.bias-tok-keyword { color: var(--fg-a626a4); font-weight: 600; }
.bias-tok-string { color: var(--fg-1a7a3c); }
.bias-tok-comment { color: var(--fg-8a939e); font-style: italic; }
.bias-tok-number { color: var(--fg-b5651d); }
.bias-tok-directive { color: var(--fg-2b579a); font-weight: 600; }
/* DSL форм (Форма/Группа/Поле/КонецПоля/Реквизит/Данные/... —
   запрос пользователя, 2026-08-04: "раскраску синтаксиса DSL в редакторе")
   — отдельный цвет от обычных ключевых слов (.bias-tok-keyword), чтобы DSL-
   разметка формы визуально отделялась от кода языка внутри того же файла. */
.bias-tok-dsl { color: var(--fg-0e7c86); font-weight: 600; }
/* "#Область"/"#КонецОбласти" (запрос пользователя, 2026-08-08) — как
   #region/#endregion в C#: чисто организационная разметка, движку
   безразлична (см. Lexer.SkipTrivia в core/Bias.Script) — приглушённый
   моноширинный вид, похожий на комментарий, но с явным акцентом (не
   курсив/серый как .bias-tok-comment), чтобы отличаться от обычного "//". */
.bias-tok-region { color: var(--fg-7a5c00); font-weight: 600; }
/* Свёрнутая строка (запрос пользователя, 2026-08-08: "фон подкрась более
   тёмным цветом") — весь заголовок ПОДСВЕЧИВАЕТСЯ КАК ОБЫЧНО (см. highlight()
   в JS — highlightChunk на реальный текст, не синтетическая метка), только
   фон строки темнее фона редактора (var(--fg-fbfbfc), см. .bias-editor выше) — сразу
   видно, что строка свёрнута И не редактируется (правки по всей строке
   запрещены, см. 'beforeinput' в bias-editor.js, не только по иконке).
   БЕЗ left-padding — сдвинул бы текст заголовка вправо относительно
   настоящей textarea под ним и разъехался бы клик-в-позицию. */
.bias-editor-folded-line { background: var(--bg-e4e8ee); border-radius: 3px; }
/* Иконка вместо текстового "⋯" (запрос пользователя: "'...' сделай
   пиктограмкой") — та же editorIcon()/renderIcon('moreHorizontal'), что и
   гутер-иконка "Форма" (см. .bias-editor-gutter-form выше). */
.bias-tok-fold-placeholder { color: var(--fg-6b7684); }
.bias-tok-fold-placeholder .ws-icon { width: 0.85em; height: 0.85em; vertical-align: -0.1em; }

/* ── Грид (Пользователи, см. loadUsersTab) — строка целиком кликабельна и
   фокусируема (tabindex, Enter/Space открывают), с подсветкой hover/focus/
   выбранной строки. ── */
.ws-grid {
  display: flex;
  flex-direction: column;
  width: 100%;
  /* Рамка — внутри ширины (2026-10-10): иначе таблица на 2 px шире своей области и даёт горизонтальную прокрутку. */
  box-sizing: border-box;
  /* Высота по содержимому (запрос пользователя, 2026-08-09: таблица не
     должна тянуться пустым хвостом до низа вкладки) — fit-content, не
     растягивание flex-родителем. */
  height: fit-content;
  flex: none;
  border: 1px solid var(--bd-dde3ea);
  /* Верхние углы НЕ скруглены — .ws-toolbar (см. выше) всегда идёт прямо
     над .ws-grid без зазора и скругляет только СВОИ верхние углы, стык
     между ними должен выглядеть одним цельным блоком, а не двумя разными
     скруглёнными прямоугольниками впритык. */
  border-radius: 0 0 6px 6px;
  /* Вбок прокручивается сама таблица, когда колонки дошли до минимума (2026-10-10); по высоте — по содержимому. */
  overflow-x: auto;
  overflow-y: hidden;
}
.ws-grid-row { display: grid; grid-template-columns: 1.1fr 1.4fr 1.2fr 0.8fr; }
/* Таблица DSL (см. FormDslBuiltins.RenderTable/wireTableGrids в Shell.html,
   запрос пользователя 2026-07-31: "Таблицу брать из наших компонентов") —
   число колонок ЗАРАНЕЕ не известно (любая .bias-форма может объявить
   сколько угодно "Колонка"), поэтому, в отличие от .ws-grid-roles/-services/
   -commands ниже (у каждой свой ФИКСИРОВАННЫЙ набор колонок), ширина колонок
   здесь через CSS-переменную --dsl-table-cols, проставляется один раз в JS
   при гидратации (см. wireTableGrids). */
.ws-grid-dsl-table .ws-grid-row { grid-template-columns: repeat(var(--dsl-table-cols, 1), 1fr); }
/* Роли — только 2 колонки (Название/Пользователей), см. loadRolesTab. */
.ws-grid-roles .ws-grid-row { grid-template-columns: 2.5fr 1fr; }
/* Активные пользователи (2026-10-04): логин, имя, куда, вход, активность, откуда. */
.ws-grid-sessions .ws-grid-row { grid-template-columns: 1.1fr 1.2fr 0.8fr 1fr 1.3fr 1.4fr; }
/* Сервисы — 5 колонок (Имя/Синоним/Описание/Адрес/Статус), см. loadServicesTab. */
.ws-grid-services .ws-grid-row { grid-template-columns: 1fr 1fr 1.6fr 1fr 0.8fr; }
/* Команды в карточке подсистемы — флажок/Имя/Синоним, см. loadSubsystemFormTab
   (запрос пользователя 2026-07-28) — checkbox-флажок в узкой первой колонке.
   Осторожно: ".ws-user-form-field input:not(.bias-field)" (см. ниже в файле)
   задаёт ЛЮБОМУ обычному текстовому input прямо в поле формы flex:1/
   min-width:0 — эта таблица лежит ВНУТРИ такого же .ws-user-form-field-
   multiline, и чекбокс без класса .bias-field невольно попадёт под тот же
   селектор (flex:1 схлопывает ширину в 0 — найдено живьём на "Модули:",
   тогда ещё плоском гриде, см. .ws-tree-module-flag выше — та же гоча,
   решена там явным width/flex:none) — при добавлении новых чекбоксов в
   формах эту гочу нужно помнить каждый раз. */
.ws-grid-commands .ws-grid-row { grid-template-columns: 2.2em 2fr 2fr; }
.ws-grid-commands .ws-grid-cell:first-child { display: flex; align-items: center; justify-content: center; }
/* Флажок — .bias-checkbox-input библиотеки (2026-10-03); здесь только защита от растяжения в ячейке. */
.ws-grid-commands .ws-grid-cell input.ws-command-flag { flex: none; min-width: auto; }
/* Глобальный поиск (см. loadGlobalSearchTab) — узкая «Строка», широкая «Текст». */
.ws-grid-global-search .ws-grid-row { grid-template-columns: 1.2fr 1fr 0.55fr 2.8fr; }
/* Синтакс-помощник (2026-10-09): имя, English, вид, сервер/WASM/JS/тест — узкие, использование. */
/* Синтакс-помощник по эталону: имя, English, вид, в оригинале, сервер/WASM/JS/тест, использование. */
.ws-grid-apiref .ws-grid-row { grid-template-columns: 2fr 1.5fr 0.9fr 1.1fr 1.1fr 0.6fr 0.55fr 0.4fr 0.6fr 0.8fr; }
.ws-grid-apicat .ws-grid-row { grid-template-columns: 2.2fr 1.5fr 1fr 1.1fr 0.65fr 0.55fr 0.4fr 0.5fr 1fr; }

/* "Структура БД" — конструктор справочников Bias.DB (см. loadDbStructureTab,
   план 2026-08-20). Константы — отдельный плоский блок НАД деревом (свои
   .dbstruct-node-*, см. renderConstants), само дерево справочников — НАСТОЯЩЕЕ
   сворачиваемое дерево на общих .ws-tree-row/.ws-tree-arrow/.ws-tree-icon
   (запрос пользователя 2026-08-27: "нормальное дерево... похожее на"
   референсный скрин — см. .dbstruct-row* ниже), поэтому у них РАЗНЫЕ базовые
   классы узла (.dbstruct-node vs .dbstruct-row) — общих правил на оба не
   заводим, проще держать раздельно, чем городить общий класс ради трёх строк. */
.dbstruct-node { display: flex; align-items: center; gap: 8px; padding: 4px 0; }
.dbstruct-node-icon { flex: none; color: var(--fg-8993a4); font-size: 13px; width: 14px; text-align: center; }
.dbstruct-node button { flex: none; }
/* Константы (запрос пользователя 2026-08-21: "объявляется только вверху") —
   отдельный блок НАД деревом справочников, не узел внутри него — нет отступа,
   свой заголовок. */
.dbstruct-constants-header { font-weight: 600; margin-top: 10px; color: var(--fg-555566); }
.dbstruct-node .dbstruct-cname, .dbstruct-node .dbstruct-ctype { flex: 1 1 auto; max-width: 220px; }

/* ── Дерево справочников (см. renderDbTree в Shell.html) — переиспользует
   .ws-tree-row/.ws-tree-arrow/.ws-tree-icon левой панели (та же визуальная
   грамматика во всём приложении), padding-left каждой строки — inline
   (глубина посчитана в JS, 4 уровня: Справочник/группа/элемент/реквизит
   табличной части). Кнопки-действия — по наведению/фокусу, не всегда видны
   (запрос пользователя): раньше на каждой строке всегда висело 2-3 кнопки
   вперемешку с полями ввода, тесно и шумно даже для маленького дерева. */
.dbstruct-row .dbstruct-name { flex: 1 1 auto; max-width: 260px; font-weight: 600; }
.dbstruct-row .dbstruct-fname, .dbstruct-row .dbstruct-tp-fname { flex: 1 1 auto; max-width: 220px; }
.dbstruct-row .dbstruct-ftype, .dbstruct-row .dbstruct-tp-ftype { flex: 1 1 auto; max-width: 220px; }
.dbstruct-row .dbstruct-tp-name { flex: 1 1 auto; max-width: 220px; font-weight: 600; }
.dbstruct-row .bias-checkbox { flex: none; margin-left: 10px; }
.dbstruct-leaf-icon { color: var(--fg-aeb8c7); font-size: 16px; }
/* Группирующие узлы "Реквизиты"/"Табличные части" — жирная подпись + счётчик
   вместо строки ввода (это не данные, а заголовок группы). */
.dbstruct-group-label { font-weight: 600; color: var(--fg-444455); }
.dbstruct-count { margin-left: 6px; color: var(--fg-8993a4); font-weight: 400; font-size: 12px; }
.dbstruct-count::before { content: '('; }
.dbstruct-count::after { content: ')'; }
/* Действия строки — справа, скрыты, пока строка не под курсором/фокусом
   (focus-within — чтобы кнопка "✕" не пропадала, пока курсор ещё в поле
   ввода этой же строки, только что переключённом по Tab). */
.dbstruct-actions { margin-left: auto; flex: none; display: flex; gap: 4px; opacity: 0; }
.dbstruct-row:hover .dbstruct-actions, .dbstruct-row:focus-within .dbstruct-actions { opacity: 1; }
.dbstruct-actions button { flex: none; }
/* Панель диффа перед "Применить" (см. renderChangeList) — деструктивная группа
   красным акцентом слева, чтобы сразу читалось "это НЕ будет сделано автоматом",
   не перепутать с обычным списком того, что применится. */
.dbstruct-diff-panel { margin-top: 12px; padding: 12px; border: 1px solid var(--bd-dde3ea); border-radius: 6px; background: var(--bg-f9fafb); }
.dbstruct-diff-group h4 { margin: 0 0 6px; font-size: 13px; }
.dbstruct-diff-group ul { margin: 0 0 10px; padding-left: 20px; }
.dbstruct-diff-additive { border-left: 3px solid var(--bd-2fa84f); padding-left: 10px; }
.dbstruct-diff-destructive { border-left: 3px solid var(--bd-d13b3b); padding-left: 10px; }
.ws-grid-header { background: var(--bg-f5f7fa); font-weight: 600; color: var(--fg-666666); font-size: 12px; text-transform: uppercase; letter-spacing: .02em; }
/* Сортировка по клику на заголовок (см. createBiasGrid/toggleSort в
   Shell.html) — пиктограмма ⇅ бледная, пока колонка не активна как критерий
   сортировки, ▲/▼ — направление, когда активна. */
.ws-grid-cell-sortable {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  outline: none;
}
.ws-grid-cell-sortable:hover, .ws-grid-cell-sortable:focus { color: var(--fg-234a85); }
.ws-grid-sort-icon { font-size: 11px; opacity: .45; flex: none; }
.ws-grid-cell-sortable:hover .ws-grid-sort-icon, .ws-grid-cell-sortable:focus .ws-grid-sort-icon { opacity: .75; }
.ws-grid-sort-active .ws-grid-sort-icon { opacity: 1; color: var(--fg-2b579a); }
.ws-grid-row:not(.ws-grid-header) {
  cursor: pointer;
  border-top: 1px solid var(--bd-eef1f4);
  outline: none;
  transition: background .1s ease;
}
/* Нижняя линия у последней строки (запрос пользователя, 2026-09-25: "в
   таблицах нет на последней строке горизонтальной линии грида") —
   разделители строк сделаны border-top, под последней строкой линии не было,
   что видно в растянутых таблицах с пустым местом ниже. Обычный грид —
   :last-child, оконный — .ws-grid-row-last (строки виртуализированы). */
:not(.ws-grid-windowed-spacer):not(.ws-grid-ghost) > .ws-grid-row:not(.ws-grid-header):last-child,
.ws-grid-row.ws-grid-row-last { border-bottom: 1px solid var(--bd-eef1f4); }
.ws-grid-row:not(.ws-grid-header):hover { background: var(--bg-eef4fb); }
/* Фокус/выбор — только ячейка (.ws-grid-cell-focused), не вся строка
   (запрос пользователя, 2026-08-09: глобальный поиск и стандартный грид).
   :focus на строке (tabindex) и .selected больше не красят фон строки. */
.ws-grid-row:not(.ws-grid-header):focus { background: transparent; box-shadow: none; }
.ws-grid-row:not(.ws-grid-header):focus:hover { background: var(--bg-eef4fb); }
.ws-grid-row.selected { background: transparent; }
.ws-grid-row.selected:hover { background: var(--bg-eef4fb); }
/* Найден "Автопоиском манифестов" (см. loadServicesTab), но ещё не загружен
   как реальный проект — информационная строка, без hover/выделения/клика.
   Селектор — ДВЕ склеенные классом ".ws-grid-row.ws-grid-row-pending" (та же
   специфичность, что у ".ws-grid-row:not(.ws-grid-header)" выше — :not(класс)
   считается как класс), а не одиночный ".ws-grid-row-pending" — тот проиграл
   бы по специфичности и просто не подействовал бы. */
.ws-grid-row.ws-grid-row-pending { cursor: default; opacity: 0.75; font-style: italic; }
.ws-grid-row.ws-grid-row-pending:hover { background: transparent; }
.ws-grid-cell { position: relative; padding: 9px 12px; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Пиктограмма типа строки справочника (запрос пользователя 2026-09-16:
   "в строках справочника в первой колонке должна быть пиктограмка типа
   строки") — папка/лист, только у таблиц с признаком ЭтоГруппа (см.
   groupColIndex/rowTypeIconHtml в bias-grid.js), вставляется inline перед
   текстом ячейки, поэтому нужен только отступ справа от иконки. */
.ws-grid-row-type-icon { display: inline-flex; align-items: center; margin-right: 6px; vertical-align: -3px; color: var(--fg-7a8699); position: relative; }
.ws-grid-row-type-icon svg { width: 14px; height: 14px; }
/* Цветные картинки строк справочника (wwwroot/icons, 2026-09-29). */
.ws-grid-row-type-img { width: 16px; height: 16px; display: block; }
.ws-grid-row-type-icon-marked .ws-grid-row-type-img { opacity: .55; filter: grayscale(.6); }
/* Пометка удаления (запрос пользователя, 2026-09-23: "меняется пиктограмка
   с крестиком, для групп тоже") — маленький красный крестик-бейдж поверх
   папки/листа (см. isMarkedRow/rowTypeIconHtml в bias-grid.js), тот же
   принцип наложения, что у исходной платформы — сама иконка типа строки не подменяется
   целиком, только помечается бейджем в углу. */
.ws-grid-row-type-icon-marked::after {
  content: '\2716';
  position: absolute;
  right: -4px;
  bottom: -3px;
  width: 9px;
  height: 9px;
  line-height: 9px;
  font-size: 7px;
  font-family: Arial, Helvetica, sans-serif;
  text-align: center;
  color: var(--fg-ffffff);
  background: var(--bg-d3392e);
  border-radius: 50%;
}
/* Треугольник разворачивания дерева (запрос пользователя 2026-09-16,
   скриншот исходной платформы: "сделай вот так") — РЕАЛЬНЫЙ кликабельный элемент (не
   ::before — нужно отличать клик по треугольнику от клика по остальной
   строке, см. click-обработчик в bias-grid.js), только у групповых строк
   (см. isGroupRow в windowRowHtml). ...-spacer — та же ширина+отступ БЕЗ
   иконки, у листьев внутри дерева, чтобы пиктограмма типа строки всех
   строк одного уровня начиналась с одной вертикали. */
.ws-grid-row-expand, .ws-grid-row-expand-spacer {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-right: 4px; vertical-align: -3px;
  border-radius: 2px;
}
.ws-grid-row-expand { color: var(--fg-5b7bab); cursor: pointer; }
.ws-grid-row-expand svg { width: 12px; height: 12px; }
.ws-grid-row-expand:hover { background: var(--bg-dde8fb); }
/* Первая ячейка со значками (треугольник/папка/лист) — значки слева по
   первой строке текста, при переносе переносится только текст (2026-09-29). */
.ws-grid-cell.ws-grid-cell-icon { display: flex; align-items: flex-start; }
.ws-grid-cell-icon > .ws-grid-row-expand, .ws-grid-cell-icon > .ws-grid-row-expand-spacer,
.ws-grid-cell-icon > .ws-grid-row-type-icon { flex: none; margin-top: 2px; vertical-align: 0; }
.ws-grid-cell-text { flex: 1; min-width: 0; }
/* Фокус на ячейку — единственная подсветка выбора (рамка + фон). См. focusCell
   в bias-grid.js. */
.ws-grid-cell-focused { box-shadow: inset 0 0 0 2px var(--sh-5b8dc9); background: var(--bg-c2d9f0); z-index: 1; }
/* Вертикальные линии сетки (запрос пользователя 2026-07-31: "добавь
   вертикальные линии в сетке грида") — тот же светлый цвет, что и
   горизонтальная граница строк (.ws-grid-row:not(.ws-grid-header) выше),
   между колонками, не после последней (+ соседний селектор — граница ТОЛЬКО
   у ячеек, у которых есть предыдущая соседняя, первая колонка её не
   получает — левый край и так уже даёт внешняя рамка .ws-grid). Один общий
   компонент — правило действует сразу везде, где есть Bias.Grid. */
.ws-grid-cell + .ws-grid-cell { border-left: 1px solid var(--bd-eef1f4); }
/* АвтоВысотаЯчейки колонки (DSL Таблица/Колонка, запрос пользователя
   2026-07-31: "Истина ячейку в таблице значение переносит и расширяет
   высоту иначе как сейчас") — снимает белые nowrap/text-overflow с базового
   .ws-grid-cell выше ТОЛЬКО у ячеек этой конкретной колонки (см.
   createBiasGrid/wireTableGrids). .ws-grid-row — display:grid без
   фиксированной высоты, значит высота строки сама подстраивается под самую
   высокую ячейку, доп.правки для этого не нужны. */
.ws-grid-cell-wrap { overflow: visible; white-space: normal; text-overflow: clip; word-break: break-word; }
/* Перенос текста во ВСЕХ ячейках данных (2026-09-29, запрос пользователя:
   "если в ячейку не влазит текст — переносить и изменять высоту ячейки") —
   строка растёт по самой высокой ячейке; в оконном гриде высоту строк
   измеряет bias-grid.js (measureWindowRows). Шапка — как была, в одну строку. */
.ws-grid-row:not(.ws-grid-header) .ws-grid-cell { white-space: normal; text-overflow: clip; overflow-wrap: anywhere; }
/* Невидимый слой замера высоты строки по ВСЕМ колонкам, включая те, что за
   горизонтальной прокруткой (measureGhostRows в bias-grid.js). Высота 0 —
   не влияет на прокрутку вьюпорта. */
/* Закреплённые группы дерева (renderPinnedTreeRows, 2026-09-30) — строка
   раскрытой группы стоит под шапкой, пока прокручивается её содержимое. */
.ws-grid-windowed-spacer > .ws-grid-row.ws-grid-row-pinned { background: var(--bg-ffffff); }
.ws-grid-windowed-spacer > .ws-grid-row.ws-grid-row-pinned:hover { background: var(--bg-eef4fb); }
.ws-grid-windowed-spacer > .ws-grid-row.ws-grid-row-pinned.selected { background: var(--bg-ffffff); }
.ws-grid-row-pinned-last { box-shadow: 0 2px 4px var(--sh-20_40_80_p14); }
.ws-grid-ghost { position: absolute; left: 0; top: 0; width: 0; height: 0; overflow: hidden; visibility: hidden; pointer-events: none; }
.ws-grid-ghost .ws-grid-ghost-line { display: flex; align-items: flex-start; width: max-content; min-height: 0; box-sizing: border-box; }
.ws-grid-ghost .ws-grid-cell { flex: none; box-sizing: border-box; }
/* Ручка изменения ширины колонки мышкой (запрос пользователя 2026-07-31:
   "чтобы можно было мышкой изменять ширину колонок, типа сплитера") —
   position:absolute на ПРАВОМ краю заголовка колонки (.ws-grid-cell выше
   получил position:relative специально для этого), не отдельная ячейка
   сетки — иначе число grid-items в шапке разошлось бы с числом ячеек в
   строках данных (см. createBiasGrid). Тот же цвет hover/курсор, что и
   у обычного .ws-splitter (левая панель/чат) — один и тот же визуальный
   язык "здесь можно потянуть". */
.ws-grid-col-resizer {
  position: absolute;
  top: 0;
  right: -4px;
  width: 7px;
  height: 100%;
  cursor: col-resize;
  z-index: 2;
}
.ws-grid-col-resizer:hover { background: var(--bg-cfe0f5); }
/* Несортируемая шапка (без .ws-grid-cell-sortable) — у ячейки overflow:hidden
   (многоточие у подписи), правая половина ручки (right:-4px) обрезалась и
   попасть в неё было почти невозможно (найдено 2026-09-25, таблица условий
   "Настройки списка") — ставим ручку ВНУТРЬ ячейки. */
.ws-grid-header > .ws-grid-cell:not(.ws-grid-cell-sortable) > .ws-grid-col-resizer { right: 0; }
.ws-grid-header > .ws-grid-cell:last-child > .ws-grid-col-resizer { right: 0; }

/* ── "Оконная" загрузка Таблицы значений (запрос пользователя, 2026-08-03,
   см. createBiasGrid.setWindowedSource) — данные остаются на сервере,
   виден только текущий диапазон. Строки в .ws-grid-windowed-spacer —
   position:absolute (виртуализация), высота распорки = Количество*38px,
   задаётся в JS. Второй заход (тот же день): динамический реflow колонок
   (см. computeColumnReflow в JS) — видимые колонки ВСЕГДА растянуты РОВНО
   под ширину вьюпорта, содержимое никогда не шире его — обычного
   горизонтального overflow-скролла больше НЕТ, "прокрутка" колонок — это
   смена набора видимых колонок (стрелки в тулбаре + горизонтальный
   wheel/трекпад). ── */
/* Поиск оконного грида — ВНУТРИ .ws-toolbar (запрос пользователя,
   2026-09-16: "поиск тоже в командную панель вставь, а не отдельно, и
   сделай её типовой для справочников") — раньше была своя отдельная полоса
   .ws-grid-windowed-toolbar НАД гридом, теперь поле живёт СРЕДИ CRUD-
   пиктограмм (см. createBiasGrid.setWindowedSource), margin-left:auto
   прижимает его к правому краю той же полосы. */
/* Поле поиска — .bias-field библиотеки (2026-10-03); здесь раскладка в панели и компактный размер. */
.ws-toolbar .ws-grid-search-wrap {
  position: relative;
  display: flex;
  align-items: center;
  margin-left: auto;
  flex: 0 1 220px;
  min-width: 120px;
}
.ws-toolbar .ws-grid-windowed-search {
  width: 100%;
  min-width: 0;
  max-width: none;
  padding: 5px 26px 5px 9px;
  font-size: 13px;
}
/* «×» очистки внутри поля поиска (2026-10-06): только у непустого поля. */
.ws-grid-search-clear {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; padding: 0; border: 0; border-radius: 3px;
  background: transparent; color: var(--fg-8a939e); cursor: pointer;
}
.ws-grid-search-clear:hover { color: var(--fg-333333); background: var(--bg-eef1f4); }
.ws-grid-search-clear .ws-icon { width: 14px; height: 14px; }
.ws-grid-windowed-search:placeholder-shown + .ws-grid-search-clear,
.ws-grid-windowed-search[style*="display: none"] + .ws-grid-search-clear { display: none; }

/* Стрелки пролистывания набора колонок (запрос пользователя, 2026-08-03,
   п.1: "скрол появляется, когда колонки не влазят") — компактные квадратные
   кнопки, тот же визуальный язык, что и .ws-toolbar-btn над обычными
   гридами (см. createBiasGrid.toolbarButton), но свои классы — это
   единственная кнопка ВНУТРИ ужe готового тулбара оконного режима, не
   общий тулбар CRUD-действий. */
.ws-grid-windowed-colnav {
  flex: none;
  min-width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-ffffff);
  color: var(--fg-2b579a);
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
  font-size: 11px;
  cursor: pointer;
}
.ws-grid-windowed-colnav:hover { background: var(--bg-eef4fb); border-color: var(--bd-2b579a); }
/* Заблокирована на границе (запрос пользователя, 2026-08-03: "если последняя
   колонка появилась — вправо прокрутки нет, и наоборот") — см. JS,
   buildWindowHeaderRow ставит .disabled по hiddenLeft/hiddenRight. */
.ws-grid-windowed-colnav:disabled { color: var(--fg-aab2bd); background: var(--bg-f5f7fa); border-color: var(--bd-e3e7ec); cursor: default; }
.ws-grid-windowed-colnav:disabled:hover { background: var(--bg-f5f7fa); border-color: var(--bd-e3e7ec); }
/* "N–M / K" — запрос пользователя, 2026-08-03: "горизонтальный скрол так и
   не появился" — маленькие стрелки легко не заметить, явный текст сразу
   показывает, что колонок больше, чем видно сейчас (см. buildWindowHeaderRow
   в JS, которая же и заполняет текст). Пусто, если все колонки и так
   помещаются — тогда сами стрелки тоже ничего не листают. */
.ws-grid-windowed-colpos { flex: none; font-size: 12px; color: var(--fg-666677); padding: 0 2px; white-space: nowrap; }
/* Высота — inline-стилем из JS (см. fitViewportHeight, запрос пользователя,
   2026-08-03, п.4) — здесь только min-height как безобидный fallback на
   случай, если JS ещё не отработал. overflow-x:hidden — реflow (см. JS)
   гарантирует, что видимое содержимое НИКОГДА не шире вьюпорта, обычный
   горизонтальный scroll-контейнер тут больше не нужен вообще. */
/* Обёртка вьюпорта — раньше здесь же была flex-строка с кастомным
   вертикальным скроллбаром справа (запрос 2026-08-03); скроллы вернули
   нативные (запрос пользователя, тот же день) — wrapper остаётся, чтобы
   не ломать разметку/селекторы. */
.ws-grid-windowed-body { display: block; }
.ws-grid-windowed-viewport { position: relative; overflow-y: auto; overflow-x: hidden; min-height: 180px; }
.ws-grid-windowed-spacer { position: relative; }
/* Заголовок — ВНУТРИ вьюпорта, прибит sticky сверху (см. site.css) — без
   этого он листался бы вместе со строками при вертикальном скролле, как
   обычная строка. z-index — чтобы строки, проезжающие "под" ним при
   вертикальном скролле, не перекрывали его собственный фон. */
.ws-grid-windowed-header-sticky { position: sticky; top: 0; z-index: 2; }
.ws-grid-windowed-spacer > .ws-grid-row { overflow: hidden; box-sizing: border-box; }
/* Нативный горизонтальный scrollbar (запрос пользователя, 2026-08-03) —
   overflow-x:auto у полосы ПОД гридом, внутри спейсер шириной = сумма
   ширин колонок (см. updateHScroll в bias-grid.js). Никакой кастомной
   отрисовки бегунка — браузерный scrollbar как есть. Скрыт (.ws-hidden),
   когда все колонки влазят. Справа — .ws-grid-windowed-scroll-corner
   шириной = вертикальный scrollbar вьюпорта, чтобы на стыке не было
   пустого белого квадрата (запрос: "угол сделай красивенько"). */
.ws-grid-windowed-hscroll-row {
  display: flex;
  align-items: stretch;
  border-top: 1px solid var(--bd-e3e7ec);
  background: var(--bg-f0f0f0);
}
.ws-grid-windowed-hscroll-row.ws-hidden { display: none; }
.ws-grid-windowed-hscroll {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  height: 16px;
  background: var(--bg-f0f0f0);
}
.ws-grid-windowed-hscroll-spacer { height: 1px; }
.ws-grid-windowed-scroll-corner {
  flex: none;
  box-sizing: border-box;
  background: var(--bg-f0f0f0);
  border-left: 1px solid var(--bd-e3e7ec);
}
.ws-grid-windowed-scroll-corner.ws-hidden { display: none; }

/* ── Форма добавления/редактирования (одна на оба случая, см. loadUsersTab) ── */
.ws-user-form {
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* Карточка "Сервис" (см. loadServiceFormTab) — шире обычной формы, у неё есть
   многострочное Описание (см. .ws-user-form-field-multiline), которому 360px
   тесно; однострочные поля (Путь/Синоним) внутри неё не тянутся во всю ширину
   сами по себе — см. .ws-user-form-field ниже, у них своя ширина не задана
   явно, просто flex:1 в границах контейнера. */
.ws-user-form-wide { max-width: 920px; }
/* Форма с Таблицей DSL (запрос пользователя 2026-07-31: "по умолчанию
   растягивай на всю область") — envelope.openPage (см.
   applyRunClientActionEnvelope в Shell.html) всегда оборачивает открытую
   форму в голый .ws-user-form (360px, рассчитан на узкие формы
   Имя/Email) — для формы с таблицей это тесно, а сколько понадобится
   места, заранее не известно (число строк/колонок разное у каждой
   .bias-формы), поэтому не фиксированная ширина, а вообще без потолка;
   класс навешивается САМ (см. wireTableGrids), без явного участия .bias. */
.ws-user-form-full-width { max-width: none; }
.ws-user-form-title { font-weight: 600; color: var(--fg-333333); }
.ws-user-form-field { display: flex; flex-direction: row; align-items: center; gap: 10px; font-size: 13px; color: var(--fg-444444); }
/* .ws-user-form-field[hidden] выше по специфичности равен встроенному
   [hidden]{display:none}, но идёт ПОЗЖЕ в каскаде — без явного правила
   ниже display:flex побеждал бы, и скрытые условные поля формы
   "Регламентное задание" (Интервал/Время/Дни недели, см.
   updateScheduleFieldsVisibility в Shell.html) всё равно оставались бы
   видимыми. Тот же приём уже применяется для .chat-msgtype-row[hidden] и
   соседних правил ниже по файлу. */
.ws-user-form-field[hidden] { display: none; }
/* Колонка подписей автоформы/служебных форм — не уже самой длинной подписи
   (--ws-form-label-w считает syncUserFormLabels, 2026-09-25), 150px —
   минимум по умолчанию; подпись не переносится и не обрезается. */
.ws-user-form-label { flex: 0 0 auto; min-width: var(--ws-form-label-w, 150px); white-space: nowrap; }
/* Форма объекта (2026-09-28): DSL-поля с подписью слева (ПолеСоСписком,
   Поле без Данные: Объект.X) — та же колонка подписей, двоеточие, отступ и
   ширина, что у полей реквизитов (.ws-user-form-field выше), иначе они
   "выпадают" из ряда. */
.bias-form[data-ar-catalog] .bias-field-with-header.bias-field-header-left {
  grid-template-columns: var(--ws-form-label-w, 150px) minmax(0, 1fr);
  column-gap: 10px;
  align-self: stretch;
  width: auto;
}
.bias-form[data-ar-catalog] .bias-field-with-header.bias-field-header-left > .bias-field-header-label { font-size: 13px; color: var(--fg-444444); white-space: nowrap; }
.bias-form[data-ar-catalog] .bias-field-with-header.bias-field-header-left > .bias-field-header-label::after { content: ':'; }
.bias-form[data-ar-catalog] .bias-field-with-header.bias-field-header-left > .bias-field-group,
.bias-form[data-ar-catalog] .bias-field-with-header.bias-field-header-left > .bias-field { justify-self: stretch; }
/* Горизонтальная группа: в колонку подписей встаёт только первый элемент
   (как у полей реквизитов ниже, .ar-slot:not(:first-child)); остальные —
   подпись прямо перед полем. */
.bias-form[data-ar-catalog] .bias-group-horizontal > .bias-field-with-header.bias-field-header-left:not(:first-child),
.bias-form[data-ar-catalog] .bias-group-horizontal-always > .bias-field-with-header.bias-field-header-left:not(:first-child) {
  grid-template-columns: max-content minmax(0, 1fr);
}
/* СквозноеВыравнивание: Использовать — в колонке ВСЕ подписи горизонтальной группы. */
.bias-group[data-through-align="Использовать"] > .ar-slot .ws-user-form-label { min-width: var(--ws-form-label-w, 150px) !important; }
.bias-form[data-ar-catalog] .bias-group[data-through-align="Использовать"] > .bias-field-with-header.bias-field-header-left {
  grid-template-columns: var(--ws-form-label-w, 150px) minmax(0, 1fr);
}
.ws-user-form-label::after { content: ':'; }
/* :not(.bias-field) — поля из библиотеки компонентов (.bias-field/.bias-select,
   см. core/Bias.Script/Ui/PageDslBuiltins.cs) сами полностью отвечают за
   свой вид (граница/паддинг/фокус-обводка), это правило — только для
   ОСТАЛЬНЫХ полей форм Админки, ещё не переведённых на общую библиотеку.
   Без исключения тут одинаковая специфичность (класс+тег) решалась бы
   порядком в каскаде — это правило идёт ПОЗЖЕ .bias-field в файле и просто
   перебивало бы его. */
.ws-user-form-field input:not(.bias-field) { flex: 1; min-width: 0; padding: 6px 8px; border: 1px solid var(--bd-c7cedb); border-radius: 4px; font-size: 13px; }
/* Поля библиотеки компонентов внутри .ws-user-form-field (флекс-строка,
   метка слева) — та же логика "тянуться на всю строку", что раньше давало
   universal-правило выше, но без узкого max-width:320px из .bias-field (тот
   расчитан на свободную канву DSL-формы, тут ширину держит сама форма). */
.ws-user-form-field .bias-field { flex: 1; min-width: 0; max-width: none; }
/* Пароль в карточке пользователя: поле + кнопка "Изменить пароль" в одну строку (2026-10-02). */
.ws-user-form-password { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; }
.ws-user-form-password .bias-btn { flex: none; white-space: nowrap; }
.ws-user-form-password .bias-field:disabled { background: var(--bg-f5f7fa); }
/* Многострочные поля (см. loadServiceFormTab) — заголовок НАД полем, не слева
   (как обычно у этой формы, см. .ws-user-form-field выше) — в одну строку с
   textarea заголовок расходовал бы половину и так небольшой ширины карточки. */
.ws-user-form-field-multiline { flex-direction: column; align-items: stretch; gap: 4px; }
/* flex: 0 0 150px у .ws-user-form-label — это flex-basis ГЛАВНОЙ оси, а она
   в .ws-user-form-field (row) горизонтальная, здесь после flex-direction:
   column выше — уже вертикальная: без сброса метка растягивалась на 150px
   ПО ВЫСОТЕ (тот самый разрыв между подписью и textarea), а не по ширине. */
.ws-user-form-field-multiline .ws-user-form-label { flex: none; }
.ws-user-form-field-multiline textarea:not(.bias-field) {
  width: 100%;
  min-width: 0;
  padding: 6px 8px;
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
  font-size: 13px;
  font-family: inherit;
  resize: vertical;
  box-sizing: border-box;
}
.ws-user-form-field-multiline textarea.bias-field { width: 100%; max-width: none; }

/* ── Закладки ВНУТРИ карточки (Модули/Команды в карточке подсистемы, запрос
   пользователя 2026-07-28: "сделай закладками внутри карточки") — свой
   маленький переключатель, НЕ общая вкладочная система Workspace (.ws-tab/
   .ws-tabbar — та про отдельные вкладки верхнего уровня, см. её комментарий).
   Стиль — подчёркивание снизу у активной, компактнее .ws-tab (это подраздел
   внутри уже открытой формы, не отдельная страница). ── */
.ws-subtabs { display: flex; gap: 4px; border-bottom: 1px solid var(--bd-dde3ea); margin-bottom: 10px; }
.ws-subtab-btn {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 8px 6px;
  font-size: 13px;
  color: var(--fg-666666);
  cursor: pointer;
}
.ws-subtab-btn:hover { color: var(--fg-234a85); }
.ws-subtab-btn.active { color: var(--fg-2b579a); border-bottom-color: var(--bd-2b579a); font-weight: 600; }

.ws-user-form-roles { display: flex; flex-wrap: wrap; gap: 10px; font-size: 13px; }
.ws-user-form-role { display: flex; align-items: center; gap: 4px; }
.ws-user-form-active { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.ws-user-form-actions { display: flex; gap: 8px; }
.ws-user-form-actions button {
  border-radius: 4px;
  padding: 6px 14px;
  font-size: 13px;
  cursor: pointer;
  border: 1px solid var(--bd-c7cedb);
  background: var(--bg-ffffff);
}
.ws-user-form-actions button:first-child { background: var(--bg-2b579a); color: var(--fg-ffffff); border-color: var(--bd-2b579a); }
.ws-user-form-actions button:first-child:hover { background: var(--bg-234a85); }
.ws-user-form-error { color: var(--fg-c62828); font-size: 12.5px; min-height: 1em; }

/* ── Табло сообщений ── */
/* display управляется ТОЛЬКО классом .visible, не атрибутом hidden — тот уже
   один раз дал баг (display:flex у .ws-messages той же специфичности, что и
   встроенный [hidden]{display:none}, перебивал его как более позднее правило
   автора). Явный класс убирает саму возможность такого конфликта. */
.ws-messages {
  display: none;
  flex-direction: column;
  flex: none;
  height: 140px;
  border-top: 1px solid var(--bd-dde3ea);
  background: var(--bg-ffffff);
}
.ws-messages.visible { display: flex; }
.ws-messages-header {
  flex: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 12px;
  font-size: 12.5px;
  color: var(--fg-777777);
  border-bottom: 1px solid var(--bd-eeeeee);
}
.ws-messages-close { background: none; border: none; font-size: 14px; cursor: pointer; color: var(--fg-999999); }
.ws-messages-body { flex: 1; overflow-y: auto; overflow-x: auto; padding: 4px 12px; }
.ws-message { padding: 1px 10px; border-radius: 4px; margin: 0; font-size: 13px; white-space: nowrap; line-height: 1.3; }
.ws-message-icon { display: inline-block; width: 14px; margin-right: 6px; font-size: 11px; text-align: center; }
.ws-message-info .ws-message-icon { color: var(--fg-3a6bc7); }
.ws-message-warning .ws-message-icon { color: var(--fg-b97a00); }
.ws-message-error .ws-message-icon { color: var(--fg-c62828); }
/* Структурные ошибки (план "грамотные ошибки", 2026-07-30) — заголовок
   "Модуль x:y текст" + строка-сниппет с маркером "<?>" на отдельной строке,
   .ws-message общий nowrap не годится только для этого вида сообщения. */
.ws-message-error { white-space: pre-line; font-family: Consolas, 'Courier New', monospace; }
/* Кликабельные Табло-сообщения (WS.showMessage(text, kind, loc), план
   "грамотные ошибки", 2026-07-30: "по клику из табло открывала модуль") —
   курсор-указатель + лёгкая подсветка при наведении, тот же акцент, что и
   .chat-goto-module-btn. */
.ws-message-clickable { cursor: pointer; }
.ws-message-clickable:hover { background: var(--bg-eef3fb); }

/* ── Статусная строка — всегда внизу, не скрывается (в отличие от табло
   сообщений выше), см. builtin "Статус" в Program.cs/extraBuiltins. ── */
.ws-statusbar {
  flex: none;
  display: flex;
  height: 22px;
  line-height: 22px;
  padding: 0 12px;
  background: var(--bg-dde3ea);
  color: var(--fg-444444);
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  border-top: 1px solid var(--bd-c7cedb);
}

/* ── Правая панель ── */
.ws-rightpanel {
  width: 280px;
  flex: none;
  background: var(--bg-fafbfc);
  border-left: 1px solid var(--bd-dde3ea);
  display: flex;
  flex-direction: column;
  transition: width .15s ease;
  overflow: hidden;
}
.ws-rightpanel.collapsed { width: 0 !important; border-left: none; }

/* ── Сплиттеры (разделители панелей, перетаскиваются мышью) ── */
.ws-splitter {
  width: 5px;
  flex: none;
  cursor: col-resize;
  background: transparent;
  position: relative;
}
.ws-splitter::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: 2px;
  width: 1px;
  background: var(--bg-dde3ea);
}
.ws-splitter:hover, .ws-splitter.dragging { background: var(--bg-cfe0f5); }
.ws-splitter.hidden { display: none; }
body.ws-resizing { cursor: col-resize; user-select: none; }

/* Горизонтальный сплиттер — над табло сообщений, тянется вверх/вниз. */
.ws-splitter-h {
  height: 5px;
  flex: none;
  cursor: row-resize;
  background: transparent;
  position: relative;
}
.ws-splitter-h::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 2px;
  height: 1px;
  background: var(--bg-dde3ea);
}
.ws-splitter-h:hover, .ws-splitter-h.dragging { background: var(--bg-cfe0f5); }
body.ws-resizing-v { cursor: row-resize; user-select: none; }
.ws-rightpanel-header {
  padding: 10px 14px;
  font-weight: 600;
  font-size: 13px;
  border-bottom: 1px solid var(--bd-dde3ea);
  color: var(--fg-444444);
}
.ws-rightpanel-body { flex: 1; padding: 14px; overflow-y: auto; }
.ws-placeholder { color: var(--fg-999999); font-size: 13px; }

/* ── Встроенный чат (правая панель) ── */
.ws-rightpanel-header { display: flex; align-items: center; justify-content: space-between; }
.ws-rightpanel-header .ws-icon-btn { color: var(--fg-444444); font-size: 15px; padding: 2px 6px; }

.ws-rightpanel-header .ws-icon-btn:hover { background: var(--bg-e7ecf3); }

.ws-rightpanel-body.chat-panel {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}

.chat-target-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 12px;
}
/* Выпадающие списки чата — .bias-field.bias-select библиотеки (2026-10-03); здесь раскладка и компактный размер. */
.chat-target { flex: 1; max-width: none; padding: 6px 30px 6px 8px; font-size: 13px; background-position: right 10px center; }
/* Красный/зелёный кружок статуса релея Клод-агента (см.
   refreshAgentStatus/updateAgentStatusDot в Shell.html) — виден только когда
   текущий собеседник в chat-target — сам агент. :not([hidden]) — ИНАЧЕ
   display тут (та же специфичность, что у встроенного [hidden]{display:none})
   навсегда перебивает атрибут hidden и кружок остаётся виден ВСЕГДА, даже в
   чужой переписке (тот же баг, что уже ловили на .chat-agent-typing
   2026-08-15 — здесь не был замечен раньше просто потому, что цвет без
   .online/.offline класса не бросается в глаза). */
.chat-agent-status-dot:not([hidden]) {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  display: inline-block;
}
.chat-agent-status-dot.online { background: var(--bg-2fa84f); box-shadow: 0 0 0 2px var(--sh-47_168_79_0p2); }
.chat-agent-status-dot.offline { background: var(--bg-d13b3b); box-shadow: 0 0 0 2px var(--sh-209_59_59_0p2); }
/* "Ждём ответа" — жёлтый, ПОВЕРХ online/offline (запрос пользователя
   2026-08-19: "после отправки запроса статус делай жёлтым кружочком") — см.
   awaitingAgentReply в Shell.html, снимается либо когда typing ушёл false
   ПОСЛЕ того как был true (реальный ответ), либо по подстраховке-таймауту. */
.chat-agent-status-dot.pending { background: var(--bg-e0a52c); box-shadow: 0 0 0 2px var(--sh-224_165_44_0p25); }

/* Тот же кружок, но ВНУТРИ кнопки "Отправить" (запрос пользователя
   2026-08-19: "в кнопке отправить показывай пиктограмму статуса кружочек") —
   красит paintStatusDot() синхронно с .chat-agent-status-dot в шапке, см.
   Shell.html. margin-right вместо gap на самой кнопке — вокруг него в
   .chat-composer-row button ещё текстовый узел, не только этот span. */
.chat-composer-send-dot:not([hidden]) {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
  margin-right: 6px;
  vertical-align: middle;
}
.chat-composer-send-dot.online { background: var(--bg-6fd88a); box-shadow: 0 0 0 2px var(--sh-111_216_138_0p35); }
.chat-composer-send-dot.offline { background: var(--bg-e58a8a); box-shadow: 0 0 0 2px var(--sh-229_138_138_0p35); }
.chat-composer-send-dot.pending { background: var(--bg-f5c563); box-shadow: 0 0 0 2px var(--sh-245_197_99_0p4); }

/* «Думает» (2026-10-05): пока ждём ответа / агент работает — кружок «дышит»
   (в шапке чата и в кнопке «Отправить»); цвет — от pending/online. */
.chat-agent-status-dot.thinking, .chat-composer-send-dot.thinking { animation: bias-agent-think 1.1s ease-in-out infinite; }
@keyframes bias-agent-think {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(.6); opacity: .5; }
}
@media (prefers-reduced-motion: reduce) {
  .chat-agent-status-dot.thinking, .chat-composer-send-dot.thinking { animation: none; }
}

/* "Печатает..." (см. updateAgentStatusDot/refreshAgentStatus в Shell.html) —
   классические три прыгающих точки, под лентой, только пока агент реально
   выполняет claude -p (см. ClaudeAgentStore.SetTyping/IsTyping). */
/* :not([hidden]) — иначе display:flex тут (та же специфичность, что у
   встроенного [hidden]{display:none}) навсегда перебивает атрибут hidden и
   точки бегают ПОСТОЯННО, даже когда агент не печатает (баг, пойман юзером
   2026-08-15: "опять тупишь, теперь три точки постоянно бегают"). */
.chat-agent-typing { padding: 4px 12px; }
.chat-agent-typing:not([hidden]) { display: flex; align-items: center; gap: 8px; }
.chat-agent-typing-dots { display: inline-flex; gap: 4px; flex: none; }
.chat-agent-typing-dots i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--bg-8b96a5);
  display: inline-block;
  animation: chat-agent-typing-bounce 1.2s infinite ease-in-out;
}
.chat-agent-typing-dots i:nth-child(2) { animation-delay: 0.15s; }
.chat-agent-typing-dots i:nth-child(3) { animation-delay: 0.3s; }
@keyframes chat-agent-typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-4px); opacity: 1; }
}
/* Текст текущего шага (запрос пользователя, 2026-08-14: "вместо точек видно
   промежуточные результаты") — рядом с точками, не вместо них: точки
   остаются общим "жив/работает" сигналом (видны даже когда статус ещё не
   пришёл или сброшен пустым), текст — что именно сейчас происходит (см.
   ClaudeAgentStore.StatusText/relay.js describeEvent). min-width: 0 —
   обязателен для text-overflow внутри flex-контейнера (без него ellipsis
   не сработал бы, flex-элемент не сжался бы ниже содержимого). */
.chat-agent-typing-status {
  min-width: 0;
  flex: 1 1 auto;
  font-size: 12px;
  color: var(--fg-7a8494);
  font-style: italic;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-msgtype-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 6px 12px;
  flex: none;
}
.chat-msgtype-row[hidden] { display: none; }

.chat-load-older {
  flex: none;
  margin: 6px 12px 0;
  padding: 4px;
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
  background: var(--bg-f4f6f9);
  color: var(--fg-2b579a);
  font-size: 11px;
  cursor: pointer;
}
.chat-load-older:hover { background: var(--bg-e7ecf3); }
.chat-load-older:disabled { opacity: 0.6; cursor: default; }
.chat-load-older[hidden] { display: none; }
.chat-msgtype, .chat-calctype { flex: 1; max-width: none; padding: 5px 30px 5px 8px; font-size: 12px; background-position: right 10px center; }
.chat-terminal-reset, .chat-clear-feed {
  flex: none;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
  font-size: 14px;
  line-height: 1;
  background: var(--bg-f4f6f9);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.chat-terminal-reset:hover, .chat-clear-feed:hover { background: var(--bg-e7ecf3); }
.chat-terminal-reset[hidden], .chat-calctype[hidden] { display: none; }

.chat-feed {
  flex: 1;
  overflow-y: auto;
  padding: 8px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.chat-feed-empty { color: var(--fg-999999); font-size: 13px; text-align: center; margin-top: 20px; }

.chat-message {
  max-width: 92%;
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 13px;
  line-height: 1.4;
  background: var(--bg-eef1f4);
  align-self: flex-start;
}
.chat-message.mine { background: var(--bg-d9e8fb); align-self: flex-end; }
.chat-message-author { font-weight: 600; font-size: 11px; color: var(--fg-2b579a); margin-bottom: 2px; }
.chat-message.mine .chat-message-author { display: none; }
.chat-message-text { white-space: pre-wrap; word-break: break-word; }
.chat-message-text.chat-text-collapsed {
  max-height: 5.6em; /* ~4 строки при line-height 1.4 */
  overflow: hidden;
}
.chat-text-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-top: 4px;
  padding: 0;
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
  background: var(--bg-255_255_255_0p6);
  font-size: 10px;
  line-height: 1;
  cursor: pointer;
}
.chat-text-toggle:hover { background: var(--bg-ffffff); }

.chat-note-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 7px;
  line-height: 1;
  color: var(--fg-8a93a6);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 3px;
}
.chat-note-header .chat-text-toggle {
  width: 14px;
  height: 14px;
  margin-top: 0;
  font-size: 8px;
  flex: none;
}

/* MsgType=="error" — план "грамотные ошибки" (2026-07-30, decision 3) — тот
   же каркас, что и .chat-note-header выше, красный акцент вместо серого. */
.chat-error-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 10px;
  line-height: 1.3;
  color: var(--fg-c0392b);
  font-weight: 600;
  margin-bottom: 3px;
}
.chat-error-header .chat-text-toggle {
  width: 14px;
  height: 14px;
  margin-top: 0;
  font-size: 8px;
  flex: none;
}
.chat-goto-module-btn { color: var(--fg-2b579a); }
.chat-goto-module-btn:hover { color: var(--fg-17325c); }

.chat-message-reply {
  border-left: 2px solid var(--bd-9fb6d6);
  padding: 2px 6px;
  margin-bottom: 4px;
  font-size: 11px;
  color: var(--fg-666677);
  background: var(--bg-255_255_255_0p5);
  cursor: pointer;
}
.chat-message-reply:hover { background: var(--bg-255_255_255_0p85); }

/* Переход к оригиналу по клику на цитату (см. gotoRepliedMessage в Shell.html) —
   кратковременная подсветка найденного сообщения. */
.chat-message.chat-message-flash { animation: chat-flash 1.5s ease-out; }
@keyframes chat-flash {
  0%, 40% { box-shadow: 0 0 0 2px var(--sh-f0b429); }
  100% { box-shadow: 0 0 0 2px transparent; }
}

.chat-message-attachments { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
/* Двойной клик/Enter/пункт "Открыть картинку" в контекстном меню — открыть
   просмотр (см. openImagePreview в Shell.html); одиночный клик теперь ничего
   не делает (см. class-doc у RenderChatFeed — раньше был <a target="_blank">,
   мешал новому выделению сообщения кликом). */
.chat-attachment-image { display: inline-block; line-height: 0; outline: none; border-radius: 4px; }
.chat-attachment-image:focus-visible { outline: 2px solid var(--bd-2b579a); outline-offset: 2px; }
.chat-attachment-image img { max-width: 8rem; max-height: 8rem; border-radius: 4px; display: block; }

/* Полноэкранный просмотр картинки (см. #chat-image-preview-overlay в Shell.html) —
   один оверлей на всю оболочку, содержимое (src) подставляется JS. */
.ws-image-preview-overlay {
  position: fixed;
  inset: 0;
  background: var(--bg-0_0_0_0p85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.ws-image-preview-overlay[hidden] { display: none; }
.ws-image-preview-overlay img { max-width: 92vw; max-height: 92vh; border-radius: 4px; box-shadow: 0 8px 40px var(--sh-0_0_0_0p5); }
.ws-image-preview-close {
  position: absolute;
  top: 16px;
  right: 20px;
  width: 36px;
  height: 36px;
  background: var(--bg-255_255_255_0p15);
  color: var(--fg-ffffff);
  border: none;
  border-radius: 50%;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}
.ws-image-preview-close:hover { background: var(--bg-255_255_255_0p3); }

.chat-attachment-file {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 12px;
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-c7cedb);
  font-size: 11px;
  text-decoration: none;
  color: var(--fg-333333);
}

.chat-message-footer { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
.chat-message-footer button {
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: 11px;
  color: var(--fg-666677);
  padding: 0 2px;
}
.chat-message-footer button:hover { text-decoration: underline; }
.chat-like-btn.liked { color: var(--fg-c0392b); font-weight: 600; }
.chat-message-time { margin-left: auto; font-size: 10px; color: var(--fg-999999); }

/* Клик по сообщению (см. Shell.html selectMessage/feed click-обработчик) —
   постоянная (не анимированная, в отличие от .chat-message-flash выше) рамка,
   держится, пока не кликнут мимо ленты или другое сообщение. */
.chat-message.selected { box-shadow: 0 0 0 2px var(--sh-e67e22); }

/* Контекстное меню по правому клику на сообщении (см. openContextMenu). */
.chat-context-menu {
  position: fixed;
  z-index: 500;
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-c7cedb);
  border-radius: 6px;
  box-shadow: 0 4px 16px var(--sh-0_0_0_0p18);
  padding: 4px;
  min-width: 180px;
  font-size: 13px;
}
.chat-context-menu button {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  padding: 6px 10px;
  border-radius: 4px;
  cursor: pointer;
  color: var(--fg-333333);
  font-size: 13px;
}
.chat-context-menu button:hover { background: var(--bg-eef1f4); }
.chat-context-menu button.danger { color: var(--fg-c0392b); }
.chat-context-menu hr { border: none; border-top: 1px solid var(--bd-eeeeee); margin: 4px 0; }

.chat-reply-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 12px;
  font-size: 11px;
  background: var(--bg-fff6dd);
  border-top: 1px solid var(--bd-eddca0);
}
/* Стилевое правило класса выше побеждает УА-стиль "[hidden] { display: none }"
   (авторские стили всегда сильнее агента пользователя, вне зависимости от
   специфичности) — без этого явного переопределения панель оставалась
   видимой (и пустой) даже когда JS выставлял hidden=true (см. cancelReply). */
.chat-reply-bar[hidden] { display: none; }
.chat-reply-bar button { background: none; border: none; cursor: pointer; font-size: 14px; }

/* height задаётся один раз здесь (стартовый размер) и дальше меняется
   ТОЛЬКО через #chat-splitter (см. makeSplitterV в Shell.html) — textarea
   внутри flex:1, поэтому растёт/сжимается вместе с самим composer'ом, без
   отдельного нативного resize:vertical на textarea (было бы два разных
   способа тянуть один и тот же размер — путаница). */
.chat-composer {
  border-top: 1px solid var(--bd-dde3ea);
  padding: 8px 12px;
  flex: none;
  height: 96px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}
.chat-composer textarea {
  width: 100%;
  box-sizing: border-box;
  flex: 1;
  min-height: 0;
  resize: none;
  font: inherit;
  font-size: 13px;
  padding: 6px 8px;
  border: 1px solid var(--bd-c7cedb);
  border-radius: 4px;
}
.chat-composer-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex: none; }
.chat-composer-attach-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 4px;
  font-size: 16px;
  cursor: pointer;
  color: var(--fg-555566);
}
.chat-composer-attach-btn:hover { background: var(--bg-e7ecf3); }
.chat-composer-files-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 11px;
  color: var(--fg-666677);
}
.chat-composer-row button {
  background: var(--bg-2b579a);
  color: var(--fg-ffffff);
  border: none;
  border-radius: 4px;
  padding: 6px 14px;
  cursor: pointer;
  font-size: 13px;
}
.chat-composer-row button:hover { background: var(--bg-234a85); }
/* Отмена редактирования (см. doEdit/cancelEdit в Shell.html) — нейтральная,
   не такая заметная, как основная синяя "Сохранить"/"Отправить" рядом. */
.chat-composer-row .chat-cancel-edit-btn { background: var(--bg-ffffff); color: var(--fg-555566); border: 1px solid var(--bd-c7cedb); }
.chat-composer-row .chat-cancel-edit-btn:hover { background: var(--bg-eef1f4); }
.chat-composer-row .chat-cancel-edit-btn[hidden] { display: none; }

/* Настройки чата — вкладка в Админке (см. loadChatSettingsTab в Shell.html),
   не правая панель — обычная ширина страницы, не узкая колонка. */
.chat-settings-tab {
  max-width: 920px;
  font-size: 13px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.chat-settings-field { display: flex; flex-direction: column; gap: 4px; }
.chat-settings-field input:not(.bias-field) { padding: 6px 8px; border: 1px solid var(--bd-c7cedb); border-radius: 4px; font-size: 13px; }
.chat-settings-field input.bias-field { max-width: 480px; }
.chat-settings-tab > button, .chat-settings-key button {
  align-self: flex-start;
  background: var(--bg-2b579a);
  color: var(--fg-ffffff);
  border: none;
  border-radius: 4px;
  padding: 6px 14px;
  cursor: pointer;
  font-size: 13px;
}
.chat-settings-photo { display: flex; align-items: center; gap: 10px; }
.chat-settings-photo img { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; }
.chat-settings-key { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.chat-settings-key span { font-family: Consolas, monospace; font-size: 12px; word-break: break-all; }
/* Кнопка-иконка "Скопировать ключ" — без заливки, как пиктограммы командной панели. */
.chat-settings-key button.chat-settings-key-copy {
  background: transparent;
  color: var(--fg-2b579a);
  border: 1px solid var(--bd-c7cedb);
  padding: 4px 6px;
  line-height: 0;
}
.chat-settings-key button.chat-settings-key-copy:hover { background: var(--bg-eef3fa); border-color: var(--bd-9fb6d6); }
.chat-settings-key button.chat-settings-key-copy[hidden] { display: none; }
.chat-settings-groups-title { margin: 22px 0 10px; font-size: 14px; color: var(--fg-333333); }

/* Закладка "Клод-агент" внутри "Настройки чата" (см. renderClaudeAgentSettings
   в Shell.html) — тот же .chat-settings-field/bias-field/bias-checkbox стиль, что
   у остальных полей этой страницы. */
.claude-agent-hint { max-width: 640px; font-size: 12px; color: var(--fg-5a6472); margin: 0 0 12px; }
.claude-agent-enabled { margin-bottom: 10px; }
.claude-agent-tools { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 14px; }
.claude-agent-tool-danger .bias-checkbox-label { color: var(--fg-aa3333); font-weight: 600; }
#claude-agent-save-status { margin-left: 10px; font-size: 12px; color: var(--fg-5a6472); }

/* ── Кастомный Да/Нет/Отмена (см. showConfirmDialog в Shell.html) ── */
/* display управляется ТОЛЬКО классом .visible, не атрибутом hidden — та же
   ловушка, что уже раз поймали на .ws-messages выше (display:flex автора той
   же специфичности, что встроенный [hidden]{display:none}, побеждает как более
   позднее правило — окно физически не скрывалось, оставалось "зависшим"). */
.ws-modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: var(--bg-20_26_36_0p45);
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.ws-modal-overlay.visible { display: flex; }
/* Окно формы (Открыть/ОткрытьМодально, 2026-10-01) — форма поверх формы-владельца. */
.ws-modal.ws-form-window { max-width: min(1100px, calc(100vw - 32px)); min-width: 420px; max-height: calc(100vh - 48px); overflow: auto; box-sizing: border-box; padding: 8px 16px 16px; }
.ws-form-window-page { display: block; padding: 4px 0 0; min-height: 0; }
/* Окно сборки подсистемы (Админка → профиль подсистемы → Собрать, 2026-09-28). */
.ws-modal.ws-build-dialog { width: min(1100px, calc(100vw - 32px)); max-width: none; display: flex; flex-direction: column; max-height: calc(100vh - 48px); box-sizing: border-box; }
.ws-build-toolbar { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
.ws-build-toolbar .bias-checkbox { margin-left: 12px; }
.ws-build-status { font-size: 12px; color: var(--fg-3a4452); margin: 4px 0 8px; min-height: 16px; }
.ws-build-status.ws-build-status-error { color: var(--fg-c62828); }
.ws-build-grid .ws-grid-windowed-viewport { height: 55vh; }
/* Окно загрузки из OData (профиль подсистемы, 2026-09-28). */
.ws-odata-grid { max-height: 30vh; overflow: auto; }
.ws-odata-log { max-height: 30vh; overflow: auto; margin: 8px 0 0; padding: 8px; background: var(--bg-f5f7fa); border: 1px solid var(--bd-dfe3ea); border-radius: 4px; font-size: 12px; white-space: pre-wrap; }
.ws-build-state { font-size: 12px; color: var(--fg-3a4452); margin: 2px 0 6px; }
.ws-build-state.ws-build-state-pending { color: var(--fg-b97a00); }
.ws-modal {
  background: var(--bg-ffffff);
  border-radius: 8px;
  box-shadow: 0 12px 32px var(--sh-0_0_0_0p25);
  padding: 10px 24px 22px;
  min-width: 320px;
  max-width: 420px;
}
.ws-modal-header { display: flex; justify-content: flex-end; }
/* Заголовок диалога (пока только у WS.prompt — см. .ws-modal-prompt-content
   ниже, запрос пользователя 2026-07-28 "этой форме сделай заголовок") —
   margin-right:auto прижимает его к ЛЕВОМУ краю ВНУТРИ .ws-modal-header,
   при этом крестик (второй ребёнок) остаётся у ПРАВОГО, как и раньше —
   не меняем сам .ws-modal-header на justify-content:space-between, у
   confirm-диалога (см. #ws-confirm-close выше) в этой шапке ТОЛЬКО крестик,
   space-between с одним ребёнком прижал бы его к ЛЕВОМУ краю, сломав
   существующий вид. */
.ws-modal-title { font-weight: 600; font-size: 15px; color: var(--fg-222222); margin-right: auto; }
.ws-modal-close {
  background: none;
  border: none;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  color: var(--fg-888888);
  padding: 4px 8px;
  border-radius: 4px;
}
.ws-modal-close:hover { background: var(--bg-eef1f4); color: var(--fg-333333); }
.ws-modal-body { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 18px; }
.ws-modal-icon { flex: none; font-size: 26px; line-height: 1.3; color: var(--fg-d68a00); }
.ws-modal-text { font-size: 14px; color: var(--fg-2a2a2a); line-height: 1.5; }
.ws-modal-actions { display: flex; justify-content: flex-end; gap: 8px; }
.ws-modal-actions button {
  border-radius: 4px;
  padding: 7px 16px;
  font-size: 13px;
  cursor: pointer;
  border: 1px solid var(--bd-c7cedb);
  background: var(--bg-ffffff);
}
.ws-modal-actions button:hover { background: var(--bg-eef1f4); }
.ws-modal-actions #ws-confirm-yes { background: var(--bg-2b579a); color: var(--fg-ffffff); border-color: var(--bd-2b579a); }
.ws-modal-actions #ws-confirm-yes:hover { background: var(--bg-234a85); }

/* ── WS.prompt (см. wwwroot/components/bias-prompt.js) — тот же .ws-modal-*
   каркас, что у Да/Нет/Отмена выше, плюс поле ввода под текстом вопроса.
   Кнопка "OK" — свой класс (не #ws-confirm-yes — тот id принадлежит ДРУГОМУ,
   статическому диалогу), тот же синий акцент. Селектор — ".ws-modal-actions
   .ws-modal-primary-btn", НЕ голый ".ws-modal-primary-btn": специфичность
   ".ws-modal-actions button" выше голого одного класса (класс+тип > один
   класс), тот правило и побеждало — фон оставался БЕЛЫМ от общего правила
   button, а цвет ТЕКСТА белым от этого правила — белым по белому текст
   "OK" был не виден (найдено живьём по скриншоту пользователя). ── */
.ws-modal-prompt-content { display: flex; flex-direction: column; gap: 10px; flex: 1; }
.ws-modal-prompt-input { width: 100%; }
.ws-modal-actions .ws-modal-primary-btn { background: var(--bg-2b579a); color: var(--fg-ffffff); border-color: var(--bd-2b579a); }
.ws-modal-actions .ws-modal-primary-btn:hover { background: var(--bg-234a85); }

/* ── WS.showScriptError (см. wwwroot/components/bias-error-dialog.js) —
   план "грамотные ошибки" (2026-07-30): decision 2, рантайм-ошибка ТОЛЬКО в
   модалке, не в Табло. Тот же .ws-modal-* каркас, что и WS.prompt выше, шире
   (.ws-modal-script-error) — заголовок "Модуль x:y" + текст ошибки могут быть
   длиннее обычного вопроса/подтверждения. ── */
.ws-modal-script-error { width: 480px; max-width: 480px; }
.ws-modal-icon-error { color: var(--fg-c62828); }
.ws-modal-script-error-header { font-family: Consolas, 'Courier New', monospace; font-size: 13px; color: var(--fg-666666); }
.ws-modal-script-error-text { font-size: 14px; color: var(--fg-2a2a2a); line-height: 1.5; white-space: pre-line; }

/* ── Контекстное меню Bias.Grid (правый клик по строке/таблице — см.
   createBiasGrid в Shell.html) — те же действия, что и в тулбаре ── */
.ws-context-menu {
  position: fixed;
  z-index: 1000;
  background: var(--bg-ffffff);
  border: 1px solid var(--bd-dde3ea);
  border-radius: 6px;
  box-shadow: 0 8px 24px var(--sh-0_0_0_0p18);
  padding: 4px;
  min-width: 160px;
  display: flex;
  flex-direction: column;
}
.ws-context-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 7px 10px;
  font-size: 13px;
  color: var(--fg-222222);
  border-radius: 4px;
  cursor: pointer;
}
.ws-context-menu-item:hover { background: var(--bg-eef1f4); }
.ws-context-menu-item:disabled { color: var(--fg-8a939e); cursor: default; background: transparent; }
/* Пункты = кнопки командной панели таблицы (2026-09-25): пиктограмма + подпись. */
.ws-context-menu-item { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.ws-context-menu-icon { flex: none; width: 16px; display: inline-flex; align-items: center; justify-content: center; color: var(--fg-5a6472); }
.ws-context-menu-icon svg { width: 15px; height: 15px; }
.ws-context-menu-sep { height: 1px; margin: 4px 2px; background: var(--bg-e5e9ee); }

/* Табличные части автоформы элемента справочника (запрос пользователя,
   2026-09-23) — Bias.Grid в памяти формы, см. arBuildPartGrid в Shell.html. */
.ar-part { margin-top: 18px; }
.ar-part-title { font-weight: 600; margin: 0 0 6px; }
/* Правка ячейки прямо в таблице (options.cellEditor в bias-grid.js). */
/* Правка ячейки (2026-09-30, запрос пользователя: "растяни поле ввода на всю
   ячейку … убери рамку синюю округлую"): поле на всю ячейку, без своей
   рамки/скругления/подсветки фокуса — ячейку выделяет её рамка
   (.ws-grid-cell-focused, 2 px внутрь, её и видно по краю). */
.ws-grid-cell.ws-grid-cell-editing { padding: 2px; overflow: visible; white-space: nowrap; }
.ws-grid-cell-editing .ar-cell-input,
.ws-grid-cell-editing .ar-cell-input:focus {
  width: 100%; max-width: none; box-sizing: border-box; height: 100%; min-height: 32px; padding: 2px 9px;
  border: 0; border-radius: 0; box-shadow: none; outline: none; background: var(--bg-ffffff);
}
.ws-grid-cell-editing .bias-ls-refedit { gap: 0; background: var(--bg-ffffff); }
.ws-grid-cell-editing .bias-ls-refpick { border: 0; border-left: 1px solid var(--bd-e3e8ee); border-radius: 0; min-width: 30px; width: 30px; background: transparent; }
.ws-grid-cell-editing .bias-ls-refpick:hover { background: var(--bg-eef1f4); }
/* Кнопки выбора из списка и очистки — пиктограммы вместо текста "..."/"×"
   (2026-09-30: "поправь кнопочку ... или замени пиктограмкой"), везде:
   ячейки, карточки, настройка списка, выбор типа. Цвет — цвет кнопки. */
:is(.bias-field-btn, .bias-ls-refpick)[data-ref-pick], :is(.bias-field-btn, .bias-ls-refpick)[data-ar-ref-pick], :is(.bias-field-btn, .bias-ls-refpick)[data-ls-ref-pick], :is(.bias-field-btn, .bias-ls-refpick)[data-comp-choose], :is(.bias-field-btn, .bias-ls-refpick)[data-ar-comp-pick], :is(.bias-field-btn, .bias-ls-refpick)[data-build-choose], :is(.bias-field-btn, .bias-ls-refpick)#ccard-action-pick, :is(.bias-field-btn, .bias-ls-refpick)[data-comp-clear], :is(.bias-field-btn, .bias-ls-refpick)[data-ar-ref-clear], :is(.bias-field-btn, .bias-ls-refpick)[data-ar-comp-clear], :is(.bias-field-btn, .bias-ls-refpick)[data-ar-enum-clear] { font-size: 0; display: inline-flex; align-items: center; justify-content: center; }
:is(.bias-field-btn, .bias-ls-refpick)[data-ref-pick]::before, :is(.bias-field-btn, .bias-ls-refpick)[data-ar-ref-pick]::before, :is(.bias-field-btn, .bias-ls-refpick)[data-ls-ref-pick]::before, :is(.bias-field-btn, .bias-ls-refpick)[data-comp-choose]::before, :is(.bias-field-btn, .bias-ls-refpick)[data-ar-comp-pick]::before, :is(.bias-field-btn, .bias-ls-refpick)[data-build-choose]::before, :is(.bias-field-btn, .bias-ls-refpick)#ccard-action-pick::before {
  content: ""; width: 16px; height: 16px; background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%206h12M4%2011h8M4%2016h5'/%3E%3Ccircle%20cx='16'%20cy='15'%20r='3.6'/%3E%3Cpath%20d='M18.6%2017.6L21%2020'/%3E%3C/svg%3E") center / 16px 16px no-repeat; mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M4%206h12M4%2011h8M4%2016h5'/%3E%3Ccircle%20cx='16'%20cy='15'%20r='3.6'/%3E%3Cpath%20d='M18.6%2017.6L21%2020'/%3E%3C/svg%3E") center / 16px 16px no-repeat;
}
:is(.bias-field-btn, .bias-ls-refpick)[data-comp-clear]::before, :is(.bias-field-btn, .bias-ls-refpick)[data-ar-ref-clear]::before, :is(.bias-field-btn, .bias-ls-refpick)[data-ar-comp-clear]::before, :is(.bias-field-btn, .bias-ls-refpick)[data-ar-enum-clear]::before {
  content: ""; width: 14px; height: 14px; background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M7%207l10%2010M17%207L7%2017'/%3E%3C/svg%3E") center / 14px 14px no-repeat; mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M7%207l10%2010M17%207L7%2017'/%3E%3C/svg%3E") center / 14px 14px no-repeat;
}

/* ── Настройка списка (запрос пользователя, 2026-09-25) — диалог над оконным
   гридом (см. showListSettingsDialog в bias-grid.js): тот же .ws-modal
   каркас, шире; слева "Доступные поля", справа условия Отбора/Сортировки. ── */
.ws-toolbar-btn.ws-toolbar-btn-active { background: var(--bg-dde7f7); border-color: var(--bd-2b579a); color: var(--fg-2b579a); }
.bias-ls-dialog { max-width: min(920px, calc(100vw - 32px)); width: 920px; display: flex; flex-direction: column; max-height: calc(100vh - 48px); box-sizing: border-box; }
.bias-ls-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--bd-d8dee8); margin: 6px 0 10px; }
.bias-ls-tab {
  background: none; border: 1px solid transparent; border-bottom: none; border-radius: 6px 6px 0 0;
  padding: 7px 16px; font-size: 13px; color: var(--fg-444444); cursor: pointer; margin-bottom: -1px;
}
.bias-ls-tab:hover { background: var(--bg-f2f5fa); }
.bias-ls-tab.active { background: var(--bg-ffffff); border-color: var(--bd-d8dee8); color: var(--fg-2b579a); font-weight: 600; }
.bias-ls-body { display: flex; min-height: 340px; height: 52vh; flex: 1 1 auto; overflow: hidden; margin-top: 8px; }
.bias-ls-fields { flex: 0 0 240px; display: flex; flex-direction: column; min-height: 0; min-width: 0; }
/* "Доступные поля": тулбар ("Выбрать") и компонент "Список" одним блоком. */
.bias-ls-fieldsbox { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; border: 1px solid var(--bd-d8dee8); border-radius: 6px; overflow: hidden; background: var(--bg-ffffff); }
.bias-ls-fieldsbox > .ws-toolbar { flex: none; border-bottom: 1px solid var(--bd-e3e8ef); }
.bias-ls-fieldsbox > [data-ls-fields] { flex: 1 1 auto; min-height: 0; border: none; border-radius: 0; }
.bias-ls-caption { font-size: 12px; color: var(--fg-5a6472); margin-bottom: 4px; }
.bias-ls-fieldlist { flex: 1 1 auto; overflow-y: auto; border: 1px solid var(--bd-d8dee8); border-radius: 6px; padding: 4px 0; outline: none; }
.bias-ls-field { padding: 5px 10px; font-size: 13px; cursor: pointer; user-select: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bias-ls-field:hover { background: var(--bg-eef3fb); }
.bias-ls-field.selected { background: var(--bg-dde7f7); }
/* Сплиттер между "Доступными полями" и таблицей условий (2026-09-25). */
.bias-ls-splitter { flex: 0 0 12px; cursor: col-resize; position: relative; }
.bias-ls-splitter::after { content: ''; position: absolute; top: 20px; bottom: 0; left: 5px; width: 2px; border-radius: 1px; background: transparent; }
.bias-ls-splitter:hover::after, .bias-ls-resizing .bias-ls-splitter::after { background: var(--bg-2b579a); }
body.bias-ls-resizing, body.bias-ls-resizing * { cursor: col-resize !important; user-select: none !important; }
.bias-ls-main { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* Таблица условий — наш Bias.Grid: тулбар и таблица одним блоком,
   таблица тянется до низа диалога. */
.bias-ls-gridbox { flex: 1 1 auto; min-height: 0; }
.bias-ls-gridbox > .ws-grid { flex: 1 1 auto; }
.bias-ls-grid-filter .ws-grid-row { grid-template-columns: 44px 1.2fr 1fr 1.4fr; }
/* Группы условий И/ИЛИ/НЕ (2026-09-25): отступ вложенности, знак группы. */
.bias-ls-indent { display: inline-block; flex: none; }
.bias-ls-group-mark { color: var(--fg-7a8494); margin-right: 6px; cursor: pointer; }
.bias-ls-group-mark:hover { color: var(--fg-2b579a); }
.bias-ls-leaf-mark { color: var(--fg-6c86b3); font-weight: 700; margin-right: 8px; }
.bias-ls-group-row.bias-ls-root-row .ws-grid-cell:nth-child(2) { color: var(--fg-2a2a2a); font-weight: 400; }
.bias-ls-group-row .ws-grid-cell:nth-child(2) { font-weight: 600; color: var(--fg-2b579a); }
.bias-ls-grid-sort .ws-grid-row { grid-template-columns: 1fr 1fr; }
.bias-ls-grid-filter .ws-grid-row:not(.ws-grid-header) > .ws-grid-cell:first-child { display: flex; align-items: center; justify-content: center; }
.bias-ls-error { min-height: 18px; margin: 8px 0 0; font-size: 13px; color: var(--fg-c62828); }
.bias-ls-actions { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.bias-ls-spacer { flex: 1; }
.bias-ls-cancel, .bias-ls-reset { border-radius: 4px; padding: 7px 16px; font-size: 13px; cursor: pointer; border: 1px solid var(--bd-c7cedb); background: var(--bg-ffffff); }
.bias-ls-cancel:hover, .bias-ls-reset:hover { background: var(--bg-eef1f4); }
@media (max-width: 640px) {
  .bias-ls-body { flex-direction: column; }
  .bias-ls-fields { flex: 0 0 140px; }
}
/* Подсказки ссылки в диалоге настройки списка — поверх .ws-modal-overlay (z 1000). */
.bias-combo-suggestions.bias-ls-suggest { z-index: 1100; }
/* Выбор типа / действия (arTypeChooser) — поверх модальных окон (окно сборки подсистемы). */
.bias-combo-suggestions.ar-type-chooser { z-index: 1100; }
/* Пункты списка выбора/подменю не обрезаются: список шире по тексту, а не
   влезло в окно — перенос (см. arTypeChooser в Shell.html, 2026-10-02). */
.bias-combo-suggestions.ar-type-chooser .bias-combo-suggestion { white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }

/* ── Bias.List — компонент "Список" (см. wwwroot/components/bias-list.js,
   запрос пользователя 2026-09-25): иерархический список БЕЗ горизонтальных
   линий между строками, ⊕/⊖ у раскрываемых, "−" у полей. ── */
.bias-list { display: flex; flex-direction: column; min-height: 0; border: 1px solid var(--bd-d8dee8); border-radius: 6px; background: var(--bg-ffffff); overflow: hidden; }
.bias-list-caption { flex: none; padding: 8px 10px; font-size: 12px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; color: var(--fg-5a6472); background: var(--bg-f5f7fa); border-bottom: 1px solid var(--bd-e3e8ef); }
.bias-list-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 4px 0; outline: none; }
.bias-list-row { display: flex; align-items: center; gap: 6px; height: 28px; padding-right: 10px; font-size: 13px; color: var(--fg-2a2a2a); cursor: default; user-select: none; white-space: nowrap; }
.bias-list-row:hover { background: var(--bg-f2f6fc); }
.bias-list-row.selected .bias-list-label { background: var(--bg-dde7f7); border-radius: 3px; }
.bias-list-body:focus .bias-list-row.selected .bias-list-label { background: var(--bg-c9daf3); }
.bias-list-label { padding: 2px 6px; overflow: hidden; text-overflow: ellipsis; }
.bias-list-toggle { flex: none; width: 14px; text-align: center; font-size: 13px; line-height: 1; color: var(--fg-7a8494); cursor: pointer; }
.bias-list-toggle:hover { color: var(--fg-2b579a); }
.bias-list-toggle-empty { cursor: default; }
.bias-list-icon { flex: none; width: 12px; text-align: center; color: var(--fg-6c86b3); font-weight: 700; }
/* "..." у ссылочного значения отбора и окно выбора (2026-09-25). */
.bias-ls-refedit { display: flex; align-items: stretch; width: 100%; height: 100%; gap: 2px; }
.bias-ls-refedit > .ar-cell-input { flex: 1 1 auto; min-width: 0; }
.bias-ls-refpick { flex: none; min-width: 30px; border: 1px solid var(--bd-cfd6e0); border-radius: 4px; font-family: monospace; }
.bias-ls-picker-overlay { z-index: 1050; }
.bias-ls-picker { width: 640px; }
.bias-ls-picker-body { height: 55vh; min-height: 280px; display: flex; flex-direction: column; margin-top: 8px; }
.bias-ls-picker-body > .bias-ls-gridbox { flex: 1 1 auto; min-height: 0; }
/* Табличные части (автоформа и редактируемые Таблицы DSL, arBuildPartGrid,
   2026-09-25): узкая "№"; шапка закреплена — невысокая таблица на форме
   прокручивается, и без sticky заголовки колонок уезжали вверх. */
.ar-part-table .ws-grid-row { grid-template-columns: 48px repeat(var(--ar-part-cols, 1), minmax(0, 1fr)); }
.ar-part-table .ws-grid-header { position: sticky; top: 0; z-index: 2; }
/* ОтметкаНезаполненного ячейки (обычная форма, ПриВыводеСтроки). */
.ar-part-table .ar-cell-unfilled { display: inline-block; min-width: 2em; border-bottom: 1px dashed var(--bd-d13b3b); }
/* Контактная информация (2026-09-30, Таблица.КонтактнаяИнформация). */
.ki-grid .ar-part-table .ws-grid-row, .ar-part-table.ki-table .ws-grid-row { grid-template-columns: minmax(140px, 0.9fr) minmax(0, 2.2fr) minmax(0, 1fr); }
.ki-grid .ws-grid-row { grid-template-columns: minmax(140px, 0.9fr) minmax(0, 2.2fr) minmax(0, 1fr); }
:is(.bias-field-btn, .bias-ls-refpick)[data-ki-fields] { font-size: 0; display: inline-flex; align-items: center; justify-content: center; }
:is(.bias-field-btn, .bias-ls-refpick)[data-ki-fields]::before {
  content: ""; width: 16px; height: 16px; background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Crect%20x='3'%20y='4'%20width='18'%20height='16'%20rx='2'/%3E%3Cpath%20d='M7%209h4M7%2013h10M7%2017h6'/%3E%3C/svg%3E") center / 16px 16px no-repeat; mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='black'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Crect%20x='3'%20y='4'%20width='18'%20height='16'%20rx='2'/%3E%3Cpath%20d='M7%209h4M7%2013h10M7%2017h6'/%3E%3C/svg%3E") center / 16px 16px no-repeat;
}
.ki-main-group { flex: 1; min-width: 0; max-width: none; }
.ki-main-group > .bias-field { max-width: none; }
.ki-dialog { width: 560px; max-width: calc(100vw - 32px); }
.ki-dialog-fields { display: grid; grid-template-columns: 1fr; gap: 2px; margin-top: 6px; }
.ki-dialog-fields .ws-user-form-label { min-width: 140px; }
.ki-dialog-preview { margin: 12px 0 6px; padding: 8px 10px; border-radius: 6px; background: var(--bg-eef1f4); }
.ki-dialog-preview > div { margin-top: 3px; font-size: 13.5px; color: var(--fg-333333); overflow-wrap: anywhere; }
/* Командная панель формы — первая кнопка основная (синяя), остальные
   вторичные: тот же вид, что у стандартных кнопок карточки автоформы
   (.ws-user-form-actions), единое правило системы (2026-09-25). */
.bias-command-bar > .bias-btn:not(:first-child) { background: var(--bg-ffffff); color: var(--fg-333333); border: 1px solid var(--bd-c7cedb); }
.bias-command-bar > .bias-btn:not(:first-child):hover { background: var(--bg-eef1f4); }

/* Доступность = Ложь у контрола DSL (FormDslBuiltins.InjectDisabledAttribute) —
   виден, но не редактируется: приглушён целиком (заголовок + поле). */
.bias-disabled { opacity: .5; }
.bias-disabled input, .bias-disabled button, .bias-disabled select, .bias-disabled textarea { cursor: not-allowed; }

/* Группа DSL без Заголовок — только укладка, без рамки (RenderGroup). */
fieldset.bias-group.bias-group-plain { border: 0; border-radius: 0; padding: 0; margin: 0 0 8px; }
/* Видимость = Ложь (атрибут hidden, FormDslBuiltins.InjectHiddenAttribute) —
   display у .bias-checkbox/.bias-field и т.п. иначе перебивает встроенное
   [hidden]{display:none} (та же гоча, что .ws-user-form-field[hidden]). */
.bias-form [hidden] { display: none !important; }

/* Форма объекта справочника (DSL "Данные: Объект.X", arFillSlots в
   Shell.html): слот — поле по типу реквизита (строка/число/дата/флажок/
   ссылка) или табличная часть. В горизонтальной группе поля делят ряд,
   флажки — по своей ширине, подписи не первого поля ряда — по тексту. */
.ar-slot, .ar-slot-part, .ar-slot-props { align-self: stretch; min-width: 0; }
/* Таблица — часть настроек компоновщика формы отчёта (Отборы/Сортировка/…), 2026-10-08. */
.bias-dcs-part { align-self: stretch; min-width: 0; }
.bias-dcs-part-bar { margin-bottom: 4px; display: flex; gap: 4px; }
.bias-dcs-part-table { width: 100%; border-collapse: collapse; background: var(--bg-ffffff); }
.bias-dcs-part-table th { text-align: left; font-weight: normal; background: var(--bg-eef1f4); border: 1px solid var(--bd-dde3ea); padding: 3px 6px; }
.bias-dcs-part-table td { border: 1px solid var(--bd-dde3ea); padding: 2px 4px; }
.bias-dcs-part-table tr.selected td { background: var(--bg-f5f7fa); }
.bias-dcs-part-table .bias-field { width: 100%; box-sizing: border-box; }
.bias-dcs-part-empty { color: #888; padding: 4px; }
/* Список подчинённого справочника в форме объекта (Таблица с ПодчиненныйСправочник). */
.ws-grid-row.ar-sub-main { font-weight: 700; }
.ar-slot-sub .ar-sub-hint { padding: 10px 2px; color: var(--ws-muted, var(--fg-888888)); font-style: italic; }
.bias-group-horizontal > .ar-slot, .bias-group-horizontal-always > .ar-slot { flex: 1 1 260px; }
.bias-group-horizontal > .ar-slot.ar-slot-bool, .bias-group-horizontal-always > .ar-slot.ar-slot-bool { flex: 0 0 auto; align-self: center; }
.bias-group-horizontal > .ar-slot.ar-slot-fixed, .bias-group-horizontal-always > .ar-slot.ar-slot-fixed { flex: 0 1 auto; }
.bias-group-horizontal > .ar-slot:not(:first-child) .ws-user-form-label,
.bias-group-horizontal-always > .ar-slot:not(:first-child) .ws-user-form-label { min-width: 0; }
.ar-object-actions { margin-bottom: 8px; }
/* Поле ввода слота в ряду не сжимается в щель (длинная общая колонка подписей). */
.ar-slot input.bias-field { min-width: 10ch; }

/* Форма подсистемы (interface.bias, loadInterfacePanelTab в Shell.html):
   разделы колонками, заголовок раздела, подгруппы, команды-ссылки;
   "none" — объекта ещё нет в Bias (серая). */
.ifp { padding: 6px 4px 24px; }
.ifp-top { display: flex; justify-content: flex-end; margin-bottom: 14px; }
.ifp-search { width: 280px; max-width: 100%; }
.ifp-sections { column-width: 300px; column-gap: 48px; }
.ifp-section { break-inside: avoid; margin: 0 0 24px; }
.ifp-section-title { color: var(--fg-1f8a5b); font-size: 17px; margin: 0 0 6px; }
.ifp-header { font-weight: 600; font-size: 13px; color: var(--fg-444444); margin: 10px 0 3px; }
/* "Разделитель;" интерфейса (2026-09-29) — полоска между командами колонки. */
.ifp-separator { height: 1px; background: var(--bg-d5dbe4); margin: 6px 0; }
.ifp-separator[hidden] { display: none; }
.ifp-cmd { display: block; padding: 3px 0 3px 18px; font-size: 13.5px; color: var(--fg-2b2b2b); text-decoration: none; }
.ifp-cmd:hover { color: var(--fg-234a85); text-decoration: underline; }
.ifp-cmd-none { color: var(--fg-a9a9a9); }
.ifp-cmd-none:hover { color: var(--fg-8a8a8a); }
/* Картинки интерфейса (2026-09-29, "Картинка: Картинки.Имя;" — wwwroot/icons):
   у команды — перед текстом, у раздела — в заголовке формы и на кнопке левой
   панели (--nav-icon, ::before — текст кнопки переписывается переводом). */
.ifp-cmd:has(.ifp-icon) { padding-left: 0; display: flex; align-items: center; gap: 7px; }
.ifp-icon { width: 18px; height: 18px; flex: none; }
.ifp-section:has(.ifp-icon) .ifp-cmd:not(:has(.ifp-icon)) { padding-left: 25px; }
.ifp-cmd-none .ifp-icon { filter: grayscale(1); opacity: .5; }
.ifp-title { display: flex; align-items: center; gap: 10px; }
.ifp-title-icon { width: 28px; height: 28px; flex: none; }
.ws-nav-item[data-icon] { padding-left: 14px; }
.ws-nav-item[data-icon]::before {
  content: ""; width: 22px; height: 22px; margin-right: 9px; vertical-align: -6px;
  background-image: var(--nav-icon); background-size: 22px 22px;
}
html[data-theme="dark"] .ws-nav-item[data-icon]::before { filter: none; }
.ifp [hidden] { display: none !important; }

/* Всплывающая форма подсистемы (toggleInterfaceFlyout в Shell.html): слой
   поверх области форм, прилегает к левой командной панели; не модальная. */
.ws-content { position: relative; }
.ifp-flyout { position: fixed; z-index: 60; background: var(--bg-ffffff); border-top: 1px solid var(--bd-d6dbe4);
  border-right: 1px solid var(--bd-d6dbe4); box-shadow: 6px 0 18px var(--sh-20_40_80_p14); overflow: auto; padding: 14px 22px 10px; box-sizing: border-box; }
.ifp-flyout[hidden] { display: none; }
.ifp-flyout-close { position: absolute; top: 10px; right: 12px; border: 0; background: none; font-size: 22px; line-height: 1; color: var(--fg-777777); cursor: pointer; }
.ifp-flyout-close:hover { color: var(--fg-222222); }
.ifp-flyout .ifp-top { justify-content: space-between; align-items: center; padding-right: 32px; }
.ifp-title { font-size: 18px; font-weight: 600; color: var(--fg-234a85); }
.ws-nav-item.ifp-nav-open { background: var(--bg-fff4c7); box-shadow: inset 3px 0 0 var(--sh-e0b400); }

/* Состояние проведения документа в форме объекта (2026-09-27). */
.ar-object-actions .ar-posted-state { margin-left: auto; align-self: center; font-size: 13px; color: var(--fg-888888); }
.ar-object-actions .ar-posted-state.ar-posted-yes { color: var(--fg-15803d); font-weight: 600; }

/* Раскладка колонок таблицы (DSL ГруппаКолонок, Bias.Grid options.layout,
   2026-09-27) — строка и шапка строятся вложенными flex-группами:
   горизонтальная — колонки рядом, вертикальная — друг под другом в одной
   ячейке записи. Линии сетки — между соседями группы (слева у горизонтальной,
   сверху у вертикальной); пустая ячейка держит высоту строки. */
.ws-grid-row.ws-grid-lay { display: flex; align-items: stretch; }
.ws-grid-lay .ws-grid-lg { display: flex; min-width: 0; }
.ws-grid-lay .ws-grid-lg-h { flex-direction: row; }
.ws-grid-lay .ws-grid-lg-v { flex-direction: column; }
.ws-grid-lay .ws-grid-lg-h > .ws-grid-lg, .ws-grid-lay .ws-grid-lg-h > .ws-grid-cell { align-self: stretch; }
.ws-grid-lay .ws-grid-cell { min-width: 0; min-height: 36px; box-sizing: border-box; }
.ws-grid-row.ws-grid-lay > * + *, .ws-grid-lay .ws-grid-lg-h > * + * { border-left: 1px solid var(--bd-eef1f4); }
.ws-grid-lay .ws-grid-lg-v > * + * { border-left: 0; border-top: 1px solid var(--bd-eef1f4); }
.ws-grid-header.ws-grid-lay .ws-grid-lg-v > * + *, .ws-grid-header.ws-grid-lay > * + *, .ws-grid-header.ws-grid-lay .ws-grid-lg-h > * + * { border-color: var(--bd-e3e8ee); }
.ws-grid-lay .ws-grid-lcap { text-align: center; }
/* Ручка ширины колонки раскладки — внутри ячейки (у неё overflow:hidden). */
.ws-grid-header.ws-grid-lay .ws-grid-cell[data-col-key] { position: relative; }
.ws-grid-header.ws-grid-lay .ws-grid-col-resizer { right: 0; }
/* Редактор ячейки раскладки: ширины там — веса flex, и отступ ячейки входит в
   её минимум — padding 2px редактора сдвигал все колонки строки. Боковой отступ
   прежний, поле растягивается на всю ячейку отрицательными полями. */
.ws-grid-lay .ws-grid-cell.ws-grid-cell-editing { padding-left: 12px; padding-right: 12px; }
.ws-grid-lay .ws-grid-cell-editing > * { margin-left: -10px; margin-right: -10px; width: calc(100% + 20px); max-width: none; box-sizing: border-box; }

/* ═══ ТЁМНАЯ ТЕМА (генерируется) ═══ */
/* Тёмная тема (2026-09-29, запрос пользователя: "тёмный как в VS Code"): те же
   переменные, значения в палитре VS Code Dark+ (поверхности #1e1e1e/#252526,
   рамки #3c3c3c, текст #cccccc, акцент #007acc/#0e639c). Переключатель — кнопка
   в шапке (Shell.html), атрибут html[data-theme], хранится в localStorage. */
html[data-theme="dark"] {
  color-scheme: dark;
  --bd-234a85: #0e639c;
  --bd-255_255_255_0p35: rgba(255, 255, 255, 0.35);
  --bd-255_255_255_0p4: rgba(255, 255, 255, 0.4);
  --bd-2b579a: #007acc;
  --bd-2fa84f: #34b255;
  --bd-9fb6d6: #454a51;
  --bd-aeb8c7: #454a51;
  --bd-c7cedb: #3e4148;
  --bd-cfd6e0: #3c4046;
  --bd-cfe0f2: #393e43;
  --bd-d13b3b: #b23434;
  --bd-d6dbe4: #3a3d44;
  --bd-d8cfa8: #4f4d43;
  --bd-d8dee8: #393d43;
  --bd-dde3ea: #383c41;
  --bd-dfe3ea: #373b41;
  --bd-e3b464: #b28334;
  --bd-e3e7ec: #363b40;
  --bd-e3e8ee: #363a40;
  --bd-e3e8ef: #363a3f;
  --bd-eddca0: #4c4a41;
  --bd-eeeeee: #393939;
  --bd-eef1f4: #33383c;
  --bd-ffffff: #333333;
  --bg-0_0_0_0p06: rgba(0, 0, 0, 0.55);
  --bg-0_0_0_0p85: rgba(0, 0, 0, 0.85);
  --bg-20_26_36_0p45: rgba(0, 0, 0, 0.55);
  --bg-234a85: #1177bb;
  --bg-255_255_255_0p15: rgba(255, 255, 255, 0.15);
  --bg-255_255_255_0p3: rgba(255, 255, 255, 0.3);
  --bg-255_255_255_0p5: rgba(255, 255, 255, 0.5);
  --bg-255_255_255_0p6: rgba(255, 255, 255, 0.6);
  --bg-255_255_255_0p85: rgba(255, 255, 255, 0.85);
  --bg-2b579a: #0e639c;
  --bg-2fa84f: #288f43;
  --bg-43_108_176_0p35: rgba(71, 139, 210, 0.35);
  --bg-6fd88a: #279042;
  --bg-8b96a5: #454c54;
  --bg-9aa7b8: #41474f;
  --bg-c2d9f0: #1e4168;
  --bg-c7cedb: #363a42;
  --bg-c9daf3: #1c3d61;
  --bg-cfd6e0: #343940;
  --bg-cfe0f5: #1b395c;
  --bg-d13b3b: #932525;
  --bg-d3392e: #932c25;
  --bg-d5dbe4: #33373c;
  --bg-d6e2f5: #193657;
  --bg-d9e8fb: #173251;
  --bg-dce8f7: #183351;
  --bg-dde3ea: #2f3237;
  --bg-dde7f7: #173251;
  --bg-dde8fb: #17304e;
  --bg-dfe4ea: #2e3236;
  --bg-e0a52c: #936f25;
  --bg-e0e7f2: #183352;
  --bg-e2e6ee: #183453;
  --bg-e4e8ee: #2b2e32;
  --bg-e58a8a: #932525;
  --bg-e5e9ee: #2b2e32;
  --bg-e74c3c: #a1260d;
  --bg-e7ecf3: #16304c;
  --bg-e8ecf2: #16304c;
  --bg-e8f0fe: #24262a;
  --bg-eef1f4: #25292c;
  --bg-eef3fa: #232629;
  --bg-eef3fb: #232529;
  --bg-eef4fb: #232629;
  --bg-f0f0f0: #292929;
  --bg-f2f4f7: #232529;
  --bg-f2f5fa: #222428;
  --bg-f2f6fc: #212427;
  --bg-f4f6f9: #222427;
  --bg-f5c563: #936f25;
  --bg-f5f7fa: #212327;
  --bg-f7f9fb: #202325;
  --bg-f9fafb: #1f2225;
  --bg-fafbfc: #1e2124;
  --bg-fbe0b0: #453821;
  --bg-fbfbfc: #1e1e23;
  --bg-fdeacb: #453721;
  --bg-fdecc8: #453921;
  --bg-ffc83d: #937425;
  --bg-fff4c7: #453e21;
  --bg-fff6dd: #453b21;
  --bg-ffffff: #1e1e1e;
  --fg-0e7c86: #4ec9b0;
  --fg-111111: #d6d6d6;
  --fg-15803d: #74e79f;
  --fg-17325c: #7ca3de;
  --fg-1a7a3c: #ce9178;
  --fg-1a8a5f: #76e5ba;
  --fg-1f8a5b: #7ae1b4;
  --fg-222222: #d4d4d4;
  --fg-234a85: #4daafc;
  --fg-2a2a2a: #cfcfcf;
  --fg-2b2b2b: #cfcfcf;
  --fg-2b579a: #3794ff;
  --fg-2ecc71: #7ae1a6;
  --fg-333333: #cacaca;
  --fg-3a4452: #b7bdc4;
  --fg-3a6bc7: #80a0db;
  --fg-444444: #bfbfbf;
  --fg-444455: #b3b3c1;
  --fg-555555: #b4b4b4;
  --fg-555566: #a8a8b6;
  --fg-5a6472: #a1a9b2;
  --fg-5b7bab: #93a8c8;
  --fg-666666: #aaaaaa;
  --fg-666677: #9e9eac;
  --fg-6b6250: #b7b2a7;
  --fg-6b7684: #959ea9;
  --fg-6c86b3: #93a6c7;
  --fg-767676: #a0a0a0;
  --fg-777777: #9f9f9f;
  --fg-7a4fb5: #a88ccf;
  --fg-7a5c00: #dcdcaa;
  --fg-7a8494: #8b93a0;
  --fg-7a8699: #89929f;
  --fg-888888: #959595;
  --fg-8993a4: #808997;
  --fg-8a5a10: #e7ba74;
  --fg-8a6d1a: #e5c876;
  --fg-8a7f57: #ada99a;
  --fg-8a8a8a: #949494;
  --fg-8a939e: #6a9955;
  --fg-8a93a3: #808997;
  --fg-8a93a6: #7f8797;
  --fg-8b95a5: #7f8897;
  --fg-999999: #8a8a8a;
  --fg-9aa3b1: #76808f;
  --fg-9aa3b2: #767f8f;
  --fg-a626a4: #c586c0;
  --fg-a9a9a9: #808080;
  --fg-aa3333: #f48771;
  --fg-aab2bd: #6e7886;
  --fg-aeb8c7: #6a7381;
  --fg-b5651d: #b5cea8;
  --fg-b97a00: #e7c074;
  --fg-c0392b: #f48771;
  --fg-c45c26: #e49c76;
  --fg-c62828: #f48771;
  --fg-cccccc: #cccccc;
  --fg-d18b1f: #e7ba74;
  --fg-d68a00: #e7be74;
  --fg-e0a800: #e7ca74;
  --fg-e74c3c: #f48771;
  --fg-fbfbfc: #f0f0f0;
  --fg-ffffff: #ffffff;
  --sh-0_0_0_0p15: rgba(0, 0, 0, 0.375);
  --sh-0_0_0_0p18: rgba(0, 0, 0, 0.45);
  --sh-0_0_0_0p25: rgba(0, 0, 0, 0.6);
  --sh-0_0_0_0p5: rgba(0, 0, 0, 0.6);
  --sh-111_216_138_0p35: rgba(111, 216, 138, 0.35);
  --sh-209_59_59_0p2: rgba(211, 69, 69, 0.2);
  --sh-20_30_50_0p15: rgba(91, 124, 189, 0.15);
  --sh-20_40_80_p14: rgba(71, 117, 209, 0.14);
  --sh-224_165_44_0p25: rgba(226, 170, 55, 0.25);
  --sh-229_138_138_0p35: rgba(229, 138, 138, 0.35);
  --sh-245_197_99_0p4: rgba(234, 193, 110, 0.4);
  --sh-43_87_154_0p15: rgba(76, 127, 205, 0.15);
  --sh-47_168_79_0p2: rgba(76, 205, 110, 0.2);
  --sh-5b8dc9: #5b8dc9;
  --sh-e0b400: #d9b626;
  --sh-e67e22: #da7f2e;
  --sh-f0b429: #ddac3c;
}
html[data-theme="dark"] body { background: #1e1e1e; }
/* Шапка — как заголовок окна VS Code. */
html[data-theme="dark"] .ws-topbar { background: #3c3c3c; }
/* Иконки пунктов Админки — встроенные SVG серого цвета, осветляем. */
html[data-theme="dark"] .ws-nav-item::before { filter: invert(0.85); }
/* ...кроме цветных плиток (2026-10-02) — их не инвертируем. */
html[data-theme="dark"] .ws-nav-item[data-cmd="Роли"]::before,
html[data-theme="dark"] .ws-nav-item[data-cmd="Регламентные задания"]::before,
html[data-theme="dark"] .ws-nav-item[data-cmd="Общие настройки"]::before,
html[data-theme="dark"] .ws-nav-item[data-cmd="Пользователи"]::before,
html[data-theme="dark"] .ws-nav-item[data-cmd="Активные пользователи"]::before,
html[data-theme="dark"] .ws-nav-item[data-cmd="Настройки чата"]::before,
html[data-theme="dark"] .ws-nav-item[data-cmd="Обновление"]::before,
html[data-theme="dark"] .ws-nav-item[data-cmd="Структура БД"]::before,
html[data-theme="dark"] .ws-nav-item[data-cmd="Глобальный поиск"]::before { filter: none; }
html[data-theme="dark"] .bias-select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23c5c5c5' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
html[data-theme="dark"] .bias-editor-symbol-nav-btn { background: #3c3c3c url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'%3E%3Cpath fill='%23c5c5c5' d='M1 1l4 4 4-4'/%3E%3C/svg%3E") no-repeat right 8px center; }
/* ═══ КОНЕЦ ТЁМНОЙ ТЕМЫ ═══ */

/* ═══ Конструктор макетов (2026-10-01, bias-template-designer.js) ═══
   Оболочка — переменные темы; сам лист — белый с чёрным текстом в любой теме
   (как бумага печатной формы, .bias-print). */
.tdz { display: flex; flex-direction: column; height: 100%; min-height: 560px; gap: 8px; }
.tdz-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tdz-top .bias-select { width: auto; min-width: 180px; }
.tdz-title { font-weight: 600; color: var(--fg-17325c); }
.tdz-spacer { flex: 1; }
.tdz-primary { background: var(--bg-2b579a); color: var(--fg-ffffff); border-color: var(--bd-2b579a); }
.tdz-body { display: flex; gap: 3px; flex: 1; min-height: 0; }
/* Конструктор макетов во всю высоту вкладки; сплиттер списка — общий .ws-splitter (2026-10-04). */
.ws-page.tdz-page { height: 100%; }
.tdz-split { align-self: stretch; }
.tdz-side { width: 220px; flex: none; display: flex; flex-direction: column; gap: 6px; min-height: 0; }
.tdz-list { overflow: auto; flex: 1; border: 1px solid var(--bd-e3e8ee); border-radius: 4px; background: var(--bg-ffffff); }
.tdz-group { padding: 6px 8px 2px; font-size: 11px; color: var(--fg-6b7684); text-transform: uppercase; letter-spacing: .03em; }
.tdz-item { display: block; width: 100%; text-align: left; border: 0; background: transparent; padding: 4px 8px 4px 14px; cursor: pointer; color: var(--fg-222222); font: inherit; }
.tdz-item:hover { background: var(--bg-eef3fa); }
.tdz-item.active { background: var(--bg-d9e8fb); font-weight: 600; }
.tdz-badge { font-size: 10px; padding: 0 4px; border-radius: 3px; background: var(--bg-fff4c7); color: var(--fg-8a6d1a); margin-left: 4px; }
.tdz-main { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
.tdz-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--bd-e3e8ee); }
.tdz-tabs button { border: 0; background: transparent; padding: 6px 12px; cursor: pointer; color: var(--fg-5a6472); font: inherit; border-bottom: 2px solid transparent; }
.tdz-tabs button.active { color: var(--fg-2b579a); border-bottom-color: var(--bd-2b579a); font-weight: 600; }
.tdz-pane { flex: 1; display: flex; flex-direction: column; min-height: 0; gap: 6px; padding-top: 6px; }
.tdz-pane[hidden] { display: none; }
.tdz-toolbar { display: flex; align-items: center; gap: 3px; flex-wrap: wrap; }
.tdz .tdz-tb { min-width: 28px; padding: 3px 7px; background: var(--bg-ffffff); color: var(--fg-3a4452); border: 1px solid var(--bd-d6dbe4); }
.tdz .tdz-tb:hover { background: var(--bg-eef3fa); }
.tdz .tdz-tb.on { background: var(--bg-d9e8fb); border-color: var(--bd-9fb6d6); color: var(--fg-17325c); }
.tdz-sep { width: 1px; height: 20px; background: var(--bg-dde3ea); margin: 0 4px; }
.tdz-num { width: 64px !important; }
.tdz-color { width: 30px; height: 26px; padding: 0; border: 1px solid var(--bd-d6dbe4); border-radius: 3px; background: var(--bg-ffffff); cursor: pointer; }
.tdz-cellbar { display: flex; gap: 6px; align-items: center; }
.tdz-cellbar .bias-select { width: auto; }
.tdz-cellbar .bias-field { width: 220px; }
.tdz-cellbar .tdz-wide { flex: 1; width: auto; }
.tdz-addr { min-width: 70px; font-family: Consolas, monospace; color: var(--fg-3a4452); }
.tdz-gridwrap { position: relative; flex: 1; overflow: auto; border: 1px solid var(--bd-d6dbe4); background: var(--bg-f5f7fa); outline: none; }
.tdz-grid { border-collapse: collapse; table-layout: fixed; background: #fff; color: #000; font-family: Arial, sans-serif; font-size: 8pt; }
.tdz-grid td { border: 1px dotted #d0d5dc; padding: 1px 3px; overflow: hidden; white-space: nowrap; vertical-align: bottom; cursor: cell; line-height: 1.25; }
.tdz-grid td.sel { box-shadow: inset 0 0 0 9999px rgba(43, 87, 154, .14); }
.tdz-grid td.cur { outline: 2px solid #2b579a; outline-offset: -2px; }
.tdz-grid td.tdz-rect { background-image: linear-gradient(135deg, rgba(224, 165, 44, .10) 25%, transparent 25%); }
.tdz-grid th { position: sticky; background: var(--bg-eef1f4); color: var(--fg-5a6472); font-weight: normal; font-size: 11px; border: 1px solid var(--bd-dde3ea); user-select: none; }
.tdz-grid thead th { top: 0; z-index: 2; }
.tdz-grid th.tdz-rowh { left: 0; z-index: 1; text-align: right; padding: 0 4px; cursor: e-resize; }
.tdz-grid th.tdz-colh { cursor: s-resize; position: sticky; }
.tdz-grid th.sel { background: var(--bg-c9daf3); color: var(--fg-17325c); }
.tdz-grid th.tdz-out { color: var(--fg-aab2bd); }
.tdz-grid th.tdz-inarea { box-shadow: inset 3px 0 0 var(--bg-e0a52c); }
.tdz-grid .tdz-corner { left: 0; z-index: 3; }
.tdz-grid .tdz-areabar { background: var(--bg-fdecc8); color: var(--fg-7a5c00); font-size: 10px; text-align: center; }
.tdz-grid .tdz-arearow th { height: 14px; padding: 0; }
.tdz-colresize { position: absolute; right: -3px; top: 0; width: 6px; height: 100%; cursor: col-resize; z-index: 4; }
.tdz-colset { margin-left: 2px; color: var(--fg-b97a00); }
.tdz-param { color: #2b579a; background: #eef3fb; border-radius: 2px; }
.tdz-editor { position: absolute; z-index: 10; font: 8pt Arial, sans-serif; padding: 1px 3px; border: 2px solid #2b579a; background: #fff; color: #000; resize: both; box-sizing: border-box; }
.tdz-props { width: 230px; flex: none; overflow: auto; border-left: 1px solid var(--bd-e3e8ee); padding: 0 8px; }
/* Макет не выбран — пустую панель свойств не показывать (была пустая колонка справа). */
.tdz-props:empty { display: none; }
.tdz-ph { font-weight: 600; margin: 10px 0 4px; color: var(--fg-3a4452); }
.tdz-lbl { display: flex; align-items: center; gap: 6px; }
.tdz-chk { display: flex; align-items: center; gap: 6px; }
.tdz-area { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; padding: 2px 0; border-bottom: 1px dashed var(--bd-e3e8ee); }
.tdz-area small { color: var(--fg-7a8494); flex-basis: 100%; }
.tdz-arealink { border: 0; background: transparent; color: var(--fg-2b579a); cursor: pointer; padding: 0; font: inherit; font-weight: 600; }
.tdz-x { margin-left: auto; border: 0; background: transparent; color: var(--fg-aa3333); cursor: pointer; font-size: 14px; }
.tdz-hint { color: var(--fg-7a8494); font-size: 12px; }
.tdz-frame { flex: 1; width: 100%; border: 1px solid var(--bd-d6dbe4); background: #fff; min-height: 400px; }
.tdz-text { flex: 1; width: 100%; font: 12px Consolas, monospace; border: 1px solid var(--bd-d6dbe4); background: var(--bg-ffffff); color: var(--fg-222222); padding: 8px; box-sizing: border-box; resize: none; }

/* ═══ Универсальный отчёт (2026-10-01, bias-universal-report.js) ═══ */
.urp { display: flex; flex-direction: column; gap: 8px; height: 100%; min-height: 560px; }
.urp-bar { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }
.urp-primary { background: var(--bg-2b579a); color: var(--fg-ffffff); border-color: var(--bd-2b579a); }
.urp-spacer { flex: 1; }
.urp-f { display: flex; flex-direction: column; gap: 2px; font-size: 12px; color: var(--fg-5a6472); }
.urp-f .bias-field, .urp-f .bias-select { min-width: 140px; }
.urp-link { border: 0; background: transparent; color: var(--fg-2b579a); cursor: pointer; text-decoration: underline; font: inherit; }
.urp-settings { border: 1px solid var(--bd-e3e8ee); border-radius: 6px; padding: 8px 12px; background: var(--bg-f7f9fb); display: flex; flex-direction: column; gap: 8px; }
.urp-settings[hidden] { display: none; }
.urp-h { font-weight: 600; color: var(--fg-3a4452); margin-bottom: 4px; }
.urp-row { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: flex-end; }
.urp-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.urp-chip { display: inline-flex; align-items: center; gap: 2px; padding: 2px 4px 2px 8px; border: 1px solid var(--bd-9fb6d6); border-radius: 12px; background: var(--bg-e8f0fe); color: var(--fg-17325c); }
.urp-chip button { border: 0; background: transparent; cursor: pointer; color: var(--fg-5a6472); padding: 0 3px; font: inherit; }
.urp-chip button:disabled { opacity: .3; cursor: default; }
.urp-add { width: auto !important; min-width: 150px; }
.urp-chk { display: inline-flex; align-items: center; gap: 4px; }
.urp-sep { width: 1px; height: 18px; background: var(--bg-dde3ea); }
.urp-status { font-size: 12px; color: var(--fg-7a8494); min-height: 16px; }
.urp-frame { flex: 1; width: 100%; min-height: 480px; border: 1px solid var(--bd-d6dbe4); background: #fff; }
/* Универсальный отчёт — вкладки настройки, отбор, подбор значений. */
.urp-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--bd-e3e8ee); margin: -2px 0 6px; }
.urp-tabs button { border: 0; background: transparent; padding: 5px 12px; cursor: pointer; color: var(--fg-5a6472); font: inherit; border-bottom: 2px solid transparent; }
.urp-tabs button.active { color: var(--fg-2b579a); border-bottom-color: var(--bd-2b579a); font-weight: 600; }
.urp-tabbody { display: flex; flex-direction: column; gap: 6px; max-height: 42vh; overflow: auto; }
.urp-hier { display: inline-flex; align-items: center; gap: 2px; font-size: 11px; color: var(--fg-5a6472); margin: 0 4px; }
.urp-ftable { border-collapse: collapse; }
.urp-ftable th { text-align: left; font-weight: 600; color: var(--fg-5a6472); font-size: 12px; padding: 2px 6px; }
.urp-ftable td { padding: 2px 6px; vertical-align: top; }
.urp-ftable .bias-select { width: auto; min-width: 160px; }
.urp-val { min-width: 220px; }
.urp-vals { display: inline-flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.urp-vchip { padding: 1px 2px 1px 8px; }
.urp-search { position: relative; display: inline-block; }
.urp-search .bias-field { min-width: 240px; }
.urp-sugg { position: absolute; z-index: 20; top: 100%; left: 0; min-width: 100%; max-height: 260px; overflow: auto; background: var(--bg-ffffff); border: 1px solid var(--bd-d6dbe4); box-shadow: 0 4px 12px rgba(0,0,0,.12); }
.urp-sugg[hidden] { display: none; }
.urp-sitem { padding: 4px 8px; cursor: pointer; white-space: nowrap; color: var(--fg-222222); }
.urp-sitem:hover { background: var(--bg-eef3fa); }
.urp-none { color: var(--fg-7a8494); cursor: default; }
.urp-x { border: 0; background: transparent; color: var(--fg-aa3333); cursor: pointer; font-size: 15px; }
.urp-hint { color: var(--fg-7a8494); font-size: 12px; margin: 2px 0; }
/* Универсальный отчёт v3 (2026-10-01): основная форма + окна "Настройка отчета", "Выбор поля". */
.urp-cmd { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding-bottom: 6px; border-bottom: 1px solid var(--bd-e3e8ee); }
.urp-cmd .bias-btn { padding: 4px 10px; }
.urp-toggle.on { background: var(--bg-d9e8fb); border-color: var(--bd-9fb6d6); color: var(--fg-17325c); }
.urp-period { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 4px 0; }
.urp-inline { display: inline-flex; align-items: center; gap: 6px; color: var(--fg-3a4452); }
.urp-inline .bias-field, .urp-inline .bias-select { width: auto; min-width: 140px; }
.urp-reg { color: var(--fg-3a4452); }
.urp-period .urp-status { margin-left: auto; }
.urp-modal-back { position: fixed; inset: 0; z-index: 1000; background: rgba(15, 25, 40, .35); display: flex; align-items: flex-start; justify-content: center; padding-top: 6vh; }
.urp-modal { background: var(--bg-ffffff); color: var(--fg-222222); border-radius: 8px; box-shadow: 0 12px 40px rgba(0,0,0,.25); width: 460px; max-width: 94vw; max-height: 86vh; display: flex; flex-direction: column; }
.urp-modal-wide { width: 1080px; }
.urp-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-bottom: 1px solid var(--bd-e3e8ee); font-weight: 600; color: var(--fg-17325c); }
.urp-modal-x { border: 0; background: transparent; font-size: 20px; cursor: pointer; color: var(--fg-6b7684); }
.urp-modal-body { padding: 10px 14px; overflow: auto; flex: 1; }
.urp-modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--bd-e3e8ee); }
.urp-shead { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.urp-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.urp-col { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.urp-frameg { border: 1px solid var(--bd-dde3ea); border-radius: 6px; padding: 14px 10px 8px; position: relative; }
.urp-fg-title { position: absolute; top: -9px; left: 10px; padding: 0 6px; background: var(--bg-ffffff); color: var(--fg-2b579a); font-size: 12px; font-weight: 600; }
.urp-tcmd { display: flex; gap: 2px; margin-bottom: 4px; }
.urp-tcmd button { border: 1px solid var(--bd-d6dbe4); background: var(--bg-f7f9fb); color: var(--fg-3a4452); width: 28px; height: 24px; border-radius: 3px; cursor: pointer; font-size: 12px; }
.urp-tcmd button:hover { background: var(--bg-eef3fa); }
.urp-t { width: 100%; border-collapse: collapse; font-size: 13px; }
.urp-t th { background: var(--bg-eef1f4); color: var(--fg-5a6472); font-weight: 600; text-align: left; padding: 3px 6px; border: 1px solid var(--bd-dde3ea); }
.urp-t td { padding: 3px 6px; border: 1px solid var(--bd-eef1f4); vertical-align: middle; }
.urp-t tbody tr { cursor: default; }
.urp-t tbody tr:hover td { background: var(--bg-f5f7fa); }
.urp-t tbody tr.sel td { background: var(--bg-d9e8fb); }
.urp-tsel { width: 100% !important; min-width: 0 !important; height: 26px; }
.urp-tin { width: 100%; min-width: 120px; height: 26px; }
.urp-muted { color: var(--fg-8a93a3); font-style: italic; }
.urp-parts { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 6px; }
.urp-vchip { display: inline-flex; align-items: center; gap: 2px; padding: 1px 2px 1px 8px; border: 1px solid var(--bd-9fb6d6); border-radius: 10px; background: var(--bg-e8f0fe); color: var(--fg-17325c); font-size: 12px; }
.urp-vchip button { border: 0; background: transparent; cursor: pointer; color: var(--fg-5a6472); }
.urp-pane { padding-top: 10px; }
.urp-tree { min-height: 260px; max-height: 50vh; overflow: auto; border: 1px solid var(--bd-dde3ea); border-radius: 4px; }
.urp-tnode { display: flex; align-items: center; gap: 4px; padding: 3px 6px; cursor: pointer; user-select: none; }
.urp-tnode:hover { background: var(--bg-f5f7fa); }
.urp-tnode.sel { background: var(--bg-d9e8fb); }
.urp-tnode.dis { color: var(--fg-8a93a3); cursor: default; }
.urp-tog { width: 14px; color: var(--fg-6b7684); text-align: center; }
.urp-ticon { width: 14px; height: 14px; border-radius: 3px; display: inline-block; }
.urp-ticon.d { background: linear-gradient(135deg, #6aa1e6, #2b579a); }
.urp-ticon.a { background: linear-gradient(135deg, #b9d18a, #6b9a2b); }
.urp-ticon.m { background: linear-gradient(135deg, #f0c36a, #c98a10); }
.urp-ticon.p { background: linear-gradient(135deg, #d39be6, #8a3fb0); }
.urp-list { border: 1px solid var(--bd-dde3ea); border-radius: 4px; min-height: 160px; max-height: 50vh; overflow: auto; }
.urp-litem { padding: 5px 10px; cursor: pointer; }
.urp-litem:hover { background: var(--bg-f5f7fa); }
.urp-litem.sel { background: var(--bg-d9e8fb); }
.urp-styles { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.urp-swatch { width: 150px; border: 2px solid var(--bd-dde3ea); border-radius: 6px; padding: 6px; cursor: pointer; display: flex; flex-direction: column; gap: 3px; }
.urp-swatch.sel { border-color: var(--bd-2b579a); }
.urp-swatch b { font-size: 12px; font-weight: 600; color: var(--fg-3a4452); }
.urp-swatch i { height: 10px; border-radius: 2px; display: block; }
.urp-sw-Стандартное i:nth-of-type(1) { background: #f2efe4; } .urp-sw-Стандартное i:nth-of-type(2) { background: #f6eccd; } .urp-sw-Стандартное i:nth-of-type(3) { background: #fbf5e3; }
.urp-sw-Классическое i:nth-of-type(1) { background: #e4e8ef; } .urp-sw-Классическое i:nth-of-type(2) { background: #d6e2f3; } .urp-sw-Классическое i:nth-of-type(3) { background: #e8eff9; }
.urp-sw-Зеленое i:nth-of-type(1) { background: #e3efdc; } .urp-sw-Зеленое i:nth-of-type(2) { background: #d2e8c6; } .urp-sw-Зеленое i:nth-of-type(3) { background: #e6f2df; }
.urp-sw-БезОформления i { background: #fff; border: 1px solid #ddd; }
/* Универсальный отчёт: плоская командная панель — заливка только у основной кнопки. */
.urp-cmd .bias-btn:not(.urp-primary), .urp-modal-foot .bias-btn:not(.urp-primary) { background: var(--bg-ffffff); color: var(--fg-3a4452); border: 1px solid var(--bd-d6dbe4); }
.urp-cmd .bias-btn:not(.urp-primary):hover, .urp-modal-foot .bias-btn:not(.urp-primary):hover { background: var(--bg-eef3fa); }
.urp-cmd .urp-toggle.on { background: var(--bg-d9e8fb); border-color: var(--bd-9fb6d6); color: var(--fg-17325c); }
.urp-sep { width: 1px; height: 22px; background: var(--bg-dde3ea); }
/* Окна универсального отчёта: перемещение за заголовок, размер за границы и углы. */
.urp-modal { position: relative; min-width: 320px; min-height: 180px; }
.urp-modal-head { cursor: move; user-select: none; }
.urp-rz { position: absolute; z-index: 2; }
.urp-rz-n, .urp-rz-s { left: 8px; right: 8px; height: 6px; cursor: ns-resize; }
.urp-rz-n { top: -3px; } .urp-rz-s { bottom: -3px; }
.urp-rz-e, .urp-rz-w { top: 8px; bottom: 8px; width: 6px; cursor: ew-resize; }
.urp-rz-e { right: -3px; } .urp-rz-w { left: -3px; }
.urp-rz-ne, .urp-rz-nw, .urp-rz-se, .urp-rz-sw { width: 12px; height: 12px; }
.urp-rz-ne { top: -4px; right: -4px; cursor: nesw-resize; } .urp-rz-sw { bottom: -4px; left: -4px; cursor: nesw-resize; }
.urp-rz-nw { top: -4px; left: -4px; cursor: nwse-resize; } .urp-rz-se { bottom: -4px; right: -4px; cursor: nwse-resize; }
.urp-rz-se::after { content: ""; position: absolute; right: 6px; bottom: 6px; width: 8px; height: 8px; border-right: 2px solid var(--bd-aeb8c7); border-bottom: 2px solid var(--bd-aeb8c7); }
body.urp-dragging, body.urp-dragging * { user-select: none !important; }

/* ═══ Библиотека компонентов: окно (bias-dialog.js) и сплиттер (bias-splitter.js), 2026-10-01 ═══ */
.bias-dialog-back { position: fixed; inset: 0; z-index: 1000; background: rgba(15, 25, 40, .35); display: flex; align-items: flex-start; justify-content: center; padding-top: 6vh; }
.bias-dialog { position: relative; background: var(--bg-ffffff); color: var(--fg-222222); border-radius: 8px; box-shadow: 0 12px 40px rgba(0,0,0,.25); width: 460px; max-width: 94vw; max-height: 88vh; min-width: 320px; min-height: 180px; display: flex; flex-direction: column; }
.bias-dialog-wide { width: 1080px; }
.bias-dialog-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-bottom: 1px solid var(--bd-e3e8ee); font-weight: 600; color: var(--fg-17325c); cursor: move; user-select: none; flex: none; }
.bias-dialog-x { border: 0; background: transparent; font-size: 20px; cursor: pointer; color: var(--fg-6b7684); }
.bias-dialog-body { padding: 10px 14px; overflow: auto; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.bias-dialog-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--bd-e3e8ee); flex: none; }
.bias-dialog-foot .bias-btn:not(.bias-dialog-primary) { background: var(--bg-ffffff); color: var(--fg-3a4452); border: 1px solid var(--bd-d6dbe4); }
.bias-dialog-foot .bias-dialog-primary { background: var(--bg-2b579a); color: var(--fg-ffffff); border-color: var(--bd-2b579a); }
.bias-rz { position: absolute; z-index: 2; }
.bias-rz-n, .bias-rz-s { left: 8px; right: 8px; height: 6px; cursor: ns-resize; }
.bias-rz-n { top: -3px; } .bias-rz-s { bottom: -3px; }
.bias-rz-e, .bias-rz-w { top: 8px; bottom: 8px; width: 6px; cursor: ew-resize; }
.bias-rz-e { right: -3px; } .bias-rz-w { left: -3px; }
.bias-rz-ne, .bias-rz-nw, .bias-rz-se, .bias-rz-sw { width: 12px; height: 12px; }
.bias-rz-ne { top: -4px; right: -4px; cursor: nesw-resize; } .bias-rz-sw { bottom: -4px; left: -4px; cursor: nesw-resize; }
.bias-rz-nw { top: -4px; left: -4px; cursor: nwse-resize; } .bias-rz-se { bottom: -4px; right: -4px; cursor: nwse-resize; }
.bias-rz-se::after { content: ""; position: absolute; right: 6px; bottom: 6px; width: 8px; height: 8px; border-right: 2px solid var(--bd-aeb8c7); border-bottom: 2px solid var(--bd-aeb8c7); }
body.bias-dragging, body.bias-dragging * { user-select: none !important; }
/* Стандарт всех всплывающих окон (2026-10-02): окна .ws-modal тоже двигаются за
   заголовок и меняют размер (bias-dialog.js, автоподключение) — ручкам размера
   нужно позиционированное окно. */
.ws-modal.bias-movable { position: relative; }
/* Обёртка содержимого (всё, кроме заголовка): пока окно не двигали — без своего
   бокса, вид прежний; с заданным размером — прокручивается она, рамка — нет. */
.bias-movable-content { display: contents; }
.ws-modal.bias-movable.bias-sized { display: flex; flex-direction: column; overflow: visible; }
.ws-modal.bias-sized > .ws-modal-header { flex: none; }
.ws-modal.bias-sized > .bias-movable-content { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: auto; }
.bias-movable > .ws-modal-header { cursor: move; user-select: none; }
.bias-movable > .ws-modal-header button, .bias-movable > .ws-modal-header input { cursor: auto; }
.bias-movable > .ws-modal-header .ws-modal-close { cursor: pointer; }
body.bias-col-resize, body.bias-col-resize * { cursor: col-resize !important; }
body.bias-row-resize, body.bias-row-resize * { cursor: row-resize !important; }
.bias-split { display: flex; min-height: 0; min-width: 0; }
.bias-split-row { flex-direction: row; }
.bias-split-col { flex-direction: column; }
.bias-split > [data-split-pane] { min-width: 0; min-height: 0; }
.bias-splitter { flex: none; position: relative; background: transparent; }
.bias-splitter-v { width: 9px; cursor: col-resize; }
.bias-splitter-h { height: 9px; cursor: row-resize; }
.bias-splitter::after { content: ""; position: absolute; background: var(--bg-dde3ea); border-radius: 2px; transition: background .12s; }
.bias-splitter-v::after { left: 3px; width: 3px; top: 0; bottom: 0; }
.bias-splitter-h::after { top: 3px; height: 3px; left: 0; right: 0; }
.bias-splitter:hover::after, .bias-splitter.active::after { background: var(--bg-9aa7b8); }

/* Универсальный отчёт: окно настройки тянется по размеру окна, таблицы — с прокруткой внутри. */
.urp-sroot { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.urp-split { flex: 1; min-height: 0; }
.urp-sroot .urp-col { min-height: 0; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.urp-sroot .bias-split-col { gap: 0; }
.urp-sroot .bias-split-col > .urp-frameg { margin: 6px 0 2px; }
.urp-frameg.grow { flex: 1 1 0; min-height: 90px; display: flex; flex-direction: column; }
.urp-tscroll { flex: 1; overflow: auto; min-height: 0; }
.urp-tscroll .urp-t thead th { position: sticky; top: 0; z-index: 1; }
.urp-pane { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.urp-tree.grow { flex: 1; max-height: none; }
.urp-list { flex: 1; }

/* Универсальный отчёт v4 (2026-10-01): всё на компонентах библиотеки. */
.urp-cbtn { display: inline-flex; align-items: center; gap: 6px; }
.urp-cbtn .ws-icon { width: 15px; height: 15px; flex: none; }
.urp-gbox { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.urp-gbox > .ws-grid { flex: 1 1 auto; min-height: 60px; }
.urp-gbox > .ws-toolbar { flex: none; }
.urp-frameg.grow > .urp-gbox { margin-top: 2px; }
.urp-g-chk .ws-grid-row { grid-template-columns: 44px 1fr; }
.urp-g-one .ws-grid-row { grid-template-columns: 1fr; }
.urp-g-two .ws-grid-row { grid-template-columns: 1.5fr 1fr; }
.urp-g-filter .ws-grid-row { grid-template-columns: 44px 1.2fr 1fr 1.6fr; }
.urp-g-chk .ws-grid-row:not(.ws-grid-header) > .ws-grid-cell:first-child,
.urp-g-filter .ws-grid-row:not(.ws-grid-header) > .ws-grid-cell:first-child { display: flex; align-items: center; justify-content: center; }
.urp-ficon { display: inline-block; width: 12px; height: 12px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
.urp-ficon.d { background: linear-gradient(135deg, #6aa1e6, #2b579a); }
.urp-ficon.a { background: linear-gradient(135deg, #b9d18a, #6b9a2b); }
.urp-ficon.m { background: linear-gradient(135deg, #f0c36a, #c98a10); }
.urp-ficon.p { background: linear-gradient(135deg, #d39be6, #8a3fb0); }
.urp-vchip { margin: 1px 4px 1px 0; }
.urp-vx { display: inline-flex; cursor: pointer; color: var(--fg-6b7684); padding: 0 2px; }
.urp-vx .ws-icon { width: 11px; height: 11px; }
.urp-vx:hover { color: var(--fg-aa3333); }
.bias-combo-suggestions.urp-suggest { z-index: 1100; }
.urp-sumbar { min-height: 18px; font-size: 12px; color: var(--fg-5a6472); text-align: right; padding: 2px 4px; }
.urp-col-tight { gap: 6px !important; }
.urp-radio { display: inline-flex; align-items: center; gap: 4px; margin-left: 10px; }
.urp-swatch { background: var(--bg-ffffff); text-align: left; font: inherit; }
.urp-tog .ws-icon { width: 12px; height: 12px; }
.urp-tree:focus { outline: 2px solid var(--bd-9fb6d6); outline-offset: -2px; }
.urp-tabs button:focus-visible, .urp-swatch:focus-visible { outline: 2px solid var(--bd-9fb6d6); outline-offset: 1px; }

/* Команда интерфейса — системный объект платформы (не из подсистемы): пометка справа. */
.ifp-sys { margin-left: auto; padding: 0 6px; border-radius: 8px; font-size: 10px; line-height: 16px; letter-spacing: .02em;
  color: #b45309; background: rgba(245, 158, 11, .14); border: 1px solid rgba(217, 119, 6, .35); flex: none; }
html[data-theme=dark] .ifp-sys { color: #fbbf24; background: rgba(245, 158, 11, .12); border-color: rgba(251, 191, 36, .35); }

/* ── Конструктор схем компоновки данных (2026-10-01, bias-dcs-designer.js) ── */
.dcd { display: flex; flex-direction: column; height: 100%; min-height: 600px; gap: 8px; }
.dcd-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dcd-top .bias-select { width: auto; min-width: 180px; }
.dcd-lbl { display: flex; align-items: center; gap: 6px; }
.dcd-title { font-weight: 600; color: var(--fg-17325c); }
.dcd-dirty { color: var(--fg-b97a00); }
.dcd-spacer { flex: 1; }
.dcd-body { display: flex; flex: 1; min-height: 0; }
.dcd-schemas { display: flex; flex-direction: column; gap: 6px; min-width: 160px; min-height: 0; }
.dcd-work { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.dcd-group { padding: 6px 8px 2px; font-size: 11px; color: var(--fg-6b7684); text-transform: uppercase; letter-spacing: .03em; }
.dcd-item { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; border: 0; background: transparent; padding: 3px 8px 3px 10px; cursor: pointer; color: var(--fg-222222); font: inherit; }
.dcd-item:hover { background: var(--bg-eef3fa); }
.dcd-item.active { background: var(--bg-d9e8fb); font-weight: 600; }
.dcd-item-ic { width: 16px; height: 16px; flex: none; }
.dcd-ed { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.dcd-tabs, .dcd-subtabs { display: flex; gap: 2px; border-bottom: 1px solid var(--bd-e3e8ee); flex-wrap: wrap; }
.dcd-tabs button, .dcd-subtabs button { border: 1px solid transparent; border-bottom: 0; background: transparent; padding: 5px 11px; cursor: pointer; color: var(--fg-5a6472); font: inherit; border-radius: 4px 4px 0 0; }
.dcd-tabs button:hover, .dcd-subtabs button:hover { background: var(--bg-eef3fa); }
.dcd-tabs button.active, .dcd-subtabs button.active { color: var(--fg-17325c); background: var(--bg-ffffff); border-color: var(--bd-e3e8ee); font-weight: 600; margin-bottom: -1px; }
.dcd-pane { flex: 1; display: flex; flex-direction: column; min-height: 0; gap: 6px; padding-top: 6px; }
.dcd-subpane { flex: 1; display: flex; flex-direction: column; min-height: 0; padding-top: 6px; }
.dcd-split { display: flex; flex: 1; min-height: 0; min-width: 0; }
.dcd-split-col { flex-direction: column; }
.dcd-side, .dcd-main { display: flex; flex-direction: column; gap: 6px; min-width: 0; min-height: 0; }
.dcd-side { min-width: 180px; }
.dcd-mid { display: flex; flex-direction: column; justify-content: center; gap: 6px; padding: 0 6px; }
.dcd-fill { flex: 1; min-height: 0; }
.dcd-bar { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.dcd-bar .bias-btn, .dcd-props .bias-btn { padding: 3px 8px; }
.dcd-bar-sub { font-size: 12px; }
.dcd-props { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 2px 0 4px; }
.dcd-props label { display: flex; align-items: center; gap: 6px; }
.dcd-props .bias-field { width: 200px; }
.dcd-props .bias-select { width: auto; min-width: 160px; }
.dcd-kind { margin-left: auto; color: var(--fg-6b7684); font-size: 12px; }
.dcd-caption { font-size: 12px; color: var(--fg-5a6472); padding: 2px 0; }
.dcd-hint { color: var(--fg-7a8494); font-size: 12px; }
.dcd-pad { padding: 12px; }
.dcd-chip { display: inline-block; padding: 0 6px; border-radius: 8px; background: var(--bg-eef3fa); color: var(--fg-3a4452); font-size: 11px; }
.dcd-tree, .dcd-list { overflow: auto; border: 1px solid var(--bd-e3e8ee); border-radius: 4px; background: var(--bg-ffffff); outline: none; }
.dcd-tree { flex: 1; min-height: 120px; }
.dcd-tnode { display: flex; align-items: center; gap: 5px; padding: 3px 8px; cursor: pointer; user-select: none; white-space: nowrap; }
.dcd-tnode:hover { background: var(--bg-f5f7fa); }
.dcd-tnode.active { background: var(--bg-d9e8fb); color: var(--fg-17325c); }
.dcd-tnode .ws-icon { width: 15px; height: 15px; flex: none; }
.dcd-troot { font-weight: 600; color: var(--fg-3a4452); cursor: default; }
.dcd-tvirt { color: var(--fg-6b7684); font-style: italic; }
.dcd-tw { display: inline-block; width: 12px; color: var(--fg-6b7684); }
.dcd-res { color: var(--fg-b97a00); font-weight: 700; margin-right: 2px; }
.dcd-ticon { display: inline-flex; }
.dcd-ticon-query .ws-icon { color: #2b7bd6; }
.dcd-ticon-object .ws-icon { color: #2e9d6b; }
.dcd-ticon-union .ws-icon { color: #b06ad8; }
.dcd-li { padding: 3px 8px; cursor: pointer; user-select: none; }
.dcd-li:hover { background: var(--bg-f5f7fa); }
.dcd-li.active { background: var(--bg-d9e8fb); }
.dcd-gridbox { flex: 1; min-height: 120px; display: flex; flex-direction: column; }
.dcd-fields, .dcd-query, .dcd-tpl-view { display: flex; flex-direction: column; min-height: 0; }
.dcd-qtext { flex: 1; min-height: 120px; width: 100%; box-sizing: border-box; resize: none; font: 13px/1.45 Consolas, "Courier New", monospace; tab-size: 4; padding: 6px 8px;
  border: 1px solid var(--bd-d6dbe4); border-radius: 4px; background: var(--bg-ffffff); color: var(--fg-222222); white-space: pre; }
.dcd-btncell { display: flex; align-items: center; gap: 4px; width: 100%; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dcd-dots { margin-left: auto; padding: 0 4px; border: 1px solid var(--bd-d6dbe4); border-radius: 3px; font-size: 10px; line-height: 14px; color: var(--fg-5a6472); background: var(--bg-f5f7fa); flex: none; }
.dcd-btncell:hover .dcd-dots { background: var(--bg-d9e8fb); }
.dcd-row-folder { font-weight: 600; }
.dcd-row-nested { font-style: italic; }
.dcd-form { display: flex; flex-direction: column; gap: 10px; padding: 4px 2px; }
.dcd-form label { display: flex; align-items: center; gap: 8px; }
.dcd-form .bias-field { flex: 1; }
.dcd-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.dcd-num { width: 70px !important; flex: none !important; }
.dcd-color { display: inline-flex; gap: 4px; align-items: center; flex: 1; }
.dcd-color input[type=color] { width: 32px; height: 26px; padding: 0; border: 1px solid var(--bd-d6dbe4); border-radius: 3px; background: var(--bg-ffffff); }
.dcd-vt { display: grid; grid-template-columns: 1fr 240px; grid-template-rows: auto 1fr; gap: 8px; flex: 1; min-height: 0; }
.dcd-vt [data-vt-q] { grid-column: 1; }
.dcd-vt-list { grid-column: 1; overflow: auto; border: 1px solid var(--bd-e3e8ee); border-radius: 4px; min-height: 0; }
.dcd-vt-g { padding: 6px 8px 2px; font-size: 11px; color: var(--fg-6b7684); text-transform: uppercase; }
.dcd-vt-i { padding: 1px 8px; }
.dcd-vt-q { grid-column: 2; grid-row: 1 / span 2; display: flex; flex-direction: column; gap: 8px; }
.dcd-vt-q label { display: flex; flex-direction: column; gap: 2px; font-size: 12px; }
.dcd-pl-top { padding-bottom: 4px; }
.dcd-pl-top .bias-select { width: auto; min-width: 240px; }
.dcd-pl-host, .dcd-nested { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.dcd-pl-grid { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.dcd-expr { display: flex; gap: 8px; flex: 1; min-height: 0; }
.dcd-expr .dcd-qtext { flex: 1; }
.dcd-expr-side { width: 260px; display: flex; flex-direction: column; gap: 4px; min-height: 0; }
.dcd-expr-side .dcd-list { flex: 1; min-height: 0; font-size: 12px; }
.dcd-tpl-area { overflow: auto; max-height: 45%; padding: 4px; border: 1px solid var(--bd-d6dbe4); background: var(--bg-f5f7fa); }
.dcd-tpl-tbl { border-collapse: collapse; background: #fff; color: #000; font: 8pt Arial, sans-serif; }
.dcd-tpl-tbl td { border: 1px dotted #c9ced6; padding: 2px 6px; min-width: 60px; height: 18px; white-space: nowrap; cursor: cell; }
.dcd-tpl-tbl td:hover { outline: 1px solid #2b579a; outline-offset: -1px; }
.dcd-tpl-par { color: #2b579a; }
.dcd-gridbox { overflow-x: auto; }
.ws-grid.dcd-g-fields, .ws-grid.dcd-g-calc { min-width: 1250px; }
.ws-grid.dcd-g-params { min-width: 1100px; }
.ws-grid.dcd-g-links { min-width: 1000px; }

/* ── Форма отчёта на схеме компоновки данных (2026-10-01, bias-dcs-report.js) ── */
/* Вкладка отчёта — во всю высоту, результат (iframe) забирает всё свободное место (2026-10-07). */
.ws-page:has(> .dcr) { height: 100%; }
.dcr { display: flex; flex-direction: column; height: 100%; min-height: 320px; gap: 6px; }
.dcr-cmd { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.dcr-btn { display: inline-flex; align-items: center; gap: 5px; padding: 4px 9px; }
.dcr-btn .ws-icon { width: 16px; height: 16px; }
.dcr-var { display: inline-flex; align-items: center; gap: 6px; margin: 0 6px; }
.dcr-var .bias-select { width: auto; min-width: 160px; }
.dcr-status { margin-left: 8px; color: var(--fg-6b7684); font-size: 12px; }
.dcr-sys { margin-left: auto; padding: 0 7px; border-radius: 8px; font-size: 10px; line-height: 16px; color: #b45309; background: rgba(245, 158, 11, .14); border: 1px solid rgba(217, 119, 6, .35); }
html[data-theme=dark] .dcr-sys { color: #fbbf24; }
.dcr-params { display: flex; flex-wrap: wrap; gap: 6px 18px; padding: 6px 8px; border: 1px solid var(--bd-e3e8ee); border-radius: 4px; background: var(--bg-f5f7fa); }
.dcr-params[hidden] { display: none; }
.dcr-param { display: inline-flex; align-items: center; gap: 6px; }
.dcr-param .bias-field { width: auto; min-width: 140px; }
.dcr-param input[type=date].bias-field { min-width: 0; width: 150px; }
.dcr-param .dcr-pvar.bias-field { min-width: 0; width: 170px; }
.dcr-num { width: 110px !important; min-width: 0 !important; }
.dcr-frame, .dcd-frame { flex: 1; min-height: 240px; width: 100%; border: 1px solid var(--bd-d6dbe4); border-radius: 4px; background: #fff; }
.dcr-sum { min-height: 16px; font-size: 12px; color: var(--fg-5a6472); text-align: right; }
.dcr-settings { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.dcd-props[hidden], .dcd-tabs[hidden] { display: none; }

/* Кнопка в конце строки контекстного меню (крестик «убрать вход», 2026-10-03). */
.ws-context-menu-item.ws-context-menu-item-trail { display: flex; align-items: center; gap: 12px; }
.ws-context-menu-trail { margin-left: auto; display: inline-flex; padding: 2px; border-radius: 4px; opacity: .55; }
.ws-context-menu-trail:hover { opacity: 1; background: rgba(209, 59, 59, .25); }

/* Шапка всплывающих окон (запрос пользователя 2026-10-03): лёгкий оттенок во всю ширину окна,
   чтобы заголовок выделялся. Отрицательные поля — компенсация внутренних отступов .ws-modal. */
.ws-modal > .ws-modal-header {
  margin: -10px -24px 12px;
  padding: 8px 14px 8px 24px;
  align-items: center;
  background: var(--bg-eef1f4);
  border-radius: 8px 8px 0 0;
}
.ws-modal.ws-form-window > .ws-modal-header { margin: -8px -16px 10px; padding: 8px 10px 8px 16px; }
.bias-dialog-head { background: var(--bg-eef1f4); border-radius: 8px 8px 0 0; }

/* Список выбора в диалоге ввода (WS.promptChoice, 2026-10-04): что добавлять — над полем имени. */
.ws-modal-prompt-choice { margin-bottom: 8px; }
.ws-modal-prompt-choice .bias-select { width: 100%; }

/* ── Табличный документ во вкладке (*.table.bias, 2026-10-04): конструктор табличного макета
   без своей шапки (имя — в заголовке вкладки), во всю вкладку без отступов; рамка, панели и
   кнопки — по теме, сам лист остаётся белым (как бумага). Только встроенный режим (.mxd-inline). ── */
.ws-page.ws-page-table-doc { padding: 0; }
/* Просмотр печатной формы / табличного документа во вкладке (showPrintHtml, 2026-10-06): лист вплотную
   к краям вкладки, без внешних отступов и рамки (поля — внутри листа). */
.ws-page.ws-page-print { padding: 0; }
.ws-page.ws-page-print iframe[data-pr-frame] { border: 0 !important; }
.mxd.mxd-inline .mxd-title { display: none; }
/* .ws-page — сильнее встроенного CSS конструктора (он добавляется после site.css и рисовал светлую рамку) */
.ws-page .mxd.mxd-inline .mxd-win { border: 0; box-shadow: none; background: var(--bg-ffffff); color: var(--fg-222222); }
.mxd.mxd-inline .mxd-tb { background: var(--bg-f5f7fa); border-bottom: 1px solid var(--bd-dde3ea); }
.mxd.mxd-inline .mxd-tb button { color: var(--fg-222222); }
.mxd.mxd-inline .mxd-tb button:hover { border-color: var(--bd-c7cedb); background: var(--bg-eef3fa); }
.mxd.mxd-inline .mxd-tb button.on { border-color: var(--bd-2b579a); background: var(--bg-d9e8fb); }
.mxd.mxd-inline .mxd-tb .sep { background: var(--bd-dde3ea); }
.mxd.mxd-inline .mxd-tb select,
.mxd.mxd-inline .mxd-props input,
.mxd.mxd-inline .mxd-props select,
.mxd.mxd-inline .mxd-props textarea,
.mxd.mxd-inline .mxd-props .row button {
  background: var(--bg-ffffff); color: var(--fg-222222); border: 1px solid var(--bd-c7cedb); border-radius: 3px;
}
.mxd.mxd-inline .mxd-props .row button:hover { background: var(--bg-eef3fa); }
.mxd.mxd-inline .mxd-corner,
.mxd.mxd-inline .mxd-top,
.mxd.mxd-inline .mxd-left { background: var(--bg-f0f0f0); border-color: var(--bd-c7cedb); }
.mxd.mxd-inline .mxd-ch,
.mxd.mxd-inline .mxd-rh { color: var(--fg-333333); border-color: var(--bd-dde3ea); }
.mxd.mxd-inline .mxd-ch.sel,
.mxd.mxd-inline .mxd-rh.sel { background: var(--bg-c9daf3); }
.mxd.mxd-inline .mxd-ch.other { color: var(--fg-7a5c00); background: var(--bg-fff4c7); }
.mxd.mxd-inline .mxd-ar { color: var(--fg-222222); border-color: var(--fg-555555); }
.mxd.mxd-inline .mxd-props { background: var(--bg-f7f9fb); border-left: 1px solid var(--bd-dde3ea); color: var(--fg-222222); }
.mxd.mxd-inline .mxd-props h4 { color: var(--fg-17325c); border-bottom-color: var(--bd-dde3ea); }
.mxd.mxd-inline .mxd-props .hint { color: var(--fg-6b7684); }
.mxd.mxd-inline .mxd-status { background: var(--bg-f5f7fa); border-top: 1px solid var(--bd-dde3ea); color: var(--fg-444444); }
/* лист — белый в любой теме */
.mxd.mxd-inline .mxd-sheet, .mxd.mxd-inline .mxd-view { background: #fff; }
/* текст ячеек без своего цвета — чёрный (иначе в тёмной теме наследовал светлый цвет темы) */
.mxd.mxd-inline .mxd-view { color: #000; }
.mxd.mxd-inline .mxd-edit, .mxd.mxd-inline .mxd-dred { background: #fff; color: #000; }

/* Схема компоновки подсистемы (*.dcs.bias, 2026-10-04) — конструктор во вкладке. */
.ws-page.ws-page-dcs-doc { height: 100%; padding: 0; }
.dcd.dcd-file { padding: 6px 10px; box-sizing: border-box; min-height: 0; }
.dcd.dcd-file .dcd-work { flex: 1; }

/* Низкая шапка всех всплывающих окон (2026-10-04, как у окна вопроса): .ws-modal и .bias-dialog. */
.ws-modal > .ws-modal-header { padding: 3px 8px 3px 24px; }
.ws-modal.ws-form-window > .ws-modal-header { padding: 3px 8px 3px 16px; }
.ws-modal-header .ws-modal-title { font-size: 13px; }
.ws-modal-header .ws-modal-close { font-size: 17px; padding: 1px 6px; }
.bias-dialog-head { padding: 3px 8px 3px 14px; font-size: 13px; }
.bias-dialog-head .bias-dialog-x { font-size: 17px; line-height: 1; padding: 1px 6px; border-radius: 4px; }
.bias-dialog-head .bias-dialog-x:hover { background: var(--bg-0_0_0_0p06); }

/* Нижняя строка окна (2026-10-04): слева Статус(), справа позиция курсора редактора;
   своя строка редактора в оболочке скрыта. */
.ws-statusbar-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ws-statusbar-right { flex: none; margin-left: 12px; }
.bias-editor-status-shell .bias-editor-status { display: none; }

/* Менеджер БД и Параметры манифеста (2026-10-04, bias-admin-db.js). */
.ws-page .dbm { display: flex; flex-direction: column; height: calc(100vh - 220px); min-height: 360px; gap: 8px; }
/* Форма в высоту вкладки (2026-10-10): Табло/окно меняют высоту — содержимое сжимается, прокрутка внутри частей. */
.ws-page.ws-page-fill { height: 100%; overflow: auto; }
.ws-page.ws-page-fill .dbm { height: 100%; min-height: 0; }
/* Минимум панелей — подпись, командная панель, шапка и одна строка таблицы; меньше места — прокручивается форма. */
.ws-page.ws-page-fill .dbm-body { min-height: 150px; }
.dbm-top { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.dbm-top .bias-select { width: auto; min-width: 320px; }
.dbm-lbl { display: flex; align-items: center; gap: 6px; }
.dbm-info { color: var(--fg-6b7684); font-size: 12.5px; }
.dbm-body { display: flex; flex: 1; min-height: 0; }
.dbm-pane { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.dbm-cap { font-weight: 600; color: var(--fg-17325c); padding: 2px 0 4px; }
.dbm-grid { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.dbm-hint { color: var(--fg-6b7684); font-size: 12.5px; margin: 0 0 8px; }
.dbm-muted { color: var(--fg-8a939e); }
.dbm-row-muted td { color: var(--fg-8a939e); }
.dbm-tabs { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.dbm-tabs .bias-btn { background: transparent; color: var(--fg-333333); border: 1px solid var(--bd-c7cedb); }
.dbm-tabs .bias-btn.active { background: var(--bg-2b579a); color: var(--fg-ffffff); border-color: var(--bd-2b579a); }
.dbm-dlg-grid { height: calc(100% - 40px); display: flex; flex-direction: column; }
.dbm-dlg-grid[hidden] { display: none; }
.mpar-missing { margin-top: 10px; color: var(--fg-b97a00); }
.mpar-form { display: flex; flex-direction: column; gap: 10px; min-width: 460px; }
.mpar-form > label:not(.bias-checkbox) { display: grid; grid-template-columns: 90px 1fr; align-items: center; gap: 8px; }
.mpar-val { display: flex; gap: 6px; }
.mpar-val .bias-field { flex: 1; }
.dbm-grid, .dbm-dlg-grid { overflow: auto; }
.ws-grid-dbm-dbs .ws-grid-row { grid-template-columns: 2fr 1.6fr 1.1fr 1fr; }
.dbm-cap .ws-spinner, .dbm-info .ws-spinner, .dbm-tabs .ws-spinner { width: 11px; height: 11px; margin-right: 6px; }
.ws-grid-dbm-tables .ws-grid-row { grid-template-columns: 0.7fr 2.4fr 1.1fr 0.8fr 0.9fr 1.3fr; }
.ws-grid-dbm-cols .ws-grid-row { grid-template-columns: 1.6fr 1.2fr 0.5fr 0.9fr 1.4fr; }
.ws-grid-dbm-rows { width: max-content; min-width: 100%; }
.ws-grid-dbm-rows .ws-grid-row { grid-template-columns: repeat(var(--dbm-cols, 1), minmax(120px, 1fr)); }

/* Монитор (2026-10-05, bias-admin-db.js). */
.ws-page.ws-page-mon { height: 100%; overflow: auto; }
.mon { display: flex; flex-direction: column; gap: 8px; min-width: 0; height: 100%; min-height: 0; }
.mon-cards { flex: none; }
.mon-body { flex: 1 1 0; min-height: auto; }
.mon-top, .mon-mid, .mon-bottom { display: flex; flex-direction: column; gap: 8px; overflow: hidden; }
/* Минимум частей монитора (2026-10-10): у таблиц — подпись, панель, шапка и одна строка; у графика — подпись и 30 px.
   Меньше места — прокручивается форма. */
.mon-body > .mon-top { min-height: 150px; }
.mon-body > .mon-mid { min-height: 64px; }
.mon-body > .mon-bottom { min-height: 110px; }
.mon-mid .mon-chart { flex: 1 1 auto; height: auto; min-height: 30px; }
.mon-top .mon-grid, .mon-bottom .mon-grid { flex: 1 1 auto; min-height: 0; max-height: none; }
.mon-grid > .ws-grid { overflow: visible; }
.mon-grid .ws-grid-header { position: sticky; top: 0; z-index: 2; }
.mon-sql, .mon-chart, .mon .dbm-cap { flex: none; }
.mon-cards { display: flex; flex-wrap: wrap; gap: 8px; }
.mon-card { flex: 1 1 110px; min-width: 0; padding: 6px 10px; border: 1px solid var(--bd-dde3ea); border-radius: 8px; background: var(--bg-fafbfc); }
.mon-card-v { font-size: 18px; font-weight: 600; color: var(--fg-17325c); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mon-card-t { font-size: 12.5px; color: var(--fg-444444); }
.mon-grid { overflow: auto; max-height: 40vh; }
/* Таблицы монитора — в ширину формы: колонки сжимаются (minmax(0,…)), длинные адреса переносятся в ячейке. */
.ws-grid-mon-active .ws-grid-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 2.6fr) minmax(0, 0.7fr) minmax(0, 3fr); }
.ws-grid-mon-slow .ws-grid-row { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 2.8fr) minmax(0, 0.8fr) minmax(0, 1.6fr); }
.ws-grid-mon-active .ws-grid-cell, .ws-grid-mon-slow .ws-grid-cell { overflow-wrap: anywhere; min-width: 0; }
.mon-row-long > * { color: var(--fg-b97a00); }
.mon-row-cancel > * { color: var(--fg-c62828); }
.mon-err { color: var(--fg-c62828); }
.mon-sql pre { margin: 4px 0 0; padding: 8px; max-height: 180px; overflow: auto; background: var(--bg-f5f7fa); border: 1px solid var(--bd-dde3ea); border-radius: 6px; font-size: 12px; white-space: pre-wrap; }
.mon-chart { display: flex; align-items: stretch; gap: 6px; height: 56px; padding: 8px 6px 8px 4px; border: 1px solid var(--bd-dde3ea); border-radius: 6px; }
.mon-axis { position: relative; flex: none; min-width: 3ch; font-size: 10px; line-height: 1; color: var(--fg-888888); text-align: right; }
.mon-axis > span { position: absolute; right: 0; transform: translateY(50%); white-space: nowrap; }
.mon-bars { position: relative; flex: 1 1 auto; min-width: 0; display: flex; align-items: flex-end; gap: 2px; border-bottom: 1px solid var(--bd-dde3ea); }
.mon-gridline { position: absolute; left: 0; right: 0; border-top: 1px dashed var(--bd-dde3ea); pointer-events: none; }
.mon-bar { position: relative; z-index: 1; flex: 1; height: 100%; display: flex; align-items: flex-end; }
.mon-bar > div { width: 100%; min-height: 1px; background: var(--bg-2b579a); border-radius: 2px 2px 0 0; }

/* Кнопка команды с Пометкой (2026-10-05) — нажатый переключатель. */
.bias-btn.bias-command-btn.bias-command-checked { background: var(--bg-d9e8fb); border-color: var(--bd-2b579a); color: var(--fg-17325c); box-shadow: inset 0 1px 3px var(--sh-0_0_0_0p25); }

/* Условное оформление: поле объекта скрыто (Видимость = Ложь), 2026-10-05. */
.ar-slot.ar-slot-ca-hidden { display: none !important; }

/* «Настройка списка» → Условное оформление (2026-10-05). */
.bias-ls-tabs { margin-bottom: 6px; }
.bias-ls-ca { display: flex; flex-direction: column; flex: 0 0 auto; }
.bias-ls-ca[hidden] { display: none; }
.bias-ls-ca-grid { height: 150px; min-height: 0; }
.bias-ls-grid-ca .ws-grid-row { grid-template-columns: 34px minmax(90px, 0.8fr) minmax(0, 1fr) minmax(0, 2fr); }
.bias-ls-ca-caption { margin: 8px 0 4px; color: var(--fg-5a6472); font-size: 0.92em; }
.bias-ls-ca-sample { display: inline-block; padding: 1px 6px; border-radius: 3px; }
.bias-ls-ca-dialog { width: min(520px, calc(100vw - 32px)); }
.bias-ls-ca-form { display: flex; flex-direction: column; gap: 10px; padding: 12px 4px; }
.bias-ls-ca-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.bias-ls-ca-row .bias-field { flex: 1 1 160px; min-width: 0; }
.bias-ls-ca-color { width: 46px; height: 28px; padding: 0 2px; border: 1px solid var(--bd-c7cedb); border-radius: 4px; background: transparent; }
.bias-ls-ca-fields-title { margin-top: 4px; }
.bias-ls-ca-fields { display: flex; flex-wrap: wrap; gap: 6px 16px; max-height: 160px; overflow: auto; }
.bias-ls-ca-preview span { display: inline-block; padding: 2px 8px; border: 1px dashed var(--bd-c7cedb); border-radius: 3px; }

/* «Изменить форму…» (2026-10-05). */
.ws-fs-dialog { width: min(560px, calc(100vw - 32px)); max-height: calc(100vh - 80px); display: flex; flex-direction: column; }
.ws-fs-hint { margin: 10px 4px 6px; color: var(--fg-5a6472); font-size: 0.92em; }
.ws-fs-list { flex: 1 1 auto; overflow: auto; min-height: 120px; max-height: 60vh; border: 1px solid var(--bd-dde3ea); border-radius: 4px; padding: 4px 0; }
.ws-fs-row { display: flex; align-items: center; gap: 10px; padding: 3px 8px; }
.ws-fs-kind { color: var(--fg-5a6472); font-size: 0.85em; margin-left: auto; white-space: nowrap; }
.ws-fs-actions { display: flex; gap: 8px; align-items: center; margin-top: 10px; }
/* Табличная часть без колонки «№» — колонки делят ширину поровну (первая не 48px). */
.ar-part-no-num .ar-part-table .ws-grid-row, .ar-part-table.ar-part-no-num .ws-grid-row { grid-template-columns: repeat(var(--ar-part-cols, 1), minmax(0, 1fr)); }

/* «Вывести список» (2026-10-06, bias-grid.js openOutputListDialog). */
.bias-ol { min-width: 360px; }
.bias-ol-cap { font-weight: 600; margin-bottom: 6px; }
.bias-ol-cols { display: flex; flex-direction: column; gap: 4px; max-height: 50vh; overflow: auto; padding: 6px 8px; border: 1px solid var(--bd-c7cedb); border-radius: 4px; }
.bias-ol-opts { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; }
.bias-ol-links { font-size: 12.5px; }
.bias-ol-status { min-height: 18px; margin-top: 8px; color: var(--fg-6b7684); font-size: 12.5px; }
.bias-ol-status .ws-spinner { width: 11px; height: 11px; margin-right: 6px; }

/* Заголовок с путём: путь — мелким тусклым курсивом, имя — крупнее и жирным (2026-10-07). */
.ws-fh-title { flex: 0 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ws-fh-title.ws-fh-title-2 { display: flex; flex-direction: column; line-height: 1.05; max-width: 70%; }
.ws-fh-path { font-size: 11px; font-weight: 400; font-style: italic; color: var(--fg-888888, #888); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ws-fh-name { font-size: 16px; font-weight: 700; color: var(--fg-222222); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Меню с подменю и сочетаниями клавиш (2026-10-07, меню «⋮» редактора): пиктограмма · название · сочетание · ▸. */
.ws-context-menu-text { flex: 1 1 auto; }
.ws-context-menu-shortcut { margin-left: 28px; color: var(--fg-8a939e); font-size: 12px; }
.ws-context-menu-arrow { margin-left: 18px; color: var(--fg-8a939e); font-size: 11px; }
.ws-context-menu-item.ws-context-menu-open { background: var(--bg-eef1f4); }
.ws-context-menu-item:disabled .ws-context-menu-icon { opacity: .45; }
.ws-context-menu-item:disabled .ws-context-menu-shortcut { opacity: .7; }

/* «☰» в шапке (2026-10-07): сворачивает левую панель до столбика пиктограмм (Админка и Рабочий стол);
   подпись пункта — во всплывающей подсказке. Кнопка подсвечена, пока панель развёрнута. */
.ws-topbar .ws-mainmenu-btn { padding: 3px 6px; margin-right: 6px; line-height: 1; display: inline-flex; align-items: center; }
.ws-topbar .ws-mainmenu-btn[aria-pressed="true"] { background: var(--bg-255_255_255_0p15); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35); }
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) { width: 52px !important; min-width: 0; overflow-x: hidden; padding-top: 6px; }
body.ws-nav-collapsed #ws-splitter-nav,
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-tree-toolbar,
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-tree-children,
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-tree-arrow { display: none !important; }
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-nav-section { font-size: 0; padding: 0; margin: 6px 10px; border-top: 1px solid var(--bd-dde3ea); }
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-nav-section:first-child { display: none; }
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-nav-item { display: flex !important; align-items: center; justify-content: center; height: 40px; padding: 0 !important; font-size: 0; }
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-nav-item::before { width: 24px !important; height: 24px !important; margin: 0 !important; background-size: 24px 24px !important; background-position: center !important; }
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-tree-icon-tile { width: auto; margin: 0; }
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-tree-icon-tile img { width: 24px; height: 24px; vertical-align: middle; }
/* Пункт без пиктограммы — первые буквы подписи в кружке. */
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-nav-item.ws-nav-noicon::after { content: attr(data-abbr); display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 6px; font-size: 11px; font-weight: 600; color: var(--fg-ffffff); background: var(--bg-6b7a90, #6b7a90); }
body.ws-nav-collapsed #ws-nav:not(.ws-nav-peek) .ws-nav-item.ws-nav-noicon::before { display: none; }
/* Свёрнута + навели на столбик — полная панель всплывает поверх форм (в потоке остаётся 52px). */
/* Кнопки прокрутки свёрнутой панели (2026-10-10) — вместо полосы прокрутки, прилипают к верху и низу
   (и у всплывшей по наведению — та же панель, кнопки не пропадают из-под мыши). */
.ws-nav-scrollbtn { display: none; }
body.ws-nav-collapsed #ws-nav { scrollbar-width: none; }
body.ws-nav-collapsed #ws-nav::-webkit-scrollbar { display: none; }
body.ws-nav-collapsed #ws-nav.ws-nav-overflow .ws-nav-scrollbtn {
  display: flex; align-items: center; justify-content: center; position: sticky; z-index: 3;
  width: 100%; height: 22px; padding: 0; border: none; background: var(--bg-ffffff); color: var(--fg-555555); cursor: pointer;
}
body.ws-nav-collapsed #ws-nav.ws-nav-overflow .ws-nav-scrollbtn-up { top: -6px; margin-top: -6px; border-bottom: 1px solid var(--bd-dde3ea); }
body.ws-nav-collapsed #ws-nav.ws-nav-overflow .ws-nav-scrollbtn-down { bottom: -8px; margin-bottom: -8px; border-top: 1px solid var(--bd-dde3ea); }
body.ws-nav-collapsed #ws-nav.ws-nav-overflow.ws-nav-peek .ws-nav-scrollbtn-up { top: -8px; margin-top: -8px; }
body.ws-nav-collapsed #ws-nav .ws-nav-scrollbtn:hover:not(:disabled) { background: var(--bg-eef4fb); }
body.ws-nav-collapsed #ws-nav .ws-nav-scrollbtn:disabled { color: var(--fg-cccccc, #ccc); cursor: default; }
body.ws-nav-collapsed #ws-nav .ws-nav-scrollbtn .ws-icon { width: 16px; height: 16px; }
body.ws-nav-collapsed #ws-nav.ws-nav-peek { width: 240px !important; margin-right: -188px; position: relative; z-index: 70; box-shadow: 6px 0 18px var(--sh-20_40_80_p14); }

/* Диалоги кода (ПоказатьВопрос и т.п., 2026-10-07). */
.ws-script-dialog { max-width: 520px; }
.ws-dialog-body { padding: 8px 0 12px; display: flex; flex-direction: column; gap: 8px; }
.ws-dialog-text { white-space: normal; line-height: 1.4; }
.ws-dialog-hint { color: var(--fg-888888); font-size: 12px; }
.ws-dialog-input { width: 100%; box-sizing: border-box; }
.ws-dialog-list { display: flex; flex-direction: column; max-height: 50vh; overflow: auto; border: 1px solid var(--bd-dde3ea); border-radius: 4px; }
.ws-dialog-list-item { text-align: left; background: none; border: 0; padding: 6px 10px; cursor: pointer; color: inherit; font: inherit; }
.ws-dialog-list-item:hover, .ws-dialog-list-item:focus { background: var(--bg-eef3fb, rgba(43,87,154,0.08)); }
.ws-dialog-buttons { display: flex; gap: 8px; justify-content: flex-end; }

/* Надпись/поле-гиперссылка с обработчиком Нажатие (2026-10-07). */
.bias-field-link { color: var(--fg-2b579a, #2b579a); text-decoration: underline; cursor: pointer; }
.bias-field-link:hover { text-decoration: none; }

/* ── Синтакс-помощник (Админка → Сервис): дерево | сплиттер | таблица / сплиттер / описание — в границах вкладки ── */
.ws-page.ws-page-apicat { height: 100%; padding: 10px 16px 12px; overflow: hidden; }
.ws-apicat-root { height: 100%; display: flex; flex-direction: column; min-height: 0; }
.ws-apicat-toolbar { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; margin: 0 0 8px; flex: none; }
.ws-apicat-label { display: flex; gap: 6px; align-items: center; }
.ws-apicat-summary { opacity: .75; }
.ws-apicat-body { flex: 1; min-height: 0; display: flex; }
.ws-apicat-tree { overflow: auto; outline: none; padding: 2px 4px 2px 0; }
.ws-apicat-node { cursor: pointer; white-space: nowrap; padding: 3px 6px; border-radius: 3px; line-height: 1.35; }
.ws-apicat-node:hover { background: var(--bg-eef4fb); }
.ws-apicat-node.sel { background: var(--bg-eef4fb); }
.ws-apicat-tree:focus .ws-apicat-node.sel { background: var(--bg-c2d9f0); box-shadow: inset 0 0 0 2px var(--sh-5b8dc9); }
.ws-apicat-node-all { font-weight: 600; }
.ws-apicat-twisty { display: inline-block; width: 14px; opacity: .7; }
.ws-apicat-count { opacity: .6; }
.ws-apicat-right { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.ws-apicat-title { font-weight: 600; margin: 2px 0 6px; flex: none; }
.ws-apicat-gridwrap { overflow: auto; }
.ws-apicat-gridwrap > .ws-grid { overflow: visible; }
.ws-apicat-gridwrap .ws-grid-header { position: sticky; top: 0; z-index: 2; }
.ws-apicat-details { overflow: auto; padding: 6px 2px 2px; }
.ws-apicat-hint { opacity: .65; margin: 4px 0; }
.ws-apicat-d-head { margin-bottom: 4px; }
.ws-apicat-d-en, .ws-apicat-d-kind, .ws-apicat-d-file { opacity: .65; }
.ws-apicat-d-desc { margin: 4px 0 6px; }
.ws-apicat-d-facts { margin: 4px 0 6px; line-height: 1.5; }
.ws-apicat-d-label { font-weight: 600; margin: 6px 0 3px; }
.ws-apicat-code { margin: 0; padding: 6px 8px; background: var(--bg-f5f7fa); border-radius: 4px; overflow: auto; font-family: Consolas, "Courier New", monospace; font-size: 12.5px; white-space: pre; }
.ws-apicat-yes { color: #2e9b48; font-weight: 600; }
.ws-apicat-no { color: #d0453f; font-weight: 600; }
.ws-apicat-gap { color: #d08a1f; font-weight: 600; }
.ws-apicat-na { opacity: .5; }

/* ── Лента «Новое» (Справка → Новое) и точка на «?» ── */
.ws-help-btn { position: relative; }
.ws-news { max-width: 980px; }
.ws-news-day { margin: 14px 0 6px; font-size: 14px; opacity: .8; }
.ws-news-item { padding: 8px 10px; border: 1px solid var(--bd-dde3ea); border-radius: 6px; margin-bottom: 8px; }
.ws-news-area { font-size: 11px; padding: 1px 7px; border-radius: 8px; background: var(--bg-eef4fb); margin-right: 4px; }
.ws-news-text { margin: 4px 0; }
.ws-news-items { font-size: 12.5px; opacity: .9; }
.ws-apicat-new { font-size: 10.5px; padding: 0 6px; border-radius: 8px; background: var(--bg-2b579a); color: #fff; margin-left: 6px; vertical-align: 1px; }

/* ── Поле вида ПолеФайла (DropFile): зона перетаскивания, ход загрузки, ссылка ── */
.bias-filefield { display: flex; flex-direction: column; gap: 8px; width: min(640px, 100%); }
.bias-filefield-zone { border: 2px dashed var(--bd-c7cedb); border-radius: 8px; padding: 28px 16px; text-align: center; cursor: pointer;
  color: var(--fg-666666); background: var(--bg-f5f7fa); outline: none; }
.bias-filefield-zone:hover, .bias-filefield-zone:focus, .bias-filefield-drag .bias-filefield-zone { border-color: var(--bg-2b579a); background: var(--bg-eef4fb); }
.bias-filefield-readonly { cursor: default; opacity: .6; }
.bias-filefield-progress { margin-top: 12px; display: flex; align-items: center; gap: 10px; }
.bias-filefield-bar { flex: 1; height: 8px; border-radius: 4px; background: var(--bg-dde3ea); overflow: hidden; }
.bias-filefield-fill { height: 100%; width: 0; background: var(--bg-2b579a); transition: width .2s; }
.bias-filefield-pct { font-size: 12px; white-space: nowrap; }
.bias-filefield-name { font-weight: 600; }
.bias-filefield-linkrow { display: flex; gap: 8px; align-items: center; }
.bias-filefield-link { flex: 1; min-width: 0; }
.bias-filefield-note { font-size: 12px; opacity: .75; }
