/* Савелий: движение персонажа.
 *
 * Разделение обязанностей:
 *   savely.css — размеры кота и «дыхание» всего силуэта (catBreathe);
 *   motion.css — движение интерфейса и четыре реакции на весь SVG;
 *   этот файл  — движение ЧАСТЕЙ кота и то, где кот появляется.
 *
 * Правила, из которых всё следует:
 *   — только transform и opacity. Сайт открывают на школьных телефонах
 *     за 8 тысяч, и любая анимация ширины там видна пальцем;
 *   — фоновое движение медленное и мелкое: кот живой, когда на него
 *     не смотрят, а не «посмотрите на меня»;
 *   — реакция короче секунды, а на обычный верный ответ — 650 мс:
 *     иначе на двадцатом повторе она бесит;
 *   — prefers-reduced-motion выключает движение ПОЛНОСТЬЮ (в самом низу).
 *
 * Подключать ПОСЛЕ savely.css и a11y.css: часть правил их переопределяет.
 */

/* ==========================================================================
   1. ОСНОВАНИЕ: относительно чего вращаются части
   --------------------------------------------------------------------------
   transform-box: fill-box означает «считай проценты от собственной рамки
   фигуры, а не от всего холста». Без него transform-origin у группы внутри
   SVG отсчитывается от viewBox, и ухо при повороте улетает за экран.
   Поэтому вся часть с движением частей завёрнута в @supports: там, где
   fill-box нет (Android WebView до 64), кот просто не дёргает ухом.
   ========================================================================== */
.cat-svg { overflow: visible; }
.cat-svg * { pointer-events: none; }

/* Кот за краем экрана замирает целиком, включая «дыхание» из savely.css.
   Это не отмена чужого решения, а стоп-кадр на то время, пока кота
   всё равно никто не видит: вернулся в кадр — задышал снова. */
.cat-still .cat-svg { animation-play-state: paused; }

@supports (transform-box: fill-box) {

  .cat-head, .cat-ear, .cat-tail, .cat-eye, .cat-gaze, .cat-pop, .cat-paw, .cat-zzz {
    transform-box: fill-box;
  }
  /* Голова поворачивается вокруг подбородка — так делает живая голова.
     Вокруг центра получается вращение колеса. */
  .cat-head { transform-origin: 50% 100%; }
  .cat-ear-l { transform-origin: 86% 94%; }   /* основание уха */
  .cat-ear-r { transform-origin: 14% 94%; }
  .cat-eye   { transform-origin: 50% 48%; }
  .cat-pop   { transform-origin: 50% 50%; }
  .cat-paw   { transform-origin: 78% 96%; }   /* локоть, а не центр лапы */
  .cat-tail  { transform-origin: 9% 71%; }    /* корень хвоста, под подбородком */

/* ==========================================================================
   2. ФОНОВАЯ ЖИЗНЬ
   --------------------------------------------------------------------------
   Два бесконечных движения, оба почти незаметные и оба взаимно простые:
   период 13 и 6.5 секунды не совпадают, поэтому кот не «тикает».
   ========================================================================== */
  @keyframes cat-sway {
    0%, 100% { transform: rotate(0deg) }
    26%      { transform: rotate(1.5deg) }
    64%      { transform: rotate(-1.3deg) }
  }
  @keyframes cat-tail-sway {
    0%, 100% { transform: rotate(-5deg) }
    50%      { transform: rotate(6deg) }
  }
  .cat-head { animation: cat-sway 13s var(--ease) infinite; }
  .cat-tail { animation: cat-tail-sway 6.5s ease-in-out infinite; }

  /* Класс ставит js/cat.js по IntersectionObserver: кот за краем экрана
     перестаёт качаться. Поворот группы внутри SVG рисуется процессором,
     а не видеокартой, — и это единственное движение на сайте, которое
     иначе крутилось бы вечно и вхолостую. */
  .cat-still .cat-head,
  .cat-still .cat-tail,
  .cat-still .cat-zzz path { animation-play-state: paused; }

/* ==========================================================================
   3. МОРГАНИЕ, УХО, ВЗГЛЯД
   --------------------------------------------------------------------------
   Классы ставит js/cat.js со случайным интервалом — в CSS случайности нет,
   а ровное моргание раз в N секунд читается как индикатор загрузки.
   Глаз не «закрывается веком», а сплющивается по вертикали: в плоском
   рисунке это и есть закрытый глаз, и это чистый transform.
   ========================================================================== */
  @keyframes cat-blink {
    0%, 100% { transform: scaleY(1) }
    42%, 58% { transform: scaleY(.06) }
  }
  @keyframes cat-ear-twitch {
    0%, 100% { transform: rotate(0deg) }
    22%      { transform: rotate(-8deg) }
    52%      { transform: rotate(3deg) }
  }
  .is-blink  .cat-eye { animation: cat-blink .24s ease-in-out; }
  .is-blink2 .cat-eye { animation: cat-blink .26s ease-in-out 2; }
  .is-ear-l .cat-ear-l,
  .is-ear-r .cat-ear-r { animation: cat-ear-twitch .55s var(--ease); }

  /* Взгляд в сторону: зрачки уезжают на 0.8 единицы из 36 — меньше
     процента ширины, но глаз это ловит мгновенно. */
  .cat-gaze { transition: transform .45s var(--ease); }
  .is-look-l .cat-gaze { transform: translate(-.8px, .15px); }
  .is-look-r .cat-gaze { transform: translate(.8px, .15px); }

/* ==========================================================================
   4. ЧТО МЕНЯЕТ ПОЗА
   ========================================================================== */
  /* Огорчился — уши падают. Это половина всей грусти: рот один читается
     хуже, чем рот вместе с ушами. */
  .pose-sad .cat-ear-l { transform: rotate(24deg) }
  .pose-sad .cat-ear-r { transform: rotate(-24deg) }
  .pose-sad .cat-head  { animation: none; transform: rotate(-1deg) }
  .pose-sad .cat-tail  { animation: none; transform: rotate(-14deg) }

  /* Гордится — подбородок вверх и уши торчком. */
  .pose-proud .cat-head  { animation: none; transform: rotate(-2.5deg) }
  .pose-proud .cat-ear-l { transform: rotate(-5deg) }
  .pose-proud .cat-ear-r { transform: rotate(5deg) }

  /* Думает — голова набок и замерла: думающий не качается. */
  .pose-think .cat-head { animation: none; transform: rotate(4deg) }
  .pose-think .cat-tail { animation-duration: 3.4s }

  /* Спит — уши расслаблены, хвост еле шевелится. */
  .pose-sleep .cat-head  { animation-duration: 4.6s }   /* дыхание, а не оглядка */
  .pose-sleep .cat-ear-l { transform: rotate(11deg) }
  .pose-sleep .cat-ear-r { transform: rotate(-11deg) }
  .pose-sleep .cat-tail  { animation-duration: 9s }

  /* Машет лапой. Задержка 60 мс — лапа трогается чуть позже головы,
     из-за этого движение читается как одно, а не как два. */
  @keyframes cat-wave {
    0%, 100% { transform: rotate(0deg) }
    20%      { transform: rotate(-22deg) }
    45%      { transform: rotate(14deg) }
    70%      { transform: rotate(-16deg) }
  }
  .pose-wave .cat-paw { animation: cat-wave 1.6s var(--ease) .06s 2; }

  /* «Z-z-z»: три буквы всплывают по очереди и растворяются. */
  @keyframes cat-z {
    0%   { opacity: 0; transform: translate(0, 2px) scale(.8) }
    30%  { opacity: 1 }
    70%  { opacity: .8 }
    100% { opacity: 0; transform: translate(2px, -4px) scale(1.05) }
  }
  .pose-sleep .cat-z1,
  .pose-sleep .cat-z2,
  .pose-sleep .cat-z3 { transform-box: fill-box; transform-origin: 50% 100%;
                        animation: cat-z 3.6s ease-in-out infinite; }
  .pose-sleep .cat-z2 { animation-delay: 1.2s }
  .pose-sleep .cat-z3 { animation-delay: 2.4s }

/* ==========================================================================
   5. РЕАКЦИИ
   --------------------------------------------------------------------------
   Классы ставит js/motion.js. Сам подскок кота живёт в motion.css (он на
   всём SVG), здесь — то, что делают части: уши, хвост и зелёная вспышка.
   ========================================================================== */
  /* Кольцо стартует ЗА головой (scale .8 — радиус меньше силуэта) и
     выходит из-за неё наружу. Так вспышка выглядит как волна от кота,
     а не как обруч, который просто появился. */
  @keyframes cat-pop {
    0%   { opacity: 0;   transform: scale(.8) }
    28%  { opacity: .5 }
    100% { opacity: 0;   transform: scale(1.25) }
  }
  @keyframes cat-ears-up {
    0%, 100% { transform: rotate(0deg) }
    35%      { transform: rotate(-9deg) }
  }
  @keyframes cat-ears-up-r {
    0%, 100% { transform: rotate(0deg) }
    35%      { transform: rotate(9deg) }
  }
  @keyframes cat-tail-flick {
    0%, 100% { transform: rotate(0deg) }
    30%      { transform: rotate(-16deg) }
    65%      { transform: rotate(12deg) }
  }
  @keyframes cat-ear-droop {
    0%       { transform: rotate(0deg) }
    100%     { transform: rotate(20deg) }
  }

  .m-happy .cat-ear-l, .m-cheer .cat-ear-l, .m-proud .cat-ear-l {
    animation: cat-ears-up .5s var(--ease);
  }
  .m-happy .cat-ear-r, .m-cheer .cat-ear-r, .m-proud .cat-ear-r {
    animation: cat-ears-up-r .5s var(--ease);
  }
  .m-happy .cat-tail, .m-cheer .cat-tail, .m-proud .cat-tail {
    animation: cat-tail-flick .55s var(--ease);
  }
  /* Вспышка — только у крупных событий: серия ответов и награда.
     На каждом верном ответе она превратилась бы в мигалку. */
  .m-cheer .cat-pop, .m-proud .cat-pop { animation: cat-pop .62s var(--ease); }

  .m-oops .cat-ear-l { animation: cat-ear-droop .3s var(--ease) both; }
  .m-oops .cat-ear-r { animation: cat-ear-droop .3s var(--ease) both;
                       animation-direction: reverse; transform: rotate(-20deg) }
  .m-oops .cat-tail  { animation: cat-tail-flick .4s var(--ease); }

}   /* конец @supports */

/* Румянец ярче там, где кот доволен. Цвет тот же (--cat-tongue),
   меняется только прозрачность — новых красок в гамме не появляется. */
.cat-blush { transition: opacity var(--dur) var(--ease); }
.pose-cheer .cat-blush, .pose-love .cat-blush,
.pose-proud .cat-blush, .pose-happy .cat-blush { opacity: .44 }
.pose-sleep .cat-blush { opacity: .16 }

.cat-paw { transition: opacity var(--dur) var(--ease); }
.pose-wave .cat-paw { opacity: 1 }
.pose-sleep .cat-zzz { opacity: 1 }

/* ==========================================================================
   6. КОТ-СОСЕД В УПРАЖНЕНИИ
   --------------------------------------------------------------------------
   Главная претензия к персонажу была не «мало анимации», а «его нет там,
   где ученик проводит всё время». Здесь он и появляется: сидит в углу,
   пока идёт упражнение или карточки, и отзывается на ответы.
   Вставляет его js/motion.js, разметку страниц он не трогает.

   position: fixed выбран сознательно: шапка упражнения на 375px уже
   вымерена по пикселям в a11y.css, и вставать в этот ряд четвёртым
   элементом — значит ломать чужую работу.
   ========================================================================== */
.savely-coach {
  position: fixed;
  left: var(--space-16, 16px);
  bottom: var(--space-16, 16px);
  width: 78px;
  z-index: 30;                /* ниже нижнего меню (40) и плашки награды (200) */
  pointer-events: none;       /* никогда не перехватывает нажатие */
  opacity: 0;
  transform: translateY(14px) scale(.9);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.savely-coach.on { opacity: 1; transform: none; }
.savely-coach .cat-avatar { width: 100%; display: block; }

/* Реплика кота. Появляется на 2.6 секунды и только по делу: ошибка,
   серия, награда. Молчит — значит всё идёт нормально. */
.savely-say {
  position: absolute;
  left: 66%;
  bottom: 66%;
  /* Кот нарисован ПОСЛЕ пузыря и перекрывал его: у svg своя анимация
     transform, а трансформированный элемент рисуется как позиционированный
     и по порядку в разметке оказывается сверху. */
  z-index: 2;
  width: max-content;
  max-width: 44vw;
  padding: 7px 12px;
  border-radius: var(--radius-card, 18px);
  /* Мягкая акцентная подложка, а не общая поверхность: на бумаге
     --surface от --paper отличается на полтона, и пузырь читался как
     случайное светлое пятно. Обе краски уже есть в теме. */
  border: var(--border-w, 1.5px) solid var(--accent-line);
  background: var(--accent-soft);
  color: var(--ink);
  font-size: var(--text-caption, 14px);
  font-weight: 600;
  line-height: 1.25;
  box-shadow: var(--shadow-soft);
  opacity: 0;
  transform: translateY(6px) scale(.94);
  transform-origin: 0 100%;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.savely-say.on { opacity: 1; transform: none; }

@media (max-width: 820px) {
  /* Над нижним меню, а не поверх него — как это уже сделано у плашки награды */
  .savely-coach {
    width: 54px;
    left: var(--space-8, 8px);
    bottom: calc(var(--tabbar-h, 64px) + var(--space-8, 8px) + env(safe-area-inset-bottom));
  }
  .savely-say { font-size: 13px; padding: 6px 10px; max-width: 58vw; }
}

/* ==========================================================================
   6Б. КОТ РЯДОМ С РАЗБОРОМ ОТВЕТА
   --------------------------------------------------------------------------
   Вставляет js/exercises.js (catAnswerNote) из общих движков: кот и
   короткая реплика стоят сразу после фидбека — между вариантами и
   разбором. Маленький, не модалка, нажатий не перехватывает (в нём и
   нечего нажимать). Появление — одно короткое движение при вставке;
   в «Парах» элемент переиспользуется, и текст просто меняется на месте.
   Краски те же, что у облачка кота-соседа выше: --accent-soft по
   --accent-line, обе уже есть в теме, дневной и ночной.
   ========================================================================== */
@keyframes cat-answer-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.cat-answer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  animation: cat-answer-in var(--dur) var(--ease);
}
.cat-answer .cat-avatar { width: 36px; height: 36px; flex: none; margin: 0; }
.cat-answer-say {
  font-size: var(--text-caption);
  font-weight: 600;
  line-height: 1.25;
  padding: 6px 12px;
  border: var(--border-w) solid var(--accent-line);
  border-radius: var(--radius-card);
  background: var(--accent-soft);
  color: var(--ink);
  max-width: 100%;
}
@media (max-width: 480px) {
  .cat-answer { gap: 8px; margin-top: 8px; }
  .cat-answer .cat-avatar { width: 30px; height: 30px; }
  .cat-answer-say { font-size: 14px; padding: 5px 10px; }
}

/* ==========================================================================
   7. КОТ В НАГРАДЕ
   --------------------------------------------------------------------------
   Плашку рисует js/achievements.js, кота в неё вставляет js/motion.js.
   ========================================================================== */
.ach-toast .ach-cat { width: 40px; flex: none; margin-right: -2px; }
@media (max-width: 480px) { .ach-toast .ach-cat { width: 32px } }

/* ==========================================================================
   8. КОТ НА МОБИЛЬНОЙ ГЛАВНОЙ
   --------------------------------------------------------------------------
   На телефоне кота на главной не было: a11y.css прячет его в приветствии,
   savely.css — в блоке «прямо сейчас». Каждое правило по отдельности
   верное («второй кот подряд лишний»), но вместе они убрали обоих, и на
   самом важном экране героя продукта не осталось.

   Возвращаем ровно одного — В ПРИВЕТСТВИЕ (dash-head), а не в карточку
   действия: здоровается с учеником персонаж, а не кнопка. dash-head —
   flex-строка, поэтому кот встаёт рядом с текстом, а не над ним; 44px
   высоты строке не добавляют — две строки приветствия и так выше.
   Правило перекрывает a11y.css просто порядком подключения: cat.css
   идёт позже. А кот в карточке «прямо сейчас» остаётся спрятанным —
   два кота подряд читаются как опечатка (тот же аргумент, что в a11y.css).
   ========================================================================== */
@media (max-width: 720px) {
  .dash-head .cat-avatar { display: inline-flex; width: 44px; height: 44px;
                           flex: none; margin: 0; }
  .today .cat-avatar { display: none; }
}

/* ==========================================================================
   9. МЕНЬШЕ ДВИЖЕНИЯ — ЗНАЧИТ БЕЗ ДВИЖЕНИЯ
   --------------------------------------------------------------------------
   Не ослабляем, а выключаем: в tokens.css длительности сводятся к 0.01ms,
   здесь снимаем сами анимации и переходы. Позы (уши домиком у грустного,
   поднятый подбородок у гордого) остаются — это рисунок, а не движение.
   Планировщик морганий в js/cat.js в этом режиме вообще не запускается.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .cat-svg, .cat-svg *,
  .savely-coach, .savely-say, .ach-toast .ach-cat,
  .cat-answer {
    animation: none !important;
    transition: none !important;
  }
  .savely-coach { transform: none !important; }
  .savely-say   { transform: none !important; }
}

/* ==========================================================================
   МЕСТО ПОД КОТА В КОНЦЕ СТРАНИЦЫ
   --------------------------------------------------------------------------
   Кот сидит поверх контента в левом нижнем углу: position: fixed, 54px,
   над нижним меню. Нажатия он не перехватывает (pointer-events: none),
   но ЧИТАТЬ под ним нельзя — а нижний отступ у main рассчитан только
   на высоту меню.

   Поймано на кроссворде: последние две подсказки («3→ повторять»,
   «4→ деревня») уходили ему под живот, и без них слово не отгадать.
   То же самое было в конце любого длинного списка.

   Добавляем ровно рост кота сверх прежнего отступа. Не трогаем сам
   --tabbar-h: он вымерен под меню, и менять его значение ради кота
   значило бы чинить одно место через другое.
   ========================================================================== */
@media (max-width: 820px) {
  main { padding-bottom: calc(var(--tabbar-h, 64px) + 54px + var(--space-16, 16px)
                              + env(safe-area-inset-bottom)); }
}
