/* 微交互(聊天外壳):入场/退场/按压/回应/提示条。2026-10-25 微交互审计后新增,加载在所有样式之后。 */

/* ---------- 10. 气泡入场:有方向的轻弹(发出从右下、收到从左下)----------
   只作用于真正新来的消息:历史加载、回声替换乐观气泡都会在行上写 style="animation:none" */
#chat-messages .message-row.sent { transform-origin: right bottom; animation: msgIn .22s cubic-bezier(.2, .8, .2, 1); }
#chat-messages .message-row.received { transform-origin: left bottom; animation: msgIn .22s cubic-bezier(.2, .8, .2, 1); }
html[data-theme="island"] #chat-messages .message-row.sent,
html[data-theme="island"] #chat-messages .message-row.received { animation: msgIn .26s cubic-bezier(.34, 1.4, .64, 1); }
@keyframes msgIn {
  from { opacity: 0; transform: translateY(10px) scale(.96); }
  to   { opacity: 1; transform: none; }
}

/* ---------- 1. 回执:乐观气泡下先占位「发送中…」,回声后换字只淡入,不改高度 ---------- */
#chat-messages .read-receipt.is-sending { opacity: .72; }
html:not([data-theme="island"]):not([data-scheme="dark"]) #chat-messages .read-receipt.is-sending { opacity: 1; }   /* 浅色下 .72 只有 2.9:1 */
#chat-messages .read-receipt.rc-swap { animation: rcSwap .12s ease-out; }
@keyframes rcSwap { from { opacity: .4; } to { opacity: 1; } }

/* ---------- 2. 表情回应:只有新增的 chip 弹出;已有的数字轻跳;下边距平滑让位 ---------- */
#chat-messages .message-row { transition: margin-bottom .2s cubic-bezier(.2, .8, .2, 1); }
.reaction-chip { animation-duration: .26s; animation-timing-function: cubic-bezier(.34, 1.56, .64, 1); }
.reaction-chip.chip-bump .reaction-count { display: inline-block; animation: chipCount .16s ease-out; }
@keyframes chipCount { 50% { transform: scale(1.15); } }

/* ---------- 3. 长按:按住慢慢压下;菜单关闭淡出缩小;克隆体回落 ---------- */
#chat-messages .message-row .message-bubble { transition: transform .16s cubic-bezier(.2, .8, .2, 1); }
#chat-messages .message-row.pressing .message-bubble { transform: scale(.97); transition: transform .32s cubic-bezier(.3, 0, .5, 1); }
html[data-theme="island"] .msg-action-menu:not(.closing) { animation: actionMenuPop .26s cubic-bezier(.34, 1.56, .64, 1); }
.msg-action-menu.closing,
html[data-theme="island"] .msg-action-menu.closing { animation: actionMenuOut .14s cubic-bezier(.4, 0, 1, 1) forwards; pointer-events: none; }
.msg-action-overlay.closing { animation: actionFadeOut .16s ease-out forwards; pointer-events: none; }
@keyframes actionMenuOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(.96); } }
@keyframes actionFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes bubbleSettle { from { transform: scale(1.02); } to { transform: scale(1); box-shadow: none; } }

/* ---------- 4. 提示条:同一条换字时轻轻一顶;退场下滑 6px + 淡出 ---------- */
.toast-msg { transition: opacity .2s cubic-bezier(.4, 0, 1, 1), transform .2s cubic-bezier(.4, 0, 1, 1); }
.toast-msg.toast-fade { opacity: 0; transform: translateX(-50%) translateY(6px); }
.toast-msg.toast-bump { animation: toastBump .16s ease-out; }
@keyframes toastBump { from { transform: translateX(-50%) scale(1.04); } to { transform: translateX(-50%) scale(1); } }

/* ---------- 5. 新消息胶囊:计数变化轻跳;收起时往下退 ---------- */
#btn-new-msg.pill-bump { animation: pillBump .2s ease-out; }
#btn-new-msg.pill-out { animation: pillOut .16s cubic-bezier(.4, 0, 1, 1) forwards; pointer-events: none; }
@keyframes pillBump { 50% { transform: translateX(-50%) scale(1.08); } }
@keyframes pillOut { to { opacity: 0; transform: translateX(-50%) translateY(10px); } }

/* ---------- 6. 在线胶囊:第一下就有按下反馈(等双击判定的 300ms 藏在里面);账号面板收起 ---------- */
.chat-profile-card.presence-only .presence-btn { transition: transform .12s ease-out; }
.chat-profile-card.presence-only .presence-btn.arming { transform: scale(.94); }
.chat-profile-card.presence-only .presence-btn.arming .status-dot { transform: scale(1.3); transition: transform .12s ease-out; }
html[data-theme="island"] .chat-profile-card.presence-only .presence-btn.arming { transform: translateY(2px); box-shadow: 0 1px 0 0 #d9ceb6; }
#switch-modal.closing { animation: actionFadeOut .18s ease-out forwards; pointer-events: none; }
#switch-modal.closing .switch-card { animation: cardSink .18s cubic-bezier(.4, 0, 1, 1) forwards; }
@keyframes cardSink { to { transform: translateY(12px); } }

/* ---------- 13. 设置开关:按下时滑块拉宽,松手弹回 ---------- */
.settings-toggle::after { transition: transform .25s cubic-bezier(.3, 1.4, .6, 1), width .2s ease; }
.settings-toggle:active::after { width: 28px; }
.settings-toggle:checked:active::after { transform: translateX(12px); }
html[data-theme="island"] .settings-toggle:active::after { width: 26px; }
html[data-theme="island"] .settings-toggle:checked:active::after { transform: translate(16px, -50%); }

/* ---------- 15. 默认皮肤发送键弹出更灵 ---------- */
html:not([data-theme="island"]) .send-btn { transition: transform .18s cubic-bezier(.34, 1.56, .64, 1), opacity .15s; }

/* ---------- 12. 键盘焦点:统一的品牌色焦点环(动森保留自己的虚线风格) ---------- */
html:not([data-theme="island"]) :where(button, [tabindex], a, .settings-toggle, [contenteditable="true"]):focus-visible {
  outline: 2px solid var(--accent, #2F7D73); outline-offset: 2px;
}
html[data-theme="island"] :where(button, [tabindex], a, .settings-toggle):focus-visible {
  outline: 3px dashed var(--accent-text, #11A89B); outline-offset: 2px;
}
#message-input:focus-visible { outline: none; }   /* 输入框有自己的聚焦样式,不再套一圈 */

/* ---------- 11. 减弱动态效果:聊天核心动画只做淡入淡出 ---------- */
@media (prefers-reduced-motion: reduce) {
  /* 历史加载/回声替换的行内联了 animation:none,这里的 !important 会盖过它 → 用 :not([style*=animation]) 排除 */
  #chat-messages .message-row.sent:not([style*="animation"]), #chat-messages .message-row.received:not([style*="animation"]),
  html[data-theme="island"] #chat-messages .message-row.sent:not([style*="animation"]), html[data-theme="island"] #chat-messages .message-row.received:not([style*="animation"]),
  .toast-msg, .msg-action-menu, html[data-theme="island"] .msg-action-menu:not(.closing), .msg-action-overlay,
  #btn-new-msg, .btn-unread-top, .modal-overlay, .modal-card, .switch-card, .overlay-panel, .reaction-chip {
    animation: fadeOnly .12s ease-out !important;
  }
  #btn-new-msg.pill-out, .msg-action-menu.closing, .msg-action-overlay.closing, #switch-modal.closing { animation: fadeOut .12s ease-out forwards !important; }
  #chat-messages .message-row.pressing .message-bubble { transform: none; }
  .toast-msg.toast-fade { transform: translateX(-50%); }
  .reaction-chip.chip-bump .reaction-count { animation: none; }
}
@keyframes fadeOnly { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }

/* 乐观气泡的文字按真实消息一样排成块:inline 时行盒吃气泡的行高,动森里比回声行高 3.5px,回声替换会跳 */
#chat-messages .message-row[id^="temp-"] .msg-sending-text { display: block; }

/* 动森 toast 的 bottom 带 !important,会盖掉「让到花生提示条上方」;这里用更高特异性补上(两套皮肤一致) */
html[data-theme="island"] body.pet-banner-on:not(.keyboard-open):not(.ex-sheet-open) .toast-msg { bottom: calc(var(--pet-banner-h, 96px) + 12px) !important; }
html[data-theme="island"] body.keyboard-open .toast-msg { top: calc(env(safe-area-inset-top, 0px) + 64px) !important; bottom: auto !important; }

/* 发送键收起:先播完缩小,再 visibility:hidden(Tab 不再落到看不见的按钮上);展开时立即可见 */
#btn-send.hidden-btn { transition: transform .18s cubic-bezier(.4, 0, 1, 1), opacity .15s, visibility 0s linear .18s; }

/* 减弱动态效果:长按浮起的气泡副本不做放大(副本的 animation 写在行内,需 !important) */
@media (prefers-reduced-motion: reduce) { body > .message-bubble { animation: none !important; } }

/* 新消息胶囊跟随输入栏实际高度(JS 写 --composer-h),兜底含底部安全区 */
#btn-new-msg { bottom: calc(var(--composer-h, calc(72px + env(safe-area-inset-bottom, 0px))) + 10px); }
/* 胶囊随输入区高度移动时平滑过渡(不再瞬跳);减弱动态效果时不过渡 */
#btn-new-msg { transition: bottom .18s cubic-bezier(.2, .8, .2, 1); }
@media (prefers-reduced-motion: reduce) { #btn-new-msg { transition: none; } }

/* ================= craft2:动效参数统一(审计 #18) =================
   面板/弹窗:入场 --dur-m + --ease-out,退场 --dur-s + --ease-in;像素世界(花生/花/世界)保留 steps() 逐帧,不走这里 */
:root {
  --dur-xs: 120ms; --dur-s: 180ms; --dur-m: 240ms; --dur-l: 320ms;
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --ease-in: cubic-bezier(.4, 0, 1, 1);
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
}
.overlay-panel { animation-duration: var(--dur-m); animation-timing-function: var(--ease-out); }
.modal-card { animation-duration: var(--dur-m); animation-timing-function: var(--ease-out); }
.gd-panel { animation-duration: var(--dur-l); animation-timing-function: var(--ease-out); }
.gd-overlay.closing .gd-panel { animation-duration: var(--dur-s); animation-timing-function: var(--ease-in); }

/* ================= craft2:解锁过渡(审计 #12) =================
   真锁屏立即隐藏(逻辑不变),由去掉 id 的视觉副本 .lock-ghost 淡出 + 轻放大;聊天页从微缩淡入接上 */
.lock-screen.lock-ghost { pointer-events: none; animation: lockOut var(--dur-m) var(--ease-in) forwards; }
@keyframes lockOut { to { opacity: 0; transform: scale(1.03); } }
#page-chat.just-unlocked .chat-layout { animation: chatIn var(--dur-l) var(--ease-out); }
@keyframes chatIn { from { opacity: .6; transform: scale(.985); } to { opacity: 1; transform: none; } }
/* PIN 输错:三次 6px 横抖(原 inputShake .4s ease 更软) */
.pin-dots.shake { animation: pinShake .32s cubic-bezier(.36, .07, .19, .97); }
@keyframes pinShake { 0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-6px); } 40%, 80% { transform: translateX(6px); } }

/* ================= craft2:语音气泡(审计 #11) =================
   每条波形不同(voice.js 按 id 生成);播放进度 = 已播的柱子点亮,不再用一块灰矩形;倍速做成安静的小胶囊 */
.vp-wave i { opacity: .38; transition: opacity var(--dur-xs) linear; }   /* 未播的柱子再实一点,和已播(1)仍分得开(v558 审 #26) */
.vp-wave i.on { opacity: 1; }
.vp-fill { background: transparent !important; opacity: 0 !important; }
.voice-play.playing .vp-wave i { animation: none; }
.vp-speed {
  border: 0; opacity: 1;
  background: color-mix(in srgb, currentColor 12%, transparent);
  border-radius: 999px; min-width: 36px; padding: 5px 9px; min-height: 28px;
  font-variant-numeric: tabular-nums; font-weight: 700;
}
.vp-speed:active { background: color-mix(in srgb, currentColor 20%, transparent); }

@media (prefers-reduced-motion: reduce) {
  .lock-screen.lock-ghost { display: none; }
  #page-chat.just-unlocked .chat-layout { animation: none; }
  .pin-dots.shake { animation: none; }
  .vp-wave i { transition: none; }
}

/* ===== 登录 / PIN 精修(auth 审计) ===== */
/* 隧道慢时验证中:实心圆点轻轻呼吸 */
.pin-dots.is-verifying span.filled { animation: pinPulse 1s ease-in-out infinite; }
@keyframes pinPulse { 50% { opacity: .4; } }
/* 登录/身份/设 PIN 三页之间淡入(只动透明度:给页面加 transform 会改变内部 fixed 浮层的定位基准)。
   锁屏出现保持瞬时,不加入场动画 */
#page-login.active .login-overlay,
#page-identity.active .identity-container,
#page-pin-setup.active .login-overlay { animation: authIn var(--dur-m, 240ms) var(--ease-out, cubic-bezier(.22, 1, .36, 1)); }
@keyframes authIn { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .pin-dots.is-verifying span.filled { animation: none; opacity: .6; }
  #page-login.active .login-overlay,
  #page-identity.active .identity-container,
  #page-pin-setup.active .login-overlay { animation: none; }
  .lock-unread { animation: none !important; }
}

/* 语音波形柱子跟气泡的墨色走:自己发的(阳光黄)用 #3A2A05,对方的(晴空蓝)用 #13314D;深色/动森各取气泡文字色 */
.message-row.sent .vp-wave i { background: var(--mine-ink, #3A2A05); }
.message-row.received .vp-wave i { background: var(--theirs-ink, #13314D); }
html[data-theme="island"] .message-row .vp-wave i { background: var(--isl-ink, #725d42); }
html[data-theme="island"] #chat-messages .read-receipt.is-sending { opacity: 1; }   /* 动森 .72 只有 3.96:1 */
