/* ==========================================================
   锚点 GEO · 设计系统
   主色 深海蓝 #0A1F44 ｜ 辅色 科技青 #00D4FF ｜ 强调 紫罗兰 #6366F1
   ========================================================== */

:root{
  /* 品牌色 */
  --navy:      #0A1F44;
  --navy-2:    #0E2A5C;
  --navy-3:    #14356F;
  --cyan:      #00D4FF;
  --cyan-dim:  #0BA5D4;
  --violet:    #6366F1;
  --violet-2:  #8B5CF6;

  /* 语义色 */
  --success:   #10B981;
  --warning:   #F59E0B;
  --danger:    #EF4444;
  --info:      #3B82F6;

  /* 中性 */
  --bg:        #F4F7FB;
  --surface:   #FFFFFF;
  --surface-1: #FFFFFF;
  --surface-rgb: 255,255,255;
  --surface-2: #F8FAFC;
  --border:    #E3E9F2;
  --border-2:  #EDF2F8;
  --text:      #0F172A;
  --text-1:    #0F172A;
  --text-2:    #475569;
  --text-3:    #94A3B8;

  /* 透明度（外观设置可调） */
  --card-alpha: 1;
  --bg-overlay: 0;
  --card-blur: calc((1 - var(--card-alpha)) * 18px);

  /* 阴影 */
  --sh-xs: 0 1px 2px rgba(15,23,42,.04);
  --sh-sm: 0 2px 8px rgba(15,23,42,.06);
  --sh-md: 0 8px 24px rgba(15,23,42,.08);
  --sh-lg: 0 16px 48px rgba(15,23,42,.12);
  --sh-glow: 0 8px var(--glow-blur) rgba(0,212,255,.22);

  /* 圆角 */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 20px;

  /* 尺寸 */
  --sidebar-w: 248px;
  --sidebar-w-mini: 72px;
  --topbar-h: 64px;

  --ease: cubic-bezier(.4,0,.2,1);

  /* ---------- 强调色 token（2026-10-01 新增） ----------
     为什么加这一组：原先「标记发布」按钮用 #10B981/#34D399、海外渠道用 #F59E0B，
     都是**浅色主题下的观感**被硬编码进页面；切到 theme-tech（深色）后，
     墨绿按钮与橙色柱条和深蓝+青紫的品牌基调打架 —— 显得廉价。
     现在统一收敛成 token：
       --accent-a / --accent-b  主操作渐变（青 → 靛紫，与品牌色一致）
       --accent-soft / -line    主操作的浅底与描边
       --brand-4                第四强调色（数据分类用，替代刺眼的橙）
       --*-ink                  深色底上的高亮前景（原 #B45309/#047857 在深底上发黑）
     浅色与深色各一套，页面只引用 token，不再写死颜色。 */
  --accent-a:      #06B6D4;
  --accent-b:      #6366F1;
  --accent-a-ink:  #0891B2;
  --accent-soft:   rgba(6,182,212,.10);
  --accent-line:   rgba(6,182,212,.34);
  --brand-4:       #8B5CF6;
  --success-ink:   #047857;
  --warning-ink:   #B45309;
  --info-ink:      #4338CA;

  /* 图表坐标轴/网格（浅色主题） */
  --chart-grid:    #EDF2F8;
  --chart-axis:    #94A3B8;
  --chart-track:   #EEF2F7;
  --chart-plain:   #CBD5E1;

  /* ── 界面缩放（2026-10-01）────────────────────────────────────────
     由外观设置「界面字号」滑块写入（theme.js 的 apply()）。
     ★ 为什么用变量 + zoom，而不是逐个改 font-size：
       全项目网页字号硬编码 551 处（main.css 153 + pages.js 行内 398），
       逐个替换既容易漏、又难一键回退；zoom 一处生效、一处回退，
       且对 JS 里的行内样式（398 处）同样有效。
     只作用于 #content（主内容区），侧栏 / 顶栏外壳不缩放 ——
       避免 100vh 布局在放大后把外壳挤出视口。
     --ui-scale 用 calc(<倍率> * 1) 得到「纯数字」，
       便于将来对单个 font-size 做 calc(13px * var(--ui-scale)) 精细缩放。 */
  --ui-zoom: 1.12;
  --ui-scale: calc(var(--ui-zoom) * 1);

  /* ── 描边宽度 / 光晕半径（2026-10-01 修「两个滑块空转」）──────────
     背景：外观设置里「描边 / 边框亮度」写 --stroke-w、「光晕强度」写 --glow-r，
     但 main.css 里这两个变量【出现 0 次】⇒ 用户调了永远没效果（既有 bug）。
     接法（改最小、回退最容易）：
       · 不逐个改 45 处 `border:var(--bd)` 的颜色，
         而是把「宽度」抽成派生变量 --bd / --bd-2，各处只引用它们。
       · --stroke-w 由 theme.js 写入，单位 px（0–3）。
       · ⚠️ 浏览器会把边框宽度取整到整数像素（CSS 规范），
         所以 1.5px 会渲染成 1px、2.5px 渲染成 2px —— 这是浏览器行为，非 bug。
       · --glow-r 同理，经 ×4 得到模糊半径（默认 8→32px，与原 --sh-glow 一致）。
     border 简写允许在宽度位置使用 var()（实测 Chrome 通过）。 */
  --stroke-w: 1px;
  --glow-r: 8px;
  --bd: var(--stroke-w) solid var(--border);
  --bd-2: var(--stroke-w) solid var(--border-2);
  --glow-blur: calc(var(--glow-r) * 4);
}

*{margin:0;padding:0;box-sizing:border-box;}

html,body{height:100%;}

body{
  font-family:var(--ui-font,"PingFang SC","Microsoft YaHei","HarmonyOS Sans SC",
              -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif);
  background:var(--bg);
  color:var(--text);
  font-size:14px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  overflow:hidden;
}

::-webkit-scrollbar{width:8px;height:8px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:#CBD5E1;border-radius:4px;}
::-webkit-scrollbar-thumb:hover{background:#94A3B8;}

/* ============ 加载动画 ============ */
#loader{
  position:fixed;inset:0;z-index:9999;
  background:linear-gradient(160deg,#0A1F44 0%,#0E2A5C 50%,#14356F 100%);
  display:flex;align-items:center;justify-content:center;
  transition:opacity .6s var(--ease),visibility .6s;
}
#loader.hide{opacity:0;visibility:hidden;}
.loader-inner{text-align:center;}
.loader-logo{
  width:96px;height:96px;margin:0 auto 24px;
  animation:float 2.4s ease-in-out infinite;
}
.loader-logo img{width:100%;height:100%;object-fit:contain;
  filter:drop-shadow(0 8px 24px rgba(0,212,255,.5));}
.loader-text{
  font-size:22px;font-weight:700;color:#fff;letter-spacing:3px;margin-bottom:20px;
}
.loader-bar{
  width:240px;height:3px;background:rgba(255,255,255,.15);
  border-radius:2px;overflow:hidden;margin:0 auto 14px;
}
.loader-bar i{
  display:block;height:100%;width:0;
  background:linear-gradient(90deg,var(--cyan),var(--violet));
  animation:loadFill 1.6s var(--ease) forwards;
  box-shadow:0 0 12px rgba(0,212,255,.8);
}
.loader-sub{font-size:12px;color:rgba(255,255,255,.5);letter-spacing:1px;}
@keyframes float{0%,100%{transform:translateY(0);}50%{transform:translateY(-10px);}}
@keyframes loadFill{to{width:100%;}}

/* ============ 布局 ============ */
.app{display:flex;height:100vh;overflow:hidden;}

/* ---------- 侧栏 ---------- */
.sidebar{
  width:var(--sidebar-w);flex-shrink:0;
  background:linear-gradient(180deg,#0A1F44 0%,#0D264F 60%,#102C5E 100%);
  display:flex;flex-direction:column;
  transition:width .3s var(--ease);
  position:relative;z-index:100;
}
.sidebar::after{
  content:"";position:absolute;top:0;right:0;bottom:0;width:1px;
  background:linear-gradient(180deg,transparent,rgba(0,212,255,.28),transparent);
}
.sidebar.collapsed{width:var(--sidebar-w-mini);}

.sidebar-head{
  height:var(--topbar-h);display:flex;align-items:center;
  justify-content:space-between;padding:0 16px;
  border-bottom:1px solid rgba(255,255,255,.07);flex-shrink:0;
}
.brand{display:flex;align-items:center;gap:11px;overflow:hidden;}
.brand-text h1{
  font-size:16px;font-weight:700;color:#fff;letter-spacing:1px;white-space:nowrap;
}
/* 侧边栏品牌字「锚点」：与登录页一致的渐变流光，克制版（8s / 弱 glow）
   只追加背景与滤镜，不动字号/间距；放在 @supports 内，
   不支持 background-clip:text 时保留上面的 color:#fff，不会把文字变透明。 */
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .brand-text h1{
    background:linear-gradient(90deg,#00E5FF 0%,#4F8CFF 16%,#A855F7 34%,#FF4D6D 52%,#FFB020 70%,#00E5FF 100%);
    background-size:300% 100%;
    -webkit-background-clip:text;background-clip:text;
    -webkit-text-fill-color:transparent;color:transparent;
    animation:brandFlow 8s linear infinite;
    filter:drop-shadow(0 0 10px rgba(0,212,255,.30));
  }
  @media (prefers-reduced-motion: reduce){
    .brand-text h1{animation:none;background-position:0% 50%;}
  }
}
.brand-text span{
  font-size:10px;color:rgba(0,212,255,.75);letter-spacing:1.6px;
  text-transform:uppercase;white-space:nowrap;
}
.collapse-btn{
  width:26px;height:26px;border:none;background:rgba(255,255,255,.08);
  color:rgba(255,255,255,.6);border-radius:7px;cursor:pointer;
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
  transition:all .2s;
}
.collapse-btn:hover{background:rgba(0,212,255,.18);color:var(--cyan);}
.sidebar.collapsed .collapse-btn{transform:rotate(180deg);}
/* 折叠态：地球标记收小 + 收窄内边距，避免与收起按钮争抢 72px 宽度 */
.sidebar.collapsed .sidebar-head{padding:0 8px;}
.sidebar.collapsed .brand{gap:0;}
.sidebar.collapsed .brand .earth-mark{width:28px;height:28px;}
.sidebar.collapsed .brand-text,
.sidebar.collapsed .ni-text,
.sidebar.collapsed .ni-badge,
.sidebar.collapsed .nav-group-title,
.sidebar.collapsed .credit-box{display:none;}

.nav{flex:1;overflow-y:auto;padding:14px 10px;}
.nav::-webkit-scrollbar{width:0;}
.nav-group{margin-bottom:18px;}
.nav-group-title{
  font-size:10px;color:rgba(255,255,255,.32);letter-spacing:1.6px;
  padding:0 10px;margin-bottom:7px;font-weight:600;
}
.nav-item{
  display:flex;align-items:center;gap:11px;
  padding:9px 11px;border-radius:10px;cursor:pointer;
  color:rgba(255,255,255,.66);font-size:13.5px;
  transition:all .2s var(--ease);position:relative;margin-bottom:2px;
  white-space:nowrap;
}
.nav-item:hover{background:rgba(255,255,255,.07);color:#fff;}
.nav-item.active{
  background:linear-gradient(90deg,rgba(0,212,255,.2),rgba(99,102,241,.1));
  color:#fff;font-weight:600;
}
.nav-item.active::before{
  content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);
  width:3px;height:18px;border-radius:0 3px 3px 0;
  background:linear-gradient(180deg,var(--cyan),var(--violet));
  box-shadow:0 0 10px rgba(0,212,255,.9);
}
.ni-icon{
  width:20px;height:20px;flex-shrink:0;display:flex;
  align-items:center;justify-content:center;
}
.nav-item.active .ni-icon{color:var(--cyan);}
.ni-text{flex:1;overflow:hidden;text-overflow:ellipsis;}
.ni-badge{
  font-size:9.5px;padding:1.5px 6px;border-radius:20px;
  background:rgba(255,255,255,.12);color:rgba(255,255,255,.7);font-weight:600;
}
.ni-badge.hot{
  background:linear-gradient(90deg,var(--cyan),var(--violet));
  color:#fff;animation:pulseB 2s infinite;
}
@keyframes pulseB{0%,100%{opacity:1;}50%{opacity:.55;}}

.sidebar-foot{padding:12px;border-top:1px solid rgba(255,255,255,.07);flex-shrink:0;}
.credit-box{
  background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.08);
  border-radius:12px;padding:11px 13px;
}
.credit-row{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;}
.credit-row span{font-size:11px;color:rgba(255,255,255,.5);}
.credit-row b{font-size:15px;color:var(--cyan);font-weight:700;}
.credit-bar{height:4px;background:rgba(255,255,255,.1);border-radius:2px;overflow:hidden;}
.credit-bar i{
  display:block;height:100%;border-radius:2px;
  background:linear-gradient(90deg,var(--cyan),var(--violet));
  box-shadow:0 0 8px rgba(0,212,255,.6);
}
.credit-tip{font-size:10px;color:rgba(255,255,255,.35);margin-top:7px;}

/* ---------- 主区 ---------- */
.main{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0;box-shadow:inset 0 0 0 100vmax rgba(0,0,0,var(--bg-overlay));}

.topbar{
  height:var(--topbar-h);flex-shrink:0;
  background:rgba(var(--surface-rgb), var(--card-alpha));border-bottom:var(--bd);
  display:flex;align-items:center;gap:20px;padding:0 22px;
  backdrop-filter:blur(var(--card-blur));
  -webkit-backdrop-filter:blur(var(--card-blur));
}
.topbar-left{display:flex;align-items:center;gap:12px;flex-shrink:0;}
.crumb{display:flex;align-items:center;gap:7px;font-size:13px;}
.crumb-root{color:var(--text-3);}
.crumb-sep{color:var(--border);}
.crumb-cur{color:var(--text);font-weight:600;}

.topbar-center{flex:1;display:flex;justify-content:center;min-width:0;}
.gsearch{
  width:100%;max-width:420px;display:flex;align-items:center;gap:9px;
  background:var(--surface-2);border:var(--bd);
  border-radius:10px;padding:0 12px;height:38px;
  transition:all .2s;cursor:pointer;
  overflow:hidden;            /* 防止 kbd 溢出到刷新按钮上 */
}
.gsearch:hover{border-color:var(--cyan);background:#fff;box-shadow:var(--sh-glow);}
.gsearch svg{color:var(--text-3);flex-shrink:0;}
.gsearch input{
  flex:1;min-width:0;          /* ⚠️ 关键: flex 子项默认 min-width:auto,
                                  输入框会坚持不收缩, 把右侧 <kbd> 挤出容器裁切。
                                  设 min-width:0 后输入框才会让步并显示省略号。 */
  border:none;background:transparent;outline:none;
  font-size:13px;color:var(--text);font-family:inherit;cursor:pointer;
  text-overflow:ellipsis;
}
.gsearch input::placeholder{color:var(--text-3);overflow:hidden;text-overflow:ellipsis;}
.gsearch kbd,
.sp-input kbd{
  font-size:10px;padding:2px 6px;background:var(--surface);
  border:var(--bd);border-radius:5px;color:var(--text-3);
  font-family:inherit;flex-shrink:0;
}

.topbar-right{display:flex;align-items:center;gap:10px;flex-shrink:0;}
.icon-btn{
  width:36px;height:36px;border:var(--bd);background:var(--surface);
  border-radius:9px;cursor:pointer;display:flex;align-items:center;
  justify-content:center;color:var(--text-2);transition:all .2s;position:relative;
}
.icon-btn:hover{background:var(--surface-2);border-color:var(--cyan);color:var(--cyan);}
.icon-btn.has-dot::after{
  content:"";position:absolute;top:8px;right:9px;width:7px;height:7px;
  background:var(--danger);border-radius:50%;border:2px solid var(--surface);
}
.engine-status{
  display:flex;align-items:center;gap:7px;
  background:linear-gradient(90deg,rgba(16,185,129,.1),rgba(0,212,255,.08));
  border:1px solid rgba(16,185,129,.25);
  padding:7px 13px;border-radius:20px;font-size:12px;color:var(--success);font-weight:600;
}
/* 状态徽标的内容由 JS 异步填充(如 #aiStatus), 未填充时不应留下一个空白药丸 */
.engine-status:empty{display:none;}
.pulse{
  width:7px;height:7px;background:var(--success);border-radius:50%;
  animation:pulseD 1.6s infinite;
}
@keyframes pulseD{
  0%{box-shadow:0 0 0 0 rgba(16,185,129,.7);}
  70%{box-shadow:0 0 0 7px rgba(16,185,129,0);}
  100%{box-shadow:0 0 0 0 rgba(16,185,129,0);}
}

/* 云端同步状态提示 */
.cloud-status{
  display:flex;align-items:center;gap:6px;
  padding:5px 10px;border-radius:999px;font-size:12px;font-weight:600;
  background:rgba(120,120,120,.12);color:rgba(255,255,255,.55);
  border:1px solid rgba(255,255,255,.1);cursor:pointer;transition:.2s;
  user-select:none;
}
.cloud-status::before{
  content:"";width:7px;height:7px;border-radius:50%;
  background:currentColor;box-shadow:0 0 6px currentColor;
}
.cloud-status:hover{background:rgba(120,120,120,.2);}
.cloud-status.online{background:rgba(0,212,255,.12);color:var(--cyan);border-color:rgba(0,212,255,.35);}
.cloud-status.local{background:rgba(250,204,21,.12);color:#facc15;border-color:rgba(250,204,21,.35);}
.cloud-status.offline{background:rgba(248,113,113,.12);color:#f87171;border-color:rgba(248,113,113,.35);}
.cloud-status.syncing{background:rgba(167,139,250,.14);color:#a78bfa;border-color:rgba(167,139,250,.4);}
.cloud-status.syncing::before{animation:csPulse 1s ease-in-out infinite;}
@keyframes csPulse{0%,100%{opacity:1;transform:scale(1);}50%{opacity:.35;transform:scale(.75);}}

/* ---------- 云端同步面板(设置页) ---------- */
.sync-card{margin-top:14px;padding:14px;border-radius:12px;background:var(--surface-2);border:1px solid var(--border,rgba(255,255,255,.08));}
.sync-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;}
.sync-title{font-size:13.5px;font-weight:700;color:var(--text-1);display:flex;align-items:center;gap:8px;}
.sync-badge{font-size:11.5px;font-weight:700;padding:3px 9px;border-radius:999px;background:rgba(120,120,120,.14);color:var(--text-3);}
.sync-badge.ok{background:rgba(0,212,255,.14);color:var(--cyan);}
.sync-badge.warn{background:rgba(250,204,21,.14);color:#facc15;}
.sync-badge.err{background:rgba(248,113,113,.14);color:#f87171;}
.sync-badge.busy{background:rgba(167,139,250,.14);color:#a78bfa;}
.sync-meta{margin-top:10px;font-size:12px;line-height:1.9;color:var(--text-3);}
.sync-meta b{color:var(--text-2);font-weight:600;}
.sync-actions{margin-top:12px;display:flex;gap:8px;flex-wrap:wrap;align-items:center;}
.sync-msg{margin-top:9px;font-size:12.5px;min-height:18px;line-height:1.6;}

.avatar{
  width:36px;height:36px;border-radius:50%;
  background:linear-gradient(135deg,var(--navy),var(--violet));
  color:#fff;display:flex;align-items:center;justify-content:center;
  font-size:14px;font-weight:700;cursor:pointer;flex-shrink:0;
  box-shadow:var(--sh-sm);
}
.mobile-toggle{display:none;}

/* 移动端抽屉遮罩：默认不显示，仅 ≤820px 侧栏展开时由 JS 加 .show */
.sidebar-mask{display:none;}

/* ---------- 内容区 ---------- */
.content{
  flex:1;overflow-y:auto;padding:22px;position:relative;
  animation:fadeUp .35s var(--ease);
  /* 界面缩放（2026-10-01）：按 --ui-zoom 整体放大内容区（含 JS 行内字号）。
     宽度补偿：zoom 会同步放大元素的占位宽度，若不除以倍率，
     放大后内容会超出容器右边界被裁切。divisor 默认为 1（不支持 zoom 的旧浏览器
     会忽略整个 zoom 声明，此处 calc 仍成立，不产生副作用）。 */
  zoom: var(--ui-zoom, 1);
  width: calc(100% / var(--ui-zoom, 1));
}
@keyframes fadeUp{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:none;}}

/* ============ 通用组件 ============ */

/* 页头 */
.page-head{
  display:flex;align-items:flex-end;justify-content:space-between;
  gap:20px;margin-bottom:20px;flex-wrap:wrap;
}
.page-title{font-size:22px;font-weight:700;letter-spacing:-.3px;}
.page-sub{font-size:13px;color:var(--text-3);margin-top:3px;}
/* 每页一行操作建议（新同事上手用）。可在 设置 → 外观 关闭：body.hints-off 时整体隐藏。
   挂载点在 .page-head 之后，由 app.js 的 ensurePageHint() 注入（文案表在 app.js PAGE_HINTS）。 */
.page-hint{
  display:flex;align-items:flex-start;gap:8px;
  margin:-8px 0 18px;padding:10px 14px;border-radius:10px;
  background:var(--surface-2);border:var(--bd);
  border-left:3px solid var(--cyan);
  font-size:12.5px;line-height:1.65;color:var(--text-2);
}
.page-hint .ph-ic{flex-shrink:0;font-size:13px;line-height:1.5;opacity:.9;}
.page-hint .ph-tx{min-width:0;}
.page-hint b{color:var(--text-1);font-weight:600;}
body.hints-off .page-hint{display:none !important;}
.page-actions{display:flex;gap:10px;flex-wrap:wrap;}

/* 按钮 */
.btn{
  display:inline-flex;align-items:center;gap:7px;
  padding:9px 17px;border-radius:10px;border:var(--bd);
  background:var(--surface);color:var(--text);font-size:13px;font-weight:600;
  cursor:pointer;transition:all .2s var(--ease);font-family:inherit;
  white-space:nowrap;
}
.btn:hover{transform:translateY(-1px);box-shadow:var(--sh-md);}
.btn:active{transform:translateY(0);}
.btn svg{flex-shrink:0;}
.btn-primary{
  background:linear-gradient(135deg,var(--navy),var(--violet));
  color:#fff;border:none;box-shadow:var(--sh-md);
}
.btn-primary:hover{box-shadow:var(--sh-lg),var(--sh-glow);}
.btn-cyan{
  background:linear-gradient(135deg,var(--cyan),var(--cyan-dim));
  color:#fff;border:none;box-shadow:0 4px 14px rgba(0,212,255,.3);
}
.btn-ghost{background:transparent;}
.btn-sm{padding:6px 12px;font-size:12px;border-radius:8px;}
.btn-block{width:100%;justify-content:center;}
.btn:disabled{opacity:.5;cursor:not-allowed;transform:none!important;}

/* 卡片 */
.card{
  background:rgba(var(--surface-rgb), var(--card-alpha));border:var(--bd);
  border-radius:var(--r-lg);box-shadow:var(--sh-xs);
  transition:all .25s var(--ease);
  backdrop-filter:blur(var(--card-blur));
  -webkit-backdrop-filter:blur(var(--card-blur));
}
.card:hover{box-shadow:var(--sh-md);}
.card-head{
  display:flex;align-items:center;justify-content:space-between;
  gap:14px;padding:16px 18px;border-bottom:var(--bd-2);
}
.card-title{font-size:14.5px;font-weight:700;display:flex;align-items:center;gap:8px;}
.card-title .dot{
  width:6px;height:6px;border-radius:50%;
  background:linear-gradient(135deg,var(--cyan),var(--violet));
}
.card-desc{font-size:12px;color:var(--text-3);font-weight:400;}
.card-body{padding:18px;}
.card-body.nopad{padding:0;}

/* 网格 */
.grid{display:grid;gap:16px;}
.g-2{grid-template-columns:repeat(2,1fr);}
.g-3{grid-template-columns:repeat(3,1fr);}
.g-4{grid-template-columns:repeat(4,1fr);}
.g-6{grid-template-columns:repeat(6,1fr);}
.g-2-1{grid-template-columns:2fr 1fr;}
.g-1-2{grid-template-columns:1fr 2fr;}

/* KPI 卡片 */
.kpi{
  background:rgba(var(--surface-rgb), var(--card-alpha));border:var(--bd);
  border-radius:var(--r-lg);padding:17px 18px;position:relative;
  overflow:hidden;transition:all .25s var(--ease);
  backdrop-filter:blur(var(--card-blur));
  -webkit-backdrop-filter:blur(var(--card-blur));
}
.kpi::before{
  content:"";position:absolute;top:0;left:0;right:0;height:3px;
  background:linear-gradient(90deg,var(--cyan),var(--violet));
  transform:scaleX(0);transform-origin:left;transition:transform .35s var(--ease);
}
.kpi:hover{transform:translateY(-3px);box-shadow:var(--sh-lg);border-color:transparent;}
.kpi:hover::before{transform:scaleX(1);}
.kpi-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:11px;}
.kpi-label{font-size:12.5px;color:var(--text-3);font-weight:500;}
.kpi-icon{
  width:34px;height:34px;border-radius:10px;display:flex;
  align-items:center;justify-content:center;flex-shrink:0;
}
.kpi-icon.cyan{background:linear-gradient(135deg,rgba(0,212,255,.14),rgba(0,212,255,.05));color:var(--cyan-dim);}
.kpi-icon.violet{background:linear-gradient(135deg,rgba(99,102,241,.14),rgba(99,102,241,.05));color:var(--violet);}
.kpi-icon.green{background:linear-gradient(135deg,rgba(16,185,129,.14),rgba(16,185,129,.05));color:var(--success);}
.kpi-icon.amber{background:linear-gradient(135deg,rgba(245,158,11,.14),rgba(245,158,11,.05));color:var(--warning);}
.kpi-icon.navy{background:linear-gradient(135deg,rgba(10,31,68,.12),rgba(10,31,68,.04));color:var(--navy);}
.kpi-icon.blue{background:linear-gradient(135deg,rgba(59,130,246,.14),rgba(59,130,246,.05));color:var(--info);}
.kpi-val{
  font-size:27px;font-weight:800;letter-spacing:-.8px;line-height:1.15;
  font-variant-numeric:tabular-nums;
}
.kpi-foot{display:flex;align-items:center;gap:8px;margin-top:9px;}
.kpi-trend{
  display:inline-flex;align-items:center;gap:3px;
  font-size:11.5px;font-weight:700;padding:2px 7px;border-radius:20px;
}
.kpi-trend.up{background:rgba(16,185,129,.1);color:var(--success);}
.kpi-trend.down{background:rgba(239,68,68,.1);color:var(--danger);}
.kpi-note{font-size:11.5px;color:var(--text-3);}
.kpi-spark{height:34px;margin-top:10px;}

/* KPI 强调色版（数据大屏 · 真实数据）：左侧色条 + 图标胶囊，每张卡一种语义色 */
.kpi-accent{--accent:#00D4FF;--accent-soft:rgba(0,212,255,.14);--accent-line:rgba(0,212,255,.34);}
.kpi-accent .kpi-bar{
  position:absolute;left:0;top:0;bottom:0;width:3px;
  background:linear-gradient(180deg,var(--accent),transparent 94%);
  box-shadow:0 0 10px var(--accent);
}
.kpi-accent::before{background:linear-gradient(90deg,var(--accent),transparent);}
.kpi-accent .kpi-ic{
  width:30px;height:30px;border-radius:9px;flex-shrink:0;
  display:flex;align-items:center;justify-content:center;
  font-size:15px;line-height:1;
  background:var(--accent-soft);border:1px solid var(--accent-line);
}
.kpi-accent .kpi-val{display:flex;align-items:baseline;gap:4px;color:var(--accent);}
.kpi-accent .kpi-val span{font-size:13px;font-weight:600;color:var(--text-3);}

/* 徽章 */
.badge{
  display:inline-flex;align-items:center;gap:4px;
  font-size:11px;font-weight:600;padding:3px 9px;border-radius:20px;
}
.badge-green{background:rgba(16,185,129,.1);color:var(--success);}
.badge-amber{background:rgba(245,158,11,.1);color:var(--warning);}
.badge-red{background:rgba(239,68,68,.1);color:var(--danger);}
.badge-blue{background:rgba(59,130,246,.1);color:var(--info);}
.badge-gray{background:var(--surface-2);color:var(--text-3);}
.badge-violet{background:rgba(99,102,241,.1);color:var(--violet);}

/* 进度条 */
.prog{height:6px;background:var(--border-2);border-radius:3px;overflow:hidden;}
.prog i{
  display:block;height:100%;border-radius:3px;
  background:linear-gradient(90deg,var(--cyan),var(--violet));
  transition:width .8s var(--ease);
}
.prog.sm{height:4px;}

/* 知识库 · 版式切换 Tab */
.kb-tabs{display:flex;gap:6px;flex-wrap:wrap;margin:0 0 12px;padding:4px;background:var(--surface-2);border:var(--bd-2);border-radius:10px;}
.kb-tab{padding:6px 12px;font-size:12.5px;color:var(--text-3);background:transparent;border:1px solid transparent;border-radius:7px;cursor:pointer;font-family:inherit;transition:all .18s var(--ease);}
.kb-tab:hover{color:var(--text-1);}
.kb-tab.on{color:#fff;background:linear-gradient(90deg,var(--violet),var(--cyan));border-color:transparent;}
/* 知识填充程度：紫→蓝渐变进度条 */
.prog.kb-fill i{background:linear-gradient(90deg,#8B5CF6,#3B82F6);}

/* 表格 */
.tbl-wrap{overflow-x:auto;}
table.tbl{width:100%;border-collapse:collapse;font-size:13px;}
table.tbl th{
  text-align:left;padding:11px 14px;font-size:11.5px;font-weight:600;
  color:var(--text-3);background:var(--surface-2);
  border-bottom:var(--bd);white-space:nowrap;
  letter-spacing:.3px;
}
table.tbl td{
  padding:12px 14px;border-bottom:var(--bd-2);
  vertical-align:middle;
}
table.tbl tbody tr{transition:background .15s;}
table.tbl tbody tr:hover{background:var(--surface-2);}
table.tbl tbody tr:last-child td{border-bottom:none;}
.tbl-rank{
  width:22px;height:22px;border-radius:6px;display:flex;
  align-items:center;justify-content:center;font-size:11px;font-weight:700;
  background:var(--surface-2);color:var(--text-3);
}
.tbl-rank.top{background:linear-gradient(135deg,var(--cyan),var(--violet));color:#fff;}

/* 表单 */
.field{margin-bottom:15px;}
.field-label{
  display:block;font-size:12.5px;font-weight:600;color:var(--text-2);
  margin-bottom:7px;
}
.field-label .req{color:var(--danger);margin-left:2px;}
.field-hint{font-size:11.5px;color:var(--text-3);font-weight:400;margin-left:6px;}
.input,.select,.textarea{
  width:100%;padding:10px 13px;border:var(--bd);
  border-radius:10px;font-size:13px;font-family:inherit;color:var(--text);
  background:var(--surface);transition:all .2s;outline:none;
}
.input:focus,.select:focus,.textarea:focus{
  border-color:var(--cyan);box-shadow:0 0 0 3px rgba(0,212,255,.12);
}
.textarea{resize:vertical;min-height:88px;line-height:1.7;}
.select{cursor:pointer;appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394A3B8' stroke-width='3'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 13px center;padding-right:34px;}

/* 分段控制 */
.seg{display:inline-flex;background:var(--surface-2);border-radius:10px;padding:3px;gap:2px;}
.seg button{
  padding:6px 14px;border:none;background:transparent;border-radius:8px;
  font-size:12.5px;font-weight:600;color:var(--text-2);cursor:pointer;
  font-family:inherit;transition:all .2s;white-space:nowrap;
}
.seg button:hover{color:var(--text);}
.seg button.on{
  background:var(--surface);color:var(--navy);
  box-shadow:var(--sh-sm);
}

/* 标签页 */
.tabs{display:flex;gap:4px;border-bottom:var(--bd);margin-bottom:18px;}
.tabs button{
  padding:10px 16px;border:none;background:transparent;font-size:13.5px;
  font-weight:600;color:var(--text-3);cursor:pointer;font-family:inherit;
  border-bottom:2px solid transparent;transition:all .2s;margin-bottom:-1px;
  white-space:nowrap;
}
.tabs button:hover{color:var(--text-2);}
.tabs button.on{color:var(--navy);border-bottom-color:var(--cyan);}

/* 空状态 */
.empty{
  text-align:center;padding:52px 20px;color:var(--text-3);
}
.empty svg{opacity:.35;margin-bottom:14px;}
.empty p{font-size:13.5px;}

/* 引擎图标 */
.eng{
  width:30px;height:30px;border-radius:8px;display:flex;
  align-items:center;justify-content:center;font-size:12px;font-weight:800;
  color:#fff;flex-shrink:0;
}

/* 渠道卡 */
.ch-card{
  background:var(--surface);border:var(--bd);
  border-radius:var(--r-md);padding:14px;transition:all .25s var(--ease);
  position:relative;overflow:hidden;
}
.ch-card:hover{transform:translateY(-2px);box-shadow:var(--sh-md);border-color:var(--cyan);}
.ch-top{display:flex;align-items:center;gap:10px;margin-bottom:11px;}
.ch-name{font-size:13.5px;font-weight:700;}
.ch-meta{font-size:11.5px;color:var(--text-3);}
.ch-stats{display:flex;gap:14px;margin-bottom:10px;}
.ch-stat{flex:1;}
.ch-stat b{display:block;font-size:16px;font-weight:800;font-variant-numeric:tabular-nums;}
.ch-stat span{font-size:10.5px;color:var(--text-3);}

/* Toast */
#toastWrap{position:fixed;top:78px;right:24px;z-index:9998;display:flex;
  flex-direction:column;gap:10px;pointer-events:none;}
.toast{
  display:flex;align-items:center;gap:11px;
  background:rgba(var(--surface-rgb), var(--card-alpha));border:var(--bd);
  border-left:3px solid var(--cyan);
  border-radius:12px;padding:13px 17px;box-shadow:var(--sh-lg);
  font-size:13px;min-width:260px;max-width:380px;
  animation:slideIn .35s var(--ease);
  backdrop-filter:blur(var(--card-blur));
  -webkit-backdrop-filter:blur(var(--card-blur));
}
.toast.out{animation:slideOut .3s var(--ease) forwards;}
.toast.success{border-left-color:var(--success);}
.toast.error{border-left-color:var(--danger);}
.toast.warn{border-left-color:var(--warning);}
.toast-icon{
  width:22px;height:22px;border-radius:50%;flex-shrink:0;
  display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;color:#fff;
}
.toast.success .toast-icon{background:var(--success);}
.toast.error .toast-icon{background:var(--danger);}
.toast.warn .toast-icon{background:var(--warning);}
.toast:not(.success):not(.error):not(.warn) .toast-icon{background:var(--cyan);}
.toast b{display:block;font-size:13px;}
.toast span{font-size:11.5px;color:var(--text-3);}
@keyframes slideIn{from{opacity:0;transform:translateX(40px);}to{opacity:1;transform:none;}}
@keyframes slideOut{to{opacity:0;transform:translateX(40px);}}

/* 模态 */
.modal{position:fixed;inset:0;z-index:9990;display:none;
  align-items:flex-start;justify-content:center;padding-top:11vh;}
.modal.show{display:flex;}
.modal-mask{position:absolute;inset:0;background:rgba(15,23,42,.42);
  backdrop-filter:blur(3px);animation:fadeIn .2s;}
@keyframes fadeIn{from{opacity:0;}to{opacity:1;}}
.search-panel{
  position:relative;width:min(620px,92vw);background:rgba(var(--surface-rgb), var(--card-alpha));
  border-radius:var(--r-lg);box-shadow:var(--sh-lg);overflow:hidden;
  animation:popIn .25s var(--ease);
  backdrop-filter:blur(var(--card-blur));
  -webkit-backdrop-filter:blur(var(--card-blur));
}
@keyframes popIn{from{opacity:0;transform:scale(.96) translateY(-10px);}to{opacity:1;transform:none;}}
.sp-input{
  display:flex;align-items:center;gap:11px;padding:15px 18px;
  border-bottom:var(--bd);
}
.sp-input svg{color:var(--text-3);flex-shrink:0;}
.sp-input input{
  flex:1;border:none;outline:none;font-size:15px;font-family:inherit;color:var(--text);
}
.sp-results{max-height:340px;overflow-y:auto;padding:7px;}
.spr-item{
  display:flex;align-items:center;gap:12px;padding:11px 13px;
  border-radius:10px;cursor:pointer;transition:background .15s;
}
.spr-item:hover{background:var(--surface-2);}
.spr-item .spr-ic{
  width:32px;height:32px;border-radius:9px;flex-shrink:0;
  background:linear-gradient(135deg,rgba(0,212,255,.13),rgba(99,102,241,.09));
  color:var(--cyan-dim);display:flex;align-items:center;justify-content:center;
}
.spr-item b{font-size:13.5px;display:block;}
.spr-item span{font-size:11.5px;color:var(--text-3);}

/* 检测扫描动画 */
.scan-box{
  background:linear-gradient(135deg,var(--navy),#14356F);
  border-radius:var(--r-lg);padding:34px;color:#fff;text-align:center;position:relative;overflow:hidden;
}
.scan-box::before{
  content:"";position:absolute;inset:0;
  background:radial-gradient(circle at 50% 50%,rgba(0,212,255,.18),transparent 62%);
}
.scan-ring{
  width:110px;height:110px;margin:0 auto 20px;position:relative;
}
.scan-ring::before,.scan-ring::after{
  content:"";position:absolute;inset:0;border-radius:50%;
  border:2px solid rgba(0,212,255,.35);
}
.scan-ring::before{animation:scanR 2s linear infinite;}
.scan-ring::after{animation:scanR 2s linear infinite .9s;}
.scan-ring span{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:26px;font-weight:800;color:var(--cyan);font-variant-numeric:tabular-nums;
}
@keyframes scanR{
  0%{transform:scale(.72);opacity:1;}
  100%{transform:scale(1.5);opacity:0;}
}
.scan-title{font-size:16px;font-weight:700;margin-bottom:6px;position:relative;}
.scan-sub{font-size:12.5px;color:rgba(255,255,255,.55);position:relative;}

/* 引擎检测行 */
.det-row{
  display:flex;align-items:center;gap:13px;padding:13px 16px;
  border-bottom:var(--bd-2);transition:background .2s;
}
.det-row:last-child{border-bottom:none;}
.det-row:hover{background:var(--surface-2);}
.det-name{flex:1;min-width:0;}
.det-name b{display:block;font-size:13.5px;margin-bottom:3px;}
.det-name span{font-size:11.5px;color:var(--text-3);}
.det-bar{width:130px;flex-shrink:0;}
.det-num{
  width:78px;text-align:right;font-size:14px;font-weight:800;
  font-variant-numeric:tabular-nums;flex-shrink:0;
}
.det-status{width:66px;text-align:right;flex-shrink:0;}

/* 企业知识库 · 删除按钮 */
.kb-del:hover{color:var(--danger);}

/* 企业数据库模块 */
.db-stat{background:var(--surface-2);border:var(--bd);border-radius:10px;padding:14px 16px;display:flex;flex-direction:column;gap:4px}
.db-stat b{font-size:22px;color:var(--cyan)}
.db-stat span{font-size:12px;color:var(--text-3)}
.db-chips{display:flex;flex-wrap:wrap;gap:6px}
.db-chip{font-size:12px;padding:4px 10px;border:var(--bd);border-radius:20px;color:var(--text-2);cursor:pointer;background:var(--surface-1);user-select:none}
.db-chip.on{background:rgba(0,212,255,.16);border-color:var(--cyan);color:var(--cyan)}
.db-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;min-height:120px;padding:6px;border:1px dashed transparent;border-radius:12px;transition:.2s}
.db-grid.drag{border-color:var(--cyan);background:rgba(0,212,255,.06)}
.db-card{background:var(--surface-2);border:var(--bd);border-radius:12px;overflow:hidden;display:flex;flex-direction:column}
.db-thumb{height:120px;display:flex;align-items:center;justify-content:center;background:#0b1830;color:var(--text-3);font-size:13px;overflow:hidden}
.db-thumb img{width:100%;height:100%;object-fit:cover}
.db-video{max-width:100%;max-height:120px}
.db-doc{color:var(--cyan);text-decoration:none}
.db-ph{padding:8px}
.db-meta{padding:8px 10px;display:flex;flex-direction:column;gap:2px}
.db-meta b{font-size:13px;color:var(--text-1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.db-meta span{font-size:11px;color:var(--text-3)}
.db-ops{display:flex;gap:6px;padding:0 10px 10px}
.db-ops .btn{flex:1}
.input-sm{height:30px;padding:0 10px;border:var(--bd);border-radius:8px;background:var(--surface-1);color:var(--text-1);font-size:12px;outline:none}
.input-sm:focus{border-color:var(--cyan)}

/* 文章卡 */
.art-card{
  background:var(--surface);border:var(--bd);
  border-radius:var(--r-md);padding:15px;transition:all .22s var(--ease);
  cursor:pointer;position:relative;
}
.art-card:hover{border-color:var(--cyan);box-shadow:var(--sh-md);transform:translateY(-2px);}
.art-card.sel{border-color:var(--cyan);background:rgba(0,212,255,.035);}
.art-chk{
  position:absolute;top:12px;right:12px;width:19px;height:19px;
  border:2px solid var(--border);border-radius:6px;background:var(--surface);
  display:flex;align-items:center;justify-content:center;transition:all .2s;
}
.art-card.sel .art-chk{background:var(--cyan);border-color:var(--cyan);color:#fff;}
.art-title{font-size:13.5px;font-weight:700;line-height:1.5;margin-bottom:8px;padding-right:26px;}
.art-tags{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:9px;}
.art-tag{
  font-size:10.5px;padding:2px 7px;border-radius:5px;
  background:var(--surface-2);color:var(--text-3);
}
.art-meta{display:flex;gap:12px;font-size:11px;color:var(--text-3);}

/* 时间线 */
.tl{position:relative;padding-left:22px;}
.tl::before{
  content:"";position:absolute;left:5px;top:6px;bottom:6px;width:2px;
  background:linear-gradient(180deg,var(--cyan),var(--border));
}
.tl-item{position:relative;padding-bottom:17px;}
.tl-item:last-child{padding-bottom:0;}
.tl-item::before{
  content:"";position:absolute;left:-21px;top:5px;width:10px;height:10px;
  border-radius:50%;background:var(--surface);border:2px solid var(--cyan);
}
.tl-item.ok::before{background:var(--success);border-color:var(--success);}
.tl-item.warn::before{background:var(--warning);border-color:var(--warning);}
.tl-time{font-size:10.5px;color:var(--text-3);margin-bottom:2px;}
.tl-text{font-size:12.5px;color:var(--text-2);line-height:1.5;}

/* 图表容器 */
.chart-box{position:relative;width:100%;}
.h-200{height:200px;}
.h-240{height:240px;}
.h-280{height:280px;}
.h-320{height:320px;}
.h-360{height:360px;}

/* 工具条 */
.toolbar{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:13px 18px;border-bottom:var(--bd-2);
}
.toolbar .spacer{flex:1;}

/* 统计条 */
.stat-strip{
  display:grid;grid-template-columns:repeat(4,1fr);gap:1px;
  background:var(--border-2);border-radius:var(--r-md);overflow:hidden;
}
.stat-cell{background:var(--surface);padding:13px 15px;text-align:center;}
.stat-cell b{display:block;font-size:18px;font-weight:800;font-variant-numeric:tabular-nums;}
.stat-cell span{font-size:11px;color:var(--text-3);}

/* 响应式 */
@media(max-width:1400px){
  .g-6{grid-template-columns:repeat(3,1fr);}
  .g-4{grid-template-columns:repeat(2,1fr);}
}
@media(max-width:1100px){
  .g-3,.g-2-1,.g-1-2{grid-template-columns:1fr;}
  .topbar-center{display:none;}
}
@media(max-width:820px){
  .sidebar{position:fixed;left:0;top:0;bottom:0;transform:translateX(-100%);
    transition:transform .3s var(--ease);
    /* z-index 必须高于顶栏的 50。基础样式里侧栏是 100，但桌面端侧栏是
       普通流内元素、顶栏在它右边，两者不重叠；一旦变成 fixed 浮层，
       248px 宽的抽屉会横跨到顶栏上方，层级必须写死且足够高。 */
    z-index:9100;}
  .sidebar.open{transform:translateX(0);}
  /* 遮罩层：压在顶栏与正文之上、侧栏之下（9000 < 9100），
     高于 AI 悬浮球(8888)，低于 Toast(9998)/Modal(9990)/Loader(9999)。 */
  .sidebar-mask{display:block;position:fixed;inset:0;z-index:9000;
    background:rgba(2,6,16,.52);
    backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);
    opacity:0;visibility:hidden;pointer-events:none;
    transition:opacity .3s var(--ease),visibility .3s var(--ease);}
  .sidebar-mask.show{opacity:1;visibility:visible;pointer-events:auto;}
  /* 抽屉展开时锁住背景滚动 */
  body.drawer-open{overflow:hidden;}
  .mobile-toggle{display:flex;}
  .g-2,.g-4,.g-6{grid-template-columns:1fr;}
  .content{padding:16px;}
  .stat-strip{grid-template-columns:repeat(2,1fr);}
}

/* ===== AI 生成 · 骨架屏 + 流式渐显 ===== */
.sk-card{
  background:var(--surface);border:var(--bd-2);border-radius:var(--r-md);
  padding:16px;overflow:hidden;
}
.sk-line{
  height:11px;border-radius:6px;margin-bottom:10px;
  background:linear-gradient(90deg,rgba(255,255,255,.04) 25%,rgba(255,255,255,.11) 37%,rgba(255,255,255,.04) 63%);
  background-size:400% 100%;animation:sk-shimmer 1.3s ease infinite;
}
.sk-line.w90{width:90%;}.sk-line.w70{width:70%;}.sk-line.w55{width:55%;}.sk-line.w40{width:40%;}.sk-line.w30{width:30%;}
@keyframes sk-shimmer{0%{background-position:100% 0}100%{background-position:-100% 0}}

.reveal{opacity:0;transform:translateY(12px);animation:revealIn .42s var(--ease) forwards;}
@keyframes revealIn{to{opacity:1;transform:none}}

/* 知识库改写中的流光遮罩 */
.kb-shimmer{position:relative;}

/* ===== 多客户作战台 · 顶栏切换器 ===== */
.client-switcher{position:relative;}
.cs-btn{
  display:flex;align-items:center;gap:8px;height:34px;padding:0 12px;
  background:var(--surface);border:var(--bd-2);border-radius:10px;
  color:var(--text);font-size:13px;cursor:pointer;transition:.18s var(--ease);white-space:nowrap;
}
.cs-btn:hover{border-color:var(--border);box-shadow:0 2px 10px rgba(0,0,0,.12);}
.cs-dot{width:9px;height:9px;border-radius:50%;background:var(--cyan);flex:none;}
.cs-name{max-width:140px;overflow:hidden;text-overflow:ellipsis;}
.cs-menu{
  position:absolute;top:42px;left:0;min-width:268px;max-height:380px;overflow:auto;
  background:var(--surface);border:var(--bd-2);border-radius:12px;
  box-shadow:0 16px 40px rgba(0,0,0,.22);padding:6px;z-index:60;display:none;
}
.cs-menu.show{display:block;animation:revealIn .18s var(--ease);}
.cs-item{
  display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:9px;cursor:pointer;
}
.cs-item:hover{background:rgba(255,255,255,.06);}
.cs-item.on{background:rgba(0,212,255,.12);}
.cs-item .cs-meta{display:flex;flex-direction:column;line-height:1.35;min-width:0;flex:1;}
.cs-item .cs-meta b{font-size:13px;font-weight:500;color:var(--text);}
.cs-item .cs-meta span{font-size:11px;color:var(--text-3);}
.cs-check{color:var(--cyan);font-style:normal;font-size:13px;}
.cs-add{border-top:var(--bd-2);margin-top:4px;}

/* ===== 运营总览表 ===== */
.agency-table{width:100%;border-collapse:collapse;font-size:13px;}
.agency-table th,.agency-table td{padding:12px 14px;text-align:left;border-bottom:var(--bd-2);}
.agency-table th{font-size:11.5px;color:var(--text-3);font-weight:500;letter-spacing:.4px;text-transform:uppercase;}
.agency-table tbody tr{cursor:pointer;transition:.15s var(--ease);}
.agency-table tbody tr:hover{background:rgba(255,255,255,.05);}
.agency-table tbody tr.on{background:rgba(0,212,255,.10);}
.agency-table .cs-dot{width:9px;height:9px;border-radius:50%;display:inline-block;margin-right:8px;vertical-align:middle;}
.hp-badge{display:inline-block;min-width:30px;text-align:center;padding:2px 8px;border-radius:20px;font-weight:500;font-size:12px;}
.hp-green{background:rgba(99,153,34,.16);color:#8bc34a;}
.hp-amber{background:rgba(186,117,23,.16);color:#e0a83c;}
.hp-red{background:rgba(226,75,74,.16);color:#f08a8a;}
.st-live{color:var(--cyan);font-size:12px;}
.st-idle{color:var(--text-3);font-size:12px;}
.kb-shimmer::after{
  content:"";position:absolute;inset:0;border-radius:var(--r-md);pointer-events:none;
  background:linear-gradient(90deg,transparent 0%,rgba(0,212,255,.10) 50%,transparent 100%);
  background-size:300% 100%;animation:sk-shimmer 1.4s ease infinite;
}

/* ===== 新增客户弹层 ===== */
.modal{
  position:fixed;inset:0;z-index:100;display:none;align-items:center;justify-content:center;
  background:rgba(0,0,0,.55);backdrop-filter:blur(4px);
  /* ⚠️ padding 不可删：面板的 max-height:100% 与 width:100% 都靠它留出安全边距。
     删掉后长弹窗会顶到视口边缘，高度一旦超限就再也够不到底部的按钮。 */
  padding:22px;
}
.modal.show{display:flex;animation:fadeIn .18s var(--ease);}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.modal-panel{
  /* ⚠️ 宽度必须用 100%（= 容器宽 = 视口 - 两侧 padding），不能用 vw：
     92vw 在窄屏会大于容器可用宽 → 横向溢出且滚不到。要更宽的面板请覆盖
     max-width（如 style="max-width:760px"），永远不要覆盖 width。
     ⚠️ max-height + flex 纵向布局 + .modal-body 的 overflow:auto，三者缺一不可。
     缺任一条件，内容超过一屏时面板会上下被裁且完全无法滚动，
     底部的「保存」按钮永远点不到 —— 引擎授权弹窗（6 个引擎）踩过这个坑。 */
  width:100%;max-width:420px;
  max-height:100%;display:flex;flex-direction:column;
  background:rgba(var(--surface-rgb), var(--card-alpha));border:var(--bd-2);border-radius:var(--r-lg);
  box-shadow:0 24px 60px rgba(0,0,0,.35);overflow:hidden;
  backdrop-filter:blur(var(--card-blur));
  -webkit-backdrop-filter:blur(var(--card-blur));
}
.modal-head{
  display:flex;align-items:center;justify-content:space-between;padding:16px 18px 10px;
  border-bottom:var(--bd-2);
  flex:0 0 auto;
}
.modal-head h3{margin:0;font-size:16px;font-weight:600;color:var(--text);}
.modal-close{
  width:30px;height:30px;border:none;background:transparent;color:var(--text-2);
  font-size:22px;line-height:1;cursor:pointer;border-radius:8px;transition:.15s;
}
.modal-close:hover{background:rgba(255,255,255,.08);color:var(--text);}
.modal-body{padding:18px;overflow:auto;flex:1 1 auto;min-height:0;overscroll-behavior:contain;}
.modal-body .field{margin-bottom:14px;}
.modal-body .field label{display:block;font-size:12.5px;color:var(--text-3);margin-bottom:6px;}
.modal-body .field .input{width:100%;box-sizing:border-box;}
.modal-body .field textarea.input{min-height:80px;resize:vertical;font-family:inherit;}
.kw-box{border:0.5px solid var(--border);border-radius:var(--r-sm);background:var(--surface-2);padding:10px 12px;max-height:170px;overflow:auto;}
.kw-row{display:flex;gap:8px;align-items:flex-start;padding:5px 0;border-bottom:0.5px dashed var(--border-2);font-size:12.5px;line-height:1.5;}
.kw-row:last-child{border-bottom:none;}
.kw-row b{flex:0 0 52px;color:var(--cyan-dim);font-weight:500;}
.kw-row span{color:var(--text-2);}
.kw-empty{color:var(--text-3);font-size:12.5px;padding:6px 0;}
.modal-body .field .input:disabled{background:var(--bg-2);color:var(--text-2);opacity:.8;}
.modal-foot{
  display:flex;justify-content:flex-end;gap:10px;padding:0 18px 18px;
  flex:0 0 auto;
}
/* 窄屏：留白收窄，面板占满可用宽（配合 .modal-panel 的 width:100%） */
@media(max-width:820px){
  .modal{padding:10px;}
  .modal-panel{max-width:100%;}
}
/* 引擎授权弹窗：把「接口地址 / 模型名」收进折叠区。
   6 个引擎全展开约 1600px 高，远超一屏 —— 收起后主区只剩 API Key，弹窗自然变矮。
   用原生 <details>/<summary>，零 JS，且键盘可达。 */
.eng-adv,.eng-note{margin-top:10px;border-top:1px dashed var(--border-2);padding-top:9px;}
.eng-adv>summary,.eng-note>summary{cursor:pointer;font-size:12px;color:var(--text-3);
  list-style:none;user-select:none;display:inline-flex;align-items:center;gap:5px;
  transition:color .15s;}
.eng-adv>summary::-webkit-details-marker,.eng-note>summary::-webkit-details-marker{display:none;}
.eng-adv>summary::before,.eng-note>summary::before{content:"\25B8";font-size:10px;color:var(--cyan-dim);
  display:inline-block;transition:transform .15s;}
.eng-adv[open]>summary::before,.eng-note[open]>summary::before{transform:rotate(90deg);}
.eng-adv>summary:hover,.eng-note>summary:hover{color:var(--text-2);}
.eng-adv .grid{margin-top:10px;}

/* 让顶栏切换按钮始终可点，避免被右侧元素盖住 */
.client-switcher{z-index:55;}
.cs-menu{z-index:60;}
.topbar{position:relative;z-index:50;}

/* 客户下拉分隔线 */
.cs-sep{height:1px;background:var(--border-2);margin:4px 8px;}

/* ==========================================================
   科技主题 / 自定义背景 / 动态图标 / AI 语音助手
   ========================================================== */

/* ---------- 深色科技主题变量 ---------- */
body.theme-tech{
  --bg:#050b14;
  --bg-2:#08101c;
  --surface:rgba(10,22,40,.72);
  --surface-1:rgba(10,22,40,.72);
  --surface-rgb:10,22,40;
  --surface-2:rgba(16,32,56,.55);
  --border:rgba(0,212,255,.13);
  --border-2:rgba(0,212,255,.07);
  --text:#e2e8f0;
  --text-1:#e2e8f0;
  --text-2:#94a3b8;
  --text-3:#64748b;
  --card-alpha:.72;
  --bg-overlay:0;
  --card-blur: calc((1 - var(--card-alpha)) * 18px);
  --sh-xs:0 1px 2px rgba(0,0,0,.25);
  --sh-sm:0 2px 8px rgba(0,0,0,.35);
  --sh-md:0 8px 24px rgba(0,0,0,.42);
  --sh-lg:0 16px 48px rgba(0,0,0,.55);
  --sh-glow:0 8px var(--glow-blur) rgba(0,212,255,.18);

  /* ---------- 强调色 token（深色取值）----------
     深色底上，浅色主题那套「深绿/深橙文字」会发黑看不清，这里整体提亮，
     并让主操作渐变偏青紫（与深海蓝背景同色系，而不是荧光绿）。 */
  --accent-a:      #22D3EE;
  --accent-b:      #818CF8;
  --accent-a-ink:  #67E8F9;
  --accent-soft:   rgba(34,211,238,.16);
  --accent-line:   rgba(34,211,238,.42);
  --brand-4:       #A78BFA;
  --success-ink:   #6EE7B7;
  --warning-ink:   #FCD34D;
  --info-ink:      #A5B4FC;

  --chart-grid:    rgba(148,163,184,.14);
  --chart-axis:    #94A3B8;
  --chart-track:   rgba(148,163,184,.18);
  --chart-plain:   #475569;
}

/* ---------- 深色主题下的通用修补（2026-10-01）----------
   起因：页面里有一批「浅色主题硬编码」（#fff 底、#F1F5F9 网格、浅底提示框），
   在深色下不是看不见就是刺眼。这里用**低特异性的 token 化修补**收掉最常见的三类，
   不去逐个改页面 —— 页面改动越少，回归面越小。 */
body.theme-tech .ch-card button[data-act="portal"],
body.theme-tech .ch-card button[data-act="copy"]{
  background:var(--surface-2)!important;color:var(--text-2)!important;
  border-color:var(--border)!important;
}
body.theme-tech .ch-card button[data-act="copy"]{color:var(--accent-a-ink)!important;}
body.theme-tech .ch-card button[data-act="portal"]:hover:not([disabled]),
body.theme-tech .ch-card button[data-act="copy"]:hover{background:var(--surface)!important;}
/* 状态色文字：深底上提亮，避免「深绿写在深蓝上」发黑 */
body.theme-tech .badge-green{color:var(--success-ink);background:rgba(16,185,129,.14);}
body.theme-tech .badge-amber{color:var(--warning-ink);background:rgba(245,158,11,.14);}
body.theme-tech ::-webkit-scrollbar-thumb{background:#1e3a5f;border-radius:4px;}
body.theme-tech ::-webkit-scrollbar-thumb:hover{background:#2a4a75;}

body.theme-tech #main{background-color:var(--bg);color:var(--text);}
body.theme-tech .topbar,
body.theme-tech .card,
body.theme-tech .kpi,
body.theme-tech .ch-card,
body.theme-tech .art-card,
body.theme-tech .search-panel,
body.theme-tech .modal-panel,
body.theme-tech .toast{
  border-color:var(--border);
}
body.theme-tech .card-head,
body.theme-tech .toolbar{border-color:var(--border);}
body.theme-tech .gsearch,
body.theme-tech .input,
body.theme-tech .select,
body.theme-tech .textarea,
body.theme-tech .seg,
body.theme-tech .sp-input,
body.theme-tech .credit-box{
  background:var(--surface-2);
  border-color:var(--border);
  color:var(--text);
}
body.theme-tech .gsearch:hover,
body.theme-tech .input:focus,
body.theme-tech .select:focus,
body.theme-tech .textarea:focus{
  background:var(--surface);
  border-color:var(--cyan);
  box-shadow:0 0 0 3px rgba(0,212,255,.12);
}
body.theme-tech .btn{background:var(--surface-2);color:var(--text);border-color:var(--border);}
body.theme-tech .btn:hover{background:var(--surface);}
body.theme-tech .btn-primary{color:#fff;}
body.theme-tech .empty{color:var(--text-3);}
body.theme-tech table.tbl th{background:var(--surface-2);color:var(--text-2);}
body.theme-tech table.tbl td{border-color:var(--border-2);}
body.theme-tech table.tbl tbody tr:hover{background:rgba(255,255,255,.04);}
body.theme-tech .tabs{border-color:var(--border);}
body.theme-tech .tabs button{color:var(--text-3);}
body.theme-tech .tabs button.on{color:var(--cyan);}
body.theme-tech .kpi:hover::before{transform:scaleX(1);}
body.theme-tech .kpi-icon{box-shadow:0 0 0 1px rgba(0,212,255,.1);}
body.theme-tech .cs-menu{background:var(--surface);border-color:var(--border);}
body.theme-tech .cs-item.on{background:rgba(0,212,255,.12);}

/* ---------- 自定义背景 ---------- */
.main.bg-custom,
.sidebar.bg-custom{background-size:cover;background-position:center;background-repeat:no-repeat;}

/* 科技网格 */
.main.bg-grid{
  background-color:var(--bg);
  background-image:
    linear-gradient(rgba(0,212,255,.04) 1px,transparent 1px),
    linear-gradient(90deg,rgba(0,212,255,.04) 1px,transparent 1px),
    radial-gradient(circle at 50% 50%,rgba(0,212,255,.06),transparent 70%);
  background-size:40px 40px,40px 40px,100% 100%;
}

/* 流动星光 */
.main.bg-stars{
  background-color:var(--bg);
  position:relative;
}
.main.bg-stars::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:
    radial-gradient(circle at 20% 30%,rgba(0,212,255,.10),transparent 40%),
    radial-gradient(circle at 80% 70%,rgba(99,102,241,.10),transparent 40%);
}
.main.bg-stars::after{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.6;
  background-image:
    radial-gradient(2px 2px at 40px 60px,rgba(255,255,255,.35),transparent),
    radial-gradient(2px 2px at 160px 120px,rgba(255,255,255,.25),transparent),
    radial-gradient(1.5px 1.5px at 220px 40px,rgba(0,212,255,.35),transparent),
    radial-gradient(2px 2px at 80px 220px,rgba(255,255,255,.2),transparent),
    radial-gradient(1.5px 1.5px at 300px 180px,rgba(99,102,241,.3),transparent);
  background-size:340px 260px,260px 340px,200px 200px,300px 300px,420px 280px;
  animation:starMove 55s linear infinite;
}
@keyframes starMove{from{background-position:0 0,0 0,0 0,0 0,0 0;}to{background-position:-340px 260px,260px -340px,200px 200px,-300px 300px,420px -280px;}}

/* 左侧玻璃流光 */
.sidebar.bg-glass{
  background:linear-gradient(180deg,rgba(10,31,68,.94) 0%,rgba(6,20,44,.9) 100%);
  backdrop-filter:blur(20px);
  border-right:1px solid rgba(0,212,255,.2);
}
.sidebar.bg-glass::before{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.45;
  background:linear-gradient(135deg,rgba(0,212,255,.12) 0%,transparent 45%,rgba(99,102,241,.12) 100%);
}

/* 左侧星河动态背景（Canvas 渲染，见 sidebar-bg.js）
   注意：侧栏的 ::after 已被右侧发光条占用，所以星河走独立 canvas 元素，
        不抢伪元素，也不影响原有的 1px 流光边。 */
.sidebar .sidebar-stars{
  position:absolute;inset:0;width:100%;height:100%;
  display:none;pointer-events:none;z-index:0;
}
.sidebar.bg-stars{background:#03060f;overflow:hidden;}
.sidebar.bg-stars .sidebar-stars{display:block;}
/* 内容必须抬到 canvas 之上（canvas 绝对定位，默认会盖住未定位的兄弟元素） */
.sidebar.bg-stars .sidebar-head,
.sidebar.bg-stars .nav,
.sidebar.bg-stars .sidebar-foot{position:relative;z-index:1;}
/* 右侧发光条保持在最上层 */
.sidebar.bg-stars::after{z-index:2;}
/* 全透明模式下由背景色统一控制，星河让位 */
body.full-transparent .sidebar .sidebar-stars{display:none!important;}
/* 关闭动效时定格在静态星空（canvas 首帧已绘制），不再逐帧重绘 */
body.motion-off .sidebar.bg-stars{background:#050b18;}

/* ---------- 动态图标 ---------- */
body:not(.motion-off) .ni-icon svg{transition:transform .25s ease,filter .25s ease;}
body:not(.motion-off) .nav-item:hover .ni-icon svg{transform:scale(1.18) rotate(6deg);filter:drop-shadow(0 0 5px var(--cyan));}
body:not(.motion-off) .nav-item.active .ni-icon svg{animation:iconPulse 2.4s ease-in-out infinite;filter:drop-shadow(0 0 8px var(--cyan));}
@keyframes iconPulse{0%,100%{transform:scale(1);}50%{transform:scale(1.15);}}

body:not(.motion-off) .kpi-icon{animation:floatIcon 4s ease-in-out infinite;}
body:not(.motion-off) .kpi:nth-child(2) .kpi-icon{animation-delay:.5s;}
body:not(.motion-off) .kpi:nth-child(3) .kpi-icon{animation-delay:1s;}
body:not(.motion-off) .kpi:nth-child(4) .kpi-icon{animation-delay:1.5s;}
body:not(.motion-off) .kpi:nth-child(5) .kpi-icon{animation-delay:2s;}
body:not(.motion-off) .kpi:nth-child(6) .kpi-icon{animation-delay:2.5s;}
@keyframes floatIcon{0%,100%{transform:translateY(0);}50%{transform:translateY(-4px);}}

body:not(.motion-off) .engine-status .pulse{animation:pulseD 1.8s infinite;}
/* ---------- 地球自转标记（锚点地球） ----------
   陆地靠平移模拟自转；经纬网是固定坐标帧，不随陆地移动。
   所有动效受 body.motion-off 管辖，关闭动效时定格为静态地球。 */
.earth-mark{display:inline-flex;flex-shrink:0;line-height:0;}
.earth-mark svg{display:block;width:100%;height:100%;overflow:visible;}
/* ⚠️ 槽位 ≠ 视觉直径：viewBox 64 中球半径 20，球只占槽位 62%。
   历代值：24px(球15px，被反馈「太小」) → 36px(球22.5) → 48px(球30)。
   要加视觉分量就调这里，别只改球半径。
   上限受 .sidebar-head 高度(--topbar-h:64px) 与侧栏宽 248px 双约束：
   48px 时 .brand 内容约 178px + 收起按钮 26px = 204px < 216px 可用宽。 */
.brand .earth-mark{width:48px;height:48px;}
.login-brand .earth-mark{width:78px;height:78px;}
.em-orbit{transform-box:view-box;transform-origin:50% 50%;}
/* ⚠️ 动画名一律用 em 前缀。main.css 里已有一个 earthSpin（登录页 .earth-continents
   用，语义是 rotate）；CSS 同名 @keyframes 后者覆盖前者，若这里也叫 earthSpin，
   陆地就不会平移而是整个球体刚性自转 —— 已踩过一次，勿改回。 */
@keyframes emSpin{from{transform:translateX(0);}to{transform:translateX(-48px);}}
@keyframes emOrbitA{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}
@keyframes emOrbitB{from{transform:rotate(360deg);}to{transform:rotate(0deg);}}
/* ★ 时长语义：emSpin 的周期 = 陆地平移「一个图块(tile)宽 48」所用时间，**不是转一圈**。
   tile 48 / 球周长 2πr≈125.66 → 48 单位仅等于 137.5°，故 9s/tile ⇒ 全周约 23.6s。
   改速度时按 tile 周期改，别按「一圈几秒」估。 */
body:not(.motion-off) .em-spin{animation:emSpin 9s linear infinite;}
body:not(.motion-off) .em-orbit-a{animation:emOrbitA 20s linear infinite;}
body:not(.motion-off) .em-orbit-b{animation:emOrbitB 30s linear infinite;}

/* ===== 引力锚 Logo（2026-09-26 替换旧地球标记）=====
   ⚠️ 名字一律 em 前缀：main.css 已有 earthSpin（登录页 .earth-continents 用，语义是 rotate），
      同名 @keyframes 会互相覆盖 —— 已踩过一次，勿改回。
   时长语义：emOrbitA/emOrbitB 是「转一圈」；emCore/emHalo 是心跳 2.9s。
   线宽按 52px/24px 可辨认标定，勿调细。 */
.em-core,.em-halo{transform-box:view-box;transform-origin:50% 50%;}
@keyframes emCore{0%,100%{transform:scale(1);}50%{transform:scale(1.13);}}
@keyframes emHalo{0%,100%{opacity:.34;transform:scale(.95);}50%{opacity:.80;transform:scale(1.12);}}
body:not(.motion-off) .em-core{animation:emCore 2.9s ease-in-out infinite;}
body:not(.motion-off) .em-halo{animation:emHalo 2.9s ease-in-out infinite;}

/* ---------- AI 语音助手 ---------- */
#aiAssistant{position:fixed;right:22px;bottom:22px;z-index:8888;font-family:inherit;pointer-events:none;}
#aiAssistant > *{pointer-events:auto;}
#aiFab{
  position:relative;width:56px;height:56px;border:none;border-radius:50%;cursor:pointer;color:#fff;
  background:linear-gradient(135deg,var(--cyan),var(--violet));
  box-shadow:0 8px 24px rgba(0,212,255,.35),0 0 0 0 rgba(0,212,255,.45);
  display:flex;align-items:center;justify-content:center;transition:transform .2s,box-shadow .2s;
}
#aiFab:hover{transform:scale(1.08);box-shadow:0 10px 30px rgba(0,212,255,.45),0 0 0 8px rgba(0,212,255,.12);}
.ai-fab-ring{
  position:absolute;inset:-6px;border-radius:50%;border:2px solid rgba(0,212,255,.35);
  animation:fabRing 2s ease-out infinite;opacity:0;
}
@keyframes fabRing{0%{transform:scale(.9);opacity:.7;}100%{transform:scale(1.35);opacity:0;}}

.ai-panel{
  position:absolute;right:0;bottom:72px;width:min(360px,92vw);max-height:520px;
  background:var(--surface);border:var(--bd);border-radius:var(--r-lg);
  box-shadow:var(--sh-lg),var(--sh-glow);
  display:flex;flex-direction:column;
  opacity:0;transform:translateY(16px) scale(.96);pointer-events:none;visibility:hidden;
  transition:all .25s var(--ease);
}
.ai-panel.show{opacity:1;transform:none;pointer-events:auto;visibility:visible;}
.ai-head{
  display:flex;align-items:center;justify-content:space-between;
  padding:14px 16px;border-bottom:var(--bd);
  background:linear-gradient(90deg,rgba(0,212,255,.10),rgba(99,102,241,.08));
  border-radius:var(--r-lg) var(--r-lg) 0 0;
}
.ai-head b{font-size:14px;color:var(--text);}
.ai-head span{font-size:11px;color:var(--text-3);display:block;margin-top:2px;}
#aiClose{width:28px;height:28px;border:none;background:var(--surface-2);border-radius:7px;color:var(--text-2);cursor:pointer;font-size:18px;line-height:1;}
#aiClose:hover{background:var(--border);color:var(--text);}

.ai-body{flex:1;overflow-y:auto;padding:14px;min-height:180px;max-height:320px;}
.ai-msg{display:flex;margin-bottom:10px;}
.ai-user{justify-content:flex-end;}
.ai-bubble{
  max-width:260px;padding:10px 13px;border-radius:14px;font-size:12.5px;line-height:1.6;
  background:var(--surface-2);color:var(--text);border:var(--bd);
}
.ai-bot .ai-bubble{background:rgba(0,212,255,.10);border-color:rgba(0,212,255,.25);color:var(--text);}
.ai-user .ai-bubble{background:rgba(99,102,241,.12);border-color:rgba(99,102,241,.25);}

.ai-quick{display:flex;gap:8px;flex-wrap:wrap;padding:0 14px 10px;}
.ai-quick button{padding:6px 11px;border-radius:18px;border:var(--bd);background:var(--surface-2);color:var(--text-2);font-size:11.5px;cursor:pointer;transition:.15s;}
.ai-quick button:hover{background:var(--cyan);color:#fff;border-color:var(--cyan);}

.ai-foot{display:flex;gap:8px;align-items:center;padding:12px 14px;border-top:var(--bd);}
#aiInput{flex:1;padding:9px 12px;border:var(--bd);border-radius:20px;background:var(--surface-2);color:var(--text);outline:none;font-size:13px;}
#aiInput:focus{border-color:var(--cyan);box-shadow:0 0 0 3px rgba(0,212,255,.1);}
#aiMic{width:34px;height:34px;border-radius:50%;border:var(--bd);background:var(--surface-2);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.2s;}
#aiMic span{width:10px;height:10px;border-radius:50%;background:var(--danger);}
#aiMic:hover{background:var(--border);}
#aiMic.listening{animation:micPulse 1s infinite;background:rgba(239,68,68,.15);}
@keyframes micPulse{0%,100%{box-shadow:0 0 0 0 rgba(239,68,68,.4);}50%{box-shadow:0 0 0 8px rgba(239,68,68,0);}}
#aiSend{padding:8px 14px;border-radius:18px;border:none;background:linear-gradient(135deg,var(--cyan),var(--violet));color:#fff;font-size:12px;font-weight:700;cursor:pointer;}
#aiSend:hover{filter:brightness(1.1);}

/* 移动端 AI 助手贴边 */
@media(max-width:820px){
  #aiAssistant{right:14px;bottom:14px;}
  .ai-panel{width:92vw;right:-8px;}
}

/* ==========================================================
   锚点 GEO · 登录 / 入口
   ========================================================== */
#loader{
  position:fixed;inset:0;z-index:9999;
  /* 登录背景 = 白色机器人（用户指定素材 assets/img/login-robot.jpg），
     压暗 + 蓝色调合，保证地球与登录卡片的对比度 */
  background-color:#01030A;
  background-image:
    linear-gradient(180deg, rgba(3,10,26,.06) 0%, rgba(2,8,20,.12) 45%, rgba(1,4,12,.26) 100%),
    radial-gradient(ellipse at 50% -10%, rgba(10,40,95,.08) 0%, rgba(4,16,40,.04) 42%, rgba(1,3,10,.14) 100%),
    url("../img/login-robot.jpg");
  background-size:cover, cover, cover;
  background-position:center center, center center, center 32%;
  background-repeat:no-repeat, no-repeat, no-repeat;
  display:flex;align-items:center;justify-content:center;
  transition:opacity .7s var(--ease),visibility .7s;
  overflow:hidden;
  /* 共享透视：登录卡与数据面板共用同一灭点（避免各写 perspective() 对不上灭点）。
     #loader 本身满屏 fixed，加 perspective 后成为 fixed 后代的包含块，其盒 = 视口，
     故品牌字 / 语音按钮 / 底部字幕的位置不变（已用 getBoundingClientRect 实测）。 */
  perspective: calc(100vw * 1.1);
  perspective-origin: 62% 44%;
}
#loader.hide{opacity:0;visibility:hidden;pointer-events:none;}

.login-bg{position:absolute;inset:0;pointer-events:none;}
.space-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;}
.login-vignette{
  position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse at 50% 42%, transparent 46%, rgba(1,3,9,.40) 100%);
}
.login-grid{
  position:absolute;inset:0;
  background-image:
    linear-gradient(rgba(0,212,255,.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,212,255,.06) 1px, transparent 1px);
  background-size:60px 60px;
  transform:perspective(500px) rotateX(55deg) translateY(-20%);
  transform-origin:50% 100%;
  animation:gridMove 24s linear infinite;
  opacity:.45;
}
@keyframes gridMove{from{background-position:0 0;}to{background-position:0 60px;}}
.login-particles{position:absolute;inset:0;}
.login-particles .p{
  position:absolute;width:2px;height:2px;background:var(--cyan);
  border-radius:50%;box-shadow:0 0 6px var(--cyan);opacity:.6;
  animation:particleFloat linear infinite;
}
@keyframes particleFloat{
  0%{transform:translateY(0) scale(1);opacity:0;}
  20%{opacity:.7;}
  80%{opacity:.5;}
  100%{transform:translateY(-120vh) scale(.6);opacity:0;}
}
.login-scanline{
  position:absolute;inset:0;
  background:linear-gradient(transparent 50%, rgba(0,212,255,.03) 50%);
  background-size:100% 4px;
  pointer-events:none;
  animation:scanline 8s linear infinite;
}
@keyframes scanline{from{background-position:0 0;}to{background-position:0 100%;}}

.login-wrap{
  position:relative;z-index:2;display:flex;align-items:center;justify-content:center;
  gap:56px;padding:40px;max-width:1440px;width:100%;flex-wrap:wrap;
}

/* 左侧主视觉：方形舞台（按用户红线再放大 —— 占据左半屏的绝对主角），
   地球点阵（Canvas）与品牌字都以此为圆心对齐 */
.login-earth{
  flex:0 0 clamp(400px, min(56vw, 76vh), 900px);
  width:clamp(400px, min(56vw, 76vh), 900px);
  height:clamp(400px, min(56vw, 76vh), 900px);
  text-align:center;position:relative;
}
.earth-glow{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:440px;height:440px;border-radius:50%;
  background:radial-gradient(circle, rgba(0,200,255,.32) 0%, rgba(0,130,230,.14) 35%, transparent 72%);
  filter:blur(26px);
  animation:glowPulse 3.2s ease-in-out infinite;
}
@keyframes glowPulse{0%,100%{opacity:.72;transform:translate(-50%,-50%) scale(1);}50%{opacity:1;transform:translate(-50%,-50%) scale(1.14);}}
/* 外层淡蓝大气晕 */
.earth-aurora{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:540px;height:540px;border-radius:50%;
  background:radial-gradient(circle, rgba(0,140,255,.18) 0%, rgba(0,90,200,.08) 40%, transparent 70%);
  filter:blur(34px);
  opacity:.55;
  animation:auroraPulse 7s ease-in-out infinite;
  pointer-events:none;
}
@keyframes auroraPulse{0%,100%{opacity:.4;}50%{opacity:.68;}}
.earth-svg{width:360px;height:360px;display:block;margin:0 auto 32px;filter:drop-shadow(0 0 42px rgba(0,180,255,.5));animation:planetBreath 9s ease-in-out infinite;}
@keyframes planetBreath{0%,100%{transform:scale(1);}50%{transform:scale(1.03);}}
.earth-continents{animation:earthSpin 28s linear infinite;transform-origin:120px 120px;fill:rgba(0,170,230,.34);stroke:rgba(140,220,255,.42);stroke-width:.9;}
.earth-grid{animation:earthSpin 40s linear infinite reverse;transform-origin:120px 120px;}
@keyframes earthSpin{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}

/* 深空星云（更收敛的蓝） */
.earth-nebula{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:560px;height:560px;border-radius:50%;
  background:
    radial-gradient(circle at 30% 30%, rgba(0,110,210,.14) 0%, transparent 45%),
    radial-gradient(circle at 70% 70%, rgba(0,90,190,.10) 0%, transparent 45%),
    radial-gradient(circle at 80% 20%, rgba(0,160,255,.08) 0%, transparent 40%);
  filter:blur(40px);
  opacity:.85;
}

/* 星空背景层 */
.earth-stars{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:480px;height:480px;border-radius:50%;
  background-image:
    radial-gradient(1.4px 1.4px at 20% 30%, rgba(255,255,255,.9), transparent),
    radial-gradient(1.2px 1.2px at 70% 20%, rgba(200,230,255,.9), transparent),
    radial-gradient(1.5px 1.5px at 80% 65%, rgba(207,239,255,.85), transparent),
    radial-gradient(1.1px 1.1px at 35% 75%, rgba(255,255,255,.75), transparent),
    radial-gradient(1.3px 1.3px at 55% 45%, rgba(255,255,255,.85), transparent),
    radial-gradient(1.0px 1.0px at 15% 60%, rgba(255,255,255,.65), transparent),
    radial-gradient(1.2px 1.2px at 88% 40%, rgba(207,239,255,.8), transparent),
    radial-gradient(1.0px 1.0px at 45% 15%, rgba(255,255,255,.6), transparent),
    radial-gradient(1.3px 1.3px at 10% 85%, rgba(207,239,255,.7), transparent),
    radial-gradient(1.1px 1.1px at 62% 82%, rgba(255,255,255,.8), transparent);
  animation:starSpin 120s linear infinite;
  opacity:.85;
}
@keyframes starSpin{from{transform:translate(-50%,-50%) rotate(0deg);}to{transform:translate(-50%,-50%) rotate(360deg);}}

/* 轨道环缓慢公转 */
.earth-orbits{animation:orbitSpin 28s linear infinite;transform-origin:120px 120px;}
@keyframes orbitSpin{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}

.login-brand{display:flex;flex-direction:column;gap:10px;text-align:center;justify-content:center;align-items:center;}
.login-brand-title{
  font-size:clamp(34px,4.2vw,76px);font-weight:900;
  letter-spacing:clamp(3px,.5vw,11px);line-height:1.05;text-indent:clamp(3px,.5vw,11px);
  color:#2F4E7C;-webkit-text-fill-color:#2F4E7C;
  background:none;
  animation:brandBreathe 3.2s ease-in-out infinite;
}
/* 白色描边：只放进支持分支，不支持描边的浏览器自动只保留光晕 */
@supports (-webkit-text-stroke:1px #000){
  .login-brand-title{-webkit-text-stroke:1.4px rgba(255,255,255,.85);}
}
/* 描边光晕呼吸（3.2s 一个明暗周期）：保留原暗投影，在其上叠白色光晕 */
@keyframes brandBreathe{
  0%,100%{filter:drop-shadow(0 4px 18px rgba(0,0,0,.55)) drop-shadow(0 0 3px rgba(255,255,255,.25));}
  50%{filter:drop-shadow(0 4px 18px rgba(0,0,0,.55)) drop-shadow(0 0 16px rgba(255,255,255,.72));}
}
/* 品牌名流动：登录页大标题已改为静态单色，此处仅供侧边栏 .brand-text h1 使用 */
@keyframes brandFlow{from{background-position:0% 50%;}to{background-position:300% 50%;}}
/* 不支持 background-clip:text 时回退成同色，避免文字跳成白色 */
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .login-brand-title{color:#2F4E7C;-webkit-text-fill-color:#2F4E7C;background:none;}
}
.login-brand-sub{
  font-size:clamp(15px,1.25vw,22px);color:rgba(150,240,255,.92);
  letter-spacing:clamp(3px,.5vw,8px);margin-top:10px;text-shadow:0 0 14px rgba(0,212,255,.5);
}

/* 登录卡片：脱离文档流固定在右下角 + 与背景倾斜显示器同向的透视
   + 更快的蓝色呼吸光晕（1.2s，明显快于品牌字 3.2s）。
   透视来自祖先 #loader（共享灭点）；这里只写元素自身变换。 */
.login-card{
  position:fixed;
  right:clamp(24px,3vw,64px);
  bottom:clamp(24px,4vh,64px);
  top:auto;left:auto;margin:0;
  width:min(30vw,480px);
  background:rgba(10,31,68,.55);
/* [UI-1] 移除卡片感：登录卡描边（原 border:1px solid rgba(0,212,255,.25)）*/
  border-radius:var(--r-xl);
  padding:22px 22px 20px;
/* [UI-1] 移除卡片感：登录卡外投影（原 box-shadow:0 24px 70px ...）*/
  backdrop-filter:blur(18px);
  -webkit-backdrop-filter:blur(18px);
  transform: rotateY(-15deg) rotateX(3deg) rotate(2deg);
/* [UI-1] 移除卡片感：登录卡呼吸动画（原 animation: cardBreathFast ...）*/
}
/* 第二层光晕：模糊光环（border-radius:inherit 保证不溢出成方块） */
/* [UI-1] 光晕 ::after 整块已移除 */
/* [UI-1] 光晕 ::after 整块已移除 */
/* [UI-1] 光晕 ::after 整块已移除 */
/* [UI-1] 光晕 ::after 整块已移除 */
/* [UI-1] 光晕 ::after 整块已移除 */
/* [UI-1] 光晕 ::after 整块已移除 */
/* [UI-1] 光晕 ::after 整块已移除 */
/* 光晕压在卡片内容之下，避免遮住控件 */
.login-card > *{position:relative;z-index:1;}
/* [UI-1] cardBreathFast 已移除 */
/* [UI-1] cardBreathFast 已移除 */
/* [UI-1] cardBreathFast 已移除 */
/* [UI-1] cardBreathFast 已移除 */
/* 短屏兜底：内容超高时卡内滚动，保证四个控件始终可达 */
@media (max-height:760px){
  .login-card{max-height:calc(100vh - 96px);overflow:auto;}
}
.login-tabs{display:flex;gap:8px;margin-bottom:16px;}
.login-tabs button{
  flex:1;padding:10px 8px;border:1px solid rgba(0,212,255,.18);
  background:rgba(0,0,0,.18);border-radius:10px;color:rgba(255,255,255,.65);
  cursor:pointer;transition:.2s;text-align:center;
}
.login-tabs button span{display:block;font-size:13px;font-weight:700;color:#fff;}
.login-tabs button small{display:block;font-size:10px;margin-top:2px;color:rgba(255,255,255,.55);}
.login-tabs button:hover{border-color:rgba(0,212,255,.4);background:rgba(0,212,255,.08);}
.login-tabs button.on{
  border-color:var(--cyan);background:linear-gradient(135deg,rgba(0,212,255,.22),rgba(99,102,241,.18));
  box-shadow:0 4px 16px rgba(0,212,255,.2);
}
.login-role-desc{
  font-size:12px;color:rgba(0,212,255,.85);text-align:center;margin-bottom:18px;
  padding:8px;border-radius:8px;background:rgba(0,212,255,.08);border:1px dashed rgba(0,212,255,.2);
}
.login-mode{display:flex;gap:16px;margin-bottom:16px;font-size:13px;color:rgba(255,255,255,.75);}
.login-mode label{display:flex;align-items:center;gap:6px;cursor:pointer;}
.login-mode input{width:15px;height:15px;accent-color:var(--cyan);}
.login-field{margin-bottom:16px;}
.login-field label{display:block;font-size:12px;font-weight:600;color:rgba(255,255,255,.8);margin-bottom:7px;}
.login-input-wrap{position:relative;display:flex;align-items:center;}
.login-field input{
  width:100%;padding:12px 42px 12px 14px;border:1px solid rgba(0,212,255,.25);
  border-radius:10px;background:rgba(0,0,0,.25);color:#fff;font-size:14px;
  outline:none;transition:.2s;font-family:inherit;
}
.login-field input::placeholder{color:rgba(255,255,255,.35);}
.login-field input:focus{border-color:var(--cyan);box-shadow:0 0 0 3px rgba(0,212,255,.12);}
.login-eye,.login-code{
  position:absolute;right:8px;top:50%;transform:translateY(-50%);
  background:transparent;border:none;color:rgba(255,255,255,.65);cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:6px;
}
.login-code{position:static;transform:none;margin-left:8px;white-space:nowrap;
  background:rgba(0,212,255,.15);border:1px solid rgba(0,212,255,.25);
  border-radius:8px;padding:9px 12px;font-size:12px;color:var(--cyan);cursor:pointer;}
.login-code:hover{background:rgba(0,212,255,.25);}
.login-code:disabled{opacity:.5;cursor:not-allowed;}
.login-eye:hover{color:#fff;}
.login-eye .eye-line{opacity:1;transition:opacity .15s;}
.login-eye.show .eye-line{opacity:0;}
.login-hint{font-size:11px;color:rgba(255,255,255,.45);margin-top:6px;}
.login-btn{
  width:100%;padding:13px;border:none;border-radius:10px;
  background:linear-gradient(135deg,var(--cyan),var(--violet));
  color:#fff;font-size:15px;font-weight:700;cursor:pointer;
  display:flex;align-items:center;justify-content:center;gap:8px;
  box-shadow:0 8px 24px rgba(0,212,255,.25);transition:.2s;margin-top:8px;
}
.login-btn:hover{filter:brightness(1.1);transform:translateY(-1px);}
.login-btn.secondary:hover{background:rgba(0,212,255,.18);color:#fff;box-shadow:0 0 16px rgba(0,212,255,.25);}
.login-msg{font-size:12px;text-align:center;margin-top:12px;min-height:18px;color:var(--danger);}

@media(max-width:980px){
  .login-wrap{flex-direction:column;gap:24px;padding:24px;}
  .login-earth{flex:none;width:min(92vw,460px);height:min(92vw,460px);}
  .login-brand-title{font-size:38px;letter-spacing:5px;text-indent:5px;}
  .login-brand-sub{font-size:13px;letter-spacing:2px;}
  /* 窄屏回到文档流（旧的堆叠行为），避免固定右下角压住居中的品牌字 */
  .login-card{
    position:relative;top:auto;left:auto;right:auto;bottom:auto;
    flex:none;width:min(360px,92vw);margin:0 auto;transform:none;
  }
  /* 圆环 HUD 小面板已删除，此处无需再隐藏 hud-tl/tr/bl/br */
}

/* ===== AI 智能核心（登录区主视觉）已移除：圆环 / 刻度 / 六边形 / 4 个 HUD 小面板
   按用户标注图删除，相关 CSS 一并清理；.hud-corner 仍由登录卡片使用，保留在下方。 ===== */

/* HUD 角标（登录卡片等仍在用，勿删） */
.hud-corner{position:absolute;width:16px;height:16px;border:2px solid rgba(0,212,255,.8);pointer-events:none;z-index:3;}
.hud-corner.tl{top:-6px;left:-6px;border-right:0;border-bottom:0;}
.hud-corner.tr{top:-6px;right:-6px;border-left:0;border-bottom:0;}
.hud-corner.bl{bottom:-6px;left:-6px;border-right:0;border-top:0;}
.hud-corner.br{bottom:-6px;right:-6px;border-left:0;border-top:0;}

/* 登录区品牌 / 卡片增强
   （.login-card 现为固定右下角，定位见上方主规则；此处不再覆盖 position） */

/* 点阵地球（Canvas）为绝对主角。 */
.login-earth{position:relative;}
/* 品牌字移到屏幕最左：
   ⚠️ 必须用 position:fixed 相对视口定位 —— .login-earth 是居中容器，
   在 2560 宽屏上它的左边缘在 x≈560px，用 left:40px（absolute）只能挪到
   容器左边缘，到不了屏幕最左边。#loader 无 transform/filter/backdrop-filter，
   fixed 的包含块就是视口，且其 overflow:hidden 不会裁切 fixed 子元素。
   align-items/text-align 必须从基类 .login-brand 的 center 改成 flex-start/left，
   否则文字仍会在块内居中。 */
.login-earth .login-brand{
  position:fixed;left:40px;top:50%;transform:translateY(-50%);
  width:auto;max-width:min(46vw,640px);
  margin:0;z-index:6;
  align-items:flex-start;justify-content:flex-start;text-align:left;
  pointer-events:none;
}
/* 左对齐后，原本为「居中视觉对称」加的 text-indent（=letter-spacing）会把字推右，
   这里清零。(0,2,0) 盖过基类 .login-brand-title 的 (0,1,0)。 */
.login-earth .login-brand-title{text-indent:0;}

/* 窄屏回退：品牌字恢复压在地球圆心居中（顺序在本条之后，同 specificity 后来者胜） */
@media(max-width:980px){
  .login-earth .login-brand{
    position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
    width:100%;max-width:none;margin:0;
    align-items:center;justify-content:center;text-align:center;
  }
  .login-earth .login-brand-title{text-indent:5px;}
}

.login-earth .login-brand-sub{white-space:nowrap;}
.login-brand-text{position:relative;padding:14px 26px;}
/* 文字背后加一层径向压暗，保证大字在当前亮/暗两种背景下都清晰。
   标题改为低对比暗色后，这层必须调淡，否则会把字一起吃掉。 */
.login-brand-text::before{
  content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:132%;height:210%;border-radius:50%;
  background:radial-gradient(ellipse at center, rgba(2,10,26,.30) 0%, rgba(2,10,26,.17) 52%, rgba(2,10,26,0) 78%);
  z-index:-1;pointer-events:none;
}
.login-card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;padding-bottom:12px;border-bottom:1px dashed rgba(0,212,255,.25);}
.login-card-kicker{font-family:Consolas,Menlo,monospace;font-size:11px;letter-spacing:2px;color:var(--cyan);}
.login-card-status{display:flex;align-items:center;gap:6px;font-family:Consolas,Menlo,monospace;font-size:10px;letter-spacing:1px;color:rgba(0,212,255,.7);}
.login-card-status i{width:7px;height:7px;border-radius:50%;background:#22e0a1;box-shadow:0 0 8px #22e0a1;animation:statusBlink 1.8s ease-in-out infinite;}
@keyframes statusBlink{0%,100%{opacity:1;}50%{opacity:.35;}}
.login-btn.secondary{margin-top:10px;background:rgba(0,212,255,.08);border:1px solid var(--cyan);color:var(--cyan);text-shadow:0 0 8px rgba(0,212,255,.4);}

.role-badge{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border-radius:20px;font-size:11px;font-weight:700;color:#fff;background:linear-gradient(135deg,var(--cyan),var(--violet));box-shadow:0 2px 8px rgba(0,212,255,.25);cursor:pointer;}
.role-badge.role-admin{background:linear-gradient(135deg,#2563EB,#6366F1);}
.role-badge.role-employee{background:linear-gradient(135deg,#6366F1,#8B5CF6);}
.role-badge.role-client{background:linear-gradient(135deg,#10B981,#0BA5D4);}
.lang-badge{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border-radius:20px;font-size:11px;font-weight:700;color:var(--cyan);border:1px solid rgba(0,212,255,.25);background:rgba(0,212,255,.08);cursor:pointer;}

/* 数据大屏科技感动效 */
.dash-fx{position:absolute;inset:0;pointer-events:none;z-index:0;overflow:hidden;}
.dash-grid{
  position:absolute;inset:-30% 0;opacity:.12;
  background-image:linear-gradient(rgba(0,212,255,.25) 1px, transparent 1px),linear-gradient(90deg, rgba(0,212,255,.25) 1px, transparent 1px);
  background-size:50px 50px;
  transform:perspective(600px) rotateX(55deg);transform-origin:50% 0;
  animation:dashGridMove 20s linear infinite;
}
@keyframes dashGridMove{from{background-position:0 0;}to{background-position:0 50px;}}
.dash-pulse{
  position:absolute;left:50%;top:30%;width:400px;height:400px;border-radius:50%;
  transform:translate(-50%,-50%);border:1px solid rgba(0,212,255,.15);
  box-shadow:0 0 60px rgba(0,212,255,.08);animation:dashPulse 4s ease-in-out infinite;
}
.dash-pulse::before,.dash-pulse::after{
  content:"";position:absolute;inset:0;border-radius:50%;border:1px solid rgba(0,212,255,.1);
  animation:dashPulse 4s ease-in-out infinite 1s;
}
.dash-pulse::after{animation-delay:2s;}
@keyframes dashPulse{0%,100%{transform:translate(-50%,-50%) scale(.9);opacity:.5;}50%{transform:translate(-50%,-50%) scale(1.3);opacity:0;}}
.dash-scan{
  position:absolute;inset:0;background:linear-gradient(transparent 95%, rgba(0,212,255,.06));
  background-size:100% 120px;animation:dashScan 6s linear infinite;
}
@keyframes dashScan{from{background-position:0 0;}to{background-position:0 120px;}}

/* 透明模式：去除所有白色底 */
body.full-transparent .card,
body.full-transparent .kpi,
body.full-transparent .topbar,
body.full-transparent .modal-panel,
body.full-transparent .toast,
body.full-transparent .search-panel{
  background:rgba(var(--surface-rgb), var(--card-alpha))!important;
  border-color:rgba(0,212,255,.18)!important;
}
body.full-transparent .card-head,
body.full-transparent .toolbar{border-color:rgba(0,212,255,.1)!important;}
body.full-transparent .input,
body.full-transparent .select,
body.full-transparent .textarea,
body.full-transparent .gsearch{
  background:rgba(0,0,0,.25)!important;
  border-color:rgba(0,212,255,.2)!important;
  color:var(--text)!important;
}
body.full-transparent .sidebar{background:linear-gradient(180deg,rgba(10,31,68,.82),rgba(8,24,52,.82))!important;}
body.full-transparent .tbl th{background:rgba(0,212,255,.08)!important;}

/* ==========================================================
   2026-09-13 · 关键词覆盖库（缺值提示 / 底部图例）
   —— 缺值一律渲染「—」而不是 0 或 NaN，用 .kw-na 做视觉区分
   ========================================================== */
.kw-na{color:var(--text-3);font-weight:600;cursor:help;opacity:.9}
.kw-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:10px 16px;border-top:var(--bd);font-size:12px;
  color:var(--text-3);flex-wrap:wrap}
.kw-foot:empty{display:none}
.kw-na-legend{line-height:1.65;flex:1 1 340px}

/* ==========================================================
   2026-09-13 · 客户数据管理弹窗（#cdataModal）
   —— 同账号作战台 + 跨账号（CEO 代查代改）共用这一个面板
   ⚠️ 弹窗三件套（.modal-panel 纵向 flex / .modal-body overflow /
      .modal 内边距）在通用样式里已经保证，本节不要覆盖它们。
   ========================================================== */
.cdata-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:10px 12px;background:var(--surface-2);border-radius:var(--r-md);
  font-size:12.5px;color:var(--text-2);margin-bottom:14px}
.cdata-bar .sp{flex:1 1 auto}
.cdata-list{display:flex;flex-direction:column;gap:8px}
.cdata-item{display:flex;align-items:center;gap:10px;padding:10px 12px;
  border:var(--bd);border-radius:var(--r-md);font-size:13px}
.cdata-item .t{font-weight:700;flex:0 0 auto}
.cdata-item .d{color:var(--text-3);font-size:12px;flex:1 1 auto;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cdata-edit{border:var(--bd);border-radius:var(--r-md);
  padding:10px 12px;margin-bottom:8px;background:var(--surface-2)}
.cdata-edit label{display:block;font-size:12px;color:var(--text-3);margin-bottom:5px}
.cdata-kv{display:flex;gap:8px;align-items:flex-start;margin-bottom:8px}
.cdata-kv .input{flex:0 0 190px}
.cdata-kv textarea.input{flex:1 1 auto;min-height:64px;resize:vertical;font-family:inherit}
.cdata-empty{padding:28px;text-align:center;color:var(--text-3);font-size:13px}
.cdata-warn{padding:10px 12px;border-radius:var(--r-sm);font-size:12.5px;
  line-height:1.65;margin-bottom:14px}
.cdata-warn.ro{background:rgba(0,212,255,.08);border:1px solid rgba(0,212,255,.28);color:var(--cyan)}
.cdata-warn.rw{background:rgba(245,158,11,.09);border:1px solid rgba(245,158,11,.3);color:var(--warning)}
.cdata-warn.err{background:rgba(226,75,74,.09);border:1px solid rgba(226,75,74,.34);color:#E24B4A}
.cdata-sec{font-size:12.5px;font-weight:700;color:var(--text-2);margin:16px 0 9px;
  display:flex;align-items:center;gap:8px}
.cdata-sec:first-child{margin-top:0}

@media(max-width:820px){
  .cdata-kv{flex-direction:column}
  .cdata-kv .input{flex:1 1 auto;width:100%}
}


/* ==========================================================
   批次 3 视觉调整（2026-09-18）
   1) 右侧主区背景 → 深蓝电路大脑
   2) 侧栏 LOGO → 锚形徽章
   3) 登录地球外层高速自转、中心核心静止
   4) 文章页「客户资料引用」信息条
   ========================================================== */

/* ---------- 1. 右侧主区背景：主视觉（深底 + 暖金轮廓光） ----------
   只在用户没有自选「外观背景」时生效，选网格 / 星河 / 自定义图时不抢戏。
   用 fixed 铺满视口，滚动内容时背景稳定不抖。
   ★ 2026-10-07 换图：原来是 bg-brain.jpg（深蓝电路大脑）。
     换的原因：老板要求主内容区用他给的主视觉；同时 old 'solid' 状态走的就是这条兜底，
     而**兜底是深色背景却没配套改字色** —— 深底 + 深字 = 看不见（用户实测截图）。
     现在两件事一起修：① 兜底换成主视觉；② 见下方 1b 段，深色主区的字色由**状态**决定，
     不再依赖"用户是否停在某个主题值"，也就不怕云同步把主题配置改回去。 */
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars){
  background-color:#06070a;
  background-image:
    linear-gradient(180deg, rgba(6,7,10,.22) 0%, rgba(6,7,10,.34) 48%, rgba(6,7,10,.58) 100%),
    radial-gradient(ellipse at 62% 16%, rgba(232,196,135,.10) 0%, rgba(6,7,10,0) 62%),
    url("../img/geo-mainbg.webp?v=20261008s107");
  background-size:cover, cover, cover;
  background-position:center center, center center, center 38%;
  background-repeat:no-repeat, no-repeat, no-repeat;
  background-attachment:fixed, fixed, fixed;
}
/* 卡片浮在背景之上，保证正文可读性不被图片干扰 */
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .card,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .kpi{
  backdrop-filter:blur(2px);
}

/* ---------- 1b. 深色主区上的文字与卡片（2026-10-07 新增，2026-10-08 修正） ----------
   ★ 修正原因（用户实测截图，这是真 bug，不是审美问题）：
     第一版把浅色字规则**只挂在 `body.bg-photo`**（= 用户选了自定义背景图）上。
     而"主区是深色"其实有两种状态：
       ① `body.bg-photo`          用户选了自定义背景图（深色）；
       ② 兜底状态 `.main:not(.bg-custom):not(.bg-grid):not(.bg-stars)`
          —— 主题值为 solid（旧版本默认值之一）就走这条，背景图是**深色**的。
     ②的字色没被覆盖 ⇒ **深底 + 深字**，用户看到的就是"看不清"。
     更麻烦的是：主题配置会被**云同步**覆盖回旧值，导致依赖配置的迁移可能失效；
     而只要判定挂在**状态**上，同步怎么改都不影响可读性。
   ⇒ 现在两个状态一起覆盖，字色走 CSS 变量（可在「外观」里改，默认白）。
   原图配色：近纯黑底 + 暖金轮廓光（金约 #E8C487）。
     · 压在背景上的字改**白/暖白**；
     · 强调色继续用产品品牌青做功能区分；金色只做点睛（标题下短线）；
     · 卡片保持**不透明**：正文可读性优先，图只从卡片间隙透出来。 */
body.bg-photo .main,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars){
  --on-bg-title: var(--title-color, #FFFFFF);
  --on-bg-sub: rgba(255,255,255,.74);
  --on-bg-weak: rgba(255,255,255,.56);
  --on-bg-line: #E8C487;
}
body.bg-photo .main .page-title,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .page-title{
  color:var(--on-bg-title);
  font-size:var(--title-size, 22px);
  text-shadow:0 1px 14px rgba(0,0,0,.55);
}
body.bg-photo .main .page-title::after,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .page-title::after{
  content:"";display:block;width:46px;height:2px;margin-top:9px;border-radius:2px;
  background:linear-gradient(90deg,var(--on-bg-line),rgba(232,196,135,0));
}
body.bg-photo .main .page-sub,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .page-sub{
  color:var(--on-bg-sub);text-shadow:0 1px 10px rgba(0,0,0,.5);
}
body.bg-photo .main .crumb-root,body.bg-photo .main .crumb-sep,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .crumb-root,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .crumb-sep{color:var(--on-bg-weak);}
body.bg-photo .main .crumb-cur,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .crumb-cur{color:var(--on-bg-title);}
/* 每页操作建议条：原来是浅底浅字，压在深背景上会变成一块"白补丁" → 改成暖玻璃 */
body.bg-photo .main .page-hint,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .page-hint{
  background:rgba(16,14,12,.55);
  border-color:rgba(232,196,135,.20);
  color:rgba(255,255,255,.84);
  backdrop-filter:blur(6px);
}
body.bg-photo .main .page-hint b,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .page-hint b{color:var(--on-bg-title);}
/* 卡片浮起（不改底色，只加暖描边与投影，避免正文对比被削弱） */
body.bg-photo .main .card,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .card{
  border-color:rgba(232,196,135,.16);
  box-shadow:0 10px 30px rgba(0,0,0,.34);
}
/* KPI / 统计条直接坐在背景上，也补一层暖描边 */
body.bg-photo .main .kpi,
body.bg-photo .main .stat,
body.bg-photo .main .stat-cell,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .kpi,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .stat,
.main:not(.bg-custom):not(.bg-grid):not(.bg-stars) .stat-cell{
  border-color:rgba(232,196,135,.16);
  box-shadow:0 8px 24px rgba(0,0,0,.30);
}

/* ---------- 2. 侧栏 LOGO：锚形徽章 ---------- */
.earth-mark img{
  display:block;width:100%;height:100%;
  object-fit:contain;
  border-radius:50%;
  filter:drop-shadow(0 0 10px rgba(0,212,255,.45)) drop-shadow(0 2px 6px rgba(0,0,0,.5));
}
.brand .earth-mark{width:52px;height:52px;}
.sidebar.collapsed .brand .earth-mark{width:30px;height:30px;}

/* ---------- 3. 登录地球：外表快转、中心静止 ----------
   点阵地球（canvas）由 login-bg.js 加速旋转，品牌字定格在圆心。
   （原「中心 HUD 环 / 六边形核心」相关规则已随圆环装饰一并删除） */

/* ---------- 4. 文章页「客户资料引用」信息条 ---------- */
.art-ctx{
  display:flex;flex-wrap:wrap;gap:8px 16px;align-items:center;
  margin-top:16px;padding:11px 14px;border-radius:10px;
  background:rgba(0,212,255,.06);
  border:1px dashed rgba(0,212,255,.26);
  font-size:12.5px;line-height:1.7;
}
.art-ctx-t{
  font-weight:700;color:var(--cyan);
  padding-right:10px;margin-right:2px;
  border-right:1px solid rgba(0,212,255,.25);
}
.art-ctx-i{color:var(--text-3);}
.art-ctx-i b{color:var(--text-1);font-weight:700;}

/* ==========================================================
   登录页 · 语音导览（Web Speech API + 字幕降级）
   #loader 内部使用，position:fixed 不参与登录卡布局；
   z-index 高于登录卡（.login-wrap 为 2），但按钮固定在左下角、
   字幕 pointer-events:none，均不遮挡输入框与登录/注册按钮。
   ========================================================== */
.voice-btn{
  position:fixed;left:22px;bottom:22px;z-index:30;
  display:inline-flex;align-items:center;gap:7px;
  padding:9px 16px;border-radius:999px;
  background:rgba(4,16,38,.62);
  border:1px solid rgba(0,212,255,.42);
  color:#DBF5FF;font-size:13px;font-weight:600;letter-spacing:.3px;
  font-family:inherit;cursor:pointer;
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  box-shadow:0 6px 22px rgba(0,0,0,.35), inset 0 0 0 1px rgba(255,255,255,.04);
  transition:border-color .22s var(--ease), background .22s var(--ease),
             color .22s var(--ease), box-shadow .22s var(--ease);
}
.voice-btn:hover{border-color:rgba(0,212,255,.78);background:rgba(6,24,54,.76);color:#fff;}
.voice-btn:focus-visible{outline:2px solid rgba(0,212,255,.9);outline-offset:2px;}
.voice-btn .voice-ico{font-size:14px;line-height:1;}
/* 正在播放：淡青色光环 */
.voice-btn.on{border-color:rgba(0,212,255,.9);box-shadow:0 0 0 3px rgba(0,212,255,.14), 0 6px 22px rgba(0,0,0,.35);}
/* 已静音 / 无语音降级 */
.voice-btn.muted{color:var(--text-3);border-color:rgba(148,163,184,.34);}
/* 自动播放被浏览器拦截：脉冲呼吸，提示用户点一下 */
.voice-btn.hint{
  color:#fff;border-color:rgba(0,212,255,.95);
  animation:voicePulse 1.5s ease-in-out infinite;
}
@keyframes voicePulse{
  0%{box-shadow:0 0 0 0 rgba(0,212,255,.45), 0 6px 22px rgba(0,0,0,.35);}
  70%{box-shadow:0 0 0 14px rgba(0,212,255,0), 0 6px 22px rgba(0,0,0,.35);}
  100%{box-shadow:0 0 0 0 rgba(0,212,255,0), 0 6px 22px rgba(0,0,0,.35);}
}

.voice-caption{
  position:fixed;left:50%;top:22px;bottom:auto;z-index:29;
  transform:translateX(-50%) translateY(-8px);
  max-width:min(760px,86vw);
  padding:12px 20px;border-radius:12px;
  background:rgba(2,10,26,.66);
  border:1px solid rgba(0,212,255,.22);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  color:#E6F7FF;font-size:14.5px;line-height:1.7;text-align:center;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .35s var(--ease), transform .35s var(--ease), visibility .35s;
  /* 最多两行，超出省略 */
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.voice-caption.show{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);}

/* ==========================================================
   与口播逐字同步的实时文字特效（assets/js/voice-charts.js 驱动）
   放在任何变换元素之外；固定于画面下方，避开右下角登录卡。
   ========================================================== */
.voice-sub{
  position:fixed;left:3vw;bottom:clamp(20px,3.6vh,52px);z-index:28;
  width:min(58vw,1040px);
  text-align:center;white-space:nowrap;overflow:hidden;
  font-weight:800;letter-spacing:1px;line-height:1.35;font-size:22px; /* JS 按视口+字数覆盖 */
  color:transparent;
  background:linear-gradient(90deg,#5FE3F4 0%, #EAFBFF 46%, #FFFFFF 100%);
  -webkit-background-clip:text;background-clip:text;
  filter:drop-shadow(0 0 12px rgba(0,212,255,.45)) drop-shadow(0 2px 10px rgba(0,0,0,.62));
  opacity:0;visibility:hidden;pointer-events:none;transform:translateY(6px);
  transition:opacity .32s var(--ease), transform .22s var(--ease), visibility .32s;
}
.voice-sub.show{opacity:1;visibility:visible;transform:translateY(0);}
/* 换段：旧文本快速淡出 + 上移（本条在 .show 之后，同权重后者胜） */
.voice-sub.switching{opacity:0;transform:translateY(-12px);}
/* 长句两行模式（voice-charts.js 在单行字号 <18px 时加 .wrap）：
   允许折行 + 收紧行高；高度自适应（无固定 height）故不会被 overflow:hidden 裁掉第二行。 */
.voice-sub.wrap{white-space:normal;line-height:1.25;}
/* 极淡的扫描线扫过 */
.voice-sub::after{
  content:"";position:absolute;left:-40%;top:0;bottom:0;width:40%;
  background:linear-gradient(90deg,transparent,rgba(0,212,255,.12),transparent);
  pointer-events:none;animation:subScan 3.6s linear infinite;
}
@keyframes subScan{from{transform:translateX(0);}to{transform:translateX(350%);}}
/* 字号自适应测量期：临时摘掉上条扫描线。
   ::after 被 subScan 的 translateX 一路推到 140% 父宽（left:-40% + 350%×40%），
   其布局盒会撑大 .voice-sub 的 scrollWidth；而 fitSub 正是靠
   「scrollWidth > clientWidth」判定单行是否溢出 → 判定结果会随动画相位来回抖动
   （实测同一句 59 字在 33.3px/单行 与 22.2px/两行 之间跳动）。
   fitSub 量测期间给元素加 .measuring，把该伪元素彻底移出布局（display:none）。 */
.voice-sub.measuring::after{display:none;animation:none;}

@media (max-width: 880px){
  .voice-btn{left:14px;bottom:14px;padding:7px 12px;font-size:12px;gap:5px;}
  .voice-btn .voice-ico{font-size:13px;}
  /* 药丸已改「顶部居中」（见 .voice-caption 主规则）：此处不再用 bottom 抬升，
     必须显式 bottom:auto，否则主规则的 top:22px 与本条的 bottom:62px 同时生效，
     会把药丸从顶 22 拉到离底 62（实测 innerW=836 时高达 724px）。 */
  .voice-caption{top:22px;bottom:auto;font-size:13px;padding:10px 14px;max-width:88vw;}
}
/* 药丸避让字幕：仅在字幕显示时把「语音导览」药丸抬到字幕带之上（纯 CSS，零 JS）。
   背景：底部带自左向右=药丸(22~135.7)｜字幕文字(≈34~683 起)｜登录卡(≈751+)；三者挤在同一条带，
   而字幕框宽 650 要同时躲开药丸与卡片需 ≈607px 余量，物理不够 → 「改字幕呈现/收窄」会让 fitSub
   重算、② 矩阵作废 → 只能动药丸的垂直位置。
   触发：`.voice-btn` 在 DOM 中位于 `.voice-sub` 之前，故用 `:has(~ .voice-sub.show)`（字幕显示态）
   纯 CSS 判定；不支持 `:has()` 的浏览器整条规则被丢弃 → 药丸停在原位，功能不受影响。
   抬升量 = clamp(20px,3.6vh,52px)（字幕底） + max(64px, 2.5*subBase) + 10px（gap）。
        subBase = clamp(17px,2.1vw,44px)（voice-charts.js subBase()）。
   ── 为什么是 max(64px, 2.5*subBase)，以及 64px 为什么是「常数」：
      · ≥1121 档 `.voice-sub` 盒宽 = min(58vw,1040) ≥ 650 → 长句恒 ≤2 行；
        带宽 ≤ 2.25*subBase（两行字号 ≤ subBase*0.9、line-height 1.25），取 2.5*subBase 作上界即可。
      · ≤1120 档 `.voice-sub` 被 `@media(max-width:1120px){width:88vw}` 收窄（521 档仅 458.5px）
        → 59 字长句会排到 **3 行**。
      · **3 行只在字号被钉在下限 FLOOR2=16px 时出现**：两行候选的循环条件是
        `while (subLines() > 2 && twoSize > FLOOR2)`，只有降到下限仍排不下才留下 3 行
        → 3 行带宽 = 3 × 1.25 × 16 = 60px（+1px 取整 = 61px），**是与 subBase 无关的常数**。
      · 故 ≤1120 档带宽上界 = max(61px, 2.25*subBase)；取 max(64px, 2.5*subBase) 后对 61px 带留 ≈13px 余量。
      · ≥1219 档 2.5*subBase ≥ 64 → 与纯 2.5*subBase 完全一致（宽屏不多抬、不去撞品牌）。
   ── 已知（仅记录，不在本次范围）：`.voice-sub` 在 ≤1120 被 88vw 收窄后，59 字长句排到 3 行（16px）
      是**早于 m24 就存在**的排版结果，文字完整、无裁切；本次只让药丸尊重它，不顺手改 `.voice-sub`。
   ⚠ 必须包在 @media (min-width:521px)：`:has()` 使权重升到 (0,3,0)，否则会反杀 ≤520 档
     `.voice-btn{top:14px;bottom:auto}`(0,1,0) → top 与 bottom 同时生效、药丸被拉伸。
   ⚠ 必须放在 ≤880 块之后（≤880 档 .voice-btn{bottom:14px} 会被本条 (0,3,0) 覆盖 —— 即 521~880 档
     字幕显示时药丸同样抬到字幕带之上，避免撞字幕）。
   实测验收（1/2/3 行分态、11 宽 × 4 高、真实药丸右缘 135.7）：见 b13/_v5/r4_btn_summary.txt。 */
@media (min-width:521px){
  .voice-btn:has(~ .voice-sub.show){
    bottom:calc(clamp(20px, 3.6vh, 52px) + max(64px, 2.5 * clamp(2.1vw, 17px, 44px)) + 10px);
  }
}
/* 手机竖屏：登录卡会几乎占满宽度，按钮留在左下角会压到卡片，
   改到左上角（HUD 装饰区，最安全）。药丸同为主规则里的顶部居中，
   但需下移到左上按钮之下（按钮位于 top:14px），故 top 用 56px 并显式 bottom:auto。 */
@media (max-width:520px){
  .voice-btn{ left:14px; right:auto; top:14px; bottom:auto; }
  .voice-caption{ top:56px; bottom:auto; }
}
@media (prefers-reduced-motion: reduce){
  .voice-btn.hint{animation:none;}
  /* 品牌字呼吸降级为静态弱光晕（保留暗投影 + 白描边） */
  .login-brand-title{animation:none;filter:drop-shadow(0 4px 18px rgba(0,0,0,.55)) drop-shadow(0 0 6px rgba(255,255,255,.4));}
  /* 登录卡（自身 + 光晕伪元素）的 1.2s 快速呼吸 */
  .login-card,.login-card::after{animation:none;}
  /* 字幕扫描线（subScan） */
  .voice-sub::after{animation:none;}
  /* 引力锚 Logo：跟随系统「减少动态效果」。⚠️ 必须 !important：
     上面是 body:not(.motion-off) .em-*(0,2,0)，裸 .em-*(0,1,0) 特异度输了会静默失效。 */
  .em-orbit-a,.em-orbit-b,.em-core,.em-halo{animation:none !important;}
}

/* ==========================================================
   侧边导航 · 作业顺序（①-⑦ 阶段 + 每条步号）
   让导航本身告诉用户「先做什么、后做什么」。
   ========================================================== */
/* 分组标题里的阶段序号：比说明文字更突出，后面用 · 分隔 */
.nav-group-title .ngt-step{
  color:var(--cyan);font-weight:700;letter-spacing:0;
  margin-right:5px;opacity:.92;
}
/* 条目步号：等宽字体保证 1-16 位宽整齐，不与右侧徽章混淆 */
.ni-step{
  font-size:11px;font-weight:700;font-family:Consolas,Menlo,monospace;
  color:var(--text-3);min-width:14px;text-align:right;flex-shrink:0;opacity:.7;
  margin-right:-3px;   /* 抵消 .nav-item 的 11px gap，避免图标被推得太靠右 */
}
.nav-item:hover .ni-step{color:var(--cyan);opacity:1;}
.nav-item.active .ni-step{color:var(--cyan);opacity:1;}
/* 折叠态（72px 宽）空间不够：隐藏步号，避免把图标挤歪 */
.sidebar.collapsed .ni-step{display:none;}

/* ==========================================================
   登录页 · 与口播同步的数据可视化层（assets/js/voice-charts.js）
   HUD 语汇：深空半透明底 + 1px 青色描边 + L 形角标 + 等宽小标签
   ========================================================== */
.login-viz{
  position:fixed;
  left:auto;
  right:clamp(24px,3vw,64px);
  top:clamp(80px,11vh,150px);
  bottom:auto;
  width:min(36vw,620px);       /* 由 46vw/560 放大 */
  height:min(34vh,300px);      /* 由 30vh/240 放大 */
  margin:0;
  pointer-events:none;
  z-index:5;                   /* 仍低于登录卡 */
  opacity:0;transition:opacity .5s ease;
  visibility:hidden;           /* 隐藏态：不可见 + 不参与命中测试 + 读屏读不到 */
  transform: rotateY(-15deg) rotateX(3deg) rotate(2deg);   /* 与登录卡同一组 */
  transform-origin: right center;
}
.login-viz.on{opacity:1;visibility:visible;}

.viz-panel{
  position:absolute;left:0;right:0;top:50%;transform:translateY(calc(-50% + 12px));
  opacity:0;transition:opacity .45s ease,transform .45s ease;
  background:rgba(3,10,26,.62);
/* [UI-1] 移除卡片感：字幕面板描边（原 border:1px solid rgba(0,212,255,.28)）*/
  border-radius:1px;
  padding:22px 20px 24px;
  box-sizing:border-box;
  font-family:Consolas,"SFMono-Regular",Menlo,monospace;
  color:#DCEBFF;
  backdrop-filter:blur(3px);
  -webkit-backdrop-filter:blur(3px);
}
.viz-panel.in{opacity:1;transform:translateY(-50%);}
.viz-panel.noanim{transition:none;}

/* L 形角标：4 个 i 各画一角 */
.viz-corner{position:absolute;width:12px;height:12px;border:2px solid rgba(0,212,255,.85);}
.viz-corner{display:none;}               /* [UI-1] 去角标（放定义之后以覆盖）*/
.login-card .hud-corner{display:none;}   /* [UI-1] 去角标 */
.viz-corner.tl{top:-1px;left:-1px;border-right:0;border-bottom:0;}
.viz-corner.tr{top:-1px;right:-1px;border-left:0;border-bottom:0;}
.viz-corner.bl{bottom:-1px;left:-1px;border-right:0;border-top:0;}
.viz-corner.br{bottom:-1px;right:-1px;border-left:0;border-top:0;}

.viz-kicker{
  font-size:11px;letter-spacing:2px;color:var(--cyan);text-transform:uppercase;
  padding-bottom:8px;margin-bottom:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  border-bottom:1px dashed rgba(0,212,255,.28);
}
.viz-body{display:flex;flex-direction:column;align-items:center;gap:12px;text-align:center;}

/* query */
.viz-qmark{
  font-size:clamp(64px,8vw,128px);font-weight:900;line-height:1;letter-spacing:0;
  color:transparent;background:linear-gradient(180deg,var(--cyan),rgba(99,102,241,.9));
  -webkit-background-clip:text;background-clip:text;
  filter:drop-shadow(0 0 18px rgba(0,212,255,.45));
}
.viz-line{font-size:clamp(17px,1.55vw,23px);color:#E7F3FF;letter-spacing:1px;line-height:1.45;}

/* chips3 */
.viz-sub{font-size:14px;color:rgba(190,225,255,.85);letter-spacing:.5px;line-height:1.5;}
.viz-chips{display:flex;flex-wrap:wrap;gap:9px;justify-content:center;}
.viz-chip{
  font-size:13.5px;padding:9px 15px;border-radius:4px;
  border:1px solid rgba(0,212,255,.3);color:rgba(190,225,255,.55);
  opacity:.45;transition:opacity .35s ease,box-shadow .35s ease,color .35s ease,border-color .35s ease;
}
.viz-chip.on{
  opacity:1;color:#EAF7FF;border-color:var(--cyan);
  box-shadow:inset 0 0 14px rgba(0,212,255,.3),0 0 10px rgba(0,212,255,.25);
}

/* compare */
.viz-compare{display:flex;gap:10px;width:100%;}
.viz-col{
  flex:1 1 0;min-width:0;padding:11px 9px;border-radius:3px;
  border:1px solid rgba(0,212,255,.18);opacity:.42;
  transition:opacity .35s ease,box-shadow .35s ease,border-color .35s ease,background .35s ease;
}
.viz-col.hot{opacity:1;border-color:var(--cyan);background:rgba(0,212,255,.09);box-shadow:0 0 16px rgba(0,212,255,.22);}
.viz-col-t{font-size:12.5px;letter-spacing:1px;color:var(--cyan);margin-bottom:8px;}
.viz-col-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px;}
.viz-col-list li{font-size:13px;color:rgba(220,235,255,.9);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* quote */
.viz-quote{
  font-size:clamp(26px,2.9vw,48px);font-weight:900;line-height:1.25;letter-spacing:1px;
  color:transparent;background:linear-gradient(180deg,#FFFFFF,var(--cyan));
  -webkit-background-clip:text;background-clip:text;
  filter:drop-shadow(0 0 16px rgba(0,212,255,.35));
}
.viz-quote-sub{font-size:14.5px;color:rgba(190,225,255,.85);letter-spacing:1px;line-height:1.5;}

/* bignum */
.viz-bignum{display:flex;align-items:baseline;gap:8px;justify-content:center;}
.viz-big-num{
  font-size:clamp(46px,5vw,88px);font-weight:900;line-height:1;
  color:transparent;background:linear-gradient(180deg,#FFFFFF,var(--cyan));
  -webkit-background-clip:text;background-clip:text;
  filter:drop-shadow(0 0 18px rgba(0,212,255,.4));
}
.viz-big-unit{font-size:clamp(17px,1.7vw,26px);font-weight:700;color:var(--cyan);}
.viz-note{font-size:13px;color:rgba(190,225,255,.78);letter-spacing:.5px;}

/* donut */
.viz-donut{position:relative;width:168px;height:168px;flex:0 0 auto;}
.viz-donut-svg{width:100%;height:100%;transform:rotate(-90deg);}
.viz-donut-bg{fill:none;stroke:rgba(0,212,255,.12);stroke-width:10;}
.viz-donut-fg{fill:none;stroke:var(--cyan);stroke-width:10;stroke-linecap:round;filter:drop-shadow(0 0 6px rgba(0,212,255,.5));}
.viz-donut-pct{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:clamp(32px,3.1vw,48px);font-weight:900;color:#EAF7FF;letter-spacing:0;
}

/* bars */
.viz-bars{display:flex;align-items:flex-end;justify-content:center;gap:18px;width:100%;height:208px;}
.viz-bar-col{flex:1 1 0;max-width:104px;display:flex;flex-direction:column;align-items:center;gap:7px;height:100%;}
.viz-bar-val{font-size:14px;font-weight:700;color:var(--cyan);}
.viz-bar-track{
  flex:1 1 auto;width:100%;display:flex;align-items:flex-end;
  background:rgba(0,212,255,.06);border-bottom:1px solid rgba(0,212,255,.3);
}
.viz-bar-fill{display:block;width:100%;height:0;background:linear-gradient(180deg,var(--cyan),rgba(99,102,241,.55));box-shadow:0 0 14px rgba(0,212,255,.4);}
.viz-bar-x{font-size:12.5px;color:rgba(190,225,255,.8);}

/* progress */
.viz-progress{display:flex;align-items:center;gap:16px;width:100%;}
.viz-progress-bar{
  flex:1 1 auto;height:20px;overflow:hidden;border-radius:2px;
  background:rgba(0,212,255,.08);border:1px solid rgba(0,212,255,.3);
}
.viz-progress-fill{
  display:block;height:100%;width:0;
  background:linear-gradient(90deg,rgba(0,212,255,.55),var(--cyan));
  box-shadow:0 0 14px rgba(0,212,255,.45);
}
.viz-progress-pct{font-size:clamp(26px,2.8vw,46px);font-weight:900;color:#EAF7FF;min-width:100px;text-align:right;}

/* warn */
.viz-warn{
  width:100%;padding:12px 12px 10px;border-radius:3px;
  border:1px solid rgba(255,86,86,.55);background:rgba(60,10,14,.42);
  box-shadow:inset 0 0 18px rgba(255,60,86,.18);
}
.viz-warn-title{font-size:clamp(17px,1.7vw,25px);font-weight:800;color:#FF8A8A;letter-spacing:1px;margin-bottom:11px;}
.viz-warn-row{display:flex;align-items:center;gap:9px;font-size:14px;color:#FFD5D5;padding:6px 0;border-top:1px dashed rgba(255,86,86,.25);}
.viz-x{color:#FF4D4D;font-style:normal;font-weight:900;}

/* layers */
.viz-layers{position:relative;width:100%;padding-left:44px;display:flex;flex-direction:column;gap:9px;}
.viz-layers::before{
  content:"";position:absolute;left:8px;top:6px;bottom:6px;width:14px;
  border:2px solid rgba(0,212,255,.6);border-right:0;border-radius:8px 0 0 8px;
}
.viz-layer{
  padding:12px 13px;border-radius:3px;text-align:left;letter-spacing:.5px;
  background:rgba(6,20,46,.6);
  font-size:13.5px;color:#E2F0FF;
}
.viz-brace-note{font-size:12.5px;letter-spacing:2px;color:var(--cyan);}

/* sources */
.viz-sources{display:flex;flex-direction:column;gap:9px;width:100%;}
.viz-source{
  display:flex;align-items:center;gap:9px;padding:11px 13px;border-radius:3px;
  border:1px solid rgba(0,212,255,.22);opacity:.3;transform:translateX(-6px);
  transition:opacity .4s ease,transform .4s ease,border-color .4s ease,background .4s ease;
}
.viz-source.on{opacity:1;transform:none;border-color:rgba(0,212,255,.5);background:rgba(0,212,255,.06);}
.viz-source-dot{width:8px;height:8px;border-radius:50%;background:var(--cyan);box-shadow:0 0 10px var(--cyan);flex:0 0 auto;}
.viz-source-t{font-size:14px;color:#E6F3FF;letter-spacing:.5px;}

/* cta（父层 pointer-events:none，这里必须放开才能点击） */
.viz-cta{
  pointer-events:auto;cursor:pointer;font-family:inherit;
  font-size:15px;font-weight:700;letter-spacing:1px;color:#EAF9FF;
  padding:15px 26px;border-radius:3px;border:1px solid var(--cyan);
  background:linear-gradient(180deg,rgba(0,212,255,.22),rgba(0,100,180,.16));
  box-shadow:0 0 18px rgba(0,212,255,.28);transition:filter .2s ease,transform .2s ease;
}
.viz-cta:hover{filter:brightness(1.15);transform:translateY(-1px);}

/* CTA 触发的登录卡高亮脉冲 */
.card-flash{animation:cardFlash 1.2s ease;}
@keyframes cardFlash{
  0%,100%{border-color:rgba(0,212,255,.25);box-shadow:0 24px 70px rgba(0,0,0,.45),inset 0 1px 0 rgba(255,255,255,.08);}
  50%{border-color:var(--cyan);box-shadow:0 0 34px rgba(0,212,255,.55),inset 0 0 22px rgba(0,212,255,.16);}
}

/* 窄屏空间不够：只留语音 + 字幕，隐藏图解层。
   断点取 1120px（原 1180px）：面板已 fixed 出流、宽度 min(46vw,560px)，
   实测 innerWidth ≥1110 时登录卡左边界 ≥713px、面板右边界 ≤555px，互不重叠；
   innerWidth <1110 时三列换行、卡片左移，此时才需要隐藏。 */
@media (max-width:1120px){
  .login-viz{display:none;}
  .voice-sub{width:88vw;}
}

/* 高度不够：数据面板若仍置于登录卡上方（top → 卡片上缘）必然与卡片重叠
   （登录卡内容高约 550~590px；实测窗口高 ≤1150px 时重叠）。
   此时把面板改为「与卡片同排、贴卡片左侧、底对齐」：仍处屏幕右半区且**明显放大**
   （width min(34vw,620) × height min(34vh,270)：1896 视口下 620×270=167,400px²，
   较改动前左下角那版 min(46vw,560)×min(30vh,240)=134,400px² 大 +25%），
   同时保证「与卡片水平不重叠、与品牌字（水平+垂直）不重叠」。窗口高 >1150px 仍走上面的
   「置于卡片上方」版式（视口越高越宽松，1080p 之上完全分开）。 */
@media (max-height:1150px){
  .login-viz{
    top:auto;
    bottom:clamp(150px,18vh,200px);   /* 上抬让出底部字幕带（原 24px/4vh/64px）。
                                         下限 140→150：实测 h≤720 档「字幕带顶边 − 面板底边」余量一度低至 2.4px，
                                         抬高 10px 后全档最小余量 ≥12px（门槛 8px）。 */
    right:calc(clamp(24px,3vw,64px) + min(30vw,480px) + 40px);
    width:min(34vw,620px);
    height:min(34vh,270px);   /* 1896 档：面板宽 620px（= min(34vw,620)）× 3D 倾斜
                                 （rotateY -15° / rotateX 3° / rotate 2°）使包围盒外扩，故 height 由 280 收到 270。
                                 ★与品牌字的分离**靠水平方向**（品牌字字形右缘 < 面板可见面左缘）实现，
                                   不靠垂直方向 —— 早期「面板顶边必须 > 品牌字块底边」的判据是错的
                                   （1896 档两者垂直方向本就相交却并不重叠，因水平不相交）。 */
    transform-origin:right bottom;
  }
}
/* 视口再矮一档：3D 倾斜（rotate 2° / rotateX 3° / rotateY -15°）会把面板「投影顶边」
   抬高约 面板宽×sin2° ≈ 21px，即便布局顶边已低于品牌字，投影顶边仍可能压到品牌字块。
   故对较矮视口（≤790px，约 ≤880 高的窗口）进一步收 height，下限 min(30vh,250px)，
   保证 4 个宽度档（1176/1256/1416/1896）下「面板顶边 > 品牌字底边」。 */
@media (max-height:790px){
  .login-viz{ height:min(30vh,250px); }
  /* 批次14：cue23 的 layers（信源分层）在 628 高视口的 188px 外壳里放不下，
     垂直溢出会同时压到 #loginCard 与底部字幕带。此处仅压缩 layers 自身的
     内边距/字号/间距 —— 不动 .login-viz 几何，故不影响 m24 已验收的
     「品牌字 / 卡片 / 字幕」分离不变量。压缩后 2 层 + 注脚可完整落入外壳。 */
  .viz-layers{gap:6px;}
  .viz-layer{padding:8px 11px;font-size:12.5px;}
  .viz-brace-note{font-size:11.5px;letter-spacing:1px;}
}
/* 短屏档（视口高 ≤1150px：面板被放到右下、与登录卡同排）会与底部字幕带重叠；
   上抬（bottom:clamp(150px,18vh,200px)）已并入上面的 @media (max-height:1150px) 块。
   ── 判定口径（唯一）：绘制面 = .viz-panel（含 background / border / backdrop-filter），
      不用 .login-viz 的 3D 倾斜包围盒，也不用内部 .viz-* 元素。
   ── 上抬后面板顶边会落入品牌字带，须保证「面板可见面左缘 ≥ 品牌字字形右缘」。
      品牌-面板水平间距随视口宽单调增：inner 1176 = −4.0px（上抬会切到品牌字竖条）·
      1256 起 ≥0。故对 (max-height:1150px) and (max-width:1415px) 把面板右边距 +40px→+20px，
      整体右移 20px：1176 档间距 −4.0 → +16.0px（卡片间距 20px 仍 ≥10px）。
   ── 逐格采样点命中实测（elementsFromPoint，2px 步长）：① 字幕/品牌覆盖矩阵见 b13/_v5/q9_summary.txt；
      面板高时间不变性(a)、最小余量(b)、面板↔卡片屏幕净距(c) 三项探测见 b13/_v5/q12_summary.txt。 */
@media (max-height:1150px) and (max-width:1415px){
  .login-viz{ right:calc(clamp(24px,3vw,64px) + min(30vw,480px) + 20px); }
}
@media (prefers-reduced-motion: reduce){
  .login-viz{transition:none;}
  .viz-panel{transition:none;}
  .viz-chip,.viz-col,.viz-source{transition:none;}
  .viz-cta{transition:none;}
  .card-flash{animation:none;}
  .card-flash{border-color:var(--cyan);box-shadow:0 0 24px rgba(0,212,255,.4);}
}

/* ==========================================================
   登录页精简（D2 · 2026-09-22）
   ----------------------------------------------------------
   目标：① 去掉品牌文字块与地球舞台 ② 去掉中间打字机面板
         ③ 登录卡回到屏幕正中央并「摆正」（去掉 3D 倾斜）
   ★ 必须放在 main.css 末尾：本块要盖过 L1425 .login-card、L1327 .login-earth、
     L1787 .voice-caption、L1807 .voice-sub、L1919 .login-viz、
     L1514 @media(max-width:980px) 等处的同类声明。
     （媒体查询不提升权重；同权重下后置者胜 ⇒ 放末尾即可全覆盖）
   ★ 只做 CSS 覆盖，HTML 结构一律不动 ⇒ 想恢复：整块删掉即可。
   ----------------------------------------------------------
   1) .login-earth 既是品牌字容器、也是地球舞台，同时是 .login-wrap 的 flex 子项
      （flex-basis = clamp(400px, min(56vw,76vh), 900px)）。display:none 让它彻底
      退出布局 ⇒ .login-wrap 只剩 .login-card 一个 flex 子项，由它原有的
      align-items/justify-content:center 自然居中，故不改 .login-wrap 的居中设置。
   2) .login-viz（#vizStage 打字机/数据面板）同为 .login-wrap 的 flex 子项，一并隐藏。
   3) .voice-caption / .voice-sub 是登录页口播的「文字」字幕，按需求去掉；
      🔊 按钮 #voiceBtn（.voice-btn）不动 —— 它是 #loader 的 position:fixed 子项，
      不参与 .login-wrap 的 flex 布局，故隐藏上面三项不会影响它。
   4) #loader 的 perspective（L1276-1277）本块【故意保留、不删】：.login-grid
      （L1293，保留的机器人背景网格）自带 transform:perspective(500px) rotateX(55deg)，
      会叠加祖先透视 ⇒ 祖先 perspective 仍有活的 3D 消费者，删掉会改变背景网格的投影
      （已实测像素差，见 D2 交付报告）。
   ========================================================== */
.login-earth{display:none !important;}          /* 品牌块 + 地球舞台：退出布局 */
.login-viz{display:none !important;}            /* 中间打字机面板：退出布局 */
.voice-caption,.voice-sub{display:none !important;}   /* 登录页字幕文字（保留 🔊 按钮） */

.login-card{
  position:relative !important;                 /* 由 fixed（右下角）改回流内 flex 子项 */
  left:auto !important; right:auto !important;
  top:auto !important;  bottom:auto !important;
  margin:0 !important;
  width:min(92vw,440px) !important;
  max-width:100% !important;                    /* 窄屏兜底：不超出 .login-wrap 内容区，防横向截断 */
  transform:none !important;                    /* ★ 摆正：去掉 rotateY(-15deg) rotateX(3deg) rotate(2deg) */
  background:rgba(10,31,68,.82);                /* 摆正后无 3D 透视压暗，略提不透明度保证可读 */
}

/* ==========================================================
   登录页再调整（D2b · 2026-09-22）
   ----------------------------------------------------------
   ① 去「动的地球/星点/轨道」：它 = .login-bg 内 <canvas id="spaceCanvas" class="space-canvas">
      （login-bg.js:11 取元素、L44 buildGlobe()、L316 buildGlobe()+buildOrbits()）。
      ★ 机器人背景在 #loader background-image（main.css:1266 url(../img/login-robot.jpg)），
        与 canvas 无关 ⇒ 仅 CSS 隐藏 canvas，不动 HTML、不改 login-bg.js，可一键恢复。
   ② 登录卡改【靠右】：.login-earth/.login-viz 都 display:none 后，.login-wrap 只剩
      .login-card 一个 flex 子项，被 L1321 justify-content:center 居中。改为 flex-end +
      margin-left:auto 双保险，并覆盖 L1514 @media(max-width:980px) 里 .login-card{margin:0 auto}。
   ========================================================== */
.space-canvas{display:none !important;}            /* 去动的地球/星点/轨道；机器人背景不受影响 */

.login-wrap{
  width:100% !important; max-width:100% !important;            /* 铺满视口：去掉 max-width:1440 居中导致的额外内缩 */
  padding-left:0 !important; padding-right:0 !important;       /* 去掉 40px 内边距，使卡片右边距直接相对视口（满足 V3） */
  justify-content:flex-end !important;                        /* 登录卡靠右（卡片是 .login-wrap 唯一 flex 子项） */
}
.login-card{
  margin-left:auto !important;                     /* 不依赖父 justify-content，双保险靠右 */
  margin-right:clamp(16px,4vw,64px) !important;
}

/* 窄屏也要靠右：覆盖 L1514 @media(max-width:980px) .login-card{margin:0 auto} 与 L1321 align-items:center */
@media(max-width:980px){
  .login-wrap{
    width:100% !important; max-width:100% !important;
    padding-left:24px !important; padding-right:24px !important;  /* 保留 L1515 的 24px 内边距 */
    justify-content:center !important;                    /* 窄屏垂直居中（覆盖宽屏 flex-end 的“底部”效应） */
    align-items:flex-end !important;                     /* 窄屏卡片靠右（覆盖 L1321 align-items:center） */
  }
  .login-card{
    width:min(78vw,380px) !important;             /* 窄屏比内容区窄，留出右距（覆盖 D2 块 max-width:100% 导致“填满无法靠右”） */
    max-width:none !important;
    margin-left:auto !important;
    margin-right:clamp(12px,3vw,24px) !important;  /* 窄屏右距缩小，防卡片挤出视口 */
  }
}

/* ==========================================================
   登录背景 · 机器人黄色灯位闪动（D3c · 2026-09-23）
   ----------------------------------------------------------
   背景 login-robot.jpg（1028x683）以 cover + center 32% 绘制在
   #loader 第三层背景。本层用同一 cover 公式构造一个与图片绘制
   矩形完全重合的定位层，灯位用图片百分比坐标摆放 —— 任意窗口
   尺寸下光斑始终钉在图片灯位上，不漂移。
   光斑 mix-blend-mode:screen 只提亮不改色；动画为不规则明暗
   （twinkle），各灯时长/相位错开避免齐闪；reduced-motion 静止。
   ========================================================== */
.login-lights{
  position:absolute;
  --lw:max(100vw, 100vh * 1.50512);         /* 图片绘制宽 = max(视口宽, 视口高 x 宽高比) */
  --lh:max(100vh, 100vw * 0.66440);         /* 图片绘制高 */
  width:var(--lw);height:var(--lh);
  left:calc((100vw - var(--lw)) / 2);       /* 水平 center（同 background-position） */
  top:calc((100vh - var(--lh)) * 0.32);     /* 垂直 32%（同 background-position） */
  pointer-events:none;
}
.login-lights i{
  position:absolute;aspect-ratio:1;border-radius:50%;
  transform:translate(-50%,-50%);
  background:radial-gradient(circle,
    rgba(255,216,144,.62) 0%,
    rgba(255,180,86,.36) 34%,
    rgba(255,152,52,.13) 62%,
    rgba(255,140,40,0) 78%);
  mix-blend-mode:screen;
  animation:hhzLightFlicker var(--d,2.6s) ease-in-out var(--dl,0s) infinite;
  will-change:opacity;
}
.login-lights i:nth-child(1){left:31.8%;top:25.0%;width:15%;  --d:2.2s;--dl:0s}    /* 头部眼部主灯 */
.login-lights i:nth-child(2){left:14.7%;top:62.1%;width:10%;  --d:2.8s;--dl:.6s}   /* 背部肩灯面板 */
.login-lights i:nth-child(3){left:33.9%;top:62.6%;width:5.5%; --d:1.9s;--dl:1.1s}  /* 胸口小灯 */
.login-lights i:nth-child(4){left:49.3%;top:93.7%;width:7%;   --d:2.5s;--dl:.3s}   /* 手部键盘灯 */
.login-lights i:nth-child(5){left:90.4%;top:60.6%;width:9%;   --d:3.1s;--dl:1.4s}  /* 大屏右侧琥珀数据块 */
.login-lights i:nth-child(6){left:43.4%;top:12.7%;width:6.5%; --d:2.1s;--dl:.9s}   /* 大屏顶部琥珀条 */
.login-lights i:nth-child(7){left:2.9%; top:25.8%;width:5%;   --d:2.9s;--dl:1.7s}  /* 左缘暖色光斑 */
@keyframes hhzLightFlicker{
  0%,100%{opacity:.20}
  38%{opacity:.88}
  52%{opacity:.42}
  66%{opacity:.95}
  82%{opacity:.50}
}
@media (prefers-reduced-motion:reduce){
  .login-lights i{animation:none;opacity:.5}
}

/* ==========================================================
   登录卡缩小（D2d · 2026-09-23）
   ----------------------------------------------------------
   需求：位置不变（右侧、垂直居中，压在大屏上），卡片整体再缩小一档。
   只在末尾追加覆盖，不改上方任何规则；恢复 = 整块删除。
   宽度 440 -> 380（约 -14%），内边距 22 -> 16，行距/字号同步微缩，
   保持卡片内部比例协调。
   ========================================================== */
.login-card{
  width:min(92vw,380px) !important;
  padding:16px 16px 14px !important;
}
.login-card-head{margin-bottom:12px !important;padding-bottom:9px !important;}
.login-role-desc{font-size:11px !important;margin-bottom:12px !important;padding:6px 7px !important;}
.login-tabs{gap:7px !important;margin-bottom:12px !important;}
.login-tabs button{padding:8px 6px !important;border-radius:8px !important;}
.login-tabs button span{font-size:12px !important;}
.login-field{margin-bottom:12px !important;}
.login-field label{font-size:11px !important;margin-bottom:5px !important;}
.login-field input{padding:9px 38px 9px 12px !important;font-size:13px !important;border-radius:8px !important;}
.login-hint{font-size:10px !important;}
.login-btn{padding:11px !important;font-size:14px !important;border-radius:9px !important;margin-top:6px !important;}
.login-btn.secondary{margin-top:8px !important;}
.login-msg{margin-top:8px !important;min-height:16px !important;}
@media(max-width:980px){
  .login-card{width:min(70vw,330px) !important;}
}


/* ==========================================================
   登录卡左移（D2e · 2026-09-23）
   ----------------------------------------------------------
   需求：在当前右侧位置基础上再往左移一档（保持垂直居中、压在大屏上方不变）。
   实现：缩小 right-anchored 的右外边距（.login-wrap 为 flex-end，
        右边缘 = 视口宽 - margin-right，margin-right 越小卡片越靠左）。
   只在末尾追加覆盖，恢复 = 整块删除。
   ========================================================== */
.login-card{
  margin-right:clamp(12px,2vw,20px) !important;   /* 桌面：右距 64px -> 20px（左移约 44px） */
}
@media (max-width:980px){
  .login-card{
    margin-right:clamp(10px,2.5vw,16px) !important; /* 窄屏同步左移 */
  }
}

/* ==========================================================
   可见度体检（visibility.js）· 批 Y-lite · 20260925s43
   ----------------------------------------------------------
   只在末尾追加覆盖，类名前缀 ve- / vi- 保证不与既有类冲突。
   恢复 = 整块删除。
   ========================================================== */
.vi-row{
  padding:11px 0;
  border-bottom:var(--bd);
}
.vi-row:last-child{ border-bottom:none; }
.vi-head{ display:flex; align-items:center; gap:8px; }
.vi-icon{ font-size:14px; }
.vi-name{ font-size:13.5px; font-weight:600; color:var(--text-1); }
.vi-verdict{ font-size:12.5px; color:var(--text-2); line-height:1.75; margin:5px 0 0 22px; }
.vi-fix{
  font-size:12.5px; color:var(--text-2); line-height:1.75; margin:6px 0 0 22px;
  padding:8px 10px; background:var(--surface-2); border-radius:8px;
  border-left:3px solid var(--accent);
}
.ve-box{ margin:7px 0 0 22px; }
.ve-box>summary{
  cursor:pointer; font-size:12px; color:var(--text-3);
  list-style:none; user-select:none;
}
.ve-box>summary::-webkit-details-marker{ display:none; }
.ve-box>summary::before{ content:'B8  '; }
.ve-box[open]>summary::before{ content:'BE  '; }
.ve-box>summary:hover{ color:var(--text-2); }
.ve-grid{
  margin-top:7px; padding:10px 11px; background:var(--surface-2); border-radius:8px;
  display:grid; grid-template-columns:76px 1fr; gap:5px 10px; align-items:start;
  font-size:12px; line-height:1.7;
}
.ve-k{ color:var(--text-3); }
.ve-v{ color:var(--text-2); word-break:break-all; }
.ve-pre{
  margin:0; font-family:ui-monospace, Menlo, Consolas, monospace; font-size:11.5px;
  white-space:pre-wrap; word-break:break-all; color:var(--text-2);
  max-height:180px; overflow:auto;
}
@media (max-width:640px){
  .ve-grid{ grid-template-columns:1fr; }
  .ve-k{ font-weight:600; }
}

/* ==========================================================
   侧栏 LOGO · 自转地球（2026-10-01 替换「引力锚」）
   ----------------------------------------------------------
   ⚠️ 必须放在 main.css 末尾：本块要盖过 L1811 .earth-mark img 的圆形裁切与青色外发光。
   动效几何：球面经线的正交投影 = 大圆横向压扁 |cos λ|（λ = 经度）。
   6 条经线相位差 = 周期/6 = 30°，正好铺满 0–150°，视觉上相邻经线等距。
   纬线与外轮廓不动 —— 它们是固定坐标帧，一动就不像球了。
   时长语义：emTurn 的周期 = 转过 180°（|cos| 的周期），不是转一圈 ⇒ 7s/半圈、全周约 14s。
   ⚠️ 动画名一律 em 前缀：main.css 已有 earthSpin（登录页用，语义是 rotate），
      同名 @keyframes 后者覆盖前者 —— 已踩过一次，勿改回。
   ⚠️ 门控必须写 body:not(.motion-off)，与 L1263 起的 .em-* 保持一致。 */
.em-mer{transform-box:view-box;transform-origin:32px 32px;}
@keyframes emTurn{
  0%{transform:scaleX(1);}
  16.667%{transform:scaleX(.866);}
  33.333%{transform:scaleX(.5);}
  50%{transform:scaleX(.03);}      /* 不写 0：奇异矩阵在个别内核上会被整帧丢弃 */
  66.667%{transform:scaleX(.5);}
  83.333%{transform:scaleX(.866);}
  100%{transform:scaleX(1);}
}
body:not(.motion-off) .em-mer{animation:emTurn 7s linear infinite;}
/* ⚠️ 这 5 条必须排在上面那条之后：两者特异度同为 (0,2,1)，靠源码顺序取胜；
   若提前写，animation 简写会把 animation-delay 重置回 0s，6 条经线就同步了。 */
body:not(.motion-off) .em-m2{animation-delay:-1.1667s;}
body:not(.motion-off) .em-m3{animation-delay:-2.3333s;}
body:not(.motion-off) .em-m4{animation-delay:-3.5s;}
body:not(.motion-off) .em-m5{animation-delay:-4.6667s;}
body:not(.motion-off) .em-m6{animation-delay:-5.8333s;}

/* 轨道环上的卫星亮斑：呼吸。
   ⚠️ 必须用 fill-box 绕自身圆心缩放 —— 用 view-box 会变成绕 (32,32) 缩放，亮斑会被拽进球心。 */
.em-satg{transform-box:fill-box;transform-origin:50% 50%;}
@keyframes emSatG{0%,100%{opacity:.42;transform:scale(.88);}50%{opacity:1;transform:scale(1.25);}}
body:not(.motion-off) .em-satg{animation:emSatG 2.6s ease-in-out infinite;}

/* img 回退层（注入脚本没跑时的兜底）：
   原 border-radius:50% 是为「方形底图裁圆」写的，自转地球是透明底方图，再裁圆会切掉轨道环两端；
   青色外发光也会和金色地球撞色，改成中性投影。 */
.earth-mark img{border-radius:0;filter:drop-shadow(0 2px 6px rgba(0,0,0,.45));}

/* 关动效时的静态兜底（站内 body.motion-off 与系统 prefers-reduced-motion 两条都要覆盖）。
   ★ 绝不能写 animation:none —— 那会让 6 条经线的 transform 变回 none、全部退回初始整圆，
     与球轮廓完全重合，「地球」就退化成「一个圆 + 几条横线」（headless 截图已实测到）。
     正确做法是「暂停」：负的 animation-delay 仍把 6 条经线钉在各自相位上，
     定格成一颗「转了一半停住」的完整线框地球。
   ⚠️ 站内开关这条必须连 delay 一起补：body.motion-off 下上面那 6 条 delay 规则不生效，
     只暂停不补相位的话，6 条又会同步到 0%。 */
body.motion-off .em-mer{animation:emTurn 7s linear infinite;animation-play-state:paused;}
body.motion-off .em-m2{animation-delay:-1.1667s;}
body.motion-off .em-m3{animation-delay:-2.3333s;}
body.motion-off .em-m4{animation-delay:-3.5s;}
body.motion-off .em-m5{animation-delay:-4.6667s;}
body.motion-off .em-m6{animation-delay:-5.8333s;}
body.motion-off .em-satg{animation:none;}
@media (prefers-reduced-motion: reduce){
  .em-mer{animation-play-state:paused !important;}
  .em-satg{animation:none !important;}
}

/* ============================================================
   批 B3 · I1 · 打印 / 另存为 PDF 样式（零依赖，window.print）
   只影响打印，不影响屏幕。核心：隐藏外壳、卡片不切断、颜色保真、A4。
   ============================================================ */
@media print {
  @page { size: A4; margin: 12mm; }

  /* ★ 2026-10-01：打印时必须取消「界面缩放」。
     屏幕上的 --ui-zoom 是给看屏用的；A4 版面已按 100% 调好，
     若沿用缩放（例如 112%）会把报告撑出页宽、分页错乱。
     这里同时归零 zoom 与宽度补偿，恢复原始排版。 */
  .content { zoom: 1 !important; width: auto !important; }

  html, body { background: #fff !important; }
  *, *::before, *::after {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }

  /* 隐藏一切外壳与操作元素 */
  .sidebar, .topbar, .page-actions, .toast, .modal, .mask, .drawer,
  .nav-item, .sidebar-foot, .credit-box, .collapse-btn, .hamburger,
  .hint-bar, .tooltip, .empty button, button { display: none !important; }

  /* 重置布局：外壳改块级，主区占满、可滚动不被裁切 */
  .app { display: block !important; height: auto !important; overflow: visible !important; }
  .main { flex: none !important; display: block !important; overflow: visible !important;
          box-shadow: none !important; min-width: 0 !important; }
  .page { padding: 0 !important; }
  .page-head { display: none !important; }

  /* 卡片不被切成两半 */
  .card { break-inside: avoid; page-break-inside: avoid; border: 1px solid #e2e8f0 !important;
          box-shadow: none !important; margin-bottom: 14px !important; }
  .grid, .stat-strip, .g-2, .g-3, .g-4 { break-inside: avoid; page-break-inside: avoid; }

  /* 中文字体回退链，禁止方框 */
  body { font-family: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB",
         "Heiti SC", "SimHei", "WenQuanYi Micro Hei", sans-serif !important;
         color: #0f172a !important; font-size: 12px !important; }

  /* 表格在打印里也要好看 */
  .tbl, table { font-size: 11px !important; }
  .tbl th, .tbl td { border: 1px solid #cbd5e1 !important; padding: 5px 7px !important; }

  /* 图表（canvas）保真可见 */
  canvas { max-width: 100% !important; }

  /* 每页页脚：报告名 + 客户 + 时间 + 页码 */
  @page { @bottom-center { content: "引力 GEO · " counter(page) " / " counter(pages);
         font-size: 9px; color: #94a3b8; } }
}

/* ==========================================================
   说明书页（功能与介绍 · 操作说明书）  · 20261001s70
   ----------------------------------------------------------
   这一整块只引用 token（--accent-a/-b/-soft/-line、--text-*、
   --surface-*、--border、--success-ink、--warning-ink），
   不写死任何颜色 —— 浅色与深色（body.theme-tech）都自动成立。
   这是 s69 配色统一之后定下的规矩，新页面必须遵守。
   ========================================================== */
.docs-tabs{display:flex;gap:8px;margin:14px 0 16px;flex-wrap:wrap}
.docs-tab{padding:8px 18px;font-size:13.5px;font-weight:600;font-family:inherit;cursor:pointer;
  background:var(--surface-1);color:var(--text-2);border:var(--bd);
  border-radius:var(--r-sm);transition:all .18s var(--ease)}
.docs-tab:hover{color:var(--text);border-color:var(--accent-line)}
.docs-tab.active{background:linear-gradient(135deg,var(--accent-a),var(--accent-b));
  color:#fff;border-color:transparent;box-shadow:var(--sh-sm)}
/* 这里刻意不加入场动画：父级 #content 在每次跳转时已经挂了 fadeUp
   （app.js 的 go()），再套一层会让正文在「动画不跑」的环境里停在
   opacity:0 —— 整页看着空白，但 DOM 里内容齐全。已实测踩到，勿加回。 */
.docs-pane{}

.docs-meta{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.docs-chip{display:inline-flex;gap:6px;align-items:baseline;padding:6px 12px;font-size:12.5px;
  background:var(--surface-1);border:var(--bd);border-radius:999px;color:var(--text-2)}
.docs-chip b{color:var(--text);font-weight:700}

.docs-banner{margin:0 0 14px;padding:12px 14px;font-size:12.5px;line-height:1.85;
  background:var(--surface-1);border:1px solid var(--warning-ink);border-radius:var(--r-sm);
  color:var(--warning-ink)}
.docs-ok{margin:0 0 14px;padding:10px 14px;font-size:12.5px;line-height:1.7;
  background:var(--surface-1);border:var(--bd);border-left:3px solid var(--success-ink);
  border-radius:var(--r-sm);color:var(--success-ink)}

.docs-card{margin-bottom:16px}
.docs-src{margin-left:10px;font-size:11.5px;font-weight:400;color:var(--text-3)}
.docs-p{margin:8px 0;font-size:13.5px;line-height:1.9;color:var(--text-2)}
.docs-note{margin:10px 0 0;font-size:12px;line-height:1.8;color:var(--text-3)}

.docs-map{margin-top:6px}
.docs-group{margin:14px 0 6px;font-size:12px;font-weight:700;letter-spacing:.3px;color:var(--text-3);
  padding-bottom:5px;border-bottom:var(--bd-2)}
.docs-row{display:grid;grid-template-columns:38px minmax(120px,180px) 1fr auto;gap:10px;
  align-items:center;padding:7px 0;border-bottom:var(--bd-2);font-size:13px}
.docs-row:last-child{border-bottom:0}
.docs-row-warn{background:var(--surface-2)}
/* 功能地图里的「步骤号」列（右对齐贴住页面名）。
   ★ 类名别复用：这一步踩过坑 —— 曾把它和操作说明书的「步骤卡片」
     共用 .docs-step，卡片于是继承了这里的 text-align:right，
     整页正文都变成右对齐（DOM 正常、观感全错）。两个用途各用各的类名。 */
.docs-stepno{font-size:11.5px;font-weight:700;color:var(--text-3);text-align:right}
.docs-name{font-weight:600;color:var(--text)}
.docs-hint{font-size:12.5px;line-height:1.7;color:var(--text-3)}
.docs-go{font-size:12.5px;font-weight:600;color:var(--accent-a-ink);white-space:nowrap;
  cursor:pointer;text-decoration:none}
.docs-go:hover{text-decoration:underline}
.docs-tag-warn{margin-left:6px;font-size:11px;font-weight:700;color:var(--warning-ink);
  border:1px solid var(--warning-ink);border-radius:4px;padding:1px 6px}

.docs-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;margin-top:10px}
.docs-principle{padding:13px 14px;background:var(--surface-2);border:var(--bd);
  border-radius:var(--r-sm)}
.docs-pt{font-size:13.5px;font-weight:700;color:var(--text);margin-bottom:6px}
.docs-pb{font-size:12.5px;line-height:1.85;color:var(--text-2)}

.docs-matrix-wrap{overflow-x:auto;margin-top:10px}
.docs-matrix{width:100%;border-collapse:collapse;font-size:12.5px;min-width:520px}
.docs-matrix th,.docs-matrix td{padding:8px 10px;text-align:center;border-bottom:var(--bd-2)}
.docs-matrix th{font-size:11.5px;font-weight:700;color:var(--text-3);white-space:nowrap}
.docs-matrix th:first-child,.docs-matrix td:first-child{text-align:left}
.docs-mx-name{font-weight:600;color:var(--text);white-space:nowrap}
.docs-yes{color:var(--success-ink);font-weight:700}
.docs-no{color:var(--text-3)}

.docs-log{margin-top:12px;padding-top:10px;border-top:var(--bd-2)}
.docs-log:first-of-type{border-top:0;padding-top:0}
.docs-log-h{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;margin-bottom:6px}
.docs-log-h b{font-size:13px;color:var(--text)}
.docs-log-h span{font-size:12px;color:var(--text-3)}
.docs-ul{margin:0;padding-left:20px;font-size:12.5px;line-height:1.9;color:var(--text-2)}
.docs-ul li{margin:2px 0}

.docs-steps{display:flex;flex-direction:column;gap:14px}
.docs-stepcard{margin:0}
.docs-step-h{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:8px}
.docs-step-no{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:26px;
  padding:0 7px;font-size:12px;font-weight:700;color:#fff;border-radius:7px;
  background:linear-gradient(135deg,var(--accent-a),var(--accent-b))}
.docs-step-t{font-size:14.5px;font-weight:700;color:var(--text);flex:1 1 auto}
.docs-goal{font-size:13px;line-height:1.85;color:var(--text-2);margin-bottom:8px}
.docs-how{margin:0 0 8px;padding-left:22px;font-size:13px;line-height:1.95;color:var(--text-2)}
.docs-how li{margin:3px 0}
.docs-pitfall{padding:9px 12px;font-size:12.5px;line-height:1.8;color:var(--text-2);
  background:var(--surface-2);border-left:3px solid var(--warning-ink);border-radius:0 var(--r-sm) var(--r-sm) 0}
.docs-pitfall b{margin-right:8px;color:var(--warning-ink)}

.docs-faq{margin-top:10px;padding-top:10px;border-top:var(--bd-2)}
.docs-faq:first-of-type{border-top:0;padding-top:0}
.docs-q{font-size:13.5px;font-weight:700;color:var(--text);margin-bottom:5px}
.docs-a{font-size:12.5px;line-height:1.9;color:var(--text-2)}

/* 窄屏：功能地图由四列折成两行，避免"提示"被挤成一列字 */
@media(max-width:820px){
  .docs-row{grid-template-columns:34px 1fr;row-gap:4px}
  .docs-hint{grid-column:1 / -1}
  .docs-go{grid-column:2;justify-self:start}
}

/* ==========================================================
   侧栏数字可读性（2026-10-01）
   ----------------------------------------------------------
   背景：今天新加的「界面缩放」(--ui-zoom，默认 1.12) 只写在 .content 上
        （见上方「内容区」块），侧栏不在缩放范围内。而侧栏里两处最小的字恰好都是数字：
          · .ni-badge  9.5px  —— 右侧徽章计数（3 / 7 / 38 / 24 / 76…），全侧栏最小
          · .ni-step   11px + opacity .7 —— 左侧步号（1 / 2 / 3.5 / 16…），又小又淡
        反馈「数字太小」指的就是这两处；把「界面缩放」拉大也救不了，因为侧栏没被缩放到。
   做法：只放大这两处，不动别的字号与间距 —— 侧栏宽 248px（折叠 72px）是硬约束，
        把整块侧栏也套 zoom 会连宽度一起放大，得同步改一堆 calc，风险远大于收益。
        .ni-text 有 flex:1 + ellipsis，徽章变宽只会让长标题提前省略，不会撑破布局。
   回退：删掉本块即可（原值：badge 9.5px/1.5px 6px，step 11px/min-width 14px/opacity .7）。
   ========================================================== */
.ni-badge{font-size:11px;padding:2px 7px;font-weight:700;}
.ni-step{font-size:12.5px;min-width:16px;opacity:.85;}

/* ==========================================================
   侧栏字号接上「界面缩放」+ 整体上调一档（2026-10-04）
   ----------------------------------------------------------
   反馈原话：「字体太小」，截图给的是**左侧导航**。
   上一块（2026-10-01）只放大了 .ni-badge / .ni-step 两个**数字**，而且它的注释已经点出根因：
     「--ui-zoom 只作用在 .content 上，侧栏不在缩放范围内 ⇒ 把界面缩放拉大也救不了」。
   实测确认：把「界面缩放」调大，内容区跟着变大、**侧栏纹丝不动** ⇒ 所以侧栏还是小。

   做法：给侧栏的**字号**乘上 --ui-zoom（该变量定义在根变量块、由 theme.js 设在 <html> 上，
        所以侧栏**能**看到它）。**不动宽度 248px**，也不必去改一堆 calc ——
        上一块担心的风险来自「给侧栏套 zoom」（那会连宽度一起放大），只缩字号没有这个问题。
        长标题由 .ni-text 的 flex:1 + ellipsis 自然截断，不会撑破布局。
   同时把基准值上调一档：原来 .nav-group-title 只有 10px，是侧栏最小的字（分组标题最该看得清）。
   回退：删掉本块即可（回到 12.5 / 10 / 11 / 12.5 与固定 18px 图标）。
   ========================================================== */
.nav-group-title{font-size:calc(12.5px * var(--ui-zoom, 1));}
.nav-group-title .ngt-step{font-size:calc(13px * var(--ui-zoom, 1));}
.ni-text{font-size:calc(15px * var(--ui-zoom, 1));}
.ni-step{font-size:calc(13px * var(--ui-zoom, 1));}
.ni-badge{font-size:calc(12px * var(--ui-zoom, 1));}
.ni-icon svg{width:calc(18px * var(--ui-zoom, 1));height:calc(18px * var(--ui-zoom, 1));}
