/* Бот лендинга: кнопка вызова, панель разговора, видео-аватар.
 *
 * Токены — общие (`shared/tokens.css`), своих цветов и своих размеров шкалы
 * здесь нет: производные считаются через color-mix от бренд-токена, чтобы правка
 * палитры доезжала сюда сама и чтобы contrast_audit продолжал видеть исходный цвет.
 *
 * ЗАЩИТА ОТ КЛАССА «hidden против авторского display» (память скилла, шесть
 * случаев с 27.07.2026). Панель и второй шаг прячутся атрибутом `hidden`, а у их
 * классов есть `display:flex` — без правила ниже атрибут не сработал бы, и
 * панель висела бы раскрытой при `aria-expanded="false"`. Правило намеренно
 * ОГРАНИЧЕНО своим компонентом: общий системный фикс на весь фронт — отдельная
 * работа, он трогает и кабинет.
 */
.lorabot[hidden],
.lorabot [hidden] { display: none !important; }

/* Рамка ПОЛЯ ВВОДА — отдельным значением, а не общим `--border`.
 * `--border` даёт 1.35:1 к белому: гейт токенов честно помечает это WARN и не
 * блокирует, потому что 1.4.11 контекстный, — для декоративной рамки карточки
 * этого достаточно. Но поле ввода это КОНТРОЛ, и его границу человек должен
 * различать. Значение взято ровно то же, что у полей кабинета
 * (`app2/css/mockup.css`, `--input-border`): 3.51:1 к белому и 3.53:1 к тёмной
 * подложке — один факт, одно значение, второго экземпляра не заводим.
 */
.lorabot { --lorabot-input-border: #7E8A99; }
[data-theme="dark"] .lorabot { --lorabot-input-border: #66707E; }

/* ── Кнопка вызова ─────────────────────────────────────────────────────────
 * Не круглый значок «чат»: круг ничего не обещает и читается как поддержка.
 * Здесь лицо и глагол — видно, с кем и о чём разговор.
 */
.lorabot__launcher {
  position: fixed;
  right: var(--space-4);
  bottom: var(--space-4);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text);
  font: 600 var(--small) / 1.2 var(--font-primary);
  cursor: pointer;
  box-shadow: var(--shadow-lg);
  min-height: 48px;               /* тач-цель с запасом над порогом 24px */
  transition: transform .18s ease, box-shadow .18s ease;
}
.lorabot__launcher:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.lorabot__launcher:focus-visible {
  outline: 3px solid var(--primary);
  outline-offset: 2px;
}
.lorabot__face {
  width: 32px; height: 32px;
  border-radius: 50%;
  object-fit: cover;
  object-position: 50% 22%;       /* лицо, а не центр кадра 3:4 */
  background: var(--bg-alt);
  flex: none;
}

/* ── Панель ────────────────────────────────────────────────────────────────
 *
 * НИЗ И ВЫСОТА СЧИТАЮТСЯ ОТ ВИДИМОЙ ОБЛАСТИ, А НЕ ОТ ЭКРАНА (06.09.2026).
 * `--lorabot-kb` — сколько экрана снизу занимает экранная клавиатура,
 * `--lorabot-vvh` — сколько от экрана осталось видно; обе ставит JS по
 * `visualViewport`. Без клавиатуры первая равна нулю, и правила читаются как
 * прежние, поэтому вид на десктопе не меняется ни на пиксель.
 *
 * ПОЧЕМУ ЭТО В БАЗОВОМ ПРАВИЛЕ, А НЕ В МОБИЛЬНОМ МЕДИА. Первая редакция фикса
 * стояла только внутри `max-width: 480px`, и замер 06.09 показал цену такого
 * сужения: планшет в портрете (768px и шире) в это медиа не попадает, а
 * клавиатура на нём точно такая же — панель так же ушла бы под неё. Признак
 * здесь не «узкий экран», а «часть экрана занята клавиатурой».
 */
.lorabot__panel {
  position: fixed;
  right: var(--space-4);
  bottom: calc(var(--space-4) + var(--lorabot-kb, 0px));
  z-index: 61;
  display: flex;
  flex-direction: column;
  width: min(380px, calc(100vw - var(--space-6)));
  height: min(600px, calc(var(--lorabot-vvh, 100vh) - var(--space-7)));
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

/* Панель принимает фокус программно (на телефоне он уходит сюда вместо поля).
 * Свой индикатор ей нужен: без него человек, попавший на панель с клавиатуры,
 * не видит, где находится фокус. */
.lorabot__panel:focus-visible { outline: 3px solid var(--primary); outline-offset: -3px; }

.lorabot__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--primary) 6%, var(--surface));
}
.lorabot__title { margin: 0; font: 600 var(--small) / 1.3 var(--font-primary); color: var(--text); }
.lorabot__sub { margin: 0; font-size: var(--small); line-height: 1.35; color: var(--text-secondary); }
.lorabot__close {
  margin-left: auto;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
}
.lorabot__close:hover { background: var(--neutral-bg); color: var(--text); }
.lorabot__close:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }

/* Аватар — окно портретом, а не значок. Единственный «характерный» приём
 * компонента; всё остальное сдержанно, потому что это утилита поверх страницы. */
.lorabot__avatar {
  position: relative;
  flex: none;
  height: 152px;
  overflow: hidden;
  background: var(--bg-alt);
  border-bottom: 1px solid var(--border);
}
.lorabot__video {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 30%;   /* кадр по глазам и рту: 18% резало рот, 42% — глаза */
  display: block;
}

/* Орган остановки движения (2.2.2). Лежит НА аватаре, но своей плашкой: на
 * видео любой прозрачный контрол теряется, а этот обязан быть опознаваем. */
.lorabot__motion {
  position: absolute;
  right: var(--space-2);
  bottom: var(--space-2);
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
}
.lorabot__motion:hover { background: var(--neutral-bg); }
.lorabot__motion:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }

/* «Показать ответ целиком» — второй орган по 2.2.2. Появляется только на время
 * печати; `hidden` работает благодаря правилу в начале файла. */
.lorabot__skip {
  margin: 0 var(--space-4) var(--space-2);
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: 500 var(--small) / 1.3 var(--font-primary);
  cursor: pointer;
}
.lorabot__skip:hover { background: var(--neutral-bg); }
.lorabot__skip:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }

.lorabot__feed {
  flex: 1 1 auto;
  min-height: 0;                  /* иначе flex-ребёнок не сжимается и рвёт панель */
  overflow-y: auto;
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.lorabot__msg {
  max-width: 88%;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: 16px;
  line-height: var(--lh-body);
  white-space: pre-wrap;
  overflow-wrap: anywhere;        /* длинная ссылка не растягивает панель */
}
.lorabot__msg--bot {
  align-self: flex-start;
  background: var(--neutral-bg);
  color: var(--text);
  border-bottom-left-radius: var(--radius-sm);
}
.lorabot__msg--me {
  align-self: flex-end;
  background: color-mix(in srgb, var(--primary) 12%, var(--surface));
  color: var(--text);
  border-bottom-right-radius: var(--radius-sm);
}
/* Ошибка обозначена НЕ ТОЛЬКО цветом: у неё есть слово «Не получилось» в тексте
 * и рамка. 1.4.1 не нарушается. */
.lorabot__msg--fail {
  align-self: flex-start;
  background: var(--error-bg);
  color: var(--error-text);
  border: 1px solid var(--error-border);
}

/* Точки ожидания. Появляются ДО запроса и означают ровно «запрос ушёл». */
.lorabot__wait { display: flex; gap: 4px; align-self: flex-start; padding: var(--space-3) var(--space-4); }
.lorabot__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-muted);
  animation: lorabot-blink 1.2s infinite ease-in-out;
}
.lorabot__dot:nth-child(2) { animation-delay: .15s; }
.lorabot__dot:nth-child(3) { animation-delay: .3s; }
@keyframes lorabot-blink { 0%,80%,100% { opacity: .25 } 40% { opacity: 1 } }

.lorabot__form {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3);
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.lorabot__input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--lorabot-input-border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: 400 16px / 1.4 var(--font-primary);
}
.lorabot__input:focus-visible { outline: 3px solid var(--primary); outline-offset: 1px; border-color: var(--primary); }
.lorabot__send {
  flex: none;
  min-width: 44px; min-height: 44px;
  display: grid; place-items: center;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--cta);
  color: var(--cta-text);
  cursor: pointer;
}
.lorabot__send:hover { background: var(--cta-hover); }
.lorabot__send:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }
.lorabot__send[disabled] { opacity: .5; cursor: default; }

/* Полоса действий: кнопки первого хода и ответы на короткий вопрос. Переносятся
 * по строкам — на 320px три кнопки в ряд не встают, а горизонтальная прокрутка
 * внутри панели читается как поломка. */
.lorabot__acts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: 0 var(--space-4) var(--space-3);
}
.lorabot__act {
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text);
  font: 500 var(--small) / 1.3 var(--font-primary);
  cursor: pointer;
}
.lorabot__act:hover { background: var(--neutral-bg); }
.lorabot__act:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }

/* Кнопка перехода в заявку. Отличается от «показать целиком» цветом действия:
 * одна продолжает чтение, другая уводит со страницы разговора. Одинаковый вид
 * при разном последствии проект уже разбирал (оценка занятия, 29.08). */
.lorabot__brief {
  margin: 0 var(--space-4) var(--space-2);
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--cta);
  color: var(--cta-text);
  font: 600 var(--small) / 1.3 var(--font-primary);
  cursor: pointer;
}
.lorabot__brief:hover { background: var(--cta-hover); }
.lorabot__brief:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }
.lorabot__brief[disabled] { opacity: .6; cursor: default; }

/* Подсказка при долгом пребывании. Стоит НАД кнопкой вызова, а не поверх
 * страницы: она предлагает разговор, а не перехватывает внимание. */
.lorabot__hint {
  position: fixed;
  right: var(--space-4);
  bottom: calc(var(--space-4) + 60px);
  z-index: 59;
  display: flex;
  align-items: stretch;
  max-width: min(320px, calc(100vw - var(--space-6)));
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.lorabot__hint-open {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: var(--space-3) var(--space-4);
  border: 0;
  background: transparent;
  color: var(--text);
  font: 400 var(--small) / 1.4 var(--font-primary);
  text-align: left;
  cursor: pointer;
}
.lorabot__hint-open:hover { background: var(--neutral-bg); }
.lorabot__hint-open:focus-visible { outline: 3px solid var(--primary); outline-offset: -3px; }
.lorabot__hint-x {
  flex: none;
  width: 44px;
  border: 0;
  border-left: 1px solid var(--border);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
}
.lorabot__hint-x:hover { background: var(--neutral-bg); color: var(--text); }
.lorabot__hint-x:focus-visible { outline: 3px solid var(--primary); outline-offset: -3px; }

@media (max-width: 480px) {
  .lorabot__hint { right: var(--space-3); left: var(--space-3); bottom: calc(var(--space-3) + 60px); max-width: none; }
}

.lorabot__vh {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ── Телефон: панель занимает низ экрана целиком ───────────────────────────
 *
 * ВЫСОТА И НИЗ СЧИТАЮТСЯ ПО ВИДИМОЙ ОБЛАСТИ, А НЕ ПО ЭКРАНУ (06.09.2026).
 * `100dvh` и `bottom: 0` описывают экран целиком, а экранная клавиатура по
 * умолчанию его не уменьшает — она лишь закрывает низ собой. Поэтому поле ввода
 * и последние реплики оказывались ПОД клавиатурой, и переписки во время печати
 * не было видно вовсе. Обе переменные ставит JS по `visualViewport`
 * (`frontend/js/lora-bot.js`), запасные значения в `var()` — для браузеров без
 * него: там поведение остаётся прежним, хуже не становится.
 */
/* Ширина в правиле обязательна. Медиазапрос по одной только высоте ловил и
 * ШИРОКОЕ невысокое окно: на 1280x520 панель растягивалась во всю ширину, и
 * строка реплики становилась 97 знаков при комфортных 45-90 (найдено ревью
 * 06.09). Телефон в ландшафте (844x390) в 900 px укладывается, десктоп — нет. */
@media (max-width: 480px), (max-width: 900px) and (max-height: 560px) {
  .lorabot__panel {
    right: 0; left: auto;
    bottom: var(--lorabot-kb, 0px);
    /* Не «во всю ширину», а «во всю ширину узкого экрана». На телефоне в
     * ландшафте (844 px) строка реплики иначе разъезжается за 90 знаков при
     * комфортном коридоре 45-90 — читать такую ленту тяжелее, чем узкую. */
    width: min(100%, 560px);
    height: min(88vh, calc(var(--lorabot-vvh, 100dvh) - 48px));
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-left: 0; border-right: 0; border-bottom: 0;
  }
  .lorabot__launcher { right: var(--space-3); bottom: var(--space-3); }
}

/* ── Компактный вид: места мало, аватар уходит в кружок ────────────────────
 *
 * Включается классом из JS, когда видимая высота меньше 520 px или поднялась
 * клавиатура. Портрет в 152 px в такой момент съедает почти половину панели, а
 * человеку нужна лента: он пришёл разговаривать, а не смотреть.
 *
 * Класс, а не медиазапрос по высоте, потому что признак не в размере окна:
 * клавиатура окно НЕ уменьшает (см. выше), и CSS о ней не знает вовсе.
 */
.lorabot__panel--compact .lorabot__avatar {
  position: absolute;
  top: calc(var(--space-3) + 2px);
  left: var(--space-4);
  z-index: 2;
  width: 40px; height: 40px;
  border-radius: 50%;
  border-bottom: 0;
  box-shadow: 0 0 0 1px var(--border);
}
/* Заголовок отступает ровно на кружок: 40 px картинки плюс зазор. */
.lorabot__panel--compact .lorabot__head {
  padding-left: calc(var(--space-4) + 40px + var(--space-3));
}
/* Кнопка остановки движения в кружок не поместилась бы. Она и не нужна: в
 * компактном виде JS останавливает само видео, и останавливать больше нечего
 * (2.2.2 требует орган только для того, что движется). */
.lorabot__panel--compact .lorabot__motion { display: none; }
.lorabot__panel--compact .lorabot__video { object-position: 50% 22%; }
/* Подпись под именем — контекст, который человек уже знает: он в разговоре. */
.lorabot__panel--compact .lorabot__sub { display: none; }
/* Зазор до края видимой области ужимается с 48 до 16 px: когда над клавиатурой
 * остаётся 300 px, сорок восемь из них — это ещё одна видимая реплика. */
.lorabot__panel--compact {
  height: min(600px, calc(var(--lorabot-vvh, 100vh) - var(--space-4)));
}
/* Кнопки-подсказки уходят, обязательные остаются. Замер 06.09: три подсказки в
 * два ряда при клавиатуре выдавливали поле ввода за нижний край панели — то
 * есть печатать было некуда, ради кнопок «а что спросить».
 *
 * ПРЯЧЕТ ИХ ТЕСНОТА, А НЕ КОМПАКТНЫЙ ВИД. Сначала правило висело на
 * `--compact`, и подсказки исчезали всюду, где экран ниже 520 px, — даже когда
 * места хватало с запасом. Класс `--tight` ставит JS, померив настоящее
 * переполнение панели: пропадают они теперь ровно тогда, когда иначе пропало бы
 * поле ввода. */
.lorabot__panel--tight .lorabot__acts:not(.lorabot__acts--keep) { display: none; }
/* И ТО ЖЕ САМОЕ ПРИ ПОДНЯТОЙ КЛАВИАТУРЕ (07.09.2026, живая проверка с
 * телефона). Переполнения в этот момент может не быть вовсе — лента сжимается,
 * поле ввода на месте, — а на разговор остаются две строки: половину экрана
 * забрала клавиатура, часть остатка ряд кнопок. Замер «влезло ли» этого не
 * видит, поэтому признак второй и независимый: класс `--kb` ставит JS по
 * `visualViewport`. Пока человек печатает, подсказка «что спросить» ему уже не
 * нужна. */
.lorabot__panel--kb .lorabot__acts:not(.lorabot__acts--keep) { display: none; }
/* Обязательную полосу спрятать нельзя — ею отвечают на заданный вопрос. Но и
 * поле ввода она вытеснять не должна (на 844x390 с клавиатурой от него не
 * оставалось ничего), поэтому в тесноте она прокручивается внутри себя. */
.lorabot__panel--tight .lorabot__acts--keep { max-height: 92px; overflow-y: auto; }

/* В тесноте отступы ужимаются. Это не украшательство: телефон в ландшафте с
 * поднятой клавиатурой оставляет 170 px видимой высоты, а собственный минимум
 * панели был 168 px — шапка 68, форма 68 и лента, которая не сжимается ниже
 * своих 32 px вертикальных отступов. Двух пикселей запаса не хватило, и поле
 * ввода снова уходило за край (замер: 0 из 44 px). Теперь минимум около 128 px. */
/* `--kb` перечислен рядом с `--tight` не для симметрии, а по замеру (07.09).
 * Новый признак срабатывает РАНЬШЕ переполнения, поэтому после его появления
 * `--tight` на узких телефонах перестал включаться вовсе — и вместе с ним ушло
 * это ужатие: на 320x568 и 360x640 с клавиатурой ленты стало на 16 px МЕНЬШЕ,
 * чем было до правки. Признак, который снимает условие другого признака, обязан
 * унаследовать его последствия. */
.lorabot__panel--tight .lorabot__feed,
.lorabot__panel--kb .lorabot__feed { padding-top: var(--space-2); padding-bottom: var(--space-2); }
.lorabot__panel--tight .lorabot__head,
.lorabot__panel--kb .lorabot__head { padding-top: var(--space-2); padding-bottom: var(--space-2); }
.lorabot__panel--tight .lorabot__form,
.lorabot__panel--kb .lorabot__form { padding: var(--space-2); }

/* Сжимается ТОЛЬКО лента. Без этого при нехватке места браузер волен ужать что
 * угодно, включая поле ввода, — а оно и есть смысл панели. */
.lorabot__head, .lorabot__form, .lorabot__acts,
.lorabot__skip, .lorabot__brief { flex: none; }

/* ── Уменьшенное движение ──────────────────────────────────────────────────
 * Петля аватара и мигание точек — движение без смысла для того, кто его
 * попросил убрать. Печать текста гасится в JS (там же, где она и живёт).
 */
@media (prefers-reduced-motion: reduce) {
  .lorabot__launcher { transition: none; }
  .lorabot__launcher:hover { transform: none; }
  .lorabot__dot { animation: none; opacity: .6; }
}
