/* ─── Enter-пресеты алертов — единый источник правды ─────────────────────
   Файл подключают ДВА клиента:
     - overlay.html      — боевое применение: класс .anim-<preset> на
                           .notification (весь блок: медиа + текст);
     - dashboard.html — живое превью в плитках: мини-карточка получает
                           ТЕ ЖЕ классы → крутит ТЕ ЖЕ ключевые кадры.
   Механика (v2, решение2026-11-14):
   - вход — fade-in (opacity) у ВСЕХ пресетов + transform-движение пресета;
     два animation в одном правиле, разные свойства → без конфликта;
   - анимируется ОДИН элемент — весь блок .notification; текст и медиа
     наследуют движение через opacity/transform родителя (своих анимаций
     у них нет);
   - exit — везде одинаковый fade-out (opacity → 0), тайминг очереди
     duration_ms + anim_duration_ms от него же;
   - только transform/opacity → GPU-композитинг (важно для OBS CEF);
   - длительность: var(--anim-dur, .25s) — overlay задаёт явно боевое
     anim_duration_ms (дефолт .25s = 100%; 500 мс = 50%), превью в
     редакторе пользуется fallback.
   Пресеты синхронизировать с ALLOWED_ANIMS (dashboard.html) и
   ANIM_PRESETS (overlay.html) — это три копии одного списка:
     fade-in (дефолт), slide-up, slide-down, slide-left, slide-right,
     scale-in, bounce, flip, swing. */

/* ─── Ключевые кадры ─────────────────────────────────────────────────── */
@keyframes anim-fade-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes anim-fade-out { from { opacity: 1; } to { opacity: 0; } }

/* Направления: slide-up — привычный вход снизу,40px как раньше;
   горизонтали — чуть больший размах, чтобы вектор читался. */
@keyframes kf-slide-up    { from { transform: translateY(40px); }  to { transform: translateY(0); } }
@keyframes kf-slide-down  { from { transform: translateY(-40px); } to { transform: translateY(0); } }
@keyframes kf-slide-left  { from { transform: translateX(-56px); } to { transform: translateX(0); } }
@keyframes kf-slide-right { from { transform: translateX(56px); }  to { transform: translateX(0); } }

@keyframes kf-scale-in    { from { transform: scale(.85); }        to { transform: scale(1); } }

/* Прыжок: влетает снизу, проскакивает цель, оседает с затуханием */
@keyframes kf-bounce {
  from { transform: translateY(64px); }
  55%  { transform: translateY(-10px); }
  75%  { transform: translateY(4px); }
  90%  { transform: translateY(-2px); }
  to   { transform: translateY(0); }
}

/* Переворот: из плоскости боком. |угол| ≤ 90° → бэкфейса (зеркала)
   не видно, perspective — в transform-функции (отдельный property не нужен) */
@keyframes kf-flip {
  from { transform: perspective(1200px) rotateY(-90deg); }
  to   { transform: perspective(1200px) rotateY(0); }
}

/* Качание: маятник с затуханием; origin — top center (правило ниже) */
@keyframes kf-swing {
  from { transform: rotate(-8deg); }
  35%  { transform: rotate(5deg); }
  60%  { transform: rotate(-3deg); }
  80%  { transform: rotate(1.5deg); }
  to   { transform: rotate(0); }
}

/* ─── Вход: общий fade + transform пресета (весь блок) ────────────────── */
.notification.anim-slide-up    { animation: anim-fade-in var(--anim-dur, .25s) ease-out both, kf-slide-up    var(--anim-dur, .25s) ease-out both; }
.notification.anim-slide-down  { animation: anim-fade-in var(--anim-dur, .25s) ease-out both, kf-slide-down  var(--anim-dur, .25s) ease-out both; }
.notification.anim-slide-left  { animation: anim-fade-in var(--anim-dur, .25s) ease-out both, kf-slide-left  var(--anim-dur, .25s) ease-out both; }
.notification.anim-slide-right { animation: anim-fade-in var(--anim-dur, .25s) ease-out both, kf-slide-right var(--anim-dur, .25s) ease-out both; }
.notification.anim-scale-in    { animation: anim-fade-in var(--anim-dur, .25s) ease-out both, kf-scale-in    var(--anim-dur, .25s) ease-out both; }
.notification.anim-fade-in     { animation: anim-fade-in var(--anim-dur, .25s) ease-out both; }
.notification.anim-bounce      { animation: anim-fade-in var(--anim-dur, .25s) ease-out both, kf-bounce      var(--anim-dur, .25s) ease-out both; }
.notification.anim-flip        { animation: anim-fade-in var(--anim-dur, .25s) ease-out both, kf-flip        var(--anim-dur, .25s) ease-out both; }
/* swing качается от верхнего края блока — как висящая табличка */
.notification.anim-swing       { animation: anim-fade-in var(--anim-dur, .25s) ease-out both, kf-swing       var(--anim-dur, .25s) ease-out both; transform-origin: top center; }

/* ─── Выход: единый fade-out для ВСЕХ пресетов ───────────────────────────
   Правило идёт ПОСЛЕ входных: та же специфичность (0,2,0) → при классах
   anim-<preset> + fade-out побеждает выход (animation-name перекрыт).
   Смена animation-name пересчитывается за один recalc: старая анимация
   отменяется (её fill исчезает), новая сразу ставит opacity from=1 →
   визуально непрерывно, без вспышки базового opacity:0. */
.notification.fade-out {
  animation: anim-fade-out var(--anim-dur, .25s) ease-out both;
}
