/**
 * animations.css — 全局操作动画层 (2026-07-10)
 *
 * 原则:
 *  - 只补缺口:toast/confirm/skeleton 已在 components.css 有动画,这里不重复
 *  - 全部使用 tokens.css 的 motion 变量(带回退值),风格统一
 *  - 时长克制(120-280ms),入场 backwards(结束后交还 hover 的 transform 控制权)
 *  - prefers-reduced-motion 由 style.css 全局兜底(animation/transition→0.01ms),无需重复
 */

/* ═══════════ 1. 通用微交互 ═══════════ */

/* 按钮按压:轻微下沉,松手回弹 */
.btn { transition: transform var(--motion-duration-fast, .15s) var(--motion-easing-standard, ease),
                   box-shadow var(--motion-duration-fast, .15s) var(--motion-easing-standard, ease); }
.btn:not(:disabled):active { transform: scale(.96); }

/* 表格行 hover 底色过渡(默认是突变) */
.table-hover > tbody > tr { transition: background-color .12s ease; }

/* 下拉项 hover 过渡(侧边栏 #sidebar a.nav-item 在 style.css:362 已有,不重复) */
.dropdown-item { transition: background-color .12s ease, color .12s ease; }

/* Bootstrap 下拉菜单打开:缩放淡入(全站排序/用户菜单/行操作通用) */
.dropdown-menu.show { animation: ani-menu-in .14s ease-out; transform-origin: top; }

/* DS 设计系统按钮按压(components.css 只有 hover,补 active) */
.ds-btn:not(:disabled):active { transform: scale(.97); }

/* 筛选 chips / 可点卡片按压与过渡 */
.ds-chip, .drv-chip { transition: background-color .15s ease, color .15s ease, border-color .15s ease; }
.stat-card:active, [role="button"]:active { transform: scale(.99); }

/* 学生列表批量条:与云盘批量条同款下滑入场(d-none 切换时自动重放) */
#stuBulkBar { animation: ani-selbar-in .18s ease-out; }

/* 页面加载 spinner 延迟 150ms 出现:快接口(<150ms)完全不闪 */
.page-loading { animation: ani-fade-in .2s ease .15s backwards; }

/* 通用退场工具类:淡出下沉(JS 加类后 ~180ms 再 remove) */
.ani-leave { opacity: 0 !important; transform: translateY(8px); transition: opacity .18s ease, transform .18s ease; pointer-events: none; }

/* DS.confirm 关闭:遮罩淡出 + 弹窗上浮缩小(入场在 components.css,这里补退场) */
.ds-confirm-overlay.is-leaving { opacity: 0; transition: opacity .15s var(--motion-easing-exit, ease-in); }
.ds-confirm-overlay.is-leaving .ds-confirm { opacity: 0; transform: scale(.96) translateY(-8px); transition: opacity .15s ease, transform .15s var(--motion-easing-exit, ease-in); }

/* 勾选框:选中时弹一下 + hover 放大 */
.form-check-input { transition: transform .12s ease, background-color .15s ease, border-color .15s ease; }
.form-check-input:hover { transform: scale(1.12); }
.form-check-input:checked { animation: ani-pop var(--motion-duration-base, .25s) var(--motion-easing-spring, cubic-bezier(.34,1.56,.64,1)); }
@keyframes ani-pop {
  0% { transform: scale(.6); }
  60% { transform: scale(1.18); }
  100% { transform: scale(1); }
}

/* ═══════════ 2. 页面切换:内容淡入上浮(navigate() 重建 DOM 时自动触发) ═══════════ */
#main-content > .page-header,
#main-content > .card,
#main-content > .row,
#main-content > .container-fluid {
  animation: ani-page-in .26s var(--motion-easing-standard, ease-out) backwards;
}
#main-content > .card { animation-delay: .05s; }
@keyframes ani-page-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* ═══════════ 3. 云盘:列表入场 / 退场 / 新文件高亮 ═══════════ */

/* 入场:网格卡片错峰上浮(--i 由 JS 注入,封顶 14 × 18ms) */
.drv-card {
  animation: ani-item-in .28s var(--motion-easing-standard, ease-out) backwards;
  animation-delay: calc(var(--i, 0) * 18ms);
  transition: box-shadow .15s ease, border-color .15s ease, transform .15s ease,
              opacity .18s ease, background-color .15s ease;
}
.drv-card:hover { transform: translateY(-2px); }
@keyframes ani-item-in {
  from { opacity: 0; transform: translateY(10px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* 列表行:纯淡入(表格加位移会闪边框)。
   选择器用 #drive-body tr.drv-row 提特异性——否则被 style.css:790
   `.table tbody tr { animation: slideInUp }` 压过,错峰完全不生效 */
#drive-body tr.drv-row {
  animation: ani-row-in .22s ease-out backwards;
  animation-delay: calc(var(--i, 0) * 14ms);
  transition: background-color .12s ease, opacity .18s ease;
}
@keyframes ani-row-in { from { opacity: 0; } to { opacity: 1; } }

/* 退场:删除/还原时先收缩淡出,再刷新列表(JS 加 .drv-out 后等 ~180ms) */
.drv-card.drv-out { opacity: 0; transform: scale(.9); pointer-events: none; }
#drive-body tr.drv-row.drv-out { opacity: 0; pointer-events: none; }

/* 新上传高亮:蓝圈呼吸两下,指给用户"你的文件在这" */
.drv-flash { animation: ani-flash 1.2s ease-out .3s backwards; }
@keyframes ani-flash {
  0%   { box-shadow: 0 0 0 0 rgba(13,110,253,.55); }
  40%  { box-shadow: 0 0 0 6px rgba(13,110,253,.18); }
  70%  { box-shadow: 0 0 0 0 rgba(13,110,253,.35); }
  100% { box-shadow: 0 0 0 0 rgba(13,110,253,0); }
}

/* 星标出现:旋转弹入 */
.drv-star { animation: ani-star-in .35s var(--motion-easing-spring, cubic-bezier(.34,1.56,.64,1)) backwards; }
@keyframes ani-star-in {
  from { opacity: 0; transform: scale(.3) rotate(-90deg); }
  to   { opacity: 1; transform: none; }
}

/* ═══════════ 4. 云盘:浮层 ═══════════ */

/* 右键/⋮ 菜单:轻微缩放淡入(display none→block 时自动重放) */
#drive-ctx { animation: ani-menu-in .14s ease-out; transform-origin: top left; }
@keyframes ani-menu-in {
  from { opacity: 0; transform: scale(.95) translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
#drive-ctx .ctx-item { transition: background-color .1s ease; }

/* 批量选择条:下滑淡入(innerHTML 重建时自动触发) */
.drv-selbar { animation: ani-selbar-in .18s ease-out; }
@keyframes ani-selbar-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* 预览画廊:遮罩淡入 + 内容轻缩放入场 */
#drive-gallery { animation: ani-fade-in .2s ease-out; }
#drive-gallery img, #drive-gallery video, #drive-gallery iframe {
  animation: ani-zoom-in .24s var(--motion-easing-standard, ease-out) backwards;
}
@keyframes ani-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ani-zoom-in {
  from { opacity: 0; transform: scale(.96); }
  to   { opacity: 1; transform: none; }
}

/* 上传进度小卡:右侧滑入 */
#drive-uploads .card { animation: ani-slide-left .25s var(--motion-easing-standard, ease-out); }
@keyframes ani-slide-left {
  from { opacity: 0; transform: translateX(24px); }
  to   { opacity: 1; transform: none; }
}
/* 进度条平滑推进 */
#drive-uploads .progress-bar { transition: width .3s ease; }

/* 拖拽悬停目标:呼吸提示 */
.drv-drop-ok { animation: ani-drop-pulse 1s ease-in-out infinite; }
@keyframes ani-drop-pulse {
  0%, 100% { background-color: rgba(13,110,253,.08); }
  50%      { background-color: rgba(13,110,253,.16); }
}

/* 空状态:淡入 + 图标轻弹 */
#drive-body .text-center.py-5 { animation: ani-fade-in .3s ease-out; }
#drive-body .text-center.py-5 > i { display: inline-block; animation: ani-pop .45s var(--motion-easing-spring, cubic-bezier(.34,1.56,.64,1)) .1s backwards; }

/* 面包屑切换淡入 */
#drive-crumbs { animation: ani-fade-in .2s ease-out; }

/* 撤销 toast:底部上滑 */
#drive-undo { animation: ani-rise .25s var(--motion-easing-spring, cubic-bezier(.34,1.56,.64,1)); }
@keyframes ani-rise {
  from { opacity: 0; transform: translateX(-50%) translateY(16px); }
  to   { opacity: 1; transform: translateX(-50%); }
}
