/* ==========================================================================
   VYSHKA Motion Layer — w1/motion-design (2026-09-09)
   Канон: ../../w1/motion-design/MOTION_GUIDE.md (social-week)
   --------------------------------------------------------------------------
   Правила слоя:
   1. Только transform / opacity (+ 2 документированных исключения:
      stroke-dashoffset у .vy-check и clip у ripple-круга — микроскопические).
   2. Постоянные анимации — только там, где есть живое состояние
      (пульс: запись/звонок). Никакого мерцания в покое.
   3. Полный kill-switch: prefers-reduced-motion + in-app
      «Уменьшить движение» (html[data-vy-motion="off"], ключ localStorage
      vyshka-msg-reduce-motion, тумблер в Настройки → Поведение).
   4. Частоты спокойные: пульс 1.8–2.4с, пузыри 12–22с, всё ≤ 320ms на
      микро-взаимодействия.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. ТОКЕНЫ ДВИЖЕНИЯ (единый источник --vy-motion-* / --vy-ease-*)
   -------------------------------------------------------------------------- */
:root {
  /* Длительности (микро → присутствие) */
  --vy-motion-fast: 120ms;      /* нажатия, ripple-старт, ховеры */
  --vy-motion-base: 200ms;      /* панельки, тумблеры, fade */
  --vy-motion-slow: 320ms;      /* поп-ин пузырей, шторы, навигация */

  /* Кривые: standard / emphasized / spring */
  --vy-ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  --vy-ease-emphasized: cubic-bezier(0.3, 0.7, 0.1, 1);
  --vy-ease-spring: cubic-bezier(0.34, 1.35, 0.5, 1);

  /* Характеристики компонентов (переопределяются на элементе при желании) */
  --vy-pulse-dur: 2.2s;         /* мягкая пульсация живого состояния */
  --vy-pulse-scale: 1.04;       /* амплитуда пульса — деликатная */
  --vy-shimmer-dur: 1.6s;       /* скелетон */
  --vy-bubble-o: 0.12;          /* макс. непрозрачность фоновых пузырей */
}

/* --------------------------------------------------------------------------
   2. KILL-SWITCH (путь A: системный prefers-reduced-motion, без JS)
   Дублируется путём B ниже (html[data-vy-motion="off"]). Оба пути дают
   ОДНО и то же: длительности → 0.01ms, все анимации/переходы гасятся,
   декор скрывается. Слой скрипта vy-motion.js держит оба в синхроне.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  :root {
    --vy-motion-fast: 0.01ms;
    --vy-motion-base: 0.01ms;
    --vy-motion-slow: 0.01ms;
    --vy-pulse-dur: 0.01ms;
    --vy-shimmer-dur: 0.01ms;
  }
  html,
  html *,
  html *::before,
  html *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .vy-motion-hide-reduce,
  .vy-bubbles {
    display: none !important;
  }
}

/* Путь B: внутриприложечный «Уменьшить движение» (классика на <html>) */
html[data-vy-motion="off"] {
  --vy-motion-fast: 0.01ms;
  --vy-motion-base: 0.01ms;
  --vy-motion-slow: 0.01ms;
  --vy-pulse-dur: 0.01ms;
  --vy-shimmer-dur: 0.01ms;
}
html[data-vy-motion="off"] *,
html[data-vy-motion="off"] *::before,
html[data-vy-motion="off"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}
html[data-vy-motion="off"] .vy-motion-hide-reduce,
html[data-vy-motion="off"] .vy-bubbles {
  display: none !important;
}

/* --------------------------------------------------------------------------
   3. УТИЛИТЫ
   .vy-motion-hide-reduce — скрыть чисто декоративный элемент при
   отключённом движении (использовать вместо «заморозки» декора).
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   4. МЯГКАЯ ПУЛЬСАЦИЯ — только живые состояния (запись / звонок)
   -------------------------------------------------------------------------- */
@keyframes vy-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(var(--vy-pulse-scale, 1.04)); }
}

/* Захват микрофона кнопкой (существующий класс чата, правок chats.js нет) */
.btn-mic.is-recording {
  animation: vy-pulse 1.8s var(--vy-ease) infinite;
  will-change: transform;
}

/* Живой звонок: деликатное «дыхание» строки статуса (только active) */
.call-overlay[data-state="active"] .call-overlay__status {
  animation: vy-pulse-soft var(--vy-pulse-dur) var(--vy-ease) infinite;
}
@keyframes vy-pulse-soft {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.68; }
}

/* Входящий/вызов: внимание к имени (мягко, без вспышек) */
.call-overlay[data-state="inbound"] .call-overlay__name,
.call-overlay[data-state="ringing"] .call-overlay__name {
  animation: vy-pulse-soft 1.9s var(--vy-ease) infinite;
}

/* Универсальный класс для живых точек/бейджей «в эфире» у соседних модулей */
.vy-pulse {
  animation: vy-pulse var(--vy-pulse-dur) var(--vy-ease) infinite;
  will-change: transform;
}

/* --------------------------------------------------------------------------
   5. ПОП-ИН СООБЩЕНИЙ (входящие/исходящие) — класс вешает vy-motion.js
   на хвостовые .msg через MutationObserver; чат-модуль не трогаем.
   -------------------------------------------------------------------------- */
@keyframes vy-bubble-pop {
  0%   { opacity: 0; transform: translateY(8px) scale(0.92); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
.msg.vy-pop-in .bubble {
  animation: vy-bubble-pop var(--vy-motion-slow) var(--vy-ease-spring) both;
  will-change: transform, opacity;
}
.msg--in.vy-pop-in .bubble  { transform-origin: 0% 100%; }  /* левый низ */
.msg--out.vy-pop-in .bubble { transform-origin: 100% 100%; } /* правый низ */

/* --------------------------------------------------------------------------
   6. RIPPLE — только главные действия (.btn-primary / .btn-send).
   Хост-класс и координаты ставит vy-motion.js (rAF-батчинг).
   -------------------------------------------------------------------------- */
.vy-ripple-host {
  position: relative;
  overflow: hidden;
}
@keyframes vy-ripple {
  from { transform: scale(0); opacity: 0.32; }
  to   { transform: scale(2.6); opacity: 0; }
}
.vy-ripple {
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  pointer-events: none;
  background: currentColor;
  color: #fff;
  transform: scale(0);
  animation: vy-ripple var(--vy-motion-slow) var(--vy-ease) forwards;
}

/* --------------------------------------------------------------------------
   7. ШТОРА/МОДАЛКА — slide+fade при открытии (.sheet уже центрирован).
   Класс .vy-sheet-in / .vy-fade-in ставит vy-motion.js при снятии .hidden.
   -------------------------------------------------------------------------- */
@keyframes vy-sheet-up {
  from { opacity: 0; transform: translateX(-50%) translateY(28px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.sheet.vy-sheet-in {
  animation: vy-sheet-up var(--vy-motion-slow) var(--vy-ease-emphasized) both;
}
@keyframes vy-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.sheet-scrim.vy-fade-in {
  animation: vy-fade var(--vy-motion-base) ease both;
}
/* Универсальные классы для модалок/штор соседних модулей (без привязки
   к .sheet): пара .vy-slide-up + .vy-fade-in на паре контент/скрим */
@keyframes vy-rise {
  from { opacity: 0; transform: translateY(28px); }
  to   { opacity: 1; transform: translateY(0); }
}
.vy-slide-up {
  animation: vy-rise var(--vy-motion-slow) var(--vy-ease-emphasized) both;
}
.vy-fade-in {
  animation: vy-fade var(--vy-motion-base) ease both;
}

/* --------------------------------------------------------------------------
   8. НАВИГАЦИЯ — микро-поп активной вкладки (класс .is-active уже есть)
   -------------------------------------------------------------------------- */
@keyframes vy-nav-pop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.14); }
  100% { transform: scale(1); }
}
.bottomnav__btn.is-active .bottomnav__icon,
.sidenav__btn.is-active .sidenav__icon {
  animation: vy-nav-pop var(--vy-motion-slow) var(--vy-ease-spring);
}
@keyframes vy-rail-in {
  from { transform: translateY(-50%) scaleY(0); }
  to   { transform: translateY(-50%) scaleY(1); }
}
.sidenav__btn.is-active::before {
  transform-origin: top center;
  animation: vy-rail-in var(--vy-motion-slow) var(--vy-ease-emphasized);
}
/* P1-фикс (2026-09-09): при живом max-ui вкладки уже играет maxi-bounce
   (scale кнопки) — микро-поп иконки сверху не наслаивается (единый
   владелец). Атрибут ставит vy-motion.js при детекте max-ui. */
html[data-vy-defer-maxui] .bottomnav__btn.is-active .bottomnav__icon,
html[data-vy-defer-maxui] .sidenav__btn.is-active .sidenav__icon {
  animation: none;
}

/* --------------------------------------------------------------------------
   9. «ДЫХАНИЕ» — точки набора текста (помощник для chat-core)
   -------------------------------------------------------------------------- */
@keyframes vy-breathe {
  0%, 100% { transform: scale(0.8); opacity: 0.35; }
  50%      { transform: scale(1.08); opacity: 0.9; }
}
.vy-typing {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  line-height: 0;
  vertical-align: middle;
}
.vy-typing i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  animation: vy-breathe 1.4s var(--vy-ease) infinite;
}
.vy-typing i:nth-child(2) { animation-delay: 0.18s; }
.vy-typing i:nth-child(3) { animation-delay: 0.36s; }

/* --------------------------------------------------------------------------
   10. СКЕЛЕТОН-ШИММЕР — transform-сдвиг блика, без layout/paint-бурж
   -------------------------------------------------------------------------- */
@keyframes vy-shimmer {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}
.vy-skeleton {
  position: relative;
  overflow: hidden;
  background: var(--surface-2, rgba(15, 23, 42, 0.06));
  border-radius: 12px;
  min-height: 14px;
}
.vy-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, currentColor 14%, transparent),
    transparent
  );
  animation: vy-shimmer var(--vy-shimmer-dur) var(--vy-ease) infinite;
  will-change: transform;
}
html[data-theme="dark"] .vy-skeleton::after {
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.09),
    transparent
  );
}

/* --------------------------------------------------------------------------
   11. ГАЛОЧКА УСПЕХА — микро-анимация (draw stroke + лёгкий поп)
   Исключение из «только transform/opacity»: stroke-dashoffset на пути 24px.
   -------------------------------------------------------------------------- */
@keyframes vy-check-draw {
  from { stroke-dashoffset: 24; }
  to   { stroke-dashoffset: 0; }
}
@keyframes vy-check-pop {
  0%  { transform: scale(0.6); opacity: 0; }
  60% { transform: scale(1.08); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
.vy-check {
  width: 24px;
  height: 24px;
  display: inline-block;
  vertical-align: middle;
  animation: vy-check-pop var(--vy-motion-slow) var(--vy-ease-spring) both;
}
.vy-check path {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 24;
  animation: vy-check-draw var(--vy-motion-base) var(--vy-ease-emphasized) both;
}

/* --------------------------------------------------------------------------
   12. ФОНОВЫЕ ПУЗЫРИ — пустые состояния (очень тихо, GPU-дёшево)
   Разметку даёт bubblesHtml() из vy-motion.js; позиции/периоды — inline-vars.
   -------------------------------------------------------------------------- */
@keyframes vy-float {
  0%   { transform: translateY(6%) scale(0.55); opacity: 0; }
  12%  { opacity: var(--vy-bubble-o, 0.12); }
  82%  { opacity: calc(var(--vy-bubble-o, 0.12) * 0.7); }
  100% { transform: translateY(-108%) scale(1); opacity: 0; }
}
.vy-bubbles {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
.vy-bubbles i {
  position: absolute;
  bottom: -10%;
  left: var(--x, 20%);
  width: var(--s, 18px);
  height: var(--s, 18px);
  border-radius: 50%;
  background: var(--accent, #1a56db);
  opacity: 0;
  animation: vy-float var(--dur, 16s) linear infinite;
  animation-delay: var(--d, 0s);
  will-change: transform, opacity;
}
/* Контейнер пустого состояния — якорь для пузырей (существующая разметка) */
.thread-empty {
  position: relative;
}
.thread-empty .thread-empty__card {
  position: relative;
  z-index: 1;
}

/* --------------------------------------------------------------------------
   13. HEART BURST — двойной тап (экспорт для шортсов, w2)
   Разбег частиц задаётся inline-переменными --tx/--ty из vy-motion.js.
   -------------------------------------------------------------------------- */
@keyframes vy-heart {
  0%   { transform: translate(0, 0) scale(0.4); opacity: 0; }
  15%  { opacity: 0.95; }
  100% { transform: translate(var(--tx, 0px), var(--ty, -90px)) scale(1.15); opacity: 0; }
}
.vy-heart-burst {
  position: fixed;
  z-index: 200;
  pointer-events: none;
  line-height: 0;
}
.vy-heart-burst i {
  position: absolute;
  display: block;
  font-size: var(--fs, 20px);
  font-style: normal;
  transform: translate(-50%, -50%);
  animation: vy-heart var(--vy-motion-slow) var(--vy-ease) forwards;
  will-change: transform, opacity;
}

/* --------------------------------------------------------------------------
   14. 60FPS-БЮДЖЕТ (метод проверки — см. MOTION_GUIDE.md §7):
   все keyframes слоя — transform/opacity; will-change только у
   бесконечных анимаций и on-mount микровзаимодействий (снимаются сами).
   -------------------------------------------------------------------------- */
