/* ============================================================
   回响 · 用户端设计系统（2026-10 全站重做）
   ------------------------------------------------------------
   方向：高级极简 + 温暖陪伴 + 现代 AI 产品。
   刻意不做的事：蓝紫模板渐变、满屏玻璃、霓虹光晕、SaaS 仪表盘感。
   分层（同一个文件里的八个层次，按顺序读）：
     L1 tokens   设计变量：色彩 / 字阶 / 间距 / 圆角 / 阴影 / 动效
     L2 base     元素与排版基线
     L3 layout   外壳：顶栏 / 页面 / 会话列 / 输入区 / 抽屉 / 弹层 / rail / tabbar
     L4 comp     组件：头像 / 按钮 / 输入 / 卡片 / 开关 / 胶囊 / 下拉 / toast / 骨架
     L5 chat     聊天：日期 / 气泡 / 打字 / 回底 / 表情面板
     L6 pages    页面：空间 / 角色 / 详情 / 关系 / 记忆 / 广场 / 我的 / 登录 / 文档 / 分享
     L7 motion   动效与无障碍降级
     L8 宽屏     min-width:720px 断点块 —— 只能有这一块，且必须在文件末尾
   ------------------------------------------------------------
   为什么仍然是一个文件：自测直接读 static/style.css 审选择器与规则顺序，
   拆分会让「谁在后面谁说话」这条已经踩过坑的层叠关系失去保护。
   两条被自测钉住的硬规矩，别顺手改：
   ① 常驻栏和跟着滚动的表面一律不许有 backdrop-filter（实测滚动掉帧的头号来源，
      也是「导航完画面还停在上一页」的成因）。全站只留 4 个瞬时浮层能糊，
      外加一条 prefers-reduced-transparency 的一刀切降级。
   ② 宽屏适配只允许一个 min-width:720px 的断点块并在文件末尾，各组件自己的
      宽屏规则都并进这里——谁在后面谁说话，否则后写的同优先级规则会静默盖掉桌面适配。
   ============================================================ */

/* ============================================================
   L1 · TOKENS —— 设计系统「夜与火」Dusk & Ember（2026-10 第四轮）
   ------------------------------------------------------------
   第三轮把**信息架构**做对了（五个一级、记忆与关系上桌、关系去游戏化），
   但产品看起来仍然像一个排版不错的普通网页。这一轮对着实页截图逐条查，
   病根不在配色，在四件从没做过的事：

   ① 字没有性格。全站只有一个系统无衬线栈（旧 --font-ui 唯一消费点是 body），
      没有 display face。竞品全都有：Replika=Pangea、Craft=Untitled、Are.na=areal。
      → 立**三声部**：无衬线说工具的话，衬线说人的话，等宽说时间的话。
        全部本机可得，零下载（国内网络下 webfont 不可靠，也不给项目添依赖）。
   ② 角色不像人。头像兜底是一块平色 + 一个字，每个角色同一张脸。
      → 立 **Aura**：8 套暖色光晕对（浅→深）+ 同心环纹，由 hashStr(pid) 确定。
        环纹本身就是品牌签名——头像即回响，删掉 logo 也认得出。
   ③ 动效是假的。--ease-out/pop/spring/io 四条**值完全相同**，
      9 个 keyframes 里 5 个无人消费。
      → 一条签名曲线 + 三条分工曲线，加一整套时长；Wave/Thread 只播一次。
   ④ 签名只活在 CSS 里。.echo 三态只有 .on 被发射过（全站唯一发射点
      chat.js 的"正在输入"），.halo/.lead 零引用。
      → Ring / Wave / Thread 三件套，八个真实时刻真的发射。

   色彩纪律升级成一句话：**气泡中性，签名跟着动作，语义跟着页面。**
   琥珀只说"TA 在想、在来"，青瓷只说"记着"，玫瑰只说"你们之间"。
   强调色（--tint）仍随用户选的三套风格走——签名是**环的形状**，不是某个色相。
   ============================================================ */

:root{
  color-scheme:light;

  /* —— 表面四阶：暮光回响 ——
        沉（聊天画布/列表底）→ 页（默认底）→ 浮（卡片）→ 最浮（弹层/输入）。
        第四轮的问题是「一个平面走天下」：只换色阶不够，页面还要有**光**。
        所以这一版在色阶之外再加一层暮光：上方一盏暖光斜照进来，
        底部慢慢沉下去。同一张纸上，光的位置决定情绪。 —— */
  --paper:#F5EFE5;
  --bg:#F5EFE5;              /* 页 */
  --sunk:#EEE5D7;            /* 沉 */
  --bg2:#FFF9F0;             /* 浮 */
  --card:#FFFCF7;--card-solid:#FFFCF7;--surface:#FFFCF7;
  --grouped:#F1EADD;
  /* --linen 是色阶里的**冷档**。
     实测：用户给的四个表面色（#F5EFE5/#EEE5D7/#FFF9F0/#FFFCF7）两两之间
     ΔE*ab 最大只有 5.2——数学上就是同一张纸，这才是「米黄单调」的病根，
     换一种米黄治不好。Kindroid 的做法是在暖档中间插一块 #dcdbd0
     （ΔE 8.6、色相从 66° 跳到 106°），Soul 插的是青绿。
     所以这里插一档偏灰绿的，只给「井」（分组底、抽屉、输入区）用。 */
  --linen:#E7E5DC;
  --sep:rgba(48,40,32,.13);--sep2:rgba(48,40,32,.07);
  --tile-1:#EFE6D7;--tile-2:#E6DAC7;--tile-3:#DACDB6;--tile-4:#CBBAA0;
  --dusk-bot:#EAE6DB;      /* 暮光的下缘往冷走一点：上暖下冷才像一天里的光 */

  /* —— 纵深：暮光 + 织纹 + 每个角色自己的房间色温 ——
        --dusk 是整页的底光（上暖下沉），--glow 是局部打光，
        --room 由 JS 按角色 hash 写入 --room-h，让每个聊天"住"在不同色温里。
        这是治「米黄单调」的正解：不是换一种米黄，是让页面会随人变。
        织纹在低端设备/省透明偏好下整层关掉。 —— */
  --dusk:linear-gradient(178deg,#FFF7EA 0%,var(--bg) 44%,var(--dusk-bot) 100%);
  --glow:radial-gradient(130% 72% at 50% -14%,rgba(255,196,116,.34) 0%,rgba(255,196,116,0) 60%);
  /* 明度从 96/91 抬到 97.6/94.2。旧的下缘 #F3E9DD 是"灰黄、闷"的来源；
     新下缘 #F8F0E9 仍然带得出色相（房间还是那个人的房间），但不压亮度。 */
  --room-top:hsl(var(--room-h,32) 84% 97.6%);
  --room-bot:hsl(var(--room-h,32) 52% 94.2%);
  --room:linear-gradient(178deg,var(--room-top) 0%,var(--room-bot) 100%);
  --grain:repeating-linear-gradient(45deg,
      rgba(48,40,32,.012) 0 1px,rgba(48,40,32,0) 1px 4px);

  /* —— 墨 —— */
  --ink:#302820;--ink2:#756B61;--ink3:#A69B90;
  --fill:rgba(48,40,32,.062);--fill2:rgba(48,40,32,.032);

  /* —— 品牌签名色 --echo：环、波、线，和"TA 正在为你做什么"的所有时刻。
        第五轮把它**钉死成回响橙**，不再跟随用户选的风格：
        验收要的是「删掉 logo 还认得出回响」，一个会随设置变色的签名做不到这件事。
        亮橙只做图形（环/波/线/点/填充），做文字一律用 --echo-ink ——
        实测 #E58A43 压在纸上只有 2.28:1，读不动。 —— */
  --echo:#E58A43;--echo-ink:#8C5A2B;--echo-deep:#B4671F;
  --echo-bg:rgba(229,138,67,.14);--echo-wash:#FBEAD4;
  /* 用户风格强调色：只做选中态与文字，永远不做签名。 */
  --tint:#8C5A2B;--tint-ink:#FFF6EE;--tint-bg:rgba(140,90,43,.12);
  /* 主行动填充：亮橙面 + 深棕字（实测 5.55:1）。
        第四轮那种"深砖红面 + 米白字"是压抑感的来源之一。 */
  --btn-ink:#E58A43;--btn-ink-ink:#302820;
  --ember:#C85B3F;--ember-deep:#A84628;
  /* 会话两侧：我＝暖橙实心，TA＝白瓷。谁在说话一眼分明，但都不是黑块。 */
  /* 我这侧＝品牌橙，但比 --echo 的 #E58A43 轻一档到 #EFA45F：
     焦橙色铺满一整条气泡时偏"重"，而深暖棕字压在 #EFA45F 上是 7.00:1
     （原来 5.55），更亮、更透气，也更好读。发送钮用同一个色，
     于是"这个橙 = 我说的话"在全页只有一条规则。 */
  --me:#E9974F;--me-ink:#302820;
  --bot:#ffffff;
  --pg1:#E58A43;--pg2:#C85B3F;
  --amb:none;

  /* —— 页面语义色：记忆＝青瓷，关系＝玫红。
        同样三态：亮色做图形，--*-ink 做文字，--*-wash 做页面气质底。
        永不当按钮填充。 —— */
  --mnem:#5FAE98;--mnem-ink:#2A6F62;--mnem-bg:rgba(95,174,152,.15);--mnem-wash:#E9F3EF;
  --bond:#D76B7B;--bond-ink:#9E4257;--bond-bg:rgba(215,107,123,.15);--bond-wash:#FBE9EC;

  /* —— 语义色 —— */
  --ok:#2F7A57;--ok-bg:rgba(47,122,87,.11);
  --warn:#B03A34;--warn-bg:rgba(176,58,52,.10);
  --gold:#8A6516;--gold-bg:#F8EFD8;

  /* —— 阴影：暖褐，不用中性灰。年轻产品的影子是"阳光下的影子"。 —— */
  --sh1:0 1px 2px rgba(120,84,44,.07);
  --sh2:0 10px 26px rgba(120,84,44,.12);
  --sh3:0 26px 68px rgba(56,36,18,.30);
  --sh-lift:0 2px 6px rgba(120,84,44,.08),0 10px 24px rgba(120,84,44,.10);
  --ring:0 0 0 3px var(--echo-bg);
  /* 内描边是一圈**半透明白**，不是深色：实测 Kindroid 的头像 rim 是
     1px solid rgba(255,255,255,.55)。白边把 Aura 和纸面切开，
     头像立刻从「一块渐变」变成「一个有边缘的东西」。 */
  --av-stroke:inset 0 0 0 1px rgba(255,255,255,.55),inset 0 0 0 2px rgba(60,44,28,.06);

  /* —— 圆角：一族四个。气泡刻意不对称（有尾巴的那一角小得多）。 —— */
  --r-btn:14px;--r-btn-sm:10px;--r-input:18px;--r-card:20px;--r-panel:24px;
  --r-bubble:22px;--r-tail:7px;--r-av:50%;--r-av-sm:50%;
  --r-hero:28px;
  --r-xs:6px;--r-s:10px;--r-m:14px;--r-l:20px;--r-xl:26px;--r-pill:999px;

  /* —— 间距 —— */
  --s-1:4px;--s-2:8px;--s-3:12px;--s-4:16px;--s-5:20px;
  --s-6:24px;--s-7:32px;--s-8:40px;--s-9:56px;--s-10:72px;
  --gut:16px;--pad-top:12px;--w-rail:264px;

  /* —— 字阶 11 档 —— */
  --fs-micro:11px;--fs-cap:12px;--fs-foot:13px;--fs-sub:14px;
  --fs-body:16px;--fs-title:18px;--fs-head:20px;--fs-headline:22px;
  --fs-greet:26px;--fs-lead:22px;
  --fs-h1:clamp(28px,4.6vw,40px);--fs-display:clamp(32px,5.6vw,52px);
  --ls-tight:0;--ls-title:-.01em;--ls-disp:-.02em;--ls-label:.01em;--ls-caps:.08em;
  --lh-tight:1.15;--lh-snug:1.45;--lh-body:1.72;--lh-prose:1.85;
  /* —— 三声部（本轮最高杠杆，且零依赖）——
        不引入 webfont：国内网络下 Google Fonts 不可达，中文子集动辄几百 KB，
        而项目本来也不该为排版添依赖。改用**系统里就有**的三套脸，
        让"谁在说话"用字体就能听出来：

        ui    无衬线 —— 导航、按钮、标签、输入、设置。工具的声音。
        prose 衬线   —— TA 说的话、记忆的句子、关系的叙事、大标题、角色名。
                       人的声音、信的声音。Songti/Noto Serif 在 iOS·Android·Win
                       都能落到一台有衬线中文的机器上。
        time  等宽   —— 日期、时长、"认识 43 天"。时间的声音。

        衬线一律不用 700：SimSun 没有真粗体，合成粗体会糊成一团。 —— */
  --font-ui:-apple-system,BlinkMacSystemFont,"SF Pro Text","PingFang SC",
      "HarmonyOS Sans SC","MiSans","Noto Sans CJK SC","Source Han Sans SC",
      "Microsoft YaHei",sans-serif;
  --font-prose:Georgia,"Iowan Old Style","Palatino Linotype","Songti SC",
      "Noto Serif CJK SC","Source Han Serif SC","Kaiti SC",SimSun,serif;
  --font-time:ui-monospace,"SF Mono","Cascadia Mono","JetBrains Mono",Menlo,Consolas,monospace;
  --font-num:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;

  /* —— 动效：一条签名曲线 + 三条分工曲线 ——
     第三轮这四条 alias 的值**完全相同**，等于没有曲线系统。这次让它们真的不一样：
     --ease-out 是签名（起手快、尾巴长，像水波散开），入场退场从它派生。 */
  --dur-press:.11s;--dur-fast:.16s;--dur-base:.24s;--dur-sheet:.34s;--dur-slow:.5s;
  --dur-echo:2.1s;
  --d-micro:.16s;--d-enter:.30s;--d-page:.44s;--d-wave:.95s;--d-thread:1.0s;
  --d-stagger:70ms;         /* 同组元素错峰：一条消息里的多个气泡 */
  --ease-out:cubic-bezier(.22,.68,.24,1);      /* 签名：回响散开 */
  --ease-pop:cubic-bezier(.16,1,.3,1);         /* 入场：easeOutExpo，落定不弹 */
  --ease-spring:cubic-bezier(.34,1.56,.64,1);  /* 全站唯一允许过冲的地方：选中态 */
  --ease-io:cubic-bezier(.4,0,1,1);            /* 退场：比入场快 40% */
  --ease-echo:var(--ease-out);
  --ease-breathe:cubic-bezier(.45,0,.55,1);    /* 头像呼吸：正弦，不用 infinite */

  /* —— 浮层底色 —— */
  --nav-bg:var(--bg);
  --hud-bg:rgba(48,36,24,.60);
  --mask-bg:rgba(48,34,20,.34);--toast-bg:rgba(44,33,22,.94);--scrim:rgba(48,34,20,.44);
  --bg-ovr-light:rgba(252,246,236,.56);--bg-ovr-dark:rgba(20,14,8,.58);
  --dim:.06;

  /* 毛玻璃的账（实测，别再往回改）：
     ① 帧时——300 条消息的会话上下滑，气泡带 blur 时 p95 40ms、掉 12 帧；全关掉后
        p95 13.4ms、0 掉帧。
     ② 合成层不刷新——backdrop-filter 只要出现就会建一个背景采样层，部分手机浏览器
        整页导航后不让这一层失效，表现成「页面还停在上一页」。
     只允许瞬时浮层留 blur（.mask/.toast/.tickle/登录卡）。 */

  /* 头像兜底＝Aura（每个 AI 像一个人）。
     第四轮：8 套色对 × 3 环距 × 3 光位，仍然靠 h%8 分桶——实测两个角色
     撞进相邻桶都是橙，"独特气质"当场失效；而且 8 套全是"一块渐变 + 一个字"，
     放大到详情页就成了色块，不像一张脸。
     第五轮改成**六轴确定性生成**（app.js auraOf()）：
       色相   黄金角 137.508° 递推，任意两个相邻角色色相差 ≥112°，永不撞桶
              （250°–290° 的紫带整段跳过——紫不是这个产品的颜色）
       外环   光晕外缘的收束深浅  --au-b
       内环   中心高光核的大小与亮  --au-core
       光位   光源在哪个方位角    --au-x/--au-y
       环距   同心环纹的间距      --au-r
       纹理   环纹 / 斜织 / 细闪 / 素面 四种之一  --au-tex
     字色写死深墨（见 .av）：底色是明暗两套共用的淡染，跟着 --ink 变亮会做出 1.24:1。
     下面 12 套色对只作**兜底**（JS 没跑到时也要有脸），顺序刻意冷暖交替。 */
  --av-g1:#C85B3F;--av-h1:#FFD9A8;   /* 暮光橙 */
  --av-g2:#2A6F62;--av-h2:#CDEDE2;   /* 青瓷 */
  --av-g3:#D76B7B;--av-h3:#FFD8E2;   /* 玫 */
  --av-g4:#B4671F;--av-h4:#FBE7C0;   /* 杏 */
  --av-g5:#4A7A9E;--av-h5:#D6E7F2;   /* 湖 */
  --av-g6:#6A9A5E;--av-h6:#DCEFD2;   /* 松 */
  --av-g7:#C9A227;--av-h7:#FBEDB8;   /* 金 */
  --av-g8:#9A6B45;--av-h8:#EFDBc6;   /* 檀 */
  --av-g9:#B04E6A;--av-h9:#F8D6E0;   /* 樱 */
  --av-g10:#5E7A9E;--av-h10:#E0E8F4; /* 靛 */
  --av-g11:#8A8272;--av-h11:#EAE4D8; /* 石 */
  --av-g12:#A84628;--av-h12:#F6DCC8; /* 陶 */
}
[data-theme=dark]{
  color-scheme:dark;
  /* 暗色仍然是**一种能力**，不再是首页的强制面貌（第四轮把 Landing/登录
     按进黑里，用户的评价是"太暗、太压抑"——见 UI_V5 诊断 D1）。
     底色带暖褐，是"夜里房间留了一盏灯"，不是显示器黑。 */
  --paper:#191310;
  --bg:#191310;--sunk:#120D0B;--bg2:#241C18;
  --card:#241C18;--card-solid:#241C18;--surface:#2A211C;
  --grouped:#1E1714;--linen:#2C2320;
  --sep:rgba(246,236,225,.13);--sep2:rgba(246,236,225,.07);
  --tile-1:#2A211C;--tile-2:#33291F;--tile-3:#3E3226;--tile-4:#4C3E2F;
  --dusk:linear-gradient(178deg,#221A15 0%,var(--bg) 46%,#13100D 100%);
  --glow:radial-gradient(130% 72% at 50% -14%,rgba(240,166,90,.17) 0%,rgba(240,166,90,0) 60%);
  --room-top:hsl(var(--room-h,32) 26% 17%);
  --room-bot:hsl(var(--room-h,32) 20% 12%);
  --room:linear-gradient(178deg,var(--room-top) 0%,var(--room-bot) 100%);
  --grain:repeating-linear-gradient(45deg,
      rgba(246,236,225,.020) 0 1px,rgba(246,236,225,0) 1px 4px);
  --ink:#F6ECE1;--ink2:rgba(246,236,225,.68);--ink3:rgba(246,236,225,.45);
  --fill:rgba(246,236,225,.078);--fill2:rgba(246,236,225,.042);
  --echo:#F0A85E;--echo-ink:#F3C08A;--echo-deep:#D98B3F;
  --echo-bg:rgba(240,168,94,.16);--echo-wash:#332415;
  --tint:#F0A85E;--tint-ink:#1E1206;--tint-bg:rgba(240,168,94,.16);
  --btn-ink:#E58A43;--btn-ink-ink:#241403;
  --ember:#E07a55;--ember-deep:#C85B3F;
  --me:#E58A43;--me-ink:#241403;
  --pg1:#F0A85E;--pg2:#C98A6E;
  --bot:#2A211C;
  --mnem:#6FC7B4;--mnem-ink:#6FC7B4;--mnem-bg:rgba(111,199,180,.15);--mnem-wash:#152823;
  --bond:#EC8FA1;--bond-ink:#EC8FA1;--bond-bg:rgba(236,143,161,.15);--bond-wash:#2C1A1E;
  --ok:#5FBF8C;--ok-bg:rgba(95,191,140,.14);
  --warn:#E8837C;--warn-bg:rgba(232,131,124,.14);
  --gold:#DCC084;--gold-bg:#2C2416;
  --sh1:0 1px 2px rgba(0,0,0,.30);
  --sh2:0 10px 26px rgba(0,0,0,.44);
  --sh3:0 26px 68px rgba(0,0,0,.62);
  --sh-lift:0 2px 6px rgba(0,0,0,.30),0 10px 24px rgba(0,0,0,.36);
  --hud-bg:rgba(246,236,225,.7);
  --mask-bg:rgba(8,5,4,.60);--toast-bg:rgba(246,236,225,.95);--scrim:rgba(8,5,4,.60);
  --av-stroke:inset 0 0 0 1px rgba(246,236,225,.09);
  --dim:.16;
}
[data-theme=light]{color-scheme:light}

/* ---------- 主题配色（与明暗正交；data-style 挂在 <html> 上） ----------
   三套风格只换一个强调色：基底、三层表面、墨、线、面、圆角、阴影、字阶全共用。
   B 的绿定在 #35694f 而不是更好看的 #3d7a5c：强调色要压在 11% 同色底上做成
   12px 的选中态胶囊文字（button.chip.on），#3d7a5c 在那一层只有 4.13:1，
   差一点点但确实读不动。A 的余烬和 C 的紫本身就在 5.5:1 以上，不用动。
   同理 C 在深色下的主按钮面从 #8a68b0 提到 #9a78c2：暗色里按钮是「浅面深字」，
   原值配 #150e1e 只有 4.21:1，「＋ 新建角色」这种 14px 按钮全站都是。 */
/* ---------- 用户风格 A/B/C：只换"我的颜色"，不换品牌签名 ----------
   第四轮把 --echo 也交给风格切换，结果是"签名跟着设置跑"——
   第五轮的规矩：**Ring/Wave/Thread 永远是回响橙**（--echo 不在这个块里被覆写），
   风格只决定主按钮、选中态、和我这一侧的气泡。
   紫色整条撤掉：原来 C 是 #6d4b93，用户明确说"紫色按钮破坏整体品牌感"，
   而它同时是导航选中态、发送键、徽章的底色。C 改成玫红，
   老用户的"我选的是第三种风格"这个偏好仍然保留，只是不再是紫。
   三套的按钮面一律"亮面 + 深字"（实测 5.5 / 6.9 / 6.9:1），
   不再出现"深砖红面 + 米白字"那种压抑感。 */
[data-style=b]{--tint:#2F6B4A;--tint-bg:rgba(47,107,74,.12);--btn-ink:#7FBF8F;--btn-ink-ink:#16291B;--ember:#4E9A6A;--ember-deep:#2F6B4A;--pg1:#2F6B4A}
[data-style=b][data-theme=dark]{--tint:#7FC9A0;--tint-bg:rgba(127,201,160,.16);--btn-ink:#5FA87A;--btn-ink-ink:#0C1A11;--ember:#7FC9A0;--ember-deep:#5FA87A;--pg1:#7FC9A0}
[data-style=c]{--tint:#9E4257;--tint-bg:rgba(158,66,87,.12);--btn-ink:#E88A9C;--btn-ink-ink:#2E141A;--ember:#C85B6E;--ember-deep:#9E4257;--pg1:#9E4257}
[data-style=c][data-theme=dark]{--tint:#EC8FA1;--tint-bg:rgba(236,143,161,.16);--btn-ink:#D76B7B;--btn-ink-ink:#2A0E15;--ember:#EC8FA1;--ember-deep:#D76B7B;--pg1:#EC8FA1}

/* ============================================================
   L1b · SIGNATURE —— 回响三件套：Ring / Wave / Thread
   ------------------------------------------------------------
     Ring   头像周围的一圈 —— 「TA 正在发生变化」：正在输入 / 有新消息 / 在线
     Wave   从头像向外荡开的一圈 —— 「有一件事发生了」：主动找你 / 新记忆 / 新关系事件
     Thread 一条自己画出来的连线 —— 「过去和现在发生了关系」

   一句话：**过去的一点，向现在荡过来。**

   第五轮的两条硬规矩（第四轮的签名"看不见"就是栽在这两条上）：
   ① 颜色钉死 --echo（回响橙），不再跟随用户风格。删掉 logo 还认得出回响，
      靠的是"永远是这个橙 + 这个形状"，不是一个会变的色相。
   ② 尺寸下限：线 2.5px、环 1.5px、端点带实心节点。
      1px 的线在手机上根本看不见（本轮实测：Thread 用 2px 无节点时，
      Landing 那张图里它几乎不存在）。
   加新用法之前先问：这一刻真的和"TA 正在回应我"或"过去连到了现在"有关吗？
   无关就别加——签名一旦到处出现，就不再是签名。
   ============================================================ */

/* ---------- Ring：TA 正在发生变化的那一圈 ----------
   .echo        容器（通常是头像的包裹层）
   .echo.on     正在输入：两圈错开荡出去（全站唯一允许 infinite 的地方）
   .echo.here   TA 在线 / 刚找过你：**静止**的一圈，不循环——看得见但不是"网页一直在动" */
.echo{position:relative;display:inline-grid;place-items:center;flex:0 0 auto}
.echo::before,.echo::after{
  content:"";position:absolute;inset:0;border-radius:50%;
  border:1.5px solid var(--echo);opacity:0;pointer-events:none}
.echo.on::before{animation:echo-out var(--dur-echo) var(--ease-out) infinite}
.echo.on::after{animation:echo-out var(--dur-echo) var(--ease-out) infinite;
  animation-delay:calc(var(--dur-echo) / 2.4)}
/* 「TA 在」的那一圈：一圈，不是两圈。
   第二/第三圈同心环是「游戏头像框」最明显的特征之一，两圈会把头像框成靶心。 */
.echo.here::before{opacity:.55;transform:none;animation:none;inset:-4px}
.echo.here::after{display:none}
@keyframes echo-out{
  0%{transform:scale(.55);opacity:.62}
  70%{opacity:0}
  100%{transform:scale(1.62);opacity:0}}

/* ---------- Wave：一次荡开，播完就消失 ----------
   用法：JS 给宿主元素加 .waving，animationend 后摘掉。
   只动 transform/opacity，不加 box-shadow 动画（实测那是掉帧的来源）。
   第五轮把一圈改成**两圈错峰**：一圈在 390 宽的手机上荡完就没了，肉眼跟不上。 */
.waving{position:relative}
.waving::before,.waving::after{
  content:"";position:absolute;left:50%;top:50%;width:100%;height:100%;
  border-radius:50%;border:2px solid var(--echo);pointer-events:none;
  transform:translate(-50%,-50%) scale(.3);opacity:0;
  animation:echo-wave var(--d-wave) var(--ease-out) 1 forwards}
.waving::after{animation-delay:.16s;border-width:1.5px}
@keyframes echo-wave{
  0%{transform:translate(-50%,-50%) scale(.3);opacity:.9}
  55%{opacity:.55}
  100%{transform:translate(-50%,-50%) scale(2.1);opacity:0}}
/* 语义变体：记忆用青瓷、关系用玫瑰——同一形状，不同气质（§色彩纪律） */
.waving.mn::before,.waving.mn::after{border-color:var(--mnem-ink)}
.waving.bd::before,.waving.bd::after{border-color:var(--bond-ink)}

/* ---------- Thread：把两个时间点连起来的那根线 ----------
   这是"回响"最直接的可视化：一条线自己画过去，连到过去那句话。
   用 stroke-dashoffset 描边，一次性、不吃帧、不参与滚动重绘。
   .thread 现在自带**两端节点**（过去一个空心点、现在一个实心点）：
   没有节点的线读起来像分隔线，有了节点才读起来"这是一条时间线"。 */
.thread{display:block;width:2.5px;height:100%;overflow:visible;flex:0 0 auto}
.thread line,.thread path{
  stroke:var(--echo);stroke-width:2.5;stroke-linecap:round;fill:none;
  stroke-dasharray:var(--tl,200);stroke-dashoffset:var(--tl,200);
  animation:echo-draw var(--d-thread) var(--ease-out) forwards}
.thread .nd{fill:var(--echo);opacity:0;animation:echo-nd .3s var(--ease-out) forwards}
.thread .nd.past{fill:none;stroke:var(--echo);stroke-width:2;animation-delay:0s}
.thread .nd.now{animation-delay:var(--d-thread)}
@keyframes echo-nd{to{opacity:1}}
.thread.mn line,.thread.mn path{stroke:var(--mnem)}
.thread.mn .nd{fill:var(--mnem)}.thread.mn .nd.past{stroke:var(--mnem)}
.thread.bd line,.thread.bd path{stroke:var(--bond)}
.thread.bd .nd{fill:var(--bond)}.thread.bd .nd.past{stroke:var(--bond)}
@keyframes echo-draw{to{stroke-dashoffset:0}}
/* 列表里的竖向连接线（echoLine() 发的 <i class="tline">）：
   用 scaleY 而不是 SVG 描边——它可能同时在页面上出现十几条，
   而且跟着列表滚动，描边会参与重绘。scaleY 只走合成层。
   ::before 是"过去"那一端的节点：有了它，一条 2.5px 的线才像一条线。 */
.tline{position:relative;display:block;width:2.5px;border-radius:2.5px;background:var(--echo);
  transform-origin:top center;animation:echo-grow var(--d-thread) var(--ease-echo) both}
.tline::before{content:"";position:absolute;left:50%;top:-3px;width:7px;height:7px;
  margin-left:-3.5px;border-radius:50%;background:var(--echo)}
.tline.mn{background:var(--mnem)}.tline.mn::before{background:var(--mnem)}
.tline.bd{background:var(--bond)}.tline.bd::before{background:var(--bond)}
@keyframes echo-grow{from{transform:scaleY(0)}to{transform:scaleY(1)}}

/* ---------- 被回响照到的那一条：短暂回亮，然后退掉 ---------- */
.lit{animation:echo-lit 1.2s var(--ease-out) 1}
@keyframes echo-lit{
  0%{background:var(--echo-bg)}
  100%{background:transparent}}

/* ---------- Aura：头像即签名 ----------
   六个轴全部由 app.js auraOf() 写进 --au-*，这里只负责把它们**组合**成一张脸。
   规矩：CSS 里不许出现 background 简写覆盖这些层（内联 background 会把整个
   多层渐变抹成一块死色，第四轮在 landing.html 踩过 9 次）。
     --au-a/--au-b   外环：内浅外深的主体
     --au-core       内环：中心高光核的半径
     --au-x/--au-y   光位：光从哪个方位照进来
     --au-r          环距：同心环纹的间距
     --au-tex        纹理：0 环纹 / 1 斜织 / 2 细闪 / 3 素面
   字色写死深墨：底色是明暗两套共用的淡染，跟着 --ink 变亮会做出 1.24:1。 */
.aura{background:
    var(--au-tex,repeating-radial-gradient(circle at 50% 50%,
      rgba(255,255,255,.17) 0 1px,rgba(255,255,255,0) 1px var(--au-r,7px))),
    radial-gradient(circle at var(--au-x,32%) var(--au-y,26%),
      rgba(255,255,255,.42) 0%,rgba(255,255,255,0) var(--au-core,34%)),
    radial-gradient(circle at var(--au-x,32%) var(--au-y,26%),
      var(--au-a,#FBEAD4) 0%,var(--au-b,#C85B3F) 138%)}
/* 头像呼吸：只给「正在陪你的那个人」（聊天顶栏）。
   有界，不 infinite——实测 Kindroid 的呼吸是 1.6s × 6 次 alternate 就停。
   一直动的头像，用户的判词是「网页一直在动」；呼吸三下停住，
   读起来才是「一个人在那儿」。列表里每个头像都呼吸 = 游戏头像框，禁止。 */
.breath{animation:av-breath 1.6s var(--ease-breathe) 6 alternate}
@keyframes av-breath{0%{transform:scale(1)}100%{transform:scale(1.045)}}

@media (prefers-reduced-motion:reduce){
  .echo.on::before,.echo.on::after{animation:none;opacity:.34;transform:none}
  .waving::before,.waving::after{animation:none;opacity:0}
  .thread line,.thread path{animation:none;stroke-dashoffset:0}
  .thread .nd{animation:none;opacity:1}
  .tline{animation:none;transform:none}
  .lit{animation:none}
  .breath{animation:none}}
/* ============================================================
   L2 · BASE
   ============================================================ */
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;height:100%}
/* 部分安卓浏览器会按字数放大正文，字号档位由我们自己管 */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
input,textarea{caret-color:var(--tint)}
/* 列表/抽屉滚到头不再带动整页回弹，滚动各归各的 */
.page,.stream,.conv,.sheet,.stk-panel,.pzseg,.pzchips{overscroll-behavior:contain}
/* 平色底 + 一层远处的暖光 + 一层极细颗粒。
   上一版这里只有一个平色 background-color，全站一个平面走到底——
   情绪和"私人空间"的感觉来自纵深，不来自间距。
   光晕是静态的（不参与动画），颗粒是 CSS 生成的（零请求），
   两者在 prefers-reduced-transparency / 低端设备下整层关掉（见 L7 末尾）。 */
/* 页面底：暮光（上暖下沉的整幅渐变）+ 一盏顶光 + 织纹。
   第四轮只有一层 glow，实测仍然是"一个平面走天下"——
   因为 glow 只在最上面 78% 有一点亮，中下部完全没有层次。
   background-attachment:fixed 让这束光不跟着内容滚，滚的是纸，不是光。 */
body{background-color:var(--bg);background-image:var(--glow),var(--grain),var(--dusk);
  background-attachment:fixed;
  color:var(--ink);
  font:var(--fs-body)/var(--lh-body) var(--font-ui);
  display:flex;flex-direction:column;overflow:hidden;
  /* 外壳高度 = 视觉视口高度（软键盘弹出时由 chat.js 写 --vv-h）。
     为什么改的是**外壳**而不是给输入条加 padding：见 chat.js 里
     fitKeyboard 那段——Chrome 108+ 键盘只缩视觉视口，100dvh 不动，
     只顶输入条的话 .stream 算的还是整屏高，最后一条就落在键盘底下。 */
  height:var(--vv-h,100dvh);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  transition:background-color .3s,color .3s}
a{color:var(--tint);text-decoration:none}
button{font:inherit;cursor:pointer;border:0;background:none;color:inherit}
input,textarea,select{font:inherit;color:inherit}
/* 组件常用 display:grid/flex，会盖掉 UA 的 [hidden]{display:none} */
[hidden]{display:none !important}
::selection{background:var(--fill)}
/* 键盘可达：只在键盘聚焦时描边，鼠标点击不打扰（表单控件已有自己的聚焦环） */
a:focus-visible,button:focus-visible,[tabindex]:focus-visible{
  outline:2px solid var(--tint);outline-offset:2px;border-radius:var(--r-xs)}
/* 标题折行时上下行长短接近，别留一行只有一两个字（shadcn 全站 text-balance） */
.auth h1,.dialog h2,.sheet .head h2,.share-head h1,.chat-empty h3,.sp-empty h3,
.ml-head h1,.doc-bar h1,.pd-tx h1,.card h2,.grp>h3,.empty h3{text-wrap:balance}
/* Chrome 自动填充会刷成蓝底黄底，暗色下尤其脏：压回主题色 */
.auth input:-webkit-autofill,.field input:-webkit-autofill{
  -webkit-text-fill-color:var(--ink);box-shadow:0 0 0 100px var(--card-solid) inset;
  transition:background-color 9999s}
/* 辅助说明统一走这一档 */
.eyebrow{font-size:var(--fs-cap);font-weight:600;color:var(--ink2);letter-spacing:var(--ls-caps)}
.hintline{font-size:var(--fs-cap);color:var(--ink2);line-height:1.6;margin-top:8px}
/* 计数、时间戳、token 数一律等宽数字 */
.day span,.clog-ts,.rl-num,.pz-meta,.kpi b,table td.num{font-variant-numeric:tabular-nums}

/* 晕动/省电动效偏好：整站过渡与动画降到瞬时 */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important}}
/* ============================================================
   L3 · 外壳与布局
   ============================================================ */
/* ---------- 顶栏：一层实底 + 发丝线。不半透明、不糊、不投影 ---------- */
.topbar{flex:0 0 auto;display:flex;align-items:center;gap:var(--s-3);
  padding:var(--pad-top) var(--gut) calc(var(--pad-top) + env(safe-area-inset-top));
  padding-top:calc(var(--pad-top) + env(safe-area-inset-top));
  background-color:var(--bg);
  border-bottom:1px solid var(--sep2);
  position:relative;z-index:5}
.topbar h1{font-size:var(--fs-title);font-weight:600;margin:0;flex:1;min-width:0;
  letter-spacing:var(--ls-title);line-height:var(--lh-tight);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.topbar .sub{font-size:var(--fs-foot);color:var(--ink2);font-weight:400;display:block;line-height:1.4;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.icon-btn{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;position:relative;
  color:var(--ink2);font-size:18px;flex:0 0 auto;
  transition:background var(--dur-fast),color var(--dur-fast),transform var(--dur-press)}
.icon-btn::before{content:"";position:absolute;inset:-4px}   /* 视觉 36px，热区 44px */
.icon-btn svg{width:21px;height:21px;display:block}
/* 按压反馈用 scale，不用 hover 抬升加阴影（Agent Elements 全站如此） */
.icon-btn:active{transform:scale(.94);background:var(--fill)}
/* 选中态 = 一层中性底 + 加深字色。不给强调色底，强调色只留给「未读/正在输入」 */
.icon-btn.on{color:var(--ink);background:var(--fill)}

/* ---------- 会话列表行（首页 / 抽屉共用一套节奏） ---------- */
.conv{flex:1 1 auto;overflow-y:auto;background-color:transparent;
  -webkit-overflow-scrolling:touch;padding-bottom:calc(var(--s-4) + env(safe-area-inset-bottom))}
.conv-row{display:flex;align-items:center;gap:var(--s-3);padding:12px var(--gut);cursor:pointer;
  position:relative;transition:background var(--dur-fast)}
/* 分隔线从文字起笔，不顶到头像下面，列表读起来更整 */
.conv-row::after{content:"";position:absolute;left:calc(var(--gut) + 46px + var(--s-3));right:0;
  bottom:0;height:1px;background:var(--sep2)}
.conv-row:last-child::after{display:none}
.conv-row:active{background:var(--fill2)}
.conv-row .av{width:46px;height:46px;font-size:18px;border-radius:50%}
.conv-mid{flex:1;min-width:0}
.conv-nm{font-size:var(--fs-body);font-weight:600;letter-spacing:var(--ls-tight);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.conv-on{display:inline-block;margin-left:6px;font-size:var(--fs-micro);font-weight:600;
  color:var(--tint);background:var(--tint-bg);border-radius:var(--r-pill);padding:1px 6px}
.conv-pv{font-size:var(--fs-foot);color:var(--ink2);line-height:1.45;margin-top:3px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.conv-pv.dim{color:var(--ink3)}
.conv-tm{flex:0 0 auto;align-self:flex-start;padding-top:2px;font-family:var(--font-num);
  font-variant-numeric:tabular-nums;font-size:var(--fs-cap);color:var(--ink2)}
.conv-tip{display:flex;align-items:center;gap:6px;padding:14px var(--gut) 9px;
  font-size:var(--fs-cap);color:var(--ink2);cursor:pointer}
.conv-tip .n{font-weight:600}
.conv-acts{display:flex;gap:var(--s-3);padding:var(--s-4) var(--gut)}
.conv-acts .btn{flex:1}

/* ---------- 页面容器 ---------- */
.page{flex:1 1 auto;overflow-y:auto;padding:var(--s-5) var(--gut) calc(var(--s-7) + env(safe-area-inset-bottom));
  background-color:transparent;-webkit-overflow-scrolling:touch}
/* 宽屏桌面下把内容收成单列，避免卡片和入口行被拉到整屏宽 */
.page.wrap{max-width:680px;margin-inline:auto;width:100%}
/* 桌面端默认滚动条又宽又灰，站内所有滚动区统一压成细条 */
.page,.stream,.conv,.sheet,.stk-panel,.pzseg,.pzchips{scrollbar-width:thin}
.page::-webkit-scrollbar,.stream::-webkit-scrollbar,.conv::-webkit-scrollbar,
.sheet::-webkit-scrollbar,.stk-panel::-webkit-scrollbar,.dialog .body::-webkit-scrollbar,
pre.prompt::-webkit-scrollbar{width:8px;height:8px}
.page::-webkit-scrollbar-thumb,.stream::-webkit-scrollbar-thumb,.conv::-webkit-scrollbar-thumb,
.sheet::-webkit-scrollbar-thumb,.stk-panel::-webkit-scrollbar-thumb,
.dialog .body::-webkit-scrollbar-thumb,pre.prompt::-webkit-scrollbar-thumb{
  background:var(--fill);border-radius:4px}
.page::-webkit-scrollbar-track,.stream::-webkit-scrollbar-track,.conv::-webkit-scrollbar-track,
.sheet::-webkit-scrollbar-track,.stk-panel::-webkit-scrollbar-track,
.dialog .body::-webkit-scrollbar-track,pre.prompt::-webkit-scrollbar-track{background:transparent}
/* 顶栏仍通铺，但内容左右内缩到和 .page.wrap 的正文对齐 */
.topbar.wrap{padding-inline:max(var(--gut), calc(50% - 324px))}

/* ---------- 卡片：一个 24 贯穿内距，靠发丝线分层，零投影 ---------- */
.card{background:var(--card-solid);border:1px solid var(--sep2);border-radius:var(--r-card);
  padding:var(--s-6);margin-bottom:var(--s-4)}
.card h2{font-size:var(--fs-title);margin:0 0 var(--s-5);color:var(--ink);font-weight:600;
  letter-spacing:var(--ls-title);line-height:var(--lh-tight);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s-2)}
.card h2 .hint{font-size:var(--fs-cap);color:var(--ink2);font-weight:400;text-align:right}
.field{margin-bottom:var(--s-4)}
.field:last-child{margin-bottom:0}
.field label{display:block;font-size:var(--fs-foot);font-weight:600;color:var(--ink2);
  margin-bottom:var(--s-2);letter-spacing:var(--ls-label)}
.field input[type=text],.field input[type=password],.field input[type=url],
.field input[type=number],.field input[type=time],.field textarea,.field select{width:100%;
  min-height:44px;padding:11px 13px;border:1px solid var(--sep);border-radius:var(--r-input);
  background:var(--fill2);outline:0;font-size:var(--fs-body);line-height:1.5;
  transition:border-color var(--dur-fast),box-shadow var(--dur-fast),background var(--dur-fast)}
.field textarea{min-height:84px;resize:vertical;line-height:var(--lh-prose)}
.field input:focus,.field textarea:focus,.field select:focus{border-color:var(--tint);
  box-shadow:var(--ring);background:var(--card-solid)}
.field input:disabled,.field textarea:disabled,.field select:disabled{
  background:var(--fill2);color:var(--ink3);cursor:not-allowed}
.field .row2{display:flex;gap:var(--s-3)}
.field .row2>*{flex:1}
h2.fold{cursor:pointer;user-select:none;justify-content:flex-start}
h2.fold .hint{margin-left:auto}
h2.fold::before{content:"▸ ";color:var(--ink3);font-size:.9em}
h2.fold.open::before{content:"▾ "}
/* ---------- 可展开的设置行（settings.js 的 foldRow / 模型列表） ----------
   原来这里是 h2.fold + ::before 画一个 ▸/▾——那是 <details> 的脸，
   在一页正经设置项里格外像开发者后台。换成整行可点：
   左标题 + 下一行副说明，右边一个会转 90° 的 ›，展开走高度过渡 + 内容淡入。
   命中区 46px（>44），键盘可达（它是 <button>，带 aria-expanded）。 */
.fold-row{display:flex;align-items:center;gap:10px;width:100%;min-height:46px;
  padding:10px 2px;text-align:left;background:none;border:0;color:var(--ink);
  cursor:pointer;transition:opacity var(--dur-fast)}
.fold-row:active{opacity:.62}
.fr-tx{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.fr-tx b{font-size:var(--fs-sub);font-weight:600;letter-spacing:var(--ls-tight);line-height:1.35}
.fr-tx i{font-style:normal;font-size:var(--fs-cap);color:var(--ink2);line-height:1.5}
.fr-ar{flex:0 0 auto;font-size:19px;line-height:1;color:var(--ink3);
  transition:transform .2s var(--ease-out),color .2s var(--ease-out)}
.fold-row.open .fr-ar{transform:rotate(90deg);color:var(--tint)}
/* 展开容器：max-height 由 JS 量出来再写，0fr→1fr 那种写法老内核不认，
   退化成"瞬间切换"比退化成"打不开"体面。 */
.fold-box{overflow:hidden;max-height:0;opacity:0;
  transition:max-height .24s var(--ease-out),opacity .2s var(--ease-out)}
.fold-box:not([hidden]){opacity:1}
.mfold-list{transition:max-height .24s var(--ease-out);overflow:hidden}
.mfold-btn{margin-top:2px}
.mfold-btn .fr-tx b{font-weight:600}
@keyframes mfold-fade{from{opacity:.4}to{opacity:1}}
.mfold-list.fade-in{animation:mfold-fade .22s var(--ease-out)}
@media (prefers-reduced-motion:reduce){
  .fold-box,.mfold-list,.fr-ar{transition:none}
  .mfold-list.fade-in{animation:none}
}

.gen-hint{margin-top:7px;font-size:var(--fs-cap);color:var(--ink2);line-height:1.6}

/* ---------- 弹层：遮罩是唯一被允许糊的一层背景 ---------- */
.mask{position:fixed;inset:0;background:var(--mask-bg);z-index:60;
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);
  opacity:0;transition:opacity var(--dur-base)}
.mask.show{opacity:1}
/* 底部抽屉：抓手 100×8、上圆角、顶发丝线、最高 82vh（shadcn Drawer 逐字） */
.sheet{position:fixed;left:0;right:0;bottom:0;max-height:82dvh;overflow:auto;z-index:61;
  background-color:var(--bg);
  border-radius:var(--r-panel) var(--r-panel) 0 0;padding:var(--s-2) var(--gut) calc(28px + env(safe-area-inset-bottom));
  box-shadow:var(--sh2);border-top:1px solid var(--sep);
  transform:translateY(100%);transition:transform var(--dur-sheet) var(--ease-out)}
.sheet.show{transform:translateY(0)}
.sheet.full{top:8px;max-height:calc(100dvh - 16px);border-radius:var(--r-panel);left:8px;right:8px;
  bottom:8px;padding:10px 14px 32px}
.sheet.full::before{display:none}
.sheet::before{content:"";display:block;width:100px;height:8px;border-radius:999px;
  background:var(--fill);margin:8px auto 16px}
.dialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-46%) scale(.97);
  width:min(440px,calc(100% - 32px));background:var(--card-solid);border:1px solid var(--sep2);
  border-radius:var(--r-panel);z-index:61;padding:var(--s-6);box-shadow:var(--sh3);
  opacity:0;transition:opacity var(--dur-base) var(--ease-out),transform var(--dur-base) var(--ease-out)}
.dialog.show{opacity:1;transform:translate(-50%,-50%) scale(1)}
.dialog h2{font-size:var(--fs-title);font-weight:600;margin:0 0 var(--s-2);letter-spacing:var(--ls-title);
  line-height:var(--lh-tight)}
.dialog .body{font-size:var(--fs-sub);color:var(--ink2);line-height:var(--lh-prose);
  white-space:pre-wrap;max-height:46vh;overflow:auto}
.dialog .btn.block{margin-top:var(--s-4)}
.sheet .head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);
  margin:2px 0 var(--s-4)}
.sheet .head h2{font-size:var(--fs-title);font-weight:600;margin:0;letter-spacing:var(--ls-title);
  line-height:var(--lh-tight)}

/* ============================================================
   L4 · 组件
   ============================================================ */
/* ---------- 头像：正圆三档，兜底是 Aura（光晕 + 同心环纹）----------
   第三轮是一块平色 + 一个字，每个角色同一张脸。现在 8 套光晕由
   hashStr(pid) 确定，环纹就是品牌签名本身——头像即回响。
   两个色标刻意拉开到 8%/128%：可视区中心偏浅，字压上去才读得动
   （实测中心约 10:1，右下角最深处也有 4.4:1）。
   字色写死深墨：底色是两套主题共用的淡染，暗色下若跟着 --ink 变亮，
   就成「亮字压亮底」（上一版实测 1.24:1）。 */
.av{width:38px;height:38px;border-radius:50%;flex:0 0 auto;display:grid;place-items:center;
  color:#17150f;font-weight:600;font-size:15px;overflow:hidden;
  font-family:var(--font-prose);letter-spacing:0;
  background:
    var(--au-tex,repeating-radial-gradient(circle at 50% 50%,
      rgba(255,255,255,.17) 0 1px,rgba(255,255,255,0) 1px var(--au-r,7px))),
    radial-gradient(circle at var(--au-x,30%) var(--au-y,24%),
      rgba(255,255,255,.40) 0%,rgba(255,255,255,0) var(--au-core,34%)),
    radial-gradient(circle at var(--au-x,30%) var(--au-y,24%),
      var(--au-a,#FBEAD4) 0%,var(--au-b,#C85B3F) 140%);
  user-select:none;-webkit-user-select:none;box-shadow:var(--av-stroke)}
.av img{width:100%;height:100%;object-fit:cover;display:block}
.av.lg{width:58px;height:58px;font-size:24px}
.av.sm{width:30px;height:30px;font-size:12px}
.av.uploadable{position:relative;cursor:pointer}
.av.uploadable::after{content:"";position:absolute;inset:0;background:rgba(0,0,0,0);
  border-radius:inherit;transition:background var(--dur-fast)}
.av.uploadable:active::after{background:rgba(0,0,0,.2)}
/* TA 在线 / 刚找过你：头像外一圈静止的细弧（不循环，循环只留给"正在输入"） */
.av.halo{box-shadow:0 0 0 2px var(--bg),0 0 0 3px var(--echo-bg),var(--av-stroke)}

/* ---------- 按钮：主行动是墨，不是品牌色。hover 只降亮度，不抬升 ---------- */
.btn{display:inline-block;padding:11px 16px;border-radius:var(--r-btn);font-size:var(--fs-sub);
  font-weight:600;background:var(--fill);color:var(--ink);text-align:center;border:0;
  letter-spacing:var(--ls-tight);line-height:1.4;
  transition:transform var(--dur-press),opacity var(--dur-fast),background var(--dur-fast),
             box-shadow var(--dur-fast),color var(--dur-fast)}
.btn:hover{background:color-mix(in srgb,var(--fill) 150%,transparent)}
.btn.primary{background:var(--btn-ink);color:var(--btn-ink-ink)}
.btn.primary:hover{background:color-mix(in srgb,var(--btn-ink) 88%,var(--bg))}
/* 次级动作 = 一层中性底 + 发丝线，不给渐变、不给投影 */
.btn.ghost{background:transparent;color:var(--ink2);box-shadow:inset 0 0 0 1px var(--sep)}
.btn.ghost:hover{background:var(--fill2);color:var(--ink)}
.btn.danger{background:var(--warn-bg);color:var(--warn)}
.btn.gold{background:var(--gold-bg);color:var(--gold)}
.btn.block{display:block;width:100%}
.btn:active{transform:scale(.98)}
.btn[disabled]{opacity:.45;pointer-events:none}
.btn.grow{flex:1}

/* ---------- 胶囊 / 开关 / 分段 ---------- */
.chip{font-size:var(--fs-cap);font-weight:600;background:var(--fill);color:var(--ink2);
  border-radius:var(--r-pill);padding:3px 10px;letter-spacing:var(--ls-label)}
button.chip{cursor:pointer;border:1px solid transparent;line-height:1.6;
  transition:background var(--dur-fast),color var(--dur-fast),border-color var(--dur-fast)}
button.chip:hover{background:var(--fill2)}
button.chip.on{background:var(--tint-bg);color:var(--tint);font-weight:600}
/* 亮色下 12px 的选中胶囊压 11% 同色底只有 4.25:1，差一点但确实读不动；
   压深一档到 --ember-deep。暗色那一侧本来就过，不动。 */
[data-theme=light] button.chip.on{color:var(--ember-deep)}
button.chip.mhide{display:none}
/* 主题配色选择器：色点 + 名字（三套风格现在只差一个强调色，色点也就用平色） */
button.chip .sw-dot{display:inline-block;width:10px;height:10px;border-radius:50%;
  margin-right:6px;vertical-align:-1px;background:var(--tint)}
button.chip .sw-dot.s-a{background:#E58A43}
button.chip .sw-dot.s-b{background:#7FBF8F}
button.chip .sw-dot.s-c{background:#E88A9C}
.switch{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);
  padding:14px 0;border-bottom:1px solid var(--sep2);font-size:var(--fs-sub)}
.switch:last-child{border-bottom:0}
.switch .desc{font-size:var(--fs-foot);color:var(--ink2);margin-top:2px;line-height:1.5}
.sw{width:47px;height:28px;border-radius:28px;background:var(--fill);position:relative;
  flex:0 0 auto;transition:background var(--dur-base) var(--ease-out);cursor:pointer}
.sw::after{content:"";position:absolute;top:2px;left:2px;width:24px;height:24px;border-radius:50%;
  background:#fff;transition:transform var(--dur-base) var(--ease-out);
  box-shadow:0 1px 3px rgba(0,0,0,.22)}
.sw.on{background:var(--btn-ink)}
.sw.on::after{transform:translateX(19px)}
/* 胶囊分段控件（登录/注册、子页切换）：选中 = 白底 + 最轻投影，不用彩色 */
.seg{display:inline-flex;background:var(--fill2);border:1px solid var(--sep2);
  border-radius:var(--r-pill);padding:3px;gap:2px;flex:0 0 auto}
.seg a{padding:6px 15px;border-radius:var(--r-pill);font-size:var(--fs-foot);font-weight:600;
  color:var(--ink2);text-decoration:none;transition:background var(--dur-fast),color var(--dur-fast)}
.seg a.on{background:var(--card-solid);color:var(--ink);box-shadow:var(--sh1)}

/* ---------- 网格与角色卡：「这是一个人」而不是「一个配置项」 ---------- */
.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-3)}
/* 我的角色：整卡可点进详情，手机上固定一行两个。零投影，靠一圈发丝线立住。 */
.pcard{background:var(--card-solid);border:0;border-radius:var(--r-card);
  padding:0;display:flex;flex-direction:column;align-items:stretch;gap:0;
  aspect-ratio:3/4;overflow:hidden;text-align:left;cursor:pointer;
  position:relative;box-shadow:var(--sh-lift);
  transition:transform var(--dur-press),box-shadow var(--dur-base)}
.pcard:active{transform:scale(.975)}
/* 通讯录里的空位：不是外面一颗大按钮，是这一堆人当中"还可以有一个人"的位置。
   虚线是"待填"的通用语言，所以这里刻意不用虚线，用一层很浅的暮光 + 一个加号。 */
.pcard.add{background:var(--echo-wash);color:var(--echo-ink);border:0;box-shadow:none;
  display:grid;place-items:center;text-align:center;padding:14px;cursor:pointer;
  transition:transform var(--dur-press),background var(--dur-fast)}
.pcard.add:active{transform:scale(.975)}
.pcard.add .pa-in{display:flex;flex-direction:column;align-items:center;gap:5px}
.pcard.add i{font-style:normal;font-size:26px;line-height:1;color:var(--echo)}
.pcard.add b{font-family:var(--font-prose);font-size:var(--fs-sub);font-weight:600}
.pcard.add em{font-style:normal;font-size:var(--fs-micro);color:var(--ink2)}
.pcard>.av{flex:1 1 auto;width:100%;min-height:0;height:auto;border-radius:0;
  font-size:34px;font-weight:600;background-size:cover;background-position:center}
.pcard>.av img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* 封面与文字区之间不做「渐隐压字」：没有配图时那道渐变会糊成一条脏带。
   改成硬切 + 一道发丝线，文字区是干净的卡片底。 */
.pcard>.av::before{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;z-index:1;
  background:var(--sep2);pointer-events:none}
.pcard:active{transform:scale(.99)}
.pcard.active{border-color:var(--ink);box-shadow:var(--ring)}
/* 名字用衬线：和详情页、聊天顶栏、首页卡同一规矩——人名是"人"的一部分，
   不是列表项标题。 */
.pcard .nm{position:relative;z-index:2;margin-top:auto;padding:11px 12px 0;
  font-family:var(--font-prose);font-size:var(--fs-sub);font-weight:600;
  letter-spacing:.01em}
.pcard .tg{font-size:var(--fs-cap);color:var(--ink2);line-height:1.5;padding:3px 12px 0;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pcard .pv{margin:5px 12px 12px;font-size:var(--fs-micro);color:var(--ink2);line-height:1.5;
  overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:1;
  -webkit-box-orient:vertical}
.pcard .pv.dim{opacity:.8}
.pcard .chip{position:absolute;top:8px;left:8px;z-index:2;margin:0;font-size:var(--fs-micro);
  background:var(--tint);color:var(--tint-ink);font-weight:600;padding:2px 8px}
.pcard .fav{position:absolute;top:8px;right:8px;z-index:2;font-size:var(--fs-micro);font-weight:600;
  color:var(--gold);background:var(--gold-bg);border-radius:var(--r-pill);padding:2px 7px}

/* ---------- toast / 空状态 ---------- */
.toast{position:fixed;left:50%;bottom:calc(96px + env(safe-area-inset-bottom));
  transform:translateX(-50%) translateY(6px);background:var(--toast-bg);color:var(--bg);
  padding:10px 18px;border-radius:var(--r-pill);font-size:var(--fs-sub);font-weight:500;z-index:99;
  max-width:84vw;text-align:center;
  opacity:0;pointer-events:none;transition:opacity var(--dur-base),transform var(--dur-base);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:var(--sh2)}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
/* 空状态：一句话 + 一行说明 + 一个动作。不插画、不图标墙、不上色。 */
.empty{text-align:center;color:var(--ink2);font-size:var(--fs-sub);
  padding:var(--s-9) var(--s-6);line-height:var(--lh-prose);max-width:34ch;
  margin-inline:auto}
.empty h3{margin:0 0 6px;font-size:var(--fs-title);font-weight:600;color:var(--ink);
  letter-spacing:var(--ls-title)}
.empty p{margin:0 0 var(--s-5)}
.empty .btn{margin-top:var(--s-1)}
/* 聊天页没有角色时的整页留白 */
.chat-empty{display:flex;flex-direction:column;align-items:center;
  text-align:center;padding:16vh 26px 40px;color:var(--ink2);gap:var(--s-1)}
.chat-empty .ce-ic{width:64px;height:64px;border-radius:50%;
  background:var(--echo-wash);color:var(--echo-ink);display:grid;place-items:center;
  margin-bottom:var(--s-4);box-shadow:0 0 0 1.5px var(--echo-bg) inset}
.chat-empty .ce-ic svg{width:28px;height:28px}
.chat-empty h3{margin:0;font-size:var(--fs-title);font-weight:600;color:var(--ink);
  letter-spacing:var(--ls-title)}
.chat-empty p{margin:0 0 var(--s-5);font-size:var(--fs-foot);line-height:var(--lh-prose);max-width:30ch}
.chat-empty .ce-acts{display:flex;gap:var(--s-2);flex-wrap:wrap;justify-content:center}

/* 首屏骨架：chat.html 服务端直出（理由见该文件注释）。占位尺寸照抄真实
   消息行（.row 的间距、38px 头像、两行气泡），动画只用 opacity 呼吸，
   不引入渐变扫光——那要给每个块挂 repaint，首屏不值得。 */
.sk{padding-top:2px}
.sk-row{display:flex;gap:var(--s-2);margin:18px 0;align-items:flex-start;
  animation:sk-pulse 1.4s ease-in-out infinite alternate}
.sk-row.me{flex-direction:row-reverse}
.sk-av{width:38px;height:38px;border-radius:50%;flex:0 0 auto;background:var(--fill2)}
.sk-lines{display:flex;flex-direction:column;gap:6px;padding-top:9px}
.sk-lines i{display:block;height:12px;border-radius:6px;background:var(--fill2)}
.sk-lines i:nth-child(1){width:64%}
.sk-lines i:nth-child(2){width:42%}
.sk-row.me .sk-lines i:nth-child(1){width:48%}
.sk-row.me .sk-lines i:nth-child(2){width:30%}
@keyframes sk-pulse{from{opacity:.55}to{opacity:1}}

/* ---------- 其他通用件 ---------- */
.filehide{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden}
.split{display:flex;gap:var(--s-3);align-items:flex-end}
.split>*{flex:1}
pre.prompt{background:var(--fill2);border:1px solid var(--sep2);border-radius:var(--r-m);
  padding:12px;font-size:var(--fs-cap);line-height:1.65;white-space:pre-wrap;word-break:break-all;
  max-height:260px;overflow:auto;font-family:var(--mono);
  color:var(--ink2)}
.grow{display:flex;align-items:center;gap:var(--s-2);justify-content:space-between}
.code-pill{font-family:var(--mono);font-size:var(--fs-body);
  font-weight:600;letter-spacing:.08em;color:var(--tint);background:var(--tint-bg);
  padding:7px 14px;border-radius:var(--r-m);user-select:all}

/* ---------- 站内弹窗与选择控件（替代原生 confirm/prompt/下拉） ---------- */
.dlg-btns{display:flex;flex-direction:column-reverse;gap:var(--s-2);margin-top:var(--s-5)}
.dlg-btns .btn{flex:1;padding:13px 8px;min-height:46px}
/* 触屏上没有 hover 补偿，弹窗这两个钮是最后一道确认，热区给到 44 以上 */
@media (hover:none){.dlg-btns .btn{padding:14px 8px}}
/* 数据与备份页的导入选件：原生「选择文件」控件和站内按钮不是一套东西，很出戏。
   这里只换皮、不改行为 —— 仍然是同一个 <input type=file>，JS 读的照样是 pick.files，
   导入的预览/写入/4MB 那道判断一个字没动。:not(.filehide) 是必须的：
   属性选择器比类选择器更具体，不排除就会把那几个藏起来的选图 input 撑出 1px 的框。 */
input[type=file]:not(.filehide){width:100%;padding:10px;border:1px solid var(--sep);
  border-radius:var(--r-input);background:var(--fill2);color:var(--ink2);
  font-size:var(--fs-sub);line-height:1.4}
input[type=file]:not(.filehide)::file-selector-button{margin-right:10px;padding:8px 14px;
  border:0;border-radius:var(--r-btn-sm);background:var(--fill);color:var(--ink);
  font:inherit;font-weight:600;cursor:pointer}
input[type=file]:not(.filehide):focus-visible{border-color:var(--tint);box-shadow:var(--ring)}
.dialog .field{margin-top:14px}
.hint-line{font-size:var(--fs-cap);color:var(--ink2);margin-top:5px;line-height:1.6}
select.pick-src,.field select.pick-src{position:absolute;width:1px;height:1px;padding:0;margin:0;
  border:0;opacity:0;pointer-events:none}
.seg button{padding:6px 15px;border-radius:var(--r-pill);font-size:var(--fs-foot);font-weight:600;
  color:var(--ink2);background:none;border:0;transition:background var(--dur-fast),color var(--dur-fast)}
.seg button.on{background:var(--card-solid);color:var(--ink);box-shadow:var(--sh1)}
.seg.pick-seg{display:flex;width:100%}
.seg.pick-seg button{flex:1}
.pick-btn{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);width:100%;
  min-height:44px;padding:11px 13px;border-radius:var(--r-input);border:1px solid var(--sep);
  background:var(--fill2);
  color:var(--ink);font-size:var(--fs-body);font-weight:500;text-align:left;
  transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}
.pick-btn:focus-visible{border-color:var(--tint);box-shadow:var(--ring)}
.pick-btn::after{content:"";flex:0 0 auto;width:7px;height:7px;border-right:1.6px solid var(--ink3);
  border-bottom:1.6px solid var(--ink3);transform:rotate(45deg);margin-top:-3px}
.pick-list{display:flex;flex-direction:column;gap:var(--s-2)}
.pick-item{padding:13px 14px;border-radius:var(--r-m);border:1px solid var(--sep2);
  background:var(--card-solid);color:var(--ink);font-size:var(--fs-body);text-align:left;
  transition:border-color var(--dur-fast),background var(--dur-fast)}
.pick-item.on{border-color:var(--ink);color:var(--ink);font-weight:600;background:var(--fill)}
/* 站内弹窗要盖在已打开的抽屉/面板上，遮罩也得跟着抬起来 */
.mask.lift{z-index:70}
.dialog.lift,.sheet.lift{z-index:71}

/* ---------- 消息动作表 / 收藏 / 排行 ---------- */
.quote{font-size:var(--fs-sub);color:var(--ink2);line-height:1.6;background:var(--fill2);
  border:1px solid var(--sep2);border-radius:var(--r-m);padding:10px 13px;margin-bottom:10px;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.actlist{display:flex;flex-direction:column;gap:6px}
.act{display:flex;align-items:center;gap:var(--s-2);width:100%;text-align:left;
  background:var(--card-solid);border:1px solid var(--sep2);border-radius:var(--r-m);
  padding:13px 15px;font-size:var(--fs-sub);font-weight:600;letter-spacing:var(--ls-tight);
  transition:background var(--dur-fast),transform var(--dur-press)}
.act:active{transform:scale(.99);background:var(--fill2)}
.act .ic{flex:0 0 auto;width:22px;text-align:center;color:var(--ink2);font-size:15px}
.act.danger{color:var(--warn)}
.act.danger .ic{color:var(--warn)}
.act.gold{color:var(--gold)}
.act.gold .ic{color:var(--gold)}
.rank{display:flex;align-items:center;gap:var(--s-2);padding:11px 0;border-bottom:1px solid var(--sep2);
  font-size:var(--fs-sub)}
.rank:last-child{border-bottom:0}
.rank .no{flex:0 0 28px;text-align:center;font-weight:600;color:var(--ink2);
  font-variant-numeric:tabular-nums}
.rank.top .no{color:var(--gold)}
.rank .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.rank .ct{flex:0 0 auto;color:var(--ink2);font-variant-numeric:tabular-nums}
.rank.me{background:var(--fill);border-radius:var(--r-m);padding:11px 10px;border-bottom:0}
.rank.me .nm{color:var(--ink)}

/* ============================================================
   L5 · 聊天（产品最重要的界面）
   ============================================================ */
/* ---------- 会话流：沉底画布 + 一处顶光。聊天是"一个地方"，不是一张表 ---------- */
.stream{flex:1 1 auto;overflow-y:auto;overflow-x:hidden;
  padding:var(--s-5) var(--gut) 20px;
  background-color:var(--sunk);background-image:var(--glow),var(--room);
  -webkit-overflow-scrolling:touch;position:relative}
/* 下面这几张是「聊天背景」这个功能本身（用户可在设置里选），不是站内装饰，
   所以渐变保留；但底色一律压到很浅，避免盖过文字。 */
.stream.bg-mist{background-image:
  radial-gradient(120% 90% at 18% 0%,#f0f1ee 0%,rgba(240,241,238,0) 60%),
  linear-gradient(160deg,#f6f6f3 0%,#eaece7 55%,#e1e5df 100%)}
.stream.bg-sand{background-image:
  radial-gradient(130% 100% at 80% -10%,#faf5ec 0%,rgba(250,245,236,0) 55%),
  linear-gradient(165deg,#f8f3ea 0%,#efe6d6 100%)}
.stream.bg-sage{background-image:
  radial-gradient(120% 90% at 20% 10%,#eef3ee 0%,rgba(238,243,238,0) 60%),
  linear-gradient(160deg,#eef3ee 0%,#dfe8e0 100%)}
.stream.bg-dusk{background-image:
  radial-gradient(140% 110% at 50% -20%,#f4eae4 0%,rgba(244,234,228,0) 55%),
  linear-gradient(170deg,#eee4e3 0%,#e3e2e7 60%,#dde2e6 100%)}
.stream.bg-rose{background-image:
  radial-gradient(120% 90% at 75% 0%,#fdefec 0%,rgba(253,239,236,0) 60%),
  linear-gradient(160deg,#fbece8 0%,#f2e0da 100%)}
.stream.bg-ink{background-image:
  radial-gradient(110% 80% at 50% -10%,#39332e 0%,rgba(57,51,46,0) 60%),
  linear-gradient(165deg,#26221e 0%,#191614 100%)}
.stream.bg-custom{background-image:var(--ovr,linear-gradient(transparent,transparent)),var(--bg-img)}
/* 有背景图/深色系时气泡改实底，保证可读性。
   但**不给气泡加 blur**：一屏十几条、而且整块跟着滚动，是实测里最贵的一笔
   （300 条的会话上下滑 p95 帧时 40ms、掉 12 帧；去掉 blur 后 13.4ms、0 掉帧）。 */
.stream.bg-custom .bubble,.stream.bg-ink .bubble{background:rgba(255,253,249,.97)}
.stream.bg-custom .row.me .bubble,.stream.bg-ink .row.me .bubble{
  background:var(--me)}
[data-theme=dark] .stream.bg-custom .bubble,[data-theme=dark] .stream.bg-ink .bubble,
[data-theme=dark] .stream.bg-mist .bubble,[data-theme=dark] .stream.bg-sand .bubble,
[data-theme=dark] .stream.bg-sage .bubble,[data-theme=dark] .stream.bg-dusk .bubble,
[data-theme=dark] .stream.bg-rose .bubble{background:rgba(30,28,26,.97)}
[data-theme=dark] .stream.bg-mist{background-image:linear-gradient(160deg,#1e1d1b 0%,#161513 100%)}
[data-theme=dark] .stream.bg-sand{background-image:linear-gradient(165deg,#241f18 0%,#1a1611 100%)}
[data-theme=dark] .stream.bg-sage{background-image:linear-gradient(160deg,#1a211c 0%,#141915 100%)}
[data-theme=dark] .stream.bg-dusk{background-image:linear-gradient(170deg,#241d21 0%,#1a1a20 60%,#171b1e 100%)}
[data-theme=dark] .stream.bg-rose{background-image:linear-gradient(160deg,#281b19 0%,#1d1514 100%)}
[data-theme=dark] .stream.bg-ink{background-image:
  radial-gradient(110% 80% at 50% -10%,#2c2723 0%,rgba(44,39,35,0) 60%),
  linear-gradient(165deg,#171513 0%,#0e0d0c 100%)}

/* ---------- 日期分隔：安静的一行小字，不抢对话 ---------- */
.day{display:flex;justify-content:center;margin:var(--s-5) 0 var(--s-3);position:relative;z-index:1}
/* 日期分隔不是纯装饰：它告诉读者「这些话是什么时候说的」，Landing 上还兼当操作提示。
   所以走 --ink2；列表右侧那种「12:01」角标仍然留在 --ink3（iOS 同一取舍）。 */
.day span{font-size:var(--fs-cap);font-weight:500;color:var(--ink2);letter-spacing:var(--ls-label);
  font-family:var(--font-time);font-variant-numeric:tabular-nums;padding:2px 4px}
.notice{text-align:center;font-size:var(--fs-foot);color:var(--ink2);margin:var(--s-3) 0;line-height:1.6}
/* ---------- 聊天页的 AI 身份提示条（2026-10-05） ----------
   为什么是"一条永远在的窄边"而不是"首次弹窗，关掉就没了"：
   陪伴产品的风险不是"用户不知道这是 AI"这件事说一次就解决的，而是一条长期有效的
   显式标识 —— 关掉之后屏幕上连一个"这是 AI"的痕迹都不留，等于没标识。
   所以这里的取舍是：第一次进来给完整的两行说明，点「知道了」后**不收掉而是收成一行**，
   再点这一行还能展开。既不在每次聊天时重复打扰，也不让标识消失。

   为什么坐在 header 和 .stream 之间而不是塞进 .stream：
   renderAll() 开头就 stream.innerHTML=''，放在流里的东西活不过一次角色切换刷新。
   这里不用 backdrop-filter（全站只剩 4 个瞬时浮层能糊，见 selftest 那条守卫）。 */
.ai-warn{flex:0 0 auto;display:flex;align-items:flex-start;gap:var(--s-2);
  padding:9px var(--gut);font-size:var(--fs-foot);line-height:1.55;color:var(--ink2);
  background:var(--fill2);border-bottom:1px solid var(--sep);cursor:pointer;
  text-align:left;width:100%;appearance:none;-webkit-appearance:none;font-family:inherit}
.ai-warn .aw-ic{flex:0 0 auto;width:15px;height:15px;margin-top:1px;color:var(--echo-ink)}
.ai-warn .aw-body{flex:1 1 auto;min-width:0}
.ai-warn b{color:var(--ink);font-weight:600}
.ai-warn .aw-more{display:block;margin-top:3px}
.ai-warn .aw-x{flex:0 0 auto;align-self:center;font-size:var(--fs-cap);color:var(--tint);
  padding:3px 9px;border:1px solid var(--sep);border-radius:var(--r-pill);
  background:var(--card-solid);white-space:nowrap}
/* 收成一行：只留「回响是 AI，不是真人。」这半句，其余隐去，末尾给一个可点的暗示 */
.ai-warn.mini{padding:5px var(--gut);font-size:var(--fs-micro);letter-spacing:var(--ls-label)}
.ai-warn.mini .aw-full,.ai-warn.mini .aw-more,.ai-warn.mini .aw-x{display:none}
.ai-warn.mini .aw-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ai-warn.mini .aw-body::after{content:"· 点此看完整提示";color:var(--tint);margin-left:6px}

/* TA 主动发来的那几条。服务端一直在给历史条目打 m.proactive，
   分两种：schedule＝到点惦记你一句；intent＝接上你上次说过的约定。
   这两种值得分开写——后者才是"它真的记得你说过的话"的证据。
   第四轮把这条证据**画出来**：标签用签名色、头像外带一圈弧，
   intent 那一种左侧还有一条 Thread 把现在连回过去（见 chat.js）。 */
/* 主动消息那一行：标签 → 头像 → 气泡，三段各有自己的位置，不许叠。
   上一版 padding-top 只有 16px，标签（11px 字 + 行高）正好压在头像上沿，
   真机截图里读起来像"字掉进头像里了"。 */
.row.pro{position:relative;padding-top:26px}
.row.pro::before{content:"TA 主动想起你";position:absolute;top:4px;left:0;
  font-size:var(--fs-micro);color:var(--echo-ink);letter-spacing:var(--ls-label);font-weight:600;
  padding:2px 9px 2px 0}
.row.pro[data-pro="intent"]::before{content:"TA 记着你说的这件事"}
/* Ring 的静态一态：TA 主动找过你的那一条，头像外一圈不动的细环。
   一圈，不是两圈（两圈 = 游戏头像框）。 */
.row.pro .av{box-shadow:0 0 0 2px var(--room-bot),0 0 0 3px var(--echo-bg),var(--av-stroke)}
/* 回响照到的那一条：一条细线从标签垂到消息上，播一次就停 */
.pro-thread{position:absolute;left:18px;top:24px;height:calc(100% - 24px);width:2.5px}
.pro-thread .tline{height:100%}


/* ---------- 气泡：一条消息一颗，像微信 ----------
   上一版把同一轮里的几句合成一张白瓷，判错的地方是：那几句本来就是
   对方分几条发出来的消息，不是一个长句。合成一张之后读起来像
   "一段没写完的话"，中间夹进来的「拍了拍」也被塞进同一张纸里
   （真机截图正是这样）。所以回到一条一气泡。
   确实有用的两件事留着：
     ① 同一轮只在入口出现一次头像——这是 chat.js 的 ensureRow 本来就在做的
        连续同人合并，不是新加的数据结构；
     ② 组内 8px、组间 18px：近的一看就是同一个人在连发，远的才是换人说话。
   影子是暖褐的、很浅的两层（贴地 1px + 散开 22px），不给描边——
   描边是"卡片"的语言，影子才是"一张纸放在桌上"的语言。
   三声部照旧：TA 用衬线（人的声音、信的声音），我用无衬线（我打出来的字）。
   衬线不用 700：SimSun 没有真粗体，合成粗体会糊成一团。 */
.row{display:flex;gap:var(--s-2);margin:18px 0;align-items:flex-start}
.row.me{flex-direction:row-reverse}
.col{max-width:78%;display:flex;flex-direction:column;gap:8px;min-width:0}
.row.me .col{align-items:flex-end}
.bubble{word-break:break-word;white-space:pre-wrap;font-size:var(--fs-body);
  line-height:1.62;position:relative;letter-spacing:var(--ls-tight);
  padding:10px 14px;border-radius:var(--r-bubble);background:var(--bot);
  box-shadow:0 1px 2px rgba(120,84,44,.055),0 8px 20px rgba(120,84,44,.05)}
.row:not(.me) .bubble{color:var(--ink);font-family:var(--font-prose);
  line-height:var(--lh-prose);letter-spacing:.005em;
  border-top-left-radius:var(--r-tail)}
.row.me .bubble{color:var(--me-ink);background:var(--me);
  box-shadow:0 1px 2px rgba(150,80,30,.10),0 8px 20px rgba(150,80,30,.09);
  border-top-right-radius:var(--r-tail)}
/* 新消息荡进来：只给 .rv（chat.js 只给"这一轮之后新增的"加，
   历史回放不加——300 条一起入场就是灾难）。同一条回复的多气泡依次错开。 */
/* 整组一次入场，200ms、只动 opacity + 8px 位移，不缩放。
   上一版是"每条气泡各自错峰飞进来"，一轮三句就是三段动画——
   那正是"设计元素在抢戏"。一次发言，一次出现。 */
.row.rv{animation:msg-in .2s var(--ease-pop) both}
@keyframes msg-in{
  from{opacity:0;transform:translateY(8px)}
  to{opacity:1;transform:none}}
/* 用户自己挑了聊天背景时，气泡必须把脸要回来：字压在照片上没有底就读不出来。
   这几条与上面那条同权重、写在它后面，所以在这里说话算数。 */
.stream.bg-mist .row:not(.me) .bubble,.stream.bg-sand .row:not(.me) .bubble,
.stream.bg-sage .row:not(.me) .bubble,.stream.bg-dusk .row:not(.me) .bubble,
.stream.bg-rose .row:not(.me) .bubble,.stream.bg-ink .row:not(.me) .bubble,
.stream.bg-custom .row:not(.me) .bubble{background:var(--bot);border:1px solid var(--sep2);
  padding:10px 14px;border-radius:var(--r-bubble);border-top-left-radius:var(--r-tail)}

.bubble.recalled{background:transparent;box-shadow:none;border-color:transparent;
  font-size:var(--fs-foot);color:var(--ink2);padding:4px 2px;font-style:normal}
.tickle{font-size:var(--fs-foot);color:var(--tint);background:var(--tint-bg);border-radius:var(--r-pill);
  padding:6px 12px;display:inline-block;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
.emoji-pic{width:132px;max-height:160px;border-radius:var(--r-m);object-fit:contain;
  background:var(--fill2);display:block}
.typing{display:inline-flex;gap:var(--s-2);align-items:center;padding:8px 0;min-height:24px}
/* 签名动效的常驻用法只有这一个：TA 正在为你打字的时候才转。
   三个跳点谁都在用；一圈荡开的弧才是「回响」——你说出去的话会回来。 */
.typing .echo{width:22px;height:22px;color:var(--echo-ink)}
.typing em{font-style:normal;font-size:var(--fs-foot);color:var(--ink2);margin-left:4px;letter-spacing:var(--ls-label)}

/* ---------- 输入区：与正文同宽的安静一块，不像后台表单 ---------- */
.composer{flex:0 0 auto;display:flex;align-items:flex-end;gap:var(--s-2);
  padding:10px var(--gut) calc(10px + env(safe-area-inset-bottom));
  background-color:var(--room-bot);
  border-top:0}
.composer textarea{flex:1;resize:none;max-height:132px;min-height:44px;
  border-radius:var(--r-xl);border:1px solid var(--sep);padding:12px 16px;
  background:var(--fill2);outline:0;font-size:var(--fs-body);line-height:1.45;
  transition:border-color var(--dur-fast),box-shadow var(--dur-fast),background var(--dur-fast)}
.composer textarea:focus{border-color:var(--tint);box-shadow:var(--ring);background:var(--card-solid)}
/* 发送钮和用户气泡同一个橙：这一个色在全页只代表一件事——"我说的话"。 */
.send{background:var(--me);color:var(--me-ink);border-radius:var(--r-pill);
  padding:0 20px;height:44px;
  font-size:var(--fs-sub);font-weight:600;flex:0 0 auto;letter-spacing:var(--ls-tight);
  transition:transform var(--dur-press),opacity var(--dur-base),background var(--dur-fast)}
.send:active{transform:scale(.9)}
/* 禁用态不能是实心灰块（暗色下 --ink3 偏亮，看着反倒像可点） */
.send[disabled]{background:var(--fill);color:var(--ink3);box-shadow:none;pointer-events:none}

/* ---------- .app 壳 / 底部导航（页面转场见 L7 动效层） ---------- */
.app{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;min-width:0}
#rail{display:none}
.tabbar{flex:0 0 auto;display:flex;
  background-color:var(--bg);
  border-top:1px solid var(--sep);
  padding-bottom:calc(5px + env(safe-area-inset-bottom))}
.tabbar a{flex:1;min-width:0;text-align:center;padding:7px 2px 6px;font-size:var(--fs-cap);font-weight:500;
  color:var(--ink2);display:flex;flex-direction:column;align-items:center;gap:3px;
  position:relative;transition:color var(--dur-fast)}
/* 当前页＝签名色。上一版这里只是"墨色 + 加粗"，五个图标里看不出自己在哪——
   而这一处正好是"每次导航都荡一圈"最该发生的地方（D2 的第八个发射时刻）。 */
.tabbar a.on{color:var(--echo-ink);font-weight:600}
.tabbar a.on .ic{animation:tab-pop var(--d-enter) var(--ease-spring) both}
.tabbar a.on .ic::after{content:"";position:absolute;bottom:-4px;left:50%;
  width:5px;height:5px;margin-left:-2.5px;border-radius:50%;background:var(--echo)}
.tabbar a.on svg{color:var(--echo-ink)}
@keyframes tab-pop{from{transform:scale(.82)}to{transform:scale(1)}}
.tabbar a .ic{display:grid;place-items:center;height:24px;position:relative}
.tabbar a .ic .av{width:24px;height:24px;font-size:11px}
.tabbar a svg{width:21px;height:21px}

/* ============================================================
   L6 · 页面
   ============================================================ */
/* ---------- 背景选择 ---------- */
.bgs{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:10px;margin:4px 0 12px}
.bgs .sw-b{aspect-ratio:3/3.4;border-radius:var(--r-m);border:1px solid var(--sep);
  position:relative;overflow:hidden;cursor:pointer;transition:transform var(--dur-fast),box-shadow var(--dur-fast);
  display:grid;place-items:end center;padding:0 0 6px}
.bgs .sw-b span{font-size:var(--fs-micro);font-weight:600;color:rgba(40,32,24,.72);
  background:var(--fill);border-radius:var(--r-pill);padding:1px 8px}
.bgs .sw-b.on{box-shadow:0 0 0 2px var(--ink)}
.bgs .sw-b.on span{background:var(--tint);color:var(--tint-ink)}
.bgs .sw-b:active{transform:scale(.97)}
.sw-b.b-none{background:var(--card-solid);display:grid;place-items:center}
.sw-b.b-none span{position:static;background:none;color:var(--ink2)}
.sw-b.b-mist{background:linear-gradient(160deg,#f6f6f3,#e1e5df)}
.sw-b.b-sand{background:linear-gradient(165deg,#f8f3ea,#efe6d6)}
.sw-b.b-sage{background:linear-gradient(160deg,#eef3ee,#dfe8e0)}
.sw-b.b-dusk{background:linear-gradient(170deg,#eee4e3,#dde2e6)}
.sw-b.b-rose{background:linear-gradient(160deg,#fbece8,#f2e0da)}
.sw-b.b-ink{background:linear-gradient(165deg,#2a2622,#171513)}
/* 曜石这块是全站唯一一块深色样本，而上面那条 span 规则给的是深棕字——
   深棕压在近黑上只有 1.1:1，明暗两个主题下都读不出「曜石」两个字。
   同理「我的图片」那块底是用户上传的任意照片，颜色不可预测，只能自己垫一层。 */
.sw-b.b-ink span{color:#f4efe7;background:rgba(255,255,255,.16)}
.sw-b.b-custom{background:var(--fill2);display:grid;place-items:center}
.sw-b.b-custom img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.sw-b.b-custom .plus{font-size:22px;color:var(--ink2);font-weight:300}
/* :not(.plus) 是必要的：那块 ＋ 也是 span，上面那条深底会连它一起垫黑，
   结果深棕的 ＋ 压在半透黑上只剩 2.06:1。角标要垫底，加号不要。 */
.sw-b.b-custom span:not(.plus){position:absolute;bottom:6px;
  background:rgba(20,17,14,.66);color:#faf7f1}

/* ---------- 表情包面板 ---------- */
.stk-panel{flex:0 0 auto;max-height:44dvh;overflow:auto;background:var(--card-solid);
  border-top:1px solid var(--sep2);padding:8px 12px calc(8px + env(safe-area-inset-bottom));
  animation:stk-in .22s var(--ease-out)}
@keyframes stk-in{from{transform:translateY(14px);opacity:.4}to{transform:none;opacity:1}}
.stk-tabs{display:flex;align-items:center;gap:6px;margin-bottom:10px}
.stk-tabs .sp{flex:1}
.stk-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(74px,1fr));gap:var(--s-2)}
.stk-cell{position:relative;background:var(--fill2);border-radius:var(--r-m);overflow:hidden;
  aspect-ratio:1/1;display:grid;place-items:center;cursor:pointer;
  transition:transform var(--dur-press),box-shadow var(--dur-fast)}
.stk-cell:active{transform:scale(.94)}
.stk-cell img{width:100%;height:100%;object-fit:cover;display:block}
.stk-cell .cap{position:absolute;left:0;right:0;bottom:0;font-size:var(--fs-micro);line-height:1.3;
  color:var(--ink2);background:linear-gradient(transparent,var(--card-solid));
  padding:10px 5px 3px;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.stk-cell.add{border:.5px dashed var(--sep);color:var(--tint);background:transparent;
  font-size:26px;font-weight:300}
.stk-cell.pending{box-shadow:0 0 0 2px var(--tint),var(--sh1)}
.stk-cell .x,.stk-cell .pen{position:absolute;top:3px;width:19px;height:19px;border-radius:50%;
  background:var(--scrim);color:#fff;font-size:var(--fs-foot);line-height:19px;text-align:center;
  opacity:0;transition:opacity var(--dur-fast)}
.stk-cell .x{right:3px}
.stk-cell .pen{right:24px}
.stk-cell:hover .x,.stk-cell:hover .pen{opacity:1}
@media (hover:none){.stk-cell .x,.stk-cell .pen{opacity:1}}  /* 触屏无 hover，操作钮常驻 */
.stk-hint{font-size:var(--fs-cap);color:var(--ink2);line-height:1.6;margin-top:9px}
.pending-strip{display:flex;align-items:center;gap:var(--s-2);padding:8px 14px 0}
.pending-strip img{width:46px;height:46px;border-radius:var(--r-input);object-fit:cover;
  }
.pending-strip .t{flex:1;font-size:var(--fs-foot);color:var(--ink2);line-height:1.5}
.pending-strip .rm{font-size:var(--fs-foot);color:var(--tint);font-weight:600;flex:0 0 auto}
/* 贴纸展示统一规格：宽 132 / 高 ≤160 / contain（emoji 图片并入同一管线） */
.sticker-pic{width:132px;max-height:160px;border-radius:var(--r-m);object-fit:contain;
  background:var(--fill2);display:block}
.sticker-pic.tall{object-fit:cover;height:160px}

/* ---------- 登录 / 注册 ---------- */
/* 参考 shadcn login 系列实测：表单列宽 384px。
   第四轮改的是"这一页该像什么"：不像 SaaS 登录页，像回到一个暗下来的房间——
   灯在上方远处，一句话在左边，表单浮在中间。 */
.auth{min-height:100dvh;display:flex;flex-direction:column;justify-content:center;
  align-items:center;padding:var(--s-7) var(--s-6);gap:0;overflow:auto;
  background-color:var(--bg);background-image:var(--glow),var(--grain);
  background-attachment:fixed}
.auth .logo{width:40px;height:40px;border-radius:var(--r-m);flex:0 0 auto;display:block;
  margin-bottom:var(--s-4)}
/* 浮在空白页上的一张**纸**，不是一块玻璃：底色提到 96%，边界交给软影而不是描边。
   blur 声明保留但已经看不出玻璃味——这一层是安全的，底下没有滚动内容
   （全站"只允许瞬时浮层糊"的账见 L1 那段注释，自测按 4 个浮层钉着）。 */
.auth .box{width:100%;background:color-mix(in srgb,var(--card-solid) 96%,transparent);
  border:0;
  border-radius:var(--r-card);padding:var(--s-6);box-shadow:var(--sh-lift);
  -webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}
/* 卡片头部：标题左、登录/注册胶囊切换右 */
.auth .head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-3);
  margin-bottom:var(--s-5)}
.auth h1{font-family:var(--font-prose);font-size:var(--fs-head);font-weight:600;margin:0;
  text-align:left;letter-spacing:.01em;line-height:var(--lh-tight)}
.auth .box p{font-size:var(--fs-foot);color:var(--ink2);margin:6px 0 0;text-align:left;line-height:1.6}
/* 左边那句话：这一页唯一该说的话。标题用衬线，读起来像有人在对你说。 */
.auth .story{position:relative}
.auth .story h2{font-family:var(--font-prose);font-weight:500;letter-spacing:.005em}
/* 到达的那一圈涟漪：登录=「有人感觉到你回来了」。
   刻意**只播一次**（iteration 1）——常驻 infinite 动画是"导航完画面还停在上一页"
   那类合成层 bug 的头号嫌疑，登录页更不该留一个永动的东西。 */
.story-ring{position:absolute;left:-4px;top:6px;width:16px;height:16px;
  pointer-events:none;display:none}
.story-ring::before,.story-ring::after{content:"";position:absolute;inset:0;
  border-radius:50%;border:1px solid var(--echo);opacity:0;
  animation:story-ripple 2.4s var(--ease-echo) .35s 1 forwards}
.story-ring::after{animation-delay:.95s}
@keyframes story-ripple{
  0%{transform:scale(.4);opacity:.75}
  70%{opacity:0}
  100%{transform:scale(4.4);opacity:0}}
@media (min-width:1024px){.story-ring{display:block}}
/* 一栏装一句话、一栏装表单：窄屏上下摞，宽屏并排（.auth-in 是唯一那层容器） */
.auth-in{display:flex;flex-direction:column;gap:var(--s-5);width:min(384px,100%)}
.auth form{display:flex;flex-direction:column;width:100%}
.auth .brand-line{display:flex;align-items:center;gap:9px;color:var(--ink2);
  font-size:var(--fs-foot);font-weight:600;margin-bottom:var(--s-5);letter-spacing:var(--ls-label)}
.auth .brand-line img{width:22px;height:22px;display:block;border-radius:6px}
.auth .brand-line b{color:var(--ink)}
.auth .brand-line i{font-style:normal;color:var(--ink2);font-weight:400;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* 窄屏：品牌信息留在卡片里；宽屏：卡片不再重复，改由左边那句带 */
.auth .story .brand-line{display:none}
@media (min-width:1024px){
  .auth .story .brand-line{display:flex;margin-bottom:var(--s-3)}
  .auth .box .brand-line{display:none}
}
.auth .consent{width:100%;margin-top:var(--s-4);padding:0 4px;
  display:flex;flex-direction:column;gap:9px}
.auth .consent .agree{margin:0;font-size:var(--fs-cap);color:var(--ink2);line-height:1.6}
.auth .consent .agree input[type=checkbox]{width:15px;height:15px;margin-top:1px}
.auth .err{font-size:var(--fs-foot);color:var(--warn);background:var(--warn-bg);
  border-radius:var(--r-m);padding:10px 13px;margin-bottom:var(--s-4);line-height:1.55}
.auth .field{margin-bottom:var(--s-4)}
.auth .field label{display:block;font-size:var(--fs-foot);font-weight:600;color:var(--ink2);margin-bottom:var(--s-2)}
.auth .field .tip{font-size:var(--fs-cap);color:var(--ink2);margin-top:5px;line-height:1.55}
.auth input{width:100%;min-height:46px;padding:13px 14px;border:1px solid var(--sep);
  border-radius:var(--r-input);
  font-size:16px;background:var(--fill2);outline:0;
  transition:border-color var(--dur-fast),box-shadow var(--dur-fast),background var(--dur-fast)}
.auth input:focus{border-color:var(--tint);box-shadow:var(--ring);background:var(--card-solid)}
/* 主行动＝墨实心。上一版这里是品牌色渐变 + 投影，是整页最「模板」的一处。 */
.auth button[type=submit]{width:100%;min-height:48px;padding:14px;background:var(--btn-ink);
  color:var(--btn-ink-ink);
  border-radius:var(--r-btn);font-size:var(--fs-sub);font-weight:600;margin-top:var(--s-2);
  letter-spacing:var(--ls-tight);
  transition:transform var(--dur-press),background var(--dur-fast)}
.auth button[type=submit]:hover{background:color-mix(in srgb,var(--btn-ink) 88%,var(--bg))}
.auth button[type=submit]:active{transform:scale(.99)}
.auth .consent .fine{font-size:var(--fs-cap);color:var(--ink2);text-align:left;margin:0;line-height:1.7}
.auth .consent .fine a{color:var(--ink2);text-decoration:underline;text-underline-offset:2px}
/* 叙事面：只在宽屏出现，且只有一句话——不再挂「三个图标 + 三行说明」的功能清单 */
.auth .story{display:none}
.auth .story h2{margin:0;font-size:var(--fs-h1);font-weight:500;line-height:var(--lh-tight);
  letter-spacing:var(--ls-disp);color:var(--ink);max-width:14em}
.auth .story h2 em{font-style:normal;color:var(--ink)}
.auth .story p{margin:var(--s-4) 0 0;font-size:var(--fs-sub);line-height:var(--lh-prose);
  color:var(--ink2);max-width:30ch}
.auth .story ul{list-style:none;margin:var(--s-5) 0 0;padding:0;display:flex;
  flex-direction:column;gap:var(--s-3)}
.auth .story li{display:flex;gap:10px;align-items:flex-start;font-size:var(--fs-foot);
  color:var(--ink2);line-height:1.55}
.auth .story li b{color:var(--ink);font-weight:600;display:block}
.auth .story li svg{width:19px;height:19px;flex:0 0 auto;margin-top:1px;color:var(--ink3)}
/* 注册页的协议勾选：checkbox 不能沿用文本框的尺寸与内边距 */
.agree{display:flex;gap:var(--s-2);align-items:flex-start;font-size:var(--fs-foot);
  color:var(--ink2);line-height:1.55;margin:14px 2px 0;cursor:pointer}
.agree input[type=checkbox]{width:17px;height:17px;padding:0;margin:1px 0 0;
  flex:0 0 auto;accent-color:var(--tint);border-radius:4px}
.agree a{color:var(--tint);font-weight:600}

/* ---------- 公开文档页（用户协议 / 隐私政策 / 数据便携） ----------
   这页的 <body> 就是文档本体，而 body 默认是应用外壳：flex + 100dvh + overflow:hidden，
   滚动交给里面的列表。文档页没有外壳，整页就是一篇长文，必须把这些还回去，
   否则协议/政策读到一半就再也滚不动、后半篇永远看不见（和 landing 的 body.ld 同理）。
   min-height 保住背景光晕铺满；底色只写 background-color，用 background 简写会把
   body 上的环境光晕清掉，协议页就比站内其他页暗一档。
   横向留白从 body 挪进子块，让吸顶条能铺满整屏宽，不再缩成一条比正文窄的内嵌白块；
   322 = 680/2 - 18，保证宽屏下吸顶条里的返回钮和正文左缘对齐。 */
body.doc{display:block;flex-direction:initial;height:auto;min-height:100dvh;overflow:visible;
  max-width:none;margin:0;padding:0;
  background-color:var(--bg);color:var(--ink)}
.doc-bar{position:sticky;top:0;z-index:2;display:flex;align-items:center;gap:6px;
  padding:10px max(18px,calc(50% - 322px)) 8px;
  background-color:color-mix(in srgb,var(--card-solid) 94%,transparent);
  border-bottom:1px solid var(--sep2)}
.doc-body{max-width:680px;margin:0 auto;padding:0 18px calc(40px + env(safe-area-inset-bottom))}
.doc-bar h1{margin:0;font-size:var(--fs-title);font-weight:600;letter-spacing:var(--ls-title)}
.doc-body h2{font-size:var(--fs-title);font-weight:600;margin:30px 0 8px;letter-spacing:var(--ls-label);
  color:var(--tint);text-wrap:balance}
.doc-body p,.doc-body li{font-size:var(--fs-body);line-height:var(--lh-prose)}
.doc-body p{margin:0 0 10px}
.doc-body ul{padding-left:20px;margin:0 0 10px}
.doc-body li{margin:4px 0}
.doc-body code{font-family:var(--font-num);font-size:.88em;background:var(--fill2);
  padding:1px 5px;border-radius:4px}
.doc-upd{font-size:var(--fs-cap);color:var(--ink2);margin:14px 0 20px;letter-spacing:var(--ls-label)}
.doc-body .dim{color:var(--ink2);font-size:var(--fs-sub);margin-top:26px;padding-top:18px;
  border-top:1px solid var(--sep2)}

/* ---------- 法律文档专用排版（2026-10-05） ----------
   协议正文和以前的三页说明不是一个量级：一份五千字、带三级标题和清单表。
   外壳不动，只补这份文档真正需要的四条：

   1. 长链不打断排版 = 横向溢出。以前 .doc-body 是唯一没有 overflow-wrap 的正文容器
      （.pz-body 1373、.bubble 978 都有），协议里必须写监管文件和第三方服务商的名字，
      一旦夹一条长 URL，360px 上整页就能左右拖。这条是这次最硬的一条修复。
   2. <ol> 以前没有规则，走浏览器默认的 40px 缩进，比 <ul> 的 20px 深一档，
      条款编号看起来像缩进错误。
   3. <dl> 用来做「收集了什么 / 存在哪 / 留多久」三栏清单——比表格窄屏友好得多，
      360px 上表格只能横拖，dl 天然换行。
   4. 三级标题 h3 以前不存在，条款里"条下面还有小项"必须有落点，否则全部堆在 h2 下。

   注意这里不许出现 backdrop-filter：全站只剩 4 个瞬时浮层能糊，是实测过滚动性能
   之后钉死的数（见 selftest 的那条守卫），协议页是长文档，更要跟着滚。 */
.doc-body{overflow-wrap:anywhere}
.doc-body h3{font-size:var(--fs-body);font-weight:600;color:var(--ink);
  margin:20px 0 6px;letter-spacing:var(--ls-label);line-height:var(--lh-snug)}
.doc-body ol{padding-left:20px;margin:0 0 10px}
.doc-body ol>li,.doc-body ul>li{margin:4px 0}
.doc-body li+li{margin-top:4px}
.doc-body dl{margin:0 0 14px}
.doc-body dt{font-weight:600;color:var(--ink);margin-top:12px;font-size:var(--fs-body);
  line-height:var(--lh-snug)}
.doc-body dd{margin:2px 0 0;padding-left:0;color:var(--ink2);font-size:var(--fs-body);
  line-height:var(--lh-prose)}
.doc-body a{text-decoration:underline;text-underline-offset:2px}
.doc-note{background:var(--fill2);border:1px solid var(--sep);border-radius:var(--r-m);
  padding:12px 14px;margin:0 0 16px;font-size:var(--fs-body);line-height:var(--lh-body)}
.doc-note p{margin:0}
.doc-note p+p{margin-top:8px}
.doc-note.warn{border-color:var(--warn);background:var(--warn-bg);color:var(--ink)}
.doc-note.warn dt,.doc-note.warn strong{color:var(--warn)}
/* 五份条款互链：一行小胶囊，窄屏自然换行，不做轮播也不做抽屉 */
.doc-rel{display:flex;flex-wrap:wrap;gap:6px 8px;align-items:baseline;
  margin:34px 0 0;padding:16px 0 0;border-top:1px solid var(--sep2)}
.doc-rel-t{width:100%;font-size:var(--fs-cap);color:var(--ink2);
  letter-spacing:var(--ls-caps);margin-bottom:2px}
.doc-rel a{font-size:var(--fs-foot);color:var(--ink2);border:1px solid var(--sep);
  border-radius:var(--r-pill);padding:4px 11px;text-decoration:none;line-height:1.4}
.doc-rel a[aria-current="page"]{color:var(--tint);border-color:var(--tint);font-weight:600}
.doc-rel a:hover,.doc-rel a:focus-visible{border-color:var(--tint);color:var(--tint)}

/* ---------- 公开分享页 ---------- */
.share-body{align-items:center;padding:var(--s-7) 20px 40px}
.share-card{width:min(420px,100%);background:var(--card-solid);border:1px solid var(--sep);
  border-radius:var(--r-panel);padding:22px;box-shadow:var(--sh2)}
.share-head{display:flex;align-items:center;gap:var(--s-3);margin-bottom:16px}
.share-head h1{font-size:var(--fs-headline);font-weight:600;margin:0;letter-spacing:var(--ls-title)}
.share-head .tag{font-size:var(--fs-foot);color:var(--ink2);margin:3px 0 0}
.share-bubbles{display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.share-bubbles .bubble{align-self:flex-start;max-width:86%;background:var(--bot);
  padding:9px 14px;border-radius:var(--r-bubble);border-top-left-radius:var(--r-xs);font-size:var(--fs-body);
  line-height:1.5}
.share-desc{font-size:var(--fs-sub);color:var(--ink2);line-height:1.65;background:var(--fill2);
  border-radius:var(--r-m);padding:11px 13px;margin-bottom:12px;
  display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}
/* 分享页是全站唯一一个「游客也会看到 AI 生成的拟人内容」的地方：不登录、不注册、
   没有任何上下文告诉他这是模型写的，卡片下面直接就是「和 ta 聊天」。
   所以这一行标识必须钉在这里，而不是只写在用户协议里。
   顺带补 overflow-wrap：人设正文是用户写的，一条长链接就能把 360px 的卡片顶出横滚。 */
.share-ai{display:flex;align-items:center;justify-content:center;gap:5px;
  font-size:var(--fs-cap);color:var(--echo-ink);background:var(--fill2);
  border:1px solid var(--sep);border-radius:var(--r-pill);padding:5px 12px;
  margin:0 auto 12px;letter-spacing:var(--ls-label)}
.share-ai svg{flex:0 0 auto;width:13px;height:13px}
.share-desc,.share-bubbles .bubble,.share-head .tag{overflow-wrap:anywhere}
.share-meta{font-size:var(--fs-cap);color:var(--ink2);text-align:center;margin-bottom:12px;
  font-variant-numeric:tabular-nums}
.btn.block{display:block;width:100%;text-align:center;text-decoration:none}
.share-alt{display:block;text-align:center;font-size:var(--fs-sub);color:var(--ink2);
  font-weight:600;margin-top:12px;text-decoration:none}
.share-tip{font-size:var(--fs-cap);color:var(--ink2);text-align:center;margin:12px 0 0}
.share-brand{font-size:var(--fs-cap);color:var(--ink2);text-align:center;margin-top:22px;
  letter-spacing:var(--ls-label)}

/* ---------- AI 帮写人设 ---------- */
.wchips{display:flex;flex-wrap:wrap;gap:7px;margin-top:6px}
.wsay{background:var(--fill2);border:1px solid var(--sep2);border-radius:var(--r-input);
  padding:10px 12px;font-size:var(--fs-cap);color:var(--ink2);line-height:1.7;white-space:pre-wrap}
.wcan{display:block;width:100%;text-align:left;background:var(--card-solid);
  border:1px solid var(--sep);border-radius:var(--r-card);padding:12px 14px;margin-bottom:10px;
  cursor:pointer;transition:transform var(--dur-press),border-color var(--dur-fast)}
.wcan:hover{border-color:var(--tint)}
.wcan:active{transform:scale(.99)}
.wcan .t{font-size:var(--fs-sub);font-weight:600;color:var(--ink);display:flex;
  align-items:baseline;gap:8px;flex-wrap:wrap}
.wcan .d{font-size:var(--fs-cap);color:var(--ink2);line-height:1.6;margin-top:5px}
.wcan .q{font-size:var(--fs-cap);color:var(--tint);margin-top:6px}
.pvitem{display:flex;gap:var(--s-2);padding:9px 0;border-bottom:1px solid var(--sep2)}
.pvitem:last-child{border-bottom:0}
.pvk{flex:0 0 62px;font-size:var(--fs-cap);color:var(--ink2);font-weight:600}
.pvv{flex:1;font-size:var(--fs-sub);color:var(--ink);line-height:1.65;
  white-space:pre-wrap;word-break:break-word}
.field textarea.paste{min-height:168px}

/* ---------- 角色广场：卡片流浏览 ---------- */
.pztabs{display:flex;gap:4px;background:var(--fill2);border:1px solid var(--sep2);
  border-radius:var(--r-pill);padding:4px;margin-bottom:16px}
.pztab{flex:1;border:0;background:transparent;padding:9px 0;border-radius:var(--r-pill);
  font-size:var(--fs-sub);font-weight:600;color:var(--ink2);cursor:pointer;
  letter-spacing:var(--ls-tight);transition:background var(--dur-fast),color var(--dur-fast)}
.pztab.on{background:var(--card-solid);color:var(--ink);font-weight:600;box-shadow:var(--sh1)}
/* 广场内嵌在角色页里时，筛选条不再吸顶（页面本身已有标签栏） */
.pzview .pzbar{position:static;padding-top:0;box-shadow:none}
.pzpage{display:flex;flex-direction:column;padding-bottom:20px}
.pzbar{position:sticky;top:0;z-index:2;display:flex;flex-direction:column;gap:10px;
  padding:2px 0 12px;margin-bottom:14px;background:inherit;
  box-shadow:0 12px 12px -12px var(--sep2)}
.pzseg{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;padding-bottom:2px}
.pzseg::-webkit-scrollbar{display:none}
.pzi{width:100%;box-sizing:border-box;background:var(--fill2);border:1px solid var(--sep);
  border-radius:var(--r-pill);padding:9px 15px;font-size:var(--fs-sub);color:var(--ink);
  outline:none;transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}
.pzi:focus{border-color:var(--tint);box-shadow:var(--ring);background:var(--card-solid)}
.pzchips{display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;padding-bottom:2px}
.pzchips::-webkit-scrollbar{display:none}
.pzchips .chip{white-space:nowrap;flex:0 0 auto}
.pztags .chip{background:transparent;border-color:var(--sep)}
.pzgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:13px}
.pzc{background:var(--card-solid);border:1px solid var(--sep);border-radius:var(--r-card);
  overflow:hidden;cursor:pointer;display:flex;flex-direction:column;text-align:left;
  transition:transform var(--dur-press),box-shadow var(--dur-base)}
.pzc:active{transform:scale(.99)}
/* 广场卡封面＝Aura 本身（.aura 由 JS 挂上）：一块平色的封面 = 每个角色同一张脸。
   注意这里必须用 background-image 而不是 background 简写，
   否则会把 .aura 那三层渐变整条抹掉。 */
.pzc-cov{position:relative;aspect-ratio:3/4;background:var(--g,var(--tile-2));
  display:flex;align-items:center;justify-content:center;
  color:#17150f;font-family:var(--font-prose);font-size:var(--fs-greet)}
.pzc-cov.aura{background-image:
    var(--au-tex,repeating-radial-gradient(circle at 50% 50%,
      rgba(255,255,255,.17) 0 1px,rgba(255,255,255,0) 1px var(--au-r,7px))),
    radial-gradient(circle at var(--au-x,32%) var(--au-y,26%),
      rgba(255,255,255,.40) 0%,rgba(255,255,255,0) var(--au-core,34%)),
    radial-gradient(circle at var(--au-x,32%) var(--au-y,26%),
      var(--au-a,#FBEAD4) 0%,var(--au-b,#C85B3F) 140%)}
/* 和「我的角色」卡同一处理：封面与文字硬切 + 发丝线，不做渐隐压字 */
.pzc-cov::after{content:"";position:absolute;left:0;right:0;bottom:0;height:.5px;z-index:1;
  background:var(--sep);pointer-events:none}
.pzc-cov img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* 有封面图就不显示字母兜底 */
.pzc-cov:has(img) .pzc-ini{display:none}
.pzc-ini{font-size:34px;font-weight:600;color:#fff;text-shadow:0 1px 8px rgba(0,0,0,.24);
  letter-spacing:var(--ls-tight)}
/* 广场卡上的角标：一屏十几张卡，每张两层 blur 太贵。压在封面上改用实色半透明黑，
   对比度比「灰玻璃」还稳（深色封面上 hud-bg 本来就发灰看不清）。 */
.pzc-cat{position:absolute;left:8px;top:8px;z-index:2;font-size:var(--fs-micro);font-weight:600;
  color:#fff;background:rgba(30,24,18,.5);
  border-radius:var(--r-pill);padding:2px 8px}
.pzc-star{position:absolute;right:7px;bottom:7px;z-index:2;display:flex;align-items:center;gap:3px;
  font-size:var(--fs-foot);font-weight:600;color:#fff;background:rgba(30,24,18,.5);
  border:0;
  border-radius:var(--r-pill);padding:4px 10px;cursor:pointer;line-height:1;
  transition:transform var(--dur-press),background var(--dur-fast)}
.pzc-star:active{transform:scale(.92)}
.pzc-star b{font-variant-numeric:tabular-nums;font-weight:600}
.pzc-star.on{background:var(--warn)}
.pzc-nm{font-size:var(--fs-body);font-weight:600;letter-spacing:var(--ls-tight);
  padding:10px 12px 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pzc-tg{font-size:var(--fs-cap);color:var(--ink2);line-height:1.45;padding:3px 12px 0;
  min-height:30px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pzc-mt{font-size:var(--fs-micro);color:var(--ink2);font-variant-numeric:tabular-nums;
  padding:6px 12px 11px;margin-top:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pz-lines{display:flex;flex-direction:column;gap:var(--s-2);margin:12px 0}
.pz-lines .b{background:var(--fill2);border-radius:var(--r-m);padding:10px 13px;
  font-size:var(--fs-sub);line-height:1.55}
.pz-meta{font-size:var(--fs-cap);color:var(--ink2);line-height:1.6}
/* 广场详情的人设正文：默认只露几行，剩下靠「展开全文」点开。
   这里刻意用 max-height + overflow:hidden，不用 overflow:clip——国产浏览器壳不认 clip。 */
.pz-body{font-size:var(--fs-sub);line-height:var(--lh-prose);white-space:pre-wrap;overflow-wrap:anywhere}
.pz-body.cut{max-height:8.5em;overflow:hidden}
.pz-more{margin-top:10px;padding:7px 14px;border:0;border-radius:var(--r-pill);
  background:var(--fill);color:var(--ink2);font-size:var(--fs-cap);font-weight:600}

/* ---------- 记忆时间线：「TA 记住了什么」而不是数据库视图 ---------- */
.tl{position:relative;padding-left:20px;margin-top:4px}
.tl::before{content:"";position:absolute;left:5px;top:6px;bottom:6px;width:1.5px;
  background:var(--sep)}
.tl-day{position:relative;margin:16px 0 0}
.tl-day::before{content:"";position:absolute;left:-19px;top:6px;width:9px;height:9px;
  border-radius:50%;background:var(--ink3);box-shadow:0 0 0 3px var(--fill)}
.tl-day .d{font-size:var(--fs-foot);font-weight:600;color:var(--ink);letter-spacing:var(--ls-label)}
.tl-day .s{font-size:var(--fs-micro);color:var(--ink2);margin-left:6px;font-weight:400}
.tl-day .k{font-size:var(--fs-cap);color:var(--ink2);margin-top:3px;line-height:1.5}
.tl-day .hl{font-size:var(--fs-sub);color:var(--ink2);margin-top:6px;line-height:1.6;
  background:var(--fill2);border-radius:var(--r-input);padding:9px 12px}
.tl-mem{font-size:var(--fs-cap);color:var(--gold);margin-top:6px;line-height:1.5}

/* ---------- 记忆条目 / 收藏夹 ---------- */
.mem{border-bottom:1px solid var(--sep2);padding:12px 0}
.mem:last-child{border-bottom:0}
.mem .top{display:flex;justify-content:space-between;gap:var(--s-2);font-size:var(--fs-cap);color:var(--ink2)}
.mem .txt{font-size:var(--fs-body);margin:5px 0 7px;line-height:1.55}
.mem .ops{display:flex;gap:var(--s-3);font-size:var(--fs-sub)}
.fav-item{border-bottom:1px solid var(--sep2);padding:12px 0}
.fav-item:last-child{border-bottom:0}
.fav-item .txt{font-size:var(--fs-sub);line-height:1.6}
.fav-item .top{display:flex;justify-content:space-between;gap:10px;align-items:center;
  font-size:var(--fs-cap);color:var(--ink2);margin-bottom:5px}
.fav-item .from{font-weight:600;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;min-width:0}
.fav-item .ops{flex:0 0 auto;display:flex;gap:var(--s-3);font-size:var(--fs-cap)}
.fav-item .ops button{color:var(--tint);font-weight:600}
.fav-item .ops button.del{color:var(--warn)}
.fav-item .note{font-size:var(--fs-cap);color:var(--ink2);margin-top:5px}

/* ---------- 「我的」：资料头卡 + 分组入口 ---------- */
.me-head{display:flex;gap:14px;align-items:center;background:var(--card-solid);
  border:1px solid var(--sep);border-radius:var(--r-panel);padding:20px 16px}
.me-head .who{flex:1;min-width:0}
.me-head .nm{font-size:var(--fs-headline);font-weight:600;letter-spacing:var(--ls-title);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:var(--lh-tight)}
.me-head .id{font-size:var(--fs-foot);color:var(--ink2);margin-top:4px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.me-head .tags{display:flex;gap:6px;flex-wrap:wrap;margin-top:12px}
.tagchip{font-size:var(--fs-cap);font-weight:600;padding:4px 10px;border-radius:var(--r-pill);
  background:var(--fill2);color:var(--ink2);font-variant-numeric:tabular-nums}
.tagchip.tint{background:var(--tint-bg);color:var(--tint)}
.tagchip.gold{background:var(--gold-bg);color:var(--gold)}
.tagchip.warn{background:var(--warn-bg);color:var(--warn)}
.grp{margin-top:26px}
.grp>h3{font-size:var(--fs-cap);font-weight:600;color:var(--ink2);margin:0 0 8px 4px;
  letter-spacing:var(--ls-caps)}
.rows{background:var(--card-solid);border:1px solid var(--sep);border-radius:var(--r-card);
  overflow:hidden}
.row-i{display:flex;align-items:center;gap:var(--s-3);width:100%;padding:13px 14px;text-align:left;
  font:inherit;color:var(--ink);cursor:pointer;border:0;position:relative;
  background:transparent;transition:background var(--dur-fast)}
.row-i::after{content:"";position:absolute;left:56px;right:0;bottom:0;height:.5px;background:var(--sep2)}
.rows .row-i:last-child::after{display:none}
.row-i:active{background:var(--fill2)}
.row-i .ic{flex:0 0 auto;width:30px;height:30px;border-radius:var(--r-btn-sm);display:grid;place-items:center;
  background:var(--fill);color:var(--ink2)}
.row-i .ic svg{width:17px;height:17px;display:block}
.row-i .ic.gold{background:var(--gold-bg);color:var(--gold)}
.row-i .ic.warn{background:var(--warn-bg);color:var(--warn)}
.row-i .tx{flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start}
.row-i .t,.row-i .d{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.row-i .t{font-size:var(--fs-sub);font-weight:600;letter-spacing:var(--ls-tight)}
.row-i .d{font-size:var(--fs-cap);color:var(--ink2);margin-top:2px;line-height:1.45}
.row-i .v{flex:0 0 auto;max-width:36%;font-size:var(--fs-foot);color:var(--ink2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right;
  font-variant-numeric:tabular-nums}
.row-i .go{flex:0 0 auto;width:15px;height:15px;color:var(--ink3)}

/* 积分明细（用户端「积分与会员」与后台用户卡共用） */
.clog{margin-top:2px}
.clog-i{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:7px 0;border-top:1px solid var(--sep2);font-size:var(--fs-foot)}
.clog-why{font-weight:600;color:var(--ink);display:flex;flex-wrap:wrap;
  align-items:baseline;gap:6px;min-width:0}
.clog-note,.clog-ts{font-weight:400;color:var(--ink2);font-size:var(--fs-cap)}
.clog-amt{flex:0 0 auto;display:flex;align-items:baseline;gap:6px;font-variant-numeric:tabular-nums}
.clog-amt b.up{color:var(--ok)}
.clog-amt b.down{color:var(--ink2)}
.clog-bal{color:var(--ink2);font-size:var(--fs-cap)}

/* ============================================================
   L7 · 动效原语 / 桌面 rail / 首页空间 / 角色详情
   ============================================================ */
/* ---------- 入场原语：JS 渲染列表时加 .stagger，逐项用 --i 排延迟 ----------
   第三轮这里躺着四个没人用的原语（.rise/.pop/.fade/.enter）和三个没人用的
   keyframes，加上一个 1.3s infinite 的骨架屏扫光——"有动效"的错觉就是这么来的。
   这一轮只留真的被发射的，并且把时长收进令牌：交互 ≤200ms、入场 ≤300ms、
   页面级 ≤450ms、故事性（Wave/Thread）一次 0.9–1.2s 且只播一次。 */
@keyframes rise{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes pop-in{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:none}}
/* .pop 有 4 处真的在用（各页空状态/弹层），别当死代码删了 */
.pop{animation:pop-in var(--d-enter) var(--ease-pop) both}
.stagger>*{animation:rise var(--d-enter) var(--ease-pop) both;
  animation-delay:calc(min(var(--i,0)*45ms,360ms) + 40ms)}
/* 首页那张"TA 还在想"的卡：原来是 6s 无限呼吸。
   无限动画是「导航完画面还停在上一页」那类合成层 bug 的头号嫌疑，
   而且陪伴产品不该有永动的东西在转。改成播一次落定。 */
.breathe{animation:rise var(--d-page) var(--ease-pop) both}

/* ---------- 页面转场：跨文档 View Transition ----------
   从被点的那个 tab 的位置荡一圈 Wave，同时页面交叉淡化——
   转场本身就在讲产品的核心动作（回响），不是通用的淡入淡出。
   退场比入场快（--io 用于 exit），符合"进入慢、离开快"的手感。 */
@view-transition{navigation:auto}
::view-transition-old(root){animation:vt-out .26s var(--ease-io) both}
::view-transition-new(root){animation:vt-in var(--d-page) var(--ease-echo) both}
@keyframes vt-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@keyframes vt-out{to{opacity:0}}
/* 不支持跨文档 VT 的浏览器（Safari<18、多数国产壳）：退化成一次性入场，
   不阻塞、不闪白。 */
@supports not (view-transition-name:none){
  .app{animation:vt-in var(--d-page) var(--ease-echo) both}}

/* ---------- 桌面侧栏 rail（≥1024px，shell.js 填充） ---------- */
@media (min-width:1024px){
  body.has-rail{flex-direction:row}
  body.has-rail #rail{display:flex;flex-direction:column;width:264px;flex:0 0 264px;
    height:100dvh;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;gap:2px;
    padding:var(--s-5) 14px calc(var(--s-5) + env(safe-area-inset-bottom));position:relative;z-index:6;
    background-color:var(--bg);
    border-right:1px solid var(--sep)}
  body.has-rail .tabbar{display:none}
  /* 首页与设置两栏的正文比 680 宽，顶栏跟着走同一条内缩公式 */
  #home-top{padding-inline:max(18px, calc(50% - 454px))}
  body.set-wide .topbar.wrap{padding-inline:max(18px, calc(50% - 474px))}
  /* 居中浮层改为在 .app 区域居中（rail 占掉 264px，视觉中线右移 132px） */
  .sheet,.dialog,.toast{left:calc(50% + 132px)}
  .sheet.full{left:calc(50% + 132px)}
  /* 登录/注册在桌面并出左边那一栏叙事，不再是一个孤零零的框 */
  .auth{padding:var(--s-10) var(--s-8)}
  .auth-in{flex-direction:row;align-items:center;gap:56px;width:min(980px,100%)}
  .auth .story{display:flex;flex-direction:column;justify-content:center;gap:var(--s-4);
    flex:1 1 400px;max-width:420px;color:var(--ink2);text-align:left}
  .auth form{flex:0 0 auto;width:400px}
}
.rl-brand{display:flex;align-items:center;gap:10px;padding:2px 8px 16px}
.rl-brand .lg{width:34px;height:34px;border-radius:var(--r-s);display:block;flex:0 0 auto}
.rl-brand .tx{display:flex;flex-direction:column;min-width:0}
.rl-brand b{font-size:var(--fs-sub);font-weight:600;letter-spacing:var(--ls-title)}
.rl-brand i{font-style:normal;font-size:var(--fs-micro);color:var(--ink2);margin-top:2px}
.rl-nav{display:flex;flex-direction:column;gap:2px}
.rl-nav a{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:var(--r-btn-sm);
  color:var(--ink2);font-size:var(--fs-sub);font-weight:600;
  transition:background var(--dur-fast),color var(--dur-fast)}
.rl-nav a .ic{display:grid;place-items:center}
.rl-nav a .ic svg{width:20px;height:20px}
.rl-nav a .ic .av{width:22px;height:22px;font-size:10px;border-radius:var(--r-av-sm)}
.rl-nav a:hover{background:var(--fill2)}
/* 当前页 = 一层中性底 + 墨色字。上一版是强调色底 + 强调色字，整条侧栏三处都在抢色。 */
.rl-nav a.on{background:var(--fill);color:var(--ink)}
.rl-sep{height:1px;background:var(--sep);margin:12px 6px}
.rl-sec{font-size:var(--fs-micro);font-weight:600;color:var(--ink2);letter-spacing:var(--ls-caps);
  padding:0 10px 6px}
.rl-convs{display:flex;flex-direction:column;gap:1px}
.rl-conv{display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:var(--r-btn-sm);
  transition:background var(--dur-fast)}
.rl-conv:hover{background:var(--fill2)}
.rl-conv.on{background:var(--fill)}
.rl-conv .av{width:32px;height:32px;font-size:13px;border-radius:50%;flex:0 0 auto}
.rl-conv .mid,.rl-me .mid{flex:1;min-width:0;display:flex;flex-direction:column}
.rl-conv .mid b,.rl-me .mid b{font-size:var(--fs-foot);font-weight:600;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rl-conv .mid i,.rl-me .mid i{font-style:normal;font-size:var(--fs-micro);color:var(--ink2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:1px;line-height:1.4}
/* 侧栏会话行补上「上次说到什么时候」和「TA 先找你了」——
   这两个值接口一直在下发（preview_at / proactive_unread），
   以前只在首页画，桌面端反而看不见哪个人先找过你。
   时间用 --ink2 不用 --ink3：它是「哪条最要紧」的真信息，
   而且这条站已经在这上面栽过两次——--ink3 压在纸上只有 2.84:1。 */
.rl-conv time.tm{flex:0 0 auto;align-self:flex-start;margin-top:1px;
  font-family:var(--font-num);font-variant-numeric:tabular-nums;
  font-size:var(--fs-micro);color:var(--ink2)}
.rl-conv .ud{flex:0 0 auto;width:8px;height:8px;border-radius:50%;
  background:var(--tint);box-shadow:0 0 0 2px var(--bg);align-self:center}
.rl-conv.on .mid b{color:var(--ink)}
.rl-none{font-size:var(--fs-foot);color:var(--ink2);padding:6px 10px}
.rl-acts{display:flex;flex-direction:column;gap:8px;margin-top:14px;padding:0 4px}
.rl-acts .btn{padding:9px 12px;font-size:var(--fs-sub)}
.rl-me{margin-top:auto;display:flex;align-items:center;gap:9px;padding:9px;
  border-radius:var(--r-m);border:1px solid var(--sep);background:var(--card-solid);
  transition:background var(--dur-fast)}
.rl-me:hover{background:var(--fill2)}
.rl-me .av{width:30px;height:30px;font-size:12px;border-radius:var(--r-av);flex:0 0 auto}

/* ---------- 首页「空间」（home.js） ---------- */
#home-top .htxt{flex:1;min-width:0}
#home-top .hello{font-size:var(--fs-headline);font-weight:600;letter-spacing:var(--ls-title);
  line-height:var(--lh-tight);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#home-top .sub{font-size:var(--fs-foot);color:var(--ink2);font-weight:400;line-height:1.35;margin-top:3px}
.me-btn{width:40px;height:40px}
.me-btn .av{width:32px;height:32px;font-size:13px;border-radius:var(--r-av)}
.conv.space{padding:var(--s-1) var(--gut) calc(var(--s-6) + env(safe-area-inset-bottom));
  background-color:transparent}
/* ---------- 页面气质（§14）----------
   第四轮给记忆/关系配了语义色，但只落在"一圈 1px 边 + 一个 11px 标签"上，
   肉眼读不出来，于是五屏还是一个颜色。这一轮把气质落到**整屏的底**：
   每个视图一层 4%–6% 的 wash，换视图就是换一间房。
   wash 全部走不透明实色（--*-wash），不用 rgba——
   rgba 压在 body 那层暮光上会随滚动位置变色，量出来永远不一致。 */
#conv[data-view="remember"]{background:linear-gradient(180deg,var(--mnem-wash) 0%,rgba(255,252,247,0) 42%)}
#conv[data-view="between"]{background:linear-gradient(180deg,var(--bond-wash) 0%,rgba(255,252,247,0) 42%)}
#conv[data-view="chat"]{background:linear-gradient(180deg,var(--echo-wash) 0%,rgba(255,252,247,0) 38%)}
#conv[data-view="remember"] .rm-find input{background:var(--card-solid)}
#conv[data-view="between"] .bt-go{background:var(--bond-wash)}
.sp-sec{margin-top:var(--s-6)}
.sp-sec:first-child{margin-top:var(--s-2)}
.sp-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin:0 2px 11px}
.sp-head h2{font-size:var(--fs-sub);font-weight:600;margin:0;letter-spacing:var(--ls-tight);
  line-height:var(--lh-tight);color:var(--ink)}
.sp-head .hint{font-size:var(--fs-micro);color:var(--ink2)}
/* 最近在聊：横向吸滑的**头像轨**（iMessage / Instagram 那一族）。
   第四轮这里是 142px 宽的 3:4 卡片，两张就铺满窄屏——那是货架，不是通讯录。 */
.sp-rail{display:flex;gap:14px;overflow-x:auto;padding:2px 2px 10px;scroll-snap-type:x proximity;
  scrollbar-width:none}
.sp-rail::-webkit-scrollbar{display:none}
.sp-railc{flex:0 0 auto;width:76px;scroll-snap-align:start;display:flex;flex-direction:column;
  align-items:center;gap:7px;background:none;border:0;border-radius:0;overflow:visible;
  text-align:center;color:inherit;cursor:pointer;
  transition:transform var(--dur-press)}
.sp-railc:active{transform:scale(.94)}
.sp-railc.more{display:grid;place-items:center;width:76px;height:76px;color:var(--ink2);
  font-size:var(--fs-micro);text-align:center;line-height:1.35}
.sp-railc.more .mo{width:76px;height:76px;display:grid;place-items:center;border-radius:50%;
  background:var(--fill);box-shadow:inset 0 0 0 1px var(--sep2)}
/* 头像即人：圆形 Aura，76px 这一档终于能把六个轴看清
   （46px 的列表头像只能看见色相，环距和纹理在那么小的尺寸上没有意义）。
   有真实头像时同样是这一颗圆，不再是一张方封面。 */
.sp-cov{position:relative;width:76px;height:76px;flex:0 0 auto}
.sp-cov>.av{position:absolute;inset:0;width:76px;height:76px;border-radius:50%;
  font-size:30px;font-weight:600;background-size:cover;background-position:center}
.sp-cov>.av img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:50%}
/* 「使用中」不再是一块压在图上的彩色胶囊（那是电商角标），
   改成头像下面一颗小点 + 名字变色：安静，但一眼能找到现在这位。 */
.sp-on{position:absolute;right:2px;bottom:2px;z-index:2;width:9px;height:9px;
  border-radius:50%;background:var(--echo);box-shadow:0 0 0 2.5px var(--bg)}
.sp-railc.on .sp-info b{color:var(--echo-ink)}
/* TA 先找你了：一颗红点，不写技术词。底色跟着页面走，不再写死卡片白。 */
.sp-unread{position:absolute;top:1px;right:1px;z-index:2;width:12px;height:12px;border-radius:50%;
  background:var(--warn);box-shadow:0 0 0 2.5px var(--bg)}

/* ---------- ①「TA 先找你了」置顶块 ----------
   这是产品的核心时刻，不该缩成卡片右上角一个 10px 的红点。
   签名环在这里呼吸（全站第二处、也是最后一处 infinite）。 */
.sp-ping{margin-top:var(--s-2);padding:14px;border-radius:var(--r-card);
  background:var(--echo-bg);border:1px solid transparent}
.sp-ping-h{display:flex;align-items:center;gap:9px;margin-bottom:11px}
.sp-ping-h .echo{width:17px;height:17px}
.sp-ping-h b{font-size:var(--fs-sub);font-weight:700;color:var(--echo-ink);letter-spacing:var(--ls-tight)}
.sp-ping-h i{font-style:normal;font-size:var(--fs-cap);color:var(--ink2);margin-left:auto}
.sp-ping-c{display:flex;align-items:center;gap:10px;padding:9px 10px;margin:0 -2px;
  border-radius:var(--r-m);background:var(--card-solid);color:inherit;
  transition:transform var(--dur-press)}
.sp-ping-c+.sp-ping-c{margin-top:6px}
.sp-ping-c:active{transform:scale(.99)}
.sp-ping-c .tx{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.sp-ping-c .tx b{font-size:var(--fs-foot);font-weight:600}
.sp-ping-c .tx i{font-style:normal;font-size:var(--fs-cap);color:var(--ink2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sp-ping-c time{flex:0 0 auto;font-size:var(--fs-micro);color:var(--ink3);
  font-family:var(--font-time)}

/* ---------- ③④ 你们的故事 / TA 记得的你 ---------- */
.sp-story:empty{display:none}
.sp-line .sp-lne{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;
  font-size:var(--fs-foot);color:var(--ink2);font-family:var(--font-time)}
.sp-line .sp-lne b{font-family:var(--font-prose);font-size:var(--fs-sub);color:var(--ink);font-weight:600}
.sp-mems{display:flex;flex-direction:column;gap:2px}
.sp-memo{display:flex;align-items:stretch;gap:10px;padding:7px 0}
.sp-memo .tline{flex:0 0 auto;width:2px;align-self:stretch;background:var(--mnem);opacity:.5}
.sp-memo p{margin:0;font-family:var(--font-prose);font-size:var(--fs-sub);line-height:var(--lh-snug);
  color:var(--ink)}
.sp-more{display:inline-block;margin-top:10px;font-size:var(--fs-cap);color:var(--mnem-ink)}
/* 时间流还空着：一圈安静的弧 + 一句"这里会长出什么" */
.sp-seed{display:flex;align-items:flex-start;gap:12px;padding:16px;
  border:0;border-radius:var(--r-card);background:var(--echo-wash)}
.sp-seed .echo{width:22px;height:22px;flex:0 0 auto;margin-top:2px}
/* 两圈静止同心弧——只画一圈会读成"没选中的单选框"，两圈才是"回响" */
.sp-seed .echo::before{border-color:var(--echo-ink);opacity:.42;inset:-3px}
.sp-seed .echo::after{border-color:var(--echo-ink);opacity:.18;inset:-8px}
.sp-seed p{margin:0;font-family:var(--font-prose);font-size:var(--fs-foot);
  line-height:var(--lh-prose);color:var(--ink2);max-width:44ch}
.sp-info{max-width:76px;padding:0;display:flex;flex-direction:column;gap:2px;min-width:0;align-items:center}
/* 名字用衬线：那是个人的名字，不是一个列表项的标题。时间用等宽。 */
.sp-info b{font-family:var(--font-prose);font-size:var(--fs-sub);font-weight:600;
  letter-spacing:var(--ls-tight);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sp-info i{font-style:normal;font-size:var(--fs-cap);color:var(--ink2);line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.sp-info time{font-size:var(--fs-micro);color:var(--ink2);margin-top:2px;
  font-family:var(--font-time);font-variant-numeric:tabular-nums}
/* 全部角色：小卡网格 */
.sp-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-3)}
.sp-cell{background:none;border:0;border-radius:0;overflow:visible;
  color:inherit;display:flex;flex-direction:column;align-items:center;gap:7px;text-align:center;
  transition:transform var(--dur-press)}
.sp-cell:active{transform:scale(.94)}
.sp-cell .sp-cov>.av{font-size:28px}
.sp-cell .sp-info{padding:6px 8px 9px}
.sp-cell .sp-info i{-webkit-line-clamp:1}
/* 广场入口横幅 */
.sp-plaza{display:flex;align-items:center;gap:var(--s-3);margin-top:var(--s-6);padding:14px 16px;
  background:var(--card-solid);border:1px solid var(--sep);border-radius:var(--r-card);
  color:inherit;transition:transform var(--dur-press),border-color var(--dur-fast)}
.sp-plaza:active{transform:scale(.99)}
.sp-plaza .pi{flex:0 0 auto;width:38px;height:38px;border-radius:var(--r-av);display:grid;place-items:center;
  background:var(--fill);color:var(--ink2);font-size:17px}
.sp-plaza .pt{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.sp-plaza .pt b{font-size:var(--fs-sub);font-weight:600;letter-spacing:var(--ls-tight)}
.sp-plaza .pt i{font-style:normal;font-size:var(--fs-cap);color:var(--ink2)}
.sp-plaza .pa{flex:0 0 auto;color:var(--ink3);font-size:20px;line-height:1}
/* 空状态：灵感入口 */
.sp-empty{display:flex;flex-direction:column;align-items:center;text-align:center;
  padding:var(--s-9) 20px 40px;color:var(--ink2);gap:var(--s-1)}
.sp-empty .se-ic{width:64px;height:64px;border-radius:50%;background:var(--echo-wash);
  color:var(--echo-ink);display:grid;place-items:center;font-size:26px;margin-bottom:var(--s-4);
  box-shadow:0 0 0 1.5px var(--echo-bg) inset}
.sp-empty h3{margin:0;font-size:var(--fs-headline);font-weight:600;color:var(--ink);
  letter-spacing:var(--ls-title)}
.sp-empty p{margin:4px 0 18px;font-size:var(--fs-sub);line-height:var(--lh-prose);max-width:280px}
.se-ideas{display:flex;flex-wrap:wrap;gap:7px;justify-content:center;align-items:center;
  margin-bottom:18px;font-size:var(--fs-cap);color:var(--ink2)}
.se-ideas .chip{background:var(--card-solid);border:1px solid var(--sep);
  padding:7px 12px;font-size:var(--fs-foot);color:var(--ink)}
.sp-empty .btn{min-width:200px}

/* ---------- 角色列表瘦身 + 详情一级视图 ---------- */
.ml-head{display:flex;align-items:baseline;gap:8px;margin:0 0 6px;min-height:16px}
.ml-head h1{font-size:var(--fs-headline);font-weight:600;letter-spacing:var(--ls-title);margin:0;
  line-height:var(--lh-tight)}
.mlq{font-size:var(--fs-foot);color:var(--ink2);font-variant-numeric:tabular-nums}
.ml-acts{display:flex;gap:var(--s-3);margin-top:14px}
.ml-acts .btn{flex:1;padding:12px 10px}
.ml-links{margin-top:8px;background:var(--card-solid);border:1px solid var(--sep);
  border-radius:var(--r-card);overflow:hidden}
.lnk{display:flex;align-items:center;justify-content:space-between;width:100%;padding:13px 14px;
  background:transparent;border:0;font:inherit;font-size:var(--fs-sub);font-weight:600;
  letter-spacing:var(--ls-tight);color:var(--ink);text-align:left;cursor:pointer;position:relative;
  transition:background var(--dur-fast)}
.lnk+.lnk::before{content:"";position:absolute;left:14px;right:0;top:0;height:1px;background:var(--sep2)}
.lnk .go{color:var(--ink3);font-size:18px;line-height:1}
.lnk:active{background:var(--fill2)}

/* 详情头：一块和正文同底的页头 + 一条发丝线。
   上一版这里是一整块角色专属渐变 + 深色蒙层 + 白字带投影，
   是整站最像「游戏角色卡」的一处；参考库里没有一个产品页这么干。
   头像与名字本身就是信息，不需要再拿颜色垫一层。 */
/* 角色详情头：铺一层这个人的 Aura 光。
   换一个人，这一屏左上角那团光就换一个色——"每个 AI 像一个人"
   不必靠主题系统，一层色相就够了。 */
.pd-hero{position:relative;display:flex;align-items:flex-end;gap:14px;
  padding:var(--s-5) 16px var(--s-5);margin:-20px -16px var(--s-4);
  background-color:var(--bg);
  background-image:radial-gradient(120% 150% at 10% -10%,
    color-mix(in srgb,var(--au-a,#ded3b6) 30%,transparent) 0%,
    rgba(0,0,0,0) 62%);
  border-bottom:1px solid var(--sep2)}
.pd-hero h1{font-family:var(--font-prose);letter-spacing:.01em}
.pd-hero>*{position:relative;z-index:1}
.pd-back{position:absolute;left:12px;top:calc(12px + env(safe-area-inset-top));z-index:2;
  width:34px;height:34px;border-radius:50%;border:0;cursor:pointer;line-height:1;
  display:grid;place-items:center;font-size:22px;color:var(--ink2);background:transparent;
  transition:transform var(--dur-press),background var(--dur-fast)}
.pd-back:active{transform:scale(.9);background:var(--fill)}
.pd-id{display:flex;align-items:flex-end;gap:13px;width:100%;min-width:0}
.pd-av{flex:0 0 auto;position:relative}
.pd-av>.av{box-shadow:var(--av-stroke)}
.pd-av .av-hint{font-size:var(--fs-micro);color:var(--ink2);text-align:center;
  margin-top:5px;letter-spacing:var(--ls-label)}
.pd-tx{flex:1;min-width:0;padding-bottom:2px}
.pd-tx h1{margin:0;font-size:var(--fs-headline);font-weight:600;letter-spacing:var(--ls-title);
  color:var(--ink);line-height:var(--lh-tight);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pd-tg{font-size:var(--fs-foot);color:var(--ink2);line-height:1.55;margin-top:6px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pd-meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;
  font-size:var(--fs-micro);color:var(--ink2);font-weight:600}
.pd-meta i{font-style:normal;opacity:.5}
.pd-meta b{color:var(--ink2);font-weight:600;background:var(--fill);border-radius:var(--r-pill);
  padding:2px 8px}

/* 关系卡：状态一眼读完，改的入口只有一个 */
.rl-now{display:flex;align-items:baseline;gap:var(--s-2);flex-wrap:wrap;
  margin:2px 0 6px;font-size:var(--fs-body);color:var(--ink2);line-height:1.5}
.rl-now b{font-weight:600;color:var(--ink);letter-spacing:var(--ls-title)}
.rl-now span{font-size:var(--fs-foot);color:var(--ink2);font-variant-numeric:tabular-nums}
.rl-st{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:2px 0 8px}
.rl-st .chip{cursor:default}
.rl-arrow{font-style:normal;color:var(--ink3);font-size:var(--fs-cap)}
.rl-tone{font-size:var(--fs-cap);color:var(--ink2);line-height:1.65}
.cb{height:3px;border-radius:999px;background:var(--fill2);overflow:hidden;margin-top:12px}
.cb>i{display:block;height:100%;border-radius:999px;
  background:var(--ink);opacity:.45;
  transition:width var(--dur-slow) var(--ease-out)}
.rl-num{font-size:var(--fs-micro);color:var(--ink2);margin-top:6px;font-variant-numeric:tabular-nums}
/* 卡片里不再套卡片：这两格原来是两块灰底小卡，套在「你们的关系」那张白卡里，
   读起来就是后台表单。改成两栏 + 一条发丝线分隔，标签在上、值在下。 */
.rl-row{display:flex;gap:0;margin-top:14px;
  border-top:1px solid var(--sep2);border-bottom:1px solid var(--sep2)}
.rl-row>div{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;
  background:none;border-radius:0;padding:11px 12px 11px 0}
.rl-row>div+div{padding-left:12px;border-left:1px solid var(--sep2)}
.rl-row span{font-size:var(--fs-micro);color:var(--ink2);order:-1}
.rl-row b{font-size:var(--fs-sub);font-weight:600;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.rl-line{font-size:var(--fs-cap);color:var(--ink2);line-height:1.65;margin-top:9px}
.rl-hist{font-size:var(--fs-cap);color:var(--ink2);line-height:1.85}

/* 开场气泡 / 最近一条 */
.pd-greet .bub{display:inline-block;max-width:86%;background:var(--bot);color:var(--ink);
  padding:10px 14px;border-radius:var(--r-bubble);border-top-left-radius:var(--r-xs);
  font-size:var(--fs-body);line-height:1.55}
.pd-last{display:flex;gap:9px;font-size:var(--fs-sub);color:var(--ink2);line-height:1.6}
.pd-last span{flex:1;min-width:0}

/* 正文：默认三行，渐隐提示还有内容 */
.pd-body .pdx{position:relative;max-height:5.4em;overflow:hidden;
  transition:max-height var(--dur-base) var(--ease-out)}
.pd-body .pdx::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2.2em;
  background:linear-gradient(180deg,transparent,var(--card-solid));pointer-events:none}
.pd-body.open .pdx{max-height:none}
.pd-body.open .pdx::after{display:none}
.pdt{font-size:var(--fs-sub);line-height:var(--lh-prose);white-space:pre-wrap}

/* 底部动作栏：一条主行动 + 三个次要。position:sticky 的东西每一帧都要重糊背后，
   所以这里不加 blur，直接把底色做实。 */
.pd-acts{position:sticky;bottom:-20px;z-index:6;display:flex;flex-direction:column;gap:8px;
  margin:18px -16px -20px;padding:12px 16px 10px;
  /* 必须是实底：96% 半透 + 下面正文会滚到它背后，真机上"人设正文"那行字
     直接从「继续聊天」那颗按钮底下透出来，读起来像叠了两层。 */
  background:var(--card-solid);
  border-top:1px solid var(--sep)}
.pd-acts>#pd-go{padding:13px}
.pd-acts-2{display:flex;gap:8px}
.pd-acts-2 .btn{flex:1;padding:10px 6px;font-size:var(--fs-cap)}

/* 编辑器里的必填提示 */
.field label .must{margin-left:6px;font-size:var(--fs-micro);font-weight:600;color:var(--tint);
  background:var(--tint-bg);border-radius:var(--r-pill);padding:2px 7px;vertical-align:1px}

/* 折叠进「进阶」的那几个模型参数：中文说法是正文，原始字段名只是对账用的角注。
   用 --ink2 而不是 --ink3：它小，但它是「这栏到底对应哪个参数」的真信息，
   装饰色（--ink3）在这条站里只给不看也不影响的笔画。 */
.field label .hint{margin-left:6px;font-size:var(--fs-micro);font-weight:400;
  color:var(--ink2);letter-spacing:0}

/* 详情页是整页：列表顶栏让位，封面直接贴到状态栏下面 */
body.on-detail .topbar{display:none}
body.on-detail .page{padding-top:0}
body.on-detail .pd-hero{margin:0 -16px var(--s-4);
  padding-top:calc(50px + env(safe-area-inset-top))}
.pd-wrap{display:grid;grid-template-columns:minmax(0,1fr)}
.pd-col{min-width:0}
/* 「已经知道的事」：这一卡的语气是记忆，所以走青瓷 + 衬线句子，
   和上面的关系卡（玫瑰）刻意不同——不同状态该有不同视觉反馈。 */
.pd-knows .kn-list{display:flex;flex-direction:column;gap:2px;margin-top:2px}
.pd-knows .kn-row{display:flex;align-items:baseline;gap:9px;padding:7px 0;
  border-top:1px solid var(--sep2)}
.pd-knows .kn-row:first-child{border-top:0}
.pd-knows .kn-dot{flex:0 0 auto;width:5px;height:5px;border-radius:50%;
  background:var(--mnem);transform:translateY(-2px)}
.pd-knows .kn-row p{margin:0;flex:1;min-width:0;font-family:var(--font-prose);
  font-size:var(--fs-sub);line-height:var(--lh-prose);color:var(--ink);overflow-wrap:anywhere}
.pd-knows .kn-row time{flex:0 0 auto;font-family:var(--font-time);font-size:var(--fs-micro);
  color:var(--ink2);font-variant-numeric:tabular-nums}
.pd-knows .kn-more{display:inline-block;margin-top:10px;font-size:var(--fs-cap);color:var(--mnem-ink)}

@media (min-width:1024px){
  body.on-detail .page.wrap{max-width:860px}
  .conv.space{max-width:940px}
  .sp-grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
}
/* 两栏要到这个宽度才不挤：rail 占了 264px，剩下的一栏得有 400px 才好看 */
@media (min-width:1120px){
  body.on-detail .pd-wrap{grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    gap:0 22px;align-items:start}
  body.on-detail .pd-acts{margin:20px 0 0}
}

/* ---------- 创建三步流程 + 边挑边看的角色卡 ---------- */
.wsteps{display:flex;align-items:center;gap:6px;margin-top:2px;
  font-size:var(--fs-cap);color:var(--ink2)}
.wsteps i{display:flex;align-items:center;gap:6px;padding:6px 11px;border-radius:var(--r-pill);
  background:var(--fill2);font-style:normal;font-weight:600;letter-spacing:var(--ls-tight);
  transition:background var(--dur-fast),color var(--dur-fast)}
.wsteps i b{width:16px;height:16px;border-radius:50%;background:var(--fill);
  display:grid;place-items:center;font-size:var(--fs-micro);font-weight:600;line-height:1}
.wsteps i.on{background:var(--tint);color:var(--tint-ink)}
.wsteps i.on b{background:rgba(255,255,255,.26);color:var(--tint-ink)}
.wsteps i.done{color:var(--ink2)}
.wsteps i.done b{background:var(--tint-bg);color:var(--tint)}
.wsteps i+i{margin-left:2px}
.wsteps i+i::before{content:"›";margin-right:1px;opacity:.45;font-weight:400}

/* 预览卡：左边一张会变的角色卡，右边说「按这些条件去写」 */
.wprev{display:flex;gap:13px;align-items:stretch;margin-bottom:16px}
  .wprev .wpc{flex:0 0 auto;width:106px;pointer-events:none;cursor:default}
.wprev .wpd{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:7px}
.wpd .k{font-size:var(--fs-foot);font-weight:600;color:var(--ink2);letter-spacing:var(--ls-tight)}
.wpd .wsay{margin-top:0;max-height:132px;overflow:auto}

/* ---------- 聊天页顶栏：把「在和谁说话」讲清楚 ---------- */
.tb-id{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.tb-id h1{flex:none}
.tb-row{display:flex;align-items:center;gap:6px;min-width:0}
.tb-row .sub{flex:0 1 auto;min-width:0}
/* 关系那一个词是"你们之间"的事，所以走玫瑰而不是中性灰——
   上一版它是 --fill 灰胶囊，和"VIP""3 条"这类标签同一个脸，读不出是关系。 */
.rlg{flex:0 0 auto;font-size:var(--fs-micro);font-weight:600;color:var(--bond-ink);
  background:var(--bond-bg);border-radius:var(--r-pill);padding:2px 8px;
  letter-spacing:var(--ls-tight)}
/* 聊天页的顶栏坐在沉底画布上，不再是"另一个颜色的条"——
   顶栏和会话区之间那道硬接缝，是把聊天读成"一个网页的一块区域"的主因。 */
.tb-chat{background-color:var(--room-top);border-bottom-color:transparent}
.tb-chat h1,.tb-chat .tb-id h1{font-family:var(--font-prose);font-weight:600;
  letter-spacing:.01em}
/* 顶栏头像收小一档，右下角留一个状态点：陪伴感来自「TA 在」，不是来自图标堆叠 */
.topbar #bot-av{flex:0 0 auto;position:relative}
.topbar #bot-av .av{width:36px;height:36px;font-size:15px;border-radius:var(--r-av)}
.tb-chat .topbar-spacer{display:none}
.topbar #bot-av::after{content:"";position:absolute;right:-1px;bottom:-1px;width:10px;height:10px;
  border-radius:50%;background:var(--ok);box-shadow:0 0 0 2.5px var(--card-solid)}

/* 输入区：表情和发送住进同一颗胶囊，聚焦时整颗亮 */
/* 键盘避让不再靠这里加 padding（--kb 那条链路已删，见 chat.js fitKeyboard）。
     外壳高度等于视觉视口之后，这一条本来就贴在可视底边上。 */
.composer{padding:8px var(--gut) calc(8px + env(safe-area-inset-bottom))}
/* 默认：白底 + 一层几乎看不见的暖影，不给常驻描边。
   focus：只把描边收到 1.5px 的品牌橙，不加外发光——
   输入框是"我在说话"的入口，它该安静地等，不该一直亮着。 */
.cmp{display:flex;align-items:flex-end;gap:4px;width:100%;background:var(--card-solid);
  border:0;border-radius:var(--r-panel);padding:4px;
  box-shadow:0 1px 2px rgba(120,84,44,.06),0 8px 20px rgba(120,84,44,.07),
             inset 0 0 0 1px var(--sep2);
  transition:box-shadow .18s var(--ease-out)}
.cmp:focus-within{box-shadow:0 1px 2px rgba(120,84,44,.06),0 8px 20px rgba(120,84,44,.07),
             inset 0 0 0 1.5px var(--me)}
.cmp>textarea{flex:1;min-width:0;border:0;background:transparent;box-shadow:none;outline:0;
  min-height:38px;max-height:132px;padding:9px 6px;border-radius:0;font-size:var(--fs-body);line-height:1.4}
.cmp>textarea:focus{border:0;box-shadow:none}
.cmp .stk-btn{flex:0 0 auto;width:34px;height:34px;margin-bottom:2px}
.cmp .send{width:38px;height:38px;padding:0;border-radius:50%;display:grid;
  place-items:center;flex:0 0 auto;position:relative}
/* 视觉 38px，热区补到 46px：发送钮是聊天页最常用的一个动作，
   38px 在手机上按不准（.icon-btn 早就是 36 视觉 + 44 热区，这里同一个套路）。
   ::before 是绝对定位，不会变成 grid 的第二项把图标挤歪。 */
.cmp .send::before{content:"";position:absolute;inset:-4px;border-radius:50%}
.cmp .send svg{width:19px;height:19px}

/* 回最新：只在往上翻历史时出现 */
.jump{position:fixed;left:50%;transform:translateX(-50%);z-index:7;
  bottom:calc(76px + env(safe-area-inset-bottom));display:grid;place-items:center;
  width:38px;height:38px;border-radius:50%;border:1px solid var(--sep);
  background:var(--card-solid);color:var(--ink2);
  box-shadow:var(--sh2);cursor:pointer;
  animation:rise var(--dur-base) var(--ease-out) both}
.jump[hidden]{display:none}
.jump:active{transform:translateX(-50%) scale(.9)}
.jump svg{width:20px;height:20px}
.jump i{position:absolute;top:-4px;right:-3px;min-width:17px;height:17px;padding:0 4px;
  border-radius:9px;background:var(--tint);color:var(--tint-ink);font-size:var(--fs-micro);
  font-style:normal;font-weight:600;display:grid;place-items:center}
@media (min-width:1024px){body.has-rail .jump{left:calc(50% + 132px)}}

/* 长按要有回应：先按下去，再弹动作表 */
.bubble.press,.sticker-pic.press,.emoji-pic.press{transform:scale(.975);
  transition:transform var(--dur-press)}
@media (prefers-reduced-motion:reduce){
  .bubble.typing::after{animation:none}
  .jump{animation:none}
  .breathe{animation:none}
  .sk::after{animation:none}
}

/* ---------- 我的/设置（小标题统一 + 宽屏两栏） ---------- */
.mini-label{display:block;font-size:var(--fs-foot);font-weight:600;color:var(--ink2);
  margin-bottom:6px;letter-spacing:var(--ls-label)}
.mini-note{font-size:var(--fs-cap);color:var(--ink2);margin-top:10px;line-height:1.6}
.set-split{display:grid;grid-template-columns:minmax(0,1fr)}
.set-nav{display:none}

/* 设置不再是一根居中的手机号：左边目录常驻，右边才是内容 */
@media (min-width:1024px){
  .page.wrap.on-split{max-width:980px;padding-top:14px}
  .set-split{grid-template-columns:212px minmax(0,1fr);gap:0 30px;align-items:start}
  .set-nav{display:flex;flex-direction:column;gap:2px;position:sticky;top:10px}
  .set-nav .sg{font-size:var(--fs-micro);font-weight:600;color:var(--ink2);
    letter-spacing:var(--ls-caps);margin:18px 10px 5px}
  .set-nav .sg:first-child{margin-top:0}
  .set-nav .sn{display:flex;align-items:center;gap:10px;padding:9px 11px;
    border-radius:var(--r-btn-sm);font-size:var(--fs-sub);color:var(--ink2);
    text-align:left;text-decoration:none;line-height:1.35;font-weight:600;
    transition:background var(--dur-fast),color var(--dur-fast)}
  .set-nav .sn .ic{flex:0 0 auto;width:17px;height:17px;display:grid;place-items:center}
  .set-nav .sn .ic svg{width:17px;height:17px;display:block}
  .set-nav .sn:hover{background:var(--fill2)}
  .set-nav .sn.on{background:var(--fill);color:var(--ink);font-weight:600}
  .set-nav .sn.warn{color:var(--warn)}
  .set-body{min-width:0}
  .set-body .card:last-child{margin-bottom:0}
  /* 桌面上一条通到 700px 的主按钮很业余：给它一个正常的按钮宽度 */
  .set-body .btn.block{max-width:320px}
  /* 短字段（密码 / 兑换码 / 模型名）不必拉满整栏 */
  .set-body .field input[type=password],.set-body .field input[type=text],
  .set-body .split input[type=text],.set-body .split input[type=password]{max-width:420px}
}

/* ---------- 主动联系的时间弧（settings.js 的 proactiveArc） ----------
   这一屏回答的是「什么时候 TA 会想起你」，不是一排开关。
   一天画成一道弧：0 点在左、24 点在右、正午在最高处；
   暗的那一段是安静时段，点落在 TA 大概会开口的那一刻，
   每个点用「那个人自己的色温」——弧上不是一个抽象排程，是一个一个具体的人。
   这是"期待"那一档，所以整块坐琥珀，不用设置页的中性灰。 */
.pa-arc{margin:2px 0 var(--s-5);padding:var(--s-4) var(--s-4) var(--s-3);
  border-radius:var(--r-card);background:var(--echo-wash);
  box-shadow:inset 0 0 0 1px var(--echo-bg)}
.pa-cap{font-size:var(--fs-cap);color:var(--echo-ink);letter-spacing:var(--ls-label);
  margin-bottom:var(--s-2);font-weight:600}
.pa-svg{display:block;width:100%;height:auto;overflow:visible}
.pa-line{fill:none;stroke:var(--echo);stroke-width:2;stroke-linecap:round;opacity:.5}
/* 安静那一段压暗 + 加粗：它要一眼看出"这段时间不会吵你"，
   不能只是同一条线上颜色浅一点。 */
.pa-quiet{fill:none;stroke:var(--ink3);stroke-width:7;stroke-linecap:round;opacity:.3}
.pa-tick{stroke:var(--sep);stroke-width:1}
.pa-now{stroke:var(--ember);stroke-width:2;stroke-linecap:round}
.pa-dot{fill:currentColor;stroke:var(--echo-wash);stroke-width:2}
.pa-dot.intent{fill:none;stroke:currentColor;stroke-width:2.5}
.pa-ticks{position:relative;height:14px;margin-top:2px}
.pa-h{position:absolute;top:0;transform:translateX(-50%);
  font-family:var(--font-time);font-size:var(--fs-micro);color:var(--ink2);
  white-space:nowrap}
/* 两端的刻度不能被拉出容器：0:00 靠左贴边、24:00 靠右贴边 */
.pa-h:first-child{transform:none;left:0 !important}
.pa-h:last-child{transform:none;left:auto !important;right:0}
.pa-next{margin:var(--s-3) 0 0;font-size:var(--fs-foot);line-height:1.6;color:var(--ink2)}
.pa-next b{font-family:var(--font-prose);color:var(--ink);font-weight:600}

/* 系统里关了「降低透明度」的用户（iOS 辅助功能 / Windows 透明效果）本来就不要毛玻璃，
   一刀切掉；老安卓和低端机也走这条，实测这是滚动掉帧的头号来源。 */
@media (prefers-reduced-transparency:reduce){
  *{-webkit-backdrop-filter:none!important;backdrop-filter:none!important}
}

/* ============================================================
   L7b · 记忆视图 / 关系视图（home.js 的 #remember 与 #between）
   ------------------------------------------------------------
   这两页最容易写成后台：一列表格 + 一排筛选。刻意反着来——
   记忆是一句一句的人话，关系是一行一行的时间。
   所以两页都没有卡片套卡片，只有分组标题 + 发丝线 + 留白，
   操作在行上，需要时才浮现。
   ============================================================ */

/* ---------- TA 记得的：安静、青瓷、一句一句的人话 ----------
   记忆用衬线读，是因为它是"TA 眼里的你"，不是数据库的一行。
   左侧 18px / 右侧 4px 的不等圆角让每条像一张撕下来的便签。 */
.rm-pick{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-bottom:var(--s-5)}
.rm-find{margin-bottom:var(--s-5)}
.rm-find input{width:100%;padding:12px 14px;border-radius:var(--r-input);
  border:1px solid var(--sep);background:var(--bg2);color:var(--ink);
  font-size:var(--fs-sub);outline:0;
  transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}
.rm-find input:focus{border-color:var(--mnem-ink);
  box-shadow:0 0 0 3px var(--mnem-bg)}
.rm-find input::placeholder{color:var(--ink3)}
.rm-hits:empty{display:none}
.rm-hits{margin-bottom:var(--s-6);padding:var(--s-4) 0 0;border-top:1px solid var(--sep2)}
.rm-hitn{margin:0 0 var(--s-3);font-size:var(--fs-foot);color:var(--mnem-ink);
  letter-spacing:var(--ls-label)}
.rm-box{display:flex;flex-direction:column}
.rm-lead{margin:0 0 var(--s-7);font-family:var(--font-prose);font-size:var(--fs-lead);
  font-weight:400;line-height:var(--lh-snug);letter-spacing:var(--ls-title);color:var(--ink);
  max-width:34ch;text-wrap:balance}
.rm-seed{margin:0 0 var(--s-7)}
.rm-seed-lead{margin:0 0 var(--s-5);font-size:var(--fs-sub);line-height:1.7;color:var(--ink2)}
.rm-seed-list{list-style:none;margin:0 0 var(--s-6);padding:var(--s-5) 0 0;
  border-top:1px solid var(--mnem-bg);display:flex;flex-direction:column;gap:0}
.rm-seed-list li{padding:var(--s-4) 0;border-bottom:1px solid var(--mnem-bg);
  display:flex;flex-direction:column;gap:3px}
.rm-seed-list li:last-child{border-bottom:0}
.rm-seed-list b{font-family:var(--font-prose);font-size:var(--fs-body);font-weight:600;
  color:var(--mnem-ink);letter-spacing:var(--ls-title)}
.rm-seed-list i{font-style:normal;font-size:var(--fs-foot);color:var(--ink2);line-height:1.6}
.rm-sec{margin-bottom:var(--s-8)}
.rm-sec-h{display:flex;align-items:baseline;gap:var(--s-3);flex-wrap:wrap;
  padding-bottom:var(--s-2);border-bottom:1px solid var(--sep)}
.rm-sec-h h3{margin:0;font-size:var(--fs-foot);font-weight:600;color:var(--ink);
  letter-spacing:var(--ls-label)}
.rm-sec-h span{font-size:var(--fs-cap);color:var(--ink2)}
.rm-row{display:flex;align-items:flex-start;gap:var(--s-4);
  padding:var(--s-4) 0;border-bottom:1px solid var(--sep2)}
.rm-row:last-child{border-bottom:0}
.rm-tx{flex:1;min-width:0}
.rm-tx p{margin:0;font-family:var(--font-prose);font-size:var(--fs-body);
  line-height:var(--lh-prose);color:var(--ink);overflow-wrap:anywhere}
.rm-tx span{display:block;margin-top:5px;font-size:var(--fs-cap);color:var(--ink2);
  font-family:var(--font-time);letter-spacing:var(--ls-label)}
/* 钉住的那条只用一条左边线标出来，不贴彩色标签 */
.rm-row.pin{padding-left:var(--s-3);border-left:2px solid var(--mnem);border-radius:18px 4px 4px 18px}
.rm-ops{display:flex;gap:var(--s-1);flex:0 0 auto;opacity:0;
  transition:opacity var(--dur-fast)}
.rm-row:hover .rm-ops,.rm-row:focus-within .rm-ops{opacity:1}
@media (hover:none){.rm-ops{opacity:1}}
.rm-op{font-size:var(--fs-micro);color:var(--ink2);padding:5px 9px;
  border-radius:var(--r-s);background:transparent;border:0;
  transition:background var(--dur-fast),color var(--dur-fast)}
.rm-op:hover{background:var(--fill);color:var(--ink)}
.rm-op.del:hover{background:var(--warn-bg);color:var(--warn)}
.rm-foot{display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);
  flex-wrap:wrap;margin-top:var(--s-2);padding-top:var(--s-5);border-top:1px solid var(--sep);
  font-size:var(--fs-cap);color:var(--ink2)}
.rm-foot a{font-size:var(--fs-foot)}

/* ---------- 你们之间：叙事、玫瑰、一条走下来的时间线 ----------
   刻意不做等级、进度条、百分比。成熟陪伴产品里没有一个给关系打分。 */
.bt-list{display:flex;flex-direction:column}
.bt-card{padding:var(--s-5) 0;border-bottom:1px solid var(--sep)}
.bt-card:last-child{border-bottom:0}
.bt-hd{display:flex;align-items:center;gap:var(--s-3)}
.bt-hd .av{width:44px;height:44px;font-size:17px}
.bt-tx{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.bt-tx b{font-family:var(--font-prose);font-size:var(--fs-body);font-weight:600;color:var(--ink);
  letter-spacing:var(--ls-title);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bt-tx i{font-style:normal;font-size:var(--fs-foot);color:var(--ink2);line-height:1.45;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bt-go{flex:0 0 auto;font-size:var(--fs-foot);font-weight:600;color:var(--bond-ink);
  padding:7px 14px;border-radius:var(--r-m);background:var(--bond-bg);
  transition:transform var(--dur-press),opacity var(--dur-fast)}
.bt-go:active{transform:scale(.97)}
/* 一句人话讲完这段关系：处了多久、现在什么分寸。没有条，没有分。
   时长走等宽——它是时间，不是数量。 */
.bt-line{margin:var(--s-3) 0 0;font-size:var(--fs-sub);line-height:1.6;color:var(--ink2);
  font-family:var(--font-time)}
.bt-tone{margin:6px 0 0;font-family:var(--font-prose);font-size:var(--fs-sub);
  line-height:var(--lh-prose);color:var(--ink);max-width:52ch}
.bt-hist{margin-top:var(--s-4);padding-left:var(--s-4);border-left:2px solid var(--bond-bg)}
.bt-ev{display:flex;gap:var(--s-3);align-items:baseline;padding:5px 0;position:relative}
.bt-ev::before{content:"";position:absolute;left:calc(-1 * var(--s-4) - 4px);top:12px;
  width:6px;height:6px;border-radius:50%;background:var(--bond)}
.bt-ev .when{flex:0 0 44px;font-family:var(--font-time);font-variant-numeric:tabular-nums;
  font-size:var(--fs-cap);color:var(--bond-ink)}
.bt-ev .why{flex:1;min-width:0;font-size:var(--fs-foot);line-height:1.55;color:var(--ink2)}

/* ============================================================
   L8 · 宽屏（桌面 / 平板横屏）适配
   全站按移动优先写，宽屏上会走形成三种：抽屉拉到两米宽、聊天气球一行上千像素、
   首页会话行要转头看。统一收成居中的阅读列；648px 正文宽度和 .page.wrap 对齐，
   顶栏用同一条内缩公式，标题才不会和正文错位。
   这一块按约定放在最后：各组件自己的 @media 都并进这里，谁在后面谁说话。
   ============================================================ */
@media (min-width:720px){
  .stream,.composer{padding-inline:max(14px, calc(50% - 324px))}
  .stk-panel{padding-inline:max(12px, calc(50% - 324px))}
  /* width:100% 不能省：flex 子项上 margin-inline:auto 会先吃掉剩余空间，
     align-items:stretch 就不再生效，正文列会缩成内容宽（首页曾被压成 313px）。
     给了确定宽度后 max-width 才负责封顶，auto 边距负责居中。 */
  .conv{max-width:680px;margin-inline:auto;width:100%;border-inline:1px solid var(--sep)}
  .tabbar{max-width:680px;margin-inline:auto;width:100%;border-inline:1px solid var(--sep)}
  .col{max-width:66%}
  /* 抽屉从贴底全屏改成居中浮层，设定表单不再被横向拉散 */
  .sheet{left:50%;right:auto;bottom:0;width:min(720px,92vw);padding-top:18px;
    transform:translate(-50%,100%);padding-bottom:26px;border-radius:var(--r-panel);
    border:1px solid var(--sep)}
  .sheet.show{transform:translate(-50%,0)}
  .sheet::before{display:none}   /* 浮层不需要「往上拖」的把手 */
  .sheet.full{left:50%;right:auto;top:min(44px,7vh);bottom:min(44px,7vh);
    width:min(760px,92vw);padding:16px 22px 26px}
  /* 触屏没有 hover，宽屏下给可点的行补个落点 */
  .conv-row:hover,.row-i:hover,.pick-item:hover,.icon-btn:hover{background:var(--fill2)}
  .pcard:hover{border-color:var(--sep)}
  .sp-railc:hover,.sp-cell:hover,.pzc:hover,.sp-plaza:hover,.lnk:hover{transform:translateY(-2px);
}
  .act:hover,.wcan:hover{background:var(--fill2)}
  /* 宽屏下角色卡不必撑到两列那么肥，回到按内容宽度自动排 */
  .grid{grid-template-columns:repeat(auto-fill,minmax(168px,1fr));gap:var(--s-3)}
  .pcard{border-radius:var(--r-card)}
  .pcard .nm{font-size:var(--fs-body);padding:12px 14px 0}
  .pcard .tg{padding:4px 14px 0}
  .pcard .pv{font-size:var(--fs-foot);margin:5px 14px 14px;-webkit-line-clamp:2}
  .pcard>.av{font-size:52px}

  /* 桌面上「最近在聊」不再横滑：横向轨道只有两三个人的时候，
     右边会空出一大片——那是"没排完"的样子。改成自动换行的头像网格。 */
  .sp-rail{display:flex;flex-wrap:wrap;gap:18px 14px;overflow:visible;
    scroll-snap-type:none}
  /* 轨道项在移动是 76px 一颗，桌面上别写 width:auto——
     那会让「全部 N 位」那颗圆塌成文字宽。 */
  .sp-railc{width:76px;flex:0 0 auto}
  .sp-railc.more{width:76px}
  .sp-grid{grid-template-columns:repeat(auto-fill,minmax(132px,1fr))}
  .pd-hero{margin:-20px -16px 18px;border-radius:0 0 var(--r-panel) var(--r-panel)}
  body.on-detail .pd-hero{margin:0 -16px 18px}
  .pd-acts{border-radius:var(--r-card);border:1px solid var(--sep);margin:20px 0 0;
    bottom:0;padding:12px 14px}
  .ml-acts .btn{max-width:240px}
  .wprev .wpc{width:124px}
  .chat-empty p{max-width:420px}
  /* 阅读宽度：桌面把气泡限到 60ch，长段落不必扭着头看 */
  .bubble{max-width:60ch}
}
