/* ============================================================================
   motion.css — слой микроанимаций и тактильного отклика CRM «Bloggers».

   Что это. Единственный файл, который отвечает за ДВИЖЕНИЕ интерфейса: отклик
   на нажатие, подмену разделов, появление списков, состояния навигации, чипов,
   тоста и модалок. Ни одной новой краски, ни одного нового отступа — только
   реакция на действие человека.

   Куда подключать. ПОСЛЕДНИМ из стилей в index.html, после styles.css,
   mobile.css и mobile-extras.css. Порядок — единственный механизм приоритета
   здесь: !important не используется нигде, кроме блока prefers-reduced-motion
   в самом низу (там перебить нужно решительно всё).

   Железные правила, которым файл следует:
     · двигаются ТОЛЬКО transform, opacity и цвет (+ filter для затемнения
       нажатия). Ни width, ни height, ни top/left/margin/padding — иначе
       соседи разъезжаются и текст прыгает. Два осознанных исключения помечены
       в тексте: .pb i (ширина — это и есть смысл индикатора) и .skeleton
       (мерцание фоном — это и есть смысл заглушки);
     · длительности и кривые берутся только из набора --m-* ниже, чтобы у всего
       приложения был один ритм;
     · статика не меняется. Скриншот неподвижной страницы (и на десктопе, и на
       телефоне) обязан совпадать с тем, что было до этого файла: всё новое
       живёт в :active, в @keyframes, которые заканчиваются на естественном
       состоянии, или в элементах, скрытых до открытия.

   Чужие зоны, куда файл НЕ лезет (важно):
     · #drawer / #drawer.sheet / #scrim / .dragging — жест bottom sheet
       (sheet.js) пишет туда ИНЛАЙНОВЫЙ transform и инлайновую opacity в
       реальном времени. Любая наша transition на этих селекторах заставила бы
       шит отставать от пальца. Анимация выезда шита уже описана в mobile.css;
     · .mnav-item:active и .bcard:active — сжатие им уже задано в mobile.css
       (.94 и .985). Дублировать не стали: наше правило шло бы позже и молча
       перебило бы чужое значение;
     · .dtiles.f5 .dtile — свои hover/active живут в styles.css и выигрывают по
       специфичности; здесь только общий .dtile, до которого те не дотягиваются;
     · .cfbar span — ширина полос воронки анимируется в styles.css (.5s). Не
       трогаем, а согласуем с ней кривую индикатора задач.
   ========================================================================== */


/* ============================== 0. Набор ритма ============================ */
/* Три длительности и три кривые на всё приложение. Промежуточные значения
   (задержки лесенки, период пульса) считаются из них через calc — так их
   нельзя рассинхронизировать, поправив что-то одно. */
:root{
  --m-fast:120ms;   /* нажатие: палец коснулся — отклик должен быть мгновенным */
  --m-base:180ms;   /* обратный ход, появление вида, вход элемента списка */
  --m-slow:240ms;   /* индикаторы прогресса: их читают глазами, а не чувствуют */

  --m-ease-out:cubic-bezier(.16,1,.3,1);    /* «выброс» — быстрый старт, мягкая посадка */
  --m-spring:cubic-bezier(.34,1.56,.64,1);  /* перелёт за 100% и возврат: толчок выбора */
  --m-ease-io:cubic-bezier(.4,0,.2,1);      /* симметричная — только для бесконечного пульса */
}


/* ========================== 1. Отклик на нажатие ========================== */
/* Сжатие делается transform:scale, а не изменением размеров: бокс элемента в
   потоке остаётся прежним, соседи стоят на месте, текст внутри не перетекает.
   Затемнение/подсветка — filter:brightness: он не требует знать, какого цвета
   поверхность под элементом, и одинаково работает на синей кнопке и на тёмной
   панели.

   Асимметрия намеренная: вход быстрый (--m-fast), возврат медленнее (--m-base).
   Так нажатие ощущается «схваченным», а отпускание — мягким. Базовое правило
   описывает ВОЗВРАТ, правило :active — вход. */
.btn,
.chip,
.titem,
.copybtn,
.dh .acts .x{
  transition:transform var(--m-base) var(--m-ease-out),
             filter var(--m-base) var(--m-ease-out);
  /* серая вспышка Android/iOS дерётся с нашим сжатием — снимаем её */
  -webkit-tap-highlight-color:transparent;
}
.btn:active,
.chip:active,
.titem:active,
.copybtn:active,
.dh .acts .x:active{
  transition-duration:var(--m-fast);
}

/* Кнопки: заметная поверхность, сжатие сильнее и с затемнением. */
.btn:active{transform:scale(.97);filter:brightness(.93)}
/* Иконочные ✕ и ✎ в шапке карточки блогера (styles.css: .dh .acts .x). */
.dh .acts .x:active{transform:scale(.94)}
/* Строки и чипы: мелкие цели, глубокое сжатие на них читается как «дёрнулось». */
.titem:active{transform:scale(.985)}
.copybtn:active{transform:scale(.97)}
.chip:active{transform:scale(.97)}

/* «Скопировано» — состояние, а не жест: цвет рамки и текста переходит плавно,
   чтобы было видно, что это ответ именно на твоё нажатие. */
.copybtn{transition:transform var(--m-base) var(--m-ease-out),
                    filter var(--m-base) var(--m-ease-out),
                    border-color var(--m-base) var(--m-ease-out),
                    color var(--m-base) var(--m-ease-out)}


/* ====================== 2. Переключение разделов =========================== */
/* Раньше вид менялся мгновенным снятием .hide — глаз не успевал заметить, что
   содержимое вообще другое. Теперь входящий вид всплывает: 6px снизу + fade.
   Уход НЕ анимируем сознательно: .hide это display:none, анимировать исчезание
   пришлось бы через задержку снятия класса (чужой JS), а на глаз это дало бы
   мигание двух видов внахлёст.

   Ключевой кадр заканчивается на естественном состоянии, fill-mode не нужен —
   после анимации никакого transform на контейнере не остаётся (иначе он стал
   бы containing block для position:fixed внутри). */
#view-base:not(.hide),
#view-info:not(.hide),
#view-templates:not(.hide),
#view-tasks:not(.hide),
#view-settings:not(.hide){
  animation:m-view-in var(--m-base) var(--m-ease-out);
}
@keyframes m-view-in{
  from{opacity:0;transform:translateY(7px)}
  to{opacity:1;transform:none}
}


/* ======================= 3. Появление списка лесенкой ====================== */
/* Карточки блогеров (мобила) и строки таблицы (десктоп) въезжают снизу с
   каскадом: глаз получает направление чтения — сверху вниз.

   Шаг задержки = --m-fast / 4 = 30 мс, и только для первых десяти. Дальше
   задержки нет: на 40 строках каскад превратился бы в «низ списка залипает».

   fill-mode:backwards обязателен. Без него элемент во время своей задержки
   стоит в нормальном виде и лишь потом прыгает в стартовый кадр — получается
   мигание вместо появления.

   Список перерисовывается на каждый фильтр и каждый ввод в поиске, поэтому
   анимация нарочно короткая (--m-base) и без масштаба: она читается как
   «пришли новые данные», а не как эффект. */
.bcard,
.btable tbody tr{
  animation:m-row-in var(--m-base) var(--m-ease-out) backwards;
}
@keyframes m-row-in{
  from{opacity:0;transform:translateY(6px)}
  to{opacity:1;transform:none}
}

.bcard:nth-child(2), .btable tbody tr:nth-child(2){animation-delay:calc(var(--m-fast) * .25)}
.bcard:nth-child(3), .btable tbody tr:nth-child(3){animation-delay:calc(var(--m-fast) * .5)}
.bcard:nth-child(4), .btable tbody tr:nth-child(4){animation-delay:calc(var(--m-fast) * .75)}
.bcard:nth-child(5), .btable tbody tr:nth-child(5){animation-delay:calc(var(--m-fast) * 1)}
.bcard:nth-child(6), .btable tbody tr:nth-child(6){animation-delay:calc(var(--m-fast) * 1.25)}
.bcard:nth-child(7), .btable tbody tr:nth-child(7){animation-delay:calc(var(--m-fast) * 1.5)}
.bcard:nth-child(8), .btable tbody tr:nth-child(8){animation-delay:calc(var(--m-fast) * 1.75)}
.bcard:nth-child(9), .btable tbody tr:nth-child(9){animation-delay:calc(var(--m-fast) * 2)}
.bcard:nth-child(10),.btable tbody tr:nth-child(10){animation-delay:calc(var(--m-fast) * 2.25)}


/* ========================= 4. Нижняя навигация ============================ */
/* #mnav существует только на мобиле (на десктопе mobile.css держит display:none),
   поэтому десктопного вида эти правила не касаются вообще.

   Три сигнала «ты здесь», каждый со своей ролью:
     · цвет всего пункта — уже задан в mobile.css (.on → var(--acc)), там же
       живёт transition:color .15s. Не дублируем;
     · иконка чуть крупнее — вес без изменения бокса (svg 22×22 фиксирован,
       scale его не двигает);
     · полоска сверху — единственный элемент, который ОДИН отвечает за «этот
       раздел открыт», и он въезжает по горизонтали от центра.

   position:relative нужен ради ::before и ничего не сдвигает: у пункта нет
   позиционированных потомков. ::after у mnav-item свободен, но берём ::before —
   ::after в проекте уже занят зоной нажатия чипа, держим привычку. */
#mnav .mnav-item{position:relative}

#mnav .mnav-item svg{
  transition:transform var(--m-base) var(--m-ease-out);
}
#mnav .mnav-item.on svg{transform:scale(1.06)}

#mnav .mnav-item::before{
  content:"";
  position:absolute;top:0;left:0;right:0;height:2px;
  background:var(--acc);
  transform:scaleX(0);
  transition:transform var(--m-base) var(--m-ease-out);
  /* Своя композиционная плоскость. Safari на iOS умеет не перерисовать
     псевдоэлемент внутри position:fixed при смене класса у родителя — полоска
     тогда остаётся там, где была нарисована в первый раз. Промоушен слоя
     закрывает этот класс бага; на десктопе не меняет ничего. */
  will-change:transform;
  backface-visibility:hidden;
}
#mnav .mnav-item.on::before{transform:scaleX(1)}

/* Сжатие пункта при нажатии (scale .94) уже описано в mobile.css — здесь его
   намеренно нет, чтобы не перебить чужое значение своим. */


/* ============================== 5. Чипы фильтров ========================== */
/* В styles.css у .chip стоит transition:.1s — то есть 100 мс на ВСЕ свойства
   разом, включая те, которые мы теперь двигаем сами. Перечисляем свойства
   явно: так видно намерение и так transform не подхватывает чужую длительность. */
.chip{
  transition:background-color var(--m-fast) var(--m-ease-out),
             border-color var(--m-fast) var(--m-ease-out),
             color var(--m-fast) var(--m-ease-out),
             transform var(--m-base) var(--m-ease-out),
             filter var(--m-base) var(--m-ease-out);
}

/* Выбранный чип толкается: перелёт до 1.04 и возврат. Это ответ на нажатие,
   а не украшение — по нему видно, ЧТО именно выбралось, когда лента чипов
   прокручена и палец попал не туда, куда целился.
   Ленты перерисовываются целиком (buildStageChips/buildSourceChips), поэтому
   толчок проигрывается заново при каждой отрисовке — он для того и короткий. */
.chip.on{animation:m-chip-pop var(--m-base) var(--m-spring)}
@keyframes m-chip-pop{
  from{transform:scale(1.04)}
  to{transform:scale(1)}
}


/* ================================= 6. Тост ================================ */
/* Было: только opacity — сообщение проявлялось «ниоткуда». Стало: въезжает
   снизу, оттуда же, где живёт нижняя навигация.

   translateX(-50%) — это центрирование из styles.css, его обязательно нести
   в каждом кадре, иначе тост уедет к левому краю.

   Уход быстрее входа: базовое правило (=состояние «скрыт») держит --m-fast,
   правило .show (=вход) держит --m-base. 120/180 ≈ 67% — ровно тот случай,
   когда появление хочется заметить, а исчезновение — нет. */
.toast{
  transform:translateX(-50%) translateY(8px);
  transition:opacity var(--m-fast) var(--m-ease-out),
             transform var(--m-fast) var(--m-ease-out);
}
.toast.show{
  transform:translateX(-50%) translateY(0);
  transition-duration:var(--m-base);
}


/* =========================== 7. Модалки и диалоги ========================= */
/* Диалог приходит из центра — там же, где внимание человека в момент, когда он
   нажал «Добавить» или «Удалить». Подложка гасит фон отдельно от карточки,
   поэтому у них разные кадры: подложка только по opacity, карточка ещё и по
   масштабу.

   Уход не анимируем — все три контейнера гасятся через display:none
   (снятие .show), анимировать исчезание можно было бы только задержкой в
   чужом JS. Осознанный размен: без мигания и без правок чужих файлов. */
#modal.show,
#cmodal.show,
#confirm.show{
  animation:m-fade-in var(--m-base) var(--m-ease-out);
}
#modal.show .card,
#cmodal.show #cbox,
#confirm.show .cfmbox{
  animation:m-pop-in var(--m-base) var(--m-ease-out);
}
@keyframes m-fade-in{
  from{opacity:0}
  to{opacity:1}
}
@keyframes m-pop-in{
  from{opacity:0;transform:scale(.96)}
  to{opacity:1;transform:none}
}


/* ============================= 8. Плитки метрик =========================== */
/* .dtiles.f5 .dtile уже получил hover/active в styles.css и выигрывает по
   специфичности — сюда попадают только плитки вне f5-сетки, чтобы у класса
   было ОДНО поведение независимо от того, в какой сетке он оказался. */
.dtile{
  transition:border-color var(--m-base) var(--m-ease-out),
             transform var(--m-fast) var(--m-ease-out);
}
.dtile:hover{border-color:var(--acc)}
.dtile:active{transform:scale(.98)}


/* ======================== 9. Индикаторы прогресса ========================= */
/* ЕДИНСТВЕННОЕ место в файле, где двигается ширина, и это не оплошность:
   ширина полосы и есть значение, которое она показывает. Соседей это не
   двигает — <i> лежит внутри .pb с overflow:hidden и фиксированной высотой.

   Кривая и длительность подобраны под .cfbar span из styles.css (те же
   cubic-bezier(.16,1,.3,1), .5s против наших 240 мс): полосы воронки длиннее и
   их четыре, полоса задач одна и короче. Чужое правило не трогаем.

   Оговорка честности: renderTasks() перерисовывает .taskbar целиком, поэтому
   на СЕГОДНЯШНЕМ коде переход почти не виден — новый элемент анимировать не с
   чего. Правило заведено на месте смысла: как только полосу начнут обновлять
   без перерисовки, она поедет плавно, а не прыжком. */
.pb i{transition:width var(--m-slow) var(--m-ease-out)}


/* ============================ 10. Скелетон (задел) ======================== */
/* Единственный селектор в файле, которого сейчас нет в разметке. Заведён по
   договорённости: интегратор вешает .skeleton на список, пока идёт загрузка.

   Мерцает фоном между двумя штатными поверхностями (--panel ↔ --panel2), а не
   градиентом-«блеском»: блик пришлось бы двигать по background-position, и это
   был бы четвёртый цвет в тёмной теме, которого в токенах нет.
   Период 1.2 с = --m-fast × 10; кривая симметричная, иначе пульс читается как
   моргание, а не как дыхание. */
.skeleton{
  background-color:var(--panel);
  border:1px solid var(--line);
  border-radius:var(--radius);
  animation:m-skeleton calc(var(--m-fast) * 10) var(--m-ease-io) infinite;
}
@keyframes m-skeleton{
  0%,100%{background-color:var(--panel)}
  50%{background-color:var(--panel2)}
}


/* ====================== 11. Человек попросил меньше движения ============== */
/* Обязательный финал. Гасим ВСЁ, что добавил этот файл, включая бесконечный
   пульс скелетона: интерфейс обязан остаться полностью рабочим, просто без
   движения. Единственное место в файле, где !important уместен — перебить
   нужно все объявления transition/animation разом, включая чужие.

   Важно, что гасим именно ПЕРЕХОД, а не конечное состояние: все смысловые
   признаки применяются мгновенно и остаются на месте — активный пункт
   навигации по-прежнему крупнее и с полоской, тост встаёт по центру,
   диалог показывается в масштабе 1. «Меньше движения» не должно означать
   «меньше информации», поэтому дополнительных сбросов transform здесь нет. */
@media (prefers-reduced-motion: reduce){
  .btn,.chip,.titem,.copybtn,.dh .acts .x,.dtile,
  .bcard,.btable tbody tr,
  #view-base,#view-info,#view-templates,#view-tasks,#view-settings,
  #mnav .mnav-item,#mnav .mnav-item svg,#mnav .mnav-item::before,
  .toast,.toast.show,
  #modal.show,#cmodal.show,#confirm.show,
  #modal.show .card,#cmodal.show #cbox,#confirm.show .cfmbox,
  .pb i,.skeleton{
    animation:none !important;
    transition:none !important;
  }
}
