/* ==========================================================================
   AI客服 · 前台挂件
   ==========================================================================
   视觉语言按 beautifului.dev（Beautiful UI for AI-native interfaces）重做：
     · 面板是"一块深色玻璃板"，不是聊天软件的白底气泡墙；
     · 助手一轮 = 一个 turn 容器，里面依次是工具链 chip、推理行、正文、卡片；
     · 工具调用是带状态点的横向 chip，不是又一个气泡；
     · 输入区是 Prompt Bar：一整块圆角面板，正文在上、工具行在下、圆形发送键；
     · 间距紧凑（4px 栅格），分隔靠 1px 低对比描边而不是留白。
   配色仍然全部来自后台字段（--acs-* 由 renderWidget 内联注入），所以同一套形态
   在浅色站点和深色站点都成立；预设只换变量，不换结构。

   布局要点：面板用 flex 而不是 grid。飘带 / 人工入口 / AI 标识都是可选节点，
   用定位行会随它们出现或消失整体错位，把聊天区压成内容高度、卡片被腰斩。
   ========================================================================== */

/* 变量与排版是**类选择器**，这样后台实时预览可以直接复用同一份样式渲染真实标记
 * （预览不再维护一套 acs-pv-* 的仿制品，也就不可能和前台走样）。
 * 只有"固定定位 / 显形"这类纯前台行为才挂在 id 上。 */
.acs-widget {
    /* 以下变量由服务端按后台字段内联覆盖，这里只做兜底 */
    --acs-accent: #4F46E5;
    --acs-surface: #FFFFFF;
    --acs-text: #111827;
    --acs-muted: #6B7280;
    --acs-header-bg: #FFFFFF;
    --acs-header-fg: #111827;
    --acs-bot-bubble: #F4F4F5;
    --acs-bot-text: #111827;
    --acs-vis-bubble: #4F46E5;
    --acs-vis-text: #FFFFFF;
    --acs-font: 14px;
    --acs-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif;
    --acs-size: 56px;
    --acs-panel-width: 396px;
    --acs-panel-height: 600px;
    --acs-panel-radius: 16px;
    --acs-launcher-corner: 10px;
    --acs-desktop-x: 24px;
    --acs-desktop-y: 24px;
    --acs-mobile-x: 16px;
    --acs-mobile-y: 16px;
    /* 软键盘占掉的高度，由 JS 从 visualViewport 量出来（见 trackKeyboard）。
     * 为什么不能靠 dvh：iOS 上软键盘是覆盖层，100dvh 只跟随浏览器工具条的收放，
     * 键盘弹起时一点都不变 —— 面板还是原高、还是贴在布局视口底部，
     * 于是输入框和发送键正好被键盘压在下面，访客打字时看不见自己打了什么。 */
    --acs-kb: 0px;
    --acs-panel-gap: 12px;
    --acs-teaser-dx: -12px;
    --acs-teaser-dy: 0px;
    --acs-ribbon-dx: 0px;
    --acs-ribbon-dy: 0px;
    --acs-badge-dx: 2px;
    --acs-badge-dy: 2px;
    --acs-launcher-dx: 0px;
    --acs-launcher-dy: 0px;
    --acs-z: 2147480000;

    /* 派生量：描边、分隔、悬浮底色统一从正文色混出来，深浅两套都自适应 */
    --acs-line: color-mix(in srgb, var(--acs-text) 12%, transparent);
    --acs-line-soft: color-mix(in srgb, var(--acs-text) 7%, transparent);
    --acs-hover: color-mix(in srgb, var(--acs-text) 5%, transparent);
    --acs-sunken: color-mix(in srgb, var(--acs-text) 4%, transparent);
    --acs-gap: 4px;
    /* 内边距分两层：base 由疏密档给，实际用的那个再套一层，是为了让极窄屏
     * 能"设上限"而不是"改成定值"——直接覆盖 --acs-pad 会把宽松档也压回紧凑。 */
    --acs-pad-base: 14px;
    --acs-pad: var(--acs-pad-base);
    /* 多渠道展开里那颗圆形图标的直径：跟着浮标尺寸缩放，但不小于可点面积下限 */
    --acs-chip: max(32px, calc(var(--acs-size) * .62));

    /* 圆角阶梯。全部从站长那一个"弹窗圆角"派生，而不是每处写死一个绝对值：
     * 之前面板是变量、里面二十处是硬值，站长把圆角调成 0 得到的是"方壳套圆块"，
     * 调到 32 得到的是"圆壳套硬角"——内圆角大于外圆角是最典型的外行感。
     * 下限压到 0 而不是 8，就是为了让"方角面板"这一档真的能做到方。
     * 999px / 50% 那一批（胶囊、圆形按钮、状态点、头像）是形状语义不是圆角档位，不并进来。 */
    --acs-r-lg: clamp(0px, calc(var(--acs-panel-radius) - 2px), 20px);
    --acs-r-md: clamp(0px, calc(var(--acs-panel-radius) - 5px), 14px);
    --acs-r-sm: clamp(0px, calc(var(--acs-panel-radius) - 8px), 10px);
    --acs-r-xs: clamp(0px, calc(var(--acs-panel-radius) - 11px), 6px);
    /* 气泡"尾角"：贴着说话人那一侧收紧的那个角 */
    --acs-r-tail: calc(var(--acs-r-lg) * .3);

    /* 动效档位。之前全站散着 9 种时长和 5 条缓动，同一类交互给不同时长、
     * 层级差很大的两件事又给同一个时长，收成三档语义：
     * 1 = 颜色/底色这类微交互，2 = 位移与浮层，3 = 面板与飘带这类大块。 */
    --acs-dur-1: 130ms;
    --acs-dur-2: 190ms;
    --acs-dur-3: 260ms;
    --acs-ease: cubic-bezier(.22, 1, .36, 1);
    --acs-ease-in: cubic-bezier(.4, 0, 1, 1);
    --acs-spring: cubic-bezier(.34, 1.56, .64, 1);

    /* 排版三个杠杆。不带内嵌字体时字族在中文站点上基本塌成一种，
     * 字重 / 字距 / 行高是在任何已装字体上都一定生效的量，所以"字面"这一维压在它们上。 */
    --acs-lh: 1.6;
    /* 次要长文（引流气泡、卡片摘要、条款、输入框）比正文收一档。派生而不是写死，
     * 是为了让"字面"这一维也能带动它们——否则宽松档只有气泡变松，说明文字还是原样。 */
    --acs-lh-sm: calc(var(--acs-lh) - .1);
    --acs-track: 0em;
    --acs-w-bold: 700;
    --acs-w-strong: 600;
    --acs-w-mid: 500;

    /* 语义色。之前绿/红/橙散落在七八处硬值里，深色面板上一律偏亮、对比不足。 */
    --acs-ok: #10B981;
    --acs-warn: #F59E0B;
    --acs-err: #EF4444;
    /* 压在强调色实心底上的前景色。默认取访客气泡文字色——站长本来就是按
     * "铺在强调色上"挑的那个值；写死 #fff 时浅色强调色（薄荷、柠檬、粉）上直接不可读。 */
    --acs-on-accent: var(--acs-vis-text);

    /* 边缘语言（theme.edge）。面板靠什么和站点分开：描边宽度、描边颜色、
     * 以及一层可叠加的抬起投影。默认档等于原来的行为。 */
    --acs-edge-w: 1px;
    --acs-edge-color: var(--acs-line);
    --acs-edge-top: var(--acs-line);
    --acs-lift: 0 0 0 0 transparent;
    --acs-panel-bg: var(--acs-surface);

    /* 默认不打阴影：靠 1px 描边分层。需要投影的站点在"弹窗投影强度"里自己开。
     * 取值用 0 0 0 0 transparent 而不是关键字 none —— none 不允许出现在
     * box-shadow 的列表里，一旦和 --acs-lift 并列，整条声明会被整体丢弃。 */
    --acs-shadow: 0 0 0 0 transparent;
    /* 投影的色调与整体强度。浅底用蓝黑（比纯黑干净），深底由 .acs-tone--dark 换成纯黑并加重。 */
    --acs-shadow-rgb: 15, 23, 42;
    --acs-shadow-boost: 1;

    /* 浮标的抬起。和面板那档投影分开，而且**默认就有**：
     * 面板有 1px 描边和自己的底色，靠边界就能和页面分开；浮标是一颗实心圆压在
     * 站点内容上，没有投影时它读起来像"画在页面里的一个圆"而不是"浮在页面上方的按钮"
     * —— 所有成熟挂件（Chaty / Crisp / Intercom / Tidio）都给浮标打投影，这不是风格
     * 偏好而是"它是可点的浮层"这条信息本身。
     * 用 filter: drop-shadow 而不是 box-shadow，是因为 acs-pulse 提醒动画整条占用了
     * box-shadow（关键帧会把静态投影一起顶掉，开着提醒动画的浮标就会一闪一闪地丢投影）；
     * drop-shadow 走 filter，两者互不干扰，而且它按 alpha 轮廓打投影——胶囊、圆形、
     * 带透明边的图片浮标都不用各写一遍形状。 */
    --acs-launcher-drop:
        drop-shadow(0 1px 2px rgba(var(--acs-shadow-rgb), calc(.14 * var(--acs-shadow-boost))))
        drop-shadow(0 6px 14px rgba(var(--acs-shadow-rgb), calc(.2 * var(--acs-shadow-boost))));
    --acs-launcher-drop-hover:
        drop-shadow(0 2px 4px rgba(var(--acs-shadow-rgb), calc(.16 * var(--acs-shadow-boost))))
        drop-shadow(0 10px 22px rgba(var(--acs-shadow-rgb), calc(.26 * var(--acs-shadow-boost))));

    color: var(--acs-text);
    font-family: var(--acs-font-family);
    font-size: var(--acs-font);
    line-height: var(--acs-lh);
    letter-spacing: var(--acs-track);

    /* 宿主主题隔离。挂件是 body 末尾的顶层节点，主题里带容器作用域的选择器选不到它，
     * 真正会漏进来的是元素选择器（button/a/input）、body 上可继承的属性、以及带
     * !important 的规则。:where() 特异性为 0，整条只算 .acs-widget 的 0,1,0，
     * 与后面的组件规则同级，靠源码顺序让组件规则胜出，所以不会反过来盖掉自己。 */
    text-align: start;
    text-transform: none;
    text-shadow: none;
    text-indent: 0;
    font-style: normal;
    font-variant: normal;
    word-spacing: normal;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

.acs-widget :where(*) { text-shadow: none; }
/* 只重置"主题一定会用元素选择器写"的那几个：button/a/input 的 text-transform 与
 * letter-spacing（font: inherit 简写不含这两项，所以「提交询盘」会变成带字距的全大写）。
 * 刻意不在后代上重置 text-align / text-indent —— 那两个靠继承工作，
 * 在后代上归零会把组件自己设在容器上的居中一起打断。 */
.acs-widget :where(button, a, input, textarea, select, label) {
    text-transform: none;
    letter-spacing: inherit;
    font-style: normal;
}
/* 原生外观归零。iOS Safari 会给 input/textarea 自己加内阴影和圆角，和这里的
 * 圆角叠成双层轮廓；.acs-consent-box 是真复选框，依赖原生外观，必须排除在外。 */
.acs-widget :where(textarea, input:not([type="checkbox"]):not([type="radio"])) {
    appearance: none;
    -webkit-appearance: none;
}
/* 元素选择器还会漏进来第二类东西：主题直接写在 img / form / p / label 这几个标签上的
 * **盒模型**。上面那几条只压住了会继承的排版属性，而 border / box-shadow / filter /
 * max-width / margin 都不继承——它们靠"主题用了元素选择器、而我们没有为这个标签写过
 * 规则"这条缝进来。典型现场是 img { max-width: 100%; box-shadow: 0 2px 8px … } 和
 * form { margin-bottom: 1.6em; background: #f7f7f7 }：前者给每张缩略图和头像加一道
 * 不属于挂件的投影，后者在询盘表单下面留一段说不清来路的空白。
 * 仍然用 :where()（特异性 0）而且排在所有组件规则之前，组件后面照常各写各的；
 * 只列"主题真的会写、而挂件真的会输出"的标签，不做 * 通配归零——那会连带打掉浏览器
 * 给 button / textarea 的可用默认值。挂件里每个 <img> 都自带明确的宽高，
 * 所以 max-width: none 不会放出任何一张原图大小的图片。 */
.acs-widget :where(img) { max-width: none; border: 0; box-shadow: none; filter: none; }
.acs-widget :where(form, p, label) { margin: 0; padding: 0; }
.acs-widget :where(form) { border: 0; background: none; }

#ai-customer-service-widget.acs-widget {
    position: fixed;
    z-index: var(--acs-z);
    bottom: var(--acs-desktop-y);
    opacity: 0;
    pointer-events: none;
    /* pointer-events 只挡鼠标。光 opacity:0 的话整个挂件仍在 Tab 序里：键盘用户会
     * 一路 Tab 进一块看不见的面板，读屏也照念。visibility 才能同时摘掉焦点和无障碍树，
     * 而且它可过渡（不像 display），淡入淡出照旧。 */
    visibility: hidden;
    transform: translateY(8px);
    transition: opacity var(--acs-dur-2) var(--acs-ease), transform var(--acs-dur-2) var(--acs-ease),
        visibility 0s linear var(--acs-dur-2);
}

#ai-customer-service-widget.acs-widget--right { right: var(--acs-desktop-x); }
#ai-customer-service-widget.acs-widget--left { left: var(--acs-desktop-x); }
#ai-customer-service-widget.acs-widget[data-visible="true"] {
    opacity: 1;
    pointer-events: auto;
    visibility: visible;
    /* 出现时立刻可见，消失时等淡出结束再撤——延迟只加在隐藏那一侧 */
    transition-delay: 0s, 0s, 0s;
    transform: translateY(0);
}

.acs-widget *,
.acs-widget *::before,
.acs-widget *::after { box-sizing: border-box; }

.acs-sr-only {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

/* 投影档位。两层写法：ambient（远、大、极淡，交代"离页面多高"）+ key（近、小、稍深，
 * 交代边缘）。单层投影要么发灰要么像糊了一团，这是所有成熟挂件都在用的分解方式。
 *
 * 色调（rgb 三元组）走 --acs-shadow-rgb：深底面板上 rgba(15,23,42,.1) 等于什么都没打，
 * 所以 .acs-tone--dark 把它换成纯黑并整体加重，再补一道顶部内高光把面板边缘"提"出来。 */
.acs-shadow--none { --acs-shadow: 0 0 0 0 transparent; }
.acs-shadow--sm {
    --acs-shadow:
        0 1px 2px rgba(var(--acs-shadow-rgb), calc(.05 * var(--acs-shadow-boost))),
        0 1px 3px -1px rgba(var(--acs-shadow-rgb), calc(.08 * var(--acs-shadow-boost)));
}
.acs-shadow--md {
    --acs-shadow:
        0 1px 3px rgba(var(--acs-shadow-rgb), calc(.05 * var(--acs-shadow-boost))),
        0 6px 16px -4px rgba(var(--acs-shadow-rgb), calc(.12 * var(--acs-shadow-boost)));
}
.acs-shadow--lg {
    --acs-shadow:
        0 2px 6px rgba(var(--acs-shadow-rgb), calc(.06 * var(--acs-shadow-boost))),
        0 16px 40px -10px rgba(var(--acs-shadow-rgb), calc(.18 * var(--acs-shadow-boost)));
}

/* 色调是服务端按窗口背景色的相对亮度算出来的（rootClasses → acs-tone--dark/light），
 * 不是又一个开关。深底下描边要提亮而不是压暗，否则分层全部消失。 */
.acs-tone--dark {
    --acs-shadow-rgb: 0, 0, 0;
    --acs-shadow-boost: 2.2;
    --acs-line: color-mix(in srgb, var(--acs-text) 16%, transparent);
    --acs-line-soft: color-mix(in srgb, var(--acs-text) 10%, transparent);
    --acs-hover: color-mix(in srgb, var(--acs-text) 11%, transparent);
    /* 深底上 4% 的亮色叠加只有约 1.08:1，肉眼等于没有；而 --acs-sunken 承担着
     * 输入区、工具 chip、推荐卡、询盘表单、离线条六个体块的底色，塌了就整块玻璃板。 */
    --acs-sunken: color-mix(in srgb, var(--acs-text) 9%, transparent);
    --acs-ok: #34D399;
    --acs-warn: #FBBF24;
    --acs-err: #F87171;
}

/* ------------------------------------------------------------------ 边缘语言（theme.edge）
 * 六档互不重叠，是"六套预设看起来不一样"里带宽最大的一维。
 * 每一档只改这几个变量，具体画在哪由下面各个浮层自己决定：
 *   --acs-edge-w / --acs-edge-color / --acs-edge-top  描边
 *   --acs-lift                                        额外一层抬起（可以是 inset）
 *   --acs-panel-bg                                    面板底（只有玻璃档要动）
 * 它和站长自己的"弹窗投影强度"是叠加关系，不互相覆盖。 */

/* 描边：默认档。1px 淡线分层，什么站点都不得罪。 */
.acs-edge--hairline { --acs-edge-w: 1px; --acs-edge-color: var(--acs-line); }

/* 全平：连描边都收到 7%，层次只靠 --acs-sunken 的底色深浅。开发者工具的做法。 */
.acs-edge--flat {
    --acs-edge-w: 1px;
    --acs-edge-color: var(--acs-line-soft);
    --acs-edge-top: var(--acs-line-soft);
}

/* 玻璃：面板半透 + 背景模糊 + 顶边高光。宿主页真的会透进来一点。 */
.acs-edge--glass {
    --acs-panel-bg: color-mix(in srgb, var(--acs-surface) 88%, transparent);
    --acs-edge-color: color-mix(in srgb, var(--acs-text) 14%, transparent);
    --acs-edge-top: color-mix(in srgb, #FFFFFF 26%, transparent);
    --acs-lift: inset 0 1px 0 0 color-mix(in srgb, #FFFFFF 10%, transparent);
}
.acs-edge--glass .acs-panel,
.acs-edge--glass .acs-picker,
.acs-edge--glass .acs-channels--list {
    backdrop-filter: blur(16px) saturate(1.4);
    -webkit-backdrop-filter: blur(16px) saturate(1.4);
}
/* 不支持背景模糊时半透的面板会直接露出站点内容，文字压在上面不可读，必须退回不透明。 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .acs-edge--glass { --acs-panel-bg: var(--acs-surface); }
}

/* 暖发光：不用灰阴影，用强调色本身扩散出去。强调色六套各不相同，
 * 所以这一档等于免费拿到一种"这个站的颜色在发光"的观感（Material 3 的 tonal elevation）。 */
.acs-edge--glow {
    --acs-edge-color: color-mix(in srgb, var(--acs-accent) 22%, transparent);
    --acs-lift: 0 10px 30px -10px color-mix(in srgb, var(--acs-accent) 38%, transparent);
}

/* 硬偏移：2px 实边 + 不带模糊的偏移块。纸感/手作那一路的物理隐喻。 */
.acs-edge--offset {
    --acs-edge-w: 2px;
    --acs-edge-color: color-mix(in srgb, var(--acs-text) 82%, transparent);
    --acs-edge-top: color-mix(in srgb, var(--acs-text) 82%, transparent);
    --acs-lift: 4px 4px 0 0 color-mix(in srgb, var(--acs-text) 82%, transparent);
}

/* 内斜面：底边一道内投影，暗示实体但不浮起。B2B 目录那一路（Polaris 的 shadow-bevel）。 */
.acs-edge--bevel {
    --acs-edge-w: 1px;
    --acs-edge-color: var(--acs-line);
    --acs-lift: inset 0 -1px 0 0 color-mix(in srgb, var(--acs-text) 11%, transparent);
}

/* ------------------------------------------------------------------ 疏密（theme.density）
 * 之前这一维只改了两处 gap，等于"没有这一维"。收成一组变量，让它真的能把
 * 同一段对话压成三种体量：紧凑（客服后台那种信息密度）→ 舒适 → 宽松（落地页那种）。
 * 边距用变量而不是各处写死，是因为移动端断点还要再把 --acs-pad 压一次，
 * 两套写死值会互相打架。 */
.acs-widget {
    --acs-turn-gap: 10px;        /* 两轮对话之间 */
    --acs-stack-gap: 6px;        /* 同一轮里气泡与卡片之间 */
    --acs-bubble-pad-y: 9px;
    --acs-bubble-pad-x: 12px;
    --acs-head-pad-y: 12px;
    --acs-chat-pad-t: 14px;
}
.acs-density--compact {
    --acs-pad-base: 11px;
    --acs-turn-gap: 7px;
    --acs-stack-gap: 4px;
    --acs-bubble-pad-y: 7px;
    --acs-bubble-pad-x: 10px;
    --acs-head-pad-y: 9px;
    --acs-chat-pad-t: 10px;
}
.acs-density--roomy {
    --acs-pad-base: 18px;
    --acs-turn-gap: 15px;
    --acs-stack-gap: 8px;
    --acs-bubble-pad-y: 12px;
    --acs-bubble-pad-x: 16px;
    --acs-head-pad-y: 16px;
    --acs-chat-pad-t: 18px;
}

/* ------------------------------------------------------------------ 字面（theme.type）
 * 动字重 / 字距 / 行高 / **字号阶梯**——不带内嵌字体时字族在中文站点上基本塌成
 * 同一种，这几个量在任何已装字体上都一定生效。
 * neutral 档不写规则：它就是 .acs-widget 里那组底值，重复一遍只会多一个走样的机会。
 *
 * 字号阶梯（--acs-fs-*）：以前每处副文字各写一个 calc(var(--acs-font) * .NN)，
 * 全窗攒出十五个互相差 .02 的比例，等于没有层级——两个本该同级的标签会差半像素，
 * 而本该分级的（标题 / 正文 / 元信息）又挤在一起。收成五级之后这一维才真的能调：
 * tight 把副文字压小（层级对比更强，开发者工具那一路），loose 抬高（层级更平，
 * 读长句更省力）。 */
.acs-widget {
    --acs-fs-2xs: calc(var(--acs-font) * .72);   /* 时间戳、字数、角标 */
    --acs-fs-xs: calc(var(--acs-font) * .8);     /* 元信息、次级说明 */
    --acs-fs-sm: calc(var(--acs-font) * .86);    /* 快捷问题、chip、摘要 */
    --acs-fs-md: calc(var(--acs-font) * .92);    /* 卡片标题、按钮 */
    --acs-fs-lg: calc(var(--acs-font) * 1);      /* 面板标题、浮标文字 */
}
.acs-type--tight {
    --acs-lh: 1.45;
    --acs-track: -.005em;
    --acs-w-bold: 800;
    --acs-w-strong: 700;
    --acs-w-mid: 600;
    --acs-fs-2xs: calc(var(--acs-font) * .68);
    --acs-fs-xs: calc(var(--acs-font) * .76);
    --acs-fs-sm: calc(var(--acs-font) * .83);
    --acs-fs-md: calc(var(--acs-font) * .91);
    --acs-fs-lg: calc(var(--acs-font) * 1);
}
.acs-type--loose {
    --acs-lh: 1.78;
    --acs-track: .014em;
    --acs-w-bold: 650;
    --acs-w-strong: 550;
    --acs-w-mid: 450;
    --acs-fs-2xs: calc(var(--acs-font) * .76);
    --acs-fs-xs: calc(var(--acs-font) * .84);
    --acs-fs-sm: calc(var(--acs-font) * .89);
    --acs-fs-md: calc(var(--acs-font) * .94);
    --acs-fs-lg: calc(var(--acs-font) * 1.02);
}

/* ------------------------------------------------------------------ 浮标 */

.acs-launcher {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--acs-size);
    height: var(--acs-size);
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: var(--acs-accent);
    color: var(--acs-on-accent);
    font: inherit;
    cursor: pointer;
    filter: var(--acs-launcher-drop);
    translate: var(--acs-launcher-dx) var(--acs-launcher-dy);
    transition: transform var(--acs-dur-2) var(--acs-ease), background var(--acs-dur-1) var(--acs-ease),
        filter var(--acs-dur-2) var(--acs-ease);
}
.acs-launcher:hover { transform: translateY(-2px) scale(1.03); filter: var(--acs-launcher-drop-hover); }
.acs-launcher:active { transform: translateY(0) scale(.97); filter: var(--acs-launcher-drop); }
.acs-launcher:focus-visible {
    outline: 2px solid var(--acs-accent);
    outline-offset: 3px;
}
.acs-launcher-icon { font-size: calc(var(--acs-size) * .4); line-height: 1; }
.acs-launcher-img {
    width: 62%;
    height: 62%;
    border-radius: inherit;
    object-fit: cover;
}
.acs-launcher-label { display: none; }
.acs-launcher-close { display: none; }

/* 胶囊：带文字。展开后**保留**文字，只把右侧图标换成关闭——只换内容会让胶囊退化成方块。 */
.acs-widget--pill .acs-launcher {
    width: auto;
    min-width: var(--acs-size);
    height: var(--acs-size);
    padding: 0 18px;
    gap: 9px;
    border-radius: var(--acs-launcher-corner);
}
.acs-widget--pill .acs-launcher-label {
    display: inline;
    max-width: 180px;
    overflow: hidden;
    font-size: var(--acs-fs-lg);
    font-weight: var(--acs-w-strong);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.acs-widget--pill .acs-launcher.is-open .acs-launcher-icon { display: none; }
.acs-widget--pill .acs-launcher.is-open .acs-launcher-close { display: inline; font-size: var(--acs-fs-lg); }
.acs-widget--bubble .acs-launcher.is-open .acs-launcher-icon { display: none; }
.acs-widget--bubble .acs-launcher.is-open .acs-launcher-close {
    display: inline;
    font-size: calc(var(--acs-size) * .32);
}

/* 未读角标。挂在浮标的**外侧**上角：站长把挂件放左边时，写死 right:0 会让角标压向
 * 页面中央那一侧，正好是面板展开的方向，两者叠在一起。所以和面板 / 气泡 / 飘带一样
 * 按 .acs-widget--right|--left 分侧，dx 的正方向也跟着翻（站长填的是"往外挪几像素"）。 */
.acs-launcher.has-badge::after {
    content: "";
    position: absolute;
    top: 0;
    width: 11px;
    height: 11px;
    border: 2px solid var(--acs-surface);
    border-radius: 50%;
    background: var(--acs-err);
}
.acs-widget--right .acs-launcher.has-badge::after {
    right: 0;
    translate: var(--acs-badge-dx) var(--acs-badge-dy);
}
.acs-widget--left .acs-launcher.has-badge::after {
    left: 0;
    translate: calc(-1 * var(--acs-badge-dx)) var(--acs-badge-dy);
}

/* 提醒动画：访客第一次打开前循环播放 */
@keyframes acs-wiggle {
    0%, 88%, 100% { transform: rotate(0deg); }
    91% { transform: rotate(-9deg); }
    94% { transform: rotate(9deg); }
    97% { transform: rotate(-5deg); }
}
@keyframes acs-bounce {
    0%, 82%, 100% { transform: translateY(0); }
    88% { transform: translateY(-9px); }
    94% { transform: translateY(-4px); }
}
@keyframes acs-pulse {
    0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--acs-accent) 42%, transparent); }
    70% { box-shadow: 0 0 0 14px transparent; }
    100% { box-shadow: 0 0 0 0 transparent; }
}
.acs-launcher.acs-fx--wiggle { animation: acs-wiggle 4s infinite ease-in-out; }
.acs-launcher.acs-fx--bounce { animation: acs-bounce 3.4s infinite ease-in-out; }
.acs-launcher.acs-fx--pulse { animation: acs-pulse 2s infinite ease-out; }

/* ------------------------------------------------------------------ 悬停提示与引流气泡 */

.acs-tooltip {
    position: absolute;
    bottom: 30%;
    padding: 7px 11px;
    border-radius: var(--acs-r-sm);
    background: color-mix(in srgb, var(--acs-text) 92%, #000);
    color: var(--acs-surface);
    font-size: var(--acs-fs-sm);
    /* nowrap 让收缩到合适宽度算出的是整行宽，不会塌成竖条 */
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--acs-dur-1) var(--acs-ease);
}
.acs-widget--right .acs-tooltip { right: calc(100% + 12px); }
.acs-widget--left .acs-tooltip { left: calc(100% + 12px); }
.acs-launcher:hover + .acs-tooltip,
.acs-launcher:focus-visible + .acs-tooltip { opacity: 1; }

/* 关键：绝对定位 + 只给 right 时，收缩到合适宽度的可用宽是
 * 「包含块宽度 − right」= 100% − (100% + 12px) = −12px，会被截到 0 从而取 min-content，
 * 中文再逐字折行 —— 1.1.0 的引流气泡就是这样塌成一列单字的。给 width:max-content 即可。 */
.acs-teaser {
    position: absolute;
    bottom: 16%;
    display: flex;
    align-items: flex-start;
    gap: 8px;
    width: max-content;
    max-width: min(250px, calc(100vw - 120px));
    padding: 10px 12px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-md);
    background: var(--acs-surface);
    color: var(--acs-text);
    font-size: var(--acs-fs-md);
    line-height: var(--acs-lh-sm);
    box-shadow: var(--acs-shadow);
    translate: var(--acs-teaser-dx) var(--acs-teaser-dy);
    animation: acs-teaser-in var(--acs-dur-3) var(--acs-ease);
    cursor: pointer;
}
.acs-widget--right .acs-teaser { right: calc(100% + 12px); }
.acs-widget--left .acs-teaser { left: calc(100% + 12px); }
@keyframes acs-teaser-in { from { opacity: 0; translate: var(--acs-teaser-dx) calc(var(--acs-teaser-dy) + 6px); } }
.acs-teaser-text { overflow-wrap: anywhere; }
.acs-teaser-close {
    flex: 0 0 auto;
    order: 2;
    margin: -2px -4px 0 0;
    padding: 0 2px;
    border: 0;
    border-radius: var(--acs-r-xs);
    background: transparent;
    color: var(--acs-muted);
    cursor: pointer;
    font-size: var(--acs-fs-2xs);
    line-height: var(--acs-lh-sm);
}
.acs-teaser-close:hover { color: var(--acs-text); }

/* 面板打开时这两个必须收起，否则和浮标叠在一起 */
.acs-widget[data-acs-open="true"] .acs-teaser,
.acs-widget[data-acs-open="true"] .acs-tooltip { display: none !important; }

/* 不渲染浮标时（show_launcher=false），面板不该再为浮标留高度 */
.acs-widget--bare { --acs-size: 0px; --acs-panel-gap: 0px; }

/* ------------------------------------------------------------------ 浮标多渠道展开
 * Chaty 那类插件的核心形态：浮标旁边直接摊开微信 / WhatsApp / 电话，
 * 想直接联系的访客不必先跟机器人说一句话。条目来自"资料 → 负责人社媒"，
 * 只维护一处。与面板互斥（打开面板即收起），所以可以共用浮标上方那片空间。 */

.acs-channels-toggle {
    position: absolute;
    bottom: calc(var(--acs-size) + 8px);
    display: grid;
    place-items: center;
    width: var(--acs-chip);
    height: var(--acs-chip);
    padding: 0;
    border: 1px solid var(--acs-line);
    border-radius: 999px;
    background: var(--acs-surface);
    color: var(--acs-text);
    font: inherit;
    box-shadow: var(--acs-shadow);
    cursor: pointer;
    /* 和浮标同一套抬起：它就贴在浮标旁边，一个有投影一个没有会读成"其中一个是贴纸" */
    filter: var(--acs-launcher-drop);
    translate: var(--acs-launcher-dx) var(--acs-launcher-dy);
    transition: transform var(--acs-dur-2) var(--acs-ease), background var(--acs-dur-1) var(--acs-ease),
        filter var(--acs-dur-2) var(--acs-ease);
}
.acs-widget--right .acs-channels-toggle { right: 0; }
.acs-widget--left .acs-channels-toggle { left: 0; }
.acs-channels-toggle:hover { background: var(--acs-hover); transform: scale(1.06); filter: var(--acs-launcher-drop-hover); }
.acs-channels-toggle:focus-visible { outline: 2px solid var(--acs-accent); outline-offset: 2px; }
.acs-channels-toggle.is-open { transform: rotate(90deg); }
.acs-channels-icon { font-size: calc(var(--acs-chip) * .42); line-height: 1; }

.acs-channels {
    position: absolute;
    bottom: calc(var(--acs-size) + var(--acs-chip) + 16px);
    display: flex;
    gap: 8px;
    translate: var(--acs-launcher-dx) var(--acs-launcher-dy);
    animation: acs-channels-in var(--acs-dur-2) var(--acs-ease);
}
/* 面板同款兜底：display:flex 会盖掉 hidden 属性的 UA 样式，不压回去就是加载即展开 */
.acs-widget .acs-channels[hidden],
#ai-customer-service-widget .acs-channels[hidden] { display: none !important; }
@keyframes acs-channels-in { from { opacity: 0; translate: var(--acs-launcher-dx) calc(var(--acs-launcher-dy) + 8px); } }
.acs-widget--right .acs-channels { right: 0; }
.acs-widget--left .acs-channels { left: 0; }
.acs-widget[data-acs-open="true"] .acs-channels,
.acs-widget[data-acs-open="true"] .acs-channels-toggle { display: none !important; }

/* 12 个渠道 × 最大浮标（96px → chip 59.5px）叠起来有 700px 多，比很多笔记本的
 * 视口还高，顶上几个会飘到屏幕外点不着。给一个按视口算的上限 + 纵向滚动兜住：
 * 减掉的是浮标本身、展开按钮和上下各 12px 的余量。 */
.acs-channels {
    max-height: calc(100dvh - var(--acs-size) - var(--acs-chip) - 48px);
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* fan：一列圆形图标，从按钮往上长。column-reverse 让第一个渠道贴着按钮。 */
.acs-channels--fan {
    flex-direction: column-reverse;
    align-items: flex-end;
    /* 圆形 chip 自带投影，overflow 会把它切掉一圈，留 4px 内边距让投影完整 */
    padding: 4px;
    margin: -4px;
}
.acs-widget--left .acs-channels--fan { align-items: flex-start; }
.acs-channels--fan .acs-channel {
    width: var(--acs-chip);
    height: var(--acs-chip);
    border-radius: 999px;
    padding: 0;
}
.acs-channels--fan .acs-channel-name { display: none; }

/* list：一块带标题的卡片，图标配文字。渠道多、名字需要写清楚时用这套。 */
.acs-channels--list {
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    width: max-content;
    max-width: min(240px, calc(100vw - 88px));
    padding: 8px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-md);
    background: var(--acs-surface);
    box-shadow: var(--acs-shadow);
}
.acs-channels-title {
    padding: 2px 8px 6px;
    color: var(--acs-muted);
    font-size: var(--acs-fs-xs);
}
.acs-channels--list .acs-channel {
    justify-content: flex-start;
    gap: 9px;
    padding: 7px 8px;
    border: 0;
    border-radius: var(--acs-r-sm);
    background: transparent;
    box-shadow: none;
}
.acs-channels--list .acs-channel:hover { background: var(--acs-hover); }

.acs-channel {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    border: 1px solid var(--acs-line);
    background: var(--acs-surface);
    color: var(--acs-text);
    font: inherit;
    font-size: var(--acs-fs-md);
    text-decoration: none;
    box-shadow: var(--acs-shadow);
    cursor: pointer;
    transition: transform var(--acs-dur-2) var(--acs-ease), background var(--acs-dur-1) var(--acs-ease);
}
.acs-channel:hover { transform: translateY(-1px); background: var(--acs-hover); }
.acs-channel:focus-visible { outline: 2px solid var(--acs-accent); outline-offset: 2px; }
.acs-channel-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.acs-channel.acs-social-copied { color: var(--acs-accent); }

/* ------------------------------------------------------------------ 面板外壳 */

.acs-panel {
    position: absolute;
    bottom: calc(var(--acs-size) + var(--acs-panel-gap));
    display: flex;
    flex-direction: column;
    width: var(--acs-panel-width);
    height: var(--acs-panel-height);
    max-width: calc(100vw - 24px);
    max-height: calc(100dvh - 32px);
    overflow: hidden;
    border: var(--acs-edge-w) solid var(--acs-edge-color);
    border-top-color: var(--acs-edge-top);
    border-radius: var(--acs-panel-radius);
    background: var(--acs-panel-bg);
    box-shadow: var(--acs-lift), var(--acs-shadow);
    isolation: isolate;
}
/* 与子节点数量无关：只有聊天区抢剩余空间，其余一律按内容高度 */
.acs-panel > * { flex: 0 0 auto; }
.acs-panel > .acs-chat { flex: 1 1 auto; min-height: 0; }
.acs-widget--right .acs-panel { right: 0; }
.acs-widget--left .acs-panel { left: 0; }
/* 深底面板：投影打在暗背景上几乎看不见，边缘会和站点糊在一起。
 * 描边改成提亮，并把顶边再提一档 —— 相当于一道来自上方的光，面板就"浮"起来了。
 * 走 border-color 而不是 inset box-shadow，是为了不和 theme.edge 的 --acs-lift 抢
 * 同一个 box-shadow 属性（那一维已经有六档取值，其中两档本身就是 inset）。 */
.acs-tone--dark.acs-edge--hairline .acs-panel,
.acs-tone--dark.acs-edge--flat .acs-panel,
.acs-tone--dark.acs-edge--bevel .acs-panel {
    border-color: color-mix(in srgb, #FFFFFF 11%, transparent);
    border-top-color: color-mix(in srgb, #FFFFFF 18%, transparent);
}

/* 关键修复：.acs-panel 是 display:flex，会盖掉 hidden 属性的 UA 样式，
 * 导致窗口加载即展开且点关闭无反应。必须显式压回去（类选择器写法让预览也受同一条约束）。 */
.acs-widget .acs-panel[hidden],
#ai-customer-service-widget .acs-panel[hidden] { display: none !important; }
.acs-panel:not([hidden]) { animation: acs-panel-in var(--acs-dur-3) var(--acs-ease); }
@keyframes acs-panel-in { from { opacity: .4; translate: 0 10px; } to { opacity: 1; translate: 0 0; } }

/* ------------------------------------------------------------------ 飘带 */

.acs-ribbon {
    position: relative;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px 6px 12px;
    background: var(--acs-accent);
    color: var(--acs-on-accent);
    font-size: var(--acs-fs-xs);
    translate: var(--acs-ribbon-dx) var(--acs-ribbon-dy);
    animation: acs-ribbon-in var(--acs-dur-3) var(--acs-ease);
}
.acs-ribbon[hidden] { display: none !important; }
@keyframes acs-ribbon-in { from { transform: translateY(-100%); } }
.acs-ribbon-track {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.acs-ribbon-close {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 18px;
    height: 18px;
    border: 0;
    border-radius: 50%;
    background: color-mix(in srgb, currentColor 22%, transparent);
    color: var(--acs-on-accent);
    cursor: pointer;
    font-size: 11px;
}
.acs-ribbon-close:hover { background: rgba(255, 255, 255, .34); }

/* ------------------------------------------------------------------ 非服务时段说明条
 * 位置在顶栏和聊天区之间，静态、不可关闭 —— 它解释的是「现在这个状态」，
 * 关掉之后访客就再也看不到自己为什么等不到人工回复了。用弱化的底色而不是
 * 强调色：它是一句说明，不是一次促销。 */

.acs-away {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--acs-line);
    background: var(--acs-sunken);
    color: var(--acs-muted);
    font-size: var(--acs-fs-sm);
    line-height: var(--acs-lh-sm);
}
.acs-away[hidden] { display: none !important; }
.acs-away-icon {
    flex: 0 0 auto;
    margin-top: 1px;
    font-size: var(--acs-fs-md);
}
/* 长文案/长连续字符不能把面板撑破 */
.acs-away-text { min-width: 0; overflow-wrap: anywhere; }

/* ------------------------------------------------------------------ 顶栏
 * Beautiful UI 的头部很轻：标题一行、状态一行小字、右侧图标按钮，靠 1px 描边分隔，
 * 不用大色块。所以默认 header_style=solid 也只是"稍深一档的同色"，而不是撞色横条。 */

.acs-header {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    padding: var(--acs-head-pad-y) var(--acs-pad);
    border-bottom: 1px solid var(--acs-line-soft);
    background: var(--acs-header-bg);
    color: var(--acs-header-fg);
}
/* 顶栏四档。solid 不写规则——它就是上面的基准（跟随 header 底色）。
 * light = 面板底色 + 一条分隔线。历史上还有个 gradient，normalizeTheme() 的白名单
 * 里已经没有它了（渐变顶栏在真实站点上几乎总是显脏），所以这里也不再留兜底选择器：
 * 老配置里的 gradient 会被 choice() 收成 light，走的就是下面这一条。 */
.acs-head--light .acs-header {
    background: var(--acs-surface);
    color: var(--acs-text);
    border-bottom-color: var(--acs-line);
}

/* accent：顶栏就是强调色本身。前景取 --acs-on-accent（站长按"铺在强调色上"挑过的那个值），
 * 写死 #fff 时薄荷/柠檬这类浅强调色上直接不可读。 */
.acs-head--accent .acs-header {
    background: var(--acs-accent);
    color: var(--acs-on-accent);
    border-bottom-color: transparent;
}
/* 顶栏内的图标按钮与头像底都是从 currentColor 混出来的，换成实心强调底后会自动跟着走。
 * 只有状态点是写死的语义绿——压在强调色上会变成"绿点撞紫底"，这里改用前景色本身。 */
.acs-head--accent .acs-header .acs-dot {
    background: var(--acs-on-accent);
    box-shadow: none;
}

/* slim：没有顶栏色块，也没有分隔线，头像与副标题都收掉，只剩一行标题贴在面板顶部。
 * 这是六套里唯一"打开后第一眼没有横条"的一档——它的差异点在留白，不在颜色。 */
.acs-head--slim .acs-header {
    gap: 8px;
    padding: 10px var(--acs-pad) 2px;
    border-bottom-color: transparent;
    background: var(--acs-surface);
    color: var(--acs-text);
}
.acs-head--slim .acs-avatar { width: 22px; height: 22px; font-size: 11px; }
.acs-head--slim .acs-agent-status { display: none; }
.acs-head--slim .acs-agent-copy strong { font-size: var(--acs-font); }
.acs-agent { display: contents; }
.acs-avatar {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    overflow: hidden;
    border-radius: var(--acs-r-sm);
    background: color-mix(in srgb, currentColor 18%, transparent);
    color: inherit;
    font-size: 15px;
    object-fit: cover;
}
.acs-avatar--fallback { font-size: 16px; }
.acs-agent-copy { display: grid; min-width: 0; gap: 1px; }
.acs-agent-copy strong {
    overflow: hidden;
    font-size: var(--acs-fs-lg);
    font-weight: var(--acs-w-strong);
    letter-spacing: calc(var(--acs-track) - .01em);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.acs-agent-status {
    display: flex;
    align-items: center;
    gap: 5px;
    overflow: hidden;
    font-size: var(--acs-fs-xs);
    opacity: .78;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.acs-dot {
    flex: 0 0 auto;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--acs-ok);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--acs-ok) 26%, transparent);
}
.acs-header-actions { display: flex; align-items: center; gap: 2px; }
.acs-icon-btn {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border: 0;
    border-radius: var(--acs-r-sm);
    background: transparent;
    color: inherit;
    cursor: pointer;
    font-size: var(--acs-fs-md);
    opacity: .8;
    transition: background var(--acs-dur-1) var(--acs-ease), opacity var(--acs-dur-1) var(--acs-ease);
}
.acs-icon-btn:hover { background: color-mix(in srgb, currentColor 14%, transparent); opacity: 1; }
.acs-icon-btn:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }

/* ------------------------------------------------------------------ 会话区
 * Beautiful UI 的一轮回复是一个 turn 容器：工具 chip → 推理行 → 正文 → 卡片，
 * 共用一个头像与一条左对齐基线，而不是每段话都套一个气泡。 */

.acs-chat {
    display: flex;
    flex-direction: column;
    gap: var(--acs-turn-gap);
    padding: var(--acs-chat-pad-t) var(--acs-pad) 6px;
    overflow-y: auto;
    /* 只设 overflow-y 时另一轴的 visible 会计算成 auto，叠卡那种带旋转的卡片就会
     * 意外撑出一条横向滚动条并被裁边。显式关掉横轴。 */
    overflow-x: hidden;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--acs-line) transparent;
    /* 滚动边缘。两头各留一段淡出，告诉访客"这个方向还有内容"——内容硬切在顶栏下沿
     * 时读起来像"这一屏就是全部"。落点由 JS 的 syncScrollEdges() 打类控制，因为 CSS
     * 拿不到滚动位置；两个量都为 0 时这条 mask 是恒等变换（0 长度的过渡段），
     * 不会在没滚动的短会话上留下任何痕迹。写成一条带变量的 mask 而不是四条组合规则，
     * 是为了避免"只有顶部""只有底部""两头都有"各写一遍。 */
    --acs-fade-t: 0px;
    --acs-fade-b: 0px;
    mask-image: linear-gradient(180deg,
        transparent 0, #000 var(--acs-fade-t),
        #000 calc(100% - var(--acs-fade-b)), transparent 100%);
}
.acs-chat.is-scrolled { --acs-fade-t: 20px; }
.acs-chat.is-scroll-more { --acs-fade-b: 20px; }
.acs-chat::-webkit-scrollbar { width: 8px; }
.acs-chat::-webkit-scrollbar-thumb {
    border: 2px solid transparent;
    border-radius: var(--acs-r-xs);
    background-clip: content-box;
    background-color: var(--acs-line);
}

.acs-message { display: flex; align-items: flex-start; gap: 8px; }
.acs-message--visitor { justify-content: flex-end; }
.acs-message-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--acs-stack-gap);
    min-width: 0;
    max-width: 100%;
}
.acs-message--visitor .acs-message-stack { align-items: flex-end; }

/* 连续同一角色的多条消息收成一组。这是所有成熟聊天界面（WhatsApp / Intercom /
 * Slack）都在做的一笔，也是"看起来像聊天"与"看起来像一列独立卡片"的分界。
 * 三件事必须一起做才成立：
 *   1. 组内间距从 --acs-turn-gap 收到 --acs-stack-gap，组**之间**仍然是一轮的距离
 *      —— 只有两级间距同时存在，"这几句是一个人接着说的"才读得出来；
 *   2. 指向说话人的那个尖角只留给组里**最后**一条。每条都带尖角时读起来是
 *      "三个人各说了一句"；
 *   3. 头像只画组里**第一条**，后面几条用 visibility 留住占位（不是 display:none）
 *      —— 撤掉那 24px 会让整组文字比第一条往外错一截。
 * 时间戳同理只留最后一条：一组里三条挨着的消息各挂一个 HH:MM 是纯噪音。
 *
 * 尖角走 --acs-r-tail 这个变量而不是直接改 border-end-*-radius：气泡形态里
 * flat 档是用 border-radius 简写把四角一起压平的，直接写长手会因为 :has() 抬高的
 * 特异性反过来盖掉 flat，留下一个对不上的角。改变量则完全不进这场特异性比较。
 *
 * 用 :has() 而不是让 JS 打类：不支持 :has() 的浏览器整条丢掉，退回"每条都带尖角
 * 带时间戳"的旧样子，那正好是可用的降级，不需要另写兜底。
 * 打字指示那条（.is-typing）不算"下一条"：它随时会被撤掉，让它去掉上一条的尖角
 * 会在回复到达的瞬间弹一下圆角。 */
.acs-message--assistant + .acs-message--assistant,
.acs-message--visitor + .acs-message--visitor {
    margin-block-start: calc(var(--acs-stack-gap) - var(--acs-turn-gap));
}
.acs-message--assistant + .acs-message--assistant .acs-message-avatar { visibility: hidden; }
.acs-message--assistant:has(+ .acs-message--assistant:not(.is-typing)),
.acs-message--visitor:has(+ .acs-message--visitor) { --acs-r-tail: var(--acs-r-lg); }
.acs-message--assistant:has(+ .acs-message--assistant:not(.is-typing)) .acs-message-time,
.acs-message--visitor:has(+ .acs-message--visitor) .acs-message-time { display: none; }

.acs-message-avatar {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    margin-top: 2px;
    overflow: hidden;
    border-radius: var(--acs-r-sm);
    background: color-mix(in srgb, var(--acs-accent) 14%, transparent);
    color: var(--acs-accent);
    font-size: 11px;
}
.acs-message-avatar img { width: 100%; height: 100%; object-fit: cover; }

.acs-message-bubble {
    max-width: 100%;
    padding: var(--acs-bubble-pad-y) var(--acs-bubble-pad-x);
    border-radius: var(--acs-r-lg);
    font-size: var(--acs-font);
    line-height: var(--acs-lh);
    overflow-wrap: anywhere;
    white-space: pre-wrap;
}
.acs-message--assistant .acs-message-bubble {
    border-end-start-radius: var(--acs-r-tail);
    background: var(--acs-bot-bubble);
    color: var(--acs-bot-text);
}
.acs-message--visitor .acs-message-bubble {
    border-end-end-radius: var(--acs-r-tail);
    background: var(--acs-vis-bubble);
    color: var(--acs-vis-text);
}

/* 气泡形态预设。四档都必须**两侧一起变**：只改客服那一侧时，站长在后台选了
 * 「描边」或「手绘」，前台看到的是"客服气泡变了、访客气泡还是原来那颗实心色块"，
 * 读起来像样式没加载完，而不像换了一种形态。
 * 两侧仍然要能分清谁在说话，所以差异从"有没有底色"挪到"底色/墨色是中性还是主色"。 */
.acs-bubble--flat .acs-message-bubble { border-radius: var(--acs-r-sm); }

.acs-bubble--outline .acs-message--assistant .acs-message-bubble {
    border: 1px solid color-mix(in srgb, var(--acs-bot-text) 16%, transparent);
    background: transparent;
}
/* 访客侧：底色撤掉后 --acs-vis-text 是按"压在主色实心底上"挑的，留着一定不可读，
 * 所以文字换回正文色，主色改由描边与一层极薄的底色承担。
 * color 与 background 都不含 color-mix：不支持的浏览器只会丢掉描边那一行，
 * 退化成"没有边框的透明气泡"仍然可读；反过来若把 color 也写成混色，
 * 丢掉的就是文字颜色，剩下白字压在被丢掉声明恢复出来的主色底上——那才是真的看不见。 */
.acs-bubble--outline .acs-message--visitor .acs-message-bubble {
    border: 1px solid color-mix(in srgb, var(--acs-vis-bubble) 58%, transparent);
    background: transparent;
    color: var(--acs-text);
}

/* 玻璃：blur 只有背后**有东西**时才看得见。气泡后面是聊天区的一整片纯色底，
 * 单靠 backdrop-filter 等于什么都没做（模糊一片纯色仍是同一片纯色）。
 * 所以材质靠三样落地：顶沿一道内高光（玻璃的厚度）、底沿一道内暗线、
 * 以及 saturate 把背后的颜色提一点（真玻璃会把透过来的光压得更艳）。
 * 手账预设的纸纹、深色站点的渐变底这些"背后真有东西"的场合，blur 才额外加分。 */
.acs-bubble--glass .acs-message--assistant .acs-message-bubble,
.acs-bubble--glass .acs-message--visitor .acs-message-bubble {
    backdrop-filter: blur(10px) saturate(1.4);
    -webkit-backdrop-filter: blur(10px) saturate(1.4);
    box-shadow:
        inset 0 1px 0 color-mix(in srgb, #fff 34%, transparent),
        inset 0 -1px 0 color-mix(in srgb, var(--acs-text) 8%, transparent);
}
.acs-bubble--glass .acs-message--assistant .acs-message-bubble {
    background: color-mix(in srgb, var(--acs-bot-bubble) 62%, transparent);
}
.acs-bubble--glass .acs-message--visitor .acs-message-bubble {
    background: color-mix(in srgb, var(--acs-vis-bubble) 66%, transparent);
}
/* 不支持 backdrop-filter（Firefox 长期默认关、旧 Safari）：半透明气泡背后没有任何
 * 模糊，聊天区的底色会直接透上来把文字拖成低对比。这时候不装玻璃，回到实心底。 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .acs-bubble--glass .acs-message--assistant .acs-message-bubble { background: var(--acs-bot-bubble); }
    .acs-bubble--glass .acs-message--visitor .acs-message-bubble { background: var(--acs-vis-bubble); }
}

.acs-bubble--sketch .acs-message--assistant .acs-message-bubble {
    border: 1.5px solid var(--acs-bot-text);
    border-radius: 255px 14px 225px 14px / 14px 225px 14px 255px;
    background: transparent;
    box-shadow: 2px 2px 0 var(--acs-bot-text);
}
/* 访客侧换一组随机半径：手绘的意思就是每一笔都不一样，两侧用同一串数字会露出
 * "这是一个 CSS 值"。墨色用主色，仍然一眼分得清谁在说话。 */
.acs-bubble--sketch .acs-message--visitor .acs-message-bubble {
    border: 1.5px solid var(--acs-vis-bubble);
    border-radius: 14px 225px 14px 255px / 225px 14px 255px 14px;
    background: transparent;
    color: var(--acs-text);
    box-shadow: 2px 2px 0 var(--acs-vis-bubble);
}

/* 时间戳。默认不开（"体验增强"里打开）：短会话里每条都挂个时间是纯噪音，
 * 但跨了几十分钟回来的访客需要它才能分清哪句是刚才说的。 */
.acs-message-time {
    padding: 0 2px;
    color: var(--acs-muted);
    font-size: var(--acs-fs-2xs);
    font-variant-numeric: tabular-nums;
    line-height: 1.2;
}

/* 正文里的网址。textContent 落地后再切成 <a>，所以这里是真链接而不是被解释的 HTML。 */
.acs-link {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    overflow-wrap: anywhere;
}
.acs-message--assistant .acs-link { color: var(--acs-accent); }
.acs-link:hover { text-decoration-thickness: 2px; }

/* 发送失败的那条：留在原位并标红，配合输入框里被放回的原文重发 */
.acs-message.is-failed .acs-message-bubble {
    background: color-mix(in srgb, var(--acs-err) 12%, var(--acs-surface));
    color: var(--acs-text);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--acs-err) 42%, transparent);
}
.acs-message.is-failed .acs-message-stack::after {
    content: "未发送";
    padding: 0 2px;
    color: var(--acs-err);
    font-size: var(--acs-fs-2xs);
}

/* 超时的那条：送到没送到还不知道，所以既不能标红说"未发送"，也不能装作已经回完。
 * 用中性的虚线边框加一句"等待确认"，随后 recoverTimedOut() 捞回答案就把这个类摘掉。 */
.acs-message.is-pending .acs-message-bubble {
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--acs-text) 22%, transparent);
    opacity: .72;
}
.acs-message.is-pending .acs-message-stack::after {
    content: "等待确认…";
    padding: 0 2px;
    color: color-mix(in srgb, var(--acs-text) 62%, transparent);
    font-size: var(--acs-fs-2xs);
}

/* 入场动效 */
@keyframes acs-rise { from { opacity: 0; translate: 0 8px; } }
@keyframes acs-pop { from { opacity: 0; scale: .92; } }
@keyframes acs-fade { from { opacity: 0; } }
.acs-anim--rise .acs-message { animation: acs-rise var(--acs-dur-3) var(--acs-ease); }
.acs-anim--pop .acs-message { animation: acs-pop var(--acs-dur-2) var(--acs-spring); }
.acs-anim--fade .acs-message { animation: acs-fade var(--acs-dur-3) var(--acs-ease); }

/* ------------------------------------------------------------------ 工具 chip
 * Beautiful UI 的 Tool Chips：一条胶囊，左边一个状态点，中间动作名，右边计数/状态。
 * 它表达"我做了什么"，所以不该长成又一个气泡。 */

.acs-toolchip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    max-width: 100%;
    padding: 5px 11px 5px 9px;
    border: 1px solid var(--acs-line);
    border-radius: 999px;
    background: var(--acs-sunken);
    color: var(--acs-muted);
    font-size: var(--acs-fs-xs);
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    animation: acs-fade var(--acs-dur-2) var(--acs-ease);
}
.acs-toolchip > i {
    flex: 0 0 auto;
    color: var(--acs-accent);
    font-size: var(--acs-fs-xs);
}
/* 完成态：状态点变绿，文字提亮一档 */
.acs-toolchip::after {
    content: "";
    flex: 0 0 auto;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--acs-ok);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--acs-ok) 22%, transparent);
}

/* ------------------------------------------------------------------ 打字指示 */

.acs-typing {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 11px 14px;
}
.acs-typing i {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--acs-muted);
}
.acs-typing--dots i { animation: acs-typing-dots 1s infinite ease-in-out; }
.acs-typing--dots i:nth-child(2) { animation-delay: .14s; }
.acs-typing--dots i:nth-child(3) { animation-delay: .28s; }
@keyframes acs-typing-dots {
    0%, 60%, 100% { transform: translateY(0); opacity: .4; }
    30% { transform: translateY(-4px); opacity: 1; }
}
.acs-typing--wave i { width: 3px; height: 12px; border-radius: 2px; animation: acs-typing-wave .9s infinite ease-in-out; }
.acs-typing--wave i:nth-child(2) { animation-delay: .12s; }
.acs-typing--wave i:nth-child(3) { animation-delay: .24s; }
@keyframes acs-typing-wave {
    0%, 100% { transform: scaleY(.45); opacity: .45; }
    50% { transform: scaleY(1); opacity: 1; }
}
/* text 档的文案由 JS 写进 span（"正在查资料…"），这里只负责呼吸感，不要再加 ::after
 * 内容，否则会出现两段重复文字。 */
.acs-typing--text {
    color: var(--acs-muted);
    font-size: var(--acs-fs-sm);
    padding: 8px 12px;
}
.acs-typing--text i { display: none; }
.acs-typing--text span { animation: acs-breathe 1.4s infinite alternate ease-in-out; }
@keyframes acs-breathe { from { opacity: .5; } to { opacity: 1; } }

/* ------------------------------------------------------------------ 内容卡
 * Beautiful UI 的 Recommendation Card：行式，左缩略图、中标题+元信息、右动作，
 * 整行可点。这是 grid / minimal / glass 三个预设的基底；stack 预设走 preset-cards.css。 */

.acs-card { width: 100%; min-width: 0; }
.acs-card-items { display: grid; gap: 6px; width: 100%; }
.acs-card-items.is-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.acs-item {
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr);
    align-items: center;
    gap: 10px;
    padding: 8px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-md);
    background: var(--acs-sunken);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--acs-dur-1) var(--acs-ease), background var(--acs-dur-1) var(--acs-ease), transform var(--acs-dur-2) var(--acs-ease);
}
.acs-item:hover {
    border-color: color-mix(in srgb, var(--acs-accent) 55%, transparent);
    background: var(--acs-hover);
    transform: translateY(-1px);
}
/* 按下要有回应。整行可点却只有 hover 时，移动端点下去到页面跳转之间是完全静止的
 * ——访客会以为没点上，于是再点一次。 */
.acs-item:active { transform: scale(.985); }
.acs-item:focus-visible { outline: 2px solid var(--acs-accent); outline-offset: 2px; }
.acs-item-thumb {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    overflow: hidden;
    border-radius: var(--acs-r-sm);
    background: color-mix(in srgb, var(--acs-accent) 12%, transparent);
    color: var(--acs-accent);
    font-size: 17px;
}
.acs-item-thumb img { width: 100%; height: 100%; object-fit: cover; }
.acs-item-main { display: grid; gap: 2px; min-width: 0; }
.acs-item-title {
    overflow: hidden;
    font-size: var(--acs-fs-md);
    font-weight: var(--acs-w-strong);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.acs-item-summary {
    overflow: hidden;
    color: var(--acs-muted);
    font-size: var(--acs-fs-xs);
    line-height: var(--acs-lh-sm);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
}
.acs-item-foot { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-top: 2px; }
.acs-item-price {
    color: var(--acs-text);
    font-size: var(--acs-fs-sm);
    font-weight: var(--acs-w-bold);
    letter-spacing: calc(var(--acs-track) - .01em);
}
.acs-item-badge {
    padding: 1px 7px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--acs-ok) 18%, transparent);
    color: color-mix(in srgb, var(--acs-ok) 78%, var(--acs-text));
    font-size: var(--acs-fs-2xs);
    font-weight: var(--acs-w-mid);
}
.acs-item-cta {
    /* 逻辑属性：挂件继承页面的 dir，阿拉伯语/希伯来语站点上 margin-left 会把它推到错的一侧 */
    margin-inline-start: auto;
    color: var(--acs-accent);
    font-size: var(--acs-fs-xs);
    font-weight: var(--acs-w-mid);
    white-space: nowrap;
}

/* minimal：去掉缩略图与边框，只留一条主色竖线 */
.acs-card-items.is-minimal .acs-item {
    grid-template-columns: minmax(0, 1fr);
    border: 0;
    /* 逻辑属性：RTL 站点上这条竖线要在右边，圆角与内边距跟着一起翻 */
    border-inline-start: 2px solid var(--acs-accent);
    border-start-start-radius: 0;
    border-start-end-radius: 10px;
    border-end-end-radius: var(--acs-r-sm);
    border-end-start-radius: 0;
    background: transparent;
    padding-block: 4px;
    padding-inline: 10px 8px;
}
.acs-card-items.is-minimal .acs-item-thumb { display: none; }
.acs-card-items.is-minimal .acs-item:hover { background: var(--acs-hover); transform: none; }

/* glass：半透明磨砂。和气泡那档同理——blur 只在背后真有东西时才看得见，
 * 所以厚度靠顶沿内高光 + 底沿内暗线交代，saturate 负责"透过玻璃的颜色更艳一点"。 */
.acs-card-items.is-glass .acs-item {
    background: color-mix(in srgb, var(--acs-surface) 55%, transparent);
    backdrop-filter: blur(10px) saturate(1.4);
    -webkit-backdrop-filter: blur(10px) saturate(1.4);
    box-shadow:
        inset 0 1px 0 color-mix(in srgb, #fff 40%, transparent),
        inset 0 -1px 0 color-mix(in srgb, var(--acs-text) 7%, transparent);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    /* 不模糊的浏览器上 55% 的半透明底 = 聊天区底色直接透上来，卡片文字掉到低对比。 */
    .acs-card-items.is-glass .acs-item { background: var(--acs-surface); }
}

/* grid 里两列会挤，缩略图缩一档 */
.acs-card-items.is-grid .acs-item { grid-template-columns: 34px minmax(0, 1fr); gap: 8px; }
.acs-card-items.is-grid .acs-item-thumb { width: 34px; height: 34px; font-size: 14px; }
.acs-card-items.is-grid .acs-item-summary { display: none; }

/* ------------------------------------------------------------------ 表单卡 / 转人工卡 */

.acs-form-card {
    display: grid;
    gap: 9px;
    width: 100%;
    padding: 12px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-lg);
    background: var(--acs-sunken);
}
.acs-form-card-title {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: var(--acs-fs-md);
    font-weight: var(--acs-w-strong);
}
.acs-form-card-title > i { color: var(--acs-accent); }
.acs-form-card-note { color: var(--acs-muted); font-size: var(--acs-fs-sm); line-height: var(--acs-lh-sm); }
.acs-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.acs-form-card.is-compact .acs-form-grid { grid-template-columns: minmax(0, 1fr); }
.acs-field { display: grid; gap: 4px; min-width: 0; }
.acs-field.is-wide { grid-column: 1 / -1; }
/* 「邮箱和电话至少留一个」那句：跨满两列，跟在字段后面 */
.acs-form-hint {
    grid-column: 1 / -1;
    margin: 0;
    color: var(--acs-muted);
    font-size: var(--acs-fs-2xs);
    line-height: var(--acs-lh-sm);
}
.acs-field > span { color: var(--acs-muted); font-size: var(--acs-fs-xs); }
.acs-field input,
.acs-field textarea {
    width: 100%;
    min-height: 32px;
    padding: 7px 9px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-sm);
    background: var(--acs-surface);
    color: var(--acs-text);
    font: inherit;
    font-size: var(--acs-fs-sm);
    transition: border-color var(--acs-dur-1) var(--acs-ease), box-shadow var(--acs-dur-1) var(--acs-ease);
}
.acs-field textarea { min-height: 62px; resize: vertical; line-height: var(--acs-lh-sm); }
.acs-field input:focus,
.acs-field textarea:focus {
    border-color: var(--acs-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--acs-accent) 18%, transparent);
    outline: 0;
}
/* 校验失败的字段。JS 提交时会给 .acs-field 挂 has-error（并写 aria-invalid），
 * 这条是主路径：确定、跨浏览器一致、由我们自己的规则决定（表单是 noValidate 的，
 * "至少留一个邮箱或电话"这种约束浏览器根本不知道）。 */
.acs-field.has-error input,
.acs-field.has-error textarea {
    border-color: var(--acs-err);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--acs-err) 16%, transparent);
}
.acs-field.has-error > span { color: var(--acs-err); }
/* :user-invalid 作为增强：访客动过之后浏览器自己能判的（type=email 格式）先红起来，
 * 不用等点提交。用 :invalid 会让必填空框一上来就是红的，所以只用 user- 这一版。 */
.acs-field input:user-invalid,
.acs-field textarea:user-invalid {
    border-color: var(--acs-err);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--acs-err) 16%, transparent);
}

.acs-form-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.acs-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 34px;
    padding: 0 15px;
    border: 0;
    border-radius: var(--acs-r-sm);
    background: var(--acs-accent);
    color: var(--acs-on-accent);
    font: inherit;
    font-size: var(--acs-fs-sm);
    font-weight: var(--acs-w-mid);
    text-decoration: none;
    cursor: pointer;
    transition: filter var(--acs-dur-1) var(--acs-ease), transform var(--acs-dur-2) var(--acs-ease);
}
.acs-btn:hover { filter: brightness(1.08); }
.acs-btn:active { transform: scale(.98); }
.acs-btn:focus-visible { outline: 2px solid var(--acs-accent); outline-offset: 2px; }
.acs-btn[disabled] { opacity: .55; cursor: wait; }
.acs-form-status { color: var(--acs-muted); font-size: var(--acs-fs-xs); }
.acs-form-status.is-error { color: var(--acs-err); }
.acs-form-status.is-done {
    display: flex;
    align-items: center;
    gap: 6px;
    color: color-mix(in srgb, var(--acs-ok) 78%, var(--acs-text));
    font-weight: var(--acs-w-strong);
}
.acs-form-status.is-done::before { content: "✓"; }

/* ------------------------------------------------------------------ 社媒 / 名片（clean 与 glass 预设） */

.acs-social { display: flex; flex-wrap: wrap; gap: 6px; }
.acs-social.is-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
.acs-social.is-list { flex-direction: column; }
.acs-social-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 11px;
    border: 1px solid var(--acs-line);
    border-radius: 999px;
    background: var(--acs-surface);
    color: var(--acs-text);
    font: inherit;
    font-size: var(--acs-fs-sm);
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--acs-dur-1) var(--acs-ease), background var(--acs-dur-1) var(--acs-ease);
}
.acs-social-item:hover { border-color: var(--acs-accent); background: var(--acs-hover); }
.acs-social-item > i { color: var(--acs-accent); }
.acs-social-copied { border-color: var(--acs-ok); }
.acs-social-copied > i { color: var(--acs-ok); }

.acs-owner {
    display: grid;
    gap: 9px;
    width: 100%;
    padding: 12px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-lg);
    background: var(--acs-sunken);
}
.acs-owner.is-glass {
    background: color-mix(in srgb, var(--acs-surface) 55%, transparent);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
.acs-owner-top { display: flex; align-items: center; gap: 10px; }
.acs-owner-photo {
    display: grid;
    place-items: center;
    width: 46px;
    height: 46px;
    overflow: hidden;
    border-radius: var(--acs-r-md);
    background: color-mix(in srgb, var(--acs-accent) 16%, transparent);
    color: var(--acs-accent);
    font-size: 19px;
}
.acs-owner-photo img { width: 100%; height: 100%; object-fit: cover; }
.acs-owner-name { font-size: var(--acs-fs-lg); font-weight: var(--acs-w-bold); }
.acs-owner-title { color: var(--acs-muted); font-size: var(--acs-fs-xs); }
.acs-owner-bio {
    /* 简介收 300 字，站长往里粘一条长链接就会撑出去（聊天区是 overflow-x:hidden，
     * 结果是被切掉一半而不是出滚动条，看起来更像坏了）。 */
    overflow-wrap: anywhere;
    color: var(--acs-muted);
    font-size: var(--acs-fs-sm);
    line-height: var(--acs-lh-sm);
}

/* ------------------------------------------------------------------ 同意确认
 * 挡在输入区前面的一块：勾了才解锁输入框。放在 composer 上方而不是盖住整个面板，
 * 是为了让欢迎语与快捷问题仍然可读——访客得先看懂这是什么服务才愿意勾同意。 */

.acs-consent {
    display: grid;
    gap: 8px;
    margin: 0 var(--acs-pad) 6px;
    padding: 10px 12px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-md);
    background: var(--acs-sunken);
}
.acs-widget .acs-consent[hidden],
#ai-customer-service-widget .acs-consent[hidden] { display: none !important; }
.acs-consent-title { font-size: var(--acs-fs-md); font-weight: var(--acs-w-bold); }
.acs-consent-row { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; }
.acs-consent-box {
    flex: 0 0 auto;
    width: 15px;
    height: 15px;
    margin: 2px 0 0;
    accent-color: var(--acs-accent);
}
/* 正文上限 600 字，按 .82em 排能到十几行——面板高度是固定的，
 * 不封顶就会把聊天区挤到只剩一条缝。封顶后正文自己滚。 */
.acs-consent-text {
    max-height: 7.5em;
    overflow-y: auto;
    overscroll-behavior: contain;
    color: var(--acs-muted);
    font-size: var(--acs-fs-xs);
    line-height: var(--acs-lh-sm);
}
.acs-consent-link { color: var(--acs-accent); text-decoration: underline; }
.acs-consent-accept {
    justify-self: start;
    padding: 6px 14px;
    border: 0;
    border-radius: 999px;
    background: var(--acs-accent);
    color: var(--acs-on-accent);
    font: inherit;
    font-size: var(--acs-fs-sm);
    cursor: pointer;
    transition: opacity var(--acs-dur-1) var(--acs-ease), transform var(--acs-dur-2) var(--acs-ease);
}
.acs-consent-accept:hover:not([disabled]) { transform: translateY(-1px); }
.acs-consent-accept:focus-visible { outline: 2px solid var(--acs-accent); outline-offset: 2px; }
.acs-consent-accept[disabled] { opacity: .45; cursor: not-allowed; transform: none; }

/* ------------------------------------------------------------------ 二维码浮层
 * 站长自己传的「扫一扫」图。整个挂件共用一个节点，由 JS 按触发元素的位置摆放：
 * 挂件根节点是 position: fixed，所以这里只要 absolute + 由 JS 写 left/top。 */

.acs-qr-pop {
    position: absolute;
    z-index: 3;
    display: grid;
    gap: 6px;
    justify-items: center;
    padding: 8px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-md);
    background: var(--acs-surface);
    box-shadow: 0 12px 30px rgba(15, 23, 42, .18);
    animation: acs-qr-in var(--acs-dur-2) var(--acs-ease);
}
.acs-widget .acs-qr-pop[hidden],
#ai-customer-service-widget .acs-qr-pop[hidden] { display: none !important; }
@keyframes acs-qr-in { from { opacity: 0; transform: scale(.94); } }
.acs-qr-img { display: block; width: 132px; height: 132px; border-radius: var(--acs-r-xs); object-fit: contain; }
.acs-qr-name {
    max-width: 132px;
    overflow-wrap: anywhere;
    color: var(--acs-muted);
    font-size: var(--acs-fs-xs);
    text-align: center;
}

/* ------------------------------------------------------------------ 快捷问题
 * 单行横滑。面板高度是固定的，标题行 + 换行的胶囊要吃掉近 60px，
 * 直接后果是卡片被截断——所以只留一行，长列表靠横滚。 */

.acs-quick-replies { padding: 2px var(--acs-pad) 8px; }
.acs-quick-replies:empty { display: none; }
.acs-quick-title { display: none; }
.acs-quick-row {
    display: flex;
    gap: 6px;
    overflow-x: auto;
    padding-bottom: 2px;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* 右端淡出，暗示还能横滑 */
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 18px), transparent 100%);
}
.acs-quick-row::-webkit-scrollbar { display: none; }
.acs-quick-reply {
    flex: 0 0 auto;
    padding: 6px 12px;
    border: 1px solid color-mix(in srgb, var(--acs-accent) 32%, transparent);
    border-radius: 999px;
    background: color-mix(in srgb, var(--acs-accent) 9%, transparent);
    color: var(--acs-accent);
    font: inherit;
    font-size: var(--acs-fs-sm);
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--acs-dur-1) var(--acs-ease), transform var(--acs-dur-2) var(--acs-ease);
}
.acs-quick-reply:hover { background: color-mix(in srgb, var(--acs-accent) 18%, transparent); transform: translateY(-1px); }
.acs-quick-reply:active { transform: scale(.96); }
.acs-quick-reply:focus-visible { outline: 2px solid var(--acs-accent); outline-offset: 2px; }
.acs-quick-reply[disabled] { opacity: .5; cursor: not-allowed; transform: none; }
.acs-quick--ghost .acs-quick-reply {
    border-color: var(--acs-line);
    background: transparent;
    color: var(--acs-text);
}
.acs-quick--sketch .acs-quick-reply {
    border: 1.5px solid var(--acs-text);
    border-radius: 255px 12px 225px 12px / 12px 225px 12px 255px;
    background: transparent;
    color: var(--acs-text);
}

/* ------------------------------------------------------------------ Prompt Bar
 * Beautiful UI 的输入区是一整块面板：正文在上，工具行在下（左工具、右计数+发送）。 */

.acs-composer { position: relative; padding: 4px var(--acs-pad) 10px; }
.acs-composer-shell {
    display: grid;
    gap: 2px;
    padding: 8px 10px 8px 12px;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-lg);
    background: var(--acs-sunken);
    transition: border-color var(--acs-dur-1) var(--acs-ease), box-shadow var(--acs-dur-1) var(--acs-ease);
}
.acs-composer-shell:focus-within {
    border-color: color-mix(in srgb, var(--acs-accent) 62%, transparent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--acs-accent) 15%, transparent);
}
.acs-composer textarea {
    display: block;
    width: 100%;
    min-height: 24px;
    max-height: 112px;
    padding: 2px 0;
    border: 0;
    outline: 0;
    background: transparent;
    color: var(--acs-text);
    font: inherit;
    line-height: var(--acs-lh-sm);
    resize: none;
}
.acs-composer textarea::placeholder { color: var(--acs-muted); opacity: 1; }
/* 输入框只在「还没勾同意」时才 disabled（等回复期间刻意不禁用），所以这里是 not-allowed */
.acs-composer textarea:disabled { cursor: not-allowed; opacity: .6; }
.acs-composer-bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.acs-composer-tools { display: flex; align-items: center; gap: 2px; }
.acs-composer-right { display: flex; align-items: center; gap: 8px; }
.acs-tool-btn { width: 26px; height: 26px; color: var(--acs-muted); }
.acs-tool-btn:hover { color: var(--acs-accent); background: var(--acs-hover); }
.acs-tool-btn.is-active { color: var(--acs-accent); background: color-mix(in srgb, var(--acs-accent) 14%, transparent); }
.acs-counter {
    color: var(--acs-muted);
    font-size: var(--acs-fs-2xs);
    font-variant-numeric: tabular-nums;
    transition: color var(--acs-dur-1) var(--acs-ease);
}
.acs-counter.is-near { color: var(--acs-warn); }
.acs-counter.is-over { color: var(--acs-err); font-weight: var(--acs-w-bold); }
.acs-send {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: 50%;
    background: var(--acs-accent);
    color: var(--acs-on-accent);
    cursor: pointer;
    font-size: var(--acs-fs-md);
    transition: filter var(--acs-dur-1) var(--acs-ease), transform var(--acs-dur-2) var(--acs-ease);
}
.acs-send:hover { filter: brightness(1.1); }
.acs-send:active { transform: scale(.94); }
.acs-send:focus-visible { outline: 2px solid var(--acs-accent); outline-offset: 2px; }
.acs-send:disabled { cursor: wait; opacity: .55; }

/* ------------------------------------------------------------------ emoji / 表情包面板 */

.acs-picker {
    position: absolute;
    /* 逻辑属性：这里左右取的是同一个值，写成物理 left/right 看不出差别，但 RTL 扫描
     * 只放过明确按 .acs-widget--right|--left 分侧的那些规则——顺手写下的物理偏移
     * 正是「浮标在左边时角标压向面板」那一类 bug 的来源，所以这里也统一。 */
    inset-inline: var(--acs-pad);
    bottom: calc(100% - 6px);
    z-index: 3;
    max-height: 208px;
    padding: 10px;
    overflow-y: auto;
    border: 1px solid var(--acs-line);
    border-radius: var(--acs-r-lg);
    background: var(--acs-surface);
    box-shadow: var(--acs-shadow);
    animation: acs-rise var(--acs-dur-2) var(--acs-ease);
}
.acs-picker[hidden] { display: none !important; }
.acs-picker-group-title {
    margin: 2px 0 6px;
    color: var(--acs-muted);
    font-size: var(--acs-fs-2xs);
    font-weight: var(--acs-w-bold);
    letter-spacing: calc(var(--acs-track) + .04em);
}
.acs-picker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(34px, 1fr));
    gap: 4px;
}
.acs-picker-grid.is-stickers { grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); }
.acs-picker-grid button {
    display: grid;
    place-items: center;
    aspect-ratio: 1;
    border: 0;
    border-radius: var(--acs-r-sm);
    background: transparent;
    color: inherit;
    cursor: pointer;
    font-size: 19px;
    line-height: 1;
    transition: background var(--acs-dur-1) var(--acs-ease), transform var(--acs-dur-1) var(--acs-ease);
}
.acs-picker-grid button:hover { background: var(--acs-hover); transform: scale(1.12); }
.acs-picker-grid button:focus-visible { outline: 2px solid var(--acs-accent); outline-offset: 1px; }
.acs-picker-grid.is-stickers button { border: 1px solid var(--acs-line-soft); overflow: hidden; }
.acs-picker-grid img { width: 100%; height: 100%; object-fit: contain; }

/* ------------------------------------------------------------------ 脚注 */

/* 人工入口与 AI 标识合成一条细脚注：原来两条各占一行，在 600px 面板里
 * 等于白扔掉一张卡片的高度。 */
.acs-handoff {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 0 var(--acs-pad) 6px;
    color: var(--acs-accent);
    font-size: var(--acs-fs-xs);
    text-decoration: none;
}
.acs-handoff:hover { text-decoration: underline; }
.acs-powered {
    padding: 0 var(--acs-pad) 8px;
    color: var(--acs-muted);
    font-size: var(--acs-fs-2xs);
    text-align: center;
}
.acs-feedback {
    padding: 0 var(--acs-pad);
    color: var(--acs-err);
    font-size: var(--acs-fs-xs);
    line-height: var(--acs-lh-sm);
}
.acs-feedback:not(:empty) { padding-bottom: 9px; }

/* ------------------------------------------------- 预设签名（acs-theme--*）
 * 前面所有轴（edge / type / density / header_style / bubble_style / quick_style）
 * 都是站长可以自由组合的**通用**档位，预设只是替他选了一组。这一节相反：它是
 * 每套预设**独占**的那一两笔，themePresets() 的 note 里已经写给站长看了，
 * 不兑现就是文案说谎 —— mint 的「行分隔」、ink 的「方角」、midnight 的
 * 「重点色靠发光而非面积」都在这里落地。
 *
 * 因此这里只写"没有通用开关能表达"的东西。凡是能变成一档轴的（圆角、密度、
 * 边缘），一律回到轴里去，不在这里重复一遍。
 *
 * 素白 plain 故意没有任何规则：它的定位就是"最不抢站点风格"，签名等于噪声。
 * 陶土 clay / 手账 paper 的 note 承诺（大圆角+主色顶栏+暖发光 / 宋体+手绘描边+
 * 硬投影）已经由 panel_radius、header_style、edge、bubble_style 全部兑现，
 * 这里只各补一笔材质。 */

/* 墨黑 Ink：note 说"方角"。panel_radius=4 经阶梯把全窗圆角压到 1~4px，但胶囊
 * 快捷问题与工具行的 999px 是写死的形状语义，阶梯带不动 —— 开发者工具里不会有
 * 药丸按钮，这两处必须由预设自己拍平。note 里的"等宽数字"不在这里：时间戳与
 * 字数计数已经全局 font-variant-numeric: tabular-nums，无需重复。 */
.acs-theme--ink .acs-quick-reply,
.acs-theme--ink .acs-toolchip { border-radius: var(--acs-r-xs); }

/* 午夜 Midnight：note 说"重点色靠发光而非面积"。玻璃边缘（edge=glass）只给了
 * 面板本身的材质，重点色仍然只出现在访客气泡和发送键上。这里补两处**无面积**的
 * 主色：顶栏下沿一条两端淡出的光线，和访客气泡的外溢光。 */
.acs-theme--midnight .acs-header {
    position: relative;
    border-bottom-color: transparent;
}
.acs-theme--midnight .acs-header::after {
    content: "";
    position: absolute;
    /* 逻辑属性：RTL 下这条线仍然横贯整条顶栏 */
    inset-inline: 0;
    bottom: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--acs-accent) 62%, transparent), transparent);
}
.acs-theme--midnight .acs-message--visitor .acs-message-bubble {
    box-shadow: 0 2px 14px -4px color-mix(in srgb, var(--acs-accent) 60%, transparent);
}

/* 薄荷 Mint：note 明写"行分隔"。默认列表是"一块块独立卡片 + 6px 间距"，那是
 * 消费品的观感；B2B 目录要的是表格 —— 零间距、方角、只留行间一条线。
 * is-grid / is-minimal / is-glass 是站长在卡片上另选的形态，不覆盖。 */
.acs-theme--mint .acs-card-items:not(.is-grid):not(.is-minimal):not(.is-glass) { gap: 0; }
.acs-theme--mint .acs-card-items:not(.is-grid):not(.is-minimal):not(.is-glass) .acs-item {
    border-radius: 0;
    border-inline: 0;
    border-block-start: 0;
    background: transparent;
    padding-block: 7px;
}
.acs-theme--mint .acs-card-items:not(.is-grid):not(.is-minimal):not(.is-glass) .acs-item:last-child {
    border-block-end: 0;
}
/* 表格行不该"浮起来"，hover 只换底色 */
.acs-theme--mint .acs-card-items:not(.is-grid):not(.is-minimal):not(.is-glass) .acs-item:hover {
    transform: none;
    border-color: var(--acs-line);
    background: var(--acs-hover);
}

/* 陶土 Clay：圆角与暖发光已由轴给出，这里只补"厚度"—— 气泡底沿一道内压痕，
 * 让 26px 的大圆角读起来像捏出来的实体而不是贴纸。 */
.acs-theme--clay .acs-message-bubble {
    box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--acs-text) 7%, transparent);
}

/* 手账 Paper：补纸的横线。background-attachment: local 让横线跟着内容滚动（贴在
 * 纸上而不是贴在窗口上）；行距由 --acs-font × --acs-lh 推出，字号一改纸也跟着改。
 * 横线不与气泡内文字基线对齐，它是纸的纹理不是书写格 —— 对齐反而会显得错版。 */
.acs-theme--paper .acs-chat {
    background-image: repeating-linear-gradient(
        180deg,
        transparent 0,
        transparent calc(var(--acs-font) * var(--acs-lh) - 1px),
        color-mix(in srgb, var(--acs-accent) 13%, transparent) calc(var(--acs-font) * var(--acs-lh) - 1px),
        color-mix(in srgb, var(--acs-accent) 13%, transparent) calc(var(--acs-font) * var(--acs-lh))
    );
    background-attachment: local;
}
/* 顶栏与正文之间用虚线，接上"手写笔记"的调子 */
.acs-theme--paper .acs-header {
    border-bottom-style: dashed;
    border-bottom-color: color-mix(in srgb, var(--acs-text) 22%, transparent);
}

/* ------------------------------------------------------------------ 移动端 */

@media (max-width: 640px) {
    /* env(safe-area-inset-*)：iPhone 底部的 home indicator 与 Safari 工具条会压住
     * 浮标，横屏时刘海侧也会切掉一块。不加安全区，浮标在 iOS 上是"能看见但点不着"。
     * 兜底值写 0px，老浏览器不认 env() 时整条 calc 才不会失效。
     * --acs-kb 把整个挂件抬到软键盘上方（键盘没弹时是 0px，等于不生效）。 */
    #ai-customer-service-widget.acs-widget { bottom: calc(var(--acs-mobile-y) + var(--acs-kb) + env(safe-area-inset-bottom, 0px)); }
    #ai-customer-service-widget.acs-widget--right { right: calc(var(--acs-mobile-x) + env(safe-area-inset-right, 0px)); }
    #ai-customer-service-widget.acs-widget--left { left: calc(var(--acs-mobile-x) + env(safe-area-inset-left, 0px)); }
    .acs-panel {
        /* 宽度必须扣掉「配置的边距 + 另一侧留 8px」。用固定的 100vw-24px 会在
         * mobile_offset_x 被调大时把面板顶出屏幕——那个值是后台可配的，不能假设。 */
        width: min(var(--acs-panel-width), calc(100vw - var(--acs-mobile-x) - 8px));
        /* 抬起多少就要矮多少，否则面板顶端会被顶出视口（标题栏和关闭按钮先没）。 */
        height: min(var(--acs-panel-height), calc(100dvh - var(--acs-size) - var(--acs-mobile-y) - var(--acs-kb) - env(safe-area-inset-bottom, 0px) - 24px));
    }
    .acs-widget--pill .acs-launcher-label { max-width: 132px; }
    .acs-teaser { max-width: min(210px, calc(100vw - 110px)); }
    .acs-channels--list { max-width: min(240px, calc(100vw - var(--acs-mobile-x) - 56px)); }
    .acs-form-grid { grid-template-columns: minmax(0, 1fr); }
    .acs-card-items.is-grid { grid-template-columns: minmax(0, 1fr); }
}

/* 极窄屏（320px 级）：内边距收一档，缩略图缩小，别的交给上面那条自适应宽度 */
@media (max-width: 380px) {
    /* 设上限而不是设定值：紧凑档已经是 11px，不该被这里回抬。 */
    .acs-panel { --acs-pad: min(var(--acs-pad-base), 11px); }
    .acs-item { grid-template-columns: 36px minmax(0, 1fr); }
    .acs-item-thumb { width: 36px; height: 36px; font-size: 15px; }
    .acs-quick-reply { font-size: var(--acs-fs-xs); padding: 5px 10px; }
}

/* ------------------------------------------------------------------ 降级与偏好 */

/* 触屏没有"把鼠标移开"这件事：:hover 会一直挂在最后点过的那个元素上。抬起来的卡片、
 * 变了色的胶囊会停在那儿，读起来像"这一项被选中了"。抬起是鼠标的语言，
 * 触屏改用按下缩一下（各组件的 :active）表达同一件事。 */
@media (hover: none) {
    .acs-item:hover,
    .acs-quick-reply:hover,
    .acs-launcher:hover,
    .acs-channels-toggle:hover { transform: none; }
    .acs-launcher:hover,
    .acs-channels-toggle:hover { filter: var(--acs-launcher-drop); }
}

@media (prefers-reduced-motion: reduce) {
    #ai-customer-service-widget.acs-widget,
    #ai-customer-service-widget.acs-widget * {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    /* 上面那条把所有动画掐掉，代价是"客服正在输入"这个唯一的进度信号也一起没了：
     * dots / wave 两档的全部含义都在动，停下来就是三颗静止的灰点，跟装饰没有区别
     * ——访客分不清是还在想还是已经断了。所以这一档改用文字表达同一件事：
     * 把本来只给读屏的那句话显形，点收起来。text 档本来就是文字，不受影响。 */
    .acs-typing--dots i,
    .acs-typing--wave i { display: none; }
    .acs-typing--dots .acs-sr-only,
    .acs-typing--wave .acs-sr-only {
        position: static !important;
        width: auto !important;
        height: auto !important;
        margin: 0 !important;
        overflow: visible !important;
        clip: auto !important;
        color: var(--acs-muted);
        font-size: var(--acs-fs-sm);
    }
}

/* 高对比度模式下把混色描边换成实色，color-mix 出来的低对比边框在这里会消失 */
@media (prefers-contrast: more) {
    #ai-customer-service-widget.acs-widget {
        --acs-line: currentColor;
        --acs-line-soft: currentColor;
    }
    .acs-item,
    .acs-form-card,
    .acs-owner,
    .acs-toolchip { border-width: 2px; }
}

/* 不支持 color-mix 的浏览器（Safari < 16.4 等）：派生量退回固定灰阶，
 * 否则描边与悬浮底色全部失效，卡片会变成"没有边界的一坨"。 */
@supports not (color: color-mix(in srgb, #000 50%, transparent)) {
    #ai-customer-service-widget.acs-widget {
        --acs-line: rgba(127, 127, 127, .28);
        --acs-line-soft: rgba(127, 127, 127, .16);
        --acs-hover: rgba(127, 127, 127, .1);
        --acs-sunken: rgba(127, 127, 127, .08);
    }
    .acs-toolchip > i,
    .acs-item-cta,
    .acs-social-item > i,
    .acs-form-card-title > i { color: var(--acs-accent); }
    .acs-quick-reply { border-color: var(--acs-accent); background: transparent; }
    .acs-item-thumb { background: var(--acs-accent); }

    /* 预设签名也得补，而且这里不补的后果比别处重：签名规则里那些 color-mix 出来的
     * 声明会被整条丢掉，而**同一条规则里不含混色的那几行照旧生效**——
     * 午夜正是这样：border-bottom-color: transparent 留着、用来替代它的那道光线没了，
     * 于是顶栏和正文之间一条分界都不剩，比根本没有签名更糟。
     * 手账的纸纹与陶土的压痕则是"直接消失"，note 里承诺的东西兑现不了。 */
    .acs-theme--midnight .acs-header { border-bottom-color: var(--acs-line); }
    .acs-theme--midnight .acs-header::after {
        background: linear-gradient(90deg, transparent, var(--acs-accent), transparent);
        opacity: .55;
    }
    .acs-theme--clay .acs-message-bubble { box-shadow: inset 0 -1px 0 rgba(127, 127, 127, .22); }
    .acs-theme--paper .acs-chat {
        background-image: repeating-linear-gradient(
            180deg,
            transparent 0,
            transparent calc(var(--acs-font) * var(--acs-lh) - 1px),
            rgba(127, 127, 127, .2) calc(var(--acs-font) * var(--acs-lh) - 1px),
            rgba(127, 127, 127, .2) calc(var(--acs-font) * var(--acs-lh))
        );
    }
    /* 描边气泡：访客那侧的边框是混色，丢掉之后剩一颗完全透明的气泡 */
    .acs-bubble--outline .acs-message--assistant .acs-message-bubble,
    .acs-bubble--outline .acs-message--visitor .acs-message-bubble { border-color: var(--acs-line); }
}

/* 打印时挂件没有意义 */
@media print {
    #ai-customer-service-widget.acs-widget { display: none !important; }
}
