/* ═══════════════════════════════════════════════════════════════════════════
   motion.css — анимации интерфейса.

   Скорости и кривые — токены из tokens.css. Двигаются только opacity и
   transform/scale: они не перестраивают страницу и идут плавно даже на
   слабых телефонах. Если в системе включено «уменьшить движение», всё
   выключается в конце файла.
   ═══════════════════════════════════════════════════════════════════════════ */

/* 1. Переход между страницами. Браузер держит старую страницу, пока новая
   не готова, и плавно сменяет одну другой (View Transitions; Chrome и
   Android WebView 126+). Шапка и нижнее меню — отдельные слои: они стоят на
   месте, меняется только содержимое. Без поддержки — обычный переход. */
@view-transition { navigation: auto; }
.app-top { view-transition-name: app-top; }
.mobile-nav { view-transition-name: app-nav; }
::view-transition-group(*) { animation-duration: var(--dur); animation-timing-function: var(--ease-out); }

/* «Перелистывание» по нижнему меню: текущий экран уезжает в сторону
   нажатой вкладки, въезжает заготовка раздела (серые строки с бликом), а
   пришедшая страница сменяет её плавно (View Transitions выше). Пока сеть
   отвечает, человек видит, что грузятся именно строки раздела. */
@keyframes page-out-left  { to { opacity: 0; transform: translateX(-28px); } }
@keyframes page-out-right { to { opacity: 0; transform: translateX(28px); } }
@keyframes page-in-left   { from { opacity: 0; transform: translateX(28px); } }
@keyframes page-in-right  { from { opacity: 0; transform: translateX(-28px); } }
.page-out-left  { animation: page-out-left  var(--dur-fast) var(--ease-in-out) forwards; }
.page-out-right { animation: page-out-right var(--dur-fast) var(--ease-in-out) forwards; }
.page-in-left   { animation: page-in-left   var(--dur) var(--ease-out); }
.page-in-right  { animation: page-in-right  var(--dur) var(--ease-out); }
.page-skeleton { padding: var(--sp-4) var(--sp-3); }
.page-skeleton .sk-title { width: 40%; height: 22px; margin: 4px 0 var(--sp-4); }
.page-skeleton .sk-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-3);
  margin-bottom: var(--sp-3);
}
.page-skeleton .sk-row { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) 0; }
.page-skeleton .sk-row + .sk-row { border-top: 1px solid var(--divider); }
.page-skeleton .sk-lines { flex: 1; min-width: 0; }
.page-skeleton .sk-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.page-skeleton .sk-tile { height: 64px; border-radius: var(--r-md); }
.page-skeleton .sk-tile.is-wide { grid-column: 1 / -1; }
.page-skeleton .sk-button { height: 34px; border-radius: var(--r-sm); margin-top: var(--sp-2); }

/* Мягкий переход внутри страницы (фильтры): содержимое проявляется с
   небольшим подъёмом. Класс ставит motion.js. Без fill-mode: после анимации
   transform снимается и не мешает fixed-элементам внутри страницы. */
@keyframes page-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.page-in { animation: page-in var(--dur) var(--ease-out); }

/* 2. Отклик на нажатие: кнопка чуть «вдавливается». */
.btn, .icon-btn, .app-btn, .user-chip, .notice-btn, .lesson-act, .m-nav-item,
.stat-note-reset, .js-stat-toggle, .rp-btn, .schedule-action {
  transition-property: transform, background-color, color, border-color, box-shadow;
  transition-duration: var(--dur-fast);
  transition-timing-function: var(--ease-out);
}
.btn:active:not(:disabled), .icon-btn:active, .app-btn:active, .user-chip:active,
.notice-btn:active, .lesson-act:active, .m-nav-item:active, .stat-note-reset:active,
.js-stat-toggle:active, .rp-btn:active, .schedule-action:active {
  transform: scale(.96);
}
/* Пункты списков не сжимаются — подсвечиваются. */
.dropdown-item { transition: background-color var(--dur-fast) var(--ease-out); }

/* 3. Выпадающие меню раскрываются, а не появляются рывком. scale — отдельное
   свойство: оно не спорит с transform, которым Bootstrap ставит меню на
   место. */
@keyframes menu-in {
  from { opacity: 0; scale: .96; }
  to   { opacity: 1; scale: 1; }
}
.dropdown-menu.show { animation: menu-in var(--dur) var(--ease-out); transform-origin: top center; }
.dropdown-menu-end.show { transform-origin: top right; }

/* 4. Пары недели проявляются по очереди. Класс ставит экран расписания,
   когда открыт другой период или другой человек, — не при каждом
   обновлении. Очередь ограничена восемью днями: дальше без задержки. */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.anim-stagger > * { animation: rise-in var(--dur) var(--ease-out) backwards; }
.anim-stagger > :nth-child(2) { animation-delay: calc(var(--stagger) * 1); }
.anim-stagger > :nth-child(3) { animation-delay: calc(var(--stagger) * 2); }
.anim-stagger > :nth-child(4) { animation-delay: calc(var(--stagger) * 3); }
.anim-stagger > :nth-child(5) { animation-delay: calc(var(--stagger) * 4); }
.anim-stagger > :nth-child(6) { animation-delay: calc(var(--stagger) * 5); }
.anim-stagger > :nth-child(7) { animation-delay: calc(var(--stagger) * 6); }
.anim-stagger > :nth-child(n+8) { animation-delay: calc(var(--stagger) * 7); }

/* 5. Заготовка строки, пока день грузится с сервера: серые полосы с бегущим
   бликом вместо надписи «Загружаю…». */
@keyframes shimmer {
  from { background-position: 150% 0; }
  to   { background-position: -50% 0; }
}
.skeleton {
  display: block;
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--surface) 30%, var(--border) 50%, var(--surface) 70%);
  background-size: 200% 100%;
  animation: shimmer 1.2s linear infinite;
}
.skeleton-num  { width: 28px; height: 28px; flex: 0 0 auto; }
.skeleton-line { height: 12px; margin: 4px 0; width: 70%; }
.skeleton-line.is-short { width: 40%; }

/* 6. Смена темы: цвета перетекают. Класс висит на <html> только во время
   переключения (motion.js) — постоянный transition на всех элементах
   тормозил бы прокрутку. */
.theme-anim, .theme-anim *, .theme-anim *::before, .theme-anim *::after {
  transition: background-color var(--dur-slow) var(--ease-in-out),
              color var(--dur-slow) var(--ease-in-out),
              border-color var(--dur-slow) var(--ease-in-out) !important;
}

/* 7. Нижнее меню: подсветка раздела — одна «таблетка», которая переезжает к
   нажатому пункту, пока грузится страница (её ставит motion.js).
   Рамка активного пункта рисуется без скрипта и того же размера, что и
   «таблетка»: раньше до запуска скрипта была старая рамка другого размера,
   и после загрузки пункт «расширялся». */
.mobile-nav .m-nav-item { position: relative; padding: 4px 2px; margin: 0; }
.mobile-nav .m-nav-item.active { background: none; box-shadow: none; }
.mobile-nav .m-nav-item.active::before,
.m-nav-pill { border-radius: 10px; background: rgba(255, 255, 255, .12); box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, .55); }
.mobile-nav .m-nav-item.active::before { content: ''; position: absolute; inset: 0 2px; pointer-events: none; }
.mobile-nav.pill-moving .m-nav-item.active::before { display: none; }
.m-nav-pill {
  position: absolute;
  top: 0; left: 0;
  pointer-events: none;
  transition: transform var(--dur-slow) var(--ease-out), width var(--dur-slow) var(--ease-out);
}
.m-nav-pill.no-anim { transition: none; }
.m-nav-pill[hidden] { display: none; }

/* Окна (выбор периода и др.): короче и снизу, затемнение — отдельно. У
   Bootstrap окно 300 мс проявлялось сверху, и на полпути текст окна и
   страницы читались друг через друга. */
.modal.fade { transition: opacity var(--dur) var(--ease-out); }
.modal.fade .modal-dialog { transform: translateY(16px); transition: transform var(--dur) var(--ease-out); }
.modal.show .modal-dialog { transform: none; }
.modal-backdrop.fade { transition: opacity var(--dur) var(--ease-out); }

/* Уменьшить движение: всё мгновенно, бликов и очередей нет. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  :root { --dur-fast: 0ms; --dur: 0ms; --dur-slow: 0ms; --stagger: 0ms; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
