/* ============================================================
   星云办公 · 主题「云笈紫韵 yun」
   云白为纸 · 紫藤为墨 · 青玉为饰 —— 配合星云形象素材
   ============================================================ */
:root{
  --bg:#f6f4f1; --card:#ffffff; --ink:#38323f; --muted:#8f8798;
  --pri:#8b6fb0; --pri-d:#6a5290; --pri-soft:#f1ebf7;
  --mint:#7fae9f; --mint-soft:#e9f2ef;
  --gold:#c2a15c; --line:#e9e3ef; --shadow:0 10px 30px rgba(90,70,120,.10);
}
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  background:
    radial-gradient(1200px 500px at 85% -100px, rgba(139,111,176,.10), transparent 60%),
    radial-gradient(900px 400px at -60px 30%, rgba(127,174,159,.10), transparent 55%),
    var(--bg);
  color:var(--ink);
  font:16px/1.75 -apple-system,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
  min-height:100vh;
}
a{color:inherit;text-decoration:none}
h1,h2,h3,.serif{font-family:"Noto Serif SC","Songti SC","SimSun",serif}

/* ---------- 导航 ---------- */
/* --nav-h：导航栏实际高度，供模板里的全屏弹窗让位用（弹窗 top 从这里开始，
   不再覆盖导航）。定义在 :root 而不是 nav 上，因为弹窗是 nav 的兄弟节点。 */
:root{--nav-h:calc(60px + env(safe-area-inset-top,0px))}
nav{position:sticky;top:0;z-index:50;background:rgba(255,255,255,.82);
  /* safe-area：全站 viewport-fit=cover，刘海屏/挖孔屏上导航会顶进状态栏，
     不留 padding 用户看到的就是「导航栏被削了头/没了」。
     无刘海设备 env()=0，此行无副作用。 */
  padding-top:env(safe-area-inset-top,0px);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line)}
.wrap{max-width:1060px;margin:0 auto;padding:0 20px}
.nav-in{display:flex;align-items:center;gap:26px;height:60px}
.logo{display:flex;align-items:center;gap:10px;font-weight:700;font-size:17px;
  letter-spacing:1px;white-space:nowrap;font-family:"Noto Serif SC","Songti SC",serif}
.navface{width:40px;height:40px;border-radius:50%;object-fit:cover;
  border:2px solid var(--pri-soft);box-shadow:0 2px 8px rgba(139,111,176,.25)}
.dot{width:7px;height:7px;border-radius:50%;background:var(--mint);display:inline-block;
  box-shadow:0 0 0 3px rgba(127,174,159,.2);margin-left:-4px}
.sp{flex:1}
.lk{color:var(--muted);font-size:14.5px;padding:6px 2px;position:relative;white-space:nowrap}
.lk:hover{color:var(--pri-d)}
.lk.on{color:var(--pri-d);font-weight:600}
.lk.on::after{content:"";position:absolute;left:0;right:0;bottom:-2px;height:3px;
  border-radius:3px;background:linear-gradient(90deg,var(--pri),var(--mint))}

/* ---------- 首页 hero ---------- */
.hero{padding:64px 0 44px;text-align:center}
.kicker{display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--pri-d);
  background:var(--pri-soft);border:1px solid var(--line);padding:5px 16px;border-radius:99px}
.hero h1{font-size:44px;letter-spacing:8px;margin:22px 0 10px;font-weight:700}
.hero h1 em{font-style:normal;color:var(--pri);
  background:linear-gradient(120deg,var(--pri),var(--mint));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
.seal{display:inline-block;vertical-align:14px;margin-left:14px;font-size:12px;letter-spacing:2px;
  color:#fff;background:linear-gradient(135deg,#c05a67,#a94b58);padding:5px 9px;border-radius:6px;line-height:1.3;
  writing-mode:vertical-rl}
.lede{color:var(--muted);letter-spacing:2px}
.search{margin:30px auto 0;max-width:520px;display:flex;background:#fff;border:1.5px solid var(--line);
  border-radius:99px;overflow:hidden;box-shadow:var(--shadow)}
.search input{flex:1;border:0;outline:0;padding:13px 22px;font:inherit;background:transparent}
.search button{border:0;background:linear-gradient(120deg,var(--pri),var(--pri-d));color:#fff;
  padding:0 30px;font:inherit;cursor:pointer;letter-spacing:4px}
.hero-rule{width:64px;height:4px;border-radius:4px;margin:36px auto 0;
  background:linear-gradient(90deg,var(--pri),var(--mint))}

/* ---------- 目录卡片 ---------- */
.sec{display:flex;align-items:center;gap:12px;margin:26px 0 22px}
.sec h2{font-size:21px;letter-spacing:3px}
.bar{width:5px;height:22px;border-radius:4px;background:linear-gradient(180deg,var(--pri),var(--mint))}
.line{flex:1;height:1px;background:var(--line)}
.cnt{color:var(--muted);font-size:13px}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:20px;padding-bottom:60px}
.cardp{position:relative;display:block;background:var(--card);border:1px solid var(--line);
  border-radius:18px;padding:26px 24px 20px;box-shadow:0 4px 14px rgba(90,70,120,.06);
  transition:.25s;overflow:hidden}
.cardp::before{content:"";position:absolute;inset:0 0 auto 0;height:4px;
  background:linear-gradient(90deg,var(--pri),var(--mint));opacity:0;transition:.25s}
.cardp:hover{transform:translateY(-4px);box-shadow:var(--shadow);border-color:#d8cbe8}
.cardp:hover::before{opacity:1}
.no{position:absolute;top:10px;right:16px;font-family:"Noto Serif SC",serif;font-size:40px;
  font-weight:700;color:var(--pri-soft);line-height:1}
.cardp h3{font-size:18.5px;margin-bottom:8px;letter-spacing:.5px;padding-right:52px}
.cardp p{color:var(--muted);font-size:14px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-bottom:14px}
.tags{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.tags span{font-size:12px;color:var(--pri-d);background:var(--pri-soft);padding:2px 10px;border-radius:99px}
.cardp .m{color:#b5aec0;font-size:12.5px;border-top:1px dashed var(--line);padding-top:10px}
/* CMS 封面卡片（云笈紫韵：封面顶满卡片顶部，圆角与卡片一致） */
.cardp .cover{display:block;width:calc(100% + 48px);height:160px;object-fit:cover;
  margin:-26px -24px 18px;border-bottom:1px solid var(--line)}
.detail .cover-hero{display:block;width:100%;max-height:400px;object-fit:cover;margin:0 0 20px;
  border-radius:12px;border:1px solid var(--line)}
.empty{grid-column:1/-1;text-align:center;color:var(--muted);padding:60px 0}

/* ---------- 文章详情 ---------- */
.detail{max-width:800px;margin:36px auto 60px;background:var(--card);border:1px solid var(--line);
  border-radius:20px;padding:48px 54px;box-shadow:var(--shadow)}
.back{display:inline-block;color:var(--muted);font-size:14px;margin-bottom:22px}
.back:hover{color:var(--pri-d)}
.detail h1{font-size:30px;letter-spacing:2px;line-height:1.5;margin-bottom:14px}
.meta{color:var(--muted);font-size:13.5px;padding-bottom:22px;border-bottom:1px solid var(--line);margin-bottom:30px}
.content p{margin:0 0 20px;font-size:16.5px;line-height:2;text-align:justify}
.content h2{font-size:22px;margin:34px 0 16px;padding-left:14px;border-left:4px solid var(--pri)}
.content h3{font-size:18px;margin:26px 0 12px;color:var(--pri-d)}
.likebar{text-align:center;padding-top:26px;border-top:1px solid var(--line)}
.likebar button{border:0;cursor:pointer;font:inherit;font-size:15px;color:#fff;
  background:linear-gradient(120deg,#c05a67,#a94b58);padding:11px 38px;border-radius:99px;
  letter-spacing:2px;box-shadow:0 6px 16px rgba(192,90,103,.35);transition:.2s}
.likebar button:hover{transform:translateY(-2px)}

/* ---------- 秘书台 ---------- */
.desk{max-width:760px;margin:0 auto;padding:2px 18px 20px}
.stage{
  --top:230px;      /* 聊天框顶边（距 stage 顶部）；人物头顶约距导航栏 5mm（留空隙，不顶栏）。
                       v1.14.1：曾为拉长聊天框调到 185px 并缩小立绘(--figw 190→155)，
                       但立绘为全身像、头顶=top-214，缩小前若再上调 top 头顶会顶进导航栏；
                       老板要求立绘保持原尺寸且不与导航栏相贴，故两个变量均回退到原始值 */
  --figw:190px;     /* 人像宽度（皮肤三件套等比缩放，比例固定），保持原始尺寸不缩小 */
  --handh:calc(var(--figw) * 0.164);   /* 手部图层高 = 宽 × 0.164（皮肤统一比例） */
  --headh:calc(var(--figw) * 1.247);   /* 身体图层高 = 宽 × 1.247 */
  --handgap:calc(var(--figw) * 1.126); /* 身体层顶部到框顶距离 */
  --overlap:3px;    /* 手指搭压框顶的深度（恒定不变） */
  position:relative;height:calc(100vh - 64px);min-height:540px}
.bubble-name{display:none}
/* 漫画对话框：白底细边 + 斜尾巴指向星云 */
.speech{position:absolute;top:14px;left:12px;z-index:7;
  background:#fff;border:1px solid #c9bfd8;border-radius:14px;
  padding:9px 16px;max-width:320px;box-shadow:2px 2px 0 rgba(90,70,120,.10);
  transition:transform .18s ease}
/* 想法跳出小动画：轻微回弹 */
.speech.pop{animation:poppop .24s ease}
@keyframes poppop{0%{transform:scale(.94)}55%{transform:scale(1.04)}100%{transform:scale(1)}}
/* 台词 / 想法双面板：共用气泡位，交替显隐 */
.speech-pane{display:flex;flex-direction:column;gap:3px}
/* 尾巴：旋转方块藏一半进框内，露出的尖角指向右下的星云 */
.speech::after{content:"";position:absolute;right:24px;bottom:-7px;width:13px;height:13px;
  background:#fff;border-right:1px solid #c9bfd8;border-bottom:1px solid #c9bfd8;
  transform:rotate(45deg)}
.speech-line{font-size:13.5px;line-height:1.7;color:var(--ink);font-family:"Noto Serif SC","Songti SC",serif;
  letter-spacing:.5px;display:block;transition:opacity .3s}
/* 内心想法：薄荷色 + 左侧竖线，一眼和台词区分开 */
.thought-line{display:block;font-size:13px;line-height:1.7;color:#4c7a6c;
  font-family:"Noto Serif SC","Songti SC",serif;letter-spacing:.5px;
  border-left:3px solid var(--mint);padding-left:8px}
.thought-meta{font-size:10px;line-height:1.4;color:var(--muted);letter-spacing:.2px}
/* 亲密度等级徽章 */
.speech-badge{position:absolute;top:-11px;left:-8px;font-size:10.5px;line-height:1;
  background:linear-gradient(120deg,var(--pri),var(--pri-d));color:#fff;
  padding:3px 8px;border-radius:99px;letter-spacing:1px;
  box-shadow:0 2px 6px rgba(139,111,176,.35)}
.chat{position:absolute;z-index:5;left:0;right:0;
  /* top 用「可视高度」前馈收紧，不用状态类阶跃。
     键盘弹起时可用高度骤降，若 top 仍固定 132px，小屏（360×640）上 .chat
     只剩 120px，而底栏（资金栏 76 + 输入框 73 = 149px）根本装不下，
     输入框必然被挤出可视区（实测被遮 72px）。

     为什么用 --vv-h（JS 写入 visualViewport.height）而不是 100%：
     100% 是 .stage 高度，安卓部分浏览器键盘弹起时 stage（100dvh）并不跟着缩，
     用它算出来永远是 132px，等于没收敛。--vv-h 是真实的可见高度，一定会缩。

     为什么这也算「前馈」不会振荡：--vv-h 来自键盘，不来自 --kb-offset，
     不构成「抬升→位置变→重新算抬升」的闭环，所以不会来回抖。

         可视 640px（常态）      → min(132, 340) = 132
         可视 400px（键盘抬起）  → min(132, 100) = 100
         可视 340px（小屏+键盘） → min(132,  40) = 40

     ⚠ 必须抽成 --chat-top 给 bottom 复用。若 bottom 里仍写 var(--top)，
     用的就是没收紧的 132px，兜底值会算成 204px（正确值 296px），
     抬升被凭空砍掉 92px —— 实测输入框被遮 87px 就是这个原因。 */
  /* ⚠⚠ v1.12.10：top 的 40px 下限在极小屏上同样有害，必须再压一道。
     反例（秘书台，可视 280px）：--chat-top 的 max(40px, 280-300=-20) 取 40，
     .chat 从 40px 开始；但 .foot 是 flex:none（底栏不允许被压扁），
     实测 185px。聊天区可用 = 280-40 = 240px，底栏就要 185px，加消息区根本不够，
     输入框被挤到 356px，超出可视区 76px —— 用户看到的就是「输入框又没了」。
     修法：top 再加一条上限约束「可视高度 - 底栏保底」，让框顶在极小屏主动上移，
     把 40px 那道留白也让出来（上移到 0 也不许再低）。
       可视 844 → min(132, 544, 844-150=694) = 132（不变）
       可视 340 → min(132,  40,      190)    =  40（不变）
       可视 280 → min(132, max(0,-20)=0, 130)=   0（让出 40px）
     --chat-foot 是底栏保底高度：chat 页无资金栏，秘书台有（实测 185 ≈ 150+余量）。 */
  --chat-foot:150px;
  --chat-top:min(var(--top),
                 max(0px, calc(var(--vv-h, 100vh) - 300px)),
                 max(0px, calc(var(--vv-h, 100vh) - var(--chat-foot))));
  top:var(--chat-top);
  /* bottom 用 min() 兜底：抬升再多也要给聊天区留够高度，防止聊天区被压成一条缝。
     --chat-min 的来历：底栏实测高度 = 资金栏 76 + 输入框 73 = 149px，
     再加一行消息约 40px，故取 190px。小于这个值输入框就会被 .inner 的
     圆角裁切边缘压掉（v1.12.2 曾用 120px，实测输入框被挤出 72px）。

     ⚠⚠ v1.12.10：--chat-min 必须跟着「可视高度」一起缩，否则它在极小屏上是反向的。
     反例（秘书台，可视 280px）：底线算 max(0, 280 - 40 - 190) = 50px，
     于是 .chat 被抬到 top:40 / bottom:50 → 只剩 190px，而该页 .foot 实测 185px
     （资金栏 76 + 输入框 73 + 弹窗待办条 36），加一行消息就装不下，
     输入框被顶到 356px（可视区只到 280px）= 又被键盘盖住。
     兜底的本意是「别把聊天区压成一条缝」，但当底栏本身都比它高时，
     这个底线就成了帮凶。改为 min(190px, 可视高度 - 底栏保底 - 一行消息)：
       可视 844 → min(190, 844-150-40=654) = 190  （不变，大屏行为完全一致）
       可视 504 → min(190, 314)              = 190  （不变）
       可视 340 → min(190, 150)              = 150  （秘书台底栏刚好放得下）
       可视 280 → min(190,  90)              =  90  （让出 40px 给消息区，不硬顶） */
  --chat-min:min(190px, max(90px, calc(var(--vv-h, 100vh) - 190px)));
  bottom:min(var(--kb-offset,0px), max(0px, calc(100% - var(--chat-top) - var(--chat-min))));
  background:rgba(255,255,255,.9);backdrop-filter:blur(10px);
  border:1px solid var(--line);border-radius:22px;box-shadow:var(--shadow);overflow:visible;
  display:flex;flex-direction:column}
.chat .inner{border-radius:22px;overflow:hidden;display:flex;flex-direction:column;height:100%;
  background:rgba(255,255,255,.9)}
/* 身体层：相对框顶固定偏移，头顶贴导航栏下，身体沉到框后被遮 */
.hero-figure{position:absolute;top:calc(var(--top) - var(--handgap));right:0;
  width:var(--figw);z-index:4;pointer-events:auto;cursor:pointer;
  filter:drop-shadow(0 16px 24px rgba(90,70,120,.20));
  outline:none;
  -webkit-tap-highlight-color:transparent;
  -webkit-touch-callout:none;
  -webkit-user-select:none;user-select:none;
  transform-origin:bottom center;
  transition:transform .22s cubic-bezier(.34,1.56,.64,1)}
/* 点一下轻微放大（底部锚定不动，向上生长），松手回弹 */
.hero-figure.pop{transform:scale(1.07)}
/* 半身像皮肤：无手搭框，以底边锚定（沉入框顶 23px，与全身一致），
   宽度与全身皮肤统一（figw），按实际宽高比 --pratio 换算高度，绝不放大 */
.hero-figure.portrait{top:auto;right:0;z-index:4;pointer-events:auto;cursor:pointer;
  bottom:calc(100% - var(--top) - 23px);
  width:min(var(--figw), calc((var(--top) - 6px) / var(--pratio, 1)));
  filter:drop-shadow(0 16px 24px rgba(90,70,120,.20));
  -webkit-tap-highlight-color:transparent;
  -webkit-touch-callout:none;
  -webkit-user-select:none;user-select:none;
  transform-origin:bottom center;
  transition:transform .22s cubic-bezier(.34,1.56,.64,1)}
.hero-figure.portrait.pop{transform:scale(1.07)}
/* 恐惧档（好感<200）：气泡轻颤 + 人物蒙一层冷灰，表现怯怯的 */
body.fear .speech{animation:shiver .9s infinite}
@keyframes shiver{0%,100%{transform:translateX(0)}25%{transform:translateX(-1.5px)}75%{transform:translateX(1.5px)}}
body.fear .hero-figure{filter:drop-shadow(0 16px 24px rgba(90,70,120,.20)) saturate(.72) brightness(.97)}
/* 手部层：始终以「框顶 - 手高 + 搭入量」锚定，手指咬合框沿，永不位移 */
.hero-hands{position:absolute;top:calc(var(--top) - var(--handh) + var(--overlap));right:0;
  width:var(--figw);z-index:6;pointer-events:none}
.chat-head{display:flex;align-items:center;gap:10px;padding:11px 18px;
  border-bottom:1px solid var(--line);background:linear-gradient(120deg,rgba(139,111,176,.08),rgba(127,174,159,.08))}
.av{flex:none}
.av img{width:52px;height:52px;border-radius:50%;object-fit:cover;border:2.5px solid var(--pri-soft);box-shadow:0 2px 8px rgba(139,111,176,.2)}
.who{flex:1;min-width:0}
.who b{font-size:17px;display:block;line-height:1.25;letter-spacing:2px}
.who span{font-size:12px;color:var(--muted)}
.st{font-size:12px;color:var(--mint);display:flex;align-items:center;gap:6px;white-space:nowrap}
.st::before{content:"";width:7px;height:7px;border-radius:50%;background:var(--mint)}
.msgs{flex:1;overflow-y:auto;padding:24px 20px;display:flex;flex-direction:column;gap:14px}
.msg{display:flex;gap:10px;align-items:flex-start}
.msg.boss{flex-direction:row-reverse}
.ava{width:36px;height:36px;border-radius:50%;flex:none;background:linear-gradient(135deg,var(--pri),var(--pri-d));
  color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px;overflow:hidden}
.ava img{width:100%;height:100%;object-fit:cover}
.ava.bossava{background:linear-gradient(135deg,#c2a15c,#a9863d)}
.msg .col{max-width:76%;display:flex;flex-direction:column;gap:4px}
.msg.boss .col{align-items:flex-end}
.bub{background:#fff;border:1px solid var(--line);padding:10px 15px;border-radius:4px 16px 16px 16px;
  font-size:13px;line-height:1.7;white-space:pre-wrap;box-shadow:0 2px 8px rgba(90,70,120,.06);word-break:break-word}
.msg.boss .bub{background:linear-gradient(120deg,var(--pri),var(--pri-d));color:#fff;border:0;
  border-radius:16px 4px 16px 16px}
.bub.report{background:var(--mint-soft);border-color:#d3e6df;max-width:100%;font-size:12.5px}
.msg .t{font-size:11px;color:#b9b2c2;padding:0 4px}
.typing{display:flex;gap:5px;padding:14px 18px}
.typing i{width:7px;height:7px;border-radius:50%;background:var(--pri);opacity:.4;animation:tk 1.1s infinite}
.typing i:nth-child(2){animation-delay:.18s}.typing i:nth-child(3){animation-delay:.36s}
@keyframes tk{0%,100%{transform:translateY(0);opacity:.35}45%{transform:translateY(-5px);opacity:.9}}
.daysep{text-align:center;font-size:12px;color:#b9b2c2;margin:6px 0}
.chips{display:flex;gap:8px;padding:10px 16px;overflow-x:auto;border-top:1px solid var(--line);scrollbar-width:none}
.chips::-webkit-scrollbar{display:none}
.chip{flex:none;font-size:13px;color:var(--pri-d);background:var(--pri-soft);border:1px solid var(--line);
  padding:7px 15px;border-radius:99px;cursor:pointer;transition:.2s;user-select:none}
.chip:hover{background:var(--pri);color:#fff}
.chip.hot{background:linear-gradient(120deg,#fdf0f1,#f9e3e6);color:#a94b58;border-color:#f0d4d8}
/* 资金栏：铜钱 + 好感 + 操作按钮 */
.wallet{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:8px 14px;border-top:1px solid var(--line);background:linear-gradient(120deg,rgba(194,161,92,.08),rgba(139,111,176,.06));flex-wrap:wrap}
.wallet-info{display:flex;gap:14px;font-size:13px}
.w-coins b,.w-favor b{font-weight:600}
.w-coins{color:#a9863d}
.w-favor{color:#a94b58}
.wallet-acts{display:flex;gap:8px}
.wallet-acts button{font:inherit;font-size:12.5px;border:1px solid var(--line);background:#fff;
  color:var(--pri-d);padding:5px 12px;border-radius:99px;cursor:pointer;transition:.2s;white-space:nowrap}
.wallet-acts button:hover{background:var(--pri);color:#fff;border-color:var(--pri)}
/* 礼物面板 */
.foot{position:relative;flex:none}
.giftbar{position:absolute;left:0;right:0;bottom:calc(100% + 8px);z-index:5;
  border:1px solid var(--line);border-radius:16px;
  padding:10px 14px;background:#fbf9fd;box-shadow:0 10px 28px rgba(90,70,120,.18);
  animation:giftin .18s ease}
@keyframes giftin{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.gift-title{display:flex;align-items:center;justify-content:space-between;gap:10px;
  font-size:12px;color:var(--muted);margin-bottom:8px}
.gift-close{border:none;background:none;font-size:16px;line-height:1;color:var(--muted);cursor:pointer;padding:2px 6px;border-radius:8px}
.gift-close:hover{background:var(--pri-soft);color:var(--pri-d)}
.gift-title b{color:var(--pri-d)}
.gift-list{display:flex;gap:10px;overflow-x:auto;scrollbar-width:none}
.gift-list::-webkit-scrollbar{display:none}
.gift{flex:none;width:88px;text-align:center;background:#fff;border:1px solid var(--line);
  border-radius:14px;padding:10px 6px;cursor:pointer;transition:.2s;user-select:none}
.gift:hover{transform:translateY(-3px);border-color:var(--pri);box-shadow:0 6px 14px rgba(139,111,176,.18)}
.gift.soldout{opacity:.35;pointer-events:none;filter:grayscale(1)}
.gift-icon{font-size:30px;line-height:1.2}
.gift-name{font-size:12.5px;margin:4px 0 2px;color:var(--ink)}
.gift-price{font-size:11px;color:var(--muted)}
.input{display:flex;gap:10px;padding:14px 16px;border-top:1px solid var(--line);background:#fff}
.input input{flex:1;border:1.5px solid var(--line);border-radius:99px;padding:11px 20px;font:inherit;outline:0}
.input input:focus{border-color:var(--pri)}
.input button{border:0;background:linear-gradient(120deg,var(--pri),var(--pri-d));color:#fff;
  padding:0 28px;border-radius:99px;font:inherit;cursor:pointer;letter-spacing:3px}
.plus{flex:none;width:42px;border:1.5px solid var(--line);border-radius:50%;background:#fff;
  color:var(--pri-d);font-size:20px;cursor:pointer}
.plus:hover{border-color:var(--pri)}
.plus.on{border-color:var(--pri);background:var(--pri-soft);color:var(--pri-d)}
/* 快速指令弹出层：由左下角 ＋ 触发，浮在消息区上方 */
.quickpop{position:absolute;left:10px;right:10px;bottom:calc(100% + 8px);z-index:6;
  background:#fff;border:1px solid var(--line);border-radius:16px;padding:10px 12px;
  box-shadow:0 10px 28px rgba(90,70,120,.18);animation:giftin .18s ease}
.quickpop-hd{display:flex;align-items:center;justify-content:space-between;gap:10px;
  font-size:12px;color:var(--muted);margin-bottom:8px}
.quick-close{border:none;background:none;font-size:16px;line-height:1;color:var(--muted);cursor:pointer;padding:2px 6px;border-radius:8px}
.quick-close:hover{background:var(--pri-soft);color:var(--pri-d)}
.quick-list{display:flex;flex-wrap:wrap;gap:8px}
.quick-list .chip{flex:1 1 calc(50% - 8px);justify-content:center;text-align:center}

/* ---------- 后台 ---------- */
.admin{max-width:920px;margin:26px auto 60px;padding:0 18px}
.admin>h1{font-size:26px;letter-spacing:4px;display:flex;align-items:center;gap:12px;margin-bottom:24px}
.admin>h1 img{width:40px;height:40px;border-radius:50%;border:2px solid var(--pri-soft)}
.panel{background:var(--card);border:1px solid var(--line);border-radius:18px;
  padding:24px 26px;margin-bottom:20px;box-shadow:0 4px 14px rgba(90,70,120,.06)}
.panel h2{font-size:17px;margin:0 0 14px;display:flex;align-items:center;gap:8px;letter-spacing:1px}
.panel p{margin-bottom:12px;font-size:14.5px;color:var(--ink)}
input,select,textarea{font:inherit;font-size:14px;padding:9px 12px;border-radius:10px;
  border:1.5px solid var(--line);background:#fdfcfe;color:var(--ink);outline:0}
input:focus,select:focus,textarea:focus{border-color:var(--pri)}
button.primary,.panel>button{font:inherit;font-size:14px;background:linear-gradient(120deg,var(--pri),var(--pri-d));
  color:#fff;border:0;border-radius:10px;padding:10px 26px;cursor:pointer;letter-spacing:2px;transition:.2s}
button.primary:hover,.panel>button:hover{opacity:.9;transform:translateY(-1px)}
table{width:100%;border-collapse:collapse;font-size:13.5px}
th,td{padding:9px 8px;border-bottom:1px solid var(--line);text-align:left}
th{color:var(--muted);font-weight:500;font-size:12.5px}
tr:hover td{background:var(--pri-soft)}
pre{white-space:pre-wrap;background:var(--pri-soft);padding:14px 16px;border-radius:12px;
  font-size:13px;line-height:1.8;font-family:inherit}
.ok{color:#3d9c7d;font-size:13px}
code{background:var(--pri-soft);padding:1px 6px;border-radius:6px;font-size:12.5px}

/* ---------- 页脚 ---------- */
footer{text-align:center;color:#b9b2c2;font-size:13px;padding:30px 0 40px;letter-spacing:2px}
footer a{color:var(--pri);margin-left:8px}

/* ---------- 移动端 ---------- */
@media (max-width:640px){
  /* 手机端导航高 54px，弹窗让位要跟着改，否则会差 6px */
  :root{--nav-h:calc(54px + env(safe-area-inset-top,0px))}
  .wrap{padding:0 16px}
  .nav-in{gap:16px;height:54px}
  /* 窄屏导航：手机宽度放不下全部菜单（390px 时「后台」被切掉，320px 连「聊天」都看不到）。
     不再硬挤，改成可横滑 + 右侧渐隐暗示「还有内容」。
     nav 本身 overflow:hidden 是为了不让渐隐遮罩被 sticky 容器裁掉，
     横滑交给内部 .nav-in。 */
  nav{overflow:hidden}
  /* 手机端菜单压紧：393px 屏上原本内容宽 413px，最右边当前页被渐隐吃掉一半，
     用户看到的是「秘书…」被切断，观感等于「菜单不见了」。
     收紧 gap / 字号 / logo 后 393px 能一屏放下，不用横滑也知道有哪些页。 */
  .nav-in{gap:9px;overflow-x:auto;overflow-y:hidden;
    scrollbar-width:none;-webkit-overflow-scrolling:touch;
    padding-right:10px;
    /* 渐隐只留 10px 且保留 40% 不透明：360px 屏实测过 14px 全透明会把
       「后台」的「台」字削掉一半，看着像菜单缺项。现在宁可让最后一项
       稍贴边，也不让任何字「消失」。 */
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 10px),rgba(0,0,0,.4));
    mask-image:linear-gradient(90deg,#000 calc(100% - 10px),rgba(0,0,0,.4))}
  .nav-in::-webkit-scrollbar{display:none}
  .lk{font-size:12.5px;padding:6px 0;flex:0 0 auto}
  .logo{flex:0 0 auto;font-size:13.5px;gap:7px}
  .navface{width:30px;height:30px}
  .hero{padding:40px 0 30px}
  .hero h1{font-size:30px;letter-spacing:5px}
  .seal{display:none}
  /* 聊天窗口移动端重排：贴边全宽 + 抬高内容区 + 大气泡大字，读消息不再费劲 */
  .desk{padding:0 8px 12px}
  .stage{
    --top:132px;      /* 框顶上台：头顶贴导航栏下，人像放大后框顶相应下移 */
    --figw:110px;     /* 人像放大：手机端星云形象更清晰醒目 */
    --overlap:3px;    /* 手指搭压深度：恒定不变 */
    height:calc(100vh - 58px);min-height:480px}
  .bubble-name{display:none}
  .speech{top:12px;left:6px;max-width:250px;padding:6px 12px;border-radius:12px}
  .speech-line{font-size:12px}
  .thought-line{font-size:11.5px}
  .thought-meta{font-size:9px}
  .chat{border-radius:16px}
  .chat-head{padding:10px 14px}
  .av img{width:44px;height:44px}
  .who b{font-size:15px}
  .msgs{padding:14px 12px;gap:12px}
  .msg{gap:8px}
  .ava{width:32px;height:32px;font-size:13px}
  .msg .col{max-width:92%}
  .bub{font-size:15px;line-height:1.75;padding:12px 16px}
  .bub.report{font-size:13.5px}
  .msg .t{font-size:10.5px}
  .chips{padding:8px 12px}
  .chip{font-size:12.5px;padding:6px 13px}
  .wallet{padding:6px 12px;gap:8px}
  .wallet-info{font-size:12.5px;gap:10px}
  .wallet-acts button{padding:5px 10px;font-size:12px}
  .input{padding:12px 14px;gap:8px}
  .input input{padding:13px 16px;font-size:16px}
  .input button{padding:0 20px;letter-spacing:2px}
  .plus{width:40px}
  .detail{padding:30px 22px;margin:20px 14px 40px}
  .detail h1{font-size:22px}
  .content p{font-size:15.5px}
  .admin{margin:18px auto 40px}
  .panel{padding:16px;border-radius:14px}
  .panel p{font-size:13.5px}
  input,select,textarea{width:100%;box-sizing:border-box}
  .panel>button{width:100%}
  table{display:block;overflow-x:auto;white-space:nowrap}
}

/* ============================================================
   聊天 UI 精修：小气泡、小字号、全尺寸防溢出
   ============================================================ */
html,body{width:100%;max-width:100%;overflow-x:hidden}
/* ⚠ v1.12.7：body 高度改用 --vv-h（JS 写入的真实可视高度）驱动，而非 100dvh。
   真机安卓/微信 XWeb 内核在软键盘弹起时 100dvh 常常【不缩】（已知兼容 bug），
   导致 body 高度不变、输入框留在原位被键盘盖住 = 「输入框没了」。
   --vv-h 由 kb-adapt.js 在初始化 + vv.resize 时实时写入 visualViewport.height，
   键盘弹起时它会真实缩小 → body 跟着缩 → 输入框自动收到可视区底部，
   不依赖事后测量抬升（前馈而非反馈）。无 JS 时回退 100dvh，零副作用。 */
/* ⚠⚠ v1.12.10：必须同时把 min-height 也改掉！
   上面 body{min-height:100vh}（第 20 行）会【压过】height。
   软键盘弹起时 100vh 在真机上不缩（它指理想视口，不含键盘），
   于是 body 的 min-height 锁死在整屏高 → height:var(--vv-h) 完全白写，
   输入框依旧被键盘盖住。这正是 v1.12.7 修了 height 却仍然「输入框没了」的真因。
   修法：min-height 也跟 --vv-h 走，并加 !important 防止被主题的 min-height:100vh 再次覆盖。 */
html.chat-html,body.chat-page{
  height:100vh;height:var(--vv-h,100dvh);
  min-height:0 !important;
  max-height:var(--vv-h,100dvh);
  overflow:hidden}
/* 关键：body 改成 flex 纵向布局，让 .desk/.stage 自动吃掉「导航栏 + 公告栏」之外的全部高度。
   原来用 height:calc(100dvh - 84px) 硬编码，一旦有公告栏就多出 40px 把输入框挤出屏幕。 */
body.chat-page{display:flex;flex-direction:column}
/* ⚠ nav 必须 flex:none。
   body.chat-page 是 flex 纵向容器，nav 默认 flex-shrink:1 会被压缩；
   而手机上 nav 又设了 overflow:hidden（为了不被 sticky 裁掉渐隐遮罩），
   两者一叠加 → 空间紧张时 nav 被压扁、内容被裁光 → 用户看到「顶部导航没了」，
   而它反复被压缩/回弹，视觉上就是「一下大一下小」。

   ⚠⚠ v1.12.5：聊天页 nav 再也不能用 sticky + backdrop-filter。
   ① 页面本身 overflow:hidden 永不滚动，sticky 毫无作用，纯引雷；
   ② 「position:sticky + backdrop-filter(-webkit-blur)」是 WebKit/XWeb 内核
      著名的渲染层消失 bug 高发组合——导航占位还在、内容/背景间歇性不渲染，
      用户看到的就是「导航忽隐忽现 / 干脆没有」，且与滚动、键盘、图层重建
      时机相关，桌面模拟永远复现不了，真机却次次中招。
   修法：聊天页 nav 改 relative（就在文档流顶部，不需要吸附），
   关掉 blur 换实底，translateZ(0) 强制独立合成层——三层保险，
   让导航在结构上「不可能消失」。 */
body.chat-page nav{flex:0 0 auto;position:relative;transform:translateZ(0);
  background:rgba(255,255,255,.95);backdrop-filter:none;-webkit-backdrop-filter:none}
body.chat-page #noticeBar{flex:0 0 auto}
body.chat-page .desk{flex:1 1 auto;display:flex;flex-direction:column;min-height:0}
body.chat-page .stage{flex:1 1 auto;height:auto;min-height:0}
.desk{width:100%;max-width:760px;min-width:0}
.stage{width:100%;min-width:0;height:calc(100vh - 84px);height:calc(100dvh - 84px);min-height:0}
.chat,.chat .inner,.msgs,.msg,.msg .col,.input,.wallet,.foot{min-width:0;max-width:100%}
/* ⚠ 这里只写 min-height，不要再写 bottom。
   上面 .chat 的 bottom 已经用了 min() 兜底（偏移最多压到还剩 260px）；
   若在这里再写一次 bottom:var(--kb-offset)，同优先级后者覆盖前者，
   兜底就完全失效——这正是 v1.12.0 之前「键盘弹起后聊天区被压扁」的根因。 */
.chat{min-height:0}
.msgs{min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}
.msg{width:100%;gap:8px}
.msg .col{display:flex;flex-direction:column;gap:3px;min-width:0}
.msg.nebula .col{max-width:min(68%,560px)}
.msg.boss .col{max-width:76%}
.msg .ava{width:30px;height:30px;font-size:12px}
.msg.nebula .bub{padding:8px 12px;border-radius:3px 12px 12px 12px;font-size:12px;line-height:1.65;box-shadow:0 1px 4px rgba(90,70,120,.045)}
.msg.nebula .bub.report{font-size:11.5px;padding:9px 12px}
.msg.boss .bub{font-size:13px;padding:9px 13px}
.bub{max-width:100%;overflow-wrap:anywhere;word-break:break-word;white-space:pre-wrap}
.msg .t{font-size:10px}
.typing{padding:10px 12px}
.input{width:100%;flex:none}
.input input{width:0;min-width:0;flex:1 1 auto;max-width:100%}
.input button,.plus{flex:0 0 auto}
.wallet,.wallet-info,.wallet-acts{min-width:0;max-width:100%}
.quickpop,.giftbar{max-width:calc(100% - 20px);min-width:0}
.speech{max-width:min(320px,calc(100% - var(--figw) - 30px));overflow-wrap:anywhere}
.hero-figure,.hero-hands{max-width:58%;height:auto}
@media(max-width:640px){
  .desk{padding:0 8px 8px;max-width:100vw}
  .stage{height:calc(100vh - 62px);height:calc(100dvh - 62px);min-height:0}
  .speech{max-width:calc(100% - var(--figw) - 20px)}
  .msgs{padding:12px 10px;gap:10px}
  .msg.nebula .col{max-width:78%}
  .msg.boss .col{max-width:82%}
  .msg.nebula .bub{font-size:12px;line-height:1.65;padding:7px 10px}
  .msg.nebula .bub.report{font-size:11.5px;padding:8px 10px}
  .msg.boss .bub{font-size:13px;padding:8px 12px}
  .msg .ava{width:28px;height:28px;font-size:11px}
  .chat-head{padding:9px 12px}
  .av img{width:40px;height:40px}
  .input{padding:10px;gap:7px}
  .input input{padding:11px 12px;font-size:16px}
  .input button{padding:0 15px;letter-spacing:1px}
  .plus{width:36px}
}
@media(max-height:600px) and (min-width:641px){
  .stage{--top:130px;--figw:110px;height:calc(100dvh - 70px)}
  .chat-head{display:none}
  .msgs{padding:10px 14px}
  .input{padding:8px 14px}
  .input input{padding:8px 16px}
}
/* ============================================================
   软键盘状态类（由 /static/js/kb-adapt.js 维护）

   ⚠ 这一块只允许做「不会引起跳变」的事。教训：
   v1.12.1 这里写过 html.kb-open .stage{--top:92px;--figw:96px}，
   本意是键盘态收紧顶距省空间。结果每次 kb-open 切换，
   聊天框就上跳 40px、人像从 110px 缩到 96px —— 用户的原话是
   「一下大一下小」。而且它会让 JS 的「量出被遮多少」反馈回路自激：
   抬升 → --top 变小 → 输入框又上移 → 判定抬多了 → 回收 → --top 变回 →
   输入框又下移 → 判定被遮 → 再抬…… 无限振荡。

   所以：键盘态只调 --chat-min（放开下限，不影响视觉），
   --top / --figw / display 一律不动。省空间交给 --kb-offset 一个变量完成。
   ============================================================ */
/* ⚠ v1.12.10：这里原来写 --chat-min:190px（固定值），会把上面按可视高度
   自适应算出来的 --chat-min 直接覆盖掉，等于键盘一弹起就又回到「硬顶 190px」，
   极小屏（秘书台 280px）输入框必然被顶出可视区。
   现在改成同样自适应，并且键盘态里 .wallet/.ovbar 会收起（见下方规则），
   底栏只剩输入框 73px，所以底栏保底取 80px 就够。 */
html.kb-open{--chat-min:min(190px, max(90px, calc(var(--vv-h, 100vh) - 190px)))}

/* ⚠⚠ v1.12.10：键盘弹起时收起「资金栏 / 待办条」。
   实测秘书台底栏 = 资金栏 76 + 待办条 32 + 输入框 73 = 184px。
   可视 280px 时，聊天区最多 280px，184px 底栏 + 消息区必然溢出 → 输入框被盖。
   这两块都不是输入必需件，键盘态收起最符合真实使用：用户正在打字，
   不需要一边看余额一边看待办；收起后底栏只剩 73px，任何屏都能放下。
   为什么安全：kb-open 只在真弹键盘时出现，且不改变 --top/--figw/display:none
   之外的结构，不会像 v1.12.1 那样引起「一下大一下小」。 */
html.kb-open body.chat-page .wallet,
html.kb-open body.chat-page .ovbar{display:none !important}
/* ⚠ 必须 !important：.ovbar 在模板里带 inline style="display:none"，
   JS 有弹窗待办时改成 style.display='flex'。内联样式优先级高于任何选择器，
   不加 !important 这条规则对它完全无效（实测资金栏收起了、待办条还杵在那儿 32px）。

   ⚠⚠ 同类问题：.speech（角色台词气泡）是 .stage 里的绝对定位元素，
   键盘弹起时它会压在输入框上方。实测秘书台可视 280px 时
   elementFromPoint 在第 (195,235) 命中的是 SPAN.thought-line，
   正好盖住输入框中心 → 点不进去。
   它是纯装饰气泡，键盘态隐藏不影响布局（绝对定位不占流），也不会引起跳变。 */
html.kb-open .speech{display:none !important}

/* 安卓浏览器在键盘弹起时会把整个 layout viewport 向上平移（visualViewport.offsetTop > 0），
   nav（聊天页为 relative，普通页为 sticky）跟着文档一起被推出可视区顶部
   → 用户看到「顶部导航也没了」。
   这里按 JS 量到的偏移量把 nav 视觉平移回屏幕顶部。
   transform 不影响 stage 布局；translate3d 保持独立合成层不被降级。
   只作用于聊天页：普通页面 nav 是 sticky，滚动吸附本来就能工作，不该被改。 */
html.kb-pan body.chat-page nav{transform:translate3d(0,var(--kb-pan,0px),0)}

/* 导航永远压在最上层。
   模板里的全屏弹窗用 z-index:9999 盖内容区，若导航只有 50 就会被盖住；
   弹窗已改为从导航下方开始，这里再把 z-index 抬到 10000 双保险——
   万一以后有人加新的全屏层，导航也不会「消失」。 */
nav{z-index:10000}

