/* ═══════════════════════════════════════════════════════════
   黑白极简设计系统（2026）
   ───────────────────────────────────────────────────────────
   原则：
   - 纯黑白灰为主，不出现任何装饰性彩色
   - 深度用多层柔和阴影表达，而不是硬边框
   - 悬浮层/导航/模态用毛玻璃建立层次
   - 动效统一 220ms / ease-out 系曲线
   - 语义色（成功/失败/警告/信息）保留但大幅降饱和
   ═══════════════════════════════════════════════════════════ */

:root {
  /* ── 基底：纯黑白 + 中性灰阶 ── */
  --bg: #f5f5f5;
  --surface: #ffffff;
  --surface-2: #fafafa;
  --border: #e0e0e0;
  --border-strong: #d4d4d4;

  /* ── 文字：黑 → 深灰 → 中灰 ── */
  --ink: #000000;
  --ink-2: #333333;
  --ink-3: #8a8a8a;

  /* ── 主色：纯黑。所有强调态都用它，不再有其他品牌色 ── */
  --brand: #000000;
  --brand-2: #1a1a1a;
  --brand-soft: #f0f0f0;
  --brand-ring: rgba(0, 0, 0, .10);

  /* ── 语义色：降饱和，避免在黑白色系里跳出来 ── */
  --ok: #4a6b52;
  --ok-soft: #f0f4f1;
  --warn: #7d6a45;
  --warn-soft: #f7f4ee;
  --danger: #8f4a44;
  --danger-soft: #f9f0ef;
  --info: #4a5568;
  --info-soft: #f0f2f6;

  /* ── 形状：卡片 16 / 基础 12 / 按钮 10 ── */
  --radius-card: 16px;
  --radius: 12px;
  --radius-sm: 10px;
  --radius-xs: 8px;
  --radius-pill: 999px;

  /* ── 阴影：多层柔和，轻量感 ── */
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, .04);
  --shadow: 0 4px 20px rgba(0, 0, 0, .08);
  --shadow-lg: 0 12px 40px rgba(0, 0, 0, .12);

  /* ── 毛玻璃 ── */
  --glass-bg: rgba(255, 255, 255, .78);
  --glass-blur: blur(16px);

  /* ── 动效：统一曲线与时长 ── */
  --ease: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --dur: 220ms;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 14px/1.6 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter",
        "HarmonyOS Sans", "Segoe UI", "PingFang SC", "Hiragino Sans GB",
        "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  color: var(--ink);
  text-decoration: none;
  transition: opacity var(--dur) var(--ease);
}
a:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ── 排版：字重克制，靠字号与灰度拉层级，行高 ≥1.5 ── */
h1, h2, h3, h4 {
  margin: 0 0 .6em;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -.02em;
}
h1 { font-size: 24px; line-height: 1.25; letter-spacing: -.03em; }
h2 { font-size: 17px; }
h3 { font-size: 15px; }
h4 { font-size: 13px; }

p { margin: 0 0 .9em; line-height: 1.65; }

/* 页面引导语 */
.page-lead {
  margin: 0;
  color: var(--ink-3);
  font-size: 13px;
  line-height: 1.6;
  /* 引导语已移出 sticky 顶栏（见 base.html 的 .lead-band），
     不再需要强制单行——折行不会再把吸顶栏撑高。
     只限制一个阅读舒适的最大宽度，避免宽屏下一行拉到 1600px。 */
  max-width: 76ch;
}
/* 引导语栏：与内容区同宽居中，位于顶栏之下、内容之上。
   空的时候不占位（模板里已判断 self.subheading() 是否为空）。

   注意不要在这里再写 calc((100% - 1600px)/2 ...)：padding 的百分比
   是相对包含块算的，配合 max-width 会双重收窄，内容区反而比 .topbar
   窄一截。跟 .content 一样用「限宽 + 定值 padding」最省心。 */
.lead-band {
  width: 100%;
  max-width: 1600px;
  margin: 0 auto;
  padding: 14px 32px 0;
}
.lead-band.is-empty { display: none; }
/* 顶栏中间的标题区：撑满剩余宽度。
   不设 flex 时它是 flex: 0 1 auto，宽度由内容决定，
   1280px 的视口下也只占 434px，引导语自然被挤到折行。 */
.topbar-title {
  flex: 1 1 auto;
  min-width: 0;
}

code, pre, .mono { font-family: var(--mono); font-size: 12px; }

/* ═══════════ 布局 ═══════════ */
.shell { display: flex; min-height: 100vh; }

/* ═══════════ 侧边栏 ═══════════ */
/* 毛玻璃质感：半透明白底 + 背景模糊，与页面背景融为一体，
   层次靠模糊和极浅阴影，不用重色块。 */
.sidebar {
  width: 240px;
  flex: 0 0 240px;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--ink-2);
  display: flex;
  flex-direction: column;
  padding: 0;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
  border-right: 1px solid var(--border);
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
  z-index: 30;
}
.sidebar::-webkit-scrollbar { width: 4px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 2px;
}
.sidebar::-webkit-scrollbar-thumb:hover { background: #bdbdbd; }

/* ── 品牌区 ── */
.sidebar .brand {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 24px 20px 20px;
}
.sidebar .brand .dot {
  width: 30px;
  height: 30px;
  flex: 0 0 30px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  background: var(--brand);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
}
.sidebar .brand-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.sidebar .brand-name {
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.25;
}
.sidebar .brand-sub {
  color: var(--ink-3);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
}

/* ── 分组 ── */
.sidebar .nav-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 0 12px;
}
.sidebar .nav-label {
  color: var(--ink-3);
  font-size: 11px;
  font-weight: 500;
  /* 上方 24 / 下方 8：标签离「自己的菜单」近、离「上一组」远，
     分组边界才一眼分明。原来上下都偏松（16/8），两组看起来像一组。 */
  padding: 24px 12px 8px;
}
/* 第一个分组不需要那么大的上方间距——上面就是品牌区，
   再顶 24 会把品牌和菜单拉得太开。 */
.sidebar .nav-group:first-of-type .nav-label { padding-top: 8px; }

/* ── 菜单项 ── */
.sidebar a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border-radius: var(--radius-xs);
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 500;
  /* 固定行高成整数：13px × 1.5 = 19.5 会让每行高度落在 35.5px 这种半像素上，
     整列导航累积下来基线会轻微跳动，鼠标扫过时看起来在抖。
     取 20px，行高 = 8+8 padding + 20 = 36px，正好是规范里的导航行高。 */
  line-height: 20px;
  text-decoration: none;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.sidebar a:hover {
  background: rgba(0, 0, 0, .045);
  color: var(--ink);
  text-decoration: none;
}
.sidebar a:hover svg.ico { color: var(--ink); }

/* 选中：浅灰底 + 黑字黑图标，不用彩色 */
.sidebar a.active {
  background: rgba(0, 0, 0, .07);
  color: var(--ink);
  font-weight: 600;
}
.sidebar a.active svg.ico { color: var(--ink); }

/* ── 图标 ── */
.sidebar svg.ico {
  /* 16.5 这种半像素在 1x 屏上会被浏览器取整，图标边缘发虚；
     统一到 16px，与 4px 网格和 8px 图标间距一起对齐基线。 */
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  color: var(--ink-3);
  transition: color var(--dur) var(--ease);
}
.sidebar .nav-text { min-width: 0; }

/* ── 徽章：黑底白字胶囊，去色后的计数气泡 ── */
.sidebar .nav-badge {
  margin-left: auto;
  background: var(--brand);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  min-width: 26px;
  height: 20px;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  padding: 0 8px;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  align-content: center;
  justify-content: center;
  text-align: center;
}

.sidebar .spacer { flex: 1; min-height: 20px; }

/* ── 底部账号区 ── */
.sidebar .foot {
  padding: 12px;
  border-top: 1px solid var(--border);
}
.sidebar .foot-card {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border-radius: var(--radius-xs);
  transition: background var(--dur) var(--ease);
}
.sidebar .foot-card:hover { background: rgba(0, 0, 0, .045); }
.sidebar .avatar {
  width: 32px;
  height: 32px;
  flex: 0 0 32px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  user-select: none;
}
.sidebar .foot-text { min-width: 0; flex: 1; }
.sidebar .foot-name {
  color: var(--ink);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
}
.sidebar .foot-meta {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin-top: 2px;
  color: var(--ink-3);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
}
.sidebar .foot-points {
  color: var(--ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.sidebar .foot-unit { color: var(--ink-3); }
.sidebar .foot-sep { color: #cfcfcf; }
.sidebar .foot-role { color: var(--ink-3); }
.sidebar .foot-role.is-admin { color: var(--ink); font-weight: 600; }

.sidebar .foot-logout {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.sidebar .foot-logout:hover {
  background: rgba(0, 0, 0, .07);
  color: var(--ink);
}
.sidebar .foot-logout svg.ico { width: 15px; height: 15px; }

/* ═══════════ 主区域 ═══════════ */
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* 顶部栏：毛玻璃吸顶，内容跟随内容区居中网格 */
.topbar {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 20px max(32px, calc((100% - 1600px) / 2 + 32px));
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 20;
}
.topbar h1 {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -.025em;
}
.topbar .right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding-top: 2px;
}

/* 内容区：限宽居中 */
.content {
  padding: 28px 32px 72px;
  width: 100%;
  max-width: 1600px;
  margin-left: auto;
  margin-right: auto;
}
/* 认证页（注册/重置密码）用：这些页面自己用 .auth-wrap 做整屏居中，
   .content 的上下 padding 会额外撑高页面，在 900px 高的视口里
   就会顶出一条本来不需要的纵向滚动条。 */
.content-flush {
  padding: 0;
  max-width: none;
}

/* ═══════════ 卡片 ═══════════ */
/* 白底 + 16px 圆角 + 多层柔和阴影，边框极浅作为兜底 */
.card {
  background: var(--surface);
  border: 1px solid rgba(0, 0, 0, .055);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  margin-bottom: 20px;
  overflow: hidden;
  transition: box-shadow var(--dur) var(--ease);
}
.card > .head {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--surface);
}
.card > .head h2, .card > .head h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
}
.card > .head .actions {
  margin-left: auto;
  display: flex;
  gap: 8px;
  align-items: center;
}
.card > .body { padding: 20px; }
.card > .body.flush { padding: 0; }

/* ═══════════ 可折叠卡片 ═══════════ */
/* 用原生 details/summary 实现，不依赖 JS；默认收起只留头部。
   参考「鉴权」这类配一次就不再看的区块——展开着占地方。 */
.card-fold > summary.head {
  cursor: pointer;
  list-style: none;
  user-select: none;
  transition: background var(--dur) var(--ease);
}
/* 去掉浏览器默认的三角标记 */
.card-fold > summary.head::-webkit-details-marker { display: none; }
.card-fold > summary.head::marker { content: ""; }
.card-fold > summary.head:hover { background: var(--surface-2); }
/* 收起时头部就是整张卡的底边，不再画分隔线 */
.card-fold:not([open]) > summary.head { border-bottom: 0; }

/* 「展开 / 收起」提示文字，跟着状态切换 */
.card-fold .fold-hint {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3);
}
.card-fold .fold-hint::after { content: "展开"; }
.card-fold[open] .fold-hint::after { content: "收起"; }

/* 设置页瀑布流：卡片按列填充，各自保持自身高度 */
.settings-flow { columns: 2; column-gap: 20px; }
.settings-flow > .card {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}
@media (max-width: 1240px) {
  .settings-flow { columns: 1; }
}

.grid { display: grid; gap: 16px; }
.grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* ── 指标卡 ── */
.stat {
  background: var(--surface);
  border: 1px solid rgba(0, 0, 0, .055);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  padding: 20px 20px;
}
.stat .label {
  color: var(--ink-3);
  font-size: 12px;
  font-weight: 500;
}
.stat .value {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -.03em;
  margin-top: 8px;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.stat .hint { color: var(--ink-3); font-size: 12px; margin-top: 4px; }
.stat .stat-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  flex-wrap: wrap;
}
.stat .stat-foot .hint { margin-top: 0; }
/* 高亮指标卡：黑底白字，去色后的「主色卡」 */
.stat.brand {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.stat.brand .label, .stat.brand .hint { color: rgba(255, 255, 255, .72); }

/* ═══════════ 表单 ═══════════ */
.field { margin-bottom: 16px; }
.field > label {
  display: block;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-2);
  margin-bottom: 4px;
}
.field > label .req { color: var(--ink); margin-left: 2px; }
.field .help {
  font-size: 12px;
  color: var(--ink-3);
  margin-top: 4px;
  line-height: 1.6;
}
.field.inline { display: flex; align-items: center; gap: 12px; }
.field.inline > label { margin: 0; }

/* 在线调试：参数名与输入框同一行。
   键名宽度由内容决定（不设 min-width），避免短参数名后面拖出一大截空白；
   输入框吃掉剩余空间。 */
.field-inline-play {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}
.field-inline-play > label {
  flex: 0 0 auto;
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 4px;
  white-space: nowrap;
}
.field-inline-play > label .inline-code {
  white-space: nowrap;
}
.field-inline-play > input,
.field-inline-play > textarea {
  flex: 1;
  min-width: 0;
}
/* 窄屏放不下就竖排，避免输入框被挤成一条缝 */
@media (max-width: 620px) {
  .field-inline-play {
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
  }
  .field-inline-play > label { min-width: 0; max-width: none; }
}

input[type=text], input[type=email], input[type=password], input[type=number],
input[type=search], input[type=url], select, textarea {
  width: 100%;
  padding: 8px 12px;
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-ring);
}
input::placeholder, textarea::placeholder { color: #b0b0b0; }
textarea { resize: vertical; min-height: 76px; }
select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%23707070' d='M0 0h10L5 6z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}

/* ═══════════ 自定义下拉菜单 ═══════════ */
/* 原生 select 被藏起来但仍保留在 DOM 里做数据源，
   所以表单提交与 JS 读写完全不受影响。 */
.sel-wrap {
  position: relative;
  display: inline-flex;
  width: 100%;
  min-width: 0;
}
.sel-native {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
.sel-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 8px 12px;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  text-align: left;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.sel-trigger:hover { border-color: var(--ink-3); }
.sel-wrap.open .sel-trigger {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-ring);
}
.sel-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sel-arrow {
  flex: 0 0 auto;
  color: var(--ink-3);
  transition: transform var(--dur) var(--ease);
}
.sel-wrap.open .sel-arrow { transform: rotate(180deg); }

/* 弹层：挂到 body 上并用 fixed 定位，
   否则会被 .card 的 overflow:hidden 裁掉 */
.sel-panel {
  position: fixed;
  z-index: 950;
  max-height: 280px;
  overflow-y: auto;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 10px 32px rgba(0, 0, 0, .14);
  animation: sel-in 140ms var(--ease);
}
@keyframes sel-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
.sel-option {
  display: block;
  width: 100%;
  padding: 8px 12px;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  text-align: left;
  color: var(--ink-2);
  background: transparent;
  border: 0;
  border-radius: var(--radius-xs);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.sel-option:hover { background: var(--surface-2); color: var(--ink); }
.sel-option.on {
  background: rgba(0, 0, 0, .06);
  color: var(--ink);
  font-weight: 600;
}
.sel-option.disabled { opacity: .45; cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) {
  .sel-panel { animation: none; }
}
input[type=checkbox] {
  width: 15px;
  height: 15px;
  accent-color: var(--brand);
  cursor: pointer;
}
input:disabled, select:disabled, textarea:disabled {
  background: #f2f2f2;
  color: var(--ink-3);
  cursor: not-allowed;
}

.row { display: flex; gap: 12px; }
.row > * { flex: 1; min-width: 0; }
.row.tight { gap: 8px; }

/* ═══════════ 按钮 ═══════════ */
/* 主按钮纯黑，次按钮白底浅边，全部 10px 圆角 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 16px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              transform 120ms var(--ease),
              box-shadow var(--dur) var(--ease);
}
.btn:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--ink);
  text-decoration: none;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }
.btn.primary {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.btn.primary:hover {
  background: #1f1f1f;
  border-color: #1f1f1f;
  box-shadow: var(--shadow-sm);
}
.btn.danger {
  color: var(--danger);
  border-color: #ddd0cf;
  background: var(--surface);
}
.btn.danger:hover { background: var(--danger-soft); border-color: #cdb8b6; }
.btn.ghost {
  border-color: transparent;
  background: transparent;
  color: var(--ink-2);
}
.btn.ghost:hover { background: rgba(0, 0, 0, .05); color: var(--ink); }
.btn.sm { padding: 8px 12px; font-size: 12px; }
.btn.block { width: 100%; }
.btn.lg { padding: 12px 24px; font-size: 15px; }
.btn-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
/* 表单底部主操作放右侧 */
.btn-row.end { justify-content: flex-end; }

/* ═══════════ 表格 ═══════════ */
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th {
  text-align: left;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3);
  padding: 12px 16px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
tbody td {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background var(--dur) var(--ease); }
tbody tr:hover { background: var(--surface-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.nowrap, th.nowrap { white-space: nowrap; }

/* ═══════════ 在线调试表格 ═══════════ */
/* 参数名 + 问号提示 + 输入框，同一行。说明不再单独占一行。 */
.play-table { margin-bottom: 4px; }
.play-table thead th {
  text-transform: none;
  letter-spacing: 0;
  font-size: 12px;
}
.play-table tbody td {
  padding: 12px 16px;
  vertical-align: middle;
}
.play-table tbody tr:hover { background: transparent; }
.play-table .play-key {
  width: 1%;
  white-space: nowrap;
  padding-top: 12px;
  padding-bottom: 12px;
}
.play-table .play-key > * { vertical-align: middle; }
.play-table .play-input { min-width: 0; }
.play-table .play-input input,
.play-table .play-input textarea {
  font-size: 13px;
}
.play-table .play-req {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

/* ── 参数说明问号：悬浮 / 聚焦时弹出提示 ── */
.play-tip {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  margin-left: 8px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  color: var(--ink-3);
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  cursor: help;
  user-select: none;
  transition: border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.play-tip:hover,
.play-tip:focus-visible {
  border-color: var(--ink-3);
  color: var(--ink);
  outline: none;
}

/* 提示气泡：默认隐藏，悬浮或键盘聚焦时出现 */
.play-tip-body {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%) translateY(2px);
  z-index: 60;
  width: max-content;
  max-width: 260px;
  padding: 8px 12px;
  border-radius: var(--radius-xs);
  background: #1a1a1a;
  color: #fff;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.55;
  white-space: normal;
  word-break: break-word;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.play-tip:hover .play-tip-body,
.play-tip:focus-visible .play-tip-body {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
/* 小三角，指向问号 */
.play-tip-body::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: #1a1a1a;
}

/* ═══════════ 徽标 ═══════════ */
/* 接口状态：点 + 文字，全站统一。不用彩色药丸——黑白体系里颜色扛不住语义，
   靠「实心 / 空心 / 浅灰」三种填充就能一眼分清，也更像设备面板上的指示灯。 */
.status-cell {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--ink-2);
  white-space: nowrap;
}

/* 去色：默认为灰阶药丸，语义变体用降饱和的浅底深字 */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 500;
  background: #f0f0f0;
  color: var(--ink-2);
  white-space: nowrap;
}
.badge.ok { background: var(--ok-soft); color: var(--ok); }
.badge.fail { background: var(--danger-soft); color: var(--danger); }
.badge.warn { background: var(--warn-soft); color: var(--warn); }
.badge.info { background: var(--info-soft); color: var(--info); }
.badge.brand { background: var(--brand); color: #fff; }
/* 免费接口：亮绿底 + 深墨字。
   这个绿比主色更跳，只在 0 积分的卡片上出现，用作「不花钱」的视觉锚点。
   亮底必须配深色文字才看得清（白字对比度不足）。 */
.badge.free {
  background: #8cff2e;
  color: #17280a;
  font-weight: 500;
}
.badge.mono { font-family: var(--mono); font-size: 11px; }

/* ═══════════ 提示条 ═══════════ */
.alert {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  line-height: 1.6;
  margin-bottom: 16px;
  border: 1px solid transparent;
  background: var(--surface-2);
  color: var(--ink-2);
}
.alert.ok { background: var(--ok-soft); color: var(--ok); border-color: #dde7e0; }
.alert.error { background: var(--danger-soft); color: var(--danger); border-color: #ecd9d7; }
.alert.warn { background: var(--warn-soft); color: var(--warn); border-color: #e8e0d2; }
.alert.info { background: var(--info-soft); color: var(--info); border-color: #dde1e8; }
.alert .close {
  margin-left: auto;
  cursor: pointer;
  opacity: .5;
  border: 0;
  background: none;
  font-size: 15px;
  line-height: 1;
  padding: 0;
  color: inherit;
  transition: opacity var(--dur) var(--ease);
}
.alert .close:hover { opacity: 1; }

/* ═══════════ 公告弹窗 ═══════════ */
/* 复用 .modal-mask / .modal-box 的遮罩与卡片基础，这里只覆盖差异部分。
   只在有未读公告时渲染，一次一条。

   重要公告靠更重的左边线和墨色标签区分，不用彩色警示条——
   黑白体系里颜色扛不住「重要程度」这层语义。 */
.announce-box {
  /* 比默认 modal 宽一些：公告正文常有几行说明 */
  max-width: 480px;
  border-left: 3px solid var(--border-strong);
}
.announce-box.important { border-left-color: var(--ink); }
.announce-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.announce-tag {
  flex: 0 0 auto;
  font-size: 11px;
  line-height: 1;
  padding: 3px 6px;
  border-radius: var(--radius-xs);
  background: #f0f0f0;
  color: var(--ink-2);
  font-weight: 500;
}
.announce-box.important .announce-tag {
  background: var(--brand);
  color: #fff;
}
.announce-time { font-size: 12px; color: var(--ink-3); }
.announce-more {
  font-size: 11px;
  color: var(--ink-3);
  padding: 2px 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  margin-left: auto;
}
/* 正文保留换行，纯文字。长公告限高滚动，不让它撑出屏幕 */
.announce-body {
  font-size: 13px;
  line-height: 1.75;
  color: var(--ink-2);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: min(48vh, 360px);
  overflow-y: auto;
}
.announce-form { margin-top: 20px; }

/* 后台列表里的公告标题单元格 */
.announce-cell-title {
  font-weight: 600;
  color: var(--ink);
}

@media (max-width: 620px) {
  .announce-box { max-width: none; }
  .announce-body { max-height: 40vh; }
}

/* ═══════════ 杂项 ═══════════ */
.muted { color: var(--ink-3); }
.text-ok { color: var(--ok); }
.text-warn { color: var(--warn); }
.text-bad { color: var(--danger); }
.small { font-size: 12px; }
.mt-0 { margin-top: 0; } .mt-1 { margin-top: 8px; } .mt-2 { margin-top: 16px; } .mt-3 { margin-top: 24px; }
.mb-0 { margin-bottom: 0; } .mb-1 { margin-bottom: 8px; } .mb-2 { margin-bottom: 16px; }
.flex { display: flex; align-items: center; gap: 12px; }
.flex.wrap { flex-wrap: wrap; }
.flex.between { justify-content: space-between; }
.ml-auto { margin-left: auto; }
.grow { flex: 1; min-width: 0; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* hidden 属性必须真的让元素消失。
   浏览器对 [hidden] 的默认样式是 display:none，但那只是 UA 样式，
   任何作者样式里的 display 都会盖掉它——.api-card-wrap 是 flex、
   .btn 是 inline-flex，于是 el.hidden=true 只改了属性、元素照样显示
   （表现为「筛选没生效，只有高亮变了」）。这里统一兜底。 */
[hidden] { display: none !important; }
/* 视觉隐藏但仍可被辅助技术/表单逻辑使用。
   用 clip 而不是 display:none——后者会让元素不参与表单的隐式提交。 */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

pre.code {
  background: #1a1a1a;
  color: #e8e8e8;
  padding: 16px;
  border-radius: var(--radius-sm);
  overflow-x: auto;
  margin: 0;
  font-size: 12px;
  line-height: 1.7;
  white-space: pre;
}
pre.code.light {
  background: var(--surface-2);
  color: var(--ink);
  border: 1px solid var(--border);
}
.inline-code {
  font-family: var(--mono);
  font-size: 12px;
  background: #f0f0f0;
  padding: 2px 8px;
  border-radius: 5px;
  color: var(--ink-2);
}

.pager {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 16px 20px;
  border-top: 1px solid var(--border);
}
.pager a, .pager span {
  padding: 8px 12px;
  border-radius: var(--radius-xs);
  font-size: 12px;
  border: 1px solid var(--border);
  color: var(--ink-2);
  background: var(--surface);
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.pager a:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
  text-decoration: none;
}
.pager .current {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
  font-weight: 600;
}
.pager .disabled { opacity: .4; pointer-events: none; }
.pager .info {
  margin-left: auto;
  border: 0;
  background: none;
  color: var(--ink-3);
}

.empty {
  padding: 48px 20px;
  text-align: center;
  color: var(--ink-3);
  font-size: 13px;
}
.empty .big { font-size: 28px; margin-bottom: 12px; opacity: .3; }

/* ═══════════ 认证页面 ═══════════ */
/* 原来在背景上叠了两层径向白雾做“卡片浮起”的错觉，这是装饰：
   它不表达任何状态，只是把注意力从表单移开。
   方向是仪器面板，底就该是平的，卡片靠边框和一道浅阴影就够。 */
.auth-wrap {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 32px 16px;
  background: var(--bg);
}
.auth-card {
  width: 100%;
  max-width: 402px;
  background: var(--surface);
  border: 1px solid rgba(0, 0, 0, .06);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
  padding: 32px 32px 28px;
}
.auth-card .brand-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}
.auth-card .brand-row .dot {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm);
  background: var(--brand);
  display: grid;
  place-items: center;
  color: #fff;
  font-weight: 600;
  font-size: 14px;
}
.auth-card .brand-row .name { font-weight: 600; font-size: 15px; }
.auth-card h1 { font-size: 20px; margin-bottom: 8px; }
.auth-card .lead { color: var(--ink-3); font-size: 13px; margin-bottom: 24px; line-height: 1.6; }
.auth-card .bottom {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--ink-3);
  text-align: center;
}
.auth-card.wide { max-width: 660px; }

/* ═══════════ 安装向导 ═══════════ */
.steps { display: flex; gap: 0; margin-bottom: 28px; }
.steps .step {
  flex: 1;
  text-align: center;
  position: relative;
  font-size: 12px;
  color: var(--ink-3);
  transition: color var(--dur) var(--ease);
}
.steps .step .n {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  margin: 0 auto 6px;
  display: grid;
  place-items: center;
  font-weight: 600;
  font-size: 12px;
  background: var(--surface);
  border: 1.5px solid var(--border-strong);
  color: var(--ink-3);
  transition: color var(--dur) var(--ease),
              background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.steps .step::before {
  content: "";
  position: absolute;
  top: 14px;
  left: 0;
  right: 50%;
  height: 1.5px;
  background: var(--border-strong);
  z-index: 0;
  transition: background var(--dur) var(--ease);
}
.steps .step:first-child::before { display: none; }
.steps .step .n { position: relative; z-index: 1; }
.steps .step.active .n {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
  box-shadow: 0 4px 12px rgba(0, 0, 0, .16);
}
.steps .step.active { color: var(--ink); font-weight: 600; }
.steps .step.done .n {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.steps .step.done::before, .steps .step.active::before { background: var(--brand); }

.panel { display: none; }
.panel.active { display: block; animation: fade-slide var(--dur) var(--ease); }
@keyframes fade-slide {
  from { opacity: 0; transform: translateY(5px); }
  to { opacity: 1; transform: none; }
}

.check-list { list-style: none; padding: 0; margin: 0; }
.check-list li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.check-list li:last-child { border-bottom: 0; }
.check-list .ico { width: 18px; flex: 0 0 18px; text-align: center; font-weight: 600; }
.check-list .ico.ok { color: var(--ok); }
.check-list .ico.bad { color: var(--danger); }
.check-list .name { font-weight: 600; min-width: 108px; }
.check-list .val { color: var(--ink-3); font-family: var(--mono); font-size: 12px; }
.check-list .hint {
  margin-left: auto;
  color: var(--danger);
  font-size: 12px;
  text-align: right;
  max-width: 46%;
}

/* ═══════════ API 参数行编辑器 ═══════════ */
/* 4 列：键名 / 说明 / 必填 / 删除（显示名已移除） */
.field-row {
  display: grid;
  grid-template-columns: 1fr 1.8fr 74px 34px;
  gap: 8px;
  align-items: start;
  padding: 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  margin-bottom: 8px;
  transition: border-color var(--dur) var(--ease);
}
.field-row:focus-within { border-color: var(--border-strong); }
.field-row input { font-size: 12px; padding: 8px 8px; }
.field-row .req-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 32px;
}
.field-row .del {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--danger);
  border-radius: var(--radius-xs);
  height: 32px;
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.field-row .del:hover { background: var(--danger-soft); border-color: #d9c4c2; }
.field-row-head {
  display: grid;
  grid-template-columns: 1fr 1.8fr 74px 34px;
  gap: 8px;
  font-size: 11px;
  font-weight: 500;
  color: var(--ink-3);
  padding: 0 11px 5px;
}

/* ═══════════ 公开主页 ═══════════ */
.home-hero {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 28px;
  padding: 20px 24px;
  margin-bottom: 16px;
  background: var(--surface);
  border: 1px solid rgba(0, 0, 0, .055);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
}
.home-hero-num { display: flex; align-items: baseline; gap: 8px; }
.home-hero-value {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.home-hero-label { font-size: 12px; color: var(--ink-3); }
.home-hero-note {
  margin-left: auto;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.6;
  max-width: 46ch;
}

/* ═══════════ 搜索 / 分类筛选栏 ═══════════ */
.api-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-bottom: 16px;
}
.api-filter-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 260px;
  max-width: 420px;
}
.api-filter-icon {
  position: absolute;
  left: 11px;
  color: var(--ink-3);
  pointer-events: none;
}
.api-filter-search input[type=search] {
  width: 100%;
  padding: 8px 12px 8px 32px;
  font-size: 13px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.api-filter-search input[type=search]:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-ring);
}
.api-filter-cat {
  width: auto;
  min-width: 132px;
  padding: 8px 32px 8px 16px;
  font-size: 13px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background-color: var(--surface);
}

/* 原生 select 被替换成自定义菜单后，wrapper 需要继承这些紧凑尺寸。
   不能直接把 .api-filter-cat 套到 wrapper 上——那个 padding 是为
   原生控件设计的，套到外层会让容器比里面的按钮高出一截。 */
.sel-wrap.sel-compact {
  width: auto;
  min-width: 132px;
  padding: 0;
}
.sel-wrap.sel-compact .sel-trigger {
  padding: 8px 16px;
  font-size: 13px;
  border-radius: var(--radius-pill);
}

/* 搜索命中高亮 */
mark {
  background: #ffe58a;
  color: var(--ink);
  padding: 0 2px;
  border-radius: 2px;
}

/* 搜索框右侧的结果计数 */
.api-filter-count {
  font-size: 12px;
  color: var(--ink-3);
  margin-left: 2px;
  font-variant-numeric: tabular-nums;
}

/* ═══════════ 分类侧边目录 + 卡片区 ═══════════ */
/* 两栏：左侧固定宽度目录，右侧卡片自适应。
   目录 sticky 吸顶，卡片多了滚动时分类仍可见。 */
.api-layout {
  display: grid;
  grid-template-columns: 176px minmax(0, 1fr);
  gap: 20px;
  align-items: start;
}
.api-cats {
  position: sticky;
  top: 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 12px;
  background: var(--surface);
  border: 1px solid rgba(0, 0, 0, .055);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
}
.api-cats-title {
  padding: 2px 8px 8px;
  font-size: 11px;
  font-weight: 500;
  color: var(--ink-3);
}
.api-cat {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 8px;
  border-radius: var(--radius-xs);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.api-cat:hover { background: var(--surface-2); color: var(--ink); }
.api-cat.on {
  background: rgba(0, 0, 0, .06);
  color: var(--ink);
  font-weight: 600;
}
.api-cat-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.api-cat-num {
  flex: 0 0 auto;
  min-width: 20px;
  padding: 0 8px;
  height: 18px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: #f0f0f0;
  color: var(--ink-3);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.api-cat.on .api-cat-num { background: var(--brand); color: #fff; }
/* 搜索时没有命中的分类：整项置灰，一眼能看出关键词落在哪个分类 */
.api-cat.is-empty { opacity: .38; }
.api-cat.is-empty .api-cat-num { background: #f0f0f0; color: var(--ink-3); }
.api-cats-empty {
  padding: 8px 8px;
  font-size: 12px;
  color: var(--ink-3);
}
.api-main { min-width: 0; }
.api-noresult { padding: 40px 0; }

/* 窄屏：目录改成横向滚动的胶囊条，不再占左侧一整列 */
@media (max-width: 860px) {
  .api-layout { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .api-cats {
    position: static;
    flex-direction: row;
    overflow-x: auto;
    padding: 8px;
    gap: 8px;
  }
  .api-cats-title { display: none; }
  .api-cat {
    flex: 0 0 auto;
    padding: 8px 12px;
    border-radius: 999px;
    background: var(--surface-2);
  }
}

/* ═══════════ 用户中心：可用接口卡片 ═══════════ */
/* 收藏按钮：叠在卡片右下角，只有五角星本身。
   不放圆形底、不占布局：统计区因此不用留 padding，
   三个数字的可用宽度和没有收藏按钮时完全一致。

   bottom 用 19px 而不是贴着统计盒子：统计盒子含 11px 上内边距和
   1px 上边框，如果按盒子中线对齐，五角星会比「数字+标签」的实际
   内容中线高出 6px（实测 296 vs 302），看着就是偏上。
   内容区高 38px、底边距卡片底 16px，按钮高 32px，
   所以 16 + (38-32)/2 = 19，星星才和统计内容落在同一条中线上。 */
.api-card-wrap .fav-form {
  position: absolute;
  right: 12px;
  bottom: 19px;
  margin: 0;
  z-index: 2;
  line-height: 0;
}
.fav-btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-3);
  cursor: pointer;
  transition: color var(--dur) var(--ease);
}
.fav-btn svg { width: 20px; height: 20px; }
/* 悬停只改颜色，不缩放。图标放大在密集列表里会让相邻行看起来在抖。 */
.fav-btn:hover { color: var(--ink); }
/* 已收藏：星星实心且用主色，比未收藏深一档，扫一眼就能看出状态 */
.fav-btn.on { color: var(--ink); }

/* 图标按钮的「进行中」态：不改内容只降透明度。
   往圆形按钮里塞「处理中…」会把它撑成胶囊、图标被挤歪，
   而收藏这类操作完成后页面立刻跳转，闪一下文字纯属噪音。 */
.is-busy {
  opacity: .5;
  cursor: progress;
  pointer-events: none;
}

/* 收藏按钮改为纯图标叠放后不再占位，统计区不需要预留 padding，
   三个数字的可用宽度和没有收藏按钮时一致。 */

/* 带收藏按钮的卡片：编辑按钮挪到右上角，不和星星打架 */
.api-card-wrap .fav-form ~ .api-card-edit { top: 10px; }

.api-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
  gap: 12px;
  /* 卡片统一等高：
     - align-items: stretch 让同一行内的卡片拉平
     - grid-auto-rows: 1fr 让「所有行」都取最高那一行的高度，
       否则最后一行只有一张卡时会比上面那一行矮一截 */
  align-items: stretch;
  grid-auto-rows: 1fr;
}
/* 包一层是为了放绝对定位的「编辑」按钮，
   所以这一层也要撑满，否则内层 .api-card 拿不到整行的高度 */
.api-card-wrap {
  position: relative;
  display: flex;
  min-width: 0;
}
.api-card-edit {
  position: absolute;
  top: 10px;
  right: 10px;
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 600;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: #fff;
  text-decoration: none;
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.api-card-wrap:hover .api-card-edit { opacity: 1; transform: none; }
.api-card-edit:hover {
  background: #1f1f1f;
  text-decoration: none;
  color: #fff;
}
@media (hover: none) {
  .api-card-edit { opacity: 1; transform: none; }
}

/* 维护中的接口卡片：细斜纹弱化，仍可点击 */
.api-card.is-maint {
  background: repeating-linear-gradient(
    45deg,
    var(--surface),
    var(--surface) 10px,
    #fafafa 10px,
    #fafafa 20px
  );
  border-style: dashed;
  border-color: #d8d8d8;
}
.api-card.is-maint .api-card-name { color: var(--ink-2); }
.maint-tag {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 12px;
  border-radius: var(--radius-xs);
  background: var(--warn-soft);
  border: 1px solid #e8e0d2;
  color: var(--warn);
  font-size: 12px;
  font-weight: 500;
}
/* 状态点：全站统一的接口状态指示。
   在线 = 实心墨点，维护中 = 空心环，已下线 = 浅灰实心。
   用形状和填充区分，而不是靠颜色——黑白色系里颜色无法承载语义。
   不做脉冲动画：维护中是静态事实，不是正在发生的信号，
   一个一直闪的点会让人误以为系统正在处理什么。 */
.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 8px;
  box-sizing: border-box;
}
.status-dot.online {
  background: var(--ink);
}
.status-dot.maint {
  background: transparent;
  border: 1.5px solid var(--warn);
}
.status-dot.offline {
  background: var(--border-strong);
}
/* 维护中标签里的点，沿用同一套语言（空心环） */
.maint-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: transparent;
  border: 1.5px solid var(--warn);
  flex: 0 0 8px;
  box-sizing: border-box;
}
.maint-note {
  font-weight: 500;
  color: #9a8a6d;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
@media (prefers-reduced-motion: reduce) {
  .maint-dot { animation: none; }
}
.api-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  /* 撑满外层容器：否则宽度由内容决定，同一行里会出现一宽一窄 */
  flex: 1;
  min-width: 0;
  background: var(--surface);
  border: 1px solid rgba(0, 0, 0, .06);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  color: var(--ink);
  text-decoration: none;
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.api-card:hover {
  text-decoration: none;
  border-color: rgba(0, 0, 0, .12);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}
.api-card-top {
  display: flex;
  /* 名称、NEW、消耗徽章垂直居中对齐到同一条线。
     原来用 flex-start，名称换行时徽章会顶在上方，三者中线对不齐。 */
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.api-card-name {
  /* 用 flex 居中而不是靠行盒：
     原来 line-height:1.4 让行盒 19.6px 大于字号 14px，
     汉字墨迹在行盒里会偏上（字体 ascent 比 descent 大），
     实测文字中线比徽章高 1px。改成 flex + line-height:1，
     行盒高度收缩到字号，文字墨迹就能与徽章落在同一条中线上。 */
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.01em;
  color: var(--ink);
}

/* 最近 7 天新增的接口：名字后面的 NEW 小标签。
   黑底白字与主色一致，字号小、紧贴名字，不抢视线。
   margin-left 已去掉——父级 .api-card-name 是 flex + gap:6px，
   再留 margin 会变成 12px。 */
.api-new {
  display: inline-block;
  flex: 0 0 auto;
  padding: 2px 4px;
  border-radius: 4px;
  background: var(--brand);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.5;
  vertical-align: 1px;
}
.api-card-desc {
  margin: 0;
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink-3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* 卡片上的分类标签：一个接口可能属于多个分类，逐个列出。
   用小号描边胶囊而不是实底——分类是描述性信息，
   不该和「N 积分/次」这种实底徽章抢注意力。 */
.api-card-cats {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 2px;
}
.api-card-cat {
  font-size: 11px;
  line-height: 1;
  padding: 3px 6px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--border);
  color: var(--ink-3);
  white-space: nowrap;
}
.api-card-stats {
  /* 三等分占满整行：用 grid 而不是 flex，
     flex 下每项宽度由内容决定（"731 ms" 比 "7" 宽），三列宽窄不一。 */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
/* 有收藏按钮时右侧让出 26px，否则星星会压住「平均耗时」
   （实测重叠 13px）。用 .has-fav 而不是 :has() 是为了兼容性；
   游客没有收藏按钮时统计区正常铺满，不留白。 */
.api-card-wrap.has-fav .api-card-stats { padding-right: 24px; }
.api-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  align-items: center;
  text-align: center;
}
.api-stat-num {
  font-size: 17px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.api-stat-num.good { color: var(--ok); }
.api-stat-num.warn { color: var(--warn); }
.api-stat-num.bad { color: var(--danger); }
.api-stat-num.muted { color: var(--ink-3); font-weight: 600; }
.api-stat-label {
  font-size: 11px;
  color: var(--ink-3);
}

/* ═══════════ 密码框内嵌小眼睛 ═══════════ */
.pwd-wrap {
  position: relative;
  display: block;
  width: 100%;
}
.pwd-wrap > input { padding-right: 40px; }
.pwd-eye {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  transition: color var(--dur) var(--ease),
              background var(--dur) var(--ease);
}
.pwd-eye:hover { color: var(--ink); background: #f0f0f0; }
.pwd-eye:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* ═══════════ 可点击的统计卡（卡密筛选） ═══════════ */
.stat-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 20px;
}
.stat-card {
  flex: 1 1 130px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 16px;
  border: 1px solid rgba(0, 0, 0, .06);
  border-radius: var(--radius-card);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  color: var(--ink);
  text-decoration: none;
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
a.stat-card:hover {
  text-decoration: none;
  border-color: rgba(0, 0, 0, .12);
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}
.stat-card.on {
  border-color: var(--brand);
  background: var(--brand);
  color: #fff;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .14);
}
.stat-card.on .stat-card-label { color: rgba(255, 255, 255, .75); }
.stat-card.plain { cursor: default; }
.stat-card-label { font-size: 12px; color: var(--ink-3); font-weight: 500; }
.stat-card-num {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

/* ═══════════ 筛选栏 ═══════════ */
.filter-bar {
  display: flex;
  gap: 12px;
  align-items: flex-end;
  flex-wrap: wrap;
}
.filter-bar .filter-item { display: flex; flex-direction: column; gap: 4px; }
.filter-bar .filter-item label {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3);
}
.filter-bar .filter-item input { width: 200px; }
.filter-bar .filter-item select { width: 130px; }
@media (max-width: 620px) {
  .filter-bar .filter-item input, .filter-bar .filter-item select { width: 100%; }
  .filter-bar .filter-item { flex: 1 1 100%; }
}

/* ═══════════ 弹窗（毛玻璃遮罩） ═══════════ */
.modal-mask {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(0, 0, 0, .32);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  animation: mask-in var(--dur) var(--ease);
}
.modal-mask[hidden] { display: none; }
.modal-box {
  width: 100%;
  max-width: 420px;
  padding: 24px 24px;
  border-radius: var(--radius-card);
  background: rgba(255, 255, 255, .92);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid rgba(0, 0, 0, .06);
  box-shadow: var(--shadow-lg);
  animation: modal-in var(--dur) var(--ease);
}
.modal-box h3 { margin: 0 0 8px; font-size: 17px; }
@keyframes mask-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(-6px) scale(.99); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .modal-box, .modal-mask { animation: none; }
}

/* ═══════════ 涨跌徽章 ═══════════ */
.delta {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* 上涨：降饱和绿；下降：降饱和红棕；持平：中性灰 */
.delta.up { background: var(--ok-soft); color: var(--ok); }
.delta.down { background: var(--danger-soft); color: var(--danger); }
.delta.flat { background: #f0f0f0; color: var(--ink-3); }

/* ═══════════ 请求示例的语言切换 ═══════════ */
.lang-tabs {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  background: #f0f0f0;
  padding: 4px;
  border-radius: var(--radius-pill);
}
.lang-tabs button {
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 8px 12px;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.lang-tabs button:hover { color: var(--ink-2); }
.lang-tabs button.on {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
}

/* ═══════════ 区间切换 ═══════════ */
.range-tabs {
  display: inline-flex;
  gap: 2px;
  background: #f0f0f0;
  padding: 4px;
  border-radius: var(--radius-pill);
}
.range-tabs a {
  font-size: 12px;
  font-weight: 600;
  padding: 8px 12px;
  border-radius: var(--radius-pill);
  color: var(--ink-3);
  text-decoration: none;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease);
  white-space: nowrap;
}
.range-tabs a:hover { color: var(--ink-2); text-decoration: none; }
.range-tabs a.on {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
}

/* ═══════════ 折线图 ═══════════ */
.chart { width: 100%; }
.chart-svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}
.chart-axis {
  font-size: 11px;
  fill: #8a8a8a;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}
.chart-axis-x { font-size: 11px; }
.chart-empty {
  padding: 48px 16px;
  text-align: center;
  color: #8a8a8a;
  font-size: 12px;
}

/* 数据点：默认只显示圆点，提示框隐藏 */
.chart-dot-group .chart-tip {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease);
}
.chart-dot-group:hover .chart-tip { opacity: 1; }
.chart-dot-group:hover circle { r: 5; }

/* 悬停热区 */
.chart-hit { cursor: pointer; }

/* 提示框：白底圆角 + 柔和阴影 */
.chart-tip-box {
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, .12));
}
.chart-tip-label {
  font-size: 11px;
  fill: #8a8a8a;
  font-family: inherit;
}
.chart-tip-value {
  font-size: 13px;
  font-weight: 600;
  fill: #000000;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}
.chart-tip-rate {
  font-size: 11px;
  font-weight: 600;
  /* 原来是绿色，图表里出现第二个高饱和色会抢走主线的注意力。
     成功率是附属信息，用次级灰即可；强调只留给主线本身。 */
  fill: var(--ink-3);
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}

/* ═══════════ 键值列表 ═══════════ */
.kv-list { display: grid; gap: 8px; }
.kv-list .kv {
  display: flex;
  gap: 12px;
  font-size: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
}
.kv-list .kv:last-child { border-bottom: 0; }
.kv-list .k { font-family: var(--mono); color: var(--ink-3); min-width: 110px; }
.kv-list .v { color: var(--ink-2); }

/* ═══════════ 响应式 ═══════════ */
@media (max-width: 900px) {
  .grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid.cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sidebar {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 50;
    transform: translateX(-100%);
    transition: transform var(--dur) var(--ease);
    /* 移动端侧栏需要更高不透明度以保证可读 */
    background: rgba(255, 255, 255, .96);
  }
  .sidebar.open { transform: none; }
  .shell.menu-open::after {
    content: "";
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .3);
    backdrop-filter: blur(4px);
    z-index: 40;
  }
  .content { padding: 20px 16px 48px; }
  .topbar { padding: 12px 16px; }
  /* 窄屏不显示引导语：顶栏是 sticky 的，引导语在这里会折成两行，
     把吸顶栏从 52px 撑到 100px，占掉手机上宝贵的首屏高度。
     引导语只是帮助理解，进去看内容一样能明白，优先保证空间。 */
  .topbar .page-lead { display: none; }
  .menu-toggle { display: inline-flex !important; }
  .field-row, .field-row-head { grid-template-columns: 1fr 1fr; }
  .field-row .req-cell, .field-row .del { height: 32px; }
}
@media (max-width: 620px) {
  .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr; }
  .row { flex-direction: column; }
  .auth-card { padding: 24px 24px; }
  table { font-size: 12px; }
  thead th, tbody td { padding: 8px 12px; }
  .hide-sm { display: none !important; }
}
.menu-toggle { display: none; }

/* ═══════════ 无障碍：尊重减弱动效 ═══════════ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ═══════════ 「我有话说」 ═══════════ */
/* 通用小头像：侧栏那份是 .sidebar .avatar 专属，
   反馈页在正文里也要用，这里补一个可复用的。 */
.avatar.sm {
  width: 30px;
  height: 30px;
  flex: 0 0 30px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  user-select: none;
}

/* ── 话题列表 ── */
.topic-list { display: flex; flex-direction: column; }
/* 三列栅格：标题区（可伸缩）/ 分类（定宽）/ 统计（定宽）。
   原来是 flex，统计块的位置会随标题长度滑动，滚列表时数字忽左忽右。
   写死后两列宽度后，每行的分类和四个数字都在同一条竖线上。 */
.topic-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px 232px;
  align-items: center;
  gap: 16px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
  transition: background var(--dur) var(--ease);
}
.topic-item:last-child { border-bottom: 0; }
.topic-item:hover { background: var(--surface-2); }
/* 置顶行左侧加一道墨线，扫视时能立刻分出「被顶上来的」 */
.topic-item.is-pinned { box-shadow: inset 3px 0 0 var(--ink); }
.topic-main { min-width: 0; }
.topic-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 4px;
}
.topic-title-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 有未读的话题标题略重一点，配合左侧小圆点 */
.topic-item.is-unread .topic-title-text { font-weight: 600; }
.unread-dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand);
}
.topic-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--ink-3);
  min-width: 0;
}
.topic-author { color: var(--ink-2); font-weight: 500; flex: 0 0 auto; }
/* 正文摘要：占满剩余宽度、单行省略，不换行 */
.topic-snippet {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink-3);
}
.topic-snippet mark { background: #fff2a8; color: var(--ink); padding: 0 1px; }

/* 分类列：定宽，保证每行分类名左端对齐 */
.topic-cat {
  font-size: 12px;
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 统计列：四个固定槽位。用 grid 等分而不是 flex，
   否则「1.2 s」和「刚刚」这种不同宽度的内容会把相邻项挤动。 */
.topic-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  align-items: center;
}
.topic-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  text-align: right;
}
.topic-stat-num {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.topic-stat-time {
  font-size: 12px;
  color: var(--ink-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
}
.topic-stat-label {
  font-size: 11px;
  color: var(--ink-3);
  white-space: nowrap;
  line-height: 1.2;
}

/* 置顶 / 已完成 标记：小号方角标签，不用药丸，避免和徽章混为一谈 */
.topic-flag {
  flex: 0 0 auto;
  font-size: 11px;
  line-height: 1;
  padding: 3px 6px;
  border-radius: var(--radius-xs);
  background: var(--brand);
  color: #fff;
}
.topic-flag.done {
  background: var(--border);
  color: var(--ink-2);
}

/* 官方标：管理员发的帖子与回复都带。用描边而不是实底，
   比置顶标签轻一档，不抢标题注意力。 */
.official-tag {
  flex: 0 0 auto;
  font-size: 11px;
  line-height: 1;
  padding: 3px 6px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--ink);
  color: var(--ink);
  font-weight: 500;
}

/* ── 列表页工具栏：分类导航 + 维度筛选 + 搜索 ── */
.fb-toolbar {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 16px;
}
/* 分类：横向药丸，可换行。5 个分类一屏放得下，比下拉框少一次点击 */
.fb-cats {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.fb-cat {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.fb-cat:hover { background: var(--surface-2); color: var(--ink); }
.fb-cat.on {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
  font-weight: 600;
}
.fb-cat-num {
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
.fb-cat.on .fb-cat-num { color: rgba(255, 255, 255, .8); }

/* 筛选行：左侧三档维度切换（分段控件），右侧搜索框 */
.fb-filter {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.fb-scopes {
  display: inline-flex;
  gap: 2px;
  background: #f0f0f0;
  padding: 4px;
  border-radius: var(--radius-pill);
  flex: 0 0 auto;
}
.fb-scope {
  font-size: 12px;
  font-weight: 600;
  padding: 8px 12px;
  border-radius: var(--radius-pill);
  color: var(--ink-3);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.fb-scope:hover { color: var(--ink-2); }
.fb-scope.on {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
}
.fb-search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 260px;
  min-width: 0;
}
.fb-search input[type=search] { flex: 1 1 auto; min-width: 0; }

/* ── 发帖页：分类单选组 ── */
.cat-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.cat-opt { position: relative; display: inline-flex; }
/* 原生 radio 隐藏但保留可访问性与键盘操作，样式交给下面的 face */
.cat-opt input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.cat-opt-face {
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.cat-opt:hover .cat-opt-face { background: var(--surface-2); }
.cat-opt input:checked + .cat-opt-face {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
  font-weight: 600;
}
.cat-opt input:focus-visible + .cat-opt-face {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* ── 详情页：点赞与管理员操作条 ── */
.topic-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.topic-admin {
  display: flex;
  gap: 8px;
  margin-left: auto;
  flex-wrap: wrap;
}
/* 点赞按钮：未赞为描边，已赞为实底黑。用填充而不是变色，
   黑白体系里颜色传达不了「已选中」。 */
.like-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.like-btn:hover { background: var(--surface-2); color: var(--ink); }
.like-btn.on {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
.topic-head-tags {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* —— 窄屏：统计列收成两行，分类挪到标题下方 —— */
@media (max-width: 860px) {
  .topic-item {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "main main"
      "cat  stats";
    align-items: center;
    gap: 8px 12px;
  }
  .topic-main { grid-area: main; }
  .topic-cat { grid-area: cat; }
  .topic-stats {
    grid-area: stats;
    grid-template-columns: repeat(4, auto);
    gap: 12px;
  }
  .topic-stat { text-align: right; }
  .topic-admin { margin-left: 0; flex: 1 1 100%; }
}
@media (max-width: 620px) {
  .fb-cat, .fb-scope { font-size: 12px; }
  .topic-stats { grid-template-columns: repeat(2, auto); gap: 8px 16px; }
}

/* ── 话题正文 ── */
.topic-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.topic-head-text { min-width: 0; }
.topic-head-name {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
/* 纯文字：保留换行，不做 Markdown 渲染 */
.topic-body {
  font-size: 14px;
  line-height: 1.75;
  color: var(--ink-2);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── 评论区 ── */
.reply-list { display: flex; flex-direction: column; }
.reply-item {
  display: flex;
  gap: 12px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.reply-item:last-child { border-bottom: 0; }
.reply-main { flex: 1; min-width: 0; }
.reply-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  flex-wrap: wrap;
}
.reply-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.reply-body {
  font-size: 14px;
  line-height: 1.7;
  color: var(--ink-2);
  white-space: pre-wrap;
  word-break: break-word;
}
