/* ============================================================
 * 门户样式 —— 首页、控制台、隐私政策共用
 *
 * 从 index.html 里抽出来的：两个页面要用同一份样式，
 * 留在 HTML 里就得复制一遍，改一处忘一处是迟早的事。
 *
 * 隐私政策页有意保留自己的一小份样式 —— 那是个内容页，
 * 排版诉求和门户不一样，硬合并反而互相牵制。
 * ============================================================ */

/*
 * 主题色 #66CCFF（天蓝）。
 *
 * 注意：#66CCFF 很浅，**直接当文字用对比度只有 1.7:1，基本看不清**。
 * 所以拆成一个色阶，而不是到处硬套同一个值：
 *   --accent       原色，用在底色、边框、进度条这些"面积大不靠它读字"的地方
 *   --accent-deep  稍深一档，给按钮底 + 深色文字，比纯浅蓝有重量感
 *   --accent-ink   深蓝，**当文字用**（标签、链接、小字）
 *   --accent-soft  极浅底，给标签和提示条的背景
 */
:root {
  --bg: #f7f7f5;
  --card: #ffffff;
  --line: rgba(0,0,0,.10);
  --line-strong: rgba(0,0,0,.22);
  --text: #1c1c1a;
  --muted: #6b6b66;
  --accent: #66ccff;
  --accent-deep: #2fa9e0;
  --accent-ink: #0b5f92;
  --accent-soft: #e8f7ff;
  --accent-rgb: 102, 204, 255;
  --good: #0f6e56;
  --good-soft: #e1f5ee;
  --warn: #854f0b;
  --warn-soft: #faeeda;
  --radius: 12px;
  /*
   * 控制台视图区之外、从视口高度里吃掉的那一截：
   *   导航 59 + wrap 上留白 28 + 页脚 22+19 + wrap 下留白 24 ≈ 152
   * 视图区要靠它算"最多能长到多高"，窄屏下导航矮一点、这个数也跟着变。
   * 做成变量是因为两处要用同一个数（.console-main 和 640px 那条媒体查询），
   * 各写各的迟早改一处忘一处。
   *
   * **注意它必须和下面 `body.console` 那两条留白改写保持一致** ——
   * 控制台页的上下留白比别的页收了一档（见 .console .wrap / .console footer），
   * 这里就是按收窄后的值算的。改一处不改另一处，页面就会多出滚动条。
   */
  --console-chrome: 156px;
}
  * { box-sizing: border-box; }
  body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font: 400 14px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
    -webkit-text-size-adjust: 100%;
  }
  .wrap { max-width: 1100px; margin: 0 auto; padding: 28px 20px 60px; }
  header { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; margin-bottom: 6px; }
  h1 { font-size: 19px; font-weight: 500; margin: 0; letter-spacing: .2px; }
  .sub { color: var(--muted); font-size: 13px; }
  .topline { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; }
  .pill {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12px; padding: 3px 10px; border-radius: 999px;
    background: var(--good-soft); color: var(--good);
  }
  .pill.warn { background: var(--warn-soft); color: var(--warn); }
  .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

  section { margin-top: 30px; }
  h2 { font-size: 14px; font-weight: 500; margin: 0 0 4px; }
  .hint { color: var(--muted); font-size: 12.5px; margin: 0 0 14px; }

  .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }

  a.card, .card {
    display: block; text-decoration: none; color: inherit;
    background: var(--card); border: 1px solid var(--line);
    border-radius: var(--radius); padding: 16px;
    transition: border-color .15s, transform .15s;
  }
  a.card:hover { border-color: var(--line-strong); transform: translateY(-1px); }
  .card .title { font-weight: 500; display: flex; align-items: center; gap: 8px; }
  .card .desc { color: var(--muted); font-size: 12.5px; margin-top: 4px; }
  .tag {
    font-size: 11px; padding: 1px 7px; border-radius: 999px;
    background: var(--accent-soft); color: var(--accent-ink); font-weight: 400;
  }
  .tag.direct { background: var(--good-soft); color: var(--good); }
  .tag.muted { background: #f1efe8; color: #5f5e5a; }

  .icon { width: 18px; height: 18px; flex: 0 0 18px; }

  #login { max-width: 360px; margin: 60px auto; }
  #login .card { padding: 22px; }
  label { display: block; font-size: 12.5px; color: var(--muted); margin: 14px 0 5px; }
  input {
    width: 100%; padding: 9px 11px; font: inherit; font-size: 14px;
    border: 1px solid var(--line-strong); border-radius: 8px; background: #fff; color: var(--text);
  }
  input:focus { outline: 2px solid rgba(var(--accent-rgb),.65); outline-offset: -1px; border-color: var(--accent-deep); }
  /*
   * 主按钮：浅蓝底 + 深蓝字。
   * 用 #66CCFF 配白字对比度只有 1.9:1，所以这里反过来配深色字 ——
   * 既保住了主题色，也读得清。
   */
  button {
    width: 100%; margin-top: 18px; padding: 10px; font: inherit; font-weight: 500;
    background: var(--accent); color: #073a58;
    border: 1px solid rgba(0,0,0,.06); border-radius: 8px; cursor: pointer;
    transition: background .15s;
  }
  button:hover:not(:disabled) { background: var(--accent-deep); color: #062c42; }
  button:disabled { opacity: .55; cursor: default; }
  .err { color: #a32d2d; font-size: 12.5px; margin-top: 12px; min-height: 18px; }
  .hidden { display: none !important; }

  code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    background: #f1efe8; padding: 1px 5px; border-radius: 5px; font-size: 12.5px;
  }
  footer { margin-top: 40px; color: var(--muted); font-size: 12px; }

  /* ---- 动态验证码 ---- */
  .otp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
  .otp-item { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px; position: relative; }
  .otp-name { font-weight: 500; font-size: 13.5px; padding-right: 20px; }
  .otp-issuer { color: var(--muted); font-size: 12px; min-height: 18px; }
  .otp-code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 25px; letter-spacing: 2px; font-weight: 500;
    margin: 8px 0 6px; color: var(--accent-ink);
    cursor: pointer; border-radius: 6px; padding: 2px 6px; margin-left: -6px;
    user-select: none;
  }
  .otp-code.expiring { color: #a32d2d; }
  .otp-bar { height: 3px; background: rgba(0,0,0,.08); border-radius: 2px; overflow: hidden; }
  .otp-bar i { display: block; height: 100%; background: var(--accent); }
  .otp-item .del {
    position: absolute; top: 8px; right: 8px; width: auto; margin: 0;
    padding: 2px 7px; border: 0; background: transparent;
    color: var(--muted); font-size: 16px; line-height: 1; cursor: pointer;
  }
  .otp-item .del:hover { color: #a32d2d; }
  .otp-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
  .otp-actions button { width: auto; margin: 0; padding: 7px 14px; font-size: 12.5px; }
  .btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--line-strong); }
  .lockbox { max-width: 400px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; }
  dialog { border: 1px solid var(--line-strong); border-radius: var(--radius); padding: 20px; max-width: 430px; width: calc(100% - 40px); background: var(--card); color: var(--text); }
  dialog::backdrop { background: rgba(0,0,0,.38); }
  dialog h3 { margin: 0 0 2px; font-size: 14px; font-weight: 500; }
  dialog .row { display: flex; gap: 10px; }
  dialog .row > div { flex: 1; }

  /* ---- 云盘 ---- */
  .quota { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; font-size: 12.5px; color: var(--muted); flex-wrap: wrap; }
  .quota b { color: var(--text); font-weight: 500; }
  .quota .bar { flex: 1; min-width: 120px; max-width: 320px; height: 6px; background: rgba(0,0,0,.08); border-radius: 3px; overflow: hidden; }
  .quota .bar i { display: block; height: 100%; background: var(--accent); }
  .quota .bar i.full { background: #a32d2d; }
  .drop {
    border: 1px dashed var(--line-strong); border-radius: var(--radius);
    padding: 22px; text-align: center; color: var(--muted); font-size: 13px;
    background: var(--card); cursor: pointer;
  }
  .drop.over { border-color: var(--accent-ink); background: var(--accent-soft); color: var(--accent-ink); }
  .drop b { color: var(--text); font-weight: 500; }
  .drop.over b { color: inherit; }
  ul.files { list-style: none; margin: 14px 0 0; padding: 0; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
  ul.files li { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--line); font-size: 13px; }
  ul.files li:last-child { border-bottom: 0; }
  ul.files .fname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  ul.files .fmeta { color: var(--muted); font-size: 12px; white-space: nowrap; }
  ul.files button.act {
    width: auto; margin: 0; padding: 4px 10px; font-size: 12px; line-height: 1.4;
    background: transparent; color: var(--accent-ink);
    border: 1px solid var(--line-strong); border-radius: 6px; cursor: pointer;
  }
  ul.files button.act.danger:hover { color: #a32d2d; border-color: #a32d2d; }
  .prog { margin-top: 12px; font-size: 12.5px; color: var(--muted); }
  .prog .bar { height: 4px; background: rgba(0,0,0,.08); border-radius: 2px; overflow: hidden; margin-top: 5px; }
  .prog .bar i { display: block; height: 100%; width: 0; background: var(--accent); }

  /* ============================================================
     顶部通栏导航
     未登录也显示 —— 这是门面，不是控制台的一部分
     ============================================================ */
  .nav {
    position: sticky; top: 0; z-index: 20;
    background: rgba(247,247,245,.88);
    backdrop-filter: saturate(180%) blur(12px);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    border-bottom: 1px solid var(--line);
  }
  .nav-inner {
    max-width: 1100px; margin: 0 auto; padding: 0 20px;
    height: 58px; display: flex; align-items: center; gap: 16px;
  }
  .brand {
    display: inline-flex; align-items: center; gap: 9px;
    text-decoration: none; color: inherit; font-weight: 500; font-size: 15px;
    flex: 0 0 auto;
  }
  .brand-icon {
    width: 26px; height: 26px; border-radius: 7px; flex: 0 0 26px;
    /* 浅蓝底配深蓝字，别用白字（对比度不够） */
    background: var(--accent); color: #073a58;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 12px; font-weight: 500; letter-spacing: -.3px;
  }
  .nav-center { flex: 1; text-align: center; }
  .nav-center a {
    color: var(--muted); text-decoration: none; font-size: 13px;
    padding: 4px 10px; border-radius: 6px;
  }
  .nav-center a:hover { color: var(--text); background: rgba(0,0,0,.04); }
  .nav-right { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }

  /* 语言切换：地球图标 + 目标语言 */
  .lang {
    width: auto; margin: 0; padding: 5px 10px; font-size: 12.5px; font-weight: 400;
    background: transparent; color: var(--muted);
    border: 1px solid var(--line-strong); border-radius: 999px;
    display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  }
  .lang:hover { color: var(--text); }
  .lang svg { width: 14px; height: 14px; }

  /*
   * 主题切换（右上角）：太阳 = 现在是浅色，月亮 = 现在是深色 ——
   * 图标说的是"当前状态"，title 说的是"点了会发生什么"（见 app.js 的 applyTheme）。
   * 两个图标都放在按钮里，靠 html[data-theme] 显示其中一个，切换时不用换 DOM。
   * 不带边框 —— 一圈线在导航里太抢，图标本身够清楚了。
   */
  .theme {
    width: auto; margin: 0; padding: 6px 9px;
    background: transparent; color: var(--muted);
    border: 0; border-radius: 999px;
    display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  }
  .theme:hover { color: var(--text); background: rgba(0,0,0,.05); }
  .theme svg { width: 15px; height: 15px; display: block; }
  .theme .ic-moon { display: none; }
  html[data-theme="dark"] .theme .ic-sun { display: none; }
  html[data-theme="dark"] .theme .ic-moon { display: block; }
  html[data-theme="dark"] .theme:hover { background: rgba(255,255,255,.08); }

  /* 未登录时的登录按钮 */
  .nav-login {
    width: auto; margin: 0; padding: 6px 15px; font-size: 13px;
    border-radius: 999px;
  }
  /* 已登录时的头像 + 名字 */
  .who {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 4px 10px 4px 4px; border-radius: 999px;
    border: 1px solid var(--line); background: var(--card);
    cursor: pointer; color: inherit; font: inherit; width: auto; margin: 0;
  }
  .who:hover { border-color: var(--line-strong); }
  .who img, .who .fallback {
    width: 26px; height: 26px; border-radius: 50%; flex: 0 0 26px;
    object-fit: cover; background: var(--accent-soft); color: var(--accent-ink);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 12px; font-weight: 500;
  }
  .who .nm { font-size: 13px; max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .who .role {
    font-size: 10.5px; padding: 1px 6px; border-radius: 999px;
    background: var(--accent-soft); color: var(--accent-ink);
  }

  /* ============================================================
     Hero —— 线稿里的那个大圆角卡片
     ============================================================ */
  .hero-wrap { max-width: 1100px; margin: 0 auto; padding: 34px 20px 0; }
  .hero {
    background: var(--card); border: 1px solid var(--line);
    border-radius: 20px; padding: 62px 40px 54px;
    text-align: center;
  }
  .hero-title {
    font-size: clamp(26px, 5.2vw, 44px);
    font-weight: 500; line-height: 1.25; margin: 0;
    letter-spacing: -.5px; min-height: 1.3em;
  }
  /* 打字机光标 */
  .caret {
    display: inline-block; width: 2px; height: .95em;
    background: var(--accent); margin-left: 4px;
    vertical-align: -.1em; animation: blink 1.05s steps(2, start) infinite;
  }
  @keyframes blink { 0%,100% { opacity: 1 } 50% { opacity: 0 } }
  @media (prefers-reduced-motion: reduce) {
    .caret { animation: none; }
  }
  .hero-rule {
    width: 54px; height: 2px; background: var(--line-strong);
    margin: 30px auto; border-radius: 1px;
  }
  /* 介绍固定英文，行高放松一点，别挤成一坨 */
  .hero-intro {
    max-width: 600px; margin: 0 auto; color: var(--muted);
    font-size: 14.5px; line-height: 1.85;
  }
  .hero-cta { margin-top: 30px; }
  .hero-cta button {
    width: auto; margin: 0; padding: 12px 40px; font-size: 15px;
    border-radius: 999px;
  }
  .hero-note {
    margin-top: 12px; font-size: 12.5px; color: var(--muted);
    display: flex; align-items: center; gap: 6px; justify-content: center;
  }
  .hero-note svg { width: 14px; height: 14px; }
  /* 未登录时提示一下下面还有东西 */
  .hero-note.warn { color: var(--warn); }

  /* 普通人看到的提示条 */
  .notice {
    margin-top: 18px; padding: 11px 14px; border-radius: var(--radius);
    background: var(--warn-soft); border: 1px solid rgba(133,79,11,.18);
    font-size: 12.5px; color: var(--warn);
  }

  /* 功能分区的角色标签 */
  .tag.admin { background: #fbeaf0; color: #993556; }

  /* 移动端：Hero 收窄，导航简化 */
  @media (max-width: 640px) {
    .hero { padding: 40px 22px 38px; border-radius: 16px; }
    .hero-rule { margin: 22px auto; }
    .nav-center { display: none; }        /* 手机上隐私政策挪到底部 */
    .who .nm { display: none; }
    .nav-inner { height: 52px; gap: 10px; }
    /* 导航矮了 7px，视图区的上限跟着放宽一点 */
    :root { --console-chrome: 150px; }
  }

  /* 轻提示 */
  .toast {
    position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
    z-index: 50; padding: 9px 18px; border-radius: 999px;
    background: #2c2c2a; color: #fff; font-size: 13px;
    box-shadow: 0 4px 16px rgba(0,0,0,.18);
  }

  /* ---- 远程连接列表 ---- */
  select {
    width: 100%; padding: 9px 11px; font: inherit; font-size: 14px;
    border: 1px solid var(--line-strong); border-radius: 8px;
    background: #fff; color: var(--text);
  }
  select:focus { outline: 2px solid rgba(var(--accent-rgb),.65); outline-offset: -1px; border-color: var(--accent-ink); }
  ul.files .kind {
    flex: 0 0 auto; font-size: 10.5px; font-weight: 500; letter-spacing: .4px;
    padding: 2px 7px; border-radius: 5px;
    background: var(--accent-soft); color: var(--accent-ink);
  }

  /* ---- 设备类型选择 ---- */
  .device-opts { display: grid; gap: 10px; margin: 16px 0 4px; }
  .device-opt {
    width: 100%; margin: 0; padding: 14px 16px; text-align: left;
    background: var(--card); color: var(--text);
    border: 1px solid var(--line-strong); border-radius: var(--radius);
    cursor: pointer; font: inherit;
    display: flex; flex-direction: column; gap: 4px;
  }
  .device-opt:hover { border-color: var(--accent-ink); background: var(--accent-soft); }
  .device-opt .dev-name { font-weight: 500; font-size: 14px; }
  .device-opt .dev-desc { color: var(--muted); font-size: 12.5px; line-height: 1.55; }
  .device-opt:hover .dev-desc { color: var(--accent-ink); }

  /* 临时设备标记 —— 要显眼，用户得知道关浏览器会掉登录 */
  .tag.temp { background: var(--warn-soft); color: var(--warn); }
  .tag { white-space: nowrap; }

  /* ---- 账号安全面板 ---- */
  .acct-row {
    display: flex; justify-content: space-between; align-items: center;
    padding: 9px 0; border-bottom: 1px solid var(--line); font-size: 13px;
  }
  .acct-row span { color: var(--muted); }
  .login-list {
    border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
    margin-bottom: 4px; max-height: 190px; overflow-y: auto;
  }
  .login-list .fmeta { padding: 10px 12px; color: var(--muted); font-size: 12.5px; }
  .login-item {
    display: flex; gap: 10px; align-items: baseline;
    padding: 7px 12px; font-size: 12px;
    border-bottom: 1px solid var(--line);
  }
  .login-item:last-child { border-bottom: 0; }
  .login-item .when { color: var(--muted); flex: 0 0 auto; }
  .login-item .ip {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    flex: 0 0 auto; min-width: 92px;
  }
  .login-item .dev {
    color: var(--muted); flex: 1; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .danger-ghost { color: #a32d2d; border-color: rgba(163,45,45,.35); }
  .danger-ghost:hover { background: rgba(163,45,45,.06); }

  /* 弹框里元素多，给它一点滚动余量 */
  dialog { max-height: 88vh; overflow-y: auto; }

  /* ---- 云盘未开通：一行小字 + 开通按钮，尽量不占地方 ---- */
  .upsell {
    border: 1px dashed var(--line-strong); border-radius: var(--radius);
    padding: 14px 16px; background: #fbfaf7;
  }
  .upsell-line {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    justify-content: space-between;
  }
  .upsell-text { color: var(--muted); font-size: 13px; }
  .upsell-btn {
    margin: 0; padding: 7px 14px; font-size: 13px;
    background: transparent; color: var(--accent-ink);
    border: 1px solid var(--accent); border-radius: 999px; cursor: pointer;
  }
  .upsell-btn:hover { background: var(--accent-soft); }

  /* ---- 开通说明 ---- */
  .plan {
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: 16px 18px; margin-bottom: 16px; background: #fbfaf7;
  }
  .plan-price { display: flex; align-items: baseline; gap: 6px; margin-bottom: 10px; }
  .plan-price b { font-size: 26px; font-weight: 500; letter-spacing: -.5px; }
  .plan-price span { color: var(--muted); font-size: 13px; }
  .plan-list { margin: 0; padding-left: 18px; color: var(--muted); font-size: 13px; }
  .plan-list li { margin-bottom: 5px; }

  /* ---- 管理设置 ---- */
  .admin-grid { display: grid; gap: 16px; }
  .admin-block {
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: 16px 18px; background: var(--card);
  }
  .admin-block h3 { font-size: 14px; font-weight: 500; margin: 0 0 12px; }
  .switch {
    display: flex; align-items: center; gap: 8px; margin: 0 0 14px;
    font-size: 13px; cursor: pointer;
  }
  .switch input { width: auto; margin: 0; }
  .row { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; }

  /* ---- 云盘多选与分享 ---- */
  ul.files li input.pick {
    flex: 0 0 auto; width: 15px; height: 15px; margin: 0;
    accent-color: var(--accent-ink); cursor: pointer;
  }
  .share-bar {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 10px 14px; margin-bottom: 12px;
    border: 1px solid var(--accent); border-radius: var(--radius);
    background: var(--accent-soft);
  }
  .share-all { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; cursor: pointer; }
  .share-all input { width: auto; margin: 0; accent-color: var(--accent-ink); }
  .share-count { font-size: 13px; color: var(--accent-ink); }
  .share-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
  .share-actions button { margin: 0; padding: 6px 12px; font-size: 12.5px; }
  .share-picked {
    margin: 0 0 14px; padding-left: 18px; max-height: 130px; overflow-y: auto;
    color: var(--muted); font-size: 12.5px;
  }
  .share-picked li { margin-bottom: 3px; word-break: break-all; }
  .share-list { max-height: 300px; overflow-y: auto; }
  /* 分享页上每行左边那格显示的是文件大小，窄一点 */
  .share-list .fname { min-width: 0; }

  /* ============================================================
     控制台：左栏 + 视图区

     布局是「左栏 + 一个视图区」两列。左栏是**原来那些功能区**的入口，
     右面一次只显示一个视图，打开时默认是 AI 对话。
     视图区自己带滚动，整页不滚 —— 聊天界面滚整页很别扭：
     输入框会被推走，滚到底还要先滚过整个历史。
     ============================================================ */

  /*
   * 控制台页的上下留白比别的页收一档。
   *
   * 视图区的高度是「视口剩下的那点地方」，所以留白少一寸、对话区就多一寸。
   * 收窄之前，页脚上面 40 + 下面 60 一共吃掉 100px，而这 100px 全是对话区
   * 本可以占的高度；现在压到 22 + 24，空对话时视图区能高出一截。
   *
   * 只动控制台这一页（body.console）—— 首页、隐私政策、分享页是内容页，
   * 它们需要那点呼吸感，没必要跟着改。
   *
   * 改了这里要同步改 :root 里的 --console-chrome，两个数是一套的。
   */
  body.console .wrap { padding-bottom: 24px; }
  body.console footer { margin-top: 22px; }

  .console-shell {
    display: grid; grid-template-columns: 186px minmax(0, 1fr);
    gap: 18px; align-items: start;
  }

  /* ---- 左栏 ---- */
  .side {
    position: sticky; top: 74px;
    background: var(--card); border: 1px solid var(--line);
    border-radius: var(--radius); padding: 14px 12px;
  }
  .side-title { font-size: 11.5px; color: var(--muted); margin: 0 0 8px 4px; }
  .side-nav { display: flex; flex-direction: column; gap: 2px; }
  /*
   * 里面有两种东西：切换视图的 <button> 和跳走的 <a>。
   * button 要显式覆盖全局那个大蓝按钮的样式，否则会变成一排蓝条。
   */
  .side-nav button, .side-nav a {
    display: block; width: 100%; text-align: left;
    margin: 0; padding: 7px 10px; border: 0; border-radius: 8px;
    background: transparent; font: inherit; font-size: 13px; font-weight: 400;
    text-decoration: none; color: var(--muted); cursor: pointer;
  }
  .side-nav button:hover, .side-nav a:hover { background: rgba(0,0,0,.04); color: var(--text); }
  /* 当前所在的那个视图要看得出来，否则点进来会怀疑自己没点动 */
  .side-nav button.on { background: var(--accent-soft); color: var(--accent-ink); font-weight: 500; }
  .side-foot { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
  .side-foot button { width: 100%; margin: 0; padding: 7px 10px; font-size: 12.5px; }

  /* ---- 视图区 ----
   *
   * 高度**由内容决定**，最长长到"视口里剩下的那点地方"为止。
   *
   * 这里原来写的是 height: calc(100vh - 156px)，也就是"永远铺满视口"。
   * 毛病出在 100vh 和那个 156px 不成比例：浏览器缩放（Ctrl+滚轮）时
   * 视口在 CSS 像素里会**变大**，而 156px 是死的、不跟着缩 —— 于是
   * 缩得越小，视图区被撑得越长。对话只有四行的时候，中间就空出一大片
   * （100% 缩放下约 300px，50% 下约 550px），而正文反而被缩到看不清，
   * 看起来就是"被无效拉长"。
   *
   * 现在改成：内容多高就多高（对话短 → 输入框紧贴在消息下面，没有空洞），
   * 内容长到超过 max-height 才停下，剩下的交给 .ai-log 自己滚。
   * 这样缩放时高度只跟内容有关，跟视口无关 —— 窗口不会跟着变大变小。
   *
   * min-height 取 min(760px, 视口剩下的高度)：
   *   - 上限是 760px 这个**固定值**，所以缩放时它不跟着变（这正是要的效果）；
   *   - 再跟"视口剩下的高度"取小，是为了矮窗口里不至于把页脚顶出屏幕 ——
   *     窗口矮的时候它就自动收成能放下的最大值，不会凭空多出一条整页滚动条。
   *     换句话说：**普通窗口里它就是"铺满"，缩小时它才不再跟着长**。
   *
   * 另外 156px 这个数本身也是偏小的：视图区下面还有页脚和两圈留白，
   * 按别的页那套留白（40 + 60）算实际要吃掉约 206px，所以原来整页永远多出
   * 约 50px 的滚动条。现在把控制台这一页的留白收窄（见 body.console .wrap），
   * 156px 才对得上真实占用 —— 页面正好停在视口底边，不多滚一行。
   */
  .console-main {
    background: var(--card); border: 1px solid var(--line);
    border-radius: var(--radius);
    display: flex; flex-direction: column;
    height: auto;
    max-height: calc(100vh - var(--console-chrome));
    max-height: calc(100dvh - var(--console-chrome));  /* 移动端地址栏收起/展开时更准 */
    min-height: min(760px, calc(100vh - var(--console-chrome)));
    min-height: min(760px, calc(100dvh - var(--console-chrome)));
    overflow: hidden;
  }
  /*
   * 一次只显示一个视图。用 .on 而不是 .hidden 切换：
   * .hidden 是 display:none !important，而视图需要的是 display:flex。
   */
  .view { display: none; }
  /*
   * flex-basis 必须是 auto、不能是 0 —— 上面 .console-main 是 height:auto，
   * 容器的高度就是各项 flex-basis 之和。写成 flex:1（等价于 basis 0）的话，
   * 视图对容器高度的贡献恒为 0，视图区就永远卡在 min-height 上，内容再长
   * 也撑不开。basis:auto 才让"内容撑开容器"这件事成立。
   */
  .view.on { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
  /* 非对话的视图（AI 云盘 / 动态验证码）自己整块滚 */
  .view.pad { overflow-y: auto; padding: 18px 18px 24px; display: none; }
  .view.pad.on { display: block; }

  /*
   * ---- AI 对话：一层极光 + 一处玻璃 ----
   *
   * 这里原来是"到处是玻璃"：工具条、气泡、代码块、标签、空态卡、输入框
   * 全都叠 backdrop-filter。结果是**没有层次** —— 毛玻璃之所以显得高级，
   * 靠的是"少数几处浮在内容之上"这件事本身；一屏七层半透明，等于谁都
   * 没浮起来，剩下的只有一层浑浊的膜。
   *
   * 而且磨砂磨不出东西：底下的 ::before 是柔和的径向渐变，几乎没有细节
   * 可磨，磨完就是一片混色的半透明 —— 蓝光斑透过白玻璃，就是那种
   * 「说不清是什么颜色」的脏。
   *
   * 所以现在只剩**输入框**一处玻璃：它确实是压在对话内容之上、需要
   * 用户感知到"浮着"的操作区，磨砂在这里有话可说。其余元素（工具条、
   * 气泡、代码块、标签）一律回到实色 —— 它们不需要和背后的光斑发生关系。
   *
   * isolation:isolate 是为了让 ::before 的 z-index:-1 只落在本视图内，
   * 不会掉到 .console-main 的白底下面（那样就什么都看不见了）。
   */
  #viewChat { position: relative; isolation: isolate; }
  /*
   * 极光背景分两层：::before 浅色、::after 深色，切主题时两层用
   * opacity 交叉淡化。**渐变本身没法 transition**（background-image
   * 不做插值），直接换 background 就是瞬间跳变 —— 整页都在缓动、
   * 唯独 AI 窗口"先变色"就是这个原因。opacity 可以过渡，所以叠两层。
   */
  #viewChat::before {
    content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
    transition: opacity .35s ease;
    /* 比原来淡一档：正文现在直接压在背景上（助手消息不再裹气泡），
       底色重了长回答会难看 */
    background:
      radial-gradient(680px 340px at 4% -14%, rgba(var(--accent-rgb), .30), transparent 62%),
      radial-gradient(560px 300px at 100% 0%, rgba(168, 150, 255, .18), transparent 64%),
      radial-gradient(760px 400px at 50% 120%, rgba(var(--accent-rgb), .14), transparent 66%),
      linear-gradient(180deg, #fdfeff 0%, #f5f8fd 100%);
  }
  html[data-theme="dark"] #viewChat::before { opacity: 0; }
  #viewChat::after {
    content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
    opacity: 0; transition: opacity .35s ease;
    background:
      radial-gradient(680px 340px at 4% -14%, rgba(var(--accent-rgb), .13), transparent 62%),
      radial-gradient(560px 300px at 100% 0%, rgba(168, 150, 255, .09), transparent 64%),
      radial-gradient(760px 400px at 50% 120%, rgba(var(--accent-rgb), .07), transparent 66%),
      linear-gradient(180deg, #14161a 0%, #101114 100%);
  }
  html[data-theme="dark"] #viewChat::after { opacity: 1; }

  /* ---- 顶部工具条：实白。
     它本来就没有"浮起来"的理由 —— 一直是顶部一条静止的栏，
     用玻璃只会把背后的光斑搅进来。和门户其它 .card 保持一致。 */
  .ai-bar {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 12px 20px;
    border-bottom: 1px solid var(--line);
    background: var(--card);
    position: relative; z-index: 2;
    /* 不许被压缩：对话变长、视图区顶到 max-height 时，只该由 .ai-log 让步，
       工具条和输入区都得保持原样，否则会被压扁。 */
    flex: none;
  }
  .ai-pick { display: flex; align-items: center; gap: 7px; margin: 0; font-size: 12px; color: var(--muted); }
  /* 覆盖全局那个 width:100% —— 这里要的是"能快速扫一眼"的小控件 */
  .ai-pick select {
    width: auto; max-width: 240px; margin: 0; padding: 6px 12px; font-size: 12.5px;
    /* 工具条变成实白之后，白底的下拉就融进去了 —— 靠边框把它划出来 */
    border: 1px solid var(--line); border-radius: 999px;
    background: var(--card); cursor: pointer;
    box-shadow: 0 1px 3px rgba(15,23,42,.05);
    transition: border-color .16s, box-shadow .16s;
  }
  .ai-pick select:hover { border-color: rgba(var(--accent-rgb), .5); }
  .ai-pick select:focus {
    border-color: rgba(var(--accent-rgb), .75);
    box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .18);
  }
  .ai-meta { margin-left: auto; font-size: 12px; color: var(--muted); }

  /* ---- 对话区 ----
   *
   * flex-basis 同样是 auto：视图区是 height:auto，这一块的高度要能反过来
   * 把视图区撑开（对话短 → 视图区就矮）。被 max-height 卡住时，它是唯一
   * 允许收缩的一块（工具条和输入区都设了 flex:none），所以滚动条只会出现在
   * 这里，不会跑到整页上去。
   */
  .ai-log {
    flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 24px 20px 10px;
    /* 这里刻意**不配** scroll-behavior: smooth ——
       它会把 JS 的编程滚动也变成动画，流式输出时内容长得比动画快，
       滚动位置落在内容底部之后，看起来就是"刚输出的那截被裁掉了"。
       跟底逻辑见 app.js 的 aiScrollBottom。 */
  }
  .ai-log::-webkit-scrollbar { width: 10px; }
  .ai-log::-webkit-scrollbar-thumb {
    background: rgba(20,45,90,.14); border: 3px solid transparent;
    background-clip: content-box; border-radius: 999px;
  }
  .ai-log::-webkit-scrollbar-thumb:hover {
    background: rgba(20,45,90,.24); background-clip: content-box;
  }

  /* 一列居中，和 GPT 那种"读完一行要回到左边"的排版一致 */
  .ai-row {
    display: flex; align-items: flex-start; gap: 12px;
    max-width: 800px; margin: 0 auto 22px;
  }
  .ai-row.me { justify-content: flex-end; }
  /*
   * 淡入**只给流式那一块**。paintAiLog 是全量重画，要是给 .ai-row 挂动画，
   * 每发一条消息整个列表都会集体闪一下 —— 看着像页面在抽。
   */
  #aiLive { animation: aiRowIn .3s ease both; }
  @keyframes aiRowIn {
    from { opacity: 0; transform: translateY(7px); }
  }
  @media (prefers-reduced-motion: reduce) { #aiLive { animation: none; } }

  /*
   * GPT 的骨架：助手那边**不裹气泡**，就是一段贴在头像右边的正文，
   * 只有自己说的话才有一个气泡。这样一屏里大半是白底文字，
   * 读长回答不会像在读一堆盒子。
   */
  .ai-ava {
    width: 30px; height: 30px; flex: 0 0 30px; margin-top: 3px;
    border-radius: 50%; display: grid; place-items: center;
    background: linear-gradient(140deg, #6fc6f2, var(--accent-ink));
    box-shadow: 0 4px 12px rgba(var(--accent-rgb), .34), inset 0 1px 0 rgba(255,255,255,.45);
  }
  .ai-ava svg { width: 15px; height: 15px; fill: #fff; }

  .ai-bubble {
    min-width: 0; max-width: 100%;
    font-size: 14px; line-height: 1.8; color: var(--text);
  }
  /* 自己说的话：一块**实色**浅蓝，一眼分得清是谁在说。
     原来是半透明蓝重叠磨砂 —— 那么同一句话在不同位置会是不同的颜色
     （取决于它正好盖住哪一块光斑），这就是「参杂」的来源。实色之后
     颜色恒定，和助手的正文之间也就有了干净的对比。 */
  .ai-row.me .ai-bubble {
    max-width: min(620px, 82%);
    padding: 10px 16px; border-radius: 20px 20px 6px 20px;
    background: #d8ecfb;
    border: 1px solid rgba(var(--accent-rgb), .55);
    box-shadow: 0 2px 10px rgba(var(--accent-rgb), .16);
  }
  .ai-text { white-space: pre-wrap; word-break: break-word; }
  /* 排版只做最小的一件事：代码块单独成块。
     真正的 Markdown 渲染要小心 —— 渲染出来的 HTML 是注入面。 */
  /* 代码要看得清，不要"透" —— 实白 + 细边，和 .card 一致 */
  .ai-text pre {
    background: var(--card); border: 1px solid var(--line);
    border-radius: 12px;
    padding: 13px 15px; overflow-x: auto; margin: 10px 0; white-space: pre;
  }
  .ai-text code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12.5px; background: rgba(var(--accent-rgb),.16); color: var(--accent-ink);
    padding: 1.5px 6px; border-radius: 6px;
  }
  .ai-text pre code { background: none; padding: 0; color: inherit; }
  /* 附件徽标：用户当时发了什么，回看时要认得出来 */
  .ai-attach {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 11.5px; padding: 3px 9px; border-radius: 999px;
    background: var(--card); border: 1px solid rgba(var(--accent-rgb), .45);
    color: var(--accent-ink); margin: 0 6px 4px 0;
  }
  /* 思考过程：回答上方的折叠块。
     收起时只占一行摘要 —— 它经常比正文还长，默认铺开会把真正的回答挤到屏幕外。 */
  .ai-reason {
    margin: 0 0 8px; font-size: 12.5px; line-height: 1.7; color: var(--muted);
  }
  .ai-reason > summary {
    cursor: pointer; list-style: none;
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 10px; border-radius: 999px;
    background: var(--card); border: 1px solid var(--line);
    color: var(--muted); font-size: 11px;
  }
  /* 去掉浏览器默认那颗三角，换成自己的箭头（默认态 ▸，展开态 ▾） */
  .ai-reason > summary::-webkit-details-marker { display: none; }
  .ai-reason > summary::before { content: '▸'; font-size: 9px; }
  .ai-reason[open] > summary::before { content: '▾'; }
  .ai-reason-body {
    margin-top: 6px; padding: 9px 12px;
    background: var(--card); border: 1px solid var(--line);
    border-radius: 12px;
    white-space: pre-wrap; word-break: break-word;
    /* 思考动辄几千字，给个上限自己滚，别把整页撑长 */
    max-height: 260px; overflow-y: auto;
  }
  /* 工具调用：摆在回答上方，让"它去干了什么"有痕迹 */
  .ai-tools { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 7px; }
  .ai-tools:empty { display: none; }
  /* 小标签一律实白。它们只有 11px 高，磨砂在上面本来就看不出来，
     纯粹是白扛一层合成开销；半透明反倒让底色把这几个字染花。 */
  .ai-tool {
    font-size: 11px; padding: 2.5px 10px; border-radius: 999px;
    background: var(--card); border: 1px solid var(--line);
    color: var(--muted);
  }
  .ai-tool.ok { background: var(--good-soft); border-color: transparent; color: var(--good); }
  .ai-tool.bad { background: rgba(163,45,45,.08); border-color: transparent; color: #a32d2d; }
  .ai-tool.run { background: var(--accent-soft); border-color: transparent; color: var(--accent-ink); }

  /* 思考中的三点，那种"它在动"的反馈。
     没有它的话，模型想得久一点，用户只会以为卡住了。 */
  .ai-dots { display: inline-flex; align-items: center; gap: 4px; height: 14px; }
  .ai-dots i {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--accent-deep); opacity: .45;
    animation: aiDot 1.2s infinite ease-in-out;
  }
  .ai-dots i:nth-child(2) { animation-delay: .15s; }
  .ai-dots i:nth-child(3) { animation-delay: .3s; }
  @keyframes aiDot {
    0%, 60%, 100% { transform: translateY(0); opacity: .35; }
    30% { transform: translateY(-4px); opacity: 1; }
  }
  @media (prefers-reduced-motion: reduce) {
    .ai-dots i { animation: none; opacity: .6; }
  }

  /* 系统提示 / 错误：和对话分开，别混进气泡里 */
  .ai-sys {
    max-width: 780px; margin: 0 auto 12px;
    font-size: 12.5px; border-radius: 12px; padding: 9px 13px;
    background: var(--warn-soft); color: var(--warn);
    border: 1px solid rgba(133,79,11,.12);
  }
  .ai-sys.bad { background: rgba(163,45,45,.07); color: #a32d2d; border-color: rgba(163,45,45,.14); }
  /* 中性的小提示（"还没有消息"之类），不占视觉重量 */
  .ai-tip { font-size: 12.5px; color: var(--muted); margin: 0 auto 14px; max-width: 780px; }

  /*
   * 空白对话的那句大字招呼（GPT 那个位置）。
   *
   * 顶部间距原来写的是 12vh —— 那会让"空对话"时视图区的高度又跟视口
   * 挂上钩：视口在 CSS 像素里越高（缩放越小、窗口越高），招呼被推得越低，
   * 视图区就跟着越长，正是这次要消灭的那个毛病。视图区现在的高度只由
   * 内容决定，里面的间距也就不该再拿 vh 当单位。96px ≈ 800px 高视口下的
   * 12vh，常用的窗口尺寸看不出变化。
   */
  .ai-hello { text-align: center; margin: 96px auto 0; max-width: 560px; padding: 0 8px; }
  .ai-hello h2 {
    font-size: 26px; font-weight: 500; letter-spacing: -.01em;
    color: var(--text); margin: 0 0 10px;
  }
  .ai-hello p { margin: 0; font-size: 13.5px; line-height: 1.8; color: var(--muted); }

  /* 还没有配置模型时的引导。**不藏起来** —— 藏了用户只会以为功能不存在。
     样式上是一张**实白卡**：它要读，要让"还没配模型"这件事清清楚楚，
     不该是块模模糊糊的玻璃挡在那儿。 */
  .ai-empty {
    text-align: center; color: var(--muted);
    /* 同上：不用 vh，视图区高度不该被视口牵着走 */
    max-width: 460px; margin: 96px auto 0; padding: 36px 28px 40px;
    border-radius: 26px;
    background: var(--card);
    border: 1px solid var(--line);
    box-shadow: 0 18px 46px rgba(15,23,42,.08);
  }
  .ai-empty h3 { font-size: 18px; font-weight: 500; color: var(--text); margin: 0 0 10px; }
  .ai-empty p { margin: 0 auto 22px; max-width: 400px; font-size: 13px; line-height: 1.8; }
  .ai-empty button {
    width: auto; margin: 0; padding: 10px 24px; border-radius: 999px;
    border: 0; color: #fff;
    background: linear-gradient(135deg, var(--accent-deep), var(--accent-ink));
    box-shadow: 0 6px 16px rgba(var(--accent-rgb), .42);
  }
  .ai-empty button:hover:not(:disabled) {
    background: linear-gradient(135deg, var(--accent-ink), #084b74);
    color: #fff;
  }

  /* ---- 输入区：一整块浮起来的毛玻璃胶囊 ---- */
  .ai-compose {
    width: 100%; max-width: 800px; margin: 0 auto;
    padding: 6px 20px 16px;
    border: 0; position: relative; z-index: 2;
    /* 同 .ai-bar：只许 .ai-log 收缩，输入区不能被压扁 */
    flex: none;
  }
  .ai-chips { display: flex; flex-wrap: wrap; gap: 6px; }
  .ai-chips:not(:empty) { margin-bottom: 8px; }
  .ai-chip {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12px; padding: 4px 8px 4px 11px; border-radius: 999px;
    background: var(--card); color: var(--accent-ink);
    border: 1px solid var(--line);
    box-shadow: 0 2px 8px rgba(15,23,42,.06);
  }
  .ai-chip button {
    width: auto; margin: 0; padding: 0 3px; border: 0;
    background: transparent; color: inherit; font-size: 14px; line-height: 1; cursor: pointer;
  }
  /*
   * 输入框本身**不再有边框** —— 边框和磨砂都交给外面这一圈。
   * 里面那个 textarea 只是一个透明的多行编辑区，这样才像"嵌在玻璃里"，
   * 而不是"一个框里又套了一个框"。
   * 焦点态也画在容器上（:focus-within），不然会有两圈光晕打架。
   */
  .ai-input-row {
    display: flex; align-items: flex-end; gap: 8px;
    padding: 8px 8px 8px 10px;
    border-radius: 28px;
    /* 全屏唯一的一处玻璃（见上方大段注释）。白度给到 .88 ——
       再透一点，背后那层极光就又"参"进来了；磨砂只需要让人隐约觉得
       后面有东西，不需要真看清后面。 */
    background: rgba(255,255,255,.88);
    border: 1px solid rgba(255,255,255,.95);
    -webkit-backdrop-filter: blur(22px) saturate(180%);
    backdrop-filter: blur(22px) saturate(180%);
    box-shadow:
      0 10px 30px rgba(15,23,42,.10),
      0 2px 6px rgba(15,23,42,.05),
      inset 0 1px 0 #fff;
    transition: border-color .18s, box-shadow .18s;
  }
  .ai-input-row:focus-within {
    border-color: rgba(var(--accent-rgb), .80);
    box-shadow:
      0 12px 34px rgba(15,23,42,.14),
      0 2px 6px rgba(15,23,42,.05),
      0 0 0 4px rgba(var(--accent-rgb), .18),
      inset 0 1px 0 #fff;
  }
  .ai-input-row textarea {
    flex: 1; min-width: 0; min-height: 34px; max-height: 190px; resize: none;
    padding: 7px 4px; font: inherit; font-size: 14px; line-height: 1.6;
    border: 0; border-radius: 14px; background: transparent; color: var(--text);
  }
  .ai-input-row textarea:focus { outline: none; border-color: transparent; }
  .ai-input-row textarea::placeholder { color: var(--muted); opacity: .8; }

  /* 两个圆钮都靠 SVG 画，不放文字 —— 放文字就会撞上「被 i18n 换掉」那个坑 */
  .ai-round, .ai-send {
    width: 34px; height: 34px; flex: 0 0 34px; margin: 0; padding: 0;
    border-radius: 50%; display: grid; place-items: center; line-height: 1;
    transition: background .16s, color .16s, border-color .16s, transform .16s, box-shadow .16s;
  }
  .ai-round svg, .ai-send svg {
    width: 17px; height: 17px; fill: none; stroke: currentColor;
    stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round;
  }
  .ai-round {
    background: transparent; color: var(--muted);
    border: 1px solid rgba(20,45,90,.12);
  }
  .ai-round:hover:not(:disabled) {
    color: var(--accent-ink); border-color: rgba(var(--accent-rgb), .55);
    background: rgba(var(--accent-rgb), .10); transform: translateY(-1px);
  }
  .ai-send {
    border: 0; color: #fff;
    background: linear-gradient(140deg, var(--accent-deep), var(--accent-ink));
    box-shadow: 0 5px 14px rgba(var(--accent-rgb), .45);
  }
  .ai-send:hover:not(:disabled) {
    transform: translateY(-1px); box-shadow: 0 8px 20px rgba(var(--accent-rgb), .55);
  }
  .ai-send:disabled { opacity: .45; box-shadow: none; transform: none; }
  /* 忙的时候按钮变「停止」：箭头收起来，用 ::after 画一个方块 */
  .ai-send.busy {
    background: rgba(255,255,255,.92); color: #a32d2d;
    border: 1px solid rgba(163,45,45,.35);
    box-shadow: 0 4px 12px rgba(163,45,45,.18);
  }
  .ai-send.busy svg { display: none; }
  .ai-send.busy::after {
    content: ""; width: 10px; height: 10px; border-radius: 2.5px; background: currentColor;
  }
  .ai-note { margin: 9px 0 0; font-size: 11.5px; color: var(--muted); text-align: center; opacity: .8; }

  /* ---- 拖文件进来时的覆盖层 ----
     盖住整个对话视图。pointer-events:none 是刻意的：它只负责"看得出来"，
     事件必须穿过去落到 #viewChat 上（dragover / drop 都在那儿监听）。
     让它参与命中测试的话，遮罩一出现就会把自己的 dragleave 触发掉，来回闪。 */
  .ai-drop {
    position: absolute; inset: 0; z-index: 9;
    display: none; place-items: center;
    pointer-events: none;
    background: rgba(240,247,255,.66);
    -webkit-backdrop-filter: blur(8px) saturate(160%);
    backdrop-filter: blur(8px) saturate(160%);
  }
  .ai-drop.on { display: grid; }
  .ai-drop-card {
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    padding: 30px 44px; border-radius: 22px; text-align: center;
    border: 2px dashed rgba(var(--accent-rgb), .85);
    background: rgba(255,255,255,.75);
    box-shadow: 0 14px 40px rgba(20,45,90,.16);
  }
  .ai-drop-icon {
    width: 46px; height: 46px; margin-bottom: 8px; border-radius: 50%;
    display: grid; place-items: center;
    font-size: 22px; line-height: 1; color: #fff;
    background: linear-gradient(135deg, var(--accent-deep), var(--accent-ink));
    box-shadow: 0 6px 16px rgba(var(--accent-rgb), .45);
    animation: aiDropBob 1.4s infinite ease-in-out;
  }
  .ai-drop-title { margin: 0; font-size: 14px; font-weight: 500; color: var(--accent-ink); }
  .ai-drop-sub { margin: 0; font-size: 12px; color: var(--muted); }
  @keyframes aiDropBob {
    0%, 100% { transform: translateY(-2px); }
    50% { transform: translateY(3px); }
  }
  @media (prefers-reduced-motion: reduce) { .ai-drop-icon { animation: none; } }

  /* ============================================================
     对话区第二款式：居中款

     同一套 DOM、同一套逻辑，只换 #viewChat 上的一个类（v-center），
     切换入口在顶部工具条的「款式」下拉里，选择记在 al_ai_style。

     **这个款式只改位置，不碰颜色** —— 底色、极光、气泡、输入框、
     工具标签全都还是经典款那一套，下面没有一条配色覆写。

     位置上的差别只有一处：空对话时输入框在**中间**，上面一行招呼。
     落地方式：.is-blank 时整块 flex 居中，顶部工具条改绝对定位脱离
     流（它要留在顶上，不能被一起居中）。一旦有消息，.is-blank 摘掉，
     自动回到"记录在上、输入框在下" —— 回车发送就是"对话框沉到底部"
     那个动作，没有任何额外的布局代码。

     那句招呼每次刷新换一句（ai.greet 词条里随机，见 app.js）。
     ============================================================ */
  #viewChat.v-center.is-blank { justify-content: center; }
  /* 空态才把工具条拎出文档流；非空态它就是顶部一条普通的栏 */
  #viewChat.v-center.is-blank .ai-bar { position: absolute; top: 0; left: 0; right: 0; }
  /* 空态的问候自己撑高度、不参与拉伸（拉伸会把输入框顶到底部去） */
  #viewChat.v-center.is-blank .ai-log {
    flex: 0 0 auto; overflow: visible;
    padding: 0 20px;
  }
  /* 居中那句问候不再需要 12vh 的顶部间距，离输入框留 30px */
  #viewChat.v-center .ai-hello { margin: 0 auto 30px; }

  /* ---- AI 设置面板 ---- */
  /* 这一页字段多，给 dialog 放宽一点 */
  dialog.ai-wide { max-width: 620px; }
  .ai-sec {
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: 14px 16px; margin-bottom: 14px; background: #fbfaf7;
  }
  .ai-sec > h4 {
    font-size: 13px; font-weight: 500; margin: 0 0 4px;
    display: flex; align-items: center; gap: 8px;
  }
  .ai-sec > .hint { margin: 0 0 12px; }
  .ai-card {
    border: 1px solid var(--line); border-radius: 10px;
    padding: 12px 14px; margin-bottom: 10px; background: var(--card);
  }
  .ai-card-head {
    display: flex; align-items: center; gap: 8px; margin-bottom: 4px;
  }
  .ai-card-head .grow { flex: 1; min-width: 0; font-size: 13px; font-weight: 500; }
  .ai-card-head button {
    width: auto; margin: 0; padding: 3px 10px; font-size: 12px;
    background: transparent; color: var(--accent-ink);
    border: 1px solid var(--line-strong); border-radius: 6px;
  }
  .ai-card-head button.danger { color: #a32d2d; border-color: rgba(163,45,45,.35); }
  .ai-ta {
    width: 100%; min-height: 62px; resize: vertical;
    padding: 8px 10px; font: inherit; font-size: 12.5px; line-height: 1.6;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    border: 1px solid var(--line-strong); border-radius: 8px;
    background: #fff; color: var(--text);
  }
  .ai-steps { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
  .ai-steps button {
    width: auto; margin: 0; padding: 5px 12px; font-size: 12.5px;
    background: transparent; color: var(--muted);
    border: 1px solid var(--line-strong); border-radius: 999px;
  }
  .ai-steps button.on { background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent); }

  /* 窄屏：左栏从竖排变横排，不然两列会把视图区挤成一条缝 */
  @media (max-width: 840px) {
    .console-shell { grid-template-columns: 1fr; gap: 10px; }
    .side { position: static; padding: 10px 10px; }
    .side-title { display: none; }
    .side-nav { flex-direction: row; overflow-x: auto; gap: 4px; }
    .side-nav button, .side-nav a { white-space: nowrap; width: auto; padding: 6px 10px; }
    .side-foot { margin-top: 8px; padding-top: 8px; }
    /* 窄屏只调下限（手机视口矮，收一档）；上限由 --console-chrome 统一算，
       别再写一份 height —— 写死高度就是上面那个"缩放被拉长"的病根 */
    .console-main { min-height: min(460px, calc(100dvh - var(--console-chrome))); }
    .ai-pick select { max-width: 150px; }
    .ai-meta { display: none; }
    /* 窄屏把两侧留白收掉，气泡能用满宽度 */
    .ai-log { padding: 18px 12px 8px; }
    .ai-compose { padding: 4px 12px 12px; }
    .ai-row { gap: 9px; }
    .ai-row.me .ai-bubble { max-width: 88%; }
    .ai-ava { width: 26px; height: 26px; flex-basis: 26px; }
    /* 窄屏手机视口矮，间距收一档；仍然用固定值，不用 vh */
    .ai-hello, .ai-empty { margin-top: 56px; }
    .ai-hello h2 { font-size: 22px; }
    /* 居中款：窄屏下两侧留白收一点（和经典款的窄屏取同一个值） */
    #viewChat.v-center.is-blank .ai-log { padding: 0 12px; }
  }

  /* ============================================================
     深色主题
     ------------------------------------------------------------
     浅色是默认；深色只在 <html data-theme="dark"> 下生效 ——
     那个属性由 app.js / privacy.js 在脚本一执行时标上（要早，
     否则深色用户会先看到一帧浅色）。

     两层做法：
     1. **变量覆写**（大头）—— 页面里绝大多数颜色都走 var(--xxx)，
        换一套变量就整站换过去了；
     2. **硬编码补丁** —— 有些地方当初写死了颜色（玻璃白、极光底、
        浅蓝气泡、暗红……），它们不跟变量走，逐个在下面按住。

     为什么不用 @media (prefers-color-scheme)：这个站的主题是
     **手动开关**，不看系统偏好 —— 用户点太阳/月亮说了算。
     分享页（share.html）不参与：它没有导航栏，进来读取的是
     默认浅色，对没登录的访客也一致。
     ============================================================ */
  html[data-theme="dark"] {
    --bg: #101114;
    --card: #1a1c20;
    --line: rgba(255,255,255,.09);
    --line-strong: rgba(255,255,255,.22);
    --text: #e7e8ea;
    --muted: #9a9ca3;
    /* 主题色不动；但 --accent-ink 是"当文字用"的那一档，深底上必须提亮 */
    --accent-ink: #8fd0f7;
    --accent-soft: rgba(102,204,255,.13);
    --good: #57c9a4;
    --good-soft: rgba(87,201,164,.13);
    --warn: #e6bb6f;
    --warn-soft: rgba(230,187,111,.13);
  }

  /* ---- 写死了浅色的地方，逐个按住 ---- */
  html[data-theme="dark"] .nav { background: rgba(16,17,20,.86); }
  html[data-theme="dark"] input,
  html[data-theme="dark"] select,
  html[data-theme="dark"] .ai-ta { background: var(--card); }
  html[data-theme="dark"] code { background: rgba(255,255,255,.08); }
  html[data-theme="dark"] .tag.muted { background: rgba(255,255,255,.09); color: #a9abb2; }
  html[data-theme="dark"] .tag.admin { background: rgba(220,90,140,.16); color: #e79ab8; }
  html[data-theme="dark"] .nav-center a:hover { background: rgba(255,255,255,.07); }
  html[data-theme="dark"] .side-nav button:hover,
  html[data-theme="dark"] .side-nav a:hover { background: rgba(255,255,255,.06); }
  html[data-theme="dark"] .otp-bar,
  html[data-theme="dark"] .quota .bar,
  html[data-theme="dark"] .prog .bar { background: rgba(255,255,255,.10); }
  html[data-theme="dark"] .upsell,
  html[data-theme="dark"] .plan,
  html[data-theme="dark"] .ai-sec { background: rgba(255,255,255,.03); }
  html[data-theme="dark"] .notice { border-color: rgba(230,187,111,.28); }
  html[data-theme="dark"] .toast { background: #33363c; }

  /* 危险色（暗红 #a32d2d）在深底上读不动，统一提亮 */
  html[data-theme="dark"] .err,
  html[data-theme="dark"] .danger-ghost,
  html[data-theme="dark"] .otp-code.expiring,
  html[data-theme="dark"] .otp-item .del:hover,
  html[data-theme="dark"] ul.files button.act.danger:hover,
  html[data-theme="dark"] .ai-card-head button.danger { color: #f28b82; }
  html[data-theme="dark"] .danger-ghost { border-color: rgba(242,139,130,.40); }
  html[data-theme="dark"] .danger-ghost:hover { background: rgba(242,139,130,.10); }
  html[data-theme="dark"] .ai-tool.bad { background: rgba(242,139,130,.14); color: #f28b82; }
  html[data-theme="dark"] .ai-sys {
    background: rgba(230,187,111,.10); color: #e6bb6f; border-color: rgba(230,187,111,.22);
  }
  html[data-theme="dark"] .ai-sys.bad {
    background: rgba(242,139,130,.12); color: #f28b82; border-color: rgba(242,139,130,.26);
  }

  /* ---- AI 对话区：玻璃、气泡、遮罩当初都是按浅色写死的 ----
     （极光的两层交叉淡化在 #viewChat 的定义处，见上） */
  html[data-theme="dark"] .ai-log::-webkit-scrollbar-thumb {
    background: rgba(255,255,255,.16); background-clip: content-box;
  }
  html[data-theme="dark"] .ai-log::-webkit-scrollbar-thumb:hover {
    background: rgba(255,255,255,.26); background-clip: content-box;
  }
  html[data-theme="dark"] .ai-row.me .ai-bubble {
    background: #234058; border-color: rgba(var(--accent-rgb), .40);
  }
  html[data-theme="dark"] .ai-input-row {
    background: rgba(30,33,38,.92);
    border-color: rgba(255,255,255,.10);
    box-shadow:
      0 10px 30px rgba(0,0,0,.45),
      0 2px 6px rgba(0,0,0,.30),
      inset 0 1px 0 rgba(255,255,255,.05);
  }
  html[data-theme="dark"] .ai-input-row:focus-within {
    border-color: rgba(var(--accent-rgb), .70);
    box-shadow:
      0 12px 34px rgba(0,0,0,.50),
      0 0 0 4px rgba(var(--accent-rgb), .16),
      inset 0 1px 0 rgba(255,255,255,.05);
  }
  html[data-theme="dark"] .ai-round { border-color: rgba(255,255,255,.16); }
  html[data-theme="dark"] .ai-send.busy {
    background: rgba(255,255,255,.12); color: #f28b82;
    border-color: rgba(242,139,130,.42); box-shadow: none;
  }
  html[data-theme="dark"] .ai-drop { background: rgba(16,17,20,.62); }
  html[data-theme="dark"] .ai-drop-card {
    background: rgba(26,28,32,.88); border-color: rgba(var(--accent-rgb), .75);
    box-shadow: 0 14px 40px rgba(0,0,0,.50);
  }
  html[data-theme="dark"] .ai-empty { box-shadow: 0 18px 46px rgba(0,0,0,.40); }
  html[data-theme="dark"] .ai-empty button:hover:not(:disabled) {
    background: linear-gradient(135deg, var(--accent-deep), #0a6ba8);
  }

  /*
   * 切换主题时的缓动窗口：只在切换后的短暂窗口内允许颜色过渡
   * （class 由 app.js / privacy.js 在切换时挂上、450ms 后摘掉）。
   * 平时常驻全局 transition 会拖累别的动效，也算白白的合成开销 ——
   * 只有用户真正看到"颜色在变"的那一下才需要它。
   */
  html.theme-anim, html.theme-anim * {
    transition:
      background-color .35s ease,
      border-color .35s ease,
      color .35s ease,
      fill .35s ease,
      box-shadow .35s ease !important;
  }

