/* ==========================================================================
   NCP服务器 · 动效库
   包含：逐帧动画(steps)、入场编排、微交互、加载态、装饰性循环动画
   ========================================================================== */

/* ------------------------------------------------------------ 基础关键帧 */
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }

@keyframes fadeUp {
  from { opacity: 0; transform: translate3d(0, 16px, 0) scale(0.985); }
  to { opacity: 1; transform: none; }
}

@keyframes fadeDown {
  from { opacity: 0; transform: translate3d(0, -14px, 0); }
  to { opacity: 1; transform: none; }
}

@keyframes fadeLeft {
  from { opacity: 0; transform: translate3d(22px, 0, 0); }
  to { opacity: 1; transform: none; }
}

@keyframes popIn {
  0% { opacity: 0; transform: scale(0.86); }
  60% { opacity: 1; transform: scale(1.03); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes springUp {
  0% { opacity: 0; transform: translate3d(0, 40px, 0) scale(0.94); }
  70% { opacity: 1; transform: translate3d(0, -4px, 0) scale(1.006); }
  100% { opacity: 1; transform: none; }
}

@keyframes blurIn {
  from { opacity: 0; filter: blur(14px); transform: scale(1.02); }
  to { opacity: 1; filter: blur(0); transform: none; }
}

/* 只用 opacity + transform：二者都能交给合成器。
   一旦在这里写 filter（哪怕是 blur(0)），animation-fill-mode: both 会让整页永久成为滤镜层，
   滚动时每帧都要重新栅格化，滚轮就会明显卡顿。 */
@keyframes pageEnter {
  0% { opacity: 0; transform: translate3d(0, 18px, 0); }
  100% { opacity: 1; transform: none; }
}

@keyframes pageLeave {
  0% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translate3d(0, -12px, 0) scale(0.99); }
}

/* ------------------------------------------------------------ 逐帧(steps) */
/* 逐帧跳动：用于状态指示灯、加载小方块 */
@keyframes frameBlink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}

/* 逐帧打字机：配合 ch 宽度 */
@keyframes typewriter {
  from { width: 0; }
  to { width: var(--tw-ch, 100%); }
}

@keyframes caretBlink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* 逐帧精灵图（横向条带） */
@keyframes spriteStrip {
  from { background-position-x: 0; }
  to { background-position-x: var(--strip-w, -192px); }
}

/* 逐帧像素块堆叠（Logo 逐帧拼装） */
@keyframes blockPlace {
  0% { opacity: 0; transform: translate3d(0, -14px, 0) scale(1.4); }
  40% { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
  70% { transform: scale(1.12, 0.88); }
  100% { opacity: 1; transform: none; }
}

/* 逐帧波形：5 根柱子按帧跳动 */
@keyframes frameBar {
  0% { transform: scaleY(0.28); }
  25% { transform: scaleY(0.85); }
  50% { transform: scaleY(0.45); }
  75% { transform: scaleY(1); }
  100% { transform: scaleY(0.28); }
}

/* 逐帧数字滚动 */
@keyframes digitRoll {
  from { transform: translateY(0.9em); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

/* ------------------------------------------------------------ 循环装饰 */
@keyframes floatSlow {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  33% { transform: translate3d(30px, 22px, 0) scale(1.06); }
  66% { transform: translate3d(-24px, 14px, 0) scale(0.96); }
}

@keyframes floatY {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-8px); }
}

@keyframes breathe {
  0%, 100% { transform: scale(1); opacity: 0.9; }
  50% { transform: scale(1.06); opacity: 1; }
}

@keyframes pulseRing {
  0% { transform: scale(0.8); opacity: 0.7; }
  100% { transform: scale(2.4); opacity: 0; }
}

@keyframes spin { to { transform: rotate(360deg); } }

@keyframes dash {
  from { stroke-dashoffset: var(--dash-from, 314); }
  to { stroke-dashoffset: var(--dash-to, 0); }
}

@keyframes shimmer {
  0% { background-position: -420px 0; }
  100% { background-position: 420px 0; }
}

@keyframes gradientShift {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}

@keyframes wiggle {
  0%, 100% { transform: rotate(0); }
  25% { transform: rotate(-3.5deg); }
  75% { transform: rotate(3.5deg); }
}

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-7px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(3px); }
}

@keyframes successPop {
  0% { transform: scale(0.4); opacity: 0; }
  55% { transform: scale(1.12); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

@keyframes toastIn {
  0% { opacity: 0; transform: translate3d(0, -18px, 0) scale(0.94); }
  100% { opacity: 1; transform: none; }
}

@keyframes toastOut {
  0% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translate3d(0, -14px, 0) scale(0.96); }
}

@keyframes drawerIn {
  0% { transform: translate3d(0, -12px, 0); opacity: 0; }
  100% { transform: none; opacity: 1; }
}

@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@keyframes progressSweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(320%); }
}

@keyframes mapPing {
  0% { transform: scale(0.5); opacity: 0.85; }
  100% { transform: scale(3.2); opacity: 0; }
}

@keyframes scanline {
  0% { transform: translateY(-100%); }
  100% { transform: translateY(1000%); }
}

/* ------------------------------------------------------------ 入场编排 */
[data-anim] { opacity: 0; }
[data-anim].is-in {
  animation: var(--anim-name, fadeUp) var(--anim-dur, 0.72s) var(--spring) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}
[data-anim="fade"] { --anim-name: fadeIn; --anim-dur: 0.9s; }
[data-anim="up"] { --anim-name: fadeUp; }
[data-anim="down"] { --anim-name: fadeDown; }
[data-anim="left"] { --anim-name: fadeLeft; }
[data-anim="pop"] { --anim-name: popIn; --anim-dur: 0.6s; }
[data-anim="spring"] { --anim-name: springUp; --anim-dur: 0.9s; }
[data-anim="blur"] { --anim-name: blurIn; --anim-dur: 0.85s; }

.page-view { animation: pageEnter 0.52s var(--spring) both; }
.page-view.is-leaving { animation: pageLeave 0.24s var(--ease-in) both; }

/* 逐帧文字：每个字符按索引依次出现（配合 JS 设置 --ci） */
.frame-text span {
  display: inline-block;
  opacity: 0;
  animation: frameIn 0.42s var(--spring-soft) both;
  animation-delay: calc(var(--ci, 0) * 42ms);
}
/* 渐变标题被拆分后，渐变需要落到每个字符上 */
.grad-text.frame-text span {
  background: linear-gradient(100deg, var(--accent) 0%, var(--accent-2) 45%, var(--teal) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
@keyframes frameIn {
  0% { opacity: 0; transform: translate3d(0, 0.42em, 0) rotate(3deg) scale(0.9); filter: blur(5px); }
  70% { opacity: 1; transform: translate3d(0, -0.06em, 0) rotate(-0.6deg) scale(1.02); filter: blur(0); }
  100% { opacity: 1; transform: none; }
}

/* 打字机 */
.tw { display: inline-block; overflow: hidden; white-space: nowrap; border-right: 2px solid var(--accent); animation: typewriter 1.6s steps(22, end) 1 both, caretBlink 0.9s steps(1, end) infinite; }
.tw-slow { animation-duration: 3.2s; }

/* 逐帧加载条：4 个方块依次点亮 */
.frame-loader { display: inline-flex; gap: 6px; align-items: flex-end; }
.frame-loader i {
  width: 7px; height: 22px; border-radius: 2px; background: var(--accent);
  animation: frameBar 1.1s steps(5, end) infinite;
  transform-origin: bottom;
}
.frame-loader i:nth-child(2) { animation-delay: 0.12s; background: var(--accent-2); }
.frame-loader i:nth-child(3) { animation-delay: 0.24s; background: var(--teal); }
.frame-loader i:nth-child(4) { animation-delay: 0.36s; background: var(--green); }
.frame-loader i:nth-child(5) { animation-delay: 0.48s; background: var(--orange); }

/* 状态点 */
.status-dot { position: relative; width: 9px; height: 9px; border-radius: 50%; background: var(--green); flex: none; }
.status-dot::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  border: 2px solid currentColor; color: var(--green);
  animation: pulseRing 2.1s var(--ease) infinite;
}
.status-dot.off { background: var(--text-3); }
.status-dot.off::after { border-color: var(--text-3); animation-duration: 3.4s; }
.status-dot.warn { background: var(--orange); }
.status-dot.warn::after { border-color: var(--orange); }

/* 骨架屏 */
.skeleton {
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-hover) 37%, var(--surface-2) 63%);
  background-size: 420px 100%;
  animation: shimmer 1.4s linear infinite;
  min-height: 14px;
}

/* 悬停微交互 */
.hover-lift { transition: transform 0.42s var(--spring), box-shadow 0.42s var(--spring), border-color 0.3s var(--ease); }
.hover-lift:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); border-color: var(--line-strong); }

.tap { transition: transform 0.16s var(--spring); }
.tap:active { transform: scale(0.965); }

/* 波点水波纹 */
.ripple-host { position: relative; overflow: hidden; }
.ripple {
  position: absolute; border-radius: 50%; transform: scale(0); pointer-events: none;
  background: currentColor; opacity: 0.22; animation: rippleOut 0.62s var(--ease) forwards;
}
@keyframes rippleOut { to { transform: scale(2.6); opacity: 0; } }

/* 打卡 / 成功勾选 */
.check-pop { animation: successPop 0.5s var(--spring-soft) both; }

/* 页面滚动进度条
   用 transform: scaleX 而不是 width：width 每次滚动都在触发布局与重绘，
   而且带 transition 会把每一帧的宽度变化排队，长页面滚起来就会一顿一顿。 */
.scroll-progress {
  position: fixed; top: 0; left: 0; height: 2px; width: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2), var(--teal));
  transform: scaleX(0); transform-origin: 0 50%;
  z-index: 90; will-change: transform;
}

/* 抖动反馈 */
.shake { animation: shake 0.42s var(--ease) both; }

/* 静态模式：?still=1 时关闭全部动画（截图 / 低性能设备 / 追求绝对稳定时使用） */
html[data-still] *, html[data-still] *::before, html[data-still] *::after {
  animation: none !important;
  transition: none !important;
}
html[data-still] [data-anim] { opacity: 1 !important; }
html[data-still] .frame-text span { opacity: 1 !important; }

/* 无障碍：尊重系统减弱动效 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  [data-anim] { opacity: 1 !important; }
}
