/* ============================================================
   必租开放平台 — 统一设计系统 (Design Tokens + Components)
   前台 Portal 与 后台 Console 共用同一套设计语言
   ============================================================ */

:root {
  /* 品牌色 */
  --c-primary: #4f46e5;
  --c-primary-d: #4338ca;
  --c-primary-l: #6366f1;
  --c-blue: #2563eb;
  --c-teal: #0ea5a4;
  --c-cyan: #06b6d4;

  /* 语义色 */
  --c-success: #10b981;
  --c-success-bg: #ecfdf5;
  --c-warning: #f59e0b;
  --c-warning-bg: #fffbeb;
  /* 警示提示块（.lv2-devnote / .wxbind-devnote）的三个派生色。
     与 .icon-box.amber 用的 #b45309 同族，集中在这里以免各页各写一份。 */
  --c-warning-line: rgba(245, 158, 11, .35);
  --c-warning-ink: #b45309;
  --c-warning-ink-strong: #92400e;
  --c-danger: #f97316;
  --c-danger-bg: #fff7ed;
  --c-required: #f97316; /* 必填项边框色（橙，与 danger 统一） */
  --c-required-bg: #fef2f2;
  --c-info: #3b82f6;
  --c-info-bg: #eff6ff;

  /* 全局提示框（Toast）背景：**橙**
     2026-09-18 由红色改为橙色（旧值是一个红色硬编码 hex，现已无任何引用，出处见 git 历史）。
     原因：`showToast()` 是全站唯一的反馈通道，成功/信息类提示也走它，
     红底 + 白字看起来像报错（用户报「保存成功」的气泡红得像失败）。
     取本文件既有的橙 #f97316（与 --c-danger / --c-required 同值，注释里就叫橙）。
     🔴 注释里**别抄**被替换掉的字面值（旧 hex / 被删文案同理）：会让
     「grep 旧值 = 0」这条判据被注释命中，排查时还得肉眼分辨是代码还是注释。 */
  --c-toast-bg: #f97316;
  --c-toast-shadow: rgba(249, 115, 22, .28);

  /* 中性色 */
  --c-ink: #0f172a;
  --c-ink-2: #334155;
  --c-ink-3: #64748b;
  --c-ink-4: #94a3b8;
  --c-line: #e2e8f0;
  --c-line-2: #eef2f7;
  --c-bg: #f6f8fc;
  --c-surface: #ffffff;

  /* ===== 历史命名别名（⚠️ 不要删）=====
     下面这些变量在 console-view.js / panels.js / panels-inline.js / 本文件里被引用了 60 余处
     （其中 `--c-text` 一个就 62 处），但**从未在 :root 定义过** →
     浏览器把 `color: var(--c-text-2)` 这类声明**整条丢弃**，
     表现是"文字颜色悄悄变成继承色、背景变透明"，纯静态排查完全看不出来（不报错、不告警）。
     修法是在这里补别名，而不是去改那 60 处调用点：
     前者零回归面，后者要逐个判断语义、还可能漏改。
     新代码请直接用上面的 --c-ink* / --c-line* / --c-bg，这组只作兼容保留。 */
  --c-ink-1:   var(--c-ink);      /* → #0f172a 主文字 */
  --c-text:    var(--c-ink);      /* → #0f172a 主文字（**无兜底引用 62 处**，最要紧的一个） */
  --c-text-1:  var(--c-ink);      /* → #0f172a 主文字 */
  --c-text-2:  var(--c-ink-2);    /* → #334155 次级文字 */
  --c-text-3:  var(--c-ink-3);    /* → #64748b 三级文字 */
  --c-border:  var(--c-line);     /* → #e2e8f0 边框 */
  --c-line-1:  var(--c-line-2);   /* → #eef2f7 更浅的分隔线 / hover 底 */
  --c-bg-2:    var(--c-bg);       /* → #f6f8fc 次级背景（图标按钮 hover、禁用开关、表头） */
  --c-bg-soft: var(--c-bg);       /* → #f6f8fc 面板底 */
  --c-warn:    var(--c-warning);  /* → #f59e0b 警示（部分调用点没写兜底色） */
  --c-purple:  var(--c-primary-l);/* → #6366f1 角色标签用的紫/靛色 */

  /* 渐变 */
  --grad-brand: linear-gradient(135deg, #4f46e5 0%, #2563eb 100%);
  --grad-hero: radial-gradient(1200px 600px at 80% -10%, rgba(99,102,241,0.18), transparent 60%),
               radial-gradient(900px 500px at 0% 10%, rgba(14,165,164,0.14), transparent 55%),
               linear-gradient(180deg, #0b1437 0%, #0f1f4d 100%);

  /* ===== Toolbar 主操作按钮（add / refresh / sync 统一视觉）=====
     .toolbar-add-btn 与 .toolbar .refresh-btn.btn-primary 共用这组变量。
     改一处即同步所有 toolbar 主操作按钮：padding、字号、圆角、背景、文字色、间距、字重。
     各页面【禁止】再在 .toolbar 模板或 .refresh-btn 上散写 background / padding / font-size 覆盖。 */
  --action-primary-bg:    var(--grad-brand); /* 背景渐变 */
  --action-primary-fg:    #fff;              /* 文字色 */
  --action-primary-pad-y: 8px;               /* 垂直内边距（PC 桌面） */
  --action-primary-pad-x: 16px;              /* 水平内边距（PC 桌面） */
  /* 显式高度（PC 桌面）：不再靠 padding+内容自然撑开。
     此前 .toolbar-add-btn 自然高度 42px（22 svg + 10×2），而 <refresh-button> 模板里
     <span> 包 svg 会产生 2px inline 基线间隙 → 44px，两边差 2px。
     统一写死高度后两种 DOM 结构强制等高，改这一个变量即全系统生效。
     移动端在 @media (max-width:1024px) 里覆盖为 var(--toolbar-h-mobile)=32px。 */
  --action-primary-height: 38px;
  --action-primary-fs:    14px;              /* 字号 */
  --action-primary-fw:    600;               /* 字重 */
  --action-primary-gap:   6px;               /* 图标↔文字间距 */
  --action-primary-radius: var(--r-lg);      /* 圆角：16px（统一所有 toolbar 主操作按钮，按"新建"按钮观感） */
  --action-primary-shadow: none;             /* 阴影：toolbar 内不加，弹窗/卡片内可用 */

  /* 间距 / 圆角 / 阴影 */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 22px;
  --r-full: 999px;

  --sh-sm: 0 1px 2px rgba(15,23,42,0.06), 0 1px 3px rgba(15,23,42,0.05);
  --sh-md: 0 4px 12px rgba(15,23,42,0.08);
  --sh-lg: 0 12px 32px rgba(15,23,42,0.12);
  --sh-brand: 0 10px 30px rgba(79,70,229,0.28);

  /* 字体 */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
          "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;

  --maxw: 1200px;
  --header-h: 64px;

  /* 移动端间距标准（桌面默认，下方媒体查询按断点覆盖）
     --m-page-pad:    页面主容器左右内边距（sub-nav-tabs / console-body 对齐）
     --m-panel-inset: page-panel / dashboard-panel 内卡片相对屏幕边缘的内缩
     --m-card-gap:    卡片/面板之间竖向间距
     --m-card-pad:    卡片内部 padding
     --m-section-gap: cap-content 内 section 间距 */
  --m-page-pad: 20px;
  --m-panel-inset: 16px;
  --m-card-gap: 22px;
  --m-card-pad: 18px;
  --m-section-gap: 22px;

  /* 间距工具令牌：统一分散写在各页面里的 margin-top:Npx，集中收口到此处 */
  --m-gap-xs: 10px;
  --m-gap-sm: 12px;
  --m-gap-md: 16px;
  --m-gap-lg: 22px; /* = --m-card-gap，卡片/面板之间的竖向间距 */

  /* 控制台二级 tab / 页面标题到首个内容块之间的统一间距 */
  --console-body-gap: 22px;

  /* ===== 表单「左标签 + 右控件」的间距标准（单一事实来源）=====
     适用：标签在左、控件在右的横向表单（当前唯一使用者 = 编辑内容弹窗 .se-modal-body）。
     放到 :root 是为了以后别的横向表单直接复用，**不要再散写 62px / 8px 这类魔数**。
     两条规则必须一起用，缺一就"不统一"：
       ① 标签列宽固定 = --form-label-col：保证同排/上下各行的**控件左缘对齐**；
       ② 标签文字**右对齐**：保证「文字右缘 → 控件左缘」的间距恒等于 --form-label-gap，
          与标签是 2 个字还是 4 个字无关 —— 这才是用户要的"一个标准，上下都统一"。
     🔴 2026-09-17 踩点：原来只有①没有②。列宽 62px 是按当时 4 字标签「入口类型」定的，
        标签又是 display:block 撑满整列 + 默认左对齐 → 2 字标签左右各留一大块空白，
        实测「标签文字右缘(43px) → 控件左缘(86px)」= 43px，而真实列间距只有 8px，
        大头全是"列宽减文字宽"的空白，且随着标签字数变长而变小 → 用户报「太宽、上下不统一」。
     列宽 42px 的算法：最长标签 2 字 ×13.5px ≈ 27px，留 15px 余量。
       以后若新增 3 字标签仍够（40.5px）；**要上 4 字标签就把 --form-label-col 调到 58px**，
       间距仍由 --form-label-gap 保证恒定，不用再动别的地方。 */
  --form-label-col: 42px;
  --form-label-gap: 10px;

  /* ===== 表格行高（list-view 单一事实来源）=====
     所有列表页（<list-view> 组件 / table.data）的行高统一在此声明，
     各页面【禁止】再用 padding 覆盖，需要紧凑用 .data-sm 变体即可。 */
  --row-pad-y: 6px;          /* 默认行垂直 padding（表头/表体一致） */
  --row-pad-x: 14px;         /* 默认行水平 padding */
  --row-pad-y-sm: 5px;       /* .data-sm 紧凑变体行垂直 padding */
  --row-pad-x-sm: 12px;      /* .data-sm 紧凑变体行水平 padding */

  /* ===== 列表页序号（#）列：全站单一事实来源 =====
     所有列表页的序号列统一由这两个变量控制，各页面/各表格【禁止】再单独写
     width / text-align / padding 或硬编码 56px、48px、42px 之类的列宽。
     JS 侧 window.LISTVIEW_IDX_WIDTH 也从 --table-idx-w 读取（见 components/list-view.js），
     即改这一处，全站列表序号列同步生效。
     ⚠️ --table-idx-pad-x 必须为 0：
     序号列很窄，若沿用 --row-pad-x(14px)，左右内边距合计 28px 会吃掉几乎整个列宽，
     内容盒只剩 4px，序号文本（5.8~13.3px）被迫溢出内容盒，
     text-align:center 只能在 4px 盒内居中 → 两位数时视觉明显偏右（实测偏 4.6px）。
     序号列不需要水平内边距，靠列表自身的 14px 间距即可。 */
  --table-idx-w: 32px;       /* 序号列宽（表头/表体一致） */
  --table-idx-pad-x: 0px;    /* 序号列水平内边距（必须 0，理由见上） */

  /* ===== 列表数据行字重（cell-title / cell-strong 单一事实来源）=====
     各 panel 在 formatter 里给主名称列包裹 <span class="cell-title">，
     CSS 统一用此变量控制是否加粗。要全部加粗就改 600，全部保持常规就 400。
     各页面【禁止】再在 panel 模板 / inline style 里散写 font-weight:600 覆盖。 */
  --row-text-weight: 400;    /* 列表数据行字重（normal=400；bold=600/700） */

  /* ===== 移动端 toolbar 紧凑布局（≤1024px）单一事实来源 =====
     PC（>1024px）保持原始布局不受影响。移动端将 toolbar 折叠为：
       控件 ≤2：尽量 1 行；控件 ≥3：1~2 行（横向 flex + wrap 自动排列）。
     不要在各 panel 模板里散写 mobile style，覆盖此处即可。 */
  --toolbar-h-mobile: 32px;      /* 移动端控件统一高度（PC 约 38px；≥32px 保证 iOS 触摸目标） */
  --toolbar-gap-mobile: 6px;     /* 移动端控件间距 */
  --toolbar-search-min-w: 120px; /* 移动端搜索框最小宽（其余空间让给同行下拉/按钮） */
}

/* 间距工具类：统一替代各页面模板里散写的 margin-top:Npx，值全部来自上方令牌 */
.mt-xs { margin-top: var(--m-gap-xs) !important; }
.mt-sm { margin-top: var(--m-gap-sm) !important; }
.mt-md { margin-top: var(--m-gap-md) !important; }
.mt-lg { margin-top: var(--m-gap-lg) !important; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; overflow-x: hidden; }
body {
  font-family: var(--font);
  color: var(--c-ink);
  background: var(--c-bg);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--c-primary); text-decoration: none; }
img { max-width: 100%; }
button { font-family: inherit; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 999px; }

/* ---------- 布局容器 ---------- */
.container { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
.container.wide { max-width: 1320px; }
.section { padding: 72px 0; }
.section.tight { padding: 48px 0; }
.section-head { text-align: center; margin-bottom: 40px; }
.section-head .eyebrow {
  display: inline-block; font-size: 13px; font-weight: 700; letter-spacing: 1px;
  color: var(--c-primary); background: var(--c-info-bg);
  padding: 4px 12px; border-radius: var(--r-full); margin-bottom: 14px;
}
.section-head h2 { font-size: 32px; margin: 0 0 10px; letter-spacing: -0.5px; }
.section-head p { color: var(--c-ink-3); margin: 0 auto; max-width: 620px; font-size: 16px; }
.grid { display: grid; gap: 22px; }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }

/* ---------- 按钮 ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid transparent; border-radius: var(--r-md);
  padding: 11px 20px; font-size: 15px; font-weight: 600; cursor: pointer;
  transition: all .18s ease; white-space: nowrap; line-height: 1;
}
.btn-primary { background: var(--grad-brand); color: #fff; box-shadow: var(--sh-brand); }
.btn-primary:hover { transform: translateY(-1px); filter: brightness(1.05); }
.btn-ghost { background: #fff; color: var(--c-ink); border-color: var(--c-line); }
.btn-ghost:hover { border-color: var(--c-primary); color: var(--c-primary); }
.btn-light { background: rgba(255,255,255,.14); color: #fff; border-color: rgba(255,255,255,.3); }
.btn-light:hover { background: rgba(255,255,255,.24); }
.btn-sm { padding: 7px 13px; font-size: 13px; border-radius: var(--r-sm); }
.btn-xs { padding: 3px 8px; font-size: 11.5px; border-radius: var(--r-sm); }
.btn-danger { background: var(--c-danger); color: #fff; }
.btn-danger:hover { filter: brightness(1.05); }
.btn-orange { background: var(--c-danger); color: #fff; box-shadow: 0 6px 18px rgba(249,115,22,.28); }
.btn-orange:hover { filter: brightness(1.05); transform: translateY(-1px); }
.btn-block { width: 100%; }
/* 凭证行内小按钮：统一收归到 .btn 体系，不再在表格单元格里写内联样式 */
.btn.btn-cred { padding: 0 6px; height: 22px; font-size: 11px; line-height: 20px; border-radius: 4px; background: none; white-space: nowrap; }
.btn.btn-cred-copy { color: var(--c-primary); border: 1px solid var(--c-border); }
.btn.btn-cred-copy:hover { background: #eef2ff; border-color: var(--c-primary); }
.btn.btn-cred-rotate { color: #ea580c; border: 1px solid #fdba74; }
.btn.btn-cred-rotate:hover { background: #fff7ed; border-color: #ea580c; }
.btn:disabled { opacity: .55; cursor: not-allowed; transform: none; }

/* 图标按钮：用于工具栏刷新/操作等纯图标按钮，比 btn-ghost.btn-sm 更紧凑 */
.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0;
  border: none; border-radius: 7px;
  background: transparent; color: var(--c-text-2);
  cursor: pointer; transition: all .18s ease;
}
.btn-icon:hover { background: var(--c-bg-2); color: var(--c-primary); }
.btn-icon svg { width: 16px; height: 16px; }
.btn-icon:disabled { opacity: .55; cursor: not-allowed; }

/* 超级编辑器操作列：启停按钮用状态色软底（对齐同表 .badge.online/.offline 配色）
   与编辑/删除按钮同为 30×30 圆角小方块，靠淡色底+彩色图标表达状态，无边框更轻 */
.btn-toggle-on { background: #ecfdf5; color: #059669; }
.btn-toggle-on:hover { background: #d1fae5; color: #047857; }
.btn-toggle-off { background: #fff7ed; color: #ea580c; }
.btn-toggle-off:hover { background: #ffedd5; color: #c2410c; }
/* 置顶：已置顶用常驻琥珀色软底（与绿/橙的启停态区分），未置顶保持 ghost 只在 hover 显色 */
.btn-pin-on { background: #fffbeb; color: #d97706; }
.btn-pin-on:hover { background: #fef3c7; color: #b45309; }

/* ---------- 徽章 / 标签 ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;
  font-size: 11.5px; font-weight: 600; padding: 2px 9px; border-radius: var(--r-full); line-height: 1.6;
}
.badge.free { background: #ecfdf5; color: #047857; }
.badge.paid { background: #fffbeb; color: #b45309; }
.badge.beta { background: #eef2ff; color: #4338ca; }
.badge.online { background: #ecfdf5; color: #059669; }
.badge.offline { background: #fff7ed; color: #ea580c; }
.badge.planned { background: #f1f5f9; color: #64748b; }
.badge.info { background: #eff6ff; color: #2563eb; }
.badge.warn { background: #fffbeb; color: #d97706; }
.badge.ok { background: #ecfdf5; color: #059669; }
.badge.success { background: #e6f7ed; color: #07c160; }
.tag {
  display: inline-block; font-size: 12px; color: var(--c-ink-3);
  background: var(--c-line-2); border-radius: var(--r-sm); padding: 2px 9px; margin: 2px 4px 2px 0;
  white-space: nowrap;
}
.tag-xs { font-size: 11.5px; padding: 1px 7px; border-radius: 10px; white-space: nowrap; }
.heat-pill { display: inline-block; font-size: 12px; font-weight: 600; padding: 2px 9px; border-radius: 10px; border: 1px solid transparent; }
.heat-pill.heat-high { background: #fee2e2; color: #dc2626; border-color: #fecaca; }
.heat-pill.heat-mid { background: #fef3c7; color: #b45309; border-color: #fde68a; }
.heat-pill.heat-low { background: #f1f5f9; color: #64748b; border-color: #e2e8f0; }
.method { font-family: var(--font-mono); font-size: 12px; font-weight: 700; padding: 2px 8px; border-radius: 6px; }
.method.get { background: #e0f2fe; color: #0369a1; }
.method.post { background: #dcfce7; color: #15803d; }
.method.put { background: #fef3c7; color: #b45309; }
.method.delete { background: #fff7ed; color: #c2410c; }

/* ---------- 卡片 ---------- */
.card {
  background: var(--c-surface); border: 1px solid var(--c-line);
  border-radius: var(--r-lg); padding: 18px; box-shadow: var(--sh-sm);
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
.card.hover:hover { transform: translateY(-4px); box-shadow: var(--sh-lg); border-color: #c7d2fe; }


/* ---------- 图标（内联 SVG） ---------- */
.ic { width: 22px; height: 22px; flex: none; }
.ic-lg { width: 40px; height: 40px; }
.icon-box {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 13px; background: var(--c-info-bg); color: var(--c-primary);
}
.icon-box.teal { background: #ccfbf1; color: #0f766e; }
.icon-box.amber { background: var(--c-warning-bg); color: #b45309; }
.icon-box.green { background: var(--c-success-bg); color: #047857; }

/* ============================================================
   前台 PORTAL 专用
   ============================================================ */
.site-header {
  position: sticky; top: 0; z-index: 50; height: var(--header-h);
  background: rgba(255,255,255,.82); backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--c-line);
}
.site-header .inner { height: 100%; display: flex; align-items: center; justify-content: space-between; }
.brand { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 18px; color: var(--c-ink); }
.brand .logo {
  width: 34px; height: 34px; border-radius: 10px; background: var(--grad-brand);
  display: grid; place-items: center; color: #fff; font-weight: 900;
}
.nav { display: flex; gap: 6px; align-items: center; }
.nav a {
  color: var(--c-ink-2); font-weight: 600; font-size: 15px; padding: 8px 14px; border-radius: var(--r-sm);
}
.nav a:hover, .nav a.active { color: var(--c-primary); background: var(--c-info-bg); }
.nav .btn { margin-left: 8px; }
.menu-toggle { display: none; }

.hero {
  position: relative; color: #fff; padding: 96px 0 110px;
  background: var(--grad-hero); overflow: hidden;
}
.hero::after {
  content: ""; position: absolute; inset: 0;
  background-image: linear-gradient(rgba(255,255,255,.04) 1px, transparent 1px),
                    linear-gradient(90deg, rgba(255,255,255,.04) 1px, transparent 1px);
  background-size: 48px 48px; mask-image: radial-gradient(circle at 70% 30%, #000, transparent 70%);
}
.hero .inner { position: relative; z-index: 1; max-width: 820px; }
.hero h1 { font-size: 52px; line-height: 1.1; margin: 18px 0 18px; letter-spacing: -1px; }
.hero p.sub { font-size: 19px; color: rgba(255,255,255,.82); margin: 0 0 30px; }
.hero .cta { display: flex; gap: 14px; flex-wrap: wrap; }
.hero-stats { display: flex; gap: 44px; margin-top: 52px; flex-wrap: wrap; }
.hero-stats .stat .n { font-size: 30px; font-weight: 800; }
.hero-stats .stat .l { color: rgba(255,255,255,.7); font-size: 14px; }
.hero-badge {
  display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.2);
  padding: 6px 14px; border-radius: var(--r-full);
}

/* 分类筛选 */
.filter-bar { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-bottom: 36px; }
.chip {
  border: 1px solid var(--c-line); background: #fff; color: var(--c-ink-2);
  padding: 9px 18px; border-radius: var(--r-full); font-weight: 600; font-size: 14px; cursor: pointer;
  transition: all .15s ease;
}
.chip:hover { border-color: var(--c-primary); color: var(--c-primary); }
.chip.active { background: var(--grad-brand); color: #fff; border-color: transparent; box-shadow: var(--sh-brand); }
/* 前台置顶标记（图钉图标 + 文字）：服务/文档/资源/案例卡片 + 分类 chip + 公告通用。
   琥珀配色与后台列表 .badge.warn 一致，靠「图标 + 文案」双通道表达，不只靠颜色。 */
.pin-mark {
  display: inline-flex; align-items: center; gap: 3px; vertical-align: middle;
  font-size: 11px; font-weight: 600; line-height: 1.6;
  padding: 1px 7px; border-radius: var(--r-full);
  background: #fffbeb; color: #d97706;
}
.pin-mark svg { width: 11px; height: 11px; flex: none; }
/* 分类 chip 选中态是品牌渐变底，置顶标记换成半透明白底白字，避免琥珀色糊在深底上看不清 */
.chip.active .pin-mark { background: rgba(255, 255, 255, .22); color: #fff; }

/* 服务卡片 */
.svc-card { display: flex; flex-direction: column; gap: 12px; height: 100%; }
.svc-card .top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.svc-card h3 { margin: 0; font-size: 18px; }
.svc-card .desc { color: var(--c-ink-3); font-size: 14px; flex: 1; }
.svc-card .foot { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; }
.svc-cat { font-size: 12px; color: var(--c-ink-4); display: flex; align-items: center; gap: 5px; }

/* API 文档卡片 */
.doc-card { font-family: var(--font-mono); }
.doc-card .endpoint { display: flex; align-items: center; gap: 10px; }
.doc-card .path { color: var(--c-ink); font-size: 14px; word-break: break-all; }
.doc-card .desc { font-family: var(--font); color: var(--c-ink-3); font-size: 14px; margin-top: 8px; }
.doc-card .params { font-family: var(--font); margin-top: 12px; border-top: 1px dashed var(--c-line); padding-top: 12px; }
.doc-card .param { display: flex; gap: 8px; font-size: 13px; padding: 3px 0; }
.doc-card .param .pn { font-family: var(--font-mono); color: var(--c-primary); font-weight: 600; }
.doc-card .param .pt { color: var(--c-ink-4); }
.doc-card pre {
  font-family: var(--font-mono); font-size: 12.5px; background: #0f172a; color: #e2e8f0;
  padding: 12px; border-radius: var(--r-sm); overflow-x: auto; margin-top: 12px;
}

/* 资源 / 案例 / 公告 */
.res-card { display: flex; gap: 14px; align-items: flex-start; }
.res-card h4 { margin: 0 0 4px; font-size: 16px; }
.res-card p { margin: 0; color: var(--c-ink-3); font-size: 13.5px; }
.case-card .quote { font-size: 16px; color: var(--c-ink-2); font-style: italic; margin: 14px 0; position: relative; padding-left: 18px; }
.case-card .quote::before { content: "“"; position: absolute; left: -2px; top: -10px; font-size: 40px; color: #c7d2fe; }
.case-logo { width: 48px; height: 48px; border-radius: 12px; background: var(--grad-brand); color: #fff; display: grid; place-items: center; font-weight: 800; }
.notice {
  display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border-radius: var(--r-md);
  border: 1px solid var(--c-line); background: #fff; margin-bottom: 12px;
}
.notice .dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 8px; flex: none; }
.notice.info .dot { background: var(--c-info); }
.notice.warning .dot { background: var(--c-warning); }
.notice.success .dot { background: var(--c-success); }
.notice .t { font-weight: 700; font-size: 14.5px; }
.notice .c { color: var(--c-ink-3); font-size: 13.5px; }
.notice .date { color: var(--c-ink-4); font-size: 12px; }

.footer {
  background: #0b1437; color: rgba(255,255,255,.7); padding: 56px 0 30px; margin-top: 40px;
}
.footer a { color: rgba(255,255,255,.8); }
.footer .cols { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 30px; }
.footer h5 { color: #fff; font-size: 15px; margin: 0 0 14px; }
.footer ul { list-style: none; padding: 0; margin: 0; }
.footer li { margin-bottom: 9px; font-size: 14px; }
.footer .bottom { border-top: 1px solid rgba(255,255,255,.12); margin-top: 36px; padding-top: 20px; font-size: 13px; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; }

/* ============================================================
   后台 CONSOLE 专用
   ============================================================ */
.console { display: flex; height: 100vh; width: 100%; background: var(--c-bg); overflow: hidden; }
.sidebar {
  width: 64px; flex: none; background: #0f172a; color: #cbd5e1;
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
  overflow-y: auto; overflow-x: hidden; transition: width .22s ease;
}
.sidebar.open { width: 240px; }
.sidebar:not(.open) { overflow: visible; }
.sidebar .brand {
  color: #fff; padding: 20px 10px 16px; border-bottom: 1px solid rgba(255,255,255,.08);
  font-size: 15px; font-weight: 700; letter-spacing: 0.3px; display: flex; align-items: center; gap: 10px;
  justify-content: center; transition: padding .22s ease, justify-content .22s ease;
}
.sidebar.open .brand { padding: 20px 20px 16px; justify-content: flex-start; }
.sidebar .brand .logo { width: 30px; height: 30px; font-size: 15px; flex: none; }
.sidebar .brand .brand-text {
  white-space: nowrap; opacity: 0; width: 0; overflow: hidden;
  transition: opacity .18s ease, width .18s ease;
  display: none;
}
.sidebar.open .brand .brand-text { opacity: 1; width: auto; display: inline; }

/* ── 扁平导航（admin 风格） ── */
.side-nav { padding: 8px 10px; flex: 1; display: flex; flex-direction: column; gap: 2px; overflow-y: auto; min-height: 0; }
.side-nav a {
  display: flex; align-items: center; justify-content: center;
  gap: 10px; padding: 10px;
  border-radius: var(--r-sm); color: #94a3b8; font-size: 13.5px;
  font-weight: 500; transition: all .15s ease; position: relative;
  text-decoration: none; line-height: 1;
}
.sidebar.open .side-nav a { justify-content: flex-start; padding: 10px 14px; }
.side-nav a .nav-icon {
  width: 18px; height: 18px; flex-shrink: 0; opacity: .7;
  display: inline-flex; align-items: center; justify-content: center;
}
.side-nav a .nav-icon svg { width: 18px; height: 18px; }
.side-nav a .nav-label {
  white-space: nowrap; opacity: 0; width: 0; overflow: hidden;
  transition: opacity .18s ease, width .18s ease;
}
.sidebar:not(.open) .side-nav a .nav-label { display: none; }
.sidebar.open .side-nav a .nav-label { opacity: 1; width: auto; display: inline; }
.side-nav a:hover { background: rgba(255,255,255,.06); color: #e2e8f0; }
.side-nav a:hover .nav-icon { opacity: 1; }
.side-nav a.active {
  background: rgba(79,70,229,.14); color: #a5b4fc; font-weight: 600;
}
.side-nav a.active .nav-icon { opacity: 1; color: #818cf8; }
.side-nav a.active::before {
  content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 3px; height: 18px; background: #6366f1; border-radius: 0 3px 3px 0;
}

/* 折叠侧边栏：鼠标悬停图标时显示自定义菜单名称提示 */
.sidebar:not(.open) .side-nav a::after {
  content: attr(data-title);
  position: absolute; left: calc(100% + 8px); top: 50%; transform: translateY(-50%);
  background: #0f172a; color: #fff; padding: 5px 10px; border-radius: var(--r-sm);
  font-size: 12px; font-weight: 500; white-space: nowrap; line-height: 1.2;
  box-shadow: var(--sh-md); opacity: 0; pointer-events: none;
  transition: opacity .12s ease; z-index: 70;
}
.sidebar:not(.open) .side-nav a:hover::after { opacity: 1; }

/* ── 菜单加载失败 / 加载中（侧边栏深色底，位于 .side-nav 内）── */
.nav-error, .nav-loading {
  padding: 14px 12px; text-align: center;
  font-size: 12.5px; line-height: 1.6; color: #94a3b8;
}
.sidebar:not(.open) .nav-error, .sidebar:not(.open) .nav-loading { padding: 12px 4px; }
.sidebar:not(.open) .nav-error-msg, .sidebar:not(.open) .nav-loading { font-size: 11px; }
.nav-error-msg { margin: 0 0 10px; color: #fca5a5; font-weight: 500; }
.nav-error-btn {
  display: inline-block; cursor: pointer;
  padding: 6px 14px; border-radius: var(--r-sm);
  border: 1px solid rgba(165,180,252,.35);
  background: rgba(79,70,229,.18); color: #a5b4fc;
  font-size: 12.5px; font-weight: 500; font-family: inherit;
  transition: background .15s ease, color .15s ease;
}
.nav-error-btn:hover:not(:disabled) { background: rgba(79,70,229,.3); color: #e0e7ff; }
.nav-error-btn:disabled { opacity: .6; cursor: default; }

.sidebar .foot { padding: 10px 14px; border-top: 1px solid rgba(255,255,255,.08); font-size: 11px; color: var(--c-ink-3); line-height: 1.6; display: flex; align-items: center; gap: 4px; }
.sidebar .foot .ic { width: 14px; height: 14px; vertical-align: -2px; flex-shrink: 0; }
.sidebar .foot a { color: var(--c-ink-3); text-decoration: none; display: inline-flex; align-items: center; gap: 3px; transition: color .15s; }
.sidebar .foot a:hover { color: var(--c-primary); }
.sidebar .foot .sep { color: rgba(255,255,255,.2); margin: 0 2px; }

.sidebar-foot { padding: 8px 10px; border-top: 1px solid rgba(255,255,255,.08); display: flex; align-items: center; justify-content: center; }
.sidebar.open .sidebar-foot { padding: 8px 14px; justify-content: flex-start; }
/* CityU 风格：底部圆形箭头切换按钮（展开态 <，收起态 >） */
.sidebar-foot .side-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0;
  border-radius: 50%;
  background: rgba(255,255,255,.06);
  color: #cbd5e1;
  border: 1px solid rgba(255,255,255,.12);
  transition: background .15s ease, color .15s ease, transform .2s ease;
}
.sidebar-foot .side-toggle:hover { background: rgba(255,255,255,.14); color: #fff; }
.sidebar-foot .side-toggle svg { width: 18px; height: 18px; }

/* ── 二级导航 tabs（内容区顶部，对齐 Element Plus el-tabs） ── */
.sub-nav-tabs {
  display: flex; gap: 18px;
  background: #fff; padding: 0 var(--m-page-pad);
  border-bottom: 1px solid var(--c-line);
  height: 48px; min-height: 48px; box-sizing: border-box;
  flex-shrink: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.sub-nav-tabs::-webkit-scrollbar { display: none; }
.sub-nav-tabs a {
  display: inline-flex; align-items: center; flex-shrink: 0;
  height: 100%; box-sizing: border-box;
  padding: 0 14px;
  margin: 0 8px;
  font-size: 13.5px; font-weight: 500; color: #334155;
  text-decoration: none; position: relative; transition: color .15s;
  line-height: 1; white-space: nowrap; border-bottom: 2px solid transparent;
}
.sub-nav-tabs a:hover { color: var(--c-primary); }
.sub-nav-tabs a.active {
  color: var(--c-primary); font-weight: 600; border-bottom-color: var(--c-primary);
}

.console-main { flex: 1; display: flex; flex-direction: column; min-width: 0; width: 100%; overflow-y: auto; }
.topbar {
  height: 62px; min-height: 62px; background: #fff; border-bottom: 1px solid var(--c-line);
  display: flex; align-items: center; justify-content: space-between; padding: 0 20px; position: sticky; top: 0; z-index: 20;
  flex-shrink: 0; box-sizing: border-box;
}
.topbar > div:first-child { min-width: 0; flex: 0 0 auto; overflow: hidden; display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.topbar h1 { font-size: 18px; margin: 0; white-space: nowrap; }
.topbar .crumb { color: var(--c-ink-4); font-size: 13px; margin-bottom: 2px; }
.topbar .right { display: flex; align-items: center; gap: 12px; }
.topbar .side-toggle {
  width: 40px !important;
  height: 40px !important;
  min-width: 40px !important;
  min-height: 40px !important;
  padding: 0 !important;
  flex-shrink: 0 !important;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  border: 1px solid var(--c-line);
  background: #fff;
  color: var(--c-ink);
  cursor: pointer;
  box-sizing: border-box;
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.topbar .side-toggle:hover { border-color: var(--c-primary); color: var(--c-primary); background: #f8fafc; }
.topbar .side-toggle svg { width: 20px; height: 20px; flex-shrink: 0; }
.avatar { width: 34px; height: 34px; border-radius: 50%; background: var(--grad-brand); color: #fff; display: grid; place-items: center; font-weight: 700; font-size: 14px; }
/* 微信登录用户的真实头像（open_users.avatar / 微信 headimgurl） */
.avatar-img { display: block; object-fit: cover; background: #eef2ff; }
.console-body { padding: var(--console-body-gap) var(--m-page-pad) var(--m-page-pad); min-width: 0; overflow-x: hidden; width: 100%; box-sizing: border-box; }
/* ── 修复：移除 flex:1 和 min-height:100% 导致的移动端大块空白
   原 .console-body { flex: 1 } 在 100vh - topbar - sub-nav-tabs 后仍占满剩余高度，
   当面板内容（如 BaseDataPanel 8 子标签）较短时，console-body 内会留下巨大空白。
   改为 min-height:0 + 自然高度，内容多时由 console-main 的 overflow-y:auto 滚动。 */
.page-panel { margin: calc(-1 * var(--console-body-gap)) calc(-1 * var(--m-page-pad) + var(--m-panel-inset)); padding: var(--console-body-gap) var(--m-panel-inset) 0; }
/* 模块页（<list-view> 直接挂在 .console-body 下、无 .page-panel 包裹）与自定义页（.page-panel）
   统一左右内缩，使所有列表页四周间距一致（修复 y 任务）。
   根因：w 任务给 .page-panel 加了 --m-panel-inset 内缩后，模块页因缺 .page-panel 只吃 console-body
   的 20px padding，比自定义页少 12px —— 找人脉等 module 标签页（人脉中心/调用日志/知识文章）
   的表格明显比自定义页更贴边。这里复用与 .page-panel 完全相同的 margin/padding 公式（单一事实来源），
   改 --m-panel-inset / --m-page-pad 即可同步两者，无需逐页改模板。上下边距保持 0，由 console-body
   的 padding-top(=--console-body-gap) 统一收口，与 .page-panel 的净上边距一致。 */
.list-view {
  margin-left: calc(-1 * var(--m-page-pad) + var(--m-panel-inset));
  margin-right: calc(-1 * var(--m-page-pad) + var(--m-panel-inset));
  padding-left: var(--m-panel-inset);
  padding-right: var(--m-panel-inset);
}
/* 标准：二级 tab 与下方任意首个内容块之间统一保留 section 间距（不依赖 :first-child 类名） */
.page-panel > .card + .card,
.cap-content > .cap-banner + .card,
.page-panel > .card + .cap-banner,
.page-panel > .cap-banner + .card { margin-top: var(--m-card-gap); }
/* 面包屑自身有 padding-top，作为首个元素时抵消，避免与 page-panel 顶部间距叠加 */
.page-panel > .cap-breadcrumb:first-child { padding-top: 0; margin-top: 0; }
.page-title { font-size: 18px; font-weight: 600; color: var(--c-ink); margin: 0 0 16px; }

/* 数据表格 */
.toolbar { display: flex; align-items: center; justify-content: flex-start; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
.toolbar .left { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.toolbar .right { display: flex; gap: 10px; align-items: center; flex-wrap: nowrap; margin-left: auto; }

/* ============================================================
   工具栏统一布局规范（单一事实来源）
   位置顺序固定为：① 搜索框(最左) → ② 下拉/筛选(左) → ③ 新建按钮(右) → ④ 更新/刷新按钮(右)。
   所有页面只要使用标准类名，无需各自写布局样式，自动套用以下顺序：
     - 搜索框：.toolbar-search     - 下拉/筛选：.select / .filter-select
     - 新建按钮：.toolbar-add-btn  - 更新/刷新：.refresh-btn（统一用 <refresh-button> 组件）
   .left 视为左组（靠最左）、.right 视为右组（靠最右）；组内顺序用 order 固定。
   列表页有独立 .toolbar-filters 中间组，顺序为「搜索→筛选→操作」，规则见 .list-view .toolbar。
   移动端 @media 内继承此处顺序，仅改尺寸/换行。
   ============================================================ */
/* 左组内顺序：搜索框第 1、下拉第 2 */
.toolbar .left .toolbar-search { order: 1; }
.toolbar .left .select,
.toolbar .left .filter-select { order: 2; }
/* 右组内顺序：新建第 1、更新/刷新第 2（两者相邻靠右） */
.toolbar .right .toolbar-add-btn { order: 1; }
.toolbar .right .refresh-btn { order: 2; }
/* 扁平 toolbar（页面未用 .left/.right 包裹，action 直接挂在 .toolbar 下）：直接按标准类名排序
   搜索1 → 下拉2 → 新建3(推到最右) → 刷新4 */
.toolbar > .toolbar-search { order: 1; }
.toolbar > .select,
.toolbar > .filter-select { order: 2; }
.toolbar > .toolbar-add-btn { order: 3; }
.toolbar > .refresh-btn { order: 4; margin-left: auto; }
/* 扁平 toolbar 含 .left 左组时，新建按钮推到最右与动作组对齐（避免移动端折行反转） */
.toolbar:has(.left) > .toolbar-add-btn { margin-left: auto; }
.toolbar .left .select { width: auto; min-width: 140px; }
.toolbar-title { font-size: 15px; font-weight: 600; color: var(--c-ink); margin-right: 4px; white-space: nowrap; }
/* 工具栏内联输入（搜索/筛选）：复用 .input 尺寸，去掉 100% 宽度以适配 flex 布局。
   用 .input.input-inline 提高特异性，确保覆盖 .input 的 width:100% */
.input.input-inline,
.select.input-inline,
.select-sm.input-inline {
  width: auto; min-width: 140px; max-width: 260px; flex: 1;
}
.select.input-inline { padding-right: 32px; background-position: right 10px center; background-size: 14px; }

/* 列表页工具栏规范组件（截图：灰底圆角搜索框 + 整行蓝色新增按钮）
   适用于 .toolbar 内的搜索框和「新增/创建」主按钮；弹窗表单内保持 .input/.btn-primary */
.toolbar-search {
  width: 100%; max-width: 320px; flex: 1;
  border: 1px solid transparent; border-radius: var(--r-md);
  padding: 8px 12px; font-size: 14px; font-family: inherit;
  background: #f1f3f6; color: var(--c-ink); outline: none;
  box-sizing: border-box; transition: background .18s ease;
}
.toolbar-search::placeholder { color: #94a3b8; }
.toolbar-search:focus { background: #e8ebf0; }
/* Toolbar 主操作按钮（视觉单一事实来源：--action-primary-* 变量）
   .toolbar-add-btn 为独立类，可置于任意位置（含 .toolbar 之外），统一蓝渐变主按钮观感；
   .toolbar .refresh-btn.btn-primary 为 <refresh-button variant="primary"> 渲染结果。
   二者共享此规则，改 --action-primary-* 变量即可同步所有按钮，不要在调用方散写 background / padding / font-size 覆盖。 */
.toolbar-add-btn,
.toolbar .refresh-btn.btn-primary {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--action-primary-gap);
  border: none; border-radius: var(--action-primary-radius);
  /* 高度统一由 --action-primary-height 锁定（PC=38px），box-sizing:border-box 让 padding 计入高度。
     背景：.toolbar-add-btn 内部是 svg+文本节点（自然高度 38px），
     <refresh-button> 内部是 <span>包 svg + <span>文本（span 是 inline 容器，会产生 2px 基线间隙 → 40px）。
     两种 DOM 结构不可能靠自然撑开对齐，故统一写死高度，移动端由 1024 媒体查询压到 32px。 */
  height: var(--action-primary-height); box-sizing: border-box;
  padding: var(--action-primary-pad-y) var(--action-primary-pad-x);
  font-size: var(--action-primary-fs); font-weight: var(--action-primary-fw);
  cursor: pointer; white-space: nowrap; line-height: 1;
  background: var(--action-primary-bg); color: var(--action-primary-fg);
  box-shadow: var(--action-primary-shadow);
  transition: filter .18s ease, transform .18s ease, opacity .18s ease;
}
/* toolbar 内所有 .btn（含 ghost / sm 变体）统一几何尺寸，只保留各自颜色差异。
   背景：list-view 的 actions 里非 primary 变体的按钮（如知识文章页「新增微信公众号文章」）
   走 .btn.btn-sm 默认样式 → 40px / 13px 字号 / 8px 圆角，与同排 --action-primary-* 的
   42px / 14px / 16px 主按钮差 2px，同排高低不齐。
   这里只统一高度、圆角、字号（几何），不碰 background，主次仍靠颜色区分。
   特异性 .toolbar .btn(0,2,0) 高于 .btn-sm(0,1,0)，可覆盖其 8px 圆角与 13px 字号。 */
.toolbar .btn {
  height: var(--action-primary-height); box-sizing: border-box;
  border-radius: var(--action-primary-radius);
  font-size: var(--action-primary-fs);
}
.toolbar-add-btn svg,
.toolbar .refresh-btn.btn-primary svg,
.toolbar .refresh-btn.btn-primary > span { display: block; line-height: 14px; }

/* .toolbar-add-btn 内图标的几何 + 配色单一事实来源（2026-09-15 修复全站「新增」按钮不一致）。
   同一个类被两种 DOM 结构使用，此前图标大小与颜色都不同：
     ① list-view 生成：<span class="btn-icon"><svg/></span>。.btn-icon 是为「行内通用图标按钮」
        写的类（30×30、前景色 var(--c-text-2) 深灰），塞进蓝底主按钮后 → svg 16px 但**发黑**，
        在蓝色渐变上几乎看不见；图标占位 30px 也让文字起点比手写面板右移 8px。
     ② 手写面板：v-html="svg('plus')" 直接插裸 <svg>，没有任何尺寸约束 → 按 inline svg
        默认渲染成 **22px**，继承按钮前景色（白）。
   收敛为「16px + 继承按钮前景色」：btn-icon 在此上下文还原成透明无色的纯容器，
   于是 ① ② 两种写法得到完全相同的几何（16px 图标 + 6px gap + 文字）。
   注意：只作用于 .toolbar-add-btn 之内，不影响别处作为独立按钮使用的 .btn-icon。
   .toolbar .btn > svg 是同一问题的「次按钮」版本：手写 <button class="btn btn-ghost btn-sm"
   v-html="svg('x')">（如消息中心「全部已读」）也是裸 22px svg，而 list-view 生成的同类
   次按钮走 .btn-icon → 16px。注意用「直接子」选择器，避免命中 .refresh-btn 里
   .rb-icon > span > svg 的刷新图标（PC 有文字时它已被 display:none 隐藏）。 */
.toolbar-add-btn > svg,
.toolbar .btn > svg,
.toolbar-add-btn .btn-icon > svg { width: 16px; height: 16px; flex: 0 0 auto; }
.toolbar-add-btn .btn-icon {
  width: auto; height: auto; padding: 0;
  background: transparent; color: inherit; border-radius: 0;
}
.toolbar-add-btn:hover,
.toolbar .refresh-btn.btn-primary:hover { transform: translateY(-1px); filter: brightness(1.05); }
.toolbar-add-btn:disabled,
.toolbar .refresh-btn.btn-primary:disabled { opacity: .55; cursor: not-allowed; transform: none; }

/* toolbar 主按钮右侧数字徽标（页面组件「保存」后的未保存计数）：
   与 label 分离，避免中文括号「保存（1）」破坏居中、视觉杂乱。
   用白底+主色字保证在渐变蓝按钮上足够对比，不再靠半透明白底（会糊成一片）。 */
.action-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 4px; margin-left: 4px;
  border-radius: 8px;
  font-size: 10px; font-weight: 700; line-height: 1;
  background: rgba(255,255,255,.92); color: var(--c-primary);
  box-shadow: 0 1px 2px rgba(0,0,0,.12);
}

/* 页面组件矩阵：存在未保存改动时，保存按钮脉冲提示 */
@keyframes lv-save-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(67,97,238,.5); }
  70%  { box-shadow: 0 0 0 10px rgba(67,97,238,0); }
  100% { box-shadow: 0 0 0 0 rgba(67,97,238,0); }
}
.lv-save-dirty { animation: lv-save-pulse 1.6s ease-out infinite; }
.lv-save-dirty:hover { animation-play-state: paused; }

.table-wrap { background: #fff; border: 1px solid var(--c-line); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--sh-sm); width: 100%; box-sizing: border-box; }
/* 列表表格单元格默认不换行（全局固定行为：table.data 与 table.data-table 通用）。
   开放平台所有列表页均继承此规则，无需在模板里逐个加 white-space:nowrap。
   若某列需换行/省略，在单元格 style 里用 overflow:hidden;text-overflow:ellipsis 配合即可。
   行高 padding 由上方 --row-pad-y / --row-pad-x 单一变量控制，不要在此处硬编码。 */
table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th,
table.data-table th {
  text-align: left; padding: var(--row-pad-y) var(--row-pad-x); background: #f0f4ff; color: #4338ca;
  font-weight: 600; font-size: 12.5px; border-bottom: 2px solid var(--c-line); white-space: nowrap;
  letter-spacing: 0.02em;
}
table.data th:first-child { width: var(--table-idx-w); text-align: center; }
table.data td:first-child { width: var(--table-idx-w); text-align: center; color: var(--c-ink-4); font-size: 12.5px; }

/* ===== 序号（#）列 —— 全站唯一事实来源 =====
   标记方式：序号单元格加 class="idx-cell"。
     · <list-view> 的 type:'index' 列由组件自动加（见 components/list-view.js）；
     · 手写表格（auth_sessions / 人脉标签 / 数据表等）手动加 class，不再写内联 width。
   宽度 / 水平内边距 / 居中全部由 --table-idx-* 两个变量决定，
   各页面与各表格【禁止】再单独写 width / text-align / padding（含 56px、48px、42px 硬编码）。
   选择器特异性刻意高于下方 freeze-cols 的 td:nth-child(1) 规则，以便统一覆盖而不靠 !important。 */
.table-wrap[class*="freeze-cols-"]:not(.freeze-cols-0) table.data th.idx-cell,
.table-wrap[class*="freeze-cols-"]:not(.freeze-cols-0) table.data td.idx-cell,
.table-wrap[class*="freeze-cols-"] table.data th.idx-cell,
.table-wrap[class*="freeze-cols-"] table.data td.idx-cell,
table.data th.idx-cell,
table.data td.idx-cell,
table.data-table th.idx-cell,
table.data-table td.idx-cell {
  width: var(--table-idx-w);
  min-width: var(--table-idx-w);
  max-width: var(--table-idx-w);
  padding-left: var(--table-idx-pad-x);
  padding-right: var(--table-idx-pad-x);
  text-align: center;
}
table.data td.idx-cell,
table.data-table td.idx-cell { color: var(--c-ink-4); font-size: 12.5px; }
table.data th.sortable { cursor: pointer; user-select: none; }
table.data th.sortable:hover { background: #e8edff; }
table.data td,
table.data-table td { padding: var(--row-pad-y) var(--row-pad-x); border-bottom: 1px solid var(--c-line-2); vertical-align: middle; white-space: nowrap; color: var(--c-ink); }
table.data tr:last-child td { border-bottom: none; }
table.data tbody tr:hover { background: #fafbff; }
/* list-view 行点击编辑：指针手势 + 明显悬停 */
table.data tbody tr.lv-row-clickable { cursor: pointer; }
table.data tbody tr.lv-row-clickable:hover { background: #f0f4ff; }

/* 表头固定：滚动时表头悬浮 */
table.data thead { position: sticky; top: 0; z-index: 5; }

/* 列表页表格：固定前 N 列，其余横向滑动。
   freeze-cols-N 支持 N=0/1/2/3/4，由 list-view 的 frozenCols 参数动态决定。 */
.table-wrap[class*="freeze-cols-"] table.data {
  border-collapse: separate;
  border-spacing: 0;
  min-width: 680px;
}
/* PC 端冻结列表：与移动端保持一致，容器横向可滚动而非 overflow:hidden 裁切。
   作用：宽度不足时表格横向滑动（冻结列 sticky 生效），避免右侧边界被裁断/缺失。
   单一事实来源：所有 freeze-cols-* 表格通用，无需逐页修改。 */
.table-wrap[class*="freeze-cols-"] { overflow-x: auto; overflow-y: hidden; }
.table-wrap[class*="freeze-cols-"] table.data th,
.table-wrap[class*="freeze-cols-"] table.data td {
  white-space: nowrap;
  word-break: normal;
  background-color: #fff;
}
.table-wrap[class*="freeze-cols-"] table.data thead th { background-color: #f0f4ff; }
.table-wrap[class*="freeze-cols-"] table.data tbody tr:hover td { background-color: #fafbff; }
/* 冻结序号列（.idx-cell，所有 freeze-cols-N > 0）。
   右边界用 background-image 渐变在单元格自身背景内画一条 1px 竖线：
   border-collapse:separate 下相邻单元格背景会覆盖普通 border，而渐变画在自身背景上不会被覆盖，
   横向滚动时随冻结列固定，给 PC 静态视图和移动端都提供清晰列分隔。
   ⚠️ 这里用 .idx-cell 而不是 td:nth-child(1)：
   冻结的语义是「冻结序号列」，不是「冻结第一列」。参数表 / 详情表的第一列（参数名称、
   API 名称、错误码…）也是 freeze-cols-N 容器，历史上被 nth-child(1) 误当成序号列，
   导致这些列被强制成 56px + 居中 + sticky（本该左对齐）。 */
.table-wrap[class*="freeze-cols-"]:not(.freeze-cols-0) table.data td.idx-cell {
  position: sticky;
  left: 0;
  z-index: 10;
  width: var(--table-idx-w);
  min-width: var(--table-idx-w);
  max-width: var(--table-idx-w);
  padding-left: var(--table-idx-pad-x);
  padding-right: var(--table-idx-pad-x);
  text-align: center;
  background-color: #fff;
  background-image: linear-gradient(to right, transparent calc(100% - 1px), var(--c-line) calc(100% - 1px), var(--c-line) 100%);
}
.table-wrap[class*="freeze-cols-"]:not(.freeze-cols-0) table.data th.idx-cell {
  position: sticky;
  left: 0;
  z-index: 11;
  width: var(--table-idx-w);
  min-width: var(--table-idx-w);
  max-width: var(--table-idx-w);
  padding-left: var(--table-idx-pad-x);
  padding-right: var(--table-idx-pad-x);
  text-align: center;
  background-color: #f0f4ff;
  background-image: linear-gradient(to right, transparent calc(100% - 1px), var(--c-line) calc(100% - 1px), var(--c-line) 100%);
}
/* 冻结首列在 hover 行时也要跟随整行背景，避免冻结列脱离悬停效果。 */
.table-wrap[class*="freeze-cols-"]:not(.freeze-cols-0) table.data tbody tr:hover td.idx-cell { background-color: #fafbff; }
/* freeze-cols-0：不冻结任何列，整表统一横向滚动。
   用 !important 兜底，防止 .lv-frozen / nth-child 等其他规则残留 sticky 定位。 */
.table-wrap.freeze-cols-0 table.data th,
.table-wrap.freeze-cols-0 table.data td {
  position: static !important;
  left: auto !important;
  box-shadow: none !important;
}
/* 冻结列单元格里的 .cell-title 不要限制 max-width，让内容随表格横向滚动展开 */
.table-wrap[class*="freeze-cols-"] table.data td .cell-title,
.table-wrap[class*="freeze-cols-"] table.data td .cell-title a {
  max-width: none !important;
  overflow: visible;
  text-overflow: clip;
}

/* 人脉标签管理表：列少且内容短，不需要 680px 最小宽度与横向滚动。
   用 table-layout:fixed + 明确列宽，让「标签名称」占满剩余空间，避免右侧白条。 */
.ptm-table-wrap { overflow-x: hidden; }
.ptm-table-wrap table.data,
.ptm-table-wrap.ptm-readonly table.data,
.ptm-table-wrap.ptm-admin table.data {
  min-width: 0 !important;
}
.ptm-table-wrap table.data th,
.ptm-table-wrap table.data td {
  background: #fff;
}
.ptm-table-wrap table.data thead th { background: #f0f4ff; }
.ptm-table-wrap table.data tbody tr:hover td { background: #fafbff; }

/* 非管理员模式（3 列：# / 类型 / 名称）：名称列自动撑满 */
.ptm-readonly table.data {
  table-layout: fixed !important;
  width: 100% !important;
}
.ptm-readonly table.data th:nth-child(1),
.ptm-readonly table.data td:nth-child(1) { width: var(--table-idx-w) !important; min-width: var(--table-idx-w) !important; max-width: var(--table-idx-w) !important; padding-left: var(--table-idx-pad-x) !important; padding-right: var(--table-idx-pad-x) !important; text-align: center !important; }
.ptm-readonly table.data th:nth-child(2),
.ptm-readonly table.data td:nth-child(2) { width: 100px !important; min-width: 100px !important; }
.ptm-readonly table.data th:nth-child(3),
.ptm-readonly table.data td:nth-child(3) { width: auto !important; min-width: 0 !important; }

/* 管理员及以上模式（5 列：# / 类型 / 名称 / 排序 / 操作）：名称列撑满，其余定宽 */
.ptm-admin table.data {
  table-layout: fixed !important;
  width: 100% !important;
}
.ptm-admin table.data th:nth-child(1),
.ptm-admin table.data td:nth-child(1) { width: var(--table-idx-w) !important; min-width: var(--table-idx-w) !important; max-width: var(--table-idx-w) !important; padding-left: var(--table-idx-pad-x) !important; padding-right: var(--table-idx-pad-x) !important; text-align: center !important; }
.ptm-admin table.data th:nth-child(2),
.ptm-admin table.data td:nth-child(2) { width: 100px !important; min-width: 100px !important; }
.ptm-admin table.data th:nth-child(3),
.ptm-admin table.data td:nth-child(3) { width: auto !important; min-width: 0 !important; }
.ptm-admin table.data th:nth-child(4),
.ptm-admin table.data td:nth-child(4) { width: 80px !important; min-width: 80px !important; max-width: 80px !important; text-align: center !important; }
.ptm-admin table.data th:nth-child(5),
.ptm-admin table.data td:nth-child(5) { width: 90px !important; min-width: 90px !important; max-width: 90px !important; text-align: center !important; }

/* 管理员但本菜单既无编辑也无删除权限（4 列：# / 类型 / 名称 / 排序） */
.ptm-admin-nodel table.data {
  table-layout: fixed !important;
  width: 100% !important;
}
.ptm-admin-nodel table.data th:nth-child(1),
.ptm-admin-nodel table.data td:nth-child(1) { width: var(--table-idx-w) !important; min-width: var(--table-idx-w) !important; max-width: var(--table-idx-w) !important; padding-left: var(--table-idx-pad-x) !important; padding-right: var(--table-idx-pad-x) !important; text-align: center !important; }
.ptm-admin-nodel table.data th:nth-child(2),
.ptm-admin-nodel table.data td:nth-child(2) { width: 100px !important; min-width: 100px !important; }
.ptm-admin-nodel table.data th:nth-child(3),
.ptm-admin-nodel table.data td:nth-child(3) { width: auto !important; min-width: 0 !important; }
.ptm-admin-nodel table.data th:nth-child(4),
.ptm-admin-nodel table.data td:nth-child(4) { width: 80px !important; min-width: 80px !important; max-width: 80px !important; text-align: center !important; }

/* 列表页表格：移动端固定前 N 列，其余横向滑动 */
@media (max-width: 768px) {
  .table-wrap[class*="freeze-cols-"] {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* 同上：Safari 下 table 的 width:max-content 不可靠，用 width:auto + min-width 方案 */
  .table-wrap[class*="freeze-cols-"] table.data {
    width: auto;
    min-width: 680px;
    min-width: max-content;
    border-collapse: separate;
    border-spacing: 0;
  }
  /* freeze-cols-0：不冻结任何列 */
  .table-wrap.freeze-cols-0 table.data th,
  .table-wrap.freeze-cols-0 table.data td {
    position: static !important;
    left: auto !important;
    box-shadow: none !important;
  }
  .table-wrap[class*="freeze-cols-"] table.data th,
  .table-wrap[class*="freeze-cols-"] table.data td {
    white-space: nowrap;
    word-break: normal;
    background-color: #fff;
  }
  .table-wrap[class*="freeze-cols-"] table.data thead th { background-color: #f0f4ff; }
  .table-wrap[class*="freeze-cols-"] table.data tbody tr:hover td { background-color: #fafbff; }
  /* 冻结序号列（与 PC 保持一致；语义同 PC：.idx-cell 而非 nth-child(1)，
     避免把参数表 / 详情表的第一列（参数名称等）误当序号列。 */
  .table-wrap[class*="freeze-cols-"]:not(.freeze-cols-0) table.data td.idx-cell {
    position: sticky;
    left: 0;
    z-index: 10;
    width: var(--table-idx-w);
    min-width: var(--table-idx-w);
    max-width: var(--table-idx-w);
    padding-left: var(--table-idx-pad-x);
    padding-right: var(--table-idx-pad-x);
    text-align: center;
    background-color: #fff;
    background-image: linear-gradient(to right, transparent calc(100% - 1px), var(--c-line) calc(100% - 1px), var(--c-line) 100%);
  }
  .table-wrap[class*="freeze-cols-"]:not(.freeze-cols-0) table.data th.idx-cell {
    position: sticky;
    left: 0;
    z-index: 11;
    width: var(--table-idx-w);
    min-width: var(--table-idx-w);
    max-width: var(--table-idx-w);
    padding-left: var(--table-idx-pad-x);
    padding-right: var(--table-idx-pad-x);
    text-align: center;
    background-color: #f0f4ff;
    background-image: linear-gradient(to right, transparent calc(100% - 1px), var(--c-line) calc(100% - 1px), var(--c-line) 100%);
  }
  .table-wrap[class*="freeze-cols-"]:not(.freeze-cols-0) table.data tbody tr:hover td.idx-cell { background-color: #fafbff; }
  /* 冻结列单元格里的 .cell-title 不要限制 max-width，让内容随表格横向滚动展开 */
  .table-wrap[class*="freeze-cols-"] table.data td .cell-title,
  .table-wrap[class*="freeze-cols-"] table.data td .cell-title a {
    max-width: none !important;
    overflow: visible;
    text-overflow: clip;
  }
}

.expand-row td { padding: 0 !important; background: #f8fafc; vertical-align: top; }
.db-tables-panel { padding: 16px 20px; }
.db-tables-head { display:flex; align-items:center; gap:10px; margin-bottom:12px; font-size:14px; }
.db-tables-head b { font-size:14px; color:var(--c-ink-2); }
table.data.data-sm th, table.data.data-sm td { padding: var(--row-pad-y-sm) var(--row-pad-x-sm); font-size:13px; }
table.data.data-sm code { font-size:12px; }
.cell-title { font-weight: var(--row-text-weight); color: var(--c-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 560px; display: inline-block; vertical-align: middle; }
.cell-sub { color: var(--c-ink-4); font-size: 12.5px; }
.row-actions { display: inline-flex; gap: 6px; align-items: center; justify-content: flex-start; }
.row-actions.center { justify-content: center; }

/* 内容管理「入口」列的入口类型 chip。
   一个租户若有多个入口（中数信联同时有官方网站 + 门户网站），每个类型渲染成独立 chip ——
   原先用「·」分隔的纯文本（`官方网站 · 门户网站` / `官方网站/门户网站`）会被读成一个名字，
   用户明确反馈过"这到底是官方网站还是门户网站"。chip 让「两个入口」一眼可辨。
   颜色/圆角全部取 :root 变量，禁止在 formatter 里散写。 */
.entry-chip {
  display: inline-block; margin-left: 4px; padding: 1px 7px;
  font-size: 11px; line-height: 1.7; white-space: nowrap; vertical-align: 1px;
  color: var(--c-primary-d); background: var(--c-info-bg);
  border: 1px solid var(--c-line); border-radius: var(--r-full);
}

/* ── 编辑弹窗「标签」编辑器 ─────────────────────────────────────────────
   结构与观感对齐 .input（同高、同圆角、同边框色），这样一行里和
   作者/归属租户那些原生 input 放在一起不会显得是两个体系。 */
.tag-edit {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  min-height: 38px; padding: 8px 10px;
  background: var(--c-surface); border: 1px solid var(--c-line); border-radius: var(--r-md);
  margin-bottom: 12px;
}
.tag-edit:focus-within { border-color: var(--c-primary-l); }
.tag-edit-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 4px 1px 8px; font-size: 12px; line-height: 1.7; white-space: nowrap;
  color: var(--c-ink-2); background: var(--c-bg);
  border: 1px solid var(--c-line); border-radius: var(--r-full);
}
.tag-edit-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; padding: 0; border: 0; cursor: pointer;
  font-size: 13px; line-height: 1; color: var(--c-ink-4);
  background: transparent; border-radius: var(--r-full);
}
.tag-edit-x:hover { color: var(--c-danger); background: var(--c-danger-bg); }
.tag-edit-input {
  flex: 1; min-width: 90px; padding: 2px 0; border: 0; outline: 0;
  font-size: 13px; font-family: inherit; color: var(--c-ink); background: transparent;
}
.tag-edit .btn { height: 28px; padding: 0 10px; font-size: 12px; }
/* 弹窗「入口类型」曾是一组可点选胶囊（.entry-pick / .entry-pick-chip / .entry-pick-hint），
   已改为与「类型 / 状态 / 租户」同款的原生 <select class="select">，故这三个类整体删除。
   删除理由（别再把它们加回来）：胶囊的**未选中态**是「灰字 + 浅底 + 描边」，
   与同一排的只读字段（.se-readonly）观感几乎一样，被用户当成"改不了的只读标签"；
   而"两个都不勾选 = 所有入口均可见"这条语义只能靠旁边的提示文字解释，
   单入口租户下还会变成"不勾选 = 所有入口均可见"这种自相矛盾的说法
   （该租户的"所有入口"就只有一个）。下拉把"不限"变成显式选项，问题一起消失。
   列表「入口」列用的 .entry-chip 是另一套（只读展示），保留在上面。 */
/* 只读态的伪输入框（观感同 .input，但灰底、不可编辑 —— 非 super_admin 看归属租户时用） */
.se-readonly {
  display: flex; align-items: center; cursor: not-allowed;
  background: var(--c-bg); color: var(--c-ink-3);
}
/* 「这条内容不会出现在门户上」这类提示用警示色（平台公共 / 未开通入口） */
.se-readonly.se-entry-warn { color: var(--c-warning); }

/* ===== 统一列表组件 <list-view> ===== */
.list-view { display: flex; flex-direction: column; }
/* 列表主体允许横向滚动，冻结列在其上 sticky 悬浮（桌面 + 移动端一致） */
.list-view .table-wrap {
  overflow-x: auto !important;
  -webkit-overflow-scrolling: touch;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
}
/* 桌面端：列表表格占满容器宽度，消除 PC 宽屏下右侧大片空白。
   移动端（≤1024px，见下方 @media）才恢复 width:auto + min-width:max-content，
   让列保持自然宽度并可横向滚动，避免列被压扁/重叠（Safari 对 max-content 支持不稳的兜底在此分支保留）。 */
.list-view table.data {
  width: 100%;
  min-width: 0;
}
@media (max-width: 1024px) {
  /* 移动端/平板：恢复内容宽度 + 横向滚动，列不被压缩 */
  .list-view table.data {
    width: auto;
    min-width: 680px;
    min-width: max-content;
  }
}
.list-view .cell-title { max-width: 100%; }
.list-view table.data th.lv-frozen,
.list-view table.data td.lv-frozen {
  position: sticky; z-index: 10; background: #fff;
  box-shadow: 2px 0 4px -2px rgba(0,0,0,.06);
}
.list-view table.data thead th.lv-frozen { background: #f0f4ff; z-index: 11; }
.list-view table.data tbody tr:hover td.lv-frozen { background: #fafbff; }
.list-view .lv-frozen.lv-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-view .lv-empty,
.list-view .lv-loading { padding: 40px 16px; text-align: center; color: var(--c-ink-4); font-size: 14px; }
/* list-view 内置 toolbar：PC 端所有控件尽量一行，移动端自动换行。
   .toolbar-filters 在 PC 端 flex:0 0 auto 与 .left/.right 同行；
   移动端在 @media(max-width:1024px) 里改为 flex:1 1 100% 独占一行并撑满。 */
/* PC 端 toolbar：控件尽量一行。.toolbar-filters 为 flex:0 0 auto，与 .left/.right 同行不独占换行；
   仅在整体宽度不足时才整体折行。移动端 @media(max-width:1024px) 内 toolbar 自动 wrap，
   .toolbar-filters 作为整块落到独立行、其内部两下拉各 flex:1 1 auto 充满整屏（见下方 1024/768 断点）。 */
.list-view .toolbar { flex-wrap: wrap; gap: 8px; align-items: center; }
.list-view .toolbar .left { flex: 1 1 auto; min-width: 0; flex-wrap: wrap; gap: 8px; align-items: center; }
.list-view .toolbar .right { flex: 0 0 auto; flex-wrap: wrap; gap: 8px; align-items: center; }
.list-view .toolbar .toolbar-filters { flex: 0 0 auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* list-view 多 filter 场景：控件簇左对齐，新增按钮永远在最右（DOM 顺序 .left -> .toolbar-filters -> .right，
   再用 margin-left:auto 把 .right 推到最右）。.toolbar-add-btn 在 .right 内部 order:999 确保多个 action 时新增仍最右。 */
.list-view .toolbar.toolbar-compact { justify-content: flex-start; }
.list-view .toolbar.toolbar-compact .right { margin-left: auto; }
.list-view .toolbar.toolbar-compact .right .toolbar-add-btn { order: 999; }
.list-view .toolbar .right .btn,
.list-view .toolbar .right .toolbar-add-btn { margin-left: 0; }
.btn-danger { background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; }
.btn-danger:hover { background: #fecaca; color: #b91c1c; }
@media (max-width: 768px) {
  /* 移动端 toolbar 布局统一由 @media (max-width:1024px) 的紧凑规则控制，
     此处不再强制 right 占满整行（会导致按钮单独一行）。 */
  .list-view .toolbar .right { width: auto; }
  .list-view .toolbar .left .toolbar-search { max-width: none; }
}
.row-actions.end { justify-content: flex-end; }
/* 展开明细子行（expandable）：内容由父级 #row-detail slot 注入 */
.list-view table.data .lv-detail-row > .lv-detail-cell { background: var(--c-bg); padding: 0; border-bottom: 1px solid var(--c-border); }
.list-view table.data .lv-detail-row .lv-detail-cell { font-size: 13px; }
/* 操作按钮：图标风格，语义化配色 */
.row-actions .btn-ghost.btn-sm { width: 30px; height: 30px; padding: 0; display:inline-flex;align-items:center;justify-content:center;border-radius:7px; }
/* 文字按钮场景（调用方加 .wrap-text 标记）：解除上面的 30px 图标按钮锁宽，
   宽度回到内容自适应，否则「恢复公开/确认下线/驳回」等文字按钮被锁成 30px 宽，
   nowrap 文字溢出按钮互相叠压（移动端举报审核页截图反馈"挤成一团"）。
   图标按钮（无 .wrap-text）保持 30px 方形不变。 */
.row-actions.wrap-text .btn-ghost.btn-sm {
  width: auto;
  min-width: 30px;
}
.row-actions .btn-ghost.btn-sm svg { width:15px;height:15px; }
.row-actions .act-edit:hover { background: #eef2ff; color: #4338ca; }
.row-actions .act-refresh:hover { background: #fff7ed; color: #d97706; }
.row-actions .act-del:hover { background: #fff7ed; color: #ea580c; }
.row-actions .act-toggle:hover { background: #f0fdf4; color: #16a34a; }
.row-actions .act-pin:hover { background: #fffbeb; color: #d97706; }
/* 分页 */
.pagination {
  background: #fcfdff;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 16px; border-top: 1px solid var(--c-line-2);
  border-radius: 0 0 var(--r-md) var(--r-md);
  font-size: 13px; flex-wrap: wrap; width: 100%; box-sizing: border-box;
}
.pagination .page-total { color: var(--c-text-2); white-space: nowrap; }
.pagination .pager { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; }
.pagination { white-space: nowrap; }
.pagination .page-numbers { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; }
.pagination .page-numbers .btn-primary,
.pagination .page-numbers .btn-ghost { min-width: 34px; height: 34px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-sm); }
.pagination .page-indicator {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 52px; height: 34px; padding: 0 10px;
  font-size: 13px; color: var(--c-ink); background: #fff;
  border: 1px solid var(--c-line); border-radius: var(--r-sm);
}
.pagination .page-jump { display: inline-flex; align-items: center; gap: 6px; color: var(--c-text-3); font-size: 12px; }
.pagination .page-prev,
.pagination .page-next {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--c-line); border-radius: var(--r-sm);
  background: #fff; color: var(--c-ink); font-size: 16px; line-height: 1;
  cursor: pointer; transition: all .12s;
}
.pagination .page-prev:hover:not(:disabled),
.pagination .page-next:hover:not(:disabled) { border-color: var(--c-primary); color: var(--c-primary); background: #eef2ff; }
.pagination .page-prev:disabled,
.pagination .page-next:disabled { opacity: .45; cursor: not-allowed; background: #f8fafc; }
.pagination .page-mobile-only { display: none; }
.pagination select {
  height: 34px; font-size: 13px; padding: 0 26px 0 10px;
  border-radius: var(--r-sm); min-width: auto; max-width: 90px;
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 8px center; background-size: 12px;
}
.pagination input[type="number"] { width: 48px; height: 34px; text-align: center; border: 1px solid var(--c-line); border-radius: var(--r-sm); font-size: 13px; outline: none; }
.pagination input[type="number"]:focus { border-color: var(--c-primary); box-shadow: 0 0 0 3px rgba(79,70,229,.1); }
@media (max-width: 768px) {
  .pagination { flex-wrap: nowrap; padding: 10px 12px; gap: 8px; border-radius: 0 0 var(--r-md) var(--r-md); }
  .pagination .page-desktop-only { display: none !important; }
  .pagination .page-mobile-only { display: inline-flex; }
  .pagination .pager { flex: 1; justify-content: flex-end; gap: 6px; }
  .pagination .page-prev,
  .pagination .page-next { width: 40px; height: 40px; font-size: 18px; border-radius: var(--r-sm); }
  .pagination .page-indicator { height: 40px; min-width: 56px; border-radius: var(--r-sm); font-size: 14px; }
  .pagination select { height: 40px; padding: 0 28px 0 10px; font-size: 14px; min-width: 64px; background-position: right 8px center; }
  .pagination .page-total { font-size: 12px; }
}
/* 空数据 */
.empty { padding: 48px 24px; text-align: center; color: var(--c-text-3); }
.empty div { margin-top: 12px; font-size: 14px; }

/* 表单 / 弹窗 */
.modal-mask {
  position: fixed; inset: 0; background: rgba(15,23,42,.5);
  display: flex; align-items: flex-start; justify-content: center; padding: 40px 16px; z-index: 1200; overflow-y: auto;
}
.modal {
  background: #fff; border-radius: var(--r-xl); width: 100%; max-width: 560px; box-shadow: var(--sh-lg);
  animation: pop .18s ease; overflow: hidden;
}
@keyframes pop { from { transform: translateY(12px) scale(.98); opacity: 0; } to { transform: none; opacity: 1; } }
.modal-head { padding: 20px 24px; border-bottom: 1px solid var(--c-line); display: flex; align-items: center; justify-content: space-between; }
.modal-head h3 { margin: 0; font-size: 18px; }
.modal-body { padding: 22px 24px; max-height: 64vh; overflow-y: auto; }
.modal-foot { padding: 16px 18px; border-top: 1px solid var(--c-line); display: flex; justify-content: flex-end; gap: 10px; }
.modal-foot.between { justify-content: space-between; align-items: center; }
/* 弹窗底部操作按钮组：取消在左、保存在右，统一间距 */
.modal-actions { display: flex; gap: 10px; align-items: center; }
/* 全局弹窗/抽屉关闭按钮：统一尺寸、圆形灰底、hover 放大，确保明显可点 */
.close-x {
  width: 36px; height: 36px;
  border: none; border-radius: 50%;
  background: #e2e8f0;
  color: #334155;
  font-size: 24px;
  font-weight: 700;
  line-height: 0;
  padding: 0 0 2px 0;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s ease, color .15s ease, transform .15s ease, box-shadow .15s ease;
  flex: 0 0 auto;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.6), 0 1px 2px rgba(15,23,42,.06);
}
.close-x:hover { background: #cbd5e1; color: #0f172a; transform: scale(1.08); box-shadow: inset 0 1px 0 rgba(255,255,255,.5), 0 2px 5px rgba(15,23,42,.12); }
.close-x:active { transform: scale(.92); }

/* 弹窗尺寸工具类（桌面端统一宽度，单一事实来源）。
   移动端由 @media (max-width:1024px)/(max-width:768px) 的 .modal{...!important} 覆盖为满屏。
   未加尺寸类的 .modal 默认走上方基础定义 max-width:560px（即 md）。 */
.modal.modal-sm { max-width: 420px; }
.modal.modal-md { max-width: 560px; }
.modal.modal-lg { max-width: 920px; }
.modal.modal-xl { max-width: 1000px; }

.field { margin-bottom: 16px; min-width: 0; width: 100%; box-sizing: border-box; }
.field label { display: block; font-weight: 600; font-size: 13.5px; margin-bottom: 8px; color: var(--c-ink-2); }
.field .hint { font-size: 12px; color: var(--c-ink-4); margin-top: 10px; line-height: 1.6; }
/* 表单字段容器在移动端弹窗内不允许被子元素撑开（配合 input[type=date] 等 intrinsic 宽度大的控件） */
.modal .field { overflow: hidden; }
/* 🔴 上面那行 overflow:hidden 有个副作用，必须用 flex-shrink:0 抵掉：
   CSS 规范里 flex item 的 `min-height:auto` 只在 `overflow: visible` 时才解析成"内容高度"，
   非 visible 一律解析成 0。而弹窗正文（.modal-body）是**列向 flex + max-height**，
   内容超高时就会按 flex-shrink 分配收缩量：普通行有内容高度兜底、缩不动，
   于是全部亏空都压到唯一"没有下限"的 .field 上 —— 它被压成 0 高、连标题一起消失（2026-09-15 用户报「标题怎么看不到」）。
   表单行本来就不应该被压缩，正确行为是让 .modal-body 自己滚（它已经有 overflow-y:auto）。 */
.modal .field { flex-shrink: 0; }

/* ── 内容管理超级编辑器：正文容器 + 紧凑表单 ────────────────────────────────
   正文高度必须给「标题栏 + 底部操作条 + 遮罩内边距」留位，否则整屏最底下的操作条会被
   挤出视口（用户得先滚动整页才看得到「保存」）。
   账：.modal-mask 内边距 40 + .modal-head ≈73 + .modal-foot ≈72 ≈ 185，取 200 留余量。
   移动端由媒体查询的 calc(100dvh - 150px) !important 接管（带 !important，天然压过这里）。
   ⚠️ 这些值只在 CSS 里定义一次，不要在模板上散写 style（铁律 19）。 */
.se-modal-body {
  max-height: calc(100vh - 200px);
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 9px;                        /* 行距**只**由容器 gap 提供，见下面 .field margin 清零 */
}
/* 紧凑化（2026-09-15 用户报「上面这三行可以紧凑一点」）：
   弹窗顶部「标题 / 类型·状态·作者 / 租户·入口类型」都是单行控件，按通用表单的排法
   （行距 16+14、控件内边距 10px）三行实测吃掉 283px，接近正文可视高度的 40%。
   🔴 关键：.field 自带 margin-bottom:16px，而容器又有 gap —— **两者是叠加的**，
   老代码里标题行下方真实间距是 16+14=30px，比看上去大一倍。
   所以这里把 margin 清零、只留 gap，控件内边距收到 7px。
   ⚠️ 只做**间距与控件内边距**，**不动字号**：用户要的是"紧凑"，不是"字变小"。
   ⚠️ 用 .se-modal-body 作用域修饰，**不要**去改 .field / .input 的全局值（会波及全站表单）。 */
.se-modal-body .field { margin-bottom: 0; }
.se-modal-body .field label { margin-bottom: 5px; }
.se-modal-body .input, .se-modal-body .select, .se-modal-body .textarea { padding: 7px 12px; }
.se-modal-body .select { padding-right: 34px; }   /* 自定义箭头在 right 12px，右内边距须同步收 */
.se-modal-body .field .hint { margin-top: 6px; }

/* 一行排布：label 在左、控件在右（2026-09-15 用户二次要求「字和框是不是可以在一行」）。
   用 grid 而**不是** flex —— 因为要处理"控件下方的说明"(.hint)：
   grid 里可以显式 `grid-column:2` 把 hint 钉到控件正下方，label 那列留空；
   flex 做不到这点（hint 会变成第三个横向兄弟，跑到 label 右边去），只能改模板结构。
   `minmax(0,1fr)` 的 0 不能省：grid 列的默认最小尺寸是 auto，会被内容（如宽 select）撑破。

   🔴 2026-09-17 间距收紧（用户报「文字和输入框中间太宽，两张图都要紧凑、上下统一」）：
   列宽与列间距**不再写魔数**，统一取 :root 的 --form-label-col / --form-label-gap
   （取值理由与踩点写在 :root 那段注释里）。配套把标签改成**右对齐**：
   文字右缘贴住列右缘后，「文字 → 框」的间距恒等于 --form-label-gap，与标签字数无关。
   实测（390px，弹窗宽 358）：文字到框 43px → 10px；控件左缘 86 → 68；输入区宽 288 → 306。 */
.se-modal-body .field {
  display: grid;
  grid-template-columns: var(--form-label-col) minmax(0, 1fr);
  align-items: center;
  column-gap: var(--form-label-gap);
}
/* text-align:right 是这条「统一标准」的一半，别删（删了就退回"列宽减文字宽"的浮动间距）。
   white-space:nowrap 保留：标签必须单行，折行会让行高不齐。 */
.se-modal-body .field > label { margin-bottom: 0; white-space: nowrap; text-align: right; }
.se-modal-body .field > .hint { grid-column: 2; margin-top: 4px; }
/* 控件统一钉高：只读态是「.input 的 div」(flex 垂直居中) 与原生 select 的固有高度差约 5px，
   并排时会看出控件不等高。钉死高度让 input / select / 只读 div 严格等高，也不受标签行高影响。 */
.se-modal-body .input, .se-modal-body .select { height: 34px; }

/* 🔴 滚动容器绝不压缩子项（2026-09-15 用户报「超级文本框输入文字多时，下面的元素不跟着往下走」）。
   .se-modal-body 是**列向 flex + max-height** 的滚动容器：内容一超高，flex 就把亏空按 flex-shrink
   摊到子项上 —— 而"内容超高"本该触发**滚动**，不是把某一项压扁。
   为什么偏偏压到富文本编辑器上：.se-editor-mount 写了**显式** min-height:300px，
   而 CSS 规定只有 min-height:auto 才解析成"内容高度"，显式值一写，这个兜底就没了 ——
   于是它成了整行里唯一"可压缩"的项。
   实测：正文 910px 时挂载点被压到 **353px**（自身内容高约 601px），而里面的 .bz-editor 仍按内容高
   绘制 → **正文溢出盒子、直接盖在下面的「标签 / 备注」上**（用户看到的"元素不跟着往下走"）。
   ⚠️ 这跟早先 `.modal .field{overflow:hidden}` 把 .field 压成 0 高是**同一类坑**，
   只是那次修在 .field 上、这次的角色是 .se-editor-mount，没被覆盖到。
   统一按「滚动容器只滚动、不压缩」处理，比逐个打补丁更稳。 */
.se-modal-body > * { flex-shrink: 0; }

/* 🔴 富文本正文跟着内容长高，由 .modal-body 统一滚一层（原设计是编辑器内部再滚一层）。
   编辑器自带 `.bz-editor-content{min-height:320px;max-height:560px;overflow:auto}`，
   560px 一封顶，正文只能在编辑器内部滚 —— 下面的元素自然就再也不往下走了。
   用 `.modal .bz-editor-content`（2 个类）压过编辑器自带的 1 个类，
   这样**不依赖**「我们的样式在编辑器注入的 <style> 之后」（那个顺序是运行期决定的，不可靠）。
   ⚠️ 副作用：编辑器内部不再滚动，它自带的工具栏 `position:sticky` 失去作用（粘在 .bz-editor
   这个 overflow:hidden 的盒子里，而该盒子自己不会滚）。权衡后接受：正文可见性 > 工具栏常驻，
   真要看工具栏就往上滚一点。 */
.modal .bz-editor-content { max-height: none; }

/* 弹窗内的「字段并排行」：三列/两列自适应折行。
   抽成类而不再在模板散写 style="display:flex;gap:12px;flex-wrap:wrap;"（铁律 19），
   也便于断点统一调整。 */
.se-form-row { display: flex; gap: 12px; flex-wrap: wrap; }
.se-form-row > .field { flex: 1; }

/* 🔴 窄屏修正，**必须写在这两条之后**（同优先级时靠源码顺序决胜；写在前面会被
   `.se-form-row > .field { flex: 1 }` 覆盖，静默失效 —— 2026-09-15 踩过一次）。
   背景：label 与控件同排后，label 的 62px 固定列会吃掉本来就不宽的字段列 ——
   实测 390px 视口下「类型/状态」各只分到 168px，控件仅剩 **98px**，
   8 个字的「生效中（原状态）」会被截断。故窄屏一行只放一个字段，控件恢复可读宽度。
   断点与全局手机端保持一致（768px），不引入新断点。 */
@media (max-width: 768px) {
  .se-form-row > .field { flex: 1 0 100%; }
}

/* 权限 / 多选勾选框（按分类分组，一级可全选） */
.checks { display: flex; flex-direction: column; gap: 0; padding: 2px 0; }
.check-group { padding: 6px 0; }
.check-group + .check-group { border-top: 1px solid var(--c-line); margin-top: 2px; }
.check-group-row { display: flex; align-items: center; gap: 8px; padding: 3px 2px 6px; user-select: none; }
.check-group-check { display: inline-flex; align-items: center; cursor: pointer; flex: 0 0 auto; }
.check-group-check input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--c-primary); cursor: pointer; }
.check-group-head { display: flex; align-items: center; gap: 8px; flex: 1; cursor: pointer; padding: 2px 0; }
.check-group-head:hover { opacity: .8; }
/* groupOnly 字段（角色管理 → 菜单权限）：没有二级可展开 → 头部静态，不给手型光标 */
.check-group-head.is-static { cursor: default; }
.check-group-head.is-static:hover { opacity: 1; }
.check-group-title { font-size: 13px; font-weight: 700; color: var(--c-ink-2); }
.check-group-count { font-size: 11px; color: var(--c-ink-4); background: var(--c-bg); padding: 1px 7px; border-radius: 10px; }
.check-group-arrow { font-size: 11px; color: var(--c-ink-4); margin-left: auto; }
.check-group-body { padding: 2px 0 4px 4px; }
.check-item { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border: 1px solid var(--c-line); border-radius: 20px; cursor: pointer; background: #fff; transition: border-color .15s, background .15s; font-size: 13px; line-height: 1.3; user-select: none; margin: 2px 4px 2px 0; }
.check-item:hover { border-color: var(--c-primary); background: #f8f7ff; }
.check-item input[type=checkbox] { width: 14px; height: 14px; accent-color: var(--c-primary); flex: 0 0 auto; }
.check-name { font-weight: 500; font-size: 13px; color: var(--c-ink-2); }

/* 角色管理弹窗里「每个菜单后面跟 6 个操作位勾选框」的样式
   （.check-item-with-actions / .check-actions / .check-act）已于 2026-09-17 整块删除：
   操作位改由「页面组件」页按角色单独配置，角色弹窗不再重复提供入口。 */
@media (max-width: 768px) {
  .check-group-body { padding-left: 0; margin: 0 -12px; }
}
.check-key, .check-desc { display: none; }
/* 密钥权限模式：全部权限 / 受限 分段切换 */
.checks-perm { padding: 2px 0; }
.seg { display: inline-flex; gap: 4px; padding: 3px; background: #f1f3f9; border-radius: 9px; margin-bottom: 10px; }
.seg-btn {
  border: none; background: transparent; color: var(--c-ink-3); font-size: 13px; font-weight: 600;
  padding: 6px 14px; border-radius: 7px; cursor: pointer; transition: all .15s; font-family: inherit;
}
.seg-btn:hover:not(:disabled) { color: var(--c-primary); }
.seg-btn.active { background: #fff; color: var(--c-primary); box-shadow: 0 1px 3px rgba(15,23,42,.12); }
.seg-btn:disabled { opacity: .45; cursor: not-allowed; }
.perm-all-note {
  display: flex; align-items: center; gap: 8px; padding: 12px 14px; margin: 4px 0;
  background: #ecfdf5; border: 1px solid #a7f3d0; border-radius: 9px; color: #047857; font-size: 13px; line-height: 1.5;
}
.perm-all-icon { font-size: 15px; }
.input, .textarea, .select {
  width: 100%; border: 1px solid var(--c-line); border-radius: var(--r-md); padding: 10px 13px;
  font-size: 14px; font-family: inherit; outline: none; background: #fff; color: var(--c-ink);
  box-sizing: border-box;
}
.input:focus, .textarea:focus, .select:focus { border-color: var(--c-primary); box-shadow: 0 0 0 3px rgba(79,70,229,.12); }

/* 搜索框：与 .input 同源风格，避免裸奔 */
.search {
  border: 1px solid var(--c-line); border-radius: var(--r-md); padding: 9px 13px; font-size: 14px; min-width: 140px; max-width: 260px; flex: 1; outline: none;
}
.search:focus { border-color: var(--c-primary); box-shadow: 0 0 0 3px rgba(79,70,229,.12); }

/* 必填表单控件：橙边框，与 danger 统一，聚焦时加深 */
.input.required, .select.required, .textarea.required { border-color: var(--c-required); background: #fff; }
.input.required:focus, .select.required:focus, .textarea.required:focus { border-color: #ea580c; box-shadow: 0 0 0 3px rgba(249,115,22,.15); }
/* iOS Safari 中 date/datetime-local 默认有最小宽度且文本居中，会撑开或短于容器，强制拉齐 */
input[type="date"].input,
input[type="datetime-local"].input {
  display: block;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  text-align: left;
  overflow: hidden;
}
/* iOS Safari 日期输入框内部 edit-field 默认有最小宽度，会撑破容器，强制其跟随 input 宽度 */
input[type="date"].input::-webkit-datetime-edit,
input[type="datetime-local"].input::-webkit-datetime-edit {
  min-width: 0;
  max-width: 100%;
}
/* 日期选择框：text 主输入 + 隐藏原生 date picker，彻底规避 iOS Safari date input 溢出 */
.date-wrap {
  position: relative;
  display: flex;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
}
.date-wrap .date-text {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  padding-right: 42px; /* 给右侧日历触发区留空，避免文本被覆盖 */
}
.date-wrap .date-native {
  position: absolute;
  right: 0;
  top: 0;
  width: 44px;
  height: 100%;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  opacity: 0;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
}
/* 隐藏原生日期选择器内部 edit-field，确保只占触发区 */
.date-wrap .date-native::-webkit-datetime-edit,
.date-wrap .date-native::-webkit-datetime-edit-fields-wrapper {
  display: none;
}
.date-wrap .date-native::-webkit-calendar-picker-indicator {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: pointer;
}
/* 移动端弹窗内日期组合框进一步收缩 */
@media (max-width: 768px) {
  .modal .date-wrap .date-text {
    padding-left: 10px;
    padding-right: 38px;
    font-size: 15px;
  }
  .modal .date-wrap .date-native {
    width: 40px;
  }
}
.textarea { resize: vertical; min-height: 84px; }
/* JSON / 代码编辑框：等宽字体 + 灰底，复用 .textarea 基础尺寸（23 处使用，此前无独立样式） */
.code-area { font-family: var(--font-mono); font-size: 13px; line-height: 1.5; background: var(--c-bg); color: var(--c-ink-2); }
/* 下拉框：移除浏览器默认箭头，使用统一自定义箭头（跨浏览器/OS 视觉一致） */
.select { appearance: none; -webkit-appearance: none; -moz-appearance: none; padding-right: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 16px; }
/* 尺寸变体：紧凑型（替代散落 font-size:12px / 小内边距） */
.input-sm { padding: 6px 10px; font-size: 12px; }
.select-sm { padding: 6px 32px 6px 10px; font-size: 12px; }
/* 自定义筛选下拉（替代原生 select，避免展开列表被浏览器撑得过高）。
   触发按钮复用 .select 外观（含自定义箭头）；浮层绝对定位 + max-height 滚动。 */
.filter-select { position: relative; display: inline-block; }
.filter-select-trigger { text-align: left; cursor: pointer; display: inline-flex; align-items: center; width: auto; min-width: 140px; }
.filter-select-trigger.open { border-color: var(--c-primary); box-shadow: 0 0 0 3px rgba(79,70,229,.12); }
.filter-select-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filter-pop {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  min-width: 100%; max-width: 320px; max-height: min(50vh, 320px); overflow-y: auto;
  background: #fff; border: 1px solid var(--c-line); border-radius: var(--r-md);
  box-shadow: var(--sh-md); padding: 6px; box-sizing: border-box;
}
.filter-pop-opt { padding: 8px 10px; font-size: 13px; border-radius: 6px; cursor: pointer; white-space: nowrap; color: var(--c-ink-2);
  display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.filter-pop-opt:hover { background: rgba(67,97,238,.08); color: var(--c-ink); }
.filter-pop-opt.active { background: rgba(67,97,238,.12); color: var(--c-primary); font-weight: 600; }
/* ── 两级钻取 + 面板内搜索（2026-09-18）──
   「页面组件」的「全部层级」下拉：根层只有 3 项（全部层级 / 一级菜单 / 二级菜单），
   点「二级菜单」才钻进去列页面。加这几条只为让钻取层能放下「›」和搜索框；
   老筛选器不写 children，观感完全不变。 */
.filter-pop-opt-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.filter-pop-opt-more { flex: 0 0 auto; color: var(--c-ink-4); font-size: 15px; line-height: 1; }
.filter-pop-back {
  display: flex; align-items: center; gap: 4px; padding: 7px 10px; margin: 0 0 4px;
  border-bottom: 1px solid var(--c-line); border-radius: 6px 6px 0 0;
  font-size: 13px; font-weight: 600; color: var(--c-primary); cursor: pointer;
}
.filter-pop-back:hover { background: rgba(67,97,238,.08); }
.filter-pop-back-ico { font-size: 16px; line-height: 1; }
.filter-pop-search {
  display: block; width: 100%; box-sizing: border-box; margin: 0 0 6px; padding: 7px 10px;
  font-size: 13px; color: var(--c-ink); background: #fff;
  border: 1px solid var(--c-line); border-radius: 8px; outline: none;
}
.filter-pop-search:focus { border-color: var(--c-primary); box-shadow: 0 0 0 3px rgba(79,70,229,.12); }
.filter-pop-empty { padding: 8px 10px; font-size: 13px; color: var(--c-ink-4); }
/* 复选 / 单选统一包装（替代裸写 inline style 的 label） */
.form-check { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; font-size: 13px; color: var(--c-ink-2); user-select: none; }
.form-check.strong { font-weight: 600; font-size: 13.5px; }
.form-check input[type=checkbox], .form-check input[type=radio] { width: 15px; height: 15px; accent-color: var(--c-primary); flex: 0 0 auto; cursor: pointer; }
/* Toggle switch（替代弹窗内原生 checkbox，移动端更大更易点） */
.field .toggle-field { display: flex; align-items: center; gap: 12px; flex-wrap: nowrap; cursor: default; }
.toggle-label { font-weight: 500; color: var(--c-ink); font-size: 14px; white-space: nowrap; }
.toggle-state { font-size: 13px; color: var(--c-ink-2); min-width: 2em; }
.toggle-switch { position: relative; display: inline-block; width: 48px; height: 26px; flex: 0 0 auto; }
.toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.ts-slider { position: absolute; cursor: pointer; inset: 0; background: var(--c-line); border-radius: 26px; transition: background .2s ease; }
.ts-slider::before { content: ""; position: absolute; height: 20px; width: 20px; left: 3px; bottom: 3px; background: #fff; border-radius: 50%; transition: transform .2s ease; box-shadow: 0 1px 3px rgba(0,0,0,.18); }
.toggle-switch input:checked + .ts-slider { background: var(--c-primary); }
.toggle-switch input:checked + .ts-slider::before { transform: translateX(22px); }
/* 表单布局工具类（替代散落 inline style） */
.flex-1 { flex: 1; }
.ml-1 { margin-left: 4px; }
.ml-3 { margin-left: 12px; }
.mt-1 { margin-top: 4px; }
.w-48 { width: 48px; }
.w-50 { width: 50px; }
.w-58 { width: 58px; }
.w-90 { width: 90px; }
.w-100 { width: 100%; }
.min-w-58 { min-width: 58px; }
.min-w-140 { min-width: 140px; }
.min-w-260 { min-width: 260px; }
.min-w-280 { min-width: 280px; }
.max-w-400 { max-width: 400px; }
.text-13 { font-size: 13px; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }

/* API 市场分类导航 + 卡片 */
.cat-nav-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 8px; cursor: pointer; font-size: 13.5px; color: var(--c-ink-2); transition: background .15s, color .15s; user-select: none; }
.cat-nav-item:hover { background: var(--c-bg); color: var(--c-ink); }
.cat-nav-item.active { background: rgba(79,70,229,.08); color: var(--c-primary); font-weight: 600; }
.cat-nav-item .ic { width: 18px; height: 18px; stroke-width: 2; }
.api-card { background: #fff; border: 1px solid var(--c-line); border-radius: 10px; padding: 18px; cursor: pointer; transition: box-shadow .15s, border-color .15s, transform .1s; display: flex; flex-direction: column; gap: 12px; }
.api-card:hover { border-color: var(--c-primary); box-shadow: 0 4px 12px rgba(79,70,229,.08); transform: translateY(-1px); }
.api-card-head { display: flex; align-items: center; gap: 14px; }
.api-card-icon { width: 40px; height: 40px; border-radius: 10px; background: rgba(79,70,229,.08); color: var(--c-primary); display: inline-flex; align-items: center; justify-content: center; }
.api-card-icon .ic { width: 22px; height: 22px; }
.api-card-title { font-size: 15px; font-weight: 600; color: var(--c-ink); line-height: 1.3; }
.api-card-desc { font-size: 12.5px; color: var(--c-ink-3); line-height: 1.6; margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.api-card .badge.free { background: #dcfce7; color: #166534; border-color: #bbf7d0; }
.api-card .badge.paid { background: #dbeafe; color: #1e40af; border-color: #bfdbfe; }
.api-card .badge.beta { background: #fef3c7; color: #92400e; border-color: #fde68a; }
.api-card .badge.online { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.api-card .badge.offline { background: #f1f5f9; color: #475569; border-color: #e2e8f0; }
.api-card { position: relative; }
.api-card .cap-edit {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  width: 24px; height: 24px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 6px; border: 1px solid transparent;
  background: transparent; color: var(--c-ink-3); opacity: .65;
  transition: opacity .15s, border-color .15s, color .15s, background .15s;
}
.api-card .cap-edit svg { width: 12px; height: 12px; }
.api-card .cap-edit:hover { opacity: 1; border-color: var(--c-line); color: var(--c-primary); background: #f8fafc; }

/* 租户 Logo 上传控件 */
.logo-upload { display: flex; align-items: center; gap: 14px; margin-bottom: 8px; }
.logo-preview { width: 56px; height: 56px; border-radius: 10px; object-fit: contain; border: 1px solid var(--c-line); background: #fff; padding: 3px; flex: 0 0 auto; }
.logo-actions { display: flex; flex-direction: column; gap: 6px; }
.logo-actions input[type=file] { font-size: 12.5px; color: var(--c-ink-3); }
.form-static-url { font-size: 12.5px; color: var(--c-text-3); word-break: break-all; padding: 6px 0; }
.form-image-preview { max-width: 100%; max-height: 220px; border-radius: 8px; border: 1px solid var(--c-border); object-fit: contain; background: var(--c-bg); }
.input:disabled, .textarea:disabled, .select:disabled,
.date-combo.disabled .input, .date-combo.disabled .date-native,
.toggle-field.disabled .toggle-switch { opacity: .65; background: var(--c-bg-2); cursor: not-allowed; }

/* 提示 / 空态 */
.toast {
  position: fixed; top: 22px; left: 50%; transform: translateX(-50%); z-index: 10000;
  display: inline-flex; align-items: center; gap: 8px;
  min-width: min-content; max-width: min(90vw, 360px);
  background: var(--c-toast-bg); color: #fff; padding: 9px 16px; border-radius: 999px;
  font-size: 13.5px; font-weight: 500; letter-spacing: 0.01em; line-height: 1.4;
  box-shadow: 0 8px 20px var(--c-toast-shadow);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  pointer-events: none;
  animation: toast-pop .2s ease;
}
@keyframes toast-pop {
  from { transform: translateX(-50%) translateY(12px) scale(.98); opacity: 0; }
  to { transform: translateX(-50%); opacity: 1; }
}
/* 全局 Toast 浮层：默认隐藏，由 showToast() 切换 .show 显隐 */
#global-toast { display: none; }
#global-toast.show { display: inline-flex; }
.empty { text-align: center; padding: 60px 20px; color: var(--c-ink-4); }
.empty .ic { width: 48px; height: 48px; opacity: .5; }
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 26px; }
.stat-card { background: #fff; border: 1px solid var(--c-line); border-radius: var(--r-lg); padding: 18px 20px; box-shadow: var(--sh-sm); }
.stat-card .n { font-size: 28px; font-weight: 800; color: var(--c-primary); }
.stat-card .l { color: var(--c-ink-3); font-size: 13px; margin-top: 2px; }

/* 租户内容统计小卡片（紧凑门户概览版） */
.portal-stats { display: flex; gap: 0; border: 1px solid var(--c-line-2); border-radius: var(--r-md); background: var(--c-surface); overflow: hidden; margin-bottom: 14px; }
.ps-item { flex: 1; text-align: center; padding: 10px 6px; border-right: 1px solid var(--c-line-2); }
.ps-item:last-child { border-right: none; }
.ps-n { display: block; font-size: 20px; font-weight: 700; color: var(--c-primary); line-height: 1.2; }
.ps-l { font-size: 11px; color: var(--c-ink-4); margin-top: 2px; }

/* ── 内容超级编辑器（标准 list-view 驱动） ───── */
.super-editor { min-height: calc(100vh - 140px); }
.se-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 20px; color: var(--c-ink-4); }
.se-empty p { margin: 0 0 12px; }

.token-box {
  display: flex; align-items: center; gap: 10px; background: #0f172a; color: #e2e8f0;
  padding: 12px 16px; border-radius: var(--r-md); font-family: var(--font-mono); font-size: 13px;
}

/* 控制台登录 */
.console-login { min-height: 100vh; display: grid; place-items: center; background: var(--grad-hero); padding: 20px; }
.login-card { background: #fff; border-radius: var(--r-xl); padding: 38px; width: 100%; max-width: 400px; box-shadow: var(--sh-lg); }
.login-card h2 { margin: 0 0 6px; }
.login-card p.sub { color: var(--c-ink-3); margin: 0 0 22px; font-size: 14px; }

/* 登录方式切换 Tab（账号密码 / 手机号登录） */
.login-tabs { display: flex; gap: 8px; margin-bottom: 18px; background: #f3f4f7; border-radius: 10px; padding: 4px; }
.login-tab { flex: 1; border: 0; background: transparent; padding: 9px 0; border-radius: 8px; font-size: 14px; color: var(--c-ink-3); cursor: pointer; transition: .15s; }
.login-tab.active { background: #fff; color: var(--c-primary); font-weight: 600; box-shadow: 0 1px 4px rgba(15,23,42,.08); }
/* 验证码输入 + 获取按钮一行 */
.code-row { display: flex; gap: 10px; }
.code-row .input { flex: 1; }
.code-row .btn { white-space: nowrap; flex-shrink: 0; }
.login-hint { margin: 14px 0 0; font-size: 12px; color: var(--c-ink-3); text-align: center; }

/* ============================================================
   登录页改版（2026-09-07）：主流程 = 手机号 + 验证码 + 微信一键登录，
   账号密码登录降级为底部链接；≥768px 左右分栏（品牌面板 + 表单），移动端单栏。
   全部样式收敛在 .login-v2 命名空间下，不污染全局 .input / .btn / .field。
   视觉单一事实来源：品牌色=var(--c-primary)、胶囊圆角=var(--r-full)、
   输入框灰底=--lv2-input-bg（与 .toolbar-search 同观感）、按钮高度=--lv2-btn-h。
   ============================================================ */
.console-login.login-v2 { padding: 24px; }
.lv2-shell { width: 100%; max-width: 880px; background: var(--c-surface); border-radius: var(--r-xl); box-shadow: var(--sh-lg); overflow: hidden; display: flex; }
.lv2-brand { flex: 0 0 320px; background: #eef2ff; padding: 36px 30px; display: flex; flex-direction: column; }
.lv2-brand-head { display: flex; align-items: center; gap: 12px; }
.lv2-mark { width: 40px; height: 40px; border-radius: 12px; background: var(--grad-brand); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-weight: 600; font-size: 15px; }
.lv2-brand-txt { display: flex; flex-direction: column; line-height: 1.35; }
.lv2-brand-name { font-size: 14px; font-weight: 600; color: var(--c-ink); font-style: normal; }
.lv2-brand-sub { font-size: 11.5px; color: var(--c-ink-3); font-style: normal; }
.lv2-slogan { font-size: 20px; font-weight: 600; color: var(--c-ink); line-height: 1.5; margin: 40px 0 0; }
.lv2-points { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.lv2-points li { position: relative; padding-left: 16px; font-size: 13px; color: var(--c-ink-2); line-height: 1.5; }
.lv2-points li::before { content: ""; position: absolute; left: 0; top: 8px; width: 6px; height: 6px; border-radius: 50%; background: var(--c-primary); }
.lv2-foot { margin: auto 0 0; padding-top: 24px; font-size: 11px; color: var(--c-ink-3); }

.lv2-panel { position: relative; flex: 1 1 auto; min-width: 0; padding: 40px 36px 28px; overflow: hidden; }
.lv2-watermark { position: absolute; right: -18px; top: 46px; font-size: 72px; font-weight: 700; color: #eef2ff; line-height: 1; user-select: none; pointer-events: none; }
.lv2-title { position: relative; font-size: 24px; font-weight: 600; color: var(--c-ink); margin: 0 0 6px; }
.lv2-sub { position: relative; font-size: 13px; color: var(--c-ink-3); margin: 0 0 24px; }
.lv2-input { position: relative; display: block; width: 100%; box-sizing: border-box; border: none; outline: none; background: #f1f3f6; border-radius: var(--r-full); padding: 0 20px; height: 50px; font-size: 15px; font-family: inherit; color: var(--c-ink); margin-bottom: 12px; }
.lv2-input::placeholder { color: #94a3b8; }
.lv2-input:focus { background: #e8ebf0; }
.lv2-row { position: relative; display: flex; gap: 10px; margin-bottom: 12px; }
.lv2-row .lv2-input { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }
.lv2-btn-ghost { flex: 0 0 auto; height: 50px; padding: 0 20px; border: none; border-radius: var(--r-full); background: #a5b4fc; color: #fff; font-size: 13.5px; font-family: inherit; cursor: pointer; box-sizing: border-box; }
.lv2-btn-ghost:disabled { background: #c7d2fe; cursor: not-allowed; }
.lv2-btn { position: relative; display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; height: 50px; box-sizing: border-box; border: none; border-radius: var(--r-full); font-size: 16px; font-weight: 500; font-family: inherit; cursor: pointer; }
.lv2-btn-primary { background: var(--grad-brand); color: #fff; margin-bottom: 12px; box-shadow: var(--sh-brand); transition: transform .18s ease, filter .18s ease; }
.lv2-btn-primary:hover { transform: translateY(-1px); filter: brightness(1.05); }
.lv2-btn-wechat { background: #fff; color: var(--c-ink); border: 1px solid var(--c-line); margin-bottom: 12px; }
.lv2-btn-wechat:hover { background: #fafbff; }
.lv2-agree { position: relative; display: flex; align-items: flex-start; gap: 8px; margin-top: 12px; font-size: 12px; color: var(--c-ink-3); line-height: 1.6; cursor: pointer; }
.lv2-agree.shake { animation: lv2-shake .5s ease; }
.lv2-check { flex: 0 0 auto; width: 16px; height: 16px; margin-top: 2px; border-radius: 50%; border: 1.5px solid #cbd5e1; box-sizing: border-box; }
.lv2-check.on { border-color: var(--c-primary); background: var(--c-primary); box-shadow: inset 0 0 0 3px #fff; }
.lv2-link { color: var(--c-primary); text-decoration: none; cursor: pointer; }
.lv2-switch { position: relative; display: flex; justify-content: space-between; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--c-line-2); }
.lv2-switch .lv2-link { color: var(--c-ink-3); font-size: 12.5px; }
@keyframes lv2-shake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-5px); } 40% { transform: translateX(5px); } 60% { transform: translateX(-3px); } 80% { transform: translateX(3px); } }

/* ── 登录方式切换 Tab（2026-09-17 改版：微信登录为默认）────────────
   胶囊分段控件，与 .lv2-input 的灰底胶囊同一观感；选中项浮起为白底。
   三个 tab 等宽自适应，手机上不换行、不溢出。 */
.lv2-tabs { position: relative; display: flex; gap: 4px; margin: 0 0 18px; padding: 4px; box-sizing: border-box; background: #f1f3f6; border-radius: var(--r-full); }
.lv2-tab { flex: 1 1 0; min-width: 0; height: 34px; padding: 0 4px; border: none; background: transparent; border-radius: var(--r-full); font-family: inherit; font-size: 13px; color: var(--c-ink-3); cursor: pointer; white-space: nowrap; transition: background .18s ease, color .18s ease, box-shadow .18s ease; }
.lv2-tab:hover { color: var(--c-ink); }
.lv2-tab.on { background: var(--c-surface); color: var(--c-ink); font-weight: 600; box-shadow: 0 1px 3px rgba(15, 23, 42, .10); }

/* ── 内嵌微信二维码（WxLogin.mount 画进这里，不弹层）─────────────
   尺寸常量与 wx-login.js 的 INLINE_QR_SIZE=168 必须一致（改一处要同步改另一处）。 */
.lv2-qr { position: relative; display: flex; flex-direction: column; align-items: center; min-height: 214px; margin: 2px 0 0; }
.lv2-qr .wxqr-code { width: 168px; height: 168px; margin: 0 auto 10px; }
.lv2-qr .wxqr-code img, .lv2-qr .wxqr-code canvas { width: 168px; height: 168px; }
.lv2-qr .wxqr-note { text-align: center; }
.lv2-qr-tip { position: relative; margin: 12px 0 0; font-size: 12.5px; color: var(--c-ink-3); text-align: center; line-height: 1.7; }
.lv2-qr-tip.err { color: #dc2626; }

/* ── 微信登录后「绑定手机号」引导 ───────────────────────────── */
.wxbind-mask { position: fixed; inset: 0; z-index: 1300; display: flex; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box; background: rgba(15, 23, 42, .55); }
.wxbind-box { width: 100%; max-width: 380px; background: var(--c-surface); border-radius: var(--r-lg); box-shadow: 0 20px 60px rgba(15, 23, 42, .28); overflow: hidden; }
.wxbind-head { padding: 14px 20px; border-bottom: 1px solid var(--c-line-2); font-size: 15px; font-weight: 600; color: var(--c-ink); }
.wxbind-body { padding: 18px 20px 4px; }
.wxbind-user { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.wxbind-avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; background: #eef2ff; }
.wxbind-avatar.letter { display: grid; place-items: center; background: var(--grad-brand); color: #fff; font-weight: 700; font-size: 16px; }
.wxbind-name { font-size: 14px; font-weight: 600; color: var(--c-ink); }
.wxbind-tip { margin: 0 0 14px; font-size: 12.5px; color: var(--c-ink-3); line-height: 1.7; }
.wxbind-foot { display: flex; align-items: center; justify-content: flex-end; gap: 10px; padding: 14px 20px 18px; }
/* ── 手机号必填版（2026-09-19 由「可跳过引导」改为强制）────────────
   内容变多了（鼓励文案 + 3 条价值 + 隐私说明），底部也从「左跳过／右提交」并排
   改成「主按钮通栏 + 退出登录」竖排：主按钮是现在唯一的正向出口，给足尺寸；
   「退出登录」弱化成小号灰字，避免用户把它当成「跳过」随手点掉。 */
.wxbind-hello { margin: 0 0 6px; font-size: 15px; font-weight: 700; color: var(--c-ink); line-height: 1.5; }
.wxbind-benefits { margin: 0 0 12px; padding: 0; list-style: none; font-size: 12.5px; color: var(--c-ink-2); line-height: 1.9; }
.wxbind-benefits li { position: relative; padding-left: 17px; }
.wxbind-benefits li::before { content: '✓'; position: absolute; left: 0; color: var(--c-primary); font-weight: 700; }
.wxbind-privacy { margin: 0 0 14px; font-size: 12px; color: var(--c-ink-4); line-height: 1.7; }
.wxbind-foot-required { flex-direction: column; align-items: stretch; gap: 10px; padding-top: 12px; }
.wxbind-submit { width: auto; flex: 0 0 auto; height: 42px; padding: 0 22px; margin-bottom: 0; font-size: 14px; }
.wxbind-foot-required .wxbind-submit { width: 100%; }
.wxbind-quit { border: none; background: none; font-family: inherit; font-size: 12.5px; color: var(--c-ink-4); cursor: pointer; padding: 2px 0; text-align: center; }
.wxbind-quit:hover { color: var(--c-ink-2); }
.wxbind-submit:disabled { opacity: .7; cursor: not-allowed; transform: none; }

/* ── 短信通道未配置时的「验证码常驻提示」（2026-09-19）────────────
   后端在腾讯云短信未配齐（或 AUTH_DEV_MODE）时**不发真短信**，只把验证码放进响应体。
   原来只靠一条 2.6 秒的提示条告知 → 错过就再也拿不到（60s 内不许重发），
   而补绑弹层是必填闸门 → 用户直接被锁在门外。改为**固定写在这一块**，不随提示条消失。
   配色用「警示」而非错误红：这不是故障，是尚未接入的既定状态。 */
.lv2-devnote, .wxbind-devnote {
  margin: 10px 0 0; padding: 9px 12px; border-radius: 10px;
  background: var(--c-warning-bg); border: 1px solid var(--c-warning-line);
  font-size: 12.5px; line-height: 1.65; color: var(--c-warning-ink);
  word-break: break-all;
}
.lv2-devnote b, .wxbind-devnote b { font-size: 15px; letter-spacing: .12em; font-weight: 700; color: var(--c-warning-ink-strong); }

@media (max-width: 767px) {
  .console-login.login-v2 { padding: 0; background: var(--c-surface); }
  .lv2-shell { max-width: none; border-radius: 0; box-shadow: none; min-height: 100vh; }
  .lv2-brand { display: none; }
  .lv2-panel { padding: 56px 24px 32px; }
}

/* ============================================================
   微信扫码登录弹层（wx-login.js）—— 样式收敛在 .wxqr-* 下
   ============================================================ */
.wxqr-mask { position: fixed; inset: 0; z-index: 1200; display: none; align-items: center; justify-content: center; background: rgba(15, 23, 42, .55); padding: 20px; box-sizing: border-box; }
.wxqr-mask.on { display: flex; }
.wxqr-box { width: 100%; max-width: 340px; background: var(--c-surface); border-radius: var(--r-lg); box-shadow: 0 20px 60px rgba(15, 23, 42, .28); overflow: hidden; }
.wxqr-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--c-line-2); }
.wxqr-title { font-size: 15px; font-weight: 600; color: var(--c-ink); }
.wxqr-close { border: none; background: none; font-size: 22px; line-height: 1; color: var(--c-ink-3); cursor: pointer; padding: 0 2px; }
.wxqr-close:hover { color: var(--c-ink); }
.wxqr-body { padding: 24px 18px 18px; text-align: center; }
.wxqr-code { width: 180px; height: 180px; margin: 0 auto 16px; padding: 8px; box-sizing: content-box; background: #fff; border: 1px solid var(--c-line-2); border-radius: var(--r-md); }
.wxqr-code img, .wxqr-code canvas { display: block; width: 180px; height: 180px; }
.wxqr-tip { margin: 0 0 6px; font-size: 14px; color: var(--c-ink); }
.wxqr-note { margin: 0; font-size: 12.5px; color: var(--c-ink-3); min-height: 18px; }
.wxqr-note.ok { color: #16a34a; }
.wxqr-note.err { color: #dc2626; }
/* 电脑扫码时手机端的收尾提示（showScanDone） */
.wxqr-hint { margin: 10px 0 0; font-size: 12px; color: var(--c-ink-3); }
.wxqr-foot { padding: 12px 18px 16px; border-top: 1px solid var(--c-line-2); text-align: center; }
.wxqr-refresh { border: none; background: none; font-size: 12.5px; font-family: inherit; color: var(--c-primary); cursor: pointer; }
.wxqr-refresh:hover { text-decoration: underline; }

/* 只读输入框（如「更新日期」系统自动维护） */
.input.ro, .input[readonly] { background: #f5f6f8; color: var(--c-ink-3); cursor: not-allowed; }

/* ============================================================
   场景详情抽屉（cap-modal）—— PC 右侧滑出，移动端全屏
   ============================================================ */
.cap-modal-mask {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(15, 23, 42, .42);
  display: flex; justify-content: flex-end; align-items: stretch;
}
.cap-modal {
  position: relative; width: 440px; max-width: 92vw; height: 100%;
  background: #fff; box-shadow: -10px 0 36px rgba(15, 23, 42, .22);
  padding: 22px 26px; box-sizing: border-box; overflow-y: auto;
  display: flex; flex-direction: column; gap: 4px;
}
.cap-modal-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-bottom: 14px; margin-bottom: 6px;
  border-bottom: 1px solid var(--c-line); flex: 0 0 auto;
  position: relative;
}
.cap-modal-head > :not(.close-x) { flex: 0 0 auto; }
.cap-invoke-box {
  margin-top: 14px; padding: 14px 16px; border-radius: var(--r-lg);
  background: var(--c-bg); border: 1px solid var(--c-line);
}
.cap-invoke-title { font-weight: 600; font-size: 14px; margin-bottom: 4px; color: var(--c-ink); }
/* 抽屉滑入/淡入动画 */
.cap-slide-enter-active, .cap-slide-leave-active { transition: opacity .25s ease; }
.cap-slide-enter-from, .cap-slide-leave-to { opacity: 0; }
.cap-slide-enter-active .cap-modal, .cap-slide-leave-active .cap-modal { transition: transform .28s cubic-bezier(.22,.61,.36,1); }
.cap-slide-enter-from .cap-modal, .cap-slide-leave-to .cap-modal { transform: translateX(100%); }

/* ============================================================
   响应式
   ============================================================ */
@media (max-width: 1024px) {
  :root {
    --m-page-pad: 16px;
    --m-panel-inset: 8px;
    --m-card-gap: 18px;
    --m-card-pad: 16px;
    --m-section-gap: 18px;
  }
  .grid.cols-4 { grid-template-columns: repeat(2, 1fr); }
  .grid.cols-3 { grid-template-columns: repeat(2, 1fr); }
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .footer .cols { grid-template-columns: 1fr 1fr; }

  /* ── 控制台横屏 / 小平板（768px < width <= 1024px）──
     手机横屏时屏幕高度不足、宽度也局促，必须让 sidebar 收起为图标栏、
     toolbar 垂直、表格横向滚动，避免内容被挤压截断。 */
  .console { min-width: 0; width: 100%; } /* 防止 flex 子项撑开整体，同时确保占满视口宽度 */
  .sidebar,
  .sidebar:not(.open) {
    width: 64px;
    overflow-x: hidden;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* 强制占满整高，菜单超长时内部滚动，避免图标溢出到 sidebar 背景外 */
  }
  .sidebar.open { width: 240px; box-shadow: var(--sh-lg); }
  .sidebar:not(.open) .brand { padding: 16px 10px 12px; justify-content: center; }
  .sidebar:not(.open) .brand-text,
  .sidebar:not(.open) .nav-label { display: none; }
  .sidebar:not(.open) .side-nav { padding: 6px 10px; }
  .sidebar:not(.open) .side-nav a { justify-content: center; padding: 8px 10px; }
  .sidebar:not(.open) .sidebar-foot { justify-content: center; padding: 8px 10px; }
  /* 横屏/小平板下隐藏折叠 sidebar 的 tooltip：触摸设备无 hover，且会遮挡主内容区 */
  .sidebar:not(.open) .side-nav a::after { display: none; }

  .console-main { width: 100%; min-width: 0; }
  .console-body { padding: var(--console-body-gap) var(--m-page-pad) var(--m-page-pad); width: 100%; box-sizing: border-box; min-width: 0; }
  .page-panel { margin: calc(-1 * var(--console-body-gap)) calc(-1 * var(--m-page-pad) + var(--m-panel-inset)); padding: var(--console-body-gap) var(--m-panel-inset) 0; }
  .page-panel > .card { border-radius: var(--r-md); padding: var(--m-card-pad); }
  .page-panel > .cap-banner { border-radius: var(--r-md); }
  .page-panel > .card + .card,
  .page-panel > .cap-banner + .card { margin-top: var(--m-card-gap); }
  .dashboard-panel .stat-grid { gap: var(--m-card-gap); margin-bottom: var(--m-card-gap); }
  .dashboard-panel .stat-card { padding: var(--m-card-pad); }
  .dashboard-panel > .card { padding: var(--m-card-pad); margin-top: var(--m-card-gap) !important; border-radius: var(--r-md); }

  /* toolbar 紧凑布局：横向 flex + wrap，控件自然排布成 1~2 行。
     旧实现为 column + 每个控件 width:100%，N 个控件就占 N 行（如菜单管理 4 控件 = 4 行），
     纵向占用过大。此处改回横向：搜索框可伸缩，下拉/按钮按内容自然宽，超出自动换行成第二行。
     控件高度/间距/搜索框最小宽统一由 --toolbar-h-mobile / --toolbar-gap-mobile /
     --toolbar-search-min-w 控制，各页面禁止再散写移动端 toolbar 样式。 */
  .toolbar { flex-direction: row; align-items: center; gap: var(--toolbar-gap-mobile); margin-bottom: 10px; }
  .toolbar .left { flex: 1 1 auto; min-width: 0; flex-direction: row; flex-wrap: wrap; width: auto; gap: var(--toolbar-gap-mobile); align-items: center; }
  .toolbar .right { flex: 0 0 auto; width: auto; margin-left: auto; display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--toolbar-gap-mobile); align-items: center; }
  /* 搜索框可伸缩（最小 --toolbar-search-min-w），把剩余空间让给同行的下拉/按钮，尽量凑成一行 */
  .toolbar .toolbar-search,
  .toolbar .left .toolbar-search { width: auto; max-width: none; flex: 1 1 var(--toolbar-search-min-w); min-width: var(--toolbar-search-min-w); }
  /* 下拉/筛选：按内容自然宽，不再占满整行 */
  .toolbar .left .input-inline { width: auto; max-width: none; min-width: 0; flex: 0 1 auto; }
  .toolbar .left .select { width: auto; flex: 0 0 auto; min-width: 0; }
  .toolbar .left .filter-select { width: auto; flex: 0 0 auto; max-width: 46vw; }
  .filter-select .filter-select-trigger { width: auto; min-width: 0; max-width: 100%; }
  /* list-view 多 filter 场景：移动端让两个下拉框横向充满整行（各约 50%），PC 端仍保持自然宽一行 */
  .list-view .toolbar .toolbar-filters { gap: var(--toolbar-gap-mobile); }
  .list-view .toolbar .toolbar-filters .filter-select { flex: 1 1 auto; min-width: 0; max-width: none; }
  .list-view .toolbar .toolbar-filters .filter-select-trigger { width: 100%; }
  .filter-pop { left: 0; right: auto; min-width: 140px; max-width: 76vw; }
  /* 右侧操作按钮：自然宽，不拉满整行 */
  .toolbar .right .input { width: auto; flex: 0 0 auto; }
  .toolbar .right .btn { flex: 0 0 auto; justify-content: center; }
  .toolbar .toolbar-add-btn { width: auto; }
  /* 直接子元素版本：覆盖 .toolbar > button / .toolbar > .refresh-btn.refresh-btn-block
     等没有 .left/.right 包裹的场景（如群活码：<div class="toolbar">+ add-btn + refresh-btn-block）。
     2 个按钮时各占一半填满整行（flex: 1 1 auto 等分剩余空间），1 个按钮时自然占满 100%。 */
  .toolbar > .refresh-btn.refresh-btn-block,
  .toolbar > .toolbar-add-btn { width: auto; flex: 1 1 auto; min-width: 0; }
  /* 控件统一高度（PC 约 43px → 移动端 --toolbar-h-mobile）。
     选择器必须包含"直子"版本：群活码/员工活码/媒体生态/知识概览/基础数据 等页 toolbar
     是 <div class="toolbar">+add-btn+refresh-btn-block，没有 .left/.right 包裹。
     此前只覆盖 .toolbar .right .refresh-btn → 直子 refresh-btn 没被压扁（44px 而非 34px）。
     改：把直子 .toolbar > .refresh-btn.refresh-btn-block 和 .toolbar > .toolbar-add-btn 也纳入高度规则。 */
  .toolbar .toolbar-search,
  .toolbar .left .input-inline,
  .toolbar .left .select,
  .filter-select .filter-select-trigger,
  .toolbar .right .input,
  .toolbar .right .btn,
  .toolbar .right .refresh-btn,
  .toolbar .toolbar-add-btn,
  .toolbar > .btn,
  .toolbar > .refresh-btn.refresh-btn-block,
  .toolbar > .toolbar-add-btn { height: var(--toolbar-h-mobile); padding-top: 0; padding-bottom: 0; }
  /* 水平内边距由 --action-primary-pad-x 统一控制（PC=18px/移动端不变），
     此前这里写死 12px 会让 .refresh-btn 在移动端比 .toolbar-add-btn 明显小一截，违反"两边一致"约束。
     现删除硬编码，全部由 :root 的 --action-primary-pad-x 控制，2 按钮场景 padding 自动统一。 */

  /* 三控件紧凑工具栏（下拉 + 搜索 + 刷新）：移动端强制单行不换行。
     仅作用于带 .toolbar-inline 的工具栏（企微通讯录 / 客户群 / 调用明细等"1下拉+1搜索+1刷新"页），
     不影响更多控件的工具栏（如 2 下拉的聚合页、4 控件的菜单管理仍按通用规则折行）。
     - .toolbar nowrap → 刷新不再被挤到第二行；
     - .left 不换行且可撑满 → 下拉与搜索始终同行，搜索框压缩让位；
     - 搜索 flex:1 + min-width:0 → 空间紧时搜索框收缩，三控件始终一行。 */
  .toolbar.toolbar-inline { flex-wrap: nowrap; }
  .toolbar.toolbar-inline > .left { flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; }
  .toolbar.toolbar-inline .toolbar-search { flex: 1 1 auto; min-width: 0; max-width: none; }
  .toolbar.toolbar-inline .select { flex: 0 0 auto; min-width: 0; max-width: 42vw; }
  /* 注意：此处 (0,4,0) 高于圆形规则 (0,3,0)。原 width:auto 会把圆形刷新按钮压成 16×32 椭圆（同 2862 的坑）。
     改为 var(--toolbar-h-mobile) 让其回到 32px 圆形；min-width:0 保留以便窄屏不被内容撑开。 */
  .toolbar.toolbar-inline > .refresh-btn,
  .toolbar.toolbar-inline > .refresh-btn.refresh-btn-block { flex: 0 0 auto; min-width: 0; width: var(--toolbar-h-mobile); }

  /* 基础数据页工具栏：下拉标签切换 + 搜索 + 新建按钮，移动端强制单行 */
  .toolbar.base-data-toolbar { flex-wrap: nowrap; }
  .toolbar.base-data-toolbar > .left { flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; }
  .toolbar.base-data-toolbar .select { flex: 0 0 auto; min-width: 0; max-width: 34vw; }
  .toolbar.base-data-toolbar .toolbar-search { flex: 1 1 auto; min-width: 0; max-width: none; }
  .toolbar.base-data-toolbar > .toolbar-add-btn { flex: 0 0 auto; min-width: 0; }

  /* 表格强制最小宽度并允许横向滚动，字段不再被压扁 */
  .table-wrap { overflow-x: auto; width: 100%; box-sizing: border-box; }
  .table-wrap[class*="freeze-cols-"] { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  /* 同上：Safari 下 table 的 width:max-content 不可靠，用 width:auto + min-width 方案 */
  .table-wrap[class*="freeze-cols-"] table.data {
    width: auto;
    min-width: 680px;
    min-width: max-content;
  }
  /* 移动端长标题列限制最大宽度；但可横向滚动的冻结列表格内，标题应随列宽自然展开 */
  .cell-title { max-width: 45vw; }
  .table-wrap[class*="freeze-cols-"] .cell-title,
  .table-wrap[class*="freeze-cols-"] .cell-title a {
    max-width: none !important;
    overflow: visible;
    text-overflow: clip;
  }

  /* 二级 tab 可横向滑动 */
  .sub-nav-tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; flex-shrink: 0; padding: 0 var(--m-page-pad); gap: 8px; }
  .sub-nav-tabs a { flex: 0 0 auto; flex-shrink: 0; -webkit-flex-shrink: 0; padding: 16px 20px; font-size: 14px; }

  .field-row { grid-template-columns: 1fr; }
  .portal-stats { display: grid; grid-template-columns: repeat(3, 1fr); }
  .ps-item { border-right: 1px solid var(--c-line-2); }
  .card { padding: var(--m-card-pad); margin-left: 0 !important; margin-right: 0 !important; }
  .cap-banner { padding: 14px var(--m-card-pad); border-radius: var(--r-md); }
  .cap-content > .cap-banner + .card { margin-top: var(--m-card-gap); }

  /* 弹窗满屏：横屏/小平板（768px < width <= 1024px）同样要铺满，避免 560px 居中上下截断 */
  .modal-mask { padding: 0 !important; }
  .modal {
    width: 100% !important; max-width: 100% !important;
    border-radius: 0 !important; min-height: auto !important; max-height: 100vh !important; max-height: 100dvh !important;
    box-shadow: none !important;
    display: flex !important; flex-direction: column !important;
  }
  .modal-head, .modal-foot { padding: 16px !important; flex: 0 0 auto !important; }
  .modal .modal-body { padding: 16px !important; overflow-x: hidden; overflow-y: auto; flex: 0 0 auto !important; max-height: calc(100vh - 150px) !important; max-height: calc(100dvh - 150px) !important; box-sizing: border-box !important; width: 100% !important; }
  .modal-foot { flex-direction: row; }
  .modal-foot .btn { flex: 1; justify-content: center; }
  /* 场景抽屉移动端全屏铺满（手机/iPad 横竖屏） */
  .cap-modal-mask { padding: 0; }
  .cap-modal { width: 100vw; max-width: 100%; padding: 16px; }
  /* 移动端关闭按钮放大，便于手指点击 */
  .close-x { width: 40px; height: 40px; font-size: 26px; }
  /* iOS Safari：输入框/下拉框/文本域字体小于 16px 时聚焦会自动放大页面，平板/手机统一提升到 16px
     覆盖 .input/.select/.textarea/.toolbar-search/.search/.input-inline/.page-size/.code-area 以及所有原生文本类输入 */
  .input, .input-inline, .input-sm, .textarea, .select, .select-sm, .toolbar-search, .search, .page-size, .code-area,
  input[type="text"], input[type="search"], input[type="email"], input[type="tel"], input[type="url"],
  input[type="number"], input[type="password"], input[type="date"], input[type="datetime-local"], input:not([type]),
  textarea, select,
  [contenteditable], .se-editor-mount [contenteditable] { font-size: 16px !important; }
.se-editor-mount { margin-bottom: 24px; }

  /* 能力/技能/软件链接中心详情移动端改为全屏弹窗 */
  .market-center-page .cap-content > .empty,
  .market-center-page .cap-content > .api-doc {
    position: fixed; inset: 0; z-index: 1000;
    background: #f8fafc;
    padding: var(--m-page-pad);
    padding-top: calc(12px + env(safe-area-inset-top));
    overflow-y: auto; border-radius: 0; margin: 0;
    box-sizing: border-box;
  }
  .market-center-page .api-doc-head {
    position: sticky; top: 0; background: #f8fafc; z-index: 1;
    margin: calc(-1 * var(--m-page-pad)) calc(-1 * var(--m-page-pad)) 12px;
    padding: 12px var(--m-page-pad);
    border-bottom: 1px solid var(--c-line);
  }
  .market-center-page .api-doc-body {
    padding-bottom: calc(var(--m-page-pad) + env(safe-area-inset-bottom));
  }
  /* 能力/技能中心详情返回按钮：仅移动端显示 */
  .market-center-page .api-doc-header .m-back-btn { display: inline-flex; margin-right: 8px; margin-left: 4px; width: 36px; height: 36px; align-items: center; justify-content: center; }
}
.menu-btn { display: none; }
@media (max-width: 768px) {
  :root {
    --m-page-pad: 16px;
    --m-panel-inset: 10px;
    --m-card-gap: 16px;
    --m-card-pad: 16px;
    --m-section-gap: 16px;
  }
  .nav { display: none; }
  .nav.open {
    display: flex; flex-direction: column; position: absolute; top: var(--header-h); left: 0; right: 0;
    background: #fff; padding: 14px; border-bottom: 1px solid var(--c-line); gap: 4px; box-shadow: var(--sh-md);
  }
  .menu-toggle { display: inline-flex; }
  .hero { padding: 64px 0 80px; }
  .hero h1 { font-size: 36px; }
  .hero-stats { gap: 28px; }
  .section { padding: 52px 0; }
  .section-head h2 { font-size: 26px; }
  .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr; }
  .menu-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; padding: 0; margin-right: 12px;
    border-radius: 50%;
    background: #eef2f7; color: #475569;
    border: 1px solid var(--c-line);
    transition: background .15s ease, color .15s ease;
  }
  .menu-btn:hover { background: #e2e8f0; color: #1e293b; }
  .menu-btn svg { width: 20px; height: 20px; }
  .sidebar { position: fixed; z-index: 60; width: 240px; transform: translateX(-100%); transition: transform .2s ease; box-shadow: var(--sh-lg); }
  .sidebar.open { transform: none; }
  .sidebar .brand { padding: 20px 20px 16px; justify-content: flex-start; }
  .sidebar .brand .brand-text { opacity: 1; width: auto; }
  .side-nav a { justify-content: flex-start; padding: 10px 14px; }
  .side-nav a .nav-label { opacity: 1; width: auto; }
  .console-main { width: 100%; }
  .console-body { padding: var(--console-body-gap) var(--m-page-pad) var(--m-page-pad); }
  .dashboard-panel .stat-grid { gap: var(--m-card-gap); margin-bottom: var(--m-card-gap); }
  .dashboard-panel .stat-card { padding: var(--m-card-pad); }
  .dashboard-panel .stat-card .n { font-size: 22px; }
  .dashboard-panel .stat-card .l { font-size: 12px; }
  .dashboard-panel > .card { padding: var(--m-card-pad); margin-top: var(--m-card-gap) !important; border-radius: var(--r-md); }
  .page-panel { margin: calc(-1 * var(--console-body-gap)) calc(-1 * var(--m-page-pad) + var(--m-panel-inset)); padding: var(--console-body-gap) var(--m-panel-inset) 0; }
  .page-panel > .card { border-radius: var(--r-md); }
  .page-panel > .cap-banner { border-radius: var(--r-md); }
  .page-panel > .card + .card,
  .page-panel > .cap-banner + .card { margin-top: var(--m-card-gap); }
  .sidebar-foot { display: none; }
  .sidebar-overlay { position: fixed; inset: 0; background: rgba(15,23,42,.45); z-index: 55; }
  .card { padding: var(--m-card-pad); margin-left: 0 !important; margin-right: 0 !important; }
  .cap-banner { padding: 14px var(--m-card-pad); border-radius: var(--r-md); }
  .cap-content > .cap-banner + .card { margin-top: var(--m-card-gap); }
  /* 二级导航 tab 统一使用移动端间距标准 */
  .sub-nav-tabs { padding: 0 var(--m-page-pad); gap: 8px; }
  .sub-nav-tabs a { padding: 16px 20px; font-size: 14px; }
  .table-wrap { overflow-x: auto; width: 100%; box-sizing: border-box; }
  table.data { min-width: 680px; }
  /* 同 1024px 断点：紧凑横向布局（此处重复一份是因为 768px 断点内原本有独立的 column 覆盖） */
  .toolbar { flex-direction: row; align-items: center; gap: var(--toolbar-gap-mobile); margin-bottom: 10px; }
  .toolbar .left { flex: 1 1 auto; min-width: 0; flex-direction: row; flex-wrap: wrap; width: auto; gap: var(--toolbar-gap-mobile); align-items: center; }
  .toolbar .right { flex: 0 0 auto; width: auto; margin-left: auto; display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--toolbar-gap-mobile); align-items: center; }
  /* 搜索框可伸缩（最小 --toolbar-search-min-w），把剩余空间让给同行的下拉/按钮，尽量凑成一行 */
  .toolbar .toolbar-search,
  .toolbar .left .toolbar-search { width: auto; max-width: none; flex: 1 1 var(--toolbar-search-min-w); min-width: var(--toolbar-search-min-w); }
  /* 下拉/筛选：按内容自然宽，不再占满整行 */
  .toolbar .left .input-inline { width: auto; max-width: none; min-width: 0; flex: 0 1 auto; }
  .toolbar .left .select { width: auto; flex: 0 0 auto; min-width: 0; }
  .toolbar .left .filter-select { width: auto; flex: 0 0 auto; max-width: 46vw; }
  .filter-select .filter-select-trigger { width: auto; min-width: 0; max-width: 100%; }
  /* list-view 多 filter 场景：移动端让两个下拉框横向充满整行（各约 50%），PC 端仍保持自然宽一行 */
  .list-view .toolbar .toolbar-filters { gap: var(--toolbar-gap-mobile); }
  .list-view .toolbar .toolbar-filters .filter-select { flex: 1 1 auto; min-width: 0; max-width: none; }
  .list-view .toolbar .toolbar-filters .filter-select-trigger { width: 100%; }
  .filter-pop { left: 0; right: auto; min-width: 140px; max-width: 76vw; }
  /* 右侧操作按钮：自然宽，不拉满整行 */
  .toolbar .right .input { width: auto; flex: 0 0 auto; }
  .toolbar .right .btn { flex: 0 0 auto; justify-content: center; }
  .toolbar .toolbar-add-btn { width: auto; }
  /* 直接子元素版本：覆盖 .toolbar > button / .toolbar > .refresh-btn.refresh-btn-block
     等没有 .left/.right 包裹的场景（如群活码：<div class="toolbar">+ add-btn + refresh-btn-block）。
     2 个按钮时各占一半填满整行（flex: 1 1 auto 等分剩余空间），1 个按钮时自然占满 100%。 */
  .toolbar > .refresh-btn.refresh-btn-block,
  .toolbar > .toolbar-add-btn { width: auto; flex: 1 1 auto; min-width: 0; }
  /* 控件统一高度（PC 约 43px → 移动端 --toolbar-h-mobile）。
     选择器必须包含"直子"版本：群活码/员工活码/媒体生态/知识概览/基础数据 等页 toolbar
     是 <div class="toolbar">+add-btn+refresh-btn-block，没有 .left/.right 包裹。
     此前只覆盖 .toolbar .right .refresh-btn → 直子 refresh-btn 没被压扁（44px 而非 34px）。
     改：把直子 .toolbar > .refresh-btn.refresh-btn-block 和 .toolbar > .toolbar-add-btn 也纳入高度规则。 */
  .toolbar .toolbar-search,
  .toolbar .left .input-inline,
  .toolbar .left .select,
  .filter-select .filter-select-trigger,
  .toolbar .right .input,
  .toolbar .right .btn,
  .toolbar .right .refresh-btn,
  .toolbar .toolbar-add-btn,
  .toolbar > .btn,
  .toolbar > .refresh-btn.refresh-btn-block,
  .toolbar > .toolbar-add-btn { height: var(--toolbar-h-mobile); padding-top: 0; padding-bottom: 0; }
  /* 水平内边距由 --action-primary-pad-x 统一控制（PC=18px/移动端不变），
     此前这里写死 12px 会让 .refresh-btn 在移动端比 .toolbar-add-btn 明显小一截，违反"两边一致"约束。
     现删除硬编码，全部由 :root 的 --action-primary-pad-x 控制，2 按钮场景 padding 自动统一。 */
  /* list-view 多 filter 场景（≥2 个筛选）：移动端强制单行，新增按钮收成圆 + 号。
     外部数据源等页有「搜索+新增+2 下拉」时，不再把 filters 挤到第二行。
     - .toolbar 自身 nowrap，.left / .toolbar-filters / .right 全部排在同一行；
     - 搜索框 flex:1 可压缩，filters 自然宽，新增按钮变等宽高圆形只保留 + 图标。 */
  .list-view .toolbar.toolbar-compact { flex-wrap: nowrap; }
  .list-view .toolbar.toolbar-compact .left { flex: 1 1 0; min-width: 0; flex-wrap: nowrap; }
  .list-view .toolbar.toolbar-compact .toolbar-search { flex: 1 1 0; min-width: 0; max-width: none; }
  .list-view .toolbar.toolbar-compact .toolbar-filters { flex: 0 1 auto; min-width: 0; display: flex; align-items: center; flex-wrap: nowrap; gap: var(--toolbar-gap-mobile); }
  .list-view .toolbar.toolbar-compact .toolbar-filters .filter-select { flex: 0 0 auto; min-width: 0; max-width: 24vw; }
  .list-view .toolbar.toolbar-compact .toolbar-filters .filter-select-trigger { width: auto; min-width: 0; }
  .list-view .toolbar.toolbar-compact .toolbar-filters .filter-select-label { min-width: 0; }
  .list-view .toolbar.toolbar-compact .right { flex: 0 0 auto; margin-left: auto; }
  .list-view .toolbar.toolbar-compact .toolbar-add-btn.icon-only { width: var(--toolbar-h-mobile); padding: 0; border-radius: 50%; justify-content: center; }
  .list-view .toolbar.toolbar-compact .toolbar-add-btn.icon-only .btn-text { display: none; }
  .list-view .toolbar.toolbar-compact .toolbar-add-btn.icon-only .btn-icon { margin: 0; }
  /* 例外：工具栏右侧有「2 个以上」操作按钮时，上面那套 nowrap 单行方案会失效。
     因为 .right 是 flex:0 0 auto、内部 .btn / .toolbar-add-btn 也都是 flex:0 0 auto，都没法压缩，
     单行放不下就整体溢出工具栏、按钮文字被裁掉（知识文章页 =「新增微信公众号文章」+「新增知识文章」，
     实测「新增微信公众号文章」被裁成「微信公众号文章」）。
     处理：这种页面允许换行 —— 搜索框 + 筛选留在第一行，.right 整组落到第二行。
     换行后第一行腾出了空间，顺带把筛选下拉放宽到 32vw 并降到 12px，
     否则「全部文档类型」会被截成「全部…」，两个下拉长得一模一样、分不清谁是谁。
     用 :has() 精确限定「筛选 + 多个操作按钮」，只有这类页面受影响；
     「筛选 + 单个操作按钮」的常规紧凑布局（内容管理 / 密钥管理 / 外部数据源等）保持原样不变。 */
  .list-view .toolbar.toolbar-compact:has(.right > * + *) { flex-wrap: wrap; }
  .list-view .toolbar.toolbar-compact:has(.right > * + *) .toolbar-filters .filter-select { max-width: 32vw; }
  .list-view .toolbar.toolbar-compact:has(.right > * + *) .toolbar-filters .filter-select-label { font-size: 12px; }

  /* 「页面组件」页有两个下拉（角色 / 分组），且该页 .right 常为空
     （超管初始无操作按钮、非超管直接隐藏保存按钮）→ 上面那套 :has(.right > * + *) 例外**不会命中**，
     于是沿用 24vw 上限 → 390px 下「超级管理员」被截成「超级管…」、「全部分组」被截成「全部…」，
     两个下拉长得一模一样，分不清哪个是角色哪个是分组。
     处理：按**页面作用域**放宽（不碰全局 24vw，避免影响内容管理 / 密钥管理 / 外部数据源等页）。
     .page-<key> 类由 console-view 的列表容器给出，仅本页生效。 */
  .page-role_menu_relations .list-view .toolbar.toolbar-compact .toolbar-filters .filter-select { max-width: 32vw; }
  .page-role_menu_relations .list-view .toolbar.toolbar-compact .toolbar-filters .filter-select-label { font-size: 12px; }

  /* 极窄屏（≤360px，如 iPhone SE 1 代）再放宽一档：
     32vw@320 = 102.4px，而「超级管理员」实需 13(pad) + 60(5 字@12px) + 32(箭头位) = 105px，
     差 5px 就会截成「超级管…」。角色名还可能更长，这里留出余量。
     按比例放宽 → 320px 下 115px，搜索框仍保有 ~28vw 可压缩空间。 */
  @media (max-width: 360px) {
    .page-role_menu_relations .list-view .toolbar.toolbar-compact .toolbar-filters .filter-select { max-width: 36vw; }
  }

  /* 移动端所有 toolbar 新建按钮统一收成圆形 + 号并排到最右侧。
     覆盖 list-view 生成按钮（.btn-icon + .btn-text）和各页面手写按钮（svg + 文本节点）。 */
  .toolbar .toolbar-add-btn {
    width: var(--toolbar-h-mobile);
    flex: 0 0 auto;
    padding: 0;
    border-radius: 50%;
    justify-content: center;
    order: 999;
    margin-left: auto;
    gap: 0;
    font-size: 0;
    line-height: 0;
    min-width: 0;
  }
  .toolbar .toolbar-add-btn .btn-text { display: none; }
  .toolbar .toolbar-add-btn svg,
  .toolbar .toolbar-add-btn .btn-icon { margin: 0; }

  /* 移动端 toolbar 刷新按钮统一收成圆形刷新图标。
     覆盖 <refresh-button> 生成按钮，含 text / loadingText / block / toolbar-inline / btn-ghost 等模式。
     注意：仅做「圆形 + 隐藏文字 + 显示图标」；是否推到最右由容器结构决定：
     - 在 list-view 的 .right 内：靠 .right { margin-left:auto } 推到 toolbar 右侧；
     - 在 .toolbar 直子元素（如 员工活码 add+refresh）：再用下方 .toolbar > .refresh-btn 规则顶到最右。 */
  .toolbar .refresh-btn.btn-primary,
  .toolbar .refresh-btn.btn-ghost {
    width: var(--toolbar-h-mobile);
    height: var(--toolbar-h-mobile);
    flex: 0 0 auto;
    padding: 0;
    border-radius: 50%;
    justify-content: center;
    align-items: center;
    gap: 0;
    font-size: 0;
    line-height: 0;
    min-width: 0;
  }
  .toolbar .refresh-btn.btn-primary .rb-text,
  .toolbar .refresh-btn.btn-ghost .rb-text { display: none !important; }
  .toolbar .refresh-btn.btn-primary .rb-icon,
  .toolbar .refresh-btn.btn-ghost .rb-icon { display: inline-flex !important; }
  .toolbar .refresh-btn.btn-primary .rb-icon svg,
  .toolbar .refresh-btn.btn-ghost .rb-icon svg {
    width: 16px;
    height: 16px;
    margin: 0;
  }
  /* 直子元素刷新按钮（add + refresh 同 toolbar）：推到最右侧 */
  .toolbar > .refresh-btn.btn-primary,
  .toolbar > .refresh-btn.btn-ghost {
    order: 1000;
    margin-left: auto;
  }
  /* 当刷新按钮紧跟新建按钮（无 .left/.right 的 toolbar，如群活码、员工活码）时，
     不要再给 refresh 加 margin-left:auto；否则两个按钮会被各自的 auto margin 顶开，
     add 偏左、refresh 偏右。让 add 的 margin-left:auto 把 add+refresh 整个 pair 推到最右，
     refresh 用 order:1000 紧跟 add。 */
  .toolbar > .toolbar-add-btn + .refresh-btn.btn-primary,
  .toolbar > .toolbar-add-btn + .refresh-btn.btn-ghost {
    margin-left: 0;
  }
  /* 三控件紧凑工具栏（.toolbar-inline：搜索+下拉+刷新）：
     用户最终要求「中间不要有空隙，正常地填满整个平」。策略：
     - .left 重新 flex:1，占据 toolbar 除刷新按钮外的剩余宽度；
     - 搜索框在 .left 内 flex:1，自动伸长填满 .left 剩余空间，不再设 34vw 上限（上限会导致 .left 内右侧留白）；
     - 下拉仍 max-width:34vw，避免过宽；
     - 刷新按钮 margin-left:auto 顶到最右，与 .left 之间只保留 toolbar 的 gap。
     这样既不留大空隙，又不会把下拉和刷新挤在一起（下拉有 max-width 兜底）。 */
  .toolbar.toolbar-inline > .left { flex: 1 1 auto; min-width: 0; }
  .toolbar.toolbar-inline .toolbar-search { max-width: none; }
  .toolbar.toolbar-inline .select { max-width: 34vw; }
  .toolbar.toolbar-inline .refresh-btn.btn-primary,
  .toolbar.toolbar-inline .refresh-btn.btn-ghost {
    order: 0 !important;
    margin-left: auto !important;
  }

  /* 移动端弹窗：全屏覆盖，不再留边距（用户确认移动端覆盖整页可接受）
     注意：本文件第 994 行另有第二套 .modal 定义且更靠后，会覆盖上面的 padding，故这里必须加 !important 才能压住 */
  .modal-mask { padding: 0 !important; }
  .modal {
    width: 100% !important; max-width: 100% !important;
    border-radius: 0 !important; min-height: auto !important; max-height: 100vh !important; max-height: 100dvh !important;
    box-shadow: none !important;
    display: flex !important; flex-direction: column !important;
  }
  .modal-head, .modal-foot { padding: 16px !important; flex: 0 0 auto !important; }
  .modal .modal-body { padding: 16px !important; overflow-x: hidden; overflow-y: auto; flex: 0 0 auto !important; max-height: calc(100vh - 150px) !important; max-height: calc(100dvh - 150px) !important; box-sizing: border-box !important; width: 100% !important; }
  .modal-foot { flex-direction: row; }
  .modal-foot .btn { flex: 1; justify-content: center; }
  .field-row { grid-template-columns: 1fr; }
  /* 超级编辑器弹窗移动端：减小编辑器高度确保按钮可触达 + iOS 触摸滚动 + 禁止点击放大 */
  .super-editor .modal-body { -webkit-overflow-scrolling: touch !important; }
  .super-editor .se-editor-mount { min-height: 200px !important; }
  /* iOS：contenteditable 根元素 font-size ≥ 16px 防止自动缩放（不覆盖子元素，保持富文本排版） */
  .cap-edit-grid { grid-template-columns: 1fr !important; }
  .cap-edit-grid .textarea { font-size: 13px; }
  .footer .cols { grid-template-columns: 1fr; }
  .portal-stats { display: grid; grid-template-columns: repeat(3, 1fr); }
  .ps-item { border-right: 1px solid var(--c-line-2); }
  .sub-nav-tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; flex-shrink: 0; padding: 0 var(--m-page-pad); gap: 8px; }
  .sub-nav-tabs a { flex: 0 0 auto; flex-shrink: 0; -webkit-flex-shrink: 0; padding: 16px 20px; font-size: 14px; }
  .relation-chart { height: 360px !important; }
  /* 移动端 toggle switch 放大，便于手指点击 */
  .toggle-switch { width: 56px; height: 30px; }
  .ts-slider { border-radius: 30px; }
  .ts-slider::before { height: 24px; width: 24px; left: 3px; bottom: 3px; }
  .toggle-switch input:checked + .ts-slider::before { transform: translateX(26px); }
  .toggle-label { font-size: 15px; }
  .toggle-state { font-size: 14px; }
}
.side-toggle svg { width: 18px; height: 18px; }
.relation-chart { width: 100%; height: 520px; background: #fafbff; border: 1px solid var(--c-line); border-radius: var(--r-lg); }

/* 页面组件矩阵：可编辑布尔复选框（pill 切换） */
.lv-checkbox { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 22px; cursor: pointer; border-radius: 999px; background: #e2e8f0; transition: background .2s ease, box-shadow .2s ease; }
.lv-checkbox input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.lv-checkbox .lv-checkmark { position: absolute; inset: 0; border-radius: 999px; transition: background .2s ease; pointer-events: none; }
.lv-checkbox:hover { box-shadow: 0 0 0 3px rgba(67,97,238,.12); }
.lv-checkbox.active { background: var(--grad-brand); box-shadow: var(--sh-brand); }
.lv-checkbox .lv-checkmark::after { content: ''; position: absolute; top: 50%; left: 50%; width: 5px; height: 9px; border: solid #fff; border-width: 0 2px 2px 0; transform: translate(-50%, -60%) rotate(45deg); opacity: 0; transition: opacity .15s ease; }
.lv-checkbox.active .lv-checkmark::after { opacity: 1; }
.lv-checkbox input:focus + .lv-checkmark { box-shadow: 0 0 0 3px rgba(67,97,238,.2); }

/* 页面组件矩阵：能力开关（授权 / 6 个操作位 / 启停 / 分页），iOS 风滑动开关。
   🔴 2026-09-18 缩小 + 去光晕。原因：本页一屏 10 行 × 9 个开关 = **90 个同屏**，
   原尺寸 44×24 且 .on 态吃 --grad-brand 渐变 + --sh-brand（0 10px 30px 的蓝色光晕）
   → 整张表像一片发光的蓝胶囊，非常吵。
   现在：尺寸 44×24 → 38×20（knob 18 → 14），.on 态改**扁平单色**、去掉 30px 光晕，
   关态背景压淡到 #e7ebf3。四个尺寸走局部变量，后续想再微调只改这一处。
   ⚠️ .lv-switch 目前**只有「页面组件」这一页在用**（全项目 type:'switch' 的 9 处定义
   全在 role_menu_relations 的 cols 里；其它页的布尔列走 .lv-checkbox）→ 直接改全局定义
   即可，不存在波及其它列表页的问题。 */
.lv-switch {
  --lv-sw-w: 38px; --lv-sw-h: 20px; --lv-sw-knob: 14px; --lv-sw-pad: 3px;
  position: relative; display: inline-flex; width: var(--lv-sw-w); height: var(--lv-sw-h);
  padding: 0; border: none; border-radius: 999px; background: #e7ebf3; cursor: pointer;
  vertical-align: middle; transition: background .2s ease, box-shadow .2s ease;
}
.lv-switch .lv-switch-knob { position: absolute; top: var(--lv-sw-pad); left: var(--lv-sw-pad); width: var(--lv-sw-knob); height: var(--lv-sw-knob); border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(15,23,42,.18); transition: transform .2s ease; }
/* 滑动行程 = 38 - 3(左内边距) - 14(knob) - 3(右内边距) = 18px */
.lv-switch.on .lv-switch-knob { transform: translateX(18px); }
.lv-switch.on { background: var(--c-primary); box-shadow: 0 1px 2px rgba(79,70,229,.32); }
.lv-switch:hover { box-shadow: 0 0 0 3px rgba(67,97,238,.12); }
.lv-switch.on:hover { box-shadow: 0 0 0 3px rgba(67,97,238,.16), 0 1px 2px rgba(79,70,229,.32); }
.lv-switch:focus { outline: none; box-shadow: 0 0 0 3px rgba(67,97,238,.2); }
/* 触屏设备（手机 / 平板）：开关恢复 44×24。
   38×20 在桌面上用鼠标点没问题，但手指点按高度只有 20px、窄于 24px 的可点下限，
   表格里一格一格点很容易点错行。宽度不变(38)只加高：knob 回到 18，行程 38-3-18-3 = 14px。 */
@media (hover: none), (pointer: coarse) {
  .lv-switch { --lv-sw-h: 24px; --lv-sw-knob: 18px; }
  .lv-switch.on .lv-switch-knob { transform: translateX(14px); }
}

/* ── 「页面组件」权限矩阵：分区竖线（2026-09-18）──
   ⚠️ 这里**只加竖线，不碰宽度**。曾经试过「切 table-layout:fixed 让声明的列宽真生效 +
   整页 max-width:900px 限宽」，结果是卡片右侧空出一大片（内容区 1272px、卡片只有 876px），
   用户 2026-09-18 明确否掉：「左右的宽度不需要定义，还是按照原来的，右边空那么多」。
   → 回到原来的 table-layout:auto 满宽铺开（12 列均摊，约 96px/列）。
   根因备查：auto 布局 + `min-width:680px` 会把多余空间按列分掉，所以 `cols[].width`
   在列少时是**不起作用**的；想要列宽真生效就必须配 fixed + 吸收列 + 限宽，
   而限宽又必然留白 —— 二者不可兼得，已按用户口径选「满宽」。
   竖线与宽度无关，可以留：矩阵 12 列里 9 个开关长得一模一样，没有分区线时很难定位到某一列。
   分区：① 身份（#/页面/一级菜单）② 授权 + 6 个操作位 ③ 页面级开关（启停/分页）。 */
@media (min-width: 1024px) {
  .page-role_menu_relations .table-wrap table.data th:nth-child(4),
  .page-role_menu_relations .table-wrap table.data td:nth-child(4),
  .page-role_menu_relations .table-wrap table.data th:nth-child(11),
  .page-role_menu_relations .table-wrap table.data td:nth-child(11) {
    border-left: 1px solid var(--c-line);
  }
  /* 「全部层级」筛选框加宽（140 → 176）：
     钻取后文案是「一级 · 产融生态入口」这种「轴 + 值」的形式，9 个字符 ≈ 125px，
     140px 的框去掉 13+32 的内边距只剩 95px → 会被省略号截成「一级 · 产融生…」。
     本页筛选从 3 个减到 2 个，腾出来的宽度正好够。
     ⚠️ 只加宽本页（.page- 作用域），不动全局 140px；窄屏仍走移动端那套 32vw 上限。 */
  .page-role_menu_relations .list-view .toolbar .toolbar-filters .filter-select-trigger {
    min-width: 176px;
  }
}
/* 只读态：整页只读（list-view editable=false）或单格置灰（col.disabledWhen）。
   去掉 hover 阴影与手型，压到 45% 透明度 —— 仍要能看清当前是开还是关。 */
.lv-switch:disabled, .lv-checkbox.disabled { opacity: .45; cursor: not-allowed; }
.lv-switch:disabled:hover, .lv-checkbox.disabled:hover { box-shadow: none; }
.lv-checkbox.disabled input { cursor: not-allowed; }
.relation-chart.relation-chart-flat { background: transparent; border: none; border-radius: 0; }

/* ============================================================
   自定义面板（开发工具 / 认证管理）
   ============================================================ */
.subtabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
.subtab {
  border: 1px solid var(--c-line); background: #fff; color: var(--c-ink-2);
  padding: 8px 16px; border-radius: var(--r-full); font-weight: 600; font-size: 13.5px; cursor: pointer;
  transition: all .15s ease;
}
.subtab:hover { border-color: var(--c-primary); color: var(--c-primary); }
.subtab.active { background: var(--grad-brand); color: #fff; border-color: transparent; box-shadow: var(--sh-brand); }

.kv { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; }
.kv > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; overflow: hidden; }
.kv span { font-size: 12px; color: var(--c-ink-4); }
.kv b { font-size: 14px; color: var(--c-ink); }
/* 能力详情页元信息行：文档地址/版本 改为横向 inline 元信息条，避免单字段时竖排孤悬 + 右侧大片空白（仅作用于详情页，不动 panels.js 的 2 列网格） */
.api-sec .kv { display: flex; flex-wrap: wrap; gap: 8px 22px; margin-top: 14px; }
.api-sec .kv > div { display: flex; flex-direction: row; align-items: baseline; gap: 6px; }
.api-sec .kv .kv-doc-wrap { max-width: 100%; }
.cap-doc-url {
  display: inline-block;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 13px;
  vertical-align: bottom;
}

.proxy-result {
  font-family: var(--font-mono); font-size: 12.5px; background: #0f172a; color: #e2e8f0;
  padding: 14px; border-radius: var(--r-md); overflow-x: auto; margin-top: 12px; max-height: 320px;
}

/* HTTP 方法徽标配色 */
.method.get { background: #eef2ff; color: #4338ca; }
.method.post { background: #ecfdf5; color: #047857; }
.method.put { background: #fffbeb; color: #b45309; }
.method.delete { background: #fef2f2; color: #b91c1c; }
.method.patch { background: #f5f3ff; color: #6d28d9; }

/* 可排序表头 */
.col-head.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.col-head.sortable:hover { background: var(--c-line-2); }
.col-head.sortable .col-label { margin-right: 2px; }
.sort-ind { display: inline-block; margin-left: 4px; font-size: 10px; color: var(--c-info); vertical-align: middle; }

/* 单元格：强调主文本 */
.cell-strong { font-weight: var(--row-text-weight); color: var(--c-ink); font-size: 14px; line-height: 1.3; }

/* ════════════════════════════════════
   控制台仪表盘 (Dashboard)
   ════════════════════════════════════ */
.dash-welcome {
  display: flex; align-items: center; justify-content: space-between;
  background: linear-gradient(135deg, #1a3a6b 0%, #2d5a9e 100%);
  color: #fff; border-radius: 10px; padding: 22px 24px;
  margin-bottom: 20px;
}
.dash-welcome h2 { color: #fff; }
.dash-welcome p { color: rgba(255,255,255,.7); }
.dash-welcome-actions { display: flex; gap: 8px; flex-shrink: 0; }
.dash-welcome-actions .btn-primary { background: rgba(255,255,255,.15); color: #fff; border: 1px solid rgba(255,255,255,.25); }
.dash-welcome-actions .btn-primary:hover { background: rgba(255,255,255,.25); }

.quick-tile {
  display: flex; align-items: center; gap: 8px; padding: 14px 16px;
  background: var(--c-surface); border: 1px solid var(--c-line-2);
  border-radius: 8px; text-decoration: none; color: var(--c-ink-1);
  font-size: 13.5px; font-weight: 500; transition: all .15s;
}
.quick-tile:hover { border-color: var(--c-info); color: var(--c-info); transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,.06); }

.dash-overview { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.dash-item { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--c-line-2); font-size: 13px; }
.dash-item:last-child { border-bottom: none; }
.dash-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.dash-dot.info { background: var(--c-info); } .dash-dot.success { background: #22c55e; }
.dash-dot.beta { background: #f59e0b; } .dash-dot.warning { background: #f97316; }
.dash-dot.online { background: #22c55e; } .dash-dot.danger { background: #f97316; }
.dash-label { color: var(--c-ink-3); flex: 1; }

/* ════════════════════════════════════
   个人中心 (Profile)
   ════════════════════════════════════ */
.profile-header { display: flex; align-items: center; gap: 18px; padding: 8px 0; }
.avatar-lg { width: 56px; height: 56px; line-height: 56px; text-align: center;
  border-radius: 14px; background: linear-gradient(135deg,#E87722,#1a3a6b);
  color: #fff; font-size: 22px; font-weight: 700; flex-shrink: 0; }
.profile-info h3 { font-size: 17px; }
.profile-info p { margin: 2px 0 !important; }
.profile-header.compact { padding: 4px 0; }
.profile-title-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-bottom: 4px; }
.profile-title-row h3 { margin: 0; line-height: 1.2; }
.profile-title-row .cell-sub { font-size: 12px; color: var(--c-ink-3); }
.profile-contact-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 18px; color: var(--c-ink-3); font-size: 13px; }
.profile-update-wrap { display: inline-flex; }

@media (max-width: 768px) {
  .profile-update-wrap { display: flex; width: 100%; }
  .profile-update-wrap .refresh-btn { flex: 1; width: 100%; justify-content: center; padding: 10px 0; }
  .profile-update-wrap .refresh-btn > span + .rb-text { margin-left: 6px; }
}

/* ════════════════════════════════════
   消息中心 (Messages)
   ════════════════════════════════════ */
.msg-list { display: flex; flex-direction: column; gap: 10px; }
.msg-item { display: flex; gap: 12px; padding: 14px 16px; background: var(--c-surface);
  border: 1px solid var(--c-line-2); border-radius: 8px; cursor: pointer;
  transition: background .2s, border-color .2s, opacity .3s; }
.msg-item:hover { background: var(--c-line-1); }
.msg-item.unread { border-left: 3px solid var(--c-info); background: #eff6ff; }
.msg-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--c-primary);
  flex-shrink: 0; margin-top: 6px; transition: opacity .25s, transform .25s; }
.msg-body { flex: 1; min-width: 0; }
.msg-title { font-size: 14px; font-weight: 600; color: var(--c-ink-1); margin-bottom: 3px; transition: opacity .2s; }
.msg-item:not(.unread) .msg-title { font-weight: 500; color: var(--c-ink-2); }
.msg-item:not(.unread) .msg-desc { color: var(--c-ink-4); }
.msg-desc { font-size: 12.5px; color: var(--c-ink-3); line-height: 1.6; margin-bottom: 5px; }
.msg-meta { display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--c-ink-4); }

/* Header avatar link */
.avatar-link { text-decoration: none; cursor: pointer; display: inline-flex; align-items: center; }

/* 问题反馈入口：外圆与头像同尺寸，仅放大内部问号并保持橙色醒目 */
.avatar-feedback {
  background: #f97316;
  color: #fff;
  font-weight: 700;
  font-size: 18px;
  box-shadow: 0 2px 8px rgba(249, 115, 22, 0.35);
}
.avatar-feedback:hover { background: #ea670c; box-shadow: 0 3px 10px rgba(249, 115, 22, 0.45); }

/* 顶部租户切换 */
.tenant-switcher { position: relative; display: inline-flex; }
.tenant-switcher-btn {
  display: inline-flex; align-items: center; gap: 6px; position: relative;
  height: 34px; padding: 0 16px 0 20px;
  background: #fff; border: 1px solid var(--c-line); border-radius: 999px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  transition: all .15s ease;
}
.tenant-switcher-btn:hover { border-color: var(--c-primary); box-shadow: 0 2px 6px rgba(67,97,238,0.10); }
.tenant-switcher.open .tenant-switcher-btn { border-color: var(--c-primary); box-shadow: 0 0 0 3px rgba(67,97,238,0.08); }
.tenant-switcher-btn .tenant-label-pc {
  font-size: 13.5px; font-weight: 600; color: var(--c-ink);
  max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tenant-switcher-btn .avatar-tenant-mobile {
  display: none; width: 34px; height: 34px; border-radius: 50%;
  background: var(--c-primary); color: #fff; flex-direction: column;
  align-items: center; justify-content: center; line-height: 1;
  padding: 2px; box-sizing: border-box;
}
.tenant-switcher-btn .avatar-tenant-mobile .tenant-abbr-row {
  font-size: 10px; font-weight: 700; transform: scale(0.92);
  line-height: 1.2; letter-spacing: 0.5px;
}
.tenant-switcher-btn .tenant-arrow { width: 14px; height: 14px; color: var(--c-ink-3); display: grid; place-items: center; }
.tenant-switcher.open .tenant-arrow { transform: rotate(180deg); color: var(--c-primary); }
.tenant-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 220px;
  background: #fff;
  border: 1px solid var(--c-line);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  padding: 8px;
  z-index: 100;
}
.tenant-dropdown-header {
  font-size: 12px;
  color: var(--c-ink-4);
  padding: 4px 8px 8px;
  border-bottom: 1px solid var(--c-line);
  margin-bottom: 4px;
}
.tenant-dropdown a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: var(--r-md);
  color: var(--c-ink);
  text-decoration: none;
  font-size: 13.5px;
}
.tenant-dropdown a:hover { background: var(--c-bg); }
.tenant-dropdown a.active { background: rgba(67,97,238,0.08); color: var(--c-primary); font-weight: 600; }
.tenant-dropdown .tenant-dot {
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--c-bg); color: var(--c-ink-3);
  display: grid; place-items: center; font-size: 11px; font-weight: 700;
}
.tenant-dropdown a.active .tenant-dot { background: var(--c-primary); color: #fff; }
.tenant-dropdown .tenant-empty { padding: 12px 8px; color: var(--c-ink-4); font-size: 13px; text-align: center; }

@media (max-width: 768px) {
  .tenant-switcher-btn { padding: 0; width: 34px; height: 34px; border-radius: 50%; justify-content: center; border: none; background: transparent; box-shadow: none; }
  .tenant-switcher-btn:hover, .tenant-switcher.open .tenant-switcher-btn { background: transparent; box-shadow: none; border-color: transparent; }
  .tenant-switcher-btn .tenant-label-pc { display: none; }
  .tenant-switcher-btn .avatar-tenant-mobile { display: flex; }
  .tenant-switcher-btn .tenant-arrow { display: none; }
  .tenant-switcher .tenant-dropdown {
    position: fixed;
    top: 68px;
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    width: 260px;
    min-width: auto;
  }
}

/* 必填星号 */
.req { color: var(--c-required); font-weight: 700; margin-left: 2px; }

/* 问题反馈弹窗：截图上传与预览 */
.feedback-upload { display: flex; flex-direction: column; gap: 8px; }
.feedback-upload-zone {
  position: relative;
  min-height: 110px;
  border: 2px dashed var(--c-line);
  border-radius: var(--r-md);
  background: var(--c-bg);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; transition: all .18s ease; overflow: hidden;
}
.feedback-upload-zone:hover { border-color: var(--c-primary); background: #f5f7ff; }
.feedback-upload-zone.disabled { opacity: .6; cursor: not-allowed; }
.feedback-upload-zone input[type="file"] {
  position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%;
}
.feedback-upload-zone.disabled input[type="file"] { cursor: not-allowed; }
.feedback-upload-placeholder {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 18px; color: var(--c-ink-3); text-align: center;
}
.feedback-upload-icon {
  width: 40px; height: 40px; border-radius: 50%;
  background: #fff; color: var(--c-primary); font-size: 22px; line-height: 40px;
  box-shadow: var(--sh-sm); font-weight: 300;
}
.feedback-upload-text { font-size: 14px; font-weight: 600; color: var(--c-ink-2); }
.feedback-upload-hint { font-size: 12px; color: var(--c-ink-4); }
.feedback-preview {
  max-width: 100%; max-height: 220px; border-radius: var(--r-md);
  object-fit: contain; padding: 10px; box-sizing: border-box;
}
.feedback-remove-img {
  align-self: flex-start;
  border: none; background: transparent; color: var(--c-danger);
  font-size: 13px; font-weight: 600; cursor: pointer; padding: 4px 0;
}
.feedback-remove-img:hover { text-decoration: underline; }

/* 问题反馈弹窗：顶部说明 banner 加宽行距与内边距，避免文字拥挤 */
.feedback-cap-banner {
  padding: 18px 20px;
  border-radius: var(--r-lg);
  background: linear-gradient(120deg, rgba(79,70,229,.06), rgba(16,163,74,.06));
}
.feedback-cap-banner p {
  font-size: 14px;
  line-height: 1.85;
  margin: 0;
  color: var(--c-ink-3);
}
.feedback-cap-banner p b { font-size: 14px; color: var(--c-primary); }
.feedback-modal .field { margin-bottom: 18px; }
.feedback-modal .field label {
  font-size: 14px;
  margin-bottom: 8px;
  color: var(--c-ink);
}

/* 问题反馈弹窗移动端优化 */
@media (max-width: 768px) {
  .modal.feedback-modal .modal-head { padding: 14px 16px; }
  .modal.feedback-modal .modal-body { padding: 14px 16px; }
  .modal.feedback-modal .modal-foot { padding: 12px 16px; }
  .feedback-upload-zone { min-height: 100px; }
  .feedback-preview { max-height: 160px; }
  .feedback-cap-banner { padding: 16px 18px; }
  .feedback-cap-banner p { font-size: 13.5px; line-height: 1.8; }
}

/* ════════════════════════════════════
   密钥管理：紧凑表格专用样式
   ════════════════════════════════════ */

/* 密钥名称 + 租户（两行合并列） */
.key-name-cell { line-height: 1.45; min-width: 0; }
.key-name-cell .cell-strong { display: block; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 200px; }
.key-name-cell .cell-sub { display: block; font-size: 12px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 200px; }

/* App Key 截断显示 */
.key-mono { font-family: var(--font-mono); font-size: 12.5px; background: var(--c-line-2); padding: 3px 8px; border-radius: 6px; cursor: default; display: inline-block; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.key-mono:hover { background: #e2e8f0; }

/* 权限紧凑标签 */
.tag-xs { font-size: 11px !important; padding: 1px 7px !important; margin: 1px 3px 1px 0 !important; border-radius: 4px !important; }

/* IP 限制图标 */
.ip-lock { font-size: 11px; cursor: help; opacity: .7; margin-left: 2px; }
.ip-lock:hover { opacity: 1; }

/* 调用次数 + 日限额（进度条可视化） */
.usage-cell { min-width: 100px; }
.usage-num { font-size: 13px; font-weight: 600; color: var(--c-ink); }
.usage-bar { width: 100%; height: 5px; background: var(--c-line-2); border-radius: 3px; margin-top: 4px; overflow: hidden; }
.usage-fill { height: 100%; border-radius: 3px; transition: width .3s ease; }

/* ════════════════════════════════════
   通用能力中心 (CapabilityCenter)
   ════════════════════════════════════ */
.capability-card {
  display: flex; flex-direction: column;
  padding: 18px; min-height: 230px;
  transition: transform .15s, box-shadow .15s, border-color .15s;
}
.capability-card:hover { transform: translateY(-2px); box-shadow: var(--sh-md); border-color: var(--c-primary-l); }
.capability-card.ready { border-left: 3px solid var(--c-success); }
.capability-card:not(.ready) { border-left: 3px solid var(--c-ink-4); opacity: .96; }

.capability-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.capability-icon { width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center;
  background: rgba(79,70,229,.08); border-radius: 10px; color: var(--c-primary); }
.capability-icon .ic { width: 22px; height: 22px; }

.capability-title { font-size: 15px; font-weight: 700; color: var(--c-ink); margin-bottom: 8px; }
.capability-desc { font-size: 13px; color: var(--c-ink-3); line-height: 1.6; flex: 1; margin-bottom: 12px; }
.capability-meta { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.capability-tag { font-size: 11px; color: var(--c-ink-3); background: var(--c-line-2); padding: 3px 8px; border-radius: var(--r-full); }
.capability-footer { display: flex; gap: 8px; margin-top: auto; }
.capability-footer .btn:disabled { cursor: not-allowed; opacity: .6; }

.settlement-item { padding: 14px; background: var(--c-bg); border-radius: var(--r-md); }
.settlement-item .n { font-size: 22px; font-weight: 800; color: var(--c-primary); margin-bottom: 4px; }
.settlement-item .l { font-size: 14px; font-weight: 600; color: var(--c-ink); margin-bottom: 6px; }
.settlement-item .d { font-size: 12.5px; color: var(--c-ink-3); line-height: 1.6; }

/* 能力面板：顶部原则 banner */
.cap-banner { display: flex; gap: 16px; align-items: flex-start; padding: 20px 24px;
  background: linear-gradient(120deg, rgba(79,70,229,.08), rgba(16,163,74,.08));
  border: 1px solid var(--c-line); border-radius: 12px; }
.cap-banner-ic { flex: 0 0 auto; width: 46px; height: 46px; border-radius: 12px;
  background: var(--c-primary); color: #fff; display: inline-flex; align-items: center; justify-content: center; }
.cap-banner-ic .ic { width: 26px; height: 26px; }
.cap-banner b { font-size: 18px; color: var(--c-ink); }
.cap-banner p { margin: 6px 0 0; font-size: 13.5px; color: var(--c-ink-3); line-height: 1.7; }
.cap-banner p b { font-size: 13.5px; color: var(--c-primary); }

/* 能力面板：API 文档详情视图（腾讯云风格） */
.api-doc { margin-top: 4px; }
.api-doc-head { padding: 16px 24px 14px; position: relative; }
/* 能力/技能中心详情返回按钮：默认隐藏，仅移动端由 .market-center-page 媒体查询显示 */
.market-center-page .api-doc-header .m-back-btn { display: none; }
.api-doc-head h2 { font-size: 22px; font-weight: 700; color: var(--c-ink); }
.api-doc-head .cell-sub { margin-top: 8px; }
.api-doc-head .cap-edit,
.api-doc-head .cap-doc {
  width: 28px; height: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 6px; border: 1px solid transparent;
  background: transparent; color: var(--c-ink-3); opacity: .65;
  transition: opacity .15s, border-color .15s, color .15s, background .15s;
}
.api-doc-head .cap-edit svg,
.api-doc-head .cap-doc svg { width: 14px; height: 14px; }
.api-doc-head .cap-edit:hover,
.api-doc-head .cap-doc:hover { opacity: 1; border-color: var(--c-line); color: var(--c-primary); background: #f8fafc; }

/* 能力详情页头部卡片 */
.api-doc-header-row { display: flex; align-items: center; gap: 12px; flex-wrap: nowrap; }
.api-doc-header-row h2 { flex: 1 1 auto; min-width: 0; }
.api-doc-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex: 0 0 auto; }
.api-doc-meta-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.api-doc-header {
  padding: 22px 24px 18px;
  position: relative;
  border-bottom: 1px solid var(--c-line);
  background: linear-gradient(180deg, rgba(79,70,229,.03), transparent 90px);
}
.api-doc-header .api-doc-title {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}
.api-doc-header .api-doc-title-text {
  flex: 1;
  min-width: 0;
}
.api-doc-header .api-doc-title-text h2 {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--c-ink);
  line-height: 1.25;
}
.api-doc-header .api-doc-title-text .cell-sub {
  margin-top: 6px;
  font-size: 13px;
  color: var(--c-ink-3);
}
.api-doc-header .cap-edit {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 30px;
  height: 30px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  border: 1px solid var(--c-line);
  background: #fff;
  color: var(--c-ink-2);
  opacity: .85;
  transition: opacity .15s, border-color .15s, color .15s, background .15s;
}
.api-doc-header .cap-edit:hover {
  opacity: 1;
  border-color: var(--c-primary);
  color: var(--c-primary);
  background: rgba(79,70,229,.05);
}
.api-doc-header .cap-edit svg { width: 14px; height: 14px; }
.api-doc-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 18px;
}
.api-doc-meta .link { font-size: 12.5px; margin-left: 2px; }
.api-doc-header .cap-invoke-box { margin-top: 18px; }
.api-doc-header .cap-invoke-title { margin-bottom: 6px; }

.api-call-pill { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 999px; background: rgba(79,70,229,.08); color: var(--c-primary); font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.api-call-pill .ic { width: 14px; height: 14px; }
.api-call-pill b { font-size: 13.5px; font-weight: 800; }

/* 「未接入」态：把 0 换成灰底文案。
   0 会被读成"被调用了 0 次"，而真相是"平台根本没实现，不可能有记录"（2026-09-19 用户即据此误判成日志坏了） */
.api-call-pill-off { background: rgba(100,116,139,.10); color: var(--c-ink-2); font-weight: 500; }
.api-call-pill-off b { font-weight: 600; color: var(--c-ink); }

/* 「规划中/未接入」条目的说明条：与 .lv2-devnote / .wxbind-devnote 同一套警示观感 */
.cap-note-warn { margin: 10px 0 0; padding: 9px 12px; border-radius: 10px; background: var(--c-warning-bg); border: 1px solid var(--c-warning-line); font-size: 12.5px; line-height: 1.65; color: var(--c-warning-ink); }
.cap-note-warn b { color: var(--c-warning-ink-strong); }
.api-key-pill { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 999px; background: rgba(100,116,139,.10); color: var(--c-ink-2); font-size: 12px; font-weight: 500; font-family: var(--font-mono); white-space: nowrap; cursor: help; }
.api-key-pill .ic { width: 13px; height: 13px; }
.api-key-pill b { font-weight: 700; color: var(--c-ink); }
.cap-invoke-actions { margin-top: 10px; }
.cap-tag { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 999px; font-size: 11.5px; font-weight: 600; vertical-align: middle; }
.cap-tag-self { background: rgba(16,185,129,.12); color: #0f9d6b; }
.cap-tag-ext { background: rgba(245,158,11,.14); color: #b9740a; }
/* （已删除此处重复的 .modal/.modal-mask/.modal-head/.modal-body 定义——统一收敛到上方 525 行的唯一基础定义，避免无 inline 宽度的弹窗静默 fallback 到 620px） */
.field-label { display: block; font-size: 12px; font-weight: 600; color: var(--c-ink-2); margin: 12px 0 6px; }
.debug-input { width: 100%; box-sizing: border-box; font-family: var(--font-mono); font-size: 12.5px; line-height: 1.5; padding: 10px 12px; border: 1px solid var(--c-border); border-radius: 8px; background: var(--c-bg); color: var(--c-ink); resize: vertical; }
.debug-actions { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.debug-result { margin-top: 14px; }
.debug-json { background: var(--c-bg); border: 1px solid var(--c-border); border-radius: 8px; padding: 12px; font-family: var(--font-mono); font-size: 12.5px; line-height: 1.55; color: var(--c-ink); white-space: pre-wrap; word-break: break-all; margin: 0; max-height: 320px; overflow: auto; }
.debug-error { margin-top: 12px; padding: 10px 12px; border-radius: 8px; background: rgba(239,68,68,.08); color: #dc2626; font-size: 12.5px; font-family: var(--font-mono); white-space: pre-wrap; word-break: break-all; }
.cap-invoke-box { margin-top: 12px; padding: 12px 14px; border: 1px solid rgba(79,70,229,.25); background: rgba(79,70,229,.06); border-radius: 10px; }
.cap-invoke-title { font-size: 12px; font-weight: 700; color: #4f46e5; margin-bottom: 8px; letter-spacing: .02em; }
.cap-invoke-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cap-method { font-size: 11px; font-weight: 700; color: #fff; background: #4f46e5; padding: 3px 8px; border-radius: 6px; }
.cap-path { font-family: var(--font-mono); font-size: 13px; color: var(--c-ink); background: rgba(255,255,255,.6); padding: 4px 8px; border-radius: 6px; border: 1px solid rgba(79,70,229,.2); word-break: break-all; max-width: 100%; }
.api-doc-body { display: flex; gap: 18px; margin-top: 14px; padding: 0 24px 16px; align-items: flex-start; }
.api-doc-main { flex: 1; min-width: 0; }
.api-sec { margin-bottom: 24px; scroll-margin-top: 16px; }
.api-sec h3 { font-size: 15px; font-weight: 700; color: var(--c-ink); margin: 0 0 12px; padding-left: 10px; border-left: 3px solid var(--c-primary); }
.api-sec > p { color: var(--c-ink-2); font-size: 13.5px; line-height: 1.7; }
/* API 详情页表格：覆盖全局首列 42px 的序号列样式，避免参数名称被挤压截断 */
.api-sec .data th:first-child,
.api-sec .data td:first-child { width: auto; min-width: 120px; text-align: left; }
.api-sec .data th,
.api-sec .data td { white-space: normal; }
/* 能力详情页表格：独立滚动容器，不被全局 freeze-cols 的固定列样式污染 */
.cap-detail-table { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cap-detail-table table.data { min-width: 100%; }
.cap-detail-table .data th,
.cap-detail-table .data td { white-space: normal; word-break: break-word; }
.api-doc-toc { width: 150px; flex: 0 0 auto; position: sticky; top: 16px; align-self: flex-start;
  padding: 14px 14px; background: #f8fafc; border: 1px solid var(--c-line); border-radius: 10px; }
/* 能力中心详情页：PC 显示表格，移动端显示参数卡片 */
.desktop-only { display: block; }
.mobile-only { display: none; }
.api-doc-subline { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-top: 10px; }
.api-doc-toc .toc-item { display: block; padding: 6px 8px; font-size: 12.5px; color: var(--c-ink-3);
  border-radius: 6px; cursor: pointer; line-height: 1.4; }
.api-doc-toc .toc-item:hover { background: #eef2f7; color: var(--c-primary); }
.code-block { background: #0f172a; color: #e2e8f0; border-radius: 8px; padding: 14px 16px;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.6; overflow-x: auto; white-space: pre; margin: 0; }
.api-example { margin-bottom: 14px; }
.api-example-title { font-size: 12px; color: #64748b; margin-bottom: 6px; font-weight: 600; }
@media (max-width: 880px) {
  .api-doc-body { flex-direction: column; }
  .api-doc-toc { width: 100%; position: static; }
  .desktop-only { display: none !important; }
  .mobile-only { display: block; }
  .api-doc-header-row { position: relative; flex-wrap: nowrap; gap: 10px; align-items: flex-start; }
  .api-doc-header-row h2 { flex: 1 1 auto; padding-right: 88px; font-size: 18px; line-height: 1.35; box-sizing: border-box; margin: 0; }
  .api-doc-actions { position: absolute; right: 0; top: 0; display: flex; gap: 8px; }
  .api-doc-header-row .cap-edit,
  .api-doc-header-row .cap-doc { position: static; margin-left: 0; }
  .api-doc-subline { gap: 8px 10px; margin-top: 10px; }
  .api-doc-meta-row { gap: 8px; margin-top: 10px; }
  .api-key-pill { font-size: 11px; padding: 3px 8px; }
  .kv { grid-template-columns: 1fr; }
  .kv-doc-wrap { grid-column: 1 / -1; }
  .cap-doc-url { font-size: clamp(11px, 3.4vw, 13px); }
  .cap-invoke-box { padding: 10px 12px; }
  .cap-invoke-row { gap: 6px; }
  .cap-method { padding: 2px 6px; font-size: 10px; }
  .cap-path { font-size: 11px; padding: 3px 6px; }
  .code-block { font-size: 11px; padding: 10px 12px; }
  .api-sec h3 { font-size: 14px; margin-bottom: 10px; }
  /* 移动端参数卡片：每个参数独立成卡，描述自动换行不截断 */
  .cap-param-cards { display: flex; flex-direction: column; gap: 10px; }
  .cap-param-card { background: #fff; border: 1px solid var(--c-line); border-radius: 10px; padding: 12px 14px; box-shadow: 0 1px 2px rgba(0,0,0,.03); }
  .cap-param-card .cap-param-name { font-family: var(--font-mono); font-size: 13px; font-weight: 600; color: var(--c-ink); word-break: break-all; margin-bottom: 6px; }
  .cap-param-card .cap-param-meta { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
  .cap-param-card .cap-param-meta .cell-sub { font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px; }
  .cap-param-card .cap-param-desc { font-size: 12.5px; color: var(--c-ink-2); line-height: 1.6; word-break: break-word; }
}

/* 能力面板：左侧树形导航（腾讯云文档风格） */
/* 顶部面包屑/二级导航统一高度：与 .topbar 形成更稳的层级，避免能力中心等页面显得过矮 */
.cap-crumb { display:flex; align-items:center; gap:8px; font-size:13.5px; font-weight:500; color:var(--c-ink-4);
  height: 48px; min-height: 48px; box-sizing: border-box; padding: 0 20px; background: #fff; border-bottom: 1px solid var(--c-line);
  margin: 0; flex-wrap:wrap; }
.cap-crumb-item { cursor:pointer; color:var(--c-ink-3); transition:color .15s; padding: 15px 0; line-height: 1; }
.cap-crumb-item:hover { color:var(--c-primary); }
.cap-crumb-item.current { color:var(--c-ink); font-weight:600; cursor:default; }
.cap-crumb-sep { color:var(--c-ink-4); }

/* 内容区内层级面包屑（由页面级 cap-crumb 降级而来，根分类已由顶部导航条显示） */
.cap-breadcrumb { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--c-ink-4);
  padding: 12px 20px 4px; margin: 0; flex-wrap:wrap; }

.cap-layout { display:flex; gap:24px; align-items:flex-start; padding: 0 var(--m-page-pad) var(--m-page-pad); }
.cap-tree { width:236px; flex:0 0 auto; position:sticky; top:0; background:#fff; border:1px solid var(--c-line); border-radius:var(--r-lg); padding:22px; max-height:calc(100vh - 70px); overflow-y:auto; }
.cap-tree-item { display:flex; align-items:center; gap:10px; padding:10px 14px; border-radius:8px; cursor:pointer; font-size:13.5px; font-weight:600; color:var(--c-ink-2); transition:all .15s; user-select:none; }
.cap-tree-item:hover { background:var(--c-bg); color:var(--c-primary); }
.cap-tree-item.active { background:rgba(79,70,229,.1); color:var(--c-primary); }
.cap-tree-ic { width:18px; height:18px; flex:0 0 auto; opacity:.85; display:inline-flex; align-items:center; justify-content:center; }
.cap-tree-ic .ic { width:18px; height:18px; }
.cap-tree-label { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.cap-tree-caret { width:14px; height:14px; color:var(--c-ink-4); flex:0 0 auto; }
.cap-tree-caret .ic { width:14px; height:14px; }
.cap-tree-children { padding:4px 0 4px 12px; margin:2px 0 6px 14px; border-left:1px solid var(--c-line); }
.cap-tree-leaf { display:flex; align-items:center; gap:8px; padding:7px 10px; border-radius:7px; cursor:pointer; font-size:13px; color:var(--c-ink-2); transition:all .15s; user-select:none; }
.cap-tree-leaf:hover { background:var(--c-bg); color:var(--c-primary); }
.cap-tree-leaf.active { background:rgba(79,70,229,.12); color:var(--c-primary); font-weight:600; }
.cap-tree-leaf-ic { width:18px; height:18px; flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center; color:var(--c-ink); }
.cap-tree-leaf-ic .ic { width:18px; height:18px; }
.cap-tree-leaf:hover .cap-tree-leaf-ic { color:var(--c-primary); }
.cap-tree-leaf.active .cap-tree-leaf-ic { color:var(--c-primary); }
.cap-tree-leaf-label { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.cap-tree-badge { font-size:11px; padding:1px 7px; border-radius:10px; background:#f1f5f9; color:#64748b; flex:0 0 auto; }
.cap-tree-badge.online { background:#ecfdf5; color:#047857; }
.cap-tree-dot { width:7px; height:7px; border-radius:50%; flex:0 0 auto; }
.cap-tree-dot.online { background:var(--c-success); }
.cap-tree-dot.planned { background:var(--c-ink-4); }
.cap-tree-dot.offline { background:#f59e0b; }
.cap-tree-empty { font-size:12px; color:var(--c-ink-4); padding:6px 10px; }
.cap-tree-node { display:flex; align-items:center; gap:8px; padding:7px 10px; border-radius:7px; cursor:pointer; font-size:13px; color:var(--c-ink-2); transition:all .15s; user-select:none; }
.cap-tree-node:hover { background:var(--c-bg); color:var(--c-primary); }
.cap-tree-node.active { background:rgba(79,70,229,.12); color:var(--c-primary); font-weight:600; }
.cap-tree-node.open { color:var(--c-primary); }
.cap-tree-node:hover .cap-tree-leaf-ic { color:var(--c-primary); }
.cap-tree-node.active .cap-tree-leaf-ic { color:var(--c-primary); }
.cap-tree-nested { padding:2px 0 4px 12px; margin:0 0 4px 14px; border-left:1px solid var(--c-line); }
.cap-tree-nested .cap-tree-leaf { padding:6px 10px; font-size:12.5px; }
.cap-tree-nested .cap-tree-leaf.no-ic { padding-left:10px; gap:8px; }
.cap-content { flex:1; min-width:0; }
.cap-cards { display:grid; grid-template-columns:repeat(auto-fill,minmax(270px,1fr)); gap:18px; }
.cap-content > .card { padding: 22px; }
.cap-content .toolbar { margin-bottom: 22px; }

/* 能力中心搜索建议下拉 */
.cap-search-box { position: relative; flex: 1; min-width: 0; }
.cap-search-box .toolbar-search { width: 100%; }
.cap-search-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  max-height: 300px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--c-line);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  padding: 6px;
  z-index: 50;
}
.cap-search-dropdown-header {
  font-size: 12px;
  color: var(--c-ink-4);
  padding: 4px 8px 8px;
  border-bottom: 1px solid var(--c-line);
  margin-bottom: 4px;
}
.cap-search-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background .15s;
}
.cap-search-item:hover { background: var(--c-bg); }
.cap-search-item-icon {
  width: 18px; height: 18px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--c-ink-3);
}
.cap-search-item-icon .ic { width: 18px; height: 18px; }
.cap-search-item-body { flex: 1; min-width: 0; }
.cap-search-item-title {
  font-size: 13.5px; font-weight: 600; color: var(--c-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cap-search-item-meta {
  font-size: 11.5px; color: var(--c-ink-4);
  margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cap-search-item-caret {
  width: 14px; height: 14px; flex: 0 0 auto;
  color: var(--c-ink-4);
  display: inline-flex; align-items: center; justify-content: center;
}
.cap-search-empty {
  padding: 14px 10px;
  color: var(--c-ink-4);
  font-size: 13px;
  text-align: center;
}
@media (max-width: 880px) {
  .cap-layout { flex-direction:column; }
  .cap-tree { width:100%; position:static; max-height:none; padding:var(--m-card-pad); }
  /* 企业能力/技能/软件链接中心：移动端统一收紧 content 区内边距与卡片间距 */
  .cap-layout { padding: 0 0 var(--m-card-gap) 0; }
  .cap-content > .card { padding: var(--m-card-pad); }
  .cap-content .toolbar { margin-bottom: var(--m-card-gap); }
  .cap-content > .cap-banner + .card { margin-top: var(--m-card-gap); }
  /* 能力/技能/软件链接中心：移动端隐藏整个卡片列表，只留分类导航 + toolbar */
  .market-center-page .cap-cards { display: none; }
  /* 能力/技能/软件链接中心：移动端 toolbar 置顶且通栏，分类树在下 */
  .market-center-page .page-panel { padding-left: 0; padding-right: 0; }
  .market-center-page .cap-layout { flex-direction: column-reverse; padding: 0; }
  /* 企业能力/技能/软件链接中心：移动端分类树卡片过矮，整页悬在半屏、下方大片灰白。
     让 cap-layout 至少撑满视口，工具栏卡保持内容高度，分类树卡拉高填满剩余高度。 */
  .market-center-page .cap-layout { min-height: 100vh; }
  .market-center-page .cap-content { flex: 0 0 auto; }
  .market-center-page .cap-tree { flex: 1 1 auto; min-height: calc(100vh - 160px); }
  .market-center-page .cap-content { width: 100%; }
  .market-center-page .cap-content > .card {
    width: 100%;
    margin-left: 0;
    margin-right: 0;
    box-sizing: border-box;
  }
  .market-center-page .cap-content > .card:first-child {
    padding: var(--m-card-pad);
  }
  /* 软件链接中心：搜索框无下拉，移动端收紧卡片底 padding 与 toolbar margin，
     使 toolbar 上下间隙对称（上=卡片顶 padding 16px，下=树顶 padding 16px） */
  .software-link-center-page .cap-content > .card:first-child {
    padding-bottom: 0;
  }
  .software-link-center-page .cap-content .toolbar {
    margin-bottom: 0;
  }
  .market-center-page .cap-tree {
    width: 100%;
    margin: 0;
    box-sizing: border-box;
    /* 移动端不再用可滚动容器：覆盖基础 .cap-tree 的 overflow-y:auto，避免触发 Safari 焦点/滚动蓝框 */
    overflow: visible;
    outline: none;
    -webkit-tap-highlight-color: transparent;
    padding: var(--m-card-pad);
  }
  /* 市场中心类页面（能力/技能/软件链接中心）：移动端 .cap-layout 为 column-reverse，
     toolbar 卡片在 DOM 中位于树之后，会以其底部阴影“线”盖住树顶，
     统一把树下移 8px（与能力中心已验收值一致），让阴影线离开树内容 */
  .market-center-page .cap-tree {
    margin-top: 8px;
  }
  /* 软件链接中心：toolbar 卡片底 padding/margin 已收紧为 0，阴影更贴树，
     需要比能力中心更大的 16px 间距，才能让阴影线彻底离开树顶 */
  .software-link-center-page .cap-tree {
    margin-top: 16px;
  }
  /* 兜底：整棵树的子元素在任何聚焦态都不显示蓝色外框 */
  .market-center-page .cap-tree *,
  .market-center-page .cap-tree :focus,
  .market-center-page .cap-tree :focus-visible { outline: none !important; -webkit-tap-highlight-color: transparent !important; }
  .market-center-page .cap-tree-item { padding: 6px 14px; }
  .market-center-page .cap-tree-children { padding: 2px 0 2px 12px; margin: 0 0 4px 14px; }
  .market-center-page .cap-tree-leaf { padding: 5px 10px; }
  /* 能力面板：移动端详情页全屏弹出，铺满视口，无圆角/无边框 */
  .market-center-page .cap-content > .card.cap-detail-mobile-modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 9999;
    margin: 0;
    border-radius: 0;
    border: none;
    overflow: hidden;
    padding: 0;
    display: flex;
    flex-direction: column;
    background: #fff;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 16px;
    box-sizing: border-box;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-head {
    padding: 8px 0 16px;
    border-bottom: 1px solid var(--c-line);
    margin-bottom: 12px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding-right: 4px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-head .cap-edit,
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-head .cap-doc {
    width: 36px;
    height: 36px;
    border: 1px solid var(--c-line);
    background: #fff;
    opacity: .9;
    border-radius: 8px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-head .cap-edit svg,
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-head .cap-doc svg {
    width: 18px;
    height: 18px;
  }
  /* 能力详情页移动端：头部与表格进一步收紧 */
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-head {
    padding: 18px 10px 18px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc {
    padding: 16px 20px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-header-row {
    position: relative; flex-wrap: nowrap; gap: 10px; align-items: flex-start;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-header-row h2 {
    font-size: 17px; margin: 0; padding-right: 44px; line-height: 1.35;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-subline {
    gap: 8px 10px; margin-top: 10px; flex-wrap: wrap;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .api-doc-meta-row {
    margin-top: 14px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-param-cards {
    gap: 10px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-param-card {
    background: #fff; border: 1px solid var(--c-line); border-radius: 10px;
    padding: 12px 14px; box-shadow: 0 1px 2px rgba(0,0,0,.03);
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-param-card .cap-param-name {
    font-family: var(--font-mono); font-size: 13px; font-weight: 600;
    color: var(--c-ink); word-break: break-all; margin-bottom: 6px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-param-card .cap-param-meta {
    display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-param-card .cap-param-meta .cell-sub {
    font-size: 12px; color: #64748b; background: #f1f5f9;
    padding: 2px 8px; border-radius: 4px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-param-card .cap-param-desc {
    font-size: 12.5px; color: var(--c-ink-2); line-height: 1.6; word-break: break-word;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-invoke-box {
    margin-top: 10px;
    padding: 10px 12px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-path {
    word-break: break-all;
    max-width: 100%;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-detail-table {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-detail-table table.data {
    min-width: 100%;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-detail-table .data th,
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-detail-table .data td {
    white-space: normal;
    word-break: break-word;
    font-size: 12px;
    padding: 8px 10px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-detail-table .data th:first-child,
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-detail-table .data td:first-child {
    min-width: 90px;
    max-width: 120px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-detail-table .data th:last-child,
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .cap-detail-table .data td:last-child {
    min-width: 140px;
  }
  .market-center-page .cap-content > .card.cap-detail-mobile-modal .code-block {
    font-size: 11px;
    padding: 10px;
  }
}

/* ============================================================
   统一列表组件 <list-view> 移动端冻结列兜底
   ============================================================
   确保 .list-view 里带 freeze-cols-* 的表格在移动端必然出现横向滚动条，
   前 N 列 sticky 固定，其余列跟随滚动。 */
@media (max-width: 768px) {
  .list-view .table-wrap[class*="freeze-cols-"] {
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch;
  }
  .list-view .table-wrap[class*="freeze-cols-"] table.data {
    width: auto;
    min-width: 680px;
    min-width: max-content;
  }
  /* freeze-cols-0：不冻结任何列 */
  .list-view .table-wrap.freeze-cols-0 table.data th,
  .list-view .table-wrap.freeze-cols-0 table.data td {
    position: static !important;
    left: auto !important;
    box-shadow: none !important;
  }
  /* 列表徽标窄屏弱化规则已下移到独立的 @media (max-width:1024px) 块，见下方 */
}

/* 列表徽标（分类/状态/类型）窄屏弱化：≤1024px（含手机与平板）字号小于标题、字重不抢视觉。
   用 !important 保证覆盖 .badge 基础规则与 .badge.* 配色变体，规避特异性/缓存导致的“改了没生效”。 */
@media (max-width: 1024px) {
  .list-view .badge {
    font-size: 10px !important;
    font-weight: 500 !important;
    padding: 1px 6px !important;
    line-height: 1.5 !important;
  }
  /* 操作按钮窄屏换行：多个文字按钮挤在窄列时自动折行，避免互相遮挡。
     仅对 .wrap-text 生效；图标按钮（如人脉标签管理操作列）保持横向不换行。 */
  .row-actions.wrap-text {
    flex-wrap: wrap !important;
    gap: 4px;
  }
  .row-actions.wrap-text .btn-sm {
    padding: 4px 8px;
    font-size: 12px;
    white-space: nowrap;
  }
}

/* ============================================================
   人脉图谱 — 标签云（参考词云布局）
   ============================================================ */
.tag-cloud-page {
  display: flex;
  flex-direction: column;
  /* 内边距统一由 .page-panel 提供（--console-body-gap 上下 / --m-panel-inset 左右）。
     原写法 padding: 12px var(--m-page-pad) 12px 把左右写死成 20px（标准是 16px），
     导致本页内容比其它列表页多内缩 4px —— 找人脉 4 个标签页里唯一偏离的一个。
     修复 y 任务：删除硬编码，改由单一事实来源 --m-panel-inset 控制。 */
}
.tag-cloud-page > .card {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  /* 首个 card 顶到 page-panel 内容上沿，与其它列表页首个内容块（toolbar）对齐；
     多个 card 之间的间距由 .page-panel > .card + .card(--m-card-gap) 统一提供 */
}
.tag-cloud-page .tag-cloud {
  flex: 0 0 auto;
}
.tag-cloud {
  position: relative;
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 4px;
  line-height: 1.15;
}
.tag-cloud-item {
  cursor: pointer;
  border-radius: 999px;
  transition: transform .12s ease, box-shadow .12s ease, opacity .12s ease;
  white-space: nowrap;
  user-select: none;
  letter-spacing: -0.01em;
  position: absolute;
}
.tag-cloud-item:hover {
  transform: translate(-50%, -50%) scale(1.05) !important;
  box-shadow: 0 4px 14px rgba(0,0,0,.1);
  opacity: 1 !important;
  z-index: 5;
}
@media (max-width: 768px) {
  /* 移动端同样不再覆盖上下内边距，跟随 --console-body-gap / --m-panel-inset 断点值 */
  .tag-cloud-page {
    min-height: auto;
  }
}

/* 人脉结果列表 */
.person-list { border-top: 1px solid var(--c-line); }
.person-row {
  display: grid;
  grid-template-columns: 1fr 120px 170px;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--c-line);
  transition: background .12s ease;
}
.person-row:hover { background: var(--c-surface); }
.person-row.person-head {
  background: #f8fafc;
  color: var(--c-ink-3);
  font-size: 12px;
  font-weight: 600;
  padding: 8px 14px;
}
.person-cell { display: flex; align-items: center; gap: 10px; }
.person-name-col { gap: 10px; }
.person-avatar {
  width: 28px; height: 28px; border-radius: 50%;
  background: #eef2ff; color: #1e40af;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; flex-shrink: 0;
}
.person-name-text { font-size: 14px; font-weight: 500; color: var(--c-ink); }
.person-status-col { flex-direction: column; align-items: flex-start; gap: 2px; }
.person-status {
  display: inline-flex; align-items: center;
  padding: 2px 8px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600;
}
.person-status.public { background: #dcfce7; color: #166534; }
.person-status.pending { background: #f3f4f6; color: #6b7280; }
.person-status.know { background: #eff6ff; color: #1e40af; }
.person-status.colleague_current { background: #f0fdf4; color: #15803d; }
.person-status.colleague_former { background: #fff7ed; color: #c2410c; }
.person-status.friend { background: #fdf4ff; color: #86198f; }
.person-status.partner { background: #ecfeff; color: #0e7490; }
.person-status.mentor { background: #fefce8; color: #a16207; }
.person-status.mentee { background: #f5f3ff; color: #6d28d9; }
.person-ops-col { justify-content: flex-end; gap: 6px; }

/* ---------- 人脉关系：中心人物 + 关系流卡片 ---------- */
.relation-center-card {
  display: inline-flex; align-items: center; gap: 16px;
  background: linear-gradient(135deg, #f5f7ff 0%, #eef2ff 100%);
  border: 1px solid #e0e7ff; border-radius: var(--r-lg);
  padding: 16px 22px; margin-bottom: 18px;
}
.relation-center-avatar {
  width: 52px; height: 52px; border-radius: 50%;
  background: var(--grad-brand); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 20px; font-weight: 700; flex-shrink: 0;
  box-shadow: 0 4px 12px rgba(79,70,229,0.25);
}
.relation-center-name { font-size: 18px; font-weight: 700; color: var(--c-ink); }
.relation-center-meta { font-size: 13px; color: var(--c-ink-3); margin-top: 2px; }

.relation-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 16px;
}
.relation-card {
  background: var(--c-surface); border: 1px solid var(--c-line); border-radius: var(--r-lg);
  padding: 18px; box-shadow: var(--sh-sm);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.relation-card:hover { transform: translateY(-2px); box-shadow: var(--sh-md); border-color: #c7d2fe; }
.relation-flow {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.relation-node {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  min-width: 70px; text-align: center;
}
.relation-node.is-center .relation-avatar {
  background: var(--grad-brand); color: #fff;
  box-shadow: 0 4px 12px rgba(79,70,229,0.22);
}
.relation-node.is-center .relation-node-name { color: var(--c-primary); font-weight: 700; }
.relation-avatar {
  width: 42px; height: 42px; border-radius: 50%;
  background: #f1f5f9; color: var(--c-ink-2);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 600; flex-shrink: 0;
}
.relation-node-name {
  font-size: 13px; font-weight: 600; color: var(--c-ink-2);
  max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.relation-arrow {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px;
  min-width: 90px;
}
.relation-type-pill {
  display: inline-flex; align-items: center;
  padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 700;
  background: #f1f5f9; color: var(--c-ink-2);
}
.relation-type-pill.know { background: #eff6ff; color: #1e40af; }
.relation-type-pill.colleague_current { background: #f0fdf4; color: #15803d; }
.relation-type-pill.colleague_former { background: #fff7ed; color: #c2410c; }
.relation-type-pill.friend { background: #fdf4ff; color: #86198f; }
.relation-type-pill.partner { background: #ecfeff; color: #0e7490; }
.relation-type-pill.mentor { background: #fefce8; color: #a16207; }
.relation-type-pill.mentee { background: #f5f3ff; color: #6d28d9; }
.relation-arrow-icon {
  width: 28px; height: 14px; color: var(--c-ink-3);
}
.relation-notes {
  margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--c-line);
  font-size: 12px; color: var(--c-ink-3); line-height: 1.5;
}
.relation-notes-label {
  display: inline-block; font-size: 11px; font-weight: 600;
  color: var(--c-ink-4); background: var(--c-line-2);
  padding: 1px 6px; border-radius: 4px; margin-right: 6px;
}
.relation-ops {
  display: flex; justify-content: flex-end; margin-top: 12px;
}

/* 新增关系弹窗：A → 关系 → B 视觉流 */
.relation-flow-form {
  display: flex; align-items: flex-end; gap: 10px;
  background: var(--c-bg); border: 1px solid var(--c-line); border-radius: var(--r-lg);
  padding: 18px 14px;
}
.form-flow-node { flex: 1; min-width: 0; }
.form-flow-label {
  font-size: 12px; font-weight: 600; color: var(--c-ink-3); margin-bottom: 8px;
}
.form-flow-person {
  display: flex; align-items: center; gap: 8px;
  background: #fff; border: 1px solid var(--c-line); border-radius: var(--r-md);
  padding: 8px 12px; font-size: 14px; font-weight: 600; color: var(--c-ink);
}
.form-flow-arrow {
  flex: none; font-size: 20px; font-weight: 700; color: var(--c-ink-3);
  padding-bottom: 10px;
}

@media (max-width: 720px) {
  .person-row { grid-template-columns: 1fr 90px 130px; padding: 8px 10px; gap: 8px; }
  .person-ops-col .btn { padding: 2px 6px; font-size: 11px; }
  .person-name-text { font-size: 13px; }
  .relation-grid { grid-template-columns: 1fr; }
  .relation-flow { flex-wrap: wrap; justify-content: center; gap: 8px; }
  .relation-arrow { order: -1; min-width: 100%; flex-direction: row; justify-content: center; gap: 8px; }
  .relation-arrow-icon { transform: rotate(90deg); width: 14px; height: 28px; }
  .relation-flow-form { flex-direction: column; align-items: stretch; gap: 8px; }
  .form-flow-arrow { text-align: center; transform: rotate(90deg); padding: 4px 0; }
}

/* ===== 全局刷新/更新按钮（<refresh-button> 组件） =====
   列表页顶部的「刷新/更新/同步」按钮统一用 <refresh-button block>，
   带 text 时只显示文字、不显示前置图标；纯图标模式不传 text。
   样式不再散落在各页面。 */
.refresh-btn { flex: 0 0 auto; white-space: nowrap; }
.refresh-btn .rb-text { line-height: 1; }
.refresh-btn .rb-icon { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; }
/* PC：有文字时隐藏图标，只显示文字；纯图标模式（不传 text）保留图标。
   用 .btn-primary/.btn-ghost 把 specificity 提到 (0,5,0)，压过上面
   .toolbar .refresh-btn.btn-primary > span { display:block }（0,3,1），
   否则 PC 端文字刷新按钮会同时渲染图标。 */
.toolbar .refresh-btn.btn-primary .rb-text ~ .rb-icon,
.toolbar .refresh-btn.btn-ghost .rb-text ~ .rb-icon { display: none; }
.refresh-btn.refresh-btn-block { width: 100%; justify-content: center; }
/* toolbar 内的 block 刷新按钮在 PC 端不应通栏，保持与新增按钮一致的内容宽度；
   移动端由 @media 规则覆盖为固定圆形尺寸，不受此处影响。 */
.toolbar .right .refresh-btn.refresh-btn-block,
.toolbar .left .refresh-btn.refresh-btn-block,
.toolbar > .refresh-btn.refresh-btn-block { width: auto; }
@keyframes rb-spin { to { transform: rotate(360deg); } }
.refresh-btn.is-loading .ic { animation: rb-spin .9s linear infinite; transform-origin: center; }
/* 移动端 toolbar 内 block 模式刷新按钮通栏铺满 */
@media (max-width: 768px) {
  /* 同上：紧凑布局下刷新按钮按内容自然宽，不再通栏占一整行 */
  /* 注意：此处 specificity 为 (0,4,0)，高于 .toolbar .refresh-btn.btn-primary(0,3,0) 的圆形规则。
     原 width:auto 会把圆形刷新按钮压回内容宽（16px SVG）形成 16×32 椭圆（即"被挤压"）。
     改为 var(--toolbar-h-mobile) 让其回到 32px 圆形，同时仍非通栏（未用 width:100%）。 */
  .toolbar .right .refresh-btn.refresh-btn-block,
  .toolbar .left .refresh-btn.refresh-btn-block { flex: 0 0 auto; width: var(--toolbar-h-mobile); }
}

/* ============================================================================
   能力详情页（企业智能能力 / 企业通用能力）正文表格：移动端横向溢出修复
   ----------------------------------------------------------------------------
   现象：手机上打开一条能力详情，「关联 AI 能力与模型」表格的「说明」列整段跑出
        屏幕外，标题栏与「能力说明」正文也被同步推宽，只能左右滑动浮层才能看全；
        「错误码」这种两列表同样被撑到 680px 横向滚动。
   根因（三层叠加，缺一不成）：
     ① 列表页窗口规则在 <=1024px 给 .table-wrap[class*="freeze-cols-"] table.data
        设了 min-width: max-content + 单元格 white-space: nowrap（为「冻结列横向
        滑动」设计）；同在 <=768px 还有一条全局 table.data { min-width: 680px }。
        两者叠加后本例表格宽度按「所有内容不换行」算，达 1232px / 680px。
     ② .api-doc-body 在 <=880px 改为 flex-direction: column，但 align-items 仍是
        flex-start —— 列方向下横向是交叉轴，flex-start 让子项按 max-content 定宽，
        于是 .api-doc-main（含正文段落）被一起撑到 1232px。
     ③ 详情浮层 .api-doc 是 position: fixed; inset: 0 + overflow-y: auto，
        按规范 overflow-x 由 visible 计算为 auto —— 浮层自己长出 858px 横向滚动区。
   做法：① 全宽度下取消详情页正文表格的 nowrap / min-width 撑宽，长文本列一律换行；
        ② <=880px 单列布局时让正文列撑满容器（消除根因 ②）；
        ③ <=880px 正文表格从「多列表格」改为「一行一张卡」的堆叠布局 —— 实测
           仅释放首列最小宽度后，auto layout 仍会把说明列压成 126px×15 行的竖条，
           只有卡片布局能让说明文字拿到整行宽度。
   作用域：选择器锚在 .api-doc-main .api-sec 内，只覆盖能力详情页正文（.api-sec 是
        表格的固定父级 section）。列表页冻结列表格、后台管理页表格、页面其它位置的
        table.data 完全不受影响。用 .api-sec 而不是 [class*="freeze-cols-"] 作锚点，
        是为了同时覆盖「带 freeze-cols 的参数表」和「不带 freeze-cols 的错误码表」，
        且特异性 (0,4,3) 足以压过全局 freeze-cols 规则 (0,3,2)。
   ========================================================================== */
.api-doc-main .api-sec .table-wrap table.data {
  width: 100%;
  min-width: 0;
  max-width: 100%;
}
.api-doc-main .api-sec .table-wrap table.data th,
.api-doc-main .api-sec .table-wrap table.data td {
  white-space: normal;
  word-break: break-word;
  overflow-wrap: anywhere;
}
/* 表格已在容器内收窄，不再需要横向滑动容器；overflow-y 保持可见避免表头/阴影被裁 */
.api-doc-main .api-sec .table-wrap {
  overflow-x: auto;
  overflow-y: visible;
}
/* 首列名称（API 名称 / 参数名称 / 错误码）在详情页要完整可读。
   基础样式 .cell-title（第 980 行）为列表页长标题设了「单行 + 省略号 + inline-block」，
   在文档表格里会把名称截断 5~10px，且 inline-block 会让 td 被匿名单元格包裹；
   这里恢复单元格语义并允许换行。仅 >880px 生效：移动端由下方卡片规则接管
   （卡片布局里整行都是 display:block，这条如果不加断点会与卡片规则同特异性打架）。 */
@media (min-width: 881px) {
  .api-doc-main .api-sec .table-wrap table.data td.cell-title {
    display: table-cell;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    max-width: none;
  }
}

@media (max-width: 880px) {
  /* 单列布局下子项必须撑满容器：否则 flex-start 让 .api-doc-main 按 max-content
     定宽，正文段落会比屏幕宽，浮层出现横向滚动（顶部注释的根因 ②）。 */
  .api-doc-body { align-items: stretch; }
  .api-doc-main { width: 100%; max-width: 100%; min-width: 0; }

  /* ---- 正文表格改为「一行一张卡」的堆叠布局 ----
     与 .cap-param-card（移动端参数卡片）保持同一套视觉：白底 / 1px 描边 / 10px 圆角。 */
  .api-doc-main .api-sec .table-wrap {
    overflow: visible;
    background: transparent;
    border: none;
    box-shadow: none;
  }
  .api-doc-main .api-sec .table-wrap table.data,
  .api-doc-main .api-sec .table-wrap table.data tbody,
  .api-doc-main .api-sec .table-wrap table.data tr,
  .api-doc-main .api-sec .table-wrap table.data td {
    display: block;
    width: auto !important;
    min-width: 0 !important;
    max-width: none !important;
    text-align: left !important;
  }
  /* 表头列名改由每张卡片的 ::before / 首列标题承担 */
  .api-doc-main .api-sec .table-wrap table.data thead { display: none; }

  .api-doc-main .api-sec .table-wrap table.data tr {
    background: #fff;
    border: 1px solid var(--c-line);
    border-radius: 10px;
    padding: 12px 14px;
    margin-bottom: 10px;
  }
  .api-doc-main .api-sec .table-wrap table.data tr:last-child { margin-bottom: 0; }

  /* 首列 = 卡片标题 */
  .api-doc-main .api-sec .table-wrap table.data td:first-child {
    font-size: 13px;
    font-weight: 500;
    line-height: 1.5;
    color: var(--c-ink);
    padding: 0;
    margin-bottom: 8px;
    border-bottom: none;
    text-align: left;
  }
  /* 中间枚举列 = 行内元信息，各带列名，排在标题下方同一行自动折行 */
  .api-doc-main .api-sec .table-wrap table.data td:not(:first-child):not(:last-child) {
    display: inline-block;
    width: auto !important;
    padding: 0;
    margin: 0 10px 6px 0;
    border-bottom: none;
    font-size: 12px;
    line-height: 1.6;
    color: #64748b;
    vertical-align: middle;
    white-space: nowrap;
  }
  .api-doc-main .api-sec .table-wrap table.data td:not(:first-child):not(:last-child)::before {
    color: #94a3b8;
    margin-right: 4px;
  }
  #sec-apis .table-wrap table.data td:nth-child(2)::before { content: "provider"; }
  #sec-apis .table-wrap table.data td:nth-child(3)::before { content: "状态"; }
  #sec-req .table-wrap table.data td:nth-child(2)::before { content: "必选"; }
  #sec-req .table-wrap table.data td:nth-child(3)::before { content: "类型"; }
  #sec-resp .table-wrap table.data td:nth-child(2)::before { content: "类型"; }

  /* 末列 = 说明正文，独占整行并与元信息用虚线分隔 */
  .api-doc-main .api-sec .table-wrap table.data td:last-child {
    padding: 8px 0 0;
    margin-top: 2px;
    border-top: 1px dashed var(--c-line);
    border-bottom: none;
    font-size: 12.5px;
    line-height: 1.65;
    color: var(--c-ink-2);
  }
  /* 只有两列（无中间枚举列）的表格不需要虚线分隔 */
  .api-doc-main .api-sec .table-wrap table.data td:first-child + td:last-child {
    padding-top: 0;
    margin-top: 0;
    border-top: none;
  }
}

/* ===== 天眼查查询面板 (TianyanchaQueryPanel) ===== */
.tyc-panel { padding: 18px 24px 24px; max-width: 1180px; }
.tyc-head { margin-bottom: 14px; }
.tyc-title { font-size: 18px; font-weight: 600; color: var(--c-ink); }
.tyc-sub { margin: 6px 0 0; font-size: 13px; color: var(--c-ink-2); line-height: 1.6; }

.tyc-searchbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tyc-input { flex: 1; min-width: 260px; height: 38px; }
.tyc-debug-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--c-ink-2); white-space: nowrap; cursor: pointer; }

.tyc-recent { margin-top: 12px; display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.tyc-recent-label { font-size: 12.5px; color: var(--c-ink-3); }
.tyc-chip { border: 1px solid var(--c-line); background: var(--c-bg-2); color: var(--c-ink-1);
  border-radius: 999px; padding: 4px 12px; font-size: 12.5px; cursor: pointer; }
.tyc-chip:hover { border-color: var(--c-primary); color: var(--c-primary); }

.tyc-banner { margin-top: 12px; padding: 9px 12px; border-radius: 8px; font-size: 13px; line-height: 1.5; }
.tyc-banner-warn { background: var(--c-warning-bg); color: var(--c-warning-ink-strong); border: 1px solid var(--c-warning-line); }
.tyc-banner-err { background: var(--c-danger-bg); color: var(--c-danger); border: 1px solid var(--c-danger); }

.tyc-body { margin-top: 16px; display: flex; gap: 18px; align-items: flex-start; }
.tyc-list { flex: 1 1 0; min-width: 0; border: 1px solid var(--c-line); border-radius: 10px; background: var(--c-surface); overflow: hidden; }
.tyc-list-head { padding: 10px 14px; font-size: 13px; font-weight: 600; color: var(--c-ink-1); border-bottom: 1px solid var(--c-line); background: var(--c-bg-2); }
.tyc-row { padding: 11px 14px; border-bottom: 1px solid var(--c-line); cursor: pointer; }
.tyc-row:last-child { border-bottom: none; }
.tyc-row:hover { background: var(--c-bg-2); }
.tyc-row.active { background: var(--c-primary-l); box-shadow: inset 3px 0 0 var(--c-primary); }
.tyc-name { font-size: 14px; font-weight: 600; color: var(--c-ink); }
.tyc-base { margin-top: 4px; font-size: 12.5px; color: var(--c-ink-2); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }

.tyc-detail { flex: 1 1 0; min-width: 0; border: 1px solid var(--c-line); border-radius: 10px; background: var(--c-surface); padding: 14px 16px; }
.tyc-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.tyc-detail-name { font-size: 16px; font-weight: 700; color: var(--c-ink); }
.tyc-kv { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 18px; }
.tyc-kv > div { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.tyc-kv label { font-size: 12px; color: var(--c-ink-3); }
.tyc-kv span { font-size: 13.5px; color: var(--c-ink); word-break: break-word; }
.tyc-full { grid-column: 1 / -1; }
.tyc-scope { line-height: 1.6; }

.tyc-raw { margin-top: 14px; background: var(--c-bg-2); border: 1px solid var(--c-line); border-radius: 8px; padding: 12px; font-size: 12px; line-height: 1.5; color: var(--c-ink-2); max-height: 320px; overflow: auto; white-space: pre-wrap; word-break: break-word; }

.tyc-debug-card { flex: 1 1 0; min-width: 0; border: 1px dashed var(--c-primary); border-radius: 10px; background: var(--c-primary-l); padding: 14px 16px; }
.tyc-debug-row { display: flex; gap: 10px; font-size: 13px; margin-bottom: 8px; }
.tyc-debug-row label { width: 56px; flex: none; color: var(--c-ink-3); }
.tyc-debug-row span { color: var(--c-ink); word-break: break-word; }
.tyc-debug-note { margin-top: 4px; font-size: 12.5px; color: var(--c-primary); }

.tyc-empty { padding: 26px 14px; text-align: center; color: var(--c-ink-3); font-size: 13px; }
.tyc-empty-hero { margin-top: 24px; }

@media (max-width: 860px) {
  .tyc-panel { padding: 14px 14px 20px; }
  .tyc-body { flex-direction: column; }
  .tyc-list, .tyc-detail, .tyc-debug-card { width: 100%; flex: 1 1 auto; }
  .tyc-kv { grid-template-columns: 1fr; }
}

/* ── 图片灯箱：弹窗内截图点击放大（2026-09-25） ── */
.img-lightbox { position: fixed; inset: 0; z-index: 1400; background: rgba(15, 23, 42, .78); display: flex; align-items: center; justify-content: center; padding: 32px; cursor: zoom-out; }
.img-lightbox img { max-width: 92vw; max-height: 88vh; border-radius: 10px; box-shadow: 0 12px 48px rgba(0,0,0,.35); background: #fff; }
.img-zoomable { cursor: zoom-in; }

/* ── 登录页页脚：版权 + 备案（2026-09-25） ── */
.lv2-beian { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 6px 18px; padding: 14px 16px 18px; font-size: 12px; color: rgba(255,255,255,.55); }
.lv2-beian a { color: rgba(255,255,255,.55); text-decoration: none; }
.lv2-beian a:hover { color: rgba(255,255,255,.85); text-decoration: underline; }
