/* ============================================================
   animations — ключевые кадры и утилиты появления
   ============================================================ */

@keyframes spin { to { transform: rotate(360deg); } }

@keyframes sheen {
  0%   { transform: translateX(-120%); }
  100% { transform: translateX(120%); }
}

@keyframes shimmer-text {
  0%   { background-position: 120% 0; }
  100% { background-position: -120% 0; }
}

@keyframes caret {
  0%, 55%  { opacity: 1; }
  56%,100% { opacity: .15; }
}

/* Только сдвиг, без scale: масштабирование слоя с градиентом
   вынуждает композитор растрировать его заново каждый кадр, а
   перемещение готового слоя — операция почти бесплатная. */
@keyframes drift-a {
  0%,100% { transform: translate3d(0, 0, 0); }
  33%     { transform: translate3d(7%, 5%, 0); }
  66%     { transform: translate3d(-5%, 8%, 0); }
}
@keyframes drift-b {
  0%,100% { transform: translate3d(0, 0, 0); }
  40%     { transform: translate3d(-9%, 7%, 0); }
  70%     { transform: translate3d(4%, -6%, 0); }
}
@keyframes drift-c {
  0%,100% { transform: translate3d(0, 0, 0); }
  45%     { transform: translate3d(9%, -7%, 0); }
  75%     { transform: translate3d(-6%, -3%, 0); }
}

@keyframes breathe {
  0%,100% { transform: scale(1); opacity: .55; }
  50%     { transform: scale(1.09); opacity: .95; }
}

@keyframes pulse-dot {
  0%,100% { opacity: 1; transform: scale(1); }
  50%     { opacity: .35; transform: scale(.82); }
}
.pulse { animation: pulse-dot 1.7s var(--e-in-out) infinite; }

@keyframes skeleton {
  0%   { background-position: 140% 0; }
  100% { background-position: -40% 0; }
}
.skeleton {
  border-radius: var(--r-sm);
  background: linear-gradient(90deg,
      var(--fill-1) 0%, var(--fill-3) 50%, var(--fill-1) 100%);
  background-size: 220% 100%;
  animation: skeleton 1.5s linear infinite;
  color: transparent !important;
}

/* Стартовое состояние для reveal(): элементы «поднимаются
   из размытия», как в системных анимациях Apple. В экономном
   профиле размытие не используется — иначе элемент, к которому
   reveal() почему-то не вызвался, остался бы размытым навсегда. */
.will-reveal {
  opacity: 0;
  transform: translate3d(0, 16px, 0);
  filter: blur(9px);
}
:root[data-perf="lite"] .will-reveal { filter: none; }

/* Появление слова. Размытие на каждом слове — дорогое
   удовольствие: filter создаёт слой и требует перерисовки, а слов
   в минуту приходит полторы сотни. В lite остаются только
   прозрачность и сдвиг — их считает композитор. */
@keyframes word-in {
  from { opacity: 0; transform: translate3d(0, 7px, 0); filter: blur(5px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}
@keyframes word-in-lite {
  from { opacity: 0; transform: translate3d(0, 5px, 0); }
  to   { opacity: 1; transform: none; }
}
.transcript .w.is-new { animation: word-in 460ms var(--e-out) both; }
:root[data-perf="lite"] .transcript .w.is-new {
  animation: word-in-lite 260ms var(--e-out) both;
}

@keyframes ripple {
  from { transform: scale(.5); opacity: .5; }
  to   { transform: scale(2.4); opacity: 0; }
}
.ripple {
  position: absolute;
  width: 44px; height: 44px;
  margin: -22px 0 0 -22px;
  border-radius: 50%;
  background: rgba(255,255,255,.24);
  pointer-events: none;
  animation: ripple 700ms var(--e-out) forwards;
}

/* Пульсация вокруг кнопки записи.
   Раньше анимировался box-shadow — а это перерисовка на каждом
   кадре, причём по площади заметно больше самой кнопки. Теперь
   расходится отдельный слой с постоянным фоном: масштаб и
   прозрачность считает композитор, перерисовки нет вовсе. */
@keyframes rec-pulse {
  0%   { transform: scale(1);    opacity: .5; }
  70%  { transform: scale(1.55); opacity: 0; }
  100% { transform: scale(1.55); opacity: 0; }
}
.rec.is-live .rec__pulse { animation: rec-pulse 2.4s var(--e-out) infinite; }

/* Мягкое мерцание точки записи */
@keyframes live-dot {
  0%,100% { opacity: 1; }
  50%     { opacity: .28; }
}
.livedot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--rose);
  box-shadow: 0 0 10px var(--rose);
  animation: live-dot 1.4s var(--e-in-out) infinite;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .will-reveal { opacity: 1; transform: none; filter: none; }
}

/* ============================================================
   Облегчённый профиль: снимаем всё, что требует перерисовки
   или лишних слоёв. Рисунок сохраняется, цена падает.
   ============================================================ */

:root[data-perf="lite"] .card__sheen,
:root[data-perf="lite"] .hero__grad { will-change: auto; }

:root[data-perf="lite"] .sheet-scrim {
  /* Размытие подложки во время перетаскивания шторки — это
     полноэкранный фильтр на каждом кадре. Затемнения достаточно. */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: rgba(0, 0, 0, .62);
}

:root[data-perf="lite"] .toast {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: rgba(32, 33, 38, .97);
}

:root[data-perf="lite"] .transcript__caret { animation-duration: 1.6s; }

:root[data-perf="lite"] .pill.is-busy .pill__sheen { animation-duration: 2.2s; }

/* Вращающееся кольцо — конический градиент с маской. Растрируется
   он один раз, но на слабых GPU маска пересчитывается; в lite
   заменяем его на спокойное свечение. */
:root[data-perf="lite"] .rec__ring { display: none; }

/* Приветствие на первом экране рисуется режимом наложения поверх
   градиента — то есть композитор читает уже отрисованное по всей
   площади текста. В экономном профиле берём обычный цвет: на
   тёмном фоне разница почти незаметна. */
:root[data-perf="lite"] .greet__text {
  mix-blend-mode: normal;
  color: rgba(255, 255, 255, .88);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .35);
}
