/* SnapFX · 动效层样式（配合 fx.js）
 *
 * 分三层开关，全部挂在 <html> 上：
 *   html.fx-ready    fx.js 已启动。只有这时才给 [data-fx] 设初始隐藏态 —— JS 没来，内容照样可见。
 *   html.fx-gsap     GSAP 在且没降级：由 JS 接管的那几处（视图进场、列表依次进场、对话气泡、按压），
 *                    这里关掉 motion.css 里对应的 CSS 关键帧，避免同一个元素被动两遍。
 *   html.fx-reduced  降级（系统减少动态效果 / data-motion="off" / 设置里关掉）。
 * html[data-motion="off"] 不依赖 fx.js：纯 CSS 也会把全站动画压成瞬时。
 *
 * --fx-spring / --fx-spring-bouncy 由 fx.js 按同一条弹簧曲线生成 linear()；
 * 浏览器不支持 linear() 时变量不定义，var() 自动落到后面的 cubic-bezier。
 */

/* ---------------------------------------------------------------- 声明式进场 */

html.fx-ready [data-fx]:not(.fx-in):not([data-fx="stagger"]) { opacity: 0; }
html.fx-ready [data-fx="stagger"]:not(.fx-in) > * { opacity: 0; }
html.fx-ready [data-fx],
html.fx-ready [data-fx="stagger"] > * { animation: none; }

@keyframes fx-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
/* 没有 GSAP 时的退路：只淡入 */
html.fx-ready .fx-css-in { animation: fx-fade .3s ease both; }

/* 补间进行中：挂 will-change，并把 transform/opacity 从 CSS 过渡里摘掉，
   否则 GSAP 每帧写的值都会被 CSS 再插值一遍，拖泥带水。补间结束 fx.js 立刻摘掉这个类。 */
.fx-live { will-change: transform, opacity; }
html.fx-ready .fx-live {
  animation: none !important;
  transition-property: color, background-color, border-color, box-shadow, filter, outline-color, fill, stroke !important;
}

/* ---------------------------------------------------------------- JS 接管的 CSS 关键帧 */

html.fx-gsap .view { animation: none; }

html.fx-gsap .chat-you:last-child,
html.fx-gsap .chat-it:last-child,
html.fx-gsap .chat-it:last-child .chat-step,
html.fx-gsap .chat-it:last-child .chat-done,
html.fx-gsap .chat-it:last-child .chat-watch,
html.fx-gsap .chat-it:last-child .chat-next,
html.fx-gsap .chat-it:last-child .chat-confirm,
html.fx-gsap .chat-it:last-child .chat-done .icon,
html.fx-gsap .chat-it:last-child .chat-opt.danger { animation: none; }

/* 与 fx.js 里 AUTO_LIST 一一对应 */
html.fx-gsap .rows > *,
html.fx-gsap .gal > *,
html.fx-gsap .pwall > *,
html.fx-gsap .jrows > *,
html.fx-gsap .lab-grid > *,
html.fx-gsap .lab-shortcuts > *,
html.fx-gsap .home-sugg-list > *,
html.fx-gsap .chat-list > *,
html.fx-gsap .tiles > *,
html.fx-gsap .animal-list > *,
html.fx-gsap .movement-list > *,
html.fx-gsap .cardlist > *,
html.fx-gsap .renlist > *,
html.fx-gsap .trace-list > *,
html.fx-gsap .personal-grid > * { animation: none; }

/* ---------------------------------------------------------------- 反馈：成功 / 出错 / 数字 */

.fx-host { position: relative; }
.fx-ring {
  position: absolute; inset: -3px; z-index: 2;
  border-radius: inherit; pointer-events: none; opacity: 0;
  border: 2px solid var(--green, #2e8b57);
}
.fx-ring-bad { border-color: var(--red, #c0392b); }
.fx-ok { outline: 2px solid var(--green, #2e8b57); outline-offset: 2px; }
.fx-bad { outline: 2px solid var(--red, #c0392b); outline-offset: 2px; }
.fx-counting { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- 底栏：滑动指示器
   fx.js 往 nav.bar 里插一个 .fx-tab-ind，窄屏是顶上一根 3px 青色条，宽屏是胶囊底色；
   原来每个 a.on 自己画的那一份（::before 条 / ::after 下划线 / 胶囊背景）在这时让出来。 */

.fx-tab-ind {
  position: absolute; left: 0; top: 0; z-index: 0;
  width: 0; height: 3px;
  border-radius: 0 0 3px 3px;
  background: var(--accent, #81d8d0);
  transform-origin: 0 50%;
  pointer-events: none;
  opacity: 0;
  transition: opacity .2s ease;
}
.fx-tab-ind.is-on { opacity: 1; }
html.fx-ready:not(.fx-gsap) .fx-tab-ind { transition: opacity .2s ease, transform .3s cubic-bezier(.22, .61, .36, 1); }
html.fx-tabs .bar > a,
html.fx-tabs .bar > button { position: relative; z-index: 1; }
html.fx-tabs .bar a.on::before,
html.fx-tabs .bar a.on::after { display: none; }

@media (min-width: 761px) {
  .fx-tab-ind { border-radius: 999px; background: var(--k-soft2, var(--soft2, #eaecf0)); }
  :root[data-theme="dark"] .fx-tab-ind { background: var(--soft2, #2c323b); }
  html.fx-tabs .bar a.on { background-color: transparent !important; }
}

/* ---------------------------------------------------------------- toast：弹簧落位
   仍然由 app.js 切 .on；这里只换过渡曲线。进场走弹簧，退场快而干脆。 */

html.fx-ready .toast {
  transform: translate3d(-50%, 14px, 0) scale(.94);
  transition: opacity .18s ease, transform .2s cubic-bezier(.4, 0, 1, 1);
}
html.fx-ready .toast.on {
  animation: none;
  transform: translate3d(-50%, 0, 0) scale(1);
  transition: opacity .2s ease, transform .6s var(--fx-spring-bouncy, cubic-bezier(.2, .9, .28, 1.12));
}
@keyframes fx-toast-shake {
  0%, 100% { transform: translate3d(-50%, 0, 0); }
  20% { transform: translate3d(calc(-50% - 6px), 0, 0); }
  40% { transform: translate3d(calc(-50% + 5px), 0, 0); }
  60% { transform: translate3d(calc(-50% - 3px), 0, 0); }
  80% { transform: translate3d(calc(-50% + 2px), 0, 0); }
}
/* 出错的提示落稳之后轻摇一下；不留填充，摇完交回过渡的终值 */
html.fx-ready .toast.on.bad { animation: fx-toast-shake .42s ease-in-out .34s; }

/* ---------------------------------------------------------------- 底部面板：弹簧上滑 */

html.fx-ready .sheet.on .sheet-card {
  transition: transform .6s var(--fx-spring, cubic-bezier(.2, .9, .28, 1.06)), opacity .2s ease;
}

/* ---------------------------------------------------------------- 弹窗：进出都有过渡
   <dialog> 的退场要靠 transition-behavior: allow-discrete + @starting-style；
   不支持的浏览器整块跳过，保留 motion.css 原来的进场关键帧。 */

@supports (transition-behavior: allow-discrete) {
  html.fx-ready dialog.mo,
  html.fx-ready dialog.plugdlg,
  html.fx-ready dialog.lb {
    opacity: 0;
    transform: translate3d(0, 10px, 0) scale(.96);
    transition: opacity .16s ease, transform .18s cubic-bezier(.4, 0, 1, 1),
                overlay .18s allow-discrete, display .18s allow-discrete;
  }
  html.fx-ready dialog.mo[open],
  html.fx-ready dialog.plugdlg[open],
  html.fx-ready dialog.lb[open] {
    animation: none;
    opacity: 1;
    transform: none;
    transition: opacity .2s ease, transform .55s var(--fx-spring, cubic-bezier(.2, .9, .28, 1.06)),
                overlay .2s allow-discrete, display .2s allow-discrete;
  }
  @starting-style {
    html.fx-ready dialog.mo[open],
    html.fx-ready dialog.plugdlg[open],
    html.fx-ready dialog.lb[open] {
      opacity: 0;
      transform: translate3d(0, 16px, 0) scale(.94);
    }
  }
  html.fx-ready dialog.mo::backdrop,
  html.fx-ready dialog.plugdlg::backdrop,
  html.fx-ready dialog.lb::backdrop {
    opacity: 0;
    transition: opacity .2s ease, overlay .2s allow-discrete, display .2s allow-discrete;
  }
  html.fx-ready dialog.mo[open]::backdrop,
  html.fx-ready dialog.plugdlg[open]::backdrop,
  html.fx-ready dialog.lb[open]::backdrop {
    animation: none;
    opacity: 1;
  }
  @starting-style {
    html.fx-ready dialog.mo[open]::backdrop,
    html.fx-ready dialog.plugdlg[open]::backdrop,
    html.fx-ready dialog.lb[open]::backdrop { opacity: 0; }
  }
  /* 窄屏上插件问答是底部抽屉：从下往上滑 */
  @media (max-width: 760px) {
    html.fx-ready dialog.plugdlg { transform: translate3d(0, 40px, 0); }
    @starting-style {
      html.fx-ready dialog.plugdlg[open] { transform: translate3d(0, 60px, 0); }
    }
  }
}

/* ---------------------------------------------------------------- 3D 容器
   尺寸由界面侧 CSS 决定；这里只管画布铺满、首帧前透明、以及没有 WebGL 时的静态回退。 */

.fx-3d { position: relative; overflow: hidden; isolation: isolate; }
.fx-3d > .fx-3d-canvas {
  position: absolute; inset: 0;
  display: block; width: 100%; height: 100%;
  opacity: 0;
  transition: opacity .9s ease;
}
.fx-3d > .fx-3d-canvas.is-live { opacity: 1; }

/* 占位图交接（fx3d.js 的 handoffIn/handoffOut）：画布插进去的那一刻占位图还在，首帧后交叉淡出 */
.fx-3d.fx-3d-handoff::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background-image: var(--fx-ph-img, none);
  background-size: var(--fx-ph-size, contain);
  background-position: var(--fx-ph-pos, center);
  background-repeat: no-repeat;
  opacity: var(--fx-ph-op, 1);
  transition: opacity .7s ease;
}
.fx-3d.fx-3d-handoff-out::after { opacity: 0; }

/* 静态回退（减少动态效果 / 没有 WebGL2 / 省流量）：一圈很淡的青色光晕 + 两颗小圆，
   不动、不耗电；界面侧若给了占位标志，它正好衬在标志后面。 */
.fx-3d.fx-3d-static::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(circle at 71% 30%, rgba(129, 216, 208, .85) 0, rgba(129, 216, 208, .85) 2.2%, rgba(129, 216, 208, 0) 3%),
    radial-gradient(circle at 27% 72%, rgba(120, 128, 138, .35) 0, rgba(120, 128, 138, .35) 2.4%, rgba(120, 128, 138, 0) 3.4%),
    radial-gradient(circle at 50% 50%, rgba(129, 216, 208, .3) 0, rgba(129, 216, 208, .12) 32%, rgba(129, 216, 208, 0) 58%);
}

/* ---------------------------------------------------------------- 全局关闭动效 */

html[data-motion="off"],
html[data-motion="off"] main,
html[data-motion="off"] .chat-canvas { scroll-behavior: auto; }
html[data-motion="off"] *,
html[data-motion="off"] *::before,
html[data-motion="off"] *::after {
  animation-duration: .01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: .01ms !important;
}
html[data-motion="off"] .askhere::after,
html[data-motion="off"] .ring,
html[data-motion="off"] .dots i,
html[data-motion="off"] .cw-bar.indet i,
html[data-motion="off"] .pbar.indet .pbar-fill { animation: none !important; }
html[data-motion="off"] .pbar.indet .pbar-fill { width: 100%; opacity: .35; }
