/* ============================================================================
 * motion.css —— 青志社团 · 动效体系（2026-10）
 * ----------------------------------------------------------------------------
 * 新建文件，**不改动 styles.css / v797.css 任何既有行**（项目铁律：追加式）。
 * 加载位置：index.html 里放在 assets/v797.css 之后。
 *
 * 【目标】让界面"更灵动"，但**克制** —— 灵动不等于花哨，更不等于卡。
 *
 * 【5 档时长（禁止自定义）】
 *   T1 120ms 微反馈（hover / 按下 / 涟漪 / tab 指示条）
 *   T2 200ms 元素进出（toast / 遮罩 / 空态 / 数据刷新）
 *   T3 280ms 视图切换
 *   T4 240ms 弹窗·抽屉（比切页短：弹窗是"被召唤"）
 *   T5 360ms 大位移强调（每页最多 1 处）
 *
 * 【4 条固定缓动（禁止各处随手写 cubic-bezier）】
 *   --mo-ease-in  cubic-bezier(.16,1,.3,1)   进入：快出慢收，干脆落位
 *   --mo-ease-out cubic-bezier(.4,0,1,1)     退出：慢入快走，干脆消失
 *   --mo-ease-back cubic-bezier(.34,1.3,.64,1) 同位置小范围移动（指示条）
 *   纯透明度用 ease
 *
 * 【⛔ 性能红线（21 视图 + 季节粒子是真约束）】
 *   只动 transform / opacity
 *   禁动 width / height / top / left / margin / padding（触发布局重排）
 *   表格 tr/td、正文、输入框内文字、数字 → **只淡入，绝不位移**
 *
 * 【错峰分档（实测节点数驱动）】
 *   节点 < 100 → 正常错峰
 *   节点 ≥ 100 → 只错峰前 8 个
 *   节点 ≥ 400（settings 546 / data 484）→ 整体只淡入，**不错峰**
 *
 * 【必须守住的既有设计】
 *   1. render._lastView：入场动画只在**真正切页**时播，15 秒轮询不重放
 *   2. .toast-root必须 pointer-events:none（v73 历史修复：toast 曾挡住登录按钮）
 *   3. .modal.dragging 拖拽中必须 transition:none（否则跟手性被破坏）
 *   4. .mascot-buddy 的 pointer-events 结构不动（D4 缺陷修复）
 * ========================================================================== */

/* ============================================================================
 * 时长 / 缓动变量（放在 :root，子元素全部继承）
 * ========================================================================== */
:root {
  --mo-t1: 120ms;
  --mo-t2: 200ms;
  --mo-t3: 280ms;
  --mo-t4: 240ms;
  --mo-t5: 360ms;

  --mo-ease-in:  cubic-bezier(.16, 1, .3, 1);
  --mo-ease-out: cubic-bezier(.4, 0, 1, 1);
  --mo-ease-back: cubic-bezier(.34, 1.3, .64, 1);

  /* 错峰参数：可被三套皮肤覆盖 */
  --mo-stagger-step: 40ms;
  --mo-stagger-cap: 200ms;
}
/* ① 青灰·紧：行高36px、元素最多 → 错峰收紧，否则 200 人列表等太久 */
[data-skin="slate"] {
  --mo-stagger-step: 24ms;
  --mo-stagger-cap: 120ms;
}


/* ============================================================================
 * 关键帧
 * ========================================================================== */

/* 视图进场：位移 8px → **14px** + 轻微缩放（比原 10px 更有"落位感"） */
@keyframes moViewIn {
  from { opacity: 0; transform: translateY(14px) scale(.994); }
  to   { opacity: 1; transform: none; }
}
/* 元素进场 */
@keyframes moIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
/* 仅淡入 —— **表格 / 正文 / 输入框内文字 / 数字 只能用这个** */
@keyframes moFade {
  from { opacity: .55; }
  to   { opacity: 1; }
}
/* 数据局部刷新：淡入幅度更小，避免"闪一下" */
@keyframes moRefresh {
  from { opacity: .7; }
  to   { opacity: 1; }
}
/* 弹窗进场 */
@keyframes moModalIn {
  from { opacity: 0; transform: translateY(12px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
/* 遮罩淡入 */
@keyframes moFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* 统一退出 */
@keyframes moFadeOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}
/* toast 从下方轻浮（只能纵向，⛔ 禁止水平位移） */
@keyframes moToastIn {
  from { opacity: 0; transform: translateY(10px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
/* 抽屉：沿用既有 sheetUp 的写法，这里补退出 */
@keyframes moSheetOut {
  from { transform: translateY(0); }
  to   { transform: translateY(100%); }
}


/* ============================================================================
 * ① 视图切换
 *
 * ⚠️ **切皮肤时不会重播任何动画** —— 这是正确行为，不是 bug。
 *  原因：切皮肤只改<html data-skin>，本文件里所有动画选择器用的都是
 *  `.mo-view-in` / `#main.mo-anim-in` 这些**类名**，切皮肤不会改变它们
 *  ⇒ 浏览器只重算样式变量的值，已播放完的 animation **不会重播**。
 *  这正是我们要的：用户是在"换外观"，不是在"导航"，不该再播一遍进场动效。
 *  （实测确认：本文件没有一处用 :not(...) 依赖 class 状态，
 *    所以不存在"class 没变导致动画该不该播"的隐患。）
 *
 * ⚠️ **不做左右方向性**：21 个视图是**平级 tab**，无层级关系。
 *    硬套"从右往左=前进"会让用户误以为存在上下级；且移动端 5 tab 也是平级，
 *    会出现"同一视图在不同设备上方向不同"的矛盾。
 *    ⇒ 保持统一向上淡入，靠**位移幅度 + 曲线**制造灵动。
 *
 * ⚠️ **不做退出动效**：视图切换是"换内容"，加退出会延迟响应、反而变慢。
 *
 * ⚠️ 必须配合 app.js 的 render._lastView 判断：**只在真正切页时**加这些类，
 *    轮询/刷新不重放（这是 v61 刻意做的优化，本文件不破坏它）。
 * ========================================================================== */
/* ⚠️ 覆盖机制说明（防后续误改）：
 *旧的 .view.view-in{animation:viewIn .26s cubic-bezier(.22,.61,.36,1)}
 * 仍在 styles.css:2076，**本文件不删它**（项目铁律：不改既有行）。
 * 本规则与它 **特异性相同 (0,2,0)**，靠 **motion.css 加载更靠后** 胜出。
 *
 * 这要求 motion.css 的<link> 必须在 styles.css **之后**（接入说明已注明顺序）。
 * 并且 app.js 侧应**移除**旧的 view-in 类（只留 mo-view-in），避免两条规则同时命中：
 *   v.classList.remove('view-in','mo-view-in'); void v.offsetWidth;
 *   v.classList.add('mo-view-in');
 *即便旧类仍在，本规则也会因"后加载"而胜出 —— 但**移除更干净**。
 *
 * ⚠️ animation是**简写**，必须完整写全（name + duration + delay/fill-mode），
 *    只写其中一项会把它余下的属性重置为初始值。
 */
.view.mo-view-in {
  animation: moViewIn var(--mo-t3) var(--mo-ease-in) both;
}
