/* Анимации страниц справочника (§12, R23): 11 образцов Kinetics (kinetics.colorion.co, Csaba Kissi, MIT) —
   их кривые и длительности; цвета образцов заменены токенами common.css. Классы состояния (.in, .show, .open, .copied,
   .shake, .empty, .sending) ставит JS страницы. Анимация — только там, где она что-то сообщает: пришло, работает, ошибка,
   раскрыто. При «уменьшить движение» — ни анимаций, ни переходов (правило в конце файла).

   Классы:
   .enter → .enter.in            появление реплики и пункта списка; .enter-1 … .enter-9 — шаг 90 мс внутри порции
   .typing > span ×3             три точки «справочник работает»
   button.sending .dots > span ×3  кнопка «Отправить», пока служба не ответила на отправку
   button                        нажатие — «сжать»
   .composer, .composer.empty .send  поле ввода и кнопка «Отправить» при пустом поле
   .skeleton > .skeleton-line ×3–5   полосы загрузки
   .toast → .toast.show          всплывающее «новый ответ» внизу
   .slide-note → .slide-note.show    строка сверху («нет связи», «не отвечает»); .again — выехать заново (slideIn)
   .shake                        поле с ошибкой: рамка --error и встряхивание (shake() в motion.js)
   details > summary             стрелка раскрытия и появление содержимого
   .copied                       «Скопировано»: галочка перед надписью
   .drawer → .drawer.open, .scrim → .scrim.open  выдвижная колонка на узком экране */

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Stagger Entrance */
.enter { opacity: 0; transform: translateY(14px);
  transition: opacity 0.45s cubic-bezier(0.16, 1, 0.3, 1), transform 0.45s cubic-bezier(0.16, 1, 0.3, 1); }
.enter.in { opacity: 1; transform: translateY(0); }
/* Пункты списка: шаг 90 мс (i * 90ms, как у образца), только первые 10 пунктов порции, дальше без задержки */
.enter-1 { transition-delay: 90ms; }
.enter-2 { transition-delay: 180ms; }
.enter-3 { transition-delay: 270ms; }
.enter-4 { transition-delay: 360ms; }
.enter-5 { transition-delay: 450ms; }
.enter-6 { transition-delay: 540ms; }
.enter-7 { transition-delay: 630ms; }
.enter-8 { transition-delay: 720ms; }
.enter-9 { transition-delay: 810ms; }

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Typing Indicator */
.typing { display: inline-flex; gap: 5px; align-items: center; padding: 6px 0; }
.typing span { width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  animation: typing-bounce 1.2s ease-in-out infinite; }
.typing span:nth-child(2) { animation-delay: 0.16s; }
.typing span:nth-child(3) { animation-delay: 0.32s; }
@keyframes typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
  30% { transform: translateY(-7px); opacity: 1; }
}

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Submit States */
/* Только точки вместо надписи, пока служба не ответила на отправку; галочка образца не берётся */
.dots { display: inline-flex; gap: 4px; align-items: center; height: 1.5em; }
.dots span { width: 6px; height: 6px; border-radius: 50%; background: currentColor;
  animation: submit-bounce 0.6s ease-in-out infinite; }
.dots span:nth-child(2) { animation-delay: 0.12s; }
.dots span:nth-child(3) { animation-delay: 0.24s; }
@keyframes submit-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Squish Button */
/* У образца scale(0.88) — смягчено до 0.94 для широких кнопок с текстом */
button { transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); }
button:active:not(:disabled) { transform: scale(0.94); transition: transform 0.08s ease-out; }

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Prompt Composer */
/* Только рамка поля и вид кнопки при пустом поле; вращающийся ореол образца не берётся — отвлекает */
.composer textarea { transition: border-color 0.3s ease; }
.composer textarea:focus { border-color: var(--accent); }
.composer.empty .send { transform: scale(0.7); opacity: 0.35; }

/* своё, в духе Kinetics: переход кнопки «Отправить» между пустым полем и текстом (у образца время не указано) */
.composer .send { transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Shimmer Skeleton */
.skeleton { padding: 4px 0; }
.skeleton-line { height: 14px; margin: 10px 0; border-radius: 6px;
  background: linear-gradient(90deg, var(--soft) 0%, var(--line) 20%, var(--soft) 40%);
  background-size: 280% 100%; animation: shimmer 1.5s linear infinite; }
.skeleton-line:nth-child(2n) { width: 82%; }
.skeleton-line:nth-child(3n) { width: 64%; }
@keyframes shimmer {
  from { background-position: 140% 0; }
  to { background-position: -140% 0; }
}

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Toast Overshoot */
/* Держится TOAST_MS и уходит тем же переходом: страница снимает .show */
.toast { position: fixed; left: 50%; bottom: 24px; z-index: 30; pointer-events: none;
  transform: translate(-50%, 140%) scale(0.9); opacity: 0;
  transition: transform 0.55s cubic-bezier(0.18, 1.25, 0.4, 1), opacity 0.3s; }
.toast.show { transform: translate(-50%, 0) scale(1); opacity: 1; pointer-events: auto; }

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Notification Slide-in */
/* Строка во всю ширину: сдвиг только по вертикали (у образца ещё translate(-50%, …)); держится, пока держится состояние */
.slide-note { transform: translateY(-160%); opacity: 0;
  transition: transform 0.55s cubic-bezier(0.18, 1.25, 0.4, 1), opacity 0.3s; }
.slide-note.show { transform: translateY(0); opacity: 1; }
/* своё, в духе Kinetics: новое появление строки, что уже видна, — в начало без перехода, потом снова .show (slideIn в motion.js) */
.slide-note.again { transition: none; }

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Error Shake */
/* Повтор — снять класс, перерисовка, поставить снова (shake() в motion.js) */
.shake, input.shake, textarea.shake { border-color: var(--error); animation: shake-x 0.45s cubic-bezier(.36,.07,.19,.97) both; }
@keyframes shake-x {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-4px); }
  40%, 60% { transform: translateX(4px); }
}

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Accordion Spring */
/* Стрелка образца; своя стрелка вместо значка браузера */
details > summary { list-style: none; }
details > summary::-webkit-details-marker { display: none; }
details > summary::after { content: '▾'; display: inline-block; margin-left: 6px;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); }
details[open] > summary::after { transform: rotate(180deg); }

/* своё, в духе Kinetics: содержимое раскрытия появляется (у образца max-height до 100px — для длинного содержимого
   и закрытого details не годится); кривая и длительность — тела образца Accordion Spring */
details[open] > summary ~ * { animation: details-in 0.45s cubic-bezier(0.16, 1, 0.3, 1) both; }
@keyframes details-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* по мотивам Kinetics, Csaba Kissi, MIT, kinetics.colorion.co — Copy Button */
/* Галочка перед «Скопировано»: держится, пока на кнопке «Скопировано» (время ставит помощник страницы) */
.copied::before { content: '✓'; display: inline-block; margin-right: 6px;
  animation: copied-fade 0.2s both, copied-pop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes copied-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes copied-pop { from { transform: scale(0.5); } to { transform: scale(1); } }

/* своё, в духе Kinetics: выдвижная колонка чатов на узком экране (выдвижной панели в Kinetics нет; кривая — Stagger Entrance).
   На широком экране вёрстка страницы ставит колонке transform: none */
.drawer { transform: translateX(-100%); transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1); }
.drawer.open { transform: translateX(0); }
.scrim { opacity: 0; pointer-events: none; transition: opacity 0.3s ease; }
.scrim.open { opacity: 0.4; pointer-events: auto; }

/* «Уменьшить движение» (R47i): ни анимаций, ни переходов; появляющиеся элементы видны сразу */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .enter { opacity: 1; transform: none; }
}
