/*
 * Базовые элементы и оболочка нового UI CRM. Цвета — var(--jg-*) из tokens.css; литералы
 * остались только на всегда-тёмных поверхностях (точки статуса в сайдбаре, левая панель
 * входа), где тема не переключается. Все селекторы — классы jg-* / внутри .jg: файл,
 * подключённый рядом со старыми страницами, не меняет их вид.
 * Размеры — из макетов (Design system, Models, Ref codes, Dialogs, Ops, Auth).
 * Отличия от макета, добавленные сознательно, помечены «не в макете».
 */

/* ── корень ─────────────────────────────────────────────────────────────── */
body.jg { margin: 0; }
.jg {
  font-family: var(--jg-font);
  font-size: 14px;
  line-height: 1.5;
  color: var(--jg-text);
  background: var(--jg-bg);
  -webkit-font-smoothing: antialiased;
  color-scheme: light; /* здесь, а не на :root — не менять нативные контролы старых страниц */
}
[data-theme='dark'] .jg { color-scheme: dark; }
.jg *, .jg *::before, .jg *::after { box-sizing: border-box; }
.jg :where(h1, h2, h3, h4, p, ul, ol, figure, dl) { margin: 0; }
.jg :where(button, input, select, textarea) { font: inherit; color: inherit; }
.jg :where(a) { color: var(--jg-link); text-decoration: none; }
.jg :where(a:hover) { color: var(--jg-link-hover); text-decoration: underline; }
.jg :where(table) { border-collapse: separate; border-spacing: 0; }
/* display у компонентов (.jg-btn — inline-flex…) иначе перебивает атрибут hidden */
.jg [hidden], .jg[hidden] { display: none !important; }

/* не в макете: у рантайма Claude Design фокуса нет вовсе — видимый фокус обязателен (handoff) */
.jg :where(a, button, [tabindex], summary):focus-visible {
  outline: 2px solid var(--jg-focus-outline);
  outline-offset: 2px;
}
/* программный фокус на не-интерактивном (плашка ошибки, заголовок, main) — не элемент управления */
.jg :where([tabindex='-1']):focus-visible { outline: none; }

.jg-mono { font-family: var(--jg-mono); }
.jg-num { font-family: var(--jg-mono); font-variant-numeric: tabular-nums; }
.jg-muted { color: var(--jg-muted); }
.jg-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── кнопки: S/M/L 30/36/42 ─────────────────────────────────────────────── */
.jg-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: var(--jg-h-m); padding: 0 16px;
  border: 1px solid transparent; border-radius: var(--jg-r-control);
  font: 600 13px/1 var(--jg-font); white-space: nowrap; text-decoration: none;
  cursor: pointer; user-select: none;
}
.jg-btn--s { height: var(--jg-h-s); padding: 0 12px; font-size: 12px; border-radius: var(--jg-r-badge); }
.jg-btn--l { height: var(--jg-h-l); padding: 0 18px; font-size: 14px; }
.jg-btn--block { width: 100%; }

.jg-btn--primary { background: var(--jg-accent); color: var(--jg-on-accent); }
.jg-btn--primary:hover { background: var(--jg-accent-hover); }
.jg-btn--secondary { background: var(--jg-surface); border-color: var(--jg-border-strong); color: var(--jg-text); font-weight: 500; }
.jg-btn--secondary:hover { background: var(--jg-btn2-hover); }
.jg-btn--ghost { padding: 0 12px; background: transparent; color: var(--jg-text-2); font-weight: 500; }
.jg-btn--ghost.jg-btn--l { padding: 0 14px; }
.jg-btn--ghost:hover { background: var(--jg-btn2-hover); color: var(--jg-text); }
.jg-btn--danger { background: var(--jg-err-bg); border-color: var(--jg-err-bd); color: var(--jg-danger); }
.jg-btn--danger:hover { background: var(--jg-err-ring); }
.jg-btn--icon { width: var(--jg-h-m); padding: 0; }
.jg-btn--s.jg-btn--icon { width: var(--jg-h-s); }

.jg-btn:disabled, .jg-btn[aria-disabled='true'] {
  background: var(--jg-disabled-bg); border-color: var(--jg-border); color: var(--jg-disabled);
  font-weight: 500; cursor: not-allowed;
}
/* у ghost в disabled — только приглушённый текст, без серой коробки */
.jg-btn--ghost:is(:disabled, [aria-disabled='true']) { background: transparent; border-color: transparent; }
/* saving: кнопка заблокирована, спиннер перед подписью — подпись говорит, что выполняется */
.jg-btn[aria-busy='true'] { cursor: progress; }
.jg-btn[aria-busy='true']::before {
  content: ''; width: 14px; height: 14px; flex: none; border-radius: 50%;
  border: 2px solid var(--jg-border-strong); border-top-color: var(--jg-accent);
  animation: jg-spin .8s linear infinite;
}
@keyframes jg-spin { to { transform: rotate(360deg); } }

/* ── поля ───────────────────────────────────────────────────────────────── */
.jg-field { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
/* Кнопки поля в строку, по левому краю — в отличие от .jg-rowactions, который прижимает
   действия строки таблицы вправо. Переносится, чтобы на узком экране не срезалась вторая. */
.jg-field__actions { display: flex; flex-wrap: wrap; gap: 6px; }
/* JET-1147: сетка коротких числовых полей (пороги сигналов). Не `.jg-grid-2`: полей пять, а
   значения — двузначные числа, и две широких колонки растягивали бы поле на треть экрана.
   `auto-fit` сам кладёт их в 1–3 колонки по ширине панели, поэтому своей медиазапроса не
   требует. */
.jg-formgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; }
/* Поле и его единица измерения в одну строку: «7» и «мин» — одна величина, а не две. */
.jg-inputrow { display: flex; align-items: center; gap: 8px; }
.jg-inputrow > .jg-input { width: 100%; min-width: 0; }
.jg-inputrow > .jg-hint { flex: none; }
/* Подписи полей — ЗАГЛАВНЫМИ, `.04em`, как в дизайн-системе (§06 «Поля»: «Имя модели»,
   «Telegram username», «Публичное био»). Тем же набраны шапки таблиц (`.jg-table th`) — в
   системе это одна роль, и расходиться они не должны.

   20.09.2026 заглавные отсюда сняли, опираясь на канву «Scenes - redaktor v2», где подписи
   полей нарисованы обычным регистром. Это была ошибка: канва одного экрана не отменяет
   систему, а `.jg-label` стоит в 84 местах — одна канва переписала подписи по всей CRM, и
   внутри продукта они разъехались с шапками таблиц. Счёт по источникам: заглавные —
   дизайн-система (24 подписи), «Диалоги» (6), «Модели» (15), «Вход» (10); обычный регистр —
   только «Scenes - redaktor v2». Возвращено системное правило. */
.jg-label {
  font-size: 12px; font-weight: 500; line-height: 1.3; letter-spacing: .04em;
  text-transform: uppercase; color: var(--jg-label);
}
.jg-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }

.jg-input, .jg-textarea, .jg-select, .jg-inputbox {
  width: 100%; min-height: var(--jg-h-l); padding: 11px 13px;
  border: 1px solid var(--jg-field-bd); border-radius: var(--jg-r-control);
  background-color: var(--jg-field); color: var(--jg-text);
  font-size: 14px; line-height: 1.3;
}
.jg-textarea { line-height: 1.5; resize: vertical; min-height: 88px; }
.jg-input--mono { font-family: var(--jg-mono); }
/* JET-1042: числовое поле предела длины — узкое, чтобы не читаться как текстовое. */
.jg-input--num { max-width: 120px; flex: 0 0 auto; }
.jg-input::placeholder, .jg-textarea::placeholder, .jg-inputbox > input::placeholder { color: var(--jg-muted); opacity: 1; }
/* background-color, не background: у select фон несёт стрелку */
.jg-input:focus, .jg-textarea:focus, .jg-select:focus, .jg-inputbox:focus-within {
  outline: none; background-color: var(--jg-field-focus); border-color: var(--jg-accent); box-shadow: 0 0 0 3px var(--jg-focus);
}
/* ошибка по Design system §06: в покое — мягкая красная рамка, в фокусе — красная + кольцо */
.jg-input[aria-invalid='true'], .jg-textarea[aria-invalid='true'], .jg-inputbox:has([aria-invalid='true']) {
  background-color: var(--jg-field-focus); border-color: var(--jg-err-bd);
}
.jg-input[aria-invalid='true']:focus, .jg-textarea[aria-invalid='true']:focus, .jg-inputbox:has([aria-invalid='true']):focus-within {
  border-color: var(--jg-err); box-shadow: 0 0 0 3px var(--jg-err-ring);
}
.jg-input:disabled, .jg-textarea:disabled, .jg-select:disabled {
  background-color: var(--jg-disabled-bg); border-color: var(--jg-border); color: var(--jg-disabled); cursor: not-allowed;
}
.jg-input[readonly], .jg-textarea[readonly], .jg-inputbox:has(> input[readonly]) {
  background-color: var(--jg-disabled-bg); border-color: var(--jg-border); color: var(--jg-text-2);
}

/* поле с элементом внутри (пароль «Показать», поиск) — рамка на обёртке */
.jg-inputbox { display: flex; align-items: center; gap: 10px; }
/* Префикс внутри рамки (@username): он часть значения, а не подпись поля, поэтому стоит
   внутри, вплотную к тексту — общий зазор 10px разрывал бы «@» и имя. */
.jg-inputbox--prefixed { gap: 2px; }
.jg-inputbox__prefix { flex: none; color: var(--jg-muted); }
.jg-inputbox > input { flex: 1; min-width: 0; border: 0; padding: 0; background: transparent; outline: none; font-size: inherit; }
.jg-inputbox__btn {
  flex: none; min-width: 24px; min-height: 24px; display: grid; place-content: center;
  border: 0; background: transparent; padding: 0 4px; margin: -4px -6px -4px 0; border-radius: 6px;
  font-size: 12px; font-weight: 500; color: var(--jg-label); cursor: pointer;
}
.jg-inputbox__btn:hover { color: var(--jg-text); }

/* нативный select: своя стрелка ▾ как в макете */
.jg-select {
  appearance: none; padding-right: 36px; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--jg-muted) 50%), linear-gradient(135deg, var(--jg-muted) 50%, transparent 50%);
  background-position: right 17px top 50%, right 12px top 50%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}

/* поиск — то же поле с элементом внутри, размер M */
.jg-search { min-height: var(--jg-h-m); padding: 0 12px; gap: 9px; font-size: 13px; }
.jg-search::before {
  content: ''; flex: none; width: 13px; height: 13px; background: var(--jg-disabled);
  -webkit-mask: var(--jg-icon-search) center / contain no-repeat; mask: var(--jg-icon-search) center / contain no-repeat;
}
.jg-search:focus-within::before { background: var(--jg-muted); }
.jg-search > input::-webkit-search-cancel-button { -webkit-appearance: none; }
.jg { --jg-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='7' cy='7' r='5.25' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3Cpath d='M11 11l3.5 3.5' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E"); }

.jg-hint { font-size: 12px; line-height: 1.5; color: var(--jg-muted); }
.jg-hint--err { color: var(--jg-err); }
/* Подпись-состояние рядом с ярлыком поля («свободен», «занят», «96 из 70 символов»). Тон
   задаётся data-tone, как у .jg-badge и .jg-note — одно правило на весь интерфейс. Без этих
   строк счётчик описания ставил data-tone='err' и оставался серым: тон был в разметке, но
   не в стилях. */
.jg-hint[data-tone='pos'] { color: var(--jg-pos); }
.jg-hint[data-tone='warn'] { color: var(--jg-warn); }
.jg-hint[data-tone='err'] { color: var(--jg-err); }
.jg-counter { font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); }

/* чекбокс и переключатель — нативные input, свой вид (Design system, раздел 06) */
.jg-check, .jg-switch { display: inline-flex; align-items: center; gap: 10px; font-size: 14px; cursor: pointer; }
.jg-check > input {
  appearance: none; margin: 0; flex: none; width: 20px; height: 20px; border-radius: 6px;
  border: 1.5px solid var(--jg-control-bd); background: var(--jg-surface); display: grid; place-content: center; cursor: pointer;
}
.jg-check > input::after { content: ''; width: 10px; height: 6px; border: solid var(--jg-on-accent); border-width: 0 0 2px 2px; transform: translateY(-1px) rotate(-45deg); opacity: 0; }
.jg-check > input:checked { background: var(--jg-accent); border-color: var(--jg-accent); }
.jg-check > input:checked::after { opacity: 1; }
/* Радиокнопка в том же ряду: круглая и с точкой вместо галочки. Без этого выбор «одно из
   двух» рисуется теми же квадратами, что независимая галочка рядом, и три одинаковых
   квадрата подряд читаются как три независимых переключателя (поймано на скриншоте
   JET-1099). Форма — единственное, что отличает одно от другого без чтения подписей. */
.jg-check > input[type="radio"] { border-radius: 50%; }
.jg-check > input[type="radio"]::after {
  width: 8px; height: 8px; border: 0; border-radius: 50%; background: var(--jg-on-accent); transform: none;
}
.jg-switch > input {
  appearance: none; margin: 0; flex: none; width: 38px; height: 22px; padding: 2px; border-radius: var(--jg-r-pill);
  background: var(--jg-control-bd); display: flex; align-items: center; cursor: pointer;
}
.jg-switch > input::after { content: ''; width: 18px; height: 18px; border-radius: 50%; background: var(--jg-knob); transition: transform .12s; }
.jg-switch > input:checked { background: var(--jg-accent); }
.jg-switch > input:checked::after { transform: translateX(16px); background: var(--jg-on-accent); }
.jg :is(.jg-check, .jg-switch) > input:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 2px; }
.jg :is(.jg-check, .jg-switch) > input:disabled { opacity: .5; cursor: not-allowed; }

/* ── пилюли-фильтры (aria-pressed) ─────────────────────────────────────── */
.jg-pills { display: flex; flex-wrap: wrap; gap: 7px; }
.jg-pill {
  display: inline-flex; align-items: center; gap: 5px; padding: 7px 13px;
  border: 1px solid transparent; border-radius: var(--jg-r-pill);
  background: var(--jg-fill); color: var(--jg-text-2); font-size: 13px; font-weight: 500; line-height: 1.3; cursor: pointer;
}
.jg-pill:hover { color: var(--jg-text); }
.jg-pill[data-tone='warn'] { background: var(--jg-warn-bg); color: var(--jg-warn-text); }
.jg-pill[aria-pressed='true'] { background: var(--jg-accent); color: var(--jg-on-accent); font-weight: 600; }
.jg-pill__n { font-variant-numeric: tabular-nums; }

/* JET-1149: полоса сводки очереди выдачи. Не `.jg-kpi` (дашборд): там герой с дельтой к
   прошлому периоду, здесь — равные плитки «состояние: сколько», без сравнения. `auto-fit`
   кладёт их в одну строку на широком и в две-три на узком, своего медиазапроса не требуя. */
.jg-qstats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
.jg-qstat {
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
  padding: 12px 14px; border-radius: var(--jg-r-tile); background: var(--jg-field);
}
.jg-qstat__num { font-family: var(--jg-mono); font-size: 22px; font-weight: 500; line-height: 1.1; font-variant-numeric: tabular-nums; }
.jg-qstat__label { font-size: 12px; color: var(--jg-text-2); overflow-wrap: anywhere; }
.jg-qstat[data-tone='warn'] { background: var(--jg-warn-bg); }
.jg-qstat[data-tone='warn'] .jg-qstat__num { color: var(--jg-warn-text); }
.jg-qstat[data-tone='err'] { background: var(--jg-err-bg); }
.jg-qstat[data-tone='err'] .jg-qstat__num { color: var(--jg-err); }
.jg-qstat[data-tone='pos'] .jg-qstat__num { color: var(--jg-pos); }

/* ── бейджи ─────────────────────────────────────────────────────────────── */
.jg-badge {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 9px; border-radius: var(--jg-r-badge);
  font-size: 12px; font-weight: 500; line-height: 1.3; white-space: nowrap;
  color: var(--jg-neu); background: var(--jg-neu-bg);
}
.jg-badge::before { content: ''; width: 6px; height: 6px; flex: none; border-radius: 50%; background: currentColor; }
.jg-badge--plain::before { content: none; }
.jg-badge--s { font-size: 11px; gap: 5px; }   /* список моделей: 4px 9px, 11px, без точки (+ --plain) */
.jg-badge[data-tone='pos'] { color: var(--jg-pos); background: var(--jg-pos-bg); }
.jg-badge[data-tone='wait'] { color: var(--jg-wait); background: var(--jg-wait-bg); }
.jg-badge[data-tone='warn'] { color: var(--jg-warn); background: var(--jg-warn-bg); }
.jg-badge[data-tone='err'] { color: var(--jg-err); background: var(--jg-err-bg); }
.jg-code { font-family: var(--jg-mono); font-size: 12px; font-weight: 500; padding: 4px 9px; border-radius: var(--jg-r-badge); background: var(--jg-fill); }

/* ── панели ─────────────────────────────────────────────────────────────── */
.jg-panel { background: var(--jg-surface); border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); }
.jg-panel--pad { padding: 22px; display: flex; flex-direction: column; gap: 16px; }
.jg-panel__head {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 20px; border-bottom: 1px solid var(--jg-border);
}
.jg-h3 { font-size: 15px; font-weight: 600; line-height: 1.4; }
.jg-kv { display: flex; flex-direction: column; gap: 10px; padding: 15px; border-radius: var(--jg-r-tile); background: var(--jg-field); }
.jg-kv__row { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 12px; }
.jg-kv__row > :first-child { color: var(--jg-text-2); }
.jg-kv__row > :last-child { font-weight: 500; text-align: right; min-width: 0; overflow-wrap: anywhere; } /* длинный crmId/строка из CLI — перенос, не вылет из drawer */

/* ── таблица: настоящая <table>, липкая шапка, скролл внутри своего блока ── */
.jg-tablebox { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.jg-tablescroll { flex: 1 1 auto; min-height: 0; overflow: auto; scroll-padding-top: 44px; /* строка под липкой шапкой не прячется */ }
.jg-table { width: 100%; min-width: 760px; table-layout: fixed; font-size: 13px; line-height: 1.3; }
.jg-table th {
  position: sticky; top: 0; z-index: 1; background: var(--jg-surface-2); border-bottom: 1px solid var(--jg-border);
  padding: 11px 16px 11px 0; font-size: 12px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase;
  color: var(--jg-label); text-align: left; white-space: nowrap;
}
.jg-table td { padding: 13px 16px 13px 0; border-bottom: 1px solid var(--jg-divider); vertical-align: middle; }
.jg-table :is(th, td):first-child { padding-left: 20px; }
.jg-table :is(th, td):last-child { padding-right: 20px; }
.jg-table tbody tr:last-child td { border-bottom: 0; }
.jg-table .jg-right { text-align: right; }
.jg-table td.jg-num { font-size: 12px; color: var(--jg-muted); }
/* «готово / всего» в колонках МЕДИА и ОФФЕРЫ; оранжевым — пока готово не всё (макет
   «Models — список и карточка»). Моноширинный с табличными цифрами, чтобы дроби в соседних
   строках стояли столбиком. */
.jg-ready { font-family: var(--jg-mono); font-variant-numeric: tabular-nums; font-size: 12px; white-space: nowrap; }
.jg-ready[data-tone='warn'] { color: var(--jg-warn); }
.jg-table td.jg-cell-2 { font-size: 12px; color: var(--jg-text-2); }   /* второстепенные колонки (Models, список) */
.jg-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* строка-ссылка: клик в любом месте строки, средняя кнопка/Ctrl/Cmd — новая вкладка,
   клавиатура (Tab → Enter) — реальная ссылка внутри первой ячейки. Область клика — JS
   (models-list.js), не position:absolute/inset:0 на tr: WebKit не всегда считает <tr>
   контейнером позиционирования, ::after последней строки перекрывал остальные (regression). */
.jg-table--rows tbody tr:has(.jg-rowlink) { cursor: pointer; }
.jg-table--rows tbody tr:hover { background: var(--jg-surface-2); }
.jg-rowlink { color: inherit; font-weight: 600; }
.jg-rowlink:hover { color: inherit; text-decoration: none; }
.jg-rowlink:focus-visible { outline: none; }
.jg-table--rows tbody tr:has(.jg-rowlink:focus-visible) { outline: 2px solid var(--jg-focus-outline); outline-offset: -2px; }
.jg-tablefoot {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 20px; border-top: 1px solid var(--jg-divider); font-size: 12px; line-height: 1.3; color: var(--jg-muted);
}
/* «N из M» справа в подвале таблицы: перенос рвал его пополам, когда подпись слева стала
   длиннее (колонки МЕДИА и ОФФЕРЫ). Переносится подпись, счётчик — нет. */
.jg-tablefoot > :last-child { flex: none; white-space: nowrap; }
.jg-entity { display: flex; align-items: center; gap: 11px; min-width: 0; }
.jg-entity__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.jg-entity__sub { font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); }
.jg-entity__meta { font-family: var(--jg-font); font-variant-numeric: tabular-nums; }
.jg-avatar {
  flex: none; width: 34px; height: 34px; border-radius: 50%; background: var(--jg-fill); overflow: hidden;
  display: grid; place-content: center; font-size: 12px; font-weight: 600; color: var(--jg-muted); text-transform: uppercase;
}
.jg-cellnote { display: block; margin-top: 3px; font-size: 11px; line-height: 1.35; color: var(--jg-warn); }
.jg-cellnote[data-tone='err'] { color: var(--jg-err); }

/* ── плашки ─────────────────────────────────────────────────────────────── */
.jg-note {
  display: flex; gap: 13px; padding: 15px 17px; border-radius: var(--jg-r-tile);
  border: 1px solid var(--jg-accent-border); background: var(--jg-accent-soft);
  font-size: 13px; line-height: 1.55; color: var(--jg-text-2);
}
.jg-note::before { content: ''; width: 8px; height: 8px; margin-top: 6px; flex: none; border-radius: 50%; background: var(--jg-accent); }
.jg-note__body { display: flex; flex-direction: column; gap: 3px; min-width: 0; overflow-wrap: anywhere; }
.jg-note__title { font-weight: 600; color: var(--jg-text); }
.jg-note--s { gap: 12px; padding: 13px 15px; font-size: 12px; }
.jg-note--s .jg-note__title { font-size: 13px; }
.jg-note[data-tone='warn'] { border-color: var(--jg-warn-bd); background: var(--jg-warn-bg); color: var(--jg-warn-text); }
.jg-note[data-tone='warn']::before { background: var(--jg-warn); }
.jg-note[data-tone='err'] { border-color: var(--jg-err-bd); background: var(--jg-err-bg); color: var(--jg-err-text); }
.jg-note[data-tone='err']::before { background: var(--jg-err); }
.jg-note:is([data-tone='err'], [data-tone='warn']) .jg-note__title { color: inherit; }
.jg-note[data-tone='wait'] { border-color: var(--jg-wait-bd); background: var(--jg-wait-soft); color: var(--jg-wait-text); }
.jg-note[data-tone='wait']::before { background: var(--jg-wait); }

/* ── состояния: пусто / ошибка / 401 / 403 / 404 ────────────────────────── */
.jg-state {
  display: flex; flex-direction: column; align-items: center; gap: 9px; text-align: center;
  max-width: 440px; margin: 0 auto; padding: 48px 24px;
}
/* плитка — только у «пусто» и «ошибки», как в Design system §09 */
.jg-state__icon { width: 34px; height: 34px; border-radius: var(--jg-r-control); background: var(--jg-fill); margin: 6px 0; }
.jg-state[data-kind='error'] .jg-state__icon { background: var(--jg-err-bg); }
.jg-state__kicker { font-size: 12px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase; color: var(--jg-label); }
.jg-state__title { font-size: 14px; font-weight: 600; }
.jg-state__text { font-size: 13px; line-height: 1.5; color: var(--jg-muted); text-wrap: pretty; overflow-wrap: anywhere; }
.jg-state__actions { display: flex; gap: 10px; margin-top: 6px; }

.jg-skel { display: block; height: 12px; border-radius: 6px; background: var(--jg-fill); animation: jg-pulse 1.4s ease-in-out infinite; }
.jg-skel--circle { width: 34px; height: 34px; border-radius: 50%; flex: none; }
.jg-skel-lines { flex: 1; gap: 6px; }
@keyframes jg-pulse { 50% { opacity: .55; } }

@media (prefers-reduced-motion: reduce) {
  .jg-skel, .jg-btn[aria-busy='true']::before { animation: none; }
  .jg-switch > input::after { transition: none; }
}

/* ── модальное окно и drawer: нативный <dialog> + showModal() ─────────────
   фон инертен, Esc закрывает, фокус возвращается на открывшую кнопку — средствами браузера */
.jg-dialog {
  width: min(520px, calc(100vw - 32px)); max-height: calc(100dvh - 48px); padding: 0;
  border: 0; border-radius: var(--jg-r-dialog); background: var(--jg-surface); color: var(--jg-text);
  box-shadow: var(--jg-shadow-pop); overflow: auto;
}
.jg-dialog::backdrop { background: var(--jg-backdrop); }
.jg-dialog__inner { display: flex; flex-direction: column; gap: 18px; padding: 24px; }
.jg-dialog__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.jg-dialog__title { font-size: 18px; font-weight: 600; line-height: 1.3; min-width: 0; overflow-wrap: anywhere; }
.jg-dialog__title[data-tone='danger'] { color: var(--jg-err-text); }
.jg-dialog__body { display: flex; flex-direction: column; gap: 16px; }
.jg-dialog__text { font-size: 13px; line-height: 1.6; color: var(--jg-text-2); text-wrap: pretty; }
.jg-dialog__close { margin: -6px -8px 0 0; color: var(--jg-muted); font-size: 15px; }
.jg-dialog__foot { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
.jg-dialog__foot-note { margin-right: auto; font-size: 11px; color: var(--jg-muted); }
.jg-form { display: flex; flex-direction: column; gap: 16px; }
.jg-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }

/* не в макете: drawer не нарисован — собран из контекстной панели Dialogs (граница слева) */
.jg-dialog--drawer {
  width: min(400px, 100vw); height: 100dvh; max-height: 100dvh; margin: 0 0 0 auto;
  border-radius: 0; border-left: 1px solid var(--jg-border); box-shadow: var(--jg-shadow-drawer);
}
.jg-dialog--drawer .jg-dialog__inner { min-height: 100%; padding: 20px; }

/* ── вкладки (role=tablist, стрелки ←/→) ─────────────────────────────────── */
.jg-tabs { display: flex; flex-wrap: wrap; gap: 4px; }
.jg-tab {
  padding: 9px 15px; border: 1px solid transparent; border-radius: var(--jg-r-control); background: transparent;
  font-size: 13px; font-weight: 500; color: var(--jg-text-2); cursor: pointer; white-space: nowrap;
}
.jg-tab:hover { color: var(--jg-text); }
.jg-tab[aria-selected='true'] { background: var(--jg-fill); color: var(--jg-text); font-weight: 600; }
.jg-tab:focus-visible { outline-offset: 0; }

/* ── оболочка: сайдбар + рабочая колонка ───────────────────────────────── */
/* `position: relative` — не украшение, а то, что заставляет `overflow: hidden` работать.
   Абсолютно позиционированный потомок берёт содержащим блоком ближайшего позиционированного
   предка; если его нет, это корень документа — и обрезка оболочки на него не распространяется.
   В интерфейсе такие потомки есть у каждого экрана: подписи для чтения с экрана `.jg-sr`
   (`position: absolute` без `top`/`left`, то есть стоящие там же, где были бы в потоке).
   Внутри длинного редактора сцены они оказываются на полутора тысячах пикселей ниже окна и
   растягивали scroll-бокс ДОКУМЕНТА: страница прокручивалась на ~1000px, хотя вся прокрутка
   должна жить внутри `.jg-cardbody`. Это и есть «вёрстка вываливается за пределы оболочки»
   с отчёта владельца: замер в браузере 20.09.2026 — `scrollTo(0, 5000)` уводил документ на
   1056px при окне 858px. */
.jg-app { position: relative; display: flex; height: 100dvh; min-height: 0; overflow: hidden; }
.jg-side {
  width: var(--jg-side-w); flex: none; display: flex; flex-direction: column; gap: 4px;
  padding: 16px 12px; overflow-y: auto;
  background: var(--jg-ink); border-right: 1px solid var(--jg-ink-border); color: var(--jg-ink-text);
  --jg-focus-outline: var(--jg-ink-focus);
}
.jg-side__brand { display: flex; align-items: center; gap: 9px; padding: 6px 10px 16px; font-size: 14px; font-weight: 600; color: var(--jg-ink-strong); }
.jg-side__logo { width: 24px; height: 24px; flex: none; border-radius: 7px; background: var(--jg-accent); }
.jg-nav { display: flex; flex-direction: column; gap: 4px; }
.jg-nav__item {
  display: flex; align-items: center; gap: 10px; padding: 9px 11px;
  border: 1px solid transparent; border-radius: var(--jg-r-control);
  font-size: 13px; font-weight: 500; line-height: 1.3; color: var(--jg-ink-text);
}
.jg-side .jg-nav__item:hover { background: var(--jg-ink-2); color: var(--jg-ink-text); text-decoration: none; }
.jg-side .jg-nav__item[aria-current='page'] { background: var(--jg-nav-active-bg); color: var(--jg-nav-active-fg); }
.jg-side__foot { margin-top: auto; padding-top: 16px; display: flex; flex-direction: column; gap: 10px; }

/* Статичная оболочка до первого paint (#jg-boot) рисует ТЕ ЖЕ пункты, что нарисует
   createShell, — иначе каждое переключение вкладки пересобирало меню на глазах (замер
   18.09.2026: сайдбар расходился с итоговым на 90% полосы). Раздел знает сама страница
   (data-section на <html>), роль берётся из прошлой сессии (crm.js) — обе до первого
   кадра, без сети и без JS-вставок в разметку.
   Пункт админа по умолчанию скрыт: показать лишнее и убрать хуже, чем дорисовать. */
#jg-boot .jg-nav__item[data-admin-only] { display: none; }
:root[data-role='admin'] #jg-boot .jg-nav__item[data-admin-only] { display: flex; }
:root[data-section='dashboard'] #jg-boot .jg-nav__item[data-section='dashboard'],
:root[data-section='dialogs'] #jg-boot .jg-nav__item[data-section='dialogs'],
:root[data-section='models'] #jg-boot .jg-nav__item[data-section='models'],
:root[data-section='referrals'] #jg-boot .jg-nav__item[data-section='referrals'],
:root[data-section='ops'] #jg-boot .jg-nav__item[data-section='ops'] {
  background: var(--jg-nav-active-bg); color: var(--jg-nav-active-fg);
}
/* Сайдбар тёмный в ОБЕИХ темах, а --jg-fill — заливка для светлых поверхностей (#F0F0F3 в
   светлой теме). Скелетон на нём горел почти белым — измеренная причина жалобы «пункты меню
   загораются белым». Здесь тёмная поверхность сайдбара, та же, что у блока аккаунтов. */
.jg-side .jg-skel { background: var(--jg-ink-2); }
.jg-side__accounts { display: flex; align-items: center; gap: 8px; padding: 10px 11px; border-radius: var(--jg-r-control); background: var(--jg-ink-2); font-size: 12px; line-height: 1.35; color: var(--jg-ink-text); }
.jg-side__accounts::before { content: ''; width: 7px; height: 7px; flex: none; border-radius: 50%; background: var(--jg-ink-muted); }
/* сайдбар тёмный в обеих темах — точки берут тёмные семантические значения (как в макете) */
.jg-side__accounts[data-tone='pos']::before { background: #3DBE8B; }
.jg-side__accounts[data-tone='warn']::before { background: #E0A83C; }
.jg-side__accounts[data-tone='err']::before { background: #F2726A; }
.jg-seg { display: flex; gap: 4px; padding: 9px 11px; border-radius: 11px; background: var(--jg-ink-2); }
.jg-seg > button {
  flex: 1; padding: 6px; border: 1px solid transparent; border-radius: var(--jg-r-badge); background: transparent;
  font-size: 11px; font-weight: 500; line-height: 1.3; color: var(--jg-ink-label); cursor: pointer;
}
.jg-seg > button[aria-pressed='true'] { background: var(--jg-ink-seg-on); color: var(--jg-ink-seg-on-text); font-weight: 600; cursor: default; }
.jg-user { display: flex; align-items: center; gap: 9px; padding: 4px 8px 2px; }
.jg-user__avatar { width: 28px; height: 28px; flex: none; border-radius: 50%; background: var(--jg-ink-avatar); display: grid; place-content: center; font-size: 11px; font-weight: 600; color: var(--jg-ink-strong); }
.jg-user__name { flex: 1; font-size: 12px; line-height: 1.35; color: var(--jg-ink-strong); }
.jg-user__logout {
  flex: none; border: 0; background: transparent; padding: 6px 8px; margin-right: -4px; border-radius: var(--jg-r-badge);
  font-size: 12px; font-weight: 500; color: var(--jg-ink-label); cursor: pointer;
}
.jg-user__logout:hover { background: var(--jg-ink-2); color: var(--jg-ink-strong); }

.jg-main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
/* не в макете: ссылка «к содержимому» видна только при фокусе с клавиатуры */
.jg-skip { position: absolute; left: 12px; top: 12px; z-index: 20; padding: 8px 12px; border-radius: var(--jg-r-control); background: var(--jg-surface); color: var(--jg-text); box-shadow: var(--jg-shadow-menu); }
.jg-skip:not(:focus) { width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* шапка экрана; высота постоянна при смене состояний (кнопка L + отступы) */
.jg-pagehead {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  min-height: 79px; padding: 18px var(--jg-gutter); background: var(--jg-surface); border-bottom: 1px solid var(--jg-border);
}
.jg-pagehead__titles { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.jg-pagehead__title { font-size: 22px; font-weight: 600; line-height: 1.25; letter-spacing: -.01em; }
.jg-pagehead__sub { min-height: 1.5em; font-size: 13px; color: var(--jg-muted); }
.jg-pagehead__actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.jg-pagehead__actions .jg-search { width: 240px; }

.jg-toolbar { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 16px var(--jg-gutter); }
.jg-page { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* Прокрутка тела страницы. Экраны со списком кладут сюда панель с `max-height: 100%` и
   прокручивают таблицу внутри себя — там эта прокрутка не появляется вовсе. А вкладки Ops
   кладут обычную колонку, и без неё содержимое просто обрезалось: на окне 1512×790 тело
   551 px, вкладка «Аккаунты Telegram» 820 px, прокрутки нет ни у чего — нижний ряд карточек
   с кнопками «Открыть» и «Редактировать профиль» недостижим (замер 20.09.2026). */
.jg-page__body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; margin: 0 var(--jg-gutter) var(--jg-gutter); }
/* без строки фильтров (загрузка/ошибка/пусто) панель не прилипает к шапке */
.jg-toolbar[hidden] + .jg-page__body { margin-top: 24px; }
/* панель списка — по высоте содержимого, но не выше доступного места (скролл внутри) */
.jg-page__body > .jg-panel { max-height: 100%; }
.jg-page__state { flex: 1; display: grid; place-content: center; }

/* низкое окно (увеличение 200%, маленький экран): вместо скролла внутри таблицы — скролл
   страницы, иначе таблице достаётся несколько пикселей (WCAG 1.4.4/1.4.10) */
@media (max-height: 640px) {
  /* Кроме «Диалогов». У них три независимые колонки и поле ответа внизу чата, и страничная
     прокрутка их ломает: оболочка растягивалась до 1988 px, а до поля ответа приходилось
     листать 1375 px (замер 20.09.2026, окно 1512×700). Причина, по которой правило вообще
     есть, — таблица, которой достаётся несколько пикселей; таблиц на этом экране нет, а
     колонки и без того прокручиваются каждая сама. */
  html:not([data-section='dialogs']) .jg-app { height: auto; min-height: 100dvh; overflow: visible; }
  html:not([data-section='dialogs']) .jg-side { position: sticky; top: 0; height: 100dvh; }
  .jg-page__body { overflow: visible; }
  .jg-page__body > .jg-panel { max-height: none; }
  .jg-tablebox, .jg-tablescroll { overflow: visible; }
}

/* режим высокой контрастности: форма и выбранное состояние без фоновых заливок */
@media (forced-colors: active) {
  .jg-pill[aria-pressed='true'], .jg-nav__item[aria-current='page'], .jg-seg > button[aria-pressed='true'], .jg-tab[aria-selected='true'] {
    border-color: Highlight; outline: 2px solid Highlight; outline-offset: -2px;
  }
}

/* ── вход: разделённый экран (Auth, артборд 01) ─────────────────────────── */
.jg-auth { min-height: 100dvh; display: flex; background: var(--jg-surface); }
.jg-auth__aside {
  width: 420px; flex: none; display: flex; flex-direction: column; justify-content: space-between;
  padding: 34px 32px; color: #FFFFFF;
  background: #1D1524 radial-gradient(110% 80% at 12% 0%, #4A2A3C 0%, rgba(29, 21, 36, 0) 62%);
  border-right: 1px solid var(--jg-ink-border); /* в тёмной теме панель иначе сливается с формой */
}
.jg-auth__brand { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.jg-auth__brand > span:first-child { display: flex; align-items: center; gap: 10px; font-size: 15px; font-weight: 600; }
.jg-auth__brand .jg-side__logo { width: 26px; height: 26px; border-radius: 8px; }
.jg-auth__pill { padding: 5px 11px; border-radius: var(--jg-r-pill); background: rgba(255, 107, 0, .16); color: var(--jg-ink-focus); font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.jg-auth__text-block { display: flex; flex-direction: column; gap: 22px; }
.jg-auth__lead { font-size: 30px; font-weight: 600; line-height: 1.2; letter-spacing: -.02em; text-wrap: pretty; }
.jg-auth__text { font-size: 14px; line-height: 1.6; color: #D9D0DE; }
.jg-auth__list { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 0; list-style: none; }
.jg-auth__item { display: flex; align-items: flex-start; gap: 11px; font-size: 13px; line-height: 1.5; color: #D9D0DE; }
.jg-auth__dot { flex: none; width: 7px; height: 7px; margin-top: 6px; border-radius: var(--jg-r-pill); background: var(--jg-accent); }
/* Форма у ЛЕВОГО края правой части — как в артборде (там `justify-content:center` только по
   вертикали). Центрирование по горизонтали было моей вольностью и на 1512 уводило форму на
   58px вправо от макетного места. */
.jg-auth__main { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; padding: 36px 52px; }
.jg-auth__form { width: 100%; max-width: 360px; display: flex; flex-direction: column; gap: 24px; }
.jg-auth__head { display: flex; flex-direction: column; gap: 6px; }
.jg-auth__title { font-size: 24px; font-weight: 600; letter-spacing: -.01em; line-height: 1.25; }
.jg-auth__sub { font-size: 13px; color: var(--jg-muted); }
.jg-auth__fields { display: flex; flex-direction: column; gap: 16px; }
.jg-auth__form .jg-input, .jg-auth__form .jg-inputbox { padding: 12px 13px; }
.jg-auth__form .jg-btn--l { height: 46px; font-size: 15px; }
.jg-auth__pass { font-family: var(--jg-mono); letter-spacing: .14em; }
.jg-auth__pass[type='text'] { font-family: var(--jg-font); letter-spacing: 0; }
@media (max-width: 900px) { .jg-auth__aside { display: none; } .jg-auth__main { padding: 32px 20px; } }

/* ══ карточка модели (этап 2): шапка, вкладки, профиль, медиатека ═══════════ */
.jg-cardhead { flex: none; display: flex; flex-direction: column; gap: 16px; padding: 18px var(--jg-gutter); background: var(--jg-surface); border-bottom: 1px solid var(--jg-border); }
.jg-cardhead__row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.jg-cardhead__back { font-size: 13px; font-weight: 500; color: var(--jg-text-2); }
.jg-cardhead__titles { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.jg-cardhead__line { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.jg-cardhead__title { font-size: 20px; font-weight: 600; line-height: 1.25; overflow-wrap: anywhere; }
.jg-cardhead__user { font-family: var(--jg-mono); font-size: 12px; color: var(--jg-muted); }
.jg-avatar--l { width: 52px; height: 52px; font-size: 16px; overflow: hidden; }
.jg-avatar:has(> img) { place-content: stretch; }
.jg-avatar > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.jg-tab[data-tone='wait'][aria-selected='true'] { background: var(--jg-wait-bg); color: var(--jg-wait-strong); }
.jg-cardbody { container-type: inline-size; flex: 1; min-height: 0; overflow-y: auto; padding: 22px var(--jg-gutter) var(--jg-gutter); display: flex; flex-direction: column; gap: 20px; }
.jg-cardbody--split { padding: 0; flex-direction: row; gap: 0; overflow: hidden; }
.jg-cols { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: 20px; align-items: start; }
.jg-stack { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.jg-section { padding: 22px; display: flex; flex-direction: column; gap: 18px; }
.jg-section--s { padding: 20px; gap: 13px; }
.jg-section--danger { border-color: var(--jg-err-bd); }
.jg-section--danger .jg-h3 { color: var(--jg-err-text); }
.jg-section__head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.jg-section__text { font-size: 12px; line-height: 1.55; color: var(--jg-text-2); }
.jg-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.jg-status-line { font-size: 12px; color: var(--jg-muted); }
.jg-status-line[data-tone='pos'] { color: var(--jg-pos); }
.jg-tag { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--jg-r-pill); background: var(--jg-accent-tint); color: var(--jg-accent-text); font-size: 11px; font-weight: 600; line-height: 1.3; }
.jg-tag::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--jg-accent); }
.jg-kvlist { display: flex; flex-direction: column; gap: 10px; }
.jg-kvlist > div { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 12px; }
.jg-kvlist dt { color: var(--jg-text-2); }
.jg-kvlist dd { margin: 0; font-weight: 500; text-align: right; min-width: 0; overflow-wrap: anywhere; }
.jg-kvlist dd[data-tone='pos'] { color: var(--jg-pos); }
.jg-kvlist dd[data-tone='warn'] { color: var(--jg-warn); }
.jg-kvlist dd[data-tone='err'] { color: var(--jg-err); }
.jg-kvlist dd[data-tone='wait'] { color: var(--jg-wait); }
/* список строк с точкой тона — предпроверки и отчёт о публикации */
.jg-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
/* JET-1150: перечисление шагов, доступных человеку («Ручное вмешательство»). Маркер здесь
   к месту, в отличие от `.jg-lines`: это список действий, а не лента событий со временем. */
.jg-bullets { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; font-size: 13px; line-height: 1.5; color: var(--jg-text-2); }
.jg-bullets > li { text-wrap: pretty; overflow-wrap: anywhere; }
.jg-lines > li { display: flex; gap: 9px; font-size: 12px; line-height: 1.45; color: var(--jg-text-2); overflow-wrap: anywhere; }
.jg-lines > li::before { content: ''; width: 7px; height: 7px; margin-top: 5px; flex: none; border-radius: 50%; background: var(--jg-neu); }
.jg-lines > li[data-tone='pos']::before { background: var(--jg-pos); }
.jg-lines > li[data-tone='warn']::before { background: var(--jg-warn); }
.jg-lines > li[data-tone='err']::before { background: var(--jg-err); }
.jg-lines > li[data-tone='err'] { color: var(--jg-err-text); }
/* JET-905: раскрываемый список файлов у сгруппированной строки (одна причина, много файлов) */
.jg-details { margin-top: 4px; }
.jg-details > summary { cursor: pointer; color: var(--jg-text-2); }
.jg-lines--nested { margin-top: 6px; padding-left: 14px; }
.jg-subhead { font-size: 12px; font-weight: 600; color: var(--jg-text); }

/* аватар и обложка — отдельные слоты */
.jg-slots { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr); gap: 16px; }
.jg-slot { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.jg-slot > .jg-actions { margin-top: auto; } /* кнопки двух слотов на одной линии, даже когда в одном идёт загрузка */
.jg-slot__img, .jg-tile__pv, .jg-maside__pv {
  position: relative; border-radius: var(--jg-r-tile); border: 1px solid var(--jg-border); overflow: hidden;
  background: repeating-linear-gradient(135deg, var(--jg-fill) 0 8px, var(--jg-surface-2) 8px 16px);
  display: grid; place-items: end center; padding: 10px; font-family: var(--jg-mono); font-size: 10px; color: var(--jg-muted);
}
.jg-slot__img { height: 140px; }
.jg-slot__img > img, .jg-tile__pv > img, .jg-maside__pv > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* видео в плитке — только первый кадр вместо постера: тот же кроп, что у фото, контролов нет */
.jg-tile__pv > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: var(--jg-fill); }
/* почему превью не показано — читаемым текстом поверх плитки, а не пустым прямоугольником */
.jg-pvfail { position: absolute; inset: 0; display: grid; align-content: center; justify-items: center; gap: 3px; padding: 10px; text-align: center; font-size: 11px; line-height: 1.35; color: var(--jg-text-2); background: var(--jg-surface-2); }
.jg-pvfail > span:last-child { font-size: 10px; color: var(--jg-muted); }
/* метка «видео» поверх кадра: z-index — потому что сам кадр лежит absolute и иначе перекрыл бы её */
.jg-tile__vmark { position: relative; z-index: 1; padding: 2px 7px; border-radius: 7px; background: var(--jg-surface); color: var(--jg-text-2); font-size: 10px; }
/* JET-944: реальный плеер в панели файла (не постер-кроп — контролы должны видеть весь кадр) */
.jg-maside__pv > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }
.jg-slot__meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 11px; font-weight: 500; }
.jg-fileinput { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.jg-progress { appearance: none; width: 100%; height: 4px; border: 0; border-radius: var(--jg-r-pill); background: var(--jg-fill); overflow: hidden; }
.jg-progress::-webkit-progress-bar { background: var(--jg-fill); }
.jg-progress::-webkit-progress-value { background: var(--jg-accent); }
.jg-progress::-moz-progress-bar { background: var(--jg-accent); }
.jg-chip { display: inline-flex; align-items: center; padding: 2px 7px; border-radius: 7px; font-size: 10px; font-weight: 500; line-height: 1.4; background: var(--jg-fill); color: var(--jg-text-2); white-space: nowrap; }
.jg-chip[data-tone='wait'] { background: var(--jg-wait-bg); color: var(--jg-wait-strong); }
.jg-chip[data-tone='pos'] { background: var(--jg-pos-bg); color: var(--jg-pos); }
.jg-dot { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 500; color: var(--jg-neu); }
.jg-dot::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.jg-dot[data-tone='pos'] { color: var(--jg-pos); }

/* медиатека: сетка + панель файла справа */
/* container-type — чтобы сетка ниже считала колонки по СВОЕЙ ширине, а не по ширине окна:
   панель файла забирает 330px, и на одном и том же 1440 сетка то шире, то уже на треть. */
/* Прокручивается ТОЛЬКО сетка (макет «Media», холсты 01/02: шапка раздела там `flex:none`).
   Раньше уезжала вся колонка — вкладки разделов, зона загрузки и фильтры пропадали вверх, и
   до фильтра приходилось возвращаться прокруткой. */
.jg-mmain { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 16px; overflow: hidden; padding: 18px var(--jg-gutter) 0; container-type: inline-size; }
.jg-drop { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 16px 18px; border: 1.5px dashed var(--jg-border-strong); border-radius: var(--jg-r-tile); background: var(--jg-surface); }
.jg-drop[data-over] { border-color: var(--jg-accent); background: var(--jg-accent-soft); }
/* min-width — по самой длинной строке заголовка («Перетащите фото и видео или выберите файлы»):
   в макете он всегда в одну строку. С 220px на 1280 с открытой панелью файла выбор назначения
   отжимал текст и заголовок ломался пополам; теперь при нехватке места на новую строку
   переносится выбор назначения (flex-wrap у .jg-drop), а заголовок остаётся целым.
   370px — натуральная ширина этой строки в САМОМ широком из доступных шрифтов (замер: 367px в
   системном запасном, 314px в IBM Plex). Считать надо по запасному, а не по Plex: шрифты с
   18.09 грузятся вне критического пути (media="print", см. model.html), поэтому первые кадры
   страницы — и вся страница, если Google не ответил, — рисуются именно запасным. */
.jg-drop__text { flex: 1; min-width: 370px; display: flex; flex-direction: column; gap: 2px; font-size: 13px; }
.jg-drop__text > span { font-size: 12px; color: var(--jg-muted); }
.jg-select--s { min-height: var(--jg-h-m); padding: 7px 34px 7px 12px; font-size: 13px; width: auto; }
.jg-queue { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); background: var(--jg-surface); }
.jg-queue > li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; padding: 10px 14px; border-bottom: 1px solid var(--jg-divider); font-size: 12px; }
.jg-queue > li:last-child { border-bottom: 0; }
.jg-queue__name { font-weight: 500; }
.jg-queue__state { display: flex; align-items: center; justify-content: flex-end; gap: 4px; font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); }
.jg-queue__state[data-tone='pos'] { color: var(--jg-pos); }
.jg-queue__state[data-tone='err'] { color: var(--jg-err); }
.jg-queue__msg { grid-column: 1 / -1; font-size: 11px; color: var(--jg-err); overflow-wrap: anywhere; }
.jg-queue__msg[data-tone='warn'] { color: var(--jg-warn); }
.jg-queue .jg-progress { grid-column: 1 / -1; }
.jg-queue__foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; font-size: 12px; color: var(--jg-muted); }
/* Макет «Media — витрина и Telegram», холсты 01/02: сетка — 4 колонки, gap 12.
   auto-fill здесь не годился: он держит ширину плитки и добивает колонками, поэтому на 1920
   получалось 8 плиток по 191px вместо композиции макета. Порог 940px — ширина рабочей
   области самого макета: 1330 холст − 330 панель файла − 2×26 отступа = 948px под сетку.
   Шире неё держим 4 колонки макета, уже — снимаем одну, иначе на 1280 с открытой панелью
   плитка ужимается до 166px. Считаем по контейнеру (.jg-mmain), а не по окну: колонки должны
   реагировать на открытие панели — она забирает 330px, и на одном 1440 сетка то 1148, то 818.
   Пятая колонка с 1400px — чтобы на действительно широком экране плитка не разъезжалась до
   400px: макет такой ширины не описывает, но 315px там ближе к его 228px, чем 398px. */
/* grid-auto-rows — НЕ украшение и не дубль `align-content: start`. С 20.09 сетка сама стала
   областью прокрутки (`.jg-msection > .jg-mgrid`), то есть высота у неё ТЕПЕРЬ ЗАДАНА. А у
   ряда `auto` минимум — это минимальный вклад плитки, и он равен нулю: у `.jg-tile` стоит
   `overflow: hidden`, а он обнуляет автоминимум. Пока файлов мало, рядов хватает места и
   этого не видно; как только рядов больше, чем влезает, алгоритм раскладки ужимает ряды до
   нуля вместо прокрутки — замер 20.09: 8 файлов → плитка 198px, 34 файла → 25px, от плитки
   остаётся полоса кадра поверх подвала. `max-content` запрещает ужимать ряд: лишнее уезжает
   в прокрутку, как и задумано. Тот же корень, что у карточек дашборда (JET-1092). */
.jg-mgrid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); grid-auto-rows: max-content; gap: 12px; align-content: start; }
@container (min-width: 1400px) { .jg-mgrid:not(.jg-mgrid--s) { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
@container (max-width: 940px) { .jg-mgrid:not(.jg-mgrid--s) { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@container (max-width: 680px) { .jg-mgrid:not(.jg-mgrid--s) { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.jg-tile { position: relative; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); background: var(--jg-surface); overflow: hidden; }
/* hover — середина между акцентом и поверхностью: #FFB27A макета в светлой, заметно и в тёмной */
.jg-tile:hover { border-color: color-mix(in srgb, var(--jg-accent) 50%, var(--jg-surface)); }
.jg-tile[data-active] { border-color: var(--jg-accent); box-shadow: 0 0 0 1px var(--jg-accent); }
.jg-tile__open { display: flex; flex-direction: column; width: 100%; padding: 0; border: 0; background: transparent; text-align: left; cursor: pointer; color: inherit; }
/* кольцо — на всей плитке: внутреннее у кнопки перекрывало превью (position/z-index детей) */
.jg-tile__open:focus-visible { outline: none; }
.jg-tile:has(> .jg-tile__open:focus-visible) { outline: 2px solid var(--jg-focus-outline); outline-offset: 2px; }
/* Пропорция превью — из макета: плитка 228px при картинке 128px, это 16/9. Фиксированная
   высота держалась, только пока держалась ширина плитки; с 4 колонками плитка растёт вместе с
   окном, и 128px превратились бы в узкую полосу поверх широкого подвала. */
.jg-tile__pv { aspect-ratio: 16 / 9; height: auto; border: 0; border-radius: 0; border-bottom: 1px solid var(--jg-border); place-items: end start; padding: 8px; }
.jg-tile__body { display: flex; flex-direction: column; gap: 7px; padding: 10px; }
.jg-tile__row { display: flex; align-items: center; justify-content: space-between; gap: 6px; flex-wrap: wrap; }
.jg-tile__id { font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); }
.jg-tile__check { position: absolute; top: 8px; left: 8px; z-index: 1; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; background: var(--jg-surface); box-shadow: 0 0 0 1px var(--jg-border-strong); }
/* Угловая метка макета — тёмная плашка поверх фотографии (rgba(21,20,26,.78) / белый), а не
   плашка цвета поверхности: светлая метка на светлом кадре не читалась. Позиция — справа:
   левый верхний угол макета свободен, а здесь в нём стоит флажок выбора (.jg-tile__check).
   Цвет метки не зависит от темы — она лежит на кадре, а не на фоне CRM. */
.jg-tile__order { position: absolute; top: 8px; right: 8px; z-index: 1; font-family: var(--jg-mono); font-size: 11px; font-weight: 600; padding: 3px 8px; border-radius: 7px; background: rgba(21, 20, 26, .78); color: #FFFFFF; }
/* Материалы для Telegram: метка окрашена по назначению — цвета из макета (холст 02) */
.jg-tile__order[data-purpose='PUBLIC'] { background: #1C3D63; color: #DCE9F7; }
.jg-tile__order[data-purpose='PREVIEW'] { background: #123A2A; color: #CDEBDD; }
.jg-tile__order[data-purpose='PAID'] { background: #5A3418; color: #FFD9B5; }
.jg-maside { width: 330px; flex: none; display: flex; flex-direction: column; gap: 16px; padding: 20px; overflow-y: auto; background: var(--jg-surface); border-left: 1px solid var(--jg-border); }
.jg-maside__pv { height: 210px; } /* макет: превью файла в панели — 210px */
.jg-maside__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.jg-maside .jg-btn:not(.jg-btn--icon) { width: 100%; }
@media (max-width: 1360px) { .jg-maside { width: 290px; } }
/* ── JET-994: два раздела медиатеки ──────────────────────────────────────── */
/* Сегментированный переключатель на поверхности контента: та же форма, что у переключателя
   темы в боковой панели (.jg-seg выше), но на токенах поверхности — ink-токены рассчитаны на
   постоянно тёмную панель и на белом фоне читались бы как чужой элемент. */
.jg-seg--content { padding: 3px; border-radius: var(--jg-r-control); background: var(--jg-fill); }
.jg-seg--content > button { padding: 7px 12px; font-size: 12px; white-space: nowrap; color: var(--jg-text-2); }
.jg-seg--content > button:hover:not([aria-pressed='true']) { color: var(--jg-text); }
.jg-seg--content > button[aria-pressed='true'] { background: var(--jg-surface); color: var(--jg-text); border-color: var(--jg-border); }
/* «Назначение» в зоне загрузки — рядом с кнопкой, не во всю ширину строки */
.jg-drop .jg-field { flex: none; gap: 5px; }
/* JET-1030: содержимое раздела — колонка с тем же ритмом 16px, что и у .jg-mmain. Раньше это
   был обычный блок без промежутков: подсказка раздела прилипала к рамке зоны загрузки, а
   пилюли фильтров — к первому ряду плиток. Скрытые блоки (очередь, состояние сетки, ошибка
   массовой пометки) — display:none и лишнего промежутка не дают. */
.jg-msection { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 16px; }
.jg-msection > .jg-mgrid { flex: 1; min-height: 0; overflow-y: auto; padding-bottom: var(--jg-gutter); }

/* заголовок панели файла: название раздела растягивается, ✕ — по месту.
   JET-1030: заголовок стоит один в своей строке, метка назначения и короткий id — строкой
   ниже. Одной строкой («Файл для Telegram · 6c664794» + чип + ✕) заголовок ломался на две-три
   строки на панели 290–330px. */
.jg-maside__head { align-items: flex-start; }
.jg-maside__heading { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.jg-maside__sub { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.jg-maside__sid { font-size: 11px; color: var(--jg-muted); }
/* подпись под плиткой (оффер/«есть и на витрине») — одной строкой, без переноса */
.jg-tile__row.jg-ellipsis { display: block; font-size: 11px; color: var(--jg-text-2); }
/* хвост строки состояния: чипы прижаты вправо и не растаскивают строку переносом */
.jg-tile__tail { display: flex; align-items: center; gap: 6px; flex: none; }
.jg-toolbar--flush { padding: 0; }
/* Пока медиатека грузится: блок держит свою высоту, но содержимое не показывает. Числа — замер
   готового состояния (вводная строка 18, панель фильтров 30). Без этого оба включались одним
   кадром вместе со вкладками разделов, и сетка прыгала вниз на 135px без единого действия
   оператора. Прятать содержимое приходится потому, что до ответа сервера оно неверное. */
.jg-msection > .jg-hint[data-skel] { min-height: 18px; }
.jg-toolbar[data-skel] { min-height: 30px; }
.jg-toolbar[data-skel] > * { display: none; }
.jg-pills--s .jg-pill { padding: 6px 12px; font-size: 12px; }
.jg-maside > * { flex-shrink: 0; }
/* Сноска панели («путь файла», «файл из оплаченного оффера удалить нельзя») в макете прижата
   к низу панели, а не висит сразу под кнопками, когда содержимого мало. */
.jg-maside__foot { margin-top: auto; padding-top: 4px; font-size: 11px; line-height: 1.5; color: var(--jg-muted); }

/* ══ вкладки «Персона для AI» и «Офферы» (этап 2); размеры — Models, холсты 03 и 05 ══════ */
/* содержимое вкладки; отступы 22px/gutter и прокрутку даёт панель карточки (.jg-cardbody),
   колонки персоны складываются по ширине самой вкладки, не окна */
.jg-tabview { display: flex; flex-direction: column; gap: 20px; min-width: 0; container-type: inline-size; }
.jg-tabview__titles { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* ── персона: сетка и секции — классы профиля (.jg-cols / .jg-stack / .jg-section) ──────── */
.jg-persona__aside { position: sticky; top: 0; } /* отступ сверху даёт padding панели прокрутки */
/* две колонки складываются по ширине своего контейнера (.jg-cardbody / .jg-tabview), не окна */
@container (max-width: 820px) {
  .jg-cols { grid-template-columns: minmax(0, 1fr); }
  .jg-persona__aside { position: static; }
}
/* персона фиолетовая, как в холсте 03: кнопка сохранения и фокус полей (остальной акцент — общий).
   Текст кнопки — цвет поверхности: белый в светлой теме (6:1), тёмный в тёмной (6.2:1) */
.jg-persona :is(.jg-input, .jg-textarea, .jg-btn--primary) {
  --jg-accent: var(--jg-wait); --jg-accent-hover: var(--jg-wait-strong); --jg-on-accent: var(--jg-surface); --jg-focus: var(--jg-wait-bg);
}

/* ── тон: пресеты (JET-1043) ────────────────────────────────────────────── */
/* Карточка пункта — тот же .jg-offercard, что выбор оффера; здесь их две в ряд, и ряд
   складывается в одну колонку по ширине ВКЛАДКИ (.jg-tabview — container), а не окна:
   левая колонка персоны сама по себе узкая, и подписи в два слова ломались бы раньше,
   чем окно дойдёт до узкой ширины. */
.jg-tones { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
/* Вид выбранного/наведённого — целиком из .jg-offercard; здесь только переведён его акцент
   в фиолетовый персоны (те же токены, что у полей и кнопки сохранения выше), чтобы вкладка
   не оказалась единственным местом с двумя акцентами сразу. */
.jg-tone {
  align-items: center; min-height: var(--jg-h-l);
  --jg-accent: var(--jg-wait); --jg-accent-soft: var(--jg-wait-soft); --jg-accent-hover-bd: var(--jg-wait-bd);
}
/* «Свой тон» — пятый пункт: занимает ряд целиком, как в макете */
.jg-tone:last-child { grid-column: 1 / -1; }
.jg-tone .jg-offercard__title { white-space: normal; overflow-wrap: anywhere; }
/* только просмотр (оператор) и время сохранения: пункт не «сломан», а не редактируется.
   Селектор с родителем — чтобы перебить .jg-offercard[data-off] (она ниже по файлу и при
   равной силе выигрывала бы: там приглушение уместно, здесь это нормальное состояние). */
.jg-tones .jg-tone[data-off] { opacity: 1; color: var(--jg-text-2); }
@container (max-width: 560px) { .jg-tones { grid-template-columns: minmax(0, 1fr); } }

.jg-stages { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.jg-stage { display: flex; gap: 13px; padding: 14px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); }
.jg-stage__n {
  width: 26px; height: 26px; flex: none; border-radius: var(--jg-r-badge); display: grid; place-content: center;
  background: var(--jg-wait-bg); color: var(--jg-wait-strong); font: 500 12px/1 var(--jg-mono);
}
.jg-stage__fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 12px; }
.jg-stage__tools { flex: none; display: flex; gap: 4px; align-items: flex-start; }
.jg-stage .jg-textarea { min-height: 64px; }
/* «+ Добавить этап» — кнопка в виде ссылки у левого края (холст 03: 13/600, цвет ссылки, без отступа) */
.jg-addlink { align-self: flex-start; padding-inline: 0; color: var(--jg-link); font-weight: 600; }
.jg-addlink:not(:disabled, [aria-disabled='true']):hover { background: transparent; color: var(--jg-link-hover); }

.jg-bans { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; padding: 0; }
.jg-bans:empty { display: none; }
.jg-ban {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px 4px 4px 11px; border-radius: var(--jg-r-pill);
  background: var(--jg-err-bg); color: var(--jg-err-text); font-size: 12px; font-weight: 500; line-height: 1.3; max-width: 100%;
}
.jg-ban--ro { padding-right: 11px; }
.jg-ban__text { overflow-wrap: anywhere; }
.jg-ban__x {
  flex: none; width: 24px; height: 24px; display: grid; place-content: center; border: 0; border-radius: 50%;
  background: transparent; color: var(--jg-err); font-size: 11px; cursor: pointer;
}
.jg-ban__x:hover { background: var(--jg-err-ring); }
.jg-addrow { display: flex; gap: 8px; }
.jg-addrow--tight { align-items: center; }
.jg-addrow > .jg-input { flex: 1; min-width: 0; }
.jg-addrow > .jg-btn { height: var(--jg-h-l); }

.jg-checklist { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.jg-checklist__item { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--jg-muted); }
.jg-checklist__box {
  width: 18px; height: 18px; flex: none; border-radius: 6px; display: grid; place-content: center;
  border: 1px solid var(--jg-control-bd); background: var(--jg-surface); font-size: 11px;
}
.jg-checklist__item[data-done='true'] { color: var(--jg-text); }
.jg-checklist__item[data-done='true'] .jg-checklist__box { border-color: var(--jg-pos-bd); background: var(--jg-pos-bg); color: var(--jg-pos); }
.jg-count { font: 500 13px/1 var(--jg-mono); font-variant-numeric: tabular-nums; }

/* ── offers ─────────────────────────────────────────────────────────────── */
.jg-rowactions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; }
.jg-rowerr { display: block; margin-top: 6px; font-size: 11px; line-height: 1.35; color: var(--jg-err); text-align: left; overflow-wrap: anywhere; }
.jg-cellnote[data-tone='muted'] { color: var(--jg-muted); }
.jg-cell-title { font-weight: 500; }
.jg-table td.jg-price { font-size: 13px; font-weight: 500; color: var(--jg-text); } /* цена — 13/500, не приглушённая */
/* JET-1137: у комбинированного альбома цен две (доллары картой, звёзды кнопкой Telegram).
   Каждая — своей строкой и целиком: в узкой колонке склеенная строка переносилась посреди
   числа и роняла «★» на отдельную строку. */
.jg-price__line { display: block; white-space: nowrap; }

/* плитка превью: фото — <img> оригинала, видео/без файла — нейтральная плитка с подписью */
.jg-thumb {
  display: grid; place-content: center; width: 100%; aspect-ratio: 1; border-radius: var(--jg-r-badge);
  background: var(--jg-fill); object-fit: cover; font-size: 11px; color: var(--jg-muted);
}
.jg-thumb--s { width: 48px; height: 48px; flex: none; }

/* выбор PAID-медиа: сетка настоящих чекбоксов; окно шире обычного — сетке нужно место */
.jg-dialog:has(.jg-pickgrid) { width: min(640px, calc(100vw - 32px)); }
.jg-picker { border: 0; margin: 0; padding: 0; min-width: 0; display: flex; flex-direction: column; gap: 9px; }
.jg-picker > legend { padding: 0; float: left; width: 100%; }
.jg-pickgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; max-height: 300px; overflow: auto; padding: 3px; }
.jg-check.jg-pick {
  position: relative; display: flex; flex-direction: column; align-items: stretch; gap: 6px; padding: 7px;
  border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); background: var(--jg-surface); font-size: 11px;
}
.jg-pick > input { position: absolute; top: 12px; left: 12px; }
.jg-pick:has(> input:checked) { border-color: var(--jg-accent); box-shadow: 0 0 0 3px var(--jg-focus); }
.jg-pick__id { font-family: var(--jg-mono); color: var(--jg-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jg-counter[data-tone='err'] { color: var(--jg-err); }

.jg-mlist { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.jg-mlist__item { display: flex; align-items: center; gap: 11px; padding: 8px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); min-width: 0; }
.jg-mlist__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; font-size: 12px; }

/* ══ этап 3: колонка «Диалоги» (холст Dialogs, список L60–105) ═══════════════════════════ */
/* Строка «модели + список + чат + панель» под общей шапкой (jg-main уже flex-column на всю
   высоту). Жила в <style> самой dialogs.html; переехала сюда вместе с адаптивом (JET-1064):
   правило раскладки и её пороги — одно место, иначе они разъедутся. */
.jg-dialogs-row { flex: 1; min-height: 0; display: flex; align-items: stretch; }
.jg-dlist { width: 330px; flex: none; display: flex; flex-direction: column; min-height: 0; background: var(--jg-surface); border-right: 1px solid var(--jg-border); }
.jg-dlist__head { display: flex; flex-direction: column; gap: 12px; padding: 16px 18px 12px; border-bottom: 1px solid var(--jg-border); }
.jg-dlist__titles { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.jg-dlist__title { font-size: 17px; font-weight: 600; line-height: 1.3; }
.jg-dlist__count { font-size: 12px; color: var(--jg-muted); white-space: nowrap; }
.jg-dlist__body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.jg-dlist__scroll { flex: 1; min-height: 0; overflow-y: auto; }
.jg-dlist__state { padding: 24px 18px; }
.jg-drow {
  display: flex; align-items: center; gap: 11px; padding: 13px 18px; text-decoration: none; color: inherit;
  border-bottom: 1px solid var(--jg-divider); border-left: 3px solid transparent; background: var(--jg-surface);
}
.jg :where(a).jg-drow:hover { text-decoration: none; color: inherit; background: var(--jg-surface-2); }
.jg-drow[data-selected] { background: var(--jg-accent-soft); border-left-color: var(--jg-accent); }
.jg :where(a).jg-drow[data-selected]:hover { background: var(--jg-accent-soft); }
/* JET-1038: в кружке может лежать фотография собеседника или его инициалы; точка режима
   остаётся поверх (она у него абсолютная, а overflow прячет только квадратные углы фото). */
.jg-drow__avatar { position: relative; flex: none; width: 36px; height: 36px; border-radius: 50%; background: var(--jg-fill); display: grid; place-content: center; font-size: 12px; font-weight: 600; color: var(--jg-muted); }
.jg-drow__photo { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.jg-drow__dot { position: absolute; right: -1px; bottom: -1px; width: 11px; height: 11px; border-radius: 50%; border: 2px solid var(--jg-surface); background: var(--jg-neu); }
.jg-drow__dot[data-tone='pos'] { background: var(--jg-pos); }
.jg-drow__dot[data-tone='wait'] { background: var(--jg-wait); }
.jg-drow__dot[data-tone='err'] { background: var(--jg-err); }
.jg-drow__text { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.jg-drow__top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
/* Имя человека — обычным шрифтом: моноширинный он был, пока в строке стоял голый tg-id. */
.jg-drow__name { font-size: 13px; font-weight: 600; }
.jg-drow__id { font-size: 11px; color: var(--jg-muted); }
.jg-drow__time { font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); white-space: nowrap; }
.jg-drow__tags { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.jg-badge--xs { padding: 2px 7px; border-radius: 7px; font-size: 11px; font-weight: 500; }

/* ══ JET-1051: столбец моделей слева от списка диалогов ═════════════════════════════════ */
.jg-mcol {
  width: 124px; flex: none; display: flex; flex-direction: column; min-height: 0;
  padding: 12px 9px; gap: 4px; overflow-y: auto;
  background: var(--jg-bg); border-right: 1px solid var(--jg-border);
}
.jg-mcol__title {
  padding: 5px 7px; font-size: 10px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--jg-muted);
}
.jg-mcol__list { display: flex; flex-direction: column; gap: 4px; }
.jg-mrow {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px;
  border: 1px solid transparent; border-radius: 10px; background: transparent;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.jg-mrow:hover { background: var(--jg-surface-2); }
/* Выбранная строка — карточка поверх фона столбца: рамка и подложка, не только цвет текста,
   чтобы выбор читался и в тёмной теме, и при forced-colors. */
.jg-mrow[data-selected] { background: var(--jg-surface); border-color: var(--jg-border); }
.jg-mrow[data-selected] .jg-mrow__name { color: var(--jg-text); }
.jg-mrow__mark {
  flex: none; width: 28px; height: 28px; border-radius: 50%; overflow: hidden;
  display: grid; place-content: center; background: var(--jg-fill);
  font-size: 10px; font-weight: 700; color: var(--jg-muted);
}
.jg-mrow__mark:has(> img) { place-content: stretch; }
.jg-mrow__mark > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.jg-mrow__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.jg-mrow__name { font-size: 11px; font-weight: 600; color: var(--jg-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jg-mrow__count { font-family: var(--jg-mono); font-size: 10px; color: var(--jg-muted); }
/* Счётчик чатов кружком на аватаре — в широкой раскладке число уже стоит словами в строке
   (`__count`), поэтому кружок показывается только на рельсе. Цвета через --jg-neu/--jg-bg:
   в светлой теме это тёмный кружок со светлым числом, в тёмной — наоборот, без второй пары
   токенов. */
.jg-mrow__n {
  display: none; position: absolute; top: -3px; right: -3px;
  min-width: 18px; height: 18px; padding: 0 4px; border-radius: var(--jg-r-pill);
  background: var(--jg-neu); color: var(--jg-bg); border: 2px solid var(--jg-bg);
  font-size: 10px; font-weight: 600; line-height: 1; place-content: center;
}
.jg-mrow[data-selected] .jg-mrow__n { background: var(--jg-accent); color: var(--jg-on-accent); }
/* Подпись модели в строке списка — не состояние, поэтому без рамки ярлыка. */
.jg-drow__model { font-size: 11px; color: var(--jg-muted); max-width: 100%; }

/* ══ этап 3: чат диалога (холст Dialogs, чат L107–161) ═══════════════════════════════════ */
/* `min-height: 0` и столбец у обёртки панели — не косметика, а доступность содержимого.
   У флекс-элемента `min-height` по умолчанию `auto`, то есть он не сжимается ниже своего
   содержимого. Обёртка `.jg-chat__panel` получала раскладку ТОЛЬКО внутри
   `@media (max-width:1400px)`, а шире была обычным блоком по высоте содержимого — и
   `overflow-y: auto` у `.jg-sale` не включался вовсе: прокручивать было нечего, панель сама
   выросла до 1988px. Всё, что ниже окна, обрезала оболочка.

   Цена ошибки: на 1680 и 1440 список «Счета диалога» уходил на 1025 и 1144 пикселя ниже
   экрана и был недостижим ни колесом, ни клавиатурой — вместе с кнопкой «Отправить набор» и
   разбором альбомного счёта. То есть на самых обычных ширинах оператор физически не мог
   довести продажу до конца. Замерено в браузере: `scrollTop` не сдвигался. */
.jg-chat { flex: 1; min-width: 0; min-height: 0; display: flex; }
.jg-chat__panel { display: flex; min-height: 0; }
.jg-chat__main { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; background: var(--jg-bg); }
.jg-chat__state { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: center; padding: 32px 24px; background: var(--jg-bg); }
.jg-chathead { display: flex; align-items: center; gap: 16px; padding: 13px 20px; background: var(--jg-surface); border-bottom: 1px solid var(--jg-border); }
.jg-chathead__avatar { position: relative; flex: none; width: 38px; height: 38px; border-radius: 50%; background: var(--jg-fill); display: grid; place-content: center; font-size: 14px; font-weight: 600; color: var(--jg-muted); }
/* inset вместо размеров по потоку: у кружка `place-content: center`, и картинка «в 100%»
   внутри такой сетки сжалась бы до нуля. */
.jg-chathead__photo { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.jg-chathead__titles { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.jg-chathead__line { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; min-width: 0; }
.jg-chathead__badges { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Имя человека — обычным шрифтом. Моноширинный остаётся только у запасного «#<tg-id>»: это
   данные, а не имя. Ровно ту же ошибку уже починили в строке списка (`.jg-drow__name`,
   JET-1038), а здесь осталась вторая копия старого правила — «Райан Гослинг» рисовался
   моноширинным и читался разреженным. */
.jg-chathead__title { font-size: 15px; font-weight: 600; line-height: 1.3; }
.jg-chathead__title[data-kind='id'] { font-family: var(--jg-mono); }
/* JET-1064: `overflow-wrap: anywhere` на узком чате ломал «#7770101 · от лица «Olyababe»»
   по ОДНОМУ символу в строке и растил шапку на пол-экрана. Подпись целиком не нужна —
   нужна её узнаваемая часть, поэтому многоточие. */
.jg-chathead__sub { font-size: 12px; color: var(--jg-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jg-chathead__actions { display: flex; align-items: center; gap: 10px; flex: none; }
.jg-chat__panelbtn { display: none; }
/* JET-1055: крестик живёт в самой выдвинутой панели — в широком окне панель стоит
   рядом с лентой и закрывать её нечем и незачем, поэтому он там скрыт. */
.jg-chat__panelclose { display: none; position: absolute; top: 10px; right: 14px; z-index: 1;
  background: var(--jg-surface); color: var(--jg-muted); font-size: 15px; }
.jg-chat .jg-note { margin: 12px 20px 0; }
.jg-chat__feedbox { flex: 1; min-height: 0; overflow-y: auto; }
.jg-feed { display: flex; flex-direction: column; gap: 12px; padding: 20px 24px; }
.jg-feed__empty { margin: auto; font-size: 13px; color: var(--jg-muted); }
.jg-msg { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; max-width: 62%; }
.jg-msg--out { align-self: flex-end; align-items: flex-end; }
.jg-msg__bubble { padding: 11px 14px; border-radius: 14px 14px 14px 4px; background: var(--jg-surface); border: 1px solid var(--jg-border); color: var(--jg-text); font-size: 13px; line-height: 1.5; overflow-wrap: anywhere; white-space: pre-wrap; }
.jg-msg--out .jg-msg__bubble { border-radius: 14px 14px 4px 14px; background: var(--jg-bubble-out); border-color: var(--jg-accent-border); color: var(--jg-bubble-out-text); }
.jg-msg__empty { color: var(--jg-muted); font-style: italic; }
/* JET-1046: вложение внутри пузыря. Превью ограничено по высоте, чтобы лента оставалась
   лентой; `aspect-ratio: auto` + фон-заглушка держат место до загрузки — без этого картинка
   дорисовывалась после отрисовки и лента прыгала под курсором. */
/* Ширина задана, а не «по содержимому»: иначе превью растягивается под длину подписи и
   соседние картинки в ленте получаются разного размера. */
.jg-msg__media { display: block; width: 240px; max-width: 100%; margin: -3px 0 6px; border-radius: 10px; overflow: hidden; background: var(--jg-fill); }
.jg-msg__img { display: block; width: 100%; max-height: 320px; object-fit: cover; }
.jg-msg__text { display: block; }
/* Подпись вместо картинки: файл есть, но показать его отсюда нечем. */
.jg-msg__file { display: block; margin-bottom: 4px; color: var(--jg-muted); font-size: 12px; }
.jg-msg--out .jg-msg__file { color: var(--jg-bubble-out-text); opacity: .75; }
/* Тот же текст, но под самой картинкой — внутри коробки превью, поэтому со своими полями. */
.jg-msg__media .jg-msg__file { margin: 0; padding: 4px 8px 5px; }
.jg-msg__meta { display: flex; align-items: center; gap: 7px; font-family: var(--jg-mono); font-size: 10px; color: var(--jg-muted); }
/* JET-1076: галочки доставки, как в самом Telegram. Одна — отправлено, две — прочитано.
   Разница видна и без цвета (одна галочка против двух) и озвучивается меткой на самом
   глифе — цвет здесь усиление, а не единственный носитель смысла.
   `letter-spacing` отрицательный: две галочки в Telegram наложены друг на друга, и без
   сжатия пара читается как два отдельных значка. */
.jg-tick { font-family: var(--jg-mono); font-size: 11px; line-height: 1; color: var(--jg-muted); }
.jg-tick--read { letter-spacing: -3px; padding-right: 3px; color: var(--jg-pos); }
.jg-msg__note { font-size: 11px; line-height: 1.45; color: var(--jg-muted); overflow-wrap: anywhere; }
.jg-msg__note[data-tone='err'] { color: var(--jg-err); }
.jg-msg__note[data-tone='warn'] { color: var(--jg-warn); }
.jg-msg__day { align-self: center; padding: 5px 12px; border-radius: var(--jg-r-pill); background: var(--jg-sys-bg); font-size: 11px; color: var(--jg-text-2); }
/* JET-1074: оплаченная выдача в ленте. НЕ пузырь: пузырь читается как сказанное моделью, а это
   событие разговора — что ушло покупателю после оплаты. Поэтому строка по центру, как разделитель
   дня, но с рамкой и подписью состояния: разделитель дня не носит состояния и не должен начать
   выглядеть как оно. Превью здесь нет ни при каком состоянии — платное не показывается в ленте. */
.jg-delivery { align-self: center; display: flex; flex-direction: column; gap: 4px; align-items: center; max-width: 70%; padding: 8px 14px; border: 1px solid var(--jg-accent-border); border-radius: var(--jg-r-pill); background: var(--jg-sys-bg); }
.jg-delivery__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 7px; }
.jg-delivery__text { font-size: 12px; color: var(--jg-text); overflow-wrap: anywhere; }
.jg-delivery .jg-msg__note { text-align: center; }
.jg-msg__skel { height: 44px; border-radius: 14px; }
.jg-msg__skel--out { align-self: flex-end; }
.jg-composer { display: flex; flex-direction: column; gap: 8px; padding: 14px 20px; background: var(--jg-surface); border-top: 1px solid var(--jg-border); }
.jg-composer__row { display: flex; align-items: flex-end; gap: 12px; }
.jg-composer__field { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.jg-composer__input { min-height: 66px; }
.jg-composer__meta { display: flex; justify-content: flex-end; }
.jg-counter[data-tone='warn'] { color: var(--jg-warn); }
.jg-hint--err[data-tone='warn'] { color: var(--jg-warn); }

/* ══ JET-1152: меню «···» ════════════════════════════════════════════════════════════════ */
/* Один компонент на весь интерфейс (`menu()` в ui.js). Всплывает вниз-вправо от кнопки и
   закрывается кликом мимо; ширину не тянет по самому длинному пункту, иначе «Копировать
   идентификатор игрока» растягивал бы меню на пол-экрана. */
.jg-menu { position: relative; display: inline-flex; flex: none; }
.jg-menu__button { font-size: 15px; line-height: 1; letter-spacing: 1px; }
.jg-menu__list {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 20;
  min-width: 220px; max-width: 280px; display: flex; flex-direction: column; padding: 6px;
  background: var(--jg-surface); border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel);
  box-shadow: var(--jg-shadow-menu);
}
.jg-menu__list[hidden] { display: none; }
.jg-menu__item {
  display: flex; flex-direction: column; gap: 1px; align-items: flex-start; width: 100%;
  padding: 8px 10px; border: 0; border-radius: var(--jg-r-badge); background: transparent;
  font-family: inherit; font-size: 13px; font-weight: 500; color: var(--jg-text);
  text-align: left; cursor: pointer;
}
.jg-menu__item:hover:not([disabled]) { background: var(--jg-fill); }
.jg-menu__item[disabled] { color: var(--jg-muted); cursor: default; }
.jg-menu__item[data-tone='danger'] { color: var(--jg-err); }
.jg-menu__item[data-tone='danger']:hover:not([disabled]) { background: var(--jg-err-bg); }
.jg-menu__note { font-size: 11px; font-weight: 400; color: var(--jg-muted); }
.jg-menu__sep { height: 1px; margin: 5px 4px; background: var(--jg-border); }

/* ══ JET-1151: вкладки правой панели диалога и вкладка «Пользователь» ════════════════════ */
/* Полоса вкладок стоит НАД обеими панелями и не прокручивается вместе с ними: переключиться
   обратно на «Пользователя» из конца длинного списка счетов иначе можно было бы только
   прокрутив панель до самого верха. */
.jg-chat__panel { flex-direction: column; }
.jg-chat__panel > .jg-tabs {
  flex: none; gap: 2px; padding: 8px 10px; background: var(--jg-surface);
  border-left: 1px solid var(--jg-border); border-bottom: 1px solid var(--jg-border);
}
.jg-chat__tabpanel { flex: 1; min-height: 0; display: flex; }
.jg-chat__tabpanel[hidden] { display: none; }

.jg-peer { width: 360px; flex: none; display: flex; flex-direction: column; min-height: 0; overflow-y: auto; background: var(--jg-surface); border-left: 1px solid var(--jg-border); }
.jg-peer__state { padding: 18px; }
.jg-peer__body { display: flex; flex-direction: column; }
.jg-peer__head, .jg-peer__section { display: flex; flex-direction: column; gap: 12px; padding: 18px; border-bottom: 1px solid var(--jg-border); }
.jg-peer__ident { display: flex; align-items: center; gap: 12px; }
.jg-peer__avatar { position: relative; flex: none; width: 44px; height: 44px; border-radius: 50%; background: var(--jg-fill); display: grid; place-content: center; font-size: 15px; font-weight: 600; color: var(--jg-muted); }
.jg-peer__photo { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.jg-peer__idtext { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.jg-peer__title { margin: 0; font-size: 15px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jg-peer__sub { margin: 0; font-size: 12px; color: var(--jg-text-2); font-family: var(--jg-mono); }
.jg-peer__badges { display: flex; flex-wrap: wrap; gap: 6px; }
/* Две цифры в ряд — «Всего оплатил» и «Средний чек». У нескольких валют строк становится
   больше двух, и они переносятся сами: складывать валюты нельзя (курса Stars↔USD нет). */
.jg-peer__spend { display: flex; flex-wrap: wrap; gap: 10px; }
.jg-peer__spend:empty { display: none; }
.jg-peer__stat { flex: 1 1 130px; display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); background: var(--jg-surface-2); }
.jg-peer__stat-value { font-size: 15px; font-weight: 600; }

.jg-peer__rows { display: flex; flex-direction: column; gap: 10px; }
/* Подпись и значение в столбик, а не в строку: в 360px «Аккаунт модели» и имя модели рядом
   не помещаются и рвут строку посередине слова. */
.jg-peer__row { display: flex; flex-direction: column; gap: 2px; }
.jg-peer__row-label { font-size: 11px; color: var(--jg-muted); }
.jg-peer__row-value { font-size: 13px; overflow-wrap: anywhere; }
.jg-peer__row-value[data-tone='warn'] { color: var(--jg-warn); }
.jg-peer__row-value[data-tone='pos'] { color: var(--jg-pos); }
.jg-peer__row-value[data-tone='err'] { color: var(--jg-err); }
.jg-peer__row-note { font-size: 11px; color: var(--jg-text-2); }

.jg-peer__purchases { display: flex; flex-direction: column; gap: 10px; }
.jg-peer__purchase { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; padding: 10px 12px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); }
.jg-peer__purchase-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.jg-peer__purchase-name { font-size: 13px; font-weight: 500; overflow-wrap: anywhere; }
.jg-peer__purchase-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.jg-peer__purchase-price { font-size: 13px; font-weight: 600; font-family: var(--jg-mono); white-space: nowrap; }

.jg-peer__note-view, .jg-peer__note-form { display: flex; flex-direction: column; gap: 8px; }
.jg-peer__note-text { margin: 0; font-size: 13px; white-space: pre-wrap; overflow-wrap: anywhere; }
.jg-peer__note-text[data-empty='true'] { color: var(--jg-muted); }
.jg-peer__note-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.jg-peer__note-meta { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.jg-peer__block { display: flex; flex-direction: column; gap: 8px; padding: 18px; }

/* Выдвижная панель узкого окна: обе вкладки занимают её целиком, ширина 360px там не нужна. */
@media (max-width: 1400px) {
  .jg-peer, .jg-sale { width: 100%; border-left: 0; }
  .jg-chat__panel > .jg-tabs { border-left: 0; padding-right: 48px; }
}

/* ══ этап 3: панель «Offer и счёт» (холст Dialogs L163–222) ══════════════════════════════ */
.jg-sale { width: 360px; flex: none; display: flex; flex-direction: column; min-height: 0; overflow-y: auto; background: var(--jg-surface); border-left: 1px solid var(--jg-border); }
.jg-sale__section { display: flex; flex-direction: column; gap: 12px; padding: 18px; border-bottom: 1px solid var(--jg-border); }
.jg-sale__section--grow { flex: 1; min-height: 0; border-bottom: 0; }
.jg-sale__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.jg-sale__offers, .jg-sale__invoices { display: flex; flex-direction: column; gap: 10px; }
.jg-sale__send { font-size: 13px; }
/* JET-1099: выбор канала продажи. Колонкой, а не в строку: подписи говорят про путь к
   покупателю и длиннее одного слова, а панель на 1280 и уже — выдвижная и узкая, там два
   варианта в строку переносятся посередине фразы. */
.jg-sale__channel { display: flex; flex-direction: column; gap: 8px; font-size: 13px; }
/* JET-1126: текст приглашения, которым уйдёт ссылка. Поле ниже обычного: это одна-две
   фразы, а не письмо, и высокое поле в узкой панели съедало бы список счетов. Подпись и
   кнопка «переписать» — одной строкой под полем, подпись переносится, кнопка не сжимается. */
.jg-sale__invite-box { display: flex; flex-direction: column; gap: 8px; }
.jg-sale__invite { min-height: 64px; }
.jg-sale__invite-foot { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.jg-sale__invite-note { flex: 1; min-width: 0; }
/* JET-988: выбор валюты цены оффера — тот же ряд «одно из двух», что и канал продажи. */
.jg-currency { display: flex; flex-wrap: wrap; gap: 8px 20px; font-size: 13px; }
/* JET-1105: по ВЕРХУ, а не по центру. У строки с длинной оговоркой («счёт не выставится»)
   текст занимает три строки, и при центрировании кружок выбора уезжал на 31 px ниже
   названия, которое он выбирает, а цена — на 26 px (замер 19.09.2026). Тот же урок, что на
   дашборде: по центру выравнивается колонка коротких строк, а не то, что стоит рядом с
   заголовком. Кружок опущен на полстроки, чтобы встать на строку названия, а не над ней. */
.jg-offercard { display: flex; align-items: flex-start; gap: 12px; padding: 12px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); background: var(--jg-surface); cursor: pointer; }
.jg-offercard:hover { border-color: var(--jg-accent-hover-bd); }
.jg-offercard[data-selected] { border-color: var(--jg-accent); background: var(--jg-accent-soft); }
.jg-offercard[data-off] { cursor: not-allowed; opacity: .75; }
.jg-offercard[data-off]:hover { border-color: var(--jg-border); }
.jg-offercard > input { flex: none; margin-top: 2px; }
.jg-offercard__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
/* Название оффера не обрезается: им оператор и выбирает, а «Приватная галерея · 2 …»
   не отвечает на вопрос, что это за набор (замер: не хватало 24 px). Перенос по словам,
   длинный идентификатор рвётся по месту — так же, как в `.jg-tone` выше. */
.jg-offercard__title { font-size: 13px; font-weight: 500; white-space: normal; overflow-wrap: anywhere; }
.jg-offercard__meta { font-size: 11px; color: var(--jg-muted); }
.jg-offercard__price { flex: none; font-family: var(--jg-mono); font-size: 13px; font-weight: 500; font-variant-numeric: tabular-nums; text-align: right; }
/* ══ JET-983: платные ступени сцены в панели диалога ════════════════════════════════════ */
/* Строка ступени: номер, название набора и сколько в нём файлов. Название — единственное
   резиновое поле, остальное не сжимается: номер ступени и число файлов теряют смысл, когда
   их обрезает многоточием. */
.jg-steplist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.jg-steprow {
  display: flex; align-items: baseline; gap: 9px; min-width: 0;
  padding: 10px 12px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile);
}
.jg-steprow__step { flex: none; font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); white-space: nowrap; }
.jg-steprow__title { flex: 1; min-width: 0; font-size: 13px; font-weight: 500; }
.jg-steprow__n { flex: none; font-size: 11px; color: var(--jg-muted); white-space: nowrap; }
/* Итог последнего хода по продаже — подпись под списком, не состояние: без рамки и тона. */
.jg-sale__lastsale { display: flex; flex-wrap: wrap; gap: 4px; }

.jg-invoice { display: flex; flex-direction: column; gap: 7px; padding: 13px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); }
.jg-invoice__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.jg-invoice__id { font-family: var(--jg-mono); font-size: 12px; font-weight: 500; min-width: 0; }
.jg-invoice__row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.jg-invoice__title { font-size: 12px; color: var(--jg-text-2); min-width: 0; }
.jg-invoice__sum { flex: none; font-family: var(--jg-mono); font-size: 13px; font-weight: 500; font-variant-numeric: tabular-nums; }
.jg-invoice__at { font-family: var(--jg-mono); font-size: 10px; color: var(--jg-muted); }
.jg-invoice__items { list-style: none; margin: 0; padding: 3px 0 0; display: flex; flex-direction: column; gap: 6px; border-top: 1px solid var(--jg-divider); }
.jg-invoice__items > li { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.jg-invoice__item { font-family: var(--jg-mono); font-size: 11px; color: var(--jg-text-2); min-width: 0; overflow-wrap: anywhere; }
.jg-invoice__state { flex: none; font-size: 11px; font-weight: 500; color: var(--jg-text-2); }
.jg-invoice__state[data-tone='pos'] { color: var(--jg-pos); }
.jg-invoice__state[data-tone='wait'] { color: var(--jg-wait); }
.jg-invoice__state[data-tone='warn'] { color: var(--jg-warn); }
.jg-invoice__state[data-tone='err'] { color: var(--jg-err); }

/* ══ JET-1064: компактная раскладка экрана «Диалоги» (макет 2A «Ноутбук 1440×820») ══════
   Порог — не круглое число, а арифметика самого макета: колонки 240 + 124 + 330 + 360 =
   1054, а макет не даёт чату быть уже 430. Значит широкая раскладка держится до 1484, ниже
   идут ширины 2A: 186 + 60 + 284 + чат + 296.
   Порог был 1579, пока рельс и список были шире канвы (200 и 350). Из-за этого на 1560 —
   ширине ОСНОВНОГО кадра макета — продукт уже отдавал компактную раскладку, а на 1512 чат
   получал 296-й вариант панели вместо 360-го.
   До появления предела его у чата не было вовсе: на 1440 он получал 290 px, на 1024 — 266,
   на 768 — 10, и его шапка уезжала за правый край окна без всякой прокрутки, то есть
   насовсем (замер 19.09.2026). */
@media (max-width: 1483px) {
  /* Узкое меню — только на этом экране: остальные страницы CRM задача не трогает. */
  html[data-section='dialogs'] { --jg-side-w: 186px; }

  /* Столбец моделей ужимается до рельса, но не прячется: другого способа переключить
     модель нет. Имя уходит в подсказку, число чатов — в кружок на аватаре. */
  .jg-mcol { width: 60px; padding: 10px 0 12px; align-items: center; }
  .jg-mcol__title { padding: 0 2px 8px; font-size: 9px; text-align: center; }
  .jg-mcol__list { align-items: center; }
  /* Радиус свой: у широкой строки он 10, а плитка рельса — 44×44 с радиусом плитки, и
     рамка выбранной модели обязана совпасть с подложкой `__mark` под ней. */
  .jg-mrow { position: relative; width: 44px; height: 44px; padding: 0; justify-content: center; border-radius: var(--jg-r-tile); }
  .jg-mrow__text { display: none; }
  .jg-mrow__mark { width: 100%; height: 100%; border-radius: var(--jg-r-tile); }
  .jg-mrow__n { display: grid; }

  .jg-dlist { width: 284px; }
  .jg-dlist__head { padding: 12px 14px 10px; gap: 9px; }
  .jg-dlist__title { font-size: 15px; }
  .jg-drow { padding: 10px 14px; gap: 9px; }
  /* Имя модели из строки уходит: его называет рельс слева, а место нужно состоянию и tg-id. */
  .jg-drow__model { display: none; }
  /* Состояние и tg-id — одной строкой: перенос растил каждую строку списка на этаж.
     Ярлыки не сжимаются (их текст всё равно не переносится и вылез бы наружу), поэтому
     уступает место tg-id — он для строки наименее важен и умеет обрываться многоточием.
     Самый полный набор — «Перехвачен» + «оплачено» + «счёт ждёт» — в 284 px влезает только
     с ужатыми ярлыками: на прежних он переполнял колонку на 13 px и заводил в списке
     боковую прокрутку. */
  .jg-drow__tags { flex-wrap: nowrap; min-width: 0; gap: 5px; }
  .jg-drow__tags .jg-badge--xs { flex: none; padding: 2px 6px; font-size: 10px; }
  .jg-drow__id { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* Фильтры не переносятся. Вместо меню «···» из макета — горизонтальная прокрутка полосы:
     все четыре фильтра остаются в одно нажатие и не заводится всплывающее меню со своим
     фокусом, Esc и кликом мимо. Полоса прокрутки скрыта, обрезанный справа фильтр и есть
     признак того, что там ещё есть. */
  /* Только фильтры списка диалогов: тот же класс полосы стоит на экранах медиатеки, сцен и
     реф-кодов, а их задача не трогает. */
  .jg-dlist__head .jg-pills { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .jg-dlist__head .jg-pills::-webkit-scrollbar { display: none; }
  .jg-dlist__head .jg-pill { flex: none; }

  /* Чат — единственная резиновая колонка, и у неё есть дно. Предел стоит на `.jg-chat`,
     а не на самой ленте: ниже 1400 панель счёта выдвижная и выходит из потока, так что
     `.jg-chat` — это и есть чат. Выше 1400 предел не работает (в `.jg-chat` лежит ещё и
     панель), но там он и не нужен: 1401 − 826 = 575, чат и так шире.
     Ниже 960 px на предел уже не хватает окна: тогда строка экрана прокручивается вбок
     сама — это по-прежнему хуже макета, но ни одна кнопка не оказывается за краем окна
     недостижимой, как было до правки. */
  .jg-chat { min-width: 430px; }
  .jg-dialogs-row { overflow-x: auto; }

  .jg-chathead { padding: 9px 16px; gap: 12px; }
  .jg-chathead__actions { gap: 8px; }
  .jg-feed { padding: 16px 18px; gap: 10px; }
  .jg-composer { padding: 10px 16px; gap: 6px; }
  .jg-composer__input { min-height: 56px; }
  .jg-composer .jg-hint { font-size: 11px; line-height: 1.4; }

  .jg-sale { width: 296px; }
  .jg-sale__section { padding: 14px; gap: 10px; }
}

/* узко (1400 и меньше): панель продажи — выдвижная, чат не сжимается до нечитаемого */
@media (max-width: 1400px) {
  .jg-chat__panelbtn { display: inline-flex; }
  .jg-chat__panel { display: none; }
  /* JET-1055. Два следствия одного места, оба нашёл владелец на стенде:
     - `display: flex` вместо `block` растягивает `.jg-sale` на всю высоту выдвижения. Блочный
       поток оставлял её высотой по содержимому, и ниже неё сквозь панель просвечивала лента —
       это и есть «панель становится полупрозрачной»;
     - `background` — подстраховка того же: панель закрашена сама, даже если внутри однажды
       окажется что-то короче её.
     Выдвижение накрывает шапку вместе с кнопкой «Оффер и счёт», поэтому крестик обязателен. */
  .jg-chat[data-panel='open'] .jg-chat__panel {
    display: flex; position: absolute; inset: 0 0 0 auto; z-index: 5;
    background: var(--jg-surface); box-shadow: var(--jg-shadow-drawer);
  }
  .jg-chat[data-panel='open'] .jg-chat__panelclose { display: inline-flex; }
  /* место под крестик: без него он наползал на «от лица «Модель»». Отступ только у строки
     заголовка — у секции целиком он сузил бы заодно и кнопку «Выставить счёт». */
  .jg-chat[data-panel='open'] .jg-sale__sell .jg-sale__head { padding-right: 40px; }
  .jg-chat { position: relative; }
}
.jg-drow__last { font-size: 12px; color: var(--jg-text-2); }
.jg-drow__last--none { color: var(--jg-muted); font-style: italic; }
.jg-invoice__line { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
/* JET-1105: «как продаётся» и «ушло ли» — одна строка, пока обе фразы в неё помещаются.
   В компактной раскладке панель 296 px, и две русские фразы туда не влезают: название
   канала обрезалось многоточием на КАЖДОЙ ширине до 1579 и превращалось в «Закрытый
   альбом …» — ровно то, что владелец увидел на стенде. Обрезать название канала нельзя:
   оно и есть ответ на вопрос «чем это продаётся». Поэтому строка переносится, и состояние
   отправки уходит во вторую строку целиком, оставаясь прижатым вправо. */
.jg-invoice__channel { flex-wrap: wrap; }
.jg-invoice__channel > .jg-invoice__title { flex: 1 1 auto; white-space: normal; }
.jg-invoice__channel > .jg-invoice__state { margin-left: auto; }
/* JET-988: ссылка покупателя на карточке счёта. Переносится и рвётся по любому символу:
   deep-link длиннее панели в 360px, а многоточие вместо хвоста превратило бы её в мусор при
   копировании глазами. */
.jg-invoice__link { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.jg-invoice__url { flex: 1 1 100%; font-size: 12px; line-height: 1.4; overflow-wrap: anywhere; }
/* JET-988: строка способов оплаты у долларового счёта — переносится, панель бывает 360px. */
.jg-invoice__methods { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.jg-invoice__methods > .jg-invoice__state { flex: 0 1 auto; }
/* JET-1099: действие «Отправить набор» на карточке альбомного счёта. Отделено линией: это не
   ещё одна строка состояния, а единственное место в карточке, где что-то произойдёт. */
.jg-invoice__act { display: flex; flex-direction: column; gap: 6px; padding-top: 4px; border-top: 1px solid var(--jg-divider); }

/* ══ этап 4: лента привлечений (холст Ref codes, кадр 02) ═══════════════════════════════ */
.jg-evlist { display: flex; flex-direction: column; }
.jg-ev { display: flex; align-items: flex-start; gap: 13px; padding: 12px 20px; border-bottom: 1px solid var(--jg-divider); }
.jg-ev:last-child { border-bottom: 0; }
.jg-ev__dot { flex: none; width: 8px; height: 8px; margin-top: 6px; border-radius: 50%; background: var(--jg-neu); }
.jg-ev__dot[data-tone='pos'] { background: var(--jg-pos); }
.jg-ev__dot[data-tone='warn'] { background: var(--jg-warn); }
.jg-ev__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.jg-ev__line { display: flex; align-items: center; gap: 9px; min-width: 0; font-size: 13px; font-weight: 500; }
.jg-ev__meta { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; font-size: 11px; color: var(--jg-muted); min-width: 0; }
.jg-ev__side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.jg-ev__raw { font-family: var(--jg-mono); font-size: 10px; color: var(--jg-muted); }
.jg-ev__at { font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); white-space: nowrap; }
/* действия строки реф-кода не переносятся: иначе строка раздувается в столбик кнопок */
.jg-table--rows .jg-rowactions { flex-wrap: nowrap; justify-content: flex-end; }

/* ══ этап 5: Ops — разбор сообщений, аккаунты, сигналы, разбор покупки ═══════════════════ */
.jg-oplist { display: flex; flex-direction: column; }
.jg-opitem { display: flex; flex-direction: column; gap: 7px; padding: 14px 20px; border-bottom: 1px solid var(--jg-divider); }
.jg-opitem:last-child { border-bottom: 0; }
.jg-opitem__head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.jg-opitem__peer { font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); }
.jg-opitem__at { margin-left: auto; font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); white-space: nowrap; }
.jg-opitem__body { font-size: 13px; line-height: 1.5; overflow-wrap: anywhere; white-space: pre-wrap; }
.jg-opitem__why { font-size: 11px; line-height: 1.45; color: var(--jg-muted); overflow-wrap: anywhere; }
.jg-opitem__why[data-tone='err'] { color: var(--jg-err); }
.jg-opitem__why[data-tone='warn'] { color: var(--jg-warn); }
.jg-opitem__meta { font-size: 11px; color: var(--jg-muted); overflow-wrap: anywhere; }
.jg-opitem__actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.jg-opitem--row { gap: 5px; }
.jg-accounts, .jg-alerts { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.jg-account { display: flex; flex-direction: column; gap: 11px; padding: 18px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); background: var(--jg-surface); }
.jg-account__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.jg-account__alert { font-size: 11px; line-height: 1.45; color: var(--jg-muted); overflow-wrap: anywhere; }
.jg-account__alert[data-tone='err'] { color: var(--jg-err); }
/* JET-979: отделяем от карточки-факта выше (свежесть/связь) — разные вопросы, не один список */
.jg-account__warmup { display: flex; flex-direction: column; gap: 10px; padding-top: 11px; border-top: 1px solid var(--jg-divider); }
.jg-account__alert[data-tone='warn'] { color: var(--jg-warn); }
.jg-alert { display: flex; flex-direction: column; gap: 10px; padding: 18px 20px; border: 1px solid var(--jg-border); border-left: 3px solid var(--jg-neu); border-radius: var(--jg-r-panel); background: var(--jg-surface); }
.jg-alert[data-tone='err'] { border-left-color: var(--jg-err); }
.jg-alert[data-tone='warn'] { border-left-color: var(--jg-warn); }
.jg-alert__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.jg-alert__title { font-size: 15px; font-weight: 600; overflow-wrap: anywhere; }
.jg-alert__meta { font-size: 13px; line-height: 1.5; color: var(--jg-text-2); overflow-wrap: anywhere; }
.jg-alert__foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.jg-alert__since { font-size: 12px; color: var(--jg-muted); }
.jg-alert__raw { margin-right: auto; font-family: var(--jg-mono); font-size: 10px; color: var(--jg-muted); }
.jg-subhead[data-tone='pos'] { color: var(--jg-pos); }
.jg-subhead[data-tone='warn'] { color: var(--jg-warn); }
.jg-subhead[data-tone='err'] { color: var(--jg-err); }
.jg-kvlist dd[data-tone='neu'] { color: var(--jg-text-2); }
/* шапка вкладки Ops: поиск и «Обновить» в один ряд, а не в столбик */
.jg-tabview .jg-section__head .jg-actions { flex-wrap: nowrap; }
.jg-tabview .jg-section__head .jg-search { width: 260px; }

/* ══ JET-990: оформление Telegram-аккаунта — форма, строка списка, карточка ══ */
/* Ряд кнопок внизу формы/диалога. Класс уже использовал telegram-login-wizard.js, но правил
   для него не было вовсе — кнопки просто ложились в поток. Одно определение на оба места. */
.jg-dialog__actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-wrap: wrap; padding-top: 4px; }
.jg-dialog__actions--split { justify-content: space-between; }
/* Плитки выбора аватара — мельче обычной медиатеки: тут выбирают одно фото, а не разбирают её. */
.jg-mgrid--s { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; }
/* мелкая сетка выбора аватара — квадрат, а не 16/9 медиатеки */
.jg-mgrid--s .jg-tile__pv { aspect-ratio: auto; height: 104px; }

/* Шапка строки аккаунта: аватар + имя/идентификатор + значок состояния (макет «Аккаунты
   Telegram · после сохранения»). Идентификатор аккаунта остаётся видимым отдельной строкой —
   именно он, а не подпись профиля, определяет, о каком аккаунте речь. */
.jg-account__id { display: flex; align-items: center; gap: 11px; min-width: 0; }
.jg-account__names { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.jg-account__name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jg-account__handle { font-family: var(--jg-mono); font-size: 11px; color: var(--jg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jg-account__foot { display: flex; gap: 8px; margin-top: auto; padding-top: 2px; }
.jg-account__foot > * { flex: 1 1 0; min-width: 0; }

/* Карточка аккаунта: метрики и лента событий. */
.jg-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
.jg-metric { display: flex; flex-direction: column; gap: 4px; padding: 14px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); background: var(--jg-surface); }
.jg-metric__label { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--jg-muted); }
.jg-metric__value { font-family: var(--jg-mono); font-size: 19px; font-weight: 500; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.jg-metric__value[data-tone='pos'] { color: var(--jg-pos); }
.jg-metric__value[data-tone='warn'] { color: var(--jg-warn); }
.jg-metric__value[data-tone='err'] { color: var(--jg-err); }
.jg-metric__note { font-size: 11px; line-height: 1.4; color: var(--jg-muted); }
.jg-events { display: flex; flex-direction: column; }
.jg-event { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--jg-divider); }
.jg-event:last-child { border-bottom: 0; }
.jg-event__text { font-size: 12px; line-height: 1.45; overflow-wrap: anywhere; }
.jg-event__result { font-size: 12px; color: var(--jg-text-2); overflow-wrap: anywhere; }
.jg-event__result[data-tone='pos'] { color: var(--jg-pos); }
.jg-event__result[data-tone='err'] { color: var(--jg-err); }
.jg-event__result[data-tone='warn'] { color: var(--jg-warn); }
.jg-event__time { font-family: var(--jg-mono); font-size: 12px; color: var(--jg-muted); text-align: right; white-space: nowrap; }
/* Список действий справа: у каждого своя подпись, что именно произойдёт. */
.jg-actionrow { display: flex; align-items: center; justify-content: space-between; gap: 12px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); padding: 11px 12px; }
.jg-actionrow__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.jg-actionrow__title { font-size: 12px; font-weight: 500; }
.jg-actionrow__note { font-size: 11px; line-height: 1.4; color: var(--jg-muted); }
@media (max-width: 760px) { .jg-event { grid-template-columns: minmax(0, 1fr); gap: 4px; } .jg-event__time { text-align: left; } }
/* JET-991: перечень препятствий к включению обработки — список причин, а не абзац. */
.jg-list { margin: 4px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.jg-list li { font-size: 12px; line-height: 1.45; }
/* ── JET-982: лесенка сцены — выбранная ступень и увеличение фото ─────────────────────── */
/* Строка лесенки кликабельна целиком: сама ступень — кнопка, панель под списком показывает
   именно её. `aria-current` — то же состояние, что читает скринридер, отдельного класса нет. */
.jg-ladder { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.jg-ladder__btn {
  display: flex; align-items: center; gap: 11px; width: 100%; padding: 8px; text-align: left;
  border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); background: var(--jg-surface);
  color: inherit; font: inherit; cursor: pointer; min-width: 0;
}
.jg-ladder__btn:hover { border-color: var(--jg-border-strong); }
.jg-ladder__btn[aria-current='true'] { border-color: var(--jg-accent); box-shadow: 0 0 0 3px var(--jg-focus); }
/* JET-1105: `--jg-focus-outline` — ЦВЕТ (tokens.css), и сокращённая запись `outline: <цвет>`
   сбрасывает стиль обводки в `none`: кольца фокуса не было вовсе. Замер в браузере:
   вычисленный `outline-style` — `none`. Писать полностью, как в общем правиле для кнопок. */
.jg-ladder__btn:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 2px; }
.jg-ladder__n { flex: none; width: 22px; font-size: 12px; font-weight: 600; color: var(--jg-text-2); }
/* В редакторе рамка уже есть у самой ступени (.jg-stage) — вложенная вторая съедала ширину,
   и название сжималось до «Первый пла…». Внутри ступени кнопка выбора без своей рамки. */
.jg-stage .jg-ladder__btn { border: 0; background: none; padding: 0; box-shadow: none; }
.jg-stage:has(.jg-ladder__btn[aria-current='true']) { border-color: var(--jg-accent); box-shadow: 0 0 0 3px var(--jg-focus); }
/* Панель ступени плотнее обычной панели: внутри короткие блоки, а не разделы страницы */
.jg-steppanel { padding: 16px; gap: 12px; }
.jg-steppanel .jg-stack { gap: 12px; }
.jg-steppanel .jg-maside__pv { height: 170px; }
/* Оба окна сцены несут лесенку И панель выбранной ступени — 400px им мало (проверено в браузере) */
.jg-dialog--drawer:has(#jg-scene-form), .jg-dialog--drawer:has(.jg-ladder) { width: min(560px, 100vw); }
/* Увеличенное фото: вписываем в окно целиком — оригинал бывает и вертикальным, и очень широким */
.jg-dialog:has(.jg-zoom) { width: auto; max-width: calc(100vw - 32px); }
/* align-self: center — иначе flex-колонка окна растянула бы фото по ширине и исказила его
   (поймано браузерной проверкой: квадратный файл рисовался прямоугольником 900×620) */
.jg-zoom {
  display: block; align-self: center; width: auto; height: auto; object-fit: contain;
  max-width: min(78vw, 900px); max-height: 70vh; border-radius: var(--jg-r-tile); background: var(--jg-fill);
}

/* ── Окно «Аккаунт Telegram»: вёрстка формы оформления (правка по находке владельца) ─────── */
/* В .jg-cols широкая колонка ПЕРВАЯ — так устроена персона, где слева содержимое, а справа
   узкий aside. В этой форме порядок обратный: первым идёт фото. Из-за этого аватару
   доставалось 262px, а полям 175px, описание переносилось по слогам, и окно вытягивалось
   вниз за край экрана. Здесь фото — колонка по содержимому, поля забирают остальное. */
.jg-cols--aside { grid-template-columns: 104px minmax(0, 1fr); }
/* Фото профиля — квадратная плитка 104×104 со скруглением, как в макете «TG accounts —
   форма и карточка» (18.09.2026), а не круглый значок 52px: рядом с ним оставалась пустая
   полоса, из-за которой колонка и читалась как «пустая». Колонка ровно по плитке — тогда
   пустой полосы не остаётся и в этом варианте. Размер задан только здесь, общий
   .jg-avatar--l не тронут: он используется и в карточке аккаунта. */
.jg-form--account .jg-avatar--l {
  width: 104px; height: 104px; border-radius: var(--jg-r-tile); font-size: 30px;
}
/* Двум колонкам со счётчиком описания 520px мало — то же решение, что у окна с .jg-pickgrid. */
.jg-dialog:has(.jg-form--account) { width: min(640px, calc(100vw - 32px)); }
/* Узкий экран — колонки в столбик. Контейнерный запрос сюда не достаёт: внутри <dialog> нет
   элемента с container-type, поэтому правило по ширине окна просмотра. */
@media (max-width: 680px) { .jg-cols--aside { grid-template-columns: minmax(0, 1fr); } }
/* Форма длиннее экрана ноутбука даже после сжатия колонок, а решение в окне одно, поэтому
   ряд кнопок держится у нижнего края области прокрутки, а не уезжает под край экрана. */
.jg-form--account .jg-dialog__actions {
  position: sticky; bottom: 0; z-index: 1;
  margin: 0 -24px; padding: 12px 24px;
  background: var(--jg-surface); border-top: 1px solid var(--jg-divider);
}
/* По макету сноска и кнопки — одна полоса: подпись слева, кнопки справа. Наши кнопки
   подписаны длиннее макетных, поэтому подписи разрешено сжиматься и переноситься внутри
   своей доли (`min-width: 0`), а не выдавливать кнопки на вторую строку. */
.jg-form--account .jg-dialog__actions .jg-dialog__foot-note { flex: 1 1 120px; min-width: 0; }

/* ── Сцены: список карточками и полноэкранный редактор ─────────────────────────────────
   Макеты «Scenes - redaktor stupeney» (экраны 01–02) и «Scenes - 1280 i temnaya tema».
   Раньше список был таблицей, а редактор — узким drawer на 560 px: в макете список это
   сетка карточек с полосой фотографий, а редактор занимает всю вкладку и делится на три
   колонки (поля сцены · ступени · выбранная ступень). На 1280 левая колонка по макету
   уходит наверх одной строкой, правая остаётся. Все цвета — токены обеих тем. */
.jg-scenetools { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.jg-scenetools__search { flex: 0 1 240px; min-width: 180px; }
.jg-input--s { height: auto; padding: 9px 12px; font-size: 13px; }

.jg-scenegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(288px, 1fr)); gap: 16px; }
.jg-scenegrid--empty { display: block; }
.jg-scenecard {
  display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); background: var(--jg-surface);
}
.jg-scenecard:hover { border-color: var(--jg-accent-hover-bd); }
/* Полоса — только бесплатные ступени: в списке видно ровно то, что игрок видит до оплаты */
.jg-scenecard__shots { display: flex; gap: 2px; height: 132px; flex: none; background: var(--jg-fill); }
.jg-scenecard__shot { width: auto; flex: 1; min-width: 0; height: 100%; aspect-ratio: auto; border-radius: 0; }
.jg-scenecard__shots--none { display: grid; place-content: center; font-size: 12px; color: var(--jg-muted); }
.jg-scenecard__body { display: flex; flex-direction: column; gap: 9px; padding: 14px 16px; flex: 1; min-width: 0; }
.jg-scenecard__title { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; }
.jg-scenecard__title > .jg-btn { min-width: 0; justify-content: flex-start; font-size: 14px; font-weight: 600; color: var(--jg-text); }
.jg-scenecard__title > .jg-btn:hover { color: var(--jg-link); }
.jg-scenecard__context {
  margin: 0; font-size: 12px; line-height: 1.5; color: var(--jg-muted);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.jg-scenecard__meta { display: flex; gap: 14px; font-size: 12px; color: var(--jg-text-2); }
.jg-scenecard__foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding-top: 4px; }
.jg-scenecard__fix {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 10px; border-radius: 9px; font-size: 12px; line-height: 1.4;
  background: var(--jg-warn-bg); color: var(--jg-warn-text);
}
.jg-scenecard__fix[data-tone='err'] { background: var(--jg-err-bg); color: var(--jg-err-text); }
.jg-scenecard__fix > .jg-btn { flex: none; color: inherit; }
.jg-scenecard__acts { display: flex; justify-content: flex-end; gap: 6px; }

/* ── редактор сцены: экран во всю вкладку ───────────────────────────────────────────── */
/* Редактор — рамка на всю вкладку: шапка и боковые колонки на поверхности, центральная
   колонка со ступенями на фоне страницы (так в макете видно, что ступени — отдельный список). */
.jg-sceneed {
  display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); background: var(--jg-surface);
}
/* Шапка редактора прилипает к верху прокрутки карточки. Раньше она уезжала вместе с телом
   (замер: при прокрутке на 500 px её y = −332), и единственной видимой главной кнопкой
   оставалась «Опубликовать изменения» из шапки МОДЕЛИ — чужое действие. Владелец нажал её
   вместо «Сохранить сцену» и решил, что сцена сохранена (20.09.2026).
   `overflow: hidden` у `.jg-sceneed` пришлось снять — он делал родителя своим контейнером
   прокрутки, и `sticky` не срабатывал вовсе; скруглённый верх теперь держит сама шапка. */
.jg-sceneed__head {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 18px 22px;
  border-bottom: 1px solid var(--jg-border); background: var(--jg-surface);
  border-radius: var(--jg-r-panel) var(--jg-r-panel) 0 0;
}
.jg-sceneed__id { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.jg-sceneed__titlerow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.jg-sceneed__head .jg-addlink { align-self: center; flex: none; }
/* Макет v2 «Scenes - redaktor v2»: редактор — ОДНА колонка, карточка «Сцена» сверху,
   «Лесенка медиа» под ней. Прежние две колонки и сжатая полоса настроек на 1280 рисовались
   по ПРОШЛОМУ макету (JET-1049) — в v2 их нет ни на одной ширине.

   Обе — ОДИНАКОВЫЕ белые панели на сером теле карточки (в макете у них один и тот же
   `background:#FFFFFF;border:1px solid #E4E4EA;border-radius:14px;padding:22px`). Раньше
   лесенка панелью не была: она лежала прямо на сером фоне, и сравнение с макетом это и
   показало первым. Тело: `padding:20px`, `gap:20px` между панелями — как в макете. */
.jg-sceneed__cols {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px;
  align-items: start; min-width: 0; padding: 20px; background: var(--jg-bg);
}
.jg-sceneed__side, .jg-sceneed__main {
  display: flex; flex-direction: column; min-width: 0; padding: 22px;
  border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); background: var(--jg-surface);
}
/* Разные внутренние промежутки — так в макете: у «Сцены» 18px (поля с подписями дышат),
   у «Лесенки» 16px. */
.jg-sceneed__side { gap: 18px; }
/* Заголовок секции и выключатель — одной строкой над разделителем (макет v2). */
.jg-sceneed__sidehead {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding-bottom: 14px; border-bottom: 1px solid var(--jg-divider);
}
.jg-sceneed__off { flex: none; }
/* Объяснение выключателя — сразу под его строкой, впритык: оно относится к чекбоксу выше,
   а не к полю ниже. Отрицательный отступ съедает `gap` карточки. */
.jg-sceneed__offhint { margin-top: -8px; }
.jg-sceneed__main { gap: 16px; }
/* Шапка лесенки отбита чертой — ровно как шапка «Сцены» выше и как в макете. Раньше черты не
   было, потому что панели не было тоже: заголовок висел прямо на сером фоне. */
.jg-sceneed__mainhead {
  display: flex; align-items: center; gap: 16px;
  padding-bottom: 14px; border-bottom: 1px solid var(--jg-divider);
}
.jg-sceneed__mainlabel { flex: 1; min-width: 0; }
.jg-sceneed__mainhead > .jg-rowactions { flex: none; flex-wrap: nowrap; }
.jg-sceneed__mainhead > .jg-hint { flex: none; }
/* Заголовок секции — тот же капс, но трекинг шире (система §02 «label», .06em): отличает
   заголовок блока от подписи поля внутри него. */
.jg-label--cap { letter-spacing: .06em; }
/* Сводка «что мешает отправке» живёт НАД лесенкой (она про готовность ступеней), поэтому
   отбивается сверху чертой от шапки лесенки, а не прижимается к низу колонки: колонки нет. */
.jg-sceneed__main > .jg-stack--s { padding-top: 16px; border-top: 1px solid var(--jg-divider); }
.jg-stepcard__fixact { margin-left: 110px; }
.jg-stack--s { gap: 10px; }
/* Без `space-between`: строку без кнопки текст пишут голым текстовым узлом (не в span), и
   на широкой карточке v2 он улетал к правому краю, оставляя точку одну слева. Кнопку к
   правому краю и так прижимает `flex: 1` у span'а ниже. */
.jg-blocker { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; line-height: 1.45; color: var(--jg-text-2); }
.jg-blocker::before { content: ''; flex: none; width: 7px; height: 7px; margin-top: 6px; border-radius: 50%; background: var(--jg-muted); }
.jg-blocker[data-tone='err']::before { background: var(--jg-err); }
.jg-blocker[data-tone='warn']::before { background: var(--jg-warn); }
.jg-blocker[data-tone='pos']::before { background: var(--jg-pos); }
.jg-blocker > span { flex: 1; min-width: 0; }
.jg-blocker > .jg-btn { flex: none; }

/* ступень — карточка с крупной обложкой (в drawer была строка на 48 px) */
/* 16px между ступенями — столько же, сколько между блоками внутри панели в макете. */
.jg-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 16px; }
.jg-stepcard {
  /* Колонка, а не строка: сама строка лесенки лежит в `__row`, а под ней раскрывается
     описание. Пока карточка была flex-строкой, раскрытое описание вставало ВТОРОЙ
     колонкой и вылезало за карточку на 1280 (поймано браузерной приёмкой). */
  /* Макет: `padding:14px` со всех сторон и радиус 12 — строка внутри панели, а не панель
     сама по себе (`--jg-r-tile`, он для этого и заведён). Было 14/16 и радиус панели. */
  display: flex; flex-direction: column; gap: 12px; padding: 14px; min-width: 0;
  border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile); background: var(--jg-surface);
}
.jg-stepcard[data-selected='true'] { border-color: var(--jg-accent); box-shadow: 0 0 0 3px var(--jg-focus); }
/* Строка лесенки — ОДНА линия (макет v2): отметка, номер, ручка и стрелки стоят в ряд.
   Столбиком они тянули карточку вниз и ломали главную мысль макета «строка — одно медиа». */
/* По верху, как обложка и настройки рядом. В макете ручка и кнопки центрируются, но там
   строка всегда короткая (108px); у нас платная ступень с набором и причиной неготовности
   вырастает втрое, и центрированный блок повисал в пустоте посреди строки, оторвавшись от
   кадра. Одна верхняя кромка у всех частей строки читается как одна строка. */
.jg-stepcard__ord { flex: none; display: flex; flex-direction: row; align-items: center; gap: 6px; }
.jg-stepcard__num { font-size: 11px; color: var(--jg-muted); }
.jg-stepcard__pickbox { flex: 1; min-width: 0; }
.jg-stepcard__pick {
  display: flex; gap: 14px; width: 100%; min-width: 0; padding: 0; text-align: left;
  border: 0; background: none; color: inherit; font: inherit; cursor: pointer;
}
.jg-stepcard__pick:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 3px; border-radius: var(--jg-r-tile); }
.jg-stepcard__pick:disabled { cursor: default; }
.jg-stepcard__cover { flex: none; width: 72px; height: 72px; aspect-ratio: auto; border-radius: 10px; object-fit: cover; }
.jg-stepcard__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.jg-stepcard__head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; min-width: 0; }
.jg-stepcard__name { font-size: 14px; font-weight: 600; min-width: 0; }
.jg-stepcard__price { font-size: 13px; font-weight: 500; color: var(--jg-text-2); }
.jg-stepcard__what { font-size: 12px; line-height: 1.5; color: var(--jg-text-2); }
.jg-stepcard__fix {
  display: block; padding: 8px 10px; border-radius: 9px; font-size: 12px; line-height: 1.4;
  background: var(--jg-warn-bg); color: var(--jg-warn-text);
}
.jg-stepcard__fix[data-tone='err'] { background: var(--jg-err-bg); color: var(--jg-err-text); }
.jg-stepcard__tools { flex: none; display: flex; flex-direction: column; gap: 4px; }
.jg-stepadd {
  display: flex; flex-direction: column; gap: 9px; padding: 14px 16px;
  border: 1px dashed var(--jg-border-strong); border-radius: var(--jg-r-panel);
}

/* Контекст сцены — обычное поле с подписью на ЛЮБОЙ ширине (макет v2). Сворачиваемая
   «Контекст сцены ▾» и сжатая полоса настроек жили по прошлому макету («Scenes - 1280»,
   JET-1049) и сняты вместе с ним: именно они прятали подписи полей, из-за чего поле
   названия выглядело безымянным (отчёт владельца 20.09.2026, стенд). */
.jg-ctx { min-width: 0; }

@media (max-width: 1023px) {
  /* Поля у карточки «Сцена» и у лесенки обязаны стоять по одной вертикали: у лесенки
     боковой отступ здесь снимается, значит и внешний отступ карточки тоже. */
  .jg-sceneed__main { padding-inline: 0; }
  .jg-sceneed__side { margin-inline: 0; }
}

/* ══ JET-1049: окна выбора источника ступени, кадры набора, перенос ступеней ═══════════════
   Макет «Scenes - redaktor stupeney», экраны 02–04 и 06. Цвета — только токены: у «до оплаты»
   семейство wait (то же, чем помечена платная ступень), у «после оплаты» — акцентное; оба
   переопределены в тёмной теме, поэтому проверяются в обеих. */

/* обложка ступени с меткой состояния файла */
.jg-stepcard__coverbox { position: relative; flex: none; display: block; }
.jg-stepcard__coverbadge {
  position: absolute; left: 6px; bottom: 6px; padding: 2px 7px; border-radius: 7px;
  font-size: 10px; font-weight: 600; background: rgba(21, 20, 26, .78); color: #FFFFFF;
}
.jg-stepcard__coverbadge[data-tone='err'] { background: var(--jg-err); }

/* ручка переноса и подсветка места, куда ступень встанет */
/* Ручка — только глиф, без плашки: в макете это серый значок 14px в цвете #B6B3BE, а не
   кнопка с заливкой. Заливка читалась как ещё один элемент управления в и без того плотной
   строке. Ширину держим явной, чтобы описание ниже выравнивалось по одной и той же сетке. */
.jg-stepcard__grip {
  display: grid; place-content: center; width: 14px; flex: none; align-self: center;
  color: var(--jg-grip); font-size: 14px; line-height: 1; cursor: grab;
}
.jg-stepcard__grip:active { cursor: grabbing; }
.jg-stepcard[data-dragging='true'] { opacity: .5; }
.jg-stepcard[data-drop='before'] { box-shadow: 0 -3px 0 0 var(--jg-accent); }
.jg-stepcard[data-drop='after'] { box-shadow: 0 3px 0 0 var(--jg-accent); }

/* кадры платного набора в карточке ступени: всегда с подписью «после оплаты» */
.jg-stepcard__set {
  display: flex; flex-direction: column; gap: 6px; padding: 9px 10px;
  border: 1px solid var(--jg-accent-border); border-radius: 10px; background: var(--jg-accent-soft);
}
.jg-stepcard__setlabel { font-size: 11px; font-weight: 600; color: var(--jg-accent-text); }
.jg-stepcard__setshots { display: flex; gap: 6px; flex-wrap: wrap; }
.jg-stepcard__setshot { width: 52px; height: 52px; flex: none; aspect-ratio: auto; border-radius: var(--jg-r-badge); }

/* ── окно выбора файла ─────────────────────────────────────────────────────────────── */
.jg-dialog:has(.jg-mediapick__grid), .jg-dialog:has(.jg-opicklist) { width: min(700px, calc(100vw - 32px)); }
.jg-mediapick__tools { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.jg-mediapick__search { flex: 0 1 240px; min-width: 160px; }
.jg-mediapick__grid { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); max-height: 46vh; }
.jg-pickgrid--empty { display: block; }
.jg-mtile {
  display: flex; flex-direction: column; gap: 6px; padding: 0; min-width: 0; text-align: left;
  border: 0; background: none; color: inherit; font: inherit; cursor: pointer;
}
.jg-mtile__pv {
  position: relative; display: block; border-radius: var(--jg-r-tile); overflow: hidden;
  border: 2px solid transparent; background: var(--jg-fill);
}
.jg-mtile__img { width: 100%; height: auto; aspect-ratio: 3 / 4; border-radius: 0; display: block; }
.jg-mtile[aria-pressed='true'] .jg-mtile__pv { border-color: var(--jg-accent); box-shadow: 0 0 0 3px var(--jg-focus); }
.jg-mtile:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 3px; border-radius: var(--jg-r-tile); }
.jg-mtile:disabled { cursor: not-allowed; }
.jg-mtile:disabled .jg-mtile__img { opacity: .45; }
.jg-mtile__note { font-size: 11px; line-height: 1.35; color: var(--jg-text-2); }
.jg-mtile[data-tone='warn'] .jg-mtile__note { color: var(--jg-warn-text); }
.jg-mtile[data-tone='err'] .jg-mtile__note { color: var(--jg-err-text); }
.jg-mtile__badge {
  position: absolute; left: 5px; bottom: 5px; padding: 2px 6px; border-radius: 6px;
  font-size: 10px; font-weight: 600; background: rgba(21, 20, 26, .78); color: #FFFFFF;
}
.jg-mtile__badge--on { left: auto; right: 5px; background: var(--jg-accent); color: var(--jg-on-accent); }

/* ── окно выбора оффера ───────────────────────────────────────────────────────────── */
.jg-opicklist { display: flex; flex-direction: column; gap: 8px; max-height: 52vh; overflow-y: auto; padding: 3px; }
.jg-opicklist__row {
  display: flex; gap: 13px; width: 100%; min-width: 0; padding: 12px; text-align: left;
  border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); background: var(--jg-surface);
  color: inherit; font: inherit; cursor: pointer;
}
.jg-opicklist__row:hover { border-color: var(--jg-accent-hover-bd); }
.jg-opicklist__row[aria-pressed='true'] { border-color: var(--jg-accent); box-shadow: 0 0 0 3px var(--jg-focus); }
.jg-opicklist__row:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 2px; }
.jg-opicklist__shots { display: flex; gap: 4px; flex: none; }
.jg-opicklist__shot { width: 46px; height: 58px; flex: none; aspect-ratio: auto; border-radius: var(--jg-r-badge); }
.jg-opicklist__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.jg-opicklist__head { display: flex; align-items: center; gap: 10px; min-width: 0; }
.jg-opicklist__title { flex: 1; min-width: 0; font-size: 13px; font-weight: 600; }
.jg-opicklist__meta { display: flex; gap: 18px; flex-wrap: wrap; font-size: 12px; color: var(--jg-text-2); }
.jg-opicklist__note { font-size: 12px; line-height: 1.45; color: var(--jg-muted); }

/* ── «до оплаты» / «после оплаты» в панели ступени ────────────────────────────────── */
.jg-setbox { display: flex; flex-direction: column; gap: 8px; padding: 11px; border-radius: 11px; border: 1px solid; }
.jg-setbox[data-when='before'] { border-color: var(--jg-wait-bd); background: var(--jg-wait-soft); }
.jg-setbox[data-when='after'] { border-color: var(--jg-accent-border); background: var(--jg-accent-soft); }
.jg-setbox__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.jg-setbox__title { font-size: 12px; font-weight: 600; }
.jg-setbox[data-when='before'] .jg-setbox__title { color: var(--jg-wait-text); }
.jg-setbox[data-when='after'] .jg-setbox__title { color: var(--jg-accent-text); }
.jg-setgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.jg-setgrid__cell { position: relative; display: block; border-radius: 9px; overflow: hidden; background: var(--jg-fill); }
.jg-setgrid__img { width: 100%; height: auto; aspect-ratio: 3 / 4; border-radius: 0; display: block; }
.jg-setgrid__badge {
  position: absolute; left: 5px; bottom: 5px; padding: 2px 6px; border-radius: 6px;
  font-size: 10px; font-weight: 600; background: rgba(21, 20, 26, .78); color: #FFFFFF;
}

/* ── окно «Это фото продаётся отдельно» ──────────────────────────────────────────── */
.jg-paidfree { display: flex; gap: 16px; min-width: 0; }
.jg-paidfree__pv { width: 88px; height: 110px; flex: none; aspect-ratio: auto; border-radius: 10px; }

/* ── полноэкранный просмотр платного набора ──────────────────────────────────────────
   Тёмное окно в ОБЕИХ темах, как сайдбар: смотреть кадры на светлом фоне неудобно.
   Тёмность сделана переопределением токенов на самом окне — тогда вложенные jg-kv/jg-mlist/
   кнопки темнеют сами, без копии каждого правила цвета. */
.jg-dialog:has(.jg-setview) {
  width: min(1160px, calc(100vw - 32px));
  --jg-surface: var(--jg-ink);
  --jg-surface-2: var(--jg-ink-2);
  --jg-field: var(--jg-ink-2);
  --jg-fill: var(--jg-ink-avatar);
  --jg-border: #3A2F45;
  --jg-border-strong: #4A3A55;
  --jg-divider: #3A2F45;
  --jg-text: var(--jg-ink-strong);
  --jg-text-2: var(--jg-ink-text);
  --jg-muted: var(--jg-ink-muted);
  --jg-label: var(--jg-ink-label);
  --jg-btn2-hover: var(--jg-ink-2);
  --jg-focus-outline: var(--jg-ink-focus);
  background: var(--jg-ink);
  color: var(--jg-ink-text);
}
.jg-setview { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 20px; min-width: 0; }
.jg-setview__main { min-width: 0; display: flex; flex-direction: column; gap: 14px; }
.jg-setview__stage {
  display: flex; align-items: center; justify-content: center; height: min(62vh, 560px);
  background: #121016; border-radius: var(--jg-r-panel); overflow: hidden;
}
.jg-setview__stage > img { max-width: 100%; max-height: 100%; object-fit: contain; }
.jg-setview__strip { display: flex; gap: 8px; flex-wrap: wrap; }
.jg-setview__thumb {
  padding: 0; border: 2px solid transparent; border-radius: 9px; overflow: hidden;
  background: none; cursor: pointer; line-height: 0;
}
.jg-setview__thumb[aria-current='true'] { border-color: var(--jg-accent); }
.jg-setview__thumb:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 2px; }
.jg-setview__thumbimg { width: 74px; height: 92px; flex: none; aspect-ratio: auto; border-radius: 0; }
.jg-setview__side { display: flex; flex-direction: column; gap: 14px; min-width: 0; max-height: 72vh; overflow-y: auto; }

@media (max-width: 1023px) {
  .jg-setview { grid-template-columns: minmax(0, 1fr); }
  .jg-setview__stage { height: min(44vh, 360px); }
  .jg-setgrid { grid-template-columns: repeat(2, 1fr); }
}

/* --- Дашборд «Воронка и выручка» (JET-670) -------------------------------------------- */
/* Холст не фиксирован: макет нарисован на 1280, но экран должен жить и шире, и уже. */
.jg-dash { gap: 16px; padding-top: 16px; overflow-y: auto; }
/* Секции дашборда не сжимаются. `.jg-dash` — прокручиваемая flex-колонка, и любой её ребёнок
   без этого ужимается до высоты, которую ему оставил экран: так таблица «Модели: счета и
   выручка» превращалась в полоску 2 px и просто пропадала с экрана (замер 20.09.2026:
   .jg-tablebox h=2 при таблице 301). У верхней карточки это уже чинили отдельной строкой —
   правило одно на всех детей, чтобы следующая секция не наступила на те же грабли. */
.jg-dash > * { flex: none; }
.jg-dash__cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.jg-dash > .jg-seg { align-self: flex-start; }
/* Верхний блок (JET-1066, макет «Dashboard B»): одна главная цифра слева, остальные
   показатели — компактными строками справа. Восемь равновеликих карточек, которые были тут
   раньше, давали ряд разной высоты (каждая по своему содержимому) и одинокую восьмую строку
   на части ширин: на 1600 px ряд распадался на 7 + 1.
   Число колонок задано явно и делит число строк нацело — при auto-fit последней строке
   достаётся остаток, и одинокая строка возвращается на первой же непредусмотренной ширине. */
/* Выравнивание по макету (JET-1071): главная цифра стоит по верху блока, а по центру
   выравнивается колонка коротких строк (align-content ниже). Было наоборот — center на
   этом гриде опускал цифру к середине, и она разъезжалась с первой строкой справа. */
.jg-kpi { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px 28px; }
/* Показатели и график — одна карточка с внутренней линией, а не две плашки с зазором:
   в макете это одна `section` с `overflow:hidden`, у верхней половины `border-bottom`.
   Отступы половин взяты оттуда же (24/26/20 сверху, 20/26/24 снизу).
   flex: none обязателен вместе с overflow: hidden. Тело экрана — колонка flex со своей
   прокруткой, и у элемента с ненаблюдаемым переполнением min-height: auto превращается
   в 0: без этой строки карточка сжималась до 2 px (двух своих рамок), а показатели и
   график вываливались поверх следующих блоков. */
.jg-dash__top { overflow: hidden; }
.jg-dash__top > .jg-kpi { padding: 24px 26px 20px; border-bottom: 1px solid var(--jg-divider); }
.jg-dash__chart { padding: 20px 26px 24px; display: flex; flex-direction: column; gap: 16px; }
.jg-kpi__hero { display: flex; flex-direction: column; gap: 8px; min-width: 0; align-items: flex-start; }
/* Подпись главного показателя — капсом с разрядкой, как в макете (JET-1083). Компактные
   строки грида её стиль не наследуют: там подпись обычная, иначе ряд читается как шапка. */
.jg-kpi__hero > .jg-kpi__label { font-weight: 500; letter-spacing: .06em; text-transform: uppercase; }
.jg-kpi__heroline { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
/* В макете число 40px. Фиксированные 40px рвут колонку на 1100–1399 px, где она всего
   ~300 px шириной, поэтому размер резиновый: 40px на широком экране, меньше на узком. */
.jg-kpi__hero-value { font-family: var(--jg-mono); font-size: clamp(28px, 2.4vw, 40px); font-weight: 500; letter-spacing: -.02em; line-height: 1.05; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.jg-kpi__change { font-size: 13px; font-weight: 600; color: var(--jg-muted); }
.jg-kpi__change[data-tone='pos'] { color: var(--jg-pos); }
.jg-kpi__change[data-tone='err'] { color: var(--jg-err); }
/* «Сравнить не с чем» — пометка об отсутствии сравнения, а не показатель: полужирным
   рядом с крупным числом она читалась как ещё одно значение (JET-1071). */
.jg-kpi__change[data-kind='none'] { font-size: 12px; font-weight: 400; color: var(--jg-muted); }
.jg-kpi__context { margin: 0; font-size: 13px; color: var(--jg-text-2); }
/* Перенос строки контекста обязан приходиться на разделитель: иначе колонка рвёт фразу
   посередине («до комиссий и / возвратов»). */
.jg-kpi__context [data-ctx-part] { white-space: nowrap; }

/* Четыре показателя макета: число колонок (2 или 1) делит их нацело, поэтому одинокая
   строка под заполненным рядом невозможна (правило JET-1066).
   Колонка по умолчанию одна — так макет и выглядит на своей ширине: его внутренний грид это
   minmax(210px, 1fr) в половине карточки, и примерно до 1500 px там помещается ровно одна
   колонка, то есть четыре строки во всю ширину половины. Вторая включается, когда на неё
   есть место: нашим подписям («Конверсия счёта в оплату») запаса нужно больше, чем коротким
   подписям макета. */
.jg-kpi__grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px 24px; min-width: 0; align-content: center; }
.jg-kpi__grid--pairs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 1500px) {
  .jg-kpi__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Строка растягивается по ряду (никакого align-items: start), поэтому соседи по ряду
   одной высоты независимо от длины примечания и от кнопки расшифровки. */
.jg-kpi__cell { display: flex; flex-direction: column; gap: 3px; min-width: 0; padding-bottom: 8px; border-bottom: 1px solid var(--jg-divider); }
/* Одна линия «подпись слева — значение справа» по макету. Столбиком строка была вдвое
   выше, и на ширинах до 1399 px блок получал лишний ряд (JET-1071). Значение не сжимается:
   длинная подпись переносится, а число остаётся целым у правого края. */
.jg-kpi__line { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; min-width: 0; }
.jg-kpi__label { font-size: 12px; color: var(--jg-text-2); min-width: 0; overflow-wrap: anywhere; }
.jg-kpi__value { flex: 0 0 auto; font-family: var(--jg-mono); font-size: 16px; font-weight: 500; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.jg-kpi__value[data-tone='pos'] { color: var(--jg-pos); }
.jg-kpi__value[data-tone='warn'] { color: var(--jg-warn); }
.jg-kpi__value[data-tone='err'] { color: var(--jg-err); }
/* Расшифровка занимает место самого числа: отдельная кнопка под строкой делала бы эту
   строку выше соседних — ровно та беда, из-за которой ряд и разъезжался. */
.jg-kpi__value--drill { padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 4px; }
.jg-kpi__value--drill:hover { color: var(--jg-accent); }
.jg-kpi__value--drill:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 3px; border-radius: 4px; }
.jg-kpi__note { font-size: 11px; line-height: 1.4; color: var(--jg-muted); }

@media (max-width: 1099px) {
  .jg-kpi { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 719px) {
  .jg-kpi__grid, .jg-kpi__grid--pairs { grid-template-columns: minmax(0, 1fr); }
}

/* График: цвета — из токенов, иначе тёмная тема получила бы светлую линию из макета. */
.jg-chart { display: flex; flex-direction: column; gap: 10px; }
.jg-chart__svg { width: 100%; height: 240px; display: block; overflow: visible; }
.jg-chart__grid { stroke: var(--jg-divider); stroke-width: 1; }
.jg-chart__bar { fill: var(--jg-accent); }
.jg-chart__line { fill: none; stroke: var(--jg-accent); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.jg-chart__area { fill: var(--jg-accent-tint); }
.jg-chart__dot { fill: var(--jg-surface); stroke: var(--jg-accent); stroke-width: 2.5; }
.jg-chart__axis { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 12px; color: var(--jg-muted); }
.jg-chart__peak { font-weight: 600; color: var(--jg-text-2); text-align: center; }

/* Воронка и топ офферов — одна и та же полоса: два списка, одно правило отрисовки. */
.jg-funnel { display: flex; flex-direction: column; gap: 14px; }
.jg-funnel__step { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.jg-funnel__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.jg-funnel__label { font-size: 13px; font-weight: 500; min-width: 0; overflow-wrap: anywhere; }
.jg-funnel__value { font-family: var(--jg-mono); font-variant-numeric: tabular-nums; font-size: 15px; font-weight: 600; flex: none; }
.jg-funnel__track { height: 8px; border-radius: var(--jg-r-pill); background: var(--jg-field); overflow: hidden; }
.jg-funnel__bar { height: 100%; border-radius: var(--jg-r-pill); background: var(--jg-accent); }
.jg-funnel__note { font-size: 11px; color: var(--jg-muted); }

@media (max-width: 1199px) {
  .jg-dash__cols { grid-template-columns: minmax(0, 1fr); }
}

/* ── JET-1056: загрузка с компьютера в окне выбора фото ──────────────────────
   Зона та же, что во вкладке «Медиатека» (один примитив `createDropZone`), но в модальном
   окне ширины меньше: `min-width: 370px` у крупной строки разорвал бы окно, поэтому узкий
   вариант её отпускает и ставит кнопку под текст. Отдельного набора цветов и рамок нет —
   вторая правда о том, как выглядит зона загрузки, нам не нужна. */
/* Столбиком по центру — зона загрузки пустой сцены (макет сцен, кадр 01). */
.jg-drop--stack {
  flex-direction: column; align-items: center; text-align: center;
  gap: 14px; padding: 26px; background: var(--jg-surface-2);
}
.jg-drop--stack .jg-drop__text { flex: none; min-width: 0; align-items: center; gap: 4px; font-size: 12px; color: var(--jg-muted); }
.jg-drop__acts { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.jg-drop__icon {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 999px;
  background: var(--jg-accent-tint); color: var(--jg-link); font-size: 18px;
}

.jg-drop--s { gap: 10px 14px; padding: 12px 14px; }
.jg-drop--s .jg-drop__text { flex: 1 1 100%; min-width: 0; font-size: 12px; }
.jg-drop--s .jg-drop__text > span { font-size: 11px; }

.jg-mediapick__upload { display: flex; flex-direction: column; gap: 8px; }
.jg-mediapick__upload > .jg-hint { margin: 0; }

/* Отклонённые файлы в окне: та же полоса, что в очереди медиатеки, мельче на одну ступень. */
.jg-queue--s > li { padding: 8px 12px; font-size: 11px; }

/* ── JET-1065: кадр видео в плитке превью ────────────────────────────────────
   Кадр рисует сам браузер по первому кадру файла (примитив `videoFrame`), поэтому внутри
   плитки лежит настоящий видео-элемент. Метка обязательна: кадр видео сам по себе читается
   как фото. Отдельного набора цветов у этого состояния нет — рамка и фон те же, что у
   плитки-заглушки, чтобы вторая правда о её виде не появилась. */
.jg-thumb--v { position: relative; overflow: hidden; place-content: end start; padding: 4px; }
.jg-thumb--v > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: var(--jg-fill); }
.jg-thumb__mark { position: relative; z-index: 1; padding: 1px 5px; border-radius: 6px; background: var(--jg-surface); color: var(--jg-text-2); font-size: 9px; line-height: 1.5; }
/* причина вместо кадра: текст по центру, метка остаётся на своём месте внизу */
.jg-thumb--v > .jg-thumb__note { position: absolute; inset: 4px; display: grid; place-content: center; text-align: center; font-size: 10px; color: var(--jg-muted); }

/* ── JET-1068: блок «Бот для платных альбомов» ───────────────────────────────
   Блок собран из готовых примитивов оформления (панель, поле, действия, список,
   переключатель) — своих цветов, рамок и отступов здесь нет специально: он стоит и в
   карточке аккаунта, и в окне «Аккаунт Telegram», и расхождение двух копий вида было бы
   первым, что сломается. Правила ниже описывают только то, чего в системе ещё не было. */

/* Заголовок с действием справа. `jg-section__head` — flex с переносом, и блок «заголовок +
   пояснение» без своей базы занимает ширину самой длинной строки пояснения: кнопка уезжает
   под него даже там, где места вдоволь (найдено браузерной проверкой, а не чтением кода). */
.jg-section__head > .jg-headtext { flex: 1 1 240px; min-width: 0; }

/* Строка ввода username: «@» и кнопка по краям, поле забирает остаток ширины. В узкой
   колонке карточки (280px) кнопка переносится под поле, а не ужимает его до нечитаемого. */
.jg-bot__input > .jg-input { flex: 1 1 170px; min-width: 0; }
.jg-bot__input > .jg-hint { font-family: var(--jg-mono); }

/* Чипы в шапке карточки аккаунта: состояние аккаунта и состояние бота альбомов рядом.
   До JET-1068 чип был один и стоял без обёртки. */
.jg-account__chips { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; min-width: 0; }
/* Длинная причина переносится внутри бейджа, а не уезжает за карточку. Замер на стенде:
   «статус не обновлялся — процесс мог остановиться» вылезал за правый край на 8px и там
   обрезался. `nowrap` верен для коротких бейджей, но эти несут фразу целиком. */
.jg-account__chips .jg-badge { white-space: normal; min-width: 0; }

/* ── JET-1072: бесплатная ступень-альбом ─────────────────────────────────────
   Визуальный язык набора один и тот же для платной и бесплатной ступени (макет, экран 02):
   полоска кадров в рамке с подписью. Различает их ПОДПИСЬ и цвет рамки: платное уходит после
   оплаты (акцентная рамка), бесплатное — сразу одним сообщением (нейтральная). Спутать их
   глазами нельзя, поэтому второй набор размеров здесь не заводим, меняем только цвет. */
.jg-stepcard__set[data-when='free'] { border-color: var(--jg-border); background: var(--jg-fill); }
.jg-stepcard__set[data-when='free'] .jg-stepcard__setlabel { color: var(--jg-text-2); }
/* число кадров — на обложке ступени, рядом с пометкой состояния файла */
.jg-stepcard__coverbadge--n { left: auto; right: 6px; bottom: 6px; }

/* Состав набора в панели ступени: строка на файл — номер, кадр, состояние и действия.
   Строками, а не сеткой плиток: у каждого файла свои четыре действия, и в плитке 52×52 они
   не помещаются, а всплывающее меню на сенсорном экране пришлось бы ещё и открывать. */
.jg-setrows { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; list-style: none; }
.jg-setrow {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  padding: 8px; border: 1px solid var(--jg-border); border-radius: 10px; background: var(--jg-surface);
}
.jg-setrow__n { flex: none; width: 16px; text-align: right; font-size: 11px; color: var(--jg-muted); }
.jg-setrow__img { width: 44px; height: 56px; flex: none; aspect-ratio: auto; border-radius: 8px; }
.jg-setrow__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; font-size: 12px; }
.jg-setrow__acts { display: flex; align-items: center; gap: 2px; flex: none; flex-wrap: wrap; justify-content: flex-end; }

/* Вариант применения отметки в окне выбора — у самого подвала, чтобы читался перед нажатием. */
.jg-mediapick__mode { display: flex; flex-direction: column; gap: 6px; }
.jg-mediapick__mode > .jg-hint { margin: 0; }

/* Разбор несостоявшегося хода AI под плашкой перехвата (JET-1094). Свёрнут по умолчанию:
   на перехваченном диалоге он не должен отодвигать ленту, а на здоровом его нет вовсе. */
.jg-turnfail { margin-top: 6px; font-size: 12px; }
.jg-turnfail > summary { cursor: pointer; font-weight: 600; color: var(--jg-text); }
.jg-turnfail__list { display: flex; flex-direction: column; gap: 10px; margin: 8px 0 0; padding: 0; list-style: none; }
.jg-turnfail__item { display: flex; flex-direction: column; gap: 5px; padding-top: 8px; border-top: 1px solid var(--jg-border); }
.jg-turnfail__item:first-child { border-top: none; padding-top: 0; }
.jg-turnfail__head { margin: 0; color: var(--jg-muted); }
.jg-turnfail__reason { margin: 0; color: var(--jg-text); overflow-wrap: anywhere; }
.jg-turnfail__none { margin: 6px 0 0; color: var(--jg-muted); }
/* Пары «название — значение» в две колонки: на 1280 читается строкой, на узком переносится. */
.jg-turnfail__facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 2px 10px; margin: 0; }
.jg-turnfail__facts dt { color: var(--jg-muted); }
.jg-turnfail__facts dd { margin: 0; color: var(--jg-text-2); overflow-wrap: anywhere; }
/* Сырое тело — длинное и в одну строку: переносим и держим высоту, иначе оно выдавит ленту. */
.jg-turnfail__raw {
  margin: 4px 0 0; padding: 8px 10px; max-height: 220px; overflow: auto;
  border-radius: var(--jg-r-tile); background: var(--jg-surface-2); color: var(--jg-text-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* ── редактор сцены v2 (JET-1106): строка лесенки это одно медиа, и всё про него правится
   в самой строке. Боковой панели ступени в макете «Scenes - redaktor v2» нет. Значения —
   только токены, как во всём файле; раскладка и размеры — по исходнику макета. ────────── */
/* Макет: строка выровнена по ВЕРХУ (`align-items:flex-start`), а ручка и кнопки действий
   центрируются сами (`align-self:center`). При общем `center` обложка 72×72 уезжала в
   середину выросшей строки — у платной ступени с набором и причиной неготовности это
   полсотни пикселей, и кадр переставал быть началом строки. */
.jg-stepcard__row { display: flex; align-items: flex-start; gap: 14px; }
.jg-stepcard__mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.jg-stepcard__mark { gap: 0; }
/* Полоса настроек — первая линия строки: цена и острота стоят рядом с кадром, а не под ним. */
.jg-stepcard__cfg { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
/* 8px между действиями и кнопки 36×36 — как в макете (там это `width:36px;height:36px`
   с радиусом 9). Маленькие 30×30 остались с прошлого макета. */
.jg-stepcard__acts { display: flex; align-items: center; gap: 8px; flex: none; }
.jg-stepcard__acts .jg-btn--icon { width: var(--jg-h-m); height: var(--jg-h-m); border-radius: 9px; }
.jg-stepcard__pricemsg { display: flex; flex-direction: column; gap: 2px; }
.jg-stepcard__pricemsg:empty { display: none; }

/* Поле цены одной ширины у всех строк — и у платной, и у бесплатной: в макете лесенка
   читается сверху вниз одной колонкой цен, а прячущееся поле ломало бы эту колонку.
   JET-1153 (второй пересмотр скоупа, 20.09.2026): рядом встал ещё и переключатель вида —
   ширина растёт по содержимому, а не фиксированной цифрой, иначе «бесплатно»/«платно» не
   помещались бы рядом с ценой и валютой. */
.jg-stepprice { display: flex; align-items: center; gap: 8px; min-width: 196px; flex: none; }
.jg-stepprice__row {
  display: flex; align-items: center; gap: 8px; width: 100%;
  border: 1px solid var(--jg-border-strong); border-radius: var(--jg-r-control);
  background: var(--jg-surface); padding: 8px 11px;
}
.jg-stepprice__row[data-paid='true'] { border-color: var(--jg-accent); }
/* JET-1153: знак валюты — не подпись, а ПЕРЕКЛЮЧАТЕЛЬ ★/$ (решение владельца 20.09.2026:
   валюта своя на каждую ступень). Он стоит ПОСЛЕ числа, на месте прежнего знака: «450 ★»
   читается слева направо, как на всех остальных экранах CRM. Сегмент тот же, что у «Времени
   суток» (.jg-seg--content), только уже — в строку лесенки полноразмерный не помещается. */
.jg-stepprice__seg { padding: 2px; gap: 2px; flex: none; background: var(--jg-fill); }
.jg-stepprice__seg > .jg-stepprice__cur {
  min-width: 26px; padding: 4px 6px; font-size: 13px; line-height: 1.2; text-align: center;
}
/* Выбранная валюта — акцентом, а НЕ поверхностью: сегмент стоит внутри поля цены, у
   которого фон уже `--jg-surface`, и общий вид `.jg-seg--content` («выбранное = surface»)
   на нём сливается в две одинаковые кнопки. Найдено браузером 20.09.2026: на снимке
   отличить ★ от $ было нельзя ни в светлой теме, ни в тёмной. */
.jg-stepprice__seg > .jg-stepprice__cur[aria-pressed='true'] {
  background: var(--jg-accent); color: var(--jg-on-accent); border-color: var(--jg-accent); font-weight: 600;
}
.jg-stepprice__seg[hidden] { display: none; }
.jg-stepprice__in {
  flex: 1; min-width: 0; width: 100%; border: 0; background: none; padding: 0;
  text-align: right; font: inherit; font-size: 14px; font-weight: 500;
  font-variant-numeric: tabular-nums; color: var(--jg-accent-text);
}
.jg-stepprice__in:focus { outline: none; }
.jg-stepprice__row:focus-within { outline: var(--jg-focus-outline); outline-offset: 2px; }
/* «бесплатно / платно» — настоящий переключатель, а не одноразовая кнопка-действие (макет,
   второй пересмотр скоупа владельцем 20.09.2026: «нужен явный переключатель... как на
   макете», должен работать и на уже сохранённой ступени). Щёлкнуть «платно» на бесплатной
   ступени раскрывает цену/валюту/название/подпись ПРЯМО В СТРОКЕ (`.jg-stepconvert` ниже),
   без отдельного окна («ничего открываться не должно»); «бесплатно» на платной — возвращает
   ступень на бесплатный оригинал набора, Offer не трогая (`revertToFree`). Тот же сегмент,
   что у ★/$ и у времени суток — `flex: none`, чтобы не растягивался вместе с полем цены. */
.jg-stepprice__kindseg { flex: none; }

/* Конвертация бесплатной ступени в платную «на месте» (JET-1153): цена уже раскрыта в
   `.jg-stepprice`, здесь — только название и обязательная подпись покупателю, компактно, в
   ширину карточки, а не в ширину узкой колонки цены. */
.jg-stepconvert { display: flex; flex-direction: column; gap: 6px; }
.jg-stepconvert[hidden] { display: none; }
.jg-stepconvert__row textarea.jg-textarea { min-height: 52px; }
.jg-stepconvert__actions { display: flex; justify-content: flex-end; gap: 8px; }

/* Острота — разогрев по цвету, пять РАЗНЫХ оттенков (в макете HEAT_ON от песочного к
   оранжевому). Одним акцентным цветом пять квадратиков не читаются как шкала. */
.jg-spice { display: flex; align-items: center; gap: 8px; flex: none; }
.jg-spice__cap { font-size: 12px; font-weight: 500; color: var(--jg-text-2); }
.jg-spice__row { display: flex; gap: 3px; }
.jg-spice__box {
  width: 22px; height: 22px; padding: 0; cursor: pointer;
  border: 1px solid var(--jg-border); border-radius: 5px;
  background: var(--jg-surface); color: transparent; font: inherit;
}
.jg-spice__box[data-on='true'] { border-color: transparent; }
.jg-spice__box[data-on='true'][data-level='1'] { background: var(--jg-heat-1); }
.jg-spice__box[data-on='true'][data-level='2'] { background: var(--jg-heat-2); }
.jg-spice__box[data-on='true'][data-level='3'] { background: var(--jg-heat-3); }
.jg-spice__box[data-on='true'][data-level='4'] { background: var(--jg-heat-4); }
.jg-spice__box[data-on='true'][data-level='5'] { background: var(--jg-heat-5); }
.jg-spice__box:disabled { cursor: default; opacity: .5; }
.jg-spice__box:focus-visible { outline: var(--jg-focus-outline); outline-offset: 2px; }

/* Кнопка-галочка, раскрывающая описание. Точка на углу — признак «описание есть»,
   чтобы пустые кадры были видны в свёрнутой лесенке. */
.jg-stepdescbtn { position: relative; }
.jg-stepdescbtn[data-open='true'] { border-color: var(--jg-accent); color: var(--jg-accent-text); background: var(--jg-accent-tint); }
.jg-stepdescbtn__dot {
  position: absolute; top: -3px; right: -3px; width: 7px; height: 7px;
  border-radius: 999px; background: var(--jg-info);
}
.jg-stepdesc { padding: 12px 0 2px 28px; display: flex; flex-direction: column; gap: 7px; }
.jg-stepdesc__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.jg-stepdesc__body { display: flex; flex-direction: column; gap: 12px; }
.jg-stepdesc__file { display: flex; flex-direction: column; gap: 7px; }
.jg-stepdesc__filehead { display: flex; align-items: center; gap: 8px; }
.jg-stepdesc__thumb { width: 36px; height: 36px; flex: none; aspect-ratio: auto; border-radius: var(--jg-r-badge); }
.jg-stepdesc__area { min-height: 76px; }
.jg-stepdesc__area[data-filled='true'] { border-color: var(--jg-accent); }
.jg-stepdesc__foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.jg-stepdesc__meta { margin-left: auto; font-size: 11px; color: var(--jg-muted); }

/* Шапка и подвал лесенки (макет): пакетная генерация слева от подсказки о переносе,
   правило роста цены и «Добавить медиа» — снизу. */
.jg-ladderfoot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding-top: 4px;
}
.jg-ladderfoot__note { font-size: 12px; line-height: 1.5; color: var(--jg-text-2); max-width: 560px; }

/* Пустая лесенка (макет, кадр 01): плашка на месте ступеней и карточка «Загрузить медиа». */
.jg-steps__empty {
  border: 1px dashed var(--jg-border-strong); border-radius: var(--jg-r-tile);
  padding: 26px; text-align: center; font-size: 14px; color: var(--jg-muted);
}
.jg-scenedrop {
  display: flex; flex-direction: column; gap: 14px; padding: 20px;
  border: 1px solid var(--jg-border); border-radius: var(--jg-r-tile);
}
.jg-scenedrop__title { font-size: 15px; font-weight: 600; }

.jg-mergebar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; margin-bottom: 12px; font-size: 12px; line-height: 1.5;
  border: 1px solid var(--jg-border-strong); border-radius: var(--jg-r-panel); background: var(--jg-fill);
}
.jg-mergebar[data-tone='warn'] { border-color: var(--jg-warn); background: var(--jg-warn-bg); color: var(--jg-warn-text); }
/* JET-1153 (баг вёрстки, вернулся 20.09.2026): `width: 168px` был правилен, пока в поле цены
   стояли только цена и валюта — с переключателем «бесплатно/платно» содержимому нужно
   ~430px. Явный `width` на flex-элементе задаёт flex-basis (`flex: none` — не растёт и не
   сжимается), и `min-width` клэмпит только ИТОГОВЫЙ размер бокса, а не то, что внутри —
   строка `.jg-stepprice__row` (100% от бокса) физически не помещала содержимое и вылезала
   поверх соседнего блока «острота» (найдено сверкой с макетом, `getBoundingClientRect`
   показал перекрытие `.jg-stepprice__seg` и `.jg-spice`). Без фиксированной ширины бокс
   считает её от содержимого (`flex-basis: auto`), и `flex-wrap` родителя переносит «остроту»
   на новую строку, когда места не хватает — как и задуман для узких экранов. */
@media (max-width: 1280px) {
  .jg-stepcard__cfg { gap: 10px; }
}

/* JET-1144: лишняя закрывающая скобка, стоявшая ЗДЕСЬ (она есть в main), съедала первое же
   правило, дописанное в конец файла: на неожиданной закрывающей скобке разбор CSS пропускает
   всё до следующего блока включительно, поэтому `white-space: pre-wrap` ниже не применялся, а
   браузер об этом не ругался. Найдено прогоном браузером: computed `white-space` оставался
   `normal`. Скобки в комментариях выше не пишем — они ломают сверку баланса. */

/* JET-1144: подпись покупателю — многострочный текст, который FansGram печатает под платным
   набором. Переводы строк сохраняем: оператор должен видеть текст так же, как его увидит
   покупатель, а не склеенным в абзац. */
.jg-caption-text { margin: 4px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }

/* ── Деньги в списках, карточка реф-кода, QR (JET-1155) ─────────────────────── */

/* Восемь колонок в 760px (.jg-table) не помещаются: на узком экране кнопки «Действий»
   наползали на бейдж статуса — это видно и на main, с семью колонками. Своя нижняя
   граница ширины для таких таблиц: .jg-tablescroll уже overflow:auto, поэтому вместо
   наложения появляется горизонтальная прокрутка. */
.jg-table--wide { min-width: 980px; }

/* Ячейка с суммами: строка на валюту — звёзды и доллары не складываются (курса нет). */
.jg-moneycell { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.jg-moneycell__row { font-variant-numeric: tabular-nums; white-space: nowrap; }
.jg-moneycell__row + .jg-moneycell__row { font-size: 11px; color: var(--jg-text-2); }

/* Заголовок сортируемой колонки. Стрелка рисуется псевдоэлементом, чтобы ширина колонки
   не прыгала между состояниями: место под метку занято всегда. */
.jg-thsort {
  display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: none;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer;
}
.jg-thsort:disabled { cursor: default; opacity: .55; }
.jg-thsort:not(:disabled):hover { color: var(--jg-accent); }
.jg-thsort:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 3px; border-radius: 4px; }
.jg-thsort__mark { width: 9px; text-align: center; opacity: .45; }
.jg-thsort__mark::before { content: '↕'; }
.jg-thsort[data-dir='desc'] .jg-thsort__mark { opacity: 1; }
.jg-thsort[data-dir='desc'] .jg-thsort__mark::before { content: '↓'; }
.jg-thsort[data-dir='asc'] .jg-thsort__mark { opacity: 1; }
.jg-thsort[data-dir='asc'] .jg-thsort__mark::before { content: '↑'; }

/* Код в строке списка — кнопка, открывающая карточку; выглядит как прежний .jg-code. */
.jg-code--btn { border: 0; cursor: pointer; color: inherit; }
.jg-code--btn:hover { background: var(--jg-fill-strong, var(--jg-fill)); color: var(--jg-accent); }
.jg-code--btn:focus-visible { outline: 2px solid var(--jg-focus-outline); outline-offset: 2px; }

/* Полоса показателей над списком. */
.jg-statband { padding: 18px 20px 16px; }
.jg-statband__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.jg-statband__title { margin: 0; font-size: 14px; font-weight: 600; }
.jg-statband__period { font-size: 12px; color: var(--jg-muted); }
.jg-statband__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px 24px; }
.jg-statband__grid--card { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; margin: 4px 0 18px; }
.jg-statband__cell { display: flex; flex-direction: column; gap: 4px; min-width: 0; padding-bottom: 10px; border-bottom: 1px solid var(--jg-divider); }
.jg-statband__label { font-size: 12px; color: var(--jg-text-2); overflow-wrap: anywhere; }
.jg-statband__value { display: flex; flex-wrap: wrap; gap: 4px 10px; font-family: var(--jg-mono); font-size: 20px; font-weight: 500; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.jg-statband__value[data-tone='warn'] { color: var(--jg-warn); }
.jg-statband__money { white-space: nowrap; }
.jg-statband__money + .jg-statband__money { font-size: 15px; color: var(--jg-text-2); }
.jg-statband__note { font-size: 11px; line-height: 1.4; color: var(--jg-muted); }
.jg-statband__foot { margin: 12px 0 0; font-size: 11px; line-height: 1.5; color: var(--jg-muted); }
.jg-statband__empty { margin: 0; font-size: 12px; color: var(--jg-text-2); }
@media (max-width: 1100px) { .jg-statband__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) {
  .jg-statband__grid, .jg-statband__grid--card { grid-template-columns: minmax(0, 1fr); }
}

/* Карточка реф-кода: слева QR и реквизиты, справа показатели и график. */
.jg-dialog--wide { width: min(820px, calc(100vw - 32px)); }
.jg-refcard { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 22px; align-items: start; }
.jg-refcard__side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.jg-refcard__main { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.jg-refcard__link { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 10px 12px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); background: var(--jg-fill); }
.jg-refcard__url { font-family: var(--jg-mono); font-size: 11px; overflow-wrap: anywhere; min-width: 0; }
.jg-refcard__h { margin: 6px 0 0; font-size: 13px; font-weight: 600; }
.jg-refcard__empty { margin: 0; padding: 14px 0; font-size: 12px; color: var(--jg-text-2); }
@media (max-width: 700px) { .jg-refcard { grid-template-columns: minmax(0, 1fr); } }

/* QR: одна фигура на все тёмные модули. Цвета — токены, поэтому обе темы работают без
   второго набора правил; белая подложка обязательна и в тёмной теме (сканеру нужен
   контраст, а не «красиво»). */
.jg-qr { padding: 10px; border: 1px solid var(--jg-border); border-radius: var(--jg-r-panel); background: #fff; }
.jg-qr__svg { display: block; width: 100%; height: auto; }
.jg-qr__bg { fill: #fff; }
.jg-qr__fg { fill: #000; }

/* JET-1153: условия платного набора в окне выбора файлов — цена с валютой, имя для CRM и
   подпись покупателю. Блок появляется только у платной ступени, поэтому он не съедает место
   у бесплатной, ради которой окно открывают чаще. Поля в столбик: в строку они на 1280
   сжимались так, что подпись покупателю (её видит покупатель) читалась хуже названия для
   CRM, хотя важность ровно обратная. */
.jg-paidset {
  display: flex; flex-direction: column; gap: 12px;
  padding: 12px 14px; border: 1px solid var(--jg-border-strong);
  border-radius: var(--jg-r-panel); background: var(--jg-fill);
}
.jg-paidset[hidden] { display: none; }
.jg-paidset__price { display: flex; align-items: center; gap: 8px; }
.jg-paidset__price > .jg-input { width: 140px; flex: none; }
.jg-paidset .jg-textarea { min-height: 72px; }
