/*!*****************************************************************************************************************************************************************************************************************************************************************!*\
  !*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[10].use[2]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[14].oneOf[10].use[3]!./src/app/globals.css ***!
  \*****************************************************************************************************************************************************************************************************************************************************************/
/*
 * Дизайн-система интерфейса (perplexigrid-стиль). Токены заданы CSS-переменными.
 *
 * Цветовые токены задаются ТЕМАМИ (JSON-файлы в src/themes/, применяются на :root
 * из @/lib/theme). Значения ниже — только дефолт-фолбэк (тёмная тема) на случай
 * первого кадра/отключённого JS. Не цветовые токены (--radius, --mono, --sans)
 * живут здесь и темами не управляются. Менять палитру — в JSON-темах, не тут.
 */

:root {
  --bg: #0a090f;
  --surface: #14121b;
  --surface-2: #1c1926;
  --border: #2a2633;
  --text: #ececf1;
  --text-muted: #9891a8;
  --accent: #a970ff;
  --accent-2: #d96bf5;
  --accent-weak: #241a38;
  --accent-grad: linear-gradient(135deg, #b57bff 0%, #d96bf5 100%);
  --ok: #45c07f;
  --ok-weak: #13241c;
  --warn: #e0a944;
  --warn-weak: #2a2113;
  --err: #ef6a6a;
  --err-weak: #2a1620;
  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 8px 28px rgba(0, 0, 0, 0.38);
  --glow: 0 0 0 1px rgba(169, 112, 255, 0.14), 0 8px 30px rgba(169, 112, 255, 0.12);
  --page-glow: radial-gradient(1100px 560px at 85% -12%, rgba(169, 112, 255, 0.1), transparent 62%);
  --mono: ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Menlo, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  /* Декоративная подсветка фона задаётся темой (--page-glow), под ней — фон.
     Фиксируем, чтобы свечение не «уезжало» при прокрутке. */
  background: var(--page-glow, none), var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

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

code,
pre {
  font-family: var(--mono);
}


/* ---------- Бренд ---------- */
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}
.brand-mark .icon {
  vertical-align: 0;
}
.brand-mark {
  line-height: 0;
  color: #fff;
  width: 28px;
  height: 28px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: var(--accent-grad);
  box-shadow: 0 2px 12px rgba(169, 112, 255, 0.4);
}
.brand h1 {
  font-size: 15px;
  margin: 0;
  font-weight: 650;
  letter-spacing: -0.01em;
}
.brand-sub {
  color: var(--text-muted);
  font-size: 12px;
  margin: 0 0 20px;
}

/* ---------- Секции статуса ---------- */
.status-group {
  margin-bottom: 18px;
}
.status-group h2 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 0 0 8px;
  font-weight: 600;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 550;
}
.pill.ok {
  background: var(--ok-weak);
  color: var(--ok);
}
.pill.warn {
  background: var(--warn-weak);
  color: var(--warn);
}
.pill.err {
  background: var(--err-weak);
  color: var(--err);
}
.pill .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.kv {
  font-size: 12px;
  color: var(--text-muted);
  margin: 6px 0 0;
}
.kv b {
  color: var(--text);
  font-weight: 550;
}
.kv code {
  background: var(--surface-2);
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 11px;
}

/* ---------- Кнопки ---------- */
button {
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
}
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-weight: 550;
  transition: background 0.12s, border-color 0.12s;
}
@media (hover: hover) {
  .btn:hover:not(:disabled) {
    background: var(--surface-2);
  }
}
.btn.primary {
  background: var(--accent-grad);
  border-color: transparent;
  color: #fff;
  box-shadow: 0 2px 12px rgba(169, 112, 255, 0.3);
}
@media (hover: hover) {
  .btn.primary:hover:not(:disabled) {
    filter: brightness(1.08);
    background: var(--accent-grad);
  }
}
.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.btn.block {
  width: 100%;
}

/* ---------- Вкладки ---------- */
.tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 22px;
}
.tab {
  padding: 10px 16px;
  border: none;
  background: none;
  color: var(--text-muted);
  font-weight: 550;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
@media (hover: hover) {
  .tab:hover {
    color: var(--text);
  }
}
.tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ---------- Карточки ---------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 18px 20px;
  margin-bottom: 16px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.card h3 {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 620;
}
.card .muted {
  color: var(--text-muted);
  font-size: 13px;
  margin: 0 0 14px;
}

.page-title {
  font-size: 20px;
  font-weight: 650;
  margin: 0 0 4px;
}
.page-sub {
  color: var(--text-muted);
  margin: 0 0 22px;
}

/* ---------- Таблицы ---------- */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
}
th,
td {
  text-align: left;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
th {
  background: var(--surface-2);
  font-weight: 600;
  color: var(--text-muted);
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}
tbody tr:last-child td {
  border-bottom: none;
}
@media (hover: hover) {
  tbody tr:hover {
    background: var(--surface-2);
  }
}

/* ---------- Бейджи стадий ---------- */
.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  background: var(--accent-weak);
  color: var(--accent);
  white-space: nowrap;
}

/* ---------- Список сценариев ---------- */
.scenario-row {
  cursor: pointer;
}
.scenario-row td:first-child {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
}
.step {
  display: flex;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px dashed var(--border);
}
.step:last-child {
  border-bottom: none;
}
.step-n {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
}
.step-tool {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--accent);
  font-weight: 600;
}
.step-note {
  color: var(--text-muted);
  font-size: 13px;
}

/* ---------- Промпт / код ---------- */
pre.code {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 16px;
  overflow-x: auto;
  font-size: 12.5px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
}

/* ---------- Дропзона ---------- */
.dropzone {
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  padding: 32px;
  text-align: center;
  color: var(--text-muted);
  transition: border-color 0.15s, background 0.15s;
  cursor: pointer;
}
.dropzone.drag {
  border-color: var(--accent);
  background: var(--accent-weak);
}

/* ---------- Чат ---------- */
.chat-empty {
  text-align: center;
  color: var(--text-muted);
  padding: 48px 20px;
}
.chat-log {
  max-height: 60vh;
  min-height: 240px;
  overflow-y: auto;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.chat-input-row {
  display: flex;
  gap: 8px;
  align-items: flex-end;
  padding: 14px;
  /* Черта над строкой ввода: лента прокручивается под ней, и без черты
     последняя реплика читается как продолжение поля. */
  border-top: 1px solid var(--border);
}
.chat-textarea {
  flex: 1 1;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  resize: vertical;
  line-height: 1.5;
}
.chat-textarea:disabled {
  background: var(--surface-2);
  cursor: not-allowed;
}
.chat-input-row input {
  flex: 1 1;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
}
.chat-input-row input:disabled {
  background: var(--surface-2);
  cursor: not-allowed;
}

.msg {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.msg-role {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: 600;
}
.msg-body {
  max-width: 80%;
  padding: 10px 14px;
  border-radius: var(--radius);
  white-space: pre-wrap;
  word-break: break-word;
}
.msg.user {
  align-items: flex-end;
}
.msg.user .msg-body {
  background: var(--accent);
  color: #fff;
}
.msg.assistant .msg-body {
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.msg-text {
  margin-top: 2px;
}
.typing {
  color: var(--text-muted);
  font-style: italic;
}

/* Markdown внутри ответа ассистента: сбрасываем pre-wrap контейнера и задаём
   аккуратные отступы блокам. */
.msg-text.markdown {
  white-space: normal;
}
.msg-text.markdown > :first-child {
  margin-top: 0;
}
.msg-text.markdown > :last-child {
  margin-bottom: 0;
}
.msg-text.markdown p {
  margin: 0 0 8px;
}
.msg-text.markdown h1,
.msg-text.markdown h2,
.msg-text.markdown h3 {
  margin: 12px 0 6px;
  font-size: 15px;
  font-weight: 650;
}
.msg-text.markdown ul,
.msg-text.markdown ol {
  margin: 4px 0 8px;
  padding-left: 20px;
}
.msg-text.markdown li {
  margin: 2px 0;
}
.msg-text.markdown code {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 12px;
}
.msg-text.markdown pre {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  overflow-x: auto;
  margin: 8px 0;
}
.msg-text.markdown pre code {
  border: none;
  padding: 0;
  background: none;
}
.msg-text.markdown table {
  border-collapse: collapse;
  margin: 8px 0;
  font-size: 12.5px;
  display: block;
  overflow-x: auto;
}
.msg-text.markdown th,
.msg-text.markdown td {
  border: 1px solid var(--border);
  padding: 5px 9px;
  white-space: nowrap;
}
.msg-text.markdown a {
  color: var(--accent);
}
.msg-text.markdown blockquote {
  margin: 8px 0;
  padding-left: 12px;
  border-left: 3px solid var(--border);
  color: var(--text-muted);
}

.tool-call {
  margin: 4px 0;
}
.tool-call-head {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-size: 12px;
}
.tool-call-head code {
  color: var(--accent);
  font-weight: 600;
}
.tool-ico {
  display: inline-flex;
  line-height: 0;
  flex: none;
}
.tool-args {
  margin-top: 6px;
  font-size: 11.5px;
}
/* Упавший инструмент (напр. недоступна библиотека): виден в чате, не тонет. */
.tool-failed .tool-call-head {
  border-color: var(--err);
  background: var(--err-weak);
}
.tool-status-err {
  color: var(--err);
  font-size: 11px;
  font-weight: 600;
}

/* Ход агента — один блок, а не россыпь пузырей. Внутри лента: рассуждение,
   вызовы инструментов и текст ответа в том порядке, в каком это случилось.
   Ширину ответа не режем на 80%: в нём таблицы и код. */
.msg.assistant .msg-body {
  max-width: 100%;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Текст внутри ленты уже отбит gap'ом — свой отступ ему не нужен. */
.msg.assistant .msg-body > .msg-text {
  margin-top: 0;
}

/* Строка состояния идущего хода: чем агент занят прямо сейчас. */
.turn-phase {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  color: var(--text-muted);
  font-size: 12px;
}

/* ---------- Рассуждение модели (extended thinking) ----------
   Свёрнутая мысль — не плашка «размышление», а полоска с ХВОСТОМ мысли: видно,
   о чём агент думает, ничего не раскрывая. Пока мысль пишется, полоска живёт;
   закончилась — замирает. */
.think {
  border-left: 2px solid var(--border);
  padding-left: 10px;
  margin: 2px 0;
}
.think-head {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 2px 0;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 12px;
  text-align: left;
  cursor: pointer;
}
.think-head:hover {
  color: var(--text);
}
/* Точка состояния: тихая, пока мысль дописана, и пульсирующая, пока идёт. */
.think-dot {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: 50%;
  background: var(--border);
}
.think.live .think-dot {
  background: var(--accent);
  animation: think-pulse 1.4s ease-in-out infinite;
}
.think-ico {
  display: inline-flex;
  line-height: 0;
  flex: none;
}
.think-label {
  flex: none;
  font-style: italic;
}
/* Хвост мысли: одна строка, остальное срезано. Растягивается на всё свободное
   место, поэтому длина полоски не прыгает от длины мысли. */
.think-peek {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0.75;
}
.think-chevron {
  display: inline-flex;
  line-height: 0;
  flex: none;
  color: var(--text-muted);
  transition: transform 0.15s ease;
}
.think.open .think-chevron,
.tool-call-head[aria-expanded="true"] .think-chevron {
  transform: rotate(90deg);
}
.think-body {
  margin: 4px 0 6px;
  color: var(--text-muted);
  font-size: 12.5px;
  line-height: 1.5;
  white-space: pre-wrap;
}
@keyframes think-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.7); }
}
/* Движение — украшение состояния, а не само состояние: цвет точки отличает
   идущую мысль от дописанной и без анимации. */
@media (prefers-reduced-motion: reduce) {
  .think.live .think-dot { animation: none; }
  .think-chevron { transition: none; }
}

/* Идущий вызов инструмента: спиннер уже крутится, поэтому рамку не трогаем —
   двух сигналов одного состояния на одной карточке достаточно одного. */
.tool-running .tool-call-head {
  border-color: var(--accent);
}

/* ---------- Список диалогов в сайдбаре ---------- */
.conv-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 34vh;
  overflow-y: auto;
}
.conv-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 7px 9px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13px;
}
@media (hover: hover) {
  .conv-item:hover {
    background: var(--surface-2);
  }
}
.conv-item.active {
  background: var(--accent-weak);
  color: var(--accent);
}
.conv-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.conv-del {
  border: none;
  background: none;
  color: var(--text-muted);
  font-size: 18px;
  line-height: 1;
  padding: 0 4px;
  border-radius: 4px;
  flex: none;
}
@media (hover: hover) {
  .conv-del:hover {
    color: var(--err);
    background: var(--err-weak);
  }
}

/* ---------- Полоса документов диалога ---------- */
.doc-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}
.doc-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.doc-chip em {
  color: var(--text-muted);
  font-style: normal;
}

/* ---------- Карточка чата ----------
   Своих полей у неё нет: их держат лента и строка ввода, а разделяющая их черта
   должна идти от края до края. Обрезка — ради скруглённых углов: лента и поле
   доходят до самого края карточки и иначе вылезали бы из них. */
.chat-card {
  padding: 0;
  overflow: hidden;
}

/* ---------- Дропзона на карточке чата ---------- */
.chat-card.drag {
  outline: 2px dashed var(--accent);
  outline-offset: -4px;
}

/* ---------- Карточка чекпоинта ---------- */
.checkpoint {
  border: 1px solid var(--warn);
  background: var(--warn-weak);
  border-radius: var(--radius);
  padding: 14px 16px;
  align-self: stretch;
}
.checkpoint-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
}
.checkpoint-ico {
  display: inline-flex;
  line-height: 0;
  color: var(--warn);
  flex: none;
}
.checkpoint-q {
  margin: 8px 0 12px;
  font-size: 13px;
}
.checkpoint-params {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.checkpoint-params td {
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
  white-space: normal;
}
.param-key {
  font-family: var(--mono);
  color: var(--text-muted);
  width: 40%;
}
.param-val {
  font-family: var(--mono);
}
/* Готовые варианты ответа: решение одним нажатием вместо формулировки текстом.
   Оформлены НЕ как главные кнопки — подтвердить/отклонить остаются заметнее,
   иначе ряд одинаковых кнопок превращает выбор в угадайку. */
.checkpoint-choices {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 10px 0;
}
.checkpoint-choices-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: 600;
}
.checkpoint-choices-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.choice-btn {
  padding: 5px 11px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font-size: 12.5px;
  cursor: pointer;
}
.choice-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.checkpoint-actions {
  display: flex;
  gap: 8px;
}
.checkpoint-reject {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* ---------- Шапка чата с кнопкой панели ---------- */
.chat-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
.chat-header .btn {
  flex: none;
  white-space: nowrap;
}

/* ---------- Заглушка пустого списка ---------- */
.drawer-empty {
  color: var(--text-muted);
  font-size: 13px;
  text-align: center;
  padding: 32px 12px;
}

/* ---------- Графики в панели ---------- */
.chart-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  grid-gap: 12px;
  gap: 12px;
}
.chart-card {
  margin: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-2);
}
.chart-card img {
  display: block;
  width: 100%;
  height: auto;
  background: #fff;
}
.chart-card figcaption {
  padding: 6px 8px;
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- Список файлов в панели ---------- */
.file-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.file-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.file-ico {
  display: inline-flex;
  line-height: 0;
  color: var(--text-muted);
  flex: none;
}
.file-main {
  min-width: 0;
  flex: 1 1;
}
.file-name {
  display: block;
  font-size: 13px;
  font-weight: 550;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (hover: hover) {
  a.file-name:hover {
    color: var(--accent);
  }
}
.file-meta {
  font-size: 11.5px;
  color: var(--text-muted);
}
.file-warn {
  color: var(--warn);
}

/* ---------- База знаний (поиск GMT) ---------- */
.kb-search {
  display: flex;
  gap: 8px;
  align-items: stretch;
  margin-bottom: 12px;
}
.kb-search .chat-textarea {
  min-height: 0;
  height: 40px;
}
.kb-results {
  margin-bottom: 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.kb-hit {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.kb-hit-name {
  font-size: 12.5px;
  font-weight: 550;
  word-break: break-word;
}
.kb-hit-meta {
  font-size: 11.5px;
  color: var(--text-muted);
  margin-top: 3px;
}
.kb-section {
  border-top: 1px solid var(--border);
  padding-top: 14px;
}
.kb-section-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.kb-section-head h4 {
  margin: 0;
  font-size: 13px;
  flex: 1 1;
}

/* Фильтры по видам + кнопка загрузки в базе знаний. */
.kb-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 10px 0 12px;
}
.kb-filters .chip {
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface);
}
.kb-filters .chip.active {
  background: var(--accent-weak);
  color: var(--accent);
  border-color: var(--accent);
}
.kb-add {
  margin-left: auto;
  padding: 4px 10px;
}
/* Строка базы знаний выше файловой (описание, теги, форма правки), поэтому
   иконка и кнопки равняются по верху — иначе они уезжали бы к середине блока.
   Специфичность двух классов нужна, чтобы перебить общее .file-row ниже. */
.file-row.kb-row {
  align-items: flex-start;
}
.file-row.kb-row .file-ico {
  margin-top: 2px;
}
.kb-row-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: none;
}
.kb-row-actions .btn {
  padding: 4px 8px;
}
.kb-src {
  margin-left: 6px;
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 0.04em;
}
.kb-edit {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}

/* ---------- Служебное ---------- */
.notice {
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  margin: 0 0 14px;
}
.notice.info {
  background: var(--accent-weak);
  color: var(--accent);
}
.notice.warn {
  background: var(--warn-weak);
  color: var(--warn);
}
.notice.err {
  background: var(--err-weak);
  color: var(--err);
}
.spinner {
  color: var(--text-muted);
  font-size: 13px;
  padding: 20px 0;
}
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}
.chip {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-muted);
}

/* ---------- Переключатель темы (сайдбар) ---------- */
.theme-switcher {
  margin-top: 16px;
}
.theme-select {
  width: 100%;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--sans);
  font-size: 13px;
  cursor: pointer;
}
@media (hover: hover) {
  .theme-select:hover {
    border-color: var(--accent);
  }
}
.theme-select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* ============================================================= */
/*  Воркспейс perplexigrid: чат | доска виджетов | источники     */
/* ============================================================= */

/* Ширины боковых колонок живут в переменных: сворачивание панели — это твин
   числа в них (см. Workspace.tsx), а не смена шаблона сетки. Значения ниже —
   первый кадр до того, как GSAP подставит свои под ширину окна. */
.workspace {
  display: grid;
  --ws-chat-w: 340px;
  --ws-side-w: 320px;
  grid-template-columns: var(--ws-chat-w) minmax(0, 1fr) var(--ws-side-w);
  grid-gap: 16px;
  gap: 16px;
  /* Без сайдбара и полосы вкладок воркспейс занимает почти весь экран: вычитаем
     верхнюю строку и отступы shell-main. Единица — dvh: на телефоне адресная
     строка съезжает, и vh (всегда считающий её свёрнутой) отдал бы больше
     высоты, чем реально видно. Первая строка — запас для старых браузеров. */
  height: calc(100vh - 92px);
  height: calc(100dvh - 92px);
  min-height: 480px;
}
/* Панель и её планка-рельс делят ОДНУ колонку сетки: планка — это свёрнутый вид
   колонки, а не четвёртая колонка. Обе всегда в разметке, видимостью управляют
   data-атрибуты ниже. Так сворачивание не снимает панель с дерева — иначе чат
   терял бы прокрутку, а доска рвала бы идущий ход. */
.ws-rail-left,
.ws-chat {
  grid-column: 1;
  grid-row: 1;
}
.ws-board {
  grid-column: 2;
  grid-row: 1;
}
.ws-side,
.ws-rail-right {
  grid-column: 3;
  grid-row: 1;
}

.workspace[data-chat="panel"] > .ws-rail-left,
.workspace[data-chat="rail"] > .ws-chat,
.workspace[data-side="panel"] > .ws-rail-right,
.workspace[data-side="rail"] > .ws-side {
  display: none;
}

/* Планка вкладок нужна только телефону — её показывает mobile.css. */
.ws-tabs {
  display: none;
}

.ws-chat {
  min-height: 0;
  overflow: hidden;
}
.ws-board {
  min-height: 0;
  overflow-y: auto;
  padding-right: 4px;
}

/* ---- Чат во встроенном режиме (левая колонка) ---- */
.chat-embedded {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.chat-embedded .chat-header {
  margin-bottom: 12px;
}
.chat-embedded .chat-card {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  margin-bottom: 0;
}
.chat-embedded .chat-log {
  max-height: none;
  min-height: 0;
  flex: 1 1 auto;
}

/* ---- Правая колонка: «Источники данных» и «Файлы диалога» вкладками ---- */
.ws-side {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.side-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 0 8px;
  border-bottom: 1px solid var(--border);
}
.side-tabs {
  display: flex;
  gap: 2px;
  flex: 1 1;
  min-width: 0;
}
/* Корешок вкладки: активную держит подчёркивание акцентом — линия совпадает
   с нижней границей шапки, поэтому вкладка читается как продолжение панели. */
.side-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px 9px;
  border: none;
  background: none;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 13px;
  font-weight: 550;
  white-space: nowrap;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s, border-color 0.15s;
}
@media (hover: hover) {
  .side-tab:hover {
    color: var(--text);
  }
}
.side-tab.active {
  color: var(--text);
  border-bottom-color: var(--accent);
}
.side-body {
  padding: 14px 16px;
  overflow-y: auto;
  flex: 1 1;
  min-height: 0;
}
.files-count {
  font-size: 11px;
  color: var(--text-muted);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 7px;
  line-height: 17px;
}
.side-tab.active .files-count {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-weak);
}
.files-empty {
  color: var(--text-muted);
  font-size: 13px;
  padding: 10px 8px;
  margin: 0;
}
.files-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.file-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 8px;
}
@media (hover: hover) {
  .file-row:hover {
    background: var(--bg);
  }
}
.file-ico {
  display: inline-flex;
  line-height: 0;
  color: var(--text-muted);
  flex: 0 0 auto;
}
.file-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
  text-decoration: none;
  font-size: 13px;
}
@media (hover: hover) {
  .file-name:hover {
    color: var(--accent);
    text-decoration: underline;
  }
}
.file-badge {
  flex: 0 0 auto;
  font-size: 11px;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 6px;
  line-height: 16px;
}
.file-size {
  flex: 0 0 auto;
  font-size: 11px;
  color: var(--text-muted);
  font-feature-settings: "tnum";
  font-variant-numeric: tabular-nums;
}
.file-dl {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--text-muted);
  text-decoration: none;
  padding: 4px;
  border-radius: 6px;
}
@media (hover: hover) {
  .file-dl:hover {
    color: var(--accent);
    background: var(--surface);
  }
}

/* ---- Сетка доски ---- */
.board-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  grid-gap: 14px;
  gap: 14px;
  align-content: start;
}
.board-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text-muted);
  height: 100%;
  gap: 12px;
  padding: 40px;
}
.board-empty > svg {
  opacity: 0.85;
  color: var(--text-muted);
}
.board-empty p {
  max-width: 420px;
  margin: 0;
}

/* ---- Карточка виджета ---- */
.widget-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
@media (hover: hover) {
  .widget-card:hover {
    border-color: var(--accent);
    box-shadow: var(--glow);
  }
}
.widget-card.span-2 {
  grid-column: span 2;
}
.widget-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
}
.widget-title {
  margin: 0;
  font-size: 13px;
  font-weight: 620;
  line-height: 1.3;
}
/* Поле переименования повторяет заголовок буква в букву: правка на месте не
   должна дёргать вёрстку шапки. Отличают его только рамка и фон — их достаточно,
   чтобы поле читалось как поле. */
.widget-title-input {
  flex: 1 1;
  min-width: 0;
  margin: -3px -6px;
  padding: 2px 5px;
  font: inherit;
  font-size: 13px;
  font-weight: 620;
  line-height: 1.3;
  color: var(--text);
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--accent);
  border-radius: 6px;
  outline: none;
}
.widget-open {
  color: var(--text-muted);
  font-size: 14px;
  text-decoration: none;
  flex: none;
}
@media (hover: hover) {
  .widget-open:hover {
    color: var(--accent);
  }
}
.widget-body {
  min-height: 0;
}
.widget-img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-sm);
}
.widget-caption {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.45;
}
.widget-caption p {
  margin: 0;
}
.widget-text.markdown {
  font-size: 13px;
  line-height: 1.5;
}
.widget-text.markdown p {
  margin: 0 0 8px;
}
.widget-missing {
  color: var(--text-muted);
  font-size: 12px;
  padding: 10px 0;
  margin: 0;
}

/* ---- Живая сеть взаимодействий (Cytoscape.js) ---- */
.net-wrap {
  display: flex;
  flex-direction: column;
  gap: 8px;
  outline: none; /* карточка ловит фокус ради Delete, но рамка тут лишняя */
}
.net-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--text-muted);
}
.net-select,
.net-search input {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: inherit;
  font-size: 11px;
  padding: 4px 6px;
}
.net-search {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.net-search input {
  width: 108px;
}
.net-slider {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}
.net-slider input {
  width: 84px;
  accent-color: var(--accent);
}
.net-slider b {
  color: var(--text);
  font-feature-settings: "tnum";
  font-variant-numeric: tabular-nums;
}
.net-actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-left: auto;
}
.net-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: inherit;
  font-size: 11px;
  padding: 4px 7px;
  cursor: pointer;
  transition: border-color 0.14s, background 0.14s, opacity 0.14s;
}
@media (hover: hover) {
  .net-btn:hover:not(:disabled) {
    border-color: var(--accent);
  }
}
.net-btn:disabled {
  opacity: 0.45;
  cursor: default;
}
/* Образец цвета для ручной раскраски. Нативный `input[type=color]` в браузерах
   приходит с толстой рамкой и полями — сводим его к квадратику того же размера,
   что и кнопки рядом, чтобы панель не разъезжалась. */
.net-paint {
  display: inline-flex;
  align-items: center;
}
.net-paint input {
  width: 26px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  cursor: pointer;
}
.net-paint input::-webkit-color-swatch-wrapper {
  padding: 2px;
}
.net-paint input::-webkit-color-swatch {
  border: none;
  border-radius: 2px;
}
.net-paint input::-moz-color-swatch {
  border: none;
  border-radius: 2px;
}
.net-btn.is-on {
  border-color: var(--accent);
  color: var(--accent);
}
.net-btn.primary:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}
/* Холст графа: высота задаётся здесь, иначе Cytoscape получит нулевой контейнер
   и не нарисует ничего. Во весь экран карточка становится заметно выше. */
.net-canvas {
  height: 320px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  overflow: hidden;
}
.net-canvas.big {
  height: min(62vh, 620px);
}
/* Легенда рисуется по описанию из файла сети (его собрал инструмент), поэтому
   здесь только оформление: цвета образцов и границы шкалы приходят инлайном. */
.net-legend {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 11px;
  color: var(--text-muted);
}
.net-legend-title {
  font-size: 12px;
  font-weight: 640;
  color: var(--text);
}
.net-legend-scale {
  max-width: 320px;
}
.net-legend-bar {
  height: 13px;
  border: 1px solid var(--border);
  border-radius: 3px;
}
/* Деления — равномерные ячейки под полосой: крайние подписи прижаты к краям,
   поэтому не свисают за пределы шкалы. */
.net-legend-ticks {
  display: flex;
  justify-content: space-between;
  margin-top: 2px;
  font-feature-settings: "tnum";
  font-variant-numeric: tabular-nums;
}
.net-legend-items {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.net-legend-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--text-muted);
}
.net-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.net-swatch {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--text-muted);
}
.net-swatch.ring {
  background: transparent;
  border: 2px solid var(--text-muted);
}
.net-count {
  margin-left: auto;
  font-feature-settings: "tnum";
  font-variant-numeric: tabular-nums;
}
.net-saved {
  color: var(--ok);
}
.net-failed {
  color: var(--err);
}
/* Подсказка «сеть двигается во весь экран» — только на тач-экране, где граф в
   карточке заморожен, чтобы не отбирать прокрутку у страницы (NetworkViewer).
   Живёт здесь, а не в mobile.css: планшет шире телефонного брейкпоинта, а
   палец у него тот же. */
.net-frozen {
  color: var(--text-muted);
  font-style: italic;
}

/* ---- Плитки метрик ---- */
.metric-tiles {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.metric-tile {
  flex: 1 1 auto;
  min-width: 72px;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  text-align: center;
}
.metric-value {
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.1;
}
.metric-label {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 3px;
}

/* ---- Мини-таблица в карточке ---- */
.widget-table-wrap {
  overflow: auto;
  max-height: 240px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.widget-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}
.widget-table th,
.widget-table td {
  padding: 4px 8px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  text-align: left;
}
.widget-table th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background: var(--surface-2);
  color: var(--text-muted);
  font-weight: 600;
}
.widget-table-note {
  font-size: 11px;
  color: var(--text-muted);
  padding: 5px 2px 0;
}

/* ---- Текстовый файл в карточке (статья, протокол) ---- */
.widget-textfile-body {
  /* Текст научной статьи держится на своих переводах строк (абзацы, заголовки
     секций), поэтому pre-wrap: переносим длинные строки, но не склеиваем
     абзацы в сплошное полотно. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  overflow: auto;
  max-height: 240px;
  margin: 0;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-family: inherit; /* проза, а не код — моноширинный шрифт тут мешает читать */
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text);
}
.widget-textfile-body.big {
  max-height: min(70vh, 640px);
  font-size: 13.5px;
}
.widget-textfile-note {
  font-size: 11px;
  color: var(--text-muted);
  padding: 5px 2px 0;
}

/* ---- Адаптив: узкие экраны — колонки в стопку ---- */
@media (max-width: 1100px) {
  /* В стопке ширины колонок не нужны — переменные перебиваем шаблоном в одну
     колонку. Сворачивание на такой ширине смысла не имеет: панели и так идут
     друг за другом (Workspace на этом брейкпоинте не подставляет ширины). */
  .workspace {
    grid-template-columns: 1fr;
    height: auto;
    min-height: 0;
  }
  /* Колонка теперь одна, а размещение выше расставляет детей по трём. Без
     сброса доска уехала бы во вторую колонку, а панель источников — в третью,
     и сетка достроила бы их пустыми. Порядок в стопке задаёт разметка. */
  .workspace > * {
    grid-column: 1;
    grid-row: auto;
  }
  .ws-board {
    overflow: visible;
  }
  .ws-side {
    overflow: visible;
    max-height: 70vh;
  }
  .ws-chat {
    overflow: visible;
  }
  .chat-embedded {
    height: auto;
  }
  .chat-embedded .chat-log {
    max-height: 50vh;
  }
}

/* ============================================================= */
/*  Стартовая страница «Проекты» + верхняя строка воркспейса      */
/* ============================================================= */
.ws-back {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 550;
}
@media (hover: hover) {
  .ws-back:hover {
    color: var(--text);
    border-color: var(--accent);
  }
}

.projects-wrap {
  display: flex;
  justify-content: center;
  padding: 40px 0;
}
.projects-card {
  width: 100%;
  max-width: 680px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px 24px 26px;
}
.projects-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}
.projects-title {
  margin: 0;
  font-size: 19px;
  font-weight: 680;
  letter-spacing: -0.01em;
}
.projects-sub {
  margin: 2px 0 0;
  color: var(--text-muted);
  font-size: 13px;
}
.projects-empty {
  color: var(--text-muted);
  font-size: 13px;
  margin: 0;
  padding: 12px 0;
}

.project-list {
  display: flex;
  flex-direction: column;
}
.project-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 12px;
  margin: 0 -12px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.12s;
}
@media (hover: hover) {
  .project-row:hover {
    background: var(--surface-2);
  }
}
.project-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.project-main {
  min-width: 0;
}
.project-name {
  font-size: 15px;
  font-weight: 580;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.project-meta {
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--text-muted);
  font-size: 12px;
  margin-top: 3px;
}
.project-side {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
.project-time {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--text-muted);
  font-size: 12px;
  white-space: nowrap;
}

/* ============================================================= */
/*  Раскладка доски: ровные ряды без дыр                          */
/* ============================================================= */
/* Ширину карточек считаем от КОЛОНКИ доски, а не от вьюпорта, —
   поэтому доска объявляется контейнером для container-queries. */
.ws-board {
  container-type: inline-size;
  container-name: board;
}

.board-grid {
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  /* dense — заполнять дыры, которые оставляют широкие карточки */
  grid-auto-flow: row dense;
  align-content: start;
}

/* Широкая карточка допустима, только если колонок реально >= 2,
   иначе грид создаёт лишнюю колонку и вылезает за край. */
.widget-card.span-2 {
  grid-column: auto;
}
@container board (min-width: 600px) {
  .widget-card.span-2 {
    grid-column: span 2;
  }
}

/* Карточки в ряду тянутся по самой высокой — чтобы «низкое» содержимое
   (метрики, короткий текст) не выглядело брошенным сверху, центрируем его. */
.widget-card {
  justify-content: flex-start;
}
.widget-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.widget-body > * {
  width: 100%;
}

/* ============================================================= */
/*  Каркас: узкая верхняя строка вместо сайдбара и вкладок        */
/* ============================================================= */
.shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}
.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 9px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  z-index: 20;
}
.brand-btn {
  border: none;
  background: none;
  padding: 0;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  color: var(--text);
}
.brand-name {
  font-size: 14px;
  font-weight: 650;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.crumb {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.crumb-name {
  font-size: 13px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Выход и шестерёнка — правая группа шапки. margin-left:auto теперь на первой
   кнопке группы, иначе выход прижался бы к крошке проекта. */
.topbar-logout {
  margin-left: auto;
  border: 1px solid transparent;
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-size: 12px;
  border-radius: var(--radius-sm);
  padding: 4px 8px;
  cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
@media (hover: hover) {
  .topbar-logout:hover {
    color: var(--text);
    border-color: var(--border);
  }
}

/* --- Экран входа --------------------------------------------------------- */
.login-screen {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  color: var(--text-muted);
  font-size: 13px;
}
.login-card {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 28px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 10px);
  background: var(--surface, transparent);
}
.login-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-weight: 600;
}
.login-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
}
.login-tab {
  font: inherit;
  font-size: 13px;
  padding: 7px 12px;
  border: none;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
}
.login-tab.is-active {
  color: var(--text);
  border-bottom-color: var(--accent, var(--text));
}
.login-title {
  margin: 4px 0 0;
  font-size: 18px;
  color: var(--text);
}
.login-subtitle {
  margin: 0;
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.5;
}
.login-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  color: var(--text-muted);
}
.login-field input {
  font: inherit;
  font-size: 14px;
  color: var(--text);
  background: var(--bg, transparent);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}
.login-field input:focus {
  outline: none;
  border-color: var(--accent, var(--text-muted));
}
.login-error {
  margin: 0;
  font-size: 12px;
  color: var(--danger, #d14343);
}
.login-submit {
  font: inherit;
  font-size: 14px;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--accent, transparent);
  color: var(--text);
  cursor: pointer;
}
.login-submit:disabled {
  opacity: 0.55;
  cursor: default;
}
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  background: none;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  padding: 5px;
  line-height: 0;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
@media (hover: hover) {
  .icon-btn:hover:not(:disabled) {
    color: var(--text);
    background: var(--surface-2);
    border-color: var(--border);
  }
}
.icon-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
@media (hover: hover) {
  .icon-btn.danger:hover:not(:disabled) {
    color: var(--err);
    background: var(--err-weak);
    border-color: var(--err);
  }
}

.shell-main {
  flex: 1 1;
  min-height: 0;
  padding: 16px 18px 18px;
}

/* ---- Модальное окно настроек ---- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 100;
}
.modal {
  width: min(860px, 100%);
  max-height: min(720px, 88vh);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  font-size: 15px;
}
.modal-close {
  border: none;
  background: none;
  color: var(--text-muted);
  font-size: 15px;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
}
@media (hover: hover) {
  .modal-close:hover {
    color: var(--text);
    background: var(--surface-2);
  }
}
.modal-tabs {
  display: flex;
  gap: 2px;
  padding: 0 12px;
  border-bottom: 1px solid var(--border);
}
.modal-tab {
  padding: 9px 14px;
  border: none;
  background: none;
  color: var(--text-muted);
  font-weight: 550;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
@media (hover: hover) {
  .modal-tab:hover {
    color: var(--text);
  }
}
.modal-tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.modal-body {
  padding: 18px;
  overflow-y: auto;
  flex: 1 1;
  min-height: 0;
}

.settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  grid-gap: 16px;
  gap: 16px;
}
.settings-block {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px;
}
.settings-block h3 {
  margin: 0 0 10px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: 600;
}

/* ---- Переименование проекта ---- */
.project-row.editing {
  cursor: default;
  gap: 10px;
}
.project-rename {
  flex: 1 1;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
}

/* ============================================================= */
/*  Ф4: меню карточки, полноэкран, экспорт                        */
/* ============================================================= */
.crumb-export {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-left: 4px;
  font-size: 12px;
  font-weight: 550;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 11px;
  white-space: nowrap;
}
@media (hover: hover) {
  .crumb-export:hover {
    color: var(--accent);
    border-color: var(--accent);
    text-decoration: none;
  }
}

/* Меню карточки (⋮) */
.widget-menu {
  position: relative;
  flex: none;
}
.widget-menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  color: var(--text-muted);
  line-height: 0;
  padding: 4px;
  border-radius: var(--radius-sm);
}
@media (hover: hover) {
  .widget-menu-btn:hover {
    color: var(--text);
    background: var(--surface-2);
  }
}
.widget-menu-pop {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 30;
  min-width: 168px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  padding: 4px;
  display: flex;
  flex-direction: column;
}
.widget-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  border: none;
  background: none;
  color: var(--text);
  font-size: 13px;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  text-decoration: none;
}
@media (hover: hover) {
  .widget-menu-item:hover {
    background: var(--surface-2);
  }
}
.widget-menu-item.danger {
  color: var(--err);
}

/* Полноэкранный просмотр карточки */
.widget-full {
  width: min(1000px, 100%);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.widget-full-body {
  padding: 18px;
  overflow: auto;
  flex: 1 1;
  min-height: 0;
}
.widget-full-img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-sm);
}

/* ============================================================= */
/*  Кнопки строки ввода чата: скрепка + стрелка «отправить»       */
/* ============================================================= */
/* Обе кнопки одного размера и выровнены по нижнему краю textarea
   (строка ввода — align-items: flex-end). */
.chat-attach,
.chat-send {
  flex: none;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.14s, border-color 0.14s, filter 0.14s, opacity 0.14s;
}

/* Скрепка — «тихая» второстепенная кнопка. */
.chat-attach {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
}
@media (hover: hover) {
  .chat-attach:hover:not(:disabled) {
    background: var(--surface-2);
    color: var(--text);
    border-color: var(--accent);
  }
}

/* Отправка — главное действие, поэтому акцентный градиент темы. */
.chat-send {
  border: 1px solid transparent;
  background: var(--accent-grad);
  color: #fff;
  box-shadow: 0 2px 12px rgba(169, 112, 255, 0.3);
}
@media (hover: hover) {
  .chat-send:hover:not(:disabled) {
    filter: brightness(1.09);
  }
}
.chat-send:active:not(:disabled) {
  filter: brightness(0.96);
}

.chat-attach:disabled,
.chat-send:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
}

/* Клавиатурный фокус должен быть виден — кнопки без текста тем более. */
.chat-attach:focus-visible,
.chat-send:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Иконка не перехватывает клик — событие всегда у кнопки. */
.chat-attach svg,
.chat-send svg {
  display: block;
  pointer-events: none;
}

/* Вращение индикатора хода. Уважает системную настройку «меньше движения». */
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
.spin {
  animation: spin 0.9s linear infinite;
  transform-origin: 50% 50%;
}
@media (prefers-reduced-motion: reduce) {
  .spin {
    animation-duration: 2.4s;
  }
}

/* ---------- Билдер кастомных сценариев ---------- */
.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
/* Заголовок внутри .page-head уже отделён им самим — свой нижний отступ лишний. */
.page-head .page-title {
  margin-bottom: 0;
}
/* Бейдж «своё» стоит рядом с id, поэтому нужен отбив слева. */
.badge.custom {
  margin-left: 6px;
}
/* Билдер открывается поверх модалки настроек — он должен быть выше её слоя. */
.builder-backdrop {
  z-index: 110;
}
.field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 12px;
}
.field > span {
  font-size: 13px;
  color: var(--text-muted);
}
.field input,
.field textarea,
.field select {
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: inherit;
  font: inherit;
}
.builder-add {
  display: flex;
  gap: 8px;
  margin: 8px 0;
}
.builder-add select,
.builder-add input {
  flex: 1 1;
  min-width: 0;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: inherit;
  font: inherit;
}
.tool-matches {
  display: flex;
  flex-direction: column;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.tool-hit {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 8px 10px;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  color: inherit;
  cursor: pointer;
  text-align: left;
}
.tool-hit:last-child {
  border-bottom: none;
}
@media (hover: hover) {
  .tool-hit:hover {
    background: var(--surface-2);
  }
}
.tool-hit b {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--accent);
}
.tool-hit span {
  font-size: 12px;
  color: var(--text-muted);
}
.builder-steps {
  list-style: none;
  padding: 0;
  margin: 10px 0;
}
.builder-steps .step {
  display: flex;
  align-items: center;
  gap: 10px;
}
.step-note-input {
  width: 100%;
  margin-top: 4px;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: inherit;
  font-size: 13px;
}
.step-actions {
  display: flex;
  gap: 4px;
}
.builder-footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
}

/* ============================================================= */
/*  Иконки                                                        */
/* ============================================================= */
/* Весь набор — линейные SVG в currentColor (см. components/icons.tsx).
   Базовое правило нужно ровно для одного: не дать иконке съехать с базовой
   линии текста, рядом с которым она стоит. */
.icon {
  display: inline-block;
  vertical-align: -0.14em;
  flex: none;
}

/* Кольцо-индикатор идущего хода. */
@keyframes pb-spin {
  to {
    transform: rotate(360deg);
  }
}
.icon.spin {
  animation: pb-spin 0.9s linear infinite;
  transform-origin: 50% 50%;
}

/* ============================================================= */
/*  Сворачивание боковых панелей                                  */
/* ============================================================= */
/* Кнопка сворачивания живёт в шапке панели и приглушена: она нужна редко и
   не должна конкурировать за внимание с содержимым. */
.panel-collapse {
  flex: none;
  opacity: 0.65;
}
@media (hover: hover) {
  .panel-collapse:hover {
    opacity: 1;
  }
}

/* Шапка колонки чата — только подпись и кнопка сворачивания: название проекта
   уже показывает верхняя строка приложения, повторять его незачем. */
.chat-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.chat-head-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  flex: 1 1;
  min-width: 0;
}

/* Свёрнутая колонка: планка во всю высоту с подписью вдоль неё. Вся планка —
   кнопка разворота, поэтому цель нажатия крупная. */
.ws-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  min-height: 0;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  color: var(--text-muted);
  transition: color 0.15s, border-color 0.15s;
}
@media (hover: hover) {
  .ws-rail:hover {
    color: var(--text);
    border-color: var(--accent);
  }
}
.ws-rail-ico {
  display: inline-flex;
  line-height: 0;
}
.ws-rail-label {
  writing-mode: vertical-rl;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Подпись левой планки читается снизу вверх — так её край смотрит внутрь
   экрана, к содержимому, которое панель откроет. */
.ws-rail-left .ws-rail-label {
  transform: rotate(180deg);
}

/* В стопке (узкий экран) планка — обычная строка во всю ширину: вертикальная
   подпись там ни к чему, а тянуть её на всю высоту ряда нечем. Блок стоит здесь,
   а не в общем адаптивном разделе выше: тот идёт по файлу раньше описания планки
   и проигрывал бы ему при равной специфичности. */
@media (max-width: 1100px) {
  .ws-rail {
    flex-direction: row;
    justify-content: flex-start;
    gap: 10px;
    padding: 12px 16px;
    min-height: 0;
  }
  .ws-rail-label {
    writing-mode: horizontal-tb;
  }
  .ws-rail-left .ws-rail-label {
    transform: none;
  }
}

/* ============================================================= */
/*  Единое поле поиска по базе знаний                             */
/* ============================================================= */
/* Одно поле ищет сразу двумя способами — по названию и по смыслу содержимого,
   поэтому оформлено как одна строка поиска, а не как форма с режимами. */
.kb-search {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 6px 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  transition: border-color 0.15s, box-shadow 0.15s;
  margin-bottom: 6px;
}
.kb-search:focus-within {
  border-color: var(--accent);
  box-shadow: var(--glow);
}
/* Лупа слева — кнопка запуска поиска и одновременно индикатор ожидания. */
.kb-search-go {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  color: var(--text-muted);
  padding: 4px;
  margin-left: -4px;
  border-radius: 6px;
  line-height: 0;
  flex: none;
  transition: color 0.12s;
}
@media (hover: hover) {
  .kb-search-go:hover:not(:disabled) {
    color: var(--accent);
  }
}
.kb-search-go:disabled {
  color: var(--accent);
  cursor: default;
}
.kb-search-input {
  flex: 1 1;
  min-width: 0;
  border: none;
  background: none;
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  padding: 9px 0;
  outline: none;
}
.kb-search-input::placeholder {
  color: var(--text-muted);
}
/* Системный крестик у input[type=search] дублировал бы нашу кнопку очистки. */
.kb-search-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
          appearance: none;
}
.kb-search-clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  color: var(--text-muted);
  padding: 4px;
  border-radius: 50%;
  line-height: 0;
  flex: none;
}
@media (hover: hover) {
  .kb-search-clear:hover {
    color: var(--text);
    background: var(--surface);
  }
}
.kb-search-hint {
  margin: 0 0 10px;
  font-size: 11.5px;
  color: var(--text-muted);
}

/* Результаты одного поиска: секции точных совпадений и найденного по смыслу. */
.kb-result-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.kb-result-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 4px 0 2px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.kb-result-hint {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  opacity: 0.75;
}
.kb-result-note {
  margin: 2px 0;
  font-size: 12px;
  color: var(--text-muted);
}
.kb-results-clear {
  align-self: flex-start;
  margin-top: 6px;
  padding: 4px 10px;
}
.kb-hit-name {
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Фрагмент содержимого может быть длинным — держим его в несколько строк
   моноширинным, чтобы отличать цитату от подписи. */
.kb-snippet {
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ============================================================= */
/*  Перетаскивание карточек доски                                 */
/* ============================================================= */
/* Ручка появляется по наведению на карточку: постоянно висящая точечная сетка
   в каждой шапке зашумляла бы доску. На тач-устройствах наведения нет, поэтому
   там ручка видна всегда (см. медиазапрос ниже). */
.widget-grip {
  display: inline-flex;
  line-height: 0;
  color: var(--text-muted);
  cursor: -webkit-grab;
  cursor: grab;
  opacity: 0;
  margin-left: -4px;
  padding: 2px;
  border-radius: 6px;
  flex: none;
  transition: opacity 0.15s, color 0.15s, background 0.15s;
  touch-action: none;
}
/* Ручка перетаскивания проявляется по наведению ИЛИ по фокусу с клавиатуры.
   Условия разведены намеренно: наведение ниже уедет под @media (hover: hover),
   а фокус обязан работать всюду — на тач-экране это единственный способ добраться
   до ручки с внешней клавиатуры. */
.widget-card:focus-within .widget-grip {
  opacity: 1;
}
@media (hover: hover) {
  .widget-card:hover .widget-grip {
    opacity: 1;
  }
}
@media (hover: hover) {
  .widget-grip:hover {
    color: var(--text);
    background: var(--surface-2);
  }
}
@media (hover: none) {
  .widget-grip {
    opacity: 1;
  }
}

/* Ведомая карточка приподнята и слегка прозрачна — видно, что под ней. */
.widget-card.is-dragging {
  cursor: -webkit-grabbing;
  cursor: grabbing;
  opacity: 0.92;
  border-color: var(--accent);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.45), var(--glow);
}
.widget-card.is-dragging .widget-grip {
  opacity: 1;
  color: var(--accent);
}
/* Место, куда карточка встанет, если отпустить сейчас. */
.widget-card.is-drop-target {
  border-color: var(--accent);
  border-style: dashed;
  background: var(--accent-weak);
}

/* ============================================================= */
/* Обучающий тур («Как пользоваться»)                            */
/* ============================================================= */

/*
 * Палитра персонажа. Она НЕ приходит из темы: сом — один и тот же герой во
 * всех темах, перекрашивать ему кожу вслед за фоном незачем. Тема меняет
 * только то, на чём он стоит (карточка, затемнение).
 */
:root {
  --som-ink: #1b3350;
  --som-body: #9fadc9;
  --som-body-dark: #8593b3;
  --som-hat: #f0b53d;
  --som-hat-dark: #e2892f;
  --som-rod: #c0794c;
  --som-glass: #e8f1fb;
}

/* ---- Кнопка запуска ---- */
.projects-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}
.btn.tour-start {
  position: relative;
}
/* Пока курс не проходили — кнопка тихо дышит рамкой. Не мигание и не бейдж:
   заметно боковым зрением, но не требует реакции. Цвет берётся из темы, поэтому
   пульс живёт на псевдоэлементе, а не на box-shadow с зашитой rgba. */
.btn.tour-start.attention::after {
  content: "";
  position: absolute;
  inset: -3px;
  border: 1px solid var(--accent);
  border-radius: calc(var(--radius-sm) + 3px);
  opacity: 0.2;
  animation: tour-attention 2.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes tour-attention {
  0%,
  100% {
    opacity: 0.15;
    transform: scale(1);
  }
  50% {
    opacity: 0.6;
    transform: scale(1.05);
  }
}
@media (prefers-reduced-motion: reduce) {
  .btn.tour-start.attention::after {
    animation: none;
  }
}

/* ---- Слой тура ---- */
/* Слой НЕ ловит указатель: подсвеченную кнопку нужно уметь нажать по-настоящему.
   Клики принимает только карточка с репликой (ниже — pointer-events: auto). */
.tour-layer {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
}
.tour-dim {
  position: absolute;
  inset: 0;
  background: rgba(6, 8, 14, 0.66);
}
/* «Дырка» в затемнении — это не вырез, а огромная тень вокруг маленького
   прямоугольника: один элемент вместо четырёх полос, и края всегда сходятся. */
.tour-spot {
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  border-radius: var(--radius);
  box-shadow: 0 0 0 9999px rgba(6, 8, 14, 0.66), 0 0 0 2px var(--accent), var(--glow);
}

/* ---- Стрелка ---- */
.tour-arrow {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.tour-arrow-line,
.tour-arrow-head {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---- Карточка с репликой ---- */
.tour-card {
  position: fixed;
  width: min(400px, calc(100vw - 36px));
  max-height: calc(100vh - 36px);
  max-height: calc(100dvh - 36px);
  overflow-y: auto;
  /* Место под полосу прокрутки резервируем всегда: иначе на длинном шаге она
     появляется и съедает 10-15 пикселей у кнопок в подвале. */
  scrollbar-gutter: stable;
  padding: 16px 18px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  pointer-events: auto;
}
/* Слайды без цели — по центру и шире: там живут схемы. */
.tour-card-center {
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(620px, calc(100vw - 36px));
}
/* Если по бокам от цели тесно, карточка становится полосой сверху или снизу. */
.tour-card-top,
.tour-card-bottom {
  width: auto;
}
.tour-close {
  position: absolute;
  top: 8px;
  right: 8px;
  border: none;
  background: none;
  color: var(--text-muted);
  padding: 4px;
  border-radius: var(--radius-sm);
  line-height: 0;
}
@media (hover: hover) {
  .tour-close:hover {
    color: var(--text);
    background: var(--surface-2);
  }
}
.tour-body {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}
.tour-card-center .tour-body {
  align-items: center;
}
.tour-som {
  flex: none;
  line-height: 0;
  filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.35));
}
.tour-text {
  min-width: 0;
}
.tour-title {
  margin: 2px 0 6px;
  font-size: 16px;
  font-weight: 660;
  letter-spacing: -0.01em;
}
.tour-say {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
}
.tour-waiting {
  margin: 10px 0 0;
  font-size: 12.5px;
  color: var(--accent);
}

/* ---- Низ карточки: прогресс и навигация ---- */
.tour-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.tour-dots {
  display: flex;
  align-items: center;
  gap: 5px;
}
.tour-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--border);
  transition: width 0.2s, background 0.2s;
}
.tour-dot.active {
  width: 16px;
  border-radius: 3px;
  background: var(--accent);
}
.tour-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.btn.tour-skip {
  background: none;
  border-color: transparent;
  color: var(--text-muted);
}
@media (hover: hover) {
  .btn.tour-skip:hover:not(:disabled) {
    background: var(--surface-2);
    color: var(--text);
  }
}

/* ---- Схемы на слайдах ---- */
/* Схема, а не скриншот: скриншот устареет с первой же правкой вёрстки. */
.tour-flow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 14px;
}
.tour-flow-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.tour-chip {
  display: inline-flex;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  font-size: 12px;
}
.tour-flow-sep {
  color: var(--text-muted);
}
.tour-mock {
  display: grid;
  grid-template-columns: 1fr 1.5fr 1fr;
  grid-gap: 8px;
  gap: 8px;
  height: 96px;
  margin-top: 14px;
}
.tour-mock-col {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 11.5px;
}
.tour-mock-col.board {
  border-style: solid;
  border-color: var(--accent);
  background: var(--accent-weak);
  color: var(--text);
}
.tour-tips {
  margin: 12px 0 0;
  padding-left: 18px;
  display: grid;
  grid-gap: 4px;
  gap: 4px;
  color: var(--text-muted);
  font-size: 12.5px;
}

/* ---- Узкий экран ---- */
/* На телефоне сбоку от цели места нет ни при каком раскладе: карточка садится
   на низ экрана, а прожектор и стрелка остаются на месте. */
@media (max-width: 860px) {
  .tour-card {
    left: 12px !important;
    right: 12px !important;
    top: auto !important;
    bottom: 12px !important;
    width: auto;
    transform: none;
    max-height: 62vh;
  }
  .tour-som {
    display: none;
  }
  .tour-card-center .tour-som {
    display: block;
  }
}

/* Длительность вызова инструмента — приглушённая, рядом с именем. */
.tool-duration {
  font-size: 11px;
  color: var(--text-muted);
  font-feature-settings: "tnum";
  font-variant-numeric: tabular-nums;
}

/* ---------- Проверки: данные на входе и сверка текста на выходе ---------- */
/* Внутри карточки чекпоинта — ответ на вопрос «а данные-то в порядке?» рядом с
   параметрами, которые биолог подтверждает. Цвет несёт вердикт: зелёный —
   расхождений нет, жёлтый — есть на что посмотреть, красный — считать нельзя. */
.gate-report {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 8px 10px;
  margin-bottom: 12px;
}
.gate-report.ok {
  border-color: var(--ok);
  background: var(--ok-weak);
}
.gate-report.warnings {
  border-color: var(--warn);
  background: var(--warn-weak);
}
.gate-report.blocking {
  border-color: var(--err);
  background: var(--err-weak);
}
.gate-title {
  font-size: 12.5px;
}
.gate-clean {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--text-muted);
}
.gate-list {
  margin: 6px 0 0;
  padding-left: 18px;
  font-size: 12.5px;
}
.gate-list li,
.verification-list li {
  margin-bottom: 3px;
}
.gate-list li.error,
.verification-list li.error {
  color: var(--err);
}
.gate-list li.warn,
.verification-list li.warn {
  color: var(--warn);
}

/* Бейдж под репликой: сворачиваемый, потому что расхождения нужны не всегда,
   а вот знать об их наличии биолог должен всегда. */
.verification {
  margin-top: 8px;
  border: 1px solid var(--warn);
  background: var(--warn-weak);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
}
.verification.blocking {
  border-color: var(--err);
  background: var(--err-weak);
}
.verification-head {
  display: flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  padding: 0;
  color: inherit;
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.verification-ico {
  display: inline-flex;
  line-height: 0;
  color: var(--warn);
  flex: none;
}
.verification.blocking .verification-ico {
  color: var(--err);
}
.verification-list {
  margin: 6px 0 0;
  padding-left: 18px;
  font-size: 12.5px;
}

/* --------------------------------------------------------------------------- *
 *  Заметка-статья в карточке доски                                             *
 * --------------------------------------------------------------------------- */
/* Цвета — только токены темы: карточка обязана переключаться в тёмную вместе с
   остальным приложением, а захардкоженный цвет из неё бы выпал. */
.note {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 180px;
}
.note.is-big {
  min-height: 60vh;
}
.note-surface {
  flex: 1 1;
  overflow-y: auto;
  color: var(--text);
  font-size: 14px;
  line-height: 1.55;
}
.note-surface .ProseMirror {
  outline: none;
  min-height: 140px;
}
.note-surface .ProseMirror h1,
.note-surface .ProseMirror h2,
.note-surface .ProseMirror h3 {
  margin: 0.7em 0 0.35em;
  line-height: 1.25;
}
.note-surface .ProseMirror blockquote {
  margin: 0.6em 0;
  padding-left: 12px;
  border-left: 3px solid var(--border);
  color: var(--text-muted);
}
.note-surface .ProseMirror pre {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px;
  overflow-x: auto;
}
.note-surface .ProseMirror table {
  border-collapse: collapse;
  width: 100%;
}
.note-surface .ProseMirror th,
.note-surface .ProseMirror td {
  border: 1px solid var(--border);
  padding: 5px 8px;
}
.note-surface .ProseMirror img {
  max-width: 100%;
}

.note-status {
  font-size: 11px;
  color: var(--text-muted);
  text-align: right;
  min-height: 14px;
}

/* Полоска конфликта заметная намеренно: её задача — остановить человека и
   получить решение, а не мягко сообщить. */
.note-conflict {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 10px;
  border: 1px solid var(--warn);
  border-radius: 8px;
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  color: var(--text);
  font-size: 12px;
}
.note-btn {
  padding: 4px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 12px;
  cursor: pointer;
}
.note-btn:hover {
  border-color: var(--accent);
}
.note-btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
}
.note-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Тулбар заметки: «пилюля» из круглых кнопок. Форма — из референса заказчика,
   цвета — из токенов темы, поэтому карточка одинаково читается в светлой и
   тёмной теме. */
.note-toolbar {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  padding: 5px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
}
.note-tool {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--text);
  background: transparent;
  border: 0;
  cursor: pointer;
  flex: none;
}
.note-tool:hover {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.note-tool.is-on {
  background: var(--accent);
  color: var(--bg);
}
.note-toolbar-sep {
  width: 1px;
  height: 20px;
  background: var(--border);
  margin: 0 3px;
}
.note-toolbar .note-status {
  margin-left: auto;
  padding-right: 6px;
}

.note-menu {
  position: relative;
}
.note-menu-pop {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  min-width: 200px;
  padding: 5px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: 0 12px 30px rgb(0 0 0 / 25%);
}
/* Палитра символов — сеткой: список из тринадцати знаков читался бы столбцом. */
.note-menu-pop.is-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  min-width: 0;
}
.note-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.note-menu-item:hover {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.note-menu-pop.is-grid .note-menu-item {
  justify-content: center;
  padding: 8px 0;
  font-size: 15px;
}
.note-menu-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.note-menu-key {
  margin-left: auto;
  color: var(--text-muted);
  font-size: 11px;
}
.note-menu-empty {
  padding: 8px 10px;
  color: var(--text-muted);
  font-size: 12px;
}

/* Диалоги ссылки и формулы */
.note-dialog {
  width: min(460px, 92vw);
  padding: 18px;
  border-radius: 14px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
}
.note-dialog-title {
  margin: 0 0 14px;
  font-size: 15px;
  color: var(--text-muted);
  font-weight: 600;
}
.note-field {
  display: block;
  margin-bottom: 14px;
  font-size: 12px;
  color: var(--text-muted);
}
.note-field input {
  width: 100%;
  margin-top: 5px;
  padding: 6px 2px;
  border: 0;
  border-bottom: 1px solid var(--accent);
  background: transparent;
  color: var(--text);
  font-size: 14px;
  outline: none;
}
.note-formula-preview {
  margin-top: 8px;
  min-height: 42px;
  display: grid;
  place-items: center;
  color: var(--text);
}
.note-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* Заголовок группы и выпадающий список внутри меню карточки «три точки». */
.widget-menu-group {
  padding: 8px 10px 3px;
  color: var(--text-muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.widget-menu-select {
  width: calc(100% - 12px);
  margin: 0 6px 4px;
  padding: 5px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 12px;
}

/* Полоска действий над сеткой доски. */
.board-toolbar {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-bottom: 10px;
}

/* ---------- Админ-панель ----------
 *
 * Панель живёт на общих классах системы: `.btn`, `.card`, `.tabs`,
 * `.table-wrap` + глобальная `table`, `.pill`, `.badge`, `.modal`. Собственных
 * правил здесь ровно столько, сколько требует раскладка страницы, — всё
 * остальное админка получает из системы бесплатно и вместе с ней же меняется.
 *
 * Цвета — только существующие токены тем. Отдельного «опасного» токена в темах
 * нет и заводить его не нужно: деструктивное красим в --err, предупреждения в
 * --warn.
 */
.admin-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

/* Шапка липкая: таблицы длинные, а «в приложение» и вкладки нужны с любого
   места прокрутки. */
.admin-header {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 20px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.admin-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-muted);
  font-size: 13px;
  text-decoration: none;
  padding: 5px 9px;
  margin-left: -9px;
  border-radius: var(--radius-sm);
  transition: background 0.12s, color 0.12s;
}
@media (hover: hover) {
  .admin-back:hover {
    color: var(--text);
    background: var(--surface-2);
    text-decoration: none;
  }
}
.admin-header-text {
  min-width: 0;
}
.admin-header-title {
  font-size: 15px;
  font-weight: 620;
  margin: 0;
  letter-spacing: -0.01em;
}
/* Подпись объясняет, что панель умеет; на телефоне её прячет мобильный слой —
   там шапка нужна узкой, а не разговорчивой. */
.admin-header-sub {
  margin: 0;
  color: var(--text-muted);
  font-size: 12px;
  line-height: 1.4;
}

/* Шире типовой «читаемой» колонки: у таблицы сотрудников семь столбцов данных
   и пять действий в строке, и на 1080px они уезжали в прокрутку на любом
   ноутбуке. Уже — прокрутка внутри `.table-wrap`, страницу это не ломает. */
.admin-page {
  width: 100%;
  max-width: 1240px;
  margin: 0 auto;
  padding: 24px 20px 48px;
}

/* ---- Показатели ----
   Числа из /api/admin/overview: сервер их считает всегда, показывать их дешевле,
   чем объяснять, почему панель молчит о размере лаборатории. */
.admin-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  grid-gap: 12px;
  gap: 12px;
}
.admin-stat {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
}
.admin-stat-value {
  font-size: 22px;
  font-weight: 650;
  line-height: 1.2;
  letter-spacing: -0.02em;
}
.admin-stat-label {
  margin-top: 2px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-weight: 600;
}

/* ---- Панель над таблицей ---- */
.admin-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.admin-search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1;
  min-width: 200px;
  max-width: 320px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-muted);
}
.admin-search:focus-within {
  border-color: var(--accent);
}
.admin-search input {
  flex: 1 1;
  min-width: 0;
  padding: 8px 0;
  border: none;
  background: none;
  color: var(--text);
  font: inherit;
}
.admin-search input:focus {
  outline: none;
}
.admin-search input::placeholder {
  color: var(--text-muted);
}
.admin-count {
  color: var(--text-muted);
  font-size: 12px;
  margin-left: auto;
}

/* ---- Таблицы ----
   Разметка общая (.table-wrap + table), здесь только то, что специфично для
   панели: столбец действий и погашенная строка отключённого сотрудника. */
/* Кнопки строки не переносим: перенос втрое поднимает высоту строки и рвёт
   связь действия с сотрудником. Вместо этого таблица едет вбок — она уже умеет. */
.admin-actions {
  display: flex;
  gap: 6px;
  flex-wrap: nowrap;
}
.admin-actions .btn {
  padding: 4px 9px;
  font-size: 12px;
}
.admin-row.is-disabled td:not(.admin-actions-cell) {
  opacity: 0.5;
}
.admin-root {
  margin-left: 6px;
}
.admin-mono {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-muted);
}
/* Название проекта может быть длинным: рвём его, а не раскладку. */
.admin-ellipsis {
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Деструктивное действие — модификатор к .btn, а не отдельная кнопка: форма и
   размеры должны совпадать с соседями по строке. */
.btn.admin-danger {
  color: var(--err);
  border-color: var(--err-weak);
}
@media (hover: hover) {
  .btn.admin-danger:hover:not(:disabled) {
    background: var(--err-weak);
    border-color: var(--err);
  }
}

/* ---- Мелочи ---- */
.admin-hint {
  color: var(--text-muted);
  font-size: 12px;
}
/* Заметное, но не тревожное пояснение: приватность проектов — это решение, а
   не сноска, и читаться должно как решение. */
.admin-note {
  margin: 0 0 14px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 13px;
}
.admin-error {
  color: var(--err);
  background: var(--err-weak);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  margin: 0 0 14px;
  font-size: 13px;
}
.admin-empty {
  text-align: center;
  color: var(--text-muted);
  font-size: 13px;
  padding: 28px 0;
}
.admin-warnings {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.admin-warning {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--warn-weak);
  color: var(--warn);
  font-size: 13px;
}
.admin-warning svg {
  flex: none;
  margin-top: 2px;
}
.admin-log {
  max-height: 340px;
  overflow: auto;
  margin: 0;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
}
.admin-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 14px;
}
/* Пояснение под таблицей, а не над ней: `.card .muted` рассчитана на подзаголовок
   и отступ держит снизу. */
.admin-card-note {
  margin: 14px 0 0;
}

/* ---- Диалоги ----
   Подложка общая (.modal-backdrop), сама карточка — своя и узкая: `.modal`
   рассчитана на настройки во весь экран, а у подтверждения нет содержимого,
   ради которого стоит расширяться. Приём тот же, что у `.note-dialog`. */
.admin-dialog {
  width: min(420px, 100%);
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.admin-dialog h4 {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 620;
}
.admin-dialog p {
  margin: 0 0 16px;
  color: var(--text-muted);
  font-size: 13px;
}
.admin-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/*!****************************************************************************************************************************************************************************************************************************************************************!*\
  !*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[10].use[2]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[14].oneOf[10].use[3]!./src/app/mobile.css ***!
  \****************************************************************************************************************************************************************************************************************************************************************/
/* =============================================================
   Мобильный слой
   =============================================================

   Переопределения для телефона. Импортируется ПОСЛЕ globals.css —
   при равной специфичности побеждает порядок, поэтому здесь почти не
   приходится наращивать селекторы.

   Брейкпоинт один: 860px. Величина не новая — на ней уже сидит
   обучающий тур в globals.css. CSS не умеет параметризовать медиазапрос,
   поэтому число повторяется в каждом блоке; держать их в одном файле —
   единственный способ не потерять их из виду.

   Полоса 861–1100px (планшет) сюда НЕ входит: она остаётся в сегодняшнем
   поведении и получит свою раскладку отдельно.

   Здесь только фундамент (П1): безопасные зоны, поля ввода, размеры
   нажимаемого. Оболочка с вкладками — П2. */

/* Высота планки вкладок. Живёт переменной: её же вычитает рабочий стол,
   чтобы панель заканчивалась ровно над планкой, а не под ней. */
:root {
  --ws-tabbar-h: 56px;
}

/* ---------- Безопасные зоны ----------
   viewport-fit=cover в layout.tsx пускает страницу под вырез и полосу-домой,
   иначе env(safe-area-inset-*) всегда нули. Расплата — отступы теперь наши. */
@media (max-width: 860px) {
  .topbar {
    padding-top: calc(9px + env(safe-area-inset-top));
    padding-left: calc(18px + env(safe-area-inset-left));
    padding-right: calc(18px + env(safe-area-inset-right));
  }

  /* Боковые вставки нужны и здесь: в альбомной ориентации вырез
     срезает край содержимого, а не только шапки. */
  .shell-main {
    padding-left: calc(18px + env(safe-area-inset-left));
    padding-right: calc(18px + env(safe-area-inset-right));
    padding-bottom: calc(18px + env(safe-area-inset-bottom));
  }

  .login-screen {
    padding: calc(24px + env(safe-area-inset-top)) calc(24px + env(safe-area-inset-right))
      calc(24px + env(safe-area-inset-bottom)) calc(24px + env(safe-area-inset-left));
  }
}

/* ---------- Поля ввода ----------
   Safari на iOS зумит страницу при фокусе на поле мельче 16px, и обратно уже
   не отъезжает. Обычно это «чинят» запретом зума (user-scalable=no) — то есть
   ломают доступность ради вёрстки. Чиним причину: на телефоне все поля 16px.

   Правило намеренно широкое (input/textarea/select), а не по классам: любое
   новое поле в любом компоненте получит защиту само. */
@media (max-width: 860px) {
  input,
  textarea,
  select,
  .chat-textarea,
  .chat-input-row input {
    font-size: 16px;
  }
}

/* Safari в альбомной ориентации самовольно увеличивает кегль текста.
   Держим наши размеры; зум пальцами при этом остаётся рабочим. */
html {
  text-size-adjust: 100%;
}

/* ---------- Размер нажимаемого ----------
   Палец попадает примерно в 44×44 CSS-пикселя — это нижняя граница и в
   рекомендациях Apple, и в WCAG 2.2 (2.5.8). Сейчас иконочные кнопки несут
   иконку 15–17px с отступом 5px, то есть вдвое меньше нужного.

   Растим ГАБАРИТ, а не иконку: min-width/min-height вместо padding — иначе
   поехали бы внутренние отступы и рамки. Список ниже — точечный, потому что
   слепое `button { min-height: 44px }` раздуло бы и мелкие кнопки внутри
   карточек виджетов. Экраны, до которых дойдут П2 и П6, добавят своё. */
@media (max-width: 860px) {
  .icon-btn,
  .modal-close {
    min-width: 44px;
    min-height: 44px;
  }

  .btn,
  .tab,
  .side-tab,
  .modal-tab,
  .topbar-logout,
  .ws-back,
  .crumb-export {
    min-height: 44px;
  }

  /* Кнопки в строке ввода чата выровнены по нижнему краю поля: пусть их
     высота совпадает с полем, а не торчит из ряда. */
  .chat-input-row .btn {
    min-height: 44px;
  }
}

/* =============================================================
   Оболочка телефона: одна панель во весь экран + планка вкладок
   ============================================================= */

/* Экран фиксированной высоты. Именно height, а не min-height: панель внутри
   должна заканчиваться там же, где экран, и прокручиваться сама — при
   min-height потомкам не от чего считать свою высоту. */
@media (max-width: 860px) {
  .shell {
    height: 100dvh;
  }
  .shell-main {
    display: flex;
    flex-direction: column;
    min-height: 0;
    /* Список проектов длиннее экрана — прокручивается здесь. На рабочем столе
       прокрутки не возникает: панель ровно по высоте, а её содержимое ездит
       внутри себя. */
    overflow-y: auto;
  }

  /* Одна колонка и одна строка во всю высоту: видимая панель — единственный
     ребёнок в потоке (планка вкладок вынута из него position: fixed). */
  .workspace {
    flex: 1 1;
    min-height: 0;
    height: auto;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    gap: 0;
    /* Место под планку вкладок, чтобы низ панели не уезжал под неё. */
    padding-bottom: var(--ws-tabbar-h);
  }

  /* Планки-рельсы телефону не нужны: сворачивать нечего, когда панель и так
     одна. Заодно снимается вопрос «а если в localStorage лежит collapsed,
     оставшийся с широкого экрана» — правила ниже показывают панель независимо
     от него. */
  .workspace > .ws-rail {
    display: none;
  }

  /* Показываем только активную вкладку.
     Специфичность здесь ровно та же, что у правил сворачивания в globals.css
     (класс + атрибут + класс), поэтому спор решает порядок файлов: mobile.css
     подключён позже и побеждает. Отсюда же явное перечисление вместо :not() —
     оно даёт предсказуемый вес селектора. */
  .workspace[data-tab="chat"] > .ws-chat {
    display: block;
  }
  .workspace[data-tab="chat"] > .ws-board,
  .workspace[data-tab="chat"] > .ws-side {
    display: none;
  }

  .workspace[data-tab="board"] > .ws-board {
    display: block;
  }
  .workspace[data-tab="board"] > .ws-chat,
  .workspace[data-tab="board"] > .ws-side {
    display: none;
  }

  /* .ws-side — колонка с вкладками внутри, ей нужен именно flex. */
  .workspace[data-tab="side"] > .ws-side {
    display: flex;
  }
  .workspace[data-tab="side"] > .ws-chat,
  .workspace[data-tab="side"] > .ws-board {
    display: none;
  }

  /* Прокрутка — внутри панели. Перебиваем overflow: visible и потолки высоты
     из блока 1100px: там панели идут стопкой и ездят страницей целиком, здесь
     панель одна и занимает ровно экран. */
  .ws-chat {
    overflow: hidden;
  }
  .ws-board {
    overflow-y: auto;
  }
  .ws-side {
    overflow: hidden;
    max-height: none;
  }
  .chat-embedded {
    height: 100%;
  }
  .chat-embedded .chat-log {
    max-height: none;
  }
}

/* =============================================================
   Чат на телефоне
   ============================================================= */

/* Переписка занимает панель целиком: уведомления и полоса файлов сверху,
   лента тянется, строка ввода прибита к низу карточки. На узком экране это
   единственная разумная раскладка — свободного места, чтобы разложить их
   иначе, попросту нет. */
@media (max-width: 860px) {
  /* Шапка колонки («ДИАЛОГ» + кнопка сворачивания) — деталь рабочего стола: там
     колонок три и одну из них сворачивают ради доски. Здесь панель одна и на
     весь экран, сворачивать её некуда и не во что: её место — нижняя планка
     вкладок. Подпись тоже лишняя — тот же корешок внизу уже подсвечен. */
  .chat-head {
    display: none;
  }

  .chat-embedded > .chat-card {
    flex: 1 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    /* Отступ от .card снизу не нужен: карточка и так упирается в край панели. */
    margin-bottom: 0;
    /* Карточка сливается с фоном. Рамка со скруглением очерчивает предмет среди
       других — а здесь переписка занимает экран целиком, очерчивать её не от
       чего, и обводка по краю экрана читается как лишняя рамка вокруг всего. */
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
  }

  /* Лента забирает всю оставшуюся высоту и ездит внутри себя.
     min-height сбрасываем: 240px на коротком экране с открытой клавиатурой
     выдавливали бы строку ввода за пределы видимого.

     Боковых полей у ленты и строки ввода больше нет: рамки, от которой они
     отступали, тоже нет, а поля панели уже заданы `.shell-main`. Оставь их —
     и содержимое отъехало бы от края экрана на 32px вместо 18px. */
  .chat-embedded .chat-log {
    flex: 1 1;
    min-height: 0;
    padding: 14px 0;
    gap: 14px;
  }
  .chat-embedded .chat-input-row {
    padding: 12px 0;
  }

  /* Реплика шире, чем на десктопе: 80% от 360px — это 288px, в них абзац
     рвётся на обрывки. */
  .msg-body {
    max-width: 92%;
  }

  /* Ссылка или путь без пробелов — обычное дело в ответах про файлы и гены.
     word-break: break-word такую строку не всегда ломает, anywhere — ломает
     всегда, и горизонтальной прокрутки страницы не возникает. */
  .msg-body,
  .msg-text.markdown {
    overflow-wrap: anywhere;
  }

  /* Клавиатура открыта — планка вкладок уходит. Две горизонтальные полосы
     плюс клавиатура не оставляют места самой переписке, а переключать вкладки
     во время набора всё равно незачем: сначала дописать. Возвращается сама,
     как только поле теряет фокус. */
  .workspace:has(.chat-textarea:focus) .ws-tabs {
    display: none;
  }
  .workspace:has(.chat-textarea:focus) {
    padding-bottom: 0;
  }
}

/* =============================================================
   Источники и файлы на телефоне
   =============================================================

   То же, что и с чатом: панель занимает экран целиком, поэтому от карточки со
   своей рамкой она превращается в обычное содержимое на общем фоне, а кнопка
   сворачивания уходит — сворачивать единственную панель некуда. */
@media (max-width: 860px) {
  .ws-side {
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
  }

  .panel-collapse {
    display: none;
  }

  /* Поля панели теперь на `.shell-main`: корешки вкладок и содержимое встают по
     одному левому краю с остальными экранами, а черта под вкладками идёт от
     края до края, как разделитель, а не как низ рамки. */
  .side-head {
    padding-left: 0;
    padding-right: 0;
  }
  .side-tab {
    padding-left: 2px;
    padding-right: 12px;
  }
  .side-body {
    padding-left: 0;
    padding-right: 0;
  }
}

/* ---------- Шапка ----------
   На 360px в строку помещается немногое, поэтому оставляем только то, без чего
   не обойтись: возврат, название проекта и шестерёнку. */
@media (max-width: 860px) {
  .topbar {
    gap: 10px;
    padding-left: calc(12px + env(safe-area-inset-left));
    padding-right: calc(12px + env(safe-area-inset-right));
  }

  /* Бренд сворачивается в знак: слово «Карманный биоинформатик» на телефоне
     съедало бы всю строку, а знак на цветной плашке узнаётся и без подписи. */
  .brand-name {
    display: none;
  }

  /* Выход уехал в настройки (блок «Учётная запись»), здесь для него места нет. */
  .topbar-logout {
    display: none;
  }

  /* Правую группу к правому краю прижимал `margin-left: auto` на кнопке выхода.
     Она скрыта — забирает эту роль шестерёнка, иначе на списке проектов (где
     нет и крошки) она бы уехала к бренду. */
  .topbar-gear {
    margin-left: auto;
  }

  /* Название проекта — единственное, что может быть длинным; отдаём ему всё
     оставшееся место и обрезаем многоточием. */
  .crumb {
    flex: 1 1;
    min-width: 0;
  }
}

/* ---------- Планка вкладок ---------- */
@media (max-width: 860px) {
  .ws-tabs {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Выше содержимого и шапки (z-index: 20), но ниже модальных окон (100). */
    z-index: 30;
    background: var(--surface);
    border-top: 1px solid var(--border);
    /* Полоса-домой закрашивается фоном планки, а не оставляет щель. */
    height: calc(var(--ws-tabbar-h) + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
  }

  .ws-tab {
    flex: 1 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: 44px;
    padding: 6px 4px;
    border: none;
    background: none;
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    transition: color 0.12s;
  }
  .ws-tab.active {
    color: var(--accent);
  }
  .ws-tab-label {
    white-space: nowrap;
  }

  /* ---- Метки о событиях в скрытых панелях ----
     Кружок сидит в углу ИКОНКИ, а не корешка: корешок — треть ширины экрана,
     и метка у его края висела бы в пустоте, непонятно к чему относясь. */
  .ws-tab-ico {
    position: relative;
    display: inline-flex;
    line-height: 0;
  }

  .ws-tab-mark {
    position: absolute;
    /* Правый верхний угол иконки, с заездом наружу: так метка не накрывает сам
       знак и читается даже поверх плотных иконок вроде «слоёв». */
    top: -5px;
    left: 100%;
    transform: translateX(-45%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    font-feature-settings: "tnum";
    font-variant-numeric: tabular-nums;
    line-height: 1;
    /* Обводка цветом планки отделяет метку от иконки под ней. */
    border: 1.5px solid var(--surface);
  }

  /* Просто «что-то появилось» — точка без числа: сколько именно пришло реплик,
     человеку не важно, важно что разговор сдвинулся. */
  .ws-tab-mark-dot {
    width: 9px;
    height: 9px;
    background: var(--accent);
  }

  /* Счётчик: карточек на доске, файлов в материалах. */
  .ws-tab-mark-count {
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    background: var(--accent);
    color: #fff;
  }

  /* У материалов метка — не новость, а содержимое: файлов столько-то, и висит
     она постоянно. Поэтому спокойная, как счётчик на вкладке «Файлы» внутри
     панели: вечно горящий акцентный кружок приучил бы не замечать метки — ровно
     то, ради чего они и заведены. */
  .ws-tab[data-tab="side"] .ws-tab-mark-count {
    background: var(--bg);
    border-color: var(--border);
    color: var(--text-muted);
  }

  /* Чекпоинт: прогон встал и ждёт решения. Другой цвет, а не размер — среди
     трёх корешков глаз ловит именно смену цвета, и «ждёт тебя» не должно
     выглядеть как «прибавилось карточек». */
  .ws-tab-mark-alert {
    width: 11px;
    height: 11px;
    background: var(--warn);
    box-shadow: 0 0 0 3px var(--warn-weak);
  }

  /* И весь корешок целиком — тоже в цвете ожидания. Метку в 11px легко
     проскочить взглядом на ходу; подсвеченный корешок виден боковым зрением, а
     остановленный прогон — единственное, что нельзя не заметить. Цвет держим
     даже на активной вкладке: пока решение не дано, ход всё равно стоит. */
  .ws-tab[data-mark="alert"],
  .ws-tab[data-mark="alert"].active {
    color: var(--warn);
  }
}

/* =============================================================
   Широкое содержимое: таблицы, код, длинные строки
   =============================================================

   Экран 360px уже любой таблицы с данными. Выбор всегда один из двух: рвать
   строки по словам или возить содержимое вбок внутри его собственной рамки.
   Для таблицы верен второй: колонка чисел, разъехавшаяся по высоте, перестаёт
   читаться как колонка. Для текста и параметров — первый.

   Чего быть НЕ должно ни при каком выборе — горизонтальной прокрутки самой
   страницы: она уводит шапку и планку вкладок, и вернуть их обратно человек
   догадывается не сразу. */

@media (max-width: 860px) {
  .widget-table-wrap {
    /* Потолок в 240px считался под колонку в треть ноутбука. Здесь карточка
       во всю ширину экрана, и обрезать таблицу нечем — пусть тянется. */
    max-height: none;
    /* Инерционная прокрутка: без неё таблица в iOS Safari «прилипает» к пальцу
       и ощущается сломанной рядом с остальным экраном. */
    -webkit-overflow-scrolling: touch;
    /* Докрутил до края — дальше не едет страница и не срабатывает жест «назад».
       Иначе горизонтальный смах внутри таблицы уносит с экрана целиком. */
    overscroll-behavior-x: contain;
  }

  /* Первая колонка остаётся на месте: увёл таблицу вбок — и без неё уже не
     понять, чья это строка. Именно она обычно и есть подпись (ген, образец,
     термин), остальное — числа. */
  .widget-table th:first-child,
  .widget-table td:first-child {
    position: -webkit-sticky;
    position: sticky;
    left: 0;
    /* Фон обязателен и обязан быть непрозрачным: под липкой колонкой проезжают
       соседние ячейки. Тот же токен, что у карточки, — иначе видна заплата. */
    background: var(--surface);
    /* Грань вместо тени: показывает край и не мажет текст, который под него
       заезжает. */
    box-shadow: 1px 0 0 var(--border);
  }
  .widget-table td:first-child {
    z-index: 1;
  }
  /* Шапка уже липнет сверху (globals.css) — здесь только порядок слоёв, чтобы
     на пересечении угловая ячейка перекрывала и строку, и колонку. */
  .widget-table th {
    z-index: 2;
  }
  .widget-table th:first-child {
    z-index: 3;
    background: var(--surface-2);
  }

  /* Параметры чекпоинта — не данные, а вопрос «это запускаем?». Их читают
     целиком и один раз, поэтому длинное значение переносим, а не отправляем в
     прокрутку: спрятанный хвост пути к файлу здесь дороже лишней строки.
     Общее правило `th, td { white-space: nowrap }` из globals.css перебиваем. */
  .checkpoint-params td {
    white-space: normal;
  }
  .checkpoint-params .param-val {
    overflow-wrap: anywhere;
  }

  /* Ленты кода и аргументов инструментов (`pre.code`) уже переносятся по словам
     и рвут длинные токены — им ничего не нужно. А вот рассуждение модели просто
     `pre-wrap`: путь к файлу или ссылка без пробелов вылезали бы из панели и
     тянули за собой страницу. */
  .think-body {
    overflow-wrap: anywhere;
  }

  /* Карточка-инсайт и подпись под графиком — тот же markdown, что и в чате, но
     правила переносов в globals.css написаны под чат (`.msg-text.markdown`) и
     сюда не достают. На ноутбуке это сходило с рук: карточка стоит в широкой
     сетке. На телефоне она ровно по ширине экрана, и таблица или строка кода
     внутри неё утащила бы вбок всю страницу. */
  .widget-text.markdown,
  .widget-caption.markdown {
    overflow-wrap: anywhere;
  }
  .widget-text.markdown pre,
  .widget-caption.markdown pre {
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }
  /* display: block превращает таблицу в прокручиваемый блок — тот же приём, что
     у markdown-таблиц в чате. */
  .widget-text.markdown table,
  .widget-caption.markdown table {
    display: block;
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }
}

/* =============================================================
   Полноэкранные виды и формы
   =============================================================

   Центрированное окно поверх затемнения — приём большого экрана: оно работает,
   пока вокруг остаётся видимый контекст. На 360px «окно с отступом 24px» — это
   почти весь экран, только с рамкой и чужими 24 пикселями по краям, которые
   ничего не показывают. Поэтому на телефоне модальные окна становятся листом
   на весь экран: настройки, конструктор сценариев, карточка во весь экран. */

@media (max-width: 860px) {
  .modal-backdrop {
    /* Отступ убираем целиком — лист занимает экран. */
    padding: 0;
    align-items: stretch;
  }

  .modal,
  .widget-full {
    width: 100%;
    max-width: none;
    /* Именно height, а не min-height: внутренние области (тело, футер) считают
       свою высоту от листа, а тело прокручивается само. */
    height: 100dvh;
    max-height: 100dvh;
    /* Скруглять и обводить нечего: за краями листа экрана уже нет. */
    border: none;
    border-radius: 0;
  }

  /* Шапка листа уходит под вырез, если её не отодвинуть; низ тела — под
     полосу-домой. Крестик при этом остаётся в зоне большого пальца сверху —
     переносить его вниз значило бы разойтись с остальными экранами. */
  .modal-head {
    padding-top: calc(14px + env(safe-area-inset-top));
  }
  .modal-body,
  .widget-full-body {
    padding-bottom: calc(18px + env(safe-area-inset-bottom));
  }

  /* Разделы настроек в строку не помещаются: пять корешков на 360px — это по
     72px на каждый. Пусть едут вбок, но полосу прокрутки прячем — под корешками
     она читается как мусор, а край обрезанного корешка и так говорит, что
     дальше есть ещё. */
  .modal-tabs {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  .modal-tabs::-webkit-scrollbar {
    display: none;
  }
  .modal-tab {
    flex: 0 0 auto;
    white-space: nowrap;
  }

  /* ---------- Формы ----------
     Поля и так в столбик (`.field` — flex column), а вот кнопки решения на
     телефоне тянем во всю ширину: промахнуться по кнопке «Создать» шириной в
     треть строки на ходу проще, чем кажется. */
  .builder-footer,
  .checkpoint-actions {
    display: flex;
    gap: 8px;
  }
  .builder-footer .btn,
  .checkpoint-actions .btn {
    flex: 1 1;
    min-height: 44px;
  }

  /* Строка «выбрать сценарий / добавить инструмент»: на узком экране select и
     кнопка встают друг под друга, а не сжимаются до нечитаемого. */
  .builder-add {
    flex-wrap: wrap;
  }
  .builder-add select,
  .builder-add input {
    flex: 1 1 100%;
    min-width: 0;
  }

  /* Стрелки порядка и удаление шага — те же 44px, что и остальное нажимаемое. */
  .step-actions .btn,
  .step-actions .icon-btn {
    min-height: 44px;
    min-width: 44px;
  }

  /* Заголовок списка проектов: название и кнопка «новый проект» в одну строку
     не всегда влезают — пусть переносится, а не сжимает кнопку. */
  .projects-head {
    flex-wrap: wrap;
  }

  /* ---------- Граф сети ----------
     Панель инструментов графа — это select, поиск, ползунок, образец цвета и
     семь кнопок. Она уже переносится по строкам (flex-wrap), здесь только размер
     нажимаемого и ширина полей: на 360px поиск в 120px бесполезен. */
  .net-btn,
  .net-select,
  .net-paint input {
    min-height: 44px;
  }
  .net-search {
    flex: 1 1 100%;
  }
  .net-search input {
    width: 100%;
    min-width: 0;
  }
  .net-slider {
    flex: 1 1 100%;
  }

  /* Холст во весь экран листа: `min(62vh, 620px)` считался под окно с отступами,
     здесь под графом стоит только легенда. */
  .net-canvas.big {
    height: min(70dvh, 620px);
  }

  /* ---------- Обучающий тур ----------
     Карточка тура садится на низ экрана, а внизу экрана стоит планка вкладок —
     и как раз на неё показывает половина шагов рабочего стола (`.ws-tab`),
     когда сама панель скрыта. Поднимаем карточку над планкой, иначе она
     закрывает собой то, на что показывает. */
  .shell:has(.ws-tabs) .tour-card {
    bottom: calc(12px + var(--ws-tabbar-h) + env(safe-area-inset-bottom)) !important;
  }
  /* vh на телефоне считается по самому высокому состоянию окна: с открытой
     адресной строкой карточка вылезала бы за экран. */
  .tour-card {
    max-height: 62dvh;
  }
}

/* ---------- Админ-панель ----------
   На телефон её раньше не адаптировали вовсе: шапка лезла под вырез, вкладки
   не помещались в строку, а поле поиска зумило страницу при фокусе.
   Сами таблицы не трогаем — они уже прокручиваются внутри `.table-wrap`. */
@media (max-width: 860px) {
  .admin-header {
    gap: 12px;
    padding-top: calc(10px + env(safe-area-inset-top));
    padding-left: calc(14px + env(safe-area-inset-left));
    padding-right: calc(14px + env(safe-area-inset-right));
  }
  /* Подпись панели на узком экране только отнимает высоту у содержимого. */
  .admin-header-sub {
    display: none;
  }

  .admin-page {
    padding: 16px calc(14px + env(safe-area-inset-left)) calc(32px + env(safe-area-inset-bottom))
      calc(14px + env(safe-area-inset-right));
  }

  /* Три раздела в строку не влезают — уводим их в горизонтальную прокрутку,
     а не в перенос: перенесённая вкладка теряет общую нижнюю линию. */
  .admin-shell .tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin-bottom: 16px;
  }
  .admin-shell .tabs::-webkit-scrollbar {
    display: none;
  }
  .admin-shell .tab {
    flex: none;
    padding: 10px 13px;
  }

  /* Меньше 16px — и iOS зумит страницу при фокусе, обратно она не возвращается. */
  /* Счётчик уводим на свою строку: рядом с полем на 390px он его душит. */
  .admin-search {
    flex: 1 1 100%;
    max-width: none;
  }
  .admin-search input {
    font-size: 16px;
  }
  .admin-count {
    margin-left: 0;
  }

  .admin-stats {
    grid-template-columns: repeat(2, 1fr);
  }

  /* Размер нажимаемого: в ячейке таблицы кнопки стоят плотно. */
  .admin-actions .btn {
    min-height: 34px;
  }

  .admin-ellipsis {
    max-width: 200px;
  }
}

