@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/fonts/literata-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/fonts/literata-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/fonts/literata-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/fonts/literata-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/manrope-cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/manrope-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/manrope-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/manrope-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light;
  /* Pastel palette from the supplied Pantone reference. */
  --marys-rose: #f6d2d4;
  --gossamer-green: #b3d0be;
  --pastel-yellow: #f2e8b3;
  --orchid-petal: #c3b7cf;
  --heavenly-pink: #f4dee3;
  --cappuccino-foam: #f5f5e9;
  --hushed-violet: #d2bebf;
  --ink: #000000;
  --paper: #ffffff;

  /* Бумажная основа: насыщенная пастель осталась плиткам этапов, фон стал тише. */
  --bg: #faf8f2;
  --panel: var(--paper);
  --panel-2: #f7f4ec;
  --line: #ece6d9;
  --text: #3b3227;
  /* Приглушённый текст светлой темы затемнён с #8a8071 (17.09.2026).
     Прежнее значение давало контраст 3.4–3.9 на трёх фонах, где стоит
     (--paper, --panel-2, --accent-dim), — ниже нормы 4.5 для мелкого текста.
     Этим цветом набраны не только подписи: им же неактивные вкладки фабрик,
     «Ещё N без заказов» и «готовность: —», то есть текст, который читают.
     Тёмная тема норму держала давно (--muted: #a39a8b, 4.3–7). Оттенок тот
     же, светлота на 15% ниже: 4.5–5.1 на всех трёх фонах. */
  --muted: #756d60;
  --accent: var(--gossamer-green);
  --accent-dim: #e4efe8;
  --ok: var(--gossamer-green);
  --warn: var(--pastel-yellow);
  --danger: var(--marys-rose);
  --ink-green: #2f4238;
  --ink-rose: #7a3339;
  --ink-yellow: #5d5326;
  --font-display: Literata, Georgia, "Times New Roman", serif;
  --radius: 12px;
  --radius-sm: 10px;
  --gap: 10px;
  --pad: 12px;
  --pad-lg: 16px;

  /* Токены, вынесенные из захардкоженных значений (тёмная тема переопределяет их ниже). */
  --c-d4a514-l: #d4a514;
  --c-c96a70-l: #c96a70;
  --c-rgba-80-66-48-1-s: rgba(80, 66, 48, .1);
  --c-rgba-47-66-56-16-s: rgba(47, 66, 56, .16);
  --c-rgba-122-51-57-12-s: rgba(122, 51, 57, .12);
  --c-d4a514-t: #d4a514;
  --c-rgba-20-12-16-82-s: rgba(20, 12, 16, .82);
  --hover: #f6f3ea;
  --tile-violet-bg: #efeaf4;
  --c-46405a-t: #46405a;
  --c-a3c6b0-s: #a3c6b0;
  --c-f4f1e8-s: #f4f1e8;
  --c-rgba-60-50-35-08-s: rgba(60, 50, 35, .08);
  --c-eef2e9-s: #eef2e9;
  --panel-3: #f4f1e7;
  --text-2: #5f5648;
  --c-rgba-255-255-255-7-s: rgba(255, 255, 255, .7);
  --tile-rose-bg-3: #fbe6e7;
  --tile-rose-text-2: #5b3a3d;
  --tile-yellow-bg: #f8f3d8;
  --tile-yellow-text: #5d5326;
  --tile-blush-bg: #f1e8e8;
  --tile-blush-text: #59464a;
  --tile-violet-text: #4c4459;
  --tile-green-bg: #e4efe8;
  --tile-green-text-2: #33463b;
  --c-f8f6ef-s: #f8f6ef;
  --c-fdf3f4-s: #fdf3f4;
  --tile-cream-bg: #fdfaea;
  --c-faf5f5-s: #faf5f5;
  --tile-violet-bg-2: #f8f6fa;
  --tile-green-bg-2: #f2f8f4;
  --c-f7f5ef-s: #f7f5ef;
  --c-eef4f0-s: #eef4f0;
  --c-rgba-120-104-80-18-l: rgba(120, 104, 80, .18);
  --c-rgba-255-255-255-72-s: rgba(255, 255, 255, .72);
  --c-rgba-120-104-80-3-l: rgba(120, 104, 80, .3);
  --c-rgba-80-66-48-06-s: rgba(80, 66, 48, .06);
  --c-e3dbc9-s: #e3dbc9;
  --c-rgba-80-66-48-08-s: rgba(80, 66, 48, .08);
  --c-ddb0b4-s: #ddb0b4;
  --c-9c9282-t: #9c9282;
  --text-3: #6b6151;
  --c-fbf5da-s: #fbf5da;
  --tile-rose-bg-2: #fadfe1;
  --c-ece7d8-s: #ece7d8;
  --c-rgba-90-74-55-08-s: rgba(90, 74, 55, .08);
  --c-fdf9ee-s: #fdf9ee;
  --line-4: #f0eade;
  --c-f3efe4-s: #f3efe4;
  --panel-6: #f5f2e8;
  --c-a09684-t: #a09684;
  --panel-5: #fbf9f3;
  --c-rgba-80-66-48-05-s: rgba(80, 66, 48, .05);
  --c-d8b26a-t: #d8b26a;
  --c-e2dccc-t: #e2dccc;
  --c-rgba-255-255-255-66-s: rgba(255, 255, 255, .66);
  --c-ddd3e6-l: #ddd3e6;
  --c-cfe3d7-l: #cfe3d7;
  --c-c0b6a2-t: #c0b6a2;
  --c-6f7f74-t: #6f7f74;
  --c-eef2f6-s: #eef2f6;
  --c-4a5866-t: #4a5866;
  --c-f3eef8-s: #f3eef8;
  --c-574a66-t: #574a66;
  --c-eef2ec-s: #eef2ec;
  --c-47563f-t: #47563f;
  --c-f1ece0-s: #f1ece0;
  --c-a9cdb8-s: #a9cdb8;
  --c-f6f2e6-s: #f6f2e6;
  --c-f4efe3-l: #f4efe3;
  --c-fdfbf5-s: #fdfbf5;
  --c-f2ecdd-l: #f2ecdd;
  --c-d9b96a-s: #d9b96a;
  --c-b8515d-s: #b8515d;
  --c-4a7c59-l: #4a7c59;
  --c-3f6b4c-t: #3f6b4c;
  --c-a94550-l: #a94550;
  --c-f9ecee-s: #f9ecee;
  --c-8e3a44-t: #8e3a44;
  --c-fdf3d8-s: #fdf3d8;
  --c-e6d3a3-l: #e6d3a3;
  --c-6d5620-t: #6d5620;
  --c-c96a74-l: #c96a74;
  --panel-raw: #fff;
  --on-accent: #fff;
  --on-accent-strong: var(--ink-green);
  --card: var(--panel);
  --star-off: var(--line);
  --rule: var(--line);
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --shadow-soft: rgba(80, 66, 48, .16);
  --warn-ink: var(--ink-yellow);
  --accent-ink: var(--ink-green);
  --c-a5454f-s: #a5454f;
  --c-ece0e2-l: #ece0e2;
  --c-e8d6d9-l: #e8d6d9;
  --c-fff8f9-s: #fff8f9;
  --c-a08b6f-t: #a08b6f;
  --tile-rose-text: #8a2f34;
  --tile-rose-bg: #fbe1e3;
  --tile-rose-line: #f1c6ca;
  --c-fff9f9-s: #fff9f9;
  --c-eef3ec-s: #eef3ec;
  --tile-green-text: #2f4238;
  --c-dbe6d9-l: #dbe6d9;
  --tile-yellow-line: #ece3bf;
  --c-rgba-40-34-28-14-s: rgba(40, 34, 28, .14);
  --c-rgba-40-34-28-10-s: rgba(40, 34, 28, .10);
  --tile-yellow-line-2: #d9c98a;
  --c-rgba-255-255-255-18-s: rgba(255, 255, 255, .18);
  --c-rgba-255-255-255-35-l: rgba(255, 255, 255, .35);
  --c-rgba-40-34-28-12-s: rgba(40, 34, 28, .12);
  --line-3: #e8e2d4;
  --c-ddd5c4-s: #ddd5c4;
  --c-rgba-80-66-48-12-s: rgba(80, 66, 48, .12);
  --c-d9b96a-l: #d9b96a;
  --c-rgba-59-50-39-72-s: rgba(59, 50, 39, .72);
  /* Подписи плашек актора в «Логе» («Вы», «Агент», «Авторазбор») — на своих
     цветных подложках. Норма AA для мелкого текста 4.5; «Вы» её держал (4.59),
     а два других нет: агент 4.44, авторазбор 3.85 при кегле 10,5 px. Оттенок
     оставлен тот же, светлота опущена ровно до нормы. */
  --c-4a7c59-t: #4a7c59;
  --c-7c6a92-t: #7b6991;
  --c-a8742f-t: #996a2b;
  --muted-2: #6b7671;
  --c-f6e3dd-s: #f6e3dd;
  --c-a8422a-t: #a8422a;
  --line-2: #e2e0d9;
  --panel-4: #fbfaf7;
  --c-7fae8c-l: #7fae8c;
  --c-d0a43f-l: #d0a43f;
  --c-c9c7bf-l: #c9c7bf;
  --c-c47a63-l: #c47a63;
  --c-f6ecd4-s: #f6ecd4;
  --c-7a5c12-t: #7a5c12;
  --c-f4e2dc-s: #f4e2dc;
  --c-8f4028-t: #8f4028;
  --c-b9cdbf-l: #b9cdbf;
  --c-ece7f1-s: #ece7f1;
  --c-b3d0be-l: #b3d0be;
  --c-cfcabb-l: #cfcabb;
  --c-0e5d59-l: #0e5d59;
  --c-rgba-80-66-48-16-s: rgba(80, 66, 48, .16);
  --c-f2ede2-s: #f2ede2;
  --c-e6d9a3-l: #e6d9a3;
  --c-fbf9fd-s: #fbf9fd;
  --c-bfd8c7-l: #bfd8c7;
  --c-eef6f0-s: #eef6f0;
  --c-d7eadc-s: #d7eadc;
  --c-2f6941-t: #2f6941;
  /* Подпись источника в ленте «Процесса работы» («образец О-026»): 10.5px на
     БЕЛОМ фоне досье. Прежний #a89e8d одинаков в обеих темах — на тёмном он
     даёт 6.6, а на светлом 2.64 при норме 4.5, и в «Продуктах» так не читался
     61 элемент: сама подпись и номера-ссылки внутри неё, которые цвет
     наследуют (аудит 22.09.2026). Тёмной теме прежнее значение оставлено. */
  --c-a89e8d-t: #7d7364;
  --tile-violet-bg-3: #efe8f2;
  --c-rgba-80-66-48-22-s: rgba(80, 66, 48, .22);
  --c-ece9e3-s: #ece9e3;
  --c-55504a-t: #55504a;
  --c-b8860b-s: #b8860b;
  --c-b8860b-t: #b8860b;
  --c-2f7d4f-t: #2f7d4f;
  --c-b3261e-t: #b3261e;
  --c-7a9-s: #7a9;
  --c-2a7-t: #2a7;
  /* Превью листа книги в «Таблицах»: бумага, сетка, шапка с буквами колонок,
     рамка и уголок заметки. Значения — те, какими их показывает Google. */
  --sheet-paper: #ffffff;
  --sheet-ink: #1f2328;
  --sheet-grid: #e0e2e6;
  --sheet-head: #f1f3f4;
  --sheet-head-ink: #5f6368;
  --sheet-frame: #b9bcc2;
  --sheet-note: #f2a33c;
  /* Что ещё не уехало в Google: новая строка и изменённая ячейка. Цвета тоже
     «бумажные» — они лежат на листе и в тёмной теме не темнеют. */
  --sheet-new: #e3f6e6;
  --sheet-new-edge: #34a853;
  --sheet-new-ink: #ffffff;
  --sheet-changed: #fdf2c8;
  --sheet-changed-edge: #e0a800;
}

/* Единый вид всех полей ввода: одна рамка, один радиус, один отступ. */
input, select, textarea {
  background: var(--paper);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  font: inherit;
  outline: none;
}
/* ОДИН ОТСТУП ЕЩЁ НЕ ЗНАЧИТ ОДНА ВЫСОТА (аудит 24.09.2026). При одинаковых
   `padding: 7px 10px` и `font-size: 13px` браузер строил три разных поля:
   текстовое 34.8 px, `select` 36.0, `input[type=date]` 36.8 — у каждого своя
   строка содержимого. В форме они стоят рядом («Номер заказа» · «Статус» ·
   «Дата размещения» в окне «Новый заказ»), и ряд полей выглядел ступенькой.
   Высоту строки назначаем сами — 20 px, — и тогда высота считается
   арифметикой, а не нравом элемента: 20 + 14 отступа + 2 рамки = 36 у всех.
   Полям даты и времени убавляем по пикселю сверху и снизу: их внутренний
   редактор («01.02.2026» со стрелками и значком календаря) на пиксель выше
   обычной строки, и без поправки они снова вырастали до 38. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
select { line-height: 20px; }
input[type="date"], input[type="time"], input[type="month"],
input[type="week"], input[type="datetime-local"] { padding-top: 6px; padding-bottom: 6px; }
input:focus, select:focus, textarea:focus { border-color: var(--accent); }
input:disabled, select:disabled, textarea:disabled { opacity: .6; }
textarea { resize: vertical; }
input[type="checkbox"] { padding: 0; }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.45 Manrope, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.hidden { display: none !important; }
.muted { color: var(--muted); font-weight: 400; }

/* ── стартовая страница: бренды ─────────────────────────── */

.gate {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  background: var(--bg);
  padding: 24px;
  overflow: auto;
}
.gate-inner { width: min(720px, 100%); text-align: center; }
.gate-logo { width: 72px; height: 72px; border-radius: 18px; object-fit: cover; }
.gate-title { margin: 14px 0 4px; font-size: 26px; letter-spacing: .2px; }
.gate-sub { margin: 0 0 26px; color: var(--muted); }
.gate-buttons {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}
.gate-btn {
  padding: 26px 18px;
  font: inherit;
  font-size: 17px;
  font-weight: 600;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  cursor: pointer;
}
.gate-btn:hover { border-color: var(--accent); background: var(--panel-2); }

/* Общие режимы и создание бренда: один ряд мелких розовых кнопок под брендами. */
.gate-extra {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}
.gate-link {
  padding: 8px 16px;
  font: inherit;
  font-size: 13px;
  color: var(--text);
  background: var(--heavenly-pink);
  border: 1px solid var(--hushed-violet);
  border-radius: 999px;
  cursor: pointer;
}
.gate-link:hover { border-color: var(--accent); }

/* ── каркас: боковое меню + рабочая область ─────────────── */

.shell { display: flex; flex: 1; min-height: 0; }

.sidebar {
  flex: 0 0 224px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 14px 12px;
  background: var(--panel);
  border-right: 1px solid var(--line);
  overflow-y: auto;
}
.side-head { display: flex; align-items: center; gap: 10px; }
.side-head-text { min-width: 0; }
.side-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--muted); margin-bottom: 6px;
}
.side-brands { display: flex; flex-direction: column; gap: 4px; }
.side-brand {
  text-align: left; padding: 7px 10px; border-radius: 8px; font: inherit;
  border: 1px solid transparent; background: transparent; color: var(--text); cursor: pointer;
}
.side-brand:hover { background: var(--panel-2); }
.side-brand.active { background: var(--panel-2); border-color: var(--line); font-weight: 600; }
.side-brand-all { background: var(--accent); border-color: var(--accent); font-weight: 600; }
.side-brand-all.active { border-color: var(--ink); }

.side-nav { display: flex; flex-direction: column; gap: 3px; }
.side-item {
  display: flex; align-items: center; gap: 8px;
  text-align: left; padding: 9px 10px; border-radius: 8px; font: inherit;
  border: 1px solid transparent; background: transparent; color: var(--text); cursor: pointer;
}
.side-item:hover { background: var(--panel-2); }
.side-item.active { background: var(--gossamer-green); border-color: var(--line); font-weight: 600; }
.side-item .badge { margin-left: auto; }
.side-item-brand { margin-top: 14px; color: var(--muted); border-top: 1px solid var(--line); border-radius: 0; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* ── topbar ─────────────────────────────────────────────── */

.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 16px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
  flex-wrap: wrap;
}

.brand { display: flex; align-items: center; gap: 10px; min-width: 210px; }

.logo {
  width: 36px; height: 36px;
  display: block;
  object-fit: cover;
  border-radius: 10px;
}

.brand-name { font-weight: 600; letter-spacing: .2px; }
.brand-sub {
  font-size: 11px; line-height: 1.35; color: var(--muted);
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}
.brand-sub.stale { color: var(--ink); background: var(--warn); border-radius: 4px; padding: 1px 4px; }

.filters { display: flex; gap: 8px; flex: 1 1 200px; min-width: 0; }

.filters input { flex: 1; min-width: 0; max-width: 420px; }

.actions { display: flex; gap: 8px; align-items: center; flex: 0 0 auto; }

.btn {
  border: 1px solid var(--line);
  background: var(--gossamer-green);
  color: var(--text);
  padding: 7px 13px;
  border-radius: 8px;
  font: inherit;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { border-color: var(--accent); }
.btn:disabled { opacity: .5; cursor: default; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--ink); font-weight: 600; }
.btn-primary:hover { background: var(--orchid-petal); border-color: var(--orchid-petal); color: var(--ink); }
.btn-ghost { background: var(--hushed-violet); }
/* МАЛАЯ КНОПКА — 30 px, И ЭТО ОБЩИЙ РАЗМЕР МАЛОГО ЭЛЕМЕНТА (аудит
   24.09.2026). Раньше высота получалась сама собой (28.8 px), и всё, что
   вставало с такой кнопкой в один ряд, с ней не сходилось: выпадашка в
   «Таблицах» («Все производства») была на 7.5 px выше соседей, переключатель
   вкладок в «Ревью» — на 5.5. Круглое число позволяет собрать ряд из разных
   элементов и получить ровную линию. */
.btn-sm { padding: 4px 9px; font-size: 13px; min-height: 30px; }
.btn-danger { background: var(--danger); border-color: var(--danger); color: var(--ink); }

.badge {
  display: inline-grid; place-items: center;
  min-width: 19px; height: 19px; padding: 0 5px;
  background: var(--warn); color: var(--ink);
  border-radius: 10px; font-size: 11px; font-weight: 700;
}

/* ── tabs: одно производство = один таб ─────────────────── */

.tabs {
  display: flex;
  gap: 4px;
  padding: 8px 14px 0;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
  flex: 0 0 auto;
}
.tabs-scroll {
  display: flex;
  gap: 4px;
  min-width: 0;
  flex: 1 1 auto;
  overflow-x: auto;
  scrollbar-width: thin;
  /* ОБРЕЗАННОЕ СЛОВО ДОЛЖНО ЧИТАТЬСЯ КАК «ПРОКРУТИ», А НЕ КАК ПОЛОМКА
     (аудит 24.09.2026). Полоса производств шире своего места (у MD Group —
     2660 px против 1029), и последняя видимая вкладка обрывалась на
     полуслове вплотную к кнопке «Скрипты»: со стороны это выглядело как
     наехавшие друг на друга элементы. Растворение края прямо говорит, что
     там продолжение; когда вкладки помещаются целиком, маски нет вовсе.

     Цвет в маске — это НЕПРОЗРАЧНОСТЬ, а не краска: браузер читает у неё
     только альфу, поэтому `black` здесь значит «видно целиком», тёмного
     двойника ему не нужно, и токеном он не становится. */
  mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
}
/* Вкладки помещаются целиком или полоса докручена до конца — край снова
   чёткий: гасить нечего. Класс ставит `markTabsScrollEnd` в `ui/app.js`. */
.tabs-scroll.at-end { mask-image: none; -webkit-mask-image: none; }
.tabs > .tab { flex: 0 0 auto; }

.tab {
  border: 1px solid transparent;
  border-bottom: none;
  background: transparent;
  color: var(--muted);
  padding: 7px 13px;
  border-radius: 8px 8px 0 0;
  font: inherit;
  cursor: pointer;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: -1px;
}
.tab:hover { color: var(--text); }
.tab.script-tab { color: var(--ink); background: var(--gossamer-green); font-weight: 600; }
.tab.script-tab:hover { background: var(--accent-dim); }

/* «Выбрать производство»: тот же список фабрик, только выпадашкой с поиском.
   Панель лежит в `body` и позиционируется по кнопке — у самой полосы стоит
   `overflow: hidden`, и внутри неё список был бы обрезан по нижнему краю. */
.tab-pick { flex: 0 0 auto; display: flex; align-items: center; }
.tab-pick-btn { white-space: nowrap; }
.tab-pick-btn.on { color: var(--text); background: var(--hover); }
.tab-pick-panel { position: fixed; z-index: 60; width: 280px; }
.tab.active {
  background: var(--bg);
  border-color: var(--line);
  color: var(--text);
  font-weight: 600;
}
.tab .n {
  background: var(--panel-2);
  border-radius: 9px;
  padding: 0 6px;
  font-size: 11px;
  font-weight: 500;
  color: var(--muted);
}
.tab.active .n { background: var(--accent); color: var(--ink); }
.tab-sep { width: 1px; background: var(--line); margin: 6px 6px 8px; flex: 0 0 auto; }

/* ── переключатель вида ─────────────────────────────────── */

/* Переключатель вида стоит в одном ряду с малыми кнопками («Правила агента»
   в «Ревью», «Скрипты по образцам» в «Продуктах»), поэтому и высота у него
   та же — 30 px. */
.viewswitch {
  display: flex; align-items: stretch; min-height: 30px;
  background: var(--hushed-violet); border: 1px solid var(--line);
  border-radius: 8px; padding: 2px;
}
.vbtn {
  display: inline-flex; align-items: center;
  border: none; background: transparent; color: var(--muted);
  padding: 0 11px; border-radius: 6px; font: inherit; cursor: pointer;
}
.vbtn.active { background: var(--gossamer-green); color: var(--text); font-weight: 600; box-shadow: 0 1px 3px var(--shadow-soft); }

/* ── таблица ────────────────────────────────────────────── */

.otable { width: 100%; border-collapse: collapse; font-size: 13px; }
.otable th, .otable td { padding: 7px 10px; text-align: left; border-bottom: 1px solid var(--line); }
.otable th {
  position: sticky; top: 0; z-index: 2;
  background: var(--panel); color: var(--muted); font-weight: 600;
  font-size: 12px; cursor: pointer; white-space: nowrap;
}
.otable th:hover { color: var(--text); }
.otable th .arrow { opacity: .5; font-size: 10px; }
.otable tbody tr:hover { background: var(--panel-2); }
.otable td.num { text-align: right; font-variant-numeric: tabular-nums; }
.otable .code { font-weight: 600; cursor: pointer; }
.otable .code:hover { color: var(--ink); background: var(--gossamer-green); }
.otable select { padding: 3px 7px; font-size: 12px; }
.otable tr.sel { background: var(--accent-dim); }
.bulkbar {
  position: sticky; bottom: 0; z-index: 3;
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  background: var(--panel); border: 1px solid var(--accent);
  border-radius: 10px; padding: 9px 12px; margin-top: 10px;
}

/* ── формы редактирования ───────────────────────────────── */

.edit-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px; }
.edit-grid label { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--muted); }
.edit-grid label.wide { grid-column: 1 / -1; }
.edit-grid input, .edit-grid select, .edit-grid textarea { font-size: 13px; }
.row-actions { display: flex; gap: 8px; align-items: center; margin-top: 12px; flex-wrap: wrap; }
/* Текстовое поле в строке действий тянется, иначе placeholder обрезается. */
.row-actions > input:not([type='checkbox']) { flex: 1 1 220px; min-width: 0; }

/* ── управление производствами ──────────────────────────── */

.subtabs { display: flex; gap: 4px; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.subtab {
  border: none; background: transparent; color: var(--muted);
  padding: 7px 13px; font: inherit; cursor: pointer; border-bottom: 2px solid transparent;
}
.subtab.active { color: var(--text); border-bottom-color: var(--accent); font-weight: 600; }

.srow, .crow {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  padding: 9px 10px; border: 1px solid var(--line); border-radius: 9px;
  margin-bottom: 7px; background: var(--panel-2);
}
.srow.arch, .crow.arch { opacity: .55; }
/* ВСЁ В СТРОКЕ ПРОИЗВОДСТВА ОДНОГО РОСТА (аудит 24.09.2026). Строка тут
   плотная нарочно — их два десятка подряд, — и выпадашки с кнопкой набраны
   мелко (30 px). Поле имени осталось от общего вида полей, 36 px, и на
   каждой строке торчало на шесть пикселей выше соседей: список читался как
   гребёнка. */
.srow input.nm { flex: 1; min-width: 150px; font-size: 12px; padding: 5px 8px; }
.srow select, .crow select { font-size: 12px; padding: 5px 8px; }
/* Печатаемая выпадашка в строке производства («объединить с…»): у неё нет
   собственной ширины, как у <select> по самой длинной строке, — задаём сами,
   иначе поле схлопывается в узкую полоску. */
.srow .combo, .crow .combo { flex: 0 0 auto; width: 168px; }
.srow .combo .combo-input, .crow .combo .combo-input { font-size: 12px; padding: 5px 24px 5px 8px; }
.crow .nm { flex: 1; min-width: 160px; }
.crow small { color: var(--muted); }

/* Окно «Чаты» в шестерёнке (22.09.2026): название · On/Off · производство ·
   бренд. Включённые чуть заметнее, без лишних слов. */
.chat-switch-row { flex-wrap: nowrap; gap: 12px; }
.chat-switch-row.on { border-color: var(--accent); background: var(--panel); }
.chat-switch-row .chat-switch-name { flex: 1 1 220px; min-width: 160px; font-weight: 600; }
.chat-switch-row:not(.on) .chat-switch-name { font-weight: 500; color: var(--muted); }
/* Подпись чатов-близнецов: «3 человека · 284 сообщения». Стоит только у
   фабрик, где чатов больше одного, — там имена совпадают и различить строки
   больше нечем (аудит 22.09.2026). */
.chat-switch-row .chat-switch-hint { font-weight: 400; font-size: 11px; margin-top: 2px; }
.chat-switch-state { flex: 0 0 28px; font-size: 12px; font-weight: 700; color: var(--muted); }
.chat-switch-row.on .chat-switch-state { color: var(--accent); }
.chat-switch-link { flex: 1 1 240px; min-width: 180px; display: flex; gap: 6px; align-items: baseline; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-switch-row:not(.on) .chat-switch-link { opacity: .6; }
.chat-switch-supplier { font-weight: 600; }
.chat-switch-dot { color: var(--muted); }
.chat-switch-divider { height: 1px; background: var(--line); margin: 10px 0 12px; }
.switch {
  flex: 0 0 auto; width: 38px; height: 22px; border-radius: 11px; padding: 0;
  border: 1px solid var(--line); background: var(--panel-2); position: relative;
  cursor: pointer; transition: background .15s ease, border-color .15s ease;
}
.switch .switch-knob {
  position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--muted); transition: transform .15s ease, background .15s ease;
}
.switch.on { background: var(--accent); border-color: var(--accent); }
.switch.on .switch-knob { transform: translateX(16px); background: var(--panel); }
.switch:disabled { opacity: .5; cursor: default; }
/* Производство с неразобранным хвостом длиннее выбранного окна. */
.plan-gap {
  display: inline-block; margin-top: 3px; padding: 1px 6px; border-radius: 4px;
  background: var(--warn); color: var(--ink);
}
.script-text {
  margin: 0;
  padding: 12px;
  max-height: 48vh;
  overflow: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
  user-select: text;
}

/* ── board ──────────────────────────────────────────────── */

.board {
  flex: 1;
  display: flex;
  gap: 12px;
  padding: 14px;
  overflow-x: auto;
  overflow-y: hidden;
  align-items: flex-start;
}

.board.history-view {
  display: block;
  overflow: auto;
  align-items: initial;
}
.history-wrap { width: min(1180px, 100%); margin: 0 auto; }
.history-body { display: flex; flex-direction: column; gap: 14px; }
.history-toolbar {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
}
.history-back { align-self: flex-start; }
.history-list { display: flex; flex-direction: column; gap: 8px; }
.history-empty {
  padding: 24px; border: 1px dashed var(--line); border-radius: var(--radius);
  color: var(--muted); text-align: center;
}

/* Свёрнутое производство: строка-раскрывашка, внутри последние заказы. */
.history-group {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden;
}
.history-group.open { border-color: var(--accent); }
.history-group-head {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 12px 14px; border: none; background: transparent; color: var(--text);
  font: inherit; text-align: left; cursor: pointer;
}
.history-group-head:hover { background: var(--panel-2); }
.history-caret { color: var(--muted); font-size: 11px; width: 12px; flex: 0 0 auto; }
.history-group-name { font-size: 15px; flex: 1; min-width: 0; }
.history-group-meta { display: flex; align-items: center; gap: 10px; font-size: 12px; flex: 0 0 auto; }

.history-group-body {
  display: flex; flex-direction: column; gap: 2px;
  padding: 4px 10px 12px; border-top: 1px solid var(--line);
}
.history-row {
  display: flex; align-items: baseline; gap: 12px; width: 100%;
  padding: 7px 8px; border: none; border-radius: var(--radius-sm);
  background: transparent; color: var(--text); font: inherit; text-align: left; cursor: pointer;
}
.history-row:hover { background: var(--panel-2); }
.history-row-code { flex: 0 0 auto; }
.history-row-title { flex: 1; min-width: 0; color: var(--muted); font-size: 12px; }
.history-row-facts { display: flex; align-items: center; gap: 8px; font-size: 12px; flex: 0 0 auto; }
.history-more { align-self: flex-start; margin: 8px 0 0 8px; }

/* Карточка на странице фабрики: полная хронология процессов. */
.history-order {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px; cursor: pointer;
}
.history-order:hover { border-color: var(--accent); }
.history-order-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; margin-bottom: 10px; flex-wrap: wrap;
}
.history-order-facts { display: flex; align-items: center; gap: 8px; font-size: 12px; }
.history-code { display: block; font-size: 15px; }
.history-title { display: block; color: var(--muted); font-size: 12px; margin-top: 2px; }
/* Пять этапов воронки — пять колонок; при четырёх последний уезжал на строку ниже. */
.process-history { display: grid; grid-template-columns: repeat(5, minmax(140px, 1fr)); gap: 10px; }
.process-step {
  position: relative; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 8px; padding: 9px 10px; min-width: 0;
}
.process-step:not(:last-child)::after {
  content: '→'; position: absolute; right: -10px; top: 50%; z-index: 1;
  color: var(--muted); transform: translateY(-50%); font-size: 12px;
}
.process-step.missing { opacity: .58; }
.process-label {
  font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .35px;
}
.process-date { margin-top: 3px; font-weight: 600; font-size: 13px; }
.process-days {
  display: inline-block; margin-top: 6px; color: var(--ink); background: var(--gossamer-green);
  border-radius: 4px; padding: 1px 5px; font-size: 12px;
}
@media (max-width: 760px) {
  .history-toolbar { align-items: stretch; flex-direction: column; }
  .history-picker select { width: 100%; min-width: 0; }
  .process-history { grid-template-columns: 1fr 1fr; }
  .process-step::after { display: none; }
  .history-group-head { flex-wrap: wrap; }
}

.col {
  flex: 0 0 268px;
  /* Ширину колонки задаёт ТОЛЬКО `flex-basis`, и `min-width: 0` — не
     украшение, а то, что заставляет его работать. У элемента гибкой строки
     минимальная ширина по умолчанию равна ширине его содержимого, поэтому
     одна длинная причина отказа в карточке («Текущий вариант не
     соответствует требованиям, принт у воротника не запрашивался», у неё
     стоит `white-space: nowrap`) раздувала колонку с 236 до 494 px, а
     соседние колонки уезжали за правый край окна: на доске образцов 24.09.2026
     «Завершено» было видно наполовину, «Архив» — не видно вовсе. Теперь
     длинная строка обрезается многоточием, как и задумано, а колонка стоит
     на своём месте. */
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  max-height: 100%;
  display: flex;
  flex-direction: column;
}
.col.drop { border-color: var(--accent); background: var(--accent-dim); }
.col.terminal { opacity: .72; }

/* Свёрнутая колонка («Отгружен» по умолчанию): показывает плитку с числом и
   кнопку разворота, а карточки не строит вовсе — их там со временем сотни.
   Уже колонки: свёрнутая занимает столько, сколько нужно её плитке. */
.col.folded { flex: 0 0 200px; }
.col-fold {
  margin: 0 8px 8px; padding: 5px 8px;
  border: 1px dashed var(--line); border-radius: var(--radius-sm);
  background: transparent; color: var(--muted); font: inherit; font-size: 12px;
  cursor: pointer;
}
.col-fold:hover { border-color: var(--accent); color: var(--ink); }

.col-head {
  padding: 9px 12px;
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 600;
}
.col-head .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex: 0 0 auto; }
.col:nth-child(4n + 1) .col-head .dot { background: var(--marys-rose); }
.col:nth-child(4n + 2) .col-head .dot { background: var(--pastel-yellow); }
.col:nth-child(4n + 3) .col-head .dot { background: var(--orchid-petal); }
.col:nth-child(4n) .col-head .dot { background: var(--gossamer-green); }
.col-head .count { margin-left: auto; color: var(--muted); font-weight: 400; }

.col-body { padding: 8px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; }
/* ПУСТУЮ КОЛОНКУ НАЗЫВАЕТ ЕЁ СОБСТВЕННЫЙ НОЛЬ (аудит 24.09.2026). Под
   плиткой с крупным «0» стояло ещё и слово «пусто» — тот же факт вторым
   почерком, и на доске MD Group он повторялся пять раз подряд. Слово ушло, а
   МЕСТО осталось: пустая колонка — это мишень для перетаскивания карточки
   (правило «пустая статусная колонка с доски не пропадает»), и без запаса
   высоты она схлопнулась бы до одной плитки, в которую нечем целиться. */
.col-body:empty { min-height: 56px; }

.card {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--gap);
  cursor: grab;
}
.card:hover { border-color: var(--accent); }
.card.dragging { opacity: .45; }

.card-code { font-weight: 600; font-size: 13px; display: flex; align-items: center; gap: 6px; }
.card-title { color: var(--muted); font-size: 12px; margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.shipping-date { color: var(--muted); }
.shipping-date-card { font-size: 12px; margin-top: 3px; }
.shipping-date-history { font-size: 12px; margin-top: 2px; }
.shipping-date-modal { margin-top: 2px; }
.shipping-date.shipping-alert {
  display: inline-block;
  padding: 4px 7px;
  border: 1px solid var(--c-d4a514-l);
  border-radius: 6px;
  background: var(--warn);
  color: var(--ink);
  font-weight: 600;
}
.shipping-date.shipping-overdue {
  border-color: var(--c-c96a70-l);
  background: var(--danger);
}
.shipping-countdown { margin-top: 2px; font-size: 11px; line-height: 1.25; }
.card-meta { display: flex; gap: 8px; margin-top: 7px; font-size: 11px; color: var(--muted); flex-wrap: wrap; }
.chip { background: var(--bg); border: 1px solid var(--line); border-radius: 5px; padding: 1px 6px; }
.chip.warn { color: var(--ink); border-color: var(--warn); background: var(--warn); }
/* Чья это строка. Показывается только в общих просмотрах — «Все бренды» и
   «Без бренда», — где данные двух компаний лежат рядом. Внутри бренда метка
   была бы повторением заголовка, поэтому её там нет. */
.chip.chip-brand { font-weight: 600; letter-spacing: .01em; }
/* Заказ идёт дольше, чем эта фабрика обычно тратит. */
.chip.danger { color: var(--ink); border-color: var(--danger); background: var(--danger); }
.otable td.stale-cell { background: var(--warn); font-weight: 600; }
/* Второе число на вкладке — отгруженные: видны на доске, но не «в работе». */
.tab .n.muted-n { opacity: .6; font-weight: 400; }
.tab-samples .n { background: var(--orchid-petal); }

/* ── задачи ─────────────────────────────────────────────── */

.task-list { display: flex; flex-direction: column; gap: 8px; }

.task-done { opacity: .7; }
.task-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.task-text { font-size: 15px; line-height: 1.4; overflow-wrap: anywhere; }
.task-done .task-text { text-decoration: line-through; }
.task-meta {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: 12px;
}

.task-source { font-size: 12px; color: var(--muted); }
.task-source summary { cursor: pointer; }
/* Сообщение фабрики приходит как есть: ссылка или артикул без пробелов не
   должны распирать строку списка. */
.task-source-text { margin-top: 4px; white-space: pre-wrap; overflow-wrap: anywhere; }
.task-source-ru {
  margin-top: 3px; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere;
}

.task-actions { display: flex; gap: 6px; flex: 0 0 auto; }

/* ── чек-листы карточки ─────────────────────────────────── */

/* `min(…, 100%)`: в месте уже 260 px жёсткая колонка не ужимается и вылезает
   за край — так чек-лист выходил за боковую панель карточки юнита. */
.checklists { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }

.checklist {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  padding: 10px 12px 12px;
  display: flex; flex-direction: column; gap: 8px;
}
.checklist.complete { border-color: var(--accent); background: var(--gossamer-green); }

.checklist-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.checklist-title { display: flex; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }
.checklist-title small { color: var(--muted); font-size: 11px; }
.checklist-count { font-size: 12px; color: var(--muted); white-space: nowrap; }

.checklist-bar { height: 4px; border-radius: 2px; background: var(--paper); overflow: hidden; }
.checklist-fill { height: 100%; background: var(--accent); transition: width .18s ease; }
.checklist.complete .checklist-fill { background: var(--ink); }

.checklist-items { display: flex; flex-direction: column; gap: 2px; }
.checklist-item {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 4px 6px; border-radius: 6px; cursor: pointer; font-size: 13px; line-height: 1.35;
}
.checklist-item:hover { background: var(--paper); }
.checklist-item input { margin: 2px 0 0; flex: 0 0 auto; }
.checklist-label { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.checklist-item.done .checklist-label { text-decoration: line-through; opacity: .55; }
.checklist-date { font-size: 11px; color: var(--muted); white-space: nowrap; }
/* «Узнать размерную таблицу»: стоит у своего пункта, пока он не отмечен. */
.checklist-ask { flex: 0 0 auto; margin-left: auto; white-space: nowrap; }
/* Готовый запрос фабрике правится прямо в окне — поле во всю его ширину.
   Без этого `textarea` берёт родную ширину в 20 знаков и стоит узким
   столбиком у левого края, а две трети окна справа остаются пустыми. Правило
   одно на оба окна со скриптом-текстом: размерная таблица и вопрос о посылке;
   высоту каждое задаёт своим числом строк. */
.ask-script-text { width: 100%; line-height: 1.5; }

.checklist-badges { display: flex; flex-wrap: wrap; gap: 4px; }
.chip.ok-chip { background: var(--gossamer-green); border-color: var(--accent); }
.dev-table-checks { min-width: 190px; }

/* ── итог разбора ───────────────────────────────────────── */
/* Живёт внутри <pre> с логом, поэтому переносы возвращаем к обычным. */
.digest { display: flex; flex-direction: column; gap: 14px; font-family: inherit; white-space: normal; }
.digest-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.digest-group { display: flex; flex-direction: column; gap: 6px; }
.digest-title {
  font-size: 12px; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); border-bottom: 1px solid var(--line); padding-bottom: 4px;
}
.digest-items { display: flex; flex-wrap: wrap; gap: 6px; }
.digest-item {
  display: flex; align-items: baseline; gap: 8px;
  padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--text); font: inherit; cursor: pointer;
  text-align: left;
}
.digest-item:hover { border-color: var(--accent); background: var(--panel-2); }
/* Задача, которую Гермес закрыл сам: она уже снята, а не ждёт (22.09.2026). */
.digest-item.digest-done .digest-detail { text-decoration: line-through; color: var(--muted); }
.digest-detail { color: var(--muted); font-size: 13px; }
.digest-at { color: var(--muted); font-size: 12px; }

/* ── modals ─────────────────────────────────────────────── */

/* ОКНО НЕ УЕЗЖАЕТ ПОД ПАНЕЛЬ ГЕРМЕСА (24.09.2026).
   Панель разговора висит справа (`.chat-panel`, 400 px, z-index 56) ПОВЕРХ
   окна, а окно центрируется по всему экрану — и правая его часть оказывалась
   под панелью. На узком окне это незаметно, а на широком (`.modal-wide`,
   до 1580 px) под панелью пропадала целая колонка: в «Запросить КИЗы» — ТНВЭД.
   Считаем панель занятым местом: окно центрируется в остатке и в него же
   вписывает свою ширину. Окна-панели (`#order-modal`, `.as-drawer`) это не
   трогает — их `.modal-panel` позиционируется от экрана, а не отсюда. */
:root { --chat-room: 0px; }
@media (min-width: 900px) { body.chat-open { --chat-room: 400px; } }
.modal {
  position: fixed; inset: 0 var(--chat-room) 0 0; z-index: 40;
  display: grid; place-items: center;
}
.modal-bg {
  position: absolute;
  inset: 0;
  background: var(--hushed-violet);
  opacity: .78;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.modal-panel {
  position: relative;
  width: min(860px, calc(94vw - var(--chat-room)));
  max-height: 88vh;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  display: flex; flex-direction: column;
  overflow: hidden;
}
.modal-narrow { width: min(480px, 94vw); }

.modal-head {
  padding: var(--pad) var(--pad-lg);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
}
/* Заголовок окна — гибкий элемент, и без `min-width: 0` он не даёт себя сжать:
   длинное имя контрагента одним словом уезжало за правый край боковой панели и
   обрезалось вместе с половиной названия. */
.modal-head > div { min-width: 0; }
.modal-code { font-weight: 700; font-size: 16px; overflow-wrap: anywhere; }
.modal-title { color: var(--muted); font-size: 13px; margin-top: 2px; }
/* Окно без подзаголовка не держит под названием пустую строку с отступом. */
.modal-title:empty { display: none; }
.modal-body { padding: var(--pad-lg); overflow-y: auto; }

.card-section { margin-bottom: 20px; }
.card-section:last-child { margin-bottom: 0; }
.card-section h3 { font-size: 13px; margin: 0 0 10px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); }

.fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-bottom: 14px; }
.field-label { font-size: 11px; color: var(--muted); }
.field-value { font-weight: 500; margin-top: 1px; }

.status-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.status-row label { font-size: 12px; color: var(--muted); }
.status-row input { flex: 1; min-width: 180px; }
.hint { font-size: 12px; color: var(--muted); margin-top: 8px; }
/* Подсказка, которую заполняют позже (итог, пропущенные чаты), до этого места не занимает. */
.hint:empty { display: none; }
.hint code { background: var(--panel-2); padding: 1px 5px; border-radius: 4px; }

/* «ⓘ» — объяснение по нажатию (24.09.2026).
   Значок маленький нарочно: он стоит рядом с названием и не должен спорить с
   ним за взгляд. Плашка выше окон (`z-index` доски — 40, панели Гермеса — 56):
   значок бывает и внутри окна. */
.info-dot {
  position: relative;
  display: inline-grid; place-items: center; vertical-align: middle;
  width: 13px; height: 13px; margin-left: 5px; padding: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: transparent; color: var(--muted);
  font-size: 9px; font-weight: 600; font-style: italic; line-height: 1;
  cursor: pointer; flex: none;
}
/* ЗНАЧОК МАЛЕНЬКИЙ НАРОЧНО, А ПОПАДАТЬ В НЕГО НАДО С ПЕРВОГО РАЗА
   (аудит 24.09.2026). Тринадцать пикселей — это размер РИСУНКА, а не мишени:
   целиться в него мышью так же трудно, как в значок календаря у поля даты, с
   которого и началось правило `NadosPicker`. Невидимая накладка расширяет
   область нажатия до 25 px во все стороны и на разметку не влияет вовсе. */
.info-dot::after { content: ''; position: absolute; inset: -6px; border-radius: 50%; }
.info-dot:hover { color: var(--text); border-color: var(--muted); }
/* В гибком ряду расстояние держит его собственный `gap`, а свой отступ
   значка прибавлялся сверху: в шапке «Ревью» между «Правилами агента» и «ⓘ»
   выходило 13 px против 8 у остальной пары, и значок читался как оторванный
   от ряда. */
.actions > .info-dot,
.row-actions > .info-dot,
.section-actions > .info-dot,
.filters > .info-dot { margin-left: 0; }
/* Внутри гибких подписей отступ даёт сам `gap` — второй съедал бы ширину и
   отправлял значок на свою строку. */
.metric-name .info-dot { margin-left: 4px; }
.metric-name-text { display: inline; }
.info-pop {
  position: fixed; z-index: 90; max-width: 280px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--panel); color: var(--text);
  font-size: 12px; line-height: 1.45; box-shadow: 0 8px 24px var(--shadow-soft);
}

/* timeline */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
  position: relative; padding: 0 0 14px 18px; border-left: 2px solid var(--line);
}
.timeline li:last-child { border-left-color: transparent; padding-bottom: 0; }
.timeline li::before {
  content: ""; position: absolute; left: -5px; top: 4px;
  width: 8px; height: 8px; border-radius: 50%; background: var(--muted);
}
.timeline li.manual::before { background: var(--accent); }
.timeline li.agent::before { background: var(--ok); }
.ev-head { font-size: 13px; display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.ev-note { color: var(--muted); font-size: 12px; margin-top: 2px; white-space: pre-wrap; }
.src { font-size: 10px; text-transform: uppercase; letter-spacing: .5px; padding: 1px 5px; border-radius: 4px; border: 1px solid var(--line); }
.src.manual { color: var(--ink); border-color: var(--accent); background: var(--gossamer-green); }
.src.agent { color: var(--ink); border-color: var(--ok); background: var(--gossamer-green); }

/* messages */
.msgs { display: flex; flex-direction: column; gap: 7px; }
.msg { background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; }
.msg.out { border-left: 3px solid var(--accent); }
.msg-head { font-size: 11px; color: var(--muted); display: flex; gap: 8px; flex-wrap: wrap; }
.msg-text { font-size: 13px; margin-top: 3px; white-space: pre-wrap; word-break: break-word; }
/* Перевод живёт под оригиналом и никогда его не заменяет. */
/* Вложение под сообщением: имя файла и две кнопки — открыть и сохранить. */
.msg-attach {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--line);
}
.msg-attach-name {
  font-size: 12px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}

.msg-ru {
  font-size: 13px;
  margin-top: 5px;
  padding-top: 5px;
  border-top: 1px dashed var(--line);
  color: var(--muted);
  white-space: pre-wrap;
  word-break: break-word;
}
.msg-ru::before { content: "RU  "; font-size: 10px; letter-spacing: .5px; color: var(--ink); background: var(--gossamer-green); font-weight: 600; }

/* Файлы заказа: строка на вложение, слева — снимок-миниатюра. */
.order-files { display: flex; flex-direction: column; gap: 7px; }
.order-file {
  display: flex; align-items: center; gap: 10px;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 8px; padding: 7px 10px;
}
.order-file-rel { border-left: 3px solid var(--accent); }
.order-file-info { flex: 1; min-width: 0; }
.order-file-head { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.order-file-name { font-size: 13px; font-weight: 600; word-break: break-word; }
.order-file-thumb {
  width: 44px; height: 44px; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--line); cursor: pointer; flex: none;
}
.order-file-locked {
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; color: var(--muted); cursor: default; background: var(--panel);
}
/* Документ вместо миниатюры — плашка с расширением: имя файла бывает
   машинным («purchaseorder image (1)-00041.xls»), а формат человек узнаёт
   с одного взгляда. */
.order-file-ext {
  display: flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 700; letter-spacing: .4px;
  color: var(--muted); background: var(--panel); cursor: default;
}
.order-file-meta { font-size: 11.5px; }
.order-file-actions { display: flex; align-items: center; gap: 4px; flex: none; }
.order-file-none { font-size: 11px; }
/* Рабочие документы производства — свёрнуты: не мусор, но и не про этот заказ. */
.order-files-more > summary {
  cursor: pointer; font-size: 12.5px; color: var(--muted);
  padding: 6px 2px; list-style: revert;
}
.order-files-more .order-files-list {
  display: flex; flex-direction: column; gap: 7px; padding-top: 4px;
}
/* Снимок и pdf — на весь просмотр, а не миниатюрой. */
.attach-image { max-width: 100%; max-height: 76vh; display: block; margin: 0 auto; border-radius: 8px; }
.attach-pdf { width: 100%; height: 76vh; border: 1px solid var(--line); border-radius: 8px; background: var(--panel-2); }
/* Лист Excel — таблицей, а не простынёй с табами: в `<pre>` колонки
   разъезжались тем сильнее, чем длиннее название товара. */
.attach-sheet { margin-top: 8px; max-height: 64vh; overflow: auto; border: 1px solid var(--line); border-radius: 8px; }
.attach-sheet .otable { font-size: 12px; border-collapse: collapse; width: max-content; min-width: 100%; }
.attach-sheet td { border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); padding: 4px 7px; vertical-align: top; }
.attach-sheet-head { font-weight: 600; background: var(--panel-2); }
/* Что ещё лежит в архиве — ссылками, а не строкой через запятую. */
/* В архиве заказа шестьдесят файлов; список не должен занимать всё окно —
   он прокручивается, а таблица остаётся видна. */
.attach-entries {
  display: flex; flex-wrap: wrap; gap: 5px; margin: 4px 0 10px;
  max-height: 116px; overflow-y: auto;
}
.attach-entry {
  padding: 4px 9px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel-2); color: var(--ink); font: inherit; font-size: 12px;
  cursor: pointer; max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.attach-entry:hover { border-color: var(--accent); background: var(--panel); }
/* Текст открытого файла — в окне, моноширинно, со скроллом. */
.attach-text {
  margin-top: 8px; max-height: 60vh; overflow: auto;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 10px; font-size: 12.5px; white-space: pre-wrap; word-break: break-word;
}

/* Состав заказа: блок виден всегда, даже когда состава нет. */
.items-actions { margin-top: 10px; }

/* review inbox */
.review {
  border: 1px solid var(--line); border-radius: 10px;
  padding: 12px; margin-bottom: 10px; background: var(--panel-2);
}
.review-head { display: flex; gap: 8px; align-items: center; font-size: 12px; color: var(--muted); }
.review-question { margin: 9px 0 5px; font-size: 15px; font-weight: 650; color: var(--text); }
.review-context { margin: 0 0 9px; color: var(--muted); font-size: 12px; white-space: pre-wrap; }
.review-why-label {
  margin-bottom: 3px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
/* Основание пишется абзацами и списками — переносы в нём смысловые: у спора об
   импорте это строка на каждое расхождившееся поле. Без `pre-wrap` весь список
   слипался в один абзац. */
.review-why-text { color: var(--text); font-size: 13px; white-space: pre-wrap; }
/* Пары «образец → товар» перед кнопкой «Да, привязать»: строка на пару, номер
   слева колонкой — так видно, что именно подтверждаешь. */
.review-pairs { display: flex; flex-direction: column; gap: 3px; }
.review-pair {
  display: flex; gap: 7px; align-items: baseline;
  color: var(--text); font-size: 13px;
}
.review-pair > strong { flex: 0 0 auto; min-width: 58px; }
.review-message { margin: 0 0 9px; }
.review-message > summary {
  cursor: pointer; font-size: 12.5px; color: var(--muted); user-select: none;
}
.review-message > summary:hover { color: var(--text); }
.review-message[open] > summary { margin-bottom: 6px; }
/* Цифра на кнопке ответа: что нарисовано, то и нажимается с клавиатуры. */
.review-key {
  display: inline-flex; align-items: center; justify-content: center;
  /* Не сжимается и не тянется: колонка цифры одинакова у всех кнопок ряда. */
  flex: 0 0 auto; margin-top: 1px;
  min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: 4px; background: var(--c-rgba-80-66-48-1-s); color: var(--muted);
  font-size: 10px; font-weight: 700; line-height: 1;
}
.btn-primary .review-key { background: var(--c-rgba-47-66-56-16-s); color: var(--on-accent-strong); }
.btn-danger .review-key { background: var(--c-rgba-122-51-57-12-s); color: var(--ink); }

/* Плашка, которая ведёт в карточку заказа или в раздел: выглядит как чип,
   ведёт себя как ссылка. */
.chip-link { font: inherit; font-size: 12px; cursor: pointer; }
.chip-link:hover { border-color: var(--accent); }

/* Что агент решает сам: панель сохранённых правил над очередью. */
.review-rules {
  border: 1px solid var(--line); border-radius: 10px; padding: 12px;
  margin-bottom: 12px; background: var(--panel-2);
}
.review-rule-row {
  display: flex; gap: 10px; align-items: center; padding: 7px 0;
  border-top: 1px solid var(--line);
}
.review-rule-row:first-of-type { border-top: none; }
.review-rule-text { flex: 1; min-width: 0; font-size: 13px; }
.review-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; min-width: 0; }
.review-feedback {
  width: 100%; min-height: 68px; margin: 10px 0 8px; resize: vertical;
  border: 1px solid var(--line); border-radius: 8px; padding: 9px 10px;
  color: var(--text); background: var(--panel); font: inherit;
}
.review-image-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
.review-image {
  width: 100%; height: 110px; object-fit: cover; border-radius: 8px;
  border: 1px solid var(--line); cursor: zoom-in; background: var(--panel);
}
.review-image.unavailable { opacity: .35; }

/* Пара «снимок из переписки / кандидаты» — вопрос, который решают глазами.
   Требование пользователя 23.09.2026: увидел два фото рядом и нажал, без
   поиска артикула и переходов. Снимок из чата крупнее кандидатов: он тот
   самый, про который спрашивают, а справа — с чем сравнить. */
.review-compare {
  display: flex; gap: 12px; align-items: flex-start;
  margin-top: 8px; padding: 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--panel);
}
.review-compare-side { flex: 0 0 168px; }
.review-compare-options {
  flex: 1 1 auto; display: grid;
  grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 8px;
}
.review-compare-option {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 6px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); color: inherit; font: inherit;
}
.review-compare-option:hover { border-color: var(--accent); }
.review-compare-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.review-compare-label { font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.review-compare-photo {
  width: 100%; height: 150px; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--line); background: var(--panel);
}
.review-compare-option .review-compare-photo { height: 104px; cursor: pointer; }
.review-compare-side .review-compare-photo { cursor: zoom-in; }
.review-compare-empty {
  height: 104px; display: flex; align-items: center; justify-content: center;
  border: 1px dashed var(--line); border-radius: 8px;
  font-size: 12px; color: var(--muted);
}
.review-compare-code { margin-top: 4px; font-size: 12px; color: var(--muted); }

/* ЦЕНА БЕЗ АДРЕСА: слева названная цена и слова фабрики, справа плитки образцов.
   Левая сторона шире обычной пары «снимок / кандидаты» — там снимок в 168 px, а
   здесь читают китайскую фразу с условием и её перевод: «单价：42 含广东省内运费不含
   税，含吊牌，标签，码标» в узкую колонку не встаёт. */
.review-price .review-compare-side { flex: 0 0 280px; }
.review-price-figure { font-size: 22px; font-weight: 700; margin-bottom: 6px; }
.review-price .msg { margin: 0; }

/* Узкое окно: пара встаёт в столбик, иначе снимки сжимаются до нечитаемого. */
@media (max-width: 720px) {
  .review-compare { flex-direction: column; }
  .review-compare-side { flex: 1 1 auto; width: 100%; }
}

/* АЛЬБОМ: вопрос о фотографиях решается глазами.
   Требование пользователя 23.09.2026 после вопроса № 192 — «я не вижу, к какому
   артикулу меня просят прикрепить эти фотографии… всё это должно быть
   визуально видно». Слева пришедшее из переписки, справа лежащее в карточке;
   плитка — кнопка, выбранная отмечена галочкой и рамкой акцента. */
.review-album {
  margin-top: 8px; padding: 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--panel);
}
.review-album-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding-bottom: 8px; border-bottom: 1px solid var(--line);
}
.review-album-face { display: flex; align-items: center; gap: 10px; flex: 1 1 auto; min-width: 0; }
.review-album-avatar {
  width: 56px; height: 56px; object-fit: cover; flex: 0 0 auto;
  border-radius: 8px; border: 1px solid var(--line); background: var(--panel-2); cursor: zoom-in;
}
.review-album-name { min-width: 0; }
.review-album-title { font-weight: 700; overflow-wrap: anywhere; }
.review-album-cols {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 10px;
}
.review-album-col { min-width: 0; }
.review-album-labels { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
.review-album-label { font-weight: 600; font-size: 13px; min-width: 0; }
.review-album-hint { font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.review-album-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 6px;
}
.review-album-tile {
  position: relative; display: block; width: 100%; padding: 3px; cursor: pointer;
  border: 2px solid var(--line); border-radius: 8px;
  background: var(--bg); color: inherit; font: inherit;
}
/* Взятая плитка отмечена и рамкой, и галочкой: одного цвета мало — выбор
   должен читаться и при беглом взгляде, и в чёрно-белом снимке экрана. */
.review-album-tile.on { border-color: var(--accent); background: var(--accent-dim); }
.review-album-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.review-album-photo {
  width: 100%; height: 76px; object-fit: cover; border-radius: 6px; display: block;
  background: var(--panel-2);
}
/* Невыбранная плитка приглушена — видно, что она отложена, а не просто стоит. */
.review-album-tile:not(.on) .review-album-photo { opacity: .45; }
.review-album-mark {
  position: absolute; top: 6px; left: 6px; width: 18px; height: 18px;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; line-height: 1; border-radius: 50%;
  border: 1px solid var(--line); background: var(--panel); color: var(--ink-green);
}
.review-album-tile.on .review-album-mark { border-color: var(--accent); }
.review-album-zoom {
  position: absolute; top: 6px; right: 6px; width: 18px; height: 18px;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; line-height: 1; border-radius: 4px; cursor: zoom-in;
  border: 1px solid var(--line); background: var(--panel); color: var(--muted);
}
.review-album-note { font-size: 11px; color: var(--muted); padding: 2px 0 0; }
/* Кадр, который положить нельзя, не нажимается вовсе: сохранение упало бы
   целиком из-за одного снимка. Курсор и бледность говорят об этом до нажатия. */
.review-album-tile.locked { cursor: not-allowed; border-style: dashed; }
.review-album-tile.locked .review-album-photo { opacity: .3; }
.review-album-tile.locked .review-album-note { color: var(--ink-rose); }
.review-album-gone {
  height: 76px; display: flex; align-items: center; justify-content: center;
  border: 1px dashed var(--line); border-radius: 6px; font-size: 11px; color: var(--muted);
}
.review-album-count { margin: 10px 0 8px; font-size: 13px; color: var(--muted); }
.review-album-count.over { color: var(--ink-rose); font-weight: 600; }
/* Разбор суммы: галочек бывает больше, чем прибавится снимков. */
.review-album-why { margin: -4px 0 8px; font-size: 12px; color: var(--muted); }
/* ОТВЕТ СЛОВАМИ (29.09.2026): плитка — снимок с номером, а не кнопка. Номер
   стоит там же, где стояла галочка, и на него ссылаются в ответе. */
.review-album-tile.view { cursor: default; border-width: 1px; }
.review-album-tile.view .review-album-photo { opacity: 1; cursor: zoom-in; }
.review-album-no {
  position: absolute; top: 6px; left: 6px; min-width: 18px; height: 18px; padding: 0 4px;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600; line-height: 1; border-radius: 9px;
  border: 1px solid var(--line); background: var(--panel); color: var(--ink);
}
.review-album-others { margin-top: 6px; }
div.review-compare-option { cursor: default; }
div.review-compare-option:hover { border-color: var(--line); }
div.review-compare-option .review-compare-photo { cursor: zoom-in; }
.review-answer-actions { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.review-answer-state { margin: 10px 0 8px; display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.review-answer-state.failed .review-why-label { color: var(--ink-rose); }
.review-answer-said { font-size: 13px; white-space: pre-wrap; overflow-wrap: anywhere; }
.chat-msg-about { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.review-album-pick-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: 8px; padding: var(--pad-lg); overflow-y: auto;
}
/* Отмеченная плитка: выбранный артикул в окне альбома и догадка Гермеса в
   вопросе о цене — одно и то же «вот это», поэтому и вид один. */
.album-pick .review-compare-option.on,
.review-price .review-compare-option.on { border-color: var(--accent); background: var(--accent-dim); }
.album-pick-panel { width: min(720px, 94vw); }
.album-pick-panel .modal-title { padding: var(--pad) var(--pad-lg) 0; }
.album-pick-panel .row-actions { padding: 0 var(--pad-lg) var(--pad-lg); }

/* «Обнаружено различие в цене» (29.09.2026): фото артикула, обе цены и три
   ответа — у каждого подпись, что он сделает. Фото — сразу, крупно: «чтобы я
   понимала, о чём речь». */
.price-diff-panel { width: min(560px, 94vw); }
.price-diff-panel:focus { outline: none; }
.price-diff-main { display: flex; gap: 14px; align-items: flex-start; }
.price-diff-photo {
  width: 150px; height: 190px; object-fit: cover; flex: none; cursor: zoom-in;
  border-radius: 10px; border: 1px solid var(--line); background: var(--bg);
}
.price-diff-nophoto {
  width: 150px; height: 190px; flex: none;
  display: flex; align-items: center; justify-content: center;
  border: 1px dashed var(--line); border-radius: 10px; font-size: 12px; color: var(--muted);
}
.price-diff-text { min-width: 0; }
.price-diff-lead { font-size: 15px; line-height: 1.45; overflow-wrap: anywhere; }
.price-diff-meta { margin-top: 8px; font-size: 12px; }
.price-diff-choices { display: grid; gap: 8px; margin-top: 16px; }
.btn.price-diff-choice {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; white-space: normal; border-radius: 12px; padding: 8px 14px;
}
.price-diff-choice-label { font-weight: 600; }
.price-diff-choice-hint { font-size: 12px; opacity: .8; font-weight: 400; }
@media (max-width: 520px) {
  .price-diff-main { flex-direction: column; }
  .price-diff-photo, .price-diff-nophoto { width: 100%; height: 220px; }
}

/* Узкое окно: полосы встают одна под другой, иначе плитки сжимаются до
   нечитаемого — а решают по ним глазами. */
@media (max-width: 720px) {
  .review-album-cols { grid-template-columns: 1fr; }
}

/* Снимок крупно поверх интерфейса. Фон и блюр — от .modal-bg (токены темы),
   здесь только раскладка самого изображения, без цветовых литералов. */
.image-zoom { z-index: 60; cursor: zoom-out; }
.image-zoom-full {
  position: relative;
  max-width: 94vw; max-height: 94vh;
  object-fit: contain;
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 40px var(--shadow-soft);
}
.review-actions .btn {
  /* Цифра — своя колонка, подпись — своя: длинный ответ переносится внутри
     подписи и второй строкой встаёт под первой, а не под цифрой. */
  display: inline-flex;
  align-items: flex-start;
  gap: 6px;
  white-space: normal;
  overflow-wrap: anywhere;
  max-width: 100%;
  min-width: 0;
  text-align: left;
  line-height: 1.35;
}
.rule-text { line-height: 1.55; white-space: pre-wrap; }
.rule-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; min-width: 0; }
.rule-actions .btn {
  width: 100%;
  max-width: 100%;
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
  text-align: left;
  line-height: 1.35;
}
.kind { font-size: 10px; text-transform: uppercase; letter-spacing: .5px;
  border: 1px solid var(--warn); color: var(--ink); background: var(--warn); border-radius: 4px; padding: 1px 5px; }

.parse-list { display: flex; flex-direction: column; gap: 6px; }
.parse-item {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: 8px; cursor: pointer; background: var(--panel-2);
}
.parse-item:hover { border-color: var(--accent); }
.parse-item .grow { flex: 1; }
.parse-item small { color: var(--muted); }

/* job panel */
/* Плашка работы отступает от открытой панели Гермеса тем же `--chat-room`,
   что и окна (аудит 24.09.2026): панель выше неё (z-index 56 против 50), и
   при открытом разговоре «Стоп» и «Свернуть» лежали под ней — разбор нельзя
   было ни остановить, ни дочитать, пока не закроешь чат. */
.job {
  position: fixed; right: calc(var(--chat-room) + 14px); bottom: 14px; z-index: 50;
  width: min(560px, calc(92vw - var(--chat-room)));
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; overflow: hidden;
  box-shadow: 0 12px 40px var(--shadow-soft);
}
.job-head { display: flex; align-items: center; gap: 9px; padding: 10px 12px; border-bottom: 1px solid var(--line); }
.job-head #job-title { margin-right: auto; }
.spinner.stopped { border-top-color: var(--warn); animation: none; }
.job-log { margin: 0; padding: 10px 12px; max-height: 230px; overflow-y: auto;
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--muted);
  white-space: pre-wrap; word-break: break-word; }
.job-log:empty { display: none; }

/* Свёрнутая плашка разбора.
 *
 * Развёрнутая занимает правый нижний угол целиком и закрывает собой кнопки
 * карточки, а разбор идёт минутами — всё это время работать в CRM нельзя.
 * Свёрнутая освобождает экран и оставляет ровно то, ради чего на неё смотрят:
 * идёт работа или кончилась и на каком она производстве из скольких. На сам
 * разбор это не влияет никак — он идёт на сервере, плашка только читает его
 * журнал, и опрос при сворачивании не прекращается.
 *
 * Плашка, а не голый кружок (16.09.2026). Кружок с одним вертящимся ободком не
 * отвечал ни на один вопрос: понять, что это вообще такое, можно было только
 * наведя мышь и дождавшись подсказки браузера. Теперь «2/3» стоит на самой
 * плашке.
 *
 * Размеры задаются здесь целиком — ширина, высота, отступы, радиус, — поэтому
 * развёрнутое состояние ничего за собой не тянет: снял класс, и всё вернулось
 * к правилу `.job` выше. Сворачивать и разворачивать можно сколько угодно раз.
 */
.job.mini {
  /* Прижата к тому же правому краю, что и кнопка Гермеса под ней, и ровно на
     её высоту выше: две плашки стоят столбиком, а не наезжают друг на друга. */
  right: calc(var(--chat-room) + 18px); bottom: 70px;
  width: auto; min-width: 44px; max-width: min(240px, 60vw); height: 40px;
  padding: 0 14px; border-radius: 999px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 20px var(--shadow-soft);
}
/* Шапка внутри плашки — обычная строка из значка и подписи. `min-width: 0`
   обязателен: без него длинная подпись распирала бы плашку за край экрана
   вместо того, чтобы обрезаться многоточием. */
.job.mini .job-head {
  border-bottom: 0; padding: 0; gap: 8px; min-width: 0; width: 100%;
  justify-content: center;
}
.job.mini #job-title, .job.mini #job-min, .job.mini #job-stop,
.job.mini #job-close, .job.mini .job-log { display: none; }
.job.mini .spinner { width: 16px; height: 16px; border-width: 2.5px; }
/* Подпись живёт только в свёрнутом виде: в развёрнутом её место занимает
   полный заголовок с названием производства. */
.job-mini-text { display: none; }
.job.mini .job-mini-text {
  display: block; min-width: 0;
  font-size: 12.5px; line-height: 1; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Пустая подпись (разбор только начался и номера ещё нет) не должна оставлять
   в плашке лишний отступ — тогда она снова становится ровным кружком. */
.job.mini .job-mini-text:empty { display: none; }
.job.mini:has(.job-mini-text:empty) { padding: 0; width: 44px; height: 44px; border-radius: 50%; }

.spinner {
  width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: spin .8s linear infinite; flex: 0 0 auto;
}
.spinner.done { border-top-color: var(--ok); animation: none; }
.spinner.failed { border-top-color: var(--danger); animation: none; }
@keyframes spin { to { transform: rotate(360deg); } }

/* «Внимание, идёт обновление программы» (29.09.2026, ui/update-watch.js).
   Выше всего остального (окна — 40, меню снимка — 95): пока сервер меняют,
   нажимать нечего. Фон — как у окон, тосты под ним не видны: ошибки
   оборванных запросов тут ни о чём не говорят. */
.update-overlay { position: fixed; inset: 0; z-index: 200; }
.update-overlay::before {
  content: ''; position: absolute; inset: 0;
  background: var(--hushed-violet); opacity: .82;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.update-overlay:focus { outline: none; }
/* Карточка — по середине ВИДИМОГО экрана (`vw`/`vh`), а не подложки: на
   телефоне доска шире экрана, подложка растягивается вместе с ней, и
   карточка по её середине уезжала вправо за край. */
.update-card {
  position: fixed; left: 50vw; top: 50vh; transform: translate(-50%, -50%);
  width: min(440px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow-y: auto;
  box-sizing: border-box;
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  padding: 28px 26px 24px; text-align: center;
  box-shadow: 0 18px 48px var(--shadow-soft);
}
.update-card .update-spinner { display: block; width: 30px; height: 30px; margin: 0 auto 16px; border-width: 3px; }
.update-card h2 {
  margin: 0 0 8px; font-family: var(--font-display); font-size: 21px; font-weight: 600;
  line-height: 1.3; color: var(--text); text-wrap: balance;
}
.update-card p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--text-2); }
.update-card .update-long { margin-top: 12px; font-size: 13px; color: var(--muted); }
body.is-updating .toasts { display: none; }

/* «Основной компьютер руководителя… выключен» (29.09.2026, ui/update-watch.js):
   за CRM ответил сервер-вход. Плашка, а не окно — доска под ней остаётся видна
   и читается. По середине ВИДИМОГО экрана (`vw`), как и карточка обновления:
   на телефоне доска шире экрана. Выше тостов и меню (60–95), ниже окна
   обновления (200); тосты на это время спускаются под неё. */
.offline-banner {
  position: fixed; top: 12px; left: 50vw; transform: translateX(-50%); z-index: 150;
  width: min(640px, calc(100vw - 24px)); box-sizing: border-box;
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 16px; border-radius: 12px;
  background: var(--danger); color: var(--ink-rose); border: 1px solid var(--c-ddb0b4-s);
  box-shadow: 0 10px 28px var(--shadow-soft);
  font-size: 14px; line-height: 1.45;
}
.offline-banner svg { flex: none; margin-top: 1px; }
.offline-banner strong { display: block; font-weight: 600; }
.offline-banner span { display: block; }
.offline-banner .offline-next { margin-top: 4px; font-size: 12px; opacity: .85; }
body.is-offline .toasts { top: 132px; }

/* Скелет раздела: мерцающие плашки, пока грузятся данные. */
.section-skeleton { padding: 22px 26px; }
.skeleton-rows { display: flex; flex-direction: column; gap: 12px; max-width: 720px; }
.skeleton-row {
  height: 46px; border-radius: 10px;
  background: linear-gradient(90deg, var(--panel-2) 25%, var(--line) 37%, var(--panel-2) 63%);
  background-size: 400% 100%;
  animation: skeleton-shimmer 1.3s ease-in-out infinite;
}
.skeleton-row:nth-child(2) { width: 92%; }
.skeleton-row:nth-child(3) { width: 84%; }
.skeleton-row:nth-child(4) { width: 88%; }
.skeleton-row:nth-child(5) { width: 78%; }
@keyframes skeleton-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .skeleton-row { animation: none; } }

/* ── Календарь года ─────────────────────────────────────────────────────────
 *
 * Классы здесь `yc-*`, а не `cal-*`, и это исправление, а не вкусовщина.
 * Мини-календарь выбора даты (`.calendar-pop`, ниже по файлу) уже занимал
 * `.cal-grid` и `.cal-day`, и стоял он ПОЗЖЕ — то есть переопределял годовую
 * сетку. Год выкладывался семью колонками вместо четырёх, клетка получала
 * фиксированные 27px, и весь календарь уезжал за правый край экрана. Имена
 * разведены, чтобы это не повторилось.
 */
.cal-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 12px 0 16px; flex-wrap: wrap; }
.cal-year-nav { display: flex; align-items: center; gap: 8px; }
.cal-year { font-size: 20px; font-weight: 700; min-width: 64px; text-align: center; }

/* Строки внутри уже цветные — своя рамка и фон вокруг них были рамкой
   вокруг рамок (аудит 16.09.2026). */
.cal-attention { margin-bottom: 18px; }
.cal-att-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 10px; border-radius: 8px; margin-top: 6px; flex-wrap: wrap;
}
.cal-att-row.overdue { background: color-mix(in srgb, var(--danger) 12%, transparent); }
.cal-att-row.soon { background: color-mix(in srgb, var(--warn) 14%, transparent); }
.cal-att-main { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.cal-att-title { background: none; border: none; font-size: 14px; font-weight: 600; cursor: pointer; color: var(--ink); padding: 0; text-align: left; }
.cal-att-title:hover { text-decoration: underline; }
.cal-att-ask { display: flex; align-items: center; gap: 8px; }
.cal-left { font-size: 12.5px; font-weight: 600; color: var(--warn-ink, var(--ink)); }

/* Цвета задач. Пастель той же природы, что и вся палитра приложения, но
 * десять различимых оттенков: рядом в клетке стоят до трёх полосок, и они
 * должны читаться как разные, а не как оттенки одного.
 *
 * Селекторы нарочно привязаны к своим классам, а не к голому `[data-c]`:
 * атрибут короткий, и правило на весь документ рано или поздно покрасило бы
 * чужой узел. */
.yc-chip[data-c="sage"],  .yc-chip-dot[data-c="sage"],  .yc-mark[data-c="sage"],  .yc-swatch[data-c="sage"]  { --cal-c: #7fa98c; }
.yc-chip[data-c="rose"],  .yc-chip-dot[data-c="rose"],  .yc-mark[data-c="rose"],  .yc-swatch[data-c="rose"]  { --cal-c: #d98b93; }
.yc-chip[data-c="sand"],  .yc-chip-dot[data-c="sand"],  .yc-mark[data-c="sand"],  .yc-swatch[data-c="sand"]  { --cal-c: #d4b163; }
.yc-chip[data-c="lilac"], .yc-chip-dot[data-c="lilac"], .yc-mark[data-c="lilac"], .yc-swatch[data-c="lilac"] { --cal-c: #9b8bc4; }
.yc-chip[data-c="sky"],   .yc-chip-dot[data-c="sky"],   .yc-mark[data-c="sky"],   .yc-swatch[data-c="sky"]   { --cal-c: #6fa3c4; }
.yc-chip[data-c="clay"],  .yc-chip-dot[data-c="clay"],  .yc-mark[data-c="clay"],  .yc-swatch[data-c="clay"]  { --cal-c: #c98a5e; }
.yc-chip[data-c="mint"],  .yc-chip-dot[data-c="mint"],  .yc-mark[data-c="mint"],  .yc-swatch[data-c="mint"]  { --cal-c: #5fb3a1; }
.yc-chip[data-c="plum"],  .yc-chip-dot[data-c="plum"],  .yc-mark[data-c="plum"],  .yc-swatch[data-c="plum"]  { --cal-c: #a8688f; }
.yc-chip[data-c="olive"], .yc-chip-dot[data-c="olive"], .yc-mark[data-c="olive"], .yc-swatch[data-c="olive"] { --cal-c: #8d9c58; }
.yc-chip[data-c="coral"], .yc-chip-dot[data-c="coral"], .yc-mark[data-c="coral"], .yc-swatch[data-c="coral"] { --cal-c: #e08a6d; }

/* Двенадцать месяцев: четыре в ряд — три ряда.
 *
 * Колонки в долях (`minmax(0, 1fr)`), поэтому месяц ужимается вместе с окном
 * и горизонтальной прокрутки не бывает никогда. `minmax(0, …)` обязателен:
 * без нуля колонка не сжимается меньше своего содержимого и сетка вылезает.  */
.yc-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  /* МЕСЯЦЫ В РЯДУ КОНЧАЮТСЯ НА ОДНОЙ ЛИНИИ (аудит 24.09.2026). В ноябре
     2026-го шесть недельных строк, в соседних месяцах пять, и от `start`
     карточка ноября свисала на 43 px ниже трёх остальных: ряд читался как
     съехавший. Растягиваем — низ у всех общий, а лишняя строка внутри
     короткого месяца просто пуста, как в бумажном календаре. */
  align-items: stretch;
}
.yc-month {
  min-width: 0;
  border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 9px; background: var(--panel);
}
.yc-month-name {
  font-weight: 700; font-size: 13.5px; margin-bottom: 7px; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.yc-days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; }
.yc-wd {
  font-size: 10px; color: var(--muted); text-align: center; padding-bottom: 2px;
  overflow: hidden; text-overflow: clip; white-space: nowrap;
}
.yc-day {
  position: relative;
  aspect-ratio: 1; min-width: 0; padding: 0;
  border: none; background: transparent; border-radius: 6px;
  cursor: pointer; color: var(--ink); font: inherit; font-size: 11.5px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  overflow: hidden;
}
.yc-day.empty { cursor: default; }
.yc-day:not(.empty):hover { background: var(--panel-2); }
.yc-day:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.yc-num { line-height: 1; }
.yc-day.has-entry .yc-num { font-weight: 700; }
.yc-day.today { outline: 2px solid var(--accent); outline-offset: -2px; }
.yc-day.today .yc-num { font-weight: 700; }

/* Тревога остаётся фоном: она про срок, а не про то, какая это задача.
 * Какая именно — говорят цветные полоски ниже. */
.yc-day.has-entry.soon { background: color-mix(in srgb, var(--warn) 26%, transparent); }
.yc-day.has-entry.overdue { background: color-mix(in srgb, var(--danger) 24%, transparent); }
.yc-day.has-entry.all-done { background: color-mix(in srgb, var(--ok) 16%, transparent); }

/* Полоски задач под числом. У периода полоска идёт через все его дни и
 * скругляется только на краях — отрезок читается отрезком. */
.yc-marks {
  position: absolute; left: 0; right: 0; bottom: 2px;
  display: flex; flex-direction: column; gap: 1px; align-items: stretch;
}
.yc-mark {
  height: 3px; background: var(--cal-c, var(--accent)); border-radius: 0;
  margin: 0 -1px;
}
.yc-mark.start { border-top-left-radius: 3px; border-bottom-left-radius: 3px; margin-left: 2px; }
.yc-mark.end { border-top-right-radius: 3px; border-bottom-right-radius: 3px; margin-right: 2px; }
.yc-mark.done { opacity: .42; }
/* Задач на дате больше, чем полосок влезло, — точка в углу; список в плашке. */
.yc-day.yc-more::after {
  content: ''; position: absolute; top: 3px; right: 3px;
  width: 3px; height: 3px; border-radius: 50%; background: var(--muted);
}

/* ── поиск по календарю (Q93) ───────────────────────────
   Строка поиска из шапки ищет по названиям и заметкам записей, а найденное
   подсвечивается прямо в сетке: клетка обводится, полоски чужих записей
   гаснут. Ничего не прячем — сетка остаётся картой года, а не выборкой. */
.yc-day.found {
  outline: 2px solid var(--accent); outline-offset: -2px;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.yc-day.found .yc-num { font-weight: 700; }
.yc-mark.faded { opacity: .18; }
.yc-mark.found { height: 4px; }
.yc-chip.faded { opacity: .38; }
.yc-chip.found { border-color: var(--cal-c, var(--accent)); box-shadow: 0 0 0 1px var(--cal-c, var(--accent)); }
/* Что нашлось — полосой над сеткой. Найденное в другом году отдельной строкой:
   сетка календарная (Q92), и туда переводит стрелка ›. */
.cal-found { margin: 10px 0 4px; }
.cal-found-other {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 6px; font-size: 12.5px; color: var(--muted);
}

/* Полоса «какая задача каким цветом» под сеткой. */
.yc-legend { margin-top: 18px; }
.yc-legend-strip { display: flex; flex-wrap: wrap; gap: 6px; }
.yc-chip {
  display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%;
  padding: 5px 10px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--ink); font: inherit; font-size: 12.5px;
  cursor: pointer;
}
.yc-chip:hover { border-color: var(--cal-c, var(--accent)); background: var(--panel-2); }
.yc-chip.done .yc-chip-title { text-decoration: line-through; color: var(--muted); }
.yc-chip-dot {
  width: 9px; height: 9px; border-radius: 3px; flex: 0 0 auto;
  background: var(--cal-c, var(--accent));
}
.yc-chip-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 26ch; }
.yc-chip-when { color: var(--muted); font-size: 11.5px; white-space: nowrap; }

/* Плашка при наведении на дату. Своя, а не `title`: появляется сразу,
 * красит задачи их цветом и умещает несколько в компактную колонку. */
.yc-tip {
  position: fixed; z-index: 80; pointer-events: none;
  width: max-content; max-width: min(340px, calc(100vw - 16px));
  padding: 8px 10px; border-radius: 10px;
  background: var(--panel); border: 1px solid var(--line);
  box-shadow: 0 10px 28px var(--shadow-soft);
  font-size: 12.5px; line-height: 1.35;
}
.yc-tip-day { font-weight: 700; margin-bottom: 5px; }
.yc-tip-row { display: flex; align-items: flex-start; gap: 6px; margin-top: 5px; min-width: 0; }
.yc-tip-row .yc-chip-dot { margin-top: 4px; }
.yc-tip-body { flex: 1 1 auto; min-width: 0; }
.yc-tip-row.done .yc-tip-title { text-decoration: line-through; color: var(--muted); }
/* Длинное название — в две строки, дальше многоточие: плашка должна отвечать,
 * что за задача, и при этом не разрастаться на пол-экрана. */
.yc-tip-title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}
.yc-tip-meta { font-size: 11px; color: var(--muted); }
.yc-tip-meta.overdue { color: var(--ink-rose); font-weight: 600; }
.yc-tip-meta.soon { color: var(--ink-yellow); font-weight: 600; }
.yc-tip-more { margin-top: 4px; color: var(--muted); font-size: 11.5px; }

/* Выбор цвета в панели записи. */
.yc-picker { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 4px; }
.yc-swatch {
  width: 24px; height: 24px; border-radius: 7px; padding: 0; cursor: pointer;
  border: 1px solid var(--line); background: var(--cal-c, var(--panel-2));
}
.yc-swatch.auto {
  width: auto; padding: 0 10px; font: inherit; font-size: 12px; color: var(--muted);
  background: var(--panel-2);
}
.yc-swatch.on { outline: 2px solid var(--ink); outline-offset: 1px; }
.yc-swatch:hover { border-color: var(--ink); }
/* Кнопка статуса идёт сразу за кружками и без подзаголовка — отступ отделяет
   её от блока «Цвет». */
.yc-picker + .btn { margin-top: 12px; }

/* Панель дня / записи */
.cal-form, .cal-dates, .cal-add-check { display: flex; flex-direction: column; gap: 8px; margin: 6px 0; }
.cal-dates { flex-direction: row; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.cal-input {
  padding: 7px 9px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--ink); font-size: 13px; width: 100%;
}
.cal-label { display: flex; flex-direction: column; gap: 3px; font-size: 12px; flex: 1; min-width: 130px; }
.cal-period { display: flex; align-items: center; gap: 5px; font-size: 13px; }
.cal-endwrap { display: flex; align-items: center; gap: 6px; }
.cal-notes {
  width: 100%; min-height: 70px; padding: 8px 9px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--panel); color: var(--ink); font-size: 13px; resize: vertical;
}
.cal-banner { padding: 9px 12px; border-radius: 8px; margin-bottom: 12px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 13px; }
.cal-banner.overdue { background: color-mix(in srgb, var(--danger) 14%, transparent); }
.cal-banner.soon { background: color-mix(in srgb, var(--warn) 16%, transparent); }
.cal-day-entry {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 8px 10px; margin-top: 5px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel-2);
  cursor: pointer; font-size: 13px; color: var(--ink);
}
.cal-day-entry-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-day-entry.done .cal-day-entry-title { text-decoration: line-through; color: var(--muted); }
.cal-checklist { display: flex; flex-direction: column; gap: 5px; margin: 6px 0; }
.cal-check { display: flex; align-items: center; gap: 8px; }
.cal-check.done .cal-check-text { text-decoration: line-through; color: var(--muted); }
.cal-check-text { flex: 1; border: 1px solid transparent; background: transparent; color: var(--ink); font-size: 13px; padding: 4px 6px; border-radius: 6px; }
.cal-check-text:focus { border-color: var(--line); background: var(--panel); }
.cal-add-check { flex-direction: row; align-items: center; }
.cal-delete { margin-top: 14px; color: var(--ink-rose); }

/* Узкое окно: колонок меньше, рядов больше. Влезть по ширине важнее, чем
 * удержать ровно три ряда, — прокрутки вбок не должно быть никогда. */
@media (max-width: 1240px) {
  .yc-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
}
@media (max-width: 900px) {
  .yc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
}
@media (max-width: 560px) {
  .yc-grid { grid-template-columns: minmax(0, 1fr); }
  .yc-chip-title { max-width: 18ch; }
}

/* toasts */
/* Колонка тостов сама клики не ловит — только сами тосты: просвет между ними и
   место под закрытым тостом остаются кнопками раздела, а не пустой рамкой
   поверх них (29.09.2026). */
.toasts {
  position: fixed; top: 62px; right: 14px; z-index: 60;
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
}
.toasts > .toast { pointer-events: auto; }
.toast {
  background: var(--panel); border: 1px solid var(--line); border-left: 3px solid var(--accent);
  padding: 9px 13px; border-radius: 8px; max-width: 400px; font-size: 13px;
  box-shadow: 0 8px 24px var(--shadow-soft);
}
.toast.error { border-left-color: var(--danger); }
.toast.ok { border-left-color: var(--ok); }

/* ── разделы левого меню ────────────────────────────────── */

.section-view { flex: 1; overflow: auto; padding: 18px 20px 28px; }
/* Обёртка сборки раздела (`renderSection`) не должна участвовать в раскладке:
   правила вроде `.section-view:has(.card-split)` рассчитывают на то, что лист
   раздела — прямой ребёнок области. `display: contents` отдаёт детей наверх. */
.section-frame { display: contents; }
/* Шапка раздела — одна строка кнопок и фильтров; названия раздела в ней нет. */
.section-head { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }
.section-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.section-list { display: flex; flex-direction: column; gap: 10px; }
.empty-state {
  padding: 24px; border: 1px dashed var(--line); border-radius: var(--radius);
  color: var(--muted); text-align: center;
}
/* Доска — ряд колонок; пустой результат поиска в него не встаёт колонкой,
   поэтому на время он занимает всю ширину. */
.board.board-empty { display: block; overflow-y: auto; }
.board.board-empty .empty-state { max-width: 520px; margin: 40px auto; }
.board.board-empty .empty-state .btn { margin-top: 12px; }

/* ── сворачиваемые блоки карточки ───────────────────────── */

.fold-head { display: flex; align-items: center; gap: 10px; }
.fold-toggle {
  display: flex; align-items: center; gap: 7px;
  border: none; background: transparent; color: var(--muted); cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .5px; padding: 0;
}
.fold-toggle::before { content: '▸'; font-size: 11px; }
.fold.open .fold-toggle::before { content: '▾'; }
.fold-head .btn { margin-left: auto; text-transform: none; letter-spacing: 0; }
.fold-body textarea { width: 100%; font-size: 13px; }

/* вкладку производства можно перетащить мышью */
.tab.dragging { opacity: .5; }

/* ── карточки разделов ──────────────────────────────────── */

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}
.entity-card {
  display: flex; flex-direction: column; gap: 9px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px;
}
.entity-card:hover { border-color: var(--accent); }
.entity-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.entity-card-title { font-size: 13px; color: var(--muted); }
.entity-card-meta { display: flex; gap: 6px; flex-wrap: wrap; font-size: 11px; }
.entity-card-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-top: auto; }
.entity-card-actions select { padding: 4px 8px; font-size: 12px; }
.section-subhead {
  margin: 18px 0 10px; font-size: 12px; text-transform: uppercase;
  letter-spacing: .5px; color: var(--muted);
}
.modal-body > .section-subhead:first-child { margin-top: 0; }
.chip-row { display: flex; gap: 6px; flex-wrap: wrap; }

/* ── формы разделов ─────────────────────────────────────── */

.form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
.form-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.form-field.wide { grid-column: 1 / -1; }
.form-label { font-size: 11px; color: var(--muted); }
.form-field input, .form-field select, .form-field textarea { width: 100%; font-size: 13px; }

/* ── ручная правка полей ────────────────────────────────── */

/* Кто вписал значение. Ручное агент не перезаписывает, и видно это должно быть
   ДО правки: иначе непонятно, почему поле не изменилось после разбора. */
.field-source {
  align-self: flex-start;
  font-size: 10.5px;
  line-height: 1.4;
  padding: 1px 5px;
  border-radius: 4px;
  border: 1px solid var(--line);
  color: var(--muted);
  cursor: help;
}
.field-source.manual { color: var(--ink); border-color: var(--ok); background: var(--gossamer-green); }
.field-source.agent { color: var(--muted); background: var(--panel); }
.form-hint { font-size: 10.5px; color: var(--muted); line-height: 1.35; }

/* Мелкая кнопка правки внутри строки таблицы или анкеты: она не должна
   перетягивать на себя взгляд с самого значения. */
.btn-xs { padding: 2px 6px; font-size: 11px; line-height: 1.3; }
.fact-row:not(:hover) .fact-edit,
.quote-table tr:not(:hover) .quote-edit .btn,
.items-table tr:not(:hover) .item-edit .btn,
.parcel-item:not(:hover) .parcel-item-edit { opacity: 0; }
.fact-edit, .quote-edit .btn, .item-edit .btn, .parcel-item-edit { transition: opacity .12s; }
/* Пока правка открыта, кнопку видно и без курсора — иначе с клавиатуры до неё
   не добраться: она исчезает ровно тогда, когда получает фокус. */
.fact-edit:focus-visible, .quote-edit .btn:focus-visible,
.item-edit .btn:focus-visible, .parcel-item-edit:focus-visible { opacity: 1; }
.item-edit, .quote-edit { text-align: right; }
.checklist-item:not(:hover) .checklist-edit { opacity: 0; }
.checklist-edit { margin-left: auto; flex: 0 0 auto; transition: opacity .12s; }
.checklist-edit:focus-visible { opacity: 1; }
.parcel-item-edit { margin-left: auto; flex: 0 0 auto; }

/* ── спор о поле в «Ревью» ──────────────────────────────── */

.review-conflict {
  margin: 0 0 9px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
}
.conflict-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
  margin: 6px 0;
}
.conflict-side { padding: 6px 8px; border-radius: 5px; border: 1px solid var(--line); }
.conflict-side.ours { border-color: var(--ok); }
.conflict-label { font-size: 10.5px; color: var(--muted); margin-bottom: 3px; }
.conflict-value {
  font-size: 14px;
  color: var(--text);
  word-break: break-word;
  white-space: pre-wrap;
}
.conflict-source { font-size: 11.5px; color: var(--muted); }
.conflict-source-head { font-weight: 600; }
.conflict-source-text { margin-top: 3px; white-space: pre-wrap; word-break: break-word; }

/* ── фотографии ─────────────────────────────────────────── */

.photo-strip { display: flex; gap: 8px; flex-wrap: wrap; }
.photo-item {
  position: relative; width: 84px; height: 84px; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--line); background: var(--panel-2); flex: 0 0 auto;
}
.photo-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-remove, .photo-save {
  position: absolute; top: 3px;
  width: 20px; height: 20px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  font-size: 11px; line-height: 1; padding: 0;
  opacity: 0; transition: opacity .15s ease;
}
.photo-remove { right: 3px; }
/* Скачивание — слева, чтобы не путать с удалением справа. */
.photo-save { left: 3px; font-size: 13px; }
.photo-item:hover .photo-remove,
.photo-item:hover .photo-save,
.photo-remove:focus-visible, .photo-save:focus-visible { opacity: 1; }
.photo-save:hover { background: var(--accent); }
.photo-remove:hover { background: var(--danger); }
.photo-drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  width: 84px; height: 84px; border-radius: 8px; cursor: pointer; text-align: center;
  border: 1px dashed var(--line); background: var(--panel-2);
  color: var(--muted); font-size: 10px; line-height: 1.25; padding: 6px;
}
.photo-drop:hover, .photo-drop.over { border-color: var(--accent); background: var(--accent-dim); }
/* Окно «Импорт файлов»: та же зона, но на всю ширину — две строки текста
   в квадрат 84×84 не помещались. */
#fi-drop { width: 100%; height: auto; padding: 18px 14px; font-size: 12px; box-sizing: border-box; }
/* Предложение вернуться к незаконченному импорту: одна строка над зоной
   перетаскивания (аудит 22.09.2026). */
.fi-pending {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 10px; border-radius: 8px;
  background: var(--panel); font-size: 12px;
}

/* ── звёзды и показатели производства ───────────────────── */

.stars { display: flex; gap: 2px; }
.star {
  border: none; background: transparent; cursor: pointer; padding: 0 1px;
  font-size: 18px; line-height: 1; color: var(--star-off);
}
.star.on { color: var(--c-d4a514-t); }
.metric-list { display: flex; flex-direction: column; gap: 8px; }
.metric-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  border: 1px solid var(--line); border-radius: 9px; padding: 9px 11px; background: var(--panel-2);
}
.metric-label { flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: 4px; }
/* Поле ввода несёт собственную ширину по умолчанию (size=20) и в узкой
   колонке распирало строку; ширину задаёт колонка. */
.metric-note { padding: 4px 8px; font-size: 12px; width: 100%; min-width: 0; box-sizing: border-box; }
.factory-rating { display: flex; align-items: baseline; gap: 8px; }
.factory-rating.big { margin-bottom: 6px; }
.rating-value { font-size: 22px; font-weight: 700; }
.factory-card { cursor: pointer; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
.stat-box { border: 1px solid var(--line); border-radius: 9px; padding: 10px; background: var(--panel-2); position: relative; }
/* «ⓘ» плитки стоит в её углу, а не в конце подписи: подпись бывает в две
   строки, и значок уезжал на вторую один, кружком непонятно от чего. */
.stat-box .info-dot { position: absolute; top: 8px; right: 8px; margin: 0; }
/* ЧИСЛА В РЯДУ ПЛИТОК СТОЯТ НА ОДНОЙ ЛИНИИ (аудит 24.09.2026). Подпись
   бывает и в одну строку («Готов → отгружен»), и в две («Размещение → начало
   пошива»), и от этого большое число первой плитки опускалось на 16 px ниже
   остальных трёх: ряд показателей в досье фабрики читался как сломанный.
   Подпись держит место под две строки всегда — пусто там или нет. */
.stat-label {
  font-size: 11px; color: var(--muted); padding-right: 16px;
  min-height: 2.9em;
}
.stat-value { font-size: 20px; font-weight: 600; margin-top: 2px; }

/* ── разработки ─────────────────────────────────────────── */

.dev-feed { display: flex; flex-direction: column; gap: 12px; }
.dev-card {
  display: flex; flex-direction: column; gap: 14px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px;
}
.dev-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
  padding-bottom: 12px; border-bottom: 1px solid var(--line);
}
.dev-head-main { min-width: 0; }
.dev-head-side { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
.dev-name { font-size: 17px; letter-spacing: .2px; }
.dev-suppliers { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; font-size: 11px; }
.dev-head select { padding: 5px 8px; font-size: 12px; }

/* Подписанные блоки: карточка читается сверху вниз, а не сплошной лентой. */
.dev-block { display: flex; flex-direction: column; gap: 7px; }
.dev-block-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: .6px; color: var(--muted);
}

/* Готовность по чек-листам одним кольцом — видно, не читая список. */
.dev-progress { display: flex; align-items: center; gap: 8px; font-size: 11px; }
.dev-progress-ring {
  --percent: 0;
  width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center;
  background: conic-gradient(var(--accent) calc(var(--percent) * 1%), var(--panel-2) 0);
  font-size: 11px; font-weight: 700;
}
.dev-progress-ring span {
  width: 32px; height: 32px; border-radius: 50%; background: var(--panel);
  display: grid; place-items: center;
}
.dev-progress.complete .dev-progress-ring { background: conic-gradient(var(--ink) 100%, var(--ink) 0); }
.dev-progress.complete .dev-progress-ring span { background: var(--gossamer-green); }

/* Этапы — вертикальная линия с точками: видно, что это хронология. */
.dev-stages {
  list-style: none; margin: 0; padding: 0 0 0 16px;
  display: flex; flex-direction: column; gap: 6px;
  border-left: 2px solid var(--line);
}
.dev-stages li {
  position: relative; display: flex; gap: 10px; align-items: baseline; font-size: 13px;
}
.dev-stages li::before {
  content: ''; position: absolute; left: -21px; top: 6px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--panel); border: 2px solid var(--accent-dim);
}
.dev-stages li:last-child::before { border-color: var(--accent); background: var(--accent); }
.dev-stages li.muted::before { display: none; }
.dev-stage-date { color: var(--muted); font-size: 12px; flex: 0 0 92px; }
/* Номер прошлой версии образца в объединённой ленте: своя работа и работа
   предыдущего номера стоят в одной ленте, и путать их нельзя. */
.dev-stage-from { flex: 0 0 auto; font-size: 11px; }
.dev-stage-text { flex: 1; }
.dev-stage-remove {
  border: none; background: transparent; color: var(--muted); cursor: pointer; font-size: 11px;
  opacity: 0; transition: opacity .15s ease;
}
.dev-stages li:hover .dev-stage-remove, .dev-stage-remove:focus-visible { opacity: 1; }
.dev-supplier-row {
  display: flex; align-items: center; gap: 10px;
  border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; background: var(--panel-2);
}
.dev-supplier-row .grow { flex: 1; }
.dev-price { width: 110px; padding: 4px 8px; font-size: 12px; }

/* ── перемещения и наличие ──────────────────────────────── */

.track-input { width: 140px; padding: 4px 8px; font-size: 12px; }
/* Плитка предмета целиком не кликается — на ней свои кнопки, и рука-курсор
   обещала бы то, чего нет. */
.stock-item .entity-card-actions .muted { font-size: 11.5px; }

/* ── заголовок рабочей области ──────────────────────────── */

.topbar-title { min-width: 120px; flex: 0 0 auto; }
.topbar-brand { font-weight: 700; font-size: 15px; line-height: 1.2; }

.side-home {
  margin-top: auto;
  padding: 9px 10px;
  text-align: left;
  font: inherit;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.side-home:hover { background: var(--panel-2); color: var(--text); }

/* ── причина простоя и ОТК на карточке ──────────────────── */

.card-reason {
  display: flex; flex-wrap: wrap; gap: 5px; align-items: baseline;
  margin-top: 7px; padding-top: 6px;
  border-top: 1px dashed var(--line);
  font-size: 11px; line-height: 1.3;
}
.card-reason-label {
  text-transform: uppercase; letter-spacing: .4px; font-size: 10px;
  color: var(--ink); background: var(--gossamer-green);
  border-radius: 4px; padding: 1px 5px;
}
.card-reason.unknown .card-reason-label { background: var(--warn); }
.card-reason.bad .card-reason-label { background: var(--warn); }
.card-reason-date { color: var(--muted); }

.col-action {
  margin-left: 6px;
  border: 1px solid var(--line);
  background: var(--warn);
  color: var(--ink);
  border-radius: 6px;
  padding: 2px 7px;
  font: inherit;
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
}
.col-action:hover { border-color: var(--accent); }
.col-head .count { margin-left: auto; }

/* ── модалка скриптов ───────────────────────────────────── */

.script-body { display: grid; grid-template-columns: minmax(230px, 300px) 1fr; gap: 16px; }
.script-picker { display: flex; flex-direction: column; gap: 14px; }
.script-group { display: flex; flex-direction: column; gap: 6px; }
.script-group-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted);
}
.script-choice {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 8px;
  text-align: left;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  cursor: pointer;
}
.script-choice:hover { border-color: var(--accent); }
.script-choice.active { background: var(--gossamer-green); border-color: var(--accent); }
.script-choice-name { font-weight: 600; font-size: 13px; }
.script-choice-hint { grid-column: 1; color: var(--muted); font-size: 11px; line-height: 1.3; }
.script-choice-count {
  grid-row: 1; grid-column: 2; align-self: center;
  min-width: 20px; text-align: center;
  background: var(--paper); border: 1px solid var(--line); border-radius: 9px;
  padding: 0 6px; font-size: 11px; color: var(--muted);
}
.script-choice-count:empty { display: none; }
.script-preview { min-width: 0; display: flex; flex-direction: column; }
.script-output { display: flex; flex-direction: column; gap: 8px; min-height: 0; }
.script-output-head { display: flex; align-items: center; gap: 10px; }
.script-output-name { font-weight: 600; font-size: 13px; margin-right: auto; }
.script-empty {
  display: grid; place-items: center; min-height: 180px; padding: 20px; text-align: center;
  border: 1px dashed var(--line); border-radius: var(--radius); color: var(--muted); font-size: 13px;
}
@media (max-width: 720px) {
  .script-body { grid-template-columns: 1fr; }
}

/* «Новые образцы»: количество кнопками, ниже — что именно будет заведено. */
.nsm-counts { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.nsm-plan { margin-bottom: 14px; }
.nsm-plan:empty, .fi-plan:empty { display: none; }
.nsm-codes {
  font: 600 15px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .3px;
  padding: 9px 11px; margin-bottom: 6px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--panel-2);
}

/* Предупреждение разбора: не отказ, но и не рядовая подсказка. Пастель
   `--warn` во всей доске работает фоном, а не цветом текста. */
.hint.warn-text {
  background: var(--warn); color: var(--ink);
  border-radius: var(--radius-sm); padding: 5px 8px; margin-top: 6px;
}

/* Согласие завести новую фабрику из бланка (Q86): галочка, без которой импорт
   нового заказа не пойдёт. Стоит сразу под предупреждением и читается как его
   продолжение, а не как отдельная настройка. */
/* Бренд бланка — выбором, а не догадкой по доске (решение Q119). */
.imp-brand {
  display: flex; align-items: center; gap: 8px; margin-top: 8px; font-size: 13px;
}
.imp-brand select { flex: 1; min-width: 0; }

.imp-new-supplier {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px; padding: 6px 8px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-size: 13px; color: var(--ink);
}
.imp-new-supplier input { margin: 0; }

/* ── «Отправить на ОТК»: выбор заказов ──────────────────── */

/* Заказов в производстве бывает полтора десятка — список прокручивается,
   а окно остаётся одного размера. */
.qcs-orders {
  display: flex; flex-direction: column; gap: 2px;
  max-height: 210px; overflow-y: auto;
  margin-bottom: 10px;
}
.qcs-orders:empty { display: none; }
.qcs-order {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 6px; border-radius: var(--radius-sm); cursor: pointer;
}
.qcs-order:hover { background: var(--panel-2); }
.qcs-code { font: 600 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }

/* «Отправить на примерку»: список образцов — те же строки, что у заказов на
   ОТК (`.qcs-order`), а письмо правят на месте, поэтому оно в textarea, а не
   в <pre>: в поле ввода кнопка «Перевести» не встаёт (TEXTAREA — в SKIP_TAGS
   у NadosTranslate), и в буфер уходит ровно то, что видно. */
.script-edit {
  width: 100%; min-height: 170px; margin: 0 0 8px;
  white-space: pre-wrap; overflow-wrap: anywhere;
  font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ── тост с действием ───────────────────────────────────── */

.toast { display: flex; align-items: center; gap: 10px; }
.toast-text { flex: 1; min-width: 0; }
.toast .btn { flex: 0 0 auto; }
/* Крестик есть у каждого тоста, и попасть в него должно быть легко: прежние
   12 px без поля мимо промахивались. */
.toast-close {
  flex: 0 0 auto; align-self: flex-start;
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; margin: -3px -6px -3px 0; padding: 0;
  border: none; border-radius: 6px; background: transparent; color: var(--muted);
  font: inherit; font-size: 13px; line-height: 1; cursor: pointer;
}
.toast-close:hover { background: var(--panel-2); color: var(--text); }

/* Заметки разработки: в ленте свёрнуты до нескольких строк, по клику целиком. */
.dev-notes {
  white-space: pre-wrap;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
  cursor: pointer;
  overflow-wrap: anywhere;
}
.dev-notes.clamped {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ── даты: клик открывает мини-календарь ────────────────── */

.date-pill {
  cursor: pointer;
  border-bottom: 1px dashed var(--line);
  padding-bottom: 1px;
}
.date-pill:hover { border-bottom-color: var(--accent); background: var(--panel-2); }

.calendar-pop {
  position: fixed;
  z-index: 70;
  width: 244px;
  padding: 10px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px var(--shadow-soft);
}
.cal-head { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.cal-title { flex: 1; text-align: center; font-weight: 600; font-size: 13px; }
.cal-nav {
  width: 24px; height: 24px; border-radius: 6px; cursor: pointer;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--text);
  font: inherit; line-height: 1; padding: 0;
}
.cal-nav:hover { border-color: var(--accent); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-weekday {
  text-align: center; font-size: 10px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .3px; padding-bottom: 2px;
}
.cal-day {
  display: grid; place-items: center;
  height: 27px; border-radius: 6px; font-size: 12px;
  border: 1px solid transparent;
}
.cal-day.empty { visibility: hidden; }
.cal-day.today { border-color: var(--ink); font-weight: 700; }
.cal-day.picked { background: var(--accent); color: var(--on-accent-strong); font-weight: 700; }
.cal-legend {
  display: flex; flex-direction: column; gap: 3px;
  margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--line);
  font-size: 11px; color: var(--muted);
}
.cal-legend-item { display: flex; align-items: center; gap: 6px; }
.cal-legend-item::before {
  content: ''; width: 11px; height: 11px; border-radius: 3px; flex: 0 0 auto;
  border: 1px solid var(--line);
}
.cal-legend-item.today::before { border-color: var(--ink); border-width: 2px; }

/* ── фотография крупно ──────────────────────────────────── */

.photo-item img, .lightbox-source { cursor: zoom-in; }
.lightbox {
  position: fixed; inset: 0; z-index: 80;
  display: grid; place-items: center;
  background: var(--c-rgba-20-12-16-82-s);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  padding: 40px;
}
.lightbox img {
  max-width: min(1100px, 92vw);
  max-height: 86vh;
  border-radius: var(--radius);
  background: var(--paper);
  object-fit: contain;
}
.lightbox-close, .lightbox-nav {
  position: absolute;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink);
  border-radius: 50%;
  cursor: pointer;
  font: inherit;
  line-height: 1;
  padding: 0;
}
.lightbox-close { top: 16px; right: 18px; width: 34px; height: 34px; font-size: 15px; }
/* «Сохранить в «Загрузки»» — рядом с крестиком, тем же кружком (29.09.2026).
   Тот же класс стоит и у снимка крупно из «Ревью» (`openImageZoom`). */
.lightbox-save {
  position: absolute; top: 16px; right: 60px; width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--paper); color: var(--ink);
  font: inherit; font-size: 16px; line-height: 1; cursor: pointer;
}
.image-zoom .lightbox-save { position: fixed; right: 18px; z-index: 1; }

/* Меню правой кнопки у снимка CRM: «Скачать в «Загрузки»», «Скопировать
   изображение». Живёт в `body` и стоит выше лайтбокса и плашки «ⓘ». */
.photo-menu {
  position: fixed; z-index: 95; min-width: 210px; padding: 4px;
  display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: 0 10px 28px var(--shadow-soft);
}
.photo-menu button {
  border: none; background: transparent; color: var(--text);
  font: inherit; font-size: 13px; text-align: left;
  padding: 7px 10px; border-radius: 6px; cursor: pointer;
}
.photo-menu button:hover, .photo-menu button:focus-visible { background: var(--panel-2); outline: none; }
.lightbox-nav { top: 50%; transform: translateY(-50%); width: 40px; height: 40px; font-size: 22px; }
.lightbox-nav.prev { left: 18px; }
.lightbox-nav.next { right: 18px; }
.lightbox-count {
  position: absolute; bottom: 18px; left: 50%; transform: translateX(-50%);
  color: var(--on-accent); font-size: 12px;
}

/* ── разработки: вид как у исходной таблицы ─────────────── */

/* Полоса вкладок — дно раздела, а не часть ленты. Прокручивается сам лист,
   поэтому заголовок сверху и ярлычки снизу остаются на месте, как в таблице. */
.section-view:has(.dev-tabs) {
  display: flex; flex-direction: column; padding-bottom: 0; overflow: hidden;
}
.dev-sheet {
  flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden;
  margin: 0 -20px; padding: 0 20px 18px;
  scrollbar-width: thin;
}

.dev-tabs {
  flex: 0 0 auto;
  display: flex;
  gap: 3px;
  align-items: flex-end;
  margin: 0 -20px;
  padding: 6px 20px;
  background: var(--panel);
  border-top: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: thin;
}
.dev-tab {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 6px;
  padding: 6px 12px 5px;
  border: 1px solid var(--line);
  border-bottom: 3px solid var(--line);
  border-radius: 0 0 8px 8px;
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
}
.dev-tab:hover { background: var(--bg); }
.dev-tab.colored { border-bottom-color: var(--tab-color); }
.dev-tab.active {
  font-weight: 700;
  background: var(--bg);
  border-bottom-width: 4px;
}
.dev-tab.colored.active { background: color-mix(in srgb, var(--tab-color) 18%, var(--paper)); }
.dev-tab-count {
  background: var(--paper); border: 1px solid var(--line); border-radius: 9px;
  padding: 0 5px; font-size: 10px; color: var(--muted); font-weight: 500;
}
.dev-tab-sep { width: 1px; align-self: stretch; background: var(--line); margin: 4px 5px; }

.dev-card.colored { border-left: 4px solid var(--tab-color); }
.dev-table-wrap { overflow-x: auto; }
.dev-table td { vertical-align: top; }
.dev-table .dev-table-photo .photo-item { width: 62px; height: 62px; }
.dev-table-notes { max-width: 420px; cursor: pointer; }
.dev-table .dev-notes { font-size: 11px; }
.dev-table .dev-notes.clamped { -webkit-line-clamp: 3; line-clamp: 3; }

/* ══════════════════════════════════════════════════════════════════════════
   Оформление раздела «Заказы» по утверждённому макету.
   Цвет держат плитки этапов сверху; колонки под ними бледные, чтобы не
   спорить с плитками. Карточка заказа открывается боковой панелью справа,
   меню сворачивается в полосу иконок.
   Правила намеренно лежат в конце файла: они перекрывают прежние.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── меню: иконки и сворачивание ────────────────────────── */

.sidebar { gap: 16px; padding: 14px 12px 12px; }
.side-head { gap: 11px; padding: 0 3px; }
.logo { width: 36px; height: 36px; border-radius: 11px; }
.brand-name { font-weight: 700; }

.side-item { gap: 11px; border-radius: 10px; padding: 9px 11px; }
.side-item svg { flex: 0 0 auto; opacity: .85; }
.side-item:hover { background: var(--hover); }
.side-item.active {
  background: var(--tile-violet-bg);
  border-color: transparent;
  color: var(--c-46405a-t);
  font-weight: 700;
}
.side-item .badge { background: var(--pastel-yellow); color: var(--ink-yellow); font-weight: 700; }

.side-foot { margin-top: auto; display: flex; flex-direction: column; gap: 4px; }
.side-toggle {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px; border-radius: 10px;
  border: 1px solid var(--line); background: transparent;
  color: var(--muted); font: inherit; font-size: 12.5px; cursor: pointer;
}
.side-toggle:hover { background: var(--hover); color: var(--text); }
.side-home {
  margin-top: 0;
  display: flex; align-items: center; gap: 11px;
  border: none; border-radius: 10px; padding: 9px 11px; font-size: 12.5px;
}
.side-home:hover { background: var(--hover); }
/* Свёрнутое меню: остаются иконки, подписи всплывают подсказкой браузера. */
.shell.nav-collapsed .sidebar {
  flex-basis: 62px;
  align-items: center;
  padding: 14px 0 12px;
}
.shell.nav-collapsed .side-head { justify-content: center; }
.shell.nav-collapsed .side-head-text { display: none; }
.shell.nav-collapsed .side-nav { align-items: center; }
.shell.nav-collapsed .side-item,
.shell.nav-collapsed .side-toggle,
.shell.nav-collapsed .side-home {
  position: relative;
  width: 42px; height: 38px;
  justify-content: center;
  padding: 0; gap: 0;
}
.shell.nav-collapsed .side-item .label,
.shell.nav-collapsed .side-toggle .label,
.shell.nav-collapsed .side-home .label { display: none; }
.shell.nav-collapsed .side-item .badge {
  position: absolute; top: 1px; right: 3px;
  min-width: 15px; height: 15px; padding: 0 4px;
  margin-left: 0; font-size: 10px;
}
.shell.nav-collapsed .side-foot { align-items: center; }

/* ── шапка и вкладки производств ────────────────────────── */

.topbar { padding: 12px 20px; gap: 12px; }
.topbar-brand { font-family: var(--font-display); font-weight: 400; font-size: 20px; }
.filters input {
  border-radius: 999px;
  background: var(--bg);
  padding: 8px 14px;
}

.btn { border-radius: 999px; background: var(--paper); border-color: var(--line); }
.btn:hover { background: var(--hover); border-color: var(--line); }
.btn-ghost { background: var(--paper); }
.btn-primary {
  background: var(--accent); border-color: var(--accent);
  color: var(--on-accent-strong); font-weight: 700;
}
.btn-primary:hover { background: var(--c-a3c6b0-s); border-color: var(--c-a3c6b0-s); color: var(--on-accent-strong); }
.btn-danger { background: var(--danger); border-color: var(--danger); color: var(--ink-rose); }

.viewswitch { background: var(--c-f4f1e8-s); border: none; border-radius: 999px; }
.vbtn { border-radius: 999px; }
.vbtn.active { background: var(--paper); box-shadow: 0 1px 2px var(--c-rgba-60-50-35-08-s); }

.tabs { padding: 9px 20px; gap: 5px; }
.tab {
  border: none; border-radius: 999px;
  padding: 6px 12px; margin-bottom: 0;
}
.tab:hover { background: var(--hover); }
.tab.active { background: var(--c-eef2e9-s); color: var(--text); font-weight: 700; }
.tab .n, .tab.active .n { background: transparent; color: inherit; opacity: .68; padding: 0; }
.tab.script-tab { background: var(--accent-dim); color: var(--ink-green); }
.tab-samples .n { background: transparent; }

/* Шестерёнка «Бренды и фабрики»: под значком без подписи место квадратное,
   а не пилюля по ширине слова. Правило стоит ЗДЕСЬ, после общего `.tab`
   из этого же блока: тот переопределяет отступы, и выше его было бы не
   перебить. */
.tab.tab-gear, .btn.btn-gear { padding: 6px 8px; }
.tab.tab-gear svg, .btn.btn-gear svg { display: block; }

/* ── плитки этапов ──────────────────────────────────────── */

/* Плитка живёт в своей колонке первой строкой — тем и держится ровно по её
   ширине, и едет вместе с ней при горизонтальной прокрутке доски. */
.fun-tile {
  flex: 0 0 auto;
  display: flex; flex-direction: column;
  padding: 13px 14px 12px; border-radius: 14px 14px 0 0;
  background: var(--panel-3); color: var(--text-2);
}
.fun-head { display: flex; align-items: baseline; gap: 8px; }
.fun-count { font-family: var(--font-display); font-size: 27px; line-height: 1; }
.fun-name { font-size: 12px; font-weight: 700; }
.fun-meter { display: flex; align-items: center; gap: 7px; margin: 9px 0; }
.fun-bar { flex: 1; height: 5px; border-radius: 3px; background: var(--c-rgba-255-255-255-7-s); overflow: hidden; }
.fun-bar i { display: block; height: 100%; border-radius: 3px; background: currentColor; opacity: .5; }
.fun-share { font-size: 10.5px; opacity: .78; white-space: nowrap; }
.fun-risk {
  margin-top: 0; padding: 5px 9px; border-radius: 8px;
  font-size: 10.5px; background: var(--c-rgba-255-255-255-7-s);
}
.fun-risk.bad { background: var(--marys-rose); color: var(--ink-rose); font-weight: 700; }

.fun-tile[data-status="new"] { background: var(--tile-rose-bg-3); color: var(--tile-rose-text-2); }
.fun-tile[data-status="production"] { background: var(--tile-yellow-bg); color: var(--tile-yellow-text); }
.fun-tile[data-status="qc_sent"] { background: var(--tile-blush-bg); color: var(--tile-blush-text); }
.fun-tile[data-status="qc"] { background: var(--tile-violet-bg); color: var(--tile-violet-text); }
.fun-tile[data-status="shipped"] { background: var(--tile-green-bg); color: var(--tile-green-text-2); }

/* ── колонки доски ──────────────────────────────────────── */

.board { gap: 11px; padding: 14px 18px 16px; }

/* Колонка начинается своей плиткой: у обеих один радиус, поэтому верхние углы
   совпадают, а карточки идут прямо под плиткой. */
.col {
  flex: 0 0 236px;
  background: var(--c-f8f6ef-s);
  border: none;
  border-radius: 14px;
}
.col[data-status="new"] { background: var(--c-fdf3f4-s); }
.col[data-status="production"] { background: var(--tile-cream-bg); }
.col[data-status="qc_sent"] { background: var(--c-faf5f5-s); }
.col[data-status="qc"] { background: var(--tile-violet-bg-2); }
.col[data-status="shipped"] { background: var(--tile-green-bg-2); }
.col[data-status="done"], .col[data-status="cancelled"] { background: var(--c-f7f5ef-s); }
.col.drop { background: var(--c-eef4f0-s); outline: 2px solid var(--accent); }

/* Название этапа, счётчик и полоска риска стоят в плитке над колонкой; здесь
   шапка остаётся только ради собственного действия колонки. */
.col-head {
  padding: 10px 13px 2px;
  border-bottom: none;
}
.col-action {
  flex: 1 0 100%;
  border-radius: 999px; border: 1px solid var(--c-rgba-120-104-80-18-l);
  background: var(--c-rgba-255-255-255-72-s);
  letter-spacing: 0; text-transform: none; font-weight: 500;
}
.col-action:hover { background: var(--paper); border-color: var(--c-rgba-120-104-80-3-l); }
.col-body { padding: 10px 8px 11px; gap: 8px; }
/* Отступ сверху уже дала шапка с действием — второй раз он не нужен. */
.col-head + .col-body { padding-top: 6px; }

/* ── карточка заказа на доске ───────────────────────────── */

.card {
  background: var(--paper);
  border: none;
  border-radius: 11px;
  padding: 11px 12px;
  box-shadow: 0 1px 2px var(--c-rgba-80-66-48-06-s);
}
.card:hover { box-shadow: 0 0 0 1px var(--c-e3dbc9-s), 0 1px 3px var(--c-rgba-80-66-48-08-s); }
.card.selected { box-shadow: 0 0 0 2px var(--c-ddb0b4-s); }
/* «ОТК не пройден» — полоска слева, как у просроченного образца: карточка в
   «В производстве» лежит вперемешку с теми, что просто шьются, и бейджа внизу
   с расстояния не видно. Тень карточки повторяется, иначе класс её снял бы. */
.card.qc-failed { box-shadow: inset 3px 0 0 var(--c-b8515d-s), 0 1px 2px var(--c-rgba-80-66-48-06-s); }
.card.qc-failed:hover { box-shadow: inset 3px 0 0 var(--c-b8515d-s), 0 0 0 1px var(--c-e3dbc9-s), 0 1px 3px var(--c-rgba-80-66-48-08-s); }
.card.qc-failed.selected { box-shadow: inset 3px 0 0 var(--c-b8515d-s), 0 0 0 2px var(--c-ddb0b4-s); }
.card-code {
  font-family: var(--font-display); font-size: 15.5px; font-weight: 400;
  align-items: baseline;
}
.card-links {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 10.5px; color: var(--c-9c9282-t);
  font-family: Manrope, sans-serif;
}
.card-title { font-size: 11.5px; }
.card-meta { gap: 5px; margin-top: 8px; }
.chip {
  border: none; border-radius: 999px;
  background: var(--panel-3); color: var(--text-3);
  padding: 2px 9px; font-size: 11px;
}
.chip.warn { background: var(--c-fbf5da-s); color: var(--ink-yellow); border-color: transparent; }
.chip.danger { background: var(--tile-rose-bg-2); color: var(--ink-rose); border-color: transparent; }
.chip.ok-chip { background: var(--accent-dim); color: var(--ink-green); border-color: transparent; }
/* Плашка-кнопка: тот же вид, но по ней переходят в карточку. */
button.chip-link { font: inherit; font-size: 11px; cursor: pointer; }
button.chip-link:hover { background: var(--c-ece7d8-s); color: var(--text); }

.card-reason {
  margin-top: 8px; padding: 6px 9px; padding-top: 6px;
  border-top: none; border-radius: 8px;
  background: var(--panel-3);
  font-size: 10.5px; color: var(--text-2);
}
.card-reason.unknown { background: var(--tile-rose-bg-2); color: var(--ink-rose); }
/* Непройденная проверка красится так же, как ненайденная причина простоя:
   и то и другое — «здесь ждут действия», а не просто справка. */
.card-reason.bad { background: var(--tile-rose-bg-2); color: var(--ink-rose); }
.card-reason-label {
  background: transparent; color: inherit; padding: 0;
  font-size: 9.5px; letter-spacing: .7px; opacity: .8; font-weight: 700;
}
.card-reason.unknown .card-reason-label,
.card-reason.bad .card-reason-label { background: transparent; }
.card-reason-date { color: inherit; opacity: .75; }

.shipping-date.shipping-alert {
  border: none; border-radius: 7px;
  background: var(--tile-yellow-bg); color: var(--ink-yellow);
  padding: 2px 8px;
}
.shipping-date.shipping-overdue { background: var(--tile-rose-bg-2); color: var(--ink-rose); }

/* ── боковая панель заказа вместо всплывающего окна ─────── */

#order-modal { display: block; pointer-events: none; }
#order-modal .modal-bg { display: none; }
#order-modal .modal-panel { pointer-events: auto; }
/* Панель шире прежних 404px: в неё встают три поля «Отправки на ОТК» в один
   ряд, а длинный адрес получателя перестал переноситься по букве.
   Ширина — ОДНОЙ переменной: её же читает отступ доски, иначе на узком экране
   правый край доски уезжал под панель (панель держала свой минимум, а отступ
   считался от доли экрана и выходил меньше). */
:root { --order-drawer: 468px; }
@media (max-width: 1240px) { :root { --order-drawer: 404px; } }
@media (max-width: 720px) { :root { --order-drawer: 92vw; } }

#order-modal .modal-panel {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: var(--order-drawer); max-height: none;
  border: none; border-left: 1px solid var(--line); border-radius: 0;
  box-shadow: -10px 0 26px var(--c-rgba-90-74-55-08-s);
}
/* Доска не прячется под панелью: правый край уходит из-под неё. */
body.order-open .board { padding-right: calc(var(--order-drawer) + 16px); }

#order-modal .modal-head {
  flex-direction: column; align-items: stretch; gap: 10px;
  padding: 14px 16px;
  background: var(--c-fdf9ee-s);
  border-bottom: 1px solid var(--line-4);
}
.drawer-top { display: flex; align-items: baseline; gap: 10px; }
#order-modal .modal-code { font-family: var(--font-display); font-size: 24px; font-weight: 400; }
#order-modal .modal-title { font-size: 12px; margin-top: 3px; }
#order-modal .modal-head .btn { margin-left: auto; align-self: flex-start; }
.status-pill {
  padding: 3px 10px; border-radius: 999px;
  font-size: 11px; font-weight: 700;
  background: var(--c-f3efe4-s); color: var(--text-2);
}
.status-pill[data-status="new"] { background: var(--tile-rose-bg-3); color: var(--tile-rose-text-2); }
.status-pill[data-status="production"] { background: var(--tile-yellow-bg); color: var(--tile-yellow-text); }
.status-pill[data-status="qc_sent"] { background: var(--tile-blush-bg); color: var(--tile-blush-text); }
.status-pill[data-status="qc"] { background: var(--tile-violet-bg); color: var(--tile-violet-text); }
.status-pill[data-status="shipped"], .status-pill[data-status="done"] { background: var(--tile-green-bg); color: var(--tile-green-text-2); }
.drawer-risks { display: flex; flex-wrap: wrap; gap: 6px; }
.drawer-risks:empty { display: none; }

.path-strip { display: flex; gap: 5px; margin-bottom: 18px; }
/* Плитка воронки — кнопка: по ней заказ и переводят на этап. */
.path-step {
  flex: 1; min-width: 0;
  padding: 7px 8px; border-radius: 8px;
  background: var(--panel-6); color: var(--c-a09684-t);
  border: 1px solid transparent;
  font: inherit; text-align: left;
  cursor: pointer;
  transition: border-color .12s ease, transform .12s ease;
}
.path-step:hover:not(:disabled) { border-color: var(--gossamer-green); transform: translateY(-1px); }
.path-step:focus-visible { outline: 2px solid var(--ink-green); outline-offset: 2px; }
.path-step:disabled { cursor: default; }
@media (prefers-reduced-motion: reduce) { .path-step { transition: none; } }
.path-step.done { background: var(--tile-green-bg); color: var(--ink-green); }
.path-step.now { background: var(--tile-yellow-bg); color: var(--ink-yellow); }
.path-name {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .4px;
  opacity: .85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.path-date { font-size: 11.5px; font-weight: 700; margin-top: 4px; }

/* ── блоки боковой панели: один вид на все ───────────────
   Раньше блоки отличались друг от друга случайно: у одних был заголовок, у
   других два, отступы между полем и кнопкой были то 4, то 12 пикселей, а
   границ не было вовсе — и панель читалась одной сплошной простынёй. Теперь
   блок один и тот же: карточка с рамкой, заголовок сверху, поля внутри. */
#order-modal .modal-body { padding: 16px 18px 28px; }
#order-modal .card-section {
  margin-bottom: 14px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
}
#order-modal .card-section h3 {
  font-size: 10px; letter-spacing: 1.4px; margin: 0 0 12px;
}
/* Свёрнутый блок не носит заголовочного отступа: под ним ничего нет. */
#order-modal .card-section.fold:not(.open) h3 { margin-bottom: 0; }
#order-modal .card-section .fold-body { padding-top: 12px; }
#order-modal .edit-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
#order-modal .row-actions { margin-top: 14px; gap: 10px; }
/* Между «Отправкой на ОТК» и «Результатом примерки» отступ больше обычного:
   это два разных решения, и второе не должно читаться продолжением кнопок
   первого. */
#order-modal #fold-qc-result { margin-top: 22px; }
/* Подпись рядом с заголовком свёрнутого блока: что внутри, не разворачивая. */
.fold-note {
  font-weight: 400; text-transform: none; letter-spacing: 0;
  font-size: 11.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.fold-note.bad { color: var(--ink-rose); }
.fold-note:empty { display: none; }
#order-modal .fold-toggle { min-width: 0; }
#order-modal .fold-head { gap: 8px; }
/* Служебная строка под кнопками блока — своей строкой, а не в ряд с ними:
   в ряду она растягивала кнопки и переносилась по слову. */
.fold-meta { font-size: 11.5px; margin-top: 10px; line-height: 1.4; }
.fold-meta:empty { display: none; }
/* «Уточнение»: две строки по умолчанию, «Показать всё» разворачивает. */
.reason-field {
  width: 100%; resize: vertical; line-height: 1.4;
  max-height: 3.4em; overflow: hidden;
}
.reason-field.expanded { max-height: 18em; overflow: auto; }
#order-modal .msg { border: none; border-radius: 10px; background: var(--panel-5); }
#order-modal .msg.out { border-left: none; background: var(--c-eef4f0-s); }

/* Дата отгрузки в шапке панели — обычная строка: срок и просрочка уже
   показаны признаками риска рядом со статусом. */
#order-modal .modal-title .shipping-date,
#order-modal .modal-title .shipping-date.shipping-alert,
#order-modal .modal-title .shipping-date.shipping-overdue {
  display: block;
  padding: 0;
  border: none;
  background: none;
  color: var(--muted);
  font-weight: 400;
}
#order-modal .modal-title .shipping-countdown { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   Разделы в оформлении «Заказов»: выбранные варианты макетов.
   Список слева и карточка справа, узкие строки задач, строка-отправка
   в перемещениях, группы контрагентов, полосы цен и таблица закрытых.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── список слева, подробности справа ───────────────────── */

.split-view { display: flex; gap: 14px; align-items: flex-start; }
.split-list { flex: 0 0 292px; display: flex; flex-direction: column; gap: 6px; }
.split-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; border: none; border-radius: 11px;
  background: var(--panel); box-shadow: 0 1px 2px var(--c-rgba-80-66-48-05-s);
  font: inherit; color: var(--text); text-align: left; cursor: pointer;
}
.split-row:hover { box-shadow: 0 0 0 1px var(--c-e3dbc9-s), 0 1px 3px var(--c-rgba-80-66-48-08-s); }
.split-row.on { box-shadow: 0 0 0 2px var(--c-ddb0b4-s); }
.split-row.transit { background: var(--tile-cream-bg); }
.split-name { font-weight: 600; }
.split-meta { margin-left: auto; color: var(--muted); font-size: 11.5px; white-space: nowrap; }
.split-note { font-size: 11px; }
/* Имя фабрики и строка «чем нашлось» — в столбик, вторая мельче и приглушена.
   Ширину не даём: имя короткое, а совпадение обрезаем многоточием, иначе
   длинный адрес растянул бы строку списка. */
.split-name-box { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.split-match { font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── плашка производства: имя, оценка, выполненные заказы ─
 *
 * Ничего больше в неё не ставим. Сумма в юанях, отсрочка платежа и время
 * ответа отсюда убраны: список из трёх десятков строк с четырьмя показателями
 * в каждой читался как таблица. Подробности живут в досье справа.            */
.factory-row { flex-wrap: wrap; }
.factory-row .split-name {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.factory-row-right {
  display: flex; align-items: center; gap: 8px; flex: 0 0 auto; margin-left: auto;
}
.factory-row-right .split-meta { margin-left: 0; }
/* Кнопку «Перевести» в плашку не ставит сам `NadosTranslate` (`.factory-row`
 * попал в его SKIP_INSIDE). Правило ниже — страховка на случай, если кнопка
 * всё же окажется внутри: в списке ей не место. */
.factory-row .nt-btn { display: none; }

/* ── фильтр по специализации ─────────────────────────────
 *
 * В разделе — ОДНА кнопка «Фильтр» под вкладками «Актуальные» / «Библиотека
 * фабрик»: чипов стало семь, и полоса из них занимала строку на всю ширину
 * ради выбора, который делают раз в месяц. Выбранное кнопка называет рядом с
 * собой, снимается оно кнопкой «Сбросить».
 *
 * Те же чипы (`.spec-chip`) остались в досье — там они назначают
 * специализацию, а не фильтруют, и выбор там ежедневный.                     */
.spec-filter, .spec-edit {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 12px;
}
.spec-filter-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--text); font: inherit; font-size: 12.5px;
  cursor: pointer; white-space: nowrap;
}
.spec-filter-btn:hover { border-color: var(--accent); background: var(--panel-2); }
.spec-filter-btn.on { background: var(--accent-dim); border-color: var(--accent); font-weight: 600; }
.spec-filter-btn.on .spec-chip-n { color: var(--ink-green); }
.spec-filter-now {
  font-size: 12px; color: var(--muted); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.spec-filter-clear {
  border: none; background: none; padding: 0; font: inherit; font-size: 12px;
  color: var(--muted); text-decoration: underline; cursor: pointer;
}
.spec-filter-clear:hover { color: var(--ink-rose); }
.spec-pick-list { display: flex; flex-direction: column; gap: 2px; max-height: 340px; overflow-y: auto; }
.spec-pick-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 8px; border-radius: 8px; cursor: pointer;
}
.spec-pick-row:hover { background: var(--panel-2); }
.spec-pick-row .grow { flex: 1; min-width: 0; }
.spec-pick-row.muted .grow { color: var(--muted); }
.spec-edit { margin: 4px 0 14px; }
.spec-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--text); font: inherit; font-size: 12.5px;
  cursor: pointer; white-space: nowrap;
}
.spec-chip:hover { border-color: var(--accent); background: var(--panel-2); }
.spec-chip.on { background: var(--accent-dim); border-color: var(--accent); font-weight: 600; }
.spec-chip.muted { color: var(--muted); }
.spec-chip-n { color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.spec-chip.on .spec-chip-n { color: var(--ink-green); }
.spec-chip-x { color: var(--muted); font-size: 11px; padding: 0 1px; }
.spec-chip-x:hover { color: var(--ink-rose); }
.spec-add { display: inline-flex; align-items: center; gap: 4px; }
.spec-add input { font-size: 12.5px; padding: 4px 9px; width: 170px; }
.split-detail {
  flex: 1; min-width: 0;
  background: var(--panel); border-radius: 15px; padding: 16px 18px;
  box-shadow: 0 1px 2px var(--c-rgba-80-66-48-06-s);
}
.split-detail .section-subhead { margin-top: 18px; }
.split-detail .section-subhead:first-child { margin-top: 0; }

.detail-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 4px; }
.detail-title { font-family: var(--font-display); font-size: 22px; }
/* Имя фабрики в строке списка: то же, что заголовок досье, но в размер строки. */
.detail-title-sm { font-family: var(--font-display); font-size: 15px; }
/* Название, по которому кликают: кнопка, а не ссылка — переход внутри окна. */
.link-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-family: var(--font-display); font-size: 15px;
  color: var(--accent, inherit); text-align: left;
}
.link-btn:hover { text-decoration: underline; }
/* Базовая фабрика в досье производства — одна строка: подпись и кнопка
   (24.09.2026). Отдельного заголовка над ней больше нет. */
.factory-base { display: flex; flex-direction: column; gap: 2px; margin-top: 12px; }
.factory-base .row-actions { margin-top: 0; align-items: center; }
.factory-base .form-label { margin: 0; }
.detail-rating { display: flex; align-items: baseline; gap: 9px; margin-left: auto; }
.detail-rating b { font-family: var(--font-display); font-size: 26px; font-weight: 400; }

.stars-view { letter-spacing: 2px; font-size: 13px; }
.stars-view .on { color: var(--c-d8b26a-t); }
.stars-view .off { color: var(--c-e2dccc-t); }

/* Три плитки в ряд, пока ряд шире 420px, дальше — сколько влезет. Жёсткие три
   колонки в узком окне (минимум приложения — 900px) резали подписи: «прошли
   ОТК сразу» не помещалось в 91 пиксель. */
.fact-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
.fact-tile { padding: 13px 14px 12px; border-radius: 14px; background: var(--panel-3); color: var(--text-2); }
.fact-tile.ok { background: var(--tile-green-bg); color: var(--ink-green); }
.fact-tile.warn { background: var(--tile-yellow-bg); color: var(--ink-yellow); }
.fact-tile.cool { background: var(--tile-violet-bg); color: var(--tile-violet-text); }
.fact-top { display: flex; align-items: baseline; gap: 8px; }
.fact-value { font-family: var(--font-display); font-size: 26px; line-height: 1; }
.fact-name { font-size: 12px; font-weight: 700; }
.fact-note { margin-top: 9px; padding: 5px 9px; border-radius: 8px; background: var(--c-rgba-255-255-255-66-s); font-size: 10.5px; }

/* «Данные» карточки: ровно две плитки в ряд — так и задумано, отсюда и класс.
   В боковой панели они получались по 80 px не из-за этой пары, а из-за того,
   что и само досье стояло в двух колонках; чинится это выше, в `.dossier-grid`.
   Совсем узкий случай разложен в одну колонку в конце файла. */
.stat-grid.two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.stat-box { min-width: 0; }
.stat-value.small { font-size: 14px; font-weight: 500; }
/* Две колонки показателей, пока каждая шире 300px. Раньше их было ровно две
   при любой ширине, а `.metric-label` держал `min-width: 200px` — в узком окне
   строка вылезала за колонку, и «Средняя стоимость образцов» обрывалась на
   «обр». Ширина считается по самой панели, а не по окну: в досье слева ещё
   список, и одного брейкпоинта по ширине экрана здесь мало. */
/* `min(300px, 100%)` вместо голых 300px: в панели уже 300 колонка не ужимается
   и вылезает за край на разницу. */
.metric-list.two { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: 8px; }
.metric-list.two .metric-label { min-width: 120px; }

/* ── задачи: один список компактных строк ───────────────── */

/* Активные и выполненные разведены по вкладкам (16.09.2026), поэтому колонок
   больше нет: каждой вкладке достаётся вся ширина. Прежняя сетка делила экран
   на 2,2 к 0,8, и текст активных задач обрезался ради колонки, в которую
   заглядывают раз в неделю. */
.task-lane.done { opacity: 1; }
.lane-count {
  margin-left: 8px; font-family: var(--font-display); font-size: 14px;
  letter-spacing: 0; text-transform: none; color: var(--muted);
}
.task-list { display: flex; flex-direction: column; gap: 2px; }
/* Строка без рамки и без карточной тени: список читается по отбивке и по
   наведению, а не по обводке вокруг каждой задачи. Разделяет строки тонкая
   черта, и та — не у первой. */
.task-row {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 7px 10px; border-radius: 8px; background: transparent;
  border-top: 1px solid var(--line);
}
.task-list > .task-row:first-child { border-top: 0; }
.task-row:hover { background: var(--panel-2); }
/* Текст активной задачи виден ЦЕЛИКОМ. Агенту велено переносить в неё всю
   конкретику дословно — размеры, количества, форматы файлов, — и обрезка по
   двум строкам съедала ровно её: задачу нельзя было выполнить, не наведя мышь
   на строку ради подсказки. */
.task-row .task-text { font-size: 13px; line-height: 1.4; }
.task-row .task-actions {
  flex: 0 0 auto; display: flex; gap: 2px; margin: 0; padding-top: 1px;
  /* Правка и удаление — редкие действия: до наведения они не должны спорить
     за внимание с текстом задачи. */
  opacity: 0; transition: opacity .12s ease;
}
.task-row:hover .task-actions,
.task-row:focus-within .task-actions { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .task-row .task-actions { transition: none; } }
.task-row.task-done .task-text { color: var(--muted); text-decoration: line-through; }

/* Галочка вместо кнопки «Выполнено»: то же действие, но размером с само
   действие. Стоит слева, как в любом списке дел. */
.task-check {
  flex: 0 0 auto; width: 20px; height: 20px; margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--line); border-radius: 50%;
  background: transparent; color: transparent;
  font: inherit; font-size: 12px; line-height: 1; cursor: pointer;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.task-check:hover { border-color: var(--accent); color: var(--accent); }
.task-check.on {
  border-color: var(--ok); background: var(--ok); color: var(--panel);
}
.task-check.on:hover { background: transparent; color: var(--ok); }
@media (prefers-reduced-motion: reduce) { .task-check { transition: none; } }

/* Подзаголовок группы: производство, приславшее просьбы. */
.task-factory {
  display: flex; align-items: center; gap: 8px; margin: 14px 0 2px;
  font-family: var(--font-display); font-size: 13px; font-weight: 600;
  color: var(--muted);
}
.task-factory:first-of-type { margin-top: 0; }
/* Возраст просьбы решает, чем заняться сейчас, поэтому он и говорит цветом:
   сегодняшняя и вчерашняя — спокойные, неделя — тёплая, дальше — тревожная.
   Прежняя плашка была жёлтой всегда, и «сегодня» выглядело так же срочно, как
   «месяц назад». Цвет текста на плашке — тёмный: серый по жёлтому на кегле
   11,5 не читался. */
.task-age {
  flex: 0 0 auto; font-size: 11.5px; color: var(--muted); white-space: nowrap;
  background: transparent; border-radius: 4px; padding: 0 2px;
}
/* У выполненной задачи в плашке две даты (Q90). Она стоит в общей строке
   пометок и переносится по словам, а не держит справа колонку под самую
   длинную из дат. */
.task-row.task-done .task-age { white-space: normal; }
.task-age.age-warm { background: var(--warn); color: var(--ink-yellow); }
.task-age.age-hot { background: var(--danger); color: var(--ink-rose); }
.task-delete, .task-edit {
  width: auto; padding: 2px 6px; border: none; background: transparent;
  cursor: pointer; font-size: 13px; line-height: 1; opacity: .45;
}
.task-delete:hover, .task-edit:hover { opacity: 1; }
/* Правка формулировки на месте: строка списка становится полем ввода. */
.task-edit-field {
  flex: 1; min-width: 0; font: inherit; font-size: 12.5px;
  padding: 2px 6px; border: 1px solid var(--accent); border-radius: 6px;
  background: var(--panel);
}
/* Основание задачи — сообщение фабрики. Свёрнуто по умолчанию: строка списка
   должна оставаться строкой, а не карточкой переписки. */
.task-row .task-source { font-size: 11.5px; }
.task-row .task-source summary { color: var(--muted); }
.task-row .task-source-text, .task-row .task-source-ru { font-size: 12px; line-height: 1.35; }
.task-chat { font-size: 11.5px; color: var(--muted); }

/* Связанная сущность — ВНУТРЕННЯЯ ссылка, а не кнопка «Открыть…».
 *
 * Номер посылки, номер заказа, имя артикула и имя фабрики уже стоят в тексте
 * задачи; отдельная кнопка рядом называла то же самое второй раз. Теперь
 * кликается сам номер. Выглядит как ссылка, а не как кнопка: рамки вокруг
 * каждого номера в списке из тридцати задач — это тридцать рамок.
 *
 * `ent-chip` — тот же вид для сущности, которой в тексте не нашлось: связь
 * есть, и потерять её нельзя, но и выдавать её за часть формулировки незачем. */
.ent-link {
  border: 0; background: transparent; color: var(--ink-green);
  padding: 0; font: inherit; cursor: pointer;
  border-bottom: 1px dashed var(--accent); border-radius: 0;
}
.ent-link:hover { color: var(--accent); border-bottom-style: solid; }
.ent-chip {
  font-size: 11.5px; border-bottom: 0;
  background: var(--panel-2); border-radius: 4px; padding: 1px 6px;
}
.ent-chip:hover { border-bottom: 0; background: var(--accent-dim); }
/* Бренд показывается только во «Всех брендах» — там задачи двух компаний
   стоят вперемешку. */
.task-brand {
  font-size: 11px; color: var(--muted); border: 1px solid var(--line);
  border-radius: 4px; padding: 1px 6px;
}
/* Задачу закрыл агент, а не человек: видно это без разворачивания основания. */
.task-closer {
  font-size: 11px; color: var(--ink-green); background: var(--accent-dim);
  border-radius: 4px; padding: 1px 6px;
}
.task-answer-head { margin-top: 6px; font-size: 11px; color: var(--muted); }

/* ── перемещения: строка = одна отправка ────────────────── */

/* ── перемещения: доска посылок ─────────────────────────────── */

/* Раздел показывает не поездки, а посылки: строки с одним трек-номером — это
   одна коробка. Доска из трёх колонок ушла вместе с состоянием «в пути»:
   состояний осталось два, и делить ими экран стало нечем. */
.parcel-list { display: flex; flex-direction: column; gap: 9px; }
/* Карточка ужата 30.08.2026: воздуха внутри было больше, чем содержимого, и
   четыре посылки не помещались на экран. Ужимались отступы, не читаемость —
   размеры шрифтов и высота строк остались прежними. */
.parcel-card {
  background: var(--panel); border-radius: 12px; padding: 10px 13px;
  box-shadow: 0 1px 2px var(--c-rgba-80-66-48-06-s);
  display: flex; flex-direction: column; gap: 7px;
}
.parcel-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Трек — имя посылки и одновременно поле: приходит он часто отдельным
   сообщением уже после отправки. Рамка появляется под курсором, чтобы
   заголовок оставался заголовком, пока его не правят. */
.parcel-track {
  font-family: var(--font-display); font-size: 15px; letter-spacing: .2px;
  border-color: transparent; background: none; padding: 3px 7px; min-width: 190px;
}
.parcel-track:hover { border-color: var(--line); background: var(--paper); }
.parcel-track:focus { border-color: var(--accent); background: var(--paper); }
.parcel-track::placeholder { color: var(--muted); }
.parcel-age.sent { background: var(--tile-violet-bg-2); border-color: var(--c-ddd3e6-l); }
.parcel-age.partial { background: var(--warn); border-color: var(--warn); }
.parcel-age.received { background: var(--tile-green-bg-2); border-color: var(--c-cfe3d7-l); }
/* У частично доехавшей коробки рядом с чипом стоит её возраст: чип занят тем,
   сколько предметов доехало, а «сколько едет» из-за этого терять не хочется. */
.parcel-since { font-size: 12px; }
.parcel-actions { margin-left: auto; display: flex; align-items: center; gap: 7px; }
.parcel-actions select { padding: 4px 8px; font-size: 11.5px; }
.parcel-route { display: flex; align-items: center; gap: 7px; font-size: 12px; flex-wrap: wrap; }
.move-arrow { color: var(--c-c0b6a2-t); }

.parcel-items { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: 6px; }
.parcel-item {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 9px; border-radius: 9px; background: var(--panel-2);
  font-size: 12.5px; cursor: pointer; min-width: 0;
}
.parcel-item.done { background: var(--tile-green-bg-2); }
.parcel-item input[type="checkbox"] { flex: 0 0 auto; }
.parcel-num {
  font-family: var(--font-display); font-size: 13.5px;
  border: none; background: none; padding: 0; color: var(--text); cursor: pointer;
  flex: 0 0 auto; white-space: nowrap;
}
.parcel-num:hover { color: var(--c-6f7f74-t); }
/* Номер без карточки за ним — просто текст: кнопкой он выглядеть не должен. */
.parcel-num.plain { cursor: default; }
.parcel-num.plain:hover { color: var(--text); }

/* Форма посылки: список того, что едет, — строка на предмет. */
.parcel-rows { display: flex; flex-direction: column; gap: 7px; }
.parcel-row { display: grid; grid-template-columns: 128px minmax(0, 1fr) auto; gap: 7px; align-items: center; }
.parcel-drop { padding: 4px 10px; }

/* Плашки внутри посылки и остатка красятся по смыслу, а не одинаково.

   Раньше количество, назначение и вид ехали одним серым чипом подряд, и три
   плашки в строке сливались в одну полосу: прочитать, где кончается «5
   предметов» и начинается «для ОТК», можно было только вглядываясь. Цвет здесь
   не украшение — он и есть граница между ними. */
.chip-qty {
  background: var(--c-eef2f6-s); color: var(--c-4a5866-t);
  font-variant-numeric: tabular-nums;
}
.chip-purpose { background: var(--c-f3eef8-s); color: var(--c-574a66-t); font-weight: 600; }
.chip-kind { background: var(--panel-3); color: var(--text-3); }
/* Маршрут внутри плашки: обе стороны кликабельны, стрелка между ними — нет.
   Свой класс, а не `.chip-route .entity-link`: правило ссылок в CRM одно, и
   вложенный селектор с тем же именем сбивал бы поиск этого правила. */
.chip-route { display: inline-flex; align-items: center; gap: 2px; }
.route-link { border-radius: 4px; padding: 0 2px; }
/* Производство — такая же плашка, но по ней переходят в досье. */
.chip-factory { background: var(--c-eef2ec-s); color: var(--c-47563f-t); cursor: pointer; }
.chip-factory:hover { background: var(--accent-dim); color: var(--ink-green); }
/* Номер заказа в шапке остатка: заголовок, который ведёт в карточку. */
.stock-title {
  font-family: var(--font-display); font-size: 13.5px; font-weight: 600;
}

/* Название артикула в строке посылки: подпись, а не второй заголовок. */
.parcel-item-name {
  color: var(--muted); font-size: 11.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/* Количество прижимается вправо, к правке: числа выстраиваются столбиком и
   читаются вместе, а не разъезжаются вслед за длиной названия. */
.parcel-item .chip-qty { margin-left: auto; }
.parcel-item .parcel-item-edit { margin-left: 4px; }

/* ── наличие: состав остатка ─────────────────────────────

   «5 предметов» не говорило ничего: какие это пять, приходилось искать в
   заказе. Теперь остаток разложен по артикулам — строка на артикул, число
   справа столбиком. */
.stock-lines {
  display: flex; flex-direction: column; gap: 2px;
  margin: 8px 0 2px; padding: 7px 9px;
  background: var(--panel-2); border-radius: 9px;
}
.stock-line {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 12.5px; line-height: 1.5;
}
.stock-line-name { font-family: var(--font-display); }
.stock-line-note { font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stock-line-qty {
  margin-left: auto; color: var(--c-4a5866-t); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.stock-line-empty .stock-line-name { font-family: inherit; color: var(--muted); }
/* Одна служебная строка «состав не записан» — подложка вокруг неё читалась
   рамкой вокруг пустоты. */
.stock-lines:has(> .stock-line-empty:only-child) { background: none; padding: 0 9px; }

/* ── выбор позиций для отправки ─────────────────────────── */

.pick-list { display: flex; flex-direction: column; gap: 9px; margin: 8px 0 4px; }
.pick-row {
  border: 1px solid var(--line); border-radius: 10px;
  padding: 8px 10px; background: var(--panel);
}
.pick-row-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 5px; font-size: 12.5px;
}
.pick-line {
  display: flex; align-items: center; gap: 9px;
  padding: 4px 0; font-size: 12.5px; cursor: pointer;
}
.pick-line input[type="checkbox"] { flex: 0 0 auto; }
.pick-name { font-family: var(--font-display); }
.pick-have { margin-left: auto; font-size: 11px; white-space: nowrap; }
.pick-qty {
  width: 76px; flex: 0 0 auto; text-align: right;
  font-variant-numeric: tabular-nums;
}
.pick-qty:disabled { opacity: .45; }

/* Строка формы состава: артикул, количество, крестик. */
.comp-row { display: grid; grid-template-columns: minmax(0, 1fr) 84px auto; gap: 7px; align-items: center; }

/* ── наличие: история под свёрткой ──────────────────────── */

.stock-history { margin-top: 10px; }
.stock-history summary {
  cursor: pointer; color: var(--muted); font-size: 11.5px;
  list-style: none; padding: 5px 0;
}
.stock-history summary::marker, .stock-history summary::-webkit-details-marker { display: none; }
.stock-history summary::before { content: '▸ '; font-size: 10px; }
.stock-history[open] summary::before { content: '▾ '; }
.stock-history .timeline { margin-top: 8px; }

/* ── контрагенты ────────────────────────────────────────── */

/* Раздел разложен вкладками по типу; групп с заголовками (`.party-group`) в
   нём больше нет, вместе с ними ушли и их правила. */

/* Адрес человек пишет строками: предупреждение, сам адрес, телефон. Схлопнутые
   в одну строку, они читаются как каша — и в панели, и в заметке. */
.hint.pre { white-space: pre-line; }

/* Библиотека контрагентов: слева имя, справа плашки брендов. Список длинный —
   прокручивается сам, а заголовки типов липнут к верху, как в выборе фабрик. */
.library-list { gap: 0; max-height: min(56vh, 460px); overflow-y: auto; }
.library-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 10px; border-bottom: 1px solid var(--line);
}
.library-row:last-child { border-bottom: 0; }
.library-row .grow { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.library-row .nm { font-size: 13px; }
.library-row .muted { font-size: 11px; }
.library-row .chip-row { flex-wrap: nowrap; }

/* Плашка-переключатель: погашенная — бренд не отмечен, зажжённая — отмечен.
   Цвет тот же, что у «по умолчанию»: в разделе это одна и та же мысль — «да». */
button.chip-toggle {
  font: inherit; font-size: 11px; cursor: pointer;
  opacity: .5; white-space: nowrap;
}
button.chip-toggle:hover { opacity: .8; }
button.chip-toggle.on {
  opacity: 1; background: var(--accent-dim); color: var(--ink-green);
}
.party-brands { color: var(--muted); }

/* ── товары: полоса закупочной цены ─────────────────────── */

.product-top { display: flex; gap: 12px; align-items: flex-start; }
.product-top .photo-strip { flex: 0 0 84px; }
.product-title { min-width: 0; flex: 1; }
.price-bars { display: flex; flex-direction: column; gap: 5px; margin-top: 12px; }
.price-row { display: flex; align-items: center; gap: 9px; font-size: 11.5px; }
.price-name { width: 108px; flex: 0 0 auto; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.price-track { flex: 1; height: 6px; border-radius: 3px; background: var(--c-f1ece0-s); overflow: hidden; }
.price-track i { display: block; height: 100%; border-radius: 3px; background: var(--c-a9cdb8-s); }
.price-value { width: 74px; text-align: right; font-weight: 600; }

/* ── закрытые заказы: итоги и таблица ───────────────────── */

/* Сводка «Завершено» — самостоятельные плитки в ряд, а не шапки колонок:
   поэтому у них своя раскладка и скруглены все четыре угла. */
.closed-tiles {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 10px;
  padding: 0 0 12px;
}
.closed-tiles .fun-tile { border-radius: 14px; }
.closed-tiles .fun-risk { margin-top: 9px; }
/* Скругление углов раньше держалось на `overflow: hidden`, но оно же делает
   таблицу собственным контейнером прокрутки — и липкая шапка внутри неё липнуть
   перестаёт. Углы скругляем самим ячейкам, а шапка липнет к верху раздела. */
.closed-table { background: var(--panel); border-radius: 13px; }
.closed-table thead th:first-child { border-top-left-radius: 13px; }
.closed-table thead th:last-child { border-top-right-radius: 13px; }
.closed-table tbody tr:last-child td:first-child { border-bottom-left-radius: 13px; }
.closed-table tbody tr:last-child td:last-child { border-bottom-right-radius: 13px; }
.closed-table th {
  background: var(--panel-5); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 2; padding: 0;
}
/* Заголовок — кнопка сортировки во всю ячейку: попадать в мелкую надпись мышью
   неудобно, а колонок восемь. */
.closed-table th .sort-th {
  display: flex; align-items: center; gap: 5px; width: 100%;
  padding: 7px 10px; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; text-align: left; letter-spacing: inherit;
  text-transform: inherit;
}
.closed-table th .sort-th:hover { color: var(--text); background: var(--c-f6f2e6-s); }
.closed-table th .sort-th.active { color: var(--ink); }
.closed-table th .sort-mark { font-size: 10px; opacity: .75; }
.closed-table td { border-bottom: 1px solid var(--c-f4efe3-l); cursor: pointer; }
.closed-table tr:last-child td { border-bottom: none; }
.closed-table tbody tr:hover td { background: var(--c-fdfbf5-s); }
.closed-table td.code { font-family: var(--font-display); font-size: 14.5px; }
.closed-more { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 12px; }

/* ── ревью: очередь и разбираемый случай ────────────────── */

.review-row { flex-direction: column; align-items: stretch; gap: 6px; }
.review-row-top { display: flex; align-items: baseline; gap: 8px; }
.review-row-top .muted { margin-left: auto; font-size: 10.5px; }
.review-row-q { font-size: 12px; }
.split-detail .review { border: none; background: transparent; padding: 0; }
.review-when { margin-left: auto; font-size: 11px; }
.split-detail .review-question { font-family: var(--font-display); font-size: 17px; margin: 12px 0 0; }
.split-detail .review-context { margin-top: 6px; }
.split-detail .msg { margin-top: 12px; }
.split-detail .review-actions { margin-top: 14px; }

/* ── карточка раздела как боковая панель ────────────────── */

.entity-top { display: flex; align-items: baseline; gap: 10px; }
#entity-modal.as-drawer { display: block; }
#entity-modal.as-drawer .modal-bg { display: none; }
/* Панель не запирает раздел: контейнер прозрачен для кликов, ловит их только
   сама панель — можно открывать соседние карточки и жать кнопки, не закрывая её. */
#entity-modal.as-drawer { pointer-events: none; }
#entity-modal.as-drawer .modal-panel { pointer-events: auto; }
#entity-modal.as-drawer .modal-panel {
  position: fixed; top: 0; right: 0; bottom: 0;
  /* 404 px — рабочая ширина панели, но не на узком окне: там она закрывала
     собой раздел целиком, а вернуться к списку можно только мимо неё. */
  width: min(404px, calc(100vw - 40px)); max-height: none;
  border: none; border-left: 1px solid var(--line); border-radius: 0;
  box-shadow: -10px 0 26px var(--c-rgba-90-74-55-08-s);
}
#entity-modal.as-drawer .modal-head { background: var(--c-fdf9ee-s); border-bottom: 1px solid var(--line-4); }
#entity-modal.as-drawer .modal-code { font-family: var(--font-display); font-size: 22px; }
#entity-modal.as-drawer .photo-strip { margin: 4px 0 12px; }
/* Тело панели — гибкий элемент в колонке, и без `min-width: 0` его нельзя
   сжать: широкая таблица внутри растягивала панель за край экрана. */
#entity-modal.as-drawer .modal-body { min-width: 0; overflow-x: hidden; }

/* Досье внутри панели — без второй рамки; правила стоят в конце файла, после
   запросов к контейнеру: те тоже правят отступ досье и при равном весе
   перебивали бы обнуление. */

/* ── разработки: лента в две колонки ────────────────────── */

.dev-feed { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start; }
.dev-card {
  border: none; border-radius: 13px; gap: 12px;
  box-shadow: 0 1px 2px var(--c-rgba-80-66-48-06-s);
}
.dev-card.colored { border-left: 4px solid var(--tab-color); }
.dev-head { border-bottom: 1px solid var(--c-f2ecdd-l); }
.dev-name { font-family: var(--font-display); font-size: 17px; }
.dev-progress.complete .dev-progress-ring { background: conic-gradient(var(--accent) 100%, var(--accent) 0); }
.dev-progress.complete .dev-progress-ring span { background: var(--accent-dim); }
@media (max-width: 1180px) {
  .dev-feed { grid-template-columns: minmax(0, 1fr); }
}

/* ── «Завершено»: выбор производства наверху ────────────── */
/* Сама шапка объявлена выше, рядом с .history-body: там она стоит до
   @media (max-width: 760px), который складывает её в колонку. */
.history-picker { flex: 0 0 auto; max-width: 260px; }
/* В обзоре выбор — единственный ребёнок шапки; без прижима вправо он прыгал
   бы по горизонтали при переходе обзор ↔ производство. */
.history-toolbar > .history-picker:only-child { margin-left: auto; }
.closed-more { padding: 10px 0 0; font-size: 12.5px; }

/* ── окно статистики производства ───────────────────────── */
.stat-scope {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline;
  margin: 4px 0 10px; font-size: 12.5px;
}

/* ── образцы: полосы статусов ───────────────────────────── */

/* ── образец с подходящим сроком ──────────────────────────
   Образцы живут в «Продуктах», строкой внутри карточки товара: полоска слева
   говорит, что срок на исходе (жёлтая) или уже прошёл (красная). У ведущего
   образца своя полоска цветом акцента — срок её перебивает, он важнее. */
.card-sample.sample-due { box-shadow: inset 3px 0 0 var(--c-d9b96a-s); }
.card-sample.sample-overdue { box-shadow: inset 3px 0 0 var(--c-b8515d-s); }
.card-sample.leading.sample-due, .card-sample.leading.sample-overdue { border-left-color: transparent; }

/* ── завершённые: ошибки проверки ───────────────────────── */
.closed-table td.center { text-align: center; }
.defect-chip { width: auto; cursor: pointer; border: none; font: inherit; }
.defect-list { margin: 8px 0 0; padding-left: 20px; display: flex; flex-direction: column; gap: 6px; }
.defect-list li { font-size: 13.5px; }

/* ── архив перемещений ──────────────────────────────────── */
.move-archive { display: flex; justify-content: center; padding: 12px 0 4px; }
.move-archive-item { border-bottom: 1px solid var(--line); padding: 8px 0; }
.move-archive-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  cursor: pointer; font-size: 13.5px;
}
.move-archive-body { display: flex; flex-direction: column; gap: 4px; padding: 8px 0 2px; }
.move-archive-line { display: flex; gap: 8px; font-size: 12.5px; }
.move-archive-line span:first-child { flex: 0 0 92px; }

/* ── решение по ОТК прямо на карточке доски ─────────────── */
.card-decide { display: flex; gap: 6px; margin-top: 8px; }
.card-decide-btn {
  flex: 1; width: auto; padding: 5px 8px; border-radius: 8px; cursor: pointer;
  font-size: 11.5px; line-height: 1.2; border: 1px solid var(--line);
  background: var(--panel); color: var(--text);
}
.card-decide-btn.passed:hover { border-color: var(--c-4a7c59-l); background: var(--tile-green-bg-2); color: var(--c-3f6b4c-t); }
.card-decide-btn.failed:hover { border-color: var(--c-a94550-l); background: var(--c-f9ecee-s); color: var(--c-8e3a44-t); }
.qc-decision-label {
  display: block; margin-bottom: 4px; font-size: 12px; color: var(--muted);
}
#qc-decision-defects { width: 100%; margin-bottom: 10px; }
#qc-decision .script-preview { max-height: 260px; overflow-y: auto; }

/* Выбор исхода: скрипт ОТК всегда про один заказ и одно решение. */
.qc-choice { display: flex; flex-direction: column; gap: 4px; }
.qc-choice .btn-lg { margin-top: 6px; }

/* Номер заказа виден до того, как текст уйдёт в WeChat: скрипт именной. */
.script-order-line {
  margin: 0 0 8px; padding: 6px 10px; border-radius: 8px;
  background: var(--c-fdf3d8-s); border: 1px solid var(--c-e6d3a3-l);
  font-size: 13px; font-weight: 600; color: var(--c-6d5620-t);
}
.script-order-line:empty { display: none; }
.btn-lg {
  width: 100%; margin-top: 10px; padding: 11px 16px;
  font-size: 14px; font-weight: 600; border-radius: 10px;
}
.btn-danger-solid {
  border: 1px solid var(--c-c96a74-l); background: var(--c-b8515d-s); color: var(--on-accent);
}
.btn-danger-solid:hover { background: var(--c-a5454f-s); }

/* ── «Заказы»: переключатель режимов и доска образцов ──────────────────────
   На месте прежней плашки «N образцов в разработке» стоит переключатель
   «Заказы | Образцы» (17.09.2026). Он узкий и тихий: это не заголовок раздела,
   а выбор того, какую из двух досок показывать. */
.orders-mode { padding: 9px 20px 0; flex: 0 0 auto; }
.mode-switch {
  display: inline-flex; gap: 2px; padding: 2px;
  border-radius: 999px; background: var(--c-f4f1e8-s);
}
.mode-btn {
  border: none; background: transparent; color: var(--muted);
  padding: 5px 16px; border-radius: 999px; font: inherit; font-size: 13px;
  cursor: pointer; white-space: nowrap;
}
.mode-btn:hover { color: var(--text); }
.mode-btn.on {
  background: var(--paper); color: var(--text); font-weight: 650;
  box-shadow: 0 1px 2px var(--c-rgba-60-50-35-08-s);
}
.mode-btn:focus-visible { outline: 2px solid var(--ink-green); outline-offset: 1px; }

/* Доска образцов собрана теми же колонками, что и доска заказов (`.col`,
   `.fun-tile`, `.card`), — своей вёрстки у неё нет. Отличаются только цвета
   этапов: статусы у образца свои. */
.col[data-sample-status="sample"] { background: var(--tile-cream-bg); }
.col[data-sample-status="fitting"] { background: var(--tile-violet-bg-2); }
.col[data-sample-status="sample_ok"] { background: var(--tile-green-bg-2); }
.col[data-sample-status="rejected"] { background: var(--c-fdf3f4-s); }
.col[data-sample-status="done"] { background: var(--c-f7f5ef-s); }
.col[data-sample-status="archive"] { background: var(--panel-2); }
.fun-tile[data-sample-status="sample"] { background: var(--tile-yellow-bg); color: var(--tile-yellow-text); }
.fun-tile[data-sample-status="fitting"] { background: var(--tile-violet-bg); color: var(--tile-violet-text); }
.fun-tile[data-sample-status="sample_ok"] { background: var(--tile-green-bg); color: var(--tile-green-text-2); }
.fun-tile[data-sample-status="rejected"] { background: var(--tile-rose-bg-3); color: var(--tile-rose-text-2); }
.fun-tile[data-sample-status="done"] { background: var(--panel-3); color: var(--text-2); }
.fun-tile[data-sample-status="archive"] { background: var(--panel-3); color: var(--text-2); }

/* Карточку образца перетаскивают между колонками — так же, как карточку
   заказа (17.09.2026): этап образца меняют прямо на доске, и второго приёма
   для этого на ней быть не должно. Курсор о том и говорит. */
.sample-card { cursor: grab; }
.sample-card.dragging { cursor: grabbing; }
.sample-card-name { margin-top: 3px; overflow-wrap: anywhere; }
.sample-card-factory {
  margin-top: 3px; font-size: 11px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sample-card.late { border-color: var(--tile-rose-line); }
.sample-card-late {
  display: inline-block; margin-top: 6px;
  padding: 2px 7px; border-radius: 6px; font-size: 11px; font-weight: 600;
  color: var(--tile-rose-text); background: var(--tile-rose-bg);
  border: 1px solid var(--tile-rose-line);
}
/* Причина отказа и список недописанного — в одну строку с многоточием: на
   карточке они напоминают, а читают их в самой карточке образца. */
.sample-card-why, .sample-card-gap {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sample-card-gap { margin-top: 5px; font-size: 11px; color: var(--c-a08b6f-t); }

/* Новый товар прямо в окне «Новые образцы»: артикул называют здесь, и он
   сразу появляется в «Продуктах» с этим названием. */
.nsm-new { margin-top: 12px; }
.nsm-new .row-inline { display: flex; gap: 8px; align-items: center; }
.nsm-new .row-inline input { flex: 1 1 auto; min-width: 0; }
.nsm-new-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.nsm-new-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px; border-radius: 999px; font-size: 12px;
  background: var(--c-eef3ec-s); color: var(--tile-green-text); border: 1px solid var(--c-dbe6d9-l);
}
.nsm-new-drop {
  border: 0; background: none; cursor: pointer; color: inherit;
  font-size: 14px; line-height: 1; padding: 0;
}

/* Готовый вопрос фабрике в карточке «Ревью»: текст правится на месте. */
.review-script { margin-top: 10px; }
.review-script > summary { cursor: pointer; font-size: 12px; color: var(--muted); }
.review-script-text {
  width: 100%; margin-top: 8px; font: inherit; font-size: 13px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--text); resize: vertical;
}

/* Совет по устранимому простою: не тревога, а подсказка — поэтому спокойнее
   красной плашки «стоит» и стоит сразу под ней. */
.card-reason.advice {
  background: var(--tile-yellow-bg); color: var(--tile-yellow-text); border-color: var(--tile-yellow-line);
}
.card-reason.advice .card-reason-label { color: var(--tile-yellow-text); }

/* Цена в «Данных» карточки правится на месте: поле должно выглядеть как
   значение, а не как форма посреди плиток. */
.stat-box .form-field { margin: 0; gap: 0; }
.stat-box .form-label { display: none; }
.stat-input {
  width: 100%; border: 1px solid transparent; background: transparent;
  padding: 2px 4px; margin-left: -4px; border-radius: 6px;
  font: inherit; font-size: 15px; color: var(--text);
}
.stat-input:hover { border-color: var(--line); }
.stat-input:focus { border-color: var(--accent); background: var(--panel-raw); }

/* У разработки производств несколько, и цена у каждого своя: строка на
   производство, имя слева, его цена справа. Одним полем на всех показать это
   нельзя — оно врёт про то, чья цена (ревизия 15.09.2026). */
.price-by-factory {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding-top: 4px;
}
.price-by-factory + .price-by-factory { border-top: 1px solid var(--line); }
.price-by-factory-name { flex: 1 1 96px; min-width: 0; font-size: 13px; color: var(--muted); }
.price-by-factory .form-field { flex: 0 1 90px; min-width: 64px; }
.price-by-factory .stat-input { text-align: right; }

/* ── разговор с Гермесом ──────────────────────────────────────────────────
   Свёрнутая панель поверх доски, а не раздел: спрашивают про то, что на
   экране, и экран должен остаться виден. Поэтому панель узкая и прижата к
   правому краю, а доска под ней не сдвигается. */
/* Только круг с иконкой: подпись «Спросить Гермеса» жила на экране постоянно,
   хотя значение кнопки читается по самой иконке. Текст остался подсказкой при
   наведении и меткой для программ чтения с экрана. */
.chat-toggle {
  position: fixed; right: 18px; bottom: 18px; z-index: 55;
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--line); background: var(--card, var(--panel-raw)); color: var(--text);
  box-shadow: 0 6px 20px var(--c-rgba-40-34-28-14-s);
  font: inherit;
}
.chat-toggle svg { flex: 0 0 auto; }
.chat-toggle:hover { border-color: var(--accent); color: var(--ink-green); }
/* Пока разговор открыт, круглая кнопка прячется: она оказывается ровно под
   панелью. Свернуть можно крестиком в шапке разговора и ⌘/. */
.chat-toggle.under-chat { display: none; }
/* Индикатор непрочитанных ошибок-оснований у кнопки чата. Цвета — токенами. */
.chat-toggle-badge {
  position: absolute; top: -3px; right: -3px;
  min-width: 18px; height: 18px; padding: 0 4px;
  display: grid; place-items: center;
  background: var(--warn); color: var(--ink);
  border: 1px solid var(--card, var(--panel-raw));
  border-radius: 9px; font-size: 11px; font-weight: 700; line-height: 1;
}

.chat-panel {
  position: fixed; right: 0; top: 0; bottom: 0; z-index: 56;
  width: 400px; max-width: 92vw;
  display: flex; flex-direction: column;
  background: var(--card, var(--panel-raw)); border-left: 1px solid var(--line);
  box-shadow: -10px 0 30px var(--c-rgba-40-34-28-10-s);
}
.chat-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  padding: 14px 14px 10px; border-bottom: 1px solid var(--line);
}
.chat-title { font-family: var(--font-display); font-size: 17px; }


.chat-log {
  flex: 1 1 auto; overflow-y: auto; padding: 14px;
  display: flex; flex-direction: column; gap: 10px;
}
.chat-empty { color: var(--muted); font-size: 13px; line-height: 1.5; }
.chat-msg {
  max-width: 92%; padding: 9px 12px; border-radius: 12px;
  font-size: 13px; line-height: 1.5;
}
/* Реплика человека прижата вправо, ответ — влево: у разговора должно быть
   видно, кто говорит, без подписей у каждой строки. */
.chat-user {
  align-self: flex-end; background: var(--accent); color: var(--on-accent-strong);
  border-bottom-right-radius: 4px;
}
.chat-agent {
  align-self: flex-start; background: var(--bg); border: 1px solid var(--line);
  border-bottom-left-radius: 4px;
}
.chat-error {
  align-self: flex-start; background: var(--tile-rose-bg); border: 1px solid var(--tile-rose-line); color: var(--tile-rose-text);
}
.chat-msg-text { white-space: pre-wrap; word-break: break-word; }
.chat-msg-at { font-size: 10px; opacity: .6; margin-top: 4px; }
.chat-user .chat-msg-at { text-align: right; }
.chat-thinking { opacity: .7; font-style: italic; }

/* Ход работы: что Гермес думал, какие инструменты позвал и что из этого
   вышло. Свёрнут — при беглом чтении нужен ответ, а подробности когда что-то
   пошло не так. */
.chat-turn { display: flex; flex-direction: column; gap: 8px; }
.chat-work { align-self: stretch; }
.chat-work-head {
  cursor: pointer; font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.chat-work-body {
  margin-top: 6px; padding: 8px 10px; border-radius: 10px;
  background: var(--bg); border: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 4px;
}
.chat-step {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 12px; color: var(--muted); min-width: 0;
}
.chat-step-thought { font-style: italic; }
.chat-step-mark { flex: 0 0 auto; width: 12px; text-align: center; }
.chat-step-text { color: var(--text); }
.chat-step-arg {
  color: var(--muted); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; min-width: 0;
}
/* Вызов, который изменил данные CRM, виден среди прочих: по нему человек
   понимает, что доска под панелью уже другая. */
.chat-step-tool.crm .chat-step-text { color: var(--ink-green); font-weight: 600; }
.chat-system { opacity: .75; font-style: italic; }

/* Вопрос о разрешении. Не в свёртке: на него надо ответить, и прятать его
   значило бы прятать остановку. */
.chat-permission {
  align-self: stretch; padding: 10px 12px; border-radius: 12px;
  background: var(--tile-yellow-bg); border: 1px solid var(--tile-yellow-line); color: var(--tile-yellow-text);
  font-size: 13px; line-height: 1.5;
}
.chat-permission.ask { border-color: var(--tile-yellow-line-2); }
.chat-permission-head { font-weight: 600; margin-bottom: 4px; }
.chat-permission-what { word-break: break-word; }
.chat-permission-arg {
  margin-top: 4px; font-family: var(--mono); font-size: 11px; opacity: .8; word-break: break-all;
}
.chat-permission-done { margin-top: 6px; font-size: 12px; opacity: .8; }
.chat-permission .row-actions { margin-top: 8px; flex-wrap: wrap; }

/* Вопрос «код не распознал это как задачу — правильно ли я понял?».
   Ширина во всю ленту, как у вопроса о разрешении: на него надо ответить, и
   выглядеть он должен не репликой, а остановкой. */
/* В окне-сообщении бывают строки со своим смыслом — счётчики, перечисления:
   переносы в них должны оставаться переносами. */
#ask-text { white-space: pre-line; }

.chat-hint { align-self: stretch; max-width: none; }
.chat-hint-actions { display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap; }

.chat-input {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 10px 14px; border-top: 1px solid var(--line);
}
.chat-attach-btn { padding: 6px 8px; color: var(--muted); }
.chat-attach-btn:hover { color: var(--ink-green); }
/* Файлы к вопросу — чипы над полем ввода; картинка показывается миниатюрой. */
.chat-attach {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 8px 14px 0; border-top: 1px solid var(--line);
}
.chat-chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 3px 6px 3px 4px; border-radius: 8px; font-size: 12px;
  background: var(--bg); border: 1px solid var(--line); color: var(--text);
}
.chat-chip img { width: 28px; height: 28px; object-fit: cover; border-radius: 5px; }
.chat-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 180px; }
.chat-chip-size { color: var(--muted); }
.chat-chip-x { border: 0; background: none; cursor: pointer; color: var(--muted); font: inherit; padding: 0 2px; }
.chat-chip-x:hover { color: var(--tile-rose-text); }
/* Вложения в отправленной реплике: миниатюры и имена файлов. */
.chat-msg-files { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.chat-msg-files img { max-width: 160px; max-height: 120px; border-radius: 8px; display: block; }
.chat-msg-files .chat-chip { background: var(--c-rgba-255-255-255-18-s); border-color: var(--c-rgba-255-255-255-35-l); color: inherit; }
.chat-panel.drop { outline: 2px dashed var(--accent); outline-offset: -4px; }
/* Ответ Гермеса: лёгкий markdown — списки, жирный, код. */
.chat-md .chat-list { margin: 2px 0 2px 18px; padding: 0; }
.chat-md .chat-list li { margin: 1px 0; }
.chat-md .chat-md-head { font-weight: 700; margin-top: 4px; }
.chat-md .chat-md-gap { height: 6px; }
.chat-md code { font-size: 12px; background: var(--panel-2); padding: 0 4px; border-radius: 4px; }
.chat-md .chat-code {
  margin: 4px 0; padding: 6px 8px; border-radius: 8px; font-size: 12px;
  background: var(--panel-2); white-space: pre-wrap; word-break: break-word;
}
.review-images { margin-top: 8px; }
/* Завершённый чек-лист стоит на акцентном фоне: приглушённый текст на нём не читается. */
.checklist.complete .muted, .checklist.complete .checklist-count,
.checklist.complete .date-pill, .checklist.complete small { color: var(--ink-green); }
.chat-input textarea {
  flex: 1 1 auto; min-width: 0; resize: none; font: inherit; font-size: 13px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg); color: var(--text);
}
.chat-input textarea:focus { border-color: var(--accent); background: var(--panel-raw); }

/* Выпадашка, в которой можно печатать (решение 14.09.2026).
   Снаружи ведёт себя как <select>, поэтому и выглядеть должна так же: те же
   рамка, высота и стрелка. Список — поверх формы, с прокруткой. */
.combo { position: relative; }
.combo-input {
  width: 100%;
  /* Стрелка та же, что браузер рисует у <select>: элемент поменялся, а
     выпадашка на экране обязана остаться прежней. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' fill='none' stroke='%237a7268' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 26px;
  cursor: pointer;
}
.combo-input:focus { cursor: text; }
.combo.disabled .combo-input { opacity: .6; cursor: default; }
/* ВЫПАДАШКА В РЯДУ ДЕЙСТВИЙ — РОСТОМ С КНОПКАМИ РЯДОМ (аудит 24.09.2026).
   В «Таблицах» ряд собран из трёх малых кнопок и выпадашки «Все
   производства», и выпадашка была на 7.5 px выше: ряд шёл ступенькой.
   Здесь она не поле формы, а такая же кнопка выбора — и размер у неё
   кнопочный. */
.row-actions > .combo .combo-input,
.section-actions > .combo .combo-input,
.actions > .combo .combo-input {
  padding: 4px 26px 4px 12px; font-size: 13px;
}

/* КНОПКА РЯДОМ С ПОЛЕМ ВВОДА — ОДНОЙ С НИМ ВЫСОТЫ (аудит 24.09.2026).
   «Применить» у комментария в карточке заказа, «Добавить» в настройках
   брендов, «+ Добавить» в окне новых образцов — всюду поле 36 px, а кнопка
   вплотную к нему 30: пара читалась как два разных ряда. Правило смотрит на
   состав самого ряда, поэтому работает и в окнах, которых ещё нет. */
.row-actions:has(> input:not([type="checkbox"]):not([type="radio"])) > .btn,
.row-inline:has(> input:not([type="checkbox"]):not([type="radio"])) > .btn,
.status-row > .btn {
  min-height: 36px;
}
.combo-menu {
  /* Поле бывает узким (в форме их два в ряд), а имена контрагентов длинные:
     список шире поля — иначе «Модель ОТК Полина» читается как «Модель ОТК
     Пол…», и выбирать приходится наугад.

     МЕСТО СПИСКУ СЧИТАЕТ JS (`place` в ui/sections.js), потому что он висит
     НАД страницей: лежал внутри поля `position: absolute` — и всякий предок с
     `overflow: hidden` его резал (у коротких окон — по дну панели). Значения
     ниже остаются запасными: ими список выглядит собой до первого открытия. */
  position: fixed; z-index: 40; left: 0; top: calc(100% + 4px);
  min-width: 100%; width: max-content; max-width: 340px;
  max-height: 240px; overflow-y: auto;
  background: var(--card, var(--panel-raw)); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 8px 24px var(--c-rgba-40-34-28-12-s);
  padding: 4px;
}
.combo-item {
  display: block; width: 100%; text-align: left; border: 0; background: none;
  padding: 6px 9px; border-radius: 7px; cursor: pointer; font: inherit;
  color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.combo-item:hover, .combo-item.marked { background: var(--bg); }
.combo-item.on { color: var(--ink-green); font-weight: 600; }
.combo-empty { padding: 8px 9px; color: var(--muted); font-size: 13px; }
.combo-group {
  padding: 7px 9px 3px; font-size: 11px; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted);
}

/* Окно разбора: бренды галочками, техническое — под свёрткой. */
.plan-brands {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-bottom: 10px;
}
.plan-brands:empty { display: none; }
.plan-brand {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px;
  font-size: 13px;
}
.plan-brand:hover { border-color: var(--muted); }
.plan-advanced { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 10px; }
.plan-advanced > summary { cursor: pointer; font-size: 13px; color: var(--muted); }
.plan-advanced-hint { margin: 8px 0; }

/* Кнопка «+ Задача» над списком: своё дело заводят там же, где его видят. */
/* Действие задачи — заметной кнопкой: смысл такой задачи в том, чтобы
   написать человеку конкретный вопрос, и вход в готовый текст должен быть
   виден, а не спрятан подписью. */
.task-ask { white-space: nowrap; }

/* Миниатюра товара в «Наличии»: компактная, одного размера у всех строк —
   с фотографией и без неё, чтобы шапки плиток не прыгали. */
.stock-thumb {
  width: 34px; height: 34px; flex: 0 0 auto; border-radius: 8px;
  overflow: hidden; background: var(--panel-3); border: 1px solid var(--line-3);
}
/* Шапка плитки разнесена по краям, и номер уезжал вправо от миниатюры, как
   будто это разные вещи. Ставим их рядом: сначала снимок, сразу за ним номер. */
.stock-item .entity-card-head { justify-content: flex-start; }
.stock-item .entity-card-head .chip { margin-left: auto; }
.stock-item .entity-card-head .chip + .chip { margin-left: 0; }
.stock-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.stock-thumb.empty {
  background:
    linear-gradient(135deg, transparent 46%, var(--c-ddd5c4-s) 46%, var(--c-ddd5c4-s) 54%, transparent 54%),
    var(--panel-3);
}

/* ── разбор: сводка и свёрнутый выбор чатов ─────────────── */
.plan-summary { margin: 10px 0 4px; font-size: 14px; }
.plan-pick { margin: 6px 0 2px; }
.plan-pick > summary {
  cursor: pointer; font-size: 13px; color: var(--muted); user-select: none;
  padding: 6px 0;
}
.plan-pick > summary:hover { color: var(--text); }
.plan-pick[open] > summary { color: var(--text); }

/* ── скрипты по образцам: выбор скрипта, производства и самих образцов ── */
/* Свои имена: `.script-picker` уже занят окном скриптов заказов, где он
   выкладывает варианты колонкой, и чужая раскладка растягивала эти чипы. */
.sample-script-picker { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
/* Чипы двух рядов выглядят одинаково, но значат разное: верхний выбирает
   скрипт, нижний — производство. Стиль общий, имена раздельные. */
.sample-script-factory, .sample-script-chip {
  flex: 0 0 auto; width: auto;
  border: 1px solid var(--line); background: var(--panel); color: var(--text);
  border-radius: 20px; padding: 5px 12px; font-size: 13px; cursor: pointer;
}
.sample-script-factory:hover, .sample-script-chip:hover { border-color: var(--muted); }
.sample-script-factory.on, .sample-script-chip.on {
  background: var(--accent-dim); border-color: var(--accent); font-weight: 600;
}
.sample-script-hint { font-size: 12px; color: var(--muted); margin-bottom: 8px; }

/* ── окно с готовым письмом —————————————————— */
/* Кому отправлять — первой строкой и не петитом: письмо читает то
   фабрика, то модель, а скопированный текст легко уходил не в тот чат.
   Одна строка на все восемь окон со скриптами (24.09.2026). */
.ask-to {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  padding: 10px 12px; margin-bottom: 12px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius);
}
.ask-to-label { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.ask-to-name { font-weight: 650; overflow-wrap: anywhere; }
/* Адресатов несколько — это предупреждение, а не справка: одно письмо
   называет чужие номера и уйти должно каждому по отдельности. */
.ask-to-many { border-color: var(--warn); }
/* Место под строкой: пока адресат не выбран, места оно не занимает. */
.ask-to-slot:empty { display: none; }
/* Переключатель языка и счёт посылок — одной строкой над текстом. */
.ask-script-head {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 8px; margin-bottom: 8px;
}
.ask-script-head .sample-script-hint { margin-bottom: 0; }

/* Список образцов с галочками: что именно уходит в текст скрипта. */
/* Список образцов не съедает окно целиком: под ним ещё адрес, текст письма и
   кнопки, а до них надо доскроллить. Шести образцов одного товара хватает,
   чтобы предыдущая высота в 320px оставляла кнопки за краем экрана. */
.sample-pick-list { max-height: 190px; overflow-y: auto; margin-bottom: 10px; }
.sample-script-address { margin-bottom: 6px; }
/* Плюсы и минусы примерки: строка и крестик рядом, поле тянется. */
.fitting-lines { margin-bottom: 8px; }
.fitting-line { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.fitting-line input { flex: 1 1 auto; min-width: 0; }
.sample-pick-group {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); margin: 10px 0 4px;
}
.sample-pick-group:first-child { margin-top: 0; }
.sample-pick-row {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 9px; border-radius: 9px; background: var(--panel-2);
  font-size: 12.5px; cursor: pointer; margin-bottom: 4px;
}
.sample-pick-row input[type="checkbox"],
.sample-pick-row input[type="radio"] { flex: 0 0 auto; }
.sample-pick-row .grow { flex: 1; min-width: 0; color: var(--muted); }
/* «Решение по примерке»: полоса уже завершённых отделена, но не заперта.
   Выбрать такой образец можно — решение иногда оформляют задним числом. */
.sample-pick-group.muted { opacity: .7; }
.sample-pick-row.spent { opacity: .72; }
.sample-pick-row.spent:hover { opacity: 1; }
/* У решения свой список, и он длиннее общего: полос три, а под ним ещё плюсы,
   минусы и текст письма. Держим его чуть выше, но всё так же прокручиваемым. */
.sample-pick-list { max-height: 230px; }

/* ── товары: вкладки «Тестируется» и «Действующие» ──────── */
.product-tabs {
  /* Переносим, а не обрезаем: на узком окне четвёртая кнопка уезжала за край
     контейнера и нажать её было нечем. */
  display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 12px;
}
.product-tab {
  width: auto; display: inline-flex; align-items: baseline; gap: 6px;
  border: 1px solid var(--line); background: var(--panel); color: var(--muted);
  border-radius: 20px; padding: 6px 14px; font-size: 13.5px; cursor: pointer;
}
.product-tab:hover { color: var(--text); }
.product-tab.on {
  background: var(--accent-dim); border-color: var(--accent);
  color: var(--text); font-weight: 600;
}

/* ── «Продукты», вид «По фабрикам» (17.09.2026) ──────────
   Фабрика — ВКЛАДКА, её текущая работа — строки внутри. Действующие и
   отложенные юниты лежат под свёрткой: они у фабрики есть, но это не работа.

   Полоса вкладок прокручивается вбок и не переносится: фабрик бывает десяток,
   и второй ряд кнопок менял бы высоту шапки от бренда к бренду. */
.factory-tabs {
  display: flex; margin: 2px 0 14px; max-width: 980px;
  border-bottom: 1px solid var(--line);
}
.factory-tabs-scroll {
  display: flex; gap: 4px; min-width: 0; flex: 1 1 auto;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
  padding-bottom: 1px;
}
.factory-tab {
  flex: 0 0 auto; display: inline-flex; align-items: baseline; gap: 7px;
  max-width: 260px; padding: 7px 13px; margin-bottom: -1px;
  border: 1px solid transparent; border-bottom: none; border-radius: 9px 9px 0 0;
  background: transparent; color: var(--muted); font: inherit; font-size: 13.5px;
  cursor: pointer; white-space: nowrap;
}
.factory-tab-name { overflow: hidden; text-overflow: ellipsis; }
.factory-tab:hover { color: var(--text); background: var(--hover); }
/* Активная вкладка видна и цветом, и рамкой: одного фона мало — в тёмной теме
   он почти сливается с полосой. */
.factory-tab.on {
  background: var(--panel); border-color: var(--line); border-bottom-color: var(--panel);
  color: var(--text); font-weight: 650;
}
.factory-tab:focus-visible { outline: 2px solid var(--ink-green); outline-offset: -2px; }
.factory-tab-n {
  flex: 0 0 auto; border-radius: 9px; padding: 0 6px;
  background: var(--panel-2); font-size: 11px; font-weight: 500; color: var(--muted);
}
.factory-tab.on .factory-tab-n { background: var(--accent); color: var(--ink); }
/* Фабрика без текущей работы: вкладка остаётся (действующие и отложенные у неё
   есть), но не зовёт себя нажать. */
.factory-tab.idle { opacity: .62; }
.factory-tab.idle .factory-tab-n { opacity: .7; }
/* «в библиотеке»: по фабрике не работают, но её юниты — бренда, и вкладка
   остаётся. Подпись словами, а не цветом: цвет тут уже занят «нет работы». */
.factory-tab-shelved {
  flex: 0 0 auto; font-size: 11px; font-weight: 500; color: var(--muted);
}
.factory-tab-panel { max-width: 980px; }
.factory-unit-group {
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--panel); padding: 12px 14px;
}
.factory-unit-group-head {
  display: flex; align-items: baseline; gap: 9px; margin-bottom: 9px;
  padding-bottom: 8px; border-bottom: 1px solid var(--line-4);
}
.factory-unit-group-name { font-size: 14.5px; font-weight: 650; min-width: 0; overflow-wrap: anywhere; }
.factory-unit { padding: 7px 0; border-bottom: 1px solid var(--line-4); min-width: 0; overflow-wrap: anywhere; }
.factory-unit:last-child { border-bottom: none; }
.factory-unit-head { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.factory-unit-name { font-size: 13.5px; font-weight: 600; min-width: 0; }
.factory-unit-code { font-size: 12.5px; }
.factory-unit-price { font-size: 12.5px; color: var(--text-2); margin-left: auto; }
.factory-unit-tag {
  padding: 1px 7px; border-radius: 999px; font-size: 11px; font-weight: 600;
  background: var(--panel-3); color: var(--text-3);
}
.factory-unit-meta, .factory-unit-note { font-size: 11.5px; margin-top: 3px; }
.factory-unit-result { display: flex; align-items: baseline; gap: 7px; margin-top: 4px; font-size: 11.5px; }
.factory-unit-result.rejected .status-pill { background: var(--tile-rose-bg-3); color: var(--ink-rose); }
.factory-unit-result.approved .status-pill { background: var(--accent-dim); color: var(--ink-green); }
.factory-unit-more { margin-top: 8px; }
.factory-unit-more > summary {
  cursor: pointer; font-size: 12px; color: var(--muted); padding: 3px 0;
}
.factory-unit-more > summary:hover { color: var(--text); }

/* ── товары: список строк, подробности — боковой панелью ── */
.product-feed { display: flex; flex-direction: column; gap: 6px; max-width: 900px; }
.product-row {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 14px; cursor: pointer;
  border-radius: 12px; background: var(--panel);
  box-shadow: 0 1px 2px var(--c-rgba-80-66-48-06-s);
}
.product-row:hover { box-shadow: 0 2px 8px var(--c-rgba-80-66-48-12-s); }
.product-row.testing { border-left: 4px solid var(--c-d9b96a-l); }
.card-thumb {
  position: relative; flex: 0 0 auto; width: 46px; height: 58px;
  border-radius: 8px; overflow: hidden; background: var(--bg);
  border: 1px solid var(--line);
}
.card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-thumb.empty {
  display: flex; align-items: center; justify-content: center; text-align: center;
  font-size: 9px; color: var(--muted); padding: 2px;
}
.card-thumb-count {
  position: absolute; right: 2px; bottom: 2px; padding: 0 4px;
  border-radius: 6px; background: var(--c-rgba-59-50-39-72-s); color: var(--on-accent); font-size: 10px;
}
.product-summary-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.product-name { font-family: var(--font-display); font-size: 16px; }
.product-summary-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.product-row-side { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
.product-price {
  flex: 0 0 auto; font-family: var(--font-display); font-size: 16px;
  font-variant-numeric: tabular-nums;
}

/* Карточка, на которую пришли по ссылке: подсветка на пару секунд, чтобы
   глазами найти её в списке. Ссылки на карточки CRM — решение от 29.08.2026. */
.focused {
  outline: 2px solid var(--accent); outline-offset: 2px;
  animation: focus-fade 2.4s ease-out 1s forwards;
}
@keyframes focus-fade { to { outline-color: transparent; } }

/* Итог утреннего разбора делится по брендам (решение Q117 от 29.08.2026):
   человек стоит в одном бренде, а в общем списке были заказы другого. */
.digest-brand { margin-top: 12px; }
.digest-brand:first-of-type { margin-top: 6px; }
.digest-brand-title {
  font-size: 12px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  color: var(--muted); padding-bottom: 4px; margin-bottom: 6px;
  border-bottom: 1px solid var(--line);
}

/* ── лог: журнал действий по дням ───────────────────────── */
.log-days { display: flex; flex-direction: column; gap: 18px; max-width: 760px; }
.log-list { display: flex; flex-direction: column; gap: 4px; }
.log-row {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 7px 11px; border-radius: 9px; background: var(--panel);
  box-shadow: 0 1px 2px var(--c-rgba-80-66-48-05-s);
}
.log-time {
  flex: 0 0 auto; font-variant-numeric: tabular-nums;
  font-size: 12px; color: var(--muted);
}
.log-actor {
  flex: 0 0 auto; font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; border-radius: 20px; padding: 1px 8px;
}
.log-actor-board { background: var(--tile-green-bg-2); color: var(--c-4a7c59-t); }
.log-actor-agent { background: var(--tile-violet-bg-2); color: var(--c-7c6a92-t); }
.log-actor-system { background: var(--tile-cream-bg); color: var(--c-a8742f-t); }
/* Основа 0, а не по содержимому: строка журнала теперь переносится (перечень
   уборки живёт под ней), и длинное действие иначе уезжало бы на вторую строку
   целиком вместо того, чтобы просто ужаться. */
.log-action { flex: 1 1 0; font-size: 13px; min-width: 0; overflow-wrap: anywhere; }

/* Действие без бренда: видно в любом бренде, но серым и с пометкой «общее»
   (решение Q116 от 29.08.2026) — иначе чужая строка читается как своя. */
.log-row.log-shared { opacity: .72; }
.log-common {
  flex: 0 0 auto; font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); border: 1px solid var(--line); border-radius: 20px; padding: 1px 7px;
}

/* Перечень при строке журнала: что именно унесла еженедельная уборка (Q89). */
.log-items { flex: 1 0 100%; margin-top: 2px; font-size: 12px; }
.log-items > summary {
  cursor: pointer; color: var(--muted); list-style: none;
  border-bottom: 1px dashed var(--line); display: inline-block;
}
.log-items > summary::-webkit-details-marker { display: none; }
.log-items-list { display: flex; flex-direction: column; gap: 2px; margin: 5px 0 2px 2px; }
.log-item { font-size: 12.5px; color: var(--ink); overflow-wrap: anywhere; }
.log-item::before { content: '• '; color: var(--muted); }
/* Хвост списка: сколько показано и чем добрать. Той же ширины, что и строки. */
.log-more {
  display: flex; align-items: center; gap: 12px;
  max-width: 760px; margin-top: 14px; font-size: 13px;
}

/* ── копирование контактов: одна кнопка «Скопировать адрес» ──────────────── */
/* Выпадашка с выбором языка подписей убрана 24.09.2026: адрес копируют одним
   видом — 名称 / 地址 / 电话, — и выбирать было не из чего. */
.copy-menu { position: relative; display: inline-flex; }

/* Селект с кнопкой копирования в одной строке формы. */
.row-inline { display: flex; gap: 6px; align-items: center; min-width: 0; }
.row-inline > :first-child { flex: 1; min-width: 0; }

/* Адрес и контакты в досье фабрики. */
.factory-contact {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  justify-content: space-between;
  padding: 12px 14px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.factory-contact-text { white-space: pre-wrap; word-break: break-word; font-size: 13px; }
.factory-contact .entity-card-actions { margin-top: 0; flex-shrink: 0; }

.script-preview {
  white-space: pre-wrap;
  word-break: break-word;
  font: inherit;
  font-size: 13px;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin: 0 0 12px;
}
/* Пока текст скрипта не подставлен — рамка вокруг пустоты незачем. */
.script-preview:empty { display: none; }

/* ── перенос разработки, списки выбора и история карточки ─── */

.transfer-choices { display: grid; gap: 10px; margin-top: 14px; }
.transfer-choice {
  display: grid; gap: 4px; text-align: left; padding: 12px 14px;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  cursor: pointer; font: inherit; color: inherit;
}
.transfer-choice:hover { border-color: var(--accent); }
.transfer-choice strong { font-size: 14px; }
.transfer-choice small { color: var(--muted); line-height: 1.4; }

.pick-list { max-height: 220px; overflow-y: auto; }
.pick-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 10px; border-bottom: 1px solid var(--line); cursor: pointer;
}
.pick-row:last-child { border-bottom: 0; }
.pick-row .grow { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pick-row .nm { font-size: 13px; }
.pick-group {
  position: sticky; top: 0; z-index: 1;
  padding: 6px 10px; background: var(--bg);
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
}
.pick-row .muted { font-size: 11px; }

.card-history { margin-top: 14px; }
.card-history > summary {
  cursor: pointer; font-size: 12px; color: var(--muted);
  padding: 6px 0; list-style: none;
}
.card-history > summary::-webkit-details-marker { display: none; }
.card-history > summary::before { content: '▸ '; }
.card-history[open] > summary::before { content: '▾ '; }

.dev-table-move { white-space: nowrap; }
.dev-table-move .btn { margin-right: 6px; }

/* ── шапка: кнопка «назад» ──────────────────────────────── */
.icon-btn {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: 0 0 auto;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--panel); color: var(--muted); cursor: pointer;
  /* `button` не наследует шрифт сам: внутри значка сейчас только svg, но
     буква, поставленная сюда завтра, набралась бы Arial'ом 13.33 px —
     единственным местом в CRM, где вылезает шрифт браузера (аудит
     24.09.2026). */
  font: inherit;
}
.icon-btn:hover:not(:disabled) { background: var(--hover); color: var(--text); border-color: var(--accent); }
.icon-btn:disabled { opacity: .35; cursor: default; }

/* ── две пары стрелок, которые нельзя путать ───────────────
 *
 * «Назад/вперёд» — ходьба по CRM: данных они не трогают. «Отменить/вернуть» —
 * правка базы. Раньше рядом стояли две пары похожих стрелок без единого
 * признака, что одна из них меняет данные.
 *
 * Различаем тремя способами сразу: формой значка (уголок против загнутой
 * стрелки), группировкой (навигация — одной кнопкой из двух половин) и
 * разделителем между парами.                                              */
.nav-arrows { display: flex; flex: 0 0 auto; }
.nav-arrows .icon-btn { border-radius: 0; }
.nav-arrows .icon-btn:first-child { border-radius: 9px 0 0 9px; }
.nav-arrows .icon-btn:last-child { border-radius: 0 9px 9px 0; margin-left: -1px; }
.nav-arrows .icon-btn:hover:not(:disabled) { z-index: 1; }
.topbar-split {
  width: 1px; height: 20px; flex: 0 0 auto;
  background: var(--line); margin: 0 2px;
}
/* Отмена — действие над данными, и в наведении она отзывается своим цветом. */
.undo-arrow:hover:not(:disabled) { border-color: var(--ink-rose); color: var(--ink-rose); }

/* Переключатель темы: только значок, в правом верхнем углу шапки.
 *
 * Стоял внизу меню; 17.09.2026 переехал сюда по просьбе пользователя. Своего
 * размера у него нет — он такая же `icon-btn`, как стрелки, и ряд шапки от
 * него не разъезжается. Подписи нет: солнце, месяц и половинка говорят сами,
 * а текущий режим — в подсказке при наведении (её ставит `ui/theme.js`). */
.topbar-theme { color: var(--muted); }

/* ── контрагенты: анкета фабрики и расчёт по артикулам ──── */
.party-card .entity-card-head { flex-wrap: wrap; }
.party-card .entity-card-head strong { min-width: 0; word-break: break-word; }
/* Адрес фабрики бывает в три строки китайского — в списке хватает двух. */
.party-card .entity-card-title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.party-card .entity-card-meta .chip {
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Счётчики прижаты к кнопкам, а не к середине: `margin-top: auto` уже есть
   у полосы действий, и два таких отступа делили бы пустоту пополам. */
.party-counters { color: var(--muted); }

.fact-list { display: flex; flex-direction: column; gap: 1px; background: var(--line); border-radius: 10px; overflow: hidden; }
/* Третья колонка — правка. Ширина фиксированная и пустая, пока курсор не над
   строкой: значение не должно ездить вбок от одного наведения. */
.fact-row {
  display: grid; grid-template-columns: 150px 1fr 76px;
  gap: 12px; padding: 8px 12px; background: var(--panel); align-items: start;
}
.fact-label { font-size: 11.5px; color: var(--muted); }
.fact-value { font-size: 13px; white-space: pre-line; word-break: break-word; }
.fact-value a { color: var(--accent-ink, var(--text)); }

.quote-wrap { overflow-x: auto; }
.quote-table { width: 100%; table-layout: fixed; }
.quote-table th { font-size: 10.5px; }
.quote-table td, .quote-table th { padding: 6px 8px; }
.quote-photo img {
  width: 44px; height: 58px; object-fit: cover; display: block;
  border-radius: 6px; border: 1px solid var(--line); cursor: zoom-in;
}
.quote-cell {
  font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Звёзды, которые посчитала CRM: показываем, но не даём нажимать. */
.stars.readonly .star { cursor: default; opacity: 1; }
.stars.readonly .star:not(.on) { opacity: .35; }
/* Пояснение под названием авто-показателя — там, где у ручных оценок поле
   комментария, поэтому строка держит ту же высоту. */
.metric-note-static {
  font-size: 12px; color: var(--muted, var(--muted-2)); line-height: 1.4; margin-top: 2px;
}

/* «Мало данных» вместо звёзд — состояние, а не прочерк: прочерк молчал, а
   молчание на месте оценки читается как ноль. Счёт наблюдений уехал под «ⓘ»
   у названия (24.09.2026). */
.metric-nodata { text-align: right; min-width: 100px; }
.metric-nodata-title { font-size: 12px; font-weight: 600; color: var(--muted); }
/* Название и значение — одной строкой: «На сколько опаздывает · 4 дня». */
.metric-name { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.metric-value {
  font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--ink-green);
}
/* Показатель без звёзд по замыслу: цифра и слово «справка» вместо оценки. */
.metric-plain { text-align: right; min-width: 90px; }
.metric-plain-value {
  font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums;
}

/* Удаление в «Завершено»: кнопка проявляется по наведению на строку — чтобы
   её нельзя было задеть, листая архив, но и искать её не приходилось. */
/* Ячейке достаётся общее правило `.row-actions` (flex + отступ сверху) — оно
   писалось для рядов кнопок в окнах, а здесь выбивало ✕ из строки на 6px вниз
   и вытесняло колонку из табличной раскладки. Возвращаем ячейке ячейкино. */
.closed-table td.row-actions {
  display: table-cell; margin: 0; width: 34px; text-align: right;
  padding-right: 8px; vertical-align: middle;
}
.closed-table .icon-btn {
  border: 0; background: none; cursor: pointer; font-size: 13px; line-height: 1;
  padding: 4px 6px; border-radius: 6px; opacity: 0; transition: opacity .12s ease;
  color: var(--muted, var(--muted-2));
}
.closed-table tr:hover .icon-btn,
.closed-table .icon-btn:focus-visible { opacity: 1; }
.closed-table .icon-btn.danger:hover { background: var(--c-f6e3dd-s); color: var(--c-a8422a-t); }
@media (prefers-reduced-motion: reduce) { .closed-table .icon-btn { transition: none; } }

/* Состав заказа: цифры в колонку, итог отделён линией. */
.items-table { width: 100%; font-size: 13px; }
.items-table td, .items-table th { padding: 7px 10px 7px 0; }
.items-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Подчёркивание тут осталось от времён, когда сам артикул был ссылкой на 1688.
   Артикул давно ведёт в свою карточку, а наружу — отдельный значок рядом. */
.items-table .code a { color: inherit; text-decoration: none; }
.items-table .items-total td { border-top: 1px solid var(--line, var(--line-2)); font-weight: 600; }

/* Миниатюра товара в составе заказа: маленькая и одинаковой высоты у всех
   строк, чтобы таблица не прыгала. */
.items-table .item-thumb { width: 46px; padding-right: 10px; }
.items-table .item-thumb img {
  width: 40px; height: 52px; object-fit: cover; border-radius: 5px;
  background: var(--panel, var(--panel-4)); display: block; cursor: zoom-in;
}

/* Временный раздел беты: отделён линией и приглушён, чтобы не читался наравне
   с основными разделами CRM. Уедет вместе с бетой. */
.side-item-beta { margin-top: 10px; border-top: 1px dashed var(--line, var(--line-2)); padding-top: 12px; opacity: .62; }
.side-item-beta:hover, .side-item-beta.active { opacity: 1; }
.side-item-beta .label { font-size: 12.5px; }
.beta-mark {
  margin-left: auto; font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted, var(--muted-2)); border: 1px solid var(--line, var(--line-2));
  border-radius: 4px; padding: 1px 4px;
}

/* ── Отладка Гермеса (временный раздел) ─────────────────────────────────── */
.trace-list { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.trace-card { background: var(--panel, var(--panel-4)); border: 1px solid var(--line, var(--line-2)); border-radius: 10px; }
.trace-card .fold-head { margin: 0; padding: 0; }
.trace-card .fold-toggle {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 11px 14px; background: none; border: 0; cursor: pointer; text-align: left;
}
.trace-title { font-weight: 600; font-size: 14px; }
.trace-when { margin-left: auto; font-size: 12px; white-space: nowrap; }
/* Исход виден до раскрытия: по цвету полосы слева понятно, где искать ошибку. */
.trace-card { border-left: 3px solid var(--line, var(--line-2)); }
.trace-card.trace-applied { border-left-color: var(--c-7fae8c-l); }
.trace-card.trace-review { border-left-color: var(--c-d0a43f-l); }
.trace-card.trace-unchanged { border-left-color: var(--c-c9c7bf-l); }
.trace-card.trace-refused, .trace-card.trace-error { border-left-color: var(--c-c47a63-l); }
.trace-mark { font-size: 11px; }
.trace-mark-review { background: var(--c-f6ecd4-s); color: var(--c-7a5c12-t); }
.trace-mark-refused, .trace-mark-error { background: var(--c-f4e2dc-s); color: var(--c-8f4028-t); }

.trace-card .fold-body { padding: 4px 14px 14px; }
/* Кнопка «Открыть «Ревью»» без причины лежит прямо в теле после шагов. */
.trace-card .fold-body > .btn { margin-top: 7px; }
.trace-step { display: grid; grid-template-columns: 168px 1fr; gap: 10px; padding: 7px 0; align-items: start; }
.trace-step + .trace-step { border-top: 1px dashed var(--line, var(--line-2)); }
.trace-step-label { font-size: 12px; color: var(--muted, var(--muted-2)); padding-top: 2px; }
.trace-step-body { font-size: 13.5px; line-height: 1.5; }

.trace-msg { border-left: 2px solid var(--line, var(--line-2)); padding: 2px 0 2px 10px; margin-bottom: 8px; }
.trace-msg.out { border-left-color: var(--c-b9cdbf-l); }
.trace-msg-meta { display: flex; gap: 10px; font-size: 11.5px; color: var(--muted, var(--muted-2)); }
.trace-msg-text { white-space: pre-wrap; word-break: break-word; }
.trace-msg-ru { color: var(--muted, var(--muted-2)); font-size: 12.5px; margin-top: 2px; }
/* Файл-основание: имя и вид документа над его текстом. */
.trace-msg-file { color: var(--muted, var(--muted-2)); font-size: 12.5px; margin-top: 6px; font-weight: 600; }

.trace-change + .trace-change { margin-top: 8px; }
.trace-change-section { font-size: 12px; color: var(--muted, var(--muted-2)); margin-bottom: 3px; }
.trace-field { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; font-size: 13px; }
.trace-field-name { min-width: 150px; }
.trace-field-from { color: var(--muted, var(--muted-2)); text-decoration: line-through; }
.trace-arrow { color: var(--muted, var(--muted-2)); }
.trace-field-to { font-weight: 600; }
.trace-line { font-size: 13.5px; line-height: 1.5; }
.trace-line + .trace-line { margin-top: 2px; }
.trace-untouched { color: var(--muted, var(--muted-2)); }

/* Свёрнутая строка должна опознаваться без раскрытия: чей чат и о чём речь.
   Обе подписи ужимаются первыми — дату справа и метку исхода они не двигают. */
.trace-where {
  font-size: 11.5px; white-space: nowrap; flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis;
}
/* Суть просьбы — предложение, а не подпись: капслок и разрядка тут только
   мешают читать. Ужимается она первой: это подсказка, а не название строки. */
.trace-gist {
  font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  flex: 1 1 auto; min-width: 0;
  text-transform: none; letter-spacing: 0; font-weight: 400;
}
/* Заголовок ужимается последним и не короче своего полного слова: строка
   обязана помещаться целиком, но узнаётся она именно по нему. */
.trace-title {
  flex: 0 1 auto; min-width: 24ch; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.trace-card .fold-toggle > .chip { flex: 0 0 auto; }
.trace-mark-rolled { background: var(--c-ece7f1-s); color: var(--tile-violet-text); }
/* Значения, которых в карточках уже нет: запись о них остаётся, сила — нет. */
.trace-changes.rolled { opacity: .55; text-decoration: line-through; }
.trace-changes.rolled .trace-field-from { text-decoration: none; }
.trace-msg-text.clamped {
  display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical;
  overflow: hidden;
}
.trace-msg-more { padding: 0; height: auto; border: 0; background: none; font-size: 12px; }
.trace-cut { margin-top: 8px; }

/* Что Гермес выучил: список правил в шапке раздела. */
.trace-rules {
  margin-top: 14px; background: var(--panel, var(--panel-4));
  border: 1px solid var(--line, var(--line-2)); border-radius: 10px;
  border-left: 3px solid var(--c-b3d0be-l);
}
.trace-rules .fold-head { margin: 0; padding: 0; }
.trace-rules .fold-toggle {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 11px 14px; background: none; border: 0; cursor: pointer; text-align: left;
}
.trace-rules .fold-body { padding: 0 14px 12px; }
.trace-rule-row {
  display: flex; align-items: start; gap: 10px; padding: 8px 0;
  border-top: 1px dashed var(--line, var(--line-2));
}
.trace-rule-body { flex: 1; min-width: 0; }
.trace-rule-name { font-size: 13px; }
.trace-rule-src { font-size: 12px; margin-top: 2px; }

/* Экраны раздела и то, что стоит между переключателем и лентой. */
.trace-screens { margin: 0 0 10px; }
.trace-untranslated {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin: 0 0 10px;
}
/* Кучи причин на экране «Не смог записать»: та же карточка журнала, только
   внутри лежит список, а не шаги. */
.trace-losses { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.trace-losses > .trace-card > .fold-body { padding-top: 0; }
.trace-losses .trace-list { margin-top: 6px; }
.trace-rules-screen { display: flex; flex-direction: column; gap: 10px; }
.trace-rules-screen .trace-rules { margin-top: 0; }
/* Заголовок целой коробки, а не строки в свёрнутом виде: обрезать его по
   ширине нечего — переносим. */
.trace-rules > .trace-title {
  display: block; margin: 0; padding: 11px 14px 4px; font-size: 13px;
  min-width: 0; overflow: visible; white-space: normal;
}
.trace-sections { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (max-width: 720px) {
  .trace-step { grid-template-columns: 1fr; gap: 2px; }
  .trace-field-name { min-width: 0; }
  .trace-gist { display: none; }
}

/* Кликабельные номера и названия. Ни цвета, ни подчёркивания — по просьбе:
   текст остаётся текстом, о том, что он ведёт в карточку, говорит курсор и
   подсказка. Обводка появляется только при переходе с клавиатуры. */
/* Внешняя ссылка позиции заказа на 1688 — значком рядом с артикулом: сам
   артикул ведёт в карточку товара CRM (29.08.2026), и две двери не должны
   притворяться одной. */
.item-1688 { margin-left: 6px; text-decoration: none; color: var(--muted); font-size: 12px; }
.item-1688:hover { color: var(--ink-green); }

/* Кликабельная сущность: при наведении подсвечивается, но НЕ подчёркивается.
   Решение пользователя 30.08.2026. Без подсветки ссылка была неотличима от
   обычного текста — о том, что имя артикула открывает его карточку, узнать
   было неоткуда. */
.entity-link {
  cursor: pointer;
  border-radius: 4px;
  padding: 0 2px; margin: 0 -2px;
  text-decoration: none;
}
.entity-link:hover { background: var(--accent-dim); color: var(--ink-green); }
.entity-link:focus-visible { outline: 2px solid var(--accent, var(--c-0e5d59-l)); outline-offset: 2px; border-radius: 3px; }

/* ── перевод китайского по нажатию ─────────────────────── */

/* Кнопка стоит у каждого китайского куска во всех разделах. Мелкая и
   приглушённая нарочно: мест, где она появляется, много, и она не должна
   спорить с самим текстом. Сам по себе перевод не запускается — только
   нажатием, и оригинал остаётся на месте. */
.nt-btn {
  display: inline-block;
  /* У обрезающего узла кнопка встаёт следующей строкой — и в колонке из
     `flex`/`grid` растянулась бы на всю ширину карточки. Ширина по тексту и
     прижатие к началу строки нужны именно там. */
  align-self: start;
  justify-self: start;
  width: fit-content;
  max-width: 100%;
  margin-left: 6px;
  padding: 0 5px;
  border: 1px solid var(--line);
  border-radius: 4px;
  font-size: 10.5px;
  line-height: 1.5;
  font-weight: 500;
  color: var(--muted);
  background: var(--panel-2);
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
  user-select: none;
}
.nt-btn:hover { color: var(--text); border-color: var(--accent); }
.nt-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nt-btn[data-busy="1"] { cursor: default; opacity: .6; }

/* Перевод встаёт под оригиналом, а не вместо него: сверить с исходником иначе
   будет нечем. Отдельной строкой — так он не сливается с китайским. */
.nt-ru {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font-size: .92em;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ── список горячих клавиш ─────────────────────────────── */

.keys-list { display: grid; gap: 8px; }
.keys-row { display: grid; grid-template-columns: 92px 1fr; gap: 12px; align-items: baseline; }
.keys-row kbd {
  font-family: var(--font-display);
  font-size: 13px;
  text-align: center;
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 7px;
  background: var(--panel-2);
  color: var(--text);
}
.keys-row span { font-size: 13.5px; color: var(--muted); }

/* Строка свежести открывает итог последнего разбора — если он был. */
.brand-sub.has-digest { cursor: pointer; text-decoration-line: underline; text-decoration-style: dotted; text-underline-offset: 3px; }

/* Строка, которую уже убрали с экрана, но ещё можно вернуть тостом. */
.row-removing { opacity: .35; pointer-events: none; }

/* «Сохранено» вместо кнопки «Сохранить»: поля уходят на сервер сами. */
.save-mark {
  font-size: 11px;
  color: var(--ink-green);
  opacity: 0;
  transition: opacity .18s ease;
}
.save-mark.on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .save-mark { transition: none; } }

/* «Выполнить всю группу» — та же галочка, что и в строке, правее счётчика. */
.task-lane-all { margin-left: auto; margin-top: 0; }

/* ── выделение пачки заказов на доске ──────────────────── */

.card.picked {
  outline: 2px solid var(--gossamer-green);
  outline-offset: 1px;
}
/* Над доской плашка выделения висит поверх: доска — ряд колонок, и обычная
   плашка встала бы в него ещё одной колонкой. */
.bulkbar.floating {
  position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%);
  z-index: 30; margin-top: 0;
  box-shadow: 0 6px 20px var(--c-rgba-80-66-48-16-s);
}
body.order-open .bulkbar.floating { left: calc(50% - 210px); }

/* Выполненные живут на своей вкладке и занимают ту же ширину, что и активные:
   узкой колонки, ради которой текст обрезался тремя строками, больше нет. */
.task-lane.done .task-row .task-text { overflow-wrap: anywhere; }

/* ── продукты: список слева, досье справа ──────────────── */

/* Раздел отдаёт «Продуктам» всю высоту, а прокручиваются колонки по
   отдельности: список слева и досье справа. Иначе, уехав по списку вниз,
   человек уводил за собой и карточку — она оказывалась выше экрана. */
.section-view:has(.card-split) {
  display: flex; flex-direction: column; overflow: hidden; padding-bottom: 18px;
}
.card-split { display: grid; grid-template-columns: 306px minmax(0, 1fr); gap: 14px; flex: 1; min-height: 0; }
.card-list {
  background: var(--panel); border-radius: 13px; padding: 8px;
  display: flex; flex-direction: column; gap: 2px;
  box-shadow: 0 1px 2px var(--c-rgba-80-66-48-06-s); overflow-y: auto;
}
/* Заголовок группы — кнопка: группы свёрнуты, пока их не раскроют. */
.card-group {
  display: flex; align-items: center; gap: 7px;
  width: 100%; padding: 8px 8px 7px; margin-top: 4px;
  border: none; background: transparent; border-radius: 9px;
  font: inherit; text-align: left; cursor: pointer;
}
.card-group:hover { background: var(--panel-2); }
.card-group:focus-visible { outline: 2px solid var(--ink-green); outline-offset: -2px; }
.card-group-arrow {
  display: inline-flex; color: var(--muted); flex: 0 0 auto;
  transition: transform .14s ease;
}
.card-group.open .card-group-arrow { transform: rotate(90deg); }
.card-group-pill {
  border-radius: 999px; padding: 3px 10px;
  font-size: 11.5px; font-weight: 600; letter-spacing: 0; white-space: nowrap;
}
.card-group .lane-count { margin-left: 0; }
.card-group-body { display: flex; flex-direction: column; gap: 2px; }
@media (prefers-reduced-motion: reduce) { .card-group-arrow { transition: none; } }
.card-row {
  display: flex; align-items: center; gap: 9px; padding: 7px 9px;
  border: none; border-radius: 9px; background: transparent;
  font: inherit; text-align: left; cursor: pointer; width: 100%;
}
.card-row:hover { background: var(--panel-2); }
.card-row.on { background: var(--accent-dim); }
.card-row:focus-visible { outline: 2px solid var(--ink-green); outline-offset: -2px; }
.card-thumb {
  width: 30px; height: 38px; border-radius: 6px; background: var(--c-f2ede2-s);
  flex: 0 0 auto; overflow: hidden;
}
.card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-row-main { min-width: 0; flex: 1; }
.card-row-name { font-family: var(--font-display); font-size: 14px; }
.card-row-sub {
  font-size: 11px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card-dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto; }

.card-detail { overflow-y: auto; }
/* ДОСЬЕ ЮНИТА МЕРИТ СВОЁ МЕСТО, А НЕ ОКНО (17.09.2026).
   Одна и та же разметка стоит в двух разных по ширине местах: правой колонкой
   «Продуктов» (там под неё метров 700) и боковой панелью поверх раздела (404).
   Переносы при этом были описаны через `@media (max-width: 1080px)` — то есть
   по ОКНУ. На большом экране панель получала вёрстку широкой колонки: две
   колонки досье по 170 px, внутри них ещё две колонки показателей по 80,
   шесть кнопок этапов в один ряд — и текст лез за края.
   Поэтому `.dossier` объявлен контейнером, а все его переносы считаются от
   собственной ширины. Панель, колонка и узкое окно чинятся одним правилом.
   `overflow-wrap: anywhere` — страховка на длинное имя артикула без пробелов:
   он переносится внутри слова вместо того, чтобы вылезти за рамку. */
.dossier {
  container-type: inline-size; container-name: dossier;
  background: var(--panel); border-radius: 13px; padding: 18px 20px;
  display: flex; flex-direction: column; gap: 14px;
  box-shadow: 0 1px 2px var(--c-rgba-80-66-48-06-s);
  min-width: 0; overflow-wrap: anywhere;
}
/* Поле ввода несёт собственную ширину (по умолчанию ~20 знаков) и в узкой
   колонке распирало строку: цена, причина отказа, дата в ленте. */
.dossier input, .dossier select, .dossier textarea { max-width: 100%; min-width: 0; }
.dossier-top { display: flex; align-items: baseline; gap: 11px; flex-wrap: wrap; }
.dossier-name { font-family: var(--font-display); font-size: 24px; min-width: 0; }
.status-pill[data-stage="development"] { background: var(--tile-violet-bg); color: var(--tile-violet-text); }
.status-pill[data-stage="sample"] { background: var(--tile-rose-bg-3); color: var(--ink-rose); }
.status-pill[data-stage="testing"] { background: var(--tile-yellow-bg); color: var(--ink-yellow); }
.status-pill[data-stage="active"] { background: var(--accent-dim); color: var(--ink-green); }
/* «Сформировать скрипт» стоит в шапке досье, но не спорит с названием:
   прижимаем к правому краю, чтобы этап и номер остались рядом с именем. */
.dossier-script { margin-left: auto; flex: 0 0 auto; }

/* Решение по примерке правится прямо в окне: моноширинный текст, чтобы
   «???» и «?» стояли ровно и было видно, где ещё не заполнено. */
.fitting-script {
  width: 100%;
  min-height: 300px;
  font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}

/* Полоса этапов — она же переключатель. */
/* Кнопок в полосе шесть. В узкой панели шесть колонок по 55 px — это
   обрезанные слова, поэтому полоса переносится рядами, а не сжимается. */
.stage-path { display: flex; gap: 7px; flex-wrap: wrap; }
.stage-step {
  flex: 1 1 104px; min-width: 0; padding: 8px 10px; border-radius: 9px;
  background: var(--panel-6); color: var(--muted); border: 1px solid transparent;
  font: inherit; text-align: left; cursor: pointer;
  transition: border-color .12s ease, transform .12s ease;
}
.stage-step.done { background: var(--accent-dim); color: var(--ink-green); }
.stage-step.now { background: var(--tile-yellow-bg); color: var(--ink-yellow); border-color: var(--c-e6d9a3-l); }
.stage-step:hover:not(:disabled) { border-color: var(--gossamer-green); transform: translateY(-1px); }
.stage-step:disabled { cursor: default; }
.stage-name { font-size: 9.5px; text-transform: uppercase; letter-spacing: .4px; }
.stage-when { font-size: 11.5px; font-weight: 700; margin-top: 4px; }
/* «Замена» и «Отложено» — не этапы пути, а свои группы раздела. Пунктир и
   приглушённый фон говорят об этом без подписи: они стоят в одном ряду с
   этапами, но чередой перехода не являются. */
.stage-step.stage-repl { border-style: dashed; border-color: var(--orchid-petal); }
.stage-step.stage-repl.now { background: var(--c-fbf9fd-s); color: var(--tile-violet-text); }
.stage-step.stage-park { border-style: dashed; }
@media (prefers-reduced-motion: reduce) { .stage-step { transition: none; } }

/* Замена — работа над действующим артикулом, вложенная в его же карточку. */
.replacement {
  border: 1px dashed var(--orchid-petal); border-radius: 11px;
  padding: 13px 14px; background: var(--c-fbf9fd-s);
  display: flex; flex-direction: column; gap: 9px;
}
.replacement.done { opacity: .72; border-style: solid; }
.replacement-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.replacement-head strong { font-size: 12.5px; color: var(--tile-violet-text); }
.replacement-chips { display: flex; gap: 6px; flex-wrap: wrap; }
/* Заметки и записи истории приходят многострочными — нумерованный список
   правок, реплика в несколько абзацев. Без этого они слипались в один
   сплошной кусок текста, хотя в старых разделах читались по пунктам. */
.replacement-notes { font-size: 12.5px; white-space: pre-wrap; }

.card-warn {
  background: var(--tile-rose-bg-2); color: var(--ink-rose);
  border-radius: 11px; padding: 11px 13px; font-size: 12.5px;
}

/* Чек-лист закрыт, а карточка ещё на тестировании: последний шаг делает
   человек, и кнопка для него стоит там же, где список. */
.card-ready {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  background: var(--tile-green-bg); color: var(--tile-green-text);
  border-radius: 11px; padding: 11px 13px; font-size: 12.5px;
}

/* Две колонки, пока обе шире 236 px, иначе одна. `min(…, 100%)` вместо голых
   пикселей: в контейнере уже 236 колонка не ужимается и вылезает за край. */
.dossier-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(min(236px, 100%), 1fr));
}
.dossier-grid > * { min-width: 0; }
.blk { font-size: 10px; letter-spacing: 1.2px; text-transform: uppercase; color: var(--muted); }

.price-reductions { display: flex; flex-direction: column; gap: 7px; }
.price-reduction {
  border: 1px solid var(--c-bfd8c7-l); border-radius: 11px; padding: 10px 12px;
  background: var(--c-eef6f0-s); color: var(--tile-green-text);
}
.price-reduction-main { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.price-reduction-main strong { font-size: 13px; }
.price-reduction-delta {
  border-radius: 999px; padding: 2px 7px; background: var(--c-d7eadc-s);
  color: var(--c-2f6941-t); font-size: 11.5px; font-weight: 700;
}
.price-reduction .muted { margin-top: 3px; font-size: 11.5px; }

.card-timeline { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.card-timeline li { display: flex; gap: 11px; align-items: baseline; font-size: 12.5px; }
.tl-date { color: var(--muted); flex: 0 0 128px; white-space: nowrap; }
.card-timeline li { flex-wrap: wrap; }
.tl-text { flex: 1; min-width: 0; white-space: pre-wrap; }
.tl-src { font-size: 10.5px; color: var(--c-a89e8d-t); white-space: nowrap; }

/* Правка записи прямо в строке ленты (17.09.2026).
   Строка, которую можно поправить, отзывается на наведение — иначе щелчок по
   ней нечем предугадать. Выведенные строки ленты («уехал к модели») этого
   класса не получают и выглядят как раньше. */
.card-timeline li.tl-editable { cursor: text; border-radius: 6px; margin: -2px -6px; padding: 2px 6px; }
.card-timeline li.tl-editable:hover { background: var(--panel-2); }
.card-timeline li.tl-editing {
  align-items: flex-start; background: var(--panel-2);
  border-radius: 6px; margin: -3px -6px; padding: 3px 6px;
  box-shadow: inset 0 0 0 1.5px var(--accent);
}
.tl-date-input { flex: 0 0 128px; font-size: 12.5px; padding: 2px 6px; }
.tl-text-input { flex: 1; min-width: 0; font-size: 12.5px; padding: 3px 6px; resize: vertical; }

/* Раскладка САМОГО раздела считается по окну — это правильно: слева список
   карточек, и решает, помещается ли он рядом с досье, именно окно. */
@media (max-width: 1080px) {
  .card-split { grid-template-columns: minmax(0, 1fr); }
}

/* Досье в узком месте описано в конце файла, после всех правил, которые оно
   переопределяет: запрос к контейнеру веса селектору не добавляет, и правило
   `.card-sample-sub` из «образцов внутри карточки» ниже перебивало бы его. */

/* ── новый групповой чат ─────────────────────────────────────────────────────
   По названию группы вспомнить, кто это, невозможно, поэтому вопрос идёт
   вместе с приметами чата и последними репликами. */
.ci-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 10px;
}
.ci-preview {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 168px;
  overflow-y: auto;
  padding: 8px 10px;
  margin-bottom: 12px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel-2);
  font-size: 12px;
}
.ci-preview:empty { display: none; }
.ci-meta:empty { display: none; }
.ci-msg { display: flex; gap: 8px; }
.ci-msg .ci-who { flex: none; color: var(--muted); }
.ci-msg.out .ci-who { color: var(--ink-green); }
.ci-kinds { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 4px; }
.ci-kind {
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.ci-kind[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-dim); }

/* «Похоже, эта фабрика у нас уже есть»: строка совпадения — она же кнопка
   ответа. Того же приёма, что плитки в «Ревью»: нажал на саму вещь, а не
   на номер рядом с ней. */
.ci-matches { display: flex; flex-direction: column; gap: 6px; margin: 12px 0 4px; }
.ci-matches-head { font-size: 12px; color: var(--muted); }
.ci-match {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  width: 100%;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.ci-match:hover { border-color: var(--accent); }
.ci-match[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-dim); }
.ci-match-name { font-weight: 600; white-space: nowrap; }
.ci-suggest { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; margin: -4px 0 4px; }
.ci-suggest .hint { flex: 1 1 auto; }
.ci-match-why { flex: 1 1 100%; font-size: 12px; color: var(--muted); }

/* Пометка «заменяется» на действующем товаре: по артикулу идёт работа.
   Она же ссылка — открывает карточку и показывает саму замену. */
.card-row-name { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.card-row-name > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-repl {
  flex: 0 0 auto;
  font-family: var(--font-display); font-size: 10px;
  border: 1px dashed var(--orchid-petal); border-radius: 999px;
  padding: 1px 7px; color: var(--tile-violet-text); background: var(--c-fbf9fd-s);
  cursor: pointer;
}
.card-repl:hover { background: var(--tile-violet-bg-3); border-style: solid; }
.replacement.just-came { box-shadow: 0 0 0 2px var(--orchid-petal); }

/* Заголовок истории с кнопкой записи этапа. */
.hist-head { display: flex; align-items: center; gap: 10px; }
.hist-add { margin-left: auto; letter-spacing: 0; text-transform: none; }
/* «Замена» в ленте помечена отдельно: это работа над тем же артикулом,
   а не запись из другой карточки. */
.tl-src.tl-repl { color: var(--tile-violet-text); background: var(--tile-violet-bg-3); border-radius: 999px; padding: 1px 7px; }

/* ── оценка решения агента в «Отладке Гермеса» ─────────── */

.trace-verdict {
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 9px;
}
.trace-verdict-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.trace-verdict-actions { display: flex; gap: 7px; align-items: center; flex-wrap: wrap; }
/* Переключатель «неразобранное / оценённое» на экране решений. */
.trace-scope { margin: 0 0 10px; }

/* Крестик «убрать запись»: тихий, пока на карточку не навели. Оценкой он не
   является, поэтому и не должен спорить за внимание с «Верно» и «Неверно». */
.trace-drop {
  margin-left: auto; flex: none; width: 24px; height: 24px; line-height: 1;
  border: 1px solid transparent; border-radius: 6px; background: transparent;
  color: var(--muted); cursor: pointer; opacity: 0; transition: opacity .12s;
}
.trace-card:hover .trace-drop, .trace-drop:focus-visible { opacity: 1; }
.trace-drop:hover { color: var(--text); border-color: var(--line); }

.trace-verdict-clear { background: transparent; border-color: transparent; color: var(--muted); }
.trace-verdict-clear:hover { color: var(--text); border-color: var(--line); }
/* Метка на свёрнутой строке: видно, что запись уже оценена. */
.trace-verdict-mark {
  border-radius: 999px; padding: 1px 8px; font-size: 10.5px; white-space: nowrap;
}
.trace-verdict-mark.correct { background: var(--accent-dim); color: var(--ink-green); }
.trace-verdict-mark.wrong { background: var(--tile-rose-bg-2); color: var(--ink-rose); }
.trace-rule-text {
  background: var(--panel-5); border-radius: 9px; padding: 9px 11px;
  font-size: 12.5px; white-space: pre-wrap;
}
.trace-correction { display: flex; flex-direction: column; gap: 4px; }
.trace-correction-text {
  background: var(--tile-rose-bg-2); color: var(--ink-rose); border-radius: 9px; padding: 9px 11px;
  font-size: 12.5px; white-space: pre-wrap;
}
.trace-correction-form { display: flex; flex-direction: column; gap: 8px; }
.trace-correction-form textarea { width: 100%; }

/* Выбор, куда переместить карточку. */

/* Незаполненное поле анкеты: видно, чего не хватает, но глаз оно не тянет —
   это подсказка, а не проблема. */
.fact-row.fact-missing .fact-value { opacity: .55; font-style: italic; }
.fact-row.fact-missing .fact-input { width: 100%; }

/* Окно разбора: строка производства, у которого нового нет, — приглушена.
   Она не ошибка, просто в этот раз читать нечего. */
.plan-stale { opacity: .6; }
.plan-reread { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); }

/* Отметки об оплате внутри карточки заказа: справка, а не рабочее поле —
   поэтому они мельче и стоят под причиной простоя. */
.pay-marks { gap: 16px; font-size: 13px; color: var(--muted); }
.pay-marks label { display: inline-flex; align-items: center; gap: 6px; }

/* «Примерка» в строке списка карточек: видна, но не спорит с названием. */
.card-row-script { flex: none; margin-left: auto; }

/* Упоминание, которое уже стало основанием карточки: видно, что оно не новое. */
.msg.msg-linked { opacity: .68; }

/* Деньги на этапе воронки: цифра справочная, поэтому мельче названия этапа. */

/* Сколько дошло: блок под составом заказа. Расхождение подсвечиваем — это
   разговор с фабрикой о деньгах, а не справка. */
.items-received { margin-top: 12px; }
.hint.items-gap { color: var(--ink); font-weight: 600; }

/* Сравнение анкет: таблица широкая, поэтому едет по горизонтали внутри себя,
   а не растягивает окно. */
.scroll-x { overflow-x: auto; }
/* ── сравнение анкет фабрик ──────────────────────────────
 *
 * На живых данных это 25 колонок и 14 строк, а в одном ответе 1718 символов
 * ссылок. Свободная ширина колонок растягивала таблицу так, что сравнивать
 * приходилось, прокручивая её в обе стороны и теряя из виду и вопрос, и чей
 * это столбец. Поэтому ширины фиксированные, шапка и первая колонка
 * закреплены, а длинное значение сворачивается, а не раздвигает столбец.   */
.anketa-bar { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.anketa-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.anketa-chips {
  display: flex; flex-wrap: wrap; gap: 5px;
  max-height: 92px; overflow-y: auto;
  padding: 6px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel-2);
}
/* Прокрутка тут одна на обе оси и живёт на этом слое: `position: sticky`
   у шапки и первой колонки считается относительно ближайшего прокручиваемого
   предка, и без него закрепление молча не работало бы. */
.anketa-scroll {
  overflow: auto;
  /* Подпись под таблицей должна оставаться в окне: высота считается от
     экрана за вычетом шапки, полосы кнопок и самой подписи. */
  max-height: min(calc(100vh - 300px), 640px);
  border: 1px solid var(--line); border-radius: 10px;
}
.anketa-table {
  border-collapse: separate; border-spacing: 0;
  table-layout: fixed; width: max-content; min-width: 100%;
  font-size: 12px;
}
/* Ширина считается на месте, под число колонок: мало колонок — занимают всё
   окно, много — ужимаются до читаемого минимума. Запасное значение стоит на
   случай, когда переменную ещё не успели проставить. */
.anketa-col-q { width: 140px; }
.anketa-col-a { width: var(--anketa-col, 168px); }
@media (max-width: 900px) { .anketa-col-q { width: 116px; } }
.anketa-table th, .anketa-table td {
  border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 5px 7px; vertical-align: top; text-align: left;
  overflow-wrap: anywhere;
}
.anketa-table th:last-child, .anketa-table td:last-child { border-right: none; }
.anketa-table tr:last-child td, .anketa-table tr:last-child th { border-bottom: none; }

/* Закреплённая шапка: имя фабрики видно, куда бы ни уехала прокрутка. */
.anketa-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--panel-2); font-weight: 600;
}
.anketa-head-name {
  font-weight: 700;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Закреплённая первая колонка: вопрос виден при прокрутке вправо. */
.anketa-table th.anketa-q {
  position: sticky; left: 0; z-index: 1;
  background: var(--panel); font-weight: 600; white-space: normal;
  /* Тень справа: при прокрутке вбок под закреплённой колонкой всегда торчит
     край наполовину уехавшего столбца. С тенью это читается как «содержимое
     проходит под колонкой», а без неё — как поехавшая вёрстка. */
  box-shadow: 3px 0 6px -3px var(--c-rgba-80-66-48-22-s);
}
.anketa-table tbody tr:nth-child(even) th.anketa-q,
.anketa-table tbody tr:nth-child(even) td { background: var(--panel-2); }
.anketa-table tbody tr:hover td, .anketa-table tbody tr:hover th.anketa-q {
  background: var(--accent-dim);
}
/* Угол — на пересечении двух закреплений, поэтому слой выше обоих. */
.anketa-table th.anketa-corner { z-index: 3; background: var(--panel-2); }

/* Длинный ответ свёрнут в три строки; щелчок по ячейке разворачивает. */
.anketa-val {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden; cursor: pointer;
}
.anketa-val.open { display: block; max-height: 260px; overflow-y: auto; }
.anketa-line + .anketa-line { margin-top: 2px; }
/* Кнопка «Перевести» в анкете нужна — половина ответов китайские, — но в
   таблице она должна быть мелкой, иначе съедает строку, которую свернули
   до трёх. */
.anketa-table .nt-btn { font-size: 10px; padding: 0 4px; margin-left: 3px; }
.anketa-table .nt-ru { font-size: 11px; }

/* Широкое окно — только для таблиц. Формы в нём читались бы хуже. */
.modal-wide { width: min(1580px, calc(97vw - var(--chat-room))); max-height: 92vh; }
.modal-wide .modal-body { padding: 12px 14px; }

/* Деньги и условия оплаты в строке фабрики: справка под названием. */
.split-money { display: flex; gap: 8px; align-items: baseline; font-size: 12px; width: 100%; }
.split-money-sum { font-weight: 600; font-variant-numeric: tabular-nums; }

/* ── образцы внутри карточки товара ───────────────────────────────────────
   Товар один, образцов у него бывает несколько — по одному на фабрику.
   Строка образца устроена как строка карточки в списке: снимок, номер,
   производство и цена. Ведущий помечен рамкой слева. */
.card-samples { display: flex; flex-direction: column; gap: 8px; }
.card-sample {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel-2); min-width: 0;
}
/* «читать по нему» — кнопка, и сжиматься ей нельзя: в узкой панели её текст
   ложился в три строки по одному слову. Переносится целиком под строку. */
.card-sample > .btn { flex: 0 0 auto; margin-left: auto; }
.card-sample.leading { border-left: 3px solid var(--accent); }
/* Строка образца открывает его форму — курсор об этом говорит. */
.card-sample { cursor: pointer; }
/* Список образцов внутри формы «Изменить»: у каждого своя строка-кнопка. */
.form-samples { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 6px; }
.form-sample-row { justify-content: flex-start; text-align: left; }
.card-sample:hover { border-color: var(--accent); }
/* Обложки в строке образца больше нет (16.09.2026): главная фотография у юнита
   одна и стоит выше, а шесть её копий подряд ничего не добавляли. Собственные
   снимки образца — брак, ткань, версия — живут в его панели. */
.card-sample-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.card-sample-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.card-sample-code { font-weight: 700; font-variant-numeric: tabular-nums; }
/* «По нему карточка» — не решение о производстве, а якорь отображения.
   Поэтому и выглядит тихо: это подпись, а не награда. */
.card-sample-lead {
  font-size: 10.5px; color: var(--muted); text-transform: lowercase;
  border: 1px dashed var(--line); border-radius: 4px; padding: 0 5px;
}
.card-sample-sub {
  font-size: 12.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.status-pill.sm { font-size: 11px; padding: 1px 7px; }
/* Статус образца своими цветами: коды у него свои, не заказные. */
.status-pill[data-sample="sample"] { background: var(--tile-yellow-bg); color: var(--tile-yellow-text); }
.status-pill[data-sample="sample_ok"] { background: var(--tile-green-bg); color: var(--tile-green-text-2); }
.status-pill[data-sample="fitting"] { background: var(--tile-violet-bg); color: var(--tile-violet-text); }
.status-pill[data-sample="done"] { background: var(--c-ece9e3-s); color: var(--c-55504a-t); }

/* Мультивыбор товаров в окне «Новые образцы». */
.nsm-label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.nsm-card {
  width: 100%; padding: 7px 10px; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--ink); font: inherit; font-size: 13px;
}
.nsm-cards {
  max-height: 260px; overflow: auto; margin-bottom: 12px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
}
.nsm-card-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; gap: 8px;
  align-items: center; padding: 7px 10px; border-bottom: 1px solid var(--line);
  cursor: pointer;
}
.nsm-card-row:last-child { border-bottom: 0; }
.nsm-card-row:hover { background: var(--panel-2); }
.nsm-card-row input { margin: 0; }
/* Сколько образцов одного товара: счётчик виден только у отмеченного. */
.nsm-card-row:not(.on) .nsm-qty { display: none; }
.nsm-qty {
  display: inline-flex; align-items: center; gap: 2px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--panel);
}
.nsm-qty.many { border-color: var(--ink); }
.nsm-qty-btn {
  width: 22px; height: 22px; border: 0; border-radius: 999px; padding: 0;
  background: none; color: var(--ink); font: inherit; font-size: 15px; line-height: 1;
  cursor: pointer;
}
.nsm-qty-btn:hover:not(:disabled) { background: var(--panel-2); }
.nsm-qty-btn:disabled { color: var(--muted); opacity: .45; cursor: default; }
.nsm-qty-value { min-width: 14px; text-align: center; font-size: 13px; font-weight: 600; }
.nsm-new-chip .nsm-qty { background: transparent; border-color: currentColor; }
.nsm-new-chip .nsm-qty-btn { color: inherit; width: 18px; height: 18px; font-size: 13px; }

/* Что случалось с фабрикой: простои и перенесённые даты отгрузки.
   Список фактов, а не оценка — потому и выглядит как журнал, а не как плитки. */
.event-log { display: flex; flex-direction: column; gap: 6px; }
.event-row {
  display: flex; align-items: baseline; gap: 12px;
  padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2);
}
.event-text { flex: 1; min-width: 0; font-size: 13px; }
.event-meta { flex: 0 0 auto; font-size: 12px; color: var(--muted); }

/* Отменённое действие в «Логе»: строка остаётся, но больше не утверждает
   того, чего в CRM уже нет. */
.log-action.log-undone { text-decoration: line-through; color: var(--muted); }

/* Что вышло у каждой фабрики: утверждена или нет, по какой цене и почему. */
.card-results { display: flex; flex-direction: column; gap: 6px; }
.card-result {
  display: flex; flex-direction: column; gap: 6px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2);
}
.card-result.rejected { border-color: var(--danger); }
.card-result-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.card-result-name { font-weight: 600; min-width: 0; }
.card-result-reason { width: 100%; min-width: 0; }

/* Выбранный, но ещё не подтверждённый ответ в «Ревью»: цифра выделяет, Enter
   подтверждает — промах по клавише больше не отвечает агенту сразу. */
.review-actions button.picked {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* «За мной»: вопрос в «Ревью», который человек взял на себя. Остаётся в списке
   и ждёт — но видно, что о нём уже помнят. */
.review-mine { display: flex; align-items: center; gap: 8px; margin: 6px 0; }

/* Куда что везти: у фабрики бывает офис и цех, оба верные. */
.address-list { display: flex; flex-direction: column; gap: 8px; }
.address-row {
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2);
}
.address-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.address-text { font-size: 13px; }
.address-edit { display: flex; flex-direction: column; gap: 12px; }
.address-edit-row {
  display: flex; flex-direction: column; gap: 8px;
  padding: 10px; border: 1px solid var(--line); border-radius: 8px;
}
.address-marks { display: flex; gap: 14px; }
.address-mark { display: flex; align-items: center; gap: 6px; font-size: 13px; }

/* Окно «Google-таблица»: ключ сервисного аккаунта, шаги настройки и путь к
   файлу ключа. Путь показываем моноширинным и с переносом по символам — его
   копируют глазами так же часто, как кнопкой. */
.sheet-step { margin-bottom: 16px; }
.sheet-step .btn { margin-top: 8px; }
.sheet-ok { font-size: 13px; font-weight: 600; }
.sheet-steps {
  margin: 8px 0 0; padding-left: 18px;
  font-size: 12px; line-height: 1.75; color: var(--muted);
}
.sheet-path {
  margin-top: 8px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; word-break: break-all;
}

/* Отложенный артикул: пауза объясняет себя прямо в шапке досье. */
.parked-plate {
  margin: 10px 0; padding: 10px 12px;
  border: 1px solid var(--line); border-left: 3px solid var(--hushed-violet);
  border-radius: 8px; background: var(--panel-2); font-size: 13px;
}
.parked-plate div { margin-top: 4px; color: var(--muted); }

/* ── общий импорт файлов ─────────────────────────────────────────────────── */
/* Окно читается сверху вниз: что принесли → что прочиталось → что предлагают
   внести. Галочка слева, потому что именно её человек и ищет. */
.fi-files { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; }
.fi-file {
  display: grid; gap: 2px; padding: 8px 10px; border-radius: 8px;
  background: var(--panel-2); border: 1px solid var(--rule);
}
.fi-preview {
  margin: 4px 0 0; padding: 6px 8px; border-radius: 6px; max-height: 88px; overflow: auto;
  background: var(--panel); border: 1px solid var(--rule);
  font: 500 11.5px/1.5 var(--mono); white-space: pre-wrap; color: var(--muted);
}
.fi-plan { display: flex; flex-direction: column; gap: 14px; margin: 4px 0 14px; }
.fi-group { border: 1px solid var(--rule); border-radius: 10px; overflow: hidden; }
.fi-group-head {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  background: var(--panel-2); border-bottom: 1px solid var(--rule);
}
.fi-group-head .muted { margin-right: auto; }
.fi-item {
  display: flex; gap: 10px; align-items: flex-start; padding: 8px 10px;
  border-top: 1px solid var(--rule); cursor: pointer;
}
.fi-item:first-of-type { border-top: 0; }
.fi-item:hover { background: var(--panel-2); }
.fi-item input { margin-top: 3px; flex: none; }
.fi-item-text { display: grid; gap: 2px; }
/* Спорное видно сразу: галочка снята, а рядом — оба значения. */
.fi-item.conflict { background: color-mix(in srgb, var(--warn, var(--c-b8860b-s)) 8%, transparent); }
.fi-was { font-size: 12.5px; color: var(--warn, var(--c-b8860b-t)); }
.fi-done { font-size: 12.5px; color: var(--ok, var(--c-2f7d4f-t)); }
.fi-fail { font-size: 12.5px; color: var(--danger, var(--c-b3261e-t)); }

/* ── «Сокращение расходов» ────────────────────────────────────────────────── */
/*
 * Раздел читают ради цифр, поэтому числа в нём набраны в одну ширину и прижаты
 * вправо: колонку «Экономия ¥» сравнивают глазами по вертикали, а не читают.
 */
.savings-tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 10px; margin: 14px 0 6px;
}
.savings-tile {
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  padding: 12px 14px;
}
.savings-tile-value {
  font-family: var(--font-display); font-size: 24px; line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.savings-tile-label { font-size: 12px; color: var(--muted); margin-top: 4px; }
.savings-tile-note { font-size: 11px; color: var(--muted); margin-top: 4px; font-style: italic; }

.savings-table { width: 100%; border-collapse: collapse; margin: 10px 0 18px; font-size: 13px; }
.savings-table th {
  text-align: left; font-weight: 500; font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
  padding: 6px 8px; border-bottom: 1px solid var(--line); vertical-align: bottom;
}
.savings-table td { padding: 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
.savings-table td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.savings-table tr.limit-reached td { background: var(--panel-2); }
.savings-article { font-family: var(--font-display); font-size: 14px; }
/* Строка с пробелами расчёта: не ошибка, но и не цифра — отдельным тоном. */
.savings-gap td {
  font-size: 12px; color: var(--muted); font-style: italic;
  padding-top: 0; border-bottom: 1px solid var(--line);
}
.savings-total td { font-weight: 600; border-top: 2px solid var(--line); border-bottom: none; }

.savings-bar {
  height: 4px; background: var(--panel-2); border-radius: 2px;
  margin-top: 4px; overflow: hidden;
}
.savings-bar-fill { height: 100%; background: var(--accent, var(--c-7a9-s)); border-radius: 2px; }

/* ── вкладка «Остаток лимита» (17.09.2026) ───────────────
   Общий счёт ожидания стоит над таблицей крупно: ради него вкладку и
   открывают, и искать его глазами среди строк не надо. */
.savings-pending {
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--panel); padding: 14px 16px; margin: 4px 0 14px;
}
.savings-pending-value { display: flex; align-items: baseline; gap: 10px; }
.savings-pending-value > span:first-child {
  font-family: var(--font-display); font-size: 26px; font-weight: 650;
}
.savings-pending-cny { font-size: 14px; color: var(--muted); }
.savings-pending-label { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.savings-pending-note { font-size: 11.5px; color: var(--muted); margin-top: 6px; }
.savings-left { font-size: 15px; font-weight: 650; }
.savings-future-bonus { font-size: 13.5px; font-weight: 600; }
.savings-done { margin: 6px 0 18px; }
.savings-done > summary { cursor: pointer; color: var(--muted); font-size: 12.5px; padding: 4px 0; }
.savings-done > summary:hover { color: var(--text); }

.savings-status { font-size: 12px; white-space: nowrap; }
.savings-status.full { font-weight: 600; }
.savings-status.paid { font-weight: 600; color: var(--ok, var(--c-2a7-t)); }
.savings-orders { margin: 4px 0 18px; font-size: 12px; }
.savings-orders summary { cursor: pointer; color: var(--muted); }
/* Десять колонок в узком окне не помещаются: прокручиваем таблицу, а не страницу. */
.savings-scroll { overflow-x: auto; max-width: 100%; }
.savings-scroll > .savings-table { min-width: 940px; }
/*
 * Журнал по месяцам: месяц — карточка, в шапке одно число (бонус), внутри
 * короткие строки в одну линию. Подробности месяца — приглушённой строкой
 * внизу. Кнопок в строках нет: строку открывают щелчком.
 */
.savings-block-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.savings-block-head .section-subhead { margin-bottom: 6px; }
.savings-month-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  margin: 10px 0; overflow: hidden;
}
.savings-month-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 10px 14px; cursor: pointer; user-select: none;
}
.savings-month-head:hover { background: var(--panel-2); }
.savings-month-title { display: flex; align-items: baseline; gap: 8px; }
.savings-chevron {
  display: inline-block; width: 10px; color: var(--muted); font-size: 10px;
  transition: transform .15s;
}
.savings-month-head.open .savings-chevron { transform: rotate(90deg); }
.savings-month-name { font-family: var(--font-display); font-size: 16px; }
.savings-month-total { display: flex; align-items: baseline; gap: 8px; font-variant-numeric: tabular-nums; }
.savings-month-total-label { font-size: 12px; color: var(--muted); }
.savings-month-total-value { font-family: var(--font-display); font-size: 16px; }
.savings-month-body { border-top: 1px solid var(--line); overflow-x: auto; }
.savings-rows { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 820px; }
.savings-rows th {
  text-align: left; font-weight: 500; font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em; padding: 8px 14px 4px;
}
.savings-rows th.num, .savings-rows td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.savings-rows td { padding: 7px 14px; vertical-align: baseline; }
.savings-rows tbody tr.savings-row td { border-top: 1px solid var(--line); }
.savings-row.editable { cursor: pointer; }
.savings-row.editable:hover td { background: var(--panel-2); }
.savings-order { color: var(--muted); white-space: nowrap; }
.savings-article { font-family: var(--font-display); font-size: 14px; margin-right: 8px; }
/* Пометка источника только там, где она что-то меняет: строку CRM здесь не правят. */
.savings-src {
  font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
  padding: 1px 5px; border-radius: 4px; color: var(--muted); border: 1px solid var(--line);
  vertical-align: middle; white-space: nowrap;
}
.savings-src.crm { background: var(--accent-dim); color: var(--ink-green); border-color: transparent; }
.savings-price-arrow { color: var(--muted); margin: 0 4px; }
.savings-rate { color: var(--muted); }
.savings-bonus { font-weight: 600; }
/* Комментарий — колонкой справа, как в таблице до CRM: две строки, целиком по
   щелчку. Ширина задана колонке, иначе длинный текст растянул бы её на всё. */
.savings-comment-head, .savings-comment-cell { width: 34%; min-width: 240px; }
.savings-comment-cell { white-space: normal; font-size: 12px; color: var(--muted); }
.savings-comment {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; cursor: pointer; overflow-wrap: anywhere;
}
/* Свёрнутый комментарий РАЗВОРАЧИВАЕТСЯ, и об этом надо сказать глазами
   (аудит 24.09.2026): курсор-палец виден только под мышью, а многоточие в
   конце второй строки читается как «текст такой», а не как «есть ещё».
   Подчёркивание под курсором — самый тихий способ это показать. */
.savings-comment:hover { color: var(--text); text-decoration: underline; text-decoration-style: dotted; }
.savings-comment.open { display: block; overflow: visible; white-space: pre-line; }
.savings-comment.open:hover { text-decoration: none; }
.savings-issue-line { color: var(--ink-yellow); margin-top: 2px; overflow-wrap: anywhere; }
.savings-month-foot {
  padding: 8px 14px 10px; font-size: 12px; color: var(--muted);
  border-top: 1px solid var(--line); display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
}
.savings-warn { font-size: 11px; color: var(--ink-yellow); }
/* Форма строки: расчёт под полями обновляется на лету. */
.savings-form-preview { margin-top: 12px; font-size: 13px; }
.savings-form-preview:empty { display: none; }
.savings-form-quota { font-size: 12px; color: var(--muted); margin-top: 4px; }
.savings-form-quota.full { color: var(--ink-yellow); }
.savings-form-error { color: var(--ink-rose); font-size: 12px; margin-top: 6px; }
.savings-form-error:empty { display: none; }
.savings-form-delete { margin-left: auto; color: var(--ink-rose); }
/* «Такой заказ есть в CRM»: подсказка под полями, строка на каждый артикул заказа. */
.savings-order-suggest {
  margin-top: 10px; padding: 8px 10px; background: var(--panel-2); border-radius: 8px; font-size: 12px;
}
.savings-order-suggest-title { color: var(--muted); margin-bottom: 4px; }
.savings-order-suggest-line {
  display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 3px 0;
}

/* ── «Таблицы»: слева список, справа превью и действия ────────────────────
   Список слева держит только название и тип — этого хватает, чтобы выбрать;
   всё остальное живёт справа. Состояние книги стоит НАД кнопками намеренно:
   выгрузка переписывает чужой документ целиком, и «Google Таблица не
   подключена» читают до нажатия, а не в тосте после. */
.table-row-type { font-size: 11px; color: var(--muted); }
/* Точка подключения: зелёная — книга привязана, тихая — ещё нет. */
.table-dot {
  width: 8px; height: 8px; border-radius: 50%; margin-left: auto;
  flex: 0 0 auto; background: var(--line);
}
.table-dot.on { background: var(--ink-green); }
.table-badge {
  padding: 2px 8px; border-radius: 999px; background: var(--accent-dim);
  font-size: 11px; white-space: nowrap;
}
/* Excel — вторая форма таблицы, а не её основной вид: плашка тише. */
.table-badge.ghost { background: var(--panel-2); color: var(--muted); }
.table-state { font-size: 12.5px; margin-top: 2px; }
/* Идущая выгрузка: строка состояния говорит о ней, а не о прошлом разе.
   Цвет — чернильный, а не заливочный `--accent`: это ТЕКСТ, и бледная мята на
   светлой бумаге не читается вовсе. */
.table-state.working { color: var(--ink-green); font-weight: 600; }
.table-state.failed { color: var(--ink-rose); font-weight: 600; }
/* Работающая кнопка: кружок и подпись в одну строку. Кнопка остаётся на месте
   и не «схлопывается» — иначе ряд прыгал бы на каждой выгрузке. */
.btn.btn-working { display: inline-flex; align-items: center; gap: 7px; }
.btn.btn-working .spinner { border-top-color: currentColor; }
/* Действия двумя рядами: сверху Google-книга, снизу за чертой — Excel.
   Это разные вещи, и одним рядом они читались как список равных кнопок. */
.table-actions { display: flex; flex-direction: column; gap: 8px; margin: 12px 0 16px; }
.table-actions select { max-width: 230px; }
.table-excel { padding-top: 10px; border-top: 1px solid var(--line); }

/* ── «Примерка»: лист сеткой, над ним — что CRM из него поняла ───────────────
 *
 * С 29.09.2026 таблица модели показывается сеткой, как остальные книги
 * (`.tp-*` ниже). Над сеткой — строка открытого листа: день примерки и по чипу
 * на блок («МОДЕЛЬ 1 · О-053 · записано»). Строка — CRM-овская, не часть
 * листа, поэтому и цвета у неё свои. */
.fit-status { display: flex; flex-direction: column; gap: 6px; margin: 2px 0 10px; }
.fit-day-head {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding-bottom: 4px; border-bottom: 1px solid var(--line);
}
.fit-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.fit-chip {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 3px 8px; border-radius: 999px; font-size: 12px;
  background: var(--panel-2); border: 1px solid var(--line);
}
.fit-chip.ok-chip { border-color: var(--ok); }
.fit-chip.warn { border-color: var(--warn); }
.fit-chip.danger { border-color: var(--danger); }
.fit-block { font-size: 11px; color: var(--muted); white-space: nowrap; }
/* Запись модели в вопросе «Ревью»: колонка узкая, и длинный ответ уходит на
   следующую строку, а не за край. */
.review-fitting .review-pair { flex-wrap: wrap; }
.review-fitting .review-pair > span { min-width: 0; overflow-wrap: anywhere; }

/* ── превью листа книги ───────────────────────────────────────────────────
 *
 * Сетку собирает сервер из той же книги, что уезжает в Google
 * (`core/tables-preview.js`), и цвета шапок, заливок и рамок приходят
 * ГОТОВЫМИ — прямо из просьб к Google. Поэтому бумага здесь светлая в любой
 * теме: это превью чужого документа, а не часть оформления CRM, и тёмный лист
 * под светлой шапкой шаблона выглядел бы поломкой. Тёмные двойники токенов
 * ниже намеренно равны светлым.
 */
.tp-box { margin-top: 4px; }
/* Вкладки одной полосой, как в самой книге: их бывает тринадцать, и в два
   ряда полоса отжимала лист вниз. Лишние уезжают прокруткой. */
.tp-tabs {
  display: flex; flex-wrap: nowrap; gap: 4px; overflow-x: auto;
  padding-bottom: 0; scrollbar-width: thin;
}
.tp-tab {
  --tab-color: var(--line);
  padding: 5px 10px; border: 1px solid var(--sheet-frame); border-bottom: none;
  border-left: 3px solid var(--tab-color); border-radius: 7px 7px 0 0;
  background: var(--panel-2); color: var(--muted);
  font: inherit; font-size: 12px; cursor: pointer; white-space: nowrap;
}
.tp-tab:hover { color: var(--text); }
.tp-tab.on { background: var(--sheet-paper); color: var(--sheet-ink); font-weight: 600; }
.tp-wrap {
  overflow: auto; max-height: 540px;
  border: 1px solid var(--sheet-frame); border-radius: 0 6px 6px 6px;
  background: var(--sheet-paper);
}
.tp-tabs:empty + .tp-wrap { border-radius: 6px; }
.tp-grid {
  border-collapse: collapse; background: var(--sheet-paper); color: var(--sheet-ink);
  /* Шрифт таблицы, а не CRM: книгу так и видят в Google. */
  font-family: Arial, Helvetica, sans-serif;
}
.tp-grid td, .tp-grid th {
  border: 1px solid var(--sheet-grid); padding: 2px 5px;
  text-align: left; vertical-align: middle; overflow: hidden;
}
.tp-letter, .tp-gutter {
  position: sticky; background: var(--sheet-head); color: var(--sheet-head-ink);
  font-weight: 400; font-size: 10px; text-align: center;
}
.tp-letter { top: 0; z-index: 2; }
.tp-gutter { left: 0; z-index: 1; width: 34px; min-width: 34px; }
.tp-grid thead .tp-gutter { top: 0; z-index: 3; }
.tp-gutter-col { width: 34px; }
/* Закрепление листа: в книге за этой чертой шапка остаётся на месте. */
.tp-frozen-row td, .tp-frozen-row th { border-bottom: 2px solid var(--sheet-frame); }
.tp-frozen-col { border-right: 2px solid var(--sheet-frame); }
.tp-check { font-size: 13px; }
.tp-photos { display: flex; gap: 3px; align-items: flex-start; }
.tp-photos img { max-height: 84px; max-width: 100%; border-radius: 2px; object-fit: cover; }
/* Уголок заметки — тот же, которым её показывает сама таблица. */
.tp-note { position: relative; }
.tp-note::after {
  content: ''; position: absolute; top: 0; right: 0;
  border: 4px solid transparent;
  border-top-color: var(--sheet-note); border-right-color: var(--sheet-note);
}
.tp-more { margin-top: 6px; font-size: 11.5px; color: var(--muted); }
/* ── что ещё не выгружено ────────────────────────────────────────────────
   Полоса над листом отвечает на единственный вопрос, ради которого сюда
   заходят: надо ли жать «Синхронизировать». Нечего выгружать — полосы нет. */
.tp-changes {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 8px; padding: 7px 10px; border-radius: 8px;
  background: var(--panel-2); font-size: 12.5px;
}
.tp-changes-text { font-weight: 600; }
/* Пока сравнивать не с чем — та же полоса, но тихая: это не новость, а
   состояние, и жирной строкой оно кричало бы попусту. */
.tp-changes.quiet { background: transparent; padding: 2px 0; color: var(--muted); }
.tp-changes .btn { margin-left: auto; }
/* Ярлык вкладки с несохранённым: число прямо на нём — иначе вкладку пришлось
   бы искать, перебирая все тринадцать. */
.tp-tab.has-changes { border-left-color: var(--sheet-new-edge); }
.tp-tab-mark {
  display: inline-block; margin-left: 5px; min-width: 15px; padding: 0 4px;
  border-radius: 999px; background: var(--sheet-new-edge); color: var(--sheet-new-ink);
  font-size: 10px; line-height: 15px; text-align: center; vertical-align: middle;
}
.tp-tab-mark:empty { min-width: 7px; height: 7px; padding: 0; line-height: 7px; }
/* Строка, которой в книге ещё нет, и ячейка, значение которой стало другим.
   Заливка идёт ПОВЕРХ цвета из книги — это и есть смысл подсветки, — но рамка
   слева оставлена узкой: лист должен остаться похожим на себя. */
.tp-grid tr.tp-row-new td { background: var(--sheet-new) !important; }
.tp-grid tr.tp-row-new .tp-gutter { box-shadow: inset 3px 0 0 var(--sheet-new-edge); }
.tp-grid tr.tp-row-changed .tp-gutter { box-shadow: inset 3px 0 0 var(--sheet-changed-edge); }
.tp-grid td.tp-changed {
  background: var(--sheet-changed) !important;
  box-shadow: inset 0 0 0 1px var(--sheet-changed-edge);
}

/* ── тёмная тема ────────────────────────────────────────────────────────── */
/* Активная тема стоит на <html data-theme="dark|light">: её ставит theme.js до первой
   отрисовки — системная (prefers-color-scheme), ручной выбор или, если системную
   узнать нельзя, вечернее время. Правила ниже — единственное место, где живёт
   тёмная палитра: остальной CSS ссылается только на переменные. */
/* ── КИЗы ───────────────────────────────────────────────── */
/*
 * Раздел про номера, поэтому главное на экране — табло: последний
 * использованный, следующий свободный, остаток и то, что уйдёт в файл.
 * Цифры крупные и моноширинные по цифрам (`tabular-nums`): их сверяют
 * глазами с прежней таблицей, и прыгающая ширина знака этому мешает.
 */
.kiz-form {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); padding: var(--pad-lg); margin: 14px 0 6px;
}
.kiz-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px; align-items: end;
}
.kiz-form > .form-field { margin-top: 12px; }
.kiz-board {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px; margin-top: 16px;
}
.kiz-cell {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--panel-2); padding: 10px 12px;
}
.kiz-cell-value {
  font-family: var(--font-display); font-size: 22px; line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.kiz-cell-label { font-size: 12px; color: var(--muted); margin-top: 4px; }
.kiz-cell-note { font-size: 11px; color: var(--muted); margin-top: 3px; font-style: italic; }

/* Итог выдачи держится на экране, пока его не уберут: за файлом уходят в
   «Загрузки» и возвращаются сверить номера. */
.kiz-done {
  margin-top: 16px; padding: 12px 14px;
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  background: var(--accent-dim);
}
.kiz-done-title { font-family: var(--font-display); font-size: 17px; }
.kiz-done-file {
  margin-top: 4px; font-size: 12px; color: var(--text-2);
  word-break: break-all;
}
.kiz-empty { text-align: left; }
.kiz-empty-title { font-family: var(--font-display); font-size: 16px; }

/* Зов прочитать таблицу: стоит между формой и табло, потому что читать его
   надо ДО нажатия, а не после. */
.kiz-notice {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  margin-top: 16px; padding: 10px 14px;
  border: 1px solid var(--warn); border-radius: var(--radius-sm);
  background: var(--tile-yellow-bg);
}
.kiz-notice-text { flex: 1 1 320px; font-size: 13px; color: var(--tile-yellow-text); }

/* Окно чтения таблицы: сверки читают глазами, поэтому они текстом в строке, а
   не значком с подсказкой по наведению. */
.kiz-read-table { min-width: 640px; }
.kiz-read-table td { vertical-align: top; }
.kiz-check { font-size: 12px; color: var(--muted); line-height: 1.6; }
.kiz-check-sign { display: inline-block; width: 14px; font-weight: 600; }
.kiz-check-ok { color: var(--ink-green); }
.kiz-check-bad { color: var(--ink-rose); }
.kiz-check-none { color: var(--muted); }
.kiz-check-warn { color: var(--ink-yellow); }
/* Последняя выдача из файла: тот же итог, но видно, что КИЗы кончились. */
.kiz-done-last { border-color: var(--warn); background: var(--tile-yellow-bg); }

.kiz-scroll { overflow-x: auto; }
.kiz-table {
  width: 100%; border-collapse: collapse; font-size: 13px; min-width: 860px;
  margin: 4px 0 18px;
}
.kiz-table th {
  text-align: left; font-weight: 500; font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
  padding: 6px 8px; border-bottom: 1px solid var(--line);
}
.kiz-table td { padding: 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
.kiz-table th.num, .kiz-table td.num {
  text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.kiz-range { font-variant-numeric: tabular-nums; white-space: nowrap; }
.kiz-file { font-size: 12px; color: var(--text-2); word-break: break-all; }
/* Строка не из выдачи (перенос прежнего счёта, чтение таблицы) — тише. */
.kiz-row-past td { color: var(--muted); font-style: italic; }
.kiz-mark {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 6px;
  background: var(--panel-2); color: var(--muted);
  font-size: 10px; font-style: normal; text-transform: uppercase; letter-spacing: .04em;
}

/* Заявка на КИЗы: строка будущего бланка. Два поля из четырнадцати правятся
   прямо в таблице — вид товара и ТНВЭД, остальное CRM знает и показывает
   текстом, чтобы его было видно, а не чтобы его правили здесь. */
/* ЗАЯВКА НА КИЗЫ. Верх окна — три поля в ряд, ниже строки будущего бланка.
   Ширину колонкам задаём явно: в таблице правятся два поля из семи, и
   раздавать место поровну значило бы отдать половину экрана составу, который
   только читают. */
.kiz-request-head {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end;
  margin-bottom: 4px;
}
.kiz-request-head .form-field:first-child { flex: 1 1 380px; max-width: 560px; }
.kiz-request-head .form-field { flex: 0 1 200px; }
.kiz-request-body { margin-top: 12px; }
.kiz-request-empty { padding: 28px 12px; }
/* Таблица фабрики не прочиталась — не серая подсказка, а отказ (как `.chip.danger`). */
.kiz-request-failed { color: var(--ink); border: 1px solid var(--danger); background: var(--danger); }
.kiz-request-table { min-width: 900px; table-layout: fixed; }
.kiz-request-table th:nth-child(1), .kiz-request-table td:nth-child(1) { width: 12%; }
.kiz-request-table th:nth-child(2), .kiz-request-table td:nth-child(2) { width: 6%; }
.kiz-request-table th:nth-child(3), .kiz-request-table td:nth-child(3) { width: 10%; }
.kiz-request-table th:nth-child(4), .kiz-request-table td:nth-child(4) { width: 19%; }
.kiz-request-table th:nth-child(5), .kiz-request-table td:nth-child(5) { width: 9%; }
.kiz-request-table th:nth-child(6), .kiz-request-table td:nth-child(6) { width: 18%; }
.kiz-request-table th:nth-child(7), .kiz-request-table td:nth-child(7) { width: 26%; }
/* Артикул не переносится: это опознавательный знак строки, и «КРТ5-» на одной
   строке, «черный» на другой читается как две разные вещи. */
.kiz-cell-article { white-space: nowrap; font-variant-numeric: tabular-nums; }
.kiz-cell-fabric { font-size: 12px; line-height: 1.4; }
.kiz-request-table .combo, .kiz-request-table input, .kiz-request-table select {
  width: 100%; min-width: 0;
}
.kiz-request-table .kiz-mark { display: block; margin: 4px 0 0; width: fit-content; }
.kiz-cell-tnved .btn-xs { margin-top: 4px; }

/* Факт от фабрики: кнопки таблицы стоят в ряду полей, по нижнему краю. */
.kiz-request-source { display: flex; align-items: center; gap: 6px; padding-bottom: 2px; }
.kiz-request-head .kiz-request-file { flex: 1 1 380px; max-width: 560px; }
.kiz-table-chip {
  display: flex; align-items: center; gap: 6px; min-height: 34px;
  padding: 4px 8px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2);
}
.kiz-table-name { font-weight: 600; word-break: break-all; }
.kiz-table-chip .btn-xs { margin-left: auto; }
.kiz-table-warnings { margin: 6px 0; }
.kiz-table-warnings .hint { margin: 2px 0; }
/* Файл тащат над окном — окно показывает, что его примет. */
.kiz-drop-over { outline: 2px dashed var(--accent); outline-offset: -6px; border-radius: 10px; }
/* Заказ таблицы — строка-заголовок перед своими строками. */
.kiz-request-table .kiz-group-row td {
  padding-top: 14px; border-bottom: 1px solid var(--line); background: var(--panel-2);
}
.kiz-group-code { font-weight: 600; }
.kiz-group-qty { float: right; font-variant-numeric: tabular-nums; }
.kiz-cell-qty input { text-align: right; font-variant-numeric: tabular-nums; }
/* Пометка под числом («заказ 80 · нет в таблице») переносится внутри своей
   ячейки, а не лезет в соседнюю: у `num` по умолчанию запрет переноса. */
.kiz-request-table td.kiz-cell-qty { white-space: normal; }
.kiz-request-table .kiz-cell-qty .kiz-mark { width: auto; max-width: 100%; margin-left: auto; text-align: right; }
/* Ноль — строка в бланк не едет: гасим её число. */
.kiz-qty-off input { color: var(--muted); }

:root[data-theme="dark"] {
  color-scheme: dark;
  --marys-rose: #4f3034;
  --gossamer-green: #2f4a3a;
  --pastel-yellow: #4a4325;
  --orchid-petal: #3d3549;
  --heavenly-pink: #4a383d;
  --cappuccino-foam: #2a2822;
  --hushed-violet: #463a3b;
  --ink: #f2ede4;
  --paper: #232019;
  --bg: #1b1916;
  --panel: #232019;
  --panel-2: #2a2721;
  --line: #37332c;
  --text: #e8e1d5;
  /* Тоже поднят до нормы (было #a39a8b). На большинстве фонов тёмной темы он
     её держал (5,8–6,4), но на подсвеченной строке «Завершено» давал 4,00: так
     набраны заголовки сортировки «Номер», «Производство», «Размещён». Плюс
     «Показать 6 заказов» на доске (4,29) и подписи этапов «Замена»,
     «Отложено» кеглем 9,5 (4,47). Стало 4,56 на худшем фоне. */
  --muted: #aea595;
  --accent: #8fb59d;
  --accent-dim: #2c3a32;
  --ok: #8fb59d;
  --warn: #4a4325;
  --danger: #4f3034;
  --ink-green: #bfe0cc;
  --ink-rose: #f2b8bd;
  --ink-yellow: #e9dcaa;

  --c-d4a514-l: #e9b516;
  --c-c96a70-l: #5e3b3d;
  --c-rgba-80-66-48-1-s: rgba(80, 66, 48, 0.16);
  --c-rgba-47-66-56-16-s: rgba(47, 66, 56, 0.26);
  --c-rgba-122-51-57-12-s: rgba(122, 51, 57, 0.19);
  --c-d4a514-t: #e8ddba;
  --c-rgba-20-12-16-82-s: rgba(20, 12, 16, 1);
  --hover: #2f2b25;
  --tile-violet-bg: #2e2637;
  --c-46405a-t: #cecbd7;
  --c-a3c6b0-s: #a3c6b0;
  --c-f4f1e8-s: #383324;
  --c-rgba-60-50-35-08-s: rgba(0, 0, 0, 0.18);
  --c-eef2e9-s: #2e3527;
  --panel-3: #2a2721;
  --text-2: #d9d1c3;
  --c-rgba-255-255-255-7-s: rgba(255, 255, 255, 0.084);
  --tile-rose-bg-3: #3e2123;
  --tile-rose-text-2: #d9c9ca;
  --tile-yellow-bg: #37331e;
  --tile-yellow-text: #e1dbc2;
  --tile-blush-bg: #342626;
  --tile-blush-text: #d5cdcf;
  --tile-violet-text: #d0ccd6;
  --tile-green-bg: #253229;
  --tile-green-text-2: #cbd7d0;
  --c-f8f6ef-s: #3d3826;
  --c-fdf3f4-s: #432427;
  --tile-cream-bg: #403b22;
  --c-faf5f5-s: #3e2929;
  --tile-violet-bg-2: #342b3d;
  --tile-green-bg-2: #293b2f;
  --c-f7f5ef-s: #3b3627;
  --c-eef4f0-s: #2a362e;
  --c-rgba-120-104-80-18-l: rgba(120, 104, 80, 0.29);
  --c-rgba-255-255-255-72-s: rgba(255, 255, 255, 0.086);
  --c-rgba-120-104-80-3-l: rgba(120, 104, 80, 0.48);
  --c-rgba-80-66-48-06-s: rgba(0, 0, 0, 0.13);
  --c-e3dbc9-s: #e3dbc9;
  --c-rgba-80-66-48-08-s: rgba(0, 0, 0, 0.18);
  --c-ddb0b4-s: #ddb0b4;
  --c-9c9282-t: #d5d2cd;
  --text-3: #c9c0b0;
  --c-fbf5da-s: #39341f;
  --tile-rose-bg-2: #3b2021;
  --c-ece7d8-s: #554e3a;
  --c-rgba-90-74-55-08-s: rgba(0, 0, 0, 0.18);
  --c-fdf9ee-s: #423923;
  --line-4: #5b513e;
  --c-f3efe4-s: #373122;
  --panel-6: #393423;
  --c-a09684-t: #d6d2cc;
  --panel-5: #433b24;
  --c-rgba-80-66-48-05-s: rgba(0, 0, 0, 0.11);
  --c-d8b26a-t: #d8b26a;
  --c-e2dccc-t: #e2dccc;
  --c-rgba-255-255-255-66-s: rgba(255, 255, 255, 0.079);
  --c-ddd3e6-l: #4d4257;
  --c-cfe3d7-l: #42574a;
  --c-c0b6a2-t: #c0b6a2;
  --c-6f7f74-t: #cfd4d0;
  --c-eef2f6-s: #273039;
  --c-4a5866-t: #cbd1d7;
  --c-f3eef8-s: #31253d;
  --c-574a66-t: #d1cbd7;
  --c-eef2ec-s: #2d3429;
  --c-47563f-t: #cfd7cb;
  --c-f1ece0-s: #342f21;
  --c-a9cdb8-s: #3d5246;
  --c-f6f2e6-s: #3b3421;
  --c-f4efe3-l: #5d533c;
  --c-fdfbf5-s: #443c25;
  --c-f2ecdd-l: #5e543b;
  --c-d9b96a-s: #d9b96a;
  --c-b8515d-s: #b8515d;
  --c-4a7c59-l: #7fb38f;
  --c-3f6b4c-t: #c8dbcd;
  --c-a94550-l: #b54a56;
  --c-f9ecee-s: #3f2227;
  --c-8e3a44-t: #e1c2c5;
  --c-fdf3d8-s: #39321f;
  --c-e6d3a3-l: #625637;
  --c-6d5620-t: #e5d9bd;
  --c-c96a74-l: #5e3b3e;
  --panel-raw: #232019;
  --on-accent: #fff;
  --on-accent-strong: #17261d;
  --card: var(--panel);
  --star-off: #5e584c;
  --rule: var(--line);
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --shadow-soft: rgba(0, 0, 0, .55);
  --warn-ink: var(--ink-yellow);
  --accent-ink: var(--ink-green);
  --c-a5454f-s: #7a3e44;
  --c-ece0e2-l: #564346;
  --c-e8d6d9-l: #574245;
  --c-fff8f9-s: #46262a;
  --c-a08b6f-t: #d9d2ca;
  --tile-rose-text: #e3bfc1;
  --tile-rose-bg: #3c2022;
  --tile-rose-line: #64353a;
  --c-fff9f9-s: #472626;
  --c-eef3ec-s: #2c3528;
  --tile-green-text: #cbd7d1;
  --c-dbe6d9-l: #475445;
  --tile-yellow-line: #615938;
  --c-rgba-40-34-28-14-s: rgba(0, 0, 0, 0.31);
  --c-rgba-40-34-28-10-s: rgba(0, 0, 0, 0.22);
  --tile-yellow-line-2: #605839;
  --c-rgba-255-255-255-18-s: rgba(255, 255, 255, 0.022);
  --c-rgba-255-255-255-35-l: rgba(255, 255, 255, 0.042);
  --c-rgba-40-34-28-12-s: rgba(0, 0, 0, 0.26);
  --line-3: #585141;
  --c-ddd5c4-s: #524b3d;
  --c-rgba-80-66-48-12-s: rgba(0, 0, 0, 0.26);
  --c-d9b96a-l: #635636;
  --c-rgba-59-50-39-72-s: rgba(59, 50, 39, 1);
  --c-4a7c59-t: #c8dacd;
  --c-7c6a92-t: #d1cbd7;
  --c-a8742f-t: #e6d4bc;
  --muted-2: #b1a99b;
  --c-f6e3dd-s: #38241e;
  --c-a8422a-t: #e7c3bb;
  --line-2: #524f47;
  --panel-4: #3f3a2a;
  --c-7fae8c-l: #639c73;
  --c-d0a43f-l: #cd9e32;
  --c-c9c7bf-l: #504e49;
  --c-c47a63-l: #b96146;
  --c-f6ecd4-s: #61522e;
  --c-7a5c12-t: #e8dbba;
  --c-f4e2dc-s: #37241e;
  --c-8f4028-t: #e6c6bc;
  --c-b9cdbf-l: #46534a;
  --c-ece7f1-s: #2d2634;
  --c-b3d0be-l: #43564a;
  --c-cfcabb-l: #535046;
  --c-0e5d59-l: #4ee4dd;
  --c-rgba-80-66-48-16-s: rgba(0, 0, 0, 0.35);
  --c-f2ede2-s: #352f22;
  --c-e6d9a3-l: #625a37;
  --c-fbf9fd-s: #362646;
  --c-bfd8c7-l: #435649;
  --c-eef6f0-s: #27392c;
  --c-d7eadc-s: #3b5442;
  --c-2f6941-t: #c3dfcc;
  --c-a89e8d-t: #a89e8d;
  --tile-violet-bg-3: #302635;
  --c-rgba-80-66-48-22-s: rgba(0, 0, 0, 0.48);
  --c-ece9e3-s: #2f2c25;
  --c-55504a-t: #d4d1cf;
  --c-b8860b-s: #c08c0c;
  --c-b8860b-t: #e8dbba;
  --c-2f7d4f-t: #c0e2ce;
  --c-b3261e-t: #e8bdba;
  --c-7a9-s: #4b6d61;
  --c-2a7-t: #bae8d7;
  /* Бумага превью НЕ темнеет: в «Таблицах» это снимок чужого документа, а
     заливки его шапок приходят с сервера светлыми — тёмный лист под ними
     читался бы как поломка. Двойники равны светлым намеренно. */
  --sheet-paper: #ffffff;
  --sheet-ink: #1f2328;
  --sheet-grid: #e0e2e6;
  --sheet-head: #f1f3f4;
  --sheet-head-ink: #5f6368;
  --sheet-frame: #b9bcc2;
  --sheet-note: #f2a33c;
  /* Что ещё не уехало в Google: новая строка и изменённая ячейка. Цвета тоже
     «бумажные» — они лежат на листе и в тёмной теме не темнеют. */
  --sheet-new: #e3f6e6;
  --sheet-new-edge: #34a853;
  --sheet-new-ink: #ffffff;
  --sheet-changed: #fdf2c8;
  --sheet-changed-edge: #e0a800;
}
/* Снимки и логотип в тёмной теме чуть приглушены, чтобы не светить. */
:root[data-theme="dark"] img { filter: brightness(.92); }
:root[data-theme="dark"] .gate-logo, :root[data-theme="dark"] .side-logo { filter: none; }

/* ── ДОСКА ЗАДАЧ: ПРОИЗВОДСТВО × ДЕЛО ─────────────────────────────────────
   Строка — производство, колонка — вид дела, в клетке число открытых задач.
   Цвета у колонок нет вовсе: девять оттенков подряд читаются как график, а
   таблица здесь про числа. Половины — «о чём писать фабрике» и «что делаем
   сами» — разделены ОДНОЙ чертой. */
.task-board-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 10px;
}
.task-pick { flex: 0 0 auto; min-width: 220px; }
.task-pick-btn { white-space: nowrap; }
.task-pick-off {
  border: 1px solid var(--line); background: var(--accent-dim); color: var(--ink-green);
  border-radius: 999px; padding: 3px 10px; font: inherit; font-size: 12px; cursor: pointer;
}
.task-pick-off:hover { border-color: var(--accent); }

.task-mx-wrap {
  overflow-x: auto; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius);
}
table.task-mx { border-collapse: collapse; width: 100%; font-size: 12.5px; }
table.task-mx th, table.task-mx td { border-bottom: 1px solid var(--line); padding: 0; }
table.task-mx tbody tr:last-child > th, table.task-mx tbody tr:last-child > td { border-bottom: 0; }
/* Шапка сидит ВПЛОТНУЮ к первой строке (24.09.2026): под её текстом стояли те
   же 9px, что и над строкой фабрики, и между подписью колонки и первой
   фабрикой лежала полоса пустоты в два отступа. Сверху место осталось — там
   шапка отделяется от кнопок раздела. */
table.task-mx thead th {
  background: var(--panel); font-weight: 400; color: var(--muted); font-size: 11px;
  line-height: 1.2; padding: 8px 6px 3px; text-align: center; vertical-align: bottom; width: 74px;
}
/* Имя производства стоит ЦЕЛИКОМ и в одну строку (24.09.2026, требование
   пользователя): «Jeans (F23) MD GROUP（裙子）» ломалось на две строки и растило
   всю строку доски вдвое. Колонка берёт ширину по самому длинному имени,
   остальные стоят по 74px, а если вместе они шире экрана — `.task-mx-wrap`
   прокручивается вбок. */
table.task-mx th.task-mx-name { text-align: left; width: auto; min-width: 200px; white-space: nowrap; }
table.task-mx tbody th.task-mx-name { font-weight: 400; padding: 0; }
.task-mx-fac {
  width: 100%; text-align: left; border: 0; background: transparent; font: inherit;
  padding: 9px 10px; cursor: pointer; color: var(--text); white-space: nowrap;
  display: flex; align-items: center; gap: 8px;
}
.task-mx-fac:hover { background: var(--panel-2); }
.task-mx-row.on .task-mx-fac { background: var(--accent-dim); font-weight: 600; }
.task-mx-cell {
  width: 100%; border: 0; background: transparent; font: inherit; cursor: pointer;
  padding: 6px 4px; color: var(--text); min-height: 34px;
}
.task-mx-cell:hover { background: var(--panel-2); }
/* Пустая клетка остаётся пустой — ни нуля, ни точки: глаз должен падать
   только на числа. Нажимать в ней нечего, и наведение тоже молчит. */
.task-mx-cell[disabled] { cursor: default; }
.task-mx-cell[disabled]:hover { background: transparent; }
/* Число в кружке (24.09.2026): в ряду одинаковых клеток глаз ловит пятно
   раньше, чем цифру, и «где работа есть» читается одним взглядом. */
.task-mx-cell .num {
  display: inline-flex; align-items: center; justify-content: center; position: relative;
  min-width: 26px; height: 26px; padding: 0 6px; border-radius: 999px;
  background: var(--accent-dim); color: var(--ink-green);
  font-family: var(--font-display); font-size: 14px; line-height: 1;
}
.task-mx-cell:hover .num { background: var(--accent); }
/* Залежавшееся — точка У КРАЯ кружка: под числом она растила строку, а сбоку
   от голой цифры читалась как звёздочка; на кружке это метка, как у счётчика
   непрочитанного. */
.task-mx-cell .dot {
  position: absolute; top: -1px; right: -1px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--c-b8515d-s);
}
table.task-mx th.split, table.task-mx td.split { border-left: 1px solid var(--line); }
.task-mx-open > td { padding: 8px 12px 10px; background: var(--bg); }

/* ── «НАСТРОЙКИ → ДОСТУП» (29.09.2026) ────────────────────────────────────
   Кто работает в CRM и вход через интернет (`ui/access.js`). */
.access-body { display: grid; gap: 12px; }
.access-users { display: grid; gap: 8px; }
.access-user {
  display: grid;
  gap: 6px;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.access-user.is-off { opacity: .6; }
.access-user-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.access-user-meta { font-size: 13px; }
.access-inline { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.access-inline input { flex: 1 1 200px; min-width: 0; }
.access-add {
  display: grid;
  gap: 10px;
  padding: 12px;
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
}
.access-handoff {
  display: grid;
  gap: 8px;
  padding: 12px;
  background: var(--accent-dim);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
}
.access-handoff-title { font-weight: 600; }
.access-handoff-text {
  margin: 0;
  font: 14px/1.5 ui-monospace, Menlo, monospace;
  white-space: pre-wrap;
  user-select: all;
}
.access-remote { display: grid; gap: 10px; margin-top: 8px; }
.access-link { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.access-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--muted); }
.access-link.is-up .access-dot { background: var(--accent); }
.access-link.is-down .access-dot { background: var(--danger); }
.access-link-why {
  padding: 6px 10px;
  font-size: 13px;
  color: var(--ink);
  background: var(--danger);
  border-radius: var(--radius-sm);
}
.access-url a { color: var(--accent-ink); }

/* ── ДОСЬЕ ЮНИТА В УЗКОМ МЕСТЕ ────────────────────────────────────────────
   Одна и та же разметка стоит правой колонкой «Продуктов» (там метров семь
   ширины) и боковой панелью поверх раздела (404 px). Мерится ширина САМОГО
   досье (`container-type` у `.dossier`), а не окна: на большом экране панель
   иначе получала вёрстку широкой колонки — две колонки по 170, внутри ещё две
   по 80, шесть кнопок этапов в ряд, — и текст лез за края.

   Блок стоит последним в файле нарочно: `@container` не добавляет селектору
   веса, и правила из середины файла (образцы, лента, кнопки) перебивали бы
   его при равной специфичности. */
@container dossier (max-width: 460px) {
  .dossier { padding: 14px; gap: 12px; }
  /* 24 px именем в 370 px панели съедали строку заголовка целиком, и этап с
     номером образца уезжали на третий ряд. */
  .dossier-name { font-size: 19px; }
  /* «Скрипты» прижималась вправо и в перенесённом ряду стояла одна посреди
     пустоты — в узком досье она просто идёт следующей. */
  .dossier-script { margin-left: 0; }
  /* Дата записи занимала 128 px из 340 — на текст оставалась треть строки.
     Ставим её отдельной строкой над текстом. */
  .tl-date { flex: 0 0 100%; }
  .card-timeline li { gap: 2px 11px; }
  /* Подпись образца в одну строку с многоточием прятала то, ради чего её
     читают: фабрику, цену и срок. В узком месте она переносится. */
  .card-sample-sub { white-space: normal; overflow: visible; text-overflow: clip; }
  /* Кнопка перестаёт быть «хвостом» строки и встаёт своей строкой слева, не
     отнимая ширину у подписи образца. Перенос делает сама подпись (основа
     100%), а кнопка остаётся по содержимому — во всю строку она читалась бы
     как главное действие карточки, а это не так. */
  .card-sample-main { flex: 1 1 100%; }
  .card-sample > .btn { margin-left: 0; flex: 0 0 auto; }
  .card-ready { flex-direction: column; align-items: flex-start; }
}
@container dossier (max-width: 330px) {
  /* Совсем узкое место (панель на маленьком экране): этапы по два в ряд,
     показатели — в одну колонку: две по 150 px уже врут про содержимое. */
  .stage-step { flex: 1 1 128px; }
  .stat-grid.two { grid-template-columns: minmax(0, 1fr); }
  .dossier { padding: 12px; }
}

/* ДОСЬЕ ВНУТРИ БОКОВОЙ ПАНЕЛИ — БЕЗ ВТОРОЙ РАМКИ.
   `.dossier` носит собственный фон, тень и отступ 18/20: в правой колонке
   «Продуктов» это его карточка, а в панели она ложилась внутрь такой же
   карточки — двойная рамка, двойной отступ и на 40 px меньше места под
   содержимое. И название стояло дважды: крупным в шапке панели и таким же
   крупным первой строкой досье.

   Стоит после запросов к контейнеру нарочно: они тоже правят отступ досье, и
   при равном весе селектора побеждает тот, кто ниже. */
.dossier-in-drawer {
  background: transparent; box-shadow: none; padding: 0; border-radius: 0;
}
.dossier-in-drawer > .dossier-top > .dossier-name { display: none; }
