/* Плавность интерфейса: единая система движения.

   Подключается последним и меняет только анимации и переходы — геометрию,
   цвета и раскладку задают предыдущие слои, поэтому ни один тест раскладки
   этот файл не затрагивает.

   Правила слоя:
   1) одна шкала длительностей и одна кривая на вид движения (переменные ниже);
   2) анимируются только `opacity`, `transform`, `box-shadow` и цвета — свойства,
      которые не вызывают пересчёт раскладки и не дёргают соседние элементы;
   3) любое движение отключается при `prefers-reduced-motion` и при выключенных
      анимациях в настройках панели (`:root[data-motion="off"]`), поэтому слой
      безопасен для пользователей с вестибулярными нарушениями;
   4) входные анимации не меняют итоговое состояние: элемент остаётся видимым,
      даже если анимация не запустилась. */

:root{
 /* Длительности: короткая для откликов на нажатие, средняя для появления
    элементов, длинная для крупных поверхностей. */
 --motion-fast:120ms;
 --motion-base:200ms;
 --motion-slow:280ms;
 /* Одна кривая на весь интерфейс. Прежняя `spring` проходила выше единицы и
    возвращалась назад — отклик кнопки от этого пружинил, хотя ничего не
    отскакивает. Имена сохранены: на них ссылаются остальные правила слоя. */
 --ease-standard:cubic-bezier(.22,1,.36,1);
 --ease-enter:cubic-bezier(.22,1,.36,1);
 --ease-exit:cubic-bezier(.22,1,.36,1);
 --ease-spring:cubic-bezier(.22,1,.36,1);
}

/* --- Управляющие элементы: отклик на наведение и нажатие --- */
.button,.icon-button,.nav-item,.dialog-item,.zone-option,.period-option,.accent-option,.text-button{
 transition:background var(--motion-fast) var(--ease-standard),
            color var(--motion-fast) var(--ease-standard),
            border-color var(--motion-fast) var(--ease-standard),
            box-shadow var(--motion-base) var(--ease-standard),
            transform var(--motion-fast) var(--ease-spring);
}
/* Наведение меняет только цвет и поверхность: подъём кнопки и рост иконки
   двигали интерфейс там, где ничего не происходит. Нажатие остаётся
   ощутимым — оно отвечает на действие. */
.button:active:not(:disabled),.icon-button:active:not(:disabled),.nav-item:active{
 transform:translateY(0) scale(.97);transition-duration:var(--motion-fast)}

/* Индикатор занятости крутится плавно и равномерно. */
button[aria-busy="true"]::after{animation:motion-spin .7s linear infinite}
@keyframes motion-spin{to{transform:rotate(360deg)}}

/* --- Поверхности: появление карточек и панелей --- */
.panel,.stat-card,.module-card,.seller-widget,.seller-row,.seller-metric,.chart-card{
 transition:box-shadow var(--motion-base) var(--ease-standard),
            border-color var(--motion-base) var(--ease-standard),
            transform var(--motion-base) var(--ease-standard);
}
/* Карточки не всплывают под курсором: они не нажимаются, и движение обещало
   отклик, которого нет. Наведение видно по рамке. */
@media(hover:hover){
 .module-card:hover,.stat-card:hover,.chart-card:hover{
  border-color:color-mix(in srgb,var(--ink) 18%,var(--border))}
}
/* Вкладка проявляется целиком, карточки внутри — без задержек по одной:
   ступенчатое появление списка заметно тормозит переключение разделов. */
.view:not([hidden]){animation:motion-view-enter var(--motion-base) var(--ease-enter)}
@keyframes motion-view-enter{from{opacity:.55;transform:translateY(5px)}to{opacity:1;transform:none}}

/* --- Модальные окна: вход и уход --- */
.modal[open]{animation:motion-modal-enter var(--motion-slow) var(--ease-enter)}
.modal.is-closing{animation:motion-modal-exit 180ms var(--ease-exit) forwards}
@keyframes motion-modal-enter{from{opacity:0;transform:translateY(12px) scale(.985)}to{opacity:1;transform:none}}
@keyframes motion-modal-exit{to{opacity:0;transform:translateY(8px) scale(.99)}}
.modal::backdrop{animation:motion-fade var(--motion-base) var(--ease-standard)}
.modal.is-closing::backdrop{animation:motion-fade var(--motion-fast) var(--ease-exit) reverse forwards}
@keyframes motion-fade{from{opacity:0}to{opacity:1}}

/* --- Всплывающие сообщения --- */
.toast{animation:motion-toast-enter var(--motion-base) var(--ease-spring)}
@keyframes motion-toast-enter{from{opacity:0;transform:translateY(12px) scale(.97)}to{opacity:1;transform:none}}
.notification-popup{animation:motion-popup-enter var(--motion-base) var(--ease-enter);
 transition:opacity var(--motion-base) var(--ease-standard),transform var(--motion-base) var(--ease-standard)}
@keyframes motion-popup-enter{from{opacity:0;transform:translateX(16px)}to{opacity:1;transform:none}}

/* --- Сообщения чата: новое сообщение проявляется со своей стороны --- */
.message-bubble{animation:motion-bubble-in var(--motion-base) var(--ease-enter)}
@keyframes motion-bubble-in{from{opacity:0;transform:translateY(6px) scale(.985)}to{opacity:1;transform:none}}

/* --- Раскрывающиеся разделы настроек --- */
.settings-category>summary::after,.analytic-group>summary::after{
 transition:transform var(--motion-base) var(--ease-spring)}
.settings-category[open]>summary::after{transform:rotate(180deg)}
.settings-category>summary,.analytic-group>summary{
 transition:background var(--motion-fast) var(--ease-standard)}

/* --- Поля ввода: фокус --- */
input,textarea,select{
 transition:border-color var(--motion-fast) var(--ease-standard),
            box-shadow var(--motion-fast) var(--ease-standard),
            background-color var(--motion-fast) var(--ease-standard)}

/* --- Таблицы и строки списков --- */
tbody tr{transition:background var(--motion-fast) var(--ease-standard)}
.history-entry,.event-row{transition:background var(--motion-fast) var(--ease-standard)}

/* --- Индикаторы запаса и прогресса --- */
.stock-gauge-fill{transition:width var(--motion-slow) var(--ease-standard),
                             background-color var(--motion-slow) var(--ease-standard)}

/* --- Значок непрочитанных: короткий импульс при изменении --- */
.unread-chip,.nav-count{transition:transform var(--motion-base) var(--ease-spring),
                                   background var(--motion-fast) var(--ease-standard)}

/* --- Ступенчатое появление (stagger) ---
   Карточки списка проявляются по очереди с шагом 40 мс. Задержка задаётся
   переменной на самой карточке, а не :nth-child: список перерисовывается
   целиком, и привязка к позиции заставляла бы анимацию играть при каждом
   обновлении данных. */
.stagger-in{animation:motion-stagger var(--motion-base) var(--ease-enter) both;
            animation-delay:calc(var(--stagger-index,0) * 40ms)}
@keyframes motion-stagger{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* --- Появление по действию ---
   Созданное хранилище или добавленная строка проступают на месте. Перелёт
   убран: он добавлял отскок там, где элемент просто появился. */
.spring-in{animation:motion-spring-in var(--motion-slow) var(--ease-standard) both}
@keyframes motion-spring-in{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:none}}

/* --- Мерцание-заглушка (shimmer) ---
   Скелет вместо пустоты на время загрузки: блик проходит по плашке.
   Полоса рисуется фоном, поэтому раскладка не пересчитывается. */
.skeleton{position:relative;overflow:hidden;border-radius:9px;
          background:var(--surface-soft,#f1f4f8);min-height:14px}
.skeleton:after{content:"";position:absolute;inset:0;transform:translateX(-100%);
                background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--ink,#000) 8%,transparent),transparent);
                animation:motion-shimmer 1.3s ease-in-out infinite}
@keyframes motion-shimmer{to{transform:translateX(100%)}}

/* --- Нажатие на строку таблицы ---
   Строка-кнопка отзывается так же, как обычная кнопка: без этого таблица
   кажется неживой рядом с остальным интерфейсом. */
tbody tr[data-action],tbody tr[data-pl]{transition:background var(--motion-fast) var(--ease-standard),
                                                   transform var(--motion-fast) var(--ease-spring)}
tbody tr[data-action]:active,tbody tr[data-pl]:active{transform:scale(.995)}

/* --- Переключатели и флажки ---
   Ручка переключателя едет, а не перескакивает; флажок отзывается нажатием.
   Двигается только `transform`, поэтому строка списка не дёргается. */
input[type="checkbox"],input[type="radio"]{
 transition:background-color var(--motion-fast) var(--ease-standard),
            border-color var(--motion-fast) var(--ease-standard),
            box-shadow var(--motion-fast) var(--ease-standard)}
input[type="checkbox"]:active:not(:disabled),input[type="radio"]:active:not(:disabled){transform:scale(.92)}
input[role="switch"]::before,input[role="switch"]::after{
 transition:transform var(--motion-base) var(--ease-spring),
            background-color var(--motion-fast) var(--ease-standard)}
.switch-row,.check-label,.store-lot,.storage-card-switch{
 transition:background-color var(--motion-fast) var(--ease-standard)}

/* --- Карточки складов ---
   Карточка приподнимается под курсором и глушится в архиве; полоса остатка
   доезжает до новой доли, а не перерисовывается рывком. */
.storage-card{transition:border-color var(--motion-base) var(--ease-standard),
                         box-shadow var(--motion-base) var(--ease-standard),
                         opacity var(--motion-base) var(--ease-standard)}
@media(hover:hover){.storage-card:hover{border-color:color-mix(in srgb,var(--ink) 18%,var(--border))}}
.storage-bar-fill{transition:width var(--motion-slow) var(--ease-enter),
                             background-color var(--motion-slow) var(--ease-standard)}
.storage-card-count strong{transition:color var(--motion-base) var(--ease-standard)}

/* --- Плашки, метки и подсказки состояния --- */
.status-pill,.store-chip,.chip,.tag,.badge{
 transition:background-color var(--motion-fast) var(--ease-standard),
            color var(--motion-fast) var(--ease-standard),
            transform var(--motion-base) var(--ease-spring)}

/* --- Сообщения об ошибке и статусе формы ---
   Текст ошибки проявляется, а не возникает кадром: иначе его легко не заметить. */
.form-error:not([hidden]),.notice,.catalog-status{
 animation:motion-fade var(--motion-base) var(--ease-enter)}

/* --- Пустые состояния и заглушки разделов --- */
.empty-state,.muted-block{animation:motion-stagger var(--motion-base) var(--ease-enter) both}

/* --- Ссылки и текстовые кнопки --- */
a:not(.button){transition:color var(--motion-fast) var(--ease-standard),
                          opacity var(--motion-fast) var(--ease-standard)}

/* --- Значки кнопок ---
   Иконка меняет только цвет вместе со своей кнопкой: отдельное движение внутри
   кнопки читается как второй, лишний отклик. */
svg.icon{transition:color var(--motion-fast) var(--ease-standard)}

/* --- Списки с прокруткой ---
   Плавная прокрутка к выбранному элементу внутри диалога склада и каталога. */
.store-lot-list,.simple-contents,.modal-body{scroll-behavior:smooth}

/* --- Подсветка рамки при фокусе ---
   Рамка поля набирает цвет и отбрасывает мягкое кольцо: взгляд находит активное
   поле в длинной форме, а раскладка не меняется — кольцо рисуется тенью. */
input:focus-visible,textarea:focus-visible,select:focus-visible{
 transition:border-color var(--motion-fast) var(--ease-standard),
            box-shadow var(--motion-base) var(--ease-enter)}
.store-label:focus-within>small,label:focus-within>small{
 transition:color var(--motion-base) var(--ease-standard)}

/* --- Раскрытие разделов настроек ---
   Содержимое проявляется и доезжает вниз, а не возникает кадром. Высота не
   анимируется: пересчёт раскладки дёргал бы остальную страницу. */
.settings-category[open]>.category-body,
.settings-category[open]>div,
.analytic-group[open]>div,
/* Раскрывающиеся панели плагинов — «Аренды», «История плагинов», «История
   выдачи». Они устроены так же (details > div), но под прежний список не
   попадали и раскрывались кадром. Путь только на CSS: правило само гаснет и
   по prefers-reduced-motion, и по [data-motion="off"] — ничего в JS добавлять
   не нужно. Высота по-прежнему не анимируется, см. заголовок блока. */
.rental-panel[open]>div,
.module-journal[open]>div,
.delivery-panel[open]>div{
 animation:motion-reveal var(--motion-slow) var(--ease-enter) both}
@keyframes motion-reveal{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}

/* --- Строки таблиц: появление сверху вниз ---
   Шаг мельче карточного (24 мс): строк больше, и длинная лестница задержек
   выглядит как подвисание. Задержка упирается в десятую строку. */
tbody tr{animation:motion-row-in var(--motion-base) var(--ease-enter) both}
tbody tr:nth-child(1){animation-delay:0ms}
tbody tr:nth-child(2){animation-delay:24ms}
tbody tr:nth-child(3){animation-delay:48ms}
tbody tr:nth-child(4){animation-delay:72ms}
tbody tr:nth-child(5){animation-delay:96ms}
tbody tr:nth-child(6){animation-delay:120ms}
tbody tr:nth-child(7){animation-delay:144ms}
tbody tr:nth-child(8){animation-delay:168ms}
tbody tr:nth-child(9){animation-delay:192ms}
tbody tr:nth-child(n+10){animation-delay:216ms}
@keyframes motion-row-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

/* --- Содержимое диалога ---
   Блоки формы въезжают вслед за самим окном, с небольшим отставанием: сначала
   появляется окно, затем его содержимое. */
.modal[open] .modal-body>*{animation:motion-stagger var(--motion-base) var(--ease-enter) both;
                           animation-delay:calc(90ms + var(--stagger-index,0) * 40ms)}
.modal[open] .modal-body>*:nth-child(2){--stagger-index:1}
.modal[open] .modal-body>*:nth-child(3){--stagger-index:2}
.modal[open] .modal-body>*:nth-child(n+4){--stagger-index:3}

/* --- Выбранный раздел меню ---
   Полоса слева вырастает из центра, вместо того чтобы включаться кадром. */
.nav-item::before{transition:transform var(--motion-base) var(--ease-spring),
                             opacity var(--motion-fast) var(--ease-standard)}
.nav-item svg{transition:color var(--motion-fast) var(--ease-standard)}

/* --- Кнопки-переключатели вкладок и периодов ---
   Выбранный вариант чуть вырастает: переключение ощущается нажатием, а не
   перекраской. */
.zone-option[aria-pressed="true"],.period-option[aria-pressed="true"],
.zone-option.selected,.period-option.selected{transform:scale(1.03)}

/* --- Полоса загрузки каталога и статусные строки --- */
#lots-status,#helper-catalog-status,.status-line{
 transition:color var(--motion-base) var(--ease-standard),
            opacity var(--motion-base) var(--ease-standard)}

/* --- Заголовок раздела при переключении вкладки --- */
#page-title,#page-description{animation:motion-fade var(--motion-base) var(--ease-enter)}

/* --- Полосы и индикаторы остатка ---
   Шкала склада и индикатор запаса доезжают до новой доли одной кривой. */
.storage-bar,.stock-gauge-track{transition:box-shadow var(--motion-base) var(--ease-standard)}

/* --- Удаляемые элементы ---
   Строка уходит вниз и гаснет: без этого удаление выглядит как сбой отрисовки. */
.is-removing{animation:motion-row-out var(--motion-fast) var(--ease-exit) forwards}
@keyframes motion-row-out{to{opacity:0;transform:translateY(6px) scale(.98)}}

/* ==========================================================================
   Приёмы из подборки 60fps.design, отобранные под эту панель.
   ==========================================================================

   Подборка — галерея записей интерфейсов, без кода и таймингов: оттуда взяты
   сами приёмы, а длительности и кривая остаются здешние (переменные выше).
   Взяты четыре, которым есть что объяснять в рабочем окне; остальные —
   конфетти, частицы, пружины, орбиты, мерцание заголовков — панели, открытой
   весь день, добавили бы шума, а не смысла.

   1. Counter / ticker — баланс к выводу доезжает до нового значения вместо
      подстановки кадром. Сам счёт живёт в tickMoney (static/app.js): текст
      по кадрам средствами CSS не посчитать.
   2. Shared element — выбранный раздел меню: подсветка переезжает со старого
      пункта на новый, а не включается на новом и гаснет на старом.
   3. Reveal — боковая панель диалога настроек выезжает от своего края.
   4. Shrink — нажатие сжимает элемент к центру; есть выше, здесь только
      распространяется на строки списка диалогов. */

/* 2. Общий элемент: подсветка выбранного раздела.
   Фон пункта меню рисуется отдельным слоем ::after, а не заливкой самой
   кнопки: заливку нельзя провести между двумя разными узлами, а слой под
   текстом масштабируется из ничего по вертикали — подсветка «раскрывается»
   на новом пункте за 200 мс, пока старая закрывается.
   Движется только transform, поэтому список не дёргается. */
.nav-item{position:relative;isolation:isolate}
.nav-item::after{
 content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;
 background:var(--accent);opacity:0;transform:scaleY(.4);
 transition:opacity var(--motion-fast) var(--ease-standard),
            transform var(--motion-base) var(--ease-enter);
}
.nav-item.selected::after{opacity:1;transform:scaleY(1)}

/* 3. Раскрытие: ящик настроек приезжает от правого края экрана, а не
    проявляется в центре. Он и закреплён у правого края (customization.css),
    поэтому движение совпадает с тем, откуда он взялся. */
.settings-drawer[open]{animation:motion-drawer-in var(--motion-slow) var(--ease-enter)}
.settings-drawer.is-closing{animation:motion-drawer-out 180ms var(--ease-exit) forwards}
@keyframes motion-drawer-in{from{opacity:0;transform:translateX(24px)}to{opacity:1;transform:none}}
@keyframes motion-drawer-out{to{opacity:0;transform:translateX(16px)}}

/* 4. Нажатие строки диалога: тот же отклик, что у кнопки. Строка списка
    переписок — кнопка и есть, а отвечала только цветом. */
.dialog-item:active:not(:disabled){transform:scale(.99)}

/* --- Отключение движения ---
   Два источника: системная настройка и переключатель панели. Оба полностью
   снимают анимации и переходы, оставляя конечные состояния видимыми. */
:root[data-motion="off"] *,
:root[data-motion="off"] *::before,
:root[data-motion="off"] *::after{
 animation-duration:.01ms!important;animation-iteration-count:1!important;
 transition-duration:.01ms!important}
:root[data-motion="off"] .button:hover,
:root[data-motion="off"] .icon-button:hover,
:root[data-motion="off"] .nav-item:hover,
:root[data-motion="off"] .module-card:hover,
:root[data-motion="off"] .stat-card:hover,
:root[data-motion="off"] .storage-card:hover,
:root[data-motion="off"] .chart-card:hover{transform:none}
:root[data-motion="off"] .zone-option[aria-pressed="true"],
:root[data-motion="off"] .period-option[aria-pressed="true"],
:root[data-motion="off"] .zone-option.selected,
:root[data-motion="off"] .period-option.selected{transform:none}
:root[data-motion="off"] .store-lot-list,
:root[data-motion="off"] .modal-body{scroll-behavior:auto}
/* Задержки входа при выключенном движении обнуляются: иначе список ещё
   проступает лестницей, хотя сама анимация уже мгновенная. */
:root[data-motion="off"] *,
:root[data-motion="off"] *::before,
:root[data-motion="off"] *::after{animation-delay:0ms!important}
/* Бесконечный блик скелета при выключенном движении не замирает на кадре,
   а исчезает: мерцающая заглушка без анимации выглядит как сбой. */
:root[data-motion="off"] .skeleton:after{display:none}
@media(prefers-reduced-motion:reduce){
 *,*::before,*::after{
  animation-duration:.01ms!important;animation-iteration-count:1!important;
  animation-delay:0ms!important;
  transition-duration:.01ms!important;scroll-behavior:auto!important}
 .button:hover,.icon-button:hover,.nav-item:hover,
 .module-card:hover,.stat-card:hover,.chart-card:hover,.storage-card:hover{transform:none}
 .zone-option[aria-pressed="true"],.period-option[aria-pressed="true"],
 .zone-option.selected,.period-option.selected{transform:none}
 .skeleton:after{display:none}
}
