/* ═══════════════════════════════════════════════════════════════════
   FinSight — Motion Library
   PDF §7 · 8 keyframes (8 motion tokens already in tokens.css).
   Loaded after tokens.css so every animation can reference --ease-* / --dur-*.

   Three rules apply to everything in this file:
     ① Animations > 240ms must be GPU-only (transform / opacity).
     ② Animations > 240ms must be interruptible (no animation-fill-mode that
        traps the element off-screen if user navigates away mid-flight).
     ③ prefers-reduced-motion downgrades all durations to 1ms.
   ═══════════════════════════════════════════════════════════════════ */

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   01 · SPRING-LIFT — hover抬起 + 软阴影
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-spring-lift {
  from { transform: translateY(0); }
  to   { transform: translateY(-1px); }
}
.fs-spring-lift {
  transition:
    transform var(--dur-normal) var(--ease-spring),
    box-shadow var(--dur-normal) var(--ease-spring);
  will-change: transform;
}
.fs-spring-lift:hover { transform: translateY(-1px); }

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   02 · PATH-DRAW — 折线 / SVG 入场
   Apply to <path> elements; set --path-len in JS or CSS to the path length.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-path-draw {
  from { stroke-dashoffset: var(--path-len, 1000); }
  to   { stroke-dashoffset: 0; }
}
.fs-path-draw {
  stroke-dasharray: var(--path-len, 1000);
  animation: fs-path-draw var(--dur-narr) var(--ease-out) both;
}

/* ── Checkmark draw — 校验通过的仪式感 (SVG <path>) ── */
@keyframes fs-checkmark-draw {
  from { stroke-dashoffset: 28; }
  to   { stroke-dashoffset: 0; }
}
.fs-checkmark-draw {
  fill: none;
  stroke: var(--gain);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 28;
  animation: fs-checkmark-draw 600ms var(--ease-out) both;
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   03 · NUMBER-ROLL — KPI 数字滚动
   Uses CSS @property + counter() for pure-CSS interpolation.
   JS sets --n on the element after first paint to trigger the transition.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@property --fs-roll-n {
  syntax: "<integer>";
  inherits: false;
  initial-value: 0;
}
.fs-num-roll {
  counter-reset: roll var(--fs-roll-n);
  font-feature-settings: "tnum" 1, "lnum" 1;
  transition: --fs-roll-n var(--dur-narr) var(--ease-out);
}
.fs-num-roll::after { content: counter(roll); }

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   04 · SHIMMER — 骨架屏 / 进度条扫光
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-shimmer {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(200%); }
}
.fs-shimmer {
  position: relative;
  overflow: hidden;
  background: var(--bg-elevated);
}
.fs-shimmer::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Use the accent at low alpha so the shimmer band is visible against
     both dark and light surface tokens (white-alpha disappears on light). */
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(var(--accent-rgb), 0.10) 50%,
    transparent 100%
  );
  animation: fs-shimmer 1600ms linear infinite;
  pointer-events: none;
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   05 · ORB-DRIFT — 背景光球缓慢漂移（已有 .luxe::before, 这里只导出 keyframe）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-orb-drift {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(2%, -1%, 0) scale(1.04); }
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   06 · STAGGER-IN — 列表 / 柱状元素错峰入场
   Container marks itself as `.fs-stagger-parent`, children get a custom
   property `--i` from JS or CSS counter to time their delay.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-stagger-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.fs-stagger-parent > * {
  animation: fs-stagger-in var(--dur-slow) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 60ms);
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   07 · RING-PULSE — CTA / 告警呼吸圈
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-ring-pulse {
  0%   { box-shadow: 0 0 0 0   rgba(var(--accent-rgb), 0.55); }
  70%  { box-shadow: 0 0 0 10px rgba(var(--accent-rgb), 0);   }
  100% { box-shadow: 0 0 0 0   rgba(var(--accent-rgb), 0);   }
}
.fs-ring-pulse {
  animation: fs-ring-pulse 2s var(--ease-out) infinite;
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   08 · TAB-SLIDE — pro-tabs 底栏 indicator
   Driven by JS-set --x and --w; transition uses spring easing.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.pro-tabs {
  position: relative;  /* Anchor for the indicator. */
}
.pro-tab-indicator {
  position: absolute;
  bottom: -1px;
  left: 0;
  height: 2px;
  width: var(--ind-w, 0px);
  transform: translateX(var(--ind-x, 0px));
  background: var(--accent);
  border-radius: 2px;
  pointer-events: none;
  transition:
    width  var(--dur-slow) var(--ease-spring),
    transform var(--dur-slow) var(--ease-spring),
    opacity var(--dur-fast) var(--ease-out);
  opacity: 0;
}
.pro-tab-indicator.ready { opacity: 1; }
/* Hide the original ::active border underline on .pro-tab so the moving
   indicator becomes the single source of truth. */
.pro-tabs .pro-tab.active { border-bottom-color: transparent; }

/* ── Tab content fade-up wipe (PDF §7.B) ── */
@keyframes fs-tab-content-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.page-tab-content.active {
  animation: fs-tab-content-in var(--dur-normal) var(--ease-out) both;
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   View Transitions — shared elements + page wipe
   PDF §8: dashboard ↔ detail logo + name 共享元素，做镜头推进
   `navigation: auto` opts in to cross-document transitions on same-origin
   navigations (Chromium 126+). Falls back to plain navigation elsewhere.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--dur-slow);
  animation-timing-function: var(--ease-spring);
}
::view-transition-old(fs-hero-logo),
::view-transition-new(fs-hero-logo),
::view-transition-old(fs-hero-name),
::view-transition-new(fs-hero-name) {
  animation-duration: var(--dur-slow);
  animation-timing-function: var(--ease-spring);
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   THEME RIPPLE — clip-path circular reveal on dark/light toggle
   JS toggles `html.vt-ripple` and sets --rx / --ry to click coordinates,
   then triggers a view transition. The new root snapshot is revealed
   from a circle expanding from the click point. (PDF §9 微交互 06)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-ripple-in {
  from { clip-path: circle(0%   at var(--rx, 50%) var(--ry, 50%)); }
  to   { clip-path: circle(150% at var(--rx, 50%) var(--ry, 50%)); }
}
html.vt-ripple::view-transition-old(root) { animation: none; }
html.vt-ripple::view-transition-new(root) {
  animation: fs-ripple-in 600ms var(--ease-out) both;
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   DROP-ZONE MAGNET — dragover 时虚线旋转 + 中心吸 4px
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-border-march {
  to { background-position: 32px 0, -32px 100%, 0 -32px, 100% 32px; }
}
#drop-zone {
  transition:
    background var(--dur-fast) var(--ease-out),
    transform var(--dur-normal) var(--ease-spring),
    border-color var(--dur-fast) var(--ease-out);
}
#drop-zone.over {
  transform: translateY(-4px);
  background:
    linear-gradient(90deg, var(--accent) 50%, transparent 0) repeat-x,
    linear-gradient(90deg, var(--accent) 50%, transparent 0) repeat-x,
    linear-gradient(0deg,  var(--accent) 50%, transparent 0) repeat-y,
    linear-gradient(0deg,  var(--accent) 50%, transparent 0) repeat-y,
    var(--accent-whisper);
  background-size: 16px 1px, 16px 1px, 1px 16px, 1px 16px;
  background-position: 0 0, 0 100%, 0 0, 100% 0;
  animation: fs-border-march 1.4s linear infinite;
  border-color: transparent;
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   TABLE-ROW HOVER 渗色 — 径向 gradient from cursor
   Container row sets --mx / --my via mousemove. Falls back to a flat tint.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.fs-row-hover {
  position: relative;
  transition: background var(--dur-fast) var(--ease-out);
}
.fs-row-hover::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(
    180px circle at var(--mx, 50%) var(--my, 50%),
    var(--accent-whisper),
    transparent 65%
  );
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.fs-row-hover:hover::before { opacity: 1; }

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   AI MEMO STREAMING — per-char fade + cursor blink
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@keyframes fs-char-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.fs-char-in {
  animation: fs-char-in 200ms var(--ease-out) both;
}
@keyframes fs-cursor-blink {
  0%, 50%   { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}
.fs-cursor {
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 2px;
  background: var(--accent);
  vertical-align: -2px;
  animation: fs-cursor-blink 1s step-end infinite;
}

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   reduced-motion — global downgrade
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}
