/* ===================================================================
   立绘动效增强（sprite-motion）—— 让立绘「活起来」
   在 styles.css 的立绘舞台（.sprite-stage / .sprite-figure）之上叠加：
     · 入场特写：切角色 / 出场时镜头推近 + 回弹（一次性，做完不再重播）
     · 鼠标视差 2.5D：立绘随指针轻微转向 / 平移
     · 说话律动：AI 生成中 / 立绘语音播放中，立绘点头呼吸更活跃
     · 随声鼓动：用 Web Audio 实时分析立绘语音音量，驱动立绘随声起伏（--sp-amp）
     · 点击立绘本体：点立绘的摆动区域 → 随机一种动作（弹跳 / 摇摆 / 跳跃 / 点头 / 惊讶抖）+ 随机播放 MP3，
                     动作做完「平稳回到呼吸」，不再重刷入场（不晃眼）
     · 切换交叉淡入：换立绘 / 切发言人时旧图柔和淡出
     · 落地光晕：立绘脚下柔影，说话时脉动
   分层原则（避免 transform 互相打架）：
     视差 / 随声缩放 → 外层 .sprite-stage-inner（CSS 变量驱动）
     入场 / 呼吸 / 说话 / 点击动作 → 内层 .sprite-figure（@keyframes 驱动）
   关键：入场特写做成独立的一次性类 .sm-enter（由 JS 在「立绘新出现」时加、动画完即摘），
        而呼吸 / 说话 / 点击动作各自独立，互不触发对方重播 —— 点击动作结束摘掉 class 即平稳回到呼吸。
   开关：body 上的 sm-idle / sm-parallax / sm-talk 类；settings.spriteMotion 持久化。尊重 prefers-reduced-motion。
   本文件在 styles.css 之后加载，仅做「叠加 / 覆盖」，不改其它源文件。
   =================================================================== */

/* ============ 0. 去掉立绘舞台右上角的「叉号」关闭键（顶栏「立绘」开关已能开关）============ */
#spriteDock .sprite-close { display: none !important; }

/* ============ 1. 外层：鼠标视差 2.5D + 随声缩放（CSS 变量驱动）============ */
#spriteStage .sprite-stage-inner {
  transform:
    perspective(1100px)
    translate3d(calc(var(--sp-x, 0) * 1px), calc(var(--sp-y, 0) * 1px), 0)
    rotateX(calc(var(--sp-rx, 0) * 1deg))
    rotateY(calc(var(--sp-ry, 0) * 1deg))
    scale(calc(1 + var(--sp-amp, 0) * 0.05));
  transform-origin: bottom center;
  transition: transform .16s cubic-bezier(.33, 1, .68, 1);
  will-change: transform;
}
body:not(.sm-parallax) #spriteStage .sprite-stage-inner {
  transform: scale(calc(1 + var(--sp-amp, 0) * 0.05));
}

/* ============ 2. 呼吸 idle（持续轻微起伏，可一键关）============ */
/* 静止态只做原地缩放呼吸，不做位移 / 旋转；否则透明立绘边缘会显得在抖。 */
@keyframes spriteIdleFloat {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.006, 1.012); }
}
/* 注意：入场特写不再写在这里（改用一次性 .sm-enter），所以点击动作做完回到这条只会平稳呼吸、不会重刷入场 */
body.sm-idle #spriteStage .sprite-figure.has-img {
  transform-origin: bottom center;
  animation: spriteIdleFloat 5.8s ease-in-out infinite;
}
body:not(.sm-idle) #spriteStage .sprite-figure.has-img {
  transform-origin: bottom center;
  animation: none;
}

/* ============ 3. 说话律动：AI 生成中 / 立绘语音播放中 → 更活跃的「说话」节奏 ============ */
@keyframes spriteTalk {
  0%   { transform: translateY(0)    scale(1)      rotate(0); }
  18%  { transform: translateY(-3px) scale(1.012)  rotate(.5deg); }
  36%  { transform: translateY(-1px) scale(1.004)  rotate(-.3deg); }
  54%  { transform: translateY(-4px) scale(1.017)  rotate(.35deg); }
  72%  { transform: translateY(-1px) scale(1.005)  rotate(-.25deg); }
  100% { transform: translateY(0)    scale(1)      rotate(0); }
}
body.sm-talk.is-generating  #spriteStage .sprite-figure.has-img,
body.sm-talk.sprite-voicing #spriteStage .sprite-figure.has-img {
  animation: spriteTalk 1.1s ease-in-out infinite;
}

/* ============ 4. 入场特写：一次性（JS 加 .sm-enter，动画结束即摘）============ */
@keyframes spriteCinematicIn {
  0%   { opacity: 0; transform: translateY(54px) scale(1.24); }
  46%  { opacity: 1; transform: translateY(-16px) scale(1.05); }
  68%  { transform: translateY(6px) scale(.988); }
  84%  { transform: translateY(-3px) scale(1.008); }
  100% { transform: translateY(0) scale(1); }
}
#spriteStage .sprite-figure.has-img.sm-enter {
  animation: spriteCinematicIn .8s cubic-bezier(.18, .82, .25, 1) both !important;
}

/* ============ 5. 点击立绘：多种动作随机一种，起止都在原位 → 做完平稳回到呼吸（不晃眼）============ */
@keyframes spritePoke {
  0%   { transform: translateY(0)    scale(1)          rotate(0); }
  15%  { transform: translateY(-13px) scale(1.045,.955) rotate(-2.4deg); }
  32%  { transform: translateY(0)    scale(.975,1.03)  rotate(2.2deg); }
  48%  { transform: translateY(-8px) scale(1.02,.99)   rotate(-1.5deg); }
  64%  { transform: translateY(0)    scale(.995,1.01)  rotate(1deg); }
  80%  { transform: translateY(-3px) scale(1.005)      rotate(-.5deg); }
  100% { transform: translateY(0)    scale(1)          rotate(0); }
}
@keyframes spriteWobble {
  0%, 100% { transform: rotate(0); }
  18%  { transform: rotate(-4deg); }
  36%  { transform: rotate(3deg); }
  54%  { transform: rotate(-2.2deg); }
  72%  { transform: rotate(1.2deg); }
  88%  { transform: rotate(-.5deg); }
}
@keyframes spriteJump {
  0%   { transform: translateY(0)   scale(1); }
  22%  { transform: translateY(-30px) scale(1.03,.97); }
  44%  { transform: translateY(0)   scale(.96,1.05); }
  60%  { transform: translateY(-11px) scale(1.01,.99); }
  78%  { transform: translateY(0)   scale(.99,1.02); }
  100% { transform: translateY(0)   scale(1); }
}
@keyframes spriteNod {
  0%   { transform: translateY(0)   scale(1); }
  28%  { transform: translateY(-7px) scale(1.012); }
  50%  { transform: translateY(3px)  scale(.99); }
  72%  { transform: translateY(-3px) scale(1.004); }
  100% { transform: translateY(0)   scale(1); }
}
@keyframes spriteSurprise {
  0%, 100% { transform: translateX(0) rotate(0); }
  12%  { transform: translateX(-6px) rotate(-1.6deg); }
  26%  { transform: translateX(6px)  rotate(1.4deg); }
  40%  { transform: translateX(-4px) rotate(-1deg); }
  54%  { transform: translateX(4px)  rotate(.8deg); }
  70%  { transform: translateX(-2px) rotate(-.4deg); }
  85%  { transform: translateX(1px); }
}
#spriteStage .sprite-figure.has-img.sm-act-poke     { animation: spritePoke     .7s  cubic-bezier(.34,1.56,.64,1) both !important; }
#spriteStage .sprite-figure.has-img.sm-act-wobble   { animation: spriteWobble   .8s  ease both !important; }
#spriteStage .sprite-figure.has-img.sm-act-jump     { animation: spriteJump     .72s cubic-bezier(.3,1.3,.5,1) both !important; }
#spriteStage .sprite-figure.has-img.sm-act-nod      { animation: spriteNod      .62s ease both !important; }
#spriteStage .sprite-figure.has-img.sm-act-surprise { animation: spriteSurprise .7s  ease both !important; }

/* ============ 6. 落地光晕：立绘脚下柔影，说话时轻微脉动 ============ */
#spriteStage .sprite-stage-inner::after {
  content: "";
  position: absolute; left: 50%; bottom: 52px; transform: translateX(-50%);
  width: 56%; height: 22px; border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, .26), transparent 70%);
  filter: blur(3px); opacity: .45; pointer-events: none; z-index: 0;
  transition: opacity .3s ease, width .3s ease;
}
body.sm-talk.is-generating  #spriteStage .sprite-stage-inner::after,
body.sm-talk.sprite-voicing #spriteStage .sprite-stage-inner::after {
  animation: spriteAura 1.1s ease-in-out infinite;
}
@keyframes spriteAura {
  0%, 100% { opacity: .45; width: 56%; }
  50%      { opacity: .3;  width: 64%; }
}

/* ============ 7. 切换交叉淡入：换立绘 / 切发言人时旧立绘柔和淡出（JS 生成 .sprite-ghost）============ */
.sprite-ghost {
  position: fixed; z-index: 4; pointer-events: none;
  background-repeat: no-repeat; background-position: bottom left; background-size: auto 100%;
  -webkit-mask-image: linear-gradient(to bottom, #000 92%, transparent);
  mask-image: linear-gradient(to bottom, #000 92%, transparent);
  filter: drop-shadow(0 14px 26px rgba(0, 0, 0, .30));
  transform-origin: bottom center;
  animation: spriteGhostOut .5s cubic-bezier(.4, 0, .2, 1) forwards;
}
@keyframes spriteGhostOut {
  from { opacity: .92; transform: translateY(0) scale(1); }
  to   { opacity: 0;   transform: translateY(-8px) scale(1.015); }
}

/* ============ 8. 点击热区：完全透明的竖长方形，正好罩住立绘身体（看不见、不挡右侧正文）============ */
/* 立绘舞台是 left:310px 起、宽 196~300、高 600~760 的竖窄条，立绘图靠左下、高度撑满。
   故热区做成贴左的竖长方形：顶部留一点、底部避开缩略条、宽度盖住竖图主体。纯透明，无边框 / 阴影 / outline。 */
.sprite-hit {
  pointer-events: auto; cursor: pointer;
  position: absolute; left: 0; top: 6%; width: 84%; bottom: 58px;
  z-index: 5;                       /* 低于群聊 / 多图缩略条(z40)，缩略条仍可正常点 */
  margin: 0; padding: 0; border: 0; outline: 0;
  background: transparent; box-shadow: none;
  -webkit-tap-highlight-color: transparent;
}

/* ============ 9. 无障碍：尊重「减少动态效果」系统偏好 ============ */
@media (prefers-reduced-motion: reduce) {
  #spriteStage .sprite-stage-inner { transition: none; transform: none !important; }
  #spriteStage .sprite-stage-inner::after { animation: none !important; }
  body #spriteStage .sprite-figure.has-img { animation: none !important; }
  #spriteStage .sprite-figure.has-img.sm-enter,
  #spriteStage .sprite-figure.has-img.sm-act-poke,
  #spriteStage .sprite-figure.has-img.sm-act-wobble,
  #spriteStage .sprite-figure.has-img.sm-act-jump,
  #spriteStage .sprite-figure.has-img.sm-act-nod,
  #spriteStage .sprite-figure.has-img.sm-act-surprise { animation: none !important; }
  .sprite-ghost { animation: none; opacity: 0; }
}
