/*
 * qbmap 设计系统 — 单一真源
 *
 * 立场：这是一台查人的仪器，不是一本读物。
 *
 * 三条不许破的规则：
 *
 * 1. 全站只有两个高饱和色：清华紫 #660874 与北大红 #9b1e26，
 *    并且它们只承载一个语义——校籍归属。不做装饰、不做强调、
 *    不做链接色。红色在这个产品里只有一个意思：北大。因此语义
 *    警示色一律走琥珀，绝不用红，否则「红=北大」这条约定会被稀释。
 *
 * 2. 交互色就是墨色本身。链接是墨色加下划线，焦点环是清华紫的
 *    低透明度版本——把交互语言挂回品牌，而不是引入第三种色相。
 *    这样做的代价是界面很安静，那正是目的：让数据和图谱发光。
 *
 * 3. 中文没有真正的 900 字重。层级靠字号、色阶、间距、字距造，
 *    不要指望 font-weight——那是英文界面的思路，在中文里只会
 *    得到一坨发虚的粗体。
 *
 * 替代的旧物：app.css 九层叠加（v4 毛玻璃/v5 衬线印章/v6 冷白/
 * v8 暖宣纸互相覆盖）、四套主题、四套导航、三种最大宽度、
 * 12 个随手取的圆角值、三处手工同步的校色常量。
 */

/* ============================================================
   1. 令牌
   ============================================================ */

:root {
  /* --- 中性色阶：偏紫的冷灰。不是纯灰——纯灰读起来像没选过 --- */
  --ground:      #f7f7f9;   /* 页面地面 */
  --surface:     #ffffff;   /* 卡片、面板 */
  --surface-2:   #f1f1f4;   /* 表头、轨道、次级块 */
  --surface-3:   #e9e9ee;   /* 悬停、按下 */
  --line:        #e3e3e9;   /* 常规分割线 */
  --line-strong: #c7c7d1;   /* 强调分割、输入框边 */

  --ink:   #16161c;         /* 正文与标题 */
  --ink-2: #55555f;         /* 次级文字 */
  --ink-3: #6e6e79;         /* 标签、脚注、占位 */

  /* --- 校籍色：全站仅有的两个高饱和色，只表示归属 --- */
  --thu:    #660874;
  --thu-bg: rgba(102, 8, 116, 0.07);
  --pku:    #9b1e26;
  --pku-bg: rgba(155, 30, 38, 0.07);

  /* --- 语义色：与校籍色严格分离；不含红 --- */
  --ok:      #1f6b4a;
  --ok-bg:   rgba(31, 107, 74, 0.08);
  --warn:    #8a6100;       /* 风险/存疑/待核 一律走这个琥珀 */
  --warn-bg: rgba(138, 97, 0, 0.09);

  /* --- 交互 --- */
  --focus-ring: rgba(102, 8, 116, 0.32);   /* 清华紫的低透明版 */
  --sel-bg:     rgba(102, 8, 116, 0.10);

  /* --- 图谱专用（graph.js 读这些，不要在 JS 里再硬写一遍） --- */
  --graph-node:   #55555f;
  --graph-edge:   #c7c7d1;
  --graph-root:   #16161c;
  --graph-dim:    0.16;     /* 悬停时非邻居的不透明度 */

  /* --- 字体 --- */
  --sans: "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC",
          "Noto Sans CJK SC", "Microsoft YaHei", system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;

  /* --- 字号阶梯：数据密集型，克制 --- */
  --fs-micro: 11px;   /* 标签、单位、脚注 */
  --fs-small: 12px;   /* 表格次级、chip */
  --fs-base:  13px;   /* 表格正文、列表 */
  --fs-body:  15px;   /* 阅读性正文 */
  --fs-lg:    17px;
  --fs-h3:    20px;
  --fs-h2:    26px;
  --fs-h1:    34px;

  /* --- 间距：4px 基准 --- */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px;

  /* --- 几何：只有一个圆角值。仪器不需要 12 种圆角 --- */
  --r: 3px;
  --shell-max: 1180px;      /* 唯一的容器宽度，取代 940/1080/1120 */
  --shell-pad: var(--s5);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground:      #131318;
    --surface:     #1a1a21;
    --surface-2:   #22222b;
    --surface-3:   #2c2c37;
    --line:        #2e2e38;
    --line-strong: #454552;

    --ink:   #e9e9ef;
    --ink-2: #a8a8b4;
    --ink-3: #82828e;

    /* 深底上原色会发闷，提亮但保持色相 */
    --thu:    #c98fd6;
    --thu-bg: rgba(201, 143, 214, 0.13);
    --pku:    #e2717a;
    --pku-bg: rgba(226, 113, 122, 0.13);

    --ok:      #6fbb96;
    --ok-bg:   rgba(111, 187, 150, 0.13);
    --warn:    #d3a44a;
    --warn-bg: rgba(211, 164, 74, 0.13);

    --focus-ring: rgba(201, 143, 214, 0.42);
    --sel-bg:     rgba(201, 143, 214, 0.14);

    --graph-node: #a8a8b4;
    --graph-edge: #454552;
    --graph-root: #e9e9ef;
    --graph-dim:  0.13;
  }
}

:root[data-theme="dark"] {
  --ground: #131318; --surface: #1a1a21; --surface-2: #22222b; --surface-3: #2c2c37;
  --line: #2e2e38; --line-strong: #454552;
  --ink: #e9e9ef; --ink-2: #a8a8b4; --ink-3: #82828e;
  --thu: #c98fd6; --thu-bg: rgba(201,143,214,.13);
  --pku: #e2717a; --pku-bg: rgba(226,113,122,.13);
  --ok: #6fbb96; --ok-bg: rgba(111,187,150,.13);
  --warn: #d3a44a; --warn-bg: rgba(211,164,74,.13);
  --focus-ring: rgba(201,143,214,.42); --sel-bg: rgba(201,143,214,.14);
  --graph-node: #a8a8b4; --graph-edge: #454552; --graph-root: #e9e9ef; --graph-dim: .13;
}

/* ============================================================
   2. 基座
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: 1.7;
  /* 中文在小字号下需要更主动的字距控制，交给各组件按需覆盖 */
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--sel-bg); }

/* 交互色 = 墨色。全站不引入第三种色相。 */
a { color: inherit; text-decoration: underline; text-underline-offset: 2px;
    text-decoration-color: var(--line-strong); transition: text-decoration-color .12s; }
a:hover { text-decoration-color: var(--ink); }

/* 焦点必须可见，且用清华紫把交互语言挂回品牌 */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--r);
}

/* 属性 hidden 必须压得过组件自带的 display。这不是特异性问题，是 UA 层
   与作者层的层级问题——任何自带 display:flex/grid 的组件用 hidden 都会失效。 */
[hidden] { display: none !important; }

/* 第三级墨色的适用边界（实测 WCAG 2.1，阈值 4.5，浅/深两套主题）：
     --ink-3 on --ground     5.04 / 4.56  ✅
     --ink-3 on --surface    5.28 / 4.86  ✅
     --ink-3 on --surface-2  4.47 / 4.15  ❌
     --ink-3 on --surface-3  4.19 / 3.94  ❌
     --ink-3 on 琥珀警示底    4.46 / 3.63  ❌  全站最低
   也就是说这个令牌只在无底色处成立。凡是自带背景的容器，就地把第三级
   墨色抬一档。这里用「局部重定义令牌」而不是逐个列出受害选择器——
   后者只能兜住今天写下的那几个，明天新写的组件照样掉出 AA。抬档后
   同底实测 6.66 / 5.85，仍明显轻于 --ink，三级墨阶不会塌。 */
.notice, .tbl thead, .chip, .school.none,
.row:hover, .tbl tbody tr:hover,
.drop-item:hover, .drop-item[aria-selected="true"] {
  --ink-3: var(--ink-2);
}

/* 数字对齐是这个产品的基本功：凡是列里的数都要等宽 */
.num, td.num, th.num, .kpi-v, .rank, .metric, time {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.35; text-wrap: balance; }
h1 { font-size: var(--fs-h1); letter-spacing: -0.02em; }
h2 { font-size: var(--fs-h2); letter-spacing: -0.01em; }
h3 { font-size: var(--fs-h3); }

p { margin: 0 0 var(--s3); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ============================================================
   3. 外壳与导航（取代四套 nav / 三种宽度 / 无当前页指示）
   ============================================================ */

.shell { max-width: var(--shell-max); margin: 0 auto; padding: 0 var(--shell-pad); }

.topbar {
  position: sticky; top: 0; z-index: 40;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.topbar-in {
  max-width: var(--shell-max); margin: 0 auto; padding: 0 var(--shell-pad);
  display: flex; align-items: center; gap: var(--s5); min-height: 52px;
}

/* 品牌：不用渐变方块。两个色点即是清华紫与北大红——产品的全部内容 */
.brand { display: flex; align-items: baseline; gap: var(--s2); text-decoration: none; flex: none; }
.brand-mark { display: inline-flex; gap: 3px; align-self: center; }
.brand-mark i { width: 6px; height: 6px; border-radius: 50%; display: block; }
.brand-mark i:first-child { background: var(--thu); }
.brand-mark i:last-child  { background: var(--pku); }
.brand-name { font-size: var(--fs-lg); font-weight: 600; letter-spacing: -0.01em; }
.brand-sub  { font-size: var(--fs-micro); color: var(--ink-3); letter-spacing: .04em; }

.nav { display: flex; gap: var(--s1); flex: 1; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.nav::-webkit-scrollbar { display: none; }
.nav a, .nav button {
  appearance: none; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: var(--fs-base); color: var(--ink-2);
  padding: var(--s2) var(--s3); border-radius: var(--r);
  text-decoration: none; white-space: nowrap;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.nav a:hover, .nav button:hover { color: var(--ink); background: var(--surface-2); }
/* 当前页指示——原来全站四套导航一个都没有 */
.nav a[aria-current="page"], .nav button[aria-pressed="true"] {
  color: var(--ink); font-weight: 600; border-bottom-color: var(--ink);
  border-radius: var(--r) var(--r) 0 0;
}

@media (max-width: 720px) {
  .topbar-in { flex-wrap: wrap; gap: var(--s3); padding-top: var(--s2); padding-bottom: var(--s2); }
  /* 必须是 flex-basis 不是 width：.nav 上的 flex:1 展开是 flex:1 1 0%，
     flex-basis:0% 在主轴上压过 width（width 只在 basis 为 auto 时参与计算）。
     实测未修时 375px 下 .nav 只有 97px——五个导航项挤在一段没有滚动条
     提示的横滑带里，绝大多数读者不会知道它能滑。修后 327px。 */
  .nav { order: 3; flex-basis: 100%; }
}

/* ============================================================
   4. 搜索
   ============================================================ */

.search { position: relative; flex: none; width: min(320px, 40vw); }
.search-lg { width: 100%; }

.input {
  width: 100%; font: inherit; font-size: var(--fs-base);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r);
  padding: var(--s2) var(--s3);
}
.search-lg .input { font-size: var(--fs-lg); padding: var(--s4) var(--s4); }
.input::placeholder { color: var(--ink-3); }
.input:focus { border-color: var(--ink-3); }

/* 下拉：每行必须带消歧信息（校徽 + 院系 + 篇数），这是搜索能不能用的关键 */
.drop {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 50;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r); box-shadow: 0 8px 24px rgba(0,0,0,.10);
  max-height: 60vh; overflow-y: auto; padding: var(--s1);
}
.drop-item {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: var(--s2); padding: var(--s2) var(--s3); border-radius: var(--r);
  cursor: pointer; text-decoration: none;
}
.drop-item:hover, .drop-item[aria-selected="true"] { background: var(--surface-2); }
.drop-name { font-size: var(--fs-base); }
.drop-name em { font-style: normal; color: var(--ink-3); margin-left: var(--s2); font-size: var(--fs-small); }
.drop-meta { font-size: var(--fs-small); color: var(--ink-3); text-align: right; white-space: nowrap; }

/* ============================================================
   5. 校籍徽章 —— 全站唯二的高饱和色，只在这里出现
   ============================================================ */

.school {
  font-size: var(--fs-micro); line-height: 1.5; padding: 1px 5px;
  border-radius: 2px; border: 1px solid currentColor; flex: none;
  letter-spacing: .02em;
}
.school.thu { color: var(--thu); background: var(--thu-bg); }
.school.pku { color: var(--pku); background: var(--pku-bg); }
/* 无校籍：不留空、不假装，明确标注。审计发现榜单混入无清北归属的学者却毫无标识 */
.school.none { color: var(--ink-3); background: var(--surface-2); border-style: dashed; }

/* ============================================================
   6. 卡片与版块
   ============================================================ */

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: var(--s4) var(--s5);
}
/* 不用左侧色条——那是 AI 生成设计的通病，且会和校籍色打架 */

.sec-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s3); padding-bottom: var(--s2); margin-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
}
.sec-title { font-size: var(--fs-lg); font-weight: 600; }
.sec-note  { font-size: var(--fs-small); color: var(--ink-3); }

/* 小标签：11px + 字距 + 三级墨色。中文靠这三样造层级，不靠字重 */
.label {
  font-size: var(--fs-micro); color: var(--ink-3);
  letter-spacing: .08em; text-transform: uppercase;
}
.label-zh { font-size: var(--fs-micro); color: var(--ink-3); letter-spacing: .12em; }

/* ============================================================
   7. KPI
   ============================================================ */

.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(108px, 1fr)); gap: 1px;
        background: var(--line); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.kpi   { background: var(--surface); padding: var(--s3) var(--s4); }
.kpi-v { display: block; font-size: var(--fs-h3); font-weight: 600; letter-spacing: -0.01em; }
.kpi-k { font-size: var(--fs-micro); color: var(--ink-3); }
.kpi-v small { font-size: var(--fs-small); font-weight: 400; color: var(--ink-3); margin-left: 2px; }

/* ============================================================
   8. 表格
   ============================================================ */

/* 宽表必须自带滚动容器——审计实测 person 页论文表把 479px 硬塞进 323px 卡片，
   整页横向溢出 153px。任何表格都要包在这个里面。 */
.tblwrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }
.tbl th, .tbl td { padding: var(--s2) var(--s3); text-align: left; vertical-align: top;
                   border-bottom: 1px solid var(--line); }
.tbl th { font-size: var(--fs-micro); font-weight: 500; color: var(--ink-3);
          letter-spacing: .06em; background: var(--surface-2);
          position: sticky; top: 0; z-index: 1; }
.tbl tbody tr:hover { background: var(--surface-2); }
.tbl td.num, .tbl th.num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl .t-dim { color: var(--ink-3); font-size: var(--fs-small); }

/* 作者位次徽章：authorships.position 全库 100% 填充却零使用。
   这是天眼查「实际控制人」标记的学术版——一眼看出哪些论文是这个人主导的。 */
.pos { font-size: var(--fs-micro); padding: 0 4px; border-radius: 2px;
       border: 1px solid var(--line-strong); color: var(--ink-2); white-space: nowrap; }
.pos.first { border-color: var(--ink-2); color: var(--ink); font-weight: 600; }
.pos.corr  { border-color: var(--ink-2); color: var(--ink); font-weight: 600; }

/* ============================================================
   9. 榜单 / 列表行
   ============================================================ */

.board { display: flex; flex-direction: column; }
.row {
  display: grid; grid-template-columns: 22px auto 1fr auto; align-items: center;
  gap: var(--s2); padding: var(--s2) var(--s2); border-bottom: 1px solid var(--line);
  text-decoration: none; font-size: var(--fs-base);
}
.row:last-child { border-bottom: 0; }
.row:hover { background: var(--surface-2); }
.rank { font-size: var(--fs-small); color: var(--ink-3); text-align: right;
        font-variant-numeric: tabular-nums; }
.row-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-name em { font-style: normal; color: var(--ink-3); font-size: var(--fs-small); margin-left: var(--s2); }
.metric { font-size: var(--fs-base); color: var(--ink-2); font-variant-numeric: tabular-nums; }
.metric u { text-decoration: none; font-size: var(--fs-micro); color: var(--ink-3); margin-left: 2px; }

/* ============================================================
   10. 标签 / 条形
   ============================================================ */

.tag {
  display: inline-block; font-size: var(--fs-small); color: var(--ink-2);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r); padding: 1px var(--s2); text-decoration: none;
}
a.tag:hover { border-color: var(--line-strong); color: var(--ink); }
.tag b { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }

.tags { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s2); }

/* 条形。display:block 是必须的——审计实测原 .fill 是 inline span，
   width/height 被 CSS 规范忽略成 0×0，全站 27 条条形图一条都没画出来。 */
.bar { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center;
       gap: var(--s3); padding: 3px 0; font-size: var(--fs-base); }
.bar-track { display: block; height: 14px; background: var(--surface-2); border-radius: 2px; overflow: hidden; }
.bar-fill  { display: block; height: 100%; background: var(--ink-3); border-radius: 2px; }
.bar-fill.thu { background: var(--thu); }
.bar-fill.pku { background: var(--pku); }
.bar-v { font-size: var(--fs-small); color: var(--ink-3); font-variant-numeric: tabular-nums;
         text-align: right; min-width: 3.5em; }

/* 迷你逐年柱状图 —— works_by_year 后端每次都在算，前端一直没画 */
.spark { display: flex; align-items: flex-end; gap: 2px; height: 34px; }
.spark i { display: block; flex: 1; min-width: 3px; background: var(--ink-3);
           border-radius: 1px 1px 0 0; }
.spark i.hi { background: var(--ink); }
.spark-x { display: flex; justify-content: space-between; font-size: var(--fs-micro);
           color: var(--ink-3); margin-top: 2px; font-variant-numeric: tabular-nums; }

/* ============================================================
   11. 提示条 —— 红色留给北大，风险一律走琥珀
   ============================================================ */

.notice {
  display: flex; gap: var(--s3); align-items: flex-start;
  font-size: var(--fs-base); border: 1px solid var(--line);
  border-radius: var(--r); padding: var(--s3) var(--s4); background: var(--surface);
}
.notice-k { font-size: var(--fs-micro); letter-spacing: .06em; flex: none;
            padding: 1px 5px; border-radius: 2px; }
.notice.warn { border-color: color-mix(in srgb, var(--warn) 32%, var(--line)); background: var(--warn-bg); }
.notice.warn .notice-k { color: var(--warn); border: 1px solid currentColor; }
.notice.ok   { border-color: color-mix(in srgb, var(--ok) 32%, var(--line)); background: var(--ok-bg); }
.notice.ok .notice-k { color: var(--ok); border: 1px solid currentColor; }
.notice p:last-child { margin: 0; }

/* 证据等级：这个产品的立身之本是可核验，所以判定必须带等级而不是只给答案 */
.grade { font-size: var(--fs-micro); padding: 0 5px; border-radius: 2px;
         border: 1px solid currentColor; white-space: nowrap; }
.grade.confirmed { color: var(--ok); }
.grade.likely    { color: var(--ink-2); }
.grade.inferred  { color: var(--warn); }
.grade.ambiguous { color: var(--ink-3); border-style: dashed; }

/* ============================================================
   12. 按钮
   ============================================================ */

.btn {
  appearance: none; font: inherit; font-size: var(--fs-base); cursor: pointer;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r);
  padding: var(--s2) var(--s3); text-decoration: none; display: inline-block;
}
.btn:hover { background: var(--surface-2); }
.btn-primary { background: var(--ink); color: var(--ground); border-color: var(--ink); }
.btn-primary:hover { background: var(--ink-2); border-color: var(--ink-2); }
.btn-quiet { border-color: transparent; color: var(--ink-2); }
.btn-quiet:hover { border-color: var(--line); color: var(--ink); }

/* ============================================================
   13. 图谱面板
   ============================================================ */

.canvas { position: relative; background: var(--surface);
          border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.canvas svg { display: block; width: 100%; height: 100%; touch-action: none; }

.legend {
  position: absolute; left: var(--s3); bottom: var(--s3);
  display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3);
  font-size: var(--fs-micro); color: var(--ink-3);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--line); border-radius: var(--r); padding: var(--s2) var(--s3);
  max-width: calc(100% - var(--s6));
}
.legend b { font-weight: 500; color: var(--ink-2); }

.zoom { position: absolute; right: var(--s3); top: var(--s3);
        display: flex; flex-direction: column; gap: var(--s1); }
.zoom button {
  width: 28px; height: 28px; display: grid; place-items: center;
  font: inherit; font-size: var(--fs-base); cursor: pointer;
  color: var(--ink-2); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r);
}
.zoom button:hover { background: var(--surface-2); color: var(--ink); }

/* ============================================================
   14. 空态 / 加载
   ============================================================ */

.empty { color: var(--ink-3); font-size: var(--fs-base); padding: var(--s6) var(--s4);
         text-align: center; }
/* 空态文案必须面向用户。原文案写着「请先跑 ingest + derive」——那是开发者备忘 */

.skel { background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
        background-size: 200% 100%; animation: skel 1.4s linear infinite; border-radius: 2px; }
@keyframes skel { to { background-position: -200% 0; } }

/* ============================================================
   15. 页脚
   ============================================================ */

.foot {
  border-top: 1px solid var(--line); margin-top: var(--s8);
  padding: var(--s5) 0 var(--s7);
  font-size: var(--fs-small); color: var(--ink-3); line-height: 1.9;
}
.foot a { text-decoration-color: var(--line-strong); }

/* ============================================================
   16. 工具类（只保留真正高频的几个，不做原子化框架）
   ============================================================ */

.stack   { display: flex; flex-direction: column; gap: var(--s4); }
.stack-s { display: flex; flex-direction: column; gap: var(--s2); }
.hstack  { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.grid2   { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--s4); }
.dim     { color: var(--ink-3); }
.mono    { font-family: var(--mono); font-size: var(--fs-small); }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
           overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* graph.js 已在用 .gz / .gz-btn 这组类名（该文件已完成重构并入库，
   不为改名去动它）。这里给出等价样式，与 .zoom 同源。 */
.gz { position: absolute; right: var(--s3); top: var(--s3);
      display: flex; flex-direction: column; gap: var(--s1); }
.gz-btn {
  width: 28px; height: 28px; display: grid; place-items: center;
  font: inherit; font-size: var(--fs-base); cursor: pointer;
  color: var(--ink-2); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r);
}
.gz-btn:hover { background: var(--surface-2); color: var(--ink); }
