/* ==========================================================================
   ДИЗАЙН-СИСТЕМА QUARIS (Этап 1 — фундамент)
   Все цвета, отступы, радиусы, типографика, тени и тайминги живут ЗДЕСЬ,
   в :root. Ниже по файлу правила ссылаются только на эти токены — поэтому
   поменять всю палитру можно в одном месте, а вид остаётся цельным.
   ========================================================================== */
:root {
  /* ---- Палитра: глубокий сине-чёрный (холодный подтон, без чистого чёрного) ----
     90% экрана — эти фоновые тона. Сам фон страницы — едва заметный радиальный
     градиент на html (см. правило html ниже): от #0D1220 в центре к #090D15 по
     краям. --bg держим сплошным для элементов, которым нужен ровный тёмный фон. */
  --bg:        #0A0E17;   /* самый тёмный фон страницы (сплошной) */
  --surface-1: #101624;   /* карточки, панели, зона загрузки, поля ввода */
  --surface-2: #161D2E;   /* приподнятые элементы: кнопки, сообщения, hover */
  --sidebar:   #0C111C;   /* боковая колонка — чуть темнее поверхности */

  /* ---- Границы (холодный синеватый белый) ---- */
  --border:        rgba(148, 163, 200, 0.10); /* обычная тонкая линия */
  --border-strong: rgba(148, 163, 200, 0.18); /* чуть заметнее (ховер) */

  /* ---- Текст трёх уровней (белый с едва заметной холодной примесью) ---- */
  --text-1: rgba(235, 240, 250, 0.95); /* заголовки, важное */
  --text-2: rgba(235, 240, 250, 0.62); /* обычный текст, подписи */
  --text-3: rgba(235, 240, 250, 0.40); /* второстепенное, подсказки */

  /* ---- Акцент — сдержанный сине-фиолетовый ----
     Только: ссылки, focus-кольца, активная сессия, кнопка «Спросить» с текстом,
     dragover-рамка. Больше нигде (правило гармонии: акценты ≈ 2% экрана). */
  --accent:       #5B7CFA;
  --accent-hover: #7290FF;
  --accent-weak:  rgba(91, 124, 250, 0.14);

  /* ---- Тёплый противовес — приглушённое золото ----
     Используем ОЧЕНЬ скупо (1–2 места): вспышка-звёздочка в интро и маркер
     «успешно загружен» у файла. Никогда рядом с синим акцентом в одном элементе. */
  --gold: #C9A96E;

  /* ---- Семантические статусы (приглушённые, только в статусах/ошибках) ---- */
  --danger:  #F87171;
  --success: #4ADE80;
  --warning: #FBBF24;

  /* ---- Шкала отступов: только эти значения ---- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  /* ---- Радиусы ---- */
  --radius-sm: 6px;   /* мелкие элементы: кнопки, поля, ячейки */
  --radius-md: 10px;  /* карточки, зона загрузки */
  --radius-lg: 14px;  /* модалки */

  /* ---- Типографика ---- */
  --font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "SF Mono", "JetBrains Mono", "Consolas", ui-monospace, monospace;
  --text-xs:  13px;
  --text-sm:  14px;
  --text-md:  16px;
  --text-lg:  20px;
  --text-xl:  28px;
  --text-2xl: 40px;

  /* ---- Тени: двухслойные мягкие, с лёгким синим подтоном ---- */
  --shadow-sm: 0 1px 2px rgba(5, 10, 25, 0.5);
  --shadow-md: 0 1px 2px rgba(5, 10, 25, 0.5), 0 8px 24px rgba(5, 10, 25, 0.4);

  /* ---- Движение ---- */
  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);   /* появления, ховер */
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);  /* панели, модалки */
  /* Мягкая «материаловская» кривая для сворачивания панелей и смены состояний —
     плавный вход и выход, без рывков. Длительности состояний 250–300мс. */
  --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
  --transition:  200ms var(--ease-smooth);        /* всё интерактивное */

  /* ---- Легаси-псевдонимы ----
     Старые правила ссылались на эти имена. Здесь мы «переклеиваем» их на
     новые токены — поэтому весь файл разом переезжает на свежую палитру,
     не переписывая каждую строчку. Постепенно уходят в следующих этапах. */
  --bg-dark:  var(--bg);
  --bg-panel: var(--surface-1);
  --bg-input: var(--surface-1);
  --fg-light: var(--text-1);
  --muted:    var(--text-2);
}

* { box-sizing: border-box; }

html {
  /* Приложение, а не документ: страница целиком не скроллится никогда —
     скроллятся только внутренние зоны (лендинг, панель, лента, результаты). */
  height: 100%;
  overflow: hidden;
  /* Тёмный фон держим на корне (html): так фоновый холст-«поток линий»
     (fixed, z-index:-1) ложится ПОВЕРХ этого фона, но ПОД контентом.
     Единственный разрешённый градиент: едва заметный радиальный — от чуть более
     светлого центра к тёмным краям, чтобы дать глубину (не должен читаться явно).
     background-attachment: fixed — градиент покрывает весь вьюпорт без «плитки». */
  background: radial-gradient(ellipse at center, #0D1220 0%, #090D15 100%) #090D15;
  background-attachment: fixed;
}

body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  font-family: var(--font-sans);
  /* ss01 — стилистический набор Inter: аккуратные «одноэтажные» a/g и т.п.
     На системном шрифте просто игнорируется, вреда нет. */
  font-feature-settings: "ss01";
  line-height: 1.5;
  /* Фон прозрачный — сквозь него виден холст-«поток линий» (тёмный фон живёт
     на html). Так линии проступают по всему экрану, а не только в hero. */
  background: transparent;
  color: var(--text-1);
  /* Чуть более гладкий текст */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Единый ритм заголовков: чуть плотнее трекинг и вес 600 (не 700+ —
   тяжёлые начертания читаются «дёшево»). */
h1, h2, h3 {
  letter-spacing: -0.01em;
  font-weight: 600;
}

/* ---------- Интро-заставка (логотип Кирилла: арки + точка) ----------
   (а) на тёмно-синем фоне проявляется логотип, (б) под ним по одной проявляются
   буквы «Quaris», (в) «камера влетает» в знак (scale 1 -> ~8, растворяется),
   (г) фон заставки перетекает в фон сайта и заставку снимаем — ЧЁТКИЙ бесшовный
   переход, без наложения двух экранов и «листания».
   Всё на transform/opacity; финальный стык цвета — background-color. */
.intro {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Фон = фон логотипа. В финале плавно переходит в фон сайта (--bg): сайт на
     более тёмном фоне, поэтому стыкуем цвета, чтобы «влёт» был бесшовным. */
  background-color: #1E2A5E;
  overflow: hidden;
  transition: background-color 0.3s var(--ease-smooth);
}
/* Финал: фон заставки становится ровно фоном сайта, затем заставку снимаем (JS).
   Не двигаем ничего translateY и не делаем crossfade — значит нет «двух страниц». */
.intro-hide {
  background-color: var(--bg);
  pointer-events: none;
}

/* Сцена: знак сверху, слово под ним. */
.intro-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(16px, 3vh, 32px);
  will-change: transform;
}

/* Знак-логотип (грузится из logo.svg через <img>). */
.intro-logo-mark {
  width: clamp(96px, 16vw, 150px);
  height: auto;
  opacity: 0;
  transform: scale(0.9);
  transform-origin: center;
  will-change: transform, opacity;
}

/* Слово «Quaris» под знаком; буквы проявляются по одной (stagger). */
.intro-word {
  display: flex;
  color: #fff;
  font-size: clamp(1.8rem, 6vw, 3rem);
  font-weight: 500;
  letter-spacing: 0.04em;
}
.intro-word span {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.4em);
  will-change: transform, opacity;
}

/* ---- Ключевые кадры (только transform / opacity) ---- */
@keyframes intro-logo-in {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: scale(1); }
}
/* «Влёт камеры»: знак разрастается и на пике растворяется. */
@keyframes intro-logo-zoom {
  from { opacity: 1; transform: scale(1); }
  70%  { opacity: 1; }
  to   { opacity: 0; transform: scale(8); }
}
@keyframes intro-logo-zoom-short {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(4); }
}
@keyframes intro-letter-in {
  from { opacity: 0; transform: translateY(0.4em); }
  to   { opacity: 1; transform: translateY(0); }
}
/* Слово гаснет чуть раньше влёта. */
@keyframes intro-word-out {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-0.2em); }
}

/* ---- Полная версия (первый визит, ~2.2с) ----
   Логотип -> буквы «Quaris» по одной -> камера влетает в знак. */
.intro-full .intro-logo-mark {
  animation:
    intro-logo-in 0.6s var(--ease-smooth) 0s both,
    intro-logo-zoom 0.7s cubic-bezier(0.4, 0, 0.9, 0.3) 1.2s forwards;
}
.intro-full .intro-word { animation: intro-word-out 0.35s var(--ease-smooth) 1.15s forwards; }
.intro-full .intro-word span { animation: intro-letter-in 0.45s var(--ease-smooth) both; }
.intro-full .intro-word span:nth-child(1) { animation-delay: 0.55s; }
.intro-full .intro-word span:nth-child(2) { animation-delay: 0.63s; }
.intro-full .intro-word span:nth-child(3) { animation-delay: 0.71s; }
.intro-full .intro-word span:nth-child(4) { animation-delay: 0.79s; }
.intro-full .intro-word span:nth-child(5) { animation-delay: 0.87s; }
.intro-full .intro-word span:nth-child(6) { animation-delay: 0.95s; }

/* ---- Короткая версия (повторный визит, ~0.7с) ----
   Только знак: быстро проявился и растворился. Слово не показываем. */
.intro-short .intro-word { display: none; }
.intro-short .intro-logo-mark {
  animation:
    intro-logo-in 0.3s var(--ease-smooth) both,
    intro-logo-zoom-short 0.4s cubic-bezier(0.4, 0, 0.9, 0.3) 0.35s forwards;
}


/* ---------- Оболочка приложения ----------
   Ровно один экран: строка 1 — шапка фиксированной высоты, строка 2 — рабочая
   область на всю оставшуюся высоту. min-height:0 у второй строки обязателен,
   иначе внутренние overflow-области не смогут сжаться и начнут «толкать» экран. */
.app-shell {
  height: 100vh;
  height: 100dvh; /* мобильные: учитывает адресную строку */
  display: grid;
  grid-template-rows: auto 1fr;
  overflow: hidden;
}
.shell-body {
  min-height: 0;
  overflow: hidden;
}

/* Лендинг — отдельное состояние ровно на один экран, без прокрутки:
   всё помещается в один вид. Манифест и подвал скрыты (остаются в разметке,
   вернуть — снять display:none). */
.landing {
  height: 100%;
  overflow: hidden;
}
.landing .manifesto,
.landing .app-footer { display: none; }
/* В рабочем режиме лендинга нет в раскладке вообще — под приложением пусто. */
body.app-mode .landing { display: none; }
/* До входа в рабочий режим не показываем приложение. */
body:not(.app-mode) .workbench { display: none; }

/* ---------- Верхняя панель (топбар) ---------- */
.topbar {
  /* Первая строка .app-shell — высоту задаёт содержимое, position уже не нужен. */
  position: relative;
  z-index: 30;
  /* На самом верху шапка прозрачная — сливается с hero. Фон и размытие
     появляются только при скролле (класс .topbar-scrolled вешает app.js),
     как в Linear/Perplexity: чистый верх → «стеклянная» панель при прокрутке. */
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--transition), backdrop-filter var(--transition),
              border-color var(--transition);
}

.topbar-scrolled {
  background: rgba(10, 10, 11, 0.8);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom-color: var(--border);
}

.topbar-inner {
  /* Шапка во всю ширину окна — под ней рабочая зона край в край. */
  max-width: none;
  margin: 0;
  height: 56px;
  padding: 0 var(--space-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.topbar-brand {
  font-weight: 600;
  letter-spacing: -0.01em;
  font-size: var(--text-md);
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* «Как это работает» и EN — плоские текстовые ссылки справа, рядом с «Войти».
   Это <button>-ы, поэтому явно гасим рамку и фон, унаследованные от общего
   стиля кнопок — иначе они выглядят как обведённые кнопки. */
.topbar-link {
  border: none;
  background: transparent;
  color: var(--text-2);
  font-size: var(--text-sm);
  padding: var(--space-1) 2px;
  position: relative;
  transition: color var(--transition);
}

.topbar-link:hover { color: var(--text-1); background: transparent; }

/* Тонкая линия под пунктом при наведении — как в верхних меню сайтов */
.topbar-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transition: transform var(--transition);
}
.topbar-link:hover::after { transform: scaleX(1); }

/* EN — такая же плоская текстовая ссылка, как «Как это работает».
   Чуть жирнее и с трекингом, чтобы аббревиатура читалась как переключатель. */
.topbar-lang {
  font-weight: 600;
  letter-spacing: 0.04em;
}

/* Мини-меню выбора языка (три языка). Кнопка показывает текущий язык (RU/EN/ZH),
   клик открывает список; выбор меняет язык. Стиль — как у остальных выпадашек. */
.lang-switch { position: relative; }
.lang-menu {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 60;
  min-width: 160px;
  margin-top: var(--space-2);
  padding: var(--space-1);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  animation: session-in 0.16s var(--ease-smooth) both;
}
.lang-menu[hidden] { display: none; }
.lang-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-1);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background var(--transition);
}
.lang-option:hover { background: var(--surface-1); }
.lang-option[aria-current="true"] { color: var(--accent); }
.lang-abbr {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-3);
}
.lang-option[aria-current="true"] .lang-abbr { color: var(--accent); }

/* ☰ в шапке — плоская иконка без рамки и заливки (в тон текстовым ссылкам).
   Переопределяем общий стиль кнопки, у которой есть рамка и фон. */
.topbar-icon {
  border: none;
  background: transparent;
  color: var(--text-2);
}
.topbar-icon:hover {
  background: var(--border);
  color: var(--text-1);
}

/* Единственная «настоящая» кнопка в шапке — «Войти» (залитая). */
.topbar-login {
  padding: var(--space-2) var(--space-4);
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-size: var(--text-sm);
}

.topbar-login:hover { background: var(--surface-2); border-color: var(--border-strong); }

/* На узких экранах прячем текстовую ссылку, чтобы шапка не переполнялась. */
@media (max-width: 720px) {
  .topbar-link { display: none; }
}

/* ---------- Стартовый экран (hero) ---------- */
.hero {
  position: relative;
  min-height: 100%; /* ровно экран лендинга; манифест ниже — скроллом внутри .landing */
  display: flex;
  /* Компактная группа по центру экрана: логотип -> слоган -> строка доверия ->
     зона загрузки -> кнопка чата. overflow НЕ режем. */
  align-items: center;
  justify-content: center;
  /* Фон прозрачный: сквозь hero виден общий холст-«поток линий» (fixed под
     всем сайтом). Тёмная подложка — на html. */
  background: transparent;
}

/* Canvas-поток живёт на ВСЮ страницу: position: fixed + inset:0 держит холст на
   весь вьюпорт, z-index:-1 — под контентом, но над тёмным фоном html. Поэтому
   поле волн не обрезается низом hero и уходит за нижний край экрана. Непрозрачные
   панели рабочей зоны (чат, сайдбар) сами перекрывают фон, когда до них
   доскроллили.
   Маска (пересечение двух градиентов): по вертикали ВЕРХ поля растворяется
   (прозрачно 0…38%, полная видимость к 62%) — над зоной загрузки фон спокойный,
   шва нет; НИЗ НЕ гасим (#000 до 100%) — поле уходит за край. По горизонтали
   мягко гасим левый/правый края. */
.hero-bg {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, transparent 38%, #000 62%, #000 100%),
    linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to bottom, transparent 0%, transparent 38%, #000 62%, #000 100%),
    linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
  mask-composite: intersect;
}
/* Появление фона запускается только после интро (класс .reveal на body).
   Долгое мягкое проявление из темноты — фон «вплывает» без рывка. */
.reveal .hero-bg {
  animation: hero-bg-in 2s ease-out 0.3s both;
}

@keyframes hero-bg-in {
  to { opacity: 1; }
}

/* Общий класс для элементов, которые проявляются после заголовка —
   каждый со своей задержкой (hero-fade-*), создавая эффект "по мере загрузки" */
.hero-fade {
  opacity: 0;
  transform: translateY(18px);
}
/* Мягкое, «дорогое» появление: длиннее и с плавным ease-out cubic-bezier,
   элемент неспешно доезжает снизу и замедляется в конце. */
.reveal .hero-fade {
  animation: hero-fade-in 0.4s var(--ease-out) both;
}

@keyframes hero-fade-in {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Каскад: после заголовка (0.5s) элементы проявляются по очереди, шаг 120 мс.
   Селектор с .reveal — чтобы задержки не сбрасывались коротким свойством
   animation в правиле «.reveal .hero-fade» (та же специфичность, но эти правила
   идут ниже по файлу, поэтому побеждают). */
/* Каскад укладываем в ≤800 мс целиком: шаг 70 мс × длительность 0.4 с —
   последний элемент (статус) заканчивает на 0.72 с. Появление быстрое и
   собранное, без затянутого «по очереди». */
.reveal .hero-fade-logo     { animation-delay: 0s;    }
.reveal .hero-fade-tagline  { animation-delay: 0.08s; }
.reveal .hero-fade-subtitle { animation-delay: 0.15s; }
.reveal .hero-fade-badges   { animation-delay: 0.22s; }
.reveal .hero-fade-dropzone { animation-delay: 0.29s; }
.reveal .hero-fade-status   { animation-delay: 0.36s; }

/* Контент поверх фона; текст не должен размываться линиями */
.hero-content {
  position: relative;
  z-index: 1;
  max-width: 640px;
  width: 100%;
  /* Группа целиком по центру: интервалы 16–24px, без пустот в пол-экрана. */
  margin: 0;
  padding: var(--space-5);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px; /* единый интервал группы (16–24px по требованию) */
}
/* Заметная акцентная кнопка входа в рабочий режим с чатом. */
.hero-cta {
  padding: var(--space-3) var(--space-6);
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: var(--text-md);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition), transform var(--transition),
              box-shadow var(--transition);
  box-shadow: var(--shadow-sm);
}
.hero-cta:hover {
  background: var(--accent-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}
.hero-cta:active { transform: translateY(0); }

/* Крупный логотип Quaris в центре hero — главный акцент экрана. Вес 700,
   ~64px (плавно уменьшается на узких), плотный трекинг делает его собранным.
   Header-логотип остаётся дублем, который виден при прокрутке. */
.hero-logo {
  margin: 0 0 var(--space-2);
  font-size: clamp(2.6rem, 8vw, 4rem); /* ≈ 42…64px */
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--text-1);
}

/* Строка-приглашение под логотипом — подзаголовочный слоган. Отступы держим
   едиными (кратными 8px), чтобы вертикальный ритм hero был ровным. */
.hero-tagline {
  margin: 0 0 var(--space-2);
  /* Слоган под логотипом — заметно мельче него (≈24…32px), чтобы иерархия
     читалась «логотип → слоган». Плотный трекинг держит текст собранным. */
  font-size: clamp(1.4rem, 4vw, 2rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-1);
}

/* Мелкий серый подзаголовок под ней */
.hero-subtitle {
  margin: 0 0 var(--space-2);
  font-size: var(--text-md);
  color: var(--text-2);
}

/* Строка «доверия» ещё мельче и приглушённее. На широком экране держим её
   одной строкой; мобильный fallback ниже разрешает аккуратный перенос. */
.hero-badges {
  margin: 0 0 var(--space-4); /* до зоны загрузки — чуть больше, но без пустоты */
  font-size: var(--text-xs);
  color: var(--text-3);
  white-space: nowrap;
}

/* ---------- Зона загрузки файла ---------- */
.dropzone {
  position: relative;
  /* Сплошная тонкая рамка вместо пунктира: пунктир читается «дёшево», как
     заглушка. Ровная линия 1px в 8% белого — спокойно и по-взрослому. */
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-7) var(--space-5);
  cursor: pointer;
  background: var(--surface-1);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--transition), background var(--transition),
              transform var(--transition), box-shadow var(--transition);
}

/* Ховер: рамка чуть ярче, карточка мягко приподнимается и отбрасывает тень —
   ощущение отзывчивой физической поверхности. */
.dropzone:hover {
  border-color: var(--border-strong);
  background: var(--surface-2);
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

/* При перетаскивании файла над зоной — плавная яркая подсветка рамки и лёгкое
   увеличение. Переход задан в .dropzone (border-color/background/transform). */
.dropzone-active {
  border-color: var(--accent);
  background: var(--accent-weak);
  box-shadow: 0 0 0 3px var(--accent-weak);
  transform: scale(1.03);
}

.dropzone-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

/* Иконка документа (inline-SVG). Наследует цвет через currentColor, поэтому
   при ховере/перетаскивании подсвечивается вместе с текстом зоны. */
.dropzone-icon {
  display: flex;
  justify-content: center;
  margin-bottom: var(--space-3);
  color: var(--text-2);
  transition: color var(--transition), transform var(--transition);
}
.dropzone:hover .dropzone-icon,
.dropzone-active .dropzone-icon {
  color: var(--text-1);
  transform: translateY(-2px);
}

.dropzone-text {
  font-size: var(--text-md);
  color: var(--text-1);
}

.dropzone-link {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.dropzone:hover .dropzone-link { color: var(--accent-hover); }

.dropzone-hint {
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-2);
}

.hero-status {
  margin-top: var(--space-4);
  min-height: 1.2em;
  font-size: var(--text-sm);
  color: var(--text-2);
}

/* Ритм группы задаёт ТОЛЬКО gap у .hero-content. Правило идёт ПОСЛЕ всех
   .hero-* и .dropzone, поэтому перебивает их собственные margin — иначе
   интервалы разъезжаются до 50–70px. */
.hero-content > * { margin: 0; }
/* Пустая строка статуса не должна занимать место и добавлять лишний интервал. */
.hero-status:empty { display: none; }

/* ---------- Элементы форм (переиспользуются везде) ---------- */
input[type="text"], input[type="password"], input[type="file"], select {
  width: 100%;
  padding: var(--space-2);
  margin-bottom: var(--space-2);
  box-sizing: border-box;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-1);
  font-family: inherit;
  font-size: var(--text-sm);
  transition: border-color var(--transition), box-shadow var(--transition),
              background-color var(--transition);
}

/* Мягкое подсвечивание поля при фокусе */
input[type="text"]:focus, input[type="password"]:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-weak);
}

button {
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-1);
  font-family: inherit;
  font-size: var(--text-sm);
  /* Плавные ховер/нажатие — единый тайминг 160 мс */
  transition: background-color var(--transition), border-color var(--transition),
              transform var(--transition), box-shadow var(--transition);
}

button:hover { border-color: var(--border-strong); }
button:active { transform: translateY(1px); }
.icon-button:hover { transform: translateY(-1px); }

.icon-button {
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  font-size: var(--text-md);
  line-height: 1;
}

/* ---------- Доступность: единое кольцо фокуса с клавиатуры ----------
   :focus-visible срабатывает только при навигации Tab'ом, не мешая мыши.
   Кольцо — акцентного цвета, с тёмным зазором под ним (смотрится на любом фоне). */
:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
/* Полям ввода оставляем их мягкую подсветку, но при клавиатурном фокусе —
   такое же чёткое кольцо, как у кнопок. */
input:focus-visible, select:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
/* Скрытый файловый input не должен рисовать своё кольцо — вместо него
   подсвечиваем всю зону загрузки через :focus-within. */
.dropzone-input:focus-visible { box-shadow: none; }
.dropzone:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-weak);
}

.muted { color: var(--text-2); font-size: var(--text-sm); }

pre {
  background: var(--sidebar);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow-x: auto;
  white-space: pre-wrap;
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

/* ---------- Приложение: сайдбар + чат ---------- */
/* Сплошной непрозрачный фон: рабочая зона полностью перекрывает всё, что под
   ней, — фоновые линии hero сюда не просвечивают (они и так обрезаны hero,
   но плотный фон — вторая страховка). */
/* ---------- Рабочая область (раскладка в стиле VS Code) ----------
   Колонки: активити-полоса | раскрывающаяся панель | результаты | чат.
   В свёрнутом состоянии колонка панели = 0, центр и чат делят ширину 50/50.
   Ширины анимируются через transition grid-template-columns (200мс). */
.workbench {
  position: relative;
  display: grid;
  grid-template-columns: 48px 0 1fr 1fr;
  grid-template-areas: "activity panel results chat";
  /* Заполняет вторую строку оболочки. Скроллятся ТОЛЬКО внутренние области
     (панель, результаты, лента чата) — сама рабочая зона не прокручивается. */
  height: 100%;
  min-height: 0;
  /* Край в край: ширина всего окна, без полей и центрирования — центр и чат
     забирают всю доступную ширину. */
  width: 100%;
  max-width: none;
  margin: 0;
  border-top: 1px solid var(--border);
  background: var(--bg);
  transition: grid-template-columns 0.28s var(--ease-smooth);
}
.workbench.panel-open {
  grid-template-columns: 48px 260px 1fr 1fr;
}

/* Активити-полоса: всегда видна. Лого сверху, ниже иконки-переключатели.
   Зафиксирована (sticky) — при скролле страницы стоит на месте, как в VS Code. */
.activity-bar {
  grid-area: activity;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) 0;
  background: var(--sidebar);
  border-right: 1px solid var(--border);
}
.activity-logo {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  margin-bottom: var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  font-size: 15px;
}
.activity-item {
  position: relative;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-3);
  cursor: pointer;
  transition: color var(--transition), background var(--transition);
}
.activity-item:hover { color: var(--text-1); background: var(--surface-2); }
.activity-item-active { color: var(--text-1); }
/* Индикатор активной вкладки — акцентная планка у левого края полосы (VS Code). */
.activity-item-active::before {
  content: "";
  position: absolute;
  left: -4px;
  top: 8px;
  bottom: 8px;
  width: 2px;
  border-radius: 1px;
  background: var(--accent);
}

/* Раскрывающаяся панель. Зафиксирована (sticky), как активити-полоса.
   overflow:hidden аккуратно подрезает содержимое, пока колонка ужимается к нулю. */
.side-panel {
  grid-area: panel;
  height: 100%;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  background: var(--sidebar);
  border-right: 1px solid var(--border);
}
.workbench:not(.panel-open) .side-panel { border-right: none; }
.panel-view {
  width: 260px; /* фиксированная ширина контента — не «схлопывается» при анимации */
  height: 100%;
  overflow-y: auto; /* длинный список файлов/сессий скроллится внутри панели */
  padding: var(--space-4);
}
.panel-view[hidden] { display: none; }
.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-3);
}
.panel-title {
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase; /* «ФАЙЛЫ» / «СЕССИИ» — капсом средствами CSS */
  letter-spacing: 0.06em;
  color: var(--text-2);
}

.file-catalog-status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-size: var(--text-xs);
}
.file-catalog-status[hidden] { display: none; }
.file-catalog-retry {
  flex: none;
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--accent);
  font-size: inherit;
  cursor: pointer;
}

.file-pair-picker {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 28px;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
}
.file-role-select {
  grid-column: 1;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 4px var(--space-2);
  min-width: 0;
  color: var(--text-2);
  font-size: var(--text-xs);
}
.file-role-select select {
  grid-column: 1 / -1;
  width: 100%;
  min-width: 0;
  padding: 6px 24px 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-1);
  font: inherit;
  text-overflow: ellipsis;
}
.file-role-select select:disabled { opacity: 0.55; }
.file-role-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  background: var(--accent-weak);
  color: var(--accent);
  font-size: 10px;
  font-weight: 700;
}
.file-swap-btn {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-2);
  cursor: pointer;
}
.file-swap-btn:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.file-swap-btn:disabled { opacity: 0.4; cursor: default; }

/* Центр: зона результатов (пока заглушка). */
.results {
  grid-area: results;
  display: grid;
  place-items: center; /* заглушка по центру, пока результата нет */
  min-width: 0;
  min-height: 0;
  padding: var(--space-4);
  border-right: 1px solid var(--border);
  overflow: auto;
}
.results-empty {
  text-align: center;
  font-size: var(--text-sm);
}

/* Есть результат: контент течёт сверху вниз (таблица -> график), со скроллом
   внутри самой зоны. Между блоками — единый вертикальный ритм. */
.results.results-filled {
  display: block;
  align-content: start;
}
.results.results-filled > * + * {
  margin-top: var(--space-4);
}
/* Прямые дети зоны результатов тянутся на всю её ширину. Без явной ширины
   блоки схлопывались до min-content (таблица 160px, график 300px) — контейнер
   с overflow:auto не растягивал их сам. */
.results.results-filled > * {
  width: 100%;
  min-width: 0;
}

.file-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.file-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: default;
  transition: background var(--transition), border-color var(--transition);
}

.file-item[data-selectable="true"] {
  cursor: pointer;
}

.file-item[data-selectable="true"]:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
}

/* Активный файл: рамка обычная, но слева — тонкая акцентная планка 2px.
   Рисуем её внутренней тенью (inset), чтобы не сдвигать содержимое карточки. */
.file-item-active {
  border-color: var(--border);
  background: var(--surface-2);
  box-shadow: inset 2px 0 0 var(--accent);
}

.file-status {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-top: 5px;
  background: var(--text-3);
}

/* «Золотая» точка №2 — маркер успешно загруженного файла (тёплое золото —
   единственное тёплое пятно в списке файлов, как лёгкая «печать готовности»). */
.file-status-ready { background: var(--gold); }
.file-status-error { background: var(--danger); }
.file-status-sheet_selection_required { background: var(--accent); }
.file-status-uploading, .file-status-processing {
  background: var(--text-3);
  animation: pulse-status 1s ease-in-out infinite;
}

@keyframes pulse-status {
  50% { opacity: 0.3; }
}

.file-info {
  min-width: 0;
  flex: 1;
}

.file-name {
  font-size: var(--text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-role-badges {
  display: inline-flex;
  gap: 3px;
  margin-right: 5px;
  vertical-align: middle;
}

.file-meta {
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--text-3); /* размер файла — самый тихий, третий уровень */
  line-height: 1.45;
}

/* Обязательный выбор листа остаётся внутри карточки файла: его нельзя закрыть
   или принять за необязательную подсказку. В UI выводятся только публичные
   имена листов — технические sheet_id используются исключительно в запросе. */
.sheet-picker {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1px solid rgba(91, 124, 250, 0.32);
  border-radius: var(--radius-md);
  background: var(--accent-weak);
}
.sheet-picker-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-1);
}
.sheet-picker-description {
  margin: 0;
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--text-2);
}
.sheet-picker-choices {
  display: grid;
  gap: var(--space-2);
}
.sheet-choice {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 3px var(--space-2);
  align-items: center;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  color: var(--text-1);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition), opacity var(--transition);
}
.sheet-choice:hover:not(:disabled) {
  border-color: var(--accent);
  background: var(--surface-2);
}
.sheet-choice:disabled { cursor: default; opacity: 0.65; }
.sheet-choice-processing { border-color: var(--accent); }
.sheet-choice-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-sm);
  font-weight: 500;
}
.sheet-choice-state {
  font-size: var(--text-xs);
  color: var(--text-2);
  white-space: nowrap;
}
.sheet-choice-state-present { color: var(--success); }
.sheet-choice-meta {
  grid-column: 1 / -1;
  font-size: var(--text-xs);
  color: var(--text-3);
}
.sheet-materialization,
.sheet-picker-error {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--text-2);
}
.sheet-materialization strong { color: var(--text-1); }
.sheet-picker-error { color: var(--danger); }
.sheet-picker-request-id { color: var(--text-3); }
.sheet-picker-retry {
  align-self: flex-start;
  margin-top: var(--space-1);
  padding: 5px 9px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-1);
  font: inherit;
  cursor: pointer;
}
.sheet-picker-retry:hover { border-color: var(--accent); }

/* Пустой список файлов: спокойная иконка-папка + две строки-подсказки.
   Рендерится из app.js, когда файлов ещё нет. */
.file-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-3);
}
.file-empty-icon {
  color: var(--text-3);
  opacity: 0.7;
}
.file-empty-title {
  font-size: var(--text-sm);
  color: var(--text-2);
}
.file-empty-hint {
  font-size: var(--text-xs);
  color: var(--text-3);
}

/* Заголовок группы в списке файлов («В этой сессии» / «Загруженные ранее»):
   тихая надпись капсом — членит список, не перетягивая внимание. */
.file-group {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-3);
  padding-top: var(--space-2);
}
.file-group:first-child {
  padding-top: 0;
}
.file-group-empty {
  font-size: var(--text-xs);
  color: var(--text-3);
  padding: 0 var(--space-1) var(--space-1);
}

/* «Загруженные ранее» чуть тише активной сессии; клик подключает файл. */
.file-item-earlier {
  opacity: 0.82;
}
.file-item-earlier:hover {
  opacity: 1;
}

/* ---------- Сессии (сайдбар в стиле Claude.ai) ---------- */

/* Кнопка «+ Новая сессия»: во всю ширину, поверхность из дизайн-системы. */
.session-new-btn {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
  /* Заметная кнопка вверху, как «+ New chat» в Claude. */
  background: var(--accent-weak);
  border: 1px solid rgba(91, 124, 250, 0.35);
  border-radius: var(--radius-md);
  color: var(--text-1);
  font-size: var(--text-sm);
  font-weight: 600;
  transition: background var(--transition), border-color var(--transition);
}
.session-new-btn:hover {
  background: rgba(91, 124, 250, 0.22);
  border-color: var(--accent);
  transform: translateY(-1px);
}

/* Заголовок секции списка («Закреплённые» / «Недавние») — тихая метка. */
.session-section {
  padding: var(--space-3) var(--space-3) var(--space-1);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
}
.session-section:first-child { padding-top: 0; }

/* Секции сайдбара: «Сессии» и «Файлы текущей сессии» — с воздухом между ними. */

.session-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* Карточка сессии: название + дата, ⋯-меню появляется на hover. */
.session-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}
/* Название одной строкой с обрезкой — не даём длинному имени ломать вёрстку. */
.session-info { min-width: 0; flex: 1; }
.session-item:hover { background: var(--surface-1); }

/* Активная сессия: поверхность светлее + акцентная планка 2px слева (inset). */
.session-item-active {
  background: var(--surface-2);
  box-shadow: inset 2px 0 0 var(--accent);
}

.session-info { min-width: 0; flex: 1; }
.session-title {
  font-size: var(--text-sm);
  color: var(--text-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.session-date {
  margin-top: 1px;
  font-size: var(--text-xs);
  color: var(--text-3); /* дата — третий уровень серого, мелким */
}

/* Кнопка ⋯ — прячется, проявляется на hover или у активной карточки. */
.session-menu-btn {
  flex: none;
  width: 24px;
  height: 24px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--text-2);
  border-radius: var(--radius-sm);
  opacity: 0;
  transition: opacity var(--transition), background var(--transition);
}
.session-item:hover .session-menu-btn,
.session-item-active .session-menu-btn { opacity: 1; }
.session-menu-btn:hover { background: var(--surface-2); }

/* Выпадающее меню карточки. */
.session-menu {
  position: absolute;
  top: 100%;
  right: var(--space-2);
  z-index: 20;
  min-width: 160px;
  margin-top: 4px;
  padding: var(--space-1);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  animation: session-in 0.16s var(--ease-out) both;
}
.session-menu-item {
  text-align: left;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-1);
  font-size: var(--text-sm);
}
.session-menu-item:hover { background: var(--surface-1); }
.session-menu-item-danger { color: var(--danger); }
.session-menu-item-danger:hover { background: rgba(229, 128, 127, 0.12); }

/* Инлайн-переименование. */
.session-rename-input {
  width: 100%;
  padding: 2px var(--space-2);
  font-size: var(--text-sm);
  background: var(--bg);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--text-1);
}

/* Пустое состояние — сессий ещё нет. */
.session-empty {
  padding: var(--space-4) var(--space-3);
  text-align: center;
  font-size: var(--text-xs);
  color: var(--text-3);
}

/* Появление новой сессии в списке — opacity + сдвиг снизу. */
@keyframes session-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.session-item-new { animation: session-in 0.3s var(--ease-out) both; }

/* Удаление — fade + лёгкое сжатие. */
@keyframes session-out {
  to { opacity: 0; transform: scale(0.98); }
}
.session-item-removing {
  animation: session-out 0.2s var(--ease-out) forwards;
  pointer-events: none;
}

/* ---------- Чат ---------- */
/* Непрозрачная поверхность №1 (--surface-1, #101624): колонка чата плотно
   лежит поверх фона .app — линии hero сквозь неё не видны. */
.chat {
  grid-area: chat;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0; /* высоту задаёт строка грида .workbench; лента скроллится внутри */
  background: var(--surface-1);
}

.chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}

.chat-feed {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-4); /* 16px между сообщениями — плотнее, без лишнего воздуха */
  /* Мягкий кросс-фейд при переключении сессий (200мс). */
  transition: opacity 0.2s var(--ease-out);
}
.chat-feed-leaving { opacity: 0; }

.chat-empty {
  margin: auto;
  text-align: center;
}

.msg {
  font-size: var(--text-sm);
  white-space: pre-wrap;
  /* Каждое сообщение мягко «въезжает»: только opacity+translateY (композитор),
     300 мс с плавным ease-out — лента ощущается живой, но не суетливой. */
  animation: msg-in 0.3s var(--ease-out) both;
}

@keyframes msg-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Вопрос пользователя — компактная карточка справа (не шире 70%). */
.msg-user {
  align-self: flex-end;
  max-width: 70%;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  border: 1px solid var(--border);
}

/* Ответ ассистента — БЕЗ карточки: обычный текст на фоне ленты, слева, во всю
   ширину. Так ответ читается как «документ», а не как пузырь чата (Perplexity). */
.msg-assistant {
  align-self: flex-start;
  max-width: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.msg-error {
  align-self: center;
  max-width: 90%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: rgba(229, 128, 127, 0.10);
  border: 1px solid rgba(229, 128, 127, 0.35);
  color: var(--danger);
}

.msg-error-guidance {
  color: var(--text-1);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Предупреждение сопровождает успешный результат и визуально отличается от
   отказа: текстовая метка + иконка/рамка, поэтому смысл не зависит от цвета. */
.warning-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border: 1px solid rgba(251, 191, 36, 0.32);
  border-radius: var(--radius-md);
  background: rgba(251, 191, 36, 0.08);
  color: var(--text-1);
}

.warning-title {
  color: var(--warning);
  font-size: var(--text-sm);
}

.warning-title::before {
  content: "⚠ ";
}

.warning-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin: 0;
  padding-left: var(--space-5);
  font-size: var(--text-sm);
}

.warning-scope {
  margin-right: var(--space-1);
}

.file-warning-card {
  margin-top: var(--space-2);
  padding: var(--space-2);
  gap: var(--space-1);
}

.file-warning-card .warning-title,
.file-warning-card .warning-list {
  font-size: var(--text-xs);
}

.file-schema-error {
  margin-top: var(--space-2);
  padding: var(--space-2);
  border: 1px solid rgba(229, 128, 127, 0.28);
  border-radius: var(--radius-sm);
  background: rgba(229, 128, 127, 0.08);
  color: var(--danger);
  font-size: var(--text-xs);
  line-height: 1.45;
}

/* Заблокированное на время запроса поле/кнопка — приглушаем и меняем курсор. */
.chat-input input:disabled,
.chat-input button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.msg-meta {
  color: var(--text-2);
  font-size: var(--text-xs);
  overflow-wrap: anywhere;
}

.msg-action,
.msg-stop-waiting {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 7px 11px;
  background: var(--surface-2);
  color: var(--text-1);
  font: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
  transition: border-color var(--transition), color var(--transition), opacity var(--transition);
}
.msg-action:hover:not(:disabled),
.msg-stop-waiting:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}
.msg-action:disabled,
.msg-stop-waiting:disabled {
  cursor: default;
  opacity: 0.5;
}

.msg-retry {
  border-color: rgba(229, 128, 127, 0.45);
  color: var(--danger);
}

.msg-text { white-space: pre-wrap; }

.msg-assistant[data-kind="clarification"] {
  padding-left: var(--space-3);
  border-left: 2px solid var(--accent);
}
.msg-kind-label {
  color: var(--accent);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.msg-clarification-options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.msg-clarification-option {
  border-color: rgba(91, 124, 250, 0.45);
}

.msg-pending {
  align-self: flex-start;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  color: var(--text-2);
}
.msg-pending > div:first-child::after {
  content: "";
  display: inline-block;
  width: 1.3em;
  animation: pending-dots 1.4s steps(4, end) infinite;
  overflow: hidden;
  vertical-align: bottom;
}
@keyframes pending-dots {
  0% { content: ""; }
  25% { content: "."; }
  50% { content: ".."; }
  75%, 100% { content: "..."; }
}

/* «Показать SQL-запрос» — плоская текстовая кнопка-переключатель. */
.msg-sql-toggle {
  border: none;
  background: transparent;
  padding: 0;
  color: var(--text-2);
  font-size: var(--text-xs);
  cursor: pointer;
  transition: color var(--transition);
}
.msg-sql-toggle:hover { color: var(--text-1); }

/* Раскрытие запроса анимируем через grid-template-rows 0fr → 1fr (и opacity).
   Это единственный способ плавно «раскрыть» блок неизвестной высоты, НЕ трогая
   height/margin — значит без Layout-реоу, всё считает композитор. overflow:hidden
   аккуратно подрезает содержимое, пока строка сжата в 0fr. */
.msg-sql-body {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  overflow: hidden;
  transition: grid-template-rows 0.25s var(--ease-out), opacity 0.25s var(--ease-out);
}
.msg-sql-open .msg-sql-body {
  grid-template-rows: 1fr;
  opacity: 1;
}
.msg-sql-body > pre {
  min-height: 0; /* обязательно для сжатия grid-элемента в ноль */
  margin: var(--space-2) 0 0;
}

/* Подсветка SQL (highlight.js): от темы atom-one-dark берём ТОЛЬКО цвета
   токенов. Фон, паддинг и шрифт темы гасим — фон блока задаёт наш pre
   (дизайн-система, --sidebar), шрифт остаётся --font-mono, перенос строк
   наследуется от pre (white-space: pre-wrap — длинные запросы читаемы). */
.msg-sql-body pre code.hljs {
  background: transparent;
  padding: 0;
  font-family: inherit;
  font-size: inherit;
  white-space: inherit;
  overflow-x: visible;
}

.msg-note {
  font-size: var(--text-xs);
  color: var(--text-2);
  font-style: italic;
}

/* JOIN-аудит виден сразу над таблицей: несовпадения — отдельные карточки,
   а не тихая подпись или спойлер. */
.join-audit {
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-1);
}
.join-audit-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-sm);
}
.join-type {
  color: var(--text-3);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}
.join-keys {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.join-key {
  padding: 4px 8px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}
.join-metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.join-metric {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-2);
  font-size: var(--text-xs);
}
.join-metric strong { color: var(--text-1); font-size: var(--text-lg); }
.join-metric-warning {
  border-color: rgba(248, 113, 113, 0.35);
  background: rgba(248, 113, 113, 0.08);
}
.join-metric-warning strong { color: #fca5a5; }

.msg-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.msg-table {
  border-collapse: collapse;
  width: 100%;
  font-size: var(--text-xs);
}

.msg-table th, .msg-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  text-align: left;
  white-space: nowrap;
}

.msg-table th {
  background: var(--surface-2);
  position: sticky;
  top: 0;
}

/* Пустая/null ячейка: пометка «(не указано)» — тихим курсивом, чтобы
   отличалась от настоящих значений, но не кричала. */
.msg-table-null {
  color: var(--text-3);
  font-style: italic;
}

.msg-table-more {
  display: block;
  width: 100%;
  border-radius: 0;
  border: none;
  border-top: 1px solid var(--border);
}

.msg-table-more[hidden] { display: none; }

/* Область графика: фиксированная высота нужна из-за maintainAspectRatio:false
   (так Chart.js занимает всю ширину зоны результатов и не «прыгает»).
   Воздух вокруг — по шкале отступов, в духе Linear/Vercel. */
.msg-chart {
  /* Растягивается по доступной ширине центра; Chart.js responsive сам
     пересчитает холст. Высота фиксированная — из-за maintainAspectRatio:false. */
  position: relative; /* якорь для кнопки сброса зума */
  width: 100%;
  height: 340px;
  padding: var(--space-3) 0 var(--space-2);
}

/* Кросс-подсветка: строка таблицы, соответствующая элементу графика. */
.msg-table tbody tr.msg-table-row-hl {
  background: var(--accent-weak);
}

/* Уголок зума справа сверху: подсказка про Shift-рамку + кнопка сброса. */
.chart-zoom-tools {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
/* Подсказка — самый тихий серый; появляется только при наведении на график,
   чтобы не висеть над данными постоянно. pointer-events:none — не мешает pan. */
.chart-zoom-hint {
  font-size: var(--text-xs);
  color: var(--text-3);
  opacity: 0;
  transition: opacity var(--transition);
  pointer-events: none;
}
.msg-chart:hover .chart-zoom-hint { opacity: 1; }

/* Кнопка «Сбросить масштаб» — видна только при активном зуме. */
.chart-reset-btn {
  padding: 4px 10px;
  font-size: var(--text-xs);
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: color var(--transition), border-color var(--transition);
}
.chart-reset-btn:hover { color: var(--text-1); border-color: var(--accent); }
.chart-reset-btn[hidden] { display: none; }

/* Внешний тултип доната: HTML-карточка вне кольца (см. donutExternalTooltip).
   pointer-events:none — не перехватывает курсор; появление мягкое, 120мс. */
.chart-ext-tooltip {
  position: absolute;
  z-index: 3;
  max-width: calc(100% - 8px); /* в узком контейнере имя ужимается многоточием */
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 8px 10px;
  background: var(--surface-1);            /* #101624 */
  border: 1px solid var(--border);          /* rgba(148,163,200,0.10) */
  border-radius: 8px;
  box-shadow: var(--shadow-md);
  font-size: var(--text-xs);
  color: var(--text-2);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.12s ease;
  pointer-events: none;
}
.chart-ext-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
}
.chart-ext-name {
  color: var(--text-1);
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- Скачивание результата (CSV/Excel над таблицей, PNG над графиком) ---------- */
.msg-table-block { margin-top: var(--space-1); }

.msg-table-tools {
  display: flex;
  justify-content: flex-end; /* панель действий — справа над таблицей */
  margin-bottom: var(--space-2);
}
.msg-chart-tools {
  display: flex;
  justify-content: flex-end;
  margin: var(--space-2) 0;
}

.download-group { position: relative; display: inline-flex; }

.download-btn,
.download-caret {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-2);
  font-size: var(--text-xs);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.download-btn { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.download-caret {
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  border-left: none;
}
/* Одиночная кнопка без выпадашки (PNG у графика) — скруглена со всех сторон. */
.msg-chart-tools .download-btn { border-radius: var(--radius-sm); }
.download-btn:hover,
.download-caret:hover {
  background: var(--surface-1);
  color: var(--text-1);
  border-color: var(--border-strong);
}

.download-menu {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 20;
  min-width: 200px;
  margin-top: 4px;
  padding: var(--space-1);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  animation: session-in 0.16s var(--ease-out) both;
}
.download-menu[hidden] { display: none; }
.download-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-1);
  font-size: var(--text-sm);
  cursor: pointer;
}
.download-menu-item:hover { background: var(--surface-1); }

.chat-input {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border);
}

/* Поле ввода — высокое (48px), спокойная поверхность; фокус подсвечивает
   акцентной рамкой и мягким свечением (см. input:focus в блоке форм). */
.chat-input input {
  margin-bottom: 0;
  height: 48px;
  padding: 0 var(--space-4);
  background: var(--surface-1);
}

/* Кнопка «Спросить»: в покое — приглушённая поверхность. Акцентной (активной)
   становится ТОЛЬКО когда в поле есть текст (класс .has-text вешает app.js) —
   интерфейс подсказывает, что вопрос готов к отправке. */
.chat-input button {
  height: 48px;
  background: var(--surface-2);
}
.chat-input button:disabled {
  opacity: 0.55;
  cursor: wait;
}
.chat-input.has-text button {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
}
.chat-input.has-text button:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

/* ---------- Панель настроек ---------- */
.settings-panel {
  position: fixed;
  top: 0;
  right: 0;
  width: 320px;
  max-width: 90vw;
  height: 100vh;
  background: var(--surface-1);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  padding: var(--space-4);
  /* Выезжает через transform (translateX) — это анимирует композитор, без
     Layout. 250 мс с ease-out даёт лёгкий «пружинистый» доводчик. */
  transform: translateX(100%);
  transition: transform 0.28s var(--ease-smooth);
  /* Выше топбара (z-index 30): иначе фиксированный топбар перекрывает крестик
     закрытия в правом верхнем углу и «съедает» клики по нему. */
  z-index: 50;
  overflow-y: auto;
}

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

.settings-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}

.settings-header h2 { font-size: var(--text-md); margin: 0; }

.settings-field {
  display: block;
  margin-bottom: var(--space-4);
  font-size: var(--text-xs);
  color: var(--text-2);
}

.settings-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s var(--ease-smooth);
  /* Над топбаром (30), под панелью (50): затемняет и топбар тоже, а клик по
     нему закрывает настройки (клик «вне» панели). */
  z-index: 45;
}

.settings-overlay-visible {
  opacity: 1;
  pointer-events: auto;
}

/* ---------- Окно «Как это работает» ---------- */
.hiw-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  z-index: 40;
}
.hiw-overlay-visible {
  opacity: 1;
  pointer-events: auto;
}

.hiw-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  /* Стартовое состояние — чуть уменьшено и прозрачно; открытие плавно "всплывает" */
  transform: translate(-50%, -48%) scale(0.98);
  width: min(640px, calc(100vw - 32px));
  max-height: min(80vh, 720px);
  overflow-y: auto;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5) var(--space-6) var(--space-6);
  z-index: 41;
  opacity: 0;
  pointer-events: none;
  box-shadow: var(--shadow-md);
  transition: opacity 0.25s ease, transform 0.25s var(--ease-out);
}
.hiw-modal-open {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}

.hiw-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-3);
}
.hiw-header h2 { margin: 0; font-size: var(--text-xl); }

.hiw-body h3 {
  margin: var(--space-5) 0 var(--space-2);
  font-size: var(--text-md);
  color: var(--text-1);
}
.hiw-intro {
  color: var(--text-2);
  margin: 0 0 var(--space-1);
}

/* Честное ограничение числовой точности — спокойная справочная заметка,
   а не runtime-warning или ошибка. */
.hiw-numeric-note {
  margin: var(--space-3) 0 0;
  padding: var(--space-3) var(--space-4);
  border-left: 2px solid var(--border-strong);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--surface-2);
  color: var(--text-2);
  font-size: var(--text-sm);
}

/* Шаги — нумерованный список с кружками-числами и вертикальной линией-связкой
   между ними (как в онбордингах Linear/Stripe). Числа рисуем счётчиком CSS,
   поэтому разметка остаётся простым <ol>. */
.hiw-steps {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: hiw;
  display: grid;
  gap: var(--space-4);
}
.hiw-steps li {
  counter-increment: hiw;
  position: relative;
  padding-left: var(--space-6);
}
/* Кружок с номером шага */
.hiw-steps li::before {
  content: counter(hiw);
  position: absolute;
  left: 0;
  top: 0;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  font-size: var(--text-xs);
  color: var(--text-2);
  background: var(--surface-1);
}
/* Тонкая вертикальная связка от кружка к следующему шагу */
.hiw-steps li::after {
  content: "";
  position: absolute;
  left: 11px;
  top: 28px;
  bottom: calc(var(--space-4) * -1);
  width: 1px;
  background: var(--border);
}
.hiw-steps li:last-child::after { display: none; }
.hiw-step-title {
  display: block;
  font-weight: 600;
  color: var(--text-1);
}
.hiw-step-desc {
  display: block;
  color: var(--text-2);
  font-size: var(--text-sm);
}

.hiw-why {
  margin: 0;
  padding-left: var(--space-5);
  display: grid;
  gap: var(--space-2);
  color: var(--text-2);
}

/* ---------- Всплывающее уведомление (тост) ---------- */
.toast {
  position: fixed;
  bottom: var(--space-5);
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--surface-2);
  border: 1px solid var(--border);
  padding: var(--space-3) var(--space-5);
  border-radius: 999px;
  font-size: var(--text-sm);
  box-shadow: var(--shadow-md);
  opacity: 0;
  transition: opacity 0.25s ease, transform 0.25s var(--ease-out);
  z-index: 50;
  pointer-events: none;
}

.toast-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ---------- Секция-манифест (под hero) ---------- */
.manifesto {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-8) var(--space-5);
}

.manifesto-inner {
  max-width: 1000px;
  width: 100%;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-8);
}

/* Крупная фраза — тот же «дорогой» стиль, что и слоган hero (40px/600/-0.02em),
   первый уровень белого. */
.manifesto-phrase {
  margin: 0;
  font-size: clamp(1.9rem, 5vw, 2.5rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-1);
}

/* Три тезиса в ряд; на мобильном (см. медиазапрос ниже) — колонкой. */
.manifesto-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  width: 100%;
}

.manifesto-card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5);
  text-align: left;
}

.manifesto-card-title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--text-1);
}

.manifesto-card-desc {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--text-2);
}

/* Утилита появления при скролле: элемент ждёт прихода в область видимости,
   затем app.js вешает .in-view и он мягко всплывает (opacity + translateY). */
.reveal-on-scroll {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.4s var(--ease-out), transform 0.4s var(--ease-out);
}
.reveal-on-scroll.in-view {
  opacity: 1;
  transform: none;
}
/* Лёгкая «лесенка» для карточек — вторая и третья появляются чуть позже. */
.manifesto-card.reveal-on-scroll:nth-child(2) { transition-delay: 0.08s; }
.manifesto-card.reveal-on-scroll:nth-child(3) { transition-delay: 0.16s; }

/* ---------- Футер ---------- */
.app-footer {
  position: relative;
  padding: var(--space-5) var(--space-4) var(--space-6);
  text-align: center;
}

.app-footer p {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--text-2);
}

/* Подпись команды — прижата к правому нижнему углу, тихий третий уровень. */
.footer-credit {
  position: absolute;
  right: var(--space-4);
  bottom: var(--space-3);
  font-size: var(--text-xs);
  letter-spacing: 0.03em;
  color: var(--text-3);
}

/* ---------- Адаптив ---------- */
@media (max-width: 860px) {
  .hero-badges {
    max-width: min(100%, 420px);
    white-space: normal;
    text-wrap: balance;
  }

  /* Узкий экран: результаты сверху, чат снизу; панель — оверлеем поверх,
     активити-полоса остаётся на месте. */
  .workbench,
  .workbench.panel-open {
    grid-template-columns: 48px 1fr;
    grid-template-areas:
      "activity results"
      "activity chat";
    grid-template-rows: minmax(0, 0.9fr) minmax(0, 1.4fr);
  }

  .side-panel {
    position: absolute;
    top: 0;
    left: 48px;
    bottom: 0;
    width: min(280px, 72vw);
    z-index: 30;
    border-right: 1px solid var(--border);
    box-shadow: var(--shadow-md);
    transform: translateX(-102%);
    transition: transform 0.28s var(--ease-smooth);
  }
  .workbench.panel-open .side-panel { transform: translateX(0); }

  .results {
    border-right: none;
    border-bottom: 1px solid var(--border);
  }

  .settings-panel {
    width: 85vw;
  }

  /* Тезисы манифеста на узком экране — колонкой, друг под другом. */
  .manifesto-cards {
    grid-template-columns: 1fr;
  }
}

/* ---------- Уважение к «уменьшить движение» ----------
   Если в системе включён prefers-reduced-motion, гасим все декоративные
   анимации и переходы: контент сразу показывается в конечном состоянии, без
   всплытий и наездов. Фоновый холст отдельно останавливается в background.js. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .hero-fade { opacity: 1 !important; transform: none !important; }
}
