/* Кабинет v2 — визуальный клон claude.ai.
   Ф1: токены тёплой палитры (светлая — дефолт, тёмная — data-theme/система), антиква Lora
   в ответах и заголовках, каркас сайдбара с разделами.
   06.07: мобильный редизайн в стилистике DeepSeek — сайдбар-шторка за гамбургером,
   шапка-аппбар (центр: название + модель), композер-карточка с пилюлей модели и
   круглыми кнопками. Палитра и антиква прежние. */

/* ── Шрифт: Lora (аналог фирменной антиквы claude.ai, кириллица, self-host) ── */
@font-face { font-family: "Lora"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/lora-normal-400-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
@font-face { font-family: "Lora"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/lora-normal-400-cyrillic-ext.woff2) format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
@font-face { font-family: "Lora"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/lora-normal-400-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Lora"; font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/lora-normal-600-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
@font-face { font-family: "Lora"; font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/lora-normal-600-cyrillic-ext.woff2) format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
@font-face { font-family: "Lora"; font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/lora-normal-600-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Lora"; font-style: italic; font-weight: 400; font-display: swap;
  src: url(fonts/lora-italic-400-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
@font-face { font-family: "Lora"; font-style: italic; font-weight: 400; font-display: swap;
  src: url(fonts/lora-italic-400-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

/* ── Шрифт: Source Serif 4 (чистый близнец Copernicus, для названия сессии) ── */
@font-face { font-family: "Source Serif 4"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/sourceserif-normal-400-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
@font-face { font-family: "Source Serif 4"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/sourceserif-normal-400-cyrillic-ext.woff2) format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
@font-face { font-family: "Source Serif 4"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/sourceserif-normal-400-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Source Serif 4"; font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/sourceserif-normal-600-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
@font-face { font-family: "Source Serif 4"; font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/sourceserif-normal-600-cyrillic-ext.woff2) format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
@font-face { font-family: "Source Serif 4"; font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/sourceserif-normal-600-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

/* Базовые палитры/темы приходят из cabinet-tokens.generated.css.
   Единственный редактируемый источник: design-system/cabinet-tokens.json. */

/* ── Микс: полотно чата — светлое (как в light),
   а шторка (.sidebar) и композер (.composer-card) — чёрные с белым текстом
   (как в dark). Своя пара --text/--dim переопределяется ЛОКАЛЬНО внутри этих
   двух блоков (custom properties наследуются вниз по DOM), глобальные токены
   остаются светлыми для ленты сообщений. ── */
:root[data-theme="mix"] .sidebar {
  background: #1f1e1d;
  --text: #eceae4;
  --dim: #a6a29a;
  --surface-2: #3a3936;
  --border: #413f3c;
}
/* Композер в миксе — пользователь 11.08 вернул чёрный (белый эксперимент 05.08 не зашёл).
   Чёрная плашка на светлом полотне и так контрастна — ринг-обводка (0 0 0 1px) не нужна,
   только тень. */
:root[data-theme="mix"] .composer-card {
  background: #1f1e1d;
  --text: #eceae4;
  --dim: #a6a29a;
  --surface-2: #3a3936;
  --border: #413f3c;
  box-shadow: 0 2px 6px rgba(31, 30, 29, .16), 0 10px 30px rgba(31, 30, 29, .26);
}
:root[data-theme="mix"] .composer-card:focus-within {
  box-shadow: 0 3px 8px rgba(31, 30, 29, .18), 0 12px 34px rgba(31, 30, 29, .30);
}
:root[data-theme="mix"] .chat.exp-float .composer-card {
  box-shadow: 0 4px 10px rgba(31, 30, 29, .20), 0 16px 40px rgba(31, 30, 29, .32);
}
/* Поповеры (список моделей, слэш-команды, порог контекста) сидят на светлом var(--surface)
   независимо от тёмной .composer-card — иначе наследуют её светлый --text/--dim и текст
   тонет в белом фоне. Возвращаем им светлую пару. */
:root[data-theme="mix"] .plus-menu,
:root[data-theme="mix"] .slash-menu,
:root[data-theme="mix"] .model-menu {
  --text: #1f1e1d;
  --dim: #75726b;
  --surface-2: #ece9de;
  --border: #e0ddd1;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
/* Страница — приложение: сама НЕ скроллится никогда (скролл живёт только в ленте
   и сайдбаре). Иначе мобильный браузер, утащив документ (клавиатура/перелив),
   оставляет его «уползшим» после уменьшения композера. */
html, body { height: 100%; overflow: hidden; overscroll-behavior: none; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  line-height: 1.55;
}
.hidden { display: none !important; }
button { font-family: inherit; }

/* ── Вход ── */
/* height + свой overflow: body теперь не скроллится, на низком экране логин листается сам */
.login { display: flex; align-items: center; justify-content: center; height: 100vh; height: 100dvh; overflow-y: auto; padding: 16px; }
.login-card { background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow); border-radius: 16px; padding: 40px 36px; width: 100%; max-width: 360px; text-align: center; }
.login-logo { width: 56px; height: 56px; line-height: 56px; margin: 0 auto 16px; border-radius: 50%; background: var(--accent); color: var(--accent-ink); font-size: 26px; font-weight: 600; font-family: var(--font-serif); }
.login-card h1 { font-size: 22px; font-weight: 600; font-family: var(--font-serif); }
.login-sub { color: var(--dim); font-size: 13px; margin: 4px 0 24px; }
.login-card input { width: 100%; background: var(--bg); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: 16px; padding: 12px 14px; margin-bottom: 12px; outline: none; }
.login-card input:focus { border-color: var(--accent); }
.login-card button { width: 100%; background: var(--accent); border: none; border-radius: 10px; color: var(--accent-ink); font-size: 15px; font-weight: 600; padding: 12px; cursor: pointer; }
.login-card button:hover { background: var(--accent-hover); }
.login-err { color: var(--err); font-size: 13px; margin-top: 12px; min-height: 18px; }

/* ── Каркас кабинета ── */
/* dvh вместо vh: мобильный браузер меряет высоту с учётом адресной строки —
   иначе низ диалога «обрезан» под панелью браузера. 100vh — фолбэк для старых.
   --app-h: JS кладёт сюда visualViewport.height. dvh НЕ сжимается под виртуальную
   клавиатуру (меряет вьюпорт без неё) — из-за этого композер уезжал под клавиатуру
   и «не возвращался». visualViewport учитывает и адресную строку, и клавиатуру. */
.cabinet { display: flex; height: 100vh; height: 100dvh; height: var(--app-h, 100dvh); }

/* Сайдбар — «овсянка»; на десктопе — колонка слева, на телефоне — шторка (ниже) */
/* Сайдбар — в тон основного полотна (var(--bg)), а не «овсянка»: левую и правую
   плашки разделяет только серая полоса border-right. Активный/наведённый тред
   по-прежнему подсвечивается var(--surface-2). */
.sidebar { width: 300px; flex: 0 0 300px; background: var(--bg); border-right: 1px solid var(--border); display: flex; flex-direction: column; }
.sidebar-head { display: flex; align-items: center; justify-content: flex-end; gap: 4px; padding: 12px 12px 8px; }
.head-btns { display: flex; gap: 4px; }
.ghost {
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; background: none; border: none; border-radius: 10px;
  color: var(--dim); cursor: pointer; transition: background .12s, color .12s;
}
.ghost:hover { background: var(--surface-2); color: var(--text); }
.ghost svg { display: block; width: 20px; height: 20px; }
.ghost.spinning svg { animation: spin .6s linear infinite; }
.ghost:disabled { opacity: .4; cursor: default; }
.ghost:disabled:hover { background: none; color: var(--dim); }
@keyframes spin { to { transform: rotate(360deg); } }
.access-modal { width: min(520px, calc(100vw - 28px)); max-height: min(720px, calc(100dvh - 28px)); overflow: auto; }
/* Экран доступа лежит ниже общей модалки (z90): иначе «Переподключить?» открывалось
   ПОД ним и подтвердить вход было нечем — кнопка есть, а нажать нельзя. */
#ai-access-backdrop { z-index: 88; }
.access-head { display:flex; justify-content:space-between; align-items:flex-start; gap:12px; }
.access-sub { color:var(--dim); font-size:13px; margin-top:3px; }
.access-body { display:grid; gap:12px; margin-top:18px; }
.access-card { border:1px solid var(--border); border-radius:14px; padding:14px; background:var(--surface); }
.access-card-head { display:flex; justify-content:space-between; gap:12px; align-items:center; }
.access-name { font-family:var(--serif); font-weight:600; font-size:18px; }
.access-status { font-size:12px; color:var(--dim); }
.access-status.ok { color:#2f8b62; }
.access-actions { display:flex; gap:8px; margin-top:12px; }
.access-action { border:0; border-radius:9px; padding:9px 12px; background:var(--accent); color:var(--accent-ink); font-weight:600; cursor:pointer; }
.access-save { border:1px solid var(--border); border-radius:9px; padding:9px 12px; background:var(--surface-2); color:var(--text); font-weight:600; cursor:pointer; }
.access-renewal { display:grid; gap:5px; margin-top:12px; color:var(--dim); font-size:13px; }
.access-renewal input { width:100%; box-sizing:border-box; padding:9px 10px; border:1px solid var(--border); border-radius:9px; background:var(--bg); color:var(--text); font:inherit; }
.access-renewal small,.access-renewal-note { color:var(--dim); font-size:12px; }
.access-action.secondary { background:var(--surface-2); color:var(--text); }
.access-login { margin-top:12px; padding:11px; border-radius:10px; background:var(--surface-2); font-size:13px; }
.access-code { font:700 21px/1.2 ui-monospace,monospace; letter-spacing:1px; margin:8px 0; user-select:all; }
.access-link { color:var(--accent); overflow-wrap:anywhere; }
.access-input { width:100%; box-sizing:border-box; margin-top:9px; padding:10px; border:1px solid var(--border); border-radius:9px; background:var(--bg); color:var(--text); }
/* Пошаговый вход: активный шаг яркий, ожидающий приглушён, пройденный с галкой. */
.access-steps { display:grid; gap:12px; }
.access-step { display:flex; gap:10px; align-items:flex-start; }
.access-step.wait { opacity:.45; }
.access-step-num { flex:0 0 22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:700; background:var(--surface); border:1px solid var(--border); color:var(--dim); }
.access-step.active .access-step-num { background:var(--accent); border-color:var(--accent); color:var(--accent-ink); }
.access-step.done .access-step-num { background:#2f8b62; border-color:#2f8b62; color:#fff; }
.access-step-body { flex:1; min-width:0; display:grid; gap:7px; }
.access-step-title { font-weight:600; color:var(--text); }
.access-hint { color:var(--dim); font-size:12px; line-height:1.45; }
.access-code-row { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.access-code-row .access-code { margin:0; flex:1; min-width:120px; }
.access-action.link { text-decoration:none; display:inline-block; }
.access-action.copied { background:#2f8b62; color:#fff; }
.access-wait { margin-top:12px; color:var(--dim); font-size:12px; }
.access-timer { color:var(--dim); font-size:12px; margin-top:2px; }
.access-error { color:#b4483c; font-size:12px; }
.access-final { margin-bottom:9px; }
.access-final.ok { color:#2f8b62; font-weight:600; }
.access-models { margin-top:16px; border-top:1px solid var(--border); padding-top:12px; }
.access-models select { width:100%; padding:10px; border:1px solid var(--border); border-radius:9px; background:var(--surface); color:var(--text); }
.new-thread { margin: 0 12px 12px; padding: 9px 12px; background: none; border: none; border-radius: 8px; color: var(--accent); font-size: 14px; font-weight: 500; cursor: pointer; text-align: left; }
.new-thread:hover { background: var(--surface-2); }
/* Шторка — одна колонка «Треды». */
.side-pages { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; overflow: hidden; }
.side-page { display: none; flex: 1; min-height: 0; flex-direction: column; }
.side-page.active { display: flex; }
.modal-close { border: none; background: none; color: var(--dim); font-size: 15px; cursor: pointer; padding: 4px 6px; border-radius: 8px; line-height: 1; }
.modal-close:hover { background: var(--surface-2); color: var(--text); }

.threads { overflow-y: auto; flex: 1; padding: 0 8px 12px; }
.thread {
  display: flex; align-items: center; gap: 9px;
  width: 100%; padding: 8px 10px; margin-bottom: 1px;
  background: none; border: none; border-radius: 8px;
  color: var(--text); font-size: 14px; text-align: left; cursor: pointer;
}
.thread:hover { background: var(--surface-2); }
.thread.active { background: var(--surface-2); font-weight: 500; }
.thread-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Действия над тредом — проступают по наведению (на десктопе), как в claude.ai */
.thread-acts { display: flex; gap: 2px; margin: -4px -4px -4px 0; opacity: 0; transition: opacity .12s; }
.thread:hover .thread-acts, .thread.active .thread-acts { opacity: 1; }
.thread-act {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: none; padding: 0;
  background: none; border: none; border-radius: 7px;
  color: var(--dim); cursor: pointer; transition: background .12s, color .12s;
}
.thread-act svg { width: 15px; height: 15px; display: block; }
.thread-act:hover { background: var(--border); color: var(--text); }
.thread-act.del:hover { background: var(--err-bg); color: var(--err); }
/* Архив тредов — тихая свёрнутая секция внизу списка, не спорит вниманием с активными чатами */
.archive-toggle {
  display: flex; align-items: center; gap: 6px; flex: none;
  width: calc(100% - 16px); margin: 2px 8px; padding: 6px 10px;
  background: none; border: none; border-radius: 7px;
  color: var(--dim); font-size: 12px; font-weight: 500; letter-spacing: .2px;
  cursor: pointer; transition: background .12s, color .12s;
}
.archive-toggle:hover { background: var(--surface-2); color: var(--text); }
.archive-toggle svg { flex: none; display: block; opacity: .7; }
.archive-toggle span { flex: 1; text-align: left; }
.archive-toggle-chev { transition: transform .16s ease; opacity: .55; }
.archive-toggle[aria-expanded="true"] .archive-toggle-chev { transform: rotate(90deg); }
.archived-threads { flex: 0 1 auto; max-height: 34vh; overflow-y: auto; padding: 0 8px 6px; }
.thread.archived { opacity: .6; transition: opacity .12s; }
.thread.archived:hover { opacity: 1; }
/* Статус-точки: пульс = работает, сплошная терракота = есть результат, пустая = тихо */
.dot { position: relative; width: 8px; height: 8px; border-radius: 50%; background: var(--border); flex: 0 0 8px; }
.dot.busy { background: var(--accent); animation: pulse 1.2s ease-in-out infinite; }
/* «Тред закончил и ждёт тебя»: красный шар стоит на месте (яркое ядро + мягкое свечение),
   а вокруг разбегается кольцо, которое стартует красным и на лету перетекает в жёлтое —
   взгляд цепляется. Отличается от busy (та мигает заливкой на месте). */
.dot.unread {
  background: var(--accent);
  box-shadow: 0 0 7px 1px color-mix(in srgb, var(--accent) 60%, transparent);
}
.dot.unread::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid var(--accent); pointer-events: none;
  /* easeOutExpo: резкий старт, замедление к концу. 1.85s (ещё ×0.8) */
  animation: dot-ping 1.85s cubic-bezier(.16, 1, .3, 1) infinite;
}
@keyframes pulse { 50% { opacity: .35; } }
/* Пауза: косметическая метка «отложил на потом», не влияет ни на
   busy/unread, ни на саму работу треда. Вокруг точки-статуса медленно облетает
   маленький спутник-«планетка» — намеренно спокойный темп (в разы медленнее pulse/
   dot-ping, которые сигналят «смотри сюда сейчас»), тусклый цвет var(--dim), а не
   яркий --accent, чтобы не спорить визуально с busy/unread. */
.dot.paused::before {
  content: ""; position: absolute; width: 2.6px; height: 2.6px; border-radius: 50%;
  top: 50%; left: 50%; margin: -1.3px 0 0 -1.3px;
  background: var(--dim); pointer-events: none;
  animation: dot-orbit 3.6s linear infinite;
}
@keyframes dot-orbit {
  from { transform: rotate(0deg) translateX(7px); }
  to   { transform: rotate(360deg) translateX(7px); }
}
/* Пик меньше (не достаёт до букв) + кольцо держит яркость, набирая жёлтый. Толщина
   истончается до субпикселя. Прозрачность добивает до 0 раньше (~75%), поэтому жёлтый
   фокус гаснет быстрее и последнюю треть пути кольцо уже невидимо. */
@keyframes dot-ping {
  0%   { transform: scale(.5);   opacity: 1;   border-color: var(--accent); border-width: 2px; }
  40%  { opacity: .8;  border-color: #f39a1a; border-width: 1.4px; }
  62%  { opacity: .45; border-color: #f7cf2e; border-width: .9px; }    /* жёлтый фокус ещё виден */
  75%  { opacity: 0;   border-color: #f9d838; border-width: .6px; }    /* полностью прозрачно */
  100% { transform: scale(1.035); opacity: 0;  border-color: #f9d838; border-width: .5px; }
}

/* Бейдж модели треда — иконка-столбец сразу за статус-точкой. Форма+цвет = семейство
   модели (Opus/Sonnet/Fable/Haiku/GPT/Claude). Буква внутри для однозначности. */
/* Клетка-«элемент» в духе таблицы Менделеева: скруглённый квадрат с двухбуквенным
   символом (большая заглавная + маленькая вторая). Форма одна, различаем цветом. */
.tmodel {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 18px; border-radius: 5px; margin-left: -2px;
  color: #fff; user-select: none; font-family: var(--font-ui, inherit);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.14);
}
/* Символ — единый inline-блок: две буквы по общей базовой линии (как «Mg»),
   а весь блок центрируется в клетке флексом родителя. tm-2 чуть приподнята,
   чтобы хвост «p/a» не тянул строку вниз и пара смотрелась по центру. */
.tmodel .tm-sym { display: inline-flex; align-items: baseline; line-height: 1; transform: translateY(.5px); }
.tmodel .tm-1 { font-size: 9.5px; font-weight: 700; letter-spacing: -.2px; }
.tmodel .tm-2 { font-size: 7px;   font-weight: 600; opacity: .92; margin-left: .4px; }
.tmodel.m-opus   { background: #7c5cd6; }
.tmodel.m-sonnet { background: #3b82c4; }
.tmodel.m-fable  { background: #d99a2b; }
.tmodel.m-haiku  { background: #2fa3a3; }
.tmodel.m-gpt    { background: #10a37f; }
.tmodel.m-gemini { background: #4285f4; }
.tmodel.m-claude { background: var(--accent); }

/* Соседний столбец — рефлексия. Пусто, пока не отрефлексировано; иначе понятный чип
   «✓ отжила»: галочка + слово читаются как «сессия исчерпана, можно убрать в архив».
   Приглушённо-зелёный, мягкая рамка — спокойный «завершённый» вид, не кричит. empty не
   занимает места. Смысл дополняет тултип. */
.trefl {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 3px;
  padding: 2px 7px 2px 5px; border-radius: 999px;
  background: rgba(47,140,90,.12); color: #2f8c5a;
  box-shadow: inset 0 0 0 1px rgba(47,140,90,.24);
  font-size: 9.5px; font-weight: 600; letter-spacing: .1px; white-space: nowrap;
}
.trefl svg { width: 11px; height: 11px; display: block; flex: 0 0 auto; }
.trefl-txt { line-height: 1; }
.trefl.empty { display: none; }

/* Остаток рефлексии: чип «отжила» гаснет новой репликой в треде, но
   след, что тред УЖЕ рефлексировали, не должен пропадать без следа — точки, по одной
   на каждую завершённую рефлексию (растёт, даже когда чип снова погашен). Приглушённее
   самого чипа — это история, не «прямо сейчас готово». */
.trefl-echo {
  flex: 0 0 auto; display: inline-flex; align-items: center;
  font-size: 9px; letter-spacing: 1px; color: rgba(47,140,90,.55);
}

/* Затемнение под шторкой — живёт только в мобильной вёрстке */
.drawer-backdrop { display: none; }

/* Чат */
/* min-height: 0 — иначе в мобильной колонке .chat не сжимается меньше ленты
   и композер выталкивается за экран (флексовый min-height:auto) */
.chat { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; position: relative; background: var(--bg); }

/* Шапка чата: на десктопе — название слева; на телефоне — аппбар
   (гамбургер · центр: название + модель · новый чат), иконки прячутся ниже 640px */
.chat-head { display: flex; align-items: center; gap: 8px; padding: 12px 20px 8px; }
.chat-head-actions { display: contents; }
.icon-btn {
  display: none; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: none;
  background: none; border: none; border-radius: 12px;
  color: var(--text); cursor: pointer; padding: 0;
}
/* :active — на тап (тач/мышь). :hover — ТОЛЬКО реальный курсор: на iOS Safari
   :hover «залипает» после touchend и оставляет тёмный прямоугольник вокруг кнопки. */
.icon-btn:active { background: var(--surface-2); }
@media (hover: hover) and (pointer: fine) {
  .icon-btn:hover { background: var(--surface-2); }
}
/* Кнопка «Избранное» — всегда видна (не зависит от breakpoint'а .icon-btn), стоит
   слева от названия треда, а не в кучке действий справа.
   Рефлексию пользователь вернул отдельно 11.08 — та же логика видимости, что у избранного. */
.fav-btn, .refl-btn { display: flex; }
/* Избранное активно — акцентный цвет, чтобы читалось с одного взгляда на шапку */
#pause-top.on { color: var(--accent); }
/* Рефлексия «зажата»: с клика и пока ход идёт, не только на миг тача. Снимается, когда ответ пришёл (см. reflectFlow). */
#reflect-top.pressed { background: var(--surface-2); color: var(--accent); }
/* Уведомления включены — тот же акцентный сигнал, что у паузы */
#push.on { color: var(--accent); }
.chat-head-mid { display: flex; align-items: baseline; gap: 12px; flex: 1; min-width: 0; }
/* Заголовок треда — полновесный, а не «обрезочек»: крупнее, жирнее,
   полным цветом текста. Ellipsis оставляем — длинное имя всё равно не рвёт шапку. */
.chat-title { font-weight: 700; font-family: var(--font-serif-alt); font-size: 21px; color: var(--text); letter-spacing: -.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-status { color: var(--dim); font-size: 13px; white-space: nowrap; }
.chat-status.status-offline { color: var(--err); font-weight: 600; }

.feed { flex: 1; overflow-y: auto; min-height: 0; }
/* Лента на всю ширину плашки (как в редакторе кода), а не центрированная колонка
   760px. Края ленты и композера совпадают (одинаковый горизонтальный padding). */
.feed-inner { max-width: none; margin: 0; padding: 8px 28px 24px; }
/* Хвост-распорка: пустое место снизу, чтобы вопрос текущего хода мог встать к верху
   экрана (claude.ai-стиль). Высоту задаёт JS (sizeTail); по умолчанию 0. */
.feed-tail { height: 0; flex: none; pointer-events: none; }

/* Сообщение-джоб: вопрос пользователя (карточка) → работа (свёртка) → ответ (документ) */
/* content-visibility:auto убран 20.07 — он был КОРНЕМ «улёта к предыдущему ответу» на
   iPhone Safari. Тред открывается внизу; ходы выше экрана никогда не были видимы, у них
   стоит заглушка 160px. При скролле ВВЕРХ каждый такой .msg реифицируется из заглушки в
   реальную высоту, добавляет высоту НАД вьюпортом, а scroll-anchoring Safari это не
   компенсирует → прокрутку перекидывает через пару ответов. pinMsgHeights лечил только
   уже просмотренные ходы, но не первый проход по истории — а это и есть момент бага.
   Рендерим все ходы целиком (как claude.ai) — геометрия стабильна, скролл не прыгает.
   Если ОЧЕНЬ длинный тред начнёт лагать — вернуть ленивую отрисовку точечно (виртуализация
   с реальными зарезервированными высотами), но НЕ через content-visibility. */
/* Докрутка вверх: лента отдаётся окнами, выше первой строки — вход в старую историю */
.load-older {
  display: block; margin: 0 auto 22px; padding: 7px 16px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--dim);
  font: inherit; font-size: 13px; cursor: pointer;
}
.load-older:hover { color: var(--text); }
.load-older:disabled { cursor: default; opacity: .6; }
.msg { margin-bottom: 26px; }
.msg-user { display: flex; justify-content: flex-end; margin-bottom: 10px; }
.msg-user-inner {
  background: var(--user-bubble); border-radius: 18px 18px 6px 18px;
  padding: 10px 14px; max-width: 85%;
  white-space: pre-wrap; word-break: break-word; font-size: 15px;
  /* Белый пузырь на светлом --bg сливается без тени — лёгкая тень отделяет его от полотна. */
  box-shadow: 0 1px 2px rgba(31, 30, 29, .05), 0 3px 12px rgba(31, 30, 29, .07);
}
.msg-time { color: var(--dim); font-size: 11px; margin-top: 4px; text-align: right; }

/* Свёртка работы («Работал N мин» + шаги) */
.work { margin: 8px 0; }
.work summary {
  list-style: none; cursor: pointer; color: var(--dim); font-size: 13px;
  display: flex; align-items: center; gap: 8px; user-select: none;
  /* одна строка: живой шаг может быть длинным — nowrap держит высоту постоянной
     (иначе перенос на 2 строки менял высоту и дёргал скролл) */
  white-space: nowrap; overflow: hidden;
}
.work summary::-webkit-details-marker { display: none; }
/* Шеврон после текста, как у DeepSeek: «Работал 43 с ⌄» */
.work summary::after { content: "⌄"; transition: transform .15s; line-height: 1; margin-top: -4px; }
.work[open] summary::after { transform: rotate(180deg); margin-top: 2px; }
.work-steps { margin: 8px 0 0 14px; border-left: 2px solid var(--border); padding-left: 12px; }
/* Ход, заведённый расписанием (таймер/оркестратор), а не пользователем: служебная плашка
   вместо пузыря пользователя — чтобы машинная инструкция не выглядела его репликой. */
.auto-ask { margin: 0 0 10px; }
.auto-ask-text {
  margin: 8px 0 0 14px; border-left: 2px solid var(--border); padding-left: 12px;
  color: var(--dim); font-size: 13px; white-space: pre-wrap; word-break: break-word;
}
.step { color: var(--dim); font-size: 13px; padding: 2px 0; display: flex; gap: 8px; }
.step-t { color: var(--border); font-size: 11px; flex: 0 0 34px; padding-top: 2px; }
/* Спиннер «думаю» = маленькая клякса (klyaksa-small.svg): анимированный вектор,
   дырки прозрачны (видят фон в любой теме). Пара к большой кляксе приветствия —
   НЕ путать: тут маленькая версия. Анимация внутри самого SVG (SMIL). */
.spinner {
  display: inline-block; width: 32px; height: 32px; vertical-align: -3px;
  flex: none;
}

/* Ответ — «как документ», антиква (фирменный приём claude.ai) */
.answer { font-size: 16px; line-height: 1.65; word-break: break-word; font-family: var(--font-serif); }
.answer > * + * { margin-top: 12px; }
.answer h1, .answer h2, .answer h3 { font-size: 1.15em; font-weight: 600; margin-top: 20px; }
/* 1.9em, не 22px: content-visibility на .msg включает paint-containment и режет
   всё, что маркер списка высунул за границу — «4.» / «10.» обрезались слева */
.answer ul, .answer ol { padding-left: 1.9em; }
.answer li + li { margin-top: 4px; }
.answer blockquote { border-left: 3px solid var(--border); padding-left: 12px; color: var(--dim); }
.answer a { color: var(--accent); }
.answer code { background: var(--surface-2); border-radius: 5px; padding: 1px 6px; font-size: .85em; font-family: ui-monospace, Menlo, monospace; }
/* Инлайн-код (путь/ключ/команда посреди текста) — ярлычок-иконка сбоку копирует
   его в буфер; сам код визуально остаётся текстом, не ссылкой (это не переход). */
.answer .inline-copy-chip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 23px; height: 23px; margin: 0 2px; padding: 0; vertical-align: -6px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px;
  color: var(--code-dim); cursor: pointer;
}
.answer .inline-copy-chip svg { width: 13px; height: 13px; }
.answer .inline-copy-chip:hover { background: var(--code-bg); border-color: var(--accent); color: var(--code-fg); }
.answer .inline-copy-chip.inline-copy--done { color: var(--accent); }
.answer .inline-copy-chip.inline-copy--err { color: #d33; }
.answer pre { background: var(--code-bg); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; overflow-x: auto; }
.answer pre code { background: none; padding: 0; font-size: 13px; line-height: 1.5; }
/* Карточка кода с шапкой (язык + «Копировать») — как в claude.ai */
.answer .codeblock { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin: 12px 0; }
.answer .codeblock pre { border: none; border-radius: 0; margin: 0; }
.answer .codebar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 10px 6px 14px; background: var(--code-bg);
  border-bottom: 1px solid var(--code-border);
  color: var(--code-dim); font-size: 12px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.answer .codebar-lang { letter-spacing: .02em; }
.answer .codebar-copy {
  display: flex; align-items: center; gap: 5px;
  background: none; border: none; color: var(--code-dim); cursor: pointer;
  font-size: 12px; padding: 3px 7px; border-radius: 7px;
  font-family: inherit; transition: color .15s, background .15s;
}
.answer .codebar-copy:hover { color: var(--code-fg); background: rgba(128,128,128,.16); }
.answer .codebar-copy svg { width: 13px; height: 13px; }

/* Карточка ПРОМТА (```prompt) — то, что пользователь уносит в другую сессию. Отдельный,
   заметный вид с ОДНОЙ акцентной кнопкой «Копировать промт»; тело — читаемый текст с
   переносом по словам, не моно-код. Не путать с обычным блоком кода. */
.answer .promptblock { border: 1px solid var(--accent); border-radius: 12px; overflow: hidden; margin: 14px 0; }
.answer .promptbar {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 8px 7px 14px; background: var(--accent);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.answer .promptbar-label { color: #fff; font-size: 11px; letter-spacing: .06em; font-weight: 700; text-transform: uppercase; }
.answer .prompt-copy {
  display: flex; align-items: center; gap: 6px; flex: none;
  background: rgba(255,255,255,.2); border: none; color: #fff; cursor: pointer;
  font-size: 13px; padding: 6px 13px; border-radius: 9px; font-family: inherit; font-weight: 600;
  transition: background .15s;
}
.answer .prompt-copy:hover { background: rgba(255,255,255,.32); }
.answer .prompt-copy:active { background: rgba(255,255,255,.4); }
.answer .prompt-copy svg { width: 14px; height: 14px; }
.answer .prompt-body {
  padding: 12px 14px; background: var(--surface); color: var(--text);
  font-size: 14px; line-height: 1.55; white-space: pre-wrap; word-break: break-word;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
/* ── Таблицы как в claude.ai ────────────────────────────────────────────
   ГЛАВНОЕ против «слова по буквам вертикально»: таблица берёт НАТУРАЛЬНУЮ ширину
   контента (width:max-content) и при нехватке места скроллит по горизонтали внутри
   .tblwrap, а НЕ давит колонки до 1 символа. Ячейки по умолчанию nowrap (слова целиком);
   длинные текстовые ячейки JS помечает .cell-wrap → перенос ПО СЛОВАМ в рамке 16–42ch,
   но никогда по буквам. Числовые колонки (JS .num) — вправо + tabular-nums. */
.tblwrap {
  position: relative; overflow-x: auto; -webkit-overflow-scrolling: touch;
  margin: 14px 0; border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface);
  /* Рамка ОБНИМАЕТ таблицу: по узкой таблице — по содержимому (без пустоты справа
     внутри рамки), по широкой — упор в 100% и горизонтальный скролл. */
  width: max-content; max-width: 100%;
}
/* Тени-подсказки «есть куда скроллить» — JS вешает .edge-l/.edge-r по scrollLeft */
.tblwrap::before, .tblwrap::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 24px; z-index: 2;
  pointer-events: none; opacity: 0; transition: opacity .15s;
}
.tblwrap::before { left: 0; border-radius: 12px 0 0 12px; background: linear-gradient(to right, rgba(0,0,0,.13), transparent); }
.tblwrap::after  { right: 0; border-radius: 0 12px 12px 0; background: linear-gradient(to left,  rgba(0,0,0,.13), transparent); }
.tblwrap.edge-l::before { opacity: 1; }
.tblwrap.edge-r::after  { opacity: 1; }
.answer table {
  border-collapse: collapse; width: max-content; max-width: 100%;
  font-size: 14px; line-height: 1.45; background: var(--surface);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.answer th, .answer td {
  padding: 9px 15px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--border);
  white-space: nowrap; word-break: normal; overflow-wrap: normal;   /* слова целиком, не по буквам */
}
.answer thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface-2); color: var(--text); font-weight: 600;
  border-bottom: 1.5px solid var(--border); letter-spacing: .01em;
}
.answer tbody tr:nth-child(even) td { background: color-mix(in srgb, var(--surface-2) 42%, transparent); }
.answer tbody tr:hover td { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.answer tbody tr:last-child td { border-bottom: none; }
.answer td.cell-wrap, .answer th.cell-wrap {
  white-space: normal; overflow-wrap: anywhere; word-break: normal;
  min-width: 16ch; max-width: 42ch;
}
.answer td.num, .answer th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── Callout-плашки ( > [!WARNING] / [!NOTE] / [!TIP] / [!IMPORTANT] / [!CAUTION] ) ──
   Цвет задаётся одним токеном --cal-fg, фон/рамка выводятся из него через color-mix —
   поэтому автоматически подхватывают тёмную тему. */
.answer .callout {
  margin: 14px 0; padding: 12px 15px; border-radius: 11px;
  border: 1px solid color-mix(in srgb, var(--cal-fg) 32%, transparent);
  background: color-mix(in srgb, var(--cal-fg) 9%, var(--surface));
  display: grid; grid-template-columns: auto 1fr; column-gap: 11px; row-gap: 3px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 14.5px; line-height: 1.55;
}
.answer .callout-ico { grid-row: 1 / span 2; display: flex; color: var(--cal-fg); padding-top: 1px; }
.answer .callout-ico svg { width: 18px; height: 18px; }
.answer .callout-title { align-self: center; font-weight: 700; font-size: 12.5px; letter-spacing: .04em; text-transform: uppercase; color: var(--cal-fg); }
.answer .callout-body { grid-column: 2; color: var(--text); }
.answer .callout-body > :first-child { margin-top: 0; }
.answer .callout-body > :last-child { margin-bottom: 0; }
.answer .callout-note      { --cal-fg: #4a8fd0; }
.answer .callout-tip       { --cal-fg: #4a9e63; }
.answer .callout-important { --cal-fg: var(--accent); }
.answer .callout-warning   { --cal-fg: #cf8a2c; }
.answer .callout-caution   { --cal-fg: #c8564b; }

/* ── GFM task-list чекбоксы ── */
.answer li:has(> input[type=checkbox]) {
  list-style: none; margin-left: -1.4em; padding: 6px 0; border-bottom: 1px solid var(--border);
}
.answer li:has(> input[type=checkbox]):first-child { border-top: 1px solid var(--border); }
.answer input[type=checkbox] {
  appearance: none; -webkit-appearance: none; width: 16px; height: 16px; margin: 0 8px 0 0;
  border: 1.5px solid var(--border); border-radius: 5px; background: var(--surface);
  position: relative; vertical-align: -3px; cursor: default;
}
/* Чек-лист (plans/2026-09-17-inline-checklist.md): у сообщений с job.checklist эти
   чекбоксы приходят живыми (disabled снят в app.js) — курсор честно обещает клик. */
.answer input[type=checkbox]:not(:disabled) { cursor: pointer; }
.answer input[type=checkbox]:checked { background: var(--accent); border-color: var(--accent); }
.answer input[type=checkbox]:checked::after {
  content: ""; position: absolute; left: 4.5px; top: 1px; width: 4px; height: 8px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
}

/* ── Mermaid-диаграммы ── */
.answer .mermaid-block {
  margin: 14px 0; padding: 14px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface); overflow-x: auto; text-align: center;
}
.answer .mermaid-block svg { max-width: 100%; height: auto; }
.answer .mermaid-loading { color: var(--dim); font-size: 13px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }

/* ── Полировка типографики ── */
.answer a { text-decoration: underline; text-underline-offset: 2px; text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.answer a:hover { text-decoration-color: var(--accent); }
.answer hr { border: none; border-top: 1px solid var(--border); margin: 22px 0; }
.answer-failed { color: var(--err); background: var(--err-bg); border-radius: 10px; padding: 10px 14px; font-size: 14px; }
.answer-canceled { color: var(--dim); font-size: 14px; padding: 2px 0; }

/* ── Пульс: карточка фоновой работы (vanilla-JS, см. parsePulse/pulseCardNode в app.js). Без контура
   (правило «тень без контура») — карточка отделяется поверхностью и мягкой
   тенью в цвет дела; терракота в работе и в готово, красный — только перебор
   плана. */
.pulse-card {
  border-radius: 12px; padding: 6px 12px 5px; margin: 4px 0 3px;
  background: var(--surface);
  box-shadow: 0 2px 10px -3px color-mix(in srgb, var(--accent) 40%, transparent);
}
.pulse-card--over { box-shadow: 0 2px 10px -3px color-mix(in srgb, var(--err) 45%, transparent); }
.pulse-card--stale { opacity: .75; }
.pulse-head { display: flex; align-items: center; gap: 5px; }
.pulse-mark { font-size: 12px; line-height: 1; }
.pulse-task { flex: 1; font-size: 12.5px; font-weight: 600; line-height: 16px; color: var(--text); }
.pulse-card--stale .pulse-task { color: var(--dim); }
.pulse-pct { font-size: 13px; font-weight: 800; letter-spacing: .2px; color: var(--accent); font-variant-numeric: tabular-nums; }
.pulse-card--over .pulse-pct { color: var(--err); }
.pulse-tube { height: 4px; border-radius: 2px; margin-top: 4px; overflow: hidden; background: var(--bg); position: relative; }
.pulse-fill { position: absolute; left: 0; top: 0; bottom: 0; right: auto; border-radius: 2px; min-width: 4px; background: var(--accent); }
.pulse-card--over .pulse-fill { background: var(--err); }
.pulse-fill--full { right: 0; }
.pulse-fill--stale-dim { opacity: .25; }
.pulse-fill--breathe { animation: pulse-card-breathe 1.8s ease-in-out infinite; }
@keyframes pulse-card-breathe { 0%, 100% { opacity: .72; } 50% { opacity: 1; } }
.pulse-ticks { display: flex; justify-content: space-between; margin-top: 2px; padding: 0 1px; }
.pulse-tick { font-size: 8.5px; color: var(--dim); font-variant-numeric: tabular-nums; }
.pulse-foot { font-size: 9.5px; line-height: 12px; margin-top: 2px; color: var(--dim); }

/* Действия под готовым ответом — лёгкие серые иконки, как у DeepSeek */
.msg-actions { display: flex; gap: 4px; margin-top: 10px; }
.msg-actions button {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0;
  background: none; border: none; border-radius: 8px;
  color: var(--dim); cursor: pointer; font-size: 14px;
}
.msg-actions button:hover { background: var(--surface-2); color: var(--text); }

.to-bottom {
  position: absolute; right: 24px; bottom: 128px;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--surface); color: var(--text); border: 1px solid var(--border); cursor: pointer; font-size: 16px;
  box-shadow: var(--shadow);
}

/* Рельс-навигация по ходам (как в приложении/DeepSeek): чёрточка на каждый вопрос,
   тап — прыжок к нему, ховер — превью. «Рыбий глаз»: свежие ходы шире и ярче, старые
   сжаты и бледнее. Активный ход — акцентная удлинённая чёрточка. */
.rail {
  position: absolute; right: 4px; top: 56px; bottom: 120px; z-index: 12;
  display: flex; flex-direction: column; align-items: flex-end; justify-content: center; gap: 0;
  pointer-events: none;   /* сами хиты кликабельны, фон — сквозной */
}
.rail.hidden { display: none; }
.tick-hit {
  pointer-events: auto; cursor: pointer; position: relative;
  padding: 7px 6px 7px 18px; display: flex; align-items: center; justify-content: flex-end;
  -webkit-tap-highlight-color: transparent;
}
.tick-hit.compact { padding-top: 2px; padding-bottom: 2px; }
.tick { width: 13px; height: 2px; border-radius: 2px; background: var(--dim); opacity: .5; transition: width .12s ease, opacity .12s ease, background .12s ease; }
.tick-hit.compact .tick { width: 8px; opacity: .3; }
.tick.active { width: 22px; height: 2.5px; background: var(--accent, var(--text)); opacity: 1; }
.rail-tip {
  position: absolute; right: 26px; top: 50%; transform: translateY(-50%);
  max-width: 240px; width: max-content; padding: 7px 10px; border-radius: 10px;
  background: var(--surface); color: var(--text); border: 1px solid var(--border);
  box-shadow: var(--shadow); font-size: 12px; line-height: 1.35;
  display: none; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  pointer-events: none; white-space: normal; z-index: 1;
}
/* Превью хода — ТОЛЬКО на устройствах с настоящим hover-указателем (мышь/трекпад).
   На iPhone `:hover` НЕ отсутствует, а срабатывает по тапу и ЗАЛИПАЕТ до тапа в другое
   место — из-за этого подсказка «висела» поверх экрана. Гейт
   (hover: hover)+(pointer: fine) отсекает тач: на телефоне превью не показывается вовсе,
   тап по чёрточке сразу прыгает к ходу. Ховер-подсветку самой чёрточки тоже убрали —
   она залипала так же. */
@media (hover: hover) and (pointer: fine) {
  .tick-hit:hover .rail-tip { display: -webkit-box; }
  .tick-hit:hover .tick { opacity: .9; }
}
/* На узком экране рельс ближе к краю и чуть выше композера */
@media (max-width: 640px) { .rail { right: 2px; bottom: 150px; } }

/* Композер-карточка (стилистика DeepSeek): поле сверху, под ним ряд инструментов.
   Без обводки — контур формирует тень; высота поля
   только авторастёт по тексту, ручного грипа больше нет. */
/* Ответ — во всю ширину ленты, а поле ввода — узкое и по центру. На телефоне экран < 768 → и так во всю ширину. */
.composer { display: flex; flex-direction: column; gap: 8px; padding: 10px 28px 16px; max-width: 768px; margin: 0 auto; width: 100%; }
/* ── «Два слоя» (гейт на 1 тред, класс .exp-float на .chat): поле ввода — плавающая карточка со
   СВОЕЙ тенью ПОВЕРХ ленты, а лента листается под ним до низа экрана (не режется на границе).
   БЕЗ blur (он в прошлый раз дал уродливую полоску + затемнение меню attach) — просто непрозрачная
   карточка над лентой на всю высоту. Нижний отступ ленты = высота композера (--composer-h из JS).
   Пин-математику двигает JS (bottomInset). Раскатка = снять гейт; откат = убрать класс. */
/* БЕЗ z-index у композера: он позиционирован → и так рисуется над статичной лентой, но НЕ
   становится stacking-context — иначе меню ＋ (position:fixed ВНУТРИ композера) запирается под
   .plus-backdrop (z55, на телефоне тёмная шторка) и attach недоступен. */
.chat.exp-float .composer { position: absolute; left: 0; right: 0; bottom: 0; background: transparent; }
.chat.exp-float .feed-inner { padding-bottom: calc(var(--composer-h, 92px) + 8px); }
/* Поле «парит» заметнее — усиленная тень карточки в режиме двух слоёв */
.chat.exp-float .composer-card { box-shadow: 0 2px 6px rgba(31, 30, 29, .12), 0 12px 34px rgba(31, 30, 29, .22); }
:root[data-theme="dark"] .chat.exp-float .composer-card { box-shadow: 0 0 0 1px rgba(255, 255, 255, .07), 0 14px 38px rgba(0, 0, 0, .58); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) .chat.exp-float .composer-card {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .07), 0 14px 38px rgba(0, 0, 0, .58);
  }
}
/* Кнопка «вниз» — над плавающей карточкой (z выше композера, отступ по высоте карточки) */
.chat.exp-float .to-bottom { z-index: 25; bottom: calc(var(--composer-h, 92px) + 16px); }
.composer-card {
  display: flex; flex-direction: column; gap: 4px;
  background: var(--surface); border: none; border-radius: 28px;
  padding: 14px 12px 8px 18px;
  box-shadow: 0 1px 2px rgba(31, 30, 29, .06), 0 4px 20px rgba(31, 30, 29, .10);
  transition: box-shadow .2s ease;
}
.composer-card:focus-within { box-shadow: 0 2px 4px rgba(31, 30, 29, .07), 0 8px 30px rgba(31, 30, 29, .14); }
/* В тёмной теме тень на тёмном не видна — карточку отделяет светлая поверхность + лёгкий контур свечением */
:root[data-theme="dark"] .composer-card { box-shadow: 0 0 0 1px rgba(255, 255, 255, .06), 0 8px 28px rgba(0, 0, 0, .45); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) .composer-card {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .06), 0 8px 28px rgba(0, 0, 0, .45);
  }
}
.composer-card.dragover { outline: 2px dashed var(--accent); outline-offset: -2px; }
.composer textarea {
  width: 100%; resize: none; background: none; border: none; outline: none;
  color: var(--text);
  /* 16px минимум: при меньшем iOS Safari сам зумит страницу на фокусе */
  font-size: 16px; font-family: inherit; line-height: 1.45;
  /* dvh, не vh: на iOS 70vh меряется без адресной строки и вылезает за реальный экран */
  padding: 2px 4px 6px 0; min-height: 26px; max-height: 70vh; max-height: 70dvh;
}
.composer textarea::placeholder { color: var(--dim); }
.composer-tools { display: flex; align-items: center; gap: 8px; }
.composer-tools .spacer { flex: 1; }
/* Инструменты композера — «голые» иконки без рамок-кружков, как у claude.ai */
.tool-btn {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none; padding: 0;
  border-radius: 10px; background: none; border: none;
  color: var(--dim); font-size: 19px; line-height: 1; cursor: pointer;
}
.tool-btn:hover { background: var(--surface-2); color: var(--text); }
.tool-btn:disabled { opacity: .4; cursor: default; }
/* Выбор модели — текстом «Opus 4.8 ⌄» (как у claude.ai), подсветка при ховере/открытом меню */
.model-wrap { position: relative; flex: none; min-width: 0; }
.model-btn {
  display: flex; align-items: center; gap: 4px;
  height: 32px; padding: 0 8px; max-width: 42vw;
  border-radius: 8px; background: none; border: none;
  color: var(--dim); font-size: 13.5px; cursor: pointer;
}
.model-btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.model-btn svg { flex: none; opacity: .7; }
.model-btn:hover, .model-btn.open { background: var(--surface-2); color: var(--text); }
/* Индикатор контекста сессии — мини-кольцо + %, в духе claude.ai. Слева от пилюли модели.
   Наполнение = токены последнего хода (вход+кеш) / окно 200k. Скрыт, пока нет замера. */
.ctx-meter {
  display: flex; align-items: center; gap: 5px; flex: none;
  height: 32px; padding: 0 7px 0 5px; border: none; background: none;
  color: var(--dim); font-size: 12px; cursor: pointer; border-radius: 8px;
}
.ctx-meter.hidden { display: none; }
.ctx-meter:hover { background: var(--surface-2); color: var(--text); }
.ctx-ring { flex: none; }
.ctx-track { stroke: var(--border); }
.ctx-arc { stroke: var(--accent); transition: stroke-dashoffset .45s ease, stroke .35s ease; }
.ctx-meter.unmeasured .ctx-arc { stroke: var(--border); }
.ctx-meter.unmeasured .ctx-pct { color: var(--dim); }
/* Зоны заполнения — «тревожность» холодно→горячо (пороги в ctxZone: 50/75/90) */
.ctx-meter.calm  .ctx-arc { stroke: #6f8f6a; } .ctx-meter.calm  .ctx-pct { color: #6f8f6a; }
.ctx-meter.work  .ctx-arc { stroke: var(--accent); } .ctx-meter.work  .ctx-pct { color: var(--accent); }
.ctx-meter.tight .ctx-arc { stroke: #e2a03f; } .ctx-meter.tight .ctx-pct { color: #b97a1e; }
.ctx-meter.edge  .ctx-arc { stroke: #d64545; } .ctx-meter.edge  .ctx-pct { color: #d64545; font-weight: 700; }
.ctx-meter.edge  { animation: ctxPulse 1.6s ease-in-out infinite; }
@keyframes ctxPulse { 0%,100% { box-shadow: 0 0 0 0 rgba(214,69,69,0); } 50% { box-shadow: 0 0 0 3px rgba(214,69,69,.30); } }
/* Размер кольца = «бак» пресета: больше окно — крупнее датчик */
.ctx-meter.p-small .ctx-ring { width: 16px; height: 16px; }
.ctx-meter.p-mid   .ctx-ring { width: 19px; height: 19px; }
.ctx-meter.p-max   .ctx-ring { width: 22px; height: 22px; }
.ctx-pct { font-variant-numeric: tabular-nums; }
.ctx-scale { font-size: 10px; color: var(--dim); letter-spacing: .2px; }
/* Кнопка отправки — терракотовый скруглённый квадрат claude.ai; при пустом поле скрыта (JS) */
.send-btn {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none; padding: 0;
  border-radius: 12px; border: none;
  background: var(--accent); color: var(--accent-ink); cursor: pointer;
}
.send-btn:hover { background: var(--accent-hover); }
.send-btn:disabled { opacity: .4; cursor: default; }
/* Кнопка «стоп» — терракотовый скруглённый квадрат (в цвет send-btn), чтобы не спорить
   визуально с остальным акцентным цветом интерфейса; стоит на месте «отправить»,
   пока движок работает */
.stop-btn {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none; padding: 0;
  border-radius: 12px; border: none;
  background: var(--accent); color: var(--accent-ink); cursor: pointer;
  transition: background .12s;
}
.stop-btn:hover { background: var(--accent-hover); }
.stop-btn:disabled { opacity: .4; cursor: default; }
/* ─────────────── Голосовая запись «как в Telegram» ───────────────
   Зажал — кнопка растёт и краснеет, поле ввода сменяется панелью с живой волной голоса.
   Повёл палец вверх — «замок» подтягивается и защёлкивается, кнопка морфится в большой ■. */
.mic-wrap { position: relative; display: flex; align-items: center; flex: none; }
.composer .mic-btn {
  touch-action: none; -webkit-user-select: none; user-select: none;
  transition: transform .18s cubic-bezier(.2,.8,.25,1), width .2s ease, height .2s ease,
              background .18s ease, color .18s ease, border-radius .2s ease;
}
/* Зажато: кнопка подросла и покраснела, мягкий пульс-ореол */
.composer .mic-btn.rec {
  background: var(--err-bg); color: var(--err); transform: scale(1.18);
  box-shadow: 0 0 0 0 rgba(217,119,87,.45); animation: mic-halo 1.4s ease-out infinite;
}
/* Закреплено (без рук): большая круглая красная кнопка-СТОП — крупная цель под палец */
.composer .mic-btn.locked {
  width: 52px; height: 52px; border-radius: 50%;
  background: var(--accent); color: var(--accent-ink); transform: none;
  box-shadow: 0 4px 16px rgba(217,119,87,.5), 0 0 0 0 rgba(217,119,87,.4);
  animation: mic-halo 1.8s ease-out infinite;
}
.composer .mic-btn.locked:hover { background: var(--accent-hover); }
@keyframes mic-halo {
  0%   { box-shadow: 0 0 0 0 rgba(217,119,87,.45); }
  70%  { box-shadow: 0 0 0 12px rgba(217,119,87,0); }
  100% { box-shadow: 0 0 0 0 rgba(217,119,87,0); }
}
/* Во время записи прячем лишние иконки — остаётся только большая кнопка + панель (чисто, как в телеге).
   #send/#stop тоже сюда: раньше при непустых вложениях (has=true) send-btn оставался виден рядом
   с разросшейся до 52px круглой mic-btn.locked — тесно, тени наезжали друг на друга. Во время записи отправлять/стопать генерацию нечем — прячем как остальное. */
.composer-card.recording #attach,
.composer-card.recording #ctx-meter,
.composer-card.recording .model-wrap,
.composer-card.recording #send,
.composer-card.recording #stop { display: none; }

/* Панель записи на месте поля ввода */
.rec-bar {
  display: flex; align-items: center; gap: 10px; min-height: 30px;
  padding: 3px 4px 6px 2px; animation: rec-in .18s ease both;
}
.rec-bar.hidden { display: none; }
@keyframes rec-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.rec-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--err); flex: none; animation: rec-blink 1s steps(1,end) infinite; }
@keyframes rec-blink { 50% { opacity: .25; } }
.rec-time { font-variant-numeric: tabular-nums; font-size: 14px; color: var(--text); font-weight: 600; flex: none; min-width: 34px; }
/* Живая волна: столбики, высоту гонит анализатор аудио (реагирует на голос) */
.rec-wave { display: flex; align-items: center; gap: 3px; height: 30px; flex: none; }
.rec-wave i {
  width: 3px; height: 26px; border-radius: 3px; background: var(--accent);
  transform: scaleY(.28); transform-origin: center; transition: transform .07s linear;
}
.rec-wave.css-anim i { animation: rec-bar 1s ease-in-out infinite; }
.rec-wave.css-anim i:nth-child(2n) { animation-delay: .15s; }
.rec-wave.css-anim i:nth-child(3n) { animation-delay: .3s; }
@keyframes rec-bar { 0%,100% { transform: scaleY(.3); } 50% { transform: scaleY(1); } }
.rec-hint { font-size: 12px; color: var(--dim); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rec-cancel {
  flex: none; width: 32px; height: 32px; border-radius: 50%; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); border: none; color: var(--dim); cursor: pointer;
}
.rec-cancel.hidden { display: none; }
.rec-cancel:hover { color: var(--err); background: var(--err-bg); }
.rec-bar.busy .rec-wave, .rec-bar.busy .rec-time { opacity: .4; }

/* «Замок» над кнопкой — подсказка «веди вверх, чтобы закрепить». Подтягивается по --p (0..1). */
.rec-lock {
  position: absolute; left: 50%; bottom: calc(100% + 8px); --p: 0;
  transform: translateX(-50%) translateY(calc(6px - var(--p) * 6px));
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  width: 34px; padding: 7px 0 5px; border-radius: 17px;
  background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);
  color: var(--dim); z-index: 20; pointer-events: none;
  opacity: calc(.55 + var(--p) * .45);
}
.rec-lock.hidden { display: none; }
.rec-lock .lk-closed { display: none; }
.rec-lock .lock-chev { color: var(--dim); opacity: calc(1 - var(--p)); height: 12px; animation: chev-up 1.1s ease-in-out infinite; }
@keyframes chev-up { 0%,100% { transform: translateY(1px); } 50% { transform: translateY(-2px); } }
/* Защёлкнулся: терракота, закрытый замок, стрелка гаснет, лёгкий «щелчок»-скейл */
.rec-lock.armed {
  color: var(--accent-ink); background: var(--accent); border-color: var(--accent);
  transform: translateX(-50%) translateY(0) scale(1.08);
}
.rec-lock.armed .lk-open { display: none; }
.rec-lock.armed .lk-closed { display: block; }
.rec-lock.armed .lock-chev { display: none; }

/* Меню ＋ (файлы, статус подписки, модель). ВАЖНО: селекторы с .composer —
   историческая грабля: раньше их перебивал блочный .composer button. */
.plus-wrap { position: relative; flex: none; }
.plus-menu {
  position: absolute; bottom: 46px; left: 0; width: 300px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 6px; z-index: 60; box-shadow: 0 12px 36px rgba(0, 0, 0, .18);
  max-height: min(520px, calc(100dvh - 100px)); overflow-y: auto;
}
.plus-menu.hidden { display: none; }
/* Меню слэш-команд — поповер над композером слева, стиль как у плюс-меню.
   Список короткий, поэтому (в отличие от плюс-меню) НЕ превращаем в нижнюю шторку. */
.slash-menu {
  position: absolute; bottom: 46px; left: 0; width: 300px; max-width: calc(100vw - 32px);
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 6px; z-index: 60; box-shadow: 0 12px 36px rgba(0, 0, 0, .18);
  max-height: min(340px, calc(100dvh - 120px)); overflow-y: auto;
}
.slash-menu.hidden { display: none; }
.composer .plus-item.slash-item.active { background: var(--surface-2); }
.slash-item .slash-cmd { font-weight: 600; }
.composer .plus-item {
  flex: none; width: 100%; height: auto; align-self: auto;
  display: flex; gap: 10px; align-items: center; text-align: left;
  background: none; color: var(--text); border: none; border-radius: 9px;
  padding: 7px 10px; font-size: 14px; font-family: inherit; cursor: pointer;
  transition: background .12s;
}
.composer .plus-item:hover { background: var(--surface-2); }
.composer .plus-item.checked { background: var(--surface-2); }
.composer .plus-item:disabled { opacity: 1; cursor: default; }
.composer .plus-item:disabled:hover { background: none; }
.plus-item .pi-txt { display: flex; flex-direction: column; gap: 1px; line-height: 1.3; min-width: 0; }
.plus-item .pi-name { font-weight: 560; }
.plus-item .pi-txt small { color: var(--dim); font-size: 11.5px; }
.plus-sep { height: 1px; background: var(--border); margin: 4px 6px; }
.plus-head { font-size: 12px; color: var(--dim); padding: 6px 10px 4px; }
/* Подменю «Модель треда» */
.composer .plus-head.model-head { display: flex; align-items: center; gap: 8px; padding: 6px 8px 4px; }
.composer .plus-back {
  flex: none; width: 28px; height: 28px; align-self: auto;
  background: none; color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; font-size: 16px; line-height: 1; cursor: pointer; padding: 0;
}
.composer .plus-back:hover { border-color: var(--accent); color: var(--accent); }
.usage-box { padding: 0 4px 4px; }
.usage-row { padding: 6px 8px 8px; }
.usage-top { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 5px; }
.usage-bar { position: relative; height: 4px; }
.usage-track { height: 100%; border-radius: 2px; background: var(--border); overflow: hidden; }
.usage-fill { height: 100%; background: var(--accent); }
.usage-row.warn .usage-fill { background: #e2a03f; }
.usage-row.crit .usage-fill { background: #d64545; }
.usage-row.warn .usage-top b { color: #b97a1e; }
.usage-row.crit .usage-top b { color: #d64545; }
.usage-note { color: var(--dim); font-size: 12px; padding: 2px 8px; }
/* Деления полоски = единицы окна (часы/дни), метка «сейчас» = сколько времени окна прошло */
.usage-dividers { position: absolute; inset: 0; display: flex; pointer-events: none; }
.usage-dividers span { flex: 1; border-right: 1.5px solid rgba(0, 0, 0, .5); }
.usage-dividers span:last-child { border-right: none; }
.usage-now {
  position: absolute; top: 0; bottom: 0; width: 2px;
  background: #f2f2f5; border-radius: 1px; transform: translateX(-1px);
}
.usage-note-row { display: flex; justify-content: space-between; color: var(--dim); font-size: 12px; padding: 2px 8px; gap: 8px; }
.usage-note-row span:last-child { white-space: nowrap; }
.plus-backdrop { position: fixed; inset: 0; z-index: 55; background: transparent; }
.plus-backdrop.hidden { display: none; }
/* Меню модели — поповер над кнопкой «Opus 4.8 ⌄» (правый край), как у claude.ai.
   Отдельный от .plus-menu: тот на телефоне становится нижней шторкой, этому нельзя. */
.model-menu {
  position: absolute; bottom: 40px; right: 0;
  width: 268px; max-width: calc(100vw - 32px);
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
  padding: 6px; z-index: 60; box-shadow: 0 14px 40px rgba(0, 0, 0, .16), 0 2px 8px rgba(0, 0, 0, .06);
  max-height: min(480px, calc(100dvh - 120px)); overflow-y: auto;
}
.model-menu.hidden { display: none; }
/* Галка текущей модели и шеврон подменю — справа, по центру строки (как у claude.ai) */
.mm-check, .mm-arrow {
  display: flex; align-items: center;
  margin-left: auto; flex: none; align-self: center; padding-left: 10px;
}
.mm-check { color: var(--accent); }
.mm-check svg { width: 15px; height: 15px; }
.mm-arrow { color: var(--dim); }
/* Заголовок группы (ChatGPT · OpenAI) — капс + тонкая верхняя черта вместо отдельного разделителя */
.mm-group {
  margin-top: 4px; padding: 8px 10px 4px; border-top: 1px solid var(--border);
  font-size: 10.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
}
/* Полоса вложений до отправки: миниатюры-чипы ВНУТРИ поля ввода, над текстом (как в claude.ai) */
.attach-strip { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-start; padding: 2px 0 8px; }
.attach-strip.hidden { display: none; }
.upload-wait { color: var(--dim); font-size: 12px; line-height: 1.35; padding: 0 2px 4px; }
.upload-wait.hidden { display: none; }
/* Черновик-чип — реальные пропорции (не квадрат-кроп): мини-версия того, что уйдёт
   в пузырь. Раньше object-fit:cover резал в квадрат — рассинхрон с отправленным (06.07). */
.attach-chip { position: relative; border-radius: 10px; overflow: hidden; border: 1px solid var(--border); box-shadow: var(--shadow); line-height: 0; }
.attach-chip img { max-width: 84px; max-height: 84px; width: auto; height: auto; display: block; }
.attach-chip.loading { opacity: .5; }
.composer .attach-chip .rm {
  position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; border-radius: 50%;
  background: rgba(0,0,0,.55); color: #fff; border: none; font-size: 12px; line-height: 20px;
  cursor: pointer; padding: 0; flex: none; align-self: auto;
}
/* Вложения внутри пузыря пользователя */
/* align-items:flex-start ОБЯЗАТЕЛЬНО: без него flex по умолчанию stretch тянет высоту
   более низкой (широкой) картинки под высоту соседней-портретной → та становится «квадратом».
   Это и был баг «иногда квадрат» при нескольких скринах в одном сообщении (06.07). */
.msg-user-imgs { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-start; margin-top: 8px; }
/* Отправленные скриншоты — реальные пропорции, компактно. Оригинал — по клику. */
.msg-user-imgs img { max-width: 120px; max-height: 120px; border-radius: 10px; border: 1px solid var(--border); display: block; cursor: zoom-in; }

/* Чип документа — в композере (черновик) и в пузыре пользователя (отправлено).
   В отличие от картинки, показываем имя файла текстом, не превью. */
.attach-chip-doc {
  line-height: normal; display: flex; align-items: center; gap: 6px;
  padding: 6px 26px 6px 10px; max-width: 180px; background: var(--surface);
}
.attach-chip-doc .doc-label {
  font-size: 12px; color: var(--text); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis;
}
/* Карточка видео в пузыре пользователя (11.08): кадр-миниатюра + ▶ + подпись «0:35 · 22 МБ». */
.msg-user-vids { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.vid-card { position: relative; display: inline-block; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--border); line-height: 0; }
.vid-card img { max-width: 150px; max-height: 170px; display: block; }
.vid-play { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 40px; height: 40px; border-radius: 50%; background: rgba(0,0,0,.45);
  color: #fff; font-size: 15px; display: flex; align-items: center; justify-content: center;
  padding-left: 3px; box-sizing: border-box; }
.vid-label { position: absolute; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,.5);
  color: #fff; font-size: 11px; line-height: 1.4; text-align: center; padding: 3px 6px; }

.msg-user-docs { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.doc-chip {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px;
  padding: 6px 10px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--surface-2); color: var(--text); max-width: 220px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.empty { text-align: center; margin-top: 24vh; padding: 0 24px; }
.empty-hello {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  font-family: var(--font-serif); font-weight: 500;
  font-size: clamp(28px, 5vw, 40px); color: var(--text); letter-spacing: .01em;
}
/* Подпись языка у экзотических приветствий — мельче и приглушённее,
   чтобы не спорить с крупным serif-приветствием */
/* Наклон — именно скошенные прямые буквы (не курсив). У serif-шрифта нет своего
   oblique-начертания, а браузер его не синтезирует, поэтому наклоняем сами через
   skewX — работает при любом шрифте. inline-block нужен, чтобы transform применился. */
.hello-hint { display: inline-block; transform: skewX(-11deg); transform-origin: bottom left; font-size: .38em; font-weight: 400; color: var(--dim); letter-spacing: 0; white-space: nowrap; }
/* Большая клякса — временный логотип Кабинета (klyaksa-big.svg), над приветствием */
.empty-blob { display: block; width: clamp(76px, 14vw, 104px); height: auto; margin: 0 auto 20px; }
.empty-sub { margin-top: 14px; color: var(--dim); font-size: 15px; font-family: var(--font-serif); }

/* ── Мобильная вёрстка (контракт 640px), стилистика DeepSeek:
   треды — шторка слева за гамбургером, шапка-аппбар с центром,
   композер-карточка на всю ширину. ── */
@media (max-width: 640px) {
  /* На тач-экране ховера нет — действия тредов держим видимыми */
  .thread-acts { opacity: 1; }
  /* Сайдбар превращается в шторку поверх чата */
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0;
    width: min(84vw, 320px); z-index: 70;
    transform: translateX(-105%);
    transition: transform .28s cubic-bezier(.32, .72, .28, 1);
    border-right: 1px solid var(--border);
  }
  .cabinet.drawer-open .sidebar { transform: none; }
  /* Шторка ОТОДВИГАЕТ чат (как у DeepSeek), а не накрывает: чат уезжает вправо
     и остаётся виден краем-карточкой; тап по нему (backdrop) закрывает шторку.
     transform задаём только в открытом состоянии — постоянный translateX(0)
     сделал бы .chat containing block'ом для fixed-детей (плюс-меню). */
  /* Чат — «плавающая плашка» над меню (модель DeepSeek): при открытии шторки
     съезжает вправо, приоткрывая меню, роняет на него мягкую тень слева и слегка
     сереет. Тень базово прозрачная — так box-shadow плавно проявляется. Никаких
     скруглений (давали «скошенные уголки») и никакого opacity на самом чате
     (полупрозрачная панель просвечивала фон → «плашки» сверху/снизу и серый след
     при закрытии). */
  .chat {
    transition: transform .28s cubic-bezier(.32, .72, .28, 1), box-shadow .28s ease;
    box-shadow: -10px 0 34px rgba(31, 30, 29, 0);
  }
  .cabinet.drawer-open .chat {
    transform: translateX(min(84vw, 320px));
    box-shadow: -10px 0 34px rgba(31, 30, 29, .24);
  }
  /* Налёт кладём НА саму плашку (::after точно по чату), а не скримом на весь экран —
     поэтому крем под статус-баром/панелью Safari не темнеет и полоски нет.
     ЦВЕТ налёта = сам фон (var(--bg), крем), НЕ тёмный: тёмный делал крем серым, а
     чёрный текст оставлял чёрным.
     Кремовый налёт поверх крема = крем не меняется; поверх текста = текст выцветает к
     фону, светлеет (эффект DeepSeek: фон тот же, текст блёкнет).
     z-index 40 — выше ленты/композера/кнопки-вниз, но ниже прозрачного бэкдропа (z65),
     который ловит тап для закрытия. */
  .chat::after {
    content: ""; position: absolute; inset: 0; z-index: 40;
    background: var(--bg); opacity: 0; pointer-events: none;
    transition: opacity .28s ease;
  }
  .cabinet.drawer-open .chat::after { opacity: .5; }
  /* Во время живого свайпа (см. app.js) налёт следует за пальцем через --drag-op,
     а не за классом drawer-open — иначе он либо не появляется, либо скачет сразу
     на .5 в момент, когда шторка ещё на середине пути. transition:none — здесь
     нужна 1:1 реакция на палец, а не .28s CSS-анимация, которая будет отставать. */
  .cabinet.dragging .chat::after { opacity: var(--drag-op, 0); transition: none; }
  /* Бэкдроп прозрачный: только ловит тап для закрытия шторки. Тёмного скрима на весь
     экран нет — он серил панель и затемнял крем под статус-баром Safari (полоски). */
  .drawer-backdrop {
    display: block; position: fixed; inset: 0; z-index: 65;
    background: transparent;
    pointer-events: none;
  }
  .cabinet.drawer-open .drawer-backdrop { pointer-events: auto; }

  /* Аппбар: одна строка — гамбургер и «+ новый чат» убраны с
     основного экрана (шторка открывается свайпом, «+ Новый чат» живёт внутри неё),
     остаётся только избранное слева от названия треда, как на десктопе. */
  #menu-btn, #new-thread-top { display: none; }
  /* В iOS верхняя безопасная зона не входит в обычный padding. Держим шапку
     ниже статус-бара и даём ей ещё воздуха, чтобы имя треда не липло к верху. */
  .chat-head { padding: max(16px, calc(env(safe-area-inset-top) + 8px)) 14px 10px; }
  .chat-title { max-width: 100%; font-size: 19px; }
  .chat-status { font-size: 12px; line-height: 1.25; }

  .feed-inner { padding: 6px 16px 18px; }
  .msg { margin-bottom: 30px; }
  .msg-user-inner { max-width: 88%; }
  .answer { line-height: 1.7; }

  .composer { padding: 0 10px calc(12px + env(safe-area-inset-bottom)); }
  .composer-card { border-radius: 26px; padding: 12px 10px 8px 16px; }
  .to-bottom { bottom: 136px; right: 16px; }

  /* Меню ＋ на телефоне — шторка снизу во всю ширину, фон затемняется */
  .plus-menu {
    position: fixed; left: 10px; right: 10px; bottom: 12px; width: auto;
    border-radius: 18px; padding: 10px;
  }
  .plus-backdrop { background: rgba(0, 0, 0, .35); }
}
/* #new-thread-top и #menu-btn на десктопе скрыты (.icon-btn{display:none}) — на
   десктопе есть смысловой дубль "+ Новый чат" в сайдбаре, а сайдбар всегда открыт
   (гамбургер не нужен). пауза/рефлексия/сжатие/rollover раньше жили здесь
   же кучкой справа — все, кроме избранного (.fav-btn), убраны. */
.modal-ok:disabled { opacity: .45; cursor: default; }
.modal-ok:disabled:hover { background: var(--accent); }

/* Картинки в ответах — миниатюры; клик открывает оригинал (вешается в renderMd) */
.answer img { display: block; max-width: min(100%, 360px); max-height: 300px; height: auto; border: 1px solid var(--border); border-radius: 12px; cursor: zoom-in; }

/* ── Модалка (замена браузерным prompt/confirm/alert), стилистика кабинета ── */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(31, 30, 29, .38);
}
.modal-backdrop.hidden { display: none; }
.modal {
  width: 100%; max-width: 400px;
  background: var(--bg); border: none; border-radius: 18px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .28);
  padding: 22px 22px 16px; animation: modal-in .16s ease;
}
@keyframes modal-in { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
.modal-title { font-family: var(--font-serif); font-weight: 600; font-size: 18px; color: var(--text); }
.modal-title:not(:empty) { margin-bottom: 12px; }
.modal-msg { color: var(--dim); font-size: 14px; line-height: 1.5; }
.modal-msg + .modal-input { margin-top: 12px; }
.modal-input {
  width: 100%; background: var(--surface); border: none; border-radius: 10px;
  color: var(--text); font-size: 15px; font-family: inherit; padding: 11px 13px; outline: none;
  box-shadow: 0 1px 2px rgba(31, 30, 29, .06), 0 3px 10px rgba(31, 30, 29, .08);
}
.modal-input:focus { box-shadow: 0 1px 3px rgba(31, 30, 29, .08), 0 4px 14px rgba(31, 30, 29, .12); }
.modal-acts { display: flex; justify-content: center; gap: 8px; margin-top: 20px; }
.modal-btn { border: none; border-radius: 10px; font-size: 14px; font-weight: 500; font-family: inherit; padding: 9px 16px; cursor: pointer; }
.modal-cancel { background: none; color: var(--dim); }
.modal-cancel:hover { background: var(--surface-2); color: var(--text); }
.modal-cancel.hidden { display: none; }
.modal-ok { background: var(--accent); color: var(--accent-ink); }
.modal-ok:hover { background: var(--accent-hover); }
.modal-ok.danger { background: var(--err); }
.modal-ok.danger:hover { filter: brightness(.92); }


/* На iPhone клавиатура уменьшает visual viewport, но fixed-диалог прежде оставался
   центрированным в полной высоте и его нижние поля оказывались ПОД клавиатурой.
   Диалог теперь живёт в доступной области и прокручивается сам; кнопки остаются видны. */
@media (max-width: 640px) {
  .modal-backdrop {
    align-items: flex-start;
    overflow-y: auto;
    padding: max(12px, env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom));
  }
  .modal {
    margin: auto 0;
    max-height: calc(100dvh - 24px - env(safe-area-inset-top) - env(safe-area-inset-bottom));
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  .modal-body { overflow-y: auto; overscroll-behavior: contain; padding-right: 2px; }
  .modal-acts { flex: 0 0 auto; position: sticky; bottom: 0; background: var(--bg); padding-top: 12px; }
}

/* ── Модалка кропа: обрезка картинки перед загрузкой (как в Телеграме) ── */
.crop-backdrop {
  position: fixed; inset: 0; z-index: 95;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(31, 30, 29, .5);
}
.crop-backdrop.hidden { display: none; }
.crop-modal {
  width: 100%; max-width: 560px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 18px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .32);
  padding: 20px 20px 14px; animation: modal-in .16s ease;
  display: flex; flex-direction: column; align-items: stretch;
}
.crop-title { font-family: var(--font-serif); font-weight: 600; font-size: 18px; color: var(--text); margin-bottom: 14px; }
.crop-wrap { display: flex; justify-content: center; }
/* сцена вплотную оборачивает картинку — координаты рамки = координаты картинки */
/* overflow:hidden обрезает затемняющую тень рамки ровно по картинке (не по всей модалке) */
.crop-stage { position: relative; display: inline-block; overflow: hidden; border-radius: 8px; line-height: 0; touch-action: none; user-select: none; }
#crop-img {
  display: block; max-width: 100%; max-height: 60vh;
  border-radius: 8px; -webkit-user-drag: none; user-select: none;
}
/* рамка выделения: затемняет всё вокруг через огромную тень, сама остаётся светлой */
.crop-sel {
  position: absolute; box-sizing: border-box;
  border: 1.5px solid #fff; cursor: move;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, .5);
}
/* ручки ВНУТРИ рамки (край сцены обрезается overflow:hidden — снаружи их бы срезало) */
.crop-h {
  position: absolute; width: 13px; height: 13px;
  background: #fff; border: 1.5px solid var(--accent); border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .4); touch-action: none;
}
/* невидимая зона захвата вокруг кружка — промахнуться мимо уголка почти нельзя */
.crop-h::before {
  content: ""; position: absolute; top: -14px; left: -14px; right: -14px; bottom: -14px;
}
.crop-h.nw { left: 1px; top: 1px; cursor: nwse-resize; }
.crop-h.ne { right: 1px; top: 1px; cursor: nesw-resize; }
.crop-h.sw { left: 1px; bottom: 1px; cursor: nesw-resize; }
.crop-h.se { right: 1px; bottom: 1px; cursor: nwse-resize; }
.crop-hint { color: var(--dim); font-size: 12.5px; text-align: center; margin-top: 12px; }
.crop-acts { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.crop-acts .modal-btn:not(.modal-ok):not(.modal-cancel) { background: var(--surface-2); color: var(--text); }
.crop-acts .modal-btn:not(.modal-ok):not(.modal-cancel):hover { filter: brightness(.96); }

/* ── Виджет «Страж сервера» — внизу сайдбара под чатами (спек 2026-07-06) ── */
.server-guard {
  flex: 0 0 auto; margin: 0 8px calc(18px + env(safe-area-inset-bottom)); padding: 9px 10px 7px;
  border-top: 1px solid var(--border); font-size: 11.5px; color: var(--dim);
}
.server-guard .sg-top { display: flex; align-items: center; gap: 7px; font-weight: 600; color: var(--text); font-size: 12px; }
.server-guard .sg-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px; }
.server-guard .sg-dot.green { background: var(--ok); }
.server-guard .sg-dot.yellow { background: #d9a441; }
.server-guard .sg-dot.red { background: var(--err); animation: sg-pulse 1.4s ease-in-out infinite; }
.server-guard .sg-sub { margin-top: 3px; line-height: 1.35; }
.server-guard .sg-warn { margin-top: 3px; color: var(--err); }
.server-guard .sg-rec { margin-top: 3px; opacity: .8; }
@keyframes sg-pulse { 50% { opacity: .4; } }

/* Лимиты подписки в углу сайдбара (Claude + GPT-5) — переиспользуют .usage-row/.usage-bar. */
.usage-guard {
  flex: 0 0 auto; margin: 0 8px 2px; padding: 8px 8px 2px;
  border-top: 1px solid var(--border);
  color: var(--text);
}
.usage-guard .usage-row { padding: 4px 2px 7px; }
.usage-guard .usage-top { font-size: 11.5px; }
.usage-guard .usage-note { padding: 2px 2px; }
/* Крик на ≥90% недельного лимита — громкий пульсирующий блок «тормози». */
.usage-alarm {
  background: var(--err); color: #fff; font-size: 12px; font-weight: 600;
  padding: 7px 9px; border-radius: 7px; margin: 2px 2px 8px; line-height: 1.35;
  animation: usage-alarm-pulse 1.15s ease-in-out infinite;
}
@keyframes usage-alarm-pulse { 50% { opacity: .6; } }
/* Шапка блока лимитов с кнопкой свернуть/развернуть. */
.usage-guard .ug-head { display: flex; align-items: center; justify-content: space-between; padding: 0 2px 1px; }
.usage-guard .ug-title { font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: var(--dim); }
.uc-toggle { background: none; border: 0; color: var(--dim); cursor: pointer; padding: 2px; display: flex; align-items: center; }
.uc-toggle:hover { color: var(--text); }
.uc-toggle svg { transition: transform .18s ease; }
.usage-guard.collapsed .uc-toggle svg { transform: rotate(180deg); }
/* Показываем ровно один из двух видов. */
.usage-guard.collapsed .usage-full { display: none; }
.usage-guard:not(.collapsed) .usage-compact { display: none; }
/* Свёрнутый вид — крохотные полосочки в строку. */
/* Свёрнутый вид — компактная сетка 2 колонки × [чип | бар | %], вдвое ниже одной колонки.
   Чипы фиксированной ширины (56px) → одинаковы во ВСЕХ строках и обоих столбцах; бары —
   1fr, поэтому равны и стоят ровно друг под другом; проценты выровнены по правому краю.
   Подписи укорочены (без версии модели), чтобы влезть в ширину сайдбара без обрезки.
   .uc-item = display:contents, чтобы его дети попадали прямо в треки сетки. */
.usage-compact {
  display: grid;
  grid-template-columns: 60px 1fr auto  60px 1fr auto;
  align-items: center; column-gap: 6px; row-gap: 6px; padding: 4px 2px 2px;
}
.uc-item { display: contents; }
.uc-item b {
  justify-self: end; font-size: 10px; color: var(--text); font-weight: 600;
  font-variant-numeric: tabular-nums;   /* цифры одной ширины → столбик % ровный */
}
/* Тег движка — цветной чип; тянется на всю ширину своего столбца → чипы одной ширины */
.uc-tag {
  justify-self: stretch; text-align: center;
  font-size: 8px; font-weight: 600; letter-spacing: -.2px;
  padding: 1.5px 2px; border-radius: 5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.eng-cl  .uc-tag { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); }
.eng-gpt .uc-tag { background: rgba(16,163,127,.16); color: #0e8f6f; }
.eng-gemini .uc-tag { background: rgba(66,133,244,.16); color: #2f6fd1; }
.uc-bar { position: relative; width: 100%; height: 3px; border-radius: 2px; background: var(--border); overflow: hidden; }
.uc-fill { display: block; height: 100%; background: var(--accent); }
.uc-item.warn .uc-fill { background: #e2a03f; }
.uc-item.warn b { color: #b97a1e; }
.uc-item.crit .uc-fill { background: #d64545; }
.uc-item.crit b { color: #d64545; }
/* Метка «сейчас», как в полном виде, но без делений — бар 3px, деления там мельчат */
.uc-now { position: absolute; top: 0; bottom: 0; width: 1px; background: #f2f2f5; }

.modal-btn.modal-extra:hover { filter: brightness(.95); }

/* ── Просмотр картинки поверх интерфейса (UI-244) — вместо ухода во внешнюю вкладку ── */
.img-lightbox {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center; padding: 24px;
  background: rgba(20, 19, 18, .82);
  animation: modal-in .16s ease;
}
.img-lightbox img {
  max-width: 100%; max-height: 100%; border-radius: 8px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .4);
  cursor: zoom-out;
}
.img-lightbox-x {
  position: fixed; top: max(14px, env(safe-area-inset-top)); right: max(16px, env(safe-area-inset-right));
  width: 36px; height: 36px; border-radius: 50%; border: none; cursor: pointer;
  background: rgba(31, 30, 29, .5); color: #fff; font-size: 16px; line-height: 1;
}
.img-lightbox-x:hover { background: rgba(31, 30, 29, .72); }
