/* ============================================================
   pixapp.io — Premium corporate design system
   Unbounded (заголовки/логотип сайта) + Manrope (UI grotesque).
   Newsreader остаётся только для типографики шаблонов визиток
   (см. --serif и cards.jsx) — это дизайн печатных макетов, не
   шрифт самого сайта.
   ============================================================ */

:root {
  /* color-scheme — без него нативные контролы (чекбоксы, input[type=color],
     скроллбары) рисуются браузером по СВОЕЙ светлой теме, даже когда
     data-theme="dark" уже перекрасил весь остальной сайт: у нас ручной
     переключатель темы, не системный prefers-color-scheme, так что
     браузер иначе о выбранной теме просто не узнаёт. Симптом — сплошной
     белый квадрат у чекбокса "Единый стиль лица и оборота" в тёмной теме
     (жалоба 2026-07-21, дизайн-аудит: settings-foot). */
  color-scheme: light;
  /* Surfaces — было тёплым кремовым (#FBFAF6/#F4F1E9), рядом с холодным
     violet-акцентом это читалось "грязным": жёлтый подтон фона спорит
     с холодным акцентом вместо того, чтобы просто дать ему звучать —
     ровно то, от чего уходят Leonardo/Krea/Ideogram (нейтральный или
     холодный холст). Теперь фон холодный нейтральный. */
  --paper:       #F6F7F6;
  --paper-2:     #EEF0EF;
  --surface:     #FFFFFF;
  --surface-ink: #14161A;

  /* Ink / text */
  --ink:        #1A1815;
  --ink-soft:   #45423B;
  --muted:      #7C786E;
  --faint:      #A8A398;

  /* Lines */
  --line:       #E2E5E4;
  --line-soft:  #EDEFEE;
  --line-strong:#CBD0CE;

  /* Accents */
  --navy:       #1C3A57;
  --navy-deep:  #122538;
  --navy-tint:  #EAF0F5;
  --navy-line:  #C9D7E2;
  /* --navy-solid — НЕ переопределяется в [data-theme="dark"] (в отличие
     от --navy выше). Нужна там, где --navy используют как сплошную
     заливку карточки/панели (landing.jsx: "Для профессионалов", BigCTA)
     с захардкоженным светлым текстом поверх — --navy в тёмной теме
     специально осветляется для контраста ТЕКСТА/акцента НА тёмном фоне
     сайта (см. [data-theme="dark"] ниже), а не для того, чтобы сама
     заливка карточки становилась светлой. Раньше эти карточки в тёмной
     теме превращались в светло-голубые с тем же светлым текстом поверх —
     контраст ~1.3:1 вместо нужных 4.5:1, текст практически не читался
     (дизайн-аудит 2026-07-21). */
  --navy-solid: #1C3A57;
  /* было золото/бронза — теперь единственный "живой" акцент сайта,
     в духе Leonardo.ai violet. Имя переменной осталось прежним, чтобы
     не перебирать все её употребления по файлу — меняется только цвет. */
  --bronze:     #6D5EF0;
  --bronze-tint:#EEECFB;

  /* Status */
  --good:       #2E6E4E;
  --good-tint:  #E4F3EA;

  /* Type */
  /* --serif — только для типографики шаблонов визиток (cards.jsx),
     это часть дизайна печатных макетов, не шрифт самого сайта. */
  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  /* --display — шрифт заголовков/логотипа самого сайта: геометричный,
     с нативной кириллицей, техничнее курсивного serif. */
  --display: "Unbounded", "Manrope", "Helvetica Neue", sans-serif;
  --sans: "Manrope", "Helvetica Neue", Helvetica, Arial, "Segoe UI", sans-serif;

  /* Radii */
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 16px;
  --r-xl: 24px;

  /* Shadow */
  --sh-sm: 0 1px 2px rgba(20,19,15,.05), 0 1px 1px rgba(20,19,15,.04);
  --sh-md: 0 4px 14px rgba(20,19,15,.07), 0 1px 3px rgba(20,19,15,.05);
  --sh-lg: 0 18px 50px rgba(20,19,15,.13), 0 4px 14px rgba(20,19,15,.07);
  --sh-card: 0 22px 48px -16px rgba(18,37,56,.34), 0 8px 18px -10px rgba(20,19,15,.22);

  /* ── "Liquid Glass" (iOS 26 style) — общий вид для всех выпадающих
     окон/поповеров/модалок сайта (аттач-меню формата/цвета/стиля,
     нудж профессии, история, QR-меню, модалки кропа/QR/аплоада/входа):
     полупрозрачная подложка + размытие фона за ней (backdrop-filter) +
     тонкая светлая окантовка + мягкая многослойная тень с internal-
     хайлайтом сверху (inset) — тот самый "блик по верхнему краю стекла".
     Отдельные переменные, НЕ переопределяют --surface — обычные
     непрозрачные поверхности (карточки, инпуты и т.д.) остаются как есть,
     стекло — только там, где эти токены используются явно. */
  --glass-bg: rgba(255,255,255,.66);
  --glass-border: rgba(255,255,255,.7);
  --glass-blur: 22px;
  --glass-shadow: 0 12px 40px rgba(20,19,15,.16), 0 2px 8px rgba(20,19,15,.08), inset 0 1px 0 rgba(255,255,255,.55);

  --ease: cubic-bezier(.22,.61,.36,1);

  /* Typography scale */
  --text-xs:   11px;
  --text-sm:   13px;
  --text-base: 15px;
  --text-md:   17px;
  --text-lg:   21px;
  --text-xl:   28px;
  --text-2xl:  38px;

  /* Spacing scale (4px grid) */
  --sp-1:  4px;
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  20px;
  --sp-6:  24px;
  --sp-8:  32px;
  --sp-10: 40px;
  --sp-12: 48px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body { overflow-x: hidden; }

::selection { background: var(--navy); color: #fff; }

a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }

h1, h2, h3, h4 { font-family: var(--display); font-weight: 500; margin: 0; letter-spacing: -.01em; text-wrap: balance; }

/* ---------- Layout helpers ---------- */
.wrap { width: 100%; max-width: 1600px; margin: 0 auto; padding: 0 32px; }
.wrap-wide { max-width: 1320px; }
.row { display: flex; align-items: center; }
.col { display: flex; flex-direction: column; }
.gap-8 { gap: 8px; } .gap-10 { gap: 10px; } .gap-12 { gap: 12px; }
.gap-14 { gap: 14px; } .gap-16 { gap: 16px; } .gap-18 { gap: 18px; }
.gap-20 { gap: 20px; } .gap-24 { gap: 24px; } .gap-32 { gap: 32px; }
.spread { justify-content: space-between; }
.center { justify-content: center; align-items: center; }

/* ---------- Eyebrow / labels ---------- */
.eyebrow {
  font-family: var(--sans);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--bronze);
}
.eyebrow.navy { color: var(--navy); }
.eyebrow.muted { color: var(--muted); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  height: 48px; padding: 0 24px;
  border-radius: var(--r-md);
  font-family: var(--sans); font-size: var(--text-base); font-weight: 600;
  letter-spacing: .01em;
  border: 1px solid transparent;
  transition: transform .25s var(--ease), background .2s var(--ease), box-shadow .25s var(--ease), border-color .2s;
  white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--navy); color: #fff; box-shadow: var(--sh-md); }
.btn-primary:hover { background: var(--navy-deep); box-shadow: var(--sh-lg); transform: translateY(-1px); }
.btn-generate { position: relative; overflow: hidden; }
.btn-generate::after {
  content: "";
  position: absolute;
  top: 0; left: -100%;
  width: 60%; height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.15), transparent);
  animation: shimmer 2.5s ease 3;
}
.btn-generate[disabled]::after { animation: none; }
@keyframes shimmer {
  0%   { left: -100%; }
  60%  { left: 150%; }
  100% { left: 150%; }
}
.btn-ink { background: var(--ink); color: var(--paper); }
.btn-ink:hover { background: #000; transform: translateY(-1px); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn-ghost:hover { border-color: var(--ink); background: rgba(0,0,0,.02); }
.btn-light { background: var(--surface); color: var(--ink); border-color: var(--line); box-shadow: var(--sh-sm); }
.btn-light:hover { border-color: var(--line-strong); box-shadow: var(--sh-md); }
.btn-sm { height: 40px; padding: 0 18px; font-size: var(--text-sm); }
.btn-lg { height: 54px; padding: 0 30px; font-size: 16px; }
.btn-block { width: 100%; }
.btn-dark {
  background: var(--ink); color: var(--paper); border: 1px solid var(--ink);
}
.btn-dark:hover { background: #000; box-shadow: var(--sh-md); }
.btn[disabled] { opacity: .5; pointer-events: none; }

/* ---------- Pills / chips ---------- */
.pill {
  display: inline-flex; align-items: center; gap: 7px;
  height: 30px; padding: 0 13px;
  border-radius: 999px;
  font-size: var(--text-sm); font-weight: 500;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-soft);
  transition: all .18s var(--ease);
  cursor: pointer;
}
.pill:hover { border-color: var(--line-strong); }
.pill.active { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.pill.navy.active { background: var(--navy); border-color: var(--navy); }

.badge {
  display: inline-flex; align-items: center; gap: 6px;
  height: 22px; padding: 0 9px;
  border-radius: 5px;
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  background: var(--bronze-tint); color: var(--bronze);
}
.badge.navy { background: var(--navy-tint); color: var(--navy); }
/* Честный бейдж "выгоднее на X%" (запрос владельца 2026-08-05) — цена всегда
   считается на лету из реальной цены/Пиксов конкретного тарифа или пакета
   (см. pricePerPixBadge в Pricing, pages.jsx), а не подбирается на глаз —
   никаких вымышленных "было/стало". */
.badge.good { background: var(--good-tint); color: var(--good); }
/* Длинные составные названия стилей ("Динамичный / Энергичный", "Строгий
   navy" рядом с кнопкой "Повторить" в узкой строке карточки Галереи)
   переносились на 2 строки внутри фиксированной height:22px — вторая
   строка обрезалась по высоте, плашка визуально ломалась и съезжала с
   соседней кнопкой (дизайн-аудит 2026-07-22). text-overflow не работает
   на самом .badge (display:inline-flex — иконка+текст в других местах
   сайта) — обрезаем текст отдельным внутренним inline-block span'ом,
   .badge сам остаётся flex-контейнером для иконки. */
.badge-clip { display: inline-block; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }

/* ---------- Cards / surfaces ---------- */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-sm);
}

/* Превью макета в «Мои макеты» — тот же приём hover-масштаба, что и у
   .hist-thumb (auth.css): лёгкий zoom по наведению, чтобы плашка читалась
   как интерактивное превью, а не декоративная картинка (запрос
   пользователя 2026-07-19). Общий child-селектор (не img), т.к. без
   реального genId сюда рендерится DOM-мок BizCard, а не <img>. */
.mylayout-thumb { cursor: pointer; }
.mylayout-thumb > * { transition: transform .18s var(--ease); }
.mylayout-thumb:hover > * { transform: scale(1.05); }

/* ---------- Inputs ---------- */
.field { display: flex; flex-direction: column; gap: 7px; }
.field label { font-size: var(--text-sm); font-weight: 600; color: var(--ink-soft); letter-spacing: .005em; }
.input, .textarea, .select {
  width: 100%;
  font-family: var(--sans); font-size: var(--text-base); color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  padding: 13px 15px;
  transition: border-color .18s, box-shadow .18s;
  outline: none;
}
.input:focus, .textarea:focus, .select:focus {
  border-color: var(--navy);
  box-shadow: 0 0 0 3px rgba(28,58,87,.12);
}
.input::placeholder, .textarea::placeholder { color: var(--faint); }
.textarea { resize: vertical; min-height: 96px; line-height: 1.5; }

/* ---------- Divider ---------- */
.hr { height: 1px; background: var(--line); border: 0; width: 100%; }

/* ---------- Tag ---------- */
.tag { font-size: var(--text-xs); color: var(--muted); }

/* ============================================================
   NAVBAR
   ============================================================ */
.nav {
  position: sticky; top: 0; z-index: 50;
  background: rgba(251,250,246,.82);
  backdrop-filter: blur(14px) saturate(1.4);
  border-bottom: 1px solid var(--line-soft);
}
.nav-inner { height: 70px; display: flex; align-items: center; justify-content: space-between; }
.logo { display: flex; align-items: center; gap: 11px; cursor: pointer; }
/* Фирменный фиолет (--bronze — переменная не переименована, см. заголовок
   раздела Accents), не navy (запрос владельца 2026-08-06) — тот же цвет,
   что уже стоял только в подвале (Footer, inline style в ui.jsx) сплошным
   hardcoded #6D5EF0; здесь — через переменную, поэтому ещё и корректно
   переключается в тёмной теме (там --bronze светлее, #9C8CFF), в отличие
   от захардкоженного значения в футере. "pixapp" (обычный текст .logo-name,
   без .b) цвет не меняли — только квадрат-плашка и ".ai". */
.logo-mark {
  width: 32px; height: 32px; border-radius: 8px;
  background: var(--bronze);
  display: grid; place-items: center;
  color: #fff; font-family: var(--display); font-size: var(--text-lg); font-weight: 600;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.14), var(--sh-sm);
  /* Строчная "p" в Unbounded не симметрична по вертикали (короткий верх до
     x-height, длинный хвост-спуск ниже базовой линии) — геометрическое
     центрирование через place-items центрирует ЛИНИЮ (ascent+descent
     шрифта), а не сами чернила буквы, из-за чего "p" визуально съезжает
     вниз (запрос владельца 2026-08-07, замерено по пикселям реального
     рендера: ~3.4px вниз на 32px-плашке). padding-bottom в em (не px) —
     тот же сдвиг автоматически верен на всех размерах плашки по сайту
     (30/32/44px и т.д., у каждого свой font-size), без правки трёх мест в
     JSX, где рендерится "p". */
  padding-bottom: 0.34em;
}
.logo-name { font-family: var(--display); font-size: var(--text-lg); font-weight: 600; letter-spacing: -.015em; }
.logo-name b { color: var(--bronze); font-weight: 600; }
.nav-links { display: flex; align-items: center; gap: 4px; }
.nav-link {
  font-size: var(--text-base); font-weight: 500; color: var(--ink-soft);
  padding: 9px 14px; border-radius: 8px;
  transition: background .16s, color .16s;
  /* Без nowrap самый длинный пункт ("Улучшить качество") первым ловил
     нехватку места и переламывался на "Улучшить"/"качество" — 2 строки
     в ряду, где всё остальное (лого, бейдж кредитов, "Создать визитку")
     держится одной, ломали общую горизонталь шапки (дизайн-аудит
     2026-07-21, 1280px). Пункт меню не должен переноситься никогда —
     на действительно узких экранах весь .nav-links вообще скрыт (см.
     мобильный @media ниже), так что здесь всегда есть одна строка. */
  white-space: nowrap;
}
.nav-link:hover { background: rgba(0,0,0,.04); color: var(--ink); }
.nav-link.active { color: var(--navy); font-weight: 600; }
/* Тот же дефицит ширины — на 1280px сумме логотипа + пунктов меню + правого
   блока не хватало ~43px до общей ширины шапки (жёсткая nowrap-версия
   выше без этого просто вылезла бы за край). Единственная область, где
   можно безопасно ужаться, не трогая просторный вид на широких экранах. */
@media (max-width: 1360px) {
  .nav-links { gap: 2px; }
  .nav-link { padding: 9px 10px; }
  /* Пунктов меню одних было мало — после nowrap выше вся шапка (лого +
     меню + правый блок) чуть-чуть, но всё же не помещалась в 1280px
     (реально проверено: 23px горизонтального переполнения страницы),
     поэтому здесь же ужимается и правый блок — тем же приёмом, что и
     меню: меньше зазор между кнопками, чуть уже паддинги, ничего не
     обрезается и не переносится. */
  .nav-actions { gap: 5px; }
  .nav-actions .btn-sm { padding: 0 12px; }
  .nav-links { gap: 0; }
}

/* Бургер — скрыт на десктопе, появляется по media-запросу ниже */
.nav-burger {
  display: none; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 9px; flex: none;
  background: none; border: 1px solid var(--line); color: var(--ink);
  cursor: pointer;
}
.nav-burger:hover { background: rgba(0,0,0,.04); }

/* Выпадающее мобильное меню */
.nav-mobile {
  border-top: 1px solid var(--line-soft);
  background: var(--paper);
  padding: 8px 20px 18px;
  animation: viewIn .2s ease both;
}
.nav-mobile-links { display: flex; flex-direction: column; gap: 2px; padding-top: 6px; }
.nav-mobile-link {
  font-size: var(--text-md); font-weight: 500; color: var(--ink-soft);
  padding: 12px 6px; border-radius: 8px;
  border-bottom: 1px solid var(--line-soft);
}
.nav-mobile-link:last-child { border-bottom: none; }
.nav-mobile-link.active { color: var(--navy); font-weight: 600; }
.nav-mobile-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding-top: 14px; margin-top: 4px;
}
.nav-mobile-actions .btn-primary { flex: 1; justify-content: center; }

/* ============================================================
   VIEW TRANSITIONS
   ============================================================ */
/* БЕЗ fill-режима both: он навсегда оставлял на .view конечный кадр
   transform: translateY(0) — а ЛЮБОЙ не-none transform делает элемент
   containing block для position:fixed потомков. Все fixed-элементы внутри
   вида (лупа апскейлера, тосты студии) позиционировались не от вьюпорта,
   а от .view — т.е. смещались ровно на высоту шапки (70px) вниз. После
   завершения анимации transform должен ИСЧЕЗАТЬ — поэтому без both. */
.view { animation: viewIn .5s var(--ease); }
@keyframes viewIn {
  from { transform: translateY(12px); }
  to   { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  *, .view { animation: none !important; transition: none !important; }
}

/* Reveal on stagger */
.rise { animation: rise .6s var(--ease) both; }
@keyframes rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }

/* ============================================================
   APP SHELL + SIDEBAR (запрос владельца 2026-08-03) — гибрид
   Canva/Ideogram/Magnific: гость видит верхнее меню (.nav выше) как и
   раньше, залогиненный — этот левый рейл вместо него (см. app.jsx —
   переключает Nav/Sidebar по auth.isAuthed(), и Sidebar в ui.jsx).
   .app-shell — только когда рейл реально смонтирован (гостевая раскладка
   вообще не оборачивается в него, см. app.jsx) — flex-ряд рейл+контент.
   ============================================================ */
.app-shell { display: flex; min-height: 100vh; align-items: stretch; }
.app-shell-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; min-height: 100vh; }

/* Тонкая полоса "где я сейчас" (запрос владельца 2026-08-03, п.5а) — см.
   PageHeader в ui.jsx. Sticky, не fixed — уезжает вместе с остальным
   контентом внутри .app-shell-main при скролле длинных страниц (Тарифы/
   Главная), а не нависает поверх них постоянно, как это делает сам
   .app-sidebar (у того другая роль — постоянная навигация). */
.page-header {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 9px;
  height: 48px; padding: 0 28px; flex: none;
  background: var(--surface); border-bottom: 1px solid var(--line-soft);
  color: var(--ink-soft); font-size: 13.5px; font-weight: 600;
}
.page-header svg { color: var(--muted); }
/* Кнопка быстрого перехода в Студию — сразу после заголовка страницы, у
   левого края (запрос владельца 2026-08-05: раньше стояла margin-left:auto
   и терялась у правого края бара), с тем же переливающимся ai-glow-field
   кольцом, что и у поля запроса в CommandHero (см. блок "AI GLOW BORDER"
   ниже) — и покрупнее соседних элементов бара, чтобы взгляд цеплялся
   именно за неё, а не терялась среди прочих 30px-кнопок хедера. */
.page-header-studio-btn {
  display: inline-flex; align-items: center; gap: 7px; margin-left: 6px;
  height: 34px; padding: 0 18px; border-radius: 10px; border: none;
  background: var(--navy); color: #fff; cursor: pointer;
  font-family: var(--sans); font-size: 13.5px; font-weight: 700;
  transition: background .12s, transform .12s;
  position: relative; overflow: hidden;
}
.page-header-studio-btn:hover { background: var(--navy-2, var(--navy)); transform: translateY(-1px); }
.page-header-studio-btn svg { color: #fff; }
/* Кольцо толще, чем дефолт .ai-glow-field (1.5px) — на 30-34px кнопке в
   шапке тонкое неспешное кольцо терялось, на 1.5px inset-рамке поля запроса
   оно рассчитано намеренно тоньше, эта кнопка — отдельный случай. Толщина
   2px: было 3px (первая правка) → владелец попросил чуть тоньше (запрос
   2026-08-06). Скорость вращения (linear, постоянная — в отличие от блика
   ниже с ease-in-out, т.е. с ускорением) — 4.5s: было 6.8s (стандарт поля
   запроса) → 3.2s ("заметнее") → 6.4s (вдвое медленнее) → 4.5s (владелец
   попросил чуть быстрее обратно, 2026-08-06). */
.page-header-studio-btn.ai-glow-field::after {
  border-radius: 10px; opacity: 1; padding: 2px;
  animation: ai-glow-spin 4.5s linear infinite;
}
/* Блик, "поблескивание" по самой поверхности кнопки (запрос владельца
   2026-08-06, "кнопка должна как бы поблескивать, привлекать нажать") —
   тот же приём, что у .btn-generate::after (полоса светлого градиента,
   бегущая слева направо), но на ::before: ::after этой кнопки уже занят
   кольцом ai-glow-field выше. infinite (не btn-generate-шные 3 прохода и
   стоп) — здесь это постоянный элемент шапки, а не разовая подсказка на
   поле ввода. */
.page-header-studio-btn::before {
  content: "";
  position: absolute; top: 0; left: -60%;
  width: 40%; height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.4), transparent);
  animation: studioBtnShine 2.6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes studioBtnShine {
  0%   { left: -60%; }
  55%  { left: 140%; }
  100% { left: 140%; }
}
@media (prefers-reduced-motion: reduce) {
  .page-header-studio-btn::before { animation: none; }
}

/* Баланс Пиксов — правый край шапки (запрос владельца 2026-08-06, по
   образцу StudyAI): всегда видно, сколько Пиксов на счету, даже вне
   Студии. "Выбрать тариф" рядом убрана (запрос владельца 2026-08-06) —
   плашка баланса сама по себе кликабельна и ведёт на /pricing. */
.page-header-right {
  display: flex; align-items: center; gap: 8px; margin-left: auto; flex: none;
}
.page-header-empty { justify-content: flex-end; }
/* Крупнее базового .credit-balance-badge (студийный вариант — тихий значок
   в узкой панели настроек) — здесь плашка одна на всю ширину шапки, весь
   акцент на ней. Было "Баланс N Пиксов" одним ровным текстом — владелец
   попросил (2026-08-06/07) убрать слово "Баланс", заменить "Пиксов" на
   короткое "pix" и вывести саму цифру вдвое крупнее/жирнее подписи — сама
   цифра и есть главная информация здесь, "pix" — просто единица рядом.
   Высота подобрана так, чтобы уверенно помещаться в 48px-бар .page-header
   (см. выше) с запасом на бордер. */
.header-balance-badge {
  padding: 7px 16px 7px 14px; border-radius: 999px; gap: 8px;
  font-size: 15px; font-weight: 800;
}
/* Было 30px/900 — владелец счёл слишком крупным и "не тем шрифтом" (запрос
   2026-08-07): 900 не загружен для var(--sans)/Manrope (в index.html только
   400-700), браузер синтезировал жирность сам — на глаз читалось как
   другой, "тяжёлый" шрифт, не как основной сайтовский. 15px/700 (вдвое
   меньше, реальный, а не синтезированный вес) + явный font-family —
   гарантия, что это тот же Manrope, что и everywhere на сайте. */
.header-balance-num {
  font-family: var(--sans); font-size: 15px; font-weight: 700; line-height: 1; letter-spacing: -.01em;
}
.header-balance-unit {
  font-size: 14px; font-weight: 700; color: var(--muted); margin-left: 1px;
}

.app-sidebar {
  flex: none; width: 240px; display: flex; flex-direction: column;
  padding: 16px 12px; gap: 4px;
  background: var(--surface); border-right: 1px solid var(--line-soft);
  position: sticky; top: 0; height: 100vh; z-index: 60;
  transition: width .18s var(--ease);
}
.app-sidebar.collapsed { width: 72px; }

.sidebar-top { display: flex; align-items: center; justify-content: space-between; padding: 4px 4px 18px; }
.sidebar-logo { gap: 9px; }
/* Свёрнутое состояние — лого и стрелка разворота складываются в столбик:
   кнопка НЕ прячется (баг первой версии — свернуть можно было, а развернуть
   обратно было решительно нечем нажать, кнопка пряталась вместе с собой). */
.app-sidebar.collapsed .sidebar-top { flex-direction: column; gap: 10px; justify-content: center; padding: 4px 0 18px; }
.app-sidebar.collapsed .logo-name { display: none; }
.sidebar-collapse-btn {
  width: 28px; height: 28px; border-radius: 8px; flex: none;
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  display: grid; place-items: center; cursor: pointer; transition: all .15s;
}
.sidebar-collapse-btn:hover { border-color: var(--navy-line); color: var(--navy); }

/* Список пунктов — то же семейство, что .nav-mobile-link, но постоянно
   видимое (не выпадающее меню), с иконкой слева всегда (иконка — то, что
   остаётся, когда рейл свёрнут в узкую полосу). */
.sidebar-links { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; }
.sidebar-link {
  display: flex; align-items: center; gap: 12px; min-width: 0;
  padding: 10px 10px; border-radius: 10px;
  font-size: 14px; font-weight: 600; color: var(--ink-soft); cursor: pointer;
  transition: background .12s, color .12s;
}
.sidebar-link:hover { background: var(--paper-2); color: var(--ink); }
.sidebar-link.active { background: var(--navy-tint); color: var(--navy); }
.sidebar-link svg { flex: none; }
.sidebar-link-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-link-ai { flex: none; margin-left: auto; }
.app-sidebar.collapsed .sidebar-link { justify-content: center; padding: 10px 0; }
.app-sidebar.collapsed .sidebar-link-text,
.app-sidebar.collapsed .sidebar-link-ai { display: none; }

/* Недавние генерации (запрос владельца 2026-08-03, п.5б) — тесно даже под
   подписи в свёрнутом рейле, поэтому вся секция целиком скрывается через
   collapsed в самом Sidebar (ui.jsx), а не через CSS-варианты, как у
   .sidebar-link. */
.sidebar-recent { padding: 10px 2px 4px; }
/* Заголовок теперь кнопка-раскрывашка (свёрнуто по умолчанию, см. Sidebar) —
   тот же язык, что у .sidebar-collapse-btn/.psp-toggle-arrow: шеврон
   поворачивается на 90° в открытом состоянии, не отдельная иконка на
   каждое состояние. */
.sidebar-recent-toggle {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 6px;
  padding: 0 8px 6px; background: none; border: none; cursor: pointer; color: var(--muted);
}
.sidebar-recent-toggle svg { flex: none; transition: transform .15s; }
.sidebar-recent-title {
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
}
.sidebar-recent-row { display: flex; gap: 6px; padding: 0 2px; }
.sidebar-recent-tile {
  flex: 1 1 0; aspect-ratio: 1; min-width: 0; padding: 0; border-radius: 8px;
  border: 1px solid var(--line-soft); background: var(--paper-2); overflow: hidden;
  cursor: pointer; transition: border-color .12s;
}
.sidebar-recent-tile:hover { border-color: var(--navy-line); }
.sidebar-recent-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }

.sidebar-spacer { flex: 1 1 auto; }

/* CTA апгрейда для бесплатного тарифа (запрос владельца 2026-08-03, "кнопка
   пробная версия как у Canva") — заметный акцентный градиент, тот же
   фиолетовый, что у AI-плашек/hero-кнопки соцвхода, не путается с обычными
   .sidebar-link. */
.sidebar-upgrade-btn {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  margin: 6px 2px 10px; padding: 10px 12px; border-radius: 12px; border: none;
  background: linear-gradient(135deg, #6D5EF0, #8B7EF5); color: #fff;
  font-family: var(--sans); font-size: 13.5px; font-weight: 700; cursor: pointer;
  box-shadow: 0 4px 14px rgba(109,94,240,.32); transition: transform .12s, box-shadow .12s;
}
.sidebar-upgrade-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(109,94,240,.4); }
.app-sidebar.collapsed .sidebar-upgrade-btn { justify-content: center; padding: 10px 0; }
.app-sidebar.collapsed .sidebar-upgrade-btn .sidebar-link-text { display: none; }

.sidebar-bottom { display: flex; flex-direction: column; gap: 8px; padding-top: 10px; border-top: 1px solid var(--line-soft); }
.sidebar-util-row { display: flex; align-items: center; gap: 6px; }
.app-sidebar.collapsed .sidebar-util-row { flex-direction: column; }
.sidebar-util-btn { flex: none; }
/* LangSwitcher/AccountMenu остаются иконка-only даже свёрнутые — их
   внутренние попапы не зависят от текста триггера, поэтому collapsed не
   требует отдельной свёрнутой разметки внутри них самих. */
.app-sidebar.collapsed .acct-trigger-sidebar { width: 40px; height: 40px; padding: 0; justify-content: center; border-radius: 50%; }
.app-sidebar.collapsed .acct-trigger-sidebar .acct-trigger-name,
.app-sidebar.collapsed .acct-trigger-sidebar .acct-trigger-chev { display: none; }
.app-sidebar.collapsed .lang-switch-code { display: none; }

/* Гамбургер и подложка — скрыты на десктопе (см. media query ниже),
   мобильный рейл превращается в выезжающую панель, а не постоянно ест
   четверть узкого экрана. */
.sidebar-mobile-toggle {
  display: none; position: fixed; top: 14px; left: 14px; z-index: 120;
  width: 40px; height: 40px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  align-items: center; justify-content: center; cursor: pointer; box-shadow: var(--sh-sm);
}
.sidebar-backdrop { display: none; }

@media (max-width: 900px) {
  .sidebar-mobile-toggle { display: flex; }
  /* Гамбургер — fixed, left:14px, 40px шириной (см. .sidebar-mobile-toggle
     выше), т.е. занимает x:14-54. .page-header (тоже свой, отдельный
     sticky-элемент) держал те же 28px левого паддинга, что и на десктопе
     (где гамбургера вообще нет) — заголовок "Студия генерации"/иконка
     начинались в x:28, ПОД гамбургером (тот выше по z-index: 120 против
     40 у шапки) и визуально перекрывались им (репорт владельца
     2026-08-03, скриншот мобильной студии). Паддинг сдвинут за правый
     край кнопки с запасом. */
  .page-header { padding-left: 62px; }
  /* Кнопка-шорткат "Студия генерации" (см. .page-header-studio-btn выше) —
     на 390px-экране в этой полосе уже гамбургер+иконка+заголовок слева и
     плашка баланса справа, места на третий элемент физически нет: текст
     переносился на 2 строки и обрезался снизу (overflow:hidden у самой
     кнопки, дизайн-аудит 2026-08-07, скриншот "Студия генера[ции]" пополам
     наползает на плашку баланса). Дублирует пункт "Студия" в сайдбаре —
     на мобильном это гамбургер-меню в один тап, безопасно скрыть, а не
     втискивать в недостаточное место. */
  .page-header-studio-btn { display: none; }
  .app-sidebar {
    position: fixed; top: 0; left: -260px; height: 100vh; width: 240px !important;
    transition: left .22s var(--ease); box-shadow: var(--sh-lg);
  }
  /* z-index:115 — БЕЗ этого выезжающий drawer наследовал базовый z-index:60
     (см. .app-sidebar выше, десктопный sticky-рейл), а .sidebar-backdrop
     под ним стоит на 110 (нужно перекрыть остальную страницу) — подложка
     оказывалась ВЫШЕ самого меню и перехватывала все клики по его пунктам
     (дизайн-аудит 2026-08-07: открытый мобильный drawer был полностью
     некликабельным, единственное рабочее действие — тап по подложке,
     который его закрывает). 115 — строго между подложкой (110) и кнопкой-
     гамбургером (120, должна остаться поверх открытого меню, чтобы им же
     можно было закрыть), ниже модалок/тостов (200+). */
  .app-sidebar.mobile-open { left: 0; z-index: 115; }
  /* Мобильный рейл всегда развёрнутый по содержимому — "свёрнутое" (узкое,
     иконка-only) состояние имеет смысл только на широком экране рядом с
     контентом; выезжающая на весь свой размер панель поверх страницы
     всегда должна быть читаемой, а не узкой полосой иконок. */
  .app-sidebar.collapsed .sidebar-link-text,
  .app-sidebar.collapsed .sidebar-link-ai,
  .app-sidebar.collapsed .logo-name,
  .app-sidebar.collapsed .sidebar-upgrade-btn .sidebar-link-text { display: revert; }
  .app-sidebar.collapsed .sidebar-upgrade-btn { justify-content: flex-start; padding: 10px 12px; }
  .app-sidebar.collapsed .sidebar-link { justify-content: flex-start; padding: 10px 10px; }
  .app-sidebar.collapsed .sidebar-top { flex-direction: row; justify-content: space-between; padding: 4px 4px 18px; }
  /* Сворачивание в узкую полосу — понятие для широкого экрана рядом с
     контентом; на мобильном рейл и так либо открыт целиком, либо закрыт
     полностью гамбургером — своя кнопка "свернуть" внутри выезжающей
     панели не имеет понятного эффекта и только путает. */
  .sidebar-collapse-btn { display: none; }
  .sidebar-backdrop { display: block; position: fixed; inset: 0; background: rgba(10,12,16,.45); z-index: 110; }
  .app-shell-main { width: 100%; }
}

/* ============================================================
   FOOTER
   ============================================================ */
.footer { background: var(--surface-ink); color: #C9C5BB; margin-top: 0; }
.footer a { color: #C9C5BB; transition: color .15s; }
.footer a:hover { color: #fff; }
.footer-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 40px; padding: 64px 0 40px; }
.footer h5 { font-family: var(--sans); font-size: var(--text-xs); font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #6F6B61; margin: 0 0 18px; }
.footer ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; font-size: var(--text-sm); }
.footer-bottom { border-top: 1px solid rgba(255,255,255,.08); padding: 22px 0; display: flex; justify-content: space-between; align-items: center; font-size: var(--text-sm); color: #6F6B61; }

/* ============================================================
   BUSINESS CARD RENDERER
   ============================================================ */
.bc {
  /* border-radius: 0 (запрос пользователя 2026-07-24, см. .up-img-wrap в
     styles.css) — этот CSS-мокап РЕАЛЬНО представляет форму физической
     визитки (прямой угол), везде, где рисуется без явного переопределения
     (studio-extras.jsx уже переопределяет через previewRadius=0 для
     основного вида результата — тут правим тот же дефолт, чтобы Cabinet
     "Мои макеты" (pages.jsx), Галерея (CardFlip) и примеры на лендинге
     (landing.jsx) не оставались единственными местами со скруглением). */
  position: relative;
  width: 420px; height: 248px;
  border-radius: 0;
  overflow: hidden;
  font-family: var(--sans);
  box-shadow: var(--sh-card);
  flex: none;
  user-select: none;
}
.bc * { box-sizing: border-box; }
.bc-stack { display: flex; gap: 22px; flex-wrap: wrap; }

/* generic helpers used inside cards via inline classes */
.bc-mono { font-family: "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace; }

/* scaling wrapper to fit card in smaller spots */
.bc-scale { transform-origin: top left; }

/* ---------- Image placeholder ---------- */
.ph {
  background-image: repeating-linear-gradient(135deg, rgba(0,0,0,.035) 0 10px, transparent 10px 20px);
  background-color: var(--paper-2);
  display: grid; place-items: center;
  color: var(--muted);
  font-family: "SFMono-Regular", ui-monospace, Menlo, monospace;
  font-size: var(--text-xs); letter-spacing: .04em;
}

/* ============================================================
   GENERATION LOADING
   ============================================================ */
.spin { animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* progress bar */
.prog { height: 4px; background: var(--line); border-radius: 999px; overflow: hidden; }
.prog > i { display: block; height: 100%; background: var(--navy); border-radius: 999px; transition: width .3s var(--ease); }

/* ============================================================
   STUDIO APP LAYOUT (Magnific-style)
   ============================================================ */
.studio-app {
  display: flex;
  height: calc(100vh - 70px);
  max-width: 1600px;
  margin: 0 auto;
  background: var(--paper-2);
  overflow: hidden;
}
/* Тот же расчёт "от шапки до низа экрана", но под другим "потолком" —
   залогиненный видит не 70px верхний Nav, а 48px .page-header (сам
   .app-sidebar — сбоку, вертикального места не ест, см. app.jsx/ui.jsx
   Sidebar, запрос владельца 2026-08-03). Без этого переопределения
   .studio-app либо не дотягивался до низа экрана на 22px (сайдбар без
   page-header), либо вылезал за него (если бы page-header просто
   добавился поверх старого расчёта). */
.app-shell-main .studio-app { height: calc(100vh - 48px); }

/* "Обработка макета" (processingStudio.jsx, 2026-07-30) — переключатель
   режима (Улучшить/Адаптировать/Подготовить) сидит НАД .studio-app одного
   из трёх режимов. .studio-app сам по себе рассчитан на высоту "от шапки
   сайта до низа экрана" (calc(100vh - 70px)) — с добавленной панелью
   вкладок это уже не так, поэтому здесь .studio-app переопределён на
   flex:1 внутри общей колонки высотой в весь остаток экрана, а вкладки
   занимают свою естественную высоту сверху. */
.processing-studio-shell {
  display: flex; flex-direction: column;
  height: calc(100vh - 70px);
  max-width: 1600px;
  margin: 0 auto;
  overflow: hidden;
}
.app-shell-main .processing-studio-shell { height: calc(100vh - 48px); }
.processing-studio-shell .studio-app {
  height: auto; flex: 1; min-height: 0; max-width: none; margin: 0;
}
/* Живой отзыв 2026-07-30: первая версия (тихие текстовые пилюли) терялась
   на странице — переключатель режима это ГЛАВНАЯ развилка всего экрана,
   должен читаться сразу, без разглядывания. Крупнее, с явной подложкой на
   весь ряд и однозначно контрастным заполненным (не просто tint) активным
   табом — тот же язык, что у большого "sh-sm" блока-переключателя, не у
   тихой ссылки. */
.processing-mode-switch {
  display: flex; gap: 6px; flex: none;
  padding: 14px 20px; background: var(--paper-2); border-bottom: 1px solid var(--line);
  box-shadow: var(--sh-sm);
}
/* 4 вкладки (Улучшить качество / Адаптировать формат / Подготовить к
   печати / Миниредактор — "AI умный анализ" был пятой, теперь встроена
   кнопкой в Подготовить к печати, см. printPrep.jsx, 2026-08-08) в сумме
   шире любого телефонного экрана — на десктопе ряд просто пошире одной
   строки не встаёт в проблему, а на мобильном при overflow-x по умолчанию
   visible (унаследовано, нигде явно не задан) содержимое молча вылезало за
   край экрана БЕЗ прокрутки: часть инструментов были физически недостижимы
   — не проскроллить, не увидеть (дизайн-аудит 2026-08-07). auto — тот же
   приём, что у .gallery-strip/.mini-saved-strip в этом файле, не новый
   паттерн для сайта. */
@media (max-width: 900px) {
  .processing-mode-switch {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    flex-wrap: nowrap;
    /* Затухание у правого края — единственный сигнал "тут ещё есть вкладки,
       прокрутите" (дизайн-аудит 2026-08-08: технически прокручивается уже
       с прошлой правки выше, но визуально ничем не намекало на это — вторая
       вкладка обрывалась ровно по краю экрана без всякого fade/стрелки). */
    -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 28px), transparent 100%);
  }
  .processing-mode-tab { flex: none; }
}
.processing-mode-tab {
  position: relative;
  display: inline-flex; align-items: center; gap: 9px;
  height: 48px; padding: 0 22px; border-radius: 11px; border: 1.5px solid var(--line-strong);
  background: var(--surface); color: var(--ink); font-family: var(--sans);
  font-size: 15px; font-weight: 700; cursor: pointer; transition: all .15s;
}
.processing-mode-tab:hover:not(.active) { border-color: var(--navy); color: var(--navy); }
.processing-mode-tab.active {
  background: var(--navy); color: #fff; border-color: var(--navy);
  box-shadow: 0 2px 8px rgba(28,58,87,.28);
}
/* Номер шага из recommendedOrder AI-анализа (запрос владельца 2026-07-31) —
   только цифра, без слова "шаг" (живой отзыв: занимало бы слишком много
   места на самой вкладке). Разовый пульс (.pulse) при РЕАЛЬНОМ переходе на
   шаг, не постоянное мигание — см. goToMode в processingStudio.jsx. */
.processing-step-badge {
  position: absolute; top: -8px; left: -8px; z-index: 1;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--bronze); color: #fff; font-size: 11px; font-weight: 800;
  display: grid; place-items: center; line-height: 1;
  border: 1.5px solid var(--paper-2); box-shadow: var(--sh-sm);
}
.processing-step-badge.pulse { animation: processingStepPulse .9s ease-out; }
@keyframes processingStepPulse {
  0%   { transform: scale(1.6); box-shadow: 0 0 0 6px var(--bronze-tint); }
  60%  { transform: scale(1); box-shadow: 0 0 0 0 transparent; }
  100% { transform: scale(1); box-shadow: 0 0 0 0 transparent; }
}

/* ---- settings panel ---- */
.settings-panel {
  width: 388px; flex: none;
  background: var(--surface);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
  min-height: 0;
  position: relative;
}
.settings-head { padding: var(--sp-6) var(--sp-6) var(--sp-4); border-bottom: 1px solid var(--line-soft); flex: none; }
.settings-title { font-size: 25px; letter-spacing: -.01em; white-space: nowrap; margin: 0; }
/* Lock overlay on settings panel during generation */
.settings-body { flex: 1; overflow-y: auto; overflow-x: clip; padding: var(--sp-6) var(--sp-6) var(--sp-8); display: flex; flex-direction: column; gap: var(--sp-5); position: relative; min-width: 0; }
.studio-app.is-generating .settings-body {
  pointer-events: none;
  user-select: none;
}
.studio-app.is-generating .settings-body::after {
  content: "";
  position: absolute; inset: 0;
  background: rgba(252,250,245,.75);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  z-index: 10;
  pointer-events: all;
}
.studio-app.is-generating .settings-foot {
  opacity: .45;
  pointer-events: none;
  transition: opacity .3s;
}

/* ============================================================
   PARAMS SIDE PANEL — ideogram-style постоянная (не поповер) колонка
   параметров слева от .settings-panel. Скрыта по умолчанию (width сжат
   до одной полоски-ручки), клик по полоске раздвигает layout — flex-item
   в .studio-app, поэтому открытие/закрытие честно двигает всё вправо,
   а не наезжает поверх контента. Светлая палитра сайта (не тёмная, как
   у ideogram) — те же токены, что и у .settings-panel.
   ============================================================ */
.params-side-panel {
  flex: none; width: 14px; background: var(--paper-2);
  border-right: 1px solid var(--line);
  display: flex; align-items: stretch;
  transition: width .22s var(--ease);
  position: relative;
  /* БЕЗ overflow:hidden здесь — раньше он резал .psp-toggle (см. ниже:
     ручка шире узкой 14px-полоски и намеренно "торчит" за правый край,
     position:absolute; right:0), из-за чего половина стрелки внутри неё
     невидимо обрезалась в ЛЮБОМ браузере, это была не проблема кэша.
     Обрезка содержимого при узкой ширине панели теперь на .psp-body
     (overflow-x:hidden там же) — он сосед ручки, не родитель, обрезается
     сам, а ручку не трогает. */
}
.params-side-panel.open { width: 240px; }
/* Полоска-ручка — целая плашка с заливкой, торчащая за правый край узкой
   14px-полоски (position:absolute; right:0, шире самой полоски НАРОЧНО —
   тот же визуальный язык, что у collapse-ручек в VS Code/Figma). Раньше
   .params-side-panel резал её своим overflow:hidden (см. выше) — теперь
   обрезка только на .psp-body, ручку можно спокойно делать шире полоски,
   не заботясь о клиппинге. Иконка внутри — контурный SVG-шеврон
   (см. studio.jsx ParamsSidePanel, тот же паттерн, что у .psp-group-caret). */
.psp-toggle {
  position: absolute; top: 50%; right: 0; transform: translateY(-50%);
  width: 22px; height: 64px; border-radius: 0 9px 9px 0;
  border: 1px solid var(--line-strong); border-left: none;
  background: var(--surface); color: var(--navy);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; z-index: 2; box-shadow: 2px 0 6px rgba(0,0,0,.05);
  transition: background .15s, color .15s;
}
.psp-toggle:hover { background: var(--navy-tint); color: var(--navy); }
/* Нативный title всплывает медленно и не показывается на touch-экранах —
   ручка сидит на самом краю вьюпорта и без подписи легко принять за
   обрезанный артефакт вёрстки, а не за элемент интерфейса. Тот же
   data-tooltip приём, что у .mq-btn выше, только справа от ручки (это
   вертикальный рельс у левого края, а не обычная кнопка в потоке). */
.psp-toggle[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute; top: 50%; left: calc(100% + 10px); transform: translateY(-50%);
  background: var(--surface); color: var(--ink-soft);
  border: 1px solid var(--line);
  padding: 7px 12px; border-radius: 8px;
  font-size: var(--text-xs); font-weight: 600; line-height: 1.4;
  white-space: nowrap; pointer-events: none; opacity: 0;
  transition: opacity .15s; z-index: 200; box-shadow: var(--sh-md);
}
.psp-toggle[data-tooltip]:hover::after,
.psp-toggle[data-tooltip]:focus-visible::after { opacity: 1; }
/* Стрелка внутри ручки была едва различима (12px в 24px viewBox читались
   как точка, не как треугольник) — увеличена в JSX до 16px. Здесь же —
   мягкая ненавязчивая подсказка "потяни": лёгкое покачивание вбок раз в
   несколько секунд, только пока панель закрыта (открытую саму стрелку не
   нужно дёргать — она уже сослужила службу). Отключается на
   prefers-reduced-motion — эта же анимация не пульс/мигание, а
   смещение позиции, к которому чувствительные пользователи наиболее
   уязвимы. */
.psp-toggle-arrow { opacity: .85; }
.params-side-panel:not(.open) .psp-toggle-arrow {
  animation: psp-nudge 2.8s ease-in-out infinite;
}
@keyframes psp-nudge {
  0%, 78%, 100% { transform: translateX(0); }
  86% { transform: translateX(2.5px); }
  94% { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .params-side-panel:not(.open) .psp-toggle-arrow { animation: none; }
}
.psp-body {
  flex: 1; min-width: 0; overflow-y: auto; overflow-x: hidden;
  padding: var(--sp-5) var(--sp-4) var(--sp-6);
  display: flex; flex-direction: column; gap: 2px;
}
.psp-group { border-bottom: 1px solid var(--line-soft); }
.psp-group:last-child { border-bottom: none; }
.psp-group-head {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 2px; background: none; border: none; cursor: pointer; text-align: left;
}
.psp-group-headtext { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.psp-group-title {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .04em;
}
/* Значение параметра — видно даже когда секция свёрнута ("Model / Auto" —
   тот же паттерн, что у ideogram), "Авто" когда пользователь ничего не
   выбрал вручную (см. PspGroup value prop). Раньше однострочный nowrap+
   ellipsis обрезал длинные значения ("Горизонтальная · Одна с…") — теперь
   переносится на 2 строки целиком, без обрезания. */
.psp-group-value {
  display: flex; flex-direction: column; gap: 1px;
  font-size: 13px; font-weight: 600; color: var(--ink);
  white-space: normal; line-height: 1.3; max-width: 168px;
}
/* Вторая строка значения (например размер "90×50 мм" под названием
   формата) — мельче и приглушённее первой, как подпись, а не как
   отдельное равнозначное значение. */
.psp-group-value-sub { font-size: 11px; font-weight: 500; color: var(--muted); }
.psp-group-caret { color: var(--muted); flex: none; transition: transform .15s; }
.psp-group.open .psp-group-caret { transform: rotate(180deg); }
.psp-group-body { padding: 0 2px 14px; display: flex; flex-direction: column; gap: 10px; }
.psp-dot { width: 6px; height: 6px; border-radius: 50%; background: #2FAE5C; flex: none; }
.psp-list { display: flex; flex-direction: column; gap: 4px; }
.psp-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 10px; border-radius: 9px; border: 1px solid var(--line);
  background: var(--surface); cursor: pointer; text-align: left;
  transition: border-color .15s, background .15s;
}
.psp-row:hover { border-color: var(--navy-line); }
.psp-row.active { border-color: var(--navy); background: var(--navy-tint); }
.psp-row-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.psp-row-lbl { font-size: 12.5px; font-weight: 600; color: var(--ink); }
.psp-row-sub { font-size: 10.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.psp-chip-wrap { display: flex; flex-wrap: wrap; gap: 6px; }
.psp-chip {
  padding: 6px 10px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-soft); font-size: 12px; font-weight: 600;
  cursor: pointer; transition: all .15s;
}
.psp-chip:hover { border-color: var(--navy-line); }
.psp-chip.active { border-color: var(--navy); background: var(--navy); color: #fff; }
/* Расположение — тот же FormatTiles, что в поповере чипа "Формат", "точно
   как в чипсах" — превью + два переключателя (.ft-split уже колонка сама
   по себе, .qf-tiles сам переносится строками на узкой панели, доп.
   override под однoколоночность не нужен). */
/* Размер — тот же .size-attach-panel/.size-attach-grid, что в поповере чипа
   "Размер" (SizeAttachButton), только без внешней рамки/фона поповера
   (панель уже сама даёт контекст) и с более узкими колонками под 240px. */
.psp-group-body > .size-attach-panel { background: none; border: none; padding: 0; }
.psp-group-body .size-attach-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
/* 2 в ряд, крупнее (было 44px в свободной flex-обёртке — мелко и плохо
   читалось). Панель открытая — 240px, minus padding var(--sp-4)×2 (32px) =
   208px на 2 колонки с зазором 8px → ровно 100px на миниатюру. ── */
.psp-thumbs { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.psp-thumb { position: relative; width: 100%; aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1px solid var(--line); cursor: zoom-in; }
.psp-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Тот же акцент "оборот", что и у .composer-chip-back в композере — единый
   визуальный язык, лицо/оборот различимы одинаково в обоих местах. */
.psp-thumb-back { border-color: var(--navy); box-shadow: 0 0 0 1px var(--navy); }
/* Подпись подгруппы "Лицо"/"Оборот" внутри секции "Загруженные фото" —
   показывается, только когда есть фото с обеих сторон (см. ParamsSidePanel),
   тот же приём (мелкая заглавная подпись), что у .zone-label/.attach-menu-divider. */
.psp-sub-label {
  font-size: 9.5px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin: 10px 0 6px;
}
.psp-sub-label:first-child { margin-top: 0; }
.psp-thumb-remove {
  position: absolute; top: 3px; right: 3px; width: 18px; height: 18px; border-radius: 50%;
  background: rgba(20,20,24,.7); color: #fff; border: none; font-size: 12px; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; z-index: 2;
}
/* Ховер-увеличение ×5 — тот же портал-паттерн, что и .spm-zoom у MirrorTile
   (см. studio.jsx MirrorTile/showZoom), просто больше кратность и без
   подписи снизу (для фото она не нужна). */
.psp-thumb-zoom {
  position: fixed; z-index: 1000; pointer-events: none;
  border-radius: 14px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-lg);
  animation: spmZoomIn .12s var(--ease) both;
}
.psp-thumb-zoom img { width: 100%; height: 100%; object-fit: contain; background: var(--paper-2); display: block; }

/* Reveal animation when results appear */
@keyframes revealUp {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}
.results-reveal {
  /* Видимое конечное состояние — базовое (важно для PDF/печати/скриншотов). */
  opacity: 1; transform: none;
}
@media (prefers-reduced-motion: no-preference) {
  .results-reveal { animation: revealUp .5s cubic-bezier(.22,.61,.36,1) both; }
}

/* Подтверждение "точно сохранено" под готовым результатом на холсте (см.
   studio.jsx, studio.canvas.savedNotice* — запрос владельца 2026-08-07).
   Тот же голос, что и .pm-cta-credits-link (ссылка на Кабинет под кнопкой
   "Сгенерировать" во время ожидания), просто отдельным неймспейсом —
   тот селектор завязан на .pm-cta-row/тёмную заливку кнопки, здесь же
   светлый фон холста. */
.results-saved-notice {
  text-align: center; margin-top: var(--sp-5);
  font-size: var(--text-sm); color: var(--muted);
}
.results-saved-notice-link { color: var(--navy); cursor: pointer; text-decoration: underline; font-weight: 600; }
.results-saved-notice-link:hover { color: var(--navy-deep); }

.settings-foot { flex: none; border-top: 1px solid var(--line); padding: var(--sp-4) var(--sp-6) var(--sp-5); background: var(--surface); }

/* Centered content column — keeps the form from sprawling edge-to-edge
   in full-width mode, falls back to tight padding in the 40% split.
   margin: 0 (а не auto) было багом — расстояние слева/справа не совпадало
   (жалоба владельца 2026-08-03, скриншот): при max-width:920px < ширины
   панели контент просто прилипал к левому краю, а не центрировался, как
   обещает сам комментарий выше. */
.settings-head > *, .settings-foot > * { max-width: 920px; margin-left: auto; margin-right: auto; }
.settings-body > * { width: 100%; max-width: 920px; margin-left: auto; margin-right: auto; }

.mode-tabs { display: grid; grid-template-columns: repeat(3,1fr); gap: 6px; background: var(--paper-2); padding: 5px; border-radius: 12px; }
.mode-tab {
  border: none; background: transparent; border-radius: 9px;
  padding: 10px 6px; display: flex; flex-direction: column; align-items: center; gap: 5px;
  color: var(--muted); font-weight: 600; font-size: var(--text-sm); cursor: pointer; transition: all .15s;
}
.mode-tab.active { background: var(--surface); color: var(--navy); box-shadow: var(--sh-sm); }

/* rich mode tabs with subtitle */
.mode-tab-rich {
  text-align: center; padding: 11px 10px; gap: 3px;
  justify-content: flex-start;
}
.mode-tab-title { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm); font-weight: 700; }
.mode-tab-sub {
  font-size: var(--text-xs); color: var(--muted); margin-top: 2px; line-height: 1.3;
  font-weight: 500;
}
.mode-tab-rich.active .mode-tab-sub { color: var(--navy); opacity: .72; }

/* download hint — .dl-actions ушёл в зону .region-zone-export (см. ниже),
   кнопки экспорта теперь у самого холста, рядом с зоной правок. */
.dl-hint { font-size: var(--text-xs); color: var(--muted); margin: 0; text-align: right; line-height: 1.4; }

/* ── QR block (расширенный режим) ── */
.qr-block { border: 1.5px solid var(--line); border-radius: 12px; background: var(--paper); overflow: hidden; }
.qr-block-toggle { display: flex; align-items: center; justify-content: space-between; padding: 10px 13px; cursor: pointer; user-select: none; transition: background .15s; }
.qr-block-toggle:hover { background: var(--paper-2); }
.qr-block-left { display: flex; align-items: center; gap: 8px; }
.qr-block-icon { width: 26px; height: 26px; border-radius: 7px; background: var(--navy-tint); display: grid; place-items: center; flex: none; }
.qr-block-lbl { font-size: var(--text-sm); font-weight: 600; color: var(--ink); }
.qr-block-badge { font-size: var(--text-xs); font-weight: 600; color: var(--navy); background: var(--navy-tint); border-radius: 20px; padding: 1px 7px; }
.qr-block-chevron { font-size: var(--text-xs); color: var(--muted); transition: transform .2s; }
.qr-block.open .qr-block-chevron { transform: rotate(180deg); }
.qr-block-body { display: none; padding: 0 12px 12px; flex-direction: column; gap: 8px; }
.qr-block.open .qr-block-body { display: flex; }
.qr-item { border: 1.5px solid var(--line); border-radius: 10px; background: var(--surface); overflow: hidden; transition: border-color .18s; }
.qr-item:focus-within { border-color: var(--navy); }
.qr-item-top { display: flex; align-items: flex-start; gap: 9px; padding: 10px 11px 7px; }
.qr-item-thumb { width: 38px; height: 38px; border-radius: 7px; background: var(--paper-2); border: 1px solid var(--line); display: grid; place-items: center; flex: none; overflow: hidden; }
.qr-item-inputs { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.qr-ifield { border: 1.5px solid var(--line); border-radius: 8px; font-size: var(--text-sm); font-family: var(--sans); outline: none; width: 100%; color: var(--ink); background: var(--paper); padding: 7px 11px; transition: border-color .15s, background .15s; }
.qr-ifield:focus { border-color: var(--navy); background: var(--surface); box-shadow: 0 0 0 3px rgba(28,58,87,.08); }
.qr-ifield::placeholder { color: var(--line-strong); }
.qr-ifield.cap { font-size: var(--text-xs); color: var(--ink-soft); }
.qr-item-del { width: 26px; height: 26px; border-radius: 7px; border: 1px solid var(--line); background: transparent; color: var(--muted); cursor: pointer; font-size: var(--text-base); display: grid; place-items: center; flex: none; transition: all .15s; margin-top: 3px; }
.qr-item-del:hover { border-color: #E05050; color: #E05050; background: #FEF0F0; }
.qr-item-foot { display: flex; gap: 5px; padding: 0 11px 10px; }
.qr-item-confirm-row { padding: 0 11px 11px; }
.qr-item-confirm-btn { display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%; padding: 8px; border-radius: 8px; border: none; background: var(--navy); color: #fff; font-size: var(--text-xs); font-weight: 600; cursor: pointer; transition: all .15s; font-family: var(--sans); }
.qr-item-confirm-btn:hover:not(:disabled) { background: var(--navy-2, #142A40); }
.qr-item-confirm-btn:disabled { opacity: .35; cursor: not-allowed; }
.qr-item-confirmed { border-color: var(--good); background: var(--paper-2); }
.qr-item-confirmed .qr-item-top { padding: 9px 11px; align-items: center; }
.qr-item-confirmed-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.qr-item-confirmed-url { font-size: var(--text-sm); font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-item-confirmed-meta { font-size: 11px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-item-edit-btn { flex: none; padding: 5px 10px; border-radius: 7px; border: 1.5px solid var(--line); background: var(--paper); color: var(--navy); font-size: var(--text-xs); font-weight: 600; cursor: pointer; transition: all .15s; font-family: var(--sans); }
.qr-item-edit-btn:hover { border-color: var(--navy); background: var(--navy-tint); }
.qr-pp { font-size: var(--text-xs); font-weight: 600; padding: 5px 11px; border-radius: 7px; border: 1.5px solid var(--line); background: var(--paper); color: var(--muted); cursor: pointer; transition: all .15s; font-family: var(--sans); white-space: nowrap; }
.qr-pp.active { border-color: var(--navy); background: var(--navy-tint); color: var(--navy); }
.qr-add-btn { display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%; padding: 9px; border-radius: 9px; border: 1.5px dashed var(--line-strong); background: transparent; color: var(--muted); font-size: var(--text-xs); font-weight: 600; cursor: pointer; transition: all .18s; font-family: var(--sans); }
.qr-add-btn:hover { border-color: var(--navy); color: var(--navy); background: var(--navy-tint); }
.qr-add-btn:disabled { opacity: .4; cursor: not-allowed; }
.qr-note { font-size: var(--text-xs); color: var(--muted); display: flex; align-items: flex-start; gap: 6px; line-height: 1.45; padding: 7px 9px; background: var(--paper-2); border-radius: 8px; }
.fp-strip {
  display: flex; gap: 7px;
}
.fp-card {
  flex: 1; border: 2px solid var(--line);
  border-radius: 12px; background: var(--paper);
  padding: 10px 8px 9px;
  cursor: pointer; transition: all .18s;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  position: relative; font-family: var(--sans);
}
.fp-card:hover { border-color: var(--navy); background: var(--navy-tint, #EEF3F8); }
.fp-card.active {
  border-color: var(--navy); background: var(--navy-tint, #EEF3F8);
  box-shadow: 0 0 0 3px rgba(28,58,87,.10);
}
.fp-check {
  position: absolute; top: 6px; right: 7px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--navy); color: #fff;
  font-size: var(--text-xs); font-weight: 700;
  display: grid; place-items: center;
}
.fp-thumb {
  height: 56px; display: flex; align-items: center; justify-content: center;
}
.fp-label { font-size: var(--text-xs); font-weight: 600; color: var(--ink); text-align: center; line-height: 1.3; }
.fp-sub   { font-size: var(--text-xs); color: var(--muted); text-align: center; line-height: 1.3; margin-top: -2px; }
.fp-size  { font-size: 9.5px; color: var(--muted); background: var(--paper-2); border-radius: 4px; padding: 1px 5px; margin-top: 1px; }

/* ── Format category cards (Print / Digital) ── */
.fcc-wrap { display: flex; flex-direction: column; gap: 14px; margin-bottom: 12px; }
.fcc-group-head {
  display: flex; align-items: center; gap: 7px;
  font-size: var(--text-xs); font-weight: 700; color: var(--ink);
  letter-spacing: .01em; margin-bottom: 7px;
}
.fcc-group-meta { margin-left: auto; font-size: var(--text-xs); font-weight: 500; color: var(--muted); letter-spacing: 0; }
.fcc-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.fcc-dot-print   { background: var(--navy); }
.fcc-dot-digital { background: var(--bronze); }
.fcc-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; }
.fcc-card {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px;
  border: 2px solid var(--line); border-radius: 12px; background: var(--paper);
  padding: 11px 6px 9px; cursor: pointer; font-family: var(--sans);
  transition: border-color .15s, background .15s, box-shadow .15s;
}
.fcc-card:hover { border-color: var(--navy); background: var(--navy-tint, #EEF3F8); }
.fcc-card.digital:hover { border-color: var(--bronze); background: var(--bronze-tint); }
.fcc-card.active { border-color: var(--navy); background: var(--navy-tint, #EEF3F8); box-shadow: 0 0 0 3px rgba(28,58,87,.10); }
.fcc-card.digital.active { border-color: var(--bronze); background: var(--bronze-tint); box-shadow: 0 0 0 3px rgba(109,94,240,.14); }
.fcc-check {
  position: absolute; top: 5px; right: 6px;
  width: 15px; height: 15px; border-radius: 50%;
  background: var(--navy); color: #fff; font-size: var(--text-xs);
  display: grid; place-items: center; line-height: 1;
}
.fcc-card.digital .fcc-check { background: var(--bronze); }
.fcc-ic    { font-size: var(--text-lg); line-height: 1; }
.fcc-label { font-size: 10.5px; font-weight: 600; color: var(--ink); text-align: center; line-height: 1.25; }
.fcc-size  { font-size: var(--text-xs); color: var(--muted); background: var(--paper-2); border-radius: 4px; padding: 1px 5px; }
.digital-fmt-note {
  display: flex; align-items: flex-start; gap: 6px;
  font-size: var(--text-xs); line-height: 1.45; color: var(--bronze);
  background: var(--bronze-tint); border: 1px solid rgba(109,94,240,.22);
  border-radius: 10px; padding: 9px 12px;
}
.mode-quick-seg {
  display: flex; background: var(--paper-2); border-radius: 10px; padding: 3px; gap: 2px;
  width: 100%;
}
/* ════════════════════════════════════════════════════════════
   UNIFIED MONOLITHIC SEGMENTED CONTROL
   Единая серая подложка, активный элемент — белый с лёгкой тенью.
   ════════════════════════════════════════════════════════════ */
.seg {
  display: flex; gap: 3px; padding: 4px; width: 100%;
  background: var(--paper-2); border: 1px solid var(--line);
  border-radius: 13px;
}
.seg-btn {
  flex: 1; border: none; background: transparent; border-radius: 9px;
  cursor: pointer; font-family: var(--sans); color: var(--ink-soft);
  font-size: var(--text-sm); font-weight: 600; line-height: 1.3; position: relative;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 10px;
  transition: background .18s, color .18s, box-shadow .18s;
}
.seg-btn:not(.active):hover { color: var(--ink); background: rgba(20,19,15,.035); }
.seg-btn.active {
  background: #fff; color: var(--ink);
  box-shadow: 0 1px 3px rgba(20,19,15,.13), 0 1px 1px rgba(20,19,15,.05);
}
.seg-btn:focus-visible { outline: 2px solid var(--navy); outline-offset: 1px; }
/* two-line variant — для переключателя режимов */
.seg-2l .seg-btn { flex-direction: column; align-items: flex-start; justify-content: center; gap: 1px; height: 38px; padding: 0 12px; text-align: left; }
.seg-btn-main { display: flex; align-items: center; gap: 6px; font-size: 13.5px; font-weight: 700; }
.seg-btn-sub  { font-size: var(--text-xs); font-weight: 400; color: var(--muted); white-space: nowrap; }
.seg-btn.active .seg-btn-sub { color: var(--ink-soft); }

/* quick/advanced mode toggle — now a segmented control */
.mode-quick-row { display: flex; flex-direction: row; gap: 8px; }
.mq-btn { flex: 1; border-radius: 12px; border: 1.5px solid var(--line); background: var(--surface); color: var(--ink-soft); font-size: 13.5px; font-weight: 600; cursor: pointer; font-family: var(--sans); position: relative; display: flex; flex-direction: row; align-items: center; justify-content: flex-start; padding: 10px 14px; gap: 8px; transition: all .18s; box-shadow: var(--sh-sm); text-align: left; }
.mq-btn.active { background: var(--navy); color: #fff; border-color: var(--navy); box-shadow: 0 4px 18px rgba(28,58,87,.28); }
.mq-btn:not(.active):hover { border-color: var(--navy-line); color: var(--ink); background: var(--navy-tint); }
.mq-sub { font-size: var(--text-xs); font-weight: 400; opacity: .72; line-height: 1.3; white-space: nowrap; }

/* ── Material tabs (Загрузить изображения / Иконки / QR) ── */
.mat-tabs {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px;
}
.mat-tab {
  position: relative; min-width: 0; cursor: pointer; font-family: var(--sans);
  color: var(--ink-soft);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  min-height: 130px;
  padding: 10px 5px 9px; text-align: center;
  border: 1.5px solid var(--line); background: var(--surface); border-radius: 11px;
  transition: background .16s, color .16s, border-color .16s, box-shadow .16s;
}
@media (max-width: 768px) {
  .mat-tab { min-height: 110px; }
}
.mat-tab:not(.active):hover { color: var(--ink); border-color: var(--line-strong); }
.mat-tab.active {
  background: var(--navy-tint); color: var(--ink);
  border-color: var(--navy); box-shadow: 0 0 0 2.5px rgba(28,58,87,.12);
}
.mat-tab-ic { display: flex; align-items: center; justify-content: center; min-height: 20px; font-size: 16px; }
.mat-tab-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; width: 100%; }
.mat-tab-lbl { font-size: var(--text-xs); font-weight: 700; line-height: 1.15; }
.mat-tab-sub { font-size: var(--text-xs); font-weight: 400; color: var(--muted); line-height: 1.2; }
.mat-tab.active .mat-tab-sub { color: var(--ink-soft); }
.mat-tab-count { color: var(--good); font-weight: 700; }
/* filled-but-inactive: dot indicator */
.mat-tab.filled:not(.active) .mat-tab-dot {
  position: absolute; top: 6px; right: 7px;
  width: 12px; height: 12px; border-radius: 50%; background: #A3E635;
  box-shadow: 0 0 0 2px var(--surface);
}
.mat-tab.filled .mat-tab-lbl { color: var(--good); }
.mat-tab.filled.active .mat-tab-lbl { color: var(--ink); }

/* Мини-превью содержимого вкладки: 1 элемент — крупная миниатюра, 2+ — упаковка мелких */
.mat-tab-preview { display: flex; align-items: center; justify-content: center; }
.mat-tab-preview.single .mat-thumb-img {
  width: 32px; height: 32px; border-radius: 7px; object-fit: cover;
  box-shadow: 0 0 0 1.5px var(--line-strong);
}
.mat-tab-preview.single .mat-thumb-social {
  min-width: 32px; height: 26px; padding: 0 8px; border-radius: 7px;
  font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center;
}
.mat-tab-preview.pack {
  display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; gap: 5px;
}
.mat-tab-preview.pack .mat-thumb-img { width: 45px; height: 45px; border-radius: 9px; object-fit: cover; flex: none; }
.mat-tab-preview.pack .mat-thumb-social {
  width: 45px; height: 45px; border-radius: 9px; overflow: hidden; flex: none;
  font-size: 15px; font-weight: 700; display: flex; align-items: center; justify-content: center;
}
.mat-thumb-more {
  width: 45px; height: 45px; border-radius: 9px; background: var(--paper-2); color: var(--muted); flex: none;
  font-size: 15px; font-weight: 700; display: flex; align-items: center; justify-content: center;
}

/* Плитка загрузки — открывается внутри своей ячейки вместо отдельной панели */
.mat-tab-upload {
  justify-content: center; border-style: dashed; border-color: var(--line-strong);
  background: var(--paper-2);
}
.mat-tab-upload:hover, .mat-tab-upload.drag { border-color: var(--navy); background: var(--navy-tint); }
.mat-tab-upload.full { cursor: default; opacity: .55; }
.mat-tab-upload-x {
  position: absolute; top: 4px; right: 4px; width: 18px; height: 18px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%; background: rgba(255,255,255,.8);
  color: var(--muted); cursor: pointer;
}
.mat-tab-upload-x:hover { color: var(--ink); background: #fff; }
.mat-tab-upload-x::before { content: ""; position: absolute; inset: -6px; }
.mat-tab-upload-thumb { width: 42px; height: 42px; border-radius: 9px; object-fit: cover; flex: none; }

/* Заменить / Удалить — раздельные действия для одиночного файла (логотип) */
.mat-tab-upload-row { display: flex; align-items: center; gap: 6px; }
.mat-tab-upload-del {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; padding: 0; border: none; border-radius: 50%;
  background: rgba(0,0,0,.05); color: var(--muted); cursor: pointer; flex: none;
}
.mat-tab-upload-del:hover { color: #C0392B; background: rgba(192,57,43,.12); }
.mat-tab-upload-del::before { content: ""; position: absolute; inset: -6px; }

/* Сетка уже загруженных файлов внутри открытой плитки (Свой макет / Изображения) —
   у каждого своя кнопка удаления, чтобы не нужно было искать assets-strip внизу. */
.mat-tab-upload-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 9px; max-width: 100%; padding: 0 4px; }
.mat-tab-upload-item {
  position: relative; width: 51px; height: 51px; border-radius: 10px; flex: none;
  border: none; padding: 0; cursor: default;
}
.mat-tab-upload-item img { width: 100%; height: 100%; border-radius: 10px; object-fit: cover; display: block; }
.mat-tab-upload-item-x {
  position: absolute; top: -9px; right: -9px; width: 24px; height: 24px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--surface); border-radius: 50%; background: #C0392B; color: #fff; cursor: pointer;
}
.mat-tab-upload-item-x::before { content: ""; position: absolute; inset: -4px; }
/* Второй бейдж на том же тайле — «Распознать» (см. .mat-tab-upload-item-x
   выше, зеркальный угол, свой цвет, чтобы две круглые кнопки не путались). */
.mat-tab-upload-item-ocr {
  position: absolute; bottom: -9px; left: -9px; width: 24px; height: 24px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--surface); border-radius: 50%; background: var(--navy); color: #fff;
  font-size: 8.5px; font-weight: 800; letter-spacing: -.02em; cursor: pointer;
}
.mat-tab-upload-item-ocr:hover { background: #0f2338; }
.mat-tab-upload-item-ocr:disabled { cursor: default; opacity: .7; }
.mat-tab-upload-item-ocr::before { content: ""; position: absolute; inset: -4px; }
.mat-tab-upload-add {
  display: flex; align-items: center; justify-content: center;
  border: 1.5px dashed var(--line-strong); background: var(--surface); color: var(--muted); cursor: pointer;
}
.mat-tab-upload-add:hover { border-color: var(--navy); color: var(--navy); background: var(--navy-tint); }

.qr-inline { margin-top: 10px; display: flex; flex-direction: column; gap: 10px; }

/* Tooltip for quick/advanced buttons */
.mq-btn[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute; top: calc(100% + 10px); left: 0;
  background: var(--surface); color: var(--ink-soft);
  border: 1px solid var(--line);
  padding: 9px 13px; border-radius: 9px;
  font-size: var(--text-xs); font-weight: 400; line-height: 1.55;
  white-space: normal; width: 230px; text-align: left;
  pointer-events: none; opacity: 0;
  transition: opacity .18s; z-index: 200;
  box-shadow: var(--sh-md);
}
.mq-btn[data-tooltip]::before {
  content: "";
  position: absolute; top: calc(100% + 4px); left: 28px;
  border: 5px solid transparent;
  border-bottom-color: var(--line);
  pointer-events: none; opacity: 0;
  transition: opacity .18s; z-index: 201;
}
.mq-btn[data-tooltip]:hover::after,
.mq-btn[data-tooltip]:hover::before { opacity: 1; }
.mq-btn.active { background: var(--navy); color: #fff; border-color: var(--navy); }
.mq-btn:not(.active):hover { border-color: var(--line-strong); color: var(--ink); }
.mq-sub { font-size: var(--text-xs); font-weight: 400; opacity: .75; }

/* ── Placeholder nudge: "не знаете, что написать? заполним за вас" —
   всплывает через 10 сек. простоя на пустом поле (см. studio.jsx PromptMode) ── */
.ph-nudge {
  display: flex; flex-direction: column; gap: 9px;
  /* Свой, а не общий --glass-bg (запрос владельца 2026-08-03, "в 2 раза
     прозрачнее") — --glass-bg общий на ~12 разных всплывающих панелей
     сайта (поповеры/меню), приглушать его глобально означало бы задеть
     их все разом. Здесь ровно половина исходной непрозрачности этой
     конкретной подсказки: светлая тема .66→.33, тёмная .58→.29. */
  background: rgba(255,255,255,.33);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border);
  border-radius: 16px; padding: 11px 14px;
  box-shadow: var(--glass-shadow);
  animation: hintIn .25s var(--ease) forwards;
}
[data-theme="dark"] .ph-nudge { background: rgba(35,38,44,.29); }
/* Подсказка про ряд иконок композера (studio.jsx showIconHint) — только
   мобильная ширина: на десктопе те же иконки уже объясняет hover+title,
   которого на тач-экране просто не существует (см. мобильный аудит). */
@media (min-width: 769px) { .composer-icon-hint { display: none; } }
.ph-nudge-head { display: flex; align-items: flex-start; gap: 8px; }
.ph-nudge-icon { flex: none; font-size: 15px; line-height: 1.4; }
.ph-nudge-text { flex: 1; font-size: var(--text-sm); color: var(--ink-soft); line-height: 1.4; }
.ph-nudge-close {
  flex: none; background: none; border: none; cursor: pointer;
  color: var(--muted); font-size: 16px; line-height: 1; padding: 0 2px; opacity: .6;
}
.ph-nudge-close:hover { opacity: 1; }
.ph-nudge-pill-trigger {
  all: unset; box-sizing: border-box; cursor: pointer;
  display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0;
}
.ph-nudge-pill-trigger .ph-nudge-text { transition: color .15s; }
.ph-nudge-pill-trigger:hover .ph-nudge-text { color: var(--navy); }
.ph-nudge-pill-arrow { flex: none; color: var(--muted); font-size: 13px; transition: transform .15s, color .15s; }
.ph-nudge-pill-trigger:hover .ph-nudge-pill-arrow { color: var(--navy); transform: translateX(2px); }
.studio-auto-gen-intro { margin-bottom: 14px; }

/* Кнопка "Удалить" в Истории/Мои макеты — приглушённая по умолчанию,
   краснеет только при наведении (доп. отступ от "Скачать"/"Открыть в
   студии" задаётся инлайново в pages.jsx, чтобы не задеть случайным
   кликом — запрос 2026-07-27). */
.hist-delete-btn { color: var(--muted); }
.hist-delete-btn:hover { color: #DC2626; border-color: #DC2626; }

/* Переключатель Список/Сетка в Кабинете → История (слияние с "Мои
   макеты", запрос 2026-07-27) — тот же язык сегмент-контрола, что и
   .cab-tabs рядом, только компактнее и без подписей (только иконки). */
.cab-view-toggle { display: flex; border: 1px solid var(--line); border-radius: 10px; padding: 3px; gap: 2px; background: var(--surface); }
.cab-view-btn { display: grid; place-items: center; width: 32px; height: 30px; border: none; border-radius: 7px; background: none; color: var(--muted); cursor: pointer; transition: background .15s, color .15s; }
.cab-view-btn:hover { color: var(--ink); }
.cab-view-btn.active { background: var(--navy); color: #fff; }
.ph-nudge-chips { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ph-nudge-chip {
  padding: 6px 12px; border-radius: 20px; font-size: var(--text-xs); font-weight: 600;
  border: 1.5px solid var(--navy-line); background: var(--surface); color: var(--navy);
  cursor: pointer; font-family: var(--sans); transition: background .15s, border-color .15s, color .15s;
}
.ph-nudge-chip:hover { background: var(--navy); border-color: var(--navy); color: #fff; }
.ph-nudge-reroll {
  padding: 6px 10px; border-radius: 20px; font-size: var(--text-xs); font-weight: 600;
  border: 1.5px dashed var(--line-strong); background: none; color: var(--muted);
  cursor: pointer; font-family: var(--sans); transition: color .15s, border-color .15s;
}
.ph-nudge-reroll:hover { color: var(--ink-soft); border-color: var(--muted); }

/* ── Превью перед вставкой (см. proposeInsertion/confirmPendingInsertion в
   studio.jsx) — раньше клик по профессии/формату сразу перезаписывал поле
   без единого шанса посмотреть на результат; теперь сначала показываем
   текст, вставка — только по явному подтверждению. ── */
.ph-nudge-preview {
  max-height: 220px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 8px; padding: 10px 12px;
  font-size: var(--text-sm); color: var(--ink); line-height: 1.5;
  white-space: pre-wrap;
}
.ph-nudge-preview-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* Переключатель вариантов (запрос 2026-07-22: реальный выбор из нескольких
   готовых текстов вместо одного случайного) — компактная строка между
   превью и кнопками "Вставить"/"Не нужно". */
.ph-nudge-variant-switch {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  font-size: var(--text-xs); color: var(--muted);
}
.ph-nudge-variant-arrow {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%; padding: 0;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); font-size: 16px; line-height: 1; cursor: pointer;
  transition: border-color .15s, color .15s;
}
.ph-nudge-variant-arrow:hover { border-color: var(--navy); color: var(--navy); }
.ph-nudge-variant-count { min-width: 90px; text-align: center; }

/* ── Шаг «формат» подсказки пустого поля (NudgeFormatPicker) ── */
.nudge-format-picker { display: flex; flex-direction: column; gap: 10px; }
.nudge-format-head {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--text-sm); color: var(--ink-soft);
}
.nudge-format-back {
  flex: none; width: 22px; height: 22px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-soft);
  cursor: pointer; font-size: 13px; line-height: 1;
}
.nudge-format-back:hover { border-color: var(--navy); color: var(--navy); }

/* AI style hint bar */
/* Studio flyer size hints */
.studio-flyer-hints {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--navy-tint); border: 1px solid var(--navy-line);
  border-radius: 11px; padding: 10px 14px;
  animation: hintIn .2s var(--ease) .04s forwards;
  opacity: 0; transform: translateY(-4px);
}
/* Разделитель печатных форматов (мм) и цифровых (px — карточки маркетплейсов/
   инфографика/соцсети) в NudgeFormatPicker — flex-basis:100% переносит всё,
   что идёт после, на новую "строку" внутри flex-wrap, а border-top этой же
   пустой полосы рисует саму горизонтальную линию во всю ширину блока. */
.nudge-format-grid .nudge-format-divider {
  flex-basis: 100%; height: 0; margin: 2px 0;
  border-top: 1px solid var(--navy-line);
}
.sfh-label {
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap;
}
.sfh-note {
  /* было var(--faint) — контраст ~2.5:1 на этом фоне, ниже WCAG AA (4.5:1)
     для обычного 11px текста; это реальная подсказка-действие («Нажмите,
     чтобы добавить размер»), а не декоративный плейсхолдер. */
  font-size: var(--text-xs); color: var(--muted); margin-left: auto; white-space: nowrap;
}
.sfh-btn {
  display: inline-flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 6px 16px; border-radius: 8px;
  border: 1.5px solid var(--navy-line); background: var(--surface);
  cursor: pointer; font-family: var(--sans);
  transition: background .15s var(--ease), border-color .15s, box-shadow .14s, transform .18s var(--ease);
}
.sfh-btn:hover {
  background: var(--navy); border-color: var(--navy);
  box-shadow: 0 4px 12px rgba(28,58,87,.22);
  transform: translateY(-2px);
}
.sfh-btn strong { font-size: var(--text-sm); font-weight: 700; color: var(--navy); line-height: 1.2; transition: color .15s; }
.sfh-btn span   { font-size: var(--text-xs); color: var(--muted); line-height: 1; transition: color .15s; }
.sfh-btn:hover strong,
.sfh-btn:hover span { color: #fff; }
/* Уже выбранный формат — раньше ничем не отличался от остальных чипов,
   человек открывал меню размера и не мог понять, что уже выбрано именно
   "Евровизитка" (или любой другой формат). */
.sfh-btn.active {
  background: var(--navy); border-color: var(--navy);
  box-shadow: 0 2px 8px rgba(28,58,87,.2);
}
.sfh-btn.active strong,
.sfh-btn.active span { color: #fff; }

/* ── "Свой размер" (CustomSizeInput) — одно поле вместо ширины/высоты
   отдельными полями (часть пользователей эти термины не связывает с тем,
   что у них в голове), плюс живой прямоугольник-превью пропорций. ── */
.sfh-custom-toggle { border-style: dashed; }
.sfh-custom {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--navy-tint); border: 1px solid var(--navy-line);
  border-radius: 11px; padding: 10px 14px; width: 100%;
}
.sfh-custom-rect-box {
  width: 44px; height: 44px; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1px solid var(--navy-line); border-radius: 8px;
}
.sfh-custom-rect {
  background: var(--navy); border-radius: 2px;
  transition: width .15s var(--ease), height .15s var(--ease), opacity .15s var(--ease);
}
.sfh-custom-fields { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 180px; }
.sfh-custom-input {
  font-family: var(--sans); font-size: var(--text-sm); font-weight: 600; color: var(--navy);
  border: 1.5px solid var(--navy-line); border-radius: 7px; padding: 6px 10px;
  background: var(--surface); outline: none;
}
.sfh-custom-input:focus { border-color: var(--navy); }
.sfh-custom-readout { font-size: var(--text-xs); color: var(--muted); }
.sfh-custom-readout-warn { color: var(--warn, #b45309); }
.sfh-custom-apply { padding: 6px 14px; }
.sfh-custom-close {
  border: none; background: none; cursor: pointer; color: var(--muted);
  font-size: 18px; line-height: 1; padding: 4px 6px; flex: none;
}
.sfh-custom-close:hover { color: var(--navy); }

/* ── Quick-format tiles (стороны / ориентация) — эти же классы теперь
   переиспользует и FormatTiles (см. styles.css выше и studio.jsx), один
   визуальный язык на авто-подсказки и на сам поповер "Формат". ── */
.qf-tiles { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.qf-tiles-label {
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; margin-right: 2px;
}
.qf-tile {
  position: relative; display: flex; align-items: center; gap: 9px;
  padding: 9px 15px 9px 12px; border-radius: 12px;
  border: 1.5px solid var(--line); background: var(--surface);
  cursor: pointer; font-family: var(--sans);
  transition: border-color .15s, background .15s, box-shadow .15s;
}
.qf-tile:hover:not(.active) { border-color: var(--line-strong); box-shadow: 0 2px 10px rgba(0,0,0,.05); }
.qf-tile.active {
  border-color: var(--navy); border-width: 2px; padding: 8.5px 14.5px 8.5px 11.5px;
  background: var(--navy-tint);
  box-shadow: 0 0 0 2.5px rgba(28,58,87,.12);
}
.qf-tile-icon { display: flex; align-items: center; justify-content: center; color: var(--navy); flex: none; }
/* min-width: 0 на .qf-tile-body само по себе не хватает: с align-items:
   flex-start заголовок/подпись внутри — тоже flex-элементы (колонки) и
   каждый сам по себе меряется по своему содержимому (fit-content), а не
   по фактической ширине body. align-items убран (дефолт — stretch): дети
   растягиваются на ширину body, и тогда обычный перенос текста по словам
   начинает работать. */
.qf-tile-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
/* "Горизонтальная"/"Вертикальная" — цельные слова без пробелов, обычный
   перенос по словам (align-items: stretch выше) их не спасает, если
   слово само по себе шире плитки: переносить браузеру просто негде, и
   текст вылезает за рамку поверх галочки (жалоба 2026-07-21, .ft-split,
   узкая колонка). overflow-wrap разрешает перенос ПОСРЕДИ слова именно
   в этом крайнем случае — на переносы обычных многословных подписей не
   влияет. */
.qf-tile-title { font-size: var(--text-sm); font-weight: 700; color: var(--ink); line-height: 1.15; overflow-wrap: break-word; }
.qf-tile-sub { font-size: var(--text-xs); font-weight: 500; color: var(--muted); line-height: 1.2; }
.qf-tile.active .qf-tile-sub { color: var(--ink-soft); }
.qf-tile-check {
  position: absolute; top: -6px; right: -6px;
  width: 17px; height: 17px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--navy); color: #fff; border: 2px solid var(--paper);
  opacity: 0; transform: scale(.6); transition: opacity .15s, transform .15s;
}
.qf-tile.active .qf-tile-check { opacity: 1; transform: scale(1); }
/* На мобильном (390px) два соседних qf-tile ("Только лицо"/"Лицо + оборот",
   "Горизонтальная"/"Вертикальная") не помещаются в одну строку — flex-wrap
   на .qf-tiles переносит их, но ширина по умолчанию — по содержимому
   (auto), а не поровну: короткая подпись даёт узкую плашку, длинная —
   плашку почти на всю ширину колонки, и пара из двух РАВНОЗНАЧНЫХ
   вариантов выбора читалась как рваная лесенка, а не как аккуратный
   переключатель (дизайн-аудит 2026-07-21). Тот же приём, что уже решает
   эту задачу в .ft-split .qf-tile (см. ниже по файлу) — на всю ширину
   строки, только здесь только для узких экранов: на десктопе .qf-tile —
   компактный чип-подсказка автоопределения формата, растягивать его на
   всю ширину было бы неуместно. */
@media (max-width: 640px) {
  .qf-tile { width: 100%; }
}

/* Description quality chips */
.desc-quality-bar {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: var(--text-sm); color: var(--muted); padding: 4px 0;
}
.dq-chip {
  height: 26px; padding: 0 11px; border-radius: 40px;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); font-size: var(--text-xs); font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s; white-space: nowrap;
}
.dq-chip:hover { border-color: var(--navy); color: var(--navy); background: var(--navy-tint); }
.dq-chip.on { border-color: var(--navy); color: #fff; background: var(--navy); }
.dq-chip.on:hover { background: var(--navy); color: #fff; }

/* gen promise */
.gen-promise { display: flex; flex-wrap: wrap; gap: 8px; padding: 8px 0; }
.gen-promise span { font-size: var(--text-xs); color: var(--muted); font-weight: 600; }
/* Вариант сразу под кнопкой «Сгенерировать» в quickMode (см. studio.jsx) —
   центрирован, чтобы совпадать с центрированной подписью кнопки
   (.pm-cta-credits) над ним, а не начинаться от левого края колонки. */
.gen-promise-inline { justify-content: center; gap: 6px 14px; padding-top: 4px; }

/* «Единый стиль лица и оборота» (см. matchStyle в studio.jsx) — раньше
   скромный однострочный чекбокс глубоко в прокручиваемой .settings-body,
   специально приглушённый, "чтобы не спорить с кнопкой «Сгенерировать»" —
   в итоге просто терялся из виду (жалоба: "незаметно совсем"). Теперь
   компактная плашка прямо над кнопкой, в неуезжающем .settings-foot —
   заметнее, но по ширине/заливке всё ещё явно вторична по сравнению с
   основной navy-заливкой Generate ниже, спорить с ней не должна. */
.match-style-pill {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; margin-bottom: 8px; border-radius: 10px;
  border: 1px solid var(--line-strong); background: var(--surface);
  cursor: pointer; font-size: var(--text-xs); color: var(--ink-soft);
  transition: border-color .15s, background .15s;
}
.match-style-pill:hover { border-color: var(--navy-line); }
.match-style-pill:has(input:checked) { border-color: var(--navy); background: var(--navy-tint); }
.match-style-pill input { flex: none; width: 16px; height: 16px; accent-color: var(--navy); cursor: pointer; }
.match-style-pill-title { flex: 1 1 auto; font-weight: 700; color: var(--ink); }
.match-style-pill-note { flex: none; font-weight: 600; color: var(--muted); }
.match-style-pill:has(input:checked) .match-style-pill-note { color: var(--navy); }

/* Полоска восстановления автосохранённой сессии (см. sessionStore.js,
   sessionOffer в studio.jsx) — раньше был position:fixed сверху по центру
   экрана и накрывал собой подпись "Напишите имя, стиль..." сразу при
   заходе в студию (жалоба 2026-07-20), затем стал обычным блоком в потоке
   страницы — но оставался тяжёлой glass-карточкой с двумя большими pill-
   кнопками в самом заметном месте редактора, прямо над полем (жалоба
   2026-07-21: "навязывается... не так объёмно"). Теперь тонкая строка —
   тот же навy-tint, что и превью формата рядом, без блюра и без крупных
   кнопок: действия — обычные текстовые ссылки, не заливные пилюли. Не
   модалка (не блокирует остальной интерфейс) — можно просто проигнорировать
   и начать печатать, полоска тогда никогда даже не появится (см. условие
   на пустое поле в studio.jsx). */
/* Действия восстановления сессии — теперь только внутри попапа "История"
   (см. .prompt-history-session ниже), баннер на холсте убран (2026-07-22).
   Текстовые ссылки, не кнопки-пилюли — не должны тянуть на себя больше
   внимания, чем сам попап. */
.session-restore-strip-link {
  flex: none; background: none; border: none; padding: 2px 1px; cursor: pointer;
  font-family: var(--sans); font-size: var(--text-xs); font-weight: 700; color: var(--ink-soft);
  text-decoration: underline; text-underline-offset: 2px; transition: color .15s;
}
.session-restore-strip-link:hover { color: var(--navy); }
.session-restore-strip-primary { color: var(--navy); }

/* quick-improve chips (canvas) */
.quick-improve-row { display: flex; gap: 8px; flex-wrap: wrap; padding: 8px 14px; }
.qi-chip {
  height: 30px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  font-size: var(--text-sm); font-weight: 600; color: var(--ink-soft);
  cursor: pointer; font-family: var(--sans); transition: all .15s;
}
.qi-chip:hover { border-color: var(--navy); color: var(--navy); background: var(--navy-tint); }

/* Voice textarea */
.vta-wrap { position: relative; }
/* Плейсхолдер-пример в композере студии — почти незаметный (в 4 раза
   бледнее обычного .textarea::placeholder), чтобы не спорил за внимание
   с текстом, который печатает сам пользователь. Специфичность селектора
   (textarea.textarea, а не просто .textarea) намеренно выше, чем у
   .settings-panel .textarea::placeholder — иначе тот перебивает эту
   бледность внутри левой колонки студии. */
.vta-wrap textarea.textarea::placeholder { opacity: .25; }
.vta-mic {
  position: absolute; bottom: 12px; right: 20px;
  height: 19px; padding: 0 7px; border-radius: 6px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); cursor: pointer; display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--text-xs); font-weight: 600; font-family: var(--sans);
  transition: all .15s; z-index: 2; white-space: nowrap;
}
.vta-mic:hover { border-color: var(--navy); color: var(--navy); }
.vta-mic.active { background: #DC2626; border-color: #DC2626; color: #fff; animation: mic-pulse .8s ease-in-out infinite; }
/* Браузер без Web Speech API (WebView внутри соцсетей/мессенджеров, часть
   мобильного Safari) — кнопка остаётся на месте, но неактивна: пояснение в
   title/aria-label, а не молчаливое исчезновение (см. мобильный аудит). */
.vta-mic:disabled { opacity: .45; cursor: not-allowed; }
.vta-mic:disabled:hover { border-color: var(--line); color: var(--muted); }
/* processing — серверный фолбэк (POST /api/transcribe) уже остановил запись,
   но текст ещё не пришёл (сеть + распознавание); отдельный вид от обычного
   :disabled — тут "фича работает, просто ждём", а не "недоступна". */
.vta-mic.processing { opacity: 1; border-color: var(--navy); color: var(--navy); background: var(--paper-2); animation: mic-processing-pulse 1s ease-in-out infinite; }
.vta-recording .textarea { padding-right: 90px; }
@keyframes mic-pulse { 0%,100%{box-shadow:0 0 0 0 rgba(220,38,38,.45);}50%{box-shadow:0 0 0 6px rgba(220,38,38,0);} }
@keyframes mic-processing-pulse { 0%,100%{opacity:1;}50%{opacity:.55;} }
@media (prefers-reduced-motion: reduce) { .vta-mic.active, .vta-mic.processing { animation: none; } }

/* ── «+»-меню материалов (изображения/иконки/QR) у текстового поля —
   заменяет прежний шаг «Материалы» Мастера настройки (Lovart-стиль). ── */
.composer-with-attach { position: relative; }
.composer-with-attach > .copilot-host { width: 100%; }
/* Drag-and-drop изображений прямо на композер (не только на кнопку «+») */
.composer-with-attach.drag-over .textarea {
  border-color: var(--navy) !important; background: var(--navy-tint) !important;
}
/* Резерв места снизу под ряд кнопок «+»/формат/цвет — без этого написанный
   или печатающийся авто-текст доходит до самого низа поля и наезжает на
   кнопки (см. Lovart: там всегда есть пустое поле под текстом).
   Селектор специально с доп. классом-предком .settings-panel — иначе
   правило ниже по каскаду (.settings-panel .textarea { padding: 15px 18px })
   с той же специфичностью перетирает padding-bottom обратно. */
.settings-panel .composer-with-attach .textarea { padding-bottom: 46px; }
/* Когда прикреплены материалы — резервируем под инлайн-чипы ещё немного
   места, сверх ряда кнопок «+»/формат/цвет, чтобы над чипами всегда
   оставалась пустая строка независимо от того, что напечатано, и текст
   никогда не мог «залезть» в зону чипов. Точное значение (с учётом
   переноса чипов на 2-ю строку) считается в рантайме и приходит
   инлайн-стилем на само поле, с safety-минимумом на 2 строки чипов (см.
   PromptMode/composerFieldStyle) — здесь тот же безопасный минимум
   статикой, для оверлеев (типизируемый плейсхолдер, копилот-подсказка),
   у которых инлайн-стиля нет. */
.settings-panel .composer-with-attach.has-chips .textarea { padding-bottom: 176px; }
/* ── Живая подсказка размера — только текстовый хинт низкой уверенности
   ("20x20" без единицы, см. looksLikeSizeNoUnit в sizeParser.js), в
   пустом пространстве .settings-body МЕЖДУ полем композера и кнопкой
   «Сгенерировать». Сам распознанный размер крупным числом здесь больше
   не показывается — переехал чипом в кнопку (.pm-cta-size-chip ниже). */
.composer-size-ghost-slot {
  flex: 1; display: flex; align-items: center; justify-content: center;
  min-height: 60px;
}
.composer-size-hint {
  font-size: var(--text-sm); font-weight: 600; color: var(--muted);
  text-align: center;
}
/* ── Ряд иконок под полем описания — раньше каждая кнопка была независимо
   position:absolute с ручным пиксельным left (см. историю правки), теперь
   один флекс-ряд + вложенные группы. gap между группами шире (20px), чем
   внутри группы (8px) — та же идея, что раньше выражалась "лишним" отступом
   у модели (+12px к обычному шагу): читаемая пауза = новая смысловая
   группа, без явной рамки/фона. */
.composer-icon-row {
  position: absolute; bottom: 12px; left: 12px; z-index: 2;
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  /* right:150px — резерв под .vta-mic (голосовой ввод, стоит независимо,
     справа:20px от этого же поля, ~125px шириной с подписью "Голосовой
     ввод"). Без этого резерва flex-wrap считал доступную ширину ряда как
     ВЕСЬ .vta-wrap (у absolute-элемента с одним left, без right, ширина
     shrink-to-fit до полной доступной), и на узком экране последняя
     иконка последней строки перед переносом могла лечь прямо под кнопкой
     микрофона (дизайн-аудит 2026-08-08, мобильный отчёт: "Голосовой ввод
     перекрывается чипами"). */
  right: 150px;
}
.composer-icon-group { display: flex; align-items: center; gap: 8px; }
.attach-wrap, .format-attach-wrap, .color-attach-wrap, .style-attach-wrap,
.size-attach-wrap, .text-size-attach-wrap, .model-attach-wrap, .prompt-history-wrap {
  display: flex; align-items: center;
}
.vta-attach {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 8px; padding: 0;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); cursor: pointer; transition: all .15s;
  flex: none; position: relative;
}
.vta-attach:hover { border-color: var(--navy); color: var(--navy); }
.vta-attach.filled { border-color: var(--navy-line); color: var(--navy); background: var(--navy-tint); }
/* Зелёная точка "выбрано вручную" — единый индикатор чипов композера И
   строк в ParamsSidePanel (см. .psp-dot ниже): то же состояние показано
   в обоих местах одинаково, чтобы не приходилось гадать, что уже выбрано. */
.vta-dot {
  position: absolute; top: -2px; right: -2px;
  width: 8px; height: 8px; border-radius: 50%;
  background: #2FAE5C; border: 1.5px solid var(--surface);
}
.attach-menu {
  position: fixed; transform: translateY(-100%);
  display: flex; flex-direction: column; gap: 2px;
  min-width: 220px; padding: 6px;
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border);
  border-radius: 16px; box-shadow: var(--glass-shadow); z-index: 60;
}
.attach-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 9px; border-radius: 8px;
  border: none; background: none; cursor: pointer;
  font-family: var(--sans); text-align: left; transition: background .12s;
}
.attach-item:hover { background: var(--paper-2); }
.attach-item-ic { display: flex; align-items: center; justify-content: center; width: 22px; height: 22px; flex: none; color: var(--muted); }
.attach-item.filled .attach-item-ic { color: var(--good); }
.attach-item-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.attach-item-lbl { font-size: var(--text-xs); font-weight: 700; color: var(--ink); line-height: 1.2; }
.attach-item-sub { font-size: var(--text-xs); font-weight: 400; color: var(--muted); line-height: 1.2; }
/* Отличает пункты, открывающие ещё один экран (Иконки → модалка, QR-код →
   поповер), от пунктов с мгновенным действием (Загрузить изображения —
   сразу системный выбор файлов, без своего шеврона). */
.attach-item-chevron { margin-left: auto; flex: none; color: var(--muted); font-size: 15px; line-height: 1; }
/* Разделитель "Оборот" в .attach-menu (см. MaterialsAttachMenu) — та же
   визуальная подпись, что у .qf-tiles-label ("Стороны" над переключателем
   лицо/оборот): мелкий, разрежённый, uppercase, приглушённый — не кнопка,
   не кликабельна, просто граница между группами пунктов меню. */
.attach-menu-divider {
  display: flex; align-items: center; gap: 8px;
  margin: 4px 2px 2px; padding-top: 6px;
  border-top: 1px solid var(--line);
}
.attach-menu-divider span {
  font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted);
}
/* Заметка про поддерживаемые форматы файлов внизу меню материалов — тот
   же приём, что и .qr-note в соседнем QR-поповере. */
.attach-menu-note {
  font-size: 10.5px; color: var(--muted); display: flex; align-items: flex-start;
  gap: 6px; line-height: 1.4; padding: 7px 9px; margin-top: 2px;
  background: var(--paper-2); border-radius: 8px;
}
.attach-popover {
  position: fixed;
  min-width: 240px; max-width: 300px; padding: 10px;
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border);
  border-radius: 16px; box-shadow: var(--glass-shadow); z-index: 60;
}
.attach-popover-qr { display: flex; flex-direction: column; gap: 10px; }

/* История текста промпта (см. PromptHistoryButton, sessionStore.js) —
   список кликабельных прошлых версий текста, каждая обрезается до 2 строк
   (длинные листовочные тексты иначе растянули бы поповер на весь экран). */
.prompt-history-popover { display: flex; flex-direction: column; gap: 2px; max-height: 320px; overflow-y: auto; }
.prompt-history-empty { padding: 10px 6px; font-size: var(--text-xs); color: var(--muted); max-width: 260px; }
.prompt-history-item {
  position: relative; display: flex; align-items: center; gap: 8px;
  padding: 8px 24px 8px 9px; border-radius: 8px; max-width: 280px;
  border: none; background: none; cursor: pointer; text-align: left;
  font-family: var(--sans); transition: background .12s;
}
.prompt-history-item:hover { background: var(--paper-2); }
.prompt-history-item-text {
  font-size: var(--text-xs); color: var(--ink-soft); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.prompt-history-item-x {
  position: absolute; top: 6px; right: 6px; width: 15px; height: 15px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; color: var(--muted);
}
.prompt-history-item-x:hover { color: #C0392B; background: rgba(192,57,43,.08); }

/* ── Несохранённая сессия (фото/QR/текст) — первая, отдельно оформленная
   строка в этом же попапе (см. sessionOffer в studio.jsx). Раньше был
   отдельный баннер прямо на холсте — по запросу 2026-07-22 убран целиком,
   единственный признак снаружи — мигающая иконка (.ph-blink ниже). ── */
.prompt-history-session {
  padding: 9px 10px; margin-bottom: 4px; border-radius: 8px;
  background: var(--navy-tint); border: 1px solid var(--navy-line);
  max-width: 280px;
}
.prompt-history-session-head {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--text-xs); font-weight: 700; color: var(--navy);
}
.prompt-history-session-head svg { flex: none; }
.prompt-history-session-meta {
  display: flex; gap: 12px; margin: 5px 0 7px 19px;
  font-size: var(--text-xs); color: var(--muted); font-weight: 600;
}
.prompt-history-session-meta span { display: inline-flex; align-items: center; gap: 4px; }
.prompt-history-session-actions { display: flex; gap: 14px; margin-left: 19px; }

/* Иконка "История" — тихое мигание, пока есть несохранённая сессия
   (замена прежнему баннеру на холсте): не яркая анимация, а медленное
   изменение прозрачности, чтобы быть заметным боковым зрением, но не
   раздражать при работе с полем ввода рядом. */
@keyframes ph-blink-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
.ph-blink { animation: ph-blink-pulse 2.2s ease-in-out infinite; }
.ph-blink:hover { animation: none; opacity: 1; }

/* ── Превью/подтверждение распознанного рукописного текста (см.
   OcrRecognizePopover, studio.jsx) — тот же визуальный язык, что и
   .attach-popover-qr (glass-card), но не поповер материалов — свой класс,
   т.к. может открыться от кнопки на чипе ИЛИ на тайле оборота. ── */
.ocr-popover {
  position: fixed; z-index: 70; padding: 12px;
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border);
  border-radius: 14px; box-shadow: var(--glass-shadow);
  display: flex; flex-direction: column; gap: 8px;
}
.ocr-popover-title { font-size: var(--text-sm); font-weight: 700; color: var(--ink); }
.ocr-popover-empty { font-size: var(--text-xs); color: var(--muted); padding: 4px 0 2px; }
/* Увеличено по запросу 2026-07-30 — "распознанный текст" должен быть явно
   читаемым, не мелким служебным полем (было 84px/13px, ширина поповера 320). */
.ocr-popover-textarea { width: 100%; min-height: 150px; resize: vertical; font-size: 15px; line-height: 1.45; }
.ocr-popover-status { font-size: var(--text-xs); color: var(--good); font-weight: 600; }
.ocr-popover-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* ── На мобильном поповер, открытый КНОПКОЙ (см. usePopoverExtras clamp в
   studio.jsx), больше не обрезается по краю экрана — но у контента вроде
   14 чипов стиля или 20+ форматов размера просто нет места, чтобы открыться
   строго над кнопкой на невысоком экране: клэмп по вертикали честно тащит
   поповер туда, где он влезает целиком, — а это может оказаться у самого
   верха экрана, поверх шапки/хиро-текста, далеко от кнопки, которая его
   открыла (проверено на реальном телефоне). Правильный паттерн для такого
   контента на узком экране — не "поповер у кнопки", а нижний лист (bottom
   sheet): фиксирован от низа экрана, независимо от того, где кнопка. Через
   !important — единственный способ перебить инлайновые top/left, которые
   на каждый рендер считает reposition() в studio.jsx. ── */
@media (max-width: 640px) {
  .attach-menu, .attach-popover {
    position: fixed !important;
    top: auto !important; bottom: 0 !important;
    left: 0 !important; right: 0 !important;
    width: auto !important; max-width: none !important; min-width: 0 !important;
    transform: none !important;
    max-height: 75vh; overflow-y: auto; overflow-x: hidden;
    border-radius: 18px 18px 0 0;
    box-shadow: 0 -8px 30px rgba(0,0,0,.18);
  }
}

/* ── Инлайн-чипы прикреплённых материалов (Lovart-стиль) — всегда у
   левого нижнего угла поля, над рядом кнопок «+»/формат/цвет (тот ряд
   уже занимает bottom:12px..38px). Отступ до кнопок — целая пустая
   строка (не просто узкий зазор), чтобы чипы визуально не «влипали» в
   ряд кнопок под ними. Ряд переносится на вторую строку, когда чипов
   много (а не скроллится по горизонтали) — растёт вверх, т.к. закреплён
   по bottom; фактическая высота меряется в PromptMode и превращается в
   отступ под текстом (см. composerFieldStyle — там же используется то
   же число 68, что и bottom здесь). ── */
/* ── Подложка под рядом чипов файлов + рядом иконок — поле теперь умеет
   скроллиться внутри себя (см. .settings-panel .textarea max-height выше),
   и последняя строка реально напечатанного текста может оказаться прямо
   под этими двумя абсолютно спозиционированными рядами: без своего фона у
   них самих (гэпы между кнопками/чипами прозрачные) текст был виден сквозь
   щели. Один общий градиент-подложка на оба ряда сразу. Ниже z-index обоих
   рядов (2 и 3), выше самого поля.
   Первая версия была 160px с растянутым на 56px переходом — фон гас не
   только позади самого ряда иконок (тот занимает всего ~40px снизу), а
   заметно выше, поверх ещё вполне читаемого напечатанного текста: строка
   над иконками выглядела вылинявшей, как будто из неё "что-то украли" (см.
   жалобу). Переход теперь короткий и фиксированный в px (не в % от высоты
   блока) — гаснет только у самой границы, где реально начинаются кнопки. */
.composer-icon-row-backdrop {
  position: absolute; left: 0; right: 0; bottom: 0; height: 54px; z-index: 1;
  background: linear-gradient(to bottom, transparent, var(--surface) 20px);
  border-radius: 0 0 var(--r-md) var(--r-md);
  pointer-events: none;
}
/* Когда прикреплены файлы — над рядом иконок ещё и ряд их превью-чипов
   (.composer-chip-row, bottom:68px) — подложке нужно дотянуться и до него. */
.composer-with-attach.has-chips .composer-icon-row-backdrop { height: 108px; }

/* ── SELECTED OPTIONS BAR — строка активных параметров (см. SelectedOptionsBar
   в studio.jsx) — обычный поток документа сразу ПОД .composer-with-attach
   (не абсолютно позиционированный третий слой поверх ряда иконок/чипов
   материалов — их пиксельный стекинг и так хрупкий, растягивать его на
   переменное число чипов, которые ещё и переносятся на вторую строку,
   было бы только жёстче ломать). Каждый чип — кликабельная кнопка,
   открывает тот же поповер, что и соответствующая иконка ряда. */
.sob-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: 8px;
}
.sob-chip {
  display: inline-flex; align-items: center; gap: 5px;
  height: 28px; padding: 0 11px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--paper-2);
  font-family: var(--sans); font-size: 12px; font-weight: 500; line-height: 1;
  color: var(--ink-soft); cursor: pointer; white-space: nowrap;
  transition: background .15s, border-color .15s, transform .1s;
  /* Появление — лёгкий fade+scale, не мигающий скачок: чип обновляется,
     когда меняется его значение (React просто перерисовывает эту одну
     кнопку — остальной ряд не перерисовывается и не мигает целиком). */
  animation: sob-chip-in .18s ease-out both;
}
@keyframes sob-chip-in {
  from { opacity: 0; transform: translateY(2px) scale(.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.sob-chip:hover { background: var(--navy-tint); border-color: var(--navy-line); color: var(--navy); }
.sob-chip:active { transform: scale(.96); }
.sob-chip:focus-visible { outline: 2px solid var(--navy); outline-offset: 1px; }
.sob-chip-icon { font-size: 13px; line-height: 1; }
/* Цвет — маленький кружок реального hex, не эмодзи-текст (см. запрос:
   "цвет должен отображаться маленьким кружком, а не просто текстом"). */
.sob-chip-dot {
  width: 12px; height: 12px; border-radius: 50%; flex: none;
  border: 1px solid rgba(0,0,0,.12);
}
/* white-space:nowrap — самый длинный реальный лейбл ("Лицо и оборот (две
   стороны)") не должен обрезаться серединой слова эллипсисом раньше, чем
   реально не влезет в доступную ширину; overflow/ellipsis остаются как
   защита от неожиданно длинного значения (например, редкое название
   ближайшего цвета), а не как обычный режим работы для штатных подписей. */
.sob-chip-label { max-width: 230px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 640px) {
  .sob-chip { height: 30px; padding: 0 10px; }
  .sob-chip-label { max-width: 160px; }
}
.composer-chip-row {
  position: absolute; left: 12px; bottom: 68px; z-index: 3;
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  max-width: calc(100% - 24px);
}
.composer-chip {
  position: relative; display: flex; align-items: center; gap: 5px; flex: none;
  height: 26px; padding: 0 8px 0 4px; border-radius: 8px; max-width: 130px;
  border: 1px solid var(--line); background: var(--surface);
  font-size: var(--text-xs); font-weight: 600; color: var(--ink-soft);
}
/* Материалы ОБОРОТА в общем ряду чипов (см. ComposerAttachChips) — тот же
   вид чипа, но с акцентной левой каймой в цвет бейджа "ОБОРОТ" на мокапе
   холста (.hero-side-badge и т.п.), чтобы не перепутать со стороной лица
   на глаз, не читая подпись каждый раз. */
.composer-chip-back { border-left: 3px solid var(--navy); padding-left: 5px; }
.composer-chip-back .composer-chip-name { color: var(--navy); }

/* "Передать макет в студию" (запрос 2026-07-30) — файл приходит из другого
   инструмента и появляется в ряду чипов уже прикреплённым (не через ручной
   выбор файла) — короткая анимация появления + пара миганий подсветкой
   даёт то же ощущение "оно только что прилетело сюда", не пытаясь буквально
   анимировать перелёт через смену страницы (технически нереалистично при
   полной смене вида, см. обсуждение). Класс временный — see justAttachedUrl
   в studio.jsx, сам снимается через ~1.8с. */
@keyframes composerChipArrive {
  0%   { transform: scale(.5); opacity: 0; }
  60%  { transform: scale(1.08); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes composerChipBlink {
  0%, 100% { box-shadow: 0 0 0 0 transparent; border-color: var(--line); }
  25%, 75% { box-shadow: 0 0 0 3px var(--bronze-tint); border-color: var(--bronze); }
  50%      { box-shadow: 0 0 0 0 transparent; border-color: var(--line); }
}
.composer-chip-just-attached {
  animation: composerChipArrive .35s ease-out, composerChipBlink 1.1s ease-in-out .35s;
}
.composer-chip-thumb { position: relative; width: 18px; height: 18px; border-radius: 4px; overflow: visible; flex: none; }
.composer-chip-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 4px; }
/* Постоянный бейдж "распознать текст" поверх миниатюры — дублирует кнопку
   из hover-превью (.composer-chip-ocr ниже) для тач-экранов, где :hover
   недостижим. Специально мелкий (не полноразмерная вторая иконка на и без
   того тесном 18px чипе), но кликабельный в любой момент, без наведения. */
.composer-chip-ocr-badge {
  position: absolute; bottom: -4px; right: -4px; z-index: 2;
  min-width: 14px; height: 14px; padding: 0 2px; border-radius: 4px;
  border: 1.5px solid var(--surface); background: var(--navy); color: #fff;
  font-size: 6.5px; font-weight: 800; letter-spacing: -.02em;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.composer-chip-ocr-badge:hover { background: var(--navy-deep); }
.composer-chip-ocr-badge:disabled { cursor: default; opacity: .8; }
.composer-chip-ocr-badge svg { width: 8px; height: 8px; }
.composer-chip-social {
  width: 18px; height: 18px; border-radius: 4px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 9px; font-weight: 800;
}
.composer-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.composer-chip-x {
  position: absolute; top: -6px; right: -6px; width: 15px; height: 15px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); cursor: pointer;
}
.composer-chip-x:hover { border-color: #C0392B; color: #C0392B; background: rgba(192,57,43,.08); }
/* Визуальный размер крестика (15px) меньше рекомендуемого тач-таргета —
   расширяем кликабельную/тач-зону невидимым псевдоэлементом, не трогая
   внешний вид (WCAG 2.5.5 ~24px минимум). */
.composer-chip-x::before { content: ""; position: absolute; inset: -7px; }
.composer-chip-preview {
  display: none;
  position: absolute; left: 0; bottom: 100%; margin-bottom: 0; z-index: 4;
  width: 140px; height: 140px; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--line); box-shadow: var(--sh-md); background: var(--surface);
}
/* margin-bottom:0 — раньше тут был отступ 8px, из-за которого при движении
   курсора от чипа вверх к превью он на мгновение оказывался ни над чипом,
   ни над превью: hover слетал, превью схлопывалось в display:none раньше,
   чем курсор доезжал до кнопки «Распознать» внизу плашки (см. обсуждение —
   кнопка была физически недостижима мышкой). Боксы вплотную — hover-цепочка
   не рвётся. */
.composer-chip:hover .composer-chip-preview { display: block; }
.composer-chip-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ── «Распознать» (рукописный текст с фото) — видна только по hover на
   превью (не отдельная постоянная иконка на самом чипе, чипы 26px и без
   того тесные) — узкая полоса внизу превью-плашки, .composer-chip-preview
   уже имеет overflow:hidden, поэтому кнопка остаётся в границах 140×140. */
.composer-chip-ocr {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  display: flex; align-items: center; justify-content: center; gap: 5px;
  height: 26px; padding: 0 6px; border: none; border-radius: 0;
  background: rgba(28,58,87,.86); color: #fff; font-size: 10.5px; font-weight: 700;
  cursor: pointer;
}
.composer-chip-ocr:hover { background: var(--navy); }
.composer-chip-ocr:disabled { cursor: default; opacity: .8; }
.composer-chip-ocr-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  height: 14px; padding: 0 3px; border-radius: 4px; border: 1px solid rgba(255,255,255,.6);
  font-size: 8px; font-weight: 800; letter-spacing: -.02em; flex: none;
}

/* ── Формат макета — кнопка после «+», открывает поповер с FormatTiles
   (вынесено из отдельного всегда-видимого блока «Формат макета»). Теперь
   в одной .composer-icon-group с Размером (см. studio.jsx) — оба про
   физическую форму макета, стоят рядом первой группой после материалов. ── */
/* 460px было под сетку 2×2 из широких карточек; 320px оказался слишком
   тесным для 2 карточек в ряд — "Лицо + оборот" ломалось посреди слова
   (жалоба 2026-07-21). 400px — по старым широким пропорциям карточки,
   просто на пару штук в ряду, а не на все 4 разом. */
/* 400px хватало по ширине слову "Лицо + оборот" (есть пробелы, переносится
   само), но не "Горизонтальная"/"Вертикальная" (слово целиком, переносить
   негде) — при двух колонках рядом (.ft-axes) на слово оставалось ~170px,
   и оно вылезало поверх галочки (жалоба 2026-07-21). 460px даёт колонке
   ~200px — слово влезает в строку целиком; overflow-wrap на .qf-tile-title
   остаётся подстраховкой на случай ещё более длинной подписи в будущем. */
.format-attach-menu { width: 480px; padding: 12px; }
/* Кнопка "Готово" — явная точка выхода из поповера с двумя независимыми
   группами выбора (см. комментарий у FormatAttachButton): клик снаружи и
   Escape по-прежнему работают, кнопка — просто более очевидный вариант
   для тач-экранов. */
.format-attach-done {
  width: 100%; margin-top: 10px; padding: 9px 14px;
  border-radius: 10px; border: 1px solid var(--line-strong);
  background: var(--paper-2); color: var(--ink);
  font-family: var(--sans); font-weight: 700; font-size: var(--text-sm);
  cursor: pointer; transition: background .12s, border-color .12s, color .12s;
}
.format-attach-done:hover { background: var(--navy); border-color: var(--navy); color: #fff; }

/* ── Размер/тип изделия — открывает тот же каталог NUDGE_FORMATS, что и
   подсказка пустого поля — весь список сразу виден (не третий отдельный
   источник размеров). ── */
/* Попап широкий и невысокий (а не узкий и длинный) — цель "видно все 24
   формата сразу, без прокрутки". auto-fill вместо repeat(8,1fr): на полном
   десктопном экране колонки сами лягут в 3 ряда (24 / 8 = 3), на более узких
   экранах grid сам уменьшит число колонок вместо нечитаемо узких кнопок. */
.size-attach-menu { width: 1180px; max-width: calc(100vw - 24px); padding: 10px; max-height: 80vh; overflow-y: auto; }
.size-attach-panel {
  display: flex; flex-direction: column; gap: 10px;
  background: var(--navy-tint); border: 1px solid var(--navy-line);
  border-radius: 11px; padding: 10px 14px;
}
.size-attach-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.size-attach-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 8px; }
.size-attach-grid .sfh-btn { width: 100%; }
/* Разделитель печатных форматов (мм) и цифровых (px) — grid-column:1/-1
   растягивает пустую полосу через все колонки, border-top рисует саму линию
   (аналог .nudge-format-divider, только для grid-раскладки этого попапа). */
.size-attach-grid .size-attach-divider {
  grid-column: 1 / -1; height: 0; margin: 2px 0;
  border-top: 1px solid var(--navy-line);
}

/* ── Цвет — открывает поповер с одной плашкой «Свой цвет» (нативный color
   picker). Своя .composer-icon-group со Стилем — оба про оформление, не
   форму. ── */
/* Круглая кнопка (а не скруглённый квадрат, как у соседних кнопок) — сама
   цветная плашка внутри тоже круг, вместе выглядит как единый цветной шарик. */
.vta-attach.vta-color { border-radius: 50%; }
.vta-color-swatch { width: 16px; height: 16px; border-radius: 50%; border: 1px solid rgba(0,0,0,.12); flex: none; }
.color-attach-menu { width: 150px; padding: 8px; }

/* ── Стиль оформления — круглая, как и кнопка цвета, со своей иконкой
   (см. StyleGlyph в studio.jsx — волшебная палочка с искрой, не палитра
   художника: раньше PaletteGlyph визуально почти дублировал соседний
   круглый цветной свотч кнопки "Цвет", хотя стиль ≠ цвет). ── */
.vta-attach.vta-style { border-radius: 50%; }
/* Ширина подобрана так, чтобы 14 стилей естественно паковались в 2 строки
   (flex-wrap сам решает, сколько чипов влезает в строку — без фиксированных
   колонок и пробелов). */
.style-attach-menu { width: 820px; max-width: calc(100vw - 24px); padding: 12px; overflow-x: auto; }
/* ── Размер текста ("Т") — единственный чип в ряду про типографику, не
   форму и не оформление, поэтому без .composer-icon-group (сам себе
   группа). Буква вместо контурной иконки — тот же приём, что и у чипа
   модели ниже (текстовое, не абстрактное содержимое). ── */
.vta-attach.vta-text-size { font-size: 13px; font-weight: 800; font-family: var(--sans); }
/* Регистр выделенного текста ("Аа") — виден только пока в композере есть
   активное выделение (studio.jsx selRange/toggleSelectionCase), поэтому
   появляется/исчезает в потоке ряда без анимации — не постоянный чип,
   на который нужно было бы намеренно смотреть. Шире соседей ("Т") — два
   символа вместо одного, фиксированная 26px-ширина обрезала бы подпись. */
.vta-attach.vta-case-toggle { width: auto; padding: 0 8px; font-size: 12px; font-weight: 800; font-family: var(--sans); margin-left: 10px; }
.text-size-attach-menu { width: 260px; padding: 12px; gap: 4px; }
.text-size-menu-title {
  font-size: var(--text-sm); font-weight: 700; color: var(--ink);
  line-height: 1.35; margin-bottom: 8px; padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
.text-size-row + .text-size-row { margin-top: 12px; }
.text-size-row-lbl { font-size: var(--text-xs); font-weight: 700; color: var(--ink); margin-bottom: 6px; }
.text-size-opts { display: flex; gap: 6px; }
.text-size-opt {
  flex: 1; text-align: center; padding: 6px 4px; border-radius: 8px;
  border: 1px solid var(--line-strong); background: var(--paper-2);
  font-family: var(--sans); font-size: 11.5px; font-weight: 700; color: var(--ink-soft);
  cursor: pointer; transition: all .15s;
}
.text-size-opt:hover { border-color: var(--navy); color: var(--navy); }
.text-size-opt.on { background: var(--navy); border-color: var(--navy); color: #fff; }

/* ── Модель генерации — своя .composer-icon-group с историей промпта,
   отдельная от параметров макета (формат/размер/цвет/стиль/текст) слева —
   лишний зазор группы читается как разделитель, тот же приём, что и
   раньше при ручном пиксельном отступе. Раньше была отдельной
   всегда-видимой строкой крупных пилюль между полем и кнопкой
   «Сгенерировать» — отвлекала от текста ещё до того, как пользователь
   начинал печатать. В отличие от круглых соседей кнопка не квадратная/
   круглая, а чип с видимым названием модели — выбор модели влияет на
   цену/качество и должен быть виден без наведения. ── */
.vta-attach.vta-model {
  width: auto; height: 26px; padding: 0 9px 0 7px; gap: 5px;
  font-size: 12px; font-weight: 600; color: var(--ink-soft);
}
.vta-model-label { white-space: nowrap; }
.model-attach-menu { width: 230px; padding: 6px; }
.model-attach-menu .attach-item.sel { background: var(--navy-tint); }
.model-attach-menu .attach-item.sel .attach-item-lbl { color: var(--navy); }
.color-pick-card {
  display: block; width: 100%;
  border: none; border-radius: 11px; overflow: hidden; cursor: pointer; padding: 0;
  background: none; font-family: var(--sans);
  transition: transform 150ms var(--ease), box-shadow 150ms var(--ease);
  outline: 2.5px solid transparent; outline-offset: 2px;
}
.color-pick-card:hover { transform: translateY(-1px) scale(1.02); box-shadow: var(--sh-md); }
.color-pick-card.sel { outline-color: var(--navy); box-shadow: 0 0 0 2.5px rgba(28,58,87,.2); }

/* ── Мобильная адаптация кластера композера («+»/формат/цвет + инлайн-
   чипы). Раньше здесь была отдельная копия из 6 пиксельных left-значений,
   синхронизируемая руками с десктопной версией (см. .composer-icon-row
   выше) — .composer-icon-row/.composer-icon-group флекс сам подстраивается
   под увеличенные тач-кнопки, пересчитывать нечего. Чипы чуть уже, чтобы
   стабильнее переносились в 2 строки на самых узких экранах.
   900px, не 768px: кнопка микрофона (.vta-mic) сидит абсолютно и НЕ
   участвует в раскладке ряда чипов (right:20px от края поля, независимо
   от того, сколько места реально заняли чипы) — на планшетном портрете
   768–834px десктопный (некомпактный) ряд чипов ещё не переносится в 2
   строки, но поле уже недостаточно широкое, и последний чип наезжает на
   микрофон (дизайн-аудит 2026-07-21). Тот же диапазон ломает и холст
   справа (см. .studio-app ниже) — оба чинятся одним и тем же расширением
   порога. ── */
@media (max-width: 900px) {
  .vta-attach { width: 30px; height: 30px; }
  .composer-chip { max-width: 108px; }
}
.color-pick-bg { position: relative; width: 100%; height: 78px; }
.color-pick-body { position: absolute; bottom: 0; left: 0; right: 0; padding: 7px 8px; display: flex; flex-direction: column; gap: 1px; }
.color-pick-name { display: block; font-size: 11px; font-weight: 700; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.3); text-align: left; line-height: 1.2; }
.color-pick-desc { display: none; }

/* ── Copilot: локальные подсказки сценариев Prompt Manager ── */
.copilot-host { position: relative; }
.copilot-ghost-overlay {
  position: absolute; inset: 0; z-index: 1;
  background: transparent !important; border-color: transparent !important; box-shadow: none !important;
  white-space: pre-wrap; word-break: break-word; overflow: hidden; pointer-events: none;
}
.copilot-ghost-text {
  color: var(--faint);
  animation: copilotGhostIn .15s ease-out both;
}
/* "электрик · Tab — вставить пример" — призрак-автодополнение в самом
   поле (запрос владельца 2026-08-03, "в 2 раза прозрачнее, чем сейчас") —
   раньше анимация домашивала до полной непрозрачности (opacity:1) уже
   через .15s, конечное состояние всегда 1 независимо от того, какой
   opacity задать статикой на самом .copilot-ghost-text (анимация
   перебивает статичное свойство на всё время + после, fill-mode "both").
   Значение конечного кадра — единственное место, которое реально решает
   итоговую прозрачность. */
@keyframes copilotGhostIn { from { opacity: 0; } to { opacity: .5; } }
@media (prefers-reduced-motion: reduce) { .copilot-ghost-text { animation: none; } }

.copilot-suggestions {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 20;
  margin: 0; padding: 6px; list-style: none;
  /* Плотнее, чем стандартный var(--glass-bg) (66% непрозрачности) —
     дизайн-аудит 2026-08-08: список автодополнения профессий floating
     поверх ряда чипов "Формат"/"Размер" под композером (position:absolute,
     не отодвигает их вниз — как и любой command-palette dropdown), и на
     обычном 66%-стекле текст чипов/лейблов под ним оставался читаемым
     сквозь размытие — два слоя текста одновременно выглядели захламлённо,
     а не как чистая панель НАД полем. Более плотный фон — тот же приём
     глубины (blur+border+shadow), просто не позволяющий контенту под ним
     "просвечивать" настолько, чтобы читался текст. */
  background: rgba(255,255,255,.94);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border); border-radius: var(--r-md);
  box-shadow: var(--glass-shadow);
  max-height: 260px; overflow-y: auto;
  animation: copilotListIn .18s var(--ease) both;
}
[data-theme="dark"] .copilot-suggestions { background: rgba(30,33,38,.96); }
@keyframes copilotListIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .copilot-suggestions { animation: none; } }
.copilot-suggestion {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 8px; cursor: pointer;
  font-family: var(--sans); font-size: var(--text-sm); color: var(--ink);
  transition: background .12s;
}
.copilot-suggestion.active, .copilot-suggestion:hover { background: var(--navy-tint); }
.copilot-suggestion-icon { font-size: 16px; line-height: 1; flex: none; }
.copilot-suggestion-label { font-weight: 600; }
.quick-close {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--text-xs); font-weight: 600; color: var(--muted);
  background: none; border: none; cursor: pointer;
  font-family: var(--sans); padding: 6px 8px; border-radius: 40px; transition: color .15s;
}
.quick-close:hover { color: var(--ink); }

/* ===== Empty canvas placeholder ===== */
.empty-canvas {
  height: 100%; min-height: 300px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 34px; padding: 40px 24px; text-align: center;
}
/* когда выше есть таблица параметров — прижать превью к ней (убрать большой отступ) */
.spm + .empty-canvas {
  height: auto; min-height: auto; justify-content: flex-start; padding-top: 40px;
}
.empty-cards { display: flex; gap: 20px; align-items: center; }
.empty-card {
  /* Реальные визитки/листовки/плакаты не печатаются со скруглёнными углами —
     скругление здесь визуально обещало бы то, чего в готовом макете не
     будет. По требованию: чёткий прямой угол, как у настоящего изделия. */
  position: relative; border-radius: 0;
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--sh-md);
  width: 240px; height: 142px; padding: 18px;
  display: flex; flex-direction: column; gap: 9px;
}
.empty-card-front { transform: rotate(-3deg); }
.empty-card-back { transform: rotate(3deg); align-items: center; justify-content: center; gap: 10px; background: var(--paper-2); }
.ec-badge {
  position: absolute; top: -10px; left: 50%; transform: translateX(-50%);
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  background: var(--navy); color: #fff; padding: 3px 10px; border-radius: 40px; white-space: nowrap;
}
.empty-card-back .ec-badge { background: var(--bronze); }
.ec-logo { width: 34px; height: 34px; border-radius: 9px; }
.ec-line { height: 9px; border-radius: 5px; background: var(--line); }
.ec-rows { margin-top: auto; display: flex; flex-direction: column; gap: 5px; }
.ec-qr { width: 46px; height: 46px; border-radius: 8px; background: var(--line); }

/* type-specific skeletons */
.ec-img { width: 100%; border-radius: 8px; background: var(--line); height: 56px; }
.ec-pill { width: 88px; height: 18px; border-radius: 40px; background: var(--line); margin: 2px auto 0; }
.empty-flyer { width: 210px; height: 280px; transform: rotate(-2deg); justify-content: flex-start; gap: 11px; }
.empty-product { width: 200px; height: 252px; transform: rotate(-2deg); justify-content: flex-start; gap: 10px; }
.ec-prod-foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; }
.empty-post { width: 248px; height: 248px; transform: rotate(-2deg); justify-content: flex-start; gap: 9px; }

/* shimmer */
.sh { position: relative; overflow: hidden; background: var(--line); }
.sh::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.65), transparent);
  transform: translateX(-100%);
  animation: ec-shimmer 1.8s var(--ease) infinite;
}
[data-theme="dark"] .sh::after { background: linear-gradient(90deg, transparent, rgba(255,255,255,.12), transparent); }
@keyframes ec-shimmer { 100% { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .sh::after { animation: none; } }

.empty-text h3 { font-family: var(--display); color: var(--ink); }
.empty-steps {
  display: inline-flex; align-items: center; gap: 10px; margin-top: 18px;
  padding: 9px 16px; border-radius: 40px; background: var(--paper-2); border: 1px solid var(--line);
}
.empty-step { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm); font-weight: 600; color: var(--ink-soft); }
.es-num {
  width: 18px; height: 18px; border-radius: 999px; background: var(--navy); color: #fff;
  display: grid; place-items: center; font-size: var(--text-xs); font-weight: 700;
}
@keyframes ec-step-glow {
  0%,100% { box-shadow: 0 0 0 0 rgba(28,58,87,.45); }
  60%      { box-shadow: 0 0 0 5px rgba(28,58,87,.0); }
}
.es-active { animation: ec-step-glow 2s ease infinite; }

/* ===== EC Pulse Wrap ===== */
/* Резервирует под скелетон РЕАЛЬНОЕ (уже увеличенное transform:scale)
   место в layout — сам transform на .ec-pulse-wrap ниже не меняет его
   layout-box, поэтому без явного width/height (см. skelBox в EmptyCanvas)
   соседний текст наехал бы на визуально увеличенный, но "нулевого" по
   layout скелетон. */
/* Плавный переход при пересчёте scale (см. фикс зацикленного ResizeObserver
   в EmptyCanvas выше) — если размер всё же скорректируется между кадрами,
   это будет плавный ресайз, а не мгновенный "прыжок". */
.ec-scale-outer { display: flex; align-items: center; justify-content: center; transition: width .18s var(--ease), height .18s var(--ease); }
/* Пока идёт генерация (EmptyCanvas busy-ветка) — GenLiveOverlay внутри уже
   position:absolute;inset:0 (не занимает место в потоке сам по себе),
   поэтому обёртке нужен и position:relative, и защитный min-размер на
   случай (маловероятный, но возможный), если skelBox ещё не успел
   измериться к моменту клика "Сгенерировать" — иначе бокс схлопнулся бы
   в 0×0 и сканер был бы невидим. */
.ec-scale-outer-busy { position: relative; max-width: 100%; min-width: 220px; }
.ec-pulse-wrap { position: relative; display: inline-flex; justify-content: center; align-items: center; transition: transform .18s var(--ease); }
@media (prefers-reduced-motion: reduce) { .ec-scale-outer, .ec-pulse-wrap { transition: none; } }
@keyframes ec-pulse-ring {
  0%   { transform: scale(1); opacity: .55; }
  100% { transform: scale(1.28); opacity: 0; }
}
.ec-pulse-ring {
  position: absolute; inset: -14px;
  border: 2px solid rgba(28,58,87,.22);
  border-radius: 22px;
  pointer-events: none;
  animation: ec-pulse-ring 2.6s ease-out infinite;
}
@media (prefers-reduced-motion: reduce) { .ec-pulse-ring { animation: none; } }

/* ===== Empty Actions ===== */
.empty-actions { display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 300px; margin-top: 10px; }
.ea-btn {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 16px; border-radius: 10px; font-size: var(--text-sm); font-weight: 500;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-soft);
  cursor: pointer; font-family: var(--sans);
  transition: border-color .15s, box-shadow .15s, transform .2s var(--ease);
  box-shadow: var(--sh-sm);
}
.ea-btn:hover { border-color: var(--line-strong); box-shadow: var(--sh-md); transform: translateY(-1px); }
.ea-btn-primary { background: var(--navy); color: #fff; border-color: var(--navy); box-shadow: var(--sh-md); font-weight: 600; }
.ea-btn-primary:hover { background: var(--navy-deep); }
.ea-btn-row { display: flex; gap: 8px; }
.ea-btn-row .ea-btn { flex: 1; justify-content: center; }
.ea-arrow { margin-left: auto; opacity: .55; }

/* ===== First-run Banner ===== */
.frb {
  background: linear-gradient(135deg, var(--navy-deep) 0%, var(--navy) 100%);
  border-bottom: 1px solid rgba(255,255,255,.07);
  padding: 14px 16px 12px;
  flex: none; overflow: hidden;
  max-height: 220px;
  transition: max-height .55s var(--ease), opacity .4s var(--ease), padding .5s var(--ease);
}
.frb-collapse { max-height: 0 !important; opacity: 0; padding-top: 0; padding-bottom: 0; }
.frb-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 11px; }
.frb-icon {
  width: 30px; height: 30px; border-radius: 8px; flex: none;
  background: rgba(140,124,247,.18); border: 1px solid rgba(140,124,247,.32);
  display: grid; place-items: center; font-size: var(--text-sm); color: rgba(140,124,247,.95);
}
.frb-title { color: #fff; font-size: var(--text-sm); font-weight: 700; line-height: 1.3; }
.frb-sub   { color: rgba(255,255,255,.42); font-size: var(--text-xs); margin-top: 2px; }
.frb-close {
  margin-left: auto; background: none; border: none;
  color: rgba(255,255,255,.3); font-size: var(--text-md); cursor: pointer;
  line-height: 1; padding: 0; flex: none; transition: color .15s; font-family: inherit;
}
.frb-close:hover { color: rgba(255,255,255,.65); }
.frb-steps { display: flex; flex-direction: column; gap: 7px; margin-bottom: 10px; }
.frb-step  { display: flex; align-items: center; gap: 9px; }
.frb-step-num {
  width: 18px; height: 18px; border-radius: 50%; flex: none;
  background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.16);
  display: grid; place-items: center; font-size: 9.5px; font-weight: 800;
  color: rgba(255,255,255,.32); transition: background .3s, color .3s, border-color .3s;
}
.frb-step.active .frb-step-num { background: rgba(140,124,247,.88); border-color: transparent; color: var(--navy-deep); }
.frb-step.done   .frb-step-num { background: rgba(46,110,78,.65);  border-color: transparent; color: #fff; }
.frb-step span:last-child { font-size: var(--text-xs); color: rgba(255,255,255,.32); transition: color .3s; }
.frb-step.active span:last-child { color: rgba(255,255,255,.88); font-weight: 500; }
.frb-step.done   span:last-child { color: rgba(255,255,255,.5); }
.frb-progress { height: 3px; background: rgba(255,255,255,.1); border-radius: 2px; overflow: hidden; }
.frb-progress-bar { height: 100%; background: rgba(140,124,247,.8); border-radius: 2px; transition: width .65s var(--ease); }
.frb-meta { display: flex; justify-content: space-between; margin-top: 5px; font-size: var(--text-xs); color: rgba(255,255,255,.28); }
.frb-meta span:last-child { color: rgba(140,124,247,.65); }
.frb-arrow {
  margin-top: 9px; font-size: var(--text-xs); font-weight: 600;
  color: rgba(140,124,247,.85);
  background: rgba(140,124,247,.1); border-top: 1px solid rgba(140,124,247,.18);
  padding: 7px 16px; margin-left: -16px; margin-right: -16px; margin-bottom: -12px;
}

/* ---- canvas ---- */
.canvas { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* .canvas-top теперь рендерится только в пустом состоянии (нет результата) —
   тулбар результата переехал в зону .region-zone-export у самого макета,
   так что высота здесь больше не должна резервироваться под кнопки экспорта. */
.canvas-top {
  height: 56px; flex: none; padding: 14px 26px 0;
  border-bottom: 1px solid var(--line);
  background: rgba(246,247,246,.86); backdrop-filter: blur(10px);
  display: flex; align-items: center; justify-content: space-between;
}
/* На мобильном заголовок (h2) и статус-тег/бейдж рядом с ним (см. upscaler.jsx
   .canvas-top .row) толкались в одну nowrap-строку с фиксированной высотой
   .canvas-top — тег переносился на 2-3 строки и обрезался/наезжал на соседей
   (см. мобильный аудит). Даём заголовку и тегу столбец и снимаем фиксированную
   высоту, чтобы блок рос по контенту вместо обрезки. */
@media (max-width: 640px) {
  .canvas-top {
    height: auto; flex-wrap: wrap; row-gap: 8px; padding: 12px 16px;
  }
  .canvas-top .row.gap-12 {
    flex-direction: column; align-items: flex-start; gap: 4px;
  }
}
/* scrollbar-gutter: stable — резервирует место под скроллбар всегда, даже
   когда контента меньше высоты окна. Без этого высота .hero-preview иногда
   попадала ровно на границу появления/исчезновения скроллбара — ResizeObserver
   (см. heroBox в studio-extras.jsx) детектировал изменение ширины, пересчитывал
   heroScale, картинка меняла размер, скроллбар пропадал/появлялся заново —
   бесконечный цикл, видимый как дрожание холста влево-вправо (воспроизводилось
   именно на 100% масштабе браузера, 2026-07-29). */
.canvas-feed { flex: 1; overflow-y: auto; scrollbar-gutter: stable; padding: 20px 26px; }

/* ---- generation block ---- */
.gen-block { max-width: 1080px; margin: 0 auto 30px; }
.gen-block-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; gap: 14px; }
.gen-grid { display: flex; flex-wrap: wrap; gap: 16px; }

.thumb {
  border: 1px solid var(--line); background: var(--surface); padding: 7px;
  border-radius: 12px; cursor: pointer; position: relative; transition: border-color .15s, box-shadow .15s, transform .15s;
  box-shadow: var(--sh-sm);
}
.thumb:hover { border-color: var(--line-strong); box-shadow: var(--sh-md); transform: translateY(-2px); }
.thumb-card { width: 248px; height: 146px; border-radius: 8px; overflow: hidden; }
.thumb-card > div { transform: scale(.59); transform-origin: top left; }
.thumb-ov {
  position: absolute; inset: 7px; border-radius: 8px;
  background: rgba(18,37,56,.55); backdrop-filter: blur(1px);
  display: flex; align-items: center; justify-content: center; gap: 7px;
  color: #fff; font-size: var(--text-sm); font-weight: 600;
  opacity: 0; transition: opacity .15s;
}
.thumb:hover .thumb-ov { opacity: 1; }
.thumb-tag { display: block; font-size: var(--text-xs); font-weight: 600; color: var(--muted); padding: 7px 3px 1px; }

/* ---- detail overlay ---- */
.overlay {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(18,22,28,.62); backdrop-filter: blur(6px);
  display: grid; place-items: center; padding: 32px;
  animation: fade .22s var(--ease) both;
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.overlay-card {
  background: var(--paper); border-radius: 20px; width: min(1040px, 100%);
  max-height: calc(100vh - 64px); overflow: hidden;
  box-shadow: var(--sh-lg); display: flex; flex-direction: column;
  animation: pop .26s var(--ease) both;
}
@keyframes pop { from { transform: translateY(14px) scale(.985); } to { transform: none; } }
.overlay-head { display: flex; align-items: center; justify-content: space-between; padding: var(--sp-4) var(--sp-6); border-bottom: 1px solid var(--line); flex: none; }
.overlay-body { overflow-y: auto; padding: 28px; }
.overlay-actions {
  display: flex; align-items: center; gap: 8px;
  padding: 14px 24px;
  border-top: 1px solid var(--line);
  flex-shrink: 0;
}
.actions-hint {
  margin-left: auto;
  font-size: var(--text-xs); color: var(--muted);
  white-space: nowrap;
}
.icon-btn {
  width: 40px; height: 40px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface); display: grid; place-items: center; color: var(--ink-soft); cursor: pointer; transition: all .15s;
}
.icon-btn:hover { border-color: var(--line-strong); color: var(--ink); }

/* ============================================================
   DARK THEME
   ============================================================ */
/* Раньше здесь была тёплая (коричневатая) шкала near-black — тот же
   "грязный" конфликт с холодным violet-акцентом, от которого в светлой
   теме уже ушли (см. комментарий у --paper в начале файла: "рядом с
   холодным violet-акцентом [тёплый фон] читался грязным"). Тёмная тема
   этот переход тогда не получила — тут та же холодная нейтральная шкала.

   Второе и главное: --navy/--navy-deep/--navy-line/--good НИКОГДА не
   переопределялись для тёмной темы — а используются как ЦВЕТ ТЕКСТА/ИКОНОК
   в сотнях мест (активные вкладки, чипы, фокус-рамки, статусы) поверх
   светлого --navy-tint из СВЕТЛОЙ темы. Светлый --navy (#1C3A57) на тёмном
   фоне почти не виден — это и есть причина "половина всего еле видна".
   Ниже — та же навигационная синяя, но осветлённая для контраста на тёмном
   фоне (~7:1 к --paper), а не другой цвет: акцент остаётся узнаваемым. */
[data-theme="dark"] {
  color-scheme: dark;
  --paper:       #14161A;
  --paper-2:     #1A1D22;
  --surface:     #23262C;
  --surface-ink: #0A0B0D;
  --ink:         #F2F4F6;
  --ink-soft:    #C6CBD2;
  --muted:       #8B929B;
  --faint:       #5E6570;
  --line:        #363A42;
  --line-soft:   #282B31;
  --line-strong: #474C56;
  --navy:        #6BA8DC;
  --navy-deep:   #2E4E6B;
  --navy-tint:   #1B2A3A;
  --navy-line:   #3A5975;
  --bronze:      #9C8CFF;
  --bronze-tint: #241F3D;
  --good:        #5FC98A;
  --good-tint:   #163226;

  --glass-bg: rgba(35,38,44,.58);
  --glass-border: rgba(255,255,255,.10);
  --glass-shadow: 0 16px 44px rgba(0,0,0,.45), 0 2px 10px rgba(0,0,0,.3), inset 0 1px 0 rgba(255,255,255,.06);
}
/* Пример-плейсхолдер композера (см. .vta-wrap правило ниже по файлу) —
   в светлой теме он намеренно "в 4 раза бледнее обычного" (opacity .25),
   чтобы не спорить с текстом пользователя; на тёмном --faint той же
   логики уже недостаточно контраста, чтобы разглядеть текст вообще —
   тот же принцип бледности, тёмная тема просто требует более высокого
   стартового opacity, чтобы остаться читаемым. */
[data-theme="dark"] .vta-wrap textarea.textarea::placeholder { opacity: .5; }
[data-theme="dark"] .nav { background: rgba(20,22,26,.88); border-bottom-color: rgba(255,255,255,.07); }
[data-theme="dark"] .card { background: var(--surface); }
[data-theme="dark"] .input, [data-theme="dark"] .textarea { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
[data-theme="dark"] .settings-panel, [data-theme="dark"] .settings-foot { background: var(--surface); }
[data-theme="dark"] .canvas-top { background: rgba(20,22,26,.82); }
[data-theme="dark"] .cmd-search { background: var(--surface); border-color: var(--line-strong); }
[data-theme="dark"] .cmd-search input, [data-theme="dark"] .all-modal-search input { color: var(--ink); }
[data-theme="dark"] .cmd-tile, [data-theme="dark"] .type-card { background: var(--surface); }
[data-theme="dark"] .all-modal-search { background: var(--paper); }
/* .topbar фон уже следует за --surface-ink (см. базовое правило ниже) —
   отдельный хардкод здесь раньше держал СТАРОЕ тёплое значение (#0A0908)
   и не обновлялся вместе с токеном. */

/* ============================================================
   TOP BAR (theme toggle)
   ============================================================ */
.topbar {
  height: 40px;
  background: var(--surface-ink);
  display: flex; align-items: center;
  border-bottom: 1px solid rgba(255,255,255,.06);
  position: sticky; top: 0; z-index: 60;
}
.topbar-inner { display: flex; align-items: center; justify-content: space-between; height: 100%; }
.topbar-note { font-size: var(--text-sm); color: #6F6B61; letter-spacing: .01em; }
.theme-toggle { display: flex; align-items: center; gap: 3px; background: rgba(255,255,255,.07); border-radius: 8px; padding: 3px; }
.theme-btn {
  height: 26px; padding: 0 12px; border-radius: 6px; border: none; cursor: pointer;
  font-size: var(--text-xs); font-weight: 600; display: inline-flex; align-items: center; gap: 5px;
  color: #7A766C; background: transparent; transition: all .15s; font-family: var(--sans);
}
.theme-btn.active { background: var(--navy); color: #fff; }
[data-theme="dark"] .theme-btn.active { background: #2E2C27; color: var(--ink); }

/* ============================================================
   IMAGE CROP MODAL (imageCropper.jsx) — апскейлер + загрузка логотипа
   ============================================================ */
.crop-modal-bg {
  position: fixed; inset: 0; z-index: 260;
  background: rgba(14,18,24,.60); backdrop-filter: blur(7px);
  display: grid; place-items: center; padding: 24px;
  animation: fade .2s var(--ease) both;
}
/* Сплошной --surface, не полупрозрачное стекло (запрос владельца
   2026-08-03) — карточка сидит на затемнённой .crop-modal-bg, стекло
   просвечивало тёмным и читалось грязно-серым, а не белым окном. */
.crop-modal {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  /* 900px (было 600) — вместе с MAX_STAGE=760 в imageCropper.jsx/
     qrOverlayEditor.jsx (запрос владельца 2026-08-08): при старой ширине
     600px даже увеличенный стейдж упирался в неё раньше, чем в реальный
     лимит экрана (max-height ниже) — превью оставалось маленьким в
     середине пустого высокого окна. */
  width: min(900px, 100%); max-height: calc(100vh - 48px);
  overflow: hidden; display: flex; flex-direction: column;
  box-shadow: var(--glass-shadow);
  animation: pop .24s var(--ease) both;
}
/* Отступы шапки/сетки размера чуть срезаны (было 18px/22px) — та же правка
   2026-08-08: меньше "воздуха" вокруг увеличенного превью, чтобы модалка
   росла в основном за счёт самого превью, а не за счёт chrome вокруг него. */
.crop-modal-head {
  padding: 14px 20px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; flex: none;
  cursor: grab;
}
.crop-modal-head button { cursor: auto; }
.crop-modal-head h3 { font-size: 16px; margin: 0; }
.crop-stage-wrap {
  flex: 1; display: grid; place-items: center; padding: 16px 22px;
  background: var(--paper-2); min-height: 240px;
}
.crop-stage { position: relative; user-select: none; }
.crop-stage-img { display: block; width: 100%; height: 100%; -webkit-user-drag: none; }
/* Затемнение всего, что вне рамки обрезки — 4 полосы вокруг rect вместо
   clip-path-трюка с "дыркой": проще, надёжнее кросс-браузерно. */
.crop-mask-bar { position: absolute; background: rgba(14,18,24,.55); pointer-events: none; }
.crop-rect {
  position: absolute; border: 1.5px solid #fff;
  box-shadow: 0 0 0 1px rgba(0,0,0,.35), 0 0 0 3000px rgba(0,0,0,0);
  cursor: move; touch-action: none;
}
.crop-handle {
  position: absolute; width: 14px; height: 14px; margin: -7px;
  background: #fff; border: 1.5px solid var(--navy); border-radius: 4px;
  touch-action: none;
}
.crop-handle-nw { top: 0; left: 0; }
.crop-handle-ne { top: 0; left: 100%; }
.crop-handle-sw { top: 100%; left: 0; }
.crop-handle-se { top: 100%; left: 100%; }
/* Средние точки краёв — тянуть только одну сторону (ширину ИЛИ высоту),
   не обе сразу, как у угловых. margin:-7px общего правила .crop-handle
   уже центрирует их и по средней оси (left/top:50%), не только по углу. */
.crop-handle-n { top: 0; left: 50%; }
.crop-handle-s { top: 100%; left: 50%; }
.crop-handle-w { top: 50%; left: 0; }
.crop-handle-e { top: 50%; left: 100%; }
.crop-aspect-row { display: flex; gap: 8px; justify-content: center; padding: 10px 20px 0; flex-wrap: wrap; }
.crop-aspect-chip {
  height: 30px; padding: 0 14px; border-radius: 8px;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); font-size: 13px; font-weight: 600; cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.crop-aspect-chip:hover { border-color: var(--navy); color: var(--navy); }
.crop-aspect-chip.active { border-color: var(--navy); background: var(--navy); color: #fff; }
.crop-rotate-btn {
  width: 30px; height: 30px; border-radius: 8px; flex: none;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); cursor: pointer; display: grid; place-items: center;
  transition: border-color .15s, color .15s;
}
.crop-rotate-btn:hover { border-color: var(--navy); color: var(--navy); }
/* Свободный поворот (imageCropper.jsx) — классическая схема с топовых
   редакторов (PowerPoint/Canva/Figma): один ВИДИМЫЙ маркер на стержне над
   рамкой, а не спрятанная зона за углом изображения — за него тянут, чтобы
   повернуть фото на любой градус. */
.crop-rotate-stem {
  position: absolute; top: -28px; left: 50%; width: 1.5px; height: 28px;
  background: var(--line-strong); transform: translateX(-50%); pointer-events: none;
}
.crop-rotate-handle {
  position: absolute; top: -28px; left: 50%; width: 24px; height: 24px;
  border-radius: 50%; transform: translate(-50%, -50%);
  background: var(--surface); border: 1.5px solid var(--navy); color: var(--navy);
  display: grid; place-items: center; touch-action: none; cursor: grab;
  box-shadow: var(--sh-sm);
}
.crop-rotate-handle:active { cursor: grabbing; }
.crop-actions { display: flex; justify-content: flex-end; gap: 10px; padding: 14px 20px; flex: none; }
/* На узком экране (мобильный) "Обрезать и продолжить" + "Пропустить" в ряд
   (nowrap-текст, см. .btn white-space:nowrap) вместе шире доступной ширины
   модалки — первая кнопка вылезала за левый край экрана (дизайн-аудит
   2026-08-08, найдено визуальным ревью после расширения превью). Тот же
   паттерн "в столбец, кнопки на всю ширину", что уже применён у
   .mini-output-secondary/.gallery-submit-teaser в этом файле. */
@media (max-width: 480px) {
  .crop-actions { flex-direction: column; }
  .crop-actions .btn { width: 100%; }
}

/* ---- split review modal (imageCropper.jsx SplitReviewModal) ---- */
.split-review-stage { cursor: default; }
.split-half { position: absolute; left: 0; width: 100%; pointer-events: none; }
.split-half-1 { background: rgba(28,58,87,.16); }
.split-half-2 { background: rgba(179,111,56,.16); }
.split-half.split-axis-x { left: auto; width: auto; top: 0; height: 100%; }
.split-half-label {
  position: absolute; font-size: 11px; font-weight: 700; color: #fff;
  background: rgba(14,18,24,.55); border-radius: 6px; padding: 3px 8px;
  pointer-events: none; left: 8px;
}
.split-half-label-2 { top: auto; }
.split-line {
  position: absolute; left: 0; width: 100%; height: 0;
  border-top: 2px dashed #fff; cursor: ns-resize; touch-action: none;
  filter: drop-shadow(0 0 2px rgba(0,0,0,.6));
}
.split-line.split-axis-x { left: 0; top: 0; width: 0; height: 100%; border-top: none; border-left: 2px dashed #fff; cursor: ew-resize; }
.split-line-handle {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 30px; height: 18px; border-radius: 9px;
  background: #fff; border: 1.5px solid var(--navy); box-shadow: var(--sh-sm);
}

/* ============================================================
   QR OVERLAY MODAL (qrOverlayEditor.jsx) — QR поверх готового макета
   Переиспользует .crop-modal-bg/.crop-modal/.crop-stage/.crop-rect/
   .crop-handle из блока IMAGE CROP MODAL выше (тот же визуальный язык).
   ============================================================ */
/* БЕЗ overflow:hidden — иначе клипается хэндл поворота (crop-rotate-handle),
   который намеренно стоит ЦЕЛИКОМ выше рамки: overflow:hidden не только
   прячет его визуально, но и делает некликабельным (клип убирает элемент
   из hit-test), клик проваливается на макет под рамкой. */
.qr-ov-rect { }
.qr-ov-preview-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.qr-ov-controls {
  display: flex; flex-direction: column; gap: 12px;
  padding: 16px 22px 0; flex: none;
}
.qr-ov-text-input {
  font-family: var(--sans); font-size: var(--text-sm); font-weight: 500; color: var(--ink);
  border: 1.5px solid var(--line-strong); border-radius: 8px; padding: 8px 12px;
  background: var(--surface); outline: none; width: 100%;
}
.qr-ov-text-input:focus { border-color: var(--navy); }
.qr-ov-color-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.qr-ov-invert-btn {
  margin-left: auto; height: 28px; padding: 0 12px; border-radius: 8px;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); font-size: var(--text-xs); font-weight: 600; cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.qr-ov-invert-btn:hover { border-color: var(--navy); color: var(--navy); }
.qr-ov-invert-btn.active { border-color: var(--navy); background: var(--navy); color: #fff; }
.qr-ov-sat-row { display: flex; align-items: center; gap: 10px; font-size: var(--text-xs); color: var(--ink-soft); }
.qr-ov-sat-row input[type="range"] { flex: 1; }
.qr-ov-hint { font-size: var(--text-xs); color: var(--muted); }
.qr-ov-mode-row { display: flex; gap: 8px; }
.qr-ov-mode-btn {
  flex: 1; height: 32px; border-radius: 8px; border: 1.5px solid var(--line-strong);
  background: var(--surface); color: var(--ink-soft); font-size: var(--text-xs); font-weight: 600;
  cursor: pointer; transition: border-color .15s, color .15s, background .15s;
}
.qr-ov-mode-btn:hover { border-color: var(--navy); color: var(--navy); }
.qr-ov-mode-btn.active { border-color: var(--navy); background: var(--navy); color: #fff; }
.qr-ov-rect-selected { outline: 2px solid var(--navy); outline-offset: 1px; }
.qr-ov-rect-unreadable { outline: 2px dashed #C0392B; outline-offset: 2px; animation: qr-ov-unreadable-pulse 1.4s ease-in-out infinite; }
@keyframes qr-ov-unreadable-pulse { 0%, 100% { outline-color: #C0392B; } 50% { outline-color: #f2b8ae; } }
.qr-ov-item-delete {
  position: absolute; top: -9px; right: -9px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--navy); color: #fff; border: 1.5px solid #fff; display: grid; place-items: center;
  cursor: pointer; box-shadow: var(--sh-sm); padding: 0;
}
.qr-ov-caption-preview {
  position: absolute; top: 100%; left: 0; width: 100%; margin-top: 4px;
  font-size: 10px; line-height: 1.2; text-align: center; color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,.6); pointer-events: none; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.qr-ov-eyedrop-btn {
  width: 40px; height: 40px; border-radius: 10px; border: 1.5px solid var(--line-strong);
  background: var(--surface); color: var(--ink); display: grid; place-items: center;
  cursor: pointer; transition: border-color .15s, color .15s, background .15s; flex: none;
}
.qr-ov-eyedrop-btn:hover { border-color: var(--navy); color: var(--navy); background: var(--paper-2); }
/* Свой цвет — тот же квадрат, что пипетка (.qr-ov-eyedrop-btn), сразу после
   неё; иконка палитры вместо заливки текущим цветом (это КНОПКА "выбрать
   цвет", а не индикатор уже выбранного — тот виден по активному свотчу в
   самой палитре). Скрытый native <input type=color> растянут поверх для
   клика, как раньше у кружка .qr-ov-color-custom. */
.qr-ov-custompick-btn { position: relative; overflow: hidden; }
.qr-ov-custompick-btn input[type="color"] {
  position: absolute; inset: -4px; width: calc(100% + 8px); height: calc(100% + 8px);
  border: none; padding: 0; cursor: pointer; opacity: 0;
}
.qr-ov-edit-selected {
  display: flex; flex-direction: column; gap: 8px; margin-top: 4px; padding-top: 10px;
  border-top: 1px dashed var(--line-strong);
}
.qr-ov-palette-group {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex: 1;
  border: 1.5px solid var(--line); border-radius: 10px; padding: 6px 10px;
  background: var(--paper-2);
}
.qr-ov-palette-label { font-size: var(--text-xs); font-weight: 600; color: var(--ink-soft); white-space: nowrap; }
.qr-ov-caption-color-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.qr-ov-caption-color-label { font-size: var(--text-xs); color: var(--ink-soft); white-space: nowrap; }
/* .crop-stage-wrap (общий с ImageCropModal) — flex:1 без overflow не может
   сжаться меньше фиксированного px-размера макета внутри, и при большом
   количестве QR (композер + "редактирование выбранного" + два цвета и т.д.
   в .qr-ov-controls) вылезал за пределы .crop-modal, наезжая на контролы
   под собой. Оборачиваем стейдж+контролы в свою прокручиваемую область —
   не трогая сам .crop-stage-wrap, чтобы не задеть ImageCropModal. */
.qr-ov-scroll-body { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }
/* .crop-stage-wrap делит flex:1 с .qr-ov-controls внутри .qr-ov-scroll-body —
   без flex-shrink:0 оно СЖИМАЕТСЯ ниже фиксированного px-размера макета
   внутри (.crop-stage со стилем width/height=stage.w/h), а сам макет
   (не будучи flex-элементом) не сжимается вместе с ним — картинка
   визуально вылезала из своей же обёртки и наезжала на контролы под ней.
   Прокрутка нужна только .qr-ov-scroll-body — сам стейдж всегда должен
   оставаться целым, в родном масштабе. Только для этой модалки (не для
   ImageCropModal, который делит .crop-stage-wrap, но не имеет такой
   проблемы — контролов там меньше и не растут динамически). */
.qr-ov-modal .crop-stage-wrap { flex-shrink: 0; overflow: hidden; }

/* ============================================================
   ALL-TYPES MODAL
   ============================================================ */
.all-modal-bg {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(14,18,24,.60); backdrop-filter: blur(7px);
  display: grid; place-items: center; padding: 24px;
  animation: fade .2s var(--ease) both;
}
/* Сплошной --surface, не стекло (запрос владельца 2026-08-03) — та же
   правка, что у .crop-modal/.auth-modal выше: карточка на затемнённой
   .all-modal-bg просвечивала тёмным и читалась грязно-серой. */
.all-modal {
  background: var(--surface);
  border-radius: 22px;
  width: min(940px, 100%); max-height: calc(100vh - 48px);
  overflow: hidden; display: flex; flex-direction: column;
  box-shadow: var(--glass-shadow); border: 1px solid var(--line);
  animation: pop .24s var(--ease) both;
}
.all-modal-head { padding: 22px 26px 16px; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 14px; flex: none; cursor: grab; }
/* Перетаскивание — только за пустое место шапки (см. useDraggableModal,
   ui.jsx); интерактивные потомки (строка поиска, кнопка закрытия) должны
   остаться со своим обычным курсором, а не "лапкой" перетаскивания. */
.all-modal-head .all-modal-search, .all-modal-head button, .all-modal-head input { cursor: auto; }
.all-modal-search {
  flex: 1; display: flex; align-items: center; gap: 10px;
  background: var(--paper-2); border: 1px solid var(--line);
  border-radius: 10px; padding: 0 14px; height: 44px;
  transition: border-color .15s, box-shadow .15s;
}
.all-modal-search:focus-within { border-color: var(--navy); box-shadow: 0 0 0 3px rgba(28,58,87,.1); }
.all-modal-search input { flex: 1; border: none; outline: none; background: transparent; font-family: var(--sans); font-size: var(--text-base); color: var(--ink); }
.all-modal-search input::placeholder { color: var(--faint); }
.all-modal-body { overflow-y: auto; padding: 22px 26px 28px; }
.all-types-section-title {
  font-family: var(--serif); font-size: 15px; font-weight: 600; color: var(--ink);
  margin: 22px 0 12px;
}
.all-types-section-title:first-child { margin-top: 0; }
.all-types-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.type-card {
  background: var(--paper); border: 1px solid var(--line); border-radius: 14px; padding: 16px 14px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px; cursor: pointer; text-align: left;
  transition: border-color .15s, box-shadow .15s, transform .15s;
}
.type-card:hover { border-color: var(--navy); box-shadow: 0 0 0 3px rgba(28,58,87,.09); transform: translateY(-2px); }
.type-card-ic { width: 42px; height: 42px; border-radius: 11px; display: grid; place-items: center; margin-bottom: 9px; flex: none; }
.type-card-name { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.type-card-sub  { font-size: var(--text-xs); color: var(--muted); line-height: 1.35; }

/* "Свой размер" — первая плитка в "Все форматы" (запрос владельца
   2026-07-31), не кнопка целиком (внутри реальные <input>, вложенный
   <button> в <button> невалиден и путает фокус/клик) — обычный div с тем
   же визуальным языком .type-card, cursor:default вместо pointer/hover-лифта. */
.type-card-custom { cursor: default; }
.type-card-custom:hover { border-color: var(--line); box-shadow: none; transform: none; }
.type-card-custom-row { display: flex; align-items: center; gap: 6px; width: 100%; margin-top: 10px; }
.type-card-custom-row input {
  width: 0; flex: 1; min-width: 0; height: 32px; border-radius: 7px; border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink); font-size: 13px; padding: 0 8px; font-family: var(--sans);
}
.type-card-custom-row span { color: var(--muted); font-size: 12px; flex: none; }
.type-card-custom-unit { margin-left: -2px; }

/* ============================================================
   ПОИСК ПО САЙТУ (Nav, значок слева от темы, Ctrl/Cmd+K) — тот же
   визуальный язык, что у ALL-TYPES MODAL выше, но у́же и без сетки: список
   строк как в command-палитре (VS Code / Spotlight), а не плитки.
   ============================================================ */
.search-modal-bg { align-items: flex-start; padding-top: 12vh; }
/* Обычное непрозрачное окно, БЕЗ liquid-glass эффекта .all-modal (запрос
   владельца 2026-08-01) — в отличие от AllTypesModal/остальных модалок,
   которые тот эффект сохраняют, тут сплошная заливка var(--surface) и
   обычная тень вместо blur/saturate по фону позади окна. */
.search-modal {
  width: min(560px, 100%); max-height: 60vh;
  background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none;
  border: 1px solid var(--line);
  box-shadow: 0 12px 40px rgba(20,19,15,.16), 0 2px 8px rgba(20,19,15,.08);
}
[data-theme="dark"] .search-modal { box-shadow: 0 16px 44px rgba(0,0,0,.45), 0 2px 10px rgba(0,0,0,.3); }
/* Строка поиска — вдвое выше (запрос владельца 2026-08-01: было 44px, как
   у .all-modal-search), с пропорционально увеличенным текстом — иначе
   более высокая полоса с той же мелкой иконкой/шрифтом читалась бы как
   лишний пустой отступ, а не как крупная строка поиска. */
.search-modal-input { margin: 18px 22px 4px; cursor: grab; flex: none; height: 59px; }
.search-modal-input input { font-size: 18px; }
.search-modal-input input, .search-modal-esc { cursor: auto; }
.search-modal-esc {
  flex: none; font-family: var(--sans); font-size: 11px; color: var(--muted);
  background: var(--paper); border: 1px solid var(--line); border-radius: 5px; padding: 2px 6px;
}
.search-modal-body { overflow-y: auto; padding: 10px 14px 18px; }
.search-modal-section-title {
  font-family: var(--sans); font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--faint); margin: 14px 10px 6px;
}
.search-modal-section-title:first-child { margin-top: 4px; }
.search-result-row {
  width: 100%; display: flex; align-items: center; gap: 12px; text-align: left; cursor: pointer;
  background: none; border: none; border-radius: 10px; padding: 9px 10px;
}
.search-result-row.active, .search-result-row:hover { background: var(--paper-2); }
.search-result-ic {
  flex: none; width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center;
  background: var(--paper-2); color: var(--muted);
}
.search-result-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.search-result-name { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.search-result-sub { font-size: var(--text-xs); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Строка-семейство ("Визитка"/"Листовка"/"Карточка маркетплейса") — сама
   не кликабельна (внутри реальные <button>-чипы, вложенный button в button
   невалиден), поэтому cursor:default вместо pointer у .search-result-row. */
.search-result-family { cursor: default; }
.search-result-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.search-result-chip {
  font-family: var(--sans); font-size: 12px; font-weight: 600; color: var(--ink);
  background: var(--paper); border: 1px solid var(--line); border-radius: 7px;
  padding: 3px 9px; cursor: pointer; transition: border-color .15s, background .15s;
}
.search-result-chip:hover { border-color: var(--navy); background: var(--navy-tint); color: var(--navy); }

/* ============================================================
   COMMAND HERO (landing search + type tiles)
   ============================================================ */
.cmd-search {
  display: flex; align-items: center; gap: 12px;
  max-width: 720px; margin: 30px auto 34px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  padding: 0 14px 0 18px; height: 60px;
  box-shadow: var(--sh-sm);
  transition: border-color .18s, box-shadow .18s;
}
.cmd-search:focus-within { border-color: var(--navy); box-shadow: 0 0 0 4px rgba(28,58,87,.1); }
.cmd-search input {
  flex: 1; border: none; outline: none; background: transparent;
  font-family: var(--sans); font-size: 16px; color: var(--ink);
}
.cmd-search input::placeholder { color: var(--faint); }
.cmd-kbd {
  font-size: var(--text-xs); color: var(--muted); border: 1px solid var(--line);
  border-radius: 6px; padding: 4px 9px; background: var(--paper); flex: none;
}

/* ── AI GLOW BORDER — переливающаяся рамка вокруг главных полей запроса
   (поиск на лендинге, поле описания макета в студии), в духе leonardo.ai,
   но адаптированная под белый фон сайта вместо их тёмной панели.
   Раньше — сплошное кольцо, у которого просто "плыл" градиент внутри себя
   (background-position). У leonardo эффект другой: сама рамка почти не
   видна, а вокруг неё бежит яркая "комета" со шлейфом. Здесь тот же
   принцип на conic-gradient, вращаемом через анимируемую custom property
   (@property ниже даёт браузеру плавно интерполировать угол, а не
   дёргать его скачками), по часовой стрелке. Две одинаковые кометы
   строго друг напротив друга (offset 180°) — с одной было ощущение
   "рваного" кольца, когда бОльшая часть контура надолго гасла; с двумя
   зеркальными дугами свечение всегда есть с обеих сторон одновременно,
   и блок читается цельным. Толщина кольца (padding: 1.5px) не менялась. ── */
@property --glow-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}
.ai-glow-field { position: relative; }
.ai-glow-field::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  padding: 1.5px;
  --glow-angle: 0deg;
  /* Второй (нижний) слой — еле видная статичная линия по всему контуру,
     чтобы кольцо не пропадало совсем в промежутках между кометами;
     первый (верхний) слой — сами анимированные кометы поверх неё. */
  background:
    conic-gradient(from var(--glow-angle),
      transparent              0deg,
      rgba(124,111,240,.35)   30deg,
      #7C6FF0                  53deg,
      #ED6FBB                  77deg,
      #B26BEE                  97deg,
      transparent            105deg,
      transparent            180deg,
      rgba(124,111,240,.35)  210deg,
      #7C6FF0                233deg,
      #ED6FBB                257deg,
      #B26BEE                277deg,
      transparent            285deg,
      transparent            360deg),
    rgba(124,111,240,.16);
  pointer-events: none;
  opacity: .95;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  animation: ai-glow-spin 6.8s linear infinite;
}
.cmd-search.ai-glow-field::after { border-radius: 14px; }
.composer-with-attach.ai-glow-field::after { border-radius: 16px; }
/* Обычная статичная рамка поля здесь не нужна — контур целиком рисует
   анимированное кольцо выше, иначе получается двойная рамка (глухая
   исходная + поверх неё ещё и переливающаяся). box-shadow тоже гасим —
   иначе на фокусе поверх анимированного кольца всплывает ещё и мягкое
   navy-свечение из :focus (та же "двойная рамка", просто одна половина
   мягкая и почти незаметная на глаз, а не отсутствующая). */
.cmd-search.ai-glow-field,
.cmd-search.ai-glow-field:focus-within {
  border-color: transparent;
  box-shadow: none;
}
.composer-with-attach.ai-glow-field .textarea,
.composer-with-attach.ai-glow-field .textarea:focus,
.composer-with-attach.ai-glow-field .textarea:focus-visible {
  border-color: transparent;
  box-shadow: none;
  /* Глобальное a11y-правило :focus-visible (см. ниже по файлу) рисует
     сплошной navy outline вокруг ЛЮБОГО фокусируемого поля — на textarea
     Chrome включает :focus-visible даже по клику мышью (не только с
     клавиатуры), поэтому здесь получалась "вторая рамка" поверх уже
     анимированного кольца ai-glow-field. Глушим именно outline, а не
     весь :focus-visible глобально — акцессибилити для остальных полей
     сайта (кнопки, обычные инпуты) не трогаем. */
  outline: none;
}
@keyframes ai-glow-spin {
  to { --glow-angle: 360deg; }
}
@media (prefers-reduced-motion: reduce) {
  .ai-glow-field::after { animation: none; }
}

.cmd-tiles { display: flex; justify-content: center; gap: 14px; flex-wrap: wrap; }
.cmd-tile {
  width: 168px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 16px; padding: 20px 14px 16px;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  cursor: pointer; transition: border-color .15s, box-shadow .15s, transform .15s;
}
.cmd-tile:hover { border-color: var(--line-strong); box-shadow: var(--sh-md); transform: translateY(-3px); }
.cmd-tile-ic {
  width: 52px; height: 52px; border-radius: 14px;
  display: grid; place-items: center; margin-bottom: 10px;
}
.cmd-tile-label { font-size: var(--text-base); font-weight: 600; color: var(--ink); }
.cmd-tile-sub { font-size: var(--text-xs); color: var(--muted); }

/* ============================================================
   LEONARDO-STYLE GALLERY — Featured strip + Community grid
   ============================================================ */
.gallery-strip {
  display: flex; gap: 4px; overflow-x: auto; padding-bottom: 6px;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
}
/* ── ideogram.ai-style masonry: плотная колоночная сетка без скруглений и
   рамок, картинка встык, без подписи под ней (только счётчик лайков
   поверх, снизу-слева) — см. скриншот-референс. Раньше здесь была чистая
   CSS column-count раскладка — заменена на JS balanced-колонки (см.
   MasonryGallery, landing.jsx, запрос владельца 2026-08-03 "мозаика без
   дыр"): column-count укладывает плитки строго по исходному порядку,
   слепо к их реальной высоте, поэтому один сильно портретный кадр мог
   утащить свою колонку вниз и оставить дыры в соседних. .gallery-grid
   (column-count) теперь используется ТОЛЬКО под скелетоны загрузки —
   реальный контент рендерится через .gallery-masonry/.gallery-masonry-col
   ниже, у скелетонов реальных пропорций всё равно нет и не нужно. */
.gallery-grid {
  column-count: 5; column-gap: 4px;
}
@media (max-width: 1100px) { .gallery-grid { column-count: 3; } }
@media (max-width: 700px)  { .gallery-grid { column-count: 2; } }
/* .gallery-submit-teaser — до этой правки текст+кнопка сидели В РЯД
   (justify-content:space-between) на любой ширине; укороченный лейбл кнопки
   (см. i18n/pages.js) сам по себе чинит overflow, но узкая колонка на
   мобильном всё равно сжимала заголовок+подзаголовок до пары слов в строке
   рядом с кнопкой — тесно и нечитаемо. Складываем в столбец, кнопка на всю
   ширину — тот же паттерн карточки-с-CTA, что на большинстве сайтов. */
@media (max-width: 700px) {
  .gallery-submit-teaser { flex-direction: column; align-items: stretch; }
  .gallery-submit-teaser .btn { width: 100%; }
}
.gallery-masonry { display: flex; align-items: flex-start; gap: 4px; }
.gallery-masonry-col { display: flex; flex-direction: column; gap: 4px; flex: 1 1 0; min-width: 0; }
.gallery-tile {
  background: var(--paper-2); border: none; border-radius: 0;
  overflow: hidden; cursor: pointer;
  break-inside: avoid; margin-bottom: 4px;
}
.gallery-tile-compact {
  flex: none; width: 200px; scroll-snap-align: start; margin-bottom: 0;
}
.gallery-tile-thumb {
  position: relative; display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
/* height:100% + object-fit:cover — плитка задаёт себе высоту через inline
   aspect-ratio (см. ExampleGalleryTile ratio prop, уже клампленный в
   MasonryGallery), картинка заполняет и обрезается по центру, а не
   "торчит" на всю свою исходную высоту (запрос владельца 2026-08-03,
   п. 3 — самое большое/вытянутое изображение не должно доминировать). */
.gallery-tile-thumb img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .25s var(--ease);
}
.gallery-tile:hover .gallery-tile-thumb img { transform: scale(1.06); }
/* Пустая плитка-заглушка, пока список грузится с сервера (см.
   LeonardoGallery, items===null) — тот же силуэт сетки, без "прыжка"
   разметки, когда реальные фото придут и заменят её. */
.gallery-tile-skeleton {
  height: 160px;
  background: linear-gradient(100deg, var(--paper-2) 30%, var(--paper) 50%, var(--paper-2) 70%);
  background-size: 200% 100%;
  animation: gallerySkeletonShimmer 1.4s ease-in-out infinite;
}
@keyframes gallerySkeletonShimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* Счётчик лайков — тот же приём, что и было задумано ("снизу-слева"), но
   теперь реальная кнопка (POST /api/example-gallery/:id/like), не просто
   декоративная цифра — сердце заливается и число меняется по клику. */
/* right, не left (дизайн-аудит 2026-08-08) — нижний левый угол зарезервирован
   под контактную строку самих сгенерированных макетов (см. CLAUDE.md/
   cardCompositing.js SAFE_ZONE.contact — телефон/email/сайт всегда там),
   поэтому бейдж лайков на реальных карточках галереи структурно ложился
   поверх номера телефона/handle'а. Нижний правый — единственный угол, не
   занятый ни контактами (низ-лево), ни логотипом (верх-лево). */
.gallery-like-btn {
  position: absolute; right: 8px; bottom: 8px; z-index: 2;
  display: flex; align-items: center; gap: 5px;
  background: rgba(20,20,24,.6); color: #fff; border: none; border-radius: 999px;
  padding: 4px 10px 4px 8px; font-size: 12px; font-weight: 700;
  cursor: pointer; backdrop-filter: blur(3px);
  transition: background .15s, transform .15s;
}
.gallery-like-btn:hover { background: rgba(20,20,24,.8); transform: scale(1.05); }
.gallery-like-btn.liked { color: #ff5c7a; }
.gallery-like-btn.liked svg { color: #ff5c7a; }
.gallery-like-btn:disabled { opacity: .6; cursor: default; pointer-events: none; }

/* ============================================================
   GALLERY LIGHTBOX — просмотр примера крупно (в духе ideogram.ai),
   запрос владельца 2026-08-02: фото + описание + инструменты сайта справа.
   ============================================================ */
.gallery-lightbox-bg {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(10,10,12,.92);
  display: flex; align-items: center; justify-content: center;
  padding: 32px; animation: fade .2s var(--ease) both;
}
.gallery-lightbox-body {
  width: 100%; max-width: 1180px; max-height: calc(100vh - 64px);
  display: flex; gap: 0; background: var(--surface); border-radius: 16px;
  overflow: hidden; box-shadow: 0 30px 80px rgba(0,0,0,.5);
  animation: pop .22s var(--ease) both;
}
.gallery-lightbox-imgwrap {
  flex: 1 1 auto; min-width: 0; background: #111;
  display: flex; align-items: center; justify-content: center;
}
.gallery-lightbox-imgwrap img { max-width: 100%; max-height: calc(100vh - 64px); display: block; object-fit: contain; }
.gallery-lightbox-side {
  flex: none; width: 320px; padding: 22px; display: flex; flex-direction: column; gap: 18px;
  overflow-y: auto; border-left: 1px solid var(--line-soft);
}
.gallery-lightbox-like {
  position: static; align-self: flex-start;
  background: var(--paper-2); color: var(--ink);
}
.gallery-lightbox-like.liked { color: #ff5c7a; }
.gallery-lightbox-desc-title, .gallery-lightbox-tools-title {
  font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--faint); margin-bottom: 8px;
}
.gallery-lightbox-desc p { font-size: 13.5px; line-height: 1.55; color: var(--ink-soft); margin: 0; }
.gallery-lightbox-tools { display: flex; flex-direction: column; gap: 8px; margin-top: auto; }
.gallery-lightbox-tool-btn {
  display: flex; align-items: center; gap: 11px; text-align: left;
  background: var(--paper-2); border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 12px; cursor: pointer; color: var(--ink);
  transition: border-color .15s, background .15s;
}
.gallery-lightbox-tool-btn:hover { border-color: var(--navy); background: var(--navy-tint); }
.gallery-lightbox-tool-text { display: flex; flex-direction: column; gap: 1px; }
.gallery-lightbox-tool-text b { font-size: 13px; font-weight: 600; }
.gallery-lightbox-tool-text small { font-size: 11px; color: var(--muted); }
.gallery-lightbox-close {
  position: absolute; top: 24px; right: 28px; z-index: 2;
  width: 40px; height: 40px; border-radius: 50%; border: none;
  background: rgba(255,255,255,.1); color: #fff; cursor: pointer;
  display: grid; place-items: center; transition: background .15s;
}
.gallery-lightbox-close:hover { background: rgba(255,255,255,.2); }
.gallery-lightbox-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 48px; height: 48px; border-radius: 50%; border: none;
  background: rgba(255,255,255,.1); color: #fff; cursor: pointer;
  display: grid; place-items: center; transition: background .15s;
}
.gallery-lightbox-nav:hover { background: rgba(255,255,255,.2); }
.gallery-lightbox-nav-prev { left: 24px; }
.gallery-lightbox-nav-next { right: 24px; }
@media (max-width: 860px) {
  .gallery-lightbox-body { flex-direction: column; max-height: calc(100vh - 32px); }
  .gallery-lightbox-side { width: auto; border-left: none; border-top: 1px solid var(--line-soft); }
  .gallery-lightbox-imgwrap img { max-height: 50vh; }
  .gallery-lightbox-nav { display: none; }
}

/* ── "Разместить свою работу" на странице Галереи (запрос владельца
   2026-08-03) — свёрнутый тизер-баннер по умолчанию, разворачивается по
   клику в панель выбора: строго из истории генераций аккаунта (.matgal-grid,
   тот же тайл-грид, что у материалов в студии), НЕ произвольная загрузка
   файла с диска — см. GallerySubmitPanel (pages.jsx). */
.gallery-submit-teaser {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding: 18px 22px; border-radius: 14px;
  border: 1px dashed var(--line-strong); background: var(--paper-2);
}
.gallery-submit-panel {
  padding: 20px 22px; border-radius: 14px;
  border: 1px solid var(--line-strong); background: var(--surface); box-shadow: var(--sh-sm);
}
.gallery-submit-preview {
  position: relative; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--line-strong); background: var(--paper-2);
}
.gallery-submit-preview img { width: 100%; max-height: 260px; object-fit: contain; display: block; }
.gallery-submit-preview .up-drop-remove { position: absolute; top: 8px; right: 8px; }
.gallery-submit-empty {
  padding: 28px 20px; border-radius: 10px; text-align: center;
  border: 1px dashed var(--line); background: var(--paper-2);
}

/* ============================================================
   STUDIO DYNAMIC SPLIT PANEL — animated width transitions
   ============================================================ */
.studio-app .settings-panel {
  flex: 2 1 0;
  width: auto;
  min-width: 0;
  max-width: none;
  transition: flex-grow .4s cubic-bezier(.22,.61,.36,1);
}
.studio-app .canvas {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
  opacity: 1;
  overflow: hidden;
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  transition: flex-grow .4s cubic-bezier(.22,.61,.36,1);
}
/* БЫЛО flex-grow:2 (равные половины 50/50) — владелец 2026-08-07 отметил,
   что при самой первой генерации (ещё нет has-results) анимация "ИИ
   создаёт ваш макет…" из-за этого показывалась мелкой, в узкой половине
   холста, и визуально "прыгала" крупнее только когда приезжал реальный
   результат. Скелетон в EmptyCanvas уже умеет вписываться максимально
   крупно в доступное место (см. cardsRef/stageRef в studio.jsx) — ему
   просто не хватало места. Те же пропорции, что у has-results ниже (1:2,
   узкая колонка настроек / широкий холст), теперь и на первой генерации —
   переход больше не прыгает 50/50 → 33/67 после первого результата. */
.studio-app.is-generating .settings-panel {
  flex-grow: 1 !important;
  min-width: 620px;
}
.studio-app.is-generating .canvas {
  flex-grow: 2 !important;
  opacity: 1 !important;
  pointer-events: auto !important;
}
/* Как только есть готовый результат — фокус смещается на холст (там же
   теперь и главное действие, "Скачать PDF"/"Сохранить"), поле ввода со
   своими настройками формата/стиля уже отработало своё и становится
   вспомогательной, более узкой колонкой, а не равноправной половиной
   экрана. Отдельное правило (не объединённый селектор с is-generating выше) —
   у обоих классов одинаковая специфичность, а стоит has-results НИЖЕ по
   файлу, поэтому при перегенерации уже показанного результата (оба класса
   разом) побеждает именно эта раскладка — те же самые пропорции 1:2, что и
   у is-generating выше, так что колонки визуально не дёргаются между
   первой генерацией, перегенерацией и готовым результатом. */
.studio-app.has-results .settings-panel {
  flex-grow: 1 !important;
  /* Без пола инструменты в тулбаре композера (флекс-ряд .composer-icon-row
     слева, см. выше, и голосовой ввод .vta-mic справа, абсолютно
     спозиционирован от правого края независимо) не рассчитаны на такую
     узкую колонку и физически накладываются друг на друга. 620px — минимум,
     при котором ряд чипов (после добавления "Т" и группировки шире, чем
     раньше) и кнопка «Голосовой ввод» ещё не пересекаются. */
  min-width: 620px;
}
.studio-app.has-results .canvas {
  flex-grow: 2 !important;
  opacity: 1 !important;
  pointer-events: auto !important;
}

/* Крупнее шрифты в расширенной левой колонке — пропорционально новой ширине */
.settings-panel .input,
.settings-panel .textarea,
.settings-panel .select { font-size: var(--text-md); padding: 15px 18px; }
/* max-height + overflow-y:auto (было overflow:hidden, без ограничения
   высоты) — раньше поле росло вместе с текстом БЕЗ потолка: длинный текст
   (полный текст визитки, а не короткая реплика чата) утягивал весь ряд
   иконок «+»/формат/размер/… далеко вниз за собой, и добраться до него
   можно было только проскроллив всю страницу мимо уже напечатанного —
   для рабочего, часто нужного ряда это раздражало. Теперь поле растёт до
   потолка, дальше сам текст скроллится ВНУТРИ поля (тот же приём, что и
   в ChatGPT/Claude), а ряд иконок остаётся на предсказуемом месте сразу
   под текстом, без прокрутки всей страницы. growHeight() (см. ниже по
   файлу) по-прежнему считает scrollHeight честно — сам расчёт не знает
   о max-height, это чисто визуальный потолок поверх. */
.settings-panel .textarea {
  border: 1.5px solid var(--line);
  border-radius: var(--r-md);
  transition: border-color .2s, box-shadow .2s;
  resize: none;
  max-height: min(50vh, 520px);
  overflow-y: auto;
}
.settings-panel .textarea:focus {
  border-color: var(--navy-line);
  box-shadow: 0 0 0 3px rgba(28, 58, 87, .07);
  outline: none;
}
.settings-panel .textarea::placeholder {
  color: var(--muted);
  transition: opacity .4s ease;
}
.settings-panel .seg-btn-main { font-size: var(--text-base); }
.settings-panel .seg-btn-sub { font-size: var(--text-sm); }
.settings-panel .pill { font-size: var(--text-sm); }
.settings-panel .gen-promise span { font-size: 13.5px; }
/* Inline results pane inside canvas */
.inline-results-body {
  flex: 1; overflow-y: auto; padding: 22px 24px 32px;
  display: flex; flex-direction: column; gap: 18px;
}
.card-preview-wrap {
  display: flex; justify-content: center;
  background: var(--paper-2); border-radius: 14px;
  padding: 22px 16px; border: 1px solid var(--line);
}

/* Mobile responsive */
/* 900px, не 768px: между 768 и ~900 холст и панель настроек ещё пытались
   стоять в строку (десктопная раскладка), но места на двоих уже не
   хватало — текст в холсте (плейсхолдер "Здесь появится ваша визитка",
   шаги 1-2-3) обрезался о правый край .studio-app (overflow:hidden), не
   перенос, а именно обрезанные буквы (дизайн-аудит 2026-07-21, типичный
   планшетный портрет ~834px). Тот же порог, что и у компактных чипов
   композера выше — обе поломки лечит одна и та же граница. */
@media (max-width: 900px) {
  /* База: до результата холст и форма сидели в одной flex-строке и сжимались
     в две узкие колонки — на телефоне это нечитаемо. Ставим колонкой всегда,
     .has-results ниже (выше по специфичности) переопределяет высоты под результат. */
  .studio-app {
    flex-direction: column;
    height: auto;
    min-height: calc(100vh - 70px);
    overflow-y: auto;
  }
  .app-shell-main .studio-app { min-height: calc(100vh - 48px); }
  .studio-app .canvas {
    width: 100% !important;
    flex: none !important;
    height: 240px;
    border-right: none;
    border-bottom: 1px solid var(--line);
  }
  .studio-app .settings-panel {
    width: 100% !important;
    /* БЫЛО flex:1 1 auto — вместе с min-height:0 от базового правила
       (desktop, строка 834, там оно нужно, чтобы панель могла сжаться до
       заданной родителем высоты) на мобильном давало ту же поломку, что уже
       чинили ровно тем же приёмом у .settings-body чуть ниже: .studio-app
       здесь не растёт вместе с контентом (сам зажат родителем до высоты
       экрана, см. комментарий у .settings-body), поэтому flex-shrink:1
       реально сжимал панель НИЖЕ её содержимого — хвост последних 300-450px
       (nudge-подсказка про иконки, "Холст", сетка форматов) визуально
       наезжал друг на друга внахлёст вместо обычной прокрутки (дизайн-аудит
       2026-08-07: текст подсказки "Иконки под полем…" обрывался на середине
       предложения, перекрытый заголовком "Холст"). flex:none — basis:auto
       (естественная высота по содержимому) и shrink:0 (не сжимать), тот же
       фикс, что и у settings-body. */
    flex: none;
    border-right: none;
    /* На десктопе панель и канвас стоят рядом с тонкой границей между —
       переставленные в столбец на мобильном, они превращаются в сплошную
       белую плашку встык с серой (см. мобильный аудит: секции читаются как
       "слоёный пирог"). Мягкая тень на шве вместо простой границы. */
    box-shadow: 0 2px 8px rgba(0,0,0,.05);
  }
  /* .params-side-panel — на десктопе это вертикальная 14px-полоска у
     ЛЕВОГО КРАЯ строки (studio-app: row), с ручкой-вкладкой, торчащей
     вправо от неё. С flex-direction:column выше она остаётся тем же
     флекс-child'ом, но теперь это первый элемент КОЛОНКИ — узкая, ничем
     не занятая полоска почти во всю высоту экрана, торчащая слева,
     наполовину заслоняющая контент под ней (дизайн-аудит 2026-07-21:
     видно на мобильном скриншоте студии как обрезанный "огрызок" у
     самого края). Все её настройки и так продублированы чипами в ряду
     композера (см. FormatAttachButton/ColorAttachButton/... — на
     мобильном они уже показаны и работают), так что на узком экране
     проще скрыть саму рельсу, чем адаптировать десктопную геометрию под
     колонку. */
  .params-side-panel { display: none; }

  /* Кнопка «Сгенерировать» остаётся на виду при скролле формы */
  .settings-foot {
    position: sticky;
    bottom: 0;
    z-index: 10;
    background: var(--surface);
  }
  /* .settings-body — на десктопе flex:1 1 0% (см. базовое правило),
     растягивается до definite-высоты .settings-panel, которую та
     наследует от .studio-app { height:calc(100vh - 70px) }. На мобильном
     .studio-app выше переключён на height:auto — вся цепочка родителей
     теряет definite-высоту, и flex-grow у 0%-basis элемента распределять
     уже нечего: .settings-body схлопывался до нескольких пикселей
     (авто-минимум flex-item с overflow:auto), а его реальное содержимое
     (текстовое поле + резерв под ряд иконок) утекало в СОБСТВЕННЫЙ
     внутренний скролл этого схлопнутого блока — прямо под прилипающую
     кнопку "Сгенерировать" (.settings-foot, z-index 10 выше ряда иконок,
     см. её правило только что выше), которая эту утечку перекрывала
     собой. Ряд «+»/формат/цвет/модель/голос был физически некликабелен —
     не виден и не долистываем (репорт владельца 2026-08-03, "мобильная
     версия — 80% пользователей"). flex:none возвращает блоку его
     естественную высоту по содержимому — скроллится вся страница целиком
     через .studio-app, отдельного вложенного скролла тут не нужно. */
  .settings-body { flex: none; overflow-y: visible; }

  /* Заголовок "Генератор макетов" + бейджи баланса/тарифа сидели в одной
     nowrap-строке (row.spread) — на узком экране заголовок упирался
     вплотную в бейдж баланса, а бейдж тарифа "⚡ Бесплатно" уходил за
     край вьюпорта и был почти не виден. Переносим бейджи на отдельную
     строку под заголовком вместо того, чтобы сжимать/резать текст. */
  .settings-head-row {
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .settings-title {
    white-space: normal;
    font-size: 21px;
  }
  .settings-head-badges {
    flex: 1 1 100%;
    justify-content: flex-start;
  }

  /* is-generating получает те же пропорции, что has-results ниже (тот же
     фикс, что и на десктопе выше по файлу, 2026-08-07) — иначе на первой
     генерации мобильный холст оставался в базовой фиксированной высоте
     240px (см. .studio-app .canvas выше), и анимация "ИИ создаёт ваш
     макет…" была мелкой ровно до первого результата. */
  .studio-app.is-generating,
  .studio-app.has-results {
    flex-direction: column;
    overflow-y: auto;
  }
  .studio-app.is-generating .settings-panel,
  .studio-app.has-results .settings-panel {
    width: 100% !important;
    /* min-width:0 — база (строка ~3367/3393, десктопный сплит-режим 620px
       минимум, чтобы ряд иконок композера не наезжал сам на себя) не имеет
       @media-guard и продолжает действовать здесь: min-width всегда
       побеждает width, даже с !important (правило CSS-модели, а не
       специфичности) — панель реально рендерилась 620px на 390px экране,
       текст (баланс/тариф/промпт/«спишется…») утекал за правый край экрана
       обрезанным (дизайн-аудит 2026-08-07, состояние busy на мобильном). */
    min-width: 0;
    max-height: 44vh;
    overflow-y: auto;
    flex: none;
    border-right: none;
    border-bottom: 1px solid var(--line);
  }
  .studio-app.is-generating .canvas,
  .studio-app.has-results .canvas {
    width: 100% !important;
    flex: none !important;
    min-height: 56vh;
  }

  /* Тулбар результата — теперь две зоны .region-zone-edit/.region-zone-export
     у самого макета (см. studio-extras.jsx), не в .canvas-top. На узком
     экране зоны складываются в столбец на всю ширину, каждая своей
     подписанной группой — вместо одного правого кластера, скроллящегося
     по горизонтали, теперь обе зоны видны целиком без прокрутки, потому
     что значков в каждой зоне мало и они переносятся сами (flex-wrap
     внутри .region-zone-box). */
  /* Прицел через .studio-app.has-results (как соседние правила в этом же
     блоке) — а не голый .region-toolbar-row: он объявлен здесь, ВЫШЕ по
     файлу, чем базовое правило .region-toolbar-row (~строка 2677+), и при
     равной специфичности выигрывает то, что позже по тексту файла, а не то,
     что внутри @media. Без второго класса align-items/flex-direction здесь
     тихо перебивались базовым правилом ниже. */
  .studio-app.has-results .region-toolbar-row { flex-direction: column; align-items: stretch; gap: 10px; }
  .studio-app.has-results .region-zone-edit { width: 100%; }
  .studio-app.has-results .region-zone-export { width: 100%; }
  .studio-app.has-results .region-edit-cta { width: 100%; }

  /* Кнопка «Сгенерировать»: бейджи баланса (.pm-cta-credit-chip) и размера
     (.pm-cta-size-chip) внутри неё стоят position:absolute у краёв, а
     подпись кнопки центрируется по ВСЕЙ её ширине, ничего не зная про них —
     на узкой мобильной кнопке центр и оба бейджа сходятся почти вплотную
     (см. мобильный аудит). Резервируем под бейджи явное поле по краям кнопки
     и чуть ужимаем сами бейджи — центр подписи гарантированно не касается ни
     одного из них.
     Числа подобраны по факту измерения (дизайн-аудит 2026-08-07,
     тестовый баланс "48874 Пикса" — 5 цифр, реалистичный потолок для
     активного платящего аккаунта): чип баланса при таком балансе рендерится
     ~100-110px, а подпись «Перегенерировать» (длиннее «Сгенерировать» —
     кнопка после готового результата, showingResults, см. studio.jsx) в
     оставшемся зазоре не помещалась и уходила ПОД чип (не CSS-обрезка text-
     overflow, а буквальное перекрытие: непрозрачный чип поверх начала слова,
     видимым текстом оставалось "ерегенерировать"). Уже фиксили тем же
     приёмом лишний зазор между иконкой и подписью (двойной gap от лишнего
     пробела в JSX, см. studio.jsx) — этого одного оказалось недостаточно
     именно на 5-значном балансе, добавляем меньший шрифт чипов + больше
     запаса слева.
     Селекторы чипов — с доп. предком .studio-app, а не голые
     .pm-cta-credit-chip/.pm-cta-size-chip: те же имена классов уже заняты
     БЕЗУСЛОВНЫМ (без media) правилом ниже по файлу (~строка 6690) с той же
     специфичностью — при равной специфичности побеждает которое ПОЗЖЕ по
     тексту файла, независимо от того, что здесь внутри @media. Голые чипы
     тут молча проигрывали базовым 12px/10px/left:14px даже на мобильном —
     та же ловушка, что уже чинили этим же приёмом чуть выше для
     .region-toolbar-row (см. комментарий там). */
  .pm-cta-btn { padding-left: 108px; padding-right: 96px; font-size: 12.5px; }
  .studio-app .pm-cta-credit-chip, .studio-app .pm-cta-size-chip { font-size: 9.5px; padding: 0 5px; height: 22px; }
  .studio-app .pm-cta-credit-chip { left: 8px; }
  .studio-app .pm-cta-size-chip { right: 8px; }
}

/* ============================================================
   STUDIO EXTRAS: voice, examples, layout radio, tabs, toolbar
   ============================================================ */

/* Voice button */
@keyframes voicePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(124,92,191,.55); }
  60%       { box-shadow: 0 0 0 9px rgba(124,92,191,0); }
}
.voice-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px; border-radius: 40px; font-size: var(--text-sm); font-weight: 600;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); cursor: pointer; font-family: var(--sans);
  transition: all .18s; white-space: nowrap; flex: none;
}
.voice-btn:hover:not(.recording) { border-color: #7C5CBF; color: #7C5CBF; }
.voice-btn.recording {
  background: #7C5CBF; color: #fff; border-color: #7C5CBF;
  animation: voicePulse 1.2s ease-in-out infinite;
}

/* Collapsible examples */
.examples-toggle {
  font-size: var(--text-sm); color: #6c757d; cursor: pointer;
  background: none; border: none; padding: 4px 0; font-family: var(--sans);
  display: inline-flex; align-items: center; gap: 6px; transition: color .15s;
}
.examples-toggle:hover { color: var(--navy); }
.examples-block {
  overflow: hidden; max-height: 0; opacity: 0;
  transition: max-height .32s ease, opacity .2s ease;
}
.examples-block.open { max-height: 480px; opacity: 1; }

/* Toolbar */
.toolbar-row { display: flex; gap: 7px; flex-wrap: wrap; }
.tool-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 33px; padding: 0 12px; border-radius: 8px;
  font-size: var(--text-sm); font-weight: 600; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-soft); cursor: pointer;
  font-family: var(--sans); transition: all .15s;
}
.tool-btn:hover { border-color: var(--line-strong); color: var(--ink); }
.tool-btn.on { border-color: var(--navy); background: var(--navy-tint); color: var(--navy); }
.tool-btn.has { border-color: var(--good); background: rgba(31,138,91,.08); color: var(--good); }

/* ---- Layout type cards ---- */
.lt-cards {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.lt-card {
  position: relative; display: flex; align-items: center; gap: 12px;
  border: 1.5px solid var(--line); border-radius: 12px;
  background: var(--surface); cursor: pointer;
  font-family: var(--sans); transition: border-color .15s, box-shadow .15s;
  overflow: hidden; padding: 12px 13px;
}
.lt-card:hover {
  border-color: var(--line-strong);
  box-shadow: 0 2px 10px rgba(0,0,0,.06);
}
.lt-card.active {
  border-color: var(--navy);
  box-shadow: 0 0 0 2.5px rgba(28,58,87,.14);
  background: var(--navy-tint);
}
.lt-card-art {
  width: 56px; height: 38px; flex: none;
  display: flex; align-items: center; justify-content: center;
  color: var(--navy);
}
.lt-card-art svg { width: 56px; height: 38px; }
.lt-card-foot {
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
}
.lt-card-label {
  font-size: var(--text-sm); font-weight: 700; color: var(--ink);
}
.lt-card-hint {
  font-size: var(--text-xs); font-weight: 500; color: var(--muted);
}
.lt-check {
  position: absolute; top: 7px; right: 7px;
  width: 18px; height: 18px; border-radius: 999px;
  background: var(--navy); color: #fff;
  display: grid; place-items: center;
}

/* ── OrientationPicker tiles ── */
.op-row { display: flex; gap: 10px; }
.op-tile {
  flex: 1; display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border-radius: 12px;
  border: 1.5px solid var(--line); background: #fff;
  cursor: pointer; font-family: var(--sans); text-align: left;
  transition: border-color .15s, background .15s;
}
[data-theme="dark"] .op-tile { background: var(--surface); }
.op-tile:hover:not(.active) { border-color: #9C8EDB; }
.op-tile.active { border: 2px solid #533AB7; background: #EEEDFE; }
.op-radio {
  width: 18px; height: 18px; border-radius: 999px; flex: none;
  border: 1.5px solid #C5C1D8; background: #fff;
  display: flex; align-items: center; justify-content: center;
}
.op-tile.active .op-radio { border-color: #533AB7; }
.op-radio-dot { width: 9px; height: 9px; border-radius: 999px; background: #533AB7; }
.op-rect-wrap { display: flex; align-items: center; justify-content: center; flex: none; }
.op-rect { border-radius: 3px; border: 1.5px solid #C5C1D8; background: #fff; }
.op-tile.active .op-rect { border-color: #533AB7; }
.op-text { display: flex; flex-direction: column; gap: 2px; }
.op-label { font-size: var(--text-sm); font-weight: 600; color: var(--ink); line-height: 1.2; }
.op-tile.active .op-label { color: #3C3489; }
.op-size { font-size: var(--text-xs); color: var(--muted); line-height: 1.2; }
.op-tile.active .op-size { color: #6155A6; }

/* ── Формат макета — превью + два независимых переключателя (см. FormatTiles
   в studio.jsx), а не сетка 2×2 готовых комбинаций. .ft-preview показывает
   живую иллюстрацию + размер, сами переключатели ниже — переиспользуют
   .qf-tiles/.qf-tile (см. дальше по файлу), тот же язык, что и у авто-
   подсказок "Стороны"/"Ориентация".
   Первая версия выглядела неряшливо по двум причинам: (1) .qf-tiles в
   исходном виде — подпись и плитки одним flex-рядом, рассчитан на широкую
   врезку в основной колонке композера; в узком 320px попапе подпись +
   2 плитки не помещались в строку, и flex-wrap ломал их вразнобой,
   лесенкой. Здесь, только внутри .ft-split, подпись — отдельной строкой
   сверху, плитки — ровной сеткой 2 колонки под ней, предсказуемо в любую
   ширину. (2) .ft-preview был того же navy-tint, что и АКТИВНАЯ плитка —
   превью (не кликабельно, просто сводка) визуально не отличалось от
   выбираемого варианта, как будто третья лишняя плитка среди настоящих.
   Теперь превью — нейтральная info-карточка (тот же приём, что у
   .attach-menu-note/.qr-note: paper-2, не navy), а navy остаётся только у
   реально выбранного. ── */
.ft-split { display: flex; flex-direction: column; gap: 16px; }
/* Две колонки рядом (Ориентация | Стороны), разделённые вертикальной линией
   — раньше оба ряда стояли друг под другом без явной границы, читались
   почти как один общий список из 4 плашек (жалоба 2026-07-21: "визуально
   вертикально разделены, чтобы явно видно, что они отдельные"). ft-axis-
   divider — та самая граница, буквально. */
/* padding слева/справа (запрос 2026-07-27) — тот же попап-паддинг 12px
   (.format-attach-menu) резал колонки Стороны/Ориентация вровень с краем
   попапа, глаз "склеивал" крайние плитки с рамкой окна; чуть больше
   воздуха по бокам + увеличенный gap между колонками (16→20px) явно
   отделяют их друг от друга и от края. */
.ft-axes { display: flex; align-items: stretch; gap: 20px; padding: 0 6px; }
.ft-axis-divider { width: 1px; flex: none; background: var(--line); align-self: stretch; }
.ft-split .qf-tiles-label { display: block; width: 100%; margin-bottom: 8px; }
/* Плитки внутри одной оси теперь стоят в столбик (2 плитки высотой, не
   в ряд) — "рядом" эти два варианта уже не помещаются: сама ось делит
   ширину попапа пополам с соседней осью (см. .ft-axes). */
.ft-split .qf-tiles { display: flex; flex-direction: column; gap: 8px; flex: 1 1 0; min-width: 0; }
/* .qf-tile "из коробки" — узкая плашка, рассчитанная на инлайн-ряд в
   широкой колонке композера, где рядом ещё и текстовая подпись; здесь —
   просторнее и крупнее, тот же масштаб, что был у старых .ft-tile
   (широкие горизонтальные карточки, крупная иконка слева), просто теперь
   в столбик внутри своей половины попапа. */
.ft-split .qf-tile {
  width: 100%; gap: 12px; padding: 14px 16px; border-radius: 13px;
}
.ft-split .qf-tile-icon svg, .ft-split .qf-tile-icon { width: 30px; height: 26px; }
.ft-split .qf-tile-title { font-size: var(--text-sm); }
.ft-split .qf-tile-check { top: 8px; right: 8px; }
/* Боковая панель (.psp-format-col, ~240px) — двум колонкам по 130+px рядом
   там физически не хватит места (та же теснота, что уже была с плитками
   в ряд), поэтому здесь оси возвращаются в столбик друг под другом,
   вертикальный разделитель между ними теряет смысл — заменяется на
   обычную горизонтальную черту. */
.psp-format-col .ft-axes { flex-direction: column; gap: 18px; }
.psp-format-col .ft-axis-divider { width: 100%; height: 1px; align-self: auto; }
/* Раньше та же форма/рамка/заливка, что и у настоящих кнопок ниже (.qf-tile)
   — читалось как ещё один кликабельный вариант (жалоба 2026-07-21: "как
   будто туда тоже можно тыкать"). Пунктирная рамка вместо сплошной — везде
   в этом попапе сплошная рамка означает "сюда можно нажать", пунктир
   устойчиво читается как "это подсказка/сводка, не действие". Без hover-
   эффекта и без cursor:pointer (курсор по умолчанию, не наследуется от
   соседних .qf-tile) — по той же причине. */
.ft-preview {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 14px; border-radius: 13px;
  background: var(--paper-2); border: 1.5px dashed var(--line-strong);
  cursor: default;
}
.ft-art {
  width: 52px; height: 40px; display: flex; align-items: center; justify-content: center;
  color: var(--muted); flex: none;
}
.ft-art-svg { width: 52px; height: 40px; }
.ft-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ft-preview-label {
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 1px;
}
.ft-title { font-size: var(--text-sm); font-weight: 700; color: var(--ink); line-height: 1.15; }
.ft-size {
  margin-top: 2px; font-size: 10.5px; font-weight: 600; color: var(--navy);
  background: rgba(28,58,87,.1); border-radius: 999px; padding: 3px 9px;
  letter-spacing: .01em; align-self: flex-start;
}

/* ── Typography accordion (расширенный режим) ── */
.typo-acc { display: flex; flex-direction: column; }
.typo-acc-head {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 11px 13px; border: 1.5px solid var(--line); border-radius: 12px;
  background: var(--surface); cursor: pointer; font-family: var(--sans);
  color: var(--ink); transition: border-color .15s, background .15s;
}
.typo-acc-head:hover { border-color: var(--line-strong); }
.typo-acc-head.open { border-color: var(--line-strong); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.typo-acc-ic { font-size: var(--text-sm); flex: none; }
.typo-acc-title { font-size: var(--text-sm); font-weight: 600; }
.typo-acc-tag {
  font-size: 10.5px; font-weight: 600; color: var(--muted);
  background: var(--paper-2); border-radius: 999px; padding: 2px 8px;
}
.typo-acc-body {
  display: flex; flex-direction: column; gap: 14px;
  padding: 15px 13px 14px;
  border: 1.5px solid var(--line-strong); border-top: none;
  border-bottom-left-radius: 12px; border-bottom-right-radius: 12px;
  background: var(--paper-2);
}
.typo-row { display: flex; flex-direction: column; gap: 7px; }
.typo-row-label { font-size: var(--text-xs); font-weight: 600; color: var(--ink-soft); }
.typo-row .seg { background: var(--surface); }
.typo-row .seg-btn { font-size: var(--text-xs); padding: 8px 5px; line-height: 1.15; white-space: normal; text-align: center; }
.typo-acc-note { font-size: var(--text-xs); color: var(--muted); line-height: 1.4; margin: 0; }

/* ── 1. Spinner animation ── */
@keyframes px-spin { to { transform: rotate(360deg); } }

/* ── 2. Custom select styling ── */
select.input {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%236c757d' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 36px !important;
  cursor: pointer;
}
select.input:hover { border-color: var(--navy-line); }

/* ── Контраст текста в выпадающих списках (select / option) ──
   Жёстко задаём цвета option, чтобы при наведении/фокусе текст
   не становился белым на светлом фоне (системная подсветка). */
.select, select.input, select.select { color: var(--ink); background-color: var(--surface); }
.select option,
select.input option,
select.select option {
  background-color: var(--surface);
  color: var(--ink);
}
.select option:checked,
.select option:hover,
.select option:focus,
select.input option:checked,
select.input option:hover,
select.input option:focus,
select.select option:checked,
select.select option:hover,
select.select option:focus {
  background-color: var(--navy);
  color: #fff;
}

/* Custom file input */
.file-row { display: flex; align-items: center; gap: 10px; }
.file-name {
  font-size: var(--text-sm); color: var(--muted); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── 3. Try-catch: toast error animation (already exists) ── */

/* ── 4. Focus-visible (keyboard nav) ── */
:focus-visible {
  outline: 2px solid var(--navy);
  outline-offset: 2px;
  border-radius: 6px;
}
:focus:not(:focus-visible) { outline: none; }

/* Stronger focus for buttons */
button:focus-visible, a:focus-visible {
  outline: 2px solid var(--navy);
  outline-offset: 3px;
  border-radius: 8px;
}

/* Skip-to-main link for a11y */
.skip-link {
  position: absolute; top: -60px; left: 12px; z-index: 9999;
  padding: 8px 16px; background: var(--navy); color: #fff;
  border-radius: 8px; font-size: var(--text-sm); font-weight: 600;
  transition: top .15s; text-decoration: none;
}
.skip-link:focus { top: 12px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 16px; font-weight: 700; color: var(--ink); letter-spacing: -.01em; }
.field-hint { font-size: var(--text-sm); color: var(--muted); margin: 0; line-height: 1.45; }
.field-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.field-head > div:first-child { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.field-head label { margin: 0; }
.col.gap-20 { gap: 20px; }

.tool-btn-sample {
  background: linear-gradient(135deg, var(--navy-tint) 0%, var(--bronze-tint) 100%) !important;
  border-color: var(--bronze) !important;
  color: var(--navy) !important;
  font-weight: 700 !important;
  padding: 0 15px !important;
  box-shadow: 0 2px 8px rgba(140,124,247,.18) !important;
}
.tool-btn-sample:hover {
  background: linear-gradient(135deg, var(--bronze-tint) 0%, var(--navy-tint) 100%) !important;
  border-color: var(--navy) !important;
  box-shadow: 0 4px 14px rgba(140,124,247,.28) !important;
  transform: translateY(-1px);
}
.tool-btn-sample.on,
.tool-btn-sample.has {
  background: linear-gradient(135deg, var(--bronze-tint) 0%, var(--navy-tint) 100%) !important;
  border-color: var(--bronze) !important;
  color: var(--bronze) !important;
  box-shadow: 0 0 0 3px rgba(140,124,247,.18) !important;
}
.add-mat-btn {
  display: flex; align-items: center; gap: 9px;
  width: 100%; padding: 11px 14px; border-radius: 11px;
  border: 1px dashed var(--line-strong); background: var(--paper);
  color: var(--ink-soft); font-size: 13.5px; font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s;
}
.add-mat-btn:hover { border-color: var(--navy); color: var(--navy); background: var(--paper-2); border-style: solid; }
.add-mat-btn.open { border-style: solid; border-color: var(--navy); color: var(--navy); background: var(--navy-tint); }
.add-mat-btn.has { border-style: solid; border-color: var(--good); color: var(--good); background: rgba(31,138,91,.06); }
.mat-expand { display: flex; flex-direction: column; gap: 12px; margin-top: 12px; }

/* Advanced settings toggle */
.adv-toggle {
  display: flex; align-items: center; gap: 8px;
  width: 100%; padding: 10px 14px; border-radius: 11px;
  border: 1px solid var(--line); background: var(--paper);
  color: var(--muted); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s;
}
.adv-toggle:hover { border-color: var(--line-strong); color: var(--ink); }
.adv-toggle.has { border-color: var(--bronze); color: var(--bronze); }
.adv-pills { display: flex; flex-wrap: wrap; gap: 4px; flex: 1; min-width: 0; overflow: hidden; max-height: 20px; }
.adv-pill {
  font-size: 10.5px; font-weight: 700; padding: 2px 7px; border-radius: 999px;
  background: var(--bronze-tint); color: var(--bronze); white-space: nowrap;
  border: 1px solid rgba(140,124,247,.3);
}
.adv-toggle.open .adv-pills { display: none; }
.adv-expand { padding: 14px 0 0; }

/* Asset upload panel (inline) */
.upload-panel {
  border: 1px solid var(--line); background: var(--paper-2);
  border-radius: 12px; padding: 14px;
}
.upload-x {
  width: 26px; height: 26px; border-radius: 7px; border: 1px solid var(--line);
  background: var(--surface); color: var(--muted); cursor: pointer;
  display: grid; place-items: center; transition: all .15s;
}
.upload-x:hover { color: var(--ink); border-color: var(--line-strong); }
.upload-preview { display: flex; align-items: center; gap: 12px; }
.upload-preview img {
  width: 64px; height: 64px; object-fit: cover; border-radius: 9px;
  border: 1px solid var(--line); background: var(--surface); flex: none;
}
.photo-thumb { position: relative; width: 64px; height: 64px; flex: none; }
.photo-thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: 9px; border: 1px solid var(--line); }
.photo-thumb button {
  position: absolute; top: -6px; right: -6px; width: 20px; height: 20px;
  border-radius: 999px; border: none; background: var(--ink); color: #fff;
  display: grid; place-items: center; cursor: pointer;
}

.social-chip {
  display: inline-flex; align-items: center; gap: 5px;
  height: 26px; padding: 0 10px; border-radius: 999px;
  font-size: var(--text-xs); font-weight: 600;
}

/* Unified selected-assets strip */
.assets-strip { display: flex; flex-wrap: wrap; gap: 12px; }
.asset-thumb {
  position: relative;
  width: 114px; height: 114px;
  border-radius: 11px;
  border: 1px solid var(--line);
  background: var(--surface);
  overflow: hidden; flex: none;
  box-shadow: var(--sh-sm);
}
.asset-thumb img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.asset-social {
  width: 100%; height: 100%;
  display: grid; place-items: center;
  font-size: 32px; font-weight: 800; letter-spacing: .01em;
}
.asset-qr {
  width: 100%; height: 100%;
  display: grid; place-items: center;
  color: var(--navy); background: var(--paper-2);
}
.composer-assets-strip { margin-top: 10px; }
.asset-tag {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 3px 5px;
  font-size: 9.5px; font-weight: 600; letter-spacing: .02em;
  color: #fff; text-align: center;
  background: linear-gradient(to top, rgba(10,12,16,.78), rgba(10,12,16,0));
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.asset-x {
  position: absolute; top: 4px; right: 4px;
  width: 20px; height: 20px; border-radius: 999px;
  border: none; background: rgba(10,12,16,.62); color: #fff;
  display: grid; place-items: center; cursor: pointer;
  transition: background .15s; backdrop-filter: blur(2px);
}
.asset-x:hover { background: #C0392B; }
/* Второй бейдж на том же тайле — «Распознать» (см. .asset-x выше,
   зеркальный угол: удаление справа, распознавание слева). Только на
   фото-образцах (.asset-thumb с img), не на соц-иконках — там просто
   `asset-social`. */
.asset-ocr {
  position: absolute; top: 4px; left: 4px;
  width: 20px; height: 20px; border-radius: 999px; padding: 0;
  border: none; background: rgba(28,58,87,.86); color: #fff;
  display: grid; place-items: center; cursor: pointer;
  font-size: 8px; font-weight: 800; letter-spacing: -.02em;
  transition: background .15s; backdrop-filter: blur(2px);
}
.asset-ocr:hover { background: var(--navy); }
.asset-ocr:disabled { cursor: default; opacity: .8; }

/* Enhanced badge on logo thumbnail */
.asset-enhanced {
  position: absolute; bottom: 0; left: 0; right: 0;
  padding: 3px 5px;
  font-size: var(--text-xs); font-weight: 800; letter-spacing: .03em; text-align: center;
  background: rgba(192,57,43,.88); color: #fff;
  pointer-events: none; white-space: nowrap;
}

/* Social modal grid */
.social-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.social-card {
  position: relative; display: flex; align-items: center; gap: 10px;
  padding: 11px 12px; border-radius: 11px; border: 1px solid var(--line);
  background: var(--paper); cursor: pointer; transition: all .15s; text-align: left;
}
.social-card:hover { border-color: var(--line-strong); }
.social-card.on { border-color: var(--navy); box-shadow: 0 0 0 2px rgba(28,58,87,.12); background: var(--surface); }
.social-badge {
  width: 34px; height: 34px; border-radius: 9px; flex: none;
  display: grid; place-items: center; font-size: var(--text-xs); font-weight: 800; letter-spacing: .01em;
}
.social-name { font-size: var(--text-sm); font-weight: 600; color: var(--ink); }
.social-check {
  position: absolute; top: 8px; right: 8px; width: 18px; height: 18px; border-radius: 999px;
  background: var(--navy); color: #fff; display: grid; place-items: center;
}

/* ── "Из галереи" (MaterialsGalleryModal, studio.jsx) — сетка миниатюр
   ранее загруженных файлов. Префикс matgal- (не gallery-), чтобы не
   пересекаться с .gallery-* — тот уже занят витриной примеров на главной
   (landing.jsx), другая, не связанная фича. ── */
.matgal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; }
.matgal-tile { display: flex; flex-direction: column; gap: 6px; position: relative; }
.matgal-tile-btn {
  width: 100%; aspect-ratio: 1; border-radius: 11px; overflow: hidden;
  border: 1px solid var(--line); background: var(--paper-2); cursor: pointer;
  padding: 0; display: grid; place-items: center; transition: border-color .15s, box-shadow .15s;
}
.matgal-tile-btn:hover:not(:disabled) { border-color: var(--navy); box-shadow: 0 0 0 2px rgba(28,58,87,.1); }
.matgal-tile-btn:disabled { cursor: default; }
.matgal-tile-btn img { width: 100%; height: 100%; object-fit: cover; display: block; }
.matgal-tile-state { color: var(--muted); }
.matgal-tile-del {
  position: absolute; top: 6px; right: 6px; width: 24px; height: 24px; border-radius: 50%;
  background: rgba(20,20,24,.68); color: #fff; border: none; display: grid; place-items: center;
  cursor: pointer; opacity: 0; transition: opacity .15s, background .15s;
}
.matgal-tile:hover .matgal-tile-del { opacity: 1; }
.matgal-tile-del:hover { background: var(--crit, #B54638); }
.matgal-tile-name {
  font-size: var(--text-xs); color: var(--ink-soft); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.matgal-loading, .matgal-empty {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 40px 20px; color: var(--muted); font-size: var(--text-sm); text-align: center;
}

/* Layout radio */
.layout-radio { display: flex; gap: 6px; }
.layout-radio-btn {
  flex: 1; height: 36px; border-radius: 9px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-soft);
  font-size: var(--text-sm); font-weight: 600; cursor: pointer;
  font-family: var(--sans); transition: all .15s;
  display: flex; align-items: center; justify-content: center; gap: 7px;
}
.layout-radio-btn.active { background: var(--navy); color: #fff; border-color: var(--navy); }

/* Side tabs (overlay) */
.side-tabs { display: flex; gap: 6px; margin-bottom: 20px; }
.side-tab {
  flex: 1; height: 40px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-soft); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s;
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.side-tab.active { background: #7C5CBF; color: #fff; border-color: #7C5CBF; }
.side-tab:disabled { opacity: .38; cursor: not-allowed; }

/* b2b side switcher (segmented, above preview) */
.side-switcher {
  display: inline-flex; gap: 4px; margin: 0 auto 16px; padding: 4px;
  background: var(--paper-2, #EFEDE7); border: 1px solid var(--line);
  border-radius: 12px; align-self: center;
}
.side-switch-tab {
  display: flex; align-items: center; gap: 7px; height: 34px; padding: 0 16px;
  border: none; background: transparent; border-radius: 9px; cursor: pointer;
  font-family: inherit; font-size: var(--text-sm); font-weight: 600; color: var(--muted);
  transition: background .15s, color .15s, box-shadow .15s; white-space: nowrap;
}
.side-switch-tab:hover { color: var(--ink-soft, var(--navy)); }
.side-switch-tab.active {
  background: var(--surface, #fff); color: var(--navy);
  box-shadow: 0 1px 3px rgba(0,0,0,.1);
}

/* hero dual-side layout */
.hero-pair { display: flex; align-items: center; justify-content: center; gap: 18px; }
.hero-pair-stack { flex-direction: column; gap: 14px; }
.hero-pair-side  { flex-direction: row; gap: 18px; }
.hero-card-wrap { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.hero-side-label {
  font-size: var(--text-xs); font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); user-select: none;
}
.hero-card-wrap.hero-active .hero-side-label { color: var(--navy); }
/* border-radius: 0 (было 14px, см. .up-img-wrap выше) — сам div уже
   получает inline borderRadius: previewRadius=0 (studio-extras.jsx), это
   правило и раньше не давало видимого скругления, правим ради согласованности,
   а не отменяем реальный эффект. */
.hero-card-wrap.hero-active > div:first-child { outline: 2px solid var(--navy); outline-offset: 3px; border-radius: 0; }
.hero-card-wrap:not(.hero-active):hover > div:first-child { outline: 1px solid var(--line-strong); outline-offset: 3px; border-radius: 0; }

/* Ruler */
.ruler-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  padding: 10px 0 0;
  justify-content: center;
}
.ruler-bar-item {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 9px; border-radius: 999px;
  background: var(--paper-2); border: 1px solid var(--line);
  font-size: var(--text-xs); font-weight: 500; color: var(--muted);
}

/* Действия "AI Умное восстановление" под макетом — см. studio-extras.jsx,
   region-inpaint-actions (перенесены сюда из левого сайдбара по запросу
   2026-07-21). Все три кнопки в ОДНУ строку — "Сохранить результат" визуально
   отделена от Отмена/Удалить объект увеличенным зазором (margin-left), а не
   в отдельном ряду, чтобы читалась как отдельное, более "финальное" действие.
   max-width раньше был 560px — при реальной длине надписей ("Удалить
   объект" + "Сохранить результат" с иконкой) три кнопки вместе с зазорами
   занимают ~556px, впритык к пределу: любая мелочь (метрики шрифта,
   округление flex-gap) перекидывала «Сохранить результат» на вторую строку
   — что и рвало саму цель этого блока ("одна строка"), а на 1440×900 после
   переноса кнопка вообще уезжала за нижний край экрана без скролла
   (дизайн-аудит 2026-07-21). Увеличенный запас держит все три в одну строку
   на любой обычной ширине холста. */
.region-inpaint-actions {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: 10px; margin: 4px auto 0; max-width: 640px;
}
.region-inpaint-actions-group { display: flex; gap: 10px; }
.region-inpaint-actions-group .btn { height: 44px; min-width: 120px; }
.region-inpaint-save {
  height: 44px; min-width: 180px; margin-left: 22px;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}

/* "Сделать точный размер для печати" (studio-extras.jsx) — компактная
   строка под линейкой размеров, тот же язык, что и region-inpaint-actions,
   но без шагов слева (это разовое одностраничное действие, не режим). */
.size-fix-bar {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: 12px; margin: 10px auto 0; max-width: 640px;
  padding: 10px 16px; border-radius: 12px;
  background: var(--bronze-tint); border: 1px dashed var(--bronze);
}
.size-fix-hint {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: var(--text-sm); font-weight: 600; color: var(--bronze); text-align: center;
}
/* "До"/"После" (studio-extras.jsx) — тот же язык переключателя, что и
   .side-switcher/.side-switch-tab (показ "Лицо и оборот"), компактнее. */
.size-fix-compare {
  display: inline-flex; gap: 3px; padding: 3px;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: 9px;
}
.size-fix-compare-btn {
  height: 28px; padding: 0 13px; border: none; background: transparent; border-radius: 6px;
  cursor: pointer; font-family: inherit; font-size: var(--text-xs); font-weight: 700; color: var(--muted);
  transition: background .15s, color .15s;
}
.size-fix-compare-btn:hover { color: var(--ink-soft); }
.size-fix-compare-btn.active { background: var(--bronze); color: #fff; }

/* "Что дорисует нейросеть" — превью безопасных/защищённых зон ДО клика на
   "Дорисовать нейросетью" (studio-extras.jsx ExtendPreviewZones). Зелёным —
   новые поля (закрасит модель), центр — уже готовый макет, не тронут. */
.extend-preview { width: 100%; max-width: 220px; margin: 2px auto 0; }
.extend-preview-label { font-size: var(--text-xs); font-weight: 700; color: var(--muted); text-align: center; margin-bottom: 4px; }
.extend-preview-frame {
  position: relative; width: 100%; overflow: hidden; border-radius: 8px;
  background: var(--paper-2); border: 1px solid var(--line);
}
.extend-preview-img { position: absolute; object-fit: cover; display: block; }
.extend-preview-zone {
  position: absolute; background: color-mix(in srgb, var(--good) 22%, transparent);
  border: 1px dashed var(--good); box-sizing: border-box;
}

/* ============================================================
   UPSCALER PAGE
   ============================================================ */
/* Fixed two-column layout for the upscaler (narrow form + wide compare),
   overriding the studio's dynamic split width rules. */
.studio-app.up-mode .settings-panel {
  flex: 0 0 408px;
  width: 408px;
  min-width: 408px;
  max-width: 408px;
  transition: none;
}
.studio-app.up-mode .up-canvas {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  opacity: 1;
  overflow: hidden;
}
/* Мобильный режим для .up-mode (Upscaler/Adapter/PrintPrep — все три
   используют один и тот же класс, "up-mode" = раскладка в духе апскейлера)
   не был описан вообще, той же дырой, что чинили выше для .mini-mode:
   .settings-panel держит фикс. 408px без @media-исключения, а .up-canvas
   (min-height:0, overflow:hidden — авто-минимум для flex-shrink становится
   0 вместо контента) сжимается почти до нуля вместо естественной высоты и
   рисуется НАВЕРХ, поверх панели настроек — на телефоне "Результат
   адаптации"/"Результат апскейла" визуально наезжал на "Определённый тип"
   и превью оригинала (дизайн-аудит 2026-08-07, при редизайне сетки размера
   в Adapter). */
@media (max-width: 900px) {
  .studio-app.up-mode .settings-panel {
    width: 100% !important; max-width: none; min-width: 0; flex: none;
  }
  .studio-app.up-mode .up-canvas {
    width: 100% !important; flex: none; min-height: 60vh;
  }
  /* Левая панель на телефоне наследовала десктопные --sp-6 (24px) отступы
     со всех сторон .settings-head/.settings-body/.settings-foot — на узком
     экране это заметно "съедало" полезную высоту, размерная сетка/цена
     уезжали ниже видимой области без прокрутки (дизайн-аудит 2026-08-07:
     "информация о размерах уже должна быть видна на экране"). */
  .studio-app.up-mode .settings-head { padding: var(--sp-4) var(--sp-4) var(--sp-3); }
  .studio-app.up-mode .settings-body { padding: var(--sp-4) var(--sp-4) var(--sp-5); gap: var(--sp-4); }
  .studio-app.up-mode .settings-foot { padding: var(--sp-3) var(--sp-4) var(--sp-4); }
}
.up-panel { display: flex; flex-direction: column; gap: 0; }
.up-drop {
  position: relative;
  border: 2px dashed var(--line-strong); border-radius: 16px;
  background: var(--paper-2); padding: 40px 24px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; cursor: pointer; transition: border-color .2s, background .2s;
  text-align: center; min-height: 200px;
}
.up-drop.drag { border-color: var(--navy); background: var(--navy-tint); }
.up-drop:hover { border-color: var(--navy); }
.up-drop-ic {
  width: 56px; height: 56px; border-radius: 14px;
  background: var(--surface); border: 1px solid var(--line);
  display: grid; place-items: center; color: var(--navy);
  box-shadow: var(--sh-sm);
}
/* object-fit:cover раньше обрезал/зумил превью под фиксированную высоту —
   на вертикальном двустороннем макете (визитка-стопкой) это буквально
   срезало обе стороны кадра до неузнаваемого фрагмента (репорт 2026-07-31,
   "макет съехал в поле просмотра"). CLAUDE.md: макет показывается целиком,
   не мнётся под контейнер — contain вместо cover, letterbox вместо кропа. */
.up-preview-thumb {
  width: 100%; height: 130px; object-fit: contain;
  border-radius: 10px; display: block;
}
.up-drop-remove {
  position: absolute; top: 10px; right: 10px; z-index: 1;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--line-strong);
  color: var(--ink-soft); display: grid; place-items: center; cursor: pointer;
  box-shadow: var(--sh-sm); transition: border-color .15s, color .15s;
}
.up-drop-remove:hover { border-color: var(--navy); color: var(--navy); }
.up-scale-row { display: flex; gap: 8px; }
.up-scale-btn {
  flex: 1; height: 38px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-soft); font-weight: 700;
  font-size: var(--text-sm); cursor: pointer; transition: all .15s; font-family: var(--sans);
}
.up-scale-btn.active { background: var(--navy); color: #fff; border-color: var(--navy); }
.up-scale-btn:hover:not(.active) { border-color: var(--line-strong); }

/* "Исправить тонкий текст на плашках" (upscaler.jsx, 2026-07-30) — та же
   .card, что у "Определённый тип", но кликабельна целиком (label) и
   подсвечивается бронзовым, когда включена, чтобы не теряться на панели. */
.up-hairline-card { transition: border-color .15s, box-shadow .15s; }
.up-hairline-card:hover { border-color: var(--bronze); }
.up-hairline-card.active { border-color: var(--bronze); box-shadow: 0 0 0 3px var(--bronze-tint); }

/* compare canvas */
.up-canvas { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.up-compare {
  flex: 1; display: grid; grid-template-columns: 1fr 1fr; gap: 22px;
  padding: 26px; min-height: 0; align-items: start;
}
.up-side { display: flex; flex-direction: column; gap: 12px; }
.up-side-label {
  font-size: var(--text-sm); font-weight: 600; color: var(--muted);
  letter-spacing: .04em; text-transform: uppercase; text-align: center;
}
.up-img-wrap {
  /* border-radius: 0 (запрос пользователя 2026-07-24) — рабочая область
     показа макета/сгенерированного результата не скругляется НИГДЕ на
     сайте: реальные печатные макеты всегда прямоугольные, скруглённый
     превью-контейнер визуально врёт про форму продукта. */
  position: relative; border-radius: 0; overflow: hidden;
  background: var(--paper-2); border: 1px solid var(--line);
  box-shadow: var(--sh-md); cursor: crosshair;
  /* 2026-07-19: раньше img ниже был width:100%;height:auto без потолка —
     для вертикального макета (например А4 портрет, ratio ~0.71) при полной
     ширине колонки высота получалась заведомо больше доступной высоты
     экрана, и сам макет вместе с подписью под ним уезжал за нижний край
     видимой области при масштабе 100% (жалоба пользователя — "не влез во
     весь экран"). max-height ограничивает высоту БЛОКА, а не только img —
     object-fit: contain на img ниже уже сам вписывает картинку в него по
     меньшей из двух осей, без обрезки, независимо от ориентации формата. */
  /* 260px → 300px (запрос пользователя 2026-07-25) — освобождает строку под
     .qr-badge.inline между подписью стороны и картинкой (см. её комментарий
     в этом файле); резерв безусловный (даже когда бейджа сейчас нет), чтобы
     не пересчитывать бюджет в JS под каждый конкретный кадр — тот же класс
     переполнения .up-compare, что уже чинили здесь (доп. строка текста без
     запаса в бюджете наезжала на блок лупы ниже). */
  max-height: calc(100vh - 300px);
  display: flex; align-items: center; justify-content: center;
}
.up-img-wrap img { display: block; max-width: 100%; max-height: calc(100vh - 300px); width: auto; height: auto; object-fit: contain; pointer-events: none; }
.up-loupe {
  position: fixed; pointer-events: none; z-index: 300;
  width: 160px; height: 160px; border-radius: 50%;
  border: 3px solid var(--navy);
  box-shadow: 0 8px 32px rgba(0,0,0,.22), 0 0 0 1px rgba(28,58,87,.3);
  overflow: hidden; background: var(--paper);
  transform: translate(-50%, -50%);
}
.up-loupe canvas { display: block; width: 160px; height: 160px; }
.up-badge-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* ── «Сканер» ожидания апскейла (Canva/Krea-стиль): исходный макет лежит
   полупрозрачным (его opacity ведёт React по progress, 50%→100%), сверху
   вниз циклично проходит световая полоса с ярким тонким передним краем.
   Курсор стрелка, не crosshair: в этой фазе лупы ещё нет. ── */
/* container-type:inline-size — единственная опора для .up-sparkle ниже: он
   считает свой размер в cqmin (% от МЕНЬШЕЙ стороны именно этого бокса),
   а боксы сканера на сайте — то тонкая горизонтальная визитка, то высокий
   плакат, то маленькая миниатюра в "Подготовить к печати" — фиксированный
   px-размер смотрелся бы то крошечным, то непропорционально огромным.
   Намеренно inline-size, НЕ size: у апскейлера/адаптера высота этого же
   блока считается ОТ содержимого (натуральная высота img внутри) — полный
   contain:size (то, что даёт container-type:size) обнулил бы её, так как
   принудительно убирает зависимость размера от контента на ОБЕИХ осях.
   inline-size контейнит только ширину, высота остаётся как считалась, а
   cqh/cqmin по спеке всё равно резолвятся от итоговой (уже посчитанной)
   высоты контейнера. */
.up-scan-wrap { cursor: default; container-type: inline-size; }
.up-scan-img {
  transition: opacity .35s linear;
  filter: saturate(.8) contrast(.95);
}
/* ── "Наложенный" вариант сканера — те же .up-scan-beam/.up-sparkle, но
   БЕЗ своей копии картинки и белой подложки: кладётся прямо поверх уже
   отрисованного реального содержимого (превью "Подготовить к печати",
   область маски в правке по маске) — луч идёт по нему, а не прячет его
   под шиммер-заглушкой. ── */
.up-scan-overlay {
  position: absolute; inset: 0; overflow: hidden;
  pointer-events: none; container-type: inline-size;
}

/* ── Плавающие точки по фону (запрос владельца 2026-08-05, "как у ChatGPT
   при генерации изображения") — один лёгкий, частый слой radial-gradient
   точек, дрейфующий по диагонали. Первая версия (два слоя, крупнее и темнее)
   на реальном скриншоте вышла "тёмной и грубой" (заметка владельца) — здесь
   специально мельче, светлее и гуще (15px шаг вместо 26/40), под неё же
   упрощён и макет-заглушка (.acb-scan-mock — всегда светлый, не тёмный
   BizCard), так что фон под точками теперь предсказуемо светлый везде.
   Отдельный <div>, не background у самого .up-scan-wrap/.up-scan-overlay —
   те уже участвуют в background от комбинируемых классов (.up-img-wrap/
   .acb-scan-wrap), а background — shorthand: сбросил бы background-image в
   none, если бы их правило в CSS шло позже этого. Просто первый ребёнок в
   разметке — красится "под" всем остальным без z-index. ── */
.up-scan-dots {
  position: absolute; inset: 0; pointer-events: none;
  background-image: radial-gradient(circle, rgba(28,58,87,.14) 1px, transparent 1.35px);
  background-size: 15px 15px;
  animation: upScanDots 18s linear infinite;
}
@keyframes upScanDots {
  from { background-position: 0 0; }
  to   { background-position: -225px -225px; } /* 225/15=15 — кратно шагу, петля бесшовная */
}
@media (prefers-reduced-motion: reduce) {
  .up-scan-dots { animation: none; }
}

.up-scan-beam {
  position: absolute; left: 0; width: 100%; height: 26%; top: 0;
  background: linear-gradient(to bottom,
    rgba(109,94,240,0) 0%,
    rgba(109,94,240,.10) 45%,
    rgba(109,94,240,.28) 82%,
    rgba(255,255,255,.85) 97%,
    rgba(109,94,240,0) 100%);
  animation: upScan 2.3s cubic-bezier(.45,.05,.55,.95) infinite;
  pointer-events: none;
}
@keyframes upScan {
  0%   { transform: translateY(-110%); }
  100% { transform: translateY(500%); } /* 100/26 ≈ 3.85 своих высот до низа + запас за край */
}

/* ── Декоративный акцент по центру сканера (ScanSparkle, studio-extras.jsx)
   — единый для ЛЮБОГО места ожидания ИИ-ответа на сайте (основная
   генерация, апскейл, адаптация формата, подготовка к печати, правка по
   маске). cqmin — % от меньшей стороны АКТУАЛЬНОГО бокса (см.
   container-type:inline-size на .up-scan-wrap/.up-scan-overlay выше), поэтому
   один и тот же размер-формула одинаково хорошо смотрится и на тонкой
   визитке, и на высоком плакате, и на маленькой миниатюре — без частных
   переопределений под каждый инструмент. Второе значение width —
   фоллбэк на px для браузеров без поддержки cqmin (перекрывается clamp
   там, где cqmin понимается). ── */
.up-sparkle {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  pointer-events: none; z-index: 2;
  /* cqmin здесь — известное ограничение спеки, не баг разметки: контейнер
     объявлен container-type:inline-size (только по ширине, см. комментарий
     .up-scan-wrap выше — намеренно, из-за апскейлера/адаптера, где высота
     блока считается от контента), а у контейнеров без containment по
     блочной оси cqb/cqh по спеке резолвятся от viewport, не от реального
     контейнера — поэтому 72cqmin на практике вёл себя как чистый 72cqw
     (ширина), НЕ учитывая высоту вовсе. С потолком 252px (было) это почти
     не было заметно на типичных размерах; без потолка (первый заход
     2026-08-07) искорка на широкой/невысокой визитке вылезала далеко за
     верх/низ карточки — репродуцировано скриншотом. Фикс: --sparkle-max —
     CSS-переменная, которую конкретное место use-сайта может выставить
     инлайн-стилем на реальный min(ширина, высота) СВОЕГО бокса (см.
     studio.jsx, busy-ветка EmptyCanvas, там же и посчитан правильный
     max(w,h) для текущего формата макета) — без неё (остальные места:
     апскейлер/адаптер/подготовка к печати/правка по маске) поведение не
     меняется, старый потолок 252px остаётся запасным значением. */
  width: 168px;
  width: max(84px, min(72cqw, var(--sparkle-max, 252px)));
  aspect-ratio: 1 / 1;
  filter: drop-shadow(0 3px 10px rgba(35,20,70,.28));
}
.up-sparkle svg { display: block; width: 100%; height: 100%; }
@media (prefers-reduced-motion: reduce) {
  .up-sparkle { display: none; }
}

/* ── Появление готового результата: из лёгкого блюра в резкость — само
   движение «мутное → чёткое» и есть сообщение продукта. Лёгкий contrast/
   saturate на итоговой картинке — переезд рабочей части бывшего инлайн-
   фильтра (его ломал невалидный sharpen(1)).
   ⚠ Анимация ИМЕННО на img, не на .up-img-wrap: любой filter (даже blur(0)
   в конечном кадре с fill both) делает элемент containing block для
   position:fixed потомков — лупа (.up-loupe, fixed по координатам курсора)
   начинала позиционироваться от обёртки, улетала за её границы и срезалась
   её overflow:hidden. Так лупа «пропала» на готовом результате. У img
   позиционируемых потомков нет — блюрить его безопасно. Конечный кадр
   совпадает со статичным filter, поэтому fill-режим не нужен вовсе. ── */
.up-reveal img {
  filter: contrast(1.04) saturate(1.08);
  animation: upReveal .8s var(--ease);
}
@keyframes upReveal {
  from { opacity: .35; filter: blur(14px); }
  to   { opacity: 1;   filter: blur(0) contrast(1.04) saturate(1.08); }
}
@media (prefers-reduced-motion: reduce) {
  .up-scan-beam { animation: none; display: none; }
  .up-scan-img { transition: none; }
  .up-reveal img { animation: none; }
}

/* ============================================================
   MINI EDITOR PAGE (miniEditor.jsx, "Миниредактор")
   ============================================================ */
.studio-app.mini-mode .settings-panel {
  flex: 0 0 380px; width: 380px; min-width: 380px; max-width: 380px; transition: none;
}
.studio-app.mini-mode .mini-canvas {
  flex: 1 1 auto; width: auto; min-width: 0; opacity: 1; overflow: auto;
  display: flex; align-items: center; justify-content: center; padding: 26px;
}
/* Мобильный режим Миниредактора не был описан вообще — ни здесь, ни в общем
   мобильном блоке (там своя `.canvas`, у мини-редактора отдельный класс
   `.mini-canvas`, общие правила её не касаются). В итоге на телефоне
   работали ДВА необъяснённых десктопных правила разом: колонка настроек
   держала жёсткие min/max-width:380px (тесно, но терпимо на 390px), а вот
   `.mini-canvas` с overflow:auto и БЕЗ min-height превращалась в flex-item с
   автоминимумом 0 (так по спеке ведёт себя overflow≠visible) — сжималась
   почти до нуля вместо естественной высоты своего контента. Сам холст
   (.mini-stage, flex:none, размер по инлайн-стилю от реального фото) при
   этом рисовался в НАТУРАЛЬНУЮ величину поверх схлопнутого родителя и
   визуально наезжал на кнопки тулбара ("Передать в промпт"/"В обработку"
   были видны только узкими полосками из-под холста, зум-бар перекрывал
   "Скачать") — дизайн-аудит 2026-08-07. Тот же класс бага, что уже чинили
   выше для .settings-panel/.settings-body — тот же приём (flex:none). */
@media (max-width: 900px) {
  .studio-app.mini-mode .settings-panel {
    width: 100% !important; max-width: none; min-width: 0; flex: none;
    /* order:2 — запрос владельца 2026-08-08: в Миниредакторе, в отличие от
       Апскейлера/Адаптера/PrintPrep (там панель настроек НАД холстом —
       разумно, редактировать нечего, пока не загрузили фото), фото уже
       ЕСТЬ и это то, ради чего открыли инструмент — на телефоне логичнее
       сначала увидеть само изображение, а инструменты редактирования
       пролистать ниже. */
    order: 2;
  }
  .studio-app.mini-mode .mini-canvas {
    width: 100% !important; flex: none; min-height: 60vh; padding: 16px;
    order: 1;
  }
}
/* Колонка "макет + история под ним" (запрос 2026-07-26) — раньше .mini-canvas
   был flex-row без direction, и зум-бар с рабочей областью вставали В РЯД,
   а не друг над другом; из-за этого зум-бар был почти не виден. position:
   relative — точка отсчёта для плавающей .mini-zoom-bar (см. ниже), которая
   не должна скроллиться вместе с .mini-stage-wrap при зуме/панорамировании. */
.mini-canvas-panel {
  position: relative; display: flex; flex-direction: column; align-items: center;
  gap: 14px; width: 100%; height: 100%; min-height: 0;
}
.mini-saved-strip { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 4px; }
.mini-saved-thumb {
  /* border-radius: 0 (см. .up-img-wrap выше) — миниатюра реального
     сохранённого макета, не декоративная иконка. */
  flex: none; width: 64px; height: 64px; border-radius: 0; overflow: hidden;
  border: 1.5px solid var(--line); background: var(--paper-2); padding: 0; cursor: pointer;
  transition: border-color .15s;
}
.mini-saved-thumb:hover { border-color: var(--navy); }
.mini-saved-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Единая панель "инструменты + цвета" (запрос 2026-07-26 "свести в одну
   общую панель как в Photoshop, правильно расположить") — одна карточка,
   иконки-инструменты БЕЗ подписей сверху (только title-tooltip, как в
   настоящей панели PS) и накладывающиеся цветовые плашки снизу. */
.mini-toolbar-card {
  padding: 10px; border-radius: 12px; border: 1px solid var(--line-strong);
  background: var(--surface); box-shadow: var(--sh-sm);
}
/* Все 7 инструментов в один ряд (запрос владельца 2026-07-31) — раньше
   4+3 в две строки, занимало лишнюю строку по высоте без надобности:
   ширины панели (~340px) достаточно, чтобы 7 квадратных кнопок влезли
   в одну строку и остались нажимаемыми (~40px каждая). */
.mini-tool-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; }
.mini-tool-btn {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1; border-radius: 8px; border: 1px solid var(--line);
  background: var(--paper-2); color: var(--ink-soft); cursor: pointer; transition: all .15s;
}
.mini-tool-btn:hover { border-color: var(--line-strong); color: var(--ink); }
.mini-tool-btn.active { background: var(--navy); border-color: var(--navy); color: #fff; }

/* Накладывающиеся плашки основного/второго цвета — тот же силуэт, что и
   foreground/background swatch в панели инструментов Photoshop: второй цвет
   квадратом ПОД, основной — квадратом НАД со сдвигом, стрелка обмена в углу.
   Реальные <input type="color"> визуально скрыты (opacity:0, но не
   display:none — так клик/фокус по-прежнему работают через .click() из JS
   и с клавиатуры), кликабельная поверхность — сама цветная плашка сверху. */
/* Плашки и пипетки — вдвое крупнее (запрос 2026-07-26 "видней и крупней"):
   swatch 30→60px, стрелка обмена 16→28px, ps-пипетки 22→44px. */
/* Плашки/пипетки уменьшены пропорционально однострочной сетке инструментов
   выше (запрос владельца 2026-07-31, "выбор цвета пропорционально этим
   кнопкам уменьшить") — swatch 60→42px (тот же размер, что и .mini-tool-btn
   в один ряд), стрелка обмена и пипетки — пропорционально следом. */
.mini-ps-swatches { margin-top: 10px; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; }
.mini-ps-swatch-stack { position: relative; width: 64px; height: 64px; flex: none; }
.mini-ps-swatch {
  position: absolute; width: 42px; height: 42px; border-radius: 8px;
  border: 2px solid var(--line-strong); box-shadow: var(--sh-sm); cursor: pointer; padding: 0;
}
.mini-ps-swatch.secondary { right: 0; bottom: 0; }
.mini-ps-swatch.primary { left: 0; top: 0; z-index: 1; }
.mini-ps-swap {
  position: absolute; top: -6px; right: -6px; z-index: 2; width: 20px; height: 20px;
  border-radius: 50%; border: 1.5px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); display: flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0; transition: all .15s;
}
.mini-ps-swap:hover { color: var(--navy); border-color: var(--navy); }
.mini-ps-color-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.mini-ps-eyedroppers { display: flex; flex-direction: column; gap: 6px; }
.mini-eyedropper-btn {
  flex: none; width: 34px; height: 34px; border-radius: 8px; border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink-soft); cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: all .15s;
}
.mini-eyedropper-btn:hover { border-color: var(--line-strong); color: var(--ink); }
.mini-eyedropper-btn.active { background: var(--navy); border-color: var(--navy); color: #fff; }
.mini-eyedropper-btn.ps { width: 34px; height: 34px; border-radius: 8px; }
.mini-eyedropper-hint { flex-basis: 100%; font-size: 11.5px; color: var(--bronze); margin-top: 6px; font-style: italic; }

/* ── Компактный редизайн панели (запрос владельца 2026-07-31, "всё на
   одном экране без скролла") — "Поворот и отражение" и "Убрать
   прозрачность" были двумя подписанными full-width секциями с
   собственными <hr>; теперь одна icon-only строка, тот же визуальный
   язык, что и .mini-eyedropper-btn (тултип вместо подписи под кнопкой). ── */
.mini-quickrow { margin-top: 10px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.mini-quick-btn {
  flex: none; width: 34px; height: 34px; border-radius: 8px; border: 1px solid var(--line-strong);
  background: var(--paper-2); color: var(--ink-soft); cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: all .15s;
}
.mini-quick-btn:hover { border-color: var(--line-strong); color: var(--ink); background: var(--surface); }
.mini-quickrow-div { width: 1px; height: 22px; background: var(--line-strong); flex: none; margin: 0 2px; }

/* Толщина+Прозрачность (кисть/ластик), Яркость+Контраст+Насыщенность
   (коррекция) — раньше каждый был своим .field на всю ширину, стопкой;
   один ряд-грид экономит по одной строке "воздуха" на каждую пару полей. */
.mini-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.mini-field-row-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
/* user-select:none (запрос владельца 2026-08-07) — подписи ползунков
   ("Толщина: 24px B/E", "Прозрачность: 0%") зажимались двойным/тройным
   кликом мимо самого ползунка и подсвечивались как обычный текст, хотя
   ничего скопировать тут по смыслу нельзя — это подписи элемента
   управления, не контент. */
.mini-field-row label, .mini-field-row-3 label { font-size: 11.5px; user-select: none; }
/* Ползунки (input[type=range]) держат браузерный дефолтный intrinsic-
   размер (~130px в Chrome), больше доступной 1fr-колонки при 3 в ряд —
   грид без явного min-width:0 на ячейках не сжимал их, и ряд "Яркость/
   Контраст/Насыщенность" вылезал за правый край панели (репорт владельца
   2026-08-03, скриншот). width:100% + min-width:0 разрешают ползунку и
   его ячейке сжаться до реальной ширины колонки. */
.mini-field-row > div, .mini-field-row-3 > div { min-width: 0; }
.mini-field-row input[type="range"], .mini-field-row-3 input[type="range"] { width: 100%; }
/* Ряд действий коррекции (Сбросить/Применить/Ч-Б/Сепия) — раньше сидел на
   общем .row.gap-8 (nowrap flex), в 380px-панели 4 кнопки без переноса
   вылезали за правый край (тот же репорт 2026-08-03). flex-wrap здесь
   безопасен — кнопки просто уйдут на вторую строку на узкой панели,
   ничего не обрежется. */
.mini-adjust-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }

/* Нижние действия — три второстепенных (Другое фото/Скачать/История) в
   компактной 2-колоночной сетке иконка+короткая подпись вместо стопки
   full-width кнопок. Главные CTA ("Передать в промпт"/"В обработку") —
   ИСПРАВЛЕНО (репорт 2026-07-31 "кнопка съехала"): раньше стояли рядом в
   2 колонки, и длинная подпись "В обработку" вылезала за свою половину и
   обрезалась краем панели — теперь обе на всю ширину, стопкой, надёжно
   при любой ширине панели. */
/* 3 колонки (было 2) — "Сохранить в историю" встала посередине, между
   "Другое фото" и "Скачать" (запрос владельца 2026-08-08: "между двумя
   кнопками должна быть по центру..."). */
.mini-output-secondary { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
/* На узком экране 3 равные колонки не оставляют места самой длинной подписи
   ("Сохранить в историю", 19 символов у .btn с white-space:nowrap) — кнопка
   вылезала за правый край панели, "Скачать" уезжал за пределы экрана
   (дизайн-аудит 2026-08-08, мобильный отчёт: "кнопки не должны обрезаться
   в миниредакторе"). Две короткие кнопки в ряд сверху, длинная — отдельной
   строкой на всю ширину снизу. */
@media (max-width: 520px) {
  .mini-output-secondary {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "other download" "save save";
  }
  .mini-output-secondary > button:nth-child(1) { grid-area: other; }
  .mini-output-secondary > button:nth-child(2) { grid-area: save; }
  .mini-output-secondary > button:nth-child(3) { grid-area: download; }
}
.mini-output-primary-stack { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }

/* Всплывающее меню "чем залить выделение" (репорт владельца 2026-08-02,
   скриншот диалога Fill в Photoshop) — открывается по клику "Действие"
   или клавише Del/Backspace/Enter, вместо выбора режима заливки ДО
   рисования выделения. Тот же "стеклянный" попап, что у .hist-pop. */
.mini-fill-popover {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 20;
  min-width: 240px; background: var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border);
  border-radius: 14px; box-shadow: var(--glass-shadow); padding: 6px 0;
}
.mini-fill-popover-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 14px; border: none; background: none; cursor: pointer;
  font-family: var(--sans); font-size: var(--text-sm); color: var(--ink); text-align: left;
  transition: background .12s;
}
.mini-fill-popover-item:hover { background: var(--paper-2); }
.mini-fill-popover-item svg { flex: none; color: var(--ink-soft); }
.mini-fill-popover-item-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.mini-fill-popover-item-text small { font-size: 11px; font-weight: 400; color: var(--muted); }
.mini-fill-popover-swatch {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  border: 1.5px solid var(--line-strong); box-shadow: 0 0 0 1px var(--surface) inset;
}
.mini-fill-popover-sep { height: 1px; margin: 5px 0; background: var(--line); flex: none; }

.mini-select {
  width: 100%; height: 40px; border-radius: 9px; border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink); padding: 0 10px; font-size: var(--text-sm);
  font-family: var(--sans); cursor: pointer;
}

/* Черновик текста на канвасе (запрос 2026-07-26, п.7) — обёртка на точке
   canvasPt (родитель — position:relative, см. .mini-stage), transform-origin
   в её углу: CSS-поворот всей обёртки (доп. пункт "поворот текста") держит
   хендл и textarea синхронными без пересчёта их координат под угол — см.
   commitTextDraft/JSX. */
.mini-text-draft-wrap { position: absolute; z-index: 5; transform-origin: 0 0; }
.mini-text-draft-input {
  position: absolute; min-width: 60px; min-height: 1.4em;
  background: rgba(255,255,255,.82); border: 1.5px dashed var(--navy);
  border-radius: 4px; padding: 2px 4px; outline: none; resize: both;
  line-height: 1.25; white-space: pre; overflow: hidden;
}
[data-theme="dark"] .mini-text-draft-input { background: rgba(20,20,20,.82); }

.mini-hint-card {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 13px 14px; border-radius: 12px;
  border: 1px dashed var(--bronze); background: var(--bronze-tint);
  font-size: var(--text-sm); line-height: 1.5; color: var(--bronze); margin-top: 14px;
}
.mini-hint-card svg { flex: none; margin-top: 2px; }

/* Панель "Применить/Отменить выделение" (репорт владельца 2026-08-02:
   действие больше не запускается само по себе, как только контур
   замкнулся — сначала можно добавить/убрать области Shift/Ctrl, действие
   выполняется только по явному клику "Применить"). */
.mini-selection-apply-bar {
  margin-top: 12px; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--navy); background: var(--navy-tint);
}

/* Оверлей "идёт обработка" поверх холста, пока летит запрос к бесплатному
   IOPaint или платной генеративной модели (репорт владельца 2026-08-02:
   "не видно что идёт реальная генерация") — раньше единственным сигналом
   был текст подсказки в левой панели. */
.mini-busy-overlay {
  position: absolute; inset: 0; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  background: rgba(10,12,16,.55); border-radius: inherit;
}
.mini-busy-overlay-text { color: #fff; font-size: 13.5px; font-weight: 600; text-align: center; padding: 0 20px; }

.mini-output-actions { display: flex; flex-direction: column; gap: 6px; }
.mini-output-actions .btn { justify-content: center; }

/* overflow: auto (доп. пункт "зум/пан", 2026-07-26) — при zoom > 1
   увеличенный холст не помещается в видимую область, скролл нативный,
   без отдельного pan-инструмента/перетаскивания холста. */
.mini-stage-wrap { display: flex; align-items: center; justify-content: center; width: 100%; flex: 1 1 auto; min-height: 0; overflow: auto; }

/* "Оконка лупы" с зумом (запрос 2026-07-26) — плавающая пилюля в правом
   верхнем углу области макета (.mini-canvas-panel — position:relative),
   НЕ внутри скроллящегося .mini-stage-wrap, поэтому остаётся на месте при
   панорамировании увеличенного холста. */
.mini-zoom-bar {
  position: absolute; top: 10px; right: 10px; z-index: 4;
  display: flex; align-items: center; gap: 6px;
  padding: 6px 10px 6px 8px; border-radius: 20px;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); box-shadow: var(--sh-sm);
}
.mini-zoom-btn, .mini-zoom-reset {
  height: 26px; border-radius: 7px; border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink-soft); cursor: pointer;
  font-size: var(--text-sm); font-family: var(--sans); transition: all .15s;
}
.mini-zoom-btn { width: 26px; display: flex; align-items: center; justify-content: center; font-size: 15px; line-height: 1; padding: 0; }
.mini-zoom-reset { min-width: 50px; padding: 0 6px; font-variant-numeric: tabular-nums; font-size: 12.5px; }
.mini-zoom-btn:hover:not(:disabled), .mini-zoom-reset:hover { border-color: var(--navy); color: var(--ink); }
.mini-zoom-btn:disabled { opacity: .4; cursor: default; }
.mini-zoom-hint { font-size: 10.5px; color: var(--muted); white-space: nowrap; margin-left: 2px; }

/* История правок под макетом (запрос 2026-07-26 "переносим под макет") —
   раньше жила в левой панели, теперь рядом с самим результатом. */
.mini-history-bar { display: flex; gap: 8px; flex: none; }

/* Панель настроек текста под макетом (запрос 2026-07-26 "так удобней
   работать и применять окончательно") — та же карточка-паттерн, что и у
   .mini-zoom-bar/.mini-hint-card, компактная строка под каждый контрол. */
.mini-text-toolbar {
  flex: none; width: min(420px, 100%); display: flex; flex-direction: column; gap: 8px;
  padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--line-strong); background: var(--surface); box-shadow: var(--sh-sm);
}
.mini-text-toolbar-row { display: flex; align-items: center; gap: 8px; }
.mini-text-toolbar-row input[type="range"] { flex: 1; }
.mini-text-toolbar-label { font-size: 11.5px; color: var(--muted); white-space: nowrap; min-width: 92px; }

.mini-shortcut-hint { font-size: 10.5px; color: var(--muted); margin-left: 6px; font-weight: 400; }

/* Панель "Слои" (запрос 2026-07-26 "слои как в photoshop") — список сверху
   вниз = порядок отображения в Photoshop (верхний слой первый), компактные
   строки с превью-действиями по наведению/всегда видимыми мелкими иконками
   (сайдбар узкий, места на подписи нет). */
.mini-layers-list { display: flex; flex-direction: column; gap: 4px; max-height: 220px; overflow-y: auto; }
.mini-layer-row {
  display: flex; align-items: center; gap: 5px; padding: 6px 7px; border-radius: 8px;
  border: 1px solid transparent; background: var(--paper-2); cursor: pointer; transition: all .15s;
}
.mini-layer-row:hover { border-color: var(--line); }
.mini-layer-row.active { border-color: var(--navy); background: var(--navy-tint); }
.mini-layer-eye { flex: none; width: 22px; height: 22px; border: none; background: none; color: var(--ink-soft); cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
.mini-layer-eye.off { opacity: .3; }
.mini-layer-name { flex: 1; min-width: 0; font-size: 12px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mini-layer-icon-btn {
  flex: none; width: 20px; height: 20px; border: none; background: none; color: var(--ink-soft);
  cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; border-radius: 5px; transition: all .15s;
}
.mini-layer-icon-btn:hover { background: var(--surface); color: var(--ink); }
.mini-layer-icon-btn:disabled { opacity: .25; cursor: default; }
.mini-layer-icon-btn.danger:hover { color: #e0483e; }

/* Хендл перетаскивания черновика текста (доп. пункт "подвинуть текст",
   2026-07-26) — маленькая кнопка слева от textarea, тянуть за неё, а не за
   само поле (иначе текстовый ввод и drag конфликтуют за mousedown). */
.mini-text-drag-handle {
  position: absolute; z-index: 6; width: 20px; height: 20px; border-radius: 5px;
  border: 1px solid var(--navy); background: var(--navy); color: #fff;
  display: flex; align-items: center; justify-content: center; cursor: grab; padding: 0;
}
.mini-text-drag-handle:active { cursor: grabbing; }
.mini-text-draft-input.dragging { opacity: .7; }
.mini-stage {
  /* border-radius: 0 (см. .up-img-wrap выше, тот же запрос 2026-07-24) —
     рабочая область показа макета в миниредакторе. */
  position: relative; flex: none; border-radius: 0; overflow: visible;
  box-shadow: var(--sh-md);
  background-image:
    linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%);
  background-size: 16px 16px; background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
[data-theme="dark"] .mini-stage {
  background-image:
    linear-gradient(45deg, #3a3a3a 25%, transparent 25%), linear-gradient(-45deg, #3a3a3a 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #3a3a3a 75%), linear-gradient(-45deg, transparent 75%, #3a3a3a 75%);
}
.mini-canvas-el { position: absolute; inset: 0; display: block; pointer-events: none; }
.mini-canvas-overlay { position: absolute; inset: 0; display: block; touch-action: none; }
.mini-canvas-overlay:focus-visible { outline: 3px solid var(--bronze); outline-offset: -3px; }

/* ============================================================
   UTILITIES
   ============================================================ */
.muted { color: var(--muted); }
.soft { color: var(--ink-soft); }
.serif { font-family: var(--display); }
.tnum { font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }
.click { cursor: pointer; }
.rel { position: relative; }
.hidden { display: none !important; }
.grow { flex: 1; }
.center-t { text-align: center; }

/* ============================================================
   STUDIO PRO BLOCKS
   ============================================================ */
.lbl-soft { color: var(--muted); font-weight: 500; }
.hint-soft {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-sm); color: var(--muted); line-height: 1.4;
}
/* Инлайн-кнопка внутри .hint-soft (например «выбрать» у рекомендации стиля) —
   выглядит как ссылка, а не отдельная кнопка, чтобы не спорить по весу с
   самим текстом подсказки. */
.hint-soft-pick {
  padding: 0; border: none; background: none; cursor: pointer;
  font: inherit; font-weight: 700; color: var(--navy);
  text-decoration: underline; text-underline-offset: 2px;
}
.hint-soft-pick:hover { color: var(--navy-deep); }

/* ---- Logo quality card ---- */
.logo-q {
  border-radius: 12px; padding: 14px 16px;
  border: 1px solid var(--line); background: var(--surface);
}
.logo-q.good  { border-color: #B8D8C5; background: #F0F7F2; }
.logo-q.warn  { border-color: #E8CFA0; background: #FBF4E6; }
.logo-q.error { border-color: #E6B6B0; background: #FBEEEC; }
[data-theme="dark"] .logo-q.good  { background: #16241C; border-color: #2C4636; }
[data-theme="dark"] .logo-q.warn  { background: #2A2310; border-color: #4A3D1A; }
[data-theme="dark"] .logo-q.error { background: #2A1714; border-color: #4A2722; }
.logo-q-title { font-weight: 700; font-size: var(--text-sm); color: var(--ink); }
.logo-q-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.logo-q-list li { display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); color: var(--ink-soft); }
.logo-q-list li svg { color: var(--good); flex: none; }
.logo-q-rec { font-size: var(--text-sm); color: var(--ink-soft); margin: 8px 0 4px; line-height: 1.5; }
.logo-q-meta { font-size: var(--text-xs); color: var(--muted); margin-bottom: 10px; }
.logo-q.warn .btn, .logo-q.error .btn { margin-top: 6px; }

/* ---- Style chips ---- */
/* 14 стилей (STYLE_CHIP_OPTIONS) в 2 строки. Именно flex-wrap, а не grid с
   фиксированными колонками — там колонка тянется по САМОМУ ШИРОКОМУ элементу
   среди обеих строк, и у коротких слов оставался некрасивый пробел до
   следующей колонки. Flex просто пакует каждую строку по факту ширины. */
.chip-wrap { display: flex; flex-wrap: wrap; gap: 7px; }
/* Доп. верхний зазор — иначе бейдж "Топ-результат" (см. .chip-badge-rec)
   налезал бы на подпись "Стиль" над первым рядом (row-gap не действует ДО
   первой строки flex-wrap, поэтому нужен именно padding-top контейнера) и на
   чипы предыдущего ряда. */
.chip-wrap-badged { padding-top: 11px; row-gap: 13px; }
/* Маленький бейдж в верхнем правом углу чипа (не по центру сверху) — не
   перекрывает ни подпись поля, ни соседние чипы. */
.chip-badge-rec {
  position: absolute; top: -8px; right: -4px;
  background: #1FAA59; color: #fff; font-size: 8px; font-weight: 800;
  letter-spacing: .02em; text-transform: uppercase; line-height: 1;
  padding: 2px 6px; border-radius: 999px; white-space: nowrap;
  box-shadow: 0 1px 4px rgba(31,170,89,.35);
}
.chip {
  height: 34px; padding: 0 10px; border-radius: 40px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-soft); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  white-space: nowrap;
}
.chip:hover { border-color: var(--line-strong); color: var(--ink); }
.chip.on { background: var(--navy); border-color: var(--navy); color: #fff; }
.chip-more { color: var(--muted); }

/* ---- Priority checks ---- */
.check-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.check-item {
  display: flex; align-items: center; gap: 9px;
  height: 40px; padding: 0 12px; border-radius: 9px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-soft); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s; text-align: left;
}
.check-item:hover { border-color: var(--line-strong); }
.check-item.on { border-color: var(--navy); background: var(--navy-tint); color: var(--navy); }
.check-box {
  width: 18px; height: 18px; border-radius: 5px; flex: none;
  border: 1.5px solid var(--line-strong); background: var(--surface);
  display: grid; place-items: center; color: #fff; transition: all .15s;
}
.check-item.on .check-box { background: var(--navy); border-color: var(--navy); }

/* ---- Contacts block ---- */
.extract-btn {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--navy-tint); border: 1px solid var(--navy-line);
  color: var(--navy); font-size: var(--text-xs); font-weight: 600;
  padding: 6px 11px; border-radius: 8px; cursor: pointer;
  font-family: var(--sans); transition: all .15s; white-space: nowrap;
}
.extract-btn:hover { background: var(--navy); color: #fff; border-color: var(--navy); }

/* ---- Task understanding card ---- */
.task-card {
  border: 1px solid var(--navy-line); border-radius: 14px; padding: 16px 18px;
  background: linear-gradient(135deg, var(--navy-tint), var(--surface));
}
.task-head {
  display: flex; align-items: center; gap: 9px;
  font-weight: 700; font-size: var(--text-sm); color: var(--ink); margin-bottom: 14px;
}
.task-edit {
  margin-left: auto; font-size: var(--text-xs); font-weight: 600; color: var(--navy);
  background: var(--surface); border: 1px solid var(--navy-line);
  padding: 5px 12px; border-radius: 8px; cursor: pointer; font-family: var(--sans);
  transition: all .15s;
}
.task-edit:hover { background: var(--navy); color: #fff; border-color: var(--navy); }
.task-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.task-grid > div { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.auto-badge {
  display: inline-block; margin-left: 8px;
  font-size: var(--text-xs); font-weight: 700; color: var(--bronze);
  background: var(--bronze-tint); padding: 2px 8px; border-radius: 40px;
  vertical-align: middle;
}

/* ---- Manual link ---- */
.manual-link {
  display: inline-flex; align-items: center; gap: 7px;
  background: none; border: none; color: var(--navy);
  font-size: var(--text-sm); font-weight: 600; cursor: pointer; font-family: var(--sans);
  padding: 0;
}
.manual-link:hover { text-decoration: underline; }
.manual-link-block {
  width: 100%; justify-content: center;
  border: 1px dashed var(--line-strong); border-radius: 11px;
  padding: 13px; color: var(--ink-soft);
}
.manual-link-block:hover { border-color: var(--navy); color: var(--navy); text-decoration: none; background: var(--paper-2); }

/* ---- AI recommendations (results) ---- */
.ai-recs {
  display: flex; flex-direction: column; gap: 11px;
  padding: 15px 17px; border-radius: 14px;
  background: #F0F7F2; border: 1px solid #B8D8C5;
}
[data-theme="dark"] .ai-recs { background: #16241C; border-color: #2C4636; }
.rec-chip {
  display: inline-flex; align-items: center; gap: 7px;
  height: 34px; padding: 0 14px; border-radius: 40px;
  border: 1px solid var(--good); background: var(--surface);
  color: var(--good); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s;
}
.rec-chip:hover { background: var(--good); color: #fff; }
.rec-chip svg { flex: none; }

/* ============================================================
   RESULTS PANE — pro
   ============================================================ */
.side-seg {
  display: flex;
  background: var(--paper-2);
  border-radius: 10px;
  padding: 3px;
  gap: 2px;
  flex: 1;
  max-width: 260px;
}
.seg-btn {
  flex: 1;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  height: 34px; border-radius: 8px; border: none;
  background: transparent; color: var(--ink-soft);
  font-size: var(--text-sm); font-weight: 500; cursor: pointer;
  font-family: var(--sans); transition: all .15s;
  white-space: nowrap;
}
.seg-btn.active {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(0,0,0,.08);
}
.seg-btn:disabled { opacity: .4; cursor: not-allowed; }
.icon-btn-sm {
  width: 34px; height: 34px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: var(--ink-soft); transition: all .15s;
}
.icon-btn-sm:hover { background: var(--paper-2); }
.icon-btn-sm:disabled { opacity: .35; cursor: not-allowed; }

/* brand row (redesigned) */
.brand-row-wrap {
  display: flex; flex-direction: column; gap: 12px;
  padding: 14px 18px;
  border-top: 1px solid var(--line);
}
.brand-row-head { display: flex; align-items: center; gap: 10px; }
.brand-row-ic {
  width: 28px; height: 28px; border-radius: 8px;
  background: var(--paper-2); border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-soft); flex: none;
}
.brand-row-title { font-size: var(--text-sm); font-weight: 600; color: var(--ink); }
.brand-row-hint { display: block; font-size: var(--text-xs); color: var(--muted); margin-top: 1px; }
.brand-chips-row { display: flex; gap: 6px; flex-wrap: wrap; }
.brand-chip {
  display: inline-flex; align-items: center; gap: 5px;
  height: 30px; padding: 0 10px;
  border-radius: 8px; border: 1px solid var(--line);
  background: var(--surface); cursor: pointer;
  font-family: var(--sans); font-size: var(--text-xs);
  color: var(--ink); transition: border-color .15s;
  white-space: nowrap;
}
.brand-chip:hover { border-color: var(--navy); }
.bc-dot { width: 9px; height: 9px; border-radius: 3px; flex: none; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.bc-label { font-size: var(--text-xs); color: var(--muted); }
.bc-value { font-weight: 600; }

/* Brand pick modal contents */
.bp-chips { display: flex; flex-wrap: wrap; gap: 9px; }
.bp-chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 36px; padding: 0 15px; border-radius: 40px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-soft); font-size: 13.5px; font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s;
}
.bp-chip:hover { border-color: var(--navy); color: var(--navy); }
.bp-chip.on { border-color: var(--navy); background: var(--navy); color: #fff; }
.bp-swatches { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.bp-swatch {
  position: relative; display: flex; align-items: center; gap: 11px;
  padding: 11px 13px; border-radius: 11px; border: 1px solid var(--line);
  background: var(--surface); cursor: pointer; font-family: var(--sans);
  transition: all .15s; text-align: left;
}
.bp-swatch:hover { border-color: var(--line-strong); }
.bp-swatch.on { border-color: var(--navy); box-shadow: 0 0 0 2px rgba(28,58,87,.12); }
.bp-swatch-dot { width: 26px; height: 26px; border-radius: 7px; flex: none; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.bp-swatch-name { font-size: var(--text-sm); font-weight: 600; color: var(--ink); }
.bp-swatch-check {
  position: absolute; top: 8px; right: 8px; width: 18px; height: 18px; border-radius: 999px;
  background: var(--navy); color: #fff; display: grid; place-items: center;
}
.bp-foot { display: flex; justify-content: flex-end; margin-top: 18px; }

/* "Наложить иконку" (studio-extras.jsx, 2026-07-29) — панель под тулбаром
   правок: выбор файла / ползунок размера / применить, плюс сам оверлей
   перетаскивается прямо на превью макета (см. onIconPointerDown). */
.icon-overlay-panel { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 0 0; }
.icon-overlay-side-switch { display: flex; gap: 6px; }
.icon-overlay-upload { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.icon-overlay-size { display: inline-flex; align-items: center; gap: 8px; font-size: var(--text-sm); color: var(--ink-soft); }

/* hero preview — крупный показ макета: скруглённых углов больше нет
   (пользователь просил убрать), поля минимальные, чтобы не липло к краю
   экрана, а сам макет растягивается на доступное место (см. heroScale в
   studio-extras.jsx — считается от реального размера этого блока). */
.hero-preview {
  display: flex; justify-content: center; align-items: center;
  flex: 1; min-height: 0;
  background: var(--paper-2);
  padding: 16px; border: 1px solid var(--line);
}
.rdiv { color: var(--line-strong); }

/* ===== Region edit (inpainting / local mask) ===== */
.region-toolbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 14px;
}
.region-toggle {
  display: inline-flex; align-items: center; gap: 7px;
  height: 34px; padding: 0 14px; border-radius: 9px;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink); font-family: var(--sans); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; transition: border-color .15s, color .15s, background .15s, box-shadow .15s;
}
.region-toggle:hover { border-color: var(--bronze); color: var(--bronze); }
.region-toggle.active {
  border-color: var(--bronze); color: #fff; background: var(--bronze);
  box-shadow: 0 0 0 3px var(--bronze-tint);
}

/* ===== B2B: кнопка правки · пресеты · баланс · скелетоны ===== */

/* 1. Тулбар результата — две подписанные зоны, "Правки"/"Готово" (см.
   UX-обзор 2026-07-20: один слитный flex-wrap ряд на 7+ кнопок одного
   веса читался как нерасчленённая куча, а перенос строк зависел от
   случайной ширины окна, а не от замысла). Подпись зоны заменяет собой
   прежний объясняющий абзац под тулбаром — то, что раньше приходилось
   ПИСАТЬ словами, теперь видно по самой группировке. */
.region-toolbar-col { flex-direction: column; align-items: flex-start; gap: 6px; }
.region-toolbar-row { display: flex; align-items: flex-end; width: 100%; gap: 18px; flex-wrap: wrap; }
.region-zone { display: flex; flex-direction: column; gap: 6px; }
.region-zone-edit { flex: 1 1 auto; min-width: 0; }
.region-zone-export { flex: none; }
.region-zone-label {
  font-size: 10.5px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted); padding-left: 2px;
}
.region-zone-box {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--paper-2); border: 1px solid var(--line-soft); border-radius: 13px; padding: 8px;
}
/* CTA «Точечная правка» — теперь единственная заливная кнопка в зоне
   (герой этого экрана: по замыслу самое частое действие, поэтому одна
   заливка на всю группу, а не в тени среди пяти одинаковых пилюль).
   Раньше была navy-tint с рамкой того же оттенка, что и настоящее
   "активно" (см. .active ниже) — то есть до первого клика уже выглядела
   почти как включённый переключатель. Сплошная заливка снимает эту
   двусмысленность без анимации-пульса, которая спорила за внимание с
   PDF-кнопкой весь сеанс. */
.region-edit-cta {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  flex: 1 1 auto; min-width: 0; height: 40px; padding: 0 18px; border-radius: 10px;
  border: 1.5px solid var(--navy); background: var(--navy);
  color: #fff; font-family: var(--sans); font-size: 13.5px; font-weight: 700;
  cursor: pointer; box-shadow: var(--sh-md);
  transition: background .15s, box-shadow .15s, transform .12s;
}
.region-edit-cta:hover { background: var(--navy-deep); box-shadow: var(--sh-lg); transform: translateY(-1px); }
.region-edit-cta:disabled { opacity: .5; pointer-events: none; }
.region-edit-cta.active {
  background: #059669; border-color: #059669;
  box-shadow: 0 0 0 3px rgba(5,150,105,.18);
}
.region-edit-cta.active:hover { background: #047857; border-color: #047857; transform: none; box-shadow: 0 0 0 3px rgba(5,150,105,.22); }
.region-edit-dot { font-size: var(--text-xs); animation: dotBlink 1.1s steps(1) infinite; }
.region-edit-hint { font-size: var(--text-xs); line-height: 1.45; color: var(--muted); max-width: 540px; margin-top: 4px; }
/* Бейдж "Бесплатный редактор" (AI Умное восстановление) — запрос
   2026-07-21: должно быть явно видно, что этот путь правки бесплатный, в
   отличие от "Точечная правка" (1 кредит) рядом. */
.region-free-badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 999px; margin-right: 4px;
  background: var(--bronze-tint); color: var(--bronze); font-weight: 700;
  font-size: var(--text-xs);
}
/* Назад/вперёд по выделению — та же .region-icon-btn пилюля, что и у
   Доработать/QR/Улучшить в тулбаре результата, только сгруппирована теснее
   (gap 4px вместо обычного зазора между секциями). */
.region-tool-history { gap: 4px; }
/* Остальные способы правки (Доработать/QR/Улучшить/Адаптировать) — тихие
   значки-кнопки с подсказкой при наведении: одного веса друг с другом,
   заметно тише заливной .region-edit-cta, чтобы не спорить с ней за
   главное внимание в зоне. Тот же значок-паттерн у Истории/Сохранить
   в зоне "Готово" ниже. */
.region-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; padding: 0; border-radius: 10px; flex: none;
  border: 1.5px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.region-icon-btn:hover { border-color: var(--navy); color: var(--navy); background: var(--navy-tint); }
.region-icon-btn:disabled { opacity: .5; pointer-events: none; }
/* Скачать — PNG/PDF раньше были двумя равными по весу кнопками (светлая
   PNG рядом с тёмной "выбранной" PDF), из-за чего заливка PDF читалась
   как переключатель формата, а не как приоритетное действие. Печатный
   PDF — основной путь сервиса (см. CLAUDE.md), поэтому это клик по
   умолчанию; PNG — под стрелкой рядом, тот же попап, что у Истории. */
.region-download-split { display: inline-flex; }
.region-download-main {
  display: inline-flex; align-items: center; gap: 7px;
  height: 38px; padding: 0 14px; border-radius: 10px 0 0 10px;
  border: none; background: var(--navy); color: #fff;
  font-family: var(--sans); font-size: 13.5px; font-weight: 700; cursor: pointer;
  box-shadow: var(--sh-md); transition: background .15s;
}
.region-download-main:hover { background: var(--navy-deep); }
.region-download-main:disabled { opacity: .5; pointer-events: none; }
.region-download-caret {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 38px; padding: 0; border-radius: 0 10px 10px 0;
  border: none; border-left: 1px solid rgba(255,255,255,.28);
  background: var(--navy); color: #fff; cursor: pointer;
  box-shadow: var(--sh-md); transition: background .15s;
}
.region-download-caret:hover { background: var(--navy-deep); }
.region-download-caret:disabled { opacity: .5; pointer-events: none; }
/* Пункт в попапе — переиспользует .hist-pop (тот же стеклянный попап,
   что у Истории версий), но это не строка версии, а кликабельная кнопка. */
.dl-pop-item {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 14px; border: none; background: none; cursor: pointer;
  font-family: var(--sans); font-size: var(--text-sm); color: var(--ink); text-align: left;
  transition: background .12s;
}
.dl-pop-item:hover { background: var(--paper-2); }

/* ── nanobanana-стиль разметки: палитра цвета + переключатель "Эскиз"/"Текст" ── */
.region-tools-row { display: flex; align-items: center; justify-content: center; gap: 16px; flex-wrap: wrap; margin-top: 2px; width: 100%; }
.region-color-row { display: flex; align-items: center; gap: 8px; }
.region-color-swatch {
  width: 26px; height: 26px; border-radius: 50%; padding: 0; flex: none;
  border: 2px solid var(--surface); box-shadow: 0 0 0 1.5px var(--line-strong);
  cursor: pointer; transition: box-shadow .15s, transform .12s;
}
.region-color-swatch:hover { transform: scale(1.08); }
.region-color-swatch.active { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--navy); }
.region-tool-row { display: flex; align-items: center; gap: 6px; }
.region-tool-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 13px; border-radius: 9px;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); font-family: var(--sans); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; transition: border-color .15s, color .15s, background .15s, box-shadow .15s;
}
.region-tool-btn:hover { border-color: var(--navy); color: var(--navy); }
.region-tool-btn.active { border-color: var(--navy); background: var(--navy); color: #fff; box-shadow: 0 0 0 3px var(--navy-tint); }
.region-tool-t-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: 4px; border: 1.6px solid currentColor;
  font: 800 10px/1 var(--sans); flex: none;
}

/* Текстовый ввод, всплывающий прямо в точке клика на макете (режим "Текст") —
   позиционирован абсолютно внутри .region-stage, координаты — в CSS-пикселях
   относительно самого canvas (см. cssPtFrom в studio-extras.jsx). */
.region-text-input {
  position: absolute; z-index: 7; transform: translateX(-2px);
  min-width: 90px; max-width: 260px; padding: 3px 6px;
  background: rgba(255,255,255,.94); border: 1.5px dashed; border-radius: 6px;
  font: 700 15px/1.2 var(--sans); outline: none; box-shadow: var(--sh-md);
}
.region-text-input::placeholder { color: var(--muted); font-weight: 500; }
@keyframes dotBlink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: .25; } }

/* 2. Пресеты режима генерации */
.gen-preset-row { display: flex; flex-direction: column; gap: 8px; margin-top: 11px; }
.gen-preset-label { font-size: var(--text-xs); font-weight: 600; color: var(--ink-soft); }
.gen-preset-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.gen-preset-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-radius: 999px;
  border: 1.5px solid var(--line); background: var(--surface);
  color: var(--ink-soft); font-family: var(--sans); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; transition: border-color .14s, color .14s, background .14s, box-shadow .14s; white-space: nowrap;
}
.gen-preset-chip:hover { border-color: var(--navy); color: var(--navy); background: var(--navy-tint); }
.gen-preset-chip.on { border-color: var(--navy); background: var(--navy); color: #fff; box-shadow: 0 3px 12px rgba(28,58,87,.22); }
.gpc-i { font-size: var(--text-sm); line-height: 1; }

/* 2b. Единый блок «Стиль оформления» — 2 ряда b2b-кнопок (замена <select>) */
.style-block { display: flex; flex-direction: column; gap: 15px; margin-top: 12px; }
.style-row { display: flex; flex-direction: column; gap: 8px; }
.style-row-label { font-size: var(--text-xs); font-weight: 600; color: var(--ink-soft); }
.style-tabs { display: flex; flex-wrap: wrap; gap: 8px; }
.style-tab {
  position: relative;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px; border-radius: 10px;
  border: 1.5px solid var(--line-strong); background: var(--surface);
  color: var(--ink-soft); font-family: var(--sans); font-size: var(--text-sm); font-weight: 600;
  line-height: 1; cursor: pointer; white-space: nowrap;
  transition: background .16s ease, color .16s ease, border-color .16s ease, box-shadow .16s ease, transform .16s ease;
}
/* hover (НЕ выбрана): нейтральный тёмный фон + белый текст — читаемо и НЕ путается с выбором */
.style-tab:not(.active):hover { background: #1E293B; border-color: #1E293B; color: #fff; }
/* АКТИВНОЕ состояние: заливка акцентом бренда, белый ПОЛУЖИРНЫЙ текст, кольцо-обводка + лёгкий подъём */
.style-tab.active {
  background: var(--navy); border-color: var(--navy); color: #fff; font-weight: 700;
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--navy), 0 4px 14px rgba(28,58,87,.30);
  transform: translateY(-1px);
}
.style-tab.active::after { content: "✓"; font-size: var(--text-xs); font-weight: 800; margin-left: 2px; opacity: .92; }
.style-tab-i { font-size: var(--text-sm); line-height: 1; }

.prompt-length-hint {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 12px; border-radius: 8px;
  background: var(--bronze-tint); border: 1px solid rgba(109,94,240,.25);
  font-size: 12px; color: var(--ink-soft); margin-top: 6px;
}
.prompt-length-hint svg { color: var(--bronze); flex: none; }
.prompt-length-hint > span { flex: 1; }
.prompt-length-count {
  font-size: 11px; color: var(--muted); flex: none; margin-left: auto;
}

.prompt-options-hint {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 12px; border-radius: 8px;
  background: var(--bronze-tint); border: 1px solid rgba(109,94,240,.25);
  font-size: 12px; color: var(--ink-soft); margin-top: 6px;
}
.prompt-options-hint svg { color: var(--bronze); flex: none; }
.prompt-options-hint > span { flex: 1; }

.format-conflict-warn {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 12px; border-radius: 8px;
  background: #FBF4E6; border: 1px solid #E8CFA0;
  font-size: 12px; color: var(--ink-soft); margin-top: 6px;
}
[data-theme="dark"] .format-conflict-warn { background: #2A2310; border-color: #4A3D1A; }
.format-conflict-warn svg { color: #B8860B; flex: none; }
[data-theme="dark"] .format-conflict-warn svg { color: #E8CFA0; }
.format-conflict-warn > span { flex: 1; }
.format-conflict-warn b { color: var(--ink); }

/* ── Поиск профессий (используется NudgeProfessionSearch в подсказке
   пустого поля) ── */
.wiz-prof-search { display: flex; flex-direction: column; gap: 8px; }

.wiz-prof-input-wrap {
  display: flex; align-items: center; gap: 8px;
  border: 1.5px solid var(--line); border-radius: 10px;
  padding: 10px 12px; background: var(--surface);
  transition: border-color 150ms;
}
.wiz-prof-input-wrap.focused { border-color: var(--navy); }

.wiz-prof-input {
  flex: 1; border: none; outline: none; background: transparent;
  font-size: var(--text-sm); color: var(--ink); font-family: var(--sans);
}
.wiz-prof-input::placeholder { color: var(--muted); }

.wiz-prof-clear {
  background: none; border: none; cursor: pointer;
  color: var(--muted); font-size: 18px; line-height: 1;
  padding: 0 2px; transition: color 130ms;
}
.wiz-prof-clear:hover { color: var(--ink); }

.wiz-prof-dropdown {
  border: 1px solid var(--glass-border); border-radius: 14px;
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  box-shadow: var(--glass-shadow);
  max-height: 280px; overflow-y: auto;
}

.wiz-prof-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 10px 14px;
  background: none; border: none; border-bottom: 1px solid var(--line-soft);
  cursor: pointer; font-family: var(--sans); text-align: left;
  transition: background 120ms;
}
.wiz-prof-item:last-child { border-bottom: none; }
.wiz-prof-item:hover,
.wiz-prof-item.selected { background: var(--navy-tint); }
.wiz-prof-item-icon  { font-size: 18px; flex: none; }
.wiz-prof-item-label { font-size: var(--text-sm); font-weight: 500; color: var(--ink); }

.wiz-prof-noresult {
  padding: 14px 16px; display: flex; flex-direction: column; gap: 10px;
}
.wiz-prof-noresult-text {
  font-size: var(--text-sm); color: var(--muted);
}
.wiz-prof-noresult-btn {
  display: inline-flex; align-items: center;
  background: var(--navy); color: #fff; border: none;
  border-radius: 8px; padding: 8px 14px;
  font-size: var(--text-sm); font-weight: 500; cursor: pointer;
  font-family: var(--sans); transition: opacity 130ms;
}
.wiz-prof-noresult-btn:hover { opacity: .88; }

.wiz-prof-selected {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; border-radius: 8px;
  background: var(--navy-tint); border: 1px solid var(--navy-line);
  font-size: var(--text-sm); color: var(--ink);
}
.wiz-prof-selected button {
  margin-left: auto; background: none; border: none;
  cursor: pointer; color: var(--muted); font-size: var(--text-sm);
}
.wiz-prof-selected button:hover { color: var(--navy); }


/* 2c. «Стиль оформления» — статические индикаторы авто-режима + ручной аккордеон */
.style-section { display: flex; flex-direction: column; gap: 8px; }
.auto-ind {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 13px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--paper);
  color: var(--ink-soft); font-family: var(--sans);
  font-size: var(--text-sm); font-weight: 600; line-height: 1.2;
  cursor: default; user-select: none;
}
.auto-ind-i { font-size: var(--text-sm); line-height: 1; flex: none; }
.style-section .style-block { margin-top: 0; }

/* 2d. Ручной блок — 4 ряда в карточках с лёгким тоновым разделением фонов */
.style-row-card {
  display: flex; flex-direction: column; gap: 9px;
  padding: 12px 13px; border-radius: 12px; border: 1px solid var(--line);
}
.style-row-card.tint-slate   { background: rgba(100,116,139,.06); }
.style-row-card.tint-neutral { background: rgba(20,19,15,.028); }
.style-row-card.tint-purple  { background: rgba(20,19,15,.028); }
.style-row-card.tint-emerald { background: rgba(20,19,15,.028); }

/* Ряды 3–4 (шрифт и размер) — спокойное АКТИВНОЕ состояние в фирменном стиле сайта:
   мягкая navy-заливка, navy-рамка и текст, без яркого кольца/свечения */
.style-tabs.tt-font .style-tab.active,
.style-tabs.tt-size .style-tab.active {
  background: var(--navy-tint); border-color: var(--navy); color: var(--navy); font-weight: 700;
  box-shadow: inset 0 0 0 1px var(--navy); transform: none;
}
.style-tabs.tt-font .style-tab.active::after,
.style-tabs.tt-size .style-tab.active::after { color: var(--navy); opacity: .85; }

/* Сводка выбранных параметров под баром «Настроить стиль и цвета вручную» —
   аккуратный текст без кнопок, с фирменными акцентами */
.style-summary {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 9px;
  padding: 10px 14px; margin-top: 7px;
  border: 1px solid var(--line); border-radius: 10px;
  background: linear-gradient(180deg, var(--paper), var(--surface));
  font-family: var(--sans);
}
.style-summary.is-auto {
  align-items: center; gap: 8px;
  border-style: dashed; border-color: var(--line-strong);
  background: rgba(28,58,87,.035);
}
.style-summary-spark {
  font-size: var(--text-sm); line-height: 1; color: var(--navy); flex: none;
  animation: ss-twinkle 2.6s ease-in-out infinite;
}
@keyframes ss-twinkle { 0%, 100% { opacity: .5; transform: scale(.92); } 50% { opacity: 1; transform: scale(1.08); } }
.style-summary-auto {
  font-size: var(--text-sm); font-weight: 500; line-height: 1.35; color: var(--ink-soft);
}
.style-summary-auto b { font-weight: 700; color: var(--navy); }
.style-summary-lbl {
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); flex: none;
}
.style-summary-text {
  display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 3px 7px;
  font-size: var(--text-sm); line-height: 1.4;
}
.ss-item { display: inline-flex; align-items: baseline; gap: 5px; }
.ss-dot {
  width: 11px; height: 11px; border-radius: 4px; flex: none; align-self: center;
  border: 1px solid rgba(0,0,0,.18); box-shadow: inset 0 0 0 1px rgba(255,255,255,.3);
}
.ss-ic { font-size: var(--text-xs); line-height: 1; align-self: center; }
.ss-cat { color: var(--muted); font-weight: 600; font-size: var(--text-xs); }
.ss-val { color: var(--ink); font-weight: 700; }
.ss-sep { color: var(--line-strong); margin-left: 4px; font-weight: 400; }

/* Палитра цветов — КВАДРАТНЫЕ плитки в стиле сайта с подписями */
.color-grid { display: flex; flex-wrap: wrap; gap: 10px 11px; align-items: flex-start; margin-bottom: 2px; }
.color-swatch {
  display: flex; flex-direction: column; align-items: stretch; gap: 6px;
  width: 60px; padding: 0; border: none; background: none; cursor: pointer; font-family: var(--sans);
}
.color-swatch-chip {
  display: block; width: 100%; aspect-ratio: 1 / 1; border-radius: 8px;
  border: 1.5px solid var(--line-strong);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.06);
  transition: box-shadow .14s, border-color .14s, transform .12s;
}
.color-swatch-name {
  font-size: 10.5px; font-weight: 600; line-height: 1.2; color: var(--ink-soft);
  text-align: center; letter-spacing: .005em;
}
.color-swatch:hover .color-swatch-chip { transform: translateY(-1px); box-shadow: inset 0 0 0 1px rgba(0,0,0,.07), 0 3px 8px rgba(0,0,0,.12); }
.color-swatch.on .color-swatch-chip { border-color: var(--navy); box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--navy); }
.color-swatch.on .color-swatch-name { color: var(--navy); font-weight: 700; }

/* «Свой цвет» — первая плитка, визуально выделена (пунктир + радуга + иконка) */
.color-swatch-custom { position: relative; }
.color-swatch-custom .color-swatch-chip-custom {
  position: relative; display: grid; place-items: center; overflow: hidden;
  border-style: dashed; border-width: 2px; border-color: var(--navy);
  background: conic-gradient(from .25turn, #e64a19, #f57f17, #2e7d32, #00838f, #1565c0, #6a1b9a, #c2185b, #e64a19);
}
.color-swatch-custom .color-swatch-custom-i {
  font-size: 16px; line-height: 1; filter: drop-shadow(0 0 2px rgba(255,255,255,.95)); pointer-events: none;
}
.color-swatch-custom input[type="color"] {
  position: absolute; inset: 0; opacity: 0; cursor: pointer; border: none; padding: 0;
}
.color-swatch-custom .color-swatch-name { color: var(--navy); }
.color-swatch-custom.on .color-swatch-chip-custom { border-style: solid; }

/* Палитра цветов (legacy) — круглые плитки + кнопка сброса */
.color-row { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; }
.color-dot {
  width: 30px; height: 30px; border-radius: 999px; padding: 0;
  border: 2px solid #fff; cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.14), 0 1px 3px rgba(0,0,0,.10);
  transition: transform .12s, box-shadow .12s;
}
.color-dot:hover { transform: scale(1.09); }
/* выбранный цвет: жирная контрастная двойная обводка (белый зазор + кольцо бренда) + увеличение */
.color-dot.on { transform: scale(1.14); box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px var(--navy), inset 0 0 0 1px rgba(0,0,0,.18); }
.color-dot.on:hover { transform: scale(1.14); }
.color-dot-custom {
  position: relative; overflow: hidden; display: grid; place-items: center;
  background: conic-gradient(from .25turn, #e64a19, #f57f17, #2e7d32, #00838f, #1565c0, #6a1b9a, #c2185b, #e64a19);
}
.color-dot-custom .color-dot-custom-i { font-size: var(--text-sm); line-height: 1; filter: drop-shadow(0 0 2px rgba(255,255,255,.9)); pointer-events: none; }
.color-dot-custom input[type="color"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; border: none; padding: 0; }
.color-reset {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 13px; border-radius: 999px;
  border: 1px dashed var(--line-strong); background: var(--surface);
  color: var(--ink-soft); font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; font-family: var(--sans); transition: all .15s;
}
.color-reset:hover { border-color: var(--navy); color: var(--navy); }
.color-reset.on { border-style: solid; border-color: var(--navy); background: var(--navy); color: #fff; font-weight: 700; box-shadow: 0 3px 12px rgba(28,58,87,.26); }

/* 4. Виджет баланса кредитов */
.credit-balance-badge {
  display: flex; align-items: center; gap: 4px;
  padding: 4px 10px; border-radius: 20px;
  background: var(--navy-tint); border: 1px solid var(--navy-line);
  font-family: var(--sans); font-size: var(--text-xs); font-weight: 700; color: var(--navy);
  cursor: pointer; transition: background .15s, transform .12s;
}
.credit-balance-badge:hover { background: var(--navy-line); transform: translateY(-1px); }

/* ===== Живой оверлей «нейросеть рисует» в пане результатов =====
   Показывается поверх hero-превью, пока реальный запрос к серверу в полёте
   (apiBusy). Висит РОВНО до реального ответа (родитель снимает apiBusy) —
   без фейкового таймера, см. GenLiveOverlay в studio-extras.jsx. */
.gen-live-content {
  filter: blur(2.5px) saturate(.75);
  transition: filter .4s var(--ease);
}
.gen-live::after {
  /* бегущее свечение по периметру превью */
  content: ""; position: absolute; inset: 0; z-index: 4; pointer-events: none;
  border-radius: inherit;
  border: 2px solid transparent;
  background: linear-gradient(var(--paper-2), var(--paper-2)) padding-box,
    conic-gradient(from var(--gen-a, 0deg),
      transparent 0 40%, rgba(28,58,87,.9) 50%, transparent 60% 100%) border-box;
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: gen-ring 2.6s linear infinite;
}
@property --gen-a { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
@keyframes gen-ring { to { --gen-a: 360deg; } }
@media (prefers-reduced-motion: reduce) {
  .gen-live::after { animation: none; }
  .gen-live-content { filter: none; }
}

/* ── "Результат предыдущего запроса" (studio-extras.jsx StaleResultBadge,
   запрос владельца 2026-07-31) — поля описания/вложения пусты, холст
   сознательно НЕ сбрасывается (см. "Оставлять результат на экране"), но
   старый макет должен явно читаться как черновик из прошлого запроса, не
   как то, что сгенерируется сейчас. Просили явно "чтобы было хорошо
   видно" — сплошная полоса на всю ширину + заметное затемнение, не тихая
   плашка в углу. z-index выше .gen-live::after (4), но ниже маркера
   иконки (5) и модалок. */
.stale-result-dim {
  position: absolute; inset: 0; z-index: 4; pointer-events: none;
  background: rgba(10, 12, 16, .38);
}
.stale-result-banner {
  position: absolute; top: 0; left: 0; right: 0; z-index: 5; pointer-events: none;
  display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap;
  padding: 11px 16px; text-align: center;
  background: var(--navy); color: #fff;
  font-size: 13.5px; font-weight: 700; letter-spacing: .01em;
  box-shadow: 0 2px 10px rgba(0,0,0,.25);
}
.stale-result-banner svg { flex: none; opacity: .9; }
.stale-result-banner-sub {
  font-size: 12px; font-weight: 500; opacity: .82;
  padding-left: 8px; margin-left: 2px; border-left: 1px solid rgba(255,255,255,.3);
}
[data-theme="dark"] .stale-result-dim { background: rgba(0, 0, 0, .5); }

/* ═══════════════════════════════════════════════════════════════════════
   AI LIVE CANVAS BUILDER — «холст, на котором ИИ прямо сейчас собирает
   макет» (статус + сканер + прогресс). Полностью непрозрачная светлая
   панель поверх заблюренной заглушки (.gen-live-content выше остаётся,
   просто скрыт под панелью — трогать не нужно). Свой, отдельный от общей
   navy/bronze темы, акцент teal/violet.
   Раньше здесь была имитация "ИИ расставляет блоки" (лог мыслей + магнитно
   появляющиеся элементы холста) — заменена на сканер в стиле апскейлера
   (.up-scan-wrap/.up-scan-img/.up-scan-beam, см. styles.css у upscaler.jsx):
   сам макет полупрозрачным, сверху вниз бежит световая полоса, сначала
   лицо целиком, потом оборот (см. GenLiveOverlay в studio-extras.jsx).
   Шрифты — везде var(--sans) сайта вместо HUD-моноширинного, статусная
   подпись и процент — полужирным. ═══════════════════════════════════════ */
/* Обёртка плавного исчезновения (FadeOutOverlay в studio-extras.jsx) — сам
   .ai-canvas-builder ниже уже position:absolute;inset:0, обёртке лишь нужно
   не мешать этому и переключать opacity по классу .acb-fade-out. Длительность
   должна совпадать с ACB_FADE_MS в JS. */
.acb-fade { position: absolute; inset: 0; opacity: 1; transition: opacity .65s ease; }
.acb-fade-out { opacity: 0; }

.ai-canvas-builder {
  --acb-teal: #14B8A6;
  --acb-violet: #8B5CF6;
  position: absolute; inset: 0; z-index: 5; pointer-events: none;
  overflow: hidden; border-radius: inherit;
  background: #FAFAF9;
  display: flex; flex-direction: column; gap: 10px;
  padding: 14px 0;
  font-size: 15px; /* база для em-размеров ниже — общий рычаг "крупнее/мельче" */
}

/* Статусная подпись — своей строкой НАД сканером (запрос владельца
   2026-08-05, второй заход: "вынести сверху за поле с анимацией" — первая
   версия была оверлеем поверх точек/луча, не подошло). ×1.33 от исходных
   .86em (был поднят до 1.72em "×2", потом попросили "в 1.5 раза меньше
   [тек ущего]" — 1.72/1.5 ≈ 1.15em). */
.acb-scan-label {
  flex: none; text-align: center; font: 700 1.15em/1.25 var(--sans); color: #2E2D29;
  padding: 0 14px;
}

/* — Сканер: тот же .up-scan-wrap/.up-scan-img/.up-scan-beam, что у
   апскейлера (см. styles.css рядом с ним). Раньше вокруг стояла своя рамка/
   скруглённый фон (border + border-radius:10px) с горизонтальным padding
   родителя (.ai-canvas-builder) — полоса сканера (.up-scan-beam, width:100%
   от НЕГО) утыкалась в эту внутреннюю рамку, а не в реальный левый/правый
   край макета, и выглядела "запертой" в маленьком прямоугольнике внутри
   панели. Убираем и рамку/скругление, и боковой padding у самого враппера
   (см. .acb-canvas-wrap) — сканер теперь идёт от края до края макета. ── */
.acb-canvas-wrap { flex: 1; min-height: 0; display: flex; }
.acb-scan-wrap {
  position: relative; flex: 1; overflow: hidden;
  background: #fff;
}
/* ── Нейтральный макет-заглушка на время сканирования (запрос владельца
   2026-08-05) — раньше здесь рисовался НАСТОЯЩИЙ стилизованный BizCard
   (renderers.navy по умолчанию — тёмно-синий фон, золотая монограмма) или
   category-мок (VerticalCardMock/PosterMock/...): поверх скелетона
   EmptyCanvas получалась вторая, визуально совсем другая "карточка" —
   путаница "что это вообще такое". Простой силуэт (чип-логотип + серые
   полосы) одинаково нейтрален для любого формата, не завязан на пропорцию
   визитки — вся прежняя ResizeObserver/CARD_RATIO/fit-подгонка под BizCard
   в GenLiveOverlay вместе с ним убрана как ненужная. ── */
.acb-scan-mock {
  position: absolute; inset: 0;
  padding: 11% 12%;
  display: flex; flex-direction: column; justify-content: space-between;
  transition: opacity .4s linear;
}
.acb-scan-mock .asm-logo { width: 11%; aspect-ratio: 1 / 1; max-width: 34px; border-radius: 22%; background: var(--navy); flex: none; }
.acb-scan-mock .asm-lines { display: flex; flex-direction: column; gap: 8px; }
.acb-scan-mock .asm-line { height: 8px; border-radius: 4px; background: #E4E1D8; }
.acb-scan-mock .asm-contact { display: flex; flex-direction: column; gap: 6px; }
.acb-scan-mock .asm-contact .asm-line { height: 6px; background: #EDEAE1; }

/* ── Прогресс: тёмная "консоль" вместо светлой полоски — контраст с
   остальной светлой панелью читается технологичнее (HUD-приём). Бегущий
   блик внутри заливки (::after, фон шире контейнера + анимация background-
   position — устойчивый к муару способ "перемещать" блик, в отличие от
   анимации transform/width на тонких элементах), засечки-сегменты по
   числу тиков. Не тронуто с прошлой версии по запросу. ── */
.acb-progress { flex: none; display: flex; align-items: center; gap: 10px; padding: 0 14px; }
.acb-progress-track {
  /* потолще (запрос владельца 2026-08-05) — было 8px */
  position: relative; flex: 1; height: 14px; border-radius: 30px; overflow: hidden;
  background: #22211D;
}
.acb-progress-bar {
  position: relative; height: 100%; border-radius: 30px;
  background: linear-gradient(90deg, var(--acb-teal), var(--acb-violet));
  transition: width .5s var(--ease);
  overflow: hidden;
}
/* Оборот — та же пара цветов, но зеркальный градиент (фиолетовый →
   бирюзовый) — лёгкое, но узнаваемое с одного взгляда отличие "это другая
   сторона", а не продублированная копия анимации лица. */
.acb-back .acb-progress-bar { background: linear-gradient(90deg, var(--acb-violet), var(--acb-teal)); }
.acb-progress-bar::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 20%, rgba(255,255,255,.55) 40%, transparent 60%);
  background-size: 220% 100%;
  animation: acbScan 1.6s linear infinite;
}
.acb-progress-ticks { position: absolute; inset: 0; display: flex; }
.acb-progress-tick { flex: 1; border-right: 1px solid rgba(250,250,249,.55); }
.acb-progress-tick:last-child { border-right: none; }
.acb-progress-pct {
  /* ×2, потом "в 1.5 раза меньше [текущего]" (запрос владельца 2026-08-05,
     два захода) от исходных .8em — 1.6/1.5 ≈ 1.07em. min-width в em
     считается от собственного font-size элемента, масштабируется вместе */
  flex: none; min-width: 2.4em; text-align: right;
  font: 700 1.07em/1 var(--sans); color: #2E2D29;
  font-variant-numeric: tabular-nums;
}

@keyframes acbScan { from { background-position: 120% 0; } to { background-position: -120% 0; } }
@media (prefers-reduced-motion: reduce) {
  .acb-progress-bar::after { animation: none; }
}

/* ── .acb-flow — та же "рамка ожидания" (текст сверху / сканер / тёмная
   полоса-отсчёт снизу), но в ПОТОКЕ документа, а не absolute-оверлеем:
   инструменты Студии обработки (ProcessingScanOverlay в studio-extras.jsx —
   апскейл/адаптация/печать) кладут её на место своего превью, высоту задаёт
   сам контент (картинка либо aspect-ratio целевого формата), а не рамка
   родителя, как у GenLiveOverlay в студии генерации. ── */
.ai-canvas-builder.acb-flow { position: relative; inset: auto; width: 100%; }
.acb-flow .up-scan-img { display: block; width: 100%; }
/* .acb-fit — пропорция задана целевым форматом (адаптер: итог ≠ исходник):
   бокс держит aspect-ratio, картинка вписывается целиком (contain, не
   cover — макет не обрезается, см. CLAUDE.md). */
.acb-flow .acb-scan-wrap.acb-fit .up-scan-img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
}

/* ── .acb-overlay-hud — вариант для сканера-ОВЕРЛЕЯ поверх уже видимого
   превью (printPrep "Исправляем…", up-scan-overlay): подпись сверху на
   полупрозрачной пилюле (под ней живая картинка — без подложки текст
   нечитаем) и та же тёмная полоса-отсчёт снизу. ── */
.up-scan-overlay.acb-overlay-hud {
  /* Копия переменных градиента с .ai-canvas-builder — этот оверлей НЕ
     внутри него (лежит поверх обычного превью printPrep), без них
     var(--acb-teal/violet) в .acb-progress-bar не резолвится и полоса
     осталась бы прозрачной. */
  --acb-teal: #14B8A6;
  --acb-violet: #8B5CF6;
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 10px 0;
}
.acb-overlay-hud .acb-scan-label {
  align-self: center; background: rgba(255,255,255,.85); border-radius: 999px;
  padding: 4px 14px; position: relative; z-index: 3;
}
.acb-overlay-hud .acb-progress { position: relative; z-index: 3; }
/* Правка по маске в ожидании ответа ИИ — тот же сканер (.up-scan-beam +
   ScanSparkle), что и везде на сайте (см. .up-scan-overlay в styles.css
   рядом с апскейлером), просто под старым именем класса ради минимальной
   правки JSX. Без border-radius/opacity — луч идёт прямо по видимому
   макету от края до края бокса, а не под затемняющей плашкой. */
.region-busy-shimmer {
  position: absolute; inset: 0; overflow: hidden; z-index: 6;
  pointer-events: none; container-type: inline-size;
}
@media (prefers-reduced-motion: reduce) {
  .region-edit-dot { animation: none; }
}
.region-clear {
  display: inline-flex; align-items: center; gap: 6px;
  height: 30px; padding: 0 11px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--paper);
  color: var(--muted); font-family: var(--sans); font-size: var(--text-xs); font-weight: 600;
  cursor: pointer; transition: all .15s;
}
.region-clear:hover:not(:disabled) { border-color: var(--line-strong); color: var(--ink); }
.region-clear:disabled { opacity: .45; cursor: default; }

.region-stage { position: relative; flex: none; }
.region-canvas {
  /* border-radius: 0 (было 14px, см. .up-img-wrap выше) — сам canvas уже
     получает inline borderRadius: previewRadius=0 (studio-extras.jsx), это
     правило и раньше не давало видимого скругления, правим ради
     согласованности, а не отменяем реальный эффект. */
  position: absolute; inset: 0; z-index: 3;
  cursor: crosshair; border-radius: 0; touch-action: none;
}
/* Клавиатурный фокус на холсте маски (Tab доступен, см. tabIndex в
   studio-extras.jsx) — без этого фокус на canvas визуально не отличим. */
.region-canvas:focus-visible {
  outline: 3px solid var(--bronze); outline-offset: -3px;
}
/* Раньше bottom:12px клал плашку ПОВЕРХ нижнего края макета (жалоба
   2026-07-23: подсказка "Область отмечена" закрывала собой сам рисунок) —
   top:100% + margin-top выносит её НИЖЕ .region-stage (тот без overflow:
   hidden, так что дочерний элемент за пределами его высоты просто рисуется
   ниже, а не обрезается), с небольшим отступом от макета. */
.region-overlay-hint {
  position: absolute; left: 50%; top: 100%; transform: translateX(-50%);
  margin-top: 10px;
  z-index: 4; pointer-events: none;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px; border-radius: 40px; white-space: nowrap;
  background: rgba(20,19,15,.78); color: #fff;
  font-size: var(--text-xs); font-weight: 600; backdrop-filter: blur(3px);
  transition: background .2s, color .2s;
}
/* плашка-трансформер: позитивное состояние после нанесения маски */
.region-overlay-hint.marked {
  background: rgba(46,110,78,.92); color: #fff;
  box-shadow: 0 2px 10px rgba(46,110,78,.35);
}

/* ===== Контекстный сайдбар локальной правки (портал в левую панель) ===== */
.region-sb {
  position: absolute; inset: 0; z-index: 20;
  display: flex; flex-direction: column;
  background: var(--surface);
  animation: region-sb-in .26s var(--ease) both;
}
@keyframes region-sb-in {
  from { opacity: 0; transform: translateX(-10px); }
  to   { opacity: 1; transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) { .region-sb { animation: none; } }
.region-sb-scroll {
  flex: 1; overflow-y: auto; min-height: 0;
  padding: 26px 22px 24px;
}
/* шаговый индикатор */
.region-sb-steps {
  display: flex; align-items: center; gap: 10px; margin-bottom: 22px;
}
.region-sb-step {
  /* было var(--faint) — контраст ~2.5:1, ниже WCAG AA даже с учётом
     bold-начертания (13px bold не дотягивает до порога "крупного текста"
     ~18.7px bold, нужны все 4.5:1). var(--muted) всё ещё заметно бледнее
     активного (bronze) и завершённого (good) шага, иерархия сохраняется. */
  display: inline-flex; align-items: center; gap: 7px;
  font-size: var(--text-sm); font-weight: 700; color: var(--muted);
  white-space: nowrap;
}
.region-sb-step.on { color: var(--bronze); }
.region-sb-step.done { color: var(--good); }
.region-sb-step-n {
  display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%;
  font-size: var(--text-xs); font-weight: 800;
  background: var(--paper-2); color: var(--muted);
  border: 1.5px solid var(--line-strong);
}
.region-sb-step.on .region-sb-step-n {
  background: var(--bronze); color: #fff; border-color: var(--bronze);
}
.region-sb-step.done .region-sb-step-n {
  background: var(--good); color: #fff; border-color: var(--good);
}
.region-sb-step-sep { flex: 1; height: 2px; border-radius: 2px; background: var(--line); }

.region-sb-title {
  font-size: var(--text-lg); line-height: 1.15; letter-spacing: -.01em;
  color: var(--ink); margin: 0 0 8px;
}
.region-sb-sub {
  font-size: var(--text-sm); line-height: 1.5; color: var(--ink-soft); margin: 0 0 16px;
}
.region-sb-sub .region-mask-tags { margin: 0 0 0 4px; vertical-align: middle; }
.region-sb-sub-kbd { margin-top: -10px; }
.region-kbd {
  display: inline-block; padding: 1px 6px; border-radius: 5px;
  border: 1px solid var(--line-strong); background: var(--paper-2);
  font: 700 .85em/1.6 var(--sans); color: var(--ink-soft);
}

/* контрастное поле ввода */
.region-sb-field { position: relative; }
.region-sb-field .region-textarea {
  min-height: 110px; font-size: var(--text-sm); line-height: 1.5;
  border-width: 2px; border-color: var(--bronze);
  background: var(--surface); color: var(--ink);
  box-shadow: 0 0 0 4px var(--bronze-tint);
}
.region-sb-field .region-textarea::placeholder { color: var(--muted); }
.region-sb-field .region-textarea:focus {
  border-color: var(--navy);
  box-shadow: 0 0 0 4px var(--navy-tint);
}

/* Готовые инструкции для "Точечная правка", сгруппированные по категориям
   (запрос 2026-07-29) — заголовок группы мельче и тише самих чипов, чтобы
   не спорить с ними за внимание. */
.region-sb-chip-group { margin-top: 14px; }
.region-sb-chip-group + .region-sb-chip-group { margin-top: 10px; }
.region-sb-chip-group-title {
  display: block; font-size: var(--text-xs); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 6px;
}
.region-sb-chips {
  display: flex; flex-wrap: wrap; gap: 8px;
}
.region-sb-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 12px; border-radius: 999px;
  border: 1px solid var(--line-strong); background: var(--paper-2);
  font-size: var(--text-sm); font-weight: 600; color: var(--ink-soft);
  cursor: pointer; transition: all .14s;
}
.region-sb-chip:hover {
  border-color: var(--bronze); color: var(--bronze);
  background: var(--bronze-tint);
}
.region-sb-chip-i { font-size: var(--text-sm); line-height: 1; }

.region-sb-clear {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 18px; padding: 0;
  background: none; border: none; cursor: pointer;
  font-size: var(--text-sm); font-weight: 600; color: var(--muted);
  transition: color .14s;
}
.region-sb-clear:hover { color: var(--ink); }
.region-sb-clear:disabled { opacity: .45; cursor: default; }

/* карточка-подсказка для шага 1 */
.region-sb-hint-card {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px; border-radius: 12px;
  border: 1px dashed var(--bronze); background: var(--bronze-tint);
  font-size: var(--text-sm); font-weight: 600; color: var(--bronze);
}

/* подвал с действиями */
.region-sb-foot {
  flex: none; border-top: 1px solid var(--line);
  padding: 16px 22px 20px; background: var(--surface);
}
.region-sb-credit {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--text-xs); color: var(--muted); margin: 0 0 12px;
}
.region-sb-actions { display: grid; grid-template-columns: auto 1fr; gap: 10px; }
.region-sb-actions .btn { height: 46px; font-size: var(--text-sm); }

.region-instruct {
  margin-top: 16px; padding: 16px;
  border: 1px solid rgba(109,94,240,.32); border-radius: 14px;
  background: var(--bronze-tint);
  animation: region-open .25s var(--ease) both;
}
@keyframes region-open {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .region-instruct { animation: none; } }
.region-instruct-label {
  display: flex; align-items: center; gap: 7px;
  font-size: var(--text-sm); font-weight: 700; color: var(--bronze); margin-bottom: 11px;
}
.region-field { position: relative; }
/* padding-right оставляет зазор под .region-mic (абсолютно спозиционирован
   поверх поля, top:9px right:9px) — раньше 48px (37 из них — реальная
   ширина+отступ иконки) почти впритык, и на конкретных ширинах сайдбара
   плейсхолдер успевал долистать слово прямо под кнопку до переноса строки,
   потому что резерв не учитывал border-width:2px у .region-sb-field
   .region-textarea (жалоба дизайн-аудита: "Измени номер телефон[а]"
   обрезалось под иконкой микрофона). Увеличенный запас с обеих сторон. */
.region-textarea { width: 100%; resize: vertical; padding-right: 58px; }
.region-mic {
  position: absolute; top: 9px; right: 9px;
  width: 34px; height: 34px; border-radius: 9px;
  display: grid; place-items: center;
  border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--muted); cursor: pointer; transition: all .15s; z-index: 2;
}
.region-mic:hover { border-color: var(--bronze); color: var(--bronze); }
.region-mic.active {
  background: #DC2626; border-color: #DC2626; color: #fff;
  animation: mic-pulse .8s ease-in-out infinite;
}
.region-mic.processing { border-color: var(--bronze); color: var(--bronze); cursor: not-allowed; animation: mic-processing-pulse 1s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .region-mic.active, .region-mic.processing { animation: none; } }
.region-actions {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 14px;
}
.region-actions-right { display: flex; align-items: center; gap: 10px; }

/* двусторонняя локальная правка — пара холстов лица и оборота */
.region-pair { display: flex; gap: 18px; }
.region-pair-side { flex-direction: row; align-items: flex-start; justify-content: center; flex-wrap: wrap; }
.region-pair-stack { flex-direction: column; align-items: center; }
.region-side { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.region-overlay-hint-sm { font-size: var(--text-xs); padding: 5px 10px; margin-top: 6px; }

/* сервисная подпись о стоимости правки */
.region-credit-note {
  font-size: var(--text-xs); color: var(--muted); margin: 9px 0 0; line-height: 1.4;
}
.region-balance-warn {
  margin-top: 9px; padding: 9px 11px; border-radius: 10px;
  background: #FBECEC; border: 1px solid #F2C9C9;
  color: #B23B3B; font-size: var(--text-xs); font-weight: 600; line-height: 1.4;
}
[data-theme="dark"] .region-balance-warn { background: #2A1414; border-color: #5A2424; color: #E89090; }

/* теги закрашенных сторон в шапке панели правки */
.region-mask-tags { display: inline-flex; gap: 6px; margin-left: auto; }
.region-mask-tag {
  font-size: 10.5px; font-weight: 700; letter-spacing: .01em;
  padding: 2px 8px; border-radius: 999px;
  background: rgba(109,94,240,.16); color: var(--bronze);
}

/* quick improvements */
.quick-improve {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 14px 16px; border-radius: 12px;
  background: var(--bronze-tint); border: 1px solid #E8D9BC;
}
[data-theme="dark"] .quick-improve { border-color: #4A3D1A; }
.qi-label { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm); font-weight: 600; color: var(--ink); }

/* audit + refine columns */
.audit-refine { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
.audit-card, .refine-card {
  border: 1px solid var(--line); border-radius: 14px; padding: 18px;
  background: var(--surface);
}
.audit-head { font-weight: 700; font-size: var(--text-base); color: var(--ink); display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.audit-rows { display: flex; flex-direction: column; gap: 12px; }
.audit-row { display: grid; grid-template-columns: 1.4fr 1.6fr auto auto; gap: 12px; align-items: center; }
.audit-k { font-size: var(--text-sm); color: var(--ink-soft); }
.audit-bar { height: 6px; border-radius: 4px; background: var(--paper-2); overflow: hidden; }
.audit-bar i { display: block; height: 100%; background: linear-gradient(90deg, var(--navy), #2C5A85); border-radius: 4px; }
.audit-v { font-size: var(--text-sm); font-weight: 600; color: var(--ink); text-align: right; white-space: nowrap; }
.audit-badge { font-size: var(--text-xs); font-weight: 700; padding: 3px 9px; border-radius: 40px; white-space: nowrap; }
.audit-badge.ok { background: #EAF5EE; color: var(--good); }
.audit-badge.mid { background: var(--navy-tint); color: var(--navy); }
[data-theme="dark"] .audit-badge.ok { background: #16241C; }
.audit-rec {
  display: flex; align-items: flex-start; gap: 8px; margin-top: 16px;
  padding: 11px 13px; border-radius: 10px; background: var(--bronze-tint);
  font-size: var(--text-sm); color: var(--ink-soft); line-height: 1.45;
}

/* refine card */
.refine-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.btn.on, .btn-light.on { border-color: var(--navy); background: var(--navy-tint); color: var(--navy); }

/* version history */
.hist-list {
  margin-top: 14px; border: 1px solid var(--line); border-radius: 11px;
  padding: 6px; background: var(--paper-2); max-height: 220px; overflow-y: auto;
}
.hist-item { display: flex; align-items: center; gap: 11px; padding: 9px 10px; border-radius: 8px; }
.hist-item.current { background: var(--surface); }
.hist-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line-strong); flex: none; }
.hist-item.current .hist-dot { background: var(--navy); }
.hist-body { flex: 1; min-width: 0; }
.hist-title { font-size: var(--text-sm); font-weight: 700; color: var(--ink); }
.hist-desc { font-size: var(--text-xs); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hist-revert {
  font-size: var(--text-xs); font-weight: 600; color: var(--navy); cursor: pointer;
  background: none; border: none; font-family: var(--sans); flex: none;
}
.hist-revert:hover { text-decoration: underline; }
.hist-cur { font-size: var(--text-xs); color: var(--muted); flex: none; }

/* QR dropdown */
.qr-wrap { position: relative; }
.qr-menu {
  position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 30;
  min-width: 220px; background: var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border);
  border-radius: 16px; padding: 6px; box-shadow: var(--glass-shadow);
}
.qr-opt {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; border-radius: 8px; border: none; background: none;
  color: var(--ink-soft); font-size: var(--text-sm); font-weight: 500; cursor: pointer;
  font-family: var(--sans); transition: background .12s; text-align: left;
}
.qr-opt:hover { background: var(--paper-2); color: var(--ink); }
.qr-opt.sugg { color: var(--navy); }
.qr-sugg-tag {
  margin-left: auto; font-size: var(--text-xs); font-weight: 700; color: var(--bronze);
  background: var(--bronze-tint); padding: 2px 7px; border-radius: 40px;
}

/* responsive */
@media (max-width: 1100px) {
  .audit-refine { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  .big-tabs { grid-template-columns: 1fr; }
  .side-seg { max-width: none; }
  .check-grid { grid-template-columns: 1fr; }
  .enh-grid { grid-template-columns: 1fr; }
  .enh-packs { grid-template-columns: 1fr; }
}

/* ===================== ENHANCE MODAL ===================== */
/* Сплошной --surface, не стекло (запрос владельца 2026-08-03) — та же
   правка, что у .crop-modal/.all-modal/.auth-modal: карточка на
   затемнённой .all-modal-bg (см. image-enhancer.jsx) просвечивала тёмным
   и читалась грязно-серой. */
.enh-modal {
  width: min(940px, 100%);
  max-height: 92vh; overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px; box-shadow: var(--glass-shadow);
  padding: 24px 26px 26px;
}
.enh-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 18px; cursor: grab; }
.enh-head button { cursor: auto; }
.enh-head h2 { font-family: var(--display); font-size: 23px; letter-spacing: -.01em; margin: 0; }
.enh-credits {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 13px; border-radius: 40px;
  background: var(--bronze-tint); color: var(--bronze-ink, var(--ink));
  font-size: var(--text-sm); font-weight: 700; white-space: nowrap;
}
.enh-warn {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 15px; border-radius: 12px; margin-bottom: 18px;
  background: rgba(201,163,92,.12); border: 1px solid rgba(201,163,92,.4);
  color: var(--ink-soft); font-size: var(--text-sm); line-height: 1.5;
}
.enh-warn svg { color: var(--bronze); flex: none; margin-top: 1px; }

/* Regen bar */
.regen-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 16px; border-radius: 12px;
  background: var(--bronze-tint); border: 1px solid rgba(140,124,247,.35);
}
.regen-hint { font-size: var(--text-sm); color: var(--bronze); font-weight: 600; }
.regen-btn { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }

/* Top bar row — tabs + history controls */
.top-bar-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: nowrap;
}
.top-bar-row .side-seg { flex: 1; min-width: 0; }

/* History popover */
.hist-pop {
  position: fixed; z-index: 9999;
  min-width: 240px; background: var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  border: 1px solid var(--glass-border);
  border-radius: 16px; box-shadow: var(--glass-shadow); padding: 10px 0;
}
.hist-pop-title {
  font-size: var(--text-xs); font-weight: 700; color: var(--muted); letter-spacing: .06em;
  text-transform: uppercase; padding: 4px 14px 10px;
}
.hist-item {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 14px; transition: background .12s;
}
.hist-item:hover { background: var(--paper-2); }
.hist-idx { font-size: var(--text-xs); font-weight: 700; color: var(--muted); width: 22px; flex: none; }
.hist-lbl { flex: 1; font-size: var(--text-sm); color: var(--ink); min-width: 0; }
.hist-revert { font-size: var(--text-xs); font-weight: 600; color: var(--navy); background: none; border: none; cursor: pointer; padding: 0; flex: none; }
.hist-cur { font-size: var(--text-xs); color: var(--muted); flex: none; }

.enh-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.enh-card {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 15px;
  background: var(--surface); overflow: hidden;
}
.enh-card.premium { border-color: var(--navy); box-shadow: 0 0 0 2px rgba(28,58,87,.1); }
.enh-card-top {
  display: flex; align-items: center; justify-content: space-between;
  padding: 13px 15px 11px;
}
.enh-card-name { font-weight: 700; font-size: var(--text-base); color: var(--ink); }
.enh-tag {
  font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 40px;
  text-transform: uppercase; letter-spacing: .03em;
}
.enh-tag.muted { background: var(--paper-2); color: var(--muted); }
.enh-tag.good  { background: rgba(31,138,91,.12); color: var(--good); }
.enh-tag.navy  { background: var(--navy-tint); color: var(--navy); }
.enh-diff-after { background: #F0FAF4; }
[data-theme="dark"] .enh-diff-after { background: #16241C; }
.enh-thumb {
  position: relative; aspect-ratio: 16/10; margin: 0 15px;
  border-radius: 11px; overflow: hidden; background: var(--paper-2);
  border: 1px solid var(--line);
  background-image:
    linear-gradient(45deg, #00000008 25%, transparent 25%, transparent 75%, #00000008 75%),
    linear-gradient(45deg, #00000008 25%, transparent 25%, transparent 75%, #00000008 75%);
  background-size: 18px 18px; background-position: 0 0, 9px 9px;
}
.enh-thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
.enh-thumb-load { width: 100%; height: 100%; display: grid; place-items: center; }
.enh-thumb-load .spin {
  width: 28px; height: 28px; border-radius: 999px;
  border: 3px solid var(--line); border-top-color: var(--navy);
}
.enh-scale-chip {
  position: absolute; top: 8px; right: 8px;
  background: var(--navy); color: #fff; font-size: var(--text-xs); font-weight: 700;
  padding: 3px 9px; border-radius: 7px;
}
.enh-meta { padding: 12px 15px 4px; flex: 1; }
.enh-size { font-size: var(--text-sm); font-weight: 700; color: var(--ink); }
.enh-mult { font-size: var(--text-xs); font-weight: 500; color: var(--bronze); }
.enh-desc { font-size: var(--text-sm); color: var(--muted); margin: 5px 0 0; line-height: 1.45; }
.enh-act { padding: 12px 15px 15px; }

.enh-buy {
  margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line);
}
.enh-buy-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.enh-packs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.enh-pack {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 18px 14px 15px; border-radius: 13px;
  border: 1px solid var(--line); background: var(--surface);
  cursor: pointer; font-family: var(--sans); transition: all .15s;
}
.enh-pack:hover { border-color: var(--navy); box-shadow: var(--sh-md); }
.enh-pack.feat { border-color: var(--bronze); }
.enh-pack-badge {
  position: absolute; top: -10px; left: 50%; transform: translateX(-50%);
  background: var(--bronze); color: #fff; font-size: var(--text-xs); font-weight: 700;
  padding: 3px 10px; border-radius: 40px; white-space: nowrap; text-transform: uppercase; letter-spacing: .03em;
}
.enh-pack-price { font-family: var(--display); font-size: 26px; font-weight: 600; color: var(--ink); }
.enh-pack-label { font-size: 13.5px; font-weight: 600; color: var(--ink-soft); }
.enh-pack-per { font-size: var(--text-xs); color: var(--muted); }
.enh-pack-cta {
  margin-top: 9px; width: 100%; text-align: center;
  background: var(--navy); color: #fff; border-radius: 8px;
  padding: 8px 0; font-size: var(--text-sm); font-weight: 600;
}
.enh-pack.feat .enh-pack-cta { background: var(--bronze); }

/* ── Color picker dropdown ── */
.cpd {
  position: absolute; top: calc(100% + 7px); left: 0; z-index: 300;
  background: var(--surface); border: 1.5px solid var(--line);
  border-radius: 14px; box-shadow: 0 8px 32px rgba(0,0,0,.14);
  padding: 12px; width: 234px;
}
.cpd-grid {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px;
}
.cpd-sw {
  width: 100%; aspect-ratio: 1; border-radius: 7px; border: none;
  cursor: pointer; transition: transform .12s, box-shadow .12s;
}
.cpd-sw:hover { transform: scale(1.2); z-index: 1; position: relative; }
.cpd-sep { height: 1px; background: var(--line); margin: 10px 0; }
.cpd-row {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
}
.cpd-lbl {
  font-size: var(--text-xs); font-weight: 500; color: var(--ink-soft); flex: 1;
}
.cpd-input {
  width: 32px; height: 28px; border: 1.5px solid var(--line);
  border-radius: 7px; cursor: pointer; padding: 2px;
  background: var(--surface); flex-shrink: 0;
}
.cpd-input::-webkit-color-swatch-wrapper { padding: 0; }
.cpd-input::-webkit-color-swatch { border: none; border-radius: 5px; }
.cpd-hex {
  font-size: var(--text-xs); font-family: var(--mono, monospace); color: var(--muted);
  letter-spacing: .04em;
}

/* ── Flyer size hints dropdown ── */
.flyer-hints {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 40;
  display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--navy-line);
  border-radius: 14px;
  padding: 10px 14px;
  box-shadow: var(--sh-md);
  /* base state for animation */
  opacity: 0;
  transform: translateY(-6px);
  animation: hintIn .2s var(--ease) .04s forwards;
}
@keyframes hintIn {
  to { opacity: 1; transform: translateY(0); }
}
.flyer-hints-label {
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; margin-right: 2px;
}
.flyer-hints-note {
  font-size: var(--text-xs); color: var(--faint); white-space: nowrap; flex-shrink: 0;
}
.flyer-hint-btn {
  display: inline-flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 5px 11px; border-radius: 10px;
  border: 1.5px solid var(--navy-line);
  background: var(--navy-tint);
  cursor: pointer; font-family: var(--sans);
  transition: background .15s var(--ease), border-color .15s, box-shadow .15s, transform .18s var(--ease);
}
.flyer-hint-btn:hover {
  background: var(--navy); border-color: var(--navy);
  box-shadow: 0 4px 14px rgba(28,58,87,.26);
  transform: translateY(-2px);
}
.flyer-hint-btn strong {
  font-size: var(--text-sm); font-weight: 700; color: var(--navy); line-height: 1.2;
  transition: color .15s;
}
.flyer-hint-btn span {
  font-size: 10.5px; color: var(--muted); line-height: 1;
  transition: color .15s;
}
.flyer-hint-btn:hover strong,
.flyer-hint-btn:hover span { color: #fff; }
[data-theme="dark"] .flyer-hints { background: var(--surface); }

/* ── Credits dots ── */
.credits-dots { display: flex; gap: 4px; align-items: center; }
.credits-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--line-strong); flex-shrink: 0; transition: background .2s; }
.credits-dot.on { background: var(--bronze); box-shadow: 0 0 4px rgba(140,124,247,.5); }

/* ── Gen meta row (under Сгенерировать) ── */
.gen-meta-row {
  display: flex; align-items: center; justify-content: center; gap: 5px; flex-wrap: wrap;
  font-size: var(--text-sm); font-weight: 500; color: var(--ink-soft); margin-top: 9px;
}
.gen-meta-sep { color: var(--line-strong); font-size: var(--text-xs); }
.cpd-pick-btn {
  font-size: var(--text-sm) !important;
  padding: 6px 13px !important;
  font-weight: 600 !important;
}

/* ── Кнопка сброса ── */
.cpd-reset-btn {
  color: var(--muted);
  gap: 5px;
}

/* ── Пульс при открытии ── */
@keyframes cpd-blink {
  0%   { box-shadow: 0 0 0 0 rgba(28,58,87,0); }
  40%  { box-shadow: 0 0 0 4px rgba(28,58,87,0.22); transform: scale(1.04); }
  100% { box-shadow: 0 0 0 0 rgba(28,58,87,0); transform: scale(1); }
}
.cpd-pulse {
  animation: cpd-blink 0.45s ease-in-out 10;
}

/* ============================================================
   HERO COMPOSER — центральное поле ввода на главной (ideogram-style),
   переиспользует компонент композера студии VoiceTextarea (см.
   Object.assign(window,...) в studio.jsx). rows=2 задаёт стартовую
   высоту в две строки, дальше — обычный autoGrow самого компонента.
   ============================================================ */
.hero-composer .textarea {
  font-size: 17px;
  padding: 20px 22px;
  padding-bottom: 60px;
  text-align: left;
}
/* Ряд чипов "+/формат/цвет/стиль/размер/модель" слева внизу — тот же
   .composer-icon-row/.composer-icon-group, что и в студии (см. landing.jsx —
   обёртка нужна явно в JSX каждого места, где рендерятся эти общие
   компоненты, сама раскладка глобальными классами в styles.css не
   восстанавливается автоматически). Когда прикреплены материалы —
   резервируем место под инлайн-чипы сверх ряда кнопок, тот же безопасный
   минимум, что у .settings-panel .composer-with-attach.has-chips. */
.hero-composer.has-chips .textarea { padding-bottom: 176px; }
/* Ряд иконок «+/формат/цвет/стиль/размер/модель» сам переносится в 2
   строки на узком экране — флекс сам подстраивается под увеличенные
   тач-кнопки (.vta-attach 30×30px на мобильном, см. выше), пересчитывать
   раскладку не нужно. Но резерв места ПОД РЯДОМ (padding-bottom самого
   textarea, задан выше как 46/60/176px) был статичным числом, рассчитанным
   на ОДНУ строку — на мобильном перенесённый в 2 строки ряд (реально
   ~80px) вылезал за пределы этого резерва, прямо туда, где на мобильной
   раскладке начинается прилипающая кнопка "Сгенерировать" (.settings-foot
   { position:sticky }, z-index выше ряда иконок, см. её мобильное правило
   ниже) — та перекрывала ряд собой. Пипетка/формат/цвет/модель/голосовой
   ввод были не просто визуально смяты, а физически некликабельны на
   телефоне (репорт владельца 2026-08-03, "мобильная версия — 80%
   пользователей"). Здесь, а не рядом с базовыми правилами выше — этот
   блок должен идти ПОСЛЕ обеих базовых деклараций (студийной и этой,
   hero) по каскаду по source order, иначе mobile-переопределение проиграет
   им при равной специфичности. Запас — с посадкой на 3 строки (щедрее
   реальных 2, чтобы не переливаться при большем числе прикреплений на ещё
   более узких экранах). */
@media (max-width: 900px) {
  .settings-panel .composer-with-attach .textarea,
  .hero-composer .textarea { padding-bottom: 130px; }
  .settings-panel .composer-with-attach.has-chips .textarea,
  .hero-composer.has-chips .textarea { padding-bottom: 260px; }
}
/* Голосовой ввод (.vta-mic) по умолчанию сидит справа внизу поля (см.
   правило в блоке "Voice textarea" выше) — здесь справа внизу ЕЩЁ и кнопка
   "Сгенерировать" (докинутая внутрь поля, как Generate у ideogram), сдвигаем
   микрофон левее с запасом, чтобы они не наезжали друг на друга. */
.hero-composer .vta-mic {
  right: 190px;
}
.hero-composer-submit {
  position: absolute;
  right: 14px;
  bottom: 12px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 40px;
  padding: 0 18px;
  border: none;
  border-radius: 999px;
  background: var(--bronze);
  color: #fff;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  transition: background .15s, transform .15s;
}
.hero-composer-submit:hover:not(:disabled) { background: var(--navy); }
.hero-composer-submit:active:not(:disabled) { transform: scale(.97); }
/* Пока не описан макет — кнопка должна ВЫГЛЯДЕТЬ неактивной, не только
   технически иметь disabled (репорт владельца 2026-08-02: кнопка была
   некликабельна, но оставалась такой же яркой заливкой — пользователь не
   понимал, почему клик ничего не делает). Приглушаем ТОТ ЖЕ фирменный цвет
   через opacity — тот же приём, что и у остальных disabled-кнопок сайта
   (.region-clear/.side-tab/.mini-zoom-btn и т.д.), а не отдельный
   бронзовый/охристый оттенок — его в текущей палитре сайта уже нет
   (замечание владельца 2026-08-02: "смотрится не в стиле сайта"). */
.hero-composer-submit:disabled { opacity: .4; cursor: not-allowed; }
/* На узких экранах ряд иконок (+/формат/цвет/стиль/размер/модель, левый
   край) и кнопка "Сгенерировать" (докинутая справа) конкурируют за одну и
   ту же полосу под полем — на ~390px кнопка физически ложится поверх
   иконок и обрезает подпись модели. Ниже 480px кнопка уезжает под поле,
   отдельной строкой — та же идея, что у полноразмерной кнопки
   «Сгенерировать» в Студии (см. .pm-cta-row в studio.jsx). ВАЖНО: кнопка
   остаётся position:absolute (а не static/в потоке) — иконки формата/
   цвета/модели ниже (.attach-wrap и соседи) тоже абсолютно спозиционированы
   с тем же bottom:12px от ЭТОГО ЖЕ .composer-with-attach — если бы кнопка
   встала в поток и вытянула контейнер, "bottom:12px" у иконок отсчитывался
   бы уже от новой, более длинной нижней границы и снова наехал бы на
   кнопку. top:100% ставит кнопку сразу под текстовым полем независимо от
   контейнера, margin-bottom на самом композере резервирует под неё место. */
@media (max-width: 480px) {
  .hero-composer-submit {
    position: absolute; top: calc(100% + 10px); right: 0; left: 0; bottom: auto;
    width: 100%; justify-content: center;
  }
  .hero-composer.composer-with-attach { margin-bottom: 58px; }
  .hero-composer .vta-mic { right: 12px; }
}

/* ============================================================
   RESPONSIVE — LANDING & SITE
   ============================================================ */
@media (max-width: 960px) {
  .how-grid   { grid-template-columns: repeat(2, 1fr) !important; }
  .feat-grid  { grid-template-columns: repeat(2, 1fr) !important; }
  .test-grid  { grid-template-columns: 1fr !important; }
  .for-all-grid { grid-template-columns: 1fr !important; }
  .pricing-grid { grid-template-columns: 1fr !important; }
  .footer-grid  { grid-template-columns: 1fr 1fr !important; }
  .nav-links   { display: none !important; }
  .nav-actions { display: none !important; }
  .nav-burger  { display: flex !important; }
  .wrap { padding-left: 20px !important; padding-right: 20px !important; }
}
@media (max-width: 640px) {
  .how-grid   { grid-template-columns: 1fr !important; }
  .feat-grid  { grid-template-columns: 1fr !important; }
  .footer-grid  { grid-template-columns: 1fr !important; }
  .all-types-grid { grid-template-columns: repeat(2, 1fr) !important; }
  .audit-refine { grid-template-columns: 1fr !important; }
  .enh-grid   { grid-template-columns: 1fr !important; }
  .enh-packs  { grid-template-columns: 1fr !important; }
  .hero-title { font-size: clamp(26px, 7vw, 46px) !important; }
  .wrap { padding-left: 16px !important; padding-right: 16px !important; }
  /* Ручной перенос строки в заголовке — под десктопный баланс ("...логотип"
     / "или карточку..."). На мобильном он не даёт браузеру самому уравнять
     строки: текст режется на два несвязанных куска, и внутри каждого
     остаются висячие одиночные слова ("логотип", "ИИ"). Прячем перенос —
     весь заголовок становится одним потоком, text-wrap:balance (см. базовое
     h1,h2,h3,h4 правило) сам подбирает более ровные строки под ширину экрана. */
  .hero-title-br { display: none; }
}

/* ============================================================
   SELECTED PARAMS MIRROR — зеркало выбранных параметров (область холста)
   ============================================================ */
.spm { max-width: 980px; margin: 0 auto 1px; container-type: inline-size; }

/* шапка: заголовок + мелкая подсказка */
.spm-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.spm-title {
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--bronze); margin: 0;
}
.spm-hint { margin: 0; font-size: var(--text-xs); line-height: 1.3; color: var(--muted); opacity: .8; }

/* b2b-группировка тегов: Параметры / На лице / На обороте */
.spm-groups { display: flex; flex-direction: column; gap: 14px; margin-bottom: 24px; }
.spm-group { display: flex; flex-direction: column; }
.spm-group + .spm-group {
  padding-top: 14px; border-top: 1px solid var(--line);
}
.spm-group-title {
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); display: block; margin-bottom: 9px;
}
/* строка тегов внутри группы */
.spm-badges { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* формат-тег показывает текст полностью (не обрезается многоточием) */
.spb.spb--full { max-width: none; }
.spb.spb--full .spb-text { overflow: visible; text-overflow: clip; white-space: nowrap; }

/* hover-увеличение живых миниатюр загруженных файлов (×4) — только в группах
   «На лице» / «На обороте». Картинка всплывает поверх интерфейса, не раздвигая
   тег и не ломая flex-сетку. Тег НЕ обрезает превью (overflow только на тексте). */
.spb--zoom { overflow: visible; }
.spb-thumb--zoom {
  position: relative;
  /* bottom left, не center — тот же приём, что у .hist-thumb-clip
     (auth.css): зум растёт вверх-вправо от исходного угла, не перекрывая
     соседние бейджи/оригинал во всех 4 стороны разом (запрос 2026-07-21,
     "на всём сайте"). */
  transform-origin: bottom left;
  transition: transform .2s ease-out, box-shadow .2s ease-out, border-radius .2s ease-out;
}
.spb-thumb--zoom:hover {
  transform: scale(4); z-index: 50; border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0,0,0,.30);
}

/* таблица: 1-я строка — два столбца, 2-я строка — миниатюры на всю ширину */
.spm-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; align-items: stretch; }
.spm-col--tiles { grid-column: 1 / -1; }
.spm-col--wide { grid-column: 1 / -1; }

/* подсказка об автоподборе, когда ничего не выбрано */
.spm-auto-note {
  margin: 0; padding: 14px 16px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-sm);
  font-family: var(--sans); font-size: 13.5px; line-height: 1.5; color: var(--ink);
}
.spm-auto-note-sub { color: var(--muted); }

/* увеличенное превью миниатюры при наведении (×8) */
.spm-zoom {
  position: fixed; z-index: 1000; pointer-events: none;
  border-radius: 14px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-lg);
  display: grid; place-items: center;
  animation: spmZoomIn .12s var(--ease) both;
}
.spm-zoom img { width: 100%; height: 100%; object-fit: contain; background: var(--paper-2); }
.spm-zoom-swatch { width: 78%; height: 78%; border-radius: 18px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.spm-zoom-soc { width: 76%; height: 76%; border-radius: 22%; display: grid; place-items: center; font-weight: 800; line-height: 1; }
.spm-zoom-emoji { line-height: 1; color: var(--ink); }
.spm-zoom-cap {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 14px;
  font-family: var(--sans); font-size: var(--text-sm); font-weight: 600; color: #fff; text-align: center;
  background: linear-gradient(to top, rgba(0,0,0,.62), rgba(0,0,0,0));
}
@keyframes spmZoomIn { from { opacity: 0; transform: scale(.95); } to { opacity: 1; transform: none; } }
.spm-col {
  display: flex; flex-direction: column; gap: 11px; min-width: 0;
  padding: 12px 11px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-sm);
}
.spm-col-h {
  margin: 0; padding-bottom: 9px; border-bottom: 1px solid var(--line);
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted);
}
.spm-col-body { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; min-width: 0; width: 100%; }
.spm-col-body .spb { max-width: 100%; }
.spm-col-body .spb-text { white-space: normal; overflow: visible; text-overflow: clip; word-break: break-word; }
.spm-empty { color: var(--muted); opacity: .6; font-size: var(--text-sm); font-style: italic; }

@container (max-width: 360px) { .spm-cols { grid-template-columns: 1fr; } }

/* столбик миниатюр — квадраты с подписями снизу (мельче в 1,5×) */
.spm-tiles { display: grid; grid-template-columns: repeat(auto-fill, 60px); justify-content: start; gap: 8px 12px; width: 100%; min-width: 0; }
.spm-tile {
  display: flex; flex-direction: column; align-items: center; gap: 5px; width: 60px;
  padding: 0; border: none; background: none; cursor: pointer; font-family: var(--sans);
}
.spm-tile-art {
  position: relative; width: 100%; aspect-ratio: 1; border-radius: 8px; overflow: hidden;
  background: var(--paper-2); border: 1px solid var(--line);
  display: grid; place-items: center;
  transition: border-color .14s var(--ease), transform .14s var(--ease), box-shadow .14s;
}
.spm-tile:hover .spm-tile-art { border-color: #C0392B; transform: translateY(-1px); box-shadow: var(--sh-sm); }
.spm-tile-art img { width: 100%; height: 100%; object-fit: cover; }
.spm-swatch { width: 58%; height: 58%; border-radius: 8px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.spm-social { width: 58%; height: 58%; border-radius: 9px; display: grid; place-items: center; font-size: var(--text-xs); font-weight: 800; }
.spm-tile-emoji { font-size: 20px; line-height: 1; color: var(--ink); }
.spm-tile-x {
  position: absolute; top: 3px; right: 3px; width: 15px; height: 15px; border-radius: 999px;
  background: rgba(255,255,255,.94); color: var(--muted); display: grid; place-items: center;
  box-shadow: var(--sh-sm); opacity: 0; transition: opacity .14s, color .14s, background .14s;
}
[data-theme="dark"] .spm-tile-x { background: rgba(20,20,20,.85); }
.spm-tile:hover .spm-tile-x { opacity: 1; }
.spm-tile:hover .spm-tile-art:hover .spm-tile-x { color: #fff; background: #C0392B; }
.spm-tile-cap { display: flex; flex-direction: column; align-items: center; gap: 1px; max-width: 100%; }
.spm-tile-pre { font-size: var(--text-xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.spm-tile-label {
  font-size: var(--text-xs); color: var(--ink); text-align: center; line-height: 1.2; font-weight: 500;
  max-width: 100%; white-space: normal; word-break: break-word; overflow-wrap: anywhere;
}
.spm-tile-pre { font-size: 8px; }

/* компактный b2b-тег */
.spb {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px 5px 8px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-sm);
  color: var(--ink); font-family: var(--sans); font-size: var(--text-sm); font-weight: 500;
  line-height: 1.1; cursor: pointer; max-width: 240px;
  transition: border-color .14s var(--ease), background .14s var(--ease), box-shadow .14s, transform .14s var(--ease);
}
.spb:hover { border-color: #C0392B; background: #FCF3F1; box-shadow: var(--sh-sm); transform: translateY(-1px); }
[data-theme="dark"] .spb:hover { background: rgba(192,57,43,.12); }
.spb-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.spb-pre { color: var(--muted); font-weight: 600; margin-right: 3px; }
.spb-emoji { font-size: var(--text-sm); line-height: 1; flex: none; }
.spb-dot { width: 12px; height: 12px; border-radius: 999px; flex: none; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.spb-thumb { width: 18px; height: 18px; border-radius: 4px; object-fit: cover; flex: none; box-shadow: inset 0 0 0 1px rgba(0,0,0,.08); }
.spb-soc { width: 18px; height: 18px; border-radius: 4px; flex: none; display: grid; place-items: center; font-size: var(--text-xs); font-weight: 800; }
.spb-x { flex: none; display: grid; place-items: center; width: 14px; height: 14px; border-radius: 999px;
  color: var(--muted); margin-left: 1px; transition: color .14s, background .14s; }
.spb:hover .spb-x { color: #fff; background: #C0392B; }

/* ── Плашка "Продолжить черновик" на лендинге ── */
.draft-resume-bar {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px; border-radius: 12px;
  background: var(--navy-tint); border: 1.5px solid var(--navy-line);
  cursor: pointer; margin-bottom: 14px;
  transition: background .15s, box-shadow .15s;
}
.draft-resume-bar:hover { background: var(--navy-line); box-shadow: var(--sh-sm); }
.draft-resume-icon { font-size: 18px; flex: none; color: var(--navy); }
.draft-resume-text { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.draft-resume-label { font-size: 11px; font-weight: 700; color: var(--navy);
  text-transform: uppercase; letter-spacing: .05em; }
.draft-resume-preview { font-size: 13px; color: var(--ink-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.draft-resume-arrow { font-size: 18px; color: var(--navy); flex: none; }
.draft-resume-close { background: none; border: none; cursor: pointer;
  color: var(--muted); font-size: 20px; padding: 0 2px; line-height: 1;
  flex: none; transition: color .13s; }
.draft-resume-close:hover { color: var(--ink); }

/* ── PROMPT MODE CTA: «Сгенерировать сразу» / «Уточнить детали» ──
   Раньше жила в потоке .settings-body вместе с полем и нуджем — превью
   готового текста (ph-nudge) могло вырасти по высоте и сдвинуть кнопку
   ровно туда, где уже стоит курсор — риск случайного платного клика.
   Теперь рендерится в .settings-foot (см. Studio ниже) — тот уже flex:none
   и стоит ПОСЛЕ прокручиваемого .settings-body в flex-колонке, поэтому
   физически не может двигаться от контента внутри .settings-body. */
.pm-cta-row { display: block; }
.pm-cta-col { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.pm-cta-btn { width: 100%; }
.pm-cta-credits { font-size: 16.5px; color: var(--muted); text-align: center; } /* ×1.5 от исходных 11px, по запросу 2026-07-25 */
.pm-cta-credits-link { color: var(--navy); cursor: pointer; text-decoration: underline; }
.pm-cta-credits-warn { color: #C0392B; font-weight: 600; }
/* Баланс кредитов слева внутри кнопки — раньше был отдельным бейджем только
   в шапке студии, теперь виден прямо там, где пользователь жмёт «Сгенерировать». */
.pm-cta-credit-chip {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; gap: 4px;
  height: 26px; padding: 0 10px; border-radius: 999px;
  background: rgba(255,255,255,.14); color: #fff;
  font-size: 12px; font-weight: 700; cursor: pointer;
  transition: background .15s;
}
.pm-cta-credit-chip:hover { background: rgba(255,255,255,.22); }
/* Реальный размер генерации (sizeGhost) — зеркально чипу баланса, но
   справа и некликабельно: это просто индикатор текущего выбора формата,
   не отдельное действие. Тот же размер шрифта/паддингов, что у "N ген."
   слева — единая пара чипов по краям кнопки. */
.pm-cta-size-chip {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center;
  height: 26px; padding: 0 10px; border-radius: 999px;
  background: rgba(255,255,255,.14); color: #fff;
  font-size: 12px; font-weight: 700; white-space: nowrap;
  pointer-events: none;
}
.btn-outline-navy { background: transparent; color: var(--navy); border-color: var(--navy-line); }
.btn-outline-navy:hover { border-color: var(--navy); background: var(--navy-tint); }
/* .pm-cta-credit-chip выше сделан под белый текст на тёмной navy-заливке
   (btn-primary) — на светлой btn-outline-navy (кнопка "Перегенерировать"
   после первого результата) тот же полупрозрачный белый чип был бы
   практически невидим. Та же логика для size-chip справа. */
.btn-outline-navy .pm-cta-credit-chip { background: var(--navy-tint); color: var(--navy); }
.btn-outline-navy .pm-cta-credit-chip:hover { background: var(--navy-line); }
.btn-outline-navy .pm-cta-size-chip { background: var(--navy-tint); color: var(--navy); }

/* ── OFFLINE BANNER: показывается сайт-вайд (App), пока navigator.onLine
   === false — генерация/правка макета требуют реального ответа ИИ и без
   сети не работают. Липкий сверху, поверх шапки. ── */
.offline-banner {
  /* Не sticky: .nav ниже уже sticky top:0 (z-index 50) — второй sticky top:0
     элемент выше в DOM просто перекрыл бы её при скролле, а не встал сверху
     стеком. Статичный блок в начале страницы достаточно заметен сам по себе:
     баннер появляется/исчезает вместе со сменой online/offline. */
  display: flex; align-items: center; justify-content: center;
  gap: 8px; width: 100%; padding: 9px 16px;
  background: #7A2E22; color: #FDEEE9;
  font-family: var(--sans); font-size: var(--text-sm); font-weight: 600;
  text-align: center;
}

/* Отчёт "сколько QR реально считывается" под холстом результата — см.
   verifyQrCountOnSide/InlineResultsPane (studio-extras.jsx). */
.qr-scan-warning {
  display: flex; align-items: center; gap: 8px; width: 100%;
  margin-top: 10px; padding: 10px 14px; border-radius: 10px;
  background: #7A2E22; color: #FDEEE9;
  font-family: var(--sans); font-size: var(--text-sm); font-weight: 600;
  text-align: left;
}

/* Тот же отчёт, положительный исход (найдено QR столько же, сколько было
   на оригинале) — тот же layout, что .qr-scan-warning, цвет вместо тревожного
   на var(--good) (уже используется для прочих "всё ок" индикаторов сайта). */
.qr-scan-ok {
  display: flex; align-items: center; gap: 8px; width: 100%;
  margin-top: 10px; padding: 10px 14px; border-radius: 10px;
  background: rgba(31,138,91,.12); color: var(--good);
  font-family: var(--sans); font-size: var(--text-sm); font-weight: 600;
  text-align: left;
}

/* Заметный бейдж QR прямо НА превью (апскейлер/адаптер) — раньше статус жил
   мелкой подписью под картинкой и терялся визуально (запрос пользователя
   2026-07-24: "должно быть более видным и заметным"). Абсолютно
   позиционирован внутри .up-img-wrap (тот уже position:relative) — не
   занимает место в потоке, поэтому не может "раздвинуть" сетку .up-compare
   и наехать на соседний блок (та же грабля, что уже была с текстовой строкой
   под подписью). top-left — противоположный угол от бейджа "AI" (top-right,
   инлайн-стиль в upscaler.jsx), чтобы не сталкивались на одном фото. */
.qr-badge {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  display: flex; align-items: center; gap: 5px;
  padding: 5px 10px; border-radius: 8px;
  font-family: var(--sans); font-size: 12.5px; font-weight: 700;
  color: #fff; box-shadow: 0 2px 8px rgba(0,0,0,.25);
}
.qr-badge.ok   { background: var(--good); }
.qr-badge.warn { background: #7A2E22; }
/* .inline — бейдж в потоке (не поверх фото), между подписью стороны и
   картинкой (запрос пользователя 2026-07-25: раньше .qr-badge перекрывал
   и сам снимок, и подпись "ОРИГИНАЛ ФОТО"/"УЛУЧШЕННОЕ ФОТО" над ним). Center
   даёт обёртка-флекс вокруг (см. upscaler.jsx/adapter.jsx) — сам бейдж
   остаётся компактной "таблеткой" по ширине контента, не на всю строку. */
.qr-badge.inline { position: static; margin: 0; box-shadow: none; }

