/* ============================================================
   Glass 模板 · iOS 26 液态玻璃风格
   契约:与 default 模板同一套 CSS 变量名 + app.js 输出的类名
   主题:[data-theme="light" | "dark" | "wallpaper"](真实环境挂在 <html>,预览环境挂在手机框上)
   ============================================================ */

/* ---------- 变量:与后台注入契约一致 ---------- */
:root, [data-theme] {
    /* 后台可注入的契约变量(注入块在本文件之后,同名会覆盖这里) */
    --brand-primary: #0a84ff;
    --bubble-self: var(--brand-primary);
    /* 质感关键:灰底 + 白气泡,气泡靠投影从背景"浮起来"(default 模板的立体感来源) */
    --bubble-other: #ffffff;
    --bubble-self-text: #ffffff;
    --bubble-other-text: #0b0b0d;
    --bubble-radius: 20px;
    /* F4-R3 融合媒体卡：媒体圆角（glass 媒体 14）+ 整卡阴影（=图片档，随主题变量换档）；
       --media-fused-fx = 融合态媒体去外投影只留内圈发丝（投影上移到整卡） */
    --media-radius: 14px;
    --media-card-shadow: 0 4px 14px var(--glass-drop2);
    --media-fused-fx: inset 0 0 0 1px rgba(127, 127, 127, 0.12);
    --bg-chat: #f2f2f7;
    --bg-bottom: #f2f2f7;
    --header-bg: #ffffff;
    --header-text: #1c1c1e;
    /* 左栏变量映射（会诊 P1：nav.css 需要，玻璃语汇对齐） */
    --surface-white: #ffffff;
    --text-stone: #8e8e93;
    --text-stone-strong: #636366;
    --border-light: rgba(60, 60, 67, .12);
    --text-primary: #1c1c1e;
    --accent-green: var(--brand-primary);
    --avatar-radius: 50%;

    /* 气泡光影配方:外层浮起投影 + 顶部受光内高光 + 发丝描边(各主题覆盖)。
       注意:含 color-mix 的增强版在文件末尾的 @supports 块里定义——
       color-mix 写进自定义属性后 var() 兜底救不了(IACVT),旧内核会把
       气泡背景整个打成透明,必须用 @supports 隔离 */
    --bubble-other-fx:
        0 2px 8px rgba(24, 28, 45, 0.08),
        0 1px 2px rgba(24, 28, 45, 0.05),
        inset 0 1px 0 rgba(255, 255, 255, 0.9),
        inset 0 0 0 1px rgba(24, 28, 45, 0.035);
    --bubble-self-fx:
        inset 0 1px 0 rgba(255, 255, 255, 0.38),
        0 2px 8px rgba(24, 28, 45, 0.14),
        0 1px 2px rgba(24, 28, 45, 0.06);

    /* glass 模板内部变量 */
    --text-dim: rgba(60, 60, 67, 0.6);
    --divider-ink: rgba(60, 60, 67, 0.5);
    --tint-strength: 62%;
    --shine-edge: rgba(0, 0, 0, 0.07);
    --shine-hi: rgba(255, 255, 255, 1);
    --shine-hi2: rgba(255, 255, 255, 0.8);
    --shine-glow: rgba(255, 255, 255, 0.5);
    --shine-dark: rgba(0, 0, 0, 0.12);
    --glass-drop: rgba(24, 26, 48, 0.1);
    --glass-drop2: rgba(24, 26, 48, 0.06);
    --btn-bg: rgba(120, 120, 128, 0.12);
    --btn-bg-hover: rgba(120, 120, 128, 0.22);
    --caret: var(--brand-primary);
    --focus-ring: rgba(10, 132, 255, 0.75);
    --emoji-panel-bg: rgba(255, 255, 255, 0.9);
    --modal-bg: rgba(0, 0, 0, 0.72);
    --glass-text-shadow: none;
    --status-green: #30d158;
    --typing-dot: rgba(120, 120, 128, 0.6);
}

[data-theme="dark"] {
    /* 左栏变量 dark 版（会诊 P1：防选中键浅色砸黑底） */
    --surface-white: #1c1c20;
    --text-stone: rgba(235, 235, 245, .55);
    --text-stone-strong: rgba(235, 235, 245, .78);
    --border-light: rgba(255, 255, 255, .12);
    --text-primary: rgba(255, 255, 255, .96);
    --bubble-other: #2a2a30;
    --bubble-other-text: rgba(255, 255, 255, 0.96);
    --bg-chat: #000000;
    --bg-bottom: #000000;
    --header-bg: #1c1c20;
    --header-text: rgba(255, 255, 255, 0.96);

    /* 深色质感:顶部一线受光 + 沉底黑影,气泡从纯黑上浮起(color-mix 版在末尾 @supports) */
    --bubble-other-fx:
        inset 0 1px 0 rgba(255, 255, 255, 0.07),
        inset 0 0 0 1px rgba(255, 255, 255, 0.045),
        0 2px 10px rgba(0, 0, 0, 0.45);
    --bubble-self-fx:
        inset 0 1px 0 rgba(255, 255, 255, 0.28),
        0 2px 10px rgba(0, 0, 0, 0.5);

    --text-dim: rgba(235, 235, 245, 0.6);
    --divider-ink: rgba(235, 235, 245, 0.5);
    --tint-strength: 45%;
    --shine-edge: rgba(255, 255, 255, 0.12);
    --shine-hi: rgba(255, 255, 255, 0.55);
    --shine-hi2: rgba(255, 255, 255, 0.3);
    --shine-glow: rgba(255, 255, 255, 0.13);
    --shine-dark: rgba(0, 0, 0, 0.5);
    --glass-drop: rgba(0, 0, 0, 0.5);
    --glass-drop2: rgba(0, 0, 0, 0.3);
    --btn-bg: rgba(120, 120, 128, 0.24);
    --btn-bg-hover: rgba(120, 120, 128, 0.38);
    --caret: #ffffff;
    --focus-ring: rgba(255, 255, 255, 0.8);
    --emoji-panel-bg: rgba(28, 28, 32, 0.92);
}

[data-theme="wallpaper"] {
    /* 左栏变量 wallpaper 版（会诊 O2：壁纸上键用浅色） */
    --text-stone: rgba(255, 255, 255, .65);
    --text-stone-strong: rgba(255, 255, 255, .85);
    --surface-white: rgba(255, 255, 255, .92);
    --border-light: rgba(255, 255, 255, .25);
    /* 真机:壁纸上的接收气泡是半透明玻璃 + 白字(见 Apple 官方截图),不是实心白底 */
    --bubble-other: rgba(255, 255, 255, 0.22);
    --bubble-other-text: #ffffff;
    --bg-chat: transparent;
    --bg-bottom: transparent;
    --header-bg: #ffffff;
    --header-text: rgba(255, 255, 255, 0.96);

    --text-dim: rgba(255, 255, 255, 0.65);
    --divider-ink: rgba(255, 255, 255, 0.85);
    --tint-strength: 16%;
    --shine-edge: rgba(255, 255, 255, 0.13);
    --shine-hi: rgba(255, 255, 255, 0.85);
    --shine-hi2: rgba(255, 255, 255, 0.5);
    --shine-glow: rgba(255, 255, 255, 0.28);
    --shine-dark: rgba(0, 0, 0, 0.22);
    --glass-drop: rgba(8, 10, 40, 0.32);
    --glass-drop2: rgba(8, 10, 40, 0.18);
    --btn-bg: rgba(255, 255, 255, 0.16);
    --btn-bg-hover: rgba(255, 255, 255, 0.28);
    --caret: #ffffff;
    --focus-ring: rgba(255, 255, 255, 0.8);
    --glass-text-shadow: 0 1px 3px rgba(20, 20, 60, 0.25);
    /* 玻璃气泡上的"正在输入"点用白色系 */
    --typing-dot: rgba(255, 255, 255, 0.75);

    /* 壁纸质感:玻璃气泡的边缘高光 + 彩色投影(color-mix 版在末尾 @supports) */
    --bubble-other-fx:
        inset 0 1px 0 rgba(255, 255, 255, 0.35),
        inset 0 0 0 1px rgba(255, 255, 255, 0.16),
        0 4px 14px rgba(8, 10, 40, 0.2);
    --bubble-self-fx:
        inset 0 1px 0 rgba(255, 255, 255, 0.4),
        0 6px 18px rgba(10, 100, 255, 0.35);
}

/* color-mix 增强层:只在支持的内核(iOS 16.2+ / Chrome 111+)启用品牌色派生的
   渐变与彩影;旧内核停留在上面的 rgba 兜底版,气泡永远有底色 */
@supports (background: color-mix(in srgb, red, #fff)) {
    :root, [data-theme] {
        /* 蓝气泡顶部受光渐变;78% 处收敛回纯色,保证底部小尾巴颜色严格一致 */
        --bubble-self-grad: linear-gradient(180deg, color-mix(in srgb, var(--bubble-self) 84%, #ffffff), var(--bubble-self) 78%);
        --bubble-self-fx:
            inset 0 1px 0 rgba(255, 255, 255, 0.38),
            0 2px 8px color-mix(in srgb, var(--bubble-self) 32%, transparent),
            0 1px 2px rgba(24, 28, 45, 0.06);
    }
    [data-theme="dark"] {
        --bubble-self-fx:
            inset 0 1px 0 rgba(255, 255, 255, 0.28),
            0 2px 10px color-mix(in srgb, var(--bubble-self) 35%, transparent);
    }
    [data-theme="wallpaper"] {
        --bubble-self-fx:
            inset 0 1px 0 rgba(255, 255, 255, 0.4),
            0 6px 18px color-mix(in srgb, var(--bubble-self) 40%, transparent);
    }
}

/* ---------- 基础 ---------- */
* {
    margin: 0; padding: 0; box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
    -webkit-touch-callout: none; /* 禁全局长按菜单(图片长按不弹"存储图像") */
}
/* 气泡文字显式可选,方便访客长按复制(与 default 模板同契约) */
.bubble {
    -webkit-user-select: text;
    user-select: text;
    -webkit-touch-callout: default;
}
html, body { height: 100%; }

body {
    font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Helvetica Neue", "Microsoft YaHei", sans-serif;
    -webkit-font-smoothing: antialiased;
    -webkit-tap-highlight-color: transparent;
    background: var(--bg-chat);
}

/* 淡品牌蓝渐变底(light):纯色灰底让毛玻璃无物可折射,给一层从顶到底淡出的品牌蓝,
   顶栏/输入区的backdrop-filter立刻有内容可糊;透明度压到2-6%,不影响读消息。
   color-mix版放@supports隔离(同文件既有惯例),旧内核回退到固定淡蓝。 */
/* 左栏一体（2026-08-25 会诊）：渐变铺到 shell（rail+stage 同面），#app 透明 */
html[data-theme="light"] .visitor-shell,
html[data-theme="light"] #app { /* 无左栏降级态 #app 也吃得到；.visitor-shell #app 下方覆盖透明 */
    background: linear-gradient(180deg, rgba(10,132,255,0.05) 0%, rgba(10,132,255,0.02) 36%, var(--bg-chat) 78%);
}
html[data-theme="light"] .visitor-shell #app { background: transparent; }
@supports (background: color-mix(in srgb, red, blue)) {
    html[data-theme="light"] .visitor-shell,
    html[data-theme="light"] #app {
        background: linear-gradient(180deg,
            color-mix(in srgb, var(--brand-primary) 6%, var(--bg-chat)) 0%,
            color-mix(in srgb, var(--brand-primary) 3%, var(--bg-chat)) 36%,
            var(--bg-chat) 78%);
    }
    html[data-theme="light"] .visitor-shell #app { background: transparent; }
}

#app {
    position: relative;
    /* --app-height 由 shared/js/app.js 用 visualViewport 维护:iOS 键盘是覆盖式,
       不消费它的话底部输入栏会被键盘挡住;无 app.js 时(本地预览)回退 100% */
    height: var(--app-height, 100%);
    overflow: hidden;
    background: var(--bg-chat);
    color: var(--header-text);
}

/* ---------- 彩色壁纸(glass.js 在 wallpaper 主题下注入) ---------- */
.lg-wallpaper {
    position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 0;
    background: linear-gradient(160deg, #2e3f9e 0%, #4b3fb8 30%, #7a3fb0 55%, #b8408f 78%, #d4645a 100%);
    overflow: hidden;
    pointer-events: none;
}
.lg-blob {
    position: absolute; border-radius: 50%;
    filter: blur(64px); opacity: 0.8; will-change: transform;
}
.lg-blob-1 { width: 56vmax; height: 56vmax; top: -16%; left: -14%;
    background: radial-gradient(circle at 35% 35%, #8b7bff, transparent 62%);
    animation: lgDrift1 28s ease-in-out infinite alternate; }
.lg-blob-2 { width: 48vmax; height: 48vmax; bottom: -18%; right: -12%;
    background: radial-gradient(circle at 55% 45%, #ff6aa9, transparent 60%);
    animation: lgDrift2 34s ease-in-out infinite alternate; }
.lg-blob-3 { width: 40vmax; height: 40vmax; top: 30%; left: 44%;
    background: radial-gradient(circle at 50% 50%, #35c8ff, transparent 58%);
    animation: lgDrift3 23s ease-in-out infinite alternate; }
@keyframes lgDrift1 { to { transform: translate(13vw, 9vh) scale(1.12); } }
@keyframes lgDrift2 { to { transform: translate(-11vw, -7vh) scale(0.92); } }
@keyframes lgDrift3 { to { transform: translate(-9vw, 11vh) scale(1.18) rotate(18deg); } }
.lg-ring {
    position: absolute; border-radius: 50%;
    border: 1.5px solid rgba(255, 255, 255, 0.26);
}
.lg-ring-1 { width: 52vmin; height: 52vmin; top: 8%; left: -8%; }
.lg-ring-2 { width: 66vmin; height: 66vmin; bottom: -6%; right: -10%; border-color: rgba(255, 255, 255, 0.18); }

/* ---------- 液态玻璃材质 ----------
   作用在各个"悬浮小件"上(真机形态:分散的独立玻璃件,不是通栏大板):
   元素自身 backdrop-filter 负责模糊(或被 glass.js 升级成折射),
   ::before 是着色层,::after 是镜面高光。 */
.composer-plus, .input-dock, .quick-msg-btn {
    position: relative;
    isolation: isolate;
    box-shadow: 0 10px 26px var(--glass-drop), 0 2px 6px var(--glass-drop2);
    backdrop-filter: blur(16px) saturate(180%);
    -webkit-backdrop-filter: blur(16px) saturate(180%);
}
.composer-plus::before, .input-dock::before, .quick-msg-btn::before {
    content: "";
    position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: -1;
    border-radius: inherit;
    pointer-events: none;
    /* 第一行是旧内核兜底(实心底,牺牲通透保可读),支持 color-mix 的内核走第二行 */
    background: var(--bg-bottom);
    background: color-mix(in srgb, var(--bg-bottom) var(--tint-strength), transparent);
}
.composer-plus::after, .input-dock::after, .quick-msg-btn::after {
    content: "";
    position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 1;
    border-radius: inherit;
    pointer-events: none;
    box-shadow:
        inset 0 0 0 1px var(--shine-edge),
        inset 1.8px 3px 0 -1.5px var(--shine-hi),
        inset -2px -2px 0 -1.5px var(--shine-hi2),
        inset 0 10px 28px -10px var(--shine-glow),
        inset 0 -7px 14px -9px var(--shine-dark);
}

/* ---------- 顶栏:通栏玻璃条(骨架同 default 模板,材质为玻璃) ---------- */
.header {
    position: absolute;
    top: 0; left: 0; right: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: calc(9px + env(safe-area-inset-top)) 16px 9px;
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
    color: var(--header-text);
    isolation: isolate;
    backdrop-filter: blur(16px) saturate(180%);
    -webkit-backdrop-filter: blur(16px) saturate(180%);
    box-shadow: 0 8px 24px var(--glass-drop2);
}
.header::before {
    content: "";
    position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: -1;
    pointer-events: none;
    /* 旧内核兜底:实心顶栏 */
    background: var(--header-bg);
    background: color-mix(in srgb, var(--header-bg) var(--tint-strength), transparent);
}
.header::after {
    content: "";
    position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 1;
    pointer-events: none;
    box-shadow:
        inset 0 1px 0 var(--shine-hi2),
        inset 0 -1px 0 var(--shine-edge);
}
.header-id {
    display: flex; align-items: center; gap: 10px;
    min-width: 0;
}
.header-logo {
    width: 36px; height: 36px;
    border-radius: var(--avatar-radius);
    object-fit: cover;
    flex-shrink: 0;
    box-shadow: inset 0 1px 1px rgba(255,255,255,0.4), 0 2px 8px rgba(20, 40, 120, 0.22);
}
.header-meta { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.header-title {
    font-size: 16px; font-weight: 600;
    letter-spacing: 0.2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    text-shadow: var(--glass-text-shadow);
}
/* 连接状态:标题下方的小字(骨架同 default)。app.js 会把 connected/disconnected
   类打在 #statusDot 上,文本写进 #status,两个必须是独立元素 */
.header-status {
    display: flex; align-items: center; gap: 5px;
    min-width: 0;
}
#statusDot {
    width: 6px; height: 6px; border-radius: 50%;
    background: #a0a3ad;
    flex-shrink: 0;
    transition: background 0.3s;
}
#statusDot.connected {
    background: var(--status-green);
    box-shadow: 0 0 6px var(--status-green);
}
#statusDot.disconnected { background: #ff453a; }
#status {
    font-size: 11.5px;
    color: var(--text-dim);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* app.js 断线时注入的重连按钮 ↻(内联 span):字号只有 11.5px,
   用负外边距把命中区扩到手指能点中的大小 */
#status span[onclick] {
    display: inline-block;
    padding: 14px;
    margin: -14px;
}

/* ---------- 消息流 ---------- */
.chat-stream {
    position: absolute; top: 0; right: 0; bottom: 0; left: 0;
    z-index: 5;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* contain:滚到边界不把滚动链传给文档(安卓下拉刷新/iOS 整页回弹) */
    overscroll-behavior: contain;
    padding: calc(72px + env(safe-area-inset-top)) 14px calc(158px + env(safe-area-inset-bottom));
    /* 横屏刘海机:左右让出安全区 */
    padding-left: max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
    display: flex; flex-direction: column;
    scrollbar-width: none;
}
.chat-stream::-webkit-scrollbar { display: none; }
/* emoji 面板(覆盖层)打开时给消息区让位,由 glass.js 切换该类 */
#app.lg-emoji-open .chat-stream { padding-bottom: calc(430px + env(safe-area-inset-bottom)); }

.welcome-text {
    align-self: center;
    max-width: 86%;
    text-align: center;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--divider-ink);
    margin: 2px 0 14px;
}

/* glass.js 注入的时间戳 / 已送达 */
.lg-time-stamp {
    align-self: center;
    font-size: 11px; font-weight: 500;
    color: var(--divider-ink);
    margin: 10px 0 12px;
    /* iOS风 pill 衬底 */
    background: rgba(120, 120, 128, 0.12);
    border-radius: 999px;
    padding: 3px 10px;
}

/* "正在输入"三点跳动气泡(glass.js 把 typingIndicator 文本升级为视觉三点) */
.lg-dots {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 1px;
}
.lg-dots i {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--typing-dot);
    animation: lg-dot-bounce 1.1s infinite ease-in-out;
}
.lg-dots i:nth-child(2) { animation-delay: 0.15s; }
.lg-dots i:nth-child(3) { animation-delay: 0.3s; }
@keyframes lg-dot-bounce {
    0%, 60%, 100% { transform: translateY(0); opacity: .45; }
    30% { transform: translateY(-4px); opacity: 1; }
}
.lg-receipt {
    align-self: flex-end;
    font-size: 11px; font-weight: 500;
    color: var(--divider-ink);
    margin: -9px 6px 12px 0;
}

.message {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    margin-bottom: 12px;
    animation: lgMsgIn 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.08) both;
}
.message.self { justify-content: flex-end; }
.message.cont-next { margin-bottom: 2px; }

.message .avatar {
    width: 28px; height: 28px;
    border-radius: var(--avatar-radius);
    object-fit: cover;
    flex-shrink: 0;
    background: var(--btn-bg);
    box-shadow: 0 1px 3px rgba(24, 28, 45, 0.18), inset 0 0 0 1px rgba(24, 28, 45, 0.04);
    transition: opacity 0.15s;
}
/* 同组只在最后一条显示头像,其余占位隐藏,保证气泡左缘对齐 */
.message.other.cont-next .avatar { opacity: 0; }
/* 没有头像元素的 other 行(如 app.js 的"正在输入"指示器)补一个头像位,
   否则气泡会比同组的靠左错一格 */
.message.other:not(:has(.avatar)) { padding-left: 36px; }

@keyframes lgMsgIn {
    from { opacity: 0; transform: translateY(14px) scale(0.96); }
    to   { opacity: 1; transform: none; }
}

/* ---------- 气泡(iMessage 形态) ---------- */
.bubble {
    position: relative;
    max-width: 72%;
    padding: 10px 15px;
    /* 真机是 17px(SF Pro 正文档位),中文同档更舒展 */
    font-size: 17px; line-height: 1.33;
    border-radius: var(--bubble-radius);
    word-break: break-word;
    white-space: pre-wrap;
}
/* 纯表情消息:真机不带气泡,直接大号裸排 */
.bubble.emoji-only {
    background: none !important;
    padding: 0 2px;
    font-size: 42px; line-height: 1.15;
    box-shadow: none;
}
.bubble.emoji-only::before, .bubble.emoji-only::after { display: none; }
.message.other .bubble {
    background: var(--bubble-other);
    color: var(--bubble-other-text);
    box-shadow: var(--bubble-other-fx);
}
.message.self .bubble {
    background: var(--bubble-self-grad, var(--bubble-self));
    color: var(--bubble-self-text);
    box-shadow: var(--bubble-self-fx);
}
/* 壁纸主题接收气泡:玻璃材质需要 backdrop 模糊(真机同款)。
   注意这是逐气泡的 backdrop 层、随消息数线性增长的开销——
   壁纸主题是展示型档位,保真优先;黑白主题无此路径 */
[data-theme="wallpaper"] .message.other .bubble,
[data-theme="wallpaper"] .message.other .voice-msg {
    backdrop-filter: blur(18px) saturate(160%);
    -webkit-backdrop-filter: blur(18px) saturate(160%);
}

/* 连续消息:发送方一侧相邻圆角收小 */
.message.other .bubble.cont-top { border-top-left-radius: 6px; }
.message.other .bubble.cont-bottom { border-bottom-left-radius: 6px; }
.message.self .bubble.cont-top { border-top-right-radius: 6px; }
.message.self .bubble.cont-bottom { border-bottom-right-radius: 6px; }

/* 小尾巴:两层伪元素,一层同色块 + 一层背景色遮罩裁出弯钩。
   壁纸主题背景非纯色会穿帮,自动隐藏。 */
.bubble.tail::before, .bubble.tail::after {
    content: "";
    position: absolute;
    bottom: -1px;
    height: 16px;
}
.message.self .bubble.tail::before {
    right: -6px;
    border-right: 16px solid var(--bubble-self);
    border-bottom-left-radius: 13px 11px;
}
.message.self .bubble.tail::after {
    right: 0;
    width: 10px;
    background: var(--bg-chat);
    border-bottom-left-radius: 8px;
    transform: translateX(10px);
}
.message.other .bubble.tail::before {
    left: -6px;
    border-left: 16px solid var(--bubble-other);
    border-bottom-right-radius: 13px 11px;
}
.message.other .bubble.tail::after {
    left: 0;
    width: 10px;
    background: var(--bg-chat);
    border-bottom-right-radius: 8px;
    transform: translateX(-10px);
}
[data-theme="wallpaper"] .bubble.tail::before,
[data-theme="wallpaper"] .bubble.tail::after { display: none; }
/* 圆角调小时尾巴形状不成立,glass.js 检测到 --bubble-radius < 12px 会挂上这个类 */
.lg-no-tail .bubble.tail::before,
.lg-no-tail .bubble.tail::after { display: none; }

/* 气泡内链接 */
.bubble a, .msg-link {
    color: var(--brand-primary);
    text-decoration: underline;
    word-break: break-all;
}
.message.self .bubble a, .message.self .bubble .msg-link {
    color: var(--bubble-self-text);
}

/* 引用/回复预览(子元素由 app.js renderReplyPreview 动态注入,类名契约见 app.js:1338-1407) */
.reply-preview {
    border-left: 3px solid rgba(127, 127, 127, 0.45);
    border-left: 3px solid color-mix(in srgb, currentColor 40%, transparent);
    padding: 2px 8px;
    margin-bottom: 6px;
    font-size: 12.5px;
    opacity: 0.75;
    border-radius: 4px;
}
.reply-preview-header { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; }
.reply-preview-name { font-weight: 600; font-size: 12px; }
.reply-preview-tag { font-size: 10.5px; opacity: 0.65; }
.reply-preview-content {
    font-size: 12.5px;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
/* 引用媒体缩略图:同 default,宽度撑满引用条、固定 140 高 cover 裁切 */
.reply-preview-img {
    display: block;
    width: 100%;
    height: 140px;
    object-fit: cover;
    border-radius: 8px;
    margin-top: 4px;
    background: rgba(127, 127, 127, 0.15);
}
.reply-video-thumb {
    position: relative;
    width: 100%; height: 140px;
    border-radius: 8px;
    overflow: hidden;
    margin-top: 4px;
}
.reply-video-thumb img { width: 100%; height: 100%; object-fit: cover; }
.reply-video-play {
    position: absolute; top: 0; right: 0; bottom: 0; left: 0;
    display: flex; align-items: center; justify-content: center;
}
.reply-video-play::after {
    content: "";
    border-left: 12px solid rgba(255, 255, 255, 0.92);
    border-top: 7px solid transparent;
    border-bottom: 7px solid transparent;
    margin-left: 2px;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.4));
}
/* 兜底:气泡内任何动态注入的图片都不许撑破气泡 */
.bubble img { max-width: 100%; }

/* ---------- 图片 / 视频 / 语音 / 链接消息 ---------- */
/* 媒体统一卡片:全部 180×120 + cover 裁切(业主定的规则,保证消息流里每张一样大) */
.msg-img {
    width: 180px; height: 120px;
    object-fit: cover;
    border-radius: 14px;
    display: block;
    cursor: pointer;
    background: var(--bubble-other);
    box-shadow: 0 4px 14px var(--glass-drop2), inset 0 0 0 1px rgba(127,127,127,0.12);
}
.img-placeholder {
    width: 180px; height: 120px;
    border-radius: 14px;
    background: var(--bubble-other);
    color: var(--text-dim);
    display: flex; align-items: center; justify-content: center;
    box-shadow: var(--bubble-other-fx);
}
.img-placeholder svg { width: 34px; height: 34px; }

.video-msg {
    position: relative;
    width: 180px; height: 120px;
    border-radius: 14px;
    overflow: hidden;
    cursor: pointer;
    background: #17181c;
    box-shadow: 0 4px 14px var(--glass-drop2);
}
/* app.js 原位播放的 video 元素同样锁定卡片尺寸 */
.video-msg-player, .inplace-video {
    width: 100% !important;
    height: 100% !important;
    object-fit: cover;
    display: block;
    border-radius: 14px;
}
.video-cover { width: 100%; height: 100%; object-fit: cover; }
.video-cover-placeholder {
    width: 100%; height: 100%;
    background: linear-gradient(150deg, #2a2c33, #17181c);
}
.play-icon {
    position: absolute; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 44px; height: 44px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.22);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.5), inset 0 0 0 1px rgba(255,255,255,0.2);
}
.play-icon::after {
    content: "";
    position: absolute; top: 50%; left: 55%;
    transform: translate(-50%, -50%);
    border-left: 14px solid #fff;
    border-top: 9px solid transparent;
    border-bottom: 9px solid transparent;
}
.video-duration {
    position: absolute; right: 8px; bottom: 6px;
    font-size: 11px; color: #fff;
    background: rgba(0, 0, 0, 0.45);
    padding: 2px 7px; border-radius: 100px;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.video-msg.loading .play-icon { opacity: 0.4; }

.voice-msg {
    display: flex; align-items: center; gap: 8px;
    min-width: 120px;
    padding: 10px 14px;
    border-radius: var(--bubble-radius);
    background: var(--bubble-other);
    color: var(--bubble-other-text);
    box-shadow: var(--bubble-other-fx);
    cursor: pointer;
}
.message.self .voice-msg {
    background: var(--bubble-self-grad, var(--bubble-self));
    color: var(--bubble-self-text);
    box-shadow: var(--bubble-self-fx);
}
.voice-icon { display: flex; }
.voice-icon svg { width: 18px; height: 18px; }
.voice-waveform { display: flex; align-items: center; gap: 2.5px; }
.voice-bar {
    width: 3px; border-radius: 2px;
    background: currentColor;
}
.voice-bar.inactive { opacity: 0.35; }
.voice-duration { font-size: 12.5px; opacity: 0.8; }
.voice-msg.playing .voice-icon { animation: lgVoicePulse 1s ease-in-out infinite; }
@keyframes lgVoicePulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

.link-msg { cursor: pointer; }
.link-title { font-weight: 600; }
.link-desc { font-size: 13px; opacity: 0.75; margin-top: 3px; }

/* ---------- 底部悬浮区 ---------- */
.bottom-dock {
    position: absolute;
    left: 10px; right: 10px;
    left: max(10px, env(safe-area-inset-left));
    right: max(10px, env(safe-area-inset-right));
    bottom: calc(12px + env(safe-area-inset-bottom));
    z-index: 20;
    display: flex; flex-direction: column;
    gap: 10px;
}
.keyboard-open .bottom-dock { bottom: 8px; }

.quick-msg-bar {
    display: flex; gap: 8px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain; /* 横滑到头不触发浏览器侧滑返回 */
    scrollbar-width: none;
    padding: 2px;
}
.quick-msg-bar::-webkit-scrollbar { display: none; }
.quick-msg-bar:empty { display: none; }

.quick-msg-btn {
    flex-shrink: 0;
    border: none;
    border-radius: 100px;
    background: transparent;
    color: var(--header-text);
    font-size: 13px; font-family: inherit;
    padding: 9px 17px;
    cursor: pointer;
    /* 注意别用过冲型贝塞尔(y>1),回弹会先放大再落回,视觉上像气泡胀了一下 */
    transition: transform 0.15s ease-out, opacity 0.2s;
    touch-action: manipulation;
    text-shadow: var(--glass-text-shadow);
}
.quick-msg-btn:active { transform: scale(0.92); }
.quick-msg-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.quick-msg-btn.sending { opacity: 0.5; pointer-events: none; }
/* 触屏设备:快捷按钮不上 backdrop(与 glass.js 不给它们上折射的意图一致,
   否则每个小按钮都是独立 backdrop 层,滚动时逐帧重采样) */
@media (pointer: coarse) {
    .quick-msg-btn {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
}
.quick-msg-btn.hide { animation: lgQuickHide 0.25s ease forwards; }
@keyframes lgQuickHide {
    /* 只动 opacity/transform;width 从 auto 到 0 无法插值,会中途跳变 */
    to { opacity: 0; transform: scale(0.85); }
}

/* 真机形态:"+" 是独立的圆形玻璃钮,输入药丸单独一条 */
.composer-row {
    display: flex; align-items: center; gap: 8px;
}
.composer-plus {
    width: 44px; height: 44px;
    flex-shrink: 0;
    border: none; border-radius: 50%;
    background: transparent;
    color: var(--header-text);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: transform 0.15s ease-out;
    touch-action: manipulation;
}
.composer-plus:active { transform: scale(0.9); }
.composer-plus:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.composer-plus svg { width: 22px; height: 22px; position: relative; z-index: 2; }

.input-dock {
    flex: 1; min-width: 0;
    display: flex; align-items: center; gap: 2px;
    min-height: 44px;
    border-radius: 22px;
    padding: 4px 5px 4px 16px;
}
.emoji-btn {
    position: relative; z-index: 2;
    /* 44px 命中区(iOS HIG),视觉图标仍是 21px;它紧邻输入框,太小会误触弹键盘。
       负上下外边距抵消高度,不把 44px 高的输入药丸撑胖 */
    width: 44px; height: 44px;
    margin-top: -6px; margin-bottom: -6px;
    flex-shrink: 0;
    border: none; border-radius: 50%;
    background: transparent;
    color: var(--text-dim);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: color 0.2s, transform 0.15s;
    touch-action: manipulation;
}
.emoji-btn:hover { color: var(--header-text); }
.emoji-btn:active { transform: scale(0.9); }
.emoji-btn:focus-visible,
.composer-plus:focus-visible,
.send-pill:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.emoji-btn svg { width: 21px; height: 21px; }
.emoji-btn.active { color: var(--header-text); }

.text-input {
    position: relative; z-index: 2;
    flex: 1; min-width: 0;
    border: none; outline: none; background: transparent;
    color: var(--header-text);
    caret-color: var(--caret);
    /* 16px:小于这个值 iOS Safari 聚焦时会自动缩放页面 */
    font-size: 16px; font-family: inherit;
    padding: 8px 4px;
}
.text-input::placeholder { color: var(--text-dim); }

.send-pill {
    position: relative; z-index: 2;
    width: 34px; height: 34px;
    flex-shrink: 0;
    border: none;
    border-radius: 17px;
    margin-left: 2px;
    background: var(--accent-green);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    box-shadow: inset 0 1.5px 0 rgba(255,255,255,0.45), inset 0 0 0 1px rgba(255,255,255,0.15), 0 4px 14px rgba(24, 28, 45, 0.28);
    box-shadow: inset 0 1.5px 0 rgba(255,255,255,0.45), inset 0 0 0 1px rgba(255,255,255,0.15), 0 4px 14px color-mix(in srgb, var(--accent-green) 45%, transparent);
    transition: transform 0.15s ease-out, opacity 0.15s, width 0.18s ease-out, margin 0.18s ease-out, filter 0.2s;
    touch-action: manipulation;
}
/* 真机行为:没有文字时发送按钮不存在,输入后才弹出(纯 CSS,依赖 :has) */
.input-dock:has(.text-input:placeholder-shown) .send-pill {
    width: 0; margin-left: 0; padding: 0;
    opacity: 0;
    transform: scale(0.4);
    pointer-events: none;
}
.send-pill { overflow: hidden; }
.send-pill::after {
    content: "";
    flex-shrink: 0;
    width: 18px; height: 18px;
    background: #fff;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7'/%3E%3C/svg%3E") center / contain no-repeat;
}
.send-pill:hover { filter: brightness(1.1); }
.send-pill:active { transform: scale(0.88); }
.send-pill.disabled { opacity: 0.45; pointer-events: none; }

/* ---------- 发送失败标记(app.js send-failed) ---------- */
.message.send-failed .bubble { opacity: 0.6; }
.message .send-failed-mark {
    font-size: 11px; color: #ff453a;
}

/* ---------- toast ---------- */
.toast {
    position: fixed;
    top: calc(70px + env(safe-area-inset-top));
    left: 50%;
    transform: translateX(-50%);
    z-index: 1001; /* 要压过 modal/loading 浮层(1000),否则弹窗期间的报错提示被盖住 */
    font-size: 13.5px;
    color: var(--header-text);
    padding: 10px 20px;
    border-radius: 100px;
    background: var(--header-bg);
    background: color-mix(in srgb, var(--header-bg) 70%, transparent);
    backdrop-filter: blur(16px) saturate(180%);
    -webkit-backdrop-filter: blur(16px) saturate(180%);
    box-shadow: 0 8px 24px var(--glass-drop), inset 0 1px 0 var(--shine-hi2), inset 0 0 0 1px var(--shine-edge);
    animation: lgToastIn 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.1);
}
.toast.warning { color: #b25000; }
.toast.error { color: #ff453a; }
@keyframes lgToastIn {
    from { opacity: 0; transform: translateX(-50%) translateY(-10px) scale(0.94); }
    to   { opacity: 1; transform: translateX(-50%); }
}
/* app.js 淡出时引用的是这个名字(toast.style.animation='toastOut ...'),必须提供 */
@keyframes toastOut {
    from { opacity: 1; transform: translateX(-50%); }
    to   { opacity: 0; transform: translateX(-50%) translateY(-10px) scale(0.94); }
}

/* ---------- 弹窗(app.js 动态创建:#image-modal / #video-modal / 加载浮层) ---------- */
.image-modal, .video-modal {
    position: fixed; top: 0; right: 0; bottom: 0; left: 0;
    z-index: 1000;
    background: var(--modal-bg);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    display: flex; align-items: center; justify-content: center;
    overscroll-behavior: contain; /* iOS 上拖弹窗背景不回弹整个文档 */
}
.image-modal img { max-width: 100%; max-height: 80vh; border-radius: 14px; }
.video-modal video { max-width: 100%; max-height: 80vh; }

/* app.js 动态创建的图片预览弹窗:#image-modal > .image-wrapper > img + .close-btn */
.image-modal .image-wrapper {
    position: relative;
    max-width: 92%; max-height: 88%;
    display: flex; align-items: center; justify-content: center;
}
.image-modal .close-btn, .video-modal .close-btn {
    /* 放图片内部右上角:竖长图时按钮不会被顶进刘海/状态栏,且命中区加大 */
    position: absolute; top: 8px; right: 8px;
    width: 40px; height: 40px; line-height: 40px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.18);
    color: #fff; font-size: 22px; line-height: 36px; text-align: center;
    cursor: pointer;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.35);
}

/* app.js 动态创建的视频弹窗:#video-modal(顶部返回栏 + 播放区) */
.video-modal { flex-direction: column; justify-content: flex-start; }
.video-modal-header {
    width: 100%;
    padding: calc(12px + env(safe-area-inset-top)) 14px 10px;
}
.video-modal-back {
    display: inline-flex; align-items: center; gap: 6px;
    border: none; border-radius: 100px;
    padding: 8px 18px 8px 14px;
    background: rgba(255, 255, 255, 0.16);
    color: #fff; font-size: 14.5px; font-family: inherit;
    cursor: pointer;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.35), inset 0 0 0 1px rgba(255,255,255,0.12);
}
.video-modal-back:active { transform: scale(0.95); }
.video-modal-back span { font-size: 17px; line-height: 1; }
.video-modal-content {
    flex: 1; width: 100%;
    min-height: 0;
    display: flex; align-items: center; justify-content: center;
    padding: 0 0 calc(20px + env(safe-area-inset-bottom));
}
.video-modal-content video { max-width: 100%; max-height: 100%; border-radius: 0; }

/* app.js 动态创建的加载浮层(#videoLoading / #imageLoading 用内联样式定位,这里只管内容) */
.loading-progress { color: #fff; font-size: 14px; margin-top: 4px; }

.loading-content {
    display: flex; flex-direction: column; align-items: center; gap: 14px;
    color: #fff;
}
.loading-spinner {
    width: 34px; height: 34px;
    border: 3px solid rgba(255, 255, 255, 0.25);
    border-top-color: #fff;
    border-radius: 50%;
    animation: lgSpin 0.9s linear infinite;
}
@keyframes lgSpin { to { transform: rotate(360deg); } }
.loading-cancel-btn {
    border: none; border-radius: 100px;
    padding: 8px 22px;
    background: rgba(255, 255, 255, 0.18);
    color: #fff; font-size: 14px;
    cursor: pointer;
}

/* ---------- emoji 面板 ---------- */
.emoji-picker-panel {
    /* 定位基准是 .bottom-dock(最近定位祖先):bottom:100% = 永远悬在整个
       输入区上方,不用叠 safe-area(dock 自己已经算过一次,再叠会双计) */
    position: absolute;
    left: 0; right: 0;
    bottom: calc(100% + 8px);
    z-index: 30;
    border-radius: 22px;
    background: var(--emoji-panel-bg);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    box-shadow: 0 14px 34px var(--glass-drop), inset 0 1px 0 var(--shine-hi2), inset 0 0 0 1px var(--shine-edge);
    max-height: 260px;
    display: none;
    flex-direction: column;
    overflow: hidden;
}
.emoji-picker-panel.open { display: flex; }
.emoji-tabs { display: flex; gap: 4px; padding: 8px 10px 0; }
.emoji-tab {
    border: none; background: transparent;
    color: var(--text-dim);
    font-size: 13px; padding: 6px 12px;
    border-radius: 100px; cursor: pointer;
}
.emoji-tab.active { background: var(--btn-bg); color: var(--header-text); }
.emoji-content { overflow-y: auto; padding: 8px 10px 12px; }
.emoji-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(38px, 1fr)); }
.emoji-item {
    border: none; background: transparent;
    font-size: 24px; padding: 5px;
    border-radius: 8px; cursor: pointer;
}
.emoji-item:active { background: var(--btn-bg); }

/* ---------- 动效偏好 ---------- */
@media (prefers-reduced-motion: reduce) {
    .lg-blob { animation: none; }
    .message { animation: none; }
}

/* ==================== 图床直读 F4（修复方案 v3）：气泡媒体列容器 + 图床按钮 ==================== */
/* AI 直读图床消息带 extra.gallery_post_id 标记时，图/视频真渲染分支包列容器，
   其下挂「查看全部效果图」按钮（文案与合辑卡逐字一致）。列容器防 .message
   flex 行布局把按钮横排在媒体右侧（方案 G2）。 */
.msg-media-col {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    /* F4-R3 降级形态（无 :has 的老内核，iOS<15.4）= 留缝减重：gap 2px + 1px 浅灰描边
       + 500 字重（GLM 形态）；现代内核由下方 :has 增强层收成 0 缝融合媒体卡。 */
    gap: 2px;
    max-width: 100%;
    /* 发丝掺墨比例变量化：老板截图要框更清楚时改 ≥50%（clean 需 ≥53%）并接受框感回潮；
       备选B"45%=3.15:1"系错数已撤档——实测 45% 仅 2.85(light)/2.47(clean) 不过 WCAG 1.4.11 3:1（0902 复核 2026-09-10） */
    --gallery-ring-ink: 14%;
}

/* ============================================================
   F4-R3 v1（2026-09-11 按钮观感修复·四家设计稿互评后指挥位合成终稿）
   形态 = 融合媒体卡：列缝收 0、图只留上圆角、钮只留下圆角（圆角 = 模板
   媒体圆角 --media-radius：default/clean/modern 12、glass 14），钮与图
   读作「一张卡片的图区 + 行动区」，不再读作独立控件。
   ★防回摆留痕：2px 品牌深描边 / 600 字重 / 条身自带阴影 是 F4-R2
   「要有控件感」旧目标函数下的有意选择（0902 终稿）；本轮反转（1px 发丝、
   500 字重、阴影从条身上移到整卡）是目标函数变更（老板要「像人发的，
   不像控件」）后的有意选择——两版各有其目标，勿按旧评审结论回摆。
   硬约束不变：零 DOM 改动（app.js:1148 拼装串与 :1232 补挂串都不动）
             ⇒ textContent 仍是「查看全部效果图」，雪佛龙走 ::after；
             两条产按钮路（首渲/补挂）形态天然一致；
             44px 命中区不动；焦点环/DOM/文案零改动。
   自律四条不变：底色低饱和 / 阴影不重于同列的图与气泡 / 无循环动效 / 无入场动画。
   ============================================================ */
/* wallpaper 主题未覆写 --text-primary ⇒ 白字配深墨箭头 2.08:1 回退（btn-review FAIL1）；scoped 跟气泡文字色 */
[data-theme="wallpaper"] .msg-gallery-link { --text-primary: var(--bubble-other-text); }
/* wallpaper 箭头略降档 .92：双满白在亮壁纸块过亮刺眼（grok3 码审），.92 白稳过 3:1 且不顶穿 base 禁 opacity 总钉语义（wallpaper 单独钉对比度） */
[data-theme="wallpaper"] .msg-gallery-link::after { color: #ffffff; opacity: .92; }

.msg-gallery-link {
    display: flex;                       /* 原 inline-flex：等宽条要撑到 180px */
    align-items: center;
    justify-content: space-between;      /* 文案靠左、雪佛龙贴右缘 = iOS 列表行语汇 */
    gap: 6px;
    box-sizing: border-box;
    align-self: stretch;                 /* 宽度跟着列容器走 = 自动等于图的 border-box 宽（图 180px） */
    max-width: 180px;                    /* 上限=图宽：防将来文案变长把栏撑得比图宽 */
    min-height: 44px;                    /* 不动：WCAG 2.5.8 / Apple 44pt / 与合辑卡 nav.css 同体系 */
    padding: 8px 12px;
    /* 降级形态描边 = 模板浅灰 1px（同时是 :has 增强层里发丝 color-mix 的兜底档） */
    border: 1px solid var(--border-light, #E5E5EA);
    border-radius: 10px;                 /* 降级形态四角圆（独立条）；融合态由 :has 层改 0 0 R R */
    /* 底 = 该模板 AI 侧气泡面（与白泡同材质），不再掺品牌色（撞脸客户气泡根因） */
    background: var(--bubble-other, #FFFFFF);
    /* 字 = 模板自带的「该表面上的墨色」（default/clean/modern = inherit → body 的 --text-charcoal；
       glass light #0b0b0d / dark rgba(255,255,255,.96) / wallpaper #fff）。
       ★仍禁 color: var(--brand-primary)：ThemeColor 是任意 6 位 HEX，对比度救不回来。 */
    color: var(--bubble-other-text, var(--text-primary, #1C1C1E));
    font-size: 13px;                     /* 不动：与四模板/合辑卡同档 */
    font-weight: 500;                    /* F4-R2 的 600 是旧目标有意选择，见块头防回摆注 */
    font-family: inherit;
    line-height: 1.2;
    text-align: left;
    cursor: pointer;
    box-shadow: none;                    /* 阴影不在条身：融合态上移到整卡（:has 层） */
    touch-action: manipulation;          /* 老 WebView 去双击缩放延迟（viewport 已 user-scalable=no，这条是保底） */
    -webkit-tap-highlight-color: transparent;   /* 按压反馈单一来源 = .is-pressed，不与系统灰闪叠成双份 */
    transition: background-color 120ms ease;
}

/* ---- 融合媒体卡增强层（:has，iOS 15.4+；老内核整组丢弃 → 自动落上方降级形态）---- */
.msg-media-col:has(> .msg-gallery-link) {
    gap: 0;                              /* 列缝收 0：图与钮缝合 */
    border-radius: var(--media-radius, 12px);
    /* 阴影从条身上移到整卡，档 = 该模板气泡/图片同档（:root --media-card-shadow：
       default/modern=0 2px 6px .06（气泡档）、clean=none（气泡本无影）、
       glass=0 4px 14px var(--glass-drop2)（图片档，暗色/壁纸随变量自动换档）） */
    box-shadow: var(--media-card-shadow, 0 2px 6px rgba(0, 0, 0, .06));
}
.msg-media-col:has(> .msg-gallery-link) .msg-img,
.msg-media-col:has(> .msg-gallery-link) .video-msg {
    border-radius: var(--media-radius, 12px) var(--media-radius, 12px) 0 0;   /* 图只留上圆角 */
    /* glass=留内圈发丝去外投影（投影已上移到整卡）；其余模板媒体本无影，none 是空操作 */
    box-shadow: var(--media-fused-fx, none);
}
.msg-media-col:has(> .msg-gallery-link) .msg-gallery-link {
    border-radius: 0 0 var(--media-radius, 12px) var(--media-radius, 12px);   /* 钮只留下圆角 */
    /* 外圈 2px 深蓝 → 1px 发丝：墨色掺 --gallery-ring-ink（默认 14%）进气泡面，
       暗色主题自动提亮；顶边 0（与图缝合，缝线图侧自带底边发丝充当）。
       ★锚点必须用 --text-primary：--bubble-other-text 在 default/clean/modern 是关键字
       inherit，不是颜色，进 color-mix 会让整条声明失效。 */
    border-color: var(--border-light, #E5E5EA);                                                        /* :has√/color-mix× 兜底 */
    border-color: color-mix(in srgb, var(--text-primary, #1C1C1E) var(--gallery-ring-ink, 14%), var(--bubble-other, #FFFFFF));
    border-top: 0;
}

/* 尾部雪佛龙：纯 CSS（border 旋转 45°）⇒ 零字形漂移、零资源、零 DOM、textContent 不变。
   F4-R3：8→7px；色 = 墨 62% 掺气泡面（对底实测见施工回执），不再 currentColor + .75 二次压暗。 */
.msg-gallery-link::after {
    content: "";
    flex: 0 0 auto;
    box-sizing: border-box;              /* 显式声明：四模板的 * 选择器不覆盖伪元素，几何不靠默认值 */
    width: 7px;
    height: 7px;
    margin: -1px 2px 0 6px;
    border-top: 2px solid currentColor;
    border-right: 2px solid currentColor;
    color: var(--text-primary, #1C1C1E);                                                               /* 兜底：纯墨 */
    color: color-mix(in srgb, var(--text-primary, #1C1C1E) 62%, var(--bubble-other, #FFFFFF));
    transform: rotate(45deg);
    transition: transform 120ms ease;
}

/* 桌面 hover：iOS 行语汇的桌面半价 = 面掺 4% 墨 + 箭头同档右挪；不碰描边/阴影
   （合成终稿只写了按压，hover 按同一语汇对齐——若复核不认可回到本块单独议） */
@media (hover: hover) {
    .msg-gallery-link:hover {
        background: rgba(127, 127, 127, .07);                                                          /* 兜底 */
        background: color-mix(in srgb, var(--text-primary, #1C1C1E) 4%, var(--bubble-other, #FFFFFF));
    }
    /* rotate(45deg) 后 translate(1.5px,-1.5px) 的合成位移恰为水平向右 ≈2.1px（本地轴已转 45°） */
    .msg-gallery-link:hover::after { transform: rotate(45deg) translate(1.5px, -1.5px); }
}

/* 按下态：双通道 = 面掺 8% 墨（iOS 行高亮语汇；锚 --text-primary，暗色自动提亮）
   ＋ 箭头右挪 ≈2.1px。删按下缩放（旧版 0.98 倍）、删边色变化、删阴影收（融合卡不靠"按扁"表达）。
   60ms 出（≤100ms 可感，ISUX 口径）。0ms 延迟由 .is-pressed（app.js pointerdown 加，
   iOS 上 :active 不可靠）与 :active（Android/桌面）双源触发，机制不动。 */
.msg-gallery-link:active,
.msg-gallery-link.is-pressed {
    background: rgba(127, 127, 127, .14);                                                              /* 兜底 ≈ 8% 深墨视觉当量 */
    background: color-mix(in srgb, var(--text-primary, #1C1C1E) 8%, var(--bubble-other, #FFFFFF));
    transition-duration: 60ms;
}
.msg-gallery-link:active::after,
.msg-gallery-link.is-pressed::after {
    transform: rotate(45deg) translate(1.5px, -1.5px);
    transition-duration: 60ms;
}

/* 键盘可达 + 焦点可见（WCAG 2.4.7）：元素本就是真 <button>，焦点环零改动 */
.msg-gallery-link:focus-visible {
    outline: 2px solid var(--brand-primary, #07C160);                                                           /* 兜底行 */
    outline: 2px solid color-mix(in srgb, var(--brand-primary, #07C160) 30%, var(--text-primary, #1C1C1E));
    outline-offset: 2px;
}

/* 尊重系统"减少动态效果"：瞬时切换保留反馈、去动画（按压已无缩放可关） */
@media (prefers-reduced-motion: reduce) {
    .msg-gallery-link { transition: none; }
    .msg-gallery-link::after { transition: none; }
}
