/* ============================================================
   Конструктор эмодзи — единый нативный чёрно-белый стиль

   Язык формы (одинаков на ВСЕХ экранах, без исключений):
   — сплошные заливки, скруглённые прямоугольники, мягкая тень;
   — никаких срезанных углов, скобок-прицела и клетчатой сетки —
     это была временная «инженерная» тема, её больше нет нигде;
   — палитра строго чёрно-белая (плюс оттенки серого); во всём
     интерфейсе всего два места с лёгким градиентом — полоска
     прогресса и бейджи-иконки (см. пометки «градиент 1/2» ниже),
     остальное — ровная заливка;
   — акцентный цвет = «инверсия темы» (белый на тёмном экране,
     чёрный на светлом) — используется для выделения выбранного
     состояния вместо прежнего синего.
   ============================================================ */

* { box-sizing: border-box; }

:root {
  /* Базовая палитра — тёмная тема (по умолчанию) */
  --bg: #000000;
  --surface: #17171a;
  --surface-strong: #232327;
  --surface-invert: #f4f4f2;
  --surface-invert-contrast: #101012;
  --line: rgba(255, 255, 255, 0.09);
  --text: #f5f5f6;
  --text-secondary: #8d8d93;
  --accent: #ffffff;
  --accent-soft: rgba(255, 255, 255, 0.12);
  --accent-contrast: #0a0a0b;
  --danger: #ff6b6b;

  --radius-xl: 26px;
  --radius-lg: 20px;
  --radius-md: 14px;
  --radius-sm: 10px;
  --radius-pill: 999px;

  --shadow-soft: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 14px 28px -16px rgba(0, 0, 0, 0.7);
  --shadow-tight: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 6px 14px -8px rgba(0, 0, 0, 0.6);

  --font-display: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-body: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* градиент 1 — полоска прогресса (серый → акцент) */
  --grad-progress: linear-gradient(90deg, var(--text-secondary), var(--accent));
  /* градиент 2 — едва заметный блик на иконках-бейджах */
  --grad-badge: linear-gradient(155deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0) 60%);
}

/* Переключается кнопкой ☀️/🌙 на стартовом экране (см. app.js: toggleTheme) —
   выбор сохраняется в localStorage и переопределяет автоопределение темы
   Telegram, пока пользователь не выберет "авто" заново. */
[data-theme="light"] {
  --bg: #f2f2f4;
  --surface: #ffffff;
  --surface-strong: #ececef;
  --surface-invert: #17171a;
  --surface-invert-contrast: #f5f5f5;
  --line: rgba(16, 16, 18, 0.08);
  --text: #17171a;
  --text-secondary: #8a8a90;
  --accent: #17171a;
  --accent-soft: rgba(16, 16, 18, 0.07);
  --accent-contrast: #ffffff;
  --danger: #d1373f;

  --shadow-soft: 0 1px 2px rgba(20, 20, 22, 0.04), 0 14px 26px -18px rgba(20, 20, 22, 0.22);
  --shadow-tight: 0 1px 2px rgba(20, 20, 22, 0.03), 0 6px 12px -8px rgba(20, 20, 22, 0.16);

  --grad-badge: linear-gradient(155deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0) 65%);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line); border-radius: var(--radius-pill); }

#fatal-error {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 999;
  max-height: 60vh;
  overflow-y: auto;
  background: #1a0d0d;
  border-bottom: 1px solid var(--danger);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  color: #ffdada;
  padding: 14px 16px;
  font-size: 13px;
  line-height: 1.4;
  font-family: var(--font-body);
  white-space: pre-wrap;
  word-break: break-word;
}

#fatal-error strong {
  display: block;
  font-family: var(--font-body);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 12px;
  color: var(--danger);
  margin-bottom: 6px;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  -webkit-tap-highlight-color: transparent;
}

/* Ровный сплошной фон — никакой чертёжной сетки */
body {
  background: var(--bg);
}

#app {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* Telegram передаёт актуальную высоту вьюпорта отдельной переменной —
     она надёжнее чистого 100vh (тот не всегда учитывает, что часть
     экрана занята клавиатурой/жестовой панелью на некоторых устройствах). */
  min-height: var(--tg-viewport-stable-height, 100vh);
  /* В fullscreen-режиме (см. requestFullscreen в app.js) наш контент идёт
     от самого края экрана, поэтому сам отступаем от системных элементов
     (чёлка/статус-бар — safe-area-inset-*) и от плавающих элементов
     самого Telegram поверх fullscreen-контента (закрыть/свернуть —
     content-safe-area-inset-*). Вне fullscreen и в обычном браузере эти
     переменные не определены — var(...) откатывается на 0px. */
  padding-top: calc(var(--tg-content-safe-area-inset-top, 0px) + var(--tg-safe-area-inset-top, 0px));
  padding-bottom: calc(var(--tg-content-safe-area-inset-bottom, 0px) + var(--tg-safe-area-inset-bottom, 0px));
}

#app-header {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 16px;
  position: sticky;
  /* top:0 здесь был некорректен: у #app сверху уже зарезервирован отступ
     под safe-area (см. padding-top у #app выше), но sticky позиционируется
     относительно вьюпорта скролл-контейнера и игнорирует чужой padding.
     Из-за этого при скролле шапка (а с ней и #progress-rail внутри неё)
     прилипала к самому верху экрана и заезжала под нативные элементы
     Telegram — кнопку "Назад", шеврон сворачивания и меню из трёх точек.
     top должен повторять тот же safe-area отступ, чтобы шапка
     останавливалась ровно под ними. */
  top: calc(var(--tg-content-safe-area-inset-top, 0px) + var(--tg-safe-area-inset-top, 0px));
  background: var(--bg);
  z-index: 5;
  /* iOS Safari баг: скруглённые блоки, уезжающие под sticky-элемент при
     скролле, иногда оставляют "призрачный" обрезок скругления поверх
     него (видно как тёмная дуга в углу шапки/тулбара). Вынос sticky-блока
     на отдельный композитный слой убирает артефакт. */
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
  will-change: transform;
}

/* Тот же баг специфичности, что и у .screen[hidden] выше (см. коммент
   там) — #app-header{display:flex} c ID-селектором перебивает дефолтный
   браузерный [hidden]{display:none}, из-за чего хедер (с залипшим
   заголовком от предыдущего экрана) оставался виден на онбординге и
   старте, хотя app.js специально ставит header.hidden=true именно для
   него (см. showScreen в app.js — единственный экран без общей шапки
   EmojiFy, см. коммент у .app-header-top ниже). */
#app-header[hidden] {
  display: none !important;
}

/* Верхняя строка общей шапки EmojiFy — показывается на каждом экране,
   кроме онбординга (см. showScreen в app.js): кнопка "назад", лого-бейдж,
   название приложения + название текущего шага (#step-title), справа —
   языковая плашка и переключатель темы. Раньше это был отдельный блок
   .start-topbar только на экране "Начало" — теперь он живёт здесь и
   виден единообразно везде. */
.app-header-top {
  display: flex;
  flex-direction: row;
  align-items: center;
  text-align: left;
  gap: 10px;
}

.app-header-back {
  flex-shrink: 0;
  font-size: 20px;
}

.app-header-badge {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  background: var(--surface-strong);
  background-image: var(--grad-badge); /* градиент 2 — как у .start-icon-badge раньше */
  color: var(--text);
}

.app-header-badge svg {
  width: 18px;
  height: 18px;
  display: block;
}

/* Кнопка-гамбургер бокового меню (см. #menu-btn/#menu-overlay в index.html
   и блок "боковое меню" в app.js) — та же круглая .icon-btn, что и
   "назад"/тема, просто со своей SVG-иконкой из трёх полосок. Стоит сразу
   после "назад" и, в отличие от неё, видна всегда, а не только когда
   есть история переходов — меню доступно с любого экрана мастера. */
.app-header-menu svg {
  width: 18px;
  height: 18px;
  display: block;
}

.app-header-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 1px;
}

.app-header-text strong {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.app-header-controls {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}

.app-header-lang-pill {
  flex-shrink: 0;
  padding: 5px 10px;
  border-radius: 999px;
  background: var(--surface-strong);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  border: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.15s ease, transform 0.1s ease, color 0.15s ease;
}

.app-header-lang-pill:active {
  transform: scale(0.92);
  background: var(--accent, var(--text));
  color: var(--surface, #fff);
}

#step-title {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  margin: 0;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

#progress-rail {
  width: 100%;
  height: 5px;
  border-radius: var(--radius-pill);
  background: var(--surface-strong);
  overflow: hidden;
  position: relative;
}

/* Тот же баг специфичности, что и у #app-header[hidden] выше — рейл
   скрыт для "Начала" и финальной заставки "Отправка" (см. showsProgress
   в showScreen в app.js), но без display:none здесь пустая полоска
   осталась бы видна даже с атрибутом hidden. */
#progress-rail[hidden] {
  display: none !important;
}

#progress-fill {
  height: 100%;
  width: 0%;
  border-radius: var(--radius-pill);
  background: var(--grad-progress); /* градиент 1 */
  transition: width 0.35s ease;
}

.icon-btn {
  position: relative;
  border: none;
  background: var(--surface-strong);
  color: var(--text);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  font-size: 16px;
  line-height: 1;
  font-family: var(--font-body);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-tight);
  transition: transform 0.15s ease;
}

.icon-btn:active {
  transform: scale(0.92);
}

.screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 14px 16px 24px;
  overflow-y: auto;
}

/* Экраны с длинным/прокручиваемым содержимым (список эмодзи, список паков)
   не центрируем — там центрирование при скролле выглядит криво. Они
   остаются прижатыми к верху, как обычный список. */
#screen-picker,
#screen-existing,
#screen-customize,
#screen-menu-info {
  justify-content: flex-start;
}

/* Некоторые экраны (например #screen-picker, .screen-start) переопределяют
   display под свою раскладку (flex/grid) — это забивает специфичностью
   браузерный дефолт [hidden] { display: none }, и скрытый экран продолжает
   рендериться под текущим (см. баг с "Выбрано: 0 из" на экране названия
   пака). !important здесь — осознанная подстраховка на уровне базового
   класса, чтобы это не повторялось при каждом новом экране со своим display. */
.screen[hidden] {
  display: none !important;
}

.hint {
  color: var(--text-secondary);
  font-size: 13.5px;
  line-height: 1.5;
  margin: 2px 0 14px;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* .stack задаёт display:flex, и это авторское правило перебивает
   встроенное браузерное [hidden]{display:none} — тот же баг
   специфичности, что и у .screen[hidden]/#app-header выше (см. коммент
   там) — без этого правила hidden=true на элементе со .stack ничего не
   скрывает. */
.stack[hidden] {
  display: none;
}

/* ---------- базовая карточка: сплошная заливка, скругление, мягкая тень ---------- */

.option-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  width: 100%;
  text-align: left;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  font-size: 15px;
  font-weight: 500;
  font-family: var(--font-body);
  box-shadow: var(--shadow-tight);
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}

.option-card:active {
  transform: scale(0.99);
}

/* маленький бейдж-галочка в углу выбранной карточки — заменяет прежние
   скобки-прицел; появляется только у .option-card (реальный выбор), у
   .color-row вместо неё просто меняется заливка (см. .color-row.expanded) */
.option-card::after {
  content: "✓";
  position: absolute;
  top: 12px;
  right: 12px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 12px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.15s ease, transform 0.15s ease;
  pointer-events: none;
}

.option-card.selected {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--text);
}

.option-card.selected::after {
  opacity: 1;
  transform: scale(1);
}

.option-card span.sub {
  font-weight: 400;
  font-size: 13px;
  color: var(--text-secondary);
}

.option-card.selected span.sub {
  color: var(--text-secondary);
}

/* ---------- однотонные иконки ----------
   Заменяют прежние цветные эмодзи везде: в заголовках карточек списка
   (заливка/обводка/текст/лого/шрифт и т.д.), в шапке, на стартовом
   экране и в баннере ошибки. Все — inline SVG на currentColor, поэтому
   отдельно красить под тёмную/светлую тему не нужно. */
.opt-icon {
  display: inline-flex;
  width: 17px;
  height: 17px;
  vertical-align: -3px;
  margin-right: 7px;
  color: var(--text-secondary);
}

.opt-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

.option-card.selected .opt-icon {
  color: var(--accent);
}

.opt-icon-inline {
  margin-right: 0;
  vertical-align: -3px;
}

#fatal-error strong .opt-icon {
  width: 14px;
  height: 14px;
  color: var(--danger);
  margin-right: 6px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.tpl-card {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-tight);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.tpl-card.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent), var(--shadow-tight);
}

.tpl-card .lottie-box {
  width: 74%;
  height: 74%;
  position: relative;
}

/* Живой предпросмотр текста/лого поверх анимации плитки — позиционируется
   в процентах от .lottie-box согласно group.rects[name]: cx/cy — центр,
   w/h — размер (уже с учётом масштаба группы), rot — угол поворота в
   градусах (доли/градусы посчитаны на сервере, см. compute_placeholder_
   rect_fraction в main.py). JS ставит left/top/width/height и крутит
   элемент через transform: translate(-50%,-50%) rotate(rot deg), чтобы
   повторить ту же трансформацию, что бот применяет к вставленным буквам. */
.tile-text-overlay {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  pointer-events: none;
  line-height: 1;
  white-space: nowrap;
  font-weight: 600;
}

.tile-logo-overlay {
  position: absolute;
  z-index: 2;
  pointer-events: none;
  object-fit: contain;
}

.tpl-card .name {
  position: absolute;
  bottom: 5px;
  left: 6px;
  right: 6px;
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.01em;
  text-align: center;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tpl-card .check {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 11px;
  font-weight: 700;
  box-shadow: var(--shadow-tight);
  display: none;
  align-items: center;
  justify-content: center;
}

.tpl-card.selected .check {
  display: flex;
}

/* ---------- picker screen: все группы шаблонов на одном экране ---------- */

#screen-picker {
  padding-top: 0;
  display: flex;
  flex-direction: column;
}

/* Превью текущих настроек (цвет/текст/лого) сверху экрана выбора эмодзи.
   Под анимацией — одна кнопка "Кастомизировать" (.picker-customize-btn),
   которая уводит на отдельный экран #screen-customize с редактированием
   цвета/текста/лого (см. ниже и app.js: el.pickerCustomizeBtn). Тот же
   .picker-preview используется и как обёртка увеличенного превью на самом
   экране кастомизации — см. #customize-preview ниже. */
.picker-preview {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 18px 16px 22px;
  margin: 0 -16px 16px;
  border-bottom: 1px solid var(--line);
}

/* Одна кнопка вместо прежней пары "Текст / лого" + "Цвета" — уводит на
   отдельный экран #screen-customize (см. app.js: el.pickerCustomizeBtn),
   где обе панели редактируются так же, как раньше, только не инлайн на
   этом экране, а на своём собственном. */
.picker-customize-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  appearance: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--surface-strong);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 600;
  padding: 10px 20px;
  box-shadow: var(--shadow-tight);
  transition: transform 0.15s ease, background 0.15s ease;
}

.picker-customize-btn .opt-icon {
  width: 16px;
  height: 16px;
}

.picker-customize-btn:active {
  transform: scale(0.97);
  background: var(--accent-soft);
}

/* Тот же блок превью, но побольше и без нижней рамки — теперь верх
   отдельного экрана #screen-customize, а не врезка на экране picker. */
#customize-preview {
  border-bottom: none;
  padding-top: 8px;
}

#customize-preview .picker-preview-frame {
  width: 190px;
  height: 190px;
}

.picker-preview-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
}

.picker-preview-link {
  appearance: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  padding: 8px 14px;
  box-shadow: var(--shadow-tight);
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}

.picker-preview-link:active {
  transform: scale(0.97);
  background: var(--accent-soft);
}

/* Кнопка "включена" — соответствующая панель ниже сейчас развёрнута. */
.picker-preview-link.active {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--text);
}

/* ---------- инлайн-панели "Текст / лого" и "Цвета" на экране picker ----------
   Разворачиваются/сворачиваются по клику на кнопки в .picker-preview-actions,
   без ухода с текущего экрана (см. app.js: bindPanelToggle, renderPicker). */
.picker-inline-panel {
  padding: 16px;
  margin: 0 -16px 16px;
  border-bottom: 1px solid var(--line);
}

/* ---- переключатель "Текст" / "SVG-лого" внутри панели контента ---- */

.content-type-switch {
  display: flex;
  gap: 4px;
  margin-bottom: 16px;
  padding: 4px;
  border-radius: var(--radius-pill);
  background: var(--bg);
}

.content-type-btn {
  flex: 1;
  padding: 10px 8px;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 600;
  font-family: var(--font-body);
  transition: background 0.15s ease, color 0.15s ease;
}

.content-type-btn.active {
  background: var(--accent);
  color: var(--accent-contrast);
}

.content-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* .content-body переопределяет display — без этого правила атрибут
   [hidden] будет перебит нашим же display:flex (та же ловушка, что и у
   .screen[hidden] выше по файлу), и обе вкладки останутся видны разом. */
.content-body[hidden] {
  display: none !important;
}

.file-picker-block {
  display: flex;
  flex-direction: column;
}

/* та же причина, что и у .content-body[hidden] выше */
.file-picker-block[hidden] {
  display: none !important;
}

.picker-preview-frame {
  width: 148px;
  height: 148px;
  padding: 6px; /* толщина видимого кольца — цвет обводки, см. mountPreviewBox в app.js */
  border-radius: 50%;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-soft);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Раньше это был инсет 78% под живую Lottie-анимацию конкретного шаблона —
   теперь сама "монета": цвет заливки на всю доступную площадь (кольцо
   вокруг — это padding родителя .picker-preview-frame, залитый цветом
   обводки), плюс на ней центрируются text/logo оверлеи (см.
   renderPreviewOverlay в app.js). position:relative — точка отсчёта для
   .tile-text-overlay/.tile-logo-overlay внутри. */
.picker-preview-box {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  position: relative;
  overflow: hidden;
}

#picker-toolbar {
  position: sticky;
  /* top:0 здесь была та же ошибка, что раньше у #app-header (см. коммент
     там): тулбар — второй sticky-элемент в том же скролле, и с top:0 он
     прилипал не под шапкой, а поверх/выше неё — под нативные кнопки
     Telegram "Назад"/шеврон/три точки. top должен быть равен высоте самой
     шапки (+ safe-area), тогда тулбар останавливается ровно под ней.
     Высоту шапки (она у разных экранов разная) app.js держит в
     --app-header-height через ResizeObserver, см. syncAppHeaderHeight. */
  top: calc(
    var(--tg-content-safe-area-inset-top, 0px) +
    var(--tg-safe-area-inset-top, 0px) +
    var(--app-header-height, 0px)
  );
  z-index: 5;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin: 0 -16px 16px;
  padding: 12px 16px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  font-size: 13.5px;
  color: var(--text-secondary);
  font-family: var(--font-body);
  /* см. коммент у #app-header — тот же sticky-ghosting фикс: скруглённый
     .picker-preview-frame сверху уезжает под этот тулбар при скролле
     списка и без своего композитного слоя иногда оставляет тёмный обрезок
     скругления поверх сплошного фона тулбара. */
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
  will-change: transform;
}

#picker-toolbar strong {
  color: var(--text);
  font-weight: 700;
}

.group-section {
  margin-bottom: 26px;
}

.group-section:last-child {
  margin-bottom: 4px;
}

.group-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
  padding-bottom: 0;
}

.group-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  font-family: var(--font-body);
}

.group-count {
  font-family: var(--font-body);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text);
  background: var(--surface-strong);
  border-radius: var(--radius-pill);
  padding: 2px 9px;
}

/* ---------- строка-аккордеон выбора цвета ---------- */

.color-row {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-tight);
  transition: border-color 0.15s ease, border-radius 0.15s ease;
}

.color-row label {
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
}

.color-row span.sub {
  display: block;
  font-size: 13px;
  font-weight: 400;
  color: var(--text-secondary);
}

.color-field {
  display: flex;
  flex-direction: column;
}

/* Тот же баг специфичности, что и у .content-body[hidden]/.color-swatch-row[hidden]
   выше по файлу: без этого правила наш же display:flex перебивает
   браузерное [hidden]{display:none}, и строка "Цвет текста" (обёрнута в
   .color-field — см. colorRow в app.js) остаётся видна, даже когда
   textColorRow.hidden = true (см. updateTextColorRowVisibility). */
.color-field[hidden] {
  display: none;
}

.color-swatch {
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: 50%;
  padding: 0;
  flex-shrink: 0;
  background: #000;
}

.color-row.expanded {
  border-color: var(--accent);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

/* ---- вариант color-row с рядом готовых свотчей (цвет текста) ---- */

.color-row--swatches {
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}

.color-swatch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 11px;
}

/* ---- группировка готовых дуо-свотчей: сплошные отдельно, градиенты
   отдельно (см. comboGroups в renderColorsForm) — два подписанных ряда
   вместо одного смешанного списка ---- */

.color-swatch-groups {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.color-swatch-group-label {
  display: block;
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

/* Тот же баг специфичности, что и .stack[hidden]/.screen[hidden] выше —
   display:flex авторским правилом перебивает браузерное
   [hidden]{display:none}. Нужно там, где ряд пресетов прячут через
   swatchRow.hidden (см. colorField: startExpanded в app.js). */
.color-swatch-row[hidden] {
  display: none;
}

.color-swatch-dot {
  /* flex: 0 0 40px (а не только width) — фиксирует именно flex-basis,
     чтобы круглешок не мог сжаться уже по главной оси флекс-контейнера
     (одного width+flex-shrink:0 для этого недостаточно в некоторых
     WebView, отсюда были "овалы" вместо кругов); aspect-ratio — второй,
     независимый от флекса, страховочный слой той же гарантии. */
  width: 40px;
  height: 40px;
  min-width: 40px;
  min-height: 40px;
  flex: 0 0 40px;
  aspect-ratio: 1 / 1;
  padding: 0;
  border: none;
  border-radius: 50%;
  flex-shrink: 0;
  /* Фон-подложка — нужен только тем свотчам, которым не проставляют inline
     style.background (сейчас это единственно "комбо" custom-кружок цвета
     заливки/обводки, см. customComboDot в app.js) — без него там
     проступает дефолтный серый фон нативной кнопки браузера. У всех
     остальных инлайновый background (цвет/градиент) просто перекрывает
     этот фон. */
  background: var(--surface-strong);
  /* Тонкая обводка — inset-кольцом внутри box-shadow, а не border: так
     кружок остаётся ровно 40×40 (border добавлял бы к размеру ещё
     border-box-хитрости в разных WebView), а сама линия рендерится чище
     на дробных пикселях. Плюс та же тень, что у остальных карточек/кнопок
     приложения (--shadow-tight, тема-зависимая) — без нее круги выглядели
     плоской аппликацией на фоне остального интерфейса, где у всего есть
     лёгкий объём. */
  box-shadow: inset 0 0 0 1px var(--line), var(--shadow-tight);
  transition: box-shadow 0.15s ease, transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.color-swatch-dot:active {
  transform: scale(0.88);
}

.color-swatch-dot.active {
  /* Тонкое кольцо выбора в акцентном цвете темы + лёгкое увеличение —
     тот же приём, что и у остальных "выбрано"-состояний в приложении
     (.start-option.selected и т.д.), но кружку это ещё и добавляет
     аккуратный "поп"-акцент вместо чисто плоской обводки. */
  transform: scale(1.08);
  box-shadow:
    0 0 0 2px var(--surface),
    0 0 0 3.5px var(--accent),
    var(--shadow-tight);
}

/* кастомный цвет — всегда последним в ряду, живое превью текущего
   цвета/градиента; открывает колесо (и для заливки/обводки — ещё и
   переключатель «Сплошной/Градиент» с готовыми схемами) ниже. Более
   заметное inset-кольцо (вместо базового чуть тоньше) — так свотч читается
   отдельным действием, даже когда залит тем же цветом, что уже выбран. */
.color-swatch-dot--custom {
  box-shadow: inset 0 0 0 1.5px var(--text-secondary), var(--shadow-tight);
}

/* Вариант custom-свотча БЕЗ живого превью (сейчас — только customComboDot
   в renderColorsForm: он ничего не красит сам, а раскрывает/сворачивает
   подробные поля заливки/обводки) — вместо пустого блёклого кружка рисуем
   аккуратный плюс двумя псевдоэлементами, чтобы назначение кнопки было
   понятно с первого взгляда. */
.color-swatch-dot--custom-icon {
  position: relative;
}

.color-swatch-dot--custom-icon::before,
.color-swatch-dot--custom-icon::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--text-secondary);
  border-radius: 1px;
}

.color-swatch-dot--custom-icon::before {
  width: 14px;
  height: 2px;
  transform: translate(-50%, -50%);
}

.color-swatch-dot--custom-icon::after {
  width: 2px;
  height: 14px;
  transform: translate(-50%, -50%);
}


/* ---- сетка готовых градиентных схем (см. gradientBody в colorField) ---- */

.color-swatch-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px 8px;
  margin-bottom: 18px;
}

.color-swatch-dot--duo {
  width: 100%;
  aspect-ratio: 1 / 1;
  height: auto;
  background: none; /* заливка задаётся инлайн — жёсткая диагональ 50/50 */
}

/* ---- цветовой пикер: колесо + слайдер яркости + hex-поле ---- */

.color-picker {
  padding: 18px 16px 16px;
  border: 1px solid var(--accent);
  border-top: none;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  background: var(--surface-strong);
  margin-top: -1px;
}

.color-wheel {
  position: relative;
  width: 100%;
  max-width: 220px;
  aspect-ratio: 1 / 1;
  margin: 0 auto 20px;
  border-radius: 50%;
  /* колесо цвета — единственный «функциональный» градиент в интерфейсе:
     это сама палитра оттенков, а не декоративный элемент оформления */
  background:
    radial-gradient(circle at center, #fff 0%, rgba(255, 255, 255, 0) 70%),
    conic-gradient(red, #ff0, #0f0, #0ff, #00f, #f0f, red);
  box-shadow: inset 0 0 0 1px var(--line), 0 0 0 6px var(--surface-strong);
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

/* ручки колеса/слайдера — намеренно чёрно-белые (не привязаны к теме),
   т.к. должны читаться поверх любого выбранного оттенка */
.color-wheel-handle,
.color-slider-handle {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid #fff;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.35);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.color-slider {
  position: relative;
  width: 100%;
  height: 14px;
  border-radius: var(--radius-pill);
  margin: 0 0 20px;
  box-shadow: inset 0 0 0 1px var(--line);
  overflow: hidden;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.color-hex-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg);
}

.color-hex-swatch {
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}

.color-hex-prefix {
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 15px;
}

.color-hex-input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 8px 0;
}

.color-hex-input:focus {
  outline: none;
}

/* ---- переключатель "Сплошной цвет" / "Градиент" — сегмент-контрол ---- */

.wheel-picker {
  display: block;
}

.color-mode-toggle {
  display: flex;
  gap: 4px;
  margin-bottom: 18px;
  padding: 4px;
  border-radius: var(--radius-pill);
  background: var(--bg);
}

.color-mode-btn {
  flex: 1;
  padding: 10px 8px;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 600;
  font-family: var(--font-body);
  transition: background 0.15s ease, color 0.15s ease;
}

.color-mode-btn.active {
  background: var(--accent);
  color: var(--accent-contrast);
}

/* ---- градиент: два стопа (переключаются табами), угол, превью ---- */

.gradient-stops {
  display: flex;
  gap: 8px;
  margin-bottom: 14px;
}

.gradient-stop-btn {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 500;
  font-family: var(--font-body);
  transition: border-color 0.15s ease, color 0.15s ease;
}

.gradient-stop-btn.active {
  border-color: var(--accent);
  color: var(--text);
}

.gradient-stop-swatch {
  width: 20px;
  height: 20px;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
  border: 1px solid var(--line);
}

.gradient-stop-editor {
  margin-bottom: 18px;
}

.gradient-angle-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.gradient-angle-label {
  font-size: 13px;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.gradient-angle-value {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text);
  flex-shrink: 0;
  min-width: 34px;
  text-align: right;
}

.gradient-angle-slider {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--line);
  outline: none;
  touch-action: pan-y;
}

.gradient-angle-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid #fff;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.55);
  cursor: pointer;
}

.gradient-angle-slider::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid #fff;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.55);
  cursor: pointer;
}

.gradient-angle-slider::-moz-range-track {
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--line);
}

.gradient-preview-bar {
  height: 14px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  overflow: hidden;
}

.text-input {
  width: 100%;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  font-size: 15px;
  font-family: inherit;
  resize: vertical;
}

.text-input:focus {
  outline: none;
  border-color: var(--accent);
}

.file-picker {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 14px 14px 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  cursor: pointer;
  box-shadow: var(--shadow-tight);
}

.file-picker-info {
  font-size: 14px;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-body);
}

.file-picker-info.chosen {
  color: var(--text);
  font-weight: 600;
}

.file-picker-btn {
  flex-shrink: 0;
  padding: 9px 16px;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 12.5px;
  font-weight: 700;
  font-family: var(--font-body);
  letter-spacing: 0.01em;
}

.file-picker-note {
  margin-top: 10px;
}

/* ---------- слайдеры масштаба/сдвига SVG-лого (экран "Кастомизация") ---------- */
.adjust-block {
  margin-top: 14px;
  padding: 14px 16px 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-tight);
}

.adjust-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

.adjust-label {
  font-size: 13px;
  color: var(--text-secondary);
  flex-shrink: 0;
  width: 82px;
}

.adjust-value {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text);
  flex-shrink: 0;
  min-width: 38px;
  text-align: right;
}

.adjust-slider {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--line);
  outline: none;
  touch-action: pan-y;
}

.adjust-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--line), var(--shadow-tight);
  cursor: pointer;
}

.adjust-slider::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--line), var(--shadow-tight);
  cursor: pointer;
}

.adjust-slider::-moz-range-track {
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--line);
}

.adjust-reset {
  appearance: none;
  border: none;
  background: none;
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  padding: 0 0 12px;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.adjust-reset:active {
  color: var(--text);
}

#app-footer {
  padding: 12px 16px 18px;
  position: sticky;
  bottom: 0;
  background: var(--bg);
}

#build-tag {
  position: fixed;
  bottom: 2px;
  right: 6px;
  font-size: 9px;
  font-family: var(--font-mono);
  opacity: 0.4;
  color: var(--text-secondary);
  pointer-events: none;
  z-index: 1000;
}

.primary-btn {
  width: 100%;
  padding: 16px;
  border: none;
  border-radius: var(--radius-lg);
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 15.5px;
  font-weight: 700;
  font-family: var(--font-body);
  letter-spacing: -0.01em;
  box-shadow: var(--shadow-soft);
  transition: transform 0.15s ease;
}

.primary-btn:active {
  transform: scale(0.99);
}

.primary-btn:disabled {
  opacity: 0.35;
}

/* Второстепенная кнопка рядом с primary-btn (см. #screen-sending: "Новый
   набор — тот же/новый текст/лого") — заметный фон в обеих темах
   (не var(--surface), который в тёмной теме почти сливается с фоном, а
   var(--surface-strong), который заметно светлее/темнее фона в любой
   теме), а не заливка акцентом, чтобы не спорить взглядом с основной
   кнопкой "Закрыть"/"Открыть готовый пак". Цвета берутся из переменных
   темы, поэтому кнопка корректно переключается вместе с темой. */
.secondary-btn {
  width: 100%;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-strong);
  color: var(--text);
  font-size: 15.5px;
  font-weight: 700;
  font-family: var(--font-body);
  letter-spacing: -0.01em;
  transition: transform 0.15s ease, background 0.15s ease;
}

.secondary-btn:active {
  transform: scale(0.99);
  background: var(--surface);
}

/* Кнопки "Новый набор — тот же/новый текст/лого" перекрашены в цвет
   кнопки "Закрыть" (.primary-btn: var(--accent)/var(--accent-contrast)),
   чтобы текст и фон совпадали. ID-селектор нужен, чтобы переопределить
   базовые .secondary-btn (в т.ч. её :active-состояние) не трогая другие
   кнопки этого класса на странице. */
#sending-restart-same-btn,
#sending-restart-fresh-btn {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}

/* .onboarding-actions по умолчанию рассчитан на одну кнопку с малым
   gap — на экране "Готово" в #screen-sending кнопок до трёх, нужен
   воздух побольше между ними. */
.sending-actions {
  gap: 18px;
}

/* ============================================================
   Стартовый экран — «Что хотите сделать?»
   Тот же язык, что и у всего остального приложения (единый стиль
   от начала до конца, никакого отдельного «онбординга»).
   ============================================================ */

.screen-start {
  padding-top: 16px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 20px;
}

.theme-toggle {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  background: var(--surface-strong);
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  transition: transform 0.15s ease;
}

.theme-toggle .opt-icon {
  width: 16px;
  height: 16px;
  color: inherit;
}

.theme-toggle:active {
  transform: scale(0.9);
}

/* ============================================================
   Боковое меню — онбординг/политика/поддержка/реферальная система.
   Открывается кнопкой-гамбургером в шапке (#menu-btn), см. блок
   "боковое меню" в app.js (openMenu/closeMenu). Фиксированный оверлей
   поверх ВСЕГО приложения (включая шапку и текущий экран мастера),
   поэтому лежит вне #app в разметке — см. #menu-overlay в index.html.
   ============================================================ */

.menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  justify-content: flex-start;
}

/* Тот же баг специфичности, что и у #app-header[hidden] выше (см.
   коммент там) — .menu-overlay{display:flex} перебивает дефолтный
   [hidden]{display:none}. */
.menu-overlay[hidden] {
  display: none !important;
}

.menu-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 0.28s ease;
}

.menu-panel {
  position: relative;
  width: min(84vw, 340px);
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  border-right: 1px solid var(--line);
  box-shadow: var(--shadow-soft);
  overflow-y: auto;
  padding: calc(var(--tg-content-safe-area-inset-top, 0px) + var(--tg-safe-area-inset-top, 0px) + 18px)
    16px
    calc(var(--tg-content-safe-area-inset-bottom, 0px) + var(--tg-safe-area-inset-bottom, 0px) + 18px);
  /* Стартовая позиция — за левым краем экрана; открытие/закрытие
     переключает .open на #menu-overlay (см. app.js), а не сам transform
     напрямую, чтобы подложка и панель анимировались синхронно одним
     классом. */
  transform: translateX(-100%);
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
}

.menu-overlay.open .menu-backdrop {
  opacity: 1;
}

.menu-overlay.open .menu-panel {
  transform: translateX(0);
}

.menu-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
  margin-bottom: 16px;
}

.menu-panel-title {
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.menu-close-btn {
  flex-shrink: 0;
  border: none;
  background: none;
  color: var(--text-secondary);
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s ease, transform 0.15s ease;
}

.menu-close-btn svg {
  width: 20px;
  height: 20px;
}

.menu-close-btn:active {
  color: var(--text);
  transform: scale(0.9);
}

.menu-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Пункт меню — иконка-бейдж + подпись (в две строки: заголовок и
   короткое пояснение) + шеврон, слева направо, как в референсе на
   скриншоте (Fragment: список платформенных пунктов). */
.menu-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  border: none;
  background: transparent;
  border-radius: var(--radius-md);
  padding: 10px 8px;
  text-align: left;
  font-family: var(--font-body);
  transition: background 0.15s ease, transform 0.15s ease;
}

.menu-item:active {
  background: var(--surface-strong);
  transform: scale(0.99);
}

.menu-item-icon {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: 12px;
  background: var(--surface-strong);
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
}

.menu-item-icon svg {
  width: 19px;
  height: 19px;
  display: block;
}

.menu-item-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
}

.menu-item-label {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--text);
}

.menu-item-sub {
  font-size: 12px;
  color: var(--text-secondary);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-item-chevron {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  color: var(--text-secondary);
}

.menu-item-chevron svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* ---------- содержимое пунктов меню (screen-menu-info) ---------- */

.menu-info-body {
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding-top: 4px;
}

.menu-info-section-title {
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 6px;
}

.menu-info-text {
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-secondary);
  margin: 0;
}

/* Карточки "Рефералы" / "Доход" в пункте меню "Реферальная система" —
   см. app.js: menuItemReferral. Второй карточке (доход) даём лёгкий
   акцент фоном var(--accent-soft), чтобы отличалась от нейтральной
   карточки счётчика, не выбиваясь из монохромной палитры темы. */
.menu-referral-stats {
  display: flex;
  gap: 10px;
}

.menu-referral-stat {
  flex: 1;
  min-width: 0;
  padding: 14px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  text-align: center;
}

.menu-referral-stat--accent {
  background: var(--accent-soft);
}

.menu-referral-stat-label {
  display: block;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 8px;
}

.menu-referral-stat-value {
  display: block;
  font-family: var(--font-mono);
  font-size: 24px;
  font-weight: 700;
  color: var(--text);
}

.menu-info-link-box {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.menu-info-link-box span {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 12.5px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-info-copy-btn {
  flex-shrink: 0;
  border: none;
  background: var(--surface-strong);
  color: var(--text);
  padding: 8px 12px;
  border-radius: var(--radius-md);
  font-size: 12.5px;
  font-weight: 700;
  font-family: var(--font-body);
  transition: transform 0.15s ease;
}

.menu-info-copy-btn:active {
  transform: scale(0.96);
}

.menu-info-copy-btn:disabled {
  opacity: 0.4;
}

/* Иконка-сквиркл над заголовком стартового экрана — тот же приём
   ("градиент 2" — едва заметный блик поверх сплошной заливки), что и у
   .app-header-badge/.onboarding-sticker-frame, просто в своём размере.
   Никакой цветной/градиентной заливки самого квадрата — только этот
   лёгкий блик, вся палитра по-прежнему чёрно-белая (см. коммент вверху
   файла). */
.start-hero {
  display: flex;
  justify-content: center;
}

.start-hero-icon {
  width: 96px;
  height: 96px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-lg);
  background: var(--surface);
  background-image: var(--grad-badge); /* градиент 2 */
  box-shadow: var(--shadow-soft);
  color: var(--text);
}

.start-hero-emoji {
  display: flex;
  align-items: center;
  justify-content: center;
  /* было системное цветное эмодзи; заменено на line-art SVG (тот же стиль,
     что и у остальных иконок приложения) — currentColor даёт однотонную
     заливку var(--text) вместо цветного стикера. */
}

.start-hero-emoji svg {
  width: 70px;
  height: 70px;
  display: block;
}

.start-heading {
  margin: 0;
  font-family: var(--font-body);
  font-size: 23px;
  font-weight: 700;
  color: var(--text);
  text-align: left;
}

.start-subheading {
  margin: -12px 0 0;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--text-secondary);
  text-align: left;
}

.start-options {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.start-option {
  display: flex;
  flex-direction: row;
  align-items: center;
  text-align: left;
  gap: 14px;
  width: 100%;
  padding: 14px;
  border: none;
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-soft);
  transition: transform 0.15s ease, opacity 0.15s ease;
}

.start-option:active {
  transform: scale(0.98);
}

/* Тот же баг специфичности, что и у .screen[hidden]/#app-header[hidden]/
   .content-body[hidden] выше по файлу: без этого правила display:flex
   выше перебивает браузерный [hidden]{display:none}, и кнопка "Файлами
   .tgs" (см. index.html: #start-option-raw-tgs, app.js: renderStart →
   el.startOptionRawTgs.hidden = !isAdminUser()) остаётся видна ВСЕМ,
   независимо от того, что вернула проверка isAdminUser(). */
.start-option[hidden] {
  display: none !important;
}

.start-option-icon {
  flex-shrink: 0;
  width: 52px;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  background: var(--surface-strong);
  background-image: var(--grad-badge); /* градиент 2 */
  color: var(--text);
}

.start-option-icon svg {
  width: 24px;
  height: 24px;
  display: block;
}

.start-option-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 3px;
}

.start-option-text strong {
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
}

.start-option-sub {
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-secondary);
}

.start-option-chevron {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--surface-strong);
  color: var(--text-secondary);
  font-size: 18px;
  font-style: normal;
  line-height: 1;
}

.start-option.disabled {
  opacity: 0.45;
  pointer-events: none;
}

.start-option.disabled .start-option-icon {
  filter: grayscale(1);
}

/* Группа «Новый пак»: кнопка-переключатель + выезжающий снизу список из
   двух под-вариантов (эмодзи / стикер). См. app.js: setNewPackExpanded. */
.start-option-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.start-option-chevron-toggle {
  transition: transform 0.22s ease;
}

#start-option-new[aria-expanded="true"] .start-option-chevron-toggle {
  transform: rotate(90deg);
}

.start-option-sub-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transform: translateY(-6px);
  margin-left: 16px;
  padding-left: 14px;
  border-left: 2px solid var(--surface-strong);
  transition: max-height 0.28s ease, opacity 0.2s ease, transform 0.28s ease;
}

.start-option-sub-list.expanded {
  opacity: 1;
  transform: translateY(0);
}

.start-option-sub-item {
  padding: 12px 14px;
}

.start-option-sub-item .start-option-icon {
  width: 44px;
  height: 44px;
}

/* Decorative arrow on the right side of new pack options */
.start-option-arrow {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--surface-strong);
  color: var(--text-secondary);
  font-size: 18px;
  font-style: normal;
  line-height: 1;
  transition: transform 0.2s ease, background 0.15s ease;
}

.start-option:hover .start-option-arrow {
  transform: translateX(3px);
  background: var(--accent-soft);
  color: var(--accent);
}



.start-footnote {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  text-align: left;
  gap: 10px;
  padding: 14px 16px;
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text-secondary);
  font-size: 12.5px;
  line-height: 1.5;
}

.start-footnote-icon {
  flex-shrink: 0;
  width: 15px;
  height: 15px;
  margin-top: 2px;
  color: var(--text-secondary);
}

.start-footnote-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* ============================================================
   Экран онбординга — приветственный слайдер, который видит
   пользователь при первом открытии мини-аппа (см. app.js:
   ONBOARDING_SLIDES / renderOnboarding). Показывается один раз,
   до стартового экрана «Что хотите сделать?», и использует тот же
   визуальный язык, что и .screen-start — свой заголовок и
   прогресс-рейл мастера здесь тоже скрыты (см. showScreen в app.js).
   ============================================================ */

.screen-onboarding {
  padding-top: 28px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 22px;
  /* Отдаём горизонтальные жесты нашему pointer-свайпу (см. app.js,
     setupOnboardingSwipe), а не браузеру/Telegram — вертикальный скролл
     при этом не трогаем. */
  touch-action: pan-y;
}

/* Стикер + бейдж/заголовок/текст + точки — раньше центрировались
   justify-content: center прямо на .screen-onboarding, но кнопки внизу
   (.onboarding-actions) имели margin-top: auto, который в flex-колонке
   съедает ВСЁ свободное место под себя ещё до того, как justify-content
   успевает что-то распределить — в итоге контент выше всё равно прилипал
   к верху. Обёртка со своим flex:1 забирает всё пространство НАД кнопками
   и уже сама центрирует своё содержимое внутри него. */
.onboarding-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
}

.onboarding-sticker-frame {
  align-self: center;
  width: 220px;
  height: 220px;
  max-width: 60vw;
  max-height: 60vw;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-xl);
  background: var(--surface);
  background-image: var(--grad-badge); /* градиент 2 */
  box-shadow: var(--shadow-soft);
  overflow: hidden;
}

.onboarding-sticker {
  width: 72%;
  height: 72%;
}

/* lottie в canvas-рендере вставляет свой <canvas> с инлайновыми
   width/height в пикселях — растягиваем его на всю рамку-контейнер,
   иначе на разных DPR анимация окажется мельче своей рамки. */
.onboarding-sticker canvas {
  width: 100% !important;
  height: 100% !important;
}

.onboarding-copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 0 8px;
  text-align: center;
}

.onboarding-badge {
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  background: var(--surface-strong);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.onboarding-heading {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.onboarding-text {
  margin: 0;
  max-width: 320px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.onboarding-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.onboarding-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--surface-strong);
  transition: width 0.2s ease, background 0.2s ease;
}

.onboarding-dot.active {
  width: 20px;
  background: var(--accent);
}

/* Прижимаем кнопки к низу экрана — теперь просто следующий блок после
   .onboarding-body, у которого flex:1 сам съедает всё место над кнопками
   (см. коммент там же), margin-top: auto здесь больше не нужен. */
.onboarding-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

/* Кнопка "Далее"/"Начать" неактивна первые 4 секунды на каждом слайде
   (см. ONBOARDING_NEXT_DELAY_SEC в app.js) — визуал disabled-состояния
   берётся из общего .primary-btn:disabled. */

/* Пикселизация больше не спрашивается в мини-аппе (шаг #screen-pixelate
   убран) — бот снова спрашивает про неё текстом в чате после отправки
   заказа, как и раньше (см. main.py: _ask_pixelate/_ask_pixelate_grid).
   Соответствующие стили (.pixelate-option-check/.pixelate-grid-*) удалены
   вместе с разметкой в index.html. */

/* ============================================================
   Заставка после отправки заказа (#screen-sending) — тот же мягкий
   стиль без техшапки, что и у онбординга/старта (см. showScreen).
   ============================================================ */

.screen-sending {
  padding-top: 28px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 22px;
}

.sending-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  text-align: center;
  padding: 0 8px;
}

.sending-icon {
  width: 64px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-xl);
  background: var(--surface);
  background-image: var(--grad-badge);
  box-shadow: var(--shadow-soft);
  color: var(--accent);
  animation: sending-pulse 1.6s ease-in-out infinite;
}

.sending-icon svg {
  width: 30px;
  height: 30px;
}

@keyframes sending-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.08); opacity: 0.75; }
}

.sending-title {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.sending-text {
  margin: 0;
  max-width: 320px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.sending-status {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  background: var(--surface-strong);
  color: var(--text);
  font-size: 13px;
  font-weight: 700;
}

.sending-spinner {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--text-secondary);
  border-top-color: var(--accent);
  animation: sending-spin 0.8s linear infinite;
}

@keyframes sending-spin {
  to { transform: rotate(360deg); }
}

/* Состояние ошибки автоотправки (см. app.js: submitOrderViaApi/showSendingScreen) —
   бэкенд недоступен / initData протух и т.п. Кнопка снизу в этом состоянии
   превращается в "Повторить" вместо "Закрыть". */
.sending-status.is-error {
  background: color-mix(in srgb, var(--danger) 16%, var(--surface-strong));
  color: var(--danger);
}

.sending-status.is-error .sending-spinner {
  display: none;
}

/* Состояние "набор реально готов" — см. app.js: pollOrderStatus. В отличие
   от is-error использует зелёный акцент по тому же color-mix паттерну, что
   и is-error выше, а не отдельную переменную (в теме её нет). */
.sending-status.is-done {
  background: color-mix(in srgb, #22c55e 18%, var(--surface-strong));
  color: #16a34a;
}

.sending-status.is-done .sending-spinner {
  display: none;
}

/* Бейдж "⏱ ~N минут" — оценка времени сборки (см. index.html: #sending-eta,
   app.js: formatEtaBadgeText). Сознательно НЕ такой же контрастный, как
   .sending-status выше: это вспомогательная, приблизительная информация,
   а не факт статуса заказа, поэтому монохромный surface + приглушённый
   текст, тот же язык, что и у .hint. */
.sending-eta {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-pill);
  background: var(--surface-strong);
  color: var(--text-secondary);
  font-size: 12.5px;
  font-weight: 600;
}

.sending-eta svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* Тот же баг специфичности [hidden] vs класса с display, что и у
   .sending-payment[hidden] ниже — без этого правила hidden игнорируется. */
.sending-eta[hidden] {
  display: none !important;
}

/* Блок оплаты прямо на экране "screen-sending" (см. index.html:
   #sending-payment, app.js: renderPaymentAwaiting/hidePaymentAwaiting) —
   показывается вместо "Собираю набор…", пока заказ ждёт оплату Stars или
   подтверждение ручного перевода. Оформлен отдельной карточкой внутри
   .sending-body, тем же приглушённым surface, что и .sending-status, чтобы
   не спорить взглядом с иконкой/заголовком выше. */
.sending-payment {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  margin-top: 4px;
  padding: 16px;
  border-radius: var(--radius-lg);
  background: var(--surface-strong);
}

/* Тот же баг специфичности, что и у #app-header[hidden] / #progress-rail[hidden]
   (см. комментарии там): класс .sending-payment{display:flex} имеет ту же
   специфичность, что и браузерное [hidden]{display:none}, и, идя в каскаде
   позже, побеждает — карточка оплаты оставалась видна (пустой серой плашкой,
   т.к. все элементы внутри неё скрыты по отдельности) даже с атрибутом
   hidden, то есть буквально у всех пользователей на каждом экране "Отправка". */
.sending-payment[hidden] {
  display: none !important;
}

.payment-amount-text {
  margin: 0 0 2px;
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  text-align: center;
}

.payment-manual-contact {
  margin: 0 0 4px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-secondary);
  text-align: center;
  word-break: break-word;
}

/* Кнопка отмены заказа внутри блока оплаты — сознательно не .secondary-btn
   (сплошной белый фон спорил бы с карточкой .sending-payment вокруг), а
   прозрачная текстовая кнопка приглушённого цвета: отмена — второстепенное
   действие рядом с основной кнопкой оплаты/подтверждения. */
.payment-cancel-btn {
  width: 100%;
  padding: 10px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: 13.5px;
  font-weight: 600;
  font-family: var(--font-body);
  letter-spacing: -0.01em;
}

.payment-cancel-btn:active {
  opacity: 0.6;
}

.payment-cancel-btn:disabled {
  opacity: 0.35;
}


