
/* ZEHIN — визуальная идентичность вдохновлена изразцами Регистана (глубокий индиго + золото),
   а не generic "AI-синий градиент", который есть у всех. Поддержка тёмной темы (prefers-color-scheme). */

/* Атрибут hidden обязан скрывать — всегда.
   Браузер прячет [hidden] своим встроенным правилом display:none, но ЛЮБОЙ наш стиль с
   собственным display побеждает встроенный, независимо от «веса» селектора. Так уже случилось
   с напоминанием о перерыве: у него стояло display:flex, и заготовка висела на экране у всех
   с первой секунды, хотя в разметке было написано «скрыто». Ошибка незаметная — код читается
   правильно, а работает нет, — поэтому закрываем весь класс таких ошибок одним правилом. */
[hidden] { display: none !important; }

:root {
  --bg: #f5f4fb;
  --bg-gradient: radial-gradient(circle at 20% 20%, #eef0ff 0%, #f5f4fb 45%);
  --card: #ffffff;
  --primary: #4338ca;
  --primary-dark: #3730a3;
  --accent: #d4a017;
  --accent-glow: #f0c33c;
  --text: #1a1a2e;
  --muted: #6b6b85;
  --border: #e5e3f3;
  --bubble-user: linear-gradient(135deg, #4338ca, #6845e8);
  --bubble-assistant: #f0eefb;
  --danger: #e0455c;
  --success: #2a9d6f;
  --radius: 16px;
  --shadow: 0 4px 24px rgba(67, 56, 202, 0.08);
  --shadow-lg: 0 12px 40px rgba(67, 56, 202, 0.14);
  --orb-glow-a: rgba(15, 157, 88, 0.32);
  --orb-glow-b: rgba(15, 157, 88, 0.5);
  --orb-glow-active-a: rgba(240, 195, 60, 0.38);
  --orb-glow-active-b: rgba(240, 195, 60, 0.52);
  --orb-halo-a: rgba(107, 240, 168, 0.4);
  --orb-halo-b: rgba(34, 181, 115, 0.2);
  /* Пятна фоновой "авроры". На светлой теме держим их очень бледными: под ними читается
     текст, и любой заметный цвет сразу превращается в грязь и мешает. */
  --aurora-1: rgba(67, 56, 202, 0.13);
  --aurora-2: rgba(34, 181, 115, 0.11);
  --aurora-3: rgba(212, 160, 23, 0.10);
  /* Фон раздела "Обучение": тетрадная клетка, корешки книг, зелёная доска. */
  --study-line: rgba(67, 56, 202, 0.07);
  --study-book-a: rgba(67, 56, 202, 0.10);
  --study-book-b: rgba(212, 160, 23, 0.10);
  --study-board: rgba(34, 181, 115, 0.10);
}

@media (prefers-color-scheme: dark) {
  /* Тёмная тема — не чёрный/серый, а глубокая ночная синева (ночное небо), на фоне которого
     фирменный шар светится как звезда. */
  :root {
    --bg: #0a0e24;
    --bg-gradient: radial-gradient(circle at 20% 15%, #131a44 0%, #0a0e24 55%);
    --card: #101433;
    --primary: #7c6df0;
    --primary-dark: #6857e0;
    --accent: #f0c33c;
    --accent-glow: #ffe285;
    --text: #e8eaff;
    --muted: #7c82ad;
    --border: #232a58;
    --bubble-user: linear-gradient(135deg, #6845e8, #7c6df0);
    --bubble-assistant: #171b47;
    --shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
    --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.55);
    /* Ночью тот же пульсирующий блеск на почти чёрном фоне субъективно бьёт по глазам сильнее,
       чем днём на светлом — приглушаем яркость свечения звезды специально для тёмной темы,
       а не одинаково везде (жалоба пользователя: "не должен резать глаза, особенно ночью"). */
    --orb-glow-a: rgba(15, 157, 88, 0.2);
    --orb-glow-b: rgba(15, 157, 88, 0.32);
    --orb-glow-active-a: rgba(240, 195, 60, 0.22);
    --orb-glow-active-b: rgba(240, 195, 60, 0.34);
    --orb-halo-a: rgba(107, 240, 168, 0.22);
    --orb-halo-b: rgba(34, 181, 115, 0.1);
    /* На тёмной теме аврора заметнее — на почти чёрном фоне слабые пятна просто не видны,
       и экран выглядит мёртвым. Но всё равно приглушённо: ночью глаза чувствительнее
       (та же логика, что и с приглушённым свечением орба). */
    --aurora-1: rgba(124, 109, 240, 0.20);
    --aurora-2: rgba(34, 181, 115, 0.16);
    --aurora-3: rgba(240, 195, 60, 0.10);
    --study-line: rgba(255, 255, 255, 0.05);
    --study-book-a: rgba(124, 109, 240, 0.16);
    --study-book-b: rgba(240, 195, 60, 0.12);
    --study-board: rgba(34, 181, 115, 0.13);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  position: relative;
}

/* Живой фон-"аврора" вместо плоской заливки — так делают современные AI-интерфейсы
   (мягкие цветные пятна, медленно перетекающие друг в друга). Реализовано ОДНИМ
   псевдоэлементом на радиальных градиентах: без картинок, без библиотек и без canvas —
   значит не тратит батарею и не грузит ничего по сети.
   pointer-events: none обязателен — иначе слой перехватывал бы клики по интерфейсу.
   z-index: 0 при position: relative у самого body держит его строго ПОД содержимым. */
body::before {
  content: '';
  position: fixed;
  inset: -20%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(38% 42% at 18% 22%, var(--aurora-1) 0%, transparent 62%),
    radial-gradient(34% 38% at 82% 26%, var(--aurora-2) 0%, transparent 60%),
    radial-gradient(42% 40% at 62% 84%, var(--aurora-3) 0%, transparent 62%);
  /* filter: blur() здесь УБРАН намеренно — он был главной причиной тормозов в браузере.
     Этот слой размером в 1.4 экрана ещё и анимирован, то есть браузер пересчитывал
     размытие огромной поверхности КАЖДЫЙ кадр. При этом визуально оно почти ничего не
     добавляло: радиальные градиенты выше и так дают мягкие края с плавным затуханием.
     will-change сообщает браузеру, что двигается только transform — слой уезжает на
     видеокарту и при движении не перерисовывается заново. */
  will-change: transform;
  animation: auroraDrift 26s ease-in-out infinite alternate;
}

/* Медленно и с малой амплитудой: фон должен ощущаться живым краем глаза, но не
   притягивать внимание и не мешать читать текст поверх него. */
@keyframes auroraDrift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(2%, -2%, 0) scale(1.06); }
  100% { transform: translate3d(-2%, 2%, 0) scale(1.02); }
}

/* Уважаем системную настройку "уменьшить движение" — для людей с чувствительностью
   к анимации постоянно плывущий фон это реальный дискомфорт, а не придирка. */
@media (prefers-reduced-motion: reduce) {
  body::before { animation: none; }
}

/* Когда вкладка свёрнута или экран не виден, гасим ВСЕ фоновые анимации. Браузер сам
   притормаживает таймеры, но CSS-анимации продолжают крутиться и держат видеоядро
   активным — на телефоне это заметный расход батареи впустую, ведь смотреть на них
   всё равно некому. Класс вешает app.js по событию visibilitychange. */
body.app-hidden::before,
body.app-hidden .star-sky::before,
body.app-hidden .star-sky::after,
body.app-hidden .shooting-star,
/* .twinkle добавлен позже остальных и сначала был здесь забыт: мерцающие звёзды
   продолжали мигать при погашенном экране и грели телефон впустую. Правило работает
   по перечислению, поэтому КАЖДУЮ новую анимацию нужно вписывать сюда руками —
   иначе она молча остаётся вечно активной. */
body.app-hidden .twinkle,
body.app-hidden .zehin-orb,
body.app-hidden .zehin-orb::before,
body.app-hidden .zehin-orb-xl::after,
body.app-hidden .facet-ring,
body.app-hidden .send-star-shine {
  animation-play-state: paused;
}

/* Экраны лежат поверх фонового слоя. */
.view { position: relative; z-index: 1; }

.hidden { display: none !important; }

@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes floatBlob {
  0%, 100% { transform: translate(0, 0) scale(1); }
  33% { transform: translate(20px, -30px) scale(1.05); }
  66% { transform: translate(-15px, 15px) scale(0.97); }
}

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

@keyframes typingBounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
  30% { transform: translateY(-6px); opacity: 1; }
}

@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

/* ===== Auth ===== */
/* Экран входа ВСЕГДА ночной, даже в светлой теме: звёзды на светлом фоне не видны, а
   именно небо со звездой — фирменный образ ZEHIN. Это единственный экран с таким
   исключением; внутри приложения тема пользователя работает как обычно. */
#auth-view {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  height: 100dvh;
  padding: 16px;
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(circle at 22% 18%, #1b2456 0%, transparent 55%),
    radial-gradient(circle at 78% 72%, #16204a 0%, transparent 55%),
    linear-gradient(160deg, #0b1030 0%, #070a20 60%, #05081a 100%);
}

/* Карточка входа поверх неба — полупрозрачная с размытием, чтобы звёзды просвечивали
   и экран не распадался на "картинка отдельно, форма отдельно". */
#auth-view .auth-card {
  /* Без backdrop-filter: под карточкой движутся звёзды, и размытие фона пересчитывалось бы
     на каждом кадре. Делаем фон плотнее — звёзды всё равно видны вокруг карточки. */
  background: rgba(19, 24, 58, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.09);
  color: #e8eaff;
}
#auth-view .subtitle { color: #a7add8; }
#auth-view .tab-btn { color: #a7add8; }
#auth-view .consent-label { color: #b9bfe4; }
#auth-view input[type="email"],
#auth-view input[type="password"],
#auth-view input[type="text"] {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(255, 255, 255, 0.16);
  color: #eef0ff;
}
#auth-view input::placeholder { color: #8d94c0; }

/* ===== Плавающий шар свёрнутого голосового режима (как у Gemini) =====
   Разговор продолжается, экран свободен. Шар перетаскивается, поэтому позиция задаётся
   через left/top из JS, а начальное место — снизу справа. */
.voice-bubble {
  position: fixed;
  right: 20px;
  bottom: 90px; /* выше строки ввода сообщения, чтобы не перекрывать её */
  z-index: 55; /* ниже оверлея голосового режима (51) не подходит: шар живёт, когда тот скрыт */
  cursor: grab;
  touch-action: none; /* иначе перетаскивание на телефоне превращается в прокрутку страницы */
  animation: fadeInUp 0.25s ease-out;
  filter: drop-shadow(0 6px 20px rgba(0, 0, 0, 0.45));
}
.voice-bubble:active { cursor: grabbing; }

/* Кнопка завершения разговора — маленький крестик в углу шара. */
.voice-bubble-close {
  position: absolute;
  top: -4px;
  right: -4px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: var(--card);
  color: var(--muted);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}
.voice-bubble-close:hover { color: var(--danger); }

/* ===== Светомузыка пробуждения =====
   Волна света расходится из центра экрана, когда ZEHIN просыпается. Живёт около секунды
   и сама себя удаляет из DOM: эффект должен ощущаться как "ожил", а не как заставка,
   которую приходится пересиживать при каждом пробуждении. */
.wake-flash {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none; /* обязательно: иначе слой перехватил бы клики */
  background:
    radial-gradient(circle at 50% 50%, rgba(240, 195, 60, 0.55) 0%, transparent 42%),
    radial-gradient(circle at 50% 50%, rgba(34, 181, 115, 0.45) 0%, transparent 60%);
  animation: wakeFlash 1.05s ease-out forwards;
}

@keyframes wakeFlash {
  0% { opacity: 0; transform: scale(0.25); }
  25% { opacity: 1; }
  100% { opacity: 0; transform: scale(1.9); }
}

@media (prefers-reduced-motion: reduce) {
  .wake-flash { animation-duration: 0.35s; }
}

/* ===== Мини-плеер ===== */
.mini-player {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 76px; /* над строкой ввода, чтобы не перекрывать её */
  z-index: 45;
  width: min(520px, calc(100vw - 32px));
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 16px;
  /* backdrop-filter здесь УБРАН: плеер висит поверх анимированного звёздного фона, и
     браузер пересчитывал размытие того, что за ним, на каждом кадре движения звёзд —
     дорого и постоянно. Фон панели и так почти непрозрачный (0.96), размытие под ним
     всё равно не читалось. */
  background: rgba(16, 20, 51, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.10);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45), 0 0 22px rgba(240, 195, 60, 0.18);
  animation: fadeInUp 0.25s ease-out;
}

.mp-art {
  width: 42px;
  height: 42px;
  flex-shrink: 0;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #0b6e4f, #22b573);
  box-shadow: 0 0 14px rgba(34, 181, 115, 0.45);
}

/* Светящийся эквалайзер: полоски пляшут, пока играет, и замирают на паузе. */
.mp-eq { display: flex; align-items: flex-end; gap: 2.5px; height: 20px; }

.mp-eq span {
  width: 3px;
  border-radius: 2px;
  background: linear-gradient(to top, var(--accent), #fff6d5);
  box-shadow: 0 0 6px rgba(240, 195, 60, 0.9);
  animation: mpBounce 0.9s ease-in-out infinite;
}
/* Разные высоты и сдвиги фаз — иначе полоски двигались бы одной стенкой. */
.mp-eq span:nth-child(1) { height: 8px;  animation-delay: 0s; }
.mp-eq span:nth-child(2) { height: 16px; animation-delay: 0.15s; }
.mp-eq span:nth-child(3) { height: 11px; animation-delay: 0.3s; }
.mp-eq span:nth-child(4) { height: 18px; animation-delay: 0.45s; }
.mp-eq span:nth-child(5) { height: 9px;  animation-delay: 0.6s; }

@keyframes mpBounce {
  0%, 100% { transform: scaleY(0.4); opacity: 0.7; }
  50% { transform: scaleY(1); opacity: 1; }
}

/* На паузе анимация останавливается — состояние видно без подписей. */
.mini-player.paused .mp-eq span { animation-play-state: paused; }

.mp-info { flex: 1; min-width: 0; }
.mp-title {
  font-size: 14.5px;
  font-weight: 600;
  color: #eef0ff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis; /* длинные названия треков не должны ломать вёрстку панели */
}
.mp-channel { font-size: 12.5px; color: #9aa1cc; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.mp-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  color: #cdd3f5;
  cursor: pointer;
  padding: 6px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  font-size: 15px;
}
.mp-btn:hover { color: var(--accent-glow); background: rgba(255, 255, 255, 0.08); }

/* Сам проигрыватель нужен только ради звука — визуально убираем, но НЕ через display:none:
   скрытый так iframe часть браузеров останавливает вместе с воспроизведением. */
.mp-frame {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .mp-eq span { animation: none; }
}

/* ===== Карточка открытия сервиса (YouTube / Telegram / Instagram) ===== */
.service-card {
  align-self: flex-start;
  max-width: 75%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 6px;
}

/* Соотношение 16:9 держим через aspect-ratio, иначе на телефоне плеер получается
   то приплюснутым, то с чёрными полями. */
.yt-embed {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: 14px;
  background: #000;
}

.service-link {
  display: inline-block;
  align-self: flex-start;
  padding: 8px 16px;
  border-radius: 10px;
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
  color: var(--primary);
  font-size: 14.5px;
  font-weight: 600;
  text-decoration: none;
}
.service-link:hover { border-color: var(--primary); }

/* ===== Сдача домашнего задания ===== */
.submit-tabs { display: flex; gap: 6px; margin: 10px 0; flex-wrap: wrap; }

.submit-tab {
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
  padding: 7px 14px;
  border-radius: 10px;
  font-size: 14.5px;
  cursor: pointer;
}
.submit-tab.active { background: var(--bubble-assistant); color: var(--primary); font-weight: 600; border-color: var(--primary); }

.submit-pane { display: flex; flex-direction: column; gap: 8px; margin: 8px 0; }
.file-pick { display: inline-block; text-align: center; cursor: pointer; }
.submit-preview { max-width: 100%; max-height: 240px; border-radius: 12px; border: 1px solid var(--border); }

/* Оценка: число + цветная полоса. С одного взгляда видно результат, а не только цифру
   в тексте — так устроен показ оценок на Coursera и подобных платформах. */
.grade-head { font-size: 17px; font-weight: 700; margin-bottom: 6px; }
.grade-good { color: var(--success); }
.grade-ok { color: var(--accent); }
.grade-weak { color: var(--danger); }

.grade-bar {
  height: 8px;
  background: var(--border);
  border-radius: 5px;
  overflow: hidden;
  margin-bottom: 10px;
}
.grade-fill { display: block; height: 100%; border-radius: 5px; transition: width 0.5s ease-out; }
.grade-fill.grade-good { background: var(--success); }
.grade-fill.grade-ok { background: var(--accent); }
.grade-fill.grade-weak { background: var(--danger); }

.grade-feedback { margin: 0; font-size: 15px; line-height: 1.5; white-space: pre-wrap; }

/* ===== Созданный документ (Word / Excel / презентация / PDF) ===== */
.doc-card {
  display: flex;
  align-items: center;
  gap: 12px;
  align-self: flex-start;
  max-width: 88%;
  margin: 4px 0;
  padding: 12px 14px;
  border-radius: 14px;
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
}
.doc-icon { font-size: 24px; flex-shrink: 0; line-height: 1; }
.doc-name {
  flex: 1;
  font-size: 14.5px;
  font-weight: 600;
  /* Длинное имя файла не должно растягивать карточку за край экрана. */
  overflow-wrap: anywhere;
}
.doc-download {
  flex-shrink: 0;
  padding: 7px 14px;
  border-radius: 20px;
  background: var(--primary);
  color: #fff;
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
}
.doc-download:hover { filter: brightness(1.1); }

/* ===== Сгенерированные изображения ===== */
.generated-image-link { display: block; margin: 10px 0; }
.generated-image {
  display: block;
  width: 100%;
  max-width: 420px;
  height: auto;
  border-radius: 14px;
  border: 1px solid var(--border);
  background: var(--bubble-assistant); /* фон, пока картинка грузится */
}

.voice-diag {
  margin: -6px 0 10px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.55);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Язык распознавания речи на уроке. Маленькая кнопка рядом с микрофоном: её роль —
   не украшать, а показывать, ЧТО сейчас слушает микрофон. Без такого указателя человек,
   переключивший язык и забывший об этом, решит, что микрофон сломался. */
.mic-lang-btn {
  flex-shrink: 0;
  min-width: 34px;
  height: 34px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.mic-lang-btn.is-active { color: var(--primary); border-color: var(--primary); }

/* Пересдача теста на уровень. Второстепенное действие — оформлено как обводка, а не
   как основная кнопка: главное на этом экране всё-таки уроки. */
.retake-placement-btn {
  display: block;
  width: 100%;
  margin: 4px 0 14px;
  padding: 11px;
  border-radius: 12px;
  background: none;
  border: 1px dashed var(--border);
  color: var(--muted);
  font-size: 14.5px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.retake-placement-btn:hover { color: var(--primary); border-color: var(--primary); }

/* ===== Шапка урок-чата: название и переключатель озвучки ===== */
.lesson-chat-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.lesson-chat-head h2 { flex: 1; min-width: 0; }
.lesson-speak-toggle {
  flex-shrink: 0;
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
  border-radius: 50%;
  width: 40px;
  height: 40px;
  font-size: 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.55;
  transition: opacity 0.15s, border-color 0.15s;
}
/* Включённое озвучивание видно сразу: иначе человек не понимает, почему стало тихо. */
.lesson-speak-toggle.is-on { opacity: 1; border-color: var(--primary); }

/* ===== Тихая строка-заметка внутри переписки ===== */
/* Не сообщение и не плашка поверх экрана: приглушённая строка по центру потока — так же,
   как разделители дат в мессенджерах. Ничего не перекрывает и уходит вверх вместе с историей.
   Сейчас используется для напоминания о перерыве. */
.chat-note {
  align-self: center;
  max-width: min(520px, 92%);
  margin: 10px auto;
  padding: 9px 14px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--bubble-assistant) 70%, transparent);
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.5;
  text-align: center;
  animation: note-in 0.3s ease;
}
@keyframes note-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ===== Остаток бесплатных сообщений ===== */
.quota-badge {
  background: var(--bubble-assistant);
  color: var(--muted);
  padding: 5px 12px;
  border-radius: 20px;
  font-size: 13.5px;
  font-weight: 700;
  white-space: nowrap;
  border: 1px solid var(--border);
}
/* Цвет предупреждает заранее, а не в момент отказа. */
.quota-badge.low { color: var(--accent); border-color: var(--accent); }
.quota-badge.empty { color: var(--danger); border-color: var(--danger); }
.quota-badge:empty { display: none; } /* пока не загрузилось — не показываем пустой кружок */

/* ===== Уведомление о новой версии =====
   Стояло сверху (top: 14px) и на телефоне 360px ложилось прямо поверх шапки, перекрывая
   кнопку «Войти / Регистрация» и переключение разделов: до них было физически не дотянуться
   пальцем. Найдено проверкой, которая заходит внутрь приложения — обычный осмотр главной
   страницы этого не показывал, потому что там кнопок под плашкой нет.

   Теперь снизу: внизу перекрывать нечего кроме поля ввода, да и то плашка уходит сама.
   Сообщение необязательное — приложение работает и на прежней версии, — поэтому оно не
   имеет права мешать. */
.update-banner {
  position: fixed;
  /* Прижата ВЛЕВО, а не по центру, и не во всю ширину. Правый нижний угол занят плавающим
     шаром голосового режима: по центру плашка накрывала его целиком, и разговор нельзя было
     ни развернуть, ни завершить — было видно только зелёный краешек. Это уже вторая жертва
     этой плашки (первой была шапка), поэтому теперь ей явно отведено СВОЁ место. */
  left: 12px;
  bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  /* z-index ниже, чем у шара (55): даже если они когда-нибудь пересекутся, нажатие достанется
     шару — он управляет живым разговором, а плашка сообщает необязательную новость. */
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 10px;
  /* Правые 104px оставлены шару с запасом на его тень и крестик. */
  max-width: calc(100vw - 116px);
  padding: 10px 14px;
  border-radius: 14px;
  background: rgba(16, 20, 51, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: #eef0ff;
  font-size: 14px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  animation: fadeInUp 0.25s ease-out;
}
.update-banner span { flex: 1; min-width: 0; }

.update-banner-btn {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  padding: 7px 16px;
  border-radius: 10px;
  font-weight: 600;
  cursor: pointer;
}

.update-banner-later {
  background: none;
  border: none;
  color: #9aa1cc;
  cursor: pointer;
  font-size: 14px;
}

/* ===== Мониторинг объекта ===== */
.monitoring-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 12px 0; }
.monitoring-actions input[type="text"] { flex: 1; min-width: 200px; }

.monitoring-incidents { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }

.incident-card {
  padding: 12px 14px;
  border-radius: 12px;
  background: var(--card);
  border: 1px solid var(--border);
  /* Цветная полоса слева — уровень важности виден с одного взгляда, без чтения текста. */
  border-left-width: 4px;
}
.incident-card.sev-info { border-left-color: var(--muted); }
.incident-card.sev-attention { border-left-color: var(--accent); }
.incident-card.sev-urgent { border-left-color: var(--danger); }

.incident-head { font-weight: 700; font-size: 15px; margin-bottom: 2px; }
.sev-urgent .incident-head { color: var(--danger); }
.sev-attention .incident-head { color: var(--accent); }

.incident-when { font-size: 13px; color: var(--muted); margin-bottom: 6px; }
.incident-desc { margin: 0; font-size: 15px; line-height: 1.45; }
.incident-facts { margin-top: 6px; font-size: 14px; color: var(--primary); font-weight: 600; }

/* ===== Подключение Telegram ===== */
.telegram-code-box {
  margin-top: 10px;
  padding: 14px;
  border-radius: 12px;
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
  text-align: center;
}

.telegram-code {
  font-size: 26px;
  font-weight: 800;
  letter-spacing: 4px; /* код набирают вручную — разрядка сильно уменьшает опечатки */
  color: var(--primary);
  font-family: 'SF Mono', Consolas, Menlo, monospace;
  margin-bottom: 6px;
  user-select: all; /* одно нажатие выделяет код целиком */
}

/* ===== Память о пользователе (Настройки) ===== */
.memory-list { display: flex; flex-direction: column; gap: 8px; margin: 10px 0; }

.memory-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.memory-tag {
  flex-shrink: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--primary);
  background: var(--card);
  border-radius: 8px;
  padding: 3px 9px;
}

.memory-text { flex: 1; font-size: 15px; line-height: 1.4; word-break: break-word; }

.memory-delete {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 17px;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 8px;
  line-height: 1;
}
.memory-delete:hover { color: var(--danger); background: var(--card); }

/* ===== Кнопка "показать пароль" ===== */
.password-wrap { position: relative; display: flex; }
.password-wrap input { flex: 1; padding-right: 44px; } /* место под кнопку, чтобы текст не заезжал */

/* Глазок «показать пароль».
   Раньше стоял color: var(--muted) — на тёмной теме это #7c82ad на почти таком же тёмном
   поле ввода, и значок практически сливался с фоном. Он работал, но человек его не видел
   и решал, что кнопка сломана (жалоба владельца). Поэтому здесь цвет задан явно и с запасом
   по контрасту, а не общим приглушённым токеном: это управляющий элемент, а не украшение.
   Плюс увеличена область нажатия — пальцем на телефоне в 19px значок попасть трудно. */
.password-eye {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: var(--text);
  opacity: 0.65;
  cursor: pointer;
  padding: 10px;
  display: flex;
  align-items: center;
  border-radius: 10px;
  transition: opacity 0.15s, background 0.15s;
}
.password-eye:hover,
.password-eye:focus-visible { opacity: 1; background: rgba(127, 127, 160, 0.14); }
/* Нажатое состояние — «пароль сейчас виден». Без него единственный признак переключения
   это подмена значка, а её на маленькой иконке легко не заметить. */
.password-eye.is-visible { opacity: 1; color: var(--primary); }

/* ===== Звёздное небо на экране входа =====
   Три слоя точек-звёзд разного размера (background-image из радиальных градиентов —
   без картинок и без единого лишнего DOM-узла). Слои двигаются с РАЗНОЙ скоростью:
   так возникает ощущение глубины, будто ближние звёзды летят быстрее дальних. */
.star-sky {
  position: fixed; /* общий фон всего приложения, а не отдельного экрана */
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 0;
}

/* Яркость неба по экранам. Порядок правил важен: побеждает последнее подошедшее, поэтому
   правило для "Обучения" стоит ПОСЛЕ правила для приложения — иначе на курсах небо
   осталось бы включённым.
   1) Экран входа — звёзды в полную силу (по умолчанию opacity: 1 выше).
   2) Внутри приложения приглушены: там читают текст, яркое небо мешало бы.
   3) В разделе "Обучение" выключены совсем — там свой фон с тетрадной клеткой, книгами
      и доской, два фона наложились бы в кашу. */
.star-sky { transition: opacity 0.4s; }
body:has(#app-view:not(.hidden)) .star-sky { opacity: 0.4; }
body:has(#panel-courses:not(.hidden)) .star-sky { opacity: 0; }

.star-sky::before,
.star-sky::after {
  content: '';
  position: absolute;
  inset: -50% 0;
  background-repeat: repeat;
  opacity: 0.9;
  will-change: transform; /* слой уезжает на видеокарту, движение не вызывает перерисовку */
}

/* Мелкие частые звёзды — дальний план, плывут медленно. */
.star-sky::before {
  background-image:
    radial-gradient(1.2px 1.2px at 20% 15%, rgba(255,255,255,0.9) 50%, transparent),
    radial-gradient(1px 1px at 65% 35%, rgba(255,255,255,0.75) 50%, transparent),
    radial-gradient(1.4px 1.4px at 40% 70%, rgba(255,240,200,0.85) 50%, transparent),
    radial-gradient(1px 1px at 85% 60%, rgba(255,255,255,0.7) 50%, transparent),
    radial-gradient(1.2px 1.2px at 10% 85%, rgba(255,255,255,0.8) 50%, transparent);
  background-size: 260px 260px;
  animation: starDrift 90s linear infinite;
}

/* Крупные редкие звёзды — ближний план, мерцают. */
.star-sky::after {
  background-image:
    radial-gradient(2.2px 2.2px at 30% 25%, rgba(255,246,213,0.95) 50%, transparent),
    radial-gradient(1.8px 1.8px at 75% 55%, rgba(200,220,255,0.9) 50%, transparent),
    radial-gradient(2px 2px at 55% 80%, rgba(255,255,255,0.9) 50%, transparent);
  background-size: 420px 420px;
  animation: starDrift 55s linear infinite, starTwinkle 5.5s ease-in-out infinite;
}

@keyframes starDrift {
  from { transform: translateY(0); }
  to { transform: translateY(260px); }
}

@keyframes starTwinkle {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}

/* Падающая звезда: одна, редко и коротко. Постоянный "звездопад" превратил бы экран
   входа в заставку и мешал бы вводить пароль — здесь она пролетает раз в ~9 секунд. */
.shooting-star {
  position: absolute;
  top: 12%;
  left: -12%;
  width: 130px;
  height: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.95), transparent);
  filter: drop-shadow(0 0 6px rgba(255, 246, 213, 0.9));
  opacity: 0;
  transform: rotate(14deg);
  animation: shootingStar 9s ease-in infinite;
}
/* Разные высота, задержка и длительность — иначе три звезды летели бы строем,
   и сразу видно, что это анимация, а не небо. */
.shooting-star.second { top: 46%; animation-delay: 4.5s; animation-duration: 11s; }
.shooting-star.third { top: 72%; animation-delay: 7.5s; animation-duration: 13s; width: 90px; }

@keyframes shootingStar {
  0%, 88% { opacity: 0; transform: translate(0, 0) rotate(14deg); }
  90% { opacity: 1; }
  100% { opacity: 0; transform: translate(130vw, 42vh) rotate(14deg); }
}

/* ===== Мерцающие звёзды ===== */
/* Настоящее небо мерцает не целиком: воздух дрожит по-разному над каждой звездой, поэтому
   они вспыхивают вразнобой. Отсюда собственные --delay и --dur у каждой прямо в разметке —
   общая анимация на всех дала бы пульсацию строем, и небо сразу выглядело бы нарисованным.
   Анимируются ТОЛЬКО opacity и transform: эти два свойства браузер считает на видеокарте,
   не перерисовывая страницу, поэтому телефон не греется даже при постоянном движении. */
.twinkle {
  position: absolute;
  width: var(--size, 2px);
  height: var(--size, 2px);
  border-radius: 50%;
  background: #fff;
  /* Ореол вокруг точки — без него звезда выглядит пикселем, а не светилом.
     box-shadow дешевле filter: blur и не создаёт отдельный слой. */
  box-shadow: 0 0 calc(var(--size, 2px) * 2.5) rgba(255, 252, 235, 0.9);
  opacity: 0.25;
  animation: twinkle var(--dur, 4s) ease-in-out var(--delay, 0s) infinite;
}
@keyframes twinkle {
  /* Долгое тусклое состояние и короткая вспышка: так мерцает настоящая звезда.
     Ровная синусоида читалась бы как «дышащая лампочка». */
  0%, 100% { opacity: 0.2; transform: scale(0.85); }
  45% { opacity: 0.35; transform: scale(0.95); }
  55% { opacity: 1; transform: scale(1.25); }
  65% { opacity: 0.4; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .star-sky::before, .star-sky::after, .shooting-star, .twinkle { animation: none; }
  .shooting-star { opacity: 0; }
  /* Звёзды остаются на месте, просто перестают мерцать — небо не пустеет. */
  .twinkle { opacity: 0.55; }
}

/* Раньше это были сплошные круги с filter: blur(60px), И они анимировались — браузер
   пересчитывал размытие двух больших поверхностей каждый кадр. Заменено на радиальный
   градиент с прозрачным затуханием: выглядит так же мягко, но рисуется один раз и почти
   бесплатно. Сам цвет задаётся ниже через --blob-color. */
.auth-blob {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle, var(--blob-color) 0%, transparent 70%) !important;
  opacity: 0.5;
  will-change: transform;
  animation: floatBlob 14s ease-in-out infinite;
  pointer-events: none;
}

.auth-blob.one { width: 420px; height: 420px; --blob-color: var(--primary); top: -120px; left: -120px; }
.auth-blob.two { width: 380px; height: 380px; --blob-color: var(--accent); bottom: -100px; right: -100px; animation-delay: -6s; }

.auth-card {
  background: var(--card);
  border-radius: 20px;
  padding: 36px;
  width: 100%;
  max-width: 380px;
  box-shadow: var(--shadow-lg);
  position: relative;
  z-index: 1;
  animation: fadeInUp 0.5s ease-out;
}

/* ===== Экран знакомства (один раз после регистрации) ===== */
#onboarding-view {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100dvh;
  padding: 20px;
  position: relative;
  overflow-x: clip; /* свечение орба не должно растягивать страницу вбок */
}
.onboarding-card {
  background: var(--card);
  border-radius: 20px;
  padding: 32px 28px;
  width: 100%;
  max-width: 400px;
  box-shadow: var(--shadow-lg);
  position: relative;
  z-index: 1;
  text-align: center;
  animation: fadeInUp 0.5s ease-out;
}
.onboarding-card .zehin-orb-3d-wrap { margin: 0 auto 14px; }
.onboarding-card h2 {
  margin: 0 0 8px;
  font-size: 24px;
  font-weight: 700;
  color: var(--text);
}
.onboarding-sub {
  margin: 0 0 22px;
  color: var(--muted);
  font-size: 14.5px;
  line-height: 1.5;
}
.onboarding-card .field {
  display: block;
  text-align: left;
  margin-bottom: 14px;
}
.onboarding-card .field-label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  color: var(--muted);
}
.onboarding-card input {
  width: 100%;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 16px; /* не меньше 16px: iOS иначе увеличивает страницу при фокусе */
}
.onboarding-card input:focus {
  outline: none;
  border-color: var(--primary);
}
.onboarding-note {
  margin: 6px 0 18px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--bubble-assistant);
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.45;
  text-align: left;
}
/* Главная кнопка экрана знакомства. Стиль повторяет кнопку формы входа намеренно: это одна
   и та же по смыслу кнопка «дальше», и выглядеть она должна одинаково. Сначала здесь стоял
   класс btn-primary, которого в проекте нет вовсе, — кнопка отрисовалась серой системной.
   Видно только глазами: разметка и код при этом были совершенно исправны. */
.onboarding-card button[type="submit"] {
  display: block;
  width: 100%;
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: #fff;
  border: none;
  padding: 13px;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(67, 56, 202, 0.3);
}
.onboarding-card button[type="submit"]:hover { box-shadow: 0 6px 20px rgba(67, 56, 202, 0.4); }
.onboarding-card button[type="submit"]:disabled { opacity: 0.6; cursor: default; }

.btn-link {
  display: block;
  width: 100%;
  margin-top: 10px;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 14px;
  cursor: pointer;
  padding: 8px;
}
.btn-link:hover { color: var(--text); }

.auth-card h1 {
  margin: 0;
  text-align: center;
  font-size: 32px;
  font-weight: 800;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  letter-spacing: -0.5px;
}

/* ===== ZEHIN orb — "агент как звезда в небе": хрустальная сфера со светящимся 8-лучевым
   звёздным ядром внутри. Дышит в покое, мерцает мягко, переключается на эквалайзер, пока
   формируется ответ. ===== */
@keyframes breathe {
  0%, 100% { transform: scale(1); box-shadow: 0 0 10px 2px var(--orb-glow-a); }
  50% { transform: scale(1.1); box-shadow: 0 0 20px 6px var(--orb-glow-b); }
}

@keyframes breatheActive {
  0%, 100% { transform: scale(1.08); box-shadow: 0 0 16px 5px var(--orb-glow-active-a); }
  50% { transform: scale(1.2); box-shadow: 0 0 24px 8px var(--orb-glow-active-b); }
}

/* Мягкий кристальный блик, не резкое мерцание — амплитуда и скорость специально приглушены,
   чтобы не "резало глаза" на маленькой звёздочке в шапке (было слишком контрастно/быстро),
   и пик яркости снижен ещё раз (0.92 вместо 1.0) — не должно напрягать глаза даже ночью. */
@keyframes twinkle {
  0%, 100% { opacity: 0.82; transform: translate(-50%, -50%) scale(0.96); }
  50% { opacity: 1; transform: translate(-50%, -50%) scale(1.05); }
}

@keyframes equalize {
  0%, 100% { height: 4px; }
  50% { height: 13px; }
}

/* Хрустальная сфера (не гранёный ромб, как раньше) — круглый "кристальный шар" с лёгкой
   гранёной текстурой на поверхности (repeating-conic-gradient — тонкие световые полосы,
   имитирующие грани) и ярким звёздным ядром внутри (::before, 8-лучевая звезда-вспышка,
   см. ниже) — по образцу референса "дышащий светящийся кристалл со звездой в центре". */
.zehin-orb {
  position: relative;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background:
    repeating-conic-gradient(from 0deg, rgba(255, 255, 255, 0.08) 0deg 2deg, transparent 2deg 18deg),
    linear-gradient(135deg, rgba(255, 255, 255, 0.55) 0%, transparent 42%),
    radial-gradient(circle at 35% 25%, #e3fff0 0%, #6bf0a8 25%, #22b573 55%, #0b6e4f 82%, #054a35 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  animation: breathe 3.2s ease-in-out infinite;
}

/* Звезда-ядро внутри сферы — 8-лучевая (16 вершин, чередование внешнего/внутреннего радиуса),
   а не прежняя 4-лучевая, чтобы читаться как яркая вспышка света в центре кристалла, а не
   просто блик на грани. Градиент вместо сплошной заливки — свет растворяется к краям лучей. */
.zehin-orb::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 78%;
  height: 78%;
  background: radial-gradient(circle, #fffbe8 0%, #ffe285 30%, #f0c33c 58%, transparent 78%);
  filter: drop-shadow(0 0 4px rgba(240, 195, 60, 0.75));
  clip-path: polygon(
    50% 0%, 56.9% 33.4%, 85.4% 14.6%, 66.6% 43.1%,
    100% 50%, 66.6% 56.9%, 85.4% 85.4%, 56.9% 66.6%,
    50% 100%, 43.1% 66.6%, 14.6% 85.4%, 33.4% 56.9%,
    0% 50%, 33.4% 43.1%, 14.6% 14.6%, 43.1% 33.4%
  );
  animation: twinkle 4.2s ease-in-out infinite;
  pointer-events: none;
  z-index: 1;
}

.zehin-orb.active {
  animation: breatheActive 1s ease-in-out infinite;
  background:
    repeating-conic-gradient(from 0deg, rgba(255, 255, 255, 0.08) 0deg 2deg, transparent 2deg 18deg),
    linear-gradient(135deg, rgba(255, 255, 255, 0.6) 0%, transparent 42%),
    radial-gradient(circle at 35% 30%, #ffffff 0%, var(--accent-glow) 30%, var(--primary) 75%, #4c3d99 100%);
}

/* Пока идёт эквалайзер — прячем блик-звёздочку, чтобы не спорил визуально с полосками */
.zehin-orb.active::before { animation: none; opacity: 0; }


.zehin-orb-lg {
  width: 64px;
  height: 64px;
  margin: 0 auto 16px;
}

/* ===== Гранёное 3D-вращение (чистый CSS: rotateY + translateZ + perspective, БЕЗ WebGL/Three.js —
   дешевле по батарее для декоративного элемента). Вращаются ТОЛЬКО грани внутри (.facet-ring),
   сама сфера и звезда-ядро (::before) остаются на месте и просто дышат — иначе звезда уезжала бы
   вместе с гранями и переставала читаться как стабильный источник света в центре. ===== */
.zehin-orb-3d-wrap { perspective: 600px; }

.zehin-orb-lg, .zehin-orb-xl { transform-style: preserve-3d; }

.facet-ring {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  animation: rotateFacetRing 14s linear infinite;
  pointer-events: none;
}

/* Раньше грани были почти в размер всего шара И толкались ВПЕРЁД (положительный translateZ) —
   f1 буквально закрывал звезду собой (прозрачное, но заметное пятно прямо перед ней), а f2/f3
   были слишком блёклые, чтобы их вообще заметить. Теперь грани: (1) заметно меньше — читаются
   как отдельные блики-грани, а не как туман на весь шар; (2) толкаются НАЗАД (отрицательный
   translateZ), то есть всегда позади плоскости звезды — звезда остаётся самым передним и самым
   ярким элементом при любом угле поворота кольца; (3) ярче/контрастнее, без размытия — иначе на
   маленьком орбе (32-100px) их не отличить от фона. */
.facet {
  position: absolute;
  inset: 32%;
  border-radius: 50%;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.5), rgba(107, 240, 168, 0.65));
  border: 1px solid rgba(255, 255, 255, 0.45);
  box-shadow: 0 0 6px rgba(107, 240, 168, 0.5);
}

@keyframes rotateFacetRing {
  from { transform: rotateY(0deg) rotateX(8deg); }
  to { transform: rotateY(360deg) rotateX(8deg); }
}

.zehin-orb-lg .facet.f1 { transform: rotateY(0deg) translateZ(-14px); }
.zehin-orb-lg .facet.f2 { transform: rotateY(120deg) translateZ(-14px); }
.zehin-orb-lg .facet.f3 { transform: rotateY(240deg) translateZ(-14px); }

.zehin-orb-xl .facet.f1 { transform: rotateY(0deg) translateZ(-22px); }
.zehin-orb-xl .facet.f2 { transform: rotateY(120deg) translateZ(-22px); }
.zehin-orb-xl .facet.f3 { transform: rotateY(240deg) translateZ(-22px); }

.eq-bars {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: 14px;
  opacity: 0;
  transition: opacity 0.2s;
}

/* Полоски внутри шара видны, когда он "занят": думает (active), слушает (listening) или
   отвечает голосом (speaking). Раньше только active — поэтому в голосовом режиме шар
   выглядел неподвижным, хотя разговор идёт. */
.zehin-orb.active .eq-bars,
.zehin-orb.listening .eq-bars,
.zehin-orb.speaking .eq-bars { opacity: 1; }

/* Пока слушаем/говорим, высоту полосок задаёт JS по реальному звуку — CSS-анимация в этот
   момент только мешала бы (боролась бы за то же свойство height). */
.zehin-orb.listening .eq-bars span,
.zehin-orb.speaking .eq-bars span {
  animation: none;
  transition: height 0.08s linear;
}

.eq-bars span {
  width: 2.5px;
  height: 4px;
  /* Золотой, а не плоский белый: эквалайзер загорается внутри изумрудного шара, и золото
     здесь читается как "звезда ожила", в тон фирменной звезде-ядру и стрик-бейджу. */
  background: linear-gradient(to top, var(--accent), #fff6d5);
  border-radius: 2px;
  box-shadow: 0 0 5px rgba(240, 195, 60, 0.75);
  animation: equalize 0.9s ease-in-out infinite;
}

.zehin-orb-lg .eq-bars { height: 22px; gap: 5px; }
.zehin-orb-lg .eq-bars span { width: 4px; }

.eq-bars span:nth-child(1) { animation-delay: 0s; }
.eq-bars span:nth-child(2) { animation-delay: 0.15s; }
.eq-bars span:nth-child(3) { animation-delay: 0.3s; }
.eq-bars span:nth-child(4) { animation-delay: 0.45s; }

.subtitle {
  color: var(--muted);
  margin: 4px 0 24px;
  font-size: 16px;
  text-align: center;
}

.tabs {
  display: flex;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
}

.tab-btn {
  flex: 1;
  padding: 10px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  font-size: 16px;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s;
}

.tab-btn.active {
  color: var(--primary);
  border-bottom-color: var(--primary);
  font-weight: 600;
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

input, select, textarea {
  padding: 12px 14px;
  border: 1.5px solid var(--border);
  border-radius: 12px;
  font-size: 16px;
  font-family: inherit;
  width: 100%;
  background: var(--card);
  color: var(--text);
  transition: border-color 0.2s, box-shadow 0.2s;
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(67, 56, 202, 0.12);
}

button {
  cursor: pointer;
  font-family: inherit;
  transition: transform 0.12s, box-shadow 0.2s, background 0.2s;
}

button:active { transform: scale(0.97); }

.auth-form button[type="submit"] {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  padding: 13px;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  margin-top: 4px;
  box-shadow: 0 4px 14px rgba(67, 56, 202, 0.3);
}

.auth-form button[type="submit"]:hover { box-shadow: 0 6px 20px rgba(67, 56, 202, 0.4); }

#forgot-password-submit {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  padding: 13px;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  margin-top: 4px;
  cursor: pointer;
}

.link-btn {
  background: none;
  border: none;
  color: var(--primary);
  font-size: 14.5px;
  cursor: pointer;
  padding: 4px 0;
  text-align: center;
}
.link-btn:hover { text-decoration: underline; }

.consent-label {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 15.5px;
  color: var(--muted);
}

.consent-label input { width: auto; margin-top: 3px; }
.consent-label a { color: var(--primary); }

.google-signin-section {
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.auth-divider {
  display: flex;
  align-items: center;
  text-align: center;
  color: var(--muted);
  font-size: 14.5px;
}
.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  border-bottom: 1px solid var(--border);
}
.auth-divider span { padding: 0 10px; }

#google-signin-button { display: flex; justify-content: center; }

.error {
  color: var(--danger);
  font-size: 15.5px;
  margin: 0;
  min-height: 16px;
}

/* ===== App shell ===== */
#app-view {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  animation: fadeIn 0.3s ease-out;
}

.app-header {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 20px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

/* ===== Гостевой чат (до регистрации) — тот же каркас, что у #app-view, но без бокового
   меню/навигации/настроек, которых у гостя просто нет. ===== */
#guest-view {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  animation: fadeIn 0.3s ease-out;
}

.guest-header {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 20px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.guest-header .brand { flex: 1; }

.guest-gate {
  margin: 16px 20px;
  padding: 16px;
  background: var(--card);
  border: 1px solid var(--accent);
  border-radius: 14px;
}
.guest-gate p { margin: 0 0 10px; }
.guest-gate .consent-label { margin-bottom: 12px; }

.brand {
  font-weight: 800;
  font-size: 21px;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  letter-spacing: -0.3px;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  transition: opacity 0.15s;
}
.brand:hover { opacity: 0.8; }

.streak-badge {
  background: linear-gradient(135deg, #fff3d6, #ffe4a3);
  color: #8a5a00;
  padding: 5px 12px;
  border-radius: 20px;
  font-size: 15.5px;
  font-weight: 700;
  transition: transform 0.3s ease;
}

@media (prefers-color-scheme: dark) {
  .streak-badge { background: linear-gradient(135deg, #3a3116, #4a3d1a); color: var(--accent-glow); }
}

.streak-badge.bump { animation: pulse 0.4s ease; }

.main-nav {
  display: flex;
  gap: 4px;
  flex: 1;
}

.nav-btn {
  background: none;
  border: none;
  padding: 8px 14px;
  border-radius: 10px;
  color: var(--muted);
  font-size: 16px;
  display: flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
}

.nav-btn:hover { background: var(--bubble-assistant); }

.nav-btn.active {
  background: var(--bubble-assistant);
  color: var(--primary);
  font-weight: 600;
}

.logout-btn {
  background: none;
  border: 1px solid var(--border);
  padding: 6px 12px;
  border-radius: 10px;
  color: var(--muted);
  font-size: 15.5px;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* На узком экране подписи в шапке съедают всё место — оставляем только иконки.
   Сами подписи не удалены из разметки: на широком экране без них пришлось бы
   угадывать значение иконки, а это худший вид "современного" интерфейса. */


.logout-btn:hover { border-color: var(--danger); color: var(--danger); }

.icon-btn {
  background: none;
  border: none;
  font-size: 20px;
  color: var(--muted);
  cursor: pointer;
  padding: 6px 8px;
  border-radius: 10px;
  line-height: 1;
}
.icon-btn:hover { background: var(--bubble-assistant); color: var(--text); }

/* ===== Боковая панель истории чатов (своя версия, вдохновлена меню Gemini/ChatGPT,
   но без клонирования: только реально существующие у нас возможности — история и заметки) ===== */
.drawer-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 40;
  animation: fadeIn 0.15s ease-out;
}

.chat-drawer {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 280px;
  max-width: 82vw;
  background: var(--card);
  border-right: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  z-index: 41;
  display: flex;
  flex-direction: column;
  padding: 16px 12px;
  animation: slideInLeft 0.2s ease-out;
}

@keyframes slideInLeft {
  from { transform: translateX(-100%); opacity: 0.6; }
  to { transform: translateX(0); opacity: 1; }
}

.drawer-new-chat {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  padding: 11px 14px;
  border-radius: 12px;
  font-weight: 600;
  cursor: pointer;
  margin-bottom: 10px;
}

.drawer-search {
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px 12px;
  color: var(--text);
  font-family: inherit;
  font-size: 15.5px;
  margin-bottom: 14px;
}

.drawer-section-label {
  font-size: 14.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  padding: 0 6px 8px;
}

.drawer-conversations {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.drawer-conversation-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 9px 10px;
  border-radius: 10px;
  cursor: pointer;
  font-size: 16px;
  color: var(--text);
}
.drawer-conversation-item:hover { background: var(--bubble-assistant); }
.drawer-conversation-item.active { background: var(--bubble-assistant); color: var(--primary); font-weight: 600; }

.drawer-conversation-title {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.drawer-conversation-delete {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 15.5px;
  padding: 2px 6px;
  border-radius: 6px;
  visibility: hidden;
}
.drawer-conversation-item:hover .drawer-conversation-delete { visibility: visible; }
.drawer-conversation-delete:hover { color: var(--danger); background: var(--bubble-assistant); }

.drawer-empty {
  color: var(--muted);
  font-size: 15.5px;
  padding: 8px 6px;
}

.drawer-footer {
  border-top: 1px solid var(--border);
  padding-top: 10px;
  margin-top: 10px;
}

.drawer-nav-item {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 9px 10px;
  border-radius: 10px;
  cursor: pointer;
  font-size: 16px;
  color: var(--text);
}
.drawer-nav-item:hover { background: var(--bubble-assistant); }

/* ===== Меню вложений в чате (своя версия меню "+" из Gemini — только реальные возможности:
   фото и PDF читает Claude нативно, заметка — переход к уже существующей функции заметок) ===== */
.attach-wrap { position: relative; }

.attach-toggle-btn {
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
  color: var(--primary);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  font-size: 21px;
  cursor: pointer;
  line-height: 1;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.15s, background 0.15s, color 0.15s;
}
.attach-toggle-btn:hover { background: var(--primary); color: white; }
.attach-toggle-btn.open { transform: rotate(45deg); background: var(--primary); color: white; }

/* Кнопка микрофона (голосовой ввод) — тот же визуальный язык, что и кнопка вложений,
   плюс отдельное "recording" состояние с пульсацией, пока идёт запись речи. */
.mic-btn {
  background: var(--bubble-assistant);
  border: 1px solid var(--border);
  color: var(--primary);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  font-size: 17px;
  cursor: pointer;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s, color 0.15s;
}
.mic-btn:hover { background: var(--primary); color: white; }
.mic-btn.recording {
  background: var(--danger);
  color: white;
  animation: micPulse 1.2s ease-in-out infinite;
}

@keyframes micPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(224, 69, 92, 0.45); }
  50% { box-shadow: 0 0 0 9px rgba(224, 69, 92, 0); }
}

.attach-menu {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 170px;
  animation: fadeInUp 0.15s ease-out;
}

.attach-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  padding: 9px 10px;
  border-radius: 9px;
  cursor: pointer;
  font-size: 16px;
  color: var(--text);
  text-align: left;
}
.attach-menu-item:hover { background: var(--bubble-assistant); }

/* ===== Пикер эмодзи — свой набор, современный набор реакций, а не полагаемся на системный
   пикер ОС (который у всех разный и часто выглядит устаревшим/непоследовательным). ===== */
.emoji-wrap { position: relative; }

.emoji-picker {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  padding: 8px;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 2px;
  width: 240px;
  animation: fadeInUp 0.15s ease-out;
}

.emoji-option {
  background: none;
  border: none;
  font-size: 22px;
  padding: 6px;
  border-radius: 8px;
  cursor: pointer;
  line-height: 1;
}
.emoji-option:hover { background: var(--bubble-assistant); }

/* ===== Заметки ===== */
.note-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 20px;
}

.note-form input,
.note-form textarea {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
  color: var(--text);
  font-family: inherit;
  font-size: 16px;
}

.note-form textarea { min-height: 80px; resize: vertical; }

.note-reminder-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 14.5px;
  color: var(--muted);
}

.note-form button {
  align-self: flex-start;
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  padding: 9px 18px;
  border-radius: 10px;
  font-weight: 600;
  cursor: pointer;
}

.notes-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.note-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 14px;
  box-shadow: var(--shadow);
}

.note-card h4 { margin: 0 0 4px; }
.note-card p { margin: 0 0 8px; color: var(--muted); font-size: 16px; white-space: pre-wrap; }
.note-card .note-reminder { color: var(--primary); font-weight: 600; font-size: 14.5px; }

.note-card-delete {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 14.5px;
  padding: 2px 0;
}
.note-card-delete:hover { color: var(--danger); }

/* ===== Panels ===== */
.panel {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
}

#panel-chat, #panel-courses {
  padding: 0;
}

#panel-chat { position: relative; }

.save-screenshot-btn {
  position: absolute;
  top: 14px;
  right: 16px;
  z-index: 4;
  background: var(--card);
  border: 1px solid var(--border);
  color: var(--muted);
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow);
}
.save-screenshot-btn:hover { color: var(--primary); border-color: var(--primary); }

/* ===== Chat ===== */
.chat-empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 6px;
  padding: 20px;
  animation: fadeIn 0.5s ease-out;
  /* Свечение вокруг орба намеренно выходит далеко за его пределы (сам орб 100px, сияние —
     около 273px) и на узком экране растягивало страницу вбок: появлялась прокрутка на 26px
     и бледная полоса справа. clip, а не hidden: hidden сделал бы элемент прокручиваемым и
     сломал бы прилипающие заголовки, а clip просто обрезает лишнее по краю экрана — там,
     где сияние и так уже растворилось, поэтому вид не меняется. */
  overflow-x: clip;
}

.chat-empty-state h2 {
  margin: 8px 0 0;
  font-size: 24px;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.chat-empty-state p {
  margin: 0;
  color: var(--muted);
  font-size: 16px;
  max-width: 320px;
}

.zehin-orb-xl {
  width: 100px;
  height: 100px;
}

/* Мягкое туманное "облако" вокруг кристалла на большой звезде (по образцу голосового режима
   ChatGPT/Gemini) — размытый радиальный ореол ПОЗАДИ гранёного кристалла, а не отдельная
   картинка: тот же принцип, что и весь остальной дизайн — только CSS, никаких изображений. */
.zehin-orb-xl::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 220%;
  height: 220%;
  transform: translate(-50%, -50%);
  /* Размытие убрано, а мягкость набрана самим градиентом (больше промежуточных остановок и
     раннее затухание): анимированный blur заставлял браузер пересчитывать размытие на
     каждом кадре дыхания, а радиальный градиент рисуется один раз. */
  background: radial-gradient(circle,
    var(--orb-halo-a) 0%,
    var(--orb-halo-a) 18%,
    var(--orb-halo-b) 42%,
    transparent 68%);
  /* Форма по содержимому: градиент и так круглый, но круглая форма страхует от квадратных
     теней, если сюда когда-нибудь снова попадёт box-shadow. */
  border-radius: 50%;
  z-index: -1;
  /* Своя анимация, а НЕ общая breathe. Раньше стояла breathe, и это давало сразу два дефекта,
     хорошо заметных на снимке экрана:
     1) её кадры задают transform: scale(...), а любой transform в анимации ЗАМЕЩАЕТ статический
        целиком — вместе со scale стиралось центрирование translate(-50%, -50%), и ореол съезжал
        вправо-вниз ровно на половину своего размера;
     2) breathe добавляет box-shadow, рассчитанный на круглый орб, а этот элемент квадратный —
        вокруг него рисовалась квадратная светящаяся рамка.
     Поэтому центрирование повторяется в КАЖДОМ кадре — иначе оно снова потеряется. */
  animation: haloBreathe 3.2s ease-in-out infinite;
  pointer-events: none;
}

@keyframes haloBreathe {
  0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.85; }
  50% { transform: translate(-50%, -50%) scale(1.1); opacity: 1; }
}

.zehin-orb-xl .eq-bars { height: 30px; gap: 6px; }
.zehin-orb-xl .eq-bars span { width: 5px; }

.messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.msg {
  max-width: 75%;
  padding: 11px 16px;
  border-radius: 18px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-wrap: break-word;
  animation: fadeInUp 0.35s ease-out;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.msg.user {
  align-self: flex-end;
  background: var(--bubble-user);
  color: white;
  border-bottom-right-radius: 4px;
}

.msg.assistant {
  align-self: flex-start;
  background: var(--bubble-assistant);
  color: var(--text);
  border-bottom-left-radius: 4px;
}

/* Markdown-контент внутри ответов ассистента (resolveTypingIndicator/addMessageBubble рендерят
   через renderMarkdownSafe) — раньше это был голый textContent без форматирования вообще. */
.msg p { margin: 0 0 8px; }
.msg p:last-child { margin-bottom: 0; }
.msg ul, .msg ol { margin: 0 0 8px; padding-left: 22px; }
.msg ul:last-child, .msg ol:last-child { margin-bottom: 0; }
.msg li { margin-bottom: 3px; }

.msg code {
  background: rgba(0, 0, 0, 0.08);
  padding: 2px 6px;
  border-radius: 5px;
  font-family: 'SF Mono', Consolas, Menlo, monospace;
  font-size: 0.9em;
}
.msg.user code { background: rgba(255, 255, 255, 0.2); }

.msg pre {
  background: rgba(0, 0, 0, 0.06);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  margin: 0 0 8px;
  overflow-x: auto;
  white-space: pre;
}
.msg pre:last-child { margin-bottom: 0; }
.msg pre code { background: none; padding: 0; font-size: 0.85em; }

.msg table {
  border-collapse: collapse;
  width: 100%;
  margin: 0 0 8px;
  font-size: 0.92em;
  display: block;
  overflow-x: auto; /* широкие таблицы скроллятся сами, не ломая ширину пузыря */
}
.msg th, .msg td {
  border: 1px solid var(--border);
  padding: 6px 10px;
  text-align: left;
}
.msg th { background: rgba(0, 0, 0, 0.06); font-weight: 700; }

.msg pre.mermaid {
  background: transparent;
  border: none;
  padding: 4px 0;
  text-align: center;
}
.msg pre.mermaid svg { max-width: 100%; height: auto; }

@media (prefers-color-scheme: dark) {
  .msg code, .msg pre, .msg th { background: rgba(255, 255, 255, 0.08); }
}

.confidence-badge {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 10px;
  margin-bottom: 6px;
  background: var(--card);
}
.confidence-badge.confidence-высокая { color: var(--success); }
.confidence-badge.confidence-средняя { color: #b8860b; }
.confidence-badge.confidence-низкая { color: var(--danger); }

.email-draft-card {
  align-self: flex-start;
  max-width: 85%;
  background: var(--card);
  border: 1px solid var(--accent);
  border-radius: 14px;
  padding: 14px 16px;
  margin: 4px 0;
}

.email-draft-label {
  margin: 0 0 8px;
  font-weight: 700;
  color: var(--accent);
  font-size: 14.5px;
}

.email-draft-card p { margin: 4px 0; font-size: 15.5px; }
.email-draft-body { white-space: pre-wrap; color: var(--muted); background: var(--bubble-assistant); border-radius: 8px; padding: 8px 10px; }

.email-draft-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.email-draft-cancel, .email-draft-send {
  border: none;
  padding: 8px 16px;
  border-radius: 9px;
  font-weight: 600;
  cursor: pointer;
  font-size: 14.5px;
}
.email-draft-cancel { background: var(--bubble-assistant); color: var(--muted); }
.email-draft-send { background: linear-gradient(135deg, var(--primary), var(--primary-dark)); color: white; }

.msg-actions {
  display: flex;
  gap: 6px;
  margin-top: 8px;
  flex-wrap: wrap;
}

.msg-action-btn {
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
  font-size: 13.5px;
  padding: 3px 9px;
  border-radius: 8px;
  cursor: pointer;
}
.msg-action-btn:hover { border-color: var(--primary); color: var(--primary); }

.explain-as-wrap { position: relative; }

.explain-as-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-lg);
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 120px;
  z-index: 5;
  animation: fadeInUp 0.15s ease-out;
}

.explain-as-option {
  background: none;
  border: none;
  padding: 6px 10px;
  border-radius: 7px;
  text-align: left;
  font-size: 14.5px;
  color: var(--text);
  cursor: pointer;
}
.explain-as-option:hover { background: var(--bubble-assistant); }

.msg.error {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--danger);
}

.msg-retry-btn {
  flex-shrink: 0;
  background: var(--danger);
  color: white;
  border: none;
  padding: 6px 12px;
  border-radius: 8px;
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
}
.msg-retry-btn:hover { opacity: 0.85; }

.typing-indicator {
  display: inline-flex;
  gap: 4px;
  align-items: center;
  padding: 4px 0;
}

.typing-indicator span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--muted);
  animation: typingBounce 1.2s infinite;
}

.typing-indicator span:nth-child(2) { animation-delay: 0.15s; }
.typing-indicator span:nth-child(3) { animation-delay: 0.3s; }

.message-form {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 20px;
  background: var(--card);
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}

.input-wrap { flex: 1; position: relative; }
.input-wrap input[type="text"] { width: 100%; }

/* Подсказка транслитерации ("privet" -> "привет") — всплывает над полем ввода,
   как у Google/Yandex-клавиатур, клик подставляет кириллический вариант. */
.translit-chip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  background: var(--primary);
  color: white;
  border: none;
  padding: 7px 14px;
  border-radius: 20px;
  font-size: 15.5px;
  box-shadow: var(--shadow);
  animation: fadeInUp 0.2s ease-out;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
}

.translit-chip .translit-chip-hint {
  opacity: 0.75;
  font-size: 13.5px;
}

.message-form input[type="text"] { flex: 1; }

/* Раньше была прямоугольная кнопка со словом "Отправить" — теперь круглая иконка-бумажный
   самолётик (как в Telegram/WhatsApp), компактнее и быстрее считывается глазом. */
.send-btn {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 3px 10px rgba(67, 56, 202, 0.25);
  transition: transform 0.15s;
}
.send-btn:hover { transform: scale(1.06); }
.send-btn:active { transform: scale(0.94); }

/* Блик, пробегающий по звезде. Сделан через clip-path на копии той же фигуры: белая
   полоса ездит внутри контура звезды и обрезается по нему, поэтому свет "скользит по
   грани", а не светится квадратом поверх. Без картинок и без JS. */
.send-star-shine {
  clip-path: polygon(-40% 0%, -20% 0%, -5% 100%, -25% 100%);
  animation: sendStarShine 3.6s ease-in-out infinite;
  opacity: 0.9;
}

@keyframes sendStarShine {
  0%, 65% { clip-path: polygon(-40% 0%, -20% 0%, -5% 100%, -25% 100%); }
  85%, 100% { clip-path: polygon(105% 0%, 125% 0%, 140% 100%, 120% 100%); }
}

/* Лёгкое золотое сияние вокруг кнопки — звезда должна выглядеть как драгоценность,
   а не как плоская иконка. Усиливается при наведении. */
.send-btn { box-shadow: 0 3px 10px rgba(67, 56, 202, 0.25), 0 0 12px rgba(240, 195, 60, 0.35); }
.send-btn:hover { box-shadow: 0 4px 14px rgba(67, 56, 202, 0.3), 0 0 20px rgba(240, 195, 60, 0.6); }

@media (prefers-reduced-motion: reduce) {
  .send-star-shine { animation: none; opacity: 0; }
}

.image-name {
  font-size: 14.5px;
  color: var(--muted);
  max-width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ===== Education ===== */
#panel-courses > div { padding: 20px; overflow-y: auto; }
#lesson-chat-view { display: flex; flex-direction: column; height: 100%; padding: 0 !important; }
#lesson-chat-view h2, #lesson-chat-view .back-btn { padding: 0 20px; }
#lesson-chat-view h2 { margin-top: 12px; }

.back-btn {
  background: none;
  border: none;
  color: var(--primary);
  font-size: 16px;
  margin-bottom: 12px;
  padding: 0;
}

.back-btn:hover { text-decoration: underline; }

.courses-list, .lessons-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Фон раздела "Обучение" — учебная атрибутика (тетрадная клетка, доска, книги, карандаши)
   на CSS-градиентах. Никаких картинок: страница остаётся лёгкой, а фон сам подстраивается
   под тему. Держим очень бледным — под ним читается список курсов, это фон, а не обои. */
#panel-courses {
  position: relative;
  background-image:
    /* тетрадная клетка */
    linear-gradient(var(--study-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--study-line) 1px, transparent 1px),
    /* корешки книг на "полке" внизу */
    repeating-linear-gradient(90deg, var(--study-book-a) 0 14px, transparent 14px 22px, var(--study-book-b) 22px 34px, transparent 34px 46px),
    /* мягкое пятно "зелёной доски" в углу */
    radial-gradient(60% 45% at 88% 12%, var(--study-board) 0%, transparent 70%);
  background-size: 34px 34px, 34px 34px, 100% 60px, 100% 100%;
  background-position: 0 0, 0 0, 0 100%, 0 0;
  background-repeat: repeat, repeat, no-repeat, no-repeat;
}

.course-card, .lesson-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 16px;
  cursor: pointer;
  transition: box-shadow 0.2s, transform 0.15s, border-color 0.2s;
  animation: fadeInUp 0.3s ease-out backwards;
}

.course-card:hover, .lesson-card:hover {
  box-shadow: var(--shadow);
  transform: translateY(-2px);
  border-color: var(--primary);
}

.course-card h3, .lesson-card h3 { margin: 0 0 4px; font-size: 17px; }
.course-card p, .lesson-card p { margin: 0; color: var(--muted); font-size: 15.5px; }

/* Личная статистика ученика по курсу — компактные плитки над списком уроков. Данные (прогресс,
   время) уже собирались в БД и раньше нигде не показывались ученику. */
.course-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}

.stat-tile {
  flex: 1 1 120px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.stat-value {
  font-size: 20px;
  font-weight: 800;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.stat-label {
  font-size: 13.5px;
  color: var(--muted);
}

#enroll-form, #lesson-chat-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

#enroll-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 15.5px;
  color: var(--muted);
}

#enroll-form button, .assignment-box button {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  padding: 11px;
  border-radius: 12px;
  font-weight: 600;
}

.assignment-box {
  margin: 16px 20px;
  padding: 12px 16px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
}

/* ===== Тест на определение уровня ===== */
.placement-box {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 16px;
  margin-bottom: 16px;
}

.placement-hint {
  margin: 0 0 10px;
  color: var(--muted);
  font-size: 16px;
}

.secondary-btn {
  background: var(--bubble-assistant);
  color: var(--primary);
  border: 1px solid var(--border);
  padding: 9px 16px;
  border-radius: 10px;
  font-weight: 600;
  cursor: pointer;
}
.secondary-btn:hover { background: var(--primary); color: white; }

/* История прошлых попыток теста — хранится в аккаунте, видна при пересдаче через время,
   чтобы ученик мог сравнить прогресс (например, через месяц-два учёбы). */
.placement-history {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

.placement-history-title {
  font-size: 14.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin: 0 0 8px;
}

.placement-history-row {
  display: flex;
  justify-content: space-between;
  font-size: 15.5px;
  padding: 4px 0;
  color: var(--text);
}

.placement-history-row .date { color: var(--muted); }

/* Прогресс-бар теста — 40 вопросов подряд одним списком читались как "стена вопросов",
   поэтому тест теперь показывает по одному вопросу за раз с индикатором прогресса. */
.placement-progress {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
}

.placement-progress-track {
  flex: 1;
  height: 8px;
  border-radius: 4px;
  background: var(--bubble-assistant);
  overflow: hidden;
}

.placement-progress-fill {
  height: 100%;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--primary), var(--accent));
  transition: width 0.3s ease-out;
}

.placement-progress-label {
  font-size: 14.5px;
  color: var(--muted);
  white-space: nowrap;
  font-weight: 600;
}

.placement-question-text {
  margin: 0 0 18px;
  font-size: 19px;
  font-weight: 700;
  line-height: 1.4;
  animation: fadeInUp 0.25s ease-out;
}

.placement-options-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
  animation: fadeInUp 0.3s ease-out;
}

.placement-option-card {
  text-align: left;
  background: var(--bubble-assistant);
  border: 2px solid transparent;
  border-radius: 12px;
  padding: 13px 16px;
  font-size: 16px;
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.15s, transform 0.15s;
}
.placement-option-card:hover { transform: translateX(2px); }
.placement-option-card.selected {
  border-color: var(--primary);
  background: var(--card);
  font-weight: 600;
}

.placement-nav {
  display: flex;
  justify-content: space-between;
  margin-top: 22px;
}

/* Экран завершения теста — тот самый "ура, тест закончился" по запросу, вместо тихой
   подмены строки текстом результата. */
.placement-celebration {
  text-align: center;
  padding: 32px 16px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  margin-bottom: 16px;
  animation: fadeInUp 0.35s ease-out;
}

.placement-celebration-emoji {
  font-size: 48px;
  margin-bottom: 8px;
  animation: pulse 1.2s ease-in-out infinite;
}

.placement-celebration h3 {
  margin: 0 0 8px;
  font-size: 21px;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.placement-celebration p {
  margin: 0;
  color: var(--muted);
  font-size: 16px;
}

/* ===== Живая камера ===== */
.camera-modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.9);
  z-index: 50;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  animation: fadeIn 0.15s ease-out;
}

#camera-video {
  max-width: 92vw;
  max-height: 70vh;
  border-radius: 16px;
  background: #000;
}

.camera-error {
  color: #ffb4be;
  font-size: 16px;
  max-width: 80vw;
  text-align: center;
}

.camera-controls {
  display: flex;
  align-items: center;
  gap: 20px;
}

.camera-capture-btn {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  border: 3px solid white;
  background: var(--accent);
  color: #1a1a2e;
  font-size: 26px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.camera-controls .secondary-btn { background: rgba(255, 255, 255, 0.15); color: white; border-color: rgba(255, 255, 255, 0.3); }

/* ===== Голосовой режим — полноэкранный overlay, тот же паттерн, что .camera-modal =====
   Переиспользует гранёный 3D-орб (см. .zehin-orb-3d-wrap выше) как визуальное "лицо" режима,
   вместо отдельного нового визуала с нуля. */
.voice-overlay {
  position: fixed;
  inset: 0;
  background: rgba(5, 7, 20, 0.94);
  z-index: 51;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  animation: fadeIn 0.15s ease-out;
}

.voice-close-btn {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.12);
  color: white;
  font-size: 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.voice-close-btn:hover { background: rgba(255, 255, 255, 0.22); }

/* Кнопка "свернуть" стоит слева от "закрыть", чтобы случайно не завершить разговор,
   когда человек всего лишь хотел освободить экран. */
.voice-minimize-btn { right: 68px; }

/* Эквалайзер голосового режима. Высота полосок задаётся из JS по реальному спектру
   микрофона — здесь только внешний вид и плавность между кадрами. */
.voice-eq {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 5px;
  height: 44px;
  opacity: 0;
  transition: opacity 0.25s;
}

/* Показываем только когда реально слушаем: в покое или во время ответа пустой
   дёргающийся эквалайзер только сбивает с толку. */
.voice-overlay.listening .voice-eq { opacity: 1; }

/* Полоски растут ОТ ЦЕНТРА в обе стороны (align-items: center вместо flex-end) — так
   эквалайзер выглядит как звуковая волна, а не как столбчатый график. */
.voice-eq { align-items: center; height: 64px; }

.voice-eq span {
  width: 5px;
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(to top, var(--accent), var(--accent-glow));
  box-shadow: 0 0 8px rgba(240, 195, 60, 0.55);
  transition: height 0.07s linear, box-shadow 0.15s linear;
}

/* Отражение под волной — добавляет глубины, как у звуковых визуализаторов. */
.voice-eq::after {
  content: '';
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 74px;
  width: 120px;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(240, 195, 60, 0.35), transparent);
}

.voice-status-text {
  color: rgba(255, 255, 255, 0.85);
  font-size: 16px;
  max-width: 80vw;
  text-align: center;
  min-height: 24px;
}

.voice-talk-btn {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  padding: 13px 28px;
  border-radius: 30px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(67, 56, 202, 0.4);
}
.voice-talk-btn:hover { transform: translateY(-1px); }

/* Состояния орба в голосовом режиме — переключаются классами listening/thinking/speaking
   на #voice-orb, тем же принципом, что setOrbThinking() переключает .active на #zehin-orb.
   listening/speaking НЕ анимируются через @keyframes — масштаб и свечение управляются
   напрямую из JS в реальном времени (громкость микрофона / ритм слов озвучки, см. app.js
   startMicVisualizer/currentUtterance 'boundary'), поэтому орб реагирует на настоящий голос,
   а не крутит одну и ту же CSS-анимацию по кругу. transition сглаживает шаг между кадрами.
   Ниже — только мгновенный fallback-глоу на случай, пока JS-визуализация ещё не подключилась
   (доступ к микрофону для анализа звука — это ОТДЕЛЬНЫЙ запрос от самого SpeechRecognition). */
#voice-orb.listening,
#voice-orb.speaking {
  transition: transform 0.08s linear, box-shadow 0.08s linear;
  box-shadow: 0 0 24px 8px var(--orb-glow-b);
}
#voice-orb.thinking {
  animation: breatheActive 1s ease-in-out infinite;
}

#submit-placement-btn {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: white;
  border: none;
  padding: 11px 20px;
  border-radius: 12px;
  font-weight: 600;
  cursor: pointer;
}

.assignment-box summary { cursor: pointer; font-weight: 600; }
.assignment-box textarea { min-height: 80px; margin: 10px 0; resize: vertical; }
#assignment-result { margin-top: 10px; font-size: 16px; white-space: pre-wrap; }

/* ===== Settings ===== */
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 16px;
  max-width: 480px;
}

.toggle-row input[type="checkbox"] {
  width: 44px;
  height: 24px;
  accent-color: var(--primary);
}

.settings-section {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 16px;
  max-width: 480px;
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
}

.settings-hint {
  margin: 0;
  color: var(--muted);
  font-size: 14.5px;
}

#settings-status {
  color: var(--muted);
  font-size: 15.5px;
}

/* ===== Scrollbars (легче под тёмную тему) ===== */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* =========================================================================
   МОБИЛЬНАЯ АДАПТАЦИЯ
   Приложение ставится на телефон как PWA и открывается на весь экран, поэтому
   здесь решаются именно проблемы телефона, а не просто "сделать поуже".
   ========================================================================= */

/* Общие правила для сенсорного ввода — нужны на всех размерах экрана. */
html {
  /* Safari на iOS самовольно увеличивает шрифт при повороте в альбом. */
  -webkit-text-size-adjust: 100%;
}

button, .nav-btn, .icon-btn, a {
  /* Серый прямоугольник вспышки при касании выглядит как визуальный дефект. */
  -webkit-tap-highlight-color: transparent;
}

.messages, .panel, .chat-drawer {
  /* Долистав до конца, палец начинал тянуть всю страницу (и обновление жестом
     сверху). Прокрутка должна оставаться внутри своего блока. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* Длинный код, ссылка или таблица не должны растягивать всю страницу вбок. */
.msg { overflow-wrap: anywhere; }
.msg pre, .msg table { max-width: 100%; overflow-x: auto; }

@media (max-width: 720px) {
  .nav-btn span, .logout-btn span { display: none; }

  /* ШАПКА НА ТЕЛЕФОНЕ.
     Было: семь элементов подряд с gap: 16px и padding: 20px по краям — только промежутки
     съедали 96px из 360, и шапка оказывалась на 147px ШИРЕ экрана. Из-за этого вся страница
     получала горизонтальную прокрутку, и содержимое выглядело обрезанным слева — ровно то,
     что владелец увидел на своём телефоне. Подписи у кнопок скрывались (правило выше),
     но этого не хватало: дело было в промежутках и отступах, а не в тексте.

     flex-wrap здесь главное: даже если на каком-то устройстве шрифт окажется крупнее и
     содержимое всё же не поместится, шапка перенесётся на вторую строку, а не вылезет за
     экран. Одна сломанная строка лучше сломанной вёрстки всей страницы. */
  .app-header {
    gap: 6px;
    padding-left: 10px;
    padding-right: 10px;
    flex-wrap: wrap;
  }
  /* Орб в шапке — украшение (индикатор «думает» дублируется в самой переписке).
     На телефоне место дороже. */
  .app-header .zehin-orb { display: none; }
  .main-nav { gap: 2px; justify-content: flex-end; }
  .nav-btn { padding: 9px 10px; }
  .logout-btn { padding: 8px 9px; }
  .streak-badge, .quota-badge { padding: 4px 9px; font-size: 13px; }

  /* КЛЮЧЕВОЕ для iPhone: поле ввода — самый нижний элемент, и без учёта
     безопасной зоны его наполовину перекрывает системная полоса жеста "домой". */
  .message-form {
    padding: 10px 8px calc(10px + env(safe-area-inset-bottom));
    gap: 5px;
  }

  /* СТРОКА ВВОДА НА ТЕЛЕФОНЕ.
     Перед полем стояли четыре кнопки (+, микрофон, голосовой режим, эмодзи) и одна после.
     На экране 360px полю оставалось около сотни пикселей, и подсказка «Напишите сообщение…»
     обрывалась на середине — поле выглядело сломанным ещё до того, как в него что-то ввели.

     Эмодзи убираем: на телефоне они и так есть в системной клавиатуре, своя кнопка здесь
     дублирует её и отнимает место у главного элемента экрана. На компьютере кнопка остаётся —
     там места хватает, а быстрого доступа к эмодзи с клавиатуры нет. */
  .emoji-wrap { display: none; }
  /* min-width: 0 обязателен: без него поле во flex-строке отказывается сжиматься ниже
     своей «естественной» ширины и выдавливает соседей за край экрана. */
  .input-wrap { min-width: 0; }
  .mic-btn, .attach-toggle-btn { width: 38px; height: 38px; }
  .app-header {
    padding-top: calc(10px + env(safe-area-inset-top));
  }

  /* Safari на iOS ПРИНУДИТЕЛЬНО зумит страницу при фокусе на поле, если его шрифт
     меньше 16px — экран прыгает, и вернуть масштаб потом можно только вручную.
     Отключать зум через viewport нельзя (это ломает доступность), поэтому просто
     держим 16px. */
  input[type="text"], input[type="email"], input[type="password"],
  input[type="search"], input[type="datetime-local"], textarea, select {
    font-size: 16px;
  }

  /* Палец — не курсор мыши: цель меньше ~44px промахивается. */
  .icon-btn, .attach-toggle-btn, .send-btn, .mic-btn, .voice-mode-btn {
    min-width: 42px;
    min-height: 42px;
  }

  /* На узком экране пузырь в 75% ширины оставляет бесполезное поле сбоку. */
  .msg { max-width: 88%; }
  .messages { padding: 14px 12px; gap: 12px; }
  .panel { padding: 14px 12px; }

  /* Меню вложений открывалось поверх поля ввода и не помещалось. */
  .attach-menu { max-width: calc(100vw - 24px); }

  .generated-image { max-width: 100%; }

  /* Напоминание о перерыве не должно налезать на поле ввода. */
  .break-reminder {
    bottom: calc(78px + env(safe-area-inset-bottom));
    font-size: 14px;
  }
}

/* Совсем узкие телефоны (примерно iPhone SE и недорогие Android): борьба за каждый пиксель. */
@media (max-width: 400px) {
  .messages { padding: 12px 8px; }
  .msg { max-width: 92%; font-size: 15px; }
  .message-form { padding-left: 8px; padding-right: 8px; }

  /* Название убираем: на главный экран ведёт и кнопка «Чат» в навигации, а места
     под семь элементов в одну строку здесь уже нет. */
  .app-header .brand { display: none; }
  .app-header { gap: 4px; padding-left: 8px; padding-right: 8px; }
  .nav-btn { padding: 9px 7px; }
}

/* Телефон в альбомной ориентации: по высоте остаётся очень мало места, поэтому
   убираем крупные декоративные отступы, чтобы переписка не сжималась в щель. */
@media (max-height: 480px) and (orientation: landscape) {
  .chat-empty-state { display: none; }
  .messages { padding: 8px 12px; }
  .message-form { padding-top: 6px; padding-bottom: 6px; }
}
