/* 图片 / 视频 / 语音消息，以及阅后即焚查看器。 */
/* ---------- 图片 / 视频消息 ---------- */
.b.media{padding:0;background:transparent;box-shadow:none;max-width:min(72%,360px)}
/* 媒体气泡只是个容器，底色必须保持 transparent。
   `.m.in .b` / `.m.out .b`（特指度 0,3,0）比上面的 `.b.media`（0,2,0）更具体，
   会把品牌渐变重新刷回容器上；己方容器又被 `.m.out .b` 改成 6px 的右上角，
   而内层 .gone/img/video 是 14px 圆角，盖不满这个角——品牌色就从角上漏出来，
   表现为「灰占位气泡右上角挂一块紫色楔形」（换强调色就换成别的颜色）。
   `.b.recalled` 当年是用 !important 打的同类补丁，这里改用更高的特指度解决。
   特指度 (0,5,0) 高于 .m.out .b，:not(.voice) 把语音气泡排除掉——
   它的渐变底是设计要的（见下方 .m.out .b.media.voice）。 */
.m.in .b.media:not(.voice),.m.out .b.media:not(.voice){background:transparent}
/* 图片用紧凑缩略图（类似微信）：限制在约 160px 见方内，等比缩放，点击进 lightbox 看大图。 */
.b.media img{display:block;width:min(100%,160px);height:min(160px,32vh);object-fit:cover;border-radius:14px;background:#0d0f12;box-shadow:var(--sh-sm);cursor:zoom-in}
.b.media video{display:block;max-width:100%;max-height:min(340px,42vh);width:min(100%,300px);border-radius:14px;background:#0d0f12;box-shadow:var(--sh-sm)}
.b.media .gone{min-width:140px;padding:16px 18px;border-radius:14px;text-align:center;font-size:13.5px;color:var(--mut);background:var(--fill);border:1px dashed var(--line-2)}
.b.media .veil{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;min-width:158px;min-height:92px;padding:18px 16px;border-radius:14px;background:var(--veil-bg);color:var(--veil-tx);font-size:12.5px;line-height:1.5;text-align:center;cursor:pointer;box-shadow:var(--sh-sm)}
.m.out .b.media .veil{background:var(--veil-out-bg);color:var(--veil-out-tx)}
.b.media .veil svg{width:22px;height:22px;opacity:.9}
/* 语音气泡：与普通文字气泡同一套外观——四角统一 16px 圆角、无尖角尾巴，
   底色直接沿用气泡变量（对方 --bub-in / 己方 --grad-out），因此换强调色自动跟随。
   宽度不设下限，短语音随 58px 最小波形收窄；上限 340px，
   按最宽内容（141px 波形 + 24px 播放键 + 时长 + 间距 + padding ≈ 310px）留足余量。 */
.b.media.voice{position:relative;padding:9px 12px;background:var(--bub-in);box-shadow:var(--sh-sm);max-width:min(100%,340px);border-radius:16px;transition:background .28s ease}
/* layout.css 的 .m.in .b / .m.out .b 会把朝向头像的那个顶角改成 6px（文字气泡的尖角），
   语音气泡不要这个尖角：用更高一级特指度把四角拉回统一 16px。 */
.m .b.media.voice,.m.in .b.media.voice,.m.out .b.media.voice{border-radius:16px}
.m.out .b.media.voice{background:var(--grad-out)}
/* 播放中高亮：对方叠一层强调色淡染（--primary-l 跟随强调色），己方叠一层压深蒙层，
   都是叠在原底色之上，换任何强调色都协调；280ms 淡入淡出 */
.b.media.voice.playing{background:linear-gradient(var(--vi-hl-in),var(--vi-hl-in)),var(--bub-in)}
.m.out .b.media.voice.playing{background:linear-gradient(var(--vi-hl-out),var(--vi-hl-out)),var(--grad-out)}
/* 行内节奏：播放键 → 波形 → 时长，统一 9px 间距、垂直居中 */
.vf{display:flex;align-items:center;gap:9px;min-width:0;cursor:pointer;transition:transform .1s}
/* 点击反馈：气泡轻微缩小 0.96，100ms 回弹 */
.vf:active{transform:scale(.96)}
/* 己方消息镜像：时长 → 波形 → 播放键（DOM 顺序不变，显示层反转，与原生 QQ 一致） */
.m.out .vf{flex-direction:row-reverse}
.vf audio{display:none}
/* 播放键：盒子就是图标尺寸（24px），44px 热区由 ::after 向外扩，不占布局、不留多余空隙。
   旧写法用 padding:10px 把 44px 热区塞进盒子，图标到波形会多出 20px 视觉间距。 */
.vp{position:relative;width:24px;height:24px;flex:none;padding:0;border:none;background:transparent;color:var(--vi-ic-in);display:flex;align-items:center;justify-content:center}
.vp::after{content:'';position:absolute;left:-10px;right:-10px;top:-10px;bottom:-10px}  /* 44×44 */
.vp svg{width:24px;height:24px}
.m.out .vp{color:var(--vi-ic-out)}
.vp:disabled{color:var(--vi-mut);cursor:default}
/* 波形：竖向圆角柱（3px 宽、3px 间距、柱顶圆角 2px），容器高 28px；
   柱数与宽度由 JS 按可用宽度计算（窄波形柱少、满宽柱多，密度均匀，封顶 24 柱），
   宽度由 JS 写入 --vw（58-141px 随时长伸缩）。
   允许收缩（flex:0 1 auto + min-width 58px）：窄屏 72% 容器装不下最宽波形时，
   柱子跟着挤窄而不是探出气泡外。 */
.vw{flex:0 1 auto;min-width:58px;display:flex;align-items:center;gap:3px;width:var(--vw,58px);height:28px;color:var(--vi-wv-in);position:relative;touch-action:none;cursor:pointer}
.m.out .vw{color:var(--vi-wv-out)}
.vw i{flex:1;min-width:2px;height:8px;background:currentColor;border-radius:2px;opacity:1;transition:opacity .15s}
/* 播放进度：播放/拖动走过的柱子高亮，未走过的压低（.on 由 JS 按进度切换） */
.b.media.voice.prog .vw i{opacity:.32}
.b.media.voice.prog .vw i.on{opacity:1}
/* 播放中：走过的柱子跟随真实包络起伏（--h 由 buildWave 写入，4-22px） */
.b.media.voice.playing .vw i.on{animation:vi-eq .6s ease-in-out infinite;animation-delay:var(--d,0s)}
.vw.load i{background:var(--vi-mut);height:8px;animation:vi-sweep .9s ease-in-out infinite;animation-delay:calc(var(--i,0)*.07s)}
@keyframes vi-sweep{0%,100%{opacity:.3}50%{opacity:.95}}
/* CSS 兜底起伏（无包络时）：每根柱子的峰值 --h 与延迟 --d 由 JS 随机写入，循环 0.6s */
.vw.anim i{animation:vi-eq .6s ease-in-out infinite;animation-delay:var(--d,0s)}
@keyframes vi-eq{0%,100%{height:4px}50%{height:var(--h,20px)}}
/* 拖动预览浮层：按住波形拖动时显示当前时间（mm:ss），松手从该点续播。
   挂在 .vf 下：绝对定位以气泡（relative）为基准，.vw 有裁剪也不会挡住浮层。 */
.vscrub{position:absolute;top:-32px;left:0;transform:translateX(-50%);display:none;align-items:center;height:20px;padding:0 7px;border-radius:6px;background:rgba(0,0,0,.75);color:#fff;font-size:11px;font-style:normal;white-space:nowrap;pointer-events:none;font-variant-numeric:tabular-nums;z-index:2}
.vscrub::after{content:'';position:absolute;left:50%;bottom:-4px;transform:translateX(-50%);width:0;height:0;border:4px solid transparent;border-top-color:rgba(0,0,0,.75)}
.vf.dragging .vscrub{display:flex}
.vw.dragging{cursor:grabbing}
.vf.dragging{transform:none} /* 拖动预览时取消 :active 的按压缩放 */
/* 失败态：声波整条消失，感叹号用 --vi-dot 红（.vp 上换 color） */
.vf.fail .vw{display:none}
.vf.fail .vp{color:var(--vi-dot)}
/* 时长组：mm:ss 文字 + 未听红点 + 倍速 chip，整组垂直居中、彼此 5px 间距。
   tabular-nums 保证跳秒不抖；红点贴时长右上角，己方镜像后自动换侧。 */
.vd{flex:none;display:inline-flex;align-items:center;gap:5px;font-size:12px;line-height:1;color:var(--vi-tx-in);font-variant-numeric:tabular-nums;white-space:nowrap}
.m.out .vd{color:var(--vi-tx-out)}
.vdot{flex:none;align-self:flex-start;width:7px;height:7px;margin-top:2px;border-radius:50%;background:var(--vi-dot)}
/* 倍速 chip：点击循环 1.0x → 1.5x → 2.0x → 0.5x。
   常态半透明收声，指到/按下才完全显形——它只是可选项，不该跟时长抢视线。 */
.vspd{flex:none;font-style:normal;font-size:10.5px;line-height:16px;padding:0 5px;border-radius:9px;background:var(--fill-2);color:var(--vi-tx-in);opacity:.6;cursor:pointer;transition:opacity .15s;-webkit-user-select:none;user-select:none}
.vspd:hover,.vspd:active{opacity:1}
.m.out .vspd{background:rgba(255,255,255,.24);color:#fff}
/* 失败提示小字：气泡外部下方（非气泡内），点击重试 */
.vfail-tip{margin-top:4px;font-size:11px;color:var(--sub);cursor:pointer}
.m.out .vfail-tip{text-align:right}
.b.media.voice .gone{min-width:120px;padding:12px 14px}
.b.media.voice .veil{min-width:120px;min-height:0;padding:12px 14px;flex-direction:row;gap:6px}
#btn-voice{flex:none}
#btn-voice:disabled{color:var(--mut)}
#up-bar{flex:none;display:flex;align-items:center;gap:10px;padding:8px max(14px,env(safe-area-inset-left)) 8px max(14px,env(safe-area-inset-right));background:var(--panel);border-top:1px solid var(--line);font-size:12.5px;color:var(--sub)}
#up-label{flex:none;max-width:46%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.up-track{flex:1;height:4px;background:var(--fill-2);border-radius:999px;overflow:hidden}
.up-track i{display:block;height:100%;width:0;background:var(--primary);border-radius:999px;transition:width .18s}
/* ---------- 待发送附件 ----------
 * 选完的图片先落在这里，等用户点发送。缩略图横排，横向可滚动，
 * 数量上限 9 个，超出部分靠滚动查看，不把输入区压得太高。 */
#pend{flex:none;background:var(--glass);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-top:1px solid var(--line);padding:10px max(12px,env(safe-area-inset-left)) 8px max(12px,env(safe-area-inset-right))}
#pend-list{display:flex;gap:8px;overflow-x:auto;overscroll-behavior-x:contain;padding-bottom:2px}
.pend-item{position:relative;width:58px;height:58px;flex:none;border-radius:9px;overflow:visible;background:var(--fill);border:1px solid var(--line-2);display:flex;align-items:center;justify-content:center}
.pend-item img{width:100%;height:100%;object-fit:cover;border-radius:8px;display:block}
/* 视频/音频没有可展示的画面，用类型标签占位，比放个破图图标清楚 */
.pend-ic{font-size:12px;color:var(--sub);font-weight:600;letter-spacing:.5px}
.pend-ic.vid{color:var(--primary-tx)}
/* 超过大小上限的附件标灰：让它看起来「不可用」，而不是发出去才报错 */
.pend-item.too-big{opacity:.42}
.pend-item.too-big::after{content:'过大';position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:11px;color:#fff;background:rgba(0,0,0,.5);border-radius:8px}
/* 删除按钮压在缩略图右上角外侧，不遮挡图片内容 */
.pend-x{position:absolute;top:-7px;right:-7px;width:21px;height:21px;border-radius:50%;border:none;background:var(--txt-2);color:var(--panel);font-size:15px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0;box-shadow:var(--sh-sm)}
.pend-x:active{transform:scale(.9)}
#pend-clear{margin-top:8px;border:none;background:none;padding:0;font-family:inherit;font-size:12px;color:var(--sub);text-decoration:underline;text-underline-offset:2px}
/* ---------- 微信式输入栏按钮：小屏上「+」与「发送」互斥 ---------- */
/* 有内容（文字或待发送附件）时隐藏「+」露出「发送」，没内容时反过来。
   桌面端宽度足够，两个都留着，符合鼠标用户的操作预期。 */
@media(max-width:719px){
  #composer.has-text #btn-more{display:none}
  #composer:not(.has-text) #btn-send{display:none}
}
#lightbox{position:fixed;inset:0;z-index:80;background:rgba(6,8,10,.94);display:flex;align-items:center;justify-content:center;padding:14px;cursor:zoom-out;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
#lightbox img{max-width:100%;max-height:100%;object-fit:contain;border-radius:8px}
/* 阅后即焚查看器：全屏遮罩，图片仅绘在 canvas 上；禁用选中/拖拽/右键保存 */
#once-view{position:fixed;inset:0;z-index:85;background:rgba(6,8,10,.97);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:16px;-webkit-user-select:none;user-select:none;touch-action:none;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.once-stage{display:flex;align-items:center;justify-content:center;max-width:92vw;max-height:78vh}
#once-canvas{max-width:92vw;max-height:78vh;border-radius:6px;box-shadow:0 10px 40px rgba(0,0,0,.5);-webkit-user-drag:none;-webkit-user-select:none;user-select:none;touch-action:none}
.once-hint{font-size:12.5px;color:rgba(255,255,255,.7);letter-spacing:.5px}
