/* ============================================================
   transitions.css — переходы между страницами

   Подключается на ВСЕХ страницах сайта. Механизм требует, чтобы обе
   стороны перехода были на него подписаны: если правило не попадёт на
   какую-то страницу, она будет открываться скачком.

   <link rel="stylesheet" href="./assets/css/transitions.css">
   (в work/ — "../assets/css/transitions.css")

   Пара к файлу: assets/js/transitions.js — там только направление
   перехода, которое стилями не выразить.

   Прогрессивное улучшение: где View Transitions не поддержаны,
   страницы открываются как раньше, ничего не ломается.
   ============================================================ */

/* ---------- 1. Включение механизма ----------

   Правила @view-transition здесь НЕТ намеренно, оно лежит инлайновым
   <style> в <head> каждой страницы.

   Из внешнего файла правило применяется не всегда до первого кадра
   приходящей страницы. Тогда браузер решает, что переход не заказан, и
   молча отменяет его — без ошибки в консоли. На замерах пропадал
   примерно каждый третий переход, и выглядело это как случайное
   мигание. Инлайновое правило разбирается вместе с разметкой и
   успевает всегда.

   Файл всё равно обязан подключаться на ОБЕИХ страницах перехода:
   без него не будет ни неподвижной оболочки, ни направления.          */

/* ---------- 2. Характер перехода ----------

   Всё, чем настраивается ощущение от перехода, собрано здесь: правила
   ниже только ссылаются на переменные.

   Асимметрия намеренная: уход быстрее прихода примерно в 1.75 раза.
   Симметричные тайминги ощущаются вялыми — это самая частая причина,
   по которой технически верный переход выглядит посредственно.        */

:root {
  --vt-out: 160ms;  /* уход: резкий старт */
  --vt-in: 280ms;   /* приход: мягкая остановка */

  --vt-ease-out: cubic-bezier(0.32, 0, 0.67, 0); /* для уходящего */
  --vt-ease-in: cubic-bezier(0.16, 1, 0.3, 1);   /* для приходящего */

  /* Сдвиг контента. Ровно столько, чтобы мозг прочитал направление.
     Больше 20px — уже читается как карусель. */
  --vt-shift: 14px;

  /* Морфинг обложки. Длиннее смены контента: обложка проезжает заметное
     расстояние и меняет размер, на 280мс это выглядит как рывок. */
  --vt-morph: 420ms;
  --vt-ease-morph: cubic-bezier(0.2, 0, 0, 1);
}

/* ---------- 3. Неподвижная оболочка ----------

   Главный приём, он даёт большую часть эффекта. Именованные элементы
   вынимаются из общего снимка и анимируются своей группой. Шапка,
   подвал и панель разделов на обеих страницах одинаковы, поэтому их
   группы просто стоят на месте, пока контент между ними сменяется.

   Возникает ощущение, что человек перелистнул страницу ВНУТРИ сайта,
   а не перезагрузил сайт целиком.                                    */

.site-header {
  view-transition-name: site-header;
}

.site-footer {
  view-transition-name: site-footer;
}

/* Липкая панель разделов услуг: без своего имени она попадёт в общий
   снимок и будет двоиться при переходе. */
.svc-nav {
  view-transition-name: section-nav;
}

/* Группе по умолчанию положена анимация положения и размера. Шапке она
   не нужна (шапка липкая, координаты те же), а подвалу вредна: на
   страницах разной длины он стоит в разных местах экрана, и группа
   поехала бы через весь экран. Снимаем — оболочка обязана стоять. */
::view-transition-group(site-header),
::view-transition-group(site-footer),
::view-transition-group(section-nav) {
  animation: none;
}

/* Панель разделов есть только на услугах: уходя с них, она осталась бы
   без пары и мигнула. Гасим её тем же фейдом, что и контент. */
::view-transition-old(section-nav) {
  animation: vt-fade-out var(--vt-out) var(--vt-ease-out) both;
}

::view-transition-new(section-nav) {
  animation: vt-fade-in var(--vt-in) var(--vt-ease-in) both;
}

/* ---------- 4. Переход вперёд ----------

   root после изъятия шапки, подвала и панели — это только контент.
   Уходящий сдвигается вверх, приходящий приезжает снизу.              */

::view-transition-old(root) {
  animation: vt-leave-up var(--vt-out) var(--vt-ease-out) both;
}

::view-transition-new(root) {
  animation: vt-enter-up var(--vt-in) var(--vt-ease-in) both;
}

@keyframes vt-leave-up {
  to {
    opacity: 0;
    transform: translateY(calc(var(--vt-shift) * -0.5));
  }
}

@keyframes vt-enter-up {
  from {
    opacity: 0;
    transform: translateY(var(--vt-shift));
  }
}

/* ---------- 5. Переход назад ----------

   Зеркалим направление. Без этого любой шаг ощущается как «дальше
   вглубь», и через несколько кликов человек теряет ориентацию.

   Класс vt-back ставит transitions.js на ВХОДЯЩУЮ страницу: правила
   ::view-transition берутся из нового документа, старый к этому
   моменту уже снят.                                                  */

html.vt-back::view-transition-old(root) {
  animation: vt-leave-down var(--vt-out) var(--vt-ease-out) both;
}

html.vt-back::view-transition-new(root) {
  animation: vt-enter-down var(--vt-in) var(--vt-ease-in) both;
}

@keyframes vt-leave-down {
  to {
    opacity: 0;
    transform: translateY(calc(var(--vt-shift) * 0.5));
  }
}

@keyframes vt-enter-down {
  from {
    opacity: 0;
    transform: translateY(calc(var(--vt-shift) * -1));
  }
}

/* ---------- 6. Морфинг обложки кейса ----------

   Обложка карточки и обложка кейса — одна картинка, которая переезжает
   и меняет размер. Имя ей выдаёт assets/js/transitions.js на лету и
   снимает сразу после перехода: имя обязано быть уникальным в
   документе, а карточек в списке пять. Прописать имя всем карточкам
   здесь — самый естественный ход и самый разрушительный: морфинг
   сломается молча, без ошибки в консоли.

   Поэтому в этом файле только характер движения, но не выдача имени.  */

::view-transition-group(case-cover) {
  animation-duration: var(--vt-morph);
  animation-timing-function: var(--vt-ease-morph);
}

/* Снимки обложки — заменяемые элементы, и по умолчанию они растягиваются
   (object-fit: fill). У карточки и у кейса разные пропорции — например,
   у STAYLY карточка горизонтальная, а обложка кейса вертикальная, — и
   картинка сплющивалась бы на старте. Кадрируем вместо растяжения. */
::view-transition-old(case-cover),
::view-transition-new(case-cover) {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Приходящий снимок выводим на полную непрозрачность заметно раньше, чем
   группа доедет. Если растянуть проявление на весь переход, обе картинки
   середину пути проводят полупрозрачными, и там, где кадры не совпадают
   (карточка и кейс кадрированы по-разному), видно двоение.

   Уходящий гасим на всю длину: он лежит снизу, и его остаток не мешает —
   зато у разных картинок, как у STAYLY, стык остаётся мягким. */
::view-transition-old(case-cover) {
  animation-name: vt-fade-out;
  animation-duration: var(--vt-morph);
}

::view-transition-new(case-cover) {
  animation-name: vt-fade-in;
  animation-duration: calc(var(--vt-morph) * 0.35);
}

/* ---------- 7. Служебные фейды ---------- */

@keyframes vt-fade-out {
  to { opacity: 0; }
}

@keyframes vt-fade-in {
  from { opacity: 0; }
}

/* ---------- 8. Приземление на якорь ----------

   scroll-behavior здесь намеренно НЕ задаётся — ни глобально, ни через
   класс. Плавность делает transitions.js своей анимацией: только так
   соблюдается потолок длительности, и только так браузер не начинает
   анимировать восстановление позиции при возврате из кейса.            */

:root {
  /* Отступ сверху для целей якорей. Разделы услуг и карточки аккордеона
     считают свой, с высотой липкой панели (pages.css, --svc-nav-h), и
     он точнее — поэтому правило ниже нулевой специфичности. */
  --vt-anchor-offset: calc(var(--header-h) + 1.6875rem);
}

/* Только блочные цели якорей. Всем подряд [id] отступ давать нельзя:
   под правило попадут поля форм калькулятора, а к ним прокрутка
   приезжает своим кодом и со своей логикой.

   [id] обязан лежать ВНУТРИ :where(). Снаружи он даёт специфичность
   селектора атрибута — ровно такую же, как у класса, — и правило
   перебивает точные отступы из pages.css просто потому, что этот файл
   подключается позже. Карточка услуги тогда приземляется под панелью. */
:where(main[id], section[id], article[id], aside[id], details[id],
       figure[id], h1[id], h2[id], h3[id]) {
  scroll-margin-top: var(--vt-anchor-offset);
}

/* Раздел получает фокус после прокрутки. Рамка по умолчанию здесь
   визуальный мусор — убираем её, но :focus-visible оставляем: с
   клавиатуры человек обязан видеть, куда он попал. */
[tabindex="-1"]:focus {
  outline: none;
}

[tabindex="-1"]:focus-visible {
  outline: 2px solid var(--accent, currentColor);
  outline-offset: 4px;
}

/* ---------- 9. Уважение к настройкам движения ----------

   Не косметика: движущиеся переходы вызывают головокружение и тошноту
   у людей с вестибулярными нарушениями. Снимаем анимации со всех групп
   принудительно — страницы просто сменяются мгновенно.               */

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