:root {
  --bg: #0a0e1a;
  --bg-card: #131826;
  --bg-elevated: #1a2033;
  --bg-tab: #0f1322;
  --border: rgba(255,255,255,0.05);
  --border-hover: rgba(255,255,255,0.10);
  --text: #e4e7eb;
  /* 注：早期在此处补 --text-1 时误写为 --text（与上面重复，属无效声明）；
     v6.9 已在下方正确补上 --text-1，此行冗余已删除。 */
  /* ⚠ v6.9 P0 对比度修复（UI/UX 评估报告）：原 --text-3 #5a6276 在卡片底上仅 2.90:1、
     浮层上 2.65:1（WCAG AA 正文要求 ≥4.5:1）——而它承载列头/时间戳/单位/meta 等
     面积最大的信息层，叠加上 10px 小字后手机与弱光环境基本不可读。
     提亮到 #7c8496（实测 card 4.72 ✅ / elev 4.31 / bg 5.13）；
     同时 --text-2 #8a92a6 → #949cab（6.41/5.86/6.97），保持「主/次/弱」三级层次不被压平。 */
  --text-1: #c3c9d4;
  --text-2: #949cab;
  /* v8.42 任务 3（对比度修复）：#7c8496 → #828b9e。
     --bg-elevated 底 4.31:1（AA 要求 4.5）→ 4.72:1 三底全过；
     与 --text-2（#949cab）层次保留（亮度差 ~24%，肉眼可辨）。 */
  --text-3: #828b9e;
  /* 热力图单元格强度系数，由 renderHeatCells 内联 style 覆盖（0~1）。
     这里给个兜底 0，避免某些路径未设置时 calc() 整条失效、背景丢失。 */
  --int: 0;
  --up: #00d68f;
  --up-dim: rgba(0,214,143,0.12);
  --up-mid: rgba(0,214,143,0.25);
  --up-strong: rgba(0,214,143,0.42);
  --down: #ff4961;
  --down-dim: rgba(255,73,97,0.12);
  --down-mid: rgba(255,73,97,0.25);
  --down-strong: rgba(255,73,97,0.42);
  --neutral: #ffaa00;
  --accent: #00d4ff;
  /* v-al9.0：强调色淡底（chip/分页激活态）—— 与 --up-dim 同规格 */
  --accent-dim: rgba(0,212,255,0.14);
  /* 彩色实底（--accent 青 / --up 绿）上的前景色，取深底色以保证对比度。
     值恰好同 --bg，但语义独立（"实底上的墨色" ≠ "页面背景"）故单列令牌。 */
  --on-accent: #0a0e1a;
  --purple: #a78bfa;
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px;
  /* ===== v6.6 设计令牌（字阶 + 圆角）=====
     新样式一律引用变量；存量 90+ 处字号逐步迁移（阶段 2），本轮不动存量。
     字阶语义：xs=列头/角标 sm=标签 md=正文 num=表格数字 lg=小标题 xl=页标题 num-xl=汇总大数字 */
  --fs-xs: 10.5px; --fs-sm: 11.5px; --fs-md: 12.5px; --fs-num: 13px;
  --fs-lg: 15px; --fs-xl: 20px; --fs-num-xl: 24px;
  --radius-s: 8px; --radius-m: 10px; --radius-l: 12px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: -apple-system,BlinkMacSystemFont,'PingFang SC','Segoe UI',Roboto,sans-serif;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  padding: var(--sp-4);
  font-size: var(--fs-num);
}
body::before {
  content: ''; position: fixed; inset: 0;
  background:
    radial-gradient(circle at 15% 5%, rgba(0,212,255,0.06) 0%, transparent 40%),
    radial-gradient(circle at 85% 95%, rgba(167,139,250,0.04) 0%, transparent 50%);
  pointer-events: none; z-index: 0;
}
.container { max-width: 1500px; margin: 0 auto; position: relative; z-index: 1; }

/* ===== 顶部 ===== */
/* ⚠ v8.62（审核 A12）：此旧段与 v8.31 sticky 段（:1944）叠加生效 ——
   background/border/radius/font-size/backdrop-filter 由本段提供，sticky/padding 由新段覆盖。
   ⚠ 隐藏约束：backdrop-filter 使 .top-bar 成为 fixed 后代的包含块 ——
   卡 1 内**禁止**出现 position:fixed 元素（v8.27 踩过：移动端底栏被顶到卡底边）。
   两段合并需视觉回归（radius/font-size 组合），暂保留叠加 + 此显式声明。 */
.top-bar {
  display: flex; justify-content: space-between; align-items: center;
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  margin-bottom: var(--sp-3);
  font-size: 12px;
  backdrop-filter: blur(20px);
}
.status { display: flex; align-items: center; gap: var(--sp-2); color: var(--text-2); }
.status-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--up); box-shadow: 0 0 10px var(--up);
  animation: pulse 2s ease-in-out infinite;
  transition: background 0.3s, box-shadow 0.3s;   /* v3 P1-3：状态切换平滑过渡，不闪 */
}
/* v3 P1-3：状态点 4 档颜色。语义映射：
   ok        = 健康：绿色脉冲，数据 ≤60s 新
   scanning  = 扫描中：蓝色脉冲，scan() 正在跑
   stale     = 数据陈旧：黄色常亮（停脉冲），>2min 没新数据
   banned    = 限流封禁：红色常亮（停脉冲），BANNED=true
   failed    = 加载失败：灰色 + 错误图标 */
.status-dot.scanning { background: #4ea1ff; box-shadow: 0 0 10px #4ea1ff; }
.status-dot.stale    { background: var(--neutral); box-shadow: 0 0 8px var(--neutral); animation: none; opacity: 1; }
.status-dot.banned   { background: var(--down); box-shadow: 0 0 8px var(--down); animation: none; opacity: .9; }
.status-dot.failed   { background: var(--text-3); box-shadow: none; animation: none; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
/* v8.27：#topStatus 已改为视觉隐藏的 live region，样式交给 .sr-only。
   .status-text / .top-sep / .countdown 三条规则随旧结构一并移除（胶囊与环形按钮取代）。
   ⚠ 元素 id 全部保留 —— tickCountdown()/setStatus() 仍在写它们。 */
.mini-btn {
  padding: 4px 10px; border-radius: 6px;
  background: var(--bg-elevated); color: var(--text-2);
  border: 1px solid var(--border); cursor: pointer;
  font-size: 11px; transition: all .15s;
}
.mini-btn:hover:not(:disabled) { border-color: var(--border-hover); color: var(--text); }
.mini-btn:disabled { opacity: .5; cursor: not-allowed; }
.mini-btn.busy { color: var(--accent); border-color: rgba(0,212,255,0.3); }

/* ===== 筛选器 ===== */
.filters {
  display: flex; gap: var(--sp-2); margin-bottom: var(--sp-4);
  padding: var(--sp-3);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  align-items: center;
  flex-wrap: wrap;
}
/* OI 趋势筛选区。flex:1 撑开剩余空间；在其它视图被隐藏后不占空间。 */
.filters .filter-scope {
  display: flex; align-items: center; gap: var(--sp-2); flex: 1; min-width: 0;
}
.filters .filter-scope[hidden] { display: none; }
.filter-label { color: var(--text-3); margin-right: var(--sp-2); font-size: 12px; }
.filter-btn {
  padding: 6px 14px; border-radius: 8px;
  background: var(--bg-elevated); color: var(--text-2);
  border: 1px solid var(--border); cursor: pointer;
  font-size: 12px; transition: all .15s;
}
.filter-btn:hover { border-color: var(--border-hover); color: var(--text); }
.filter-btn.active { background: var(--up); color: var(--on-accent); border-color: var(--up); font-weight: 600; }

/* ===== 聚合卡片 ===== */
.summary-row {
  display: grid; grid-template-columns: 1fr 1fr 1fr;
  gap: var(--sp-3); margin-bottom: var(--sp-4);
}
.summary-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: var(--sp-5);
  position: relative; overflow: hidden;
}
.summary-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.08), transparent);
}
.summary-label { color: var(--text-3); font-size: 12px; margin-bottom: var(--sp-3); letter-spacing: .3px; }

/* v-al5.0：旧版「OI 异动监控」面板样式已删（.anom-stats/.anom-stat/.as-*，
   .anom-filter/.anom-filter-btn）—— 面板由 viewAlerts + alerts-ui.css 上位替代。 */

.summary-value {
  font-size: 30px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.5px;
  margin-bottom: var(--sp-2);
}
.value-up { color: var(--up); }
.value-down { color: var(--down); }
.summary-meta { font-size: 11px; color: var(--text-3); }

/* v-sig2.0（T01）：汇总卡口径标注行 —— 显示「TopN 聚合 · 有效 N/共 M · 当前周期」。
   口径单一真源 = 服务端 board.meta（前端只消费）。 */
.sum-calibre {
  font-size: 12px; color: var(--text-3);
  margin: var(--sp-2) 0 var(--sp-4);
  letter-spacing: .02em;
  display: flex; align-items: center; gap: 6px;
}
.sum-calibre::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); opacity: .7; flex: none;
}

/* ===== 节标题 ===== */
.section-title {
  display: flex; justify-content: space-between; align-items: center;
  margin: var(--sp-5) 0 var(--sp-3);
}
.section-title h3 {
  font-size: 14px; font-weight: 500;
  color: var(--text);
  display: flex; align-items: center; gap: var(--sp-3);
}
/* v-al8.2：视图标题统一「英文眉题 + 中文主标题」——眉题用强调色加粗，
   对齐 OI 异动监控页头（OI ANOMALY ALERTS 全市场 OI 异动监控）的签名。
   中文保持普通色，英文在前、不留分隔点。h3 是 flex，眉题与中文之间的间距由 gap 承担。 */
.section-title h3 .sec-en { color: var(--accent); font-weight: 800; letter-spacing: .5px; }
/* v-al8.2b（宗翰 22:27）：视图级标题字号与 OI 异动监控页头对齐 = 20px。
   只给每个页签的**第一个** section-title 加 .sec-h1（它承担页面命名）；
   同一页签的第 2 节（资金费率与期限基差 / 聚合 CVD）保持 14px，
   否则一个页签里会出现两个等大的大标题，层级反而塌掉。 */
.section-title h3.sec-h1 { font-size: 20px; letter-spacing: .5px; }
/* v-al8.3（宗翰 22:35）：币种全景的标题上移到页面第一行后，
   它作为非首元素时那 24px 上边距会变成顶部一段凭空空白 —— 首行时收成 --sp-2，
   像 OI 异动监控页头一样贴着导航。 */
#viewCoins > .section-title:first-child { margin-top: var(--sp-2); }
.count-badge {
  display: inline-flex; padding: 3px 10px;
  background: var(--up-dim); color: var(--up);
  border-radius: 20px; font-size: 11px; font-weight: 500;
}
.count-badge.down { background: var(--down-dim); color: var(--down); }

/* ===== 热力图网格 ===== */
.heat-grid {
  display: grid; grid-template-columns: repeat(8, 1fr);
  gap: var(--sp-2);
}
.heat-cell {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: var(--sp-3) var(--sp-2);
  text-align: center;
  transition: transform .15s, border-color .15s, box-shadow .15s;
  cursor: pointer;
  position: relative; overflow: hidden;
}
.heat-cell:hover {
  transform: translateY(-2px);
  border-color: var(--border-hover);
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
  z-index: 2;
}
.heat-cell .sym { font-size: var(--fs-num); font-weight: 600; margin-bottom: 4px; letter-spacing: .3px; }
.heat-cell .pct {
  font-size: 11px; font-weight: 500;
  font-variant-numeric: tabular-nums;
  margin-bottom: 2px;
}
.heat-cell .amt {
  font-size: 10px; color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
/* 热度分档 */
.heat-cell[data-tier="1"]  { background: var(--up-dim);     border-color: rgba(0,214,143,0.18); }
.heat-cell[data-tier="2"]  { background: var(--up-mid);     border-color: rgba(0,214,143,0.32); }
.heat-cell[data-tier="3"]  { background: var(--up-strong);  border-color: rgba(0,214,143,0.55); }
.heat-cell[data-tier="d1"] { background: var(--down-dim);   border-color: rgba(255,73,97,0.18); }
.heat-cell[data-tier="d2"] { background: var(--down-mid);   border-color: rgba(255,73,97,0.32); }
.heat-cell[data-tier="d3"] { background: var(--down-strong); border-color: rgba(255,73,97,0.55); }
.heat-cell .pct.up { color: var(--up); }
.heat-cell .pct.down { color: var(--down); }

/* ===== 市场分组（5 列聚合看板，仿截图）===== */
.market-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
@media (max-width: 1300px) { .market-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 900px)  { .market-grid { grid-template-columns: repeat(2, 1fr); } }

/* ⚠ 移动端 < 600px 排版优化：之前没有任何针对真正手机的断点，< 900px 只把
   market-grid 改 2 列、< 700px 把 coin360-stats 改 2 列；10 个 Tab 塞进 360px
   必折行/挤压、3 列 summary-row 文字溢出、heat-cell padding 太大、count-badge
   字号在小屏过响。整套断点集中处理：顶栏收窄、Tab 横向滚动、统计卡 1-2 列、
   表格/卡片 padding 收紧、字号下沉。 */
@media (max-width: 600px) {
  /* 整体外边距收紧 —— 16px → 12px，省 8% 横向空间给内容 */
  body { padding: var(--sp-3); }
  /* 顶栏 padding 收紧（v8.31：两张卡已取消外层容器，此处只收内边距） */
  .top-bar { padding: var(--sp-2) var(--sp-3); }
  /* 10 个 Tab 在 360px 屏必挤压 —— 改成横向滚动（隐藏滚动条，touch 友好）。
     white-space:nowrap 已写在 .tab 上，此处只覆盖 flex-wrap + overflow-x。 */
  .tab-bar {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;            /* Firefox 隐藏滚动条 */
  }
  .tab-bar::-webkit-scrollbar { display: none; }   /* Chrome/Safari 隐藏 */
  .tab { padding: 7px 11px; font-size: var(--fs-sm); }
  /* 标题字号下沉 + section-title 上下间距 */
  h3 { font-size: var(--fs-lg); }
  .section-title { gap: var(--sp-2); }
  .count-badge { font-size: 11px; padding: 3px 8px; }
  /* summary 3列 → 1列（sumUp/sumDown/sumNet 纵向叠放，更易读） */
  .summary-row { grid-template-columns: 1fr; gap: var(--sp-2); }
  .summary-value { font-size: 18px; }
  /* market-grid < 600px → 1列（之前 < 900px 是 2 列，手机屏还要再降一档） */
  .market-grid { grid-template-columns: 1fr !important; }
  /* heat-cell 收紧 padding + 字号下沉，2 行信息仍清晰 */
  .heat-cell { padding: var(--sp-2); }
  .heat-cell .sym { font-size: 11px; }
  .heat-cell .pct { font-size: 11px; }
  .heat-cell .amt { font-size: 10px; }
  /* v8.60：热力图控制条滑杆小屏收窄（配合 .heat-toolbar 已有 flex-wrap 换行） */
  .ht-range { min-width: 110px; flex: 1; }
  /* 异动监控 alert-row padding 收 */
  .alert-row { padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); }
  /* coin360 卡片 < 600px → 1列 */
  .coin360-stats { grid-template-columns: 1fr !important; }
  /* 卡片 padding 收：var(--sp-4) var(--sp-5) → var(--sp-3) var(--sp-4) */
  .ccard, .mgroup, .heat-side { padding: var(--sp-3) var(--sp-4); }
  /* 提示/说明文字 12px → 11px */
  .hint { font-size: 12px; }
  /* 表格卡片 padding 收（不影响 min-width 横向滚动） */
  .coin-table-card { padding: 0; }
  /* 筛选栏移动端（v5.12 恢复）：按钮组窄视口禁止字内折断，空间不足整体换行。 */
  .filters { overflow: hidden; }
  .filter-label { flex: none; white-space: nowrap; }
  .filter-btn { flex: none; white-space: nowrap; }
  .filters .filter-scope { flex-wrap: wrap; row-gap: var(--sp-2); }
  /* 周期栏移动端（v5.13 恢复）：9 按钮总宽 ~430px，让 period-bar 收缩到容器宽度、
     内部按钮超宽走横向滚动（.filters overflow:hidden 外裁剪兜底）。 */
  .period-bar {
    flex: 1 1 auto;
    min-width: 0;
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .period-bar .period-btn { flex: none; }
  .period-bar::-webkit-scrollbar { display: none; }
  /* ⚠ 节标题 section-title 默认 flex 不换行，移动端"15M 持仓减少 [0] 红深表示跌幅大"
     之类长标题会撑爆 viewport（实测 left=228 + width=200 = right=428 > 375）。
     加 flex-wrap: wrap 让长标题换行；count-badge/h3 单独成行。 */
  .section-title { flex-wrap: wrap; }
}

.mgroup {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: var(--sp-4);
  transition: border-color .15s, transform .15s;
  position: relative; overflow: hidden;
}
.mgroup::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.06), transparent);
}
.mgroup:hover { border-color: var(--border-hover); transform: translateY(-2px); }

.mgroup-head {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: var(--sp-3);
}
.mgroup-name { font-size: var(--fs-num); font-weight: 600; letter-spacing: .3px; }
.mgroup-rank { font-size: 10px; color: var(--text-3); }

.mgroup-oi {
  font-size: var(--fs-xl); font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.3px;
  margin-bottom: 2px;
}
.mgroup-chg {
  font-size: 12px; font-weight: 500;
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--sp-2);
}
.mgroup-flow {
  font-size: 11px; color: var(--text-2);
  font-variant-numeric: tabular-nums;
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--sp-3);
}
.mgroup-top-label {
  font-size: 10px; color: var(--text-3);
  margin-bottom: var(--sp-2);
  letter-spacing: .3px;
}
.mgroup-item {
  display: flex; justify-content: space-between; align-items: center;
  padding: 4px 0;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.mgroup-item .msym { font-weight: 600; }
.mgroup-item .mchg { font-weight: 500; }
.mgroup-item .mflow { color: var(--text-3); font-size: var(--fs-xs); }
.mgroup-count {
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
  display: flex; justify-content: space-between;
  font-size: var(--fs-xs); color: var(--text-3);
}

/* ===== 已订阅币种标签 ===== */
/* ===== v8.50c：watch-bar 容器卡重构（替代旧散 chips + wgroup/w-empty 旧样式）===== */
.watch-bar {
  display: flex; flex-direction: column; gap: 10px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; padding: 12px 14px 14px;
  margin-bottom: var(--sp-5);
}
.wb-head { display: flex; align-items: center; gap: 10px; }
.wb-title {
  display: flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 700; color: var(--text-1);
}
.wb-star { color: var(--gold); font-size: 12px; }
.wb-cnt {
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  color: var(--accent); background: var(--accent-bg);
  border-radius: 9px; padding: 0 7px;
}
.w-restore {
  margin-left: auto; font-size: 11px; color: var(--text-3);
  cursor: pointer; border: 1px solid var(--border); border-radius: 7px;
  padding: 2px 10px; transition: all .15s;
}
.w-restore:hover { color: var(--up); border-color: var(--up); background: var(--up-bg); }
.wb-gtitle {
  font-size: 11px; font-weight: 600; letter-spacing: .4px;
  color: var(--text-2); display: flex; align-items: center; gap: 8px;
}
.wb-gtitle::before { content: ''; width: 6px; height: 6px; border-radius: 2px; background: var(--bd2); }
.wb-gtitle .wb-auto { font-weight: 500; color: var(--text-3); }
.wb-chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.wb-empty {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: var(--bg2); border: 1px dashed var(--bd2); border-radius: 9px;
  padding: 12px 14px; color: var(--text-3); font-size: 12px;
}
.wb-empty-ico { font-size: 16px; color: var(--gold); }
.wb-empty b { color: var(--text-2); font-weight: 600; }
.watch-chip {
  padding: 5px 13px; border-radius: 17px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-2);
  font-size: 12px; font-weight: 500;
  cursor: pointer;
  transition: all .15s;
  display: flex; align-items: center; gap: 6px;
}
.watch-chip:hover { border-color: var(--accent); color: var(--text-1); }
.watch-chip.active {
  background: var(--up-dim); color: var(--up);
  border-color: var(--up);
}
.watch-chip .wchg { font-variant-numeric: tabular-nums; font-size: var(--fs-xs); opacity: .85; }
/* 自选组 chip 的 × 删除钮（沿用 .w-x 类，委托在 watchBar 点击监听） */
.watch-chip .w-x { margin-left: 3px; color: var(--text-3); font-size: 11px; padding: 0 1px; }
.watch-chip .w-x:hover { color: var(--up); }
.watch-chip:hover { border-color: var(--accent); color: var(--text-1); }

/* ===== 搜索框 ===== */
.search-input {
  padding: 7px 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-size: 12px;
  font-family: inherit;
  width: 220px;
  outline: none;
  transition: border-color .15s;
}
.search-input:focus { border-color: var(--accent); }
.search-input::placeholder { color: var(--text-3); }

/* ⚠ P2-5 键盘可达性：所有交互元素（button / .tab / .period-btn / .filter-btn /
   search-input）都加 :focus-visible 焦点环，让 Tab 键浏览时位置清晰。
   （v-al5.0：原列表中的 .anom-filter-btn 已随面板删除移除。）
   用 :focus-visible 而非 :focus，避免鼠标点击时也出现焦点环（这是现代浏览器
   的标准做法：键盘 focus → 显示，鼠标 focus → 隐藏）。
   outline 偏移 2px 让焦点环与元素边框不重叠。 */
:where(button, .tab, .period-btn, .filter-btn, .search-input):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: inherit;
}

/* ===== 币种全景表格 ===== */
.coin-table-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  /* ⚠ 原来是 overflow:hidden —— 窄屏时列被硬裁剪且无法滑动，等于数据丢了。 */
  overflow-x: auto;
  /* v8.44（任务 ③）表头吸顶修复。缺陷实测（Puppeteer 滚动诊断）：
     overflow-x:auto 会把 overflow-y 的**计算值**顶成 auto（CSS Overflow 规范：
     一轴非 visible/clip 时另一轴的 visible 计算为 auto）→ 容器成为表头 sticky 的
     滚动容器，而容器高度=内容高度、无纵向溢出 → 吸附**永不触发**，
     300 行滚动时表头与卡片一起滚出视口（实测 headerTop 与卡顶同步到 -164px）。
     单改 top 值无效 —— sticky 根本没有发生。两级修法见下方媒体查询：
       ≥900px（表格 min-width 760 无横向溢出）：overflow-x:clip 解除困住，
         表头改吸视口 top:var(--stack-h)（贴住吸顶堆叠下沿）；
       <900px（必须保留横向滚动）：卡片限高 + 内部纵向滚动，表头吸在卡顶。 */
}
/* 窄屏：保留横向滚动兜底，同时限高让表头在卡内吸附可用 */
@media (max-width: 899px) {
  .coin-table-card { max-height: calc(100vh - 140px); }
}
/* 宽屏：无横向滚动需求，clip 不产生滚动容器（与 visible 配对不触发计算翻转），
   表头 sticky 相对视口生效（top 覆盖放在下方 1962 行基准规则之后，见该处注释） */
@media (min-width: 900px) {
  .coin-table-card { overflow-x: clip; }
}
.coin-row {
  display: grid;
  grid-template-columns: 46px 100px 1fr 1fr 1fr 1fr 1fr 1fr;
  /* 低于这个宽度就横向滚动，而不是把列压成一团。 */
  min-width: 680px;
  /* v-al9.0：行高 50 → 34px（对齐异动事件表 31.7px 量级）——一屏 12 → 17 行。
     显式 height + align-items:center，不再靠 padding 推算。 */
  padding: 0 var(--sp-4);
  height: 34px;
  font-size: 12px;
  align-items: center;
  border-bottom: 1px solid var(--border);
  font-variant-numeric: tabular-nums;
}
.coin-row:last-child { border-bottom: none; }
/* v-al9.0：偶数行浅底 —— 行高压到 34px 后，11 列长距离扫视极易串行；
   异动事件表不需要（9 列且带色条锚点）。 */
#coinBody > .coin-row.coins-row:nth-child(even) { background: rgba(255,255,255,0.022); }
#coinBody > .coin-row.coins-row:hover { background: rgba(255,255,255,0.05); }
/* 数字列右对齐便于纵向比对（与 COIN 360° 一致）。
   此前主币种表全部左对齐、COIN 360° 右对齐，同一份数据两种读法，扫视容易串行。 */
.coin-row.coins-row > div:nth-child(n+2),
.coin-row.header.coins-head > div:nth-child(n+2) { text-align: right; }
.coin-row.header {
  color: var(--text-3);
  background: var(--bg-elevated);
  user-select: none;
}
/* ⚠ 只有真能排序的列才给手型。之前整个表头都是 pointer，
   而「实时持仓/资金流动」点了根本不重排 —— 假交互比没有交互更糟。 */
.coin-row.header > div[data-sort] { cursor: pointer; }
.coin-row.header > div[data-sort]:hover { color: var(--text); }
.coin-row.header > div.sorted { color: var(--accent); }
.coin-row .csym { font-weight: 600; }

/* ===== v-al9.0：币种全景新增四块（宗翰 2026-09-19 手稿定稿） =====
   顺序：标题行 → 我的自选行 → 24h 涨跌幅榜 → 本页独立周期栏 → 表格 → 分页条 */

/* ① 我的自选行（从原 watch-bar 里拆出，独立成行且排最前） */
.wb-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px;
  padding: var(--sp-2) var(--sp-4); margin-bottom: var(--sp-2); min-height: 40px;
}
.wb-row .wb-title { font-weight: 600; font-size: 13px; }
.wb-row .wb-cnt {
  background: var(--up-dim); color: var(--up); border-radius: 20px;
  padding: 1px 8px; font-size: 11px; margin-left: 6px;
}
.wb-row .wb-empty { color: var(--text-3); font-size: 12.5px; }
.wb-row .wb-chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.wb-row .w-restore { color: var(--text-3); font-size: 12px; cursor: pointer; }
.wb-row .w-restore:hover { color: var(--up); }
/* 当前筛选态清除胶囊（「只看 BTC ×」）—— 必须有显式清除入口，否则用户被困在筛选里 */
.wb-row .w-filter {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--accent-dim); border: 1px solid var(--accent); color: var(--accent);
  border-radius: 999px; padding: 2px 10px; font-size: 12px; font-weight: 600;
}
.wb-row .w-filter b { font-weight: 700; }
.wb-row .w-filter .w-x { color: var(--accent); font-size: 13px; margin-left: 2px; }
.wb-row .w-filter .w-x:hover { color: var(--up); }
/* ===== v-al9.0 窄屏适配（实测：768/390 下 pb-bar 有 3-7 个子元素溢出、rank-row 12 格挤不下）===== */
@media (max-width: 900px) {
  /* 周期栏：换行 + 收掉次要说明，周期段可横滑（与表格横滚同一套语言） */
  .pb-bar { height: auto; min-height: 34px; flex-wrap: wrap; padding: 6px var(--sp-3); gap: 6px; }
  .pb-bar .pb-scope { display: none; }
  .pb-seg { max-width: 100%; overflow-x: auto; }
  /* 涨跌幅榜：窄屏**换行不横滑** —— 榜的价值就是一眼看到最强/最弱两端，
     横滑会把另一端推到屏幕外，等于把它的用途废掉。改 3 格/行（12 格 = 4 行）。 */
  .rank-row { flex-wrap: wrap; }
  .rank-row .rk-cell { flex: 1 1 calc(33.333% - 5px); }
  .rank-row .rk-div { display: none; }   /* 换行后中间竖线语义混乱，收起 */
  .rank-head { flex-wrap: wrap; gap: 6px; }
}
@media (max-width: 700px) {
  .wb-row, .rank-bar, .pb-bar { padding-left: var(--sp-3); padding-right: var(--sp-3); }
  .pb-note { display: none; }
  .pager { gap: 6px; padding: var(--sp-2) var(--sp-3); }
  .pager .pg-btn { width: 30px; height: 30px; }
  .pager .pg-info { font-size: 12px; }
}
/* 榜格激活态：让用户看到是哪一格在筛选 */
.rk-cell.active { border-color: var(--accent); background: var(--accent-dim); }
.rk-div { width: 1px; background: var(--border-hover); margin: 2px 4px; flex: none; }

/* ② 24h 涨跌幅榜（原「成交额 Top 12」改为按涨跌幅入榜） */
.rank-bar {
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px;
  padding: var(--sp-2) var(--sp-4) var(--sp-3); margin-bottom: var(--sp-3);
}
.rank-head { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--text-3); min-height: 22px; }
.rank-head .rk-title { font-weight: 600; font-size: 13px; color: var(--text); }
.rank-row { display: flex; gap: 6px; margin-top: var(--sp-2); }
.rk-cell {
  flex: 1 1 0; min-width: 0; display: flex; align-items: baseline; justify-content: space-between; gap: 5px;
  background: var(--bg-elevated); border: 1px solid transparent; border-radius: 7px;
  padding: 4px 8px; font-size: 12px; cursor: pointer;
}
.rk-cell:hover { border-color: var(--border-hover); }
.rk-cell b { font-weight: 700; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rk-cell em { font-style: normal; font-family: var(--mono); font-size: 11px; flex: none; }
.rk-cell.up em { color: var(--up); }
.rk-cell.dn em { color: var(--down); }

/* ③ 本页独立周期栏（只作用于「OI 变化 / OI 净增」两列，不改顶部全局周期） */
.pb-bar {
  display: flex; align-items: center; gap: 10px; height: 34px;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px;
  padding: 0 var(--sp-4); margin-bottom: var(--sp-2); font-size: 12px; color: var(--text-3);
}
.pb-bar .pb-label { font-weight: 600; color: var(--text-2); }
.pb-bar .pb-win b { color: var(--text); }
.pb-bar .pb-scope { font-size: 11.5px; }
.pb-seg { display: inline-flex; border: 1px solid var(--border-hover); border-radius: 999px; overflow: hidden; }
.pb-seg button {
  background: none; border: none; border-left: 1px solid var(--border);
  color: var(--text-2); font-size: 11.5px; font-family: var(--mono);
  padding: 3px 10px; cursor: pointer;
}
.pb-seg button:first-child { border-left: none; }
.pb-seg button:hover { color: var(--text); }
.pb-seg button.on { background: var(--accent-dim); color: var(--accent); font-weight: 700; }

/* ④ 分页条（表格卡片底部） */
.pager {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-4); border-top: 1px solid var(--border);
  font-size: 12.5px; color: var(--text-2); background: var(--bg-card);
}
.pager .pg-info b { color: var(--text); font-weight: 600; }
.pager .pg-sel {
  background: var(--bg-elevated); color: var(--text-1); border: 1px solid var(--border);
  border-radius: 999px; padding: 4px 10px; font-size: 12px; font-family: inherit; cursor: pointer;
}
.pager .pg-btn {
  width: 32px; height: 32px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg-elevated); color: var(--text-2); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-family: var(--mono);
}
.pager .pg-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--text); }
/* 激活页：强调色淡底 + 描边 + 强调色字（不做实心块配白字，对比度不达标） */
.pager .pg-btn.on { background: var(--accent-dim); border-color: var(--accent); color: var(--accent); font-weight: 700; }
.pager .pg-btn:disabled { border-color: var(--border); color: var(--text-3); opacity: .45; cursor: not-allowed; }
.pager .pg-gap { color: var(--text-3); padding: 0 2px; }
.pager .pg-div { width: 1px; height: 18px; background: var(--border-hover); }
/* ===== v8.50（方案 C）：行尾 ☆ 自选星标 =====
   点击 = myWatchToggle（持久化，与热力图联动）。注意：行点击会触发全局的
   「选中标的看右栏」委托，星标在全局委托里前置拦截（见 click 委托处注释）。 */
.row-star {
  cursor: pointer; text-align: center;
  color: var(--text-3); font-size: 13px; line-height: 1;
  user-select: none;
}
.row-star:hover { color: var(--gold); transform: scale(1.15); }
.row-star.on { color: var(--gold); }
/* v8.50c：旧 wgroup/w-empty/w-restore 散件样式已由 watch-bar 容器卡内的新样式取代（见 .wb-* / .w-restore 新定义）。 */
/* ===== v8.48（UI 批次①）：币种图标徽标 =====
   圆形徽标 + 符号/首字母，底色取币种品牌色；长尾币按符号 hash 落 12 色盘。
   只作视觉锚点（扫视定位行），不承担任何数据语义。 */
.coin-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  font-size: 10px; font-weight: 700; color: #fff;
  flex: none; vertical-align: -6px; margin-right: 7px;
  letter-spacing: 0;
}
/* v8.48（UI 批次①）：OI 变化列零点双向幅度条 —— 中轴零点，
   增持向右（红）、减持向左（绿），宽度 = |变化| / 5% 封顶。
   与涨跌列的纯数字形成「数值+图形」双通道，扫视 300 行时先看条再看数。 */
.oi-chg-bar {
  display: inline-block; width: 46px; height: 5px; border-radius: 3px;
  background: var(--bg-elevated); position: relative;
  vertical-align: 2px; margin-right: 7px;
}
.oi-chg-bar::before { /* 中轴零点刻度 */
  content: ''; position: absolute; left: 50%; top: -2px; bottom: -2px;
  width: 1px; background: var(--border);
}
.oi-chg-bar i { position: absolute; top: 0; bottom: 0; border-radius: 3px; }
.oi-chg-bar i.pos { left: 50%; background: var(--up); }
.oi-chg-bar i.neg { right: 50%; background: var(--down); }
/* ===== v8.48（UI 批次③）：多空表比值位置条 + 解读 pill ===== */
.ls-ratio { display: inline-flex; align-items: center; gap: 8px; }
.ls-ratio-bar {
  display: inline-block; width: 52px; height: 5px; border-radius: 3px;
  background: var(--bg-elevated); position: relative; vertical-align: middle;
}
.ls-ratio-bar::before {   /* 1.0 中线刻度（对数标尺中点） */
  content: ''; position: absolute; left: 50%; top: -2px; bottom: -2px;
  width: 1px; background: var(--gold); opacity: .7;
}
.ls-ratio-bar i { position: absolute; top: 0; bottom: 0; border-radius: 3px; }
.ls-ratio-bar i.pos { left: 50%; background: var(--up); }
.ls-ratio-bar i.neg { right: 50%; background: var(--down); }
.ls-pill {
  display: inline-block; font-size: 11px; font-weight: 600;
  padding: 2px 10px; border-radius: 9px; white-space: nowrap;
}
/* v8.50e（审计修复）：底色与文字同 token —— 原写死红底配绿字（--up 实为绿）自相矛盾。 */
.ls-pill.up { background: rgba(0,214,143,.12); color: var(--up); }
.ls-pill.down { background: rgba(255,73,97,.12); color: var(--down); }
/* ===== v8.48（UI 批次④）：费率表着色 + 年化量级条 ===== */
.fund-rate, .fund-yr { font-family: var(--mono); font-weight: 600; }
.fund-rate.up, .fund-yr.up { color: var(--up); }
.fund-rate.down, .fund-yr.down { color: var(--down); }
.fund-rate.gold, .fund-yr.gold { color: var(--gold); }
.fund-yr-bar {
  width: 64px; height: 4px; border-radius: 2px;
  background: var(--bg-elevated); margin: 4px auto 0; overflow: hidden;
}
.fund-yr-bar i { display: block; height: 100%; border-radius: 2px; }
.fund-yr-bar i.up { background: var(--up); }
.fund-yr-bar i.down { background: var(--down); }
.fund-yr-bar i.gold { background: var(--gold); }
/* ===== v8.48（UI 批次⑤）：资金流 KPI 三卡 + 双向增量条 ===== */
.flow-kpi { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); margin-bottom: var(--sp-3); }
@media (max-width: 760px) { .flow-kpi { grid-template-columns: repeat(2, 1fr); } }
.fk-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: var(--sp-3) var(--sp-4); }
.fk-k { font-size: 10.5px; color: var(--text-3); }
.fk-v { font-family: var(--mono); font-size: 16px; font-weight: 700; margin-top: 3px; }
/* v8.50e（审计修复）：本页色板既定约定 = --up 绿（涨/正向）、--down 红（跌/负向），
   与页面原有「资金流动表」的 inflow→'up' 一致。v8.48 曾把「流入」挂 down 类
   （注释写"流入绿"但 --down 实为红）→ 流入显红、流出显绿，语义反转。此处归正。 */
.fk-v.up   { color: var(--up); }     /* 正向：流入 / 净流入为正 → 绿 */
.fk-v.down { color: var(--down); }   /* 负向：流出 / 净流入为负 → 红 */
.fk-s { font-size: 10px; color: var(--text-3); margin-top: 2px; }
.flow-bar {
  display: inline-block; width: 56px; height: 6px; border-radius: 3px;
  background: var(--bg-elevated); position: relative; vertical-align: 2px; margin-right: 8px;
}
.flow-bar::before { content: ''; position: absolute; left: 50%; top: -2px; bottom: -2px; width: 1px; background: var(--border); }
.flow-bar i { position: absolute; top: 0; bottom: 0; border-radius: 3px; }
/* v8.50e（审计修复）：流入向右 = --up 绿、流出向左 = --down 红（同 KPI 约定）。 */
.flow-bar i.pos { left: 50%; background: var(--up); }     /* 流入（正向）→ 绿 */
.flow-bar i.neg { right: 50%; background: var(--down); }  /* 流出（负向）→ 红 */
/* ===== v8.48（UI 批次⑥）：CVD 同向条（青色系，与 force-bar 红绿区分）===== */
.cvd-bar {
  display: inline-block; width: 48px; height: 5px; border-radius: 3px;
  background: var(--bg-elevated); position: relative; vertical-align: 2px; margin-right: 8px;
}
.cvd-bar::before { content: ''; position: absolute; left: 50%; top: -2px; bottom: -2px; width: 1px; background: var(--border); }
.cvd-bar i { position: absolute; top: 0; bottom: 0; border-radius: 3px; background: var(--accent); opacity: .6; }
.cvd-bar i.pos { left: 50%; }
.cvd-bar i.neg { right: 50%; }

/* ===== 点击币种名复制 =====
   光标 + hover 下划线暗示"可点"，点完闪青光确认已复制。
   ⚠ 只对有复制语义的元素生效；热力图 .sym（下钻）和 .watch-chip
     （切换自选）保持原交互，不加 pointer 以免误导用户。 */
.csym, .msym, .coin-title {
  cursor: pointer;
  /* 双击选词不受影响（user-select 不设 none），只在视觉上提示可点 */
}
.csym:hover, .msym:hover, .coin-title:hover {
  color: var(--accent);
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}
/* 复制成功：元素闪一下青光 + 轻微放大，900ms 后 JS 移除 class */
@keyframes copied-flash {
  0%   { background: rgba(0, 212, 255, 0.28); transform: scale(1); }
  35%  { background: rgba(0, 212, 255, 0.34); transform: scale(1.06); }
  100% { background: transparent; transform: scale(1); }
}
.copied {
  animation: copied-flash .9s ease;
  border-radius: 4px;
}
/* 尊重系统的"减弱动态效果"偏好 */
@media (prefers-reduced-motion: reduce) {
  .copied { animation: none; background: rgba(0, 212, 255, 0.25); }
}

/* ⚠ v7.5 已删除以下死 CSS（实机 DOM 命中数全为 0，均为已下线的 ETF 资金流 /
   COIN 360° 旧交易所表遗留）：
     .coin-table-row / .coin-table-head（通用表格行，无元素使用）
     .coin360-exch-head / .coin360-exch-row（旧交易所 9 列表，含周期列 flex 与 .c360-pct）
     .coin360-flow-head / .coin360-flow-row（旧资金流 7 列表，含对齐规则）
   注意：.coin-row / .coin-table-card / .coin360-stats 等仍在用的类未受影响。 */
.coin-row .up { color: var(--up); }
.coin-row .down { color: var(--down); }
.coin-row .dim { color: var(--text-3); }
/* v3.8c：COIN360 同币重查的加载指示 —— 不清空内容（防闪动），只半透明 + progress 光标 */
#detailOverlay.c360-busy #coin360Card,
#detailOverlay.c360-busy .coin360-stats { opacity: .55; transition: opacity .15s; }
#detailOverlay.c360-busy { cursor: progress; }

/* ===== 占位卡片 ===== */
.placeholder-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: var(--sp-6);
  text-align: center;
  margin: var(--sp-5) 0;
}

/* P2-D3：首屏骨架屏 shimmer 动画。200 个币的全市场扫描要 30s+（60s 刷新间隔下余量更足），期间主区域
   空白会让用户以为页面坏掉。3 张带流光动画的占位卡片让等待状态有反馈。 */
@keyframes shimmer {
  0% { background-position: -468px 0; }
  100% { background-position: 468px 0; }
}
.sk-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: var(--sp-4);
  /* linear-gradient 配合 background-size 大于元素宽度 → 移动产生"光带扫过"。 */
  background-image: linear-gradient(90deg,
    var(--bg-card) 0px,
    rgba(255, 255, 255, 0.04) 40px,
    rgba(255, 255, 255, 0.08) 80px,
    var(--bg-card) 120px);
  background-size: 1200px 100%;
  animation: shimmer 1.6s infinite linear;
  min-height: 120px;
}
.sk-card + .sk-card { margin-top: var(--sp-3); }
.sk-line {
  height: 12px;
  background: var(--border);
  border-radius: 4px;
  margin-bottom: 8px;
}
.sk-line.short { width: 30%; }
.sk-line.mid   { width: 60%; }
.sk-line.long  { width: 90%; }

/* ===== 异动列表 ===== */
.alert-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  /* ⚠ P2-4 窄屏：原来是 overflow:hidden，alert-row 5 列（160px+80px+1fr+1fr+1fr）
     在 < 900px 屏幕列会被硬裁剪且无法滑动。改成 overflow-x:auto，
     并给 alert-row 加 min-width:560px，让窄屏可以横向滚动查看。 */
  overflow-x: auto;
}
.alert-row {
  display: grid;
  grid-template-columns: 160px 80px 1fr 1fr 1fr;
  padding: var(--sp-3) var(--sp-4);
  font-size: 12px;
  align-items: center;
  border-bottom: 1px solid var(--border);
}
.alert-row:last-child { border-bottom: none; }
.alert-row.header { color: var(--text-3); background: var(--bg-elevated); }
.alert-row .sym { font-weight: 600; }
.alert-row .up { color: var(--up); }
.alert-row .down { color: var(--down); }
.alert-row strong { color: var(--text); font-weight: 600; }

/* ===== Tab 导航 ===== */
.tab-bar {
  /* 外层 padding 收窄、间距收到 6px：10 个 Tab 文字长短不一（4~9 字），
     间距过大会显得松散断裂，收窄后整条更像一个连贯的导航条。
     每个 Tab 自身加大 padding 保证点击区域，视觉上「紧凑而不拥挤」。 */
  display: flex; gap: 6px; padding: 6px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; margin-top: var(--sp-5); margin-bottom: var(--sp-4);
  overflow-x: auto;
  scrollbar-width: thin;   /* 窄屏出现横滚时滚动条别抢视觉 */
}
.tab {
  padding: 9px 15px; border-radius: 9px;
  color: var(--text-2); cursor: pointer;
  white-space: nowrap;
  transition: color .18s, background .18s;
  font-size: var(--fs-md); font-weight: 500;
  letter-spacing: .2px;
  flex: none;   /* 不许被压缩，否则长标题会折行、高度参差 */
}
.tab:hover { color: var(--text); background: var(--bg-elevated); }
.tab.active {
  /* v8.34（UI 设计评估）：选中色由 --up（涨绿）改为 --accent（品牌青）。
     理由：本页绿色是**严格保留给「涨 / 多头占优 / 资金流入」的**语义色，
     而导航表达的是「你在哪」——用一个方向性的数据色去表达位置，会让一块满屏
     红绿数字的看板上多出一处与之抢解读的饱和绿（且位置在所有数据之上）。
     换成品牌青后，选中态与数据涨跌彻底解耦，对比度仍达标：
     #0a0e1a on #00d4ff = 10.88:1（AA 正文要求 ≥4.5:1）。
     ⚠ v8.34 同时**移除选中态光晕**（原 box-shadow: 0 2px 8px var(--up-mid)）：
       分段控件的语义是「选中块在轨道内」，8px 模糊会把内边距那 6px 间隙点亮，
       把刚加出来的呼吸空间又吃回去。青色实底在深色轨道上已足够醒目，不需要光晕。 */
  background: var(--accent); color: var(--on-accent);
  font-weight: 600;
}

/* Tab 分组标签（发现 / 深挖 / 佐证）：浅色 11px label + 细分隔线，
   视觉上把 10 个 Tab 拆成三段，引导用户按「先看结论 → 再深挖 → 最后佐证」动线走。 */
.tab-group-label {
  display: flex; align-items: center; padding: 0 8px 0 4px;
  font-size: 11px; font-weight: 600;
  color: var(--text-3);
  letter-spacing: .8px;
  text-transform: uppercase;
  user-select: none; flex: none;
}
.tab-group-sep {
  width: 1px; align-self: stretch; margin: 4px 4px;
  background: var(--border);
  flex: none;
}

/* ===== 视图面板切换 ===== */
.view-panel { display: none; }
.view-panel.active { display: block; animation: fadeIn .25s ease; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* 各表格的列宽（复用 .coin-row 的 grid 由这些类覆盖）*/
.ls-row,   .coin-row.ls-row   { grid-template-columns: 1fr 1fr 1fr 1fr 2fr; }
.fund-row, .coin-row.fund-row { grid-template-columns: 1fr 1fr 1fr 1fr 2fr; }
/* ⚠ P2-4 窄屏兜底：上面这些 5-6 列表格在 < 800px 屏列会被压成一团。
   min-width 给一个"列宽合理"的最小值，父容器 coin-table-card 已 overflow-x:auto，
   窄屏自动出横向滚动条。alert-row 在 alert-card 里也已独立处理（min-width:560px）。 */
.ls-row, .fund-row { min-width: 580px; }
/* v8.25：币种全景 = 原「实时持仓榜」+ 全景列 —— 变成 **9 列**（比基础 .coin-row 多一列「占比」）。
   必须显式给列定义，否则第 9 格会掉到下一行。
   min-width 同步 680 → 760，避免 9 列被压成一团（更窄的屏仍由 .coin-table-card 横向滚动兜底）。 */
/* v8.50（方案 C）：全景表 9→10 列（行尾加「★ 自选」36px 列）；
   min-width 760 → 796（+36），窄屏仍由 coin-table-card 横滚兜底。 */
/* v3.9：全景表 10→11 列，对齐用户给定的目标表头（截图来自 newarch.html）：
   币种 | 价格 | 24h% | 实时 OI(币) | 24h 额($) | OI变化 {p} | OI净增 {p}($) | 量能倍数 | 评分 | 占比 | ★
   改动：去掉「#」序号列（截图无）、新增「量能倍数」「评分」、占比移到评分后。
   min-width 796 → 900。 */
/* v-al9.0（币种全景重设计）：列宽按内容分配，取消「9 列等分」——
   旧写法 9×1fr 让每个数字列都是 148.2px，「24h%」和「OI 净增」一样宽 → 数字间大片空白，
   而币种列（含图标+名称）只拿到 96px。现按各列内容定宽，富余全部给币种列。
   合计 1080px → min-width 同步（窄屏横滚兜底）。 */
.coins-head, .coin-row.coins-row {
  /* 列宽 = 内容比例（不是等分，也不是硬内容宽）。
     ⚠ 实测教训：严格按内容定宽时合计只需 1080px，而容器 1468px →
       富余 390px 全被币种列吃掉（实测 536px），头重脚轻。
     改为按内容比例分配：币种列权重最高（含图标+名称，原来只给 96px 是错的），
     数字列按各自内容需求排序（OI 名义值/净增最长、评分最短），整体铺满无留白。 */
  grid-template-columns: minmax(150px,1.5fr) 1.05fr 0.85fr 1.05fr 1.18fr 0.95fr 1.18fr 0.95fr 0.92fr 0.78fr 1fr 0.36fr;
  column-gap: 16px;
  /* 低于此宽横向滚动而不是把列压成一团（与原 900px 兜底同义，随列数增长上调） */
  min-width: 1140px;
}
.alert-row { min-width: 560px; }
/* 表格数据区（按需加载视图的 body）也要 overflow-x:auto，否则 min-width
   设了但数据行超出的部分仍然 visible，会把列撑出右边距。coin-table-card
   本身有 overflow-x:auto，但 lsBody/fundBody/flowBody/cvdBody
   这些内嵌 div 是数据行的直接 parent，单独加。 */
#lsBody, #fundBody { overflow-x: auto; }
/* 表头与数据行必须同宽，否则列对不齐 */
.coin-table-card .coin-row:not(.header).ls-row,
.coin-table-card .coin-row:not(.header).fund-row { }

/* 热力图（双网格版）控制条 */
.heat-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-3);
}
.heat-toolbar-left, .heat-toolbar-right {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
}
.ht-label { font-size: 12px; color: var(--text-3); }
/* v8.60：单一阈值滑杆（原生 accent-color，不造轮子）+ 即时计数 */
.ht-range { accent-color: var(--accent); min-width: 140px; cursor: pointer; }
.ht-count { font-size: 12px; color: var(--text-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ht-count b { font-weight: 600; }
.ht-count b.up { color: var(--up); }
.ht-count b.down { color: var(--down); }
/* v8.49（批次①）：替代「应用」按钮的即时性说明（输入即生效）—— v8.60 已随双框退役，保留类名防遗漏引用 */
.ht-hint { font-size: 10.5px; color: var(--text-3); opacity: .8; margin-left: 2px; }
.ht-input {
  width: 56px; padding: 4px 6px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-1);
  font-size: var(--fs-num);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.ht-unit { font-size: 12px; color: var(--text-3); }
.ht-btn {
  padding: 4px 12px; border-radius: 6px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-2); cursor: pointer; font-size: 12px;
  transition: all .15s;
}
.ht-btn:hover { border-color: var(--accent); color: var(--text-1); }
.ht-btn.primary {
  background: var(--accent); color: var(--on-accent);
  border-color: var(--accent); font-weight: 600;
}
.ht-periods { display: inline-flex; gap: 2px; padding: 2px; background: var(--bg-elevated); border-radius: 8px; border: 1px solid var(--border); }
.ht-p {
  padding: 4px 10px; border-radius: 6px;
  background: transparent; border: none;
  color: var(--text-2); cursor: pointer; font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.ht-p:hover { color: var(--text-1); }
.ht-p.active { background: var(--accent); color: var(--on-accent); font-weight: 600; }

/* 订阅币种 */
.heat-watch {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  margin-bottom: var(--sp-3);
  font-size: var(--fs-num);
}
.hw-label { color: var(--text-3); font-size: 12px; }
.hw-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 8px; border-radius: 6px;
  background: var(--bg-elevated); color: var(--text-1);
  border: 1px solid var(--border);
}
.hw-x {
  cursor: pointer; padding: 0 2px;
  color: var(--text-3); font-weight: bold;
  border-radius: 4px;
}
.hw-x:hover { background: var(--down-dim); color: var(--down); }

/* 热力图 3 列汇总 */
.heat-summary {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.hs-card {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; padding: var(--sp-4) var(--sp-5);
  position: relative; overflow: hidden;
}
.hs-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.08), transparent);
}
.hs-label { font-size: 12px; color: var(--text-3); margin-bottom: var(--sp-2); }
.hs-value { font-size: 28px; font-weight: 600; font-variant-numeric: tabular-nums; }
.hs-value.up   { color: var(--up); }
.hs-value.down { color: var(--down); }

/* 双网格布局 */
.heat-duo {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3);
}
@media (max-width: 1100px) { .heat-duo { grid-template-columns: 1fr; } }
/* ⚠ 聚合看板热力图双网格 inline style 改 class：原来用 inline style: 1fr 1fr
   在 < 600px 移动端撑爆 viewport。移到 class 便于在 @media 内统一降为 1 列。 */
.heat-duo-inline { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
@media (max-width: 600px) { .heat-duo-inline { grid-template-columns: 1fr; } }
.heat-side {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; padding: var(--sp-4);
  position: relative; overflow: hidden;
}
.up-side::before, .down-side::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.06), transparent);
}
.heat-side-head {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: var(--sp-3);
}
.heat-side-head h4 {
  font-size: var(--fs-num); font-weight: 500;
  margin: 0; color: var(--text-2);
}
.heat-side-count { font-size: 11px; color: var(--text-3); }

/* 双网格内的单元格：8 列 × N 行，背景强度按变化率 */
.heat-grid-large {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 4px;
}
.heat-cell-lg {
  padding: 6px 8px; border-radius: 6px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  cursor: pointer; transition: transform .15s, border-color .15s;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.heat-cell-lg:hover { border-color: var(--accent); transform: scale(1.04); }
.heat-cell-lg .sym { font-size: 12px; font-weight: 600; color: var(--text-1); margin-bottom: 2px; }
.heat-cell-lg .pct { font-size: 11px; font-weight: 500; }
.heat-cell-lg .amt { font-size: 10px; color: var(--text-3); margin-top: 1px; }  /* v6.9 小字提升：9.5→10（最小可读字号） */
.heat-cell-lg .pct.up, .heat-cell-lg .amt.up   { color: var(--up); }
.heat-cell-lg .pct.down, .heat-cell-lg .amt.down { color: var(--down); }

/* 档位颜色（强度由 --int 控制不透明度，CSS 变量在 style 上） */
.heat-cell-lg.v-1  { background: rgba(0,214,143, calc(.08 + var(--int) * .14)); }
.heat-cell-lg.v-2  { background: rgba(0,214,143, calc(.14 + var(--int) * .22)); }
.heat-cell-lg.v-3  { background: rgba(0,214,143, calc(.25 + var(--int) * .35)); }
.heat-cell-lg.v-d1 { background: rgba(255,73,97, calc(.08 + var(--int) * .14)); }
.heat-cell-lg.v-d2 { background: rgba(255,73,97, calc(.14 + var(--int) * .22)); }
.heat-cell-lg.v-d3 { background: rgba(255,73,97, calc(.25 + var(--int) * .35)); }

.heat-empty {
  grid-column: 1/-1; text-align: center;
  color: var(--text-3); padding: var(--sp-5);
  font-size: var(--fs-num);
}
/* ===== v8.49（批次②）：周期重算骨架 loading（动效 C）=====
   refreshHeatPeriod 进行中：网格替换为 12 个脉冲占位块。
   只动 opacity（无重排），prefers-reduced-motion 下脉冲归零（静态占位）。 */
.heat-cell-lg.skel {
  background: var(--bg-elevated); border: 1px solid var(--border);
  min-height: 52px; cursor: default; pointer-events: none;
  animation: heatSkel 1.2s ease-in-out infinite;
}
.heat-cell-lg.skel::after {
  content: ''; display: block; height: 8px; width: 60%;
  margin: 14px auto 0; border-radius: 4px; background: var(--border);
}
@keyframes heatSkel { 0%, 100% { opacity: .35; } 50% { opacity: .7; } }
@media (prefers-reduced-motion: reduce) {
  .heat-cell-lg.skel { animation: none; opacity: .5; }
}
/* v8.49（批次③）：订阅 ★ 角标 + 溢出尾卡 + 强度边框 */
.heat-cell-lg.star::after {
  content: '★'; position: absolute; top: 2px; right: 5px;
  font-size: 8px; color: var(--gold);
}
.heat-cell-lg { position: relative; }
.heat-more {
  border: 1px dashed var(--border); border-radius: 6px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  color: var(--text-3); font-size: 11px; cursor: pointer; text-align: center;
  padding: 6px 4px; line-height: 1.5;
}
.heat-more:hover { border-color: var(--accent); color: var(--text); }
.heat-more .sub { font-size: 9.5px; opacity: .75; }
/* v8.49（批次⑥）：stagger 入场（动效 A）—— 仅 transform/opacity */
@keyframes heatIn {
  from { opacity: 0; transform: translateY(10px) scale(.96); }
  to   { opacity: 1; transform: none; }
}
.heat-cell-lg.anim { animation: heatIn .4s cubic-bezier(.2,.7,.3,1) backwards; }
@media (prefers-reduced-motion: reduce) {
  .heat-cell-lg.anim { animation: none; }
}
/* v8.49（批次⑤）：就地下钻面板（动效 E）*/
#heatDrill {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .25s ease-out;
}
#heatDrill.open { grid-template-rows: 1fr; }
#heatDrill > div { overflow: hidden; }
.heat-drill-card {
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-card); padding: var(--sp-4);
}
.heat-drill-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); margin: var(--sp-3) 0; }
@media (max-width: 700px) { .heat-drill-grid { grid-template-columns: repeat(2, 1fr); } }
.hd-k { font-size: 10.5px; color: var(--text-3); }
.hd-v { font-family: var(--mono); font-size: 13px; font-weight: 600; margin-top: 2px; font-variant-numeric: tabular-nums; }
/* v8.50e（审计修复）：下钻面板涨跌 pill 独立类。
   缺陷复盘：v8.49 引入时复用了 .flow-sig 类，而该类已在 v8.48d（撤销异动机会榜 chip）
   时删除、且无全局 .up/.down 兜底 → pill 只剩字号/内边距，涨红跌绿配色丢失。 */
.hd-pill {
  display: inline-block; font-size: 10.5px; font-weight: 700;
  padding: 0 8px; border-radius: 9px; white-space: nowrap;
}
.hd-pill.up   { color: var(--up);      background: rgba(0,214,143,.10); }
.hd-pill.down { color: var(--down);    background: rgba(255,73,97,.10); }
.hd-pill.dim  { color: var(--text-3);  background: var(--bg2); }

/* v-al9.12：「聚合 CVD」页已整体删除，其样式块（cvd-summary / cvd-stat / cv-* / cvd-row / force-*）
   同步移除 —— 8 个选择器在 html/js 中已零引用。 */

/* ===== 币种全景（卡片流，仿截图 OI 异动卡片）===== */
.coin-cards {
  display: grid;
  /* 多周期表从 6 列扩到 8 列，卡片下限同步提到 520px，否则列会被挤窄。
     ⚠ min() 不能省：视口 < 520px 时固定下限会让栅格溢出屏幕、出现横向滚动条。 */
  grid-template-columns: repeat(auto-fill, minmax(min(520px, 100%), 1fr));
  gap: var(--sp-3);
}
.coin-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: var(--sp-4);
  transition: border-color .15s, transform .15s;
  position: relative; overflow: hidden;
}
.coin-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.06), transparent);
}
.coin-card:hover { border-color: var(--border-hover); transform: translateY(-2px); }

/* ===== COIN 360° ===== */
.coin360-ctrl {
  display: flex; align-items: flex-end; gap: var(--sp-3); flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.cc-group { display: flex; flex-direction: column; gap: 4px; }
.cc-label { font-size: 12px; color: var(--text-3); }
.cc-input {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-1);
  padding: 6px 10px;
  font-size: var(--fs-num);
  font-variant-numeric: tabular-nums;
}
.cc-input { width: 120px; }
.cc-btn {
  padding: 6px 16px; border-radius: 6px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-2); cursor: pointer; font-size: var(--fs-num);
  transition: all .15s;
}
.cc-btn:hover { border-color: var(--accent); color: var(--text-1); }
.cc-btn.primary {
  background: var(--accent); color: var(--on-accent);
  border-color: var(--accent); font-weight: 600;
}

.coin360-stats {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}
/* v7.4：三行统一 4 卡（12 张 = 4×3），列数由上面的基础规则给。
   原先的 .cc4 / .cc3 修饰类已删除 —— 它们存在的理由是"行内卡数≠列数会靠左留空"，
   现在每行都是 4 张，基础规则即正确；去掉修饰类后 1100px / 700px 断点也能对三行
   一致生效（此前 .cc4 特异性 0-2-0 高于媒体查询里的 .coin360-stats 0-1-0，
   窄屏下会把那两行硬钉在 4 列不收口）。 */
@media (max-width: 1100px) { .coin360-stats { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 700px)  { .coin360-stats { grid-template-columns: repeat(2, 1fr); } }
/* ===== v8.48（UI 批次②）：12 卡三组重组的可视化件 ===== */
/* 组标题：小字浅色 + 左侧色点，只作视觉分层不抢卡片 */
.c360-group-label {
  display: flex; align-items: center; gap: 8px;
  font-size: 11px; letter-spacing: .5px; color: var(--text-3);
  margin: var(--sp-4) 0 var(--sp-2); font-weight: 600;
}
.c360-group-label::before {
  content: ''; width: 7px; height: 7px; border-radius: 2px;
  background: var(--accent); opacity: .75;
}
.view-panel .c360-group-label:first-of-type { margin-top: var(--sp-2); }
/* 合并卡跨两列（多空对比条卡）；≤700px 两列布局时正好占满整行 */
.ccard-span2 { grid-column: span 2; }
@media (max-width: 700px) { .ccard-span2 { grid-column: 1 / -1; } }
/* 双色多空对比条：左红（多）右绿（空），flex 比例即持仓占比 */
.ls-bar-wrap {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: var(--sp-3) 0 var(--sp-2);
}
.ls-bar-val { font-family: var(--mono); font-size: 12px; white-space: nowrap; }
.ls-bar-val.up { color: var(--up); }
.ls-bar-val.down { color: var(--down); }
.ls-bar {
  flex: 1; height: 8px; border-radius: 4px; overflow: hidden;
  display: flex; background: var(--bg-elevated);
}
.ls-bar i { flex: 1; background: var(--up); }
.ls-bar i.neg { background: var(--down); }
/* vs 24期均值偏离角标：右上角；|偏离|≥5% 才着色，否则灰 */
.cc-dev {
  position: absolute; top: 10px; right: 12px;
  font-family: var(--mono); font-size: 10.5px; color: var(--text-3);
}
.ccard { position: relative; }
.cc-dev.hot-up { color: var(--up); }
.cc-dev.hot-down { color: var(--down); }
.ccard {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; padding: var(--sp-4) var(--sp-5);
  position: relative; overflow: hidden;
  /* 统一高度：有的卡片带副标题、有的不带，不约束的话同一行会高低错落、
     数值基线对不齐（实测视觉上像"没设计好"）。 */
  display: flex; flex-direction: column;
  min-height: 98px;
}
.ccard::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.08), transparent);
}
.ccard .cc-label {
  font-size: 11px; color: var(--text-3);
  margin-bottom: var(--sp-2);
  line-height: 1.3;
}
.ccard .cc-value {
  font-size: var(--fs-num-xl); font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  word-break: break-all;   /* 超长数值（如 24H 成交）不撑破卡片 */
}
.ccard .cc-value.dim { color: var(--text-3); }
.ccard .cc-value.up   { color: var(--up); }
.ccard .cc-value.down { color: var(--down); }
/* margin-top:auto 把副标题推到卡片底部，无副标题的卡片靠 min-height 撑齐 */
.ccard .cc-sub {
  font-size: 10px; color: var(--text-3);
  margin-top: auto; padding-top: 6px;
  line-height: 1.3;
}
/* v-al9.10：CVD 卡第 4 行 —— 15m / 1h 两个短窗口并排（11px，等宽数字；不给 cc-sub 的 margin-top:auto，
   让两行靠在一起，避免被卡片底部撑开） */
/* v-al9.11：仅 CVD 卡压紧（它多一行，需把行距收回 110.3 → 与左邻同高）。
   ⚠ 用 .cc-cvd 限定，**不动全局 .cc-sub** —— 否则 11 张卡一起位移（手稿 D3：保持卡高） */
.ccard.cc-cvd .cc-sub { margin-top: 4px; padding-top: 0; }
.ccard .cc-w2 {
  font-size: 11px; color: var(--text-3); line-height: 1.4; margin-top: 2px;
  font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ccard .cc-w2 b { font-weight: 500; color: var(--text-2); }

/* ⚠ 这里原有一条 `.coin360-head { grid-template-columns: 80px 1fr 80px repeat(6, 1fr) }`
   的通用定义，它排在 .coin360-flow-head 之后，同为单类选择器就会覆盖它，
   导致资金流表「表头 9 列、数据行 7 列」完全错位。
   交易所表与资金流表列数不同，必须由各自的类单独控制，删掉这条通用规则。 */

.coin-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: var(--sp-2); }
.coin-title { font-size: 14px; font-weight: 600; letter-spacing: .3px; }
.coin-sub { font-size: 11px; color: var(--text-3); margin-bottom: var(--sp-3); }
.coin-badges { display: flex; gap: var(--sp-2); margin-bottom: var(--sp-3); flex-wrap: wrap; }
.coin-badge {
  padding: 3px 9px; border-radius: 20px;
  font-size: var(--fs-xs); font-weight: 500;
  background: var(--up-dim); color: var(--up);
}
.coin-badge.warn { background: rgba(255,170,0,0.12); color: var(--neutral); }
.coin-badge.mute { background: var(--bg-elevated); color: var(--text-2); }

/* 价仓组合信号条：醒目、独立一行，hover 显示推理原因 */
.coin-signal {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 6px 10px; border-radius: 8px; margin-bottom: var(--sp-3);
  font-size: 12px; font-weight: 600; cursor: help;
}
.coin-signal .sig-label { font-weight: 700; }
.coin-signal .sig-action { font-size: 11px; font-weight: 500; opacity: .85; }
/* ⚠ v3 修复「产品 P0-1」（信号块去指令化；注意不是 P1-2）：
   信号块不再用买卖色块（绿/红），仅保留 warn/mute 区分。
   up/down 改为与 warn 一致的淡中性底色 + 文字 micro 标方向（用 var(--up)/--down
   作 accent 但透明度大幅降低），让平台审核/用户读到的是"信号描述"而非"买卖指令"。 */
.coin-signal.signal-up   { background: rgba(0,230,120,0.04); color: var(--text); border: 1px solid rgba(0,230,120,0.10); }
.coin-signal.signal-up   .sig-label::after { content: ' ▲'; color: var(--up); opacity: .55; font-size: 10px; }
.coin-signal.signal-down { background: rgba(255,60,60,0.04);  color: var(--text); border: 1px solid rgba(255,60,60,0.10); }
.coin-signal.signal-down .sig-label::after { content: ' ▼'; color: var(--down); opacity: .55; font-size: 10px; }
.coin-signal.signal-warn { background: rgba(255,170,0,0.06);  color: var(--neutral); border: 1px solid rgba(255,170,0,0.20); }
/* v3 修复「算法 P1-2」：静默累积/派发（accum）—— 横盘蓄势突破前兆，独立中性色（紫/青），
   既不暗示多也不暗示空，只是"持仓在悄悄堆量而价格没动"的事实描述。 */
.coin-signal.signal-accum { background: rgba(167,139,250,0.06); color: var(--text); border: 1px solid rgba(167,139,250,0.25); }
.coin-signal.signal-accum .sig-label::after { content: ' ◆'; color: var(--purple); opacity: .75; font-size: 10px; }
.coin-signal.signal-mute { background: var(--bg-elevated); color: var(--text-2); border: 1px solid var(--border); }
/* v6 异动进行中档：醒目中性（青色系，与主题呼应），闪电标记区别于静默的菱形 */
.coin-signal.signal-moving { background: rgba(0,212,255,0.06); color: var(--text); border: 1px solid rgba(0,212,255,0.25); }
.coin-signal.signal-moving .sig-label::after { content: ' ⚡'; opacity: .8; font-size: 10px; }

/* 卡片内价格行：当前价 + 24H 涨跌 */
.coin-price {
  display: flex; align-items: baseline; gap: var(--sp-2);
  flex-wrap: wrap; row-gap: 6px;
  margin-bottom: var(--sp-3);
  font-variant-numeric: tabular-nums;
}
.coin-price .cur { font-size: var(--fs-lg); font-weight: 600; color: var(--text); }
.coin-price .chg { font-size: 12px; font-weight: 500; }
.coin-price .tag { font-size: 10px; color: var(--text-3); }
/* 实时 OI：与「24H 价格」并排，中间用细分隔线隔开两组数据。
   .coin-price 是 baseline 对齐，分隔线要单独用 center，否则会被基线拉变形。 */
.coin-price .oi { font-size: var(--fs-lg); font-weight: 600; color: var(--text); }
.coin-price .oi-sep {
  width: 1px; height: 12px; background: var(--border);
  align-self: center; margin: 0 var(--sp-1); flex: none;
}

/* v7.1「实时 OI(币) + 实时持仓价值」打包成一组。
   动因：.coin-price 是单个 flex-wrap 容器，手机上会任意折行，把数值与它紧跟的
   标签拆到两行去（用户反馈"看着不顺眼"）。绑成子容器后可在移动端整组换行。
   桌面端此规则与原先"所有 span 平铺在同层 flex"渲染完全一致（同样 gap 8px / baseline），
   不改变任何现有视觉 —— 换行行为只在 ≤600px 块里启用。 */
.coin-price .coin-oi-group {
  display: inline-flex; align-items: baseline;
  flex-wrap: wrap; gap: var(--sp-2); row-gap: 6px;
}

/* v7.1 实时 OI 的快照时间改为「数值上行 / 时间下行小字」—— 与周期表
   「O.I. 持仓量」列同款排布（原来是 margin-left:3px 跟在数值右侧同一行）。
   用纵向 flex 而非给内层 span 加 display:block：后者在 inline 父元素里会生成
   匿名块盒、父元素高度算不准。 */
.coin-price .coin-oi-group .oi {
  display: inline-flex; flex-direction: column; align-items: flex-start;
}
.coin-price .coin-oi-group .oi .oi-t {
  font-size: 10px; font-weight: 400; opacity: .6; line-height: 1.3;
}
/* 分组内的分隔竖线钉在第一行（数值行）。这组里 .oi 是两行高，而基线基线规则下
   原来的 align-self:center 会按两行总高居中，竖线会掉到数值/时间的交界处。 */
.coin-price .coin-oi-group .oi-sep { align-self: flex-start; margin-top: 4px; }
/* 24H 成交额数值最长（如 $1.23B），字号压一档避免卡片被撑宽。 */
.coin-price .oi.vol { font-size: 14px; }

/* 卡片内的涨跌/次要色。
   ⚠ 此前 .up/.down/.dim 只在 .coin-row / .alert-row / .heat-cell 等作用域下
      定义过，而卡片内的多周期表、异动事件表用了这些类却没有对应规则，
      涨跌色一直是失效的（全部渲染成默认文字色）。补上与全局涨绿跌红一致。 */
.coin-card .up   { color: var(--up); }
.coin-card .down { color: var(--down); }
.coin-card .dim  { color: var(--text-3); }
/* v8.0 量能倍数五档着色：暖橙 = 放量、冷蓝 = 缩量、中性 = 与常态持平。
   ⚠ 刻意避开 --up/--down 的红绿 —— 成交量本身没有方向，若用红绿表示"放量"，
     会与紧邻的「价格变化率」列混淆（那一列才是红涨绿跌）。
   ⚠ 同色系内用两级明度表达强弱（强档更亮），背景 --bg-card:#131826 下实测对比度：
     vol-hot 8.14 / vol-warm 4.76 / vol-flat 6.41 / vol-cool 8.41 / vol-cold 4.93，
     全部 ≥4.5（正文级），移动端小字也安全。 */
.coin-card .vol-extreme { color: #FFC53D; }
.coin-card .vol-hot  { color: #EF9F27; }
.coin-card .vol-warm { color: #BA7517; }
.coin-card .vol-flat { color: var(--text-2); }
.coin-card .vol-cool { color: #85B7EB; }
.coin-card .vol-cold { color: #378ADD; }
/* v8.9c：右栏 640 面板内嵌的周期表与卡片共用同一套着色 ——
   原规则全部作用域在 .coin-card 下，面板（.detail-col）吃不到，表内全变默认色。
   此处同值复刻一份 .detail-col 作用域；.mute（口径缺失）顺手补上（全局原本无定义）。 */
.detail-col .up   { color: var(--up); }
.detail-col .down { color: var(--down); }
.detail-col .dim  { color: var(--text-3); }
.detail-col .mute { color: var(--text-3); }
.detail-col .vol-extreme { color: #FFC53D; }
.detail-col .vol-hot  { color: #EF9F27; }
.detail-col .vol-warm { color: #BA7517; }
.detail-col .vol-flat { color: var(--text-2); }
.detail-col .vol-cool { color: #85B7EB; }
.detail-col .vol-cold { color: #378ADD; }

/* 多周期表 */
.coin-periods { margin-bottom: var(--sp-3); }
/* ============================================================
   周期表列轨道 —— **单一真源**（v-al9.8 / P1-8：把两处各写一份的「形状」收敛到这里）
   ⚠ 规则（v-al9.7 踩坑，用户截图实证）：
     · 格数由 JS 决定：`periodTableHTML()` 每行固定输出 9 格（周期 + 8 指标）
     · **两条轨道的轨道数必须相等**，且必须等于上面那个格数
     · 只改宽度不改条数 ⇒ 安全；**改条数就两处一起改** —— 所以它们现在并列在同一个块里，
       一眼能看到兄弟值（此前分散在 L1452 与 L2220，只改一处就会翻车）
   ⚠ 条数不足的症状：第 N 格被浏览器自动换到「第二行第 1 列」——表头读成两行、
     每行数据多一行、行高从 ~29px 涨到 56px。
   ⚠ 改完**必须**跑 `node scripts/verify-period-cols.mjs`：它用真机渲染逐行比对
     「子元素数 = 计算后的轨道数」，并带负向对照与极值注入 —— 静态检查替代不了这一步。
   ⚠ 全局唯一性由 `tests/period.cols.test.mjs` 守卫：任何地方再出现硬编码的
     `grid-template-columns` 周期行规则都会让 `npm test` 变红。
   ============================================================ */
:root {
  /* ① 卡片 / COIN360 覆盖层（容器宽 ~620–1468px）：周期列固定 70px 以容纳「1440前→720前」 */
  --pt-cols-card: 70px 1fr 1fr 1fr 1.15fr 1.15fr 1fr 1fr 1fr;
  /* ② 右栏 640 面板（可用 ~570px）：不拍脑袋等分，按各列**实测最长内容**定比例
     （v-al9.7 口径：周期 24 / OI变化率 49 / OI增量 51 / 主动净流 53 / 持仓量 59 /
       持仓价值 57 / 量能倍数 38 / 快照价格 74 / 价格变化率 49 —— 合计 454px，
       再加 8 列间距 × 8px = 518px ≤ 570px，余 ~52px 由 fr 按比例吸收）。
     用 fr 而非固定 px：面板出现纵向滚动条时（570→562）各列按比例微缩，不会硬溢出。 */
  --pt-cols-panel: 30px 52fr 54fr 56fr 64fr 60fr 42fr 78fr 54fr;
}
.coin-period-row {
  /* 8 列：周期 / OI 变化率 / OI 增量 / O.I. 持仓量 / O.I. 持仓价值 / 成交量 / 快照价格 / 价格变化率
     v5.2：快照拆双列——「O.I. 持仓量」（币数）+「O.I. 持仓价值」（美元），
     与数据明细表「持仓币数/持仓价值」两列一一对应。
     实时值（无滞后）显示在卡片顶部：实时 OI(币) + 实时持仓价值。
     v3 P0-4：周期列 70px 容纳实测分钟数后缀；数字列 white-space:nowrap 对齐。
     v-al9.6：+主动净流列（9 列）—— 插在「OI 增量(本段)」之后。
     ⚠ v-al9.8：轨道改由 `--pt-cols-card` 提供（与右栏轨道并列定义在上面的 :root 块）。 */
  display: grid; grid-template-columns: var(--pt-cols-card);
  padding: 5px 0; font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--border);
  align-items: center;
}
.coin-period-row:last-child { border-bottom: none; }
.coin-period-row.head { color: var(--text-3); font-size: 10px; border: none; padding-bottom: var(--sp-2); }
.coin-period-row .p {
  color: var(--text-2); font-weight: 500;
  cursor: help;   /* 提示可悬停查看精确区间 */
  font-size: var(--fs-xs);  /* 略小以容纳 "5m · 5min" */
  line-height: 1.3;
  white-space: nowrap;
}

/* 卡片内异动表 */
.coin-events-label {
  font-size: 10px; color: var(--text-3);
  margin: var(--sp-3) 0 var(--sp-2);
  letter-spacing: .3px;
}
.coin-event-row {
  /* 6 列：方向 / 名义变化 / 持仓变化 / 增量(USD) / 价格变化 / 触发时间
     v6.5：新增「价格变化」列 —— 与 OI 事件**同一触发窗口**的价格变化率
     （数据 = 多周期表中该周期行的 pricePct，同源同值），
     让 OI 异动和价格行为直接对照：OI +5% 配价 +2%（多头开仓推动）
     与 OI +5% 配价 −1%（背离）是完全不同的故事。 */
  display: grid; grid-template-columns: 46px 1fr 1fr 1fr 1fr 1fr;
  padding: 4px 0; font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.coin-event-row.head { color: var(--text-3); font-size: 10px; }

/* ===== 时间选择器 ===== */
.period-bar {
  display: flex; gap: 2px;
  /* 用比卡片更深的 --bg-tab 做底，与外层 .filters 卡片形成层次，
     再加一圈描边，整体像一个完整的分段控件而非散落的按钮。 */
  background: var(--bg-tab);
  border: 1px solid var(--border);
  border-radius: 10px; padding: 3px;
  /* 9 个周期按钮的宽度是固定的，不许被 flex 压扁（否则文字换行、高度参差） */
  flex: none;
}
.period-btn {
  padding: 6px 13px; border-radius: 7px;
  color: var(--text-3); cursor: pointer;
  font-size: var(--fs-sm); font-weight: 500;
  /* 数字等宽，5m/15m 切换时宽度不跳动 */
  font-variant-numeric: tabular-nums;
  letter-spacing: .2px;
  border: none; background: transparent;
  transition: color .18s, background .18s;
}
.period-btn:hover { color: var(--text); background: rgba(255,255,255,0.05); }
.period-btn.active {
  background: var(--accent); color: var(--bg);
  font-weight: 600;
  box-shadow: 0 2px 6px rgba(0,212,255,0.28);   /* 青色光晕，与主题色呼应 */
}

/* ===== 错误状态 ===== */
.error-banner {
  background: var(--down-dim); border: 1px solid rgba(255,73,97,0.3);
  border-radius: 12px; padding: var(--sp-4);
  color: var(--down); text-align: center;
}

/* ⚠ 深度评估 A3：全站风险提示条 —— v8.27 已从顶部迁到页脚（.page-footer），
   原 .disclaimer 规则随之删除。保留这段说明避免后来者以为样式丢了。 */
/* ===== v6.6 UI 优化（移动优先 · 详见 OI市场监控_UI设计方案.html）===== */
/* v6.9：上标（如周期表「OI 变化率*」的 *）默认 font-size:smaller 会缩到 8.33px，
   低于最小可读字号 —— 显式提到 10px。全站仅 1 处使用，全局规则安全。 */
sup { font-size: 10px; }
/* ① 卡片 hover 边框统一提亮（桌面增强，移动端无 hover 不生效） */
.summary-card:hover, .anom-stat:hover, .coin-card:hover, .coin-table-card:hover {
  border-color: var(--border-hover);
}
/* ===== v6.6 / v8.27：右下角悬浮按钮组（ⓘ 风险提示 + ↑ 回到顶部） =====
   原先 #backTop 自带 fixed 定位；v8.27 增加 ⓘ 后若各自定位，两个 bottom 值要
   手工错开、移动端还要各算一次安全区 —— 统一收进 .fab-stack，容器负责定位与显隐。 */
.fab-stack {
  position: fixed;
  right: calc(var(--sp-4) + env(safe-area-inset-right, 0px));
  bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column; gap: var(--sp-2);
  z-index: 90;
  opacity: 0; pointer-events: none; transform: translateY(8px);
  transition: opacity .25s ease, transform .25s ease;
}
.fab-stack.show { opacity: 1; pointer-events: auto; transform: none; }
.fab-stack button {
  width: 40px; height: 40px; border-radius: 50%; padding: 0;
  background: var(--bg-elevated); border: 1px solid var(--border-hover);
  color: var(--text-2); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  backdrop-filter: blur(8px);
  transition: color .18s, border-color .18s, transform .12s;
}
.fab-stack button:hover { color: var(--text); border-color: var(--up-mid); }
.fab-stack button:active { transform: scale(.92); }
#backTop { font-size: 17px; line-height: 1; }
/* ⓘ 用衬线斜体 i 绘制 —— 不依赖 U+24D8 字形（部分安卓字体缺字会渲染成方框） */
#riskTop {
  font-size: 17px; font-weight: 700; font-style: italic;
  font-family: Georgia, 'Times New Roman', serif;
  line-height: 1;
}

/* ===== v8.27 页脚：数据来源 / 口径 / 免责声明 =====
   承接原顶部 .disclaimer（34px 提示条 + 12px 间距 = 首屏省 46px）。
   不用卡片外壳 —— 页脚不该再抢视觉，用一条虚线顶部边界与内容区分开即可。 */
.page-footer {
  margin-top: var(--sp-6);
  padding: var(--sp-4) var(--sp-2) var(--sp-2);
  border-top: 1px dashed var(--border-hover);
  border-radius: 8px;
  /* 吸顶头部高 ~92px：锚点跳转进来时留出偏移，否则标题被头部压住 */
  scroll-margin-top: 104px;
}
.page-footer .pf-meta {
  /* v8.28（用户要求）：整行**水平居中 + 各项垂直居中**。
     原先左对齐，而下面的免责段落是居中的 —— 同一块页脚里两种对齐方式，
     左边缘与中轴都不成线，观感散。居中后元信息与免责段共用一条中轴。
     align-items:center 是为了「某一项折成两行时，同行其它项垂直居中」，
     不加的话默认 stretch，短项会被拉伸、文字贴在行首（视觉上偏上）。 */
  display: flex; flex-wrap: wrap;
  justify-content: center; align-items: center;
  gap: var(--sp-2) var(--sp-5);
  font-size: 12px; color: var(--text-2);
  padding-bottom: var(--sp-3); margin-bottom: var(--sp-3);
  border-bottom: 1px dashed var(--border);
  text-align: center;
}
.page-footer .pf-meta b { color: var(--text-3); font-weight: 500; }
.page-footer .pf-disc {
  font-size: 12px; color: var(--text-3);
  line-height: 1.85; text-align: center;
}
.page-footer .pf-disc b { color: var(--text-2); font-weight: 600; }
/* ⓘ 点击后的定位高亮：告诉用户「要看的是这一块」 */
.page-footer.flash { animation: pfFlash 1.5s ease; }
@keyframes pfFlash {
  0%, 100% { background: transparent; }
  25%      { background: rgba(0,212,255,.08); }
  60%      { background: rgba(0,212,255,.04); }
}

@media (max-width: 600px) {
  /* ③ 免责声明：v8.27 已迁到页脚（.page-footer），此处只保留页脚的移动端收紧 */
  .page-footer { margin-top: var(--sp-5); padding: var(--sp-3) 0 var(--sp-1); scroll-margin-top: 0; }
  .page-footer .pf-meta { gap: 4px var(--sp-3); font-size: 11px; }
  /* v8.28：免责段在移动端原来改成左对齐（长段窄屏左对齐更好读），
     但上一行元信息现在是居中的 —— 同一块页脚两种对齐会显得散。
     统一为居中，与元信息共用中轴。 */
  .page-footer .pf-disc { font-size: 11px; line-height: 1.8; text-align: center; }
  /* ④ 汇总卡横排紧凑化：label 左 / 大数字右 / meta 小字，卡高 ~140→~64px */
  .summary-row { grid-template-columns: 1fr; gap: var(--sp-2); }
  .summary-card {
    padding: var(--sp-3) var(--sp-4);
    display: grid; grid-template-columns: 1fr auto;
    align-items: baseline; column-gap: var(--sp-3); row-gap: 1px;
  }
  .summary-card::before { display: none; }
  .summary-label { grid-column: 1; grid-row: 1; margin: 0; font-size: var(--fs-sm); }
  .summary-value { grid-column: 2; grid-row: 1; font-size: 18px; text-align: right; }
  .summary-meta { grid-column: 1 / -1; grid-row: 2; font-size: var(--fs-xs); }
  /* ⑤ 横向可滚区域右缘渐隐：暗示"右边还有内容"。
     v6.9：补齐 cvd/hold/flow/ls/fund 五个容器 —— 此前只覆盖 3 个，
     这 5 个视图的右缘列被生硬裁切（用户不知道右边还有内容）。
     ⚠ v7.5 修复：原选择器 .anom-table-wrap 在全文件已无任何元素使用
     （实机 querySelectorAll('.anom-table-wrap').length === 0）—— 异动表的真实容器
     是横向滚动的 .alert-card（内含 #anomBody / #alertBody 两张异动表），
     所以这条渐隐规则一直是空转、从未生效。改用 .alert-card 后两张异动表同时受控。 */
  .period-bar, .coin-table-card, .alert-card,
  #cvdBody, #flowBody, #lsBody, #fundBody {
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 22px), transparent 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 22px), transparent 100%);
  }
  /* ⑥ 顶栏更新时间不折行 */
  #updateTime { white-space: nowrap; font-size: var(--fs-sm); }
  /* ⑧ 热力图汇总卡（总增持/总减持/净变化）：与聚合看板汇总卡同款紧凑横排 ——
     原三列 28px 大数字在 390px 屏被压成三窄条、长数值溢出换行，观感差。
     改单列横排：label 左 / 数值右，28px→20px，信息密度与可读性兼顾。 */
  .heat-summary { grid-template-columns: 1fr; gap: var(--sp-2); }
  .hs-card {
    padding: var(--sp-3) var(--sp-4);
    display: grid; grid-template-columns: 1fr auto;
    align-items: baseline; column-gap: var(--sp-3);
  }
  .hs-card::before { display: none; }
  .hs-label { grid-column: 1; grid-row: 1; margin: 0; font-size: var(--fs-sm); }
  .hs-value { grid-column: 2; grid-row: 1; font-size: var(--fs-xl); text-align: right; }
}
/* ── v-al29（2026-09-22）：数据明细表（.c360-dt）的样式已整体删除 ────────────────
   容器 #c360DataTable 已于 v8.6 按用户要求移除，其渲染器 renderC360DataTable()
   也已在 P0-5b 一并删除 ⇒ 这些规则匹配 0 个元素。共 10 条（容器/表头吸附/涨跌色/翻页器）。
   实测留痕：桌面 1600 / 窄屏 390 / 粗指针 三档 × 6 个视图，全部 0 命中。
   ⚠ `.pager` 这个类名**仍被「币种列表分页器」使用**（见上方 :627 起的 .pager / .pg-btn / .pg-info 段）
     —— 所以只删带 `.c360-dt` / `#c360Pager` 前缀的 **scoped** 规则，绝不要碰通用 .pager。 */

/* ===== v5.17 移动端体验优化（<600px，全部收口在本块，桌面端零影响） ===== */
@media (max-width: 600px) {
  /* 安全区：iPhone 刘海/底部横条不遮挡内容（需 viewport-fit=cover 配合） */
  body {
    padding: max(10px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-right))
             max(16px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
  }

  /* 触控体验：去掉蓝色点击高亮、消除双击缩放延迟、按下给亮度反馈 */
  * { -webkit-tap-highlight-color: transparent; }
  button, .tab, .heat-cell, .filter-btn, .period-btn, .mini-btn { touch-action: manipulation; }
  button:active:not(:disabled), .tab:active, .heat-cell:active { filter: brightness(1.3); }

  /* 触控目标 ≥32px（Apple HIG 最低推荐；原 mini-btn 仅 ~24px 高，手机上难点中） */
  .mini-btn { padding: 8px 12px; font-size: 12px; min-height: 32px; white-space: nowrap; }
  .tab { padding: 9px 13px; font-size: 12px; min-height: 34px; }
  .period-btn { padding: 9px 13px; min-height: 34px; }
  .filter-btn { padding: 9px 13px; min-height: 34px; }
  .search-input { font-size: 16px; width: 100%; max-width: 260px; }   /* 16px 防 iOS 聚焦自动放大 */

  /* v8.27：状态不再有「长文案被挤出屏幕」的问题 —— 胶囊固定 116px 槽位，
     长句走视觉隐藏的 live region，此处原 .status-text 省略规则随之删除。 */

  /* ① 卡片九周期表（8 列最痛点）：容器横向滚动 + min-width，
        滚动区出血到卡片边缘（负 margin），视觉上滑动范围更大更自然 */
  .coin-card { padding: var(--sp-3); }
  .coin-periods {
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0 calc(-1 * var(--sp-3));
    padding: 0 var(--sp-3);
  }
  .coin-periods::-webkit-scrollbar { display: none; }
  .coin-period-row { min-width: 620px; }

  /* ② 全部数据表（coins/hold/flow/ls/fund/cvd/anomaly）：前两列（序号+币种）吸附，
        横向滑动时币种名始终可见 —— 不用来回滑回去猜"这是哪个币" */
  .coin-row > div:first-child,
  .coin-row > div:nth-child(2),
  .alert-row > div:first-child,
  .alert-row > div:nth-child(2) {
    position: sticky; z-index: 2;
    background: var(--bg-card);
  }
  .coin-row > div:first-child, .alert-row > div:first-child { left: 0; }
  .coin-row > div:nth-child(2), .alert-row > div:nth-child(2) {
    left: 46px;   /* = 第一列 grid 宽 46px */
    box-shadow: 6px 0 8px -6px rgba(0,0,0,0.5);   /* 右缘渐隐阴影，暗示下面还有列 */
  }
  .coin-row.header > div:first-child,
  .coin-row.header > div:nth-child(2) { background: var(--bg-elevated); }

  /* ③ 数据明细表（16 列）时间列吸附 —— 随 .c360-dt 一并删除（见上方 v-al29 说明） */

  /* ④ 横向滚动容器统一：滚动条隐藏（手机无鼠标）、惯性滚动、防止滚动链带动页面 */
  .coin-table-card, #flowBody, #lsBody, #fundBody, #cvdBody, #anomBody {
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    overscroll-behavior-x: contain;
  }
  .coin-table-card::-webkit-scrollbar, #anomBody::-webkit-scrollbar { display: none; }

  /* ⑤ 卡片顶部数据条：字号微降避免折行过碎 */
  .coin-price .cur { font-size: 14px; }
  .coin-price .oi { font-size: var(--fs-num); }

  /* v7.1 实时 OI(币) + 实时持仓价值 整组换行到独立一行（内部不再被拆散）。
     flex-basis:100% 让这组独占一行，两组数据并排在同一行上。
     行首分隔线隐藏 —— 换行本身已是视觉分隔，留着会变成上一行末尾一根孤立的竖线。 */
  .coin-price .oi-sep.lead { display: none; }
  .coin-price .coin-oi-group { flex-basis: 100%; }

  /* ⑥ 热力图：列数改由文末 v6.8 段统一收口（移动端 5 列）；此处仅保留格子最小高度提升可点性 */
  .heat-cell { min-height: 58px; }

  /* ⑦ COIN 360° 统计卡 2 列保持（信息密度 vs 可读平衡），卡片间距收紧 */
  .ccard { padding: var(--sp-3); }

  /* ⑧ 汇总卡：1 列纵排已定，数字再大一档（手机远距离/单手扫读） */
  .summary-value { font-size: var(--fs-xl); }

  /* ── v-al29（2026-09-22）：COIN 360° 数据明细翻页器（#c360Pager）的移动端样式已删除 ──
     原为 v6.6b→v6.7 的「分段控件式翻页器 + sticky 吸底」，容器本身已于 v8.6 随明细表移除。
     共 6 条规则，实测三档 × 6 视图全部 0 命中。 */

  /* ===== v6.7 热力图持仓增减网格：移动端 8 列 → 5 列 =====
     8 列在 390px 屏每格仅 ~44px，SKHYNIX/SAMSUNG 类长符号必然溢出截断。
     5 列（用户建议）+ 符号 11px + 超长省略号兜底，桌面 8 列不变。 */
  .heat-grid-large { grid-template-columns: repeat(5, 1fr); gap: 3px; }
  .heat-cell-lg { padding: 5px 3px; min-width: 0; overflow: hidden; }
  .heat-cell-lg .sym {
    font-size: 11px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .heat-cell-lg .pct { font-size: var(--fs-xs); }
  .heat-cell-lg .amt { font-size: 10px; }   /* v6.9：9→10，移动端最小可读字号 */

  /* ===== v6.8 聚合看板内嵌热力图（.heat-grid / .heat-cell）：8 列 → 5 列 =====
     此处此前刻意保留 8 列（"一眼扫全市场"），但 390px 屏每格仅 ~34px：
     符号 SKHYNIX/SAMSUNG（6-7 字符 × 11px ≈ 40px+）与金额 -$11.61M（10px ≈ 45px）
     必然超出格子，被 .heat-cell{overflow:hidden} 裁掉 —— 用户可见的"截断"。
     与热力图视图的 .heat-grid-large（v6.7 已改 5 列）统一：5 列每格 ~62px，
     符号 ellipsis 兜底、金额 9px 收紧，信息完整可读。
     注：桌面端（>600px）保持 8 列不变，本规则仅在移动端媒体块内生效。 */
  .heat-grid { grid-template-columns: repeat(5, 1fr); gap: 4px; }
  .heat-cell { padding: 5px 3px; min-width: 0; }
  .heat-cell .sym {
    font-size: 11px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .heat-cell .pct { font-size: var(--fs-xs); }
  .heat-cell .amt { font-size: 10px; }      /* v6.9：9→10，移动端最小可读字号 */

  /* ===== v6.9 P1-② 统计卡体系统一：移动端紧凑横排 =====
     评估发现 6 套统计卡组件并存（summary-card / hs-card / cvd-stat / anom-stat /
     ccard / coin360-stats），label/value/sub 类名各写各的；此前只优化了前 2 套，
     CVD / 异动监控 / COIN 360° 仍是 140px 高卡纵排，首屏被卡片占满（CVD 视图浪费 ~200px）。
     本轮把已跑通的「label 左 / 数值右 / 副标题下行」模式复制到其余 3 套，
     与聚合看板、热力图视觉语言统一。容器列数：anom-stats 2×2 → 1 列。
     注：后续可抽 .stat-card 基础组件做根治（已列入阶段 2）。 */
  .ccard {
    padding: var(--sp-3) var(--sp-4);
    display: grid; grid-template-columns: 1fr auto;
    align-items: baseline; column-gap: var(--sp-3); row-gap: 1px;
    min-height: 0;                       /* 覆盖 .ccard 的 min-height:98px */
  }
  .ccard::before { display: none; }
  .ccard .cc-label {
    grid-column: 1; grid-row: 1; margin: 0; font-size: var(--fs-sm);
  }
  .ccard .cc-value {
    grid-column: 2; grid-row: 1; text-align: right; line-height: 1.2;
  }
  .cv-value { font-size: 18px; }
  .as-value { font-size: var(--fs-xl); }         /* 32 → 20 */
  .ccard .cc-value { font-size: 18px; }  /* 24 → 18 */
  .ccard .cc-sub {
    grid-column: 1 / -1; grid-row: 2;
    margin: 0; padding: 0; font-size: var(--fs-xs);
  }
  /* 异动监控 4 卡：2×2 → 单列紧凑行（与其余卡片语言一致） */
  .anom-stats { grid-template-columns: 1fr; }
}

/* ===== v7.0 口径说明轻触可达（移动端）+ 无障碍 =====
   背景：全站 700+ 处 title 承载口径解释（"名义 vs 持仓"、"快照时刻"、
   "实时值 vs 区间起点" 等核心概念），移动端没有 hover —— 这些说明
   100% 不可达，属功能缺失而非观感问题。
   方案：轻触带 title 的元素 → 底部浮层显示文案（JS 绑定见 IIFE 末尾）。
   浮层放 bottom 而非 top：顶部已被状态栏/周期栏/筛选栏占满，底部更少遮挡。 */
#tipToast {
  position: fixed; left: 50%;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 18px);
  transform: translate(-50%, 10px);
  max-width: min(92vw, 440px);
  padding: 10px 14px;
  border-radius: var(--radius-m);
  background: #161d2e;
  border: 1px solid var(--border-hover);
  color: var(--text-1);
  font-size: 12px; line-height: 1.55; text-align: left;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
  opacity: 0; pointer-events: none; z-index: 60;
  transition: opacity .18s ease, transform .18s ease;
}
#tipToast.show { opacity: 1; transform: translate(-50%, 0); }

/* 无 hover 设备（触屏）：给承载口径说明的元素加下划虚线，作为"可轻触查看"的
   视觉暗示。不加在 button/.hw-x 等自身有动作的元素上，避免误以为要长按。
   选择器限定到几条关键的说明锚点，全站撒虚线下划线会变成视觉噪音。 */
@media (hover: none), (pointer: coarse) {
  .coin-sub[title],
  .coin-period-row.head [title],
  .coin-event-row.head [title],
  .alert-card [title],
  .coin-table-card [title] {
    text-decoration: underline dotted rgba(148, 156, 171, .5);
    text-underline-offset: 3px;
  }
}

/* ===== v7.5 触控目标（仅粗指针 / 无 hover 设备；桌面完全不受影响）=====
   实测（390×844 + hasTouch）暴露的问题：
     · 主控件仅 34~37px 高：.tab / .period-btn / .filter-btn / .anom-filter-btn /
       .mini-btn / .cc-btn / 翻页按钮
     · 行内币种符号 .mgroup-item .msym 仅 13×18px —— 列表里最难命中
   分级处理（不做"一律 44px"，否则 25 行的币种列表会被撑高到 1100px，密度崩掉）：
     · 独立按钮 / 输入框 → 44px（Apple HIG、WCAG 2.5.5 AAA）
     · 列表行内符号 → 抬到 WCAG 2.2 AA 的 24px 下限，并用伪元素把热区撑满整行高度
       （仅纵向 ±6px，横向不外扩 —— 避免抢相邻行的点击）
   位置在样式表最末：同特异性下后者胜，压过上方 .tab/.period-btn 的 min-height:34px。 */
@media (hover: none), (pointer: coarse) {
  /* v-al29b（2026-09-23）：列表里原有的 .anom-filter-btn 已移除 ——
     它的专用样式在 v-al5.0 就随旧面板删掉了（见上方两处留痕注释），
     但这两处**选择器列表成员**被漏了下来（选择器列表最容易漏，与 `.pager` 同类）。
     实测：5 个状态（含点遍全部 tab / 开覆盖层 / 窄屏 / 粗指针）× 6 个选择器匹配数恒为 0。
     ⚠ 不要「补回来」—— 若将来真需要该按钮，请先让它出现在 HTML/DOM 里。 */
  .tab, .period-btn, .filter-btn, .mini-btn, .cc-btn {
    min-height: 44px;
  }
  .cc-input { min-height: 44px; }
  /* 币种列表行：行高 21px → 25px，符号热区撑满整行（纵向 ±6px，横向 0） */
  .mgroup-item { padding: 6px 0; }
  .mgroup-item .msym { position: relative; }
  .mgroup-item .msym::after { content: ''; position: absolute; inset: -6px 0; }
  /* 卡片标题（可点，进入 COIN 360°）：20px → 24px 下限 */
  .coin-title { min-height: 24px; }
  /* 订阅币种的删除键：自身很小，只能在 chip 内纵向撑开热区 */
  .hw-x { position: relative; }
  .hw-x::after { content: ''; position: absolute; inset: -8px -3px; }
}

/* ============================================================
   v8.1 工作台：三栏布局 / 左栏分组导航 / 右栏详情 / 覆盖层
   ⚠ 断点由「中栏 ≥ 652px」反推（.coin-period-row 620 + 卡片内边距 32）：
      <1000px 单栏（导航退回横向）｜≥1000px 两栏 180 + 1fr
      ｜≥1320px 三栏 180 + 1fr + 370（1320−48−180−370−32 = 690px ✅）
      左/右栏不得再加宽，否则周期表会出现卡内横向滚动。
   ============================================================ */
.workbench { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); align-items: start; }
/* ⚠ min-width:0 必须：grid 子项默认按内容最小宽参与分配，缺它会把右栏挤出视口 */
.main-col { min-width: 0; }
.detail-col { display: none; }
/* v8.21：全宽区 —— 「市场聚合看板」+「全市场异动监控」横跨工作台整宽。
   ⚠ grid-column: 1 / -1 在单列（<1360px）时等价于占满该列，无副作用。 */
.wide-row { grid-column: 1 / -1; min-width: 0; }

/* ============================================================
   顶部区（v8.31）：两张**彼此独立**的卡片
   ============================================================
   卡 1 .top-bar  ── 状态胶囊 + 环形刷新 + 导出 + 更新时间
   卡 2 .tab-bar  ── 视图导航（独占一行、居中）

   为什么导航不并进卡 1（v8.26 试过并被否）：卡 1 里状态文案随扫描阶段变化、
   倒计时每秒在走，导航夹在中间会随相邻元素伸缩而位移 —— 点哪算哪。
   分卡后导航位置在整页生命周期内恒定。

   ⚠ v8.31（用户要求「两个元素不要捆绑在一起」）：**取消外层包裹容器**，
     两卡是同级的兄弟元素。改法是各自 position:sticky：
       卡 1 → top: 0
       卡 2 → top: var(--head-h)   ← 正好贴住卡 1 下沿
     这样吸顶后两卡相邻无缝（不会留出被内容穿透的横缝），静止时靠
     卡 1 的 margin-bottom 形成 12px 间距。
     附带好处：不再需要「给容器铺不透明底遮缝」，
     也就顺带消除了 v8.30 那类「不透明底抹掉 body::before 装饰光晕」的接缝风险。

   ⚠ 导航**不是** .top-bar 的后代 —— 那会让 .top-bar 的 backdrop-filter
     成为 position:fixed 后代的包含块、把移动端底栏顶到卡片底边。
     两者平级，≤600px 的 position:fixed 底栏可正常工作（已实机验证 bottom gap = 0）。 */

/* 卡 1 的高度，供卡 2 的吸顶偏移使用。
   ⚠ v8.61 修正：56px 是按已删除的「环形刷新按钮」（行高 34）推算的过期值——
     卡 1 实际 = padding 10×2 + 胶囊 26 + 边框 1×2 = **48px**，
     旧的 56px 让卡 2 吸顶后与卡 1 底部恒定漏 8px，下层内容透出（宗翰截图）。
   运行时 syncStackH() 会用 offsetHeight 实测覆写此值（含未来增删卡 1 内容的场景），
   此值仅作无 JS 兜底。 */
:root { --head-h: 48px; }
/* v8.44（任务 ③）：吸顶堆叠总高兜底值 125px（卡1 56 + 导航卡 69 @1500px）。
   ⚠ v8.48：实测该值随视口宽度变——1050px 时导航卡只 41px（堆叠 97px），
     硬编码会让表头吸附位低于堆叠底沿，行内容从 97~125px 的缝里漏出（用户截图）。
   修法：syncStackH() 运行时实测堆叠底沿并覆写此变量（见 JS），此值仅作无 JS 兜底。 */
:root { --stack-h: 125px; }

.top-bar {
  position: sticky; top: 0; z-index: 31;
  padding: 10px var(--sp-4);
  /* 静止态两卡之间的 12px 间距；吸顶时卡 2 用 top:var(--head-h) 贴住下沿，不留缝 */
  margin-bottom: var(--sp-3);
  gap: var(--sp-4);
}
.tb-actions { gap: var(--sp-3); }
.tb-stamp {
  font-size: var(--fs-sm); color: var(--text-2);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* ── 状态胶囊 ──
   宽度固定 116px（= 最长态「限流中 47s」所需），短标签居中留白。
   ⚠ 一定要用固定 width 而不是 min-width —— 目的是「任何状态切换都不推动相邻控件」。
   ⚠ 116 而不是 88：88px 是按常见态（4 字标签）量的，实测限流态带倒计时会折行。 */
.status-chip-wrap { position: relative; flex: none; }
.status-chip {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  width: 116px; height: 26px; padding: 0 10px;
  border-radius: 999px; cursor: pointer; font-family: inherit;
  font-size: var(--fs-md); font-weight: 500; white-space: nowrap;
  background: var(--up-dim); border: 1px solid var(--up-mid); color: var(--up);
  transition: background .18s, border-color .18s, color .18s;
}
.status-chip:hover { border-color: var(--up); }
/* 五态：颜色沿用既有语义令牌，不引入新色系（暖橙已被量能占用，此处只用绿/蓝/黄/红/灰） */
.status-chip[data-state="scanning"] { background: rgba(78,161,255,.10); border-color: rgba(78,161,255,.3); color: #4ea1ff; }
.status-chip[data-state="stale"]    { background: rgba(255,170,0,.10);  border-color: rgba(255,170,0,.3);  color: var(--neutral); }
.status-chip[data-state="banned"]   { background: var(--down-dim);      border-color: var(--down-mid);      color: var(--down); }
.status-chip[data-state="failed"]   { background: var(--bg-elevated);   border-color: var(--border-hover);  color: var(--text-2); }
.status-chip-txt { overflow: hidden; text-overflow: ellipsis; }

/* ── 状态详情浮层 ── */
.status-pop {
  position: absolute; left: 0; top: calc(100% + 8px); z-index: 60;
  width: 340px; max-width: min(340px, calc(100vw - 32px));
  background: var(--bg-elevated); border: 1px solid var(--border-hover);
  border-radius: var(--r-l); padding: var(--sp-4);
  box-shadow: 0 18px 40px rgba(0,0,0,.55);
}
.status-pop::before {
  content: ''; position: absolute; left: 26px; top: -6px;
  width: 11px; height: 11px; background: var(--bg-elevated);
  border-left: 1px solid var(--border-hover); border-top: 1px solid var(--border-hover);
  transform: rotate(45deg);
}
.sp-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.sp-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--up); flex: none; }
.sp-title { font-size: 13.5px; font-weight: 600; color: var(--text); }
.sp-list { display: grid; grid-template-columns: auto 1fr; gap: 5px var(--sp-3); margin: 0; }
.sp-list dt { font-size: 12px; color: var(--text-3); }
.sp-list dd { font-size: 12px; color: var(--text-1); margin: 0; font-variant-numeric: tabular-nums; }
.sp-foot {
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px dashed var(--border-hover);
  font-size: 12px; color: var(--text-3); line-height: 1.75;
}
.sp-foot b { color: var(--text-2); font-weight: 600; }


/* ── 视觉隐藏但可被读屏读取（#topStatus / #countdown）── */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* ── 临时提示条（flashStatus 的落地处）──
   原先 flashStatus 把提示写进 #topStatus → 胶囊会被撑长、推动右侧控件。
   现在独立成顶部居中的轻提示，3 秒后自动消失。 */
.status-toast {
  position: fixed; left: 50%; top: 68px; z-index: 95;
  transform: translate(-50%, -8px);
  max-width: min(520px, calc(100vw - 32px));
  padding: 8px var(--sp-4); border-radius: 999px;
  background: var(--bg-elevated); border: 1px solid var(--border-hover);
  color: var(--text); font-size: var(--fs-md);
  box-shadow: 0 10px 28px rgba(0,0,0,.5);
  opacity: 0; pointer-events: none;
  transition: opacity .22s ease, transform .22s ease;
}
.status-toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ── 卡 2：视图导航 ──
   宽度收到内容宽并居中（v8.26 用户要求）；同时保留 safe center 兜底：
   内容溢出时自动退化为 flex-start，不会把首个标签顶出滚动区。
   ⚠ v8.27：原来 .tab-bar 的基样式是「独立卡片」——放进头部后就变成卡片套卡片。
     这里改写为「内嵌切换器」：更深的 --bg-tab 底 + 更小内边距。
   ⚠ 内边距必须写在本选择器上（0-2-0），不要用 `.top-bar .tab-bar`（0-3-0）——
     那会压过移动端媒体查询里的底栏 padding，把底部导航条挤变形。
   ⚠ v8.31：与状态卡平级，各自 sticky。top 用 --head-h 贴住状态卡下沿 ——
     比「共用外层容器」更简单，也避免了容器不透明底要吃装饰光晕的问题。
   ⚠ v8.35（用户要求）：**铺满** —— 宽度与上方状态卡一致（两者都是 .container 的
     直接子元素、同为块级 flex 容器，width:auto 即自然对齐），不再 fit-content + 居中。 */
.tab-bar.top-nav {
  position: sticky; top: var(--head-h); z-index: 30;
  margin: 0 0 var(--sp-3);
  /* v8.34：内边距 3px → 5px、外轨圆角 10px → 12px。
     原 3px 配 10px 圆角时，胶囊圆角 7px 正好等于「外圆角 − 内边距」= 同心圆角，
     两端只剩 4px 可见间隙 —— 曲线几乎重合，选中块读起来像"贴死"在轨道端头。
     改 5px 后间隙 6px，且仍保持同心（12 − 5 = 7 = 胶囊圆角）。
     ⚠ 移动端底栏另有 padding 覆盖（0-2-0 同特异性、位置在后），不受影响。 */
  padding: 5px;
  gap: 2px;
  background: var(--bg-tab);
  border-color: var(--border);
  border-radius: 12px;
  /* overflow-x:auto 是**兜底**：正常宽度下 5 格等分铺满、永不触发；
     只有在极窄（<660px）且 flex 已到 min-content 下限时才会出现横滚。 */
  flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none;
  justify-content: flex-start;
}
.tab-bar.top-nav::-webkit-scrollbar { display: none; }
/* v8.34/35：导航标签 = **等宽分段**，且随轨道铺满。
   原先宽度随字数漂移（66 / 79 / 92 / 92 / 105）；v8.34 先按最宽标签定 102px 定宽，
   v8.35 用户要求「与上方控件同长」，改为 5 格等分整个轨道 —— v8.34 那个定宽令牌随之退场。
   ⚠ 关键：**不要写 `min-width: 0`**。flex 项默认 `min-width: auto`（= min-content），
     它保证格宽不会被压到比「图标 + 文字」还窄；一旦写 0，窄屏下文字会同相邻格重叠。
     保留默认后，窄到装不下时各格自动停在下限、由轨道横滚兜底（实测 601px 仍无重叠无横滚）。
   ⚠ 等分同时消掉一个细节：选中态 font-weight 500→600 会让文字宽 0.4px ——
     等分后这 0.4px 不再参与整条控件的宽度计算。 */
.tab-bar.top-nav .tab {
  flex: 1 1 0;
  padding: 6px 12px; border-radius: 7px;
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
/* v8.34：标签前置线性图标 —— 形状记忆先于文字识别，切换时靠扫视定位而非逐字读。
   13px、stroke 1.4、currentColor（随标签文字色走，选中态自动变深色）。
   aria-hidden：纯装饰，可访问名仍由 <span> 里的文字提供。 */
.tab-bar.top-nav .tab .ti { width: 13px; height: 13px; flex: none; display: block; }
/* v8.35：≤700px 隐藏导航图标，**为了保证「等长分段」成立**。
   flex 项默认 `min-width: auto`（= min-content），所以格宽有一个下限：
   带图标时最宽的「资金流 · CVD」min-content = 13(图标) + 6(间距) + 77(文字) + 24(内边距)
   = **119.7px**。而格宽 = (视口 − 32(body) − 12(轨道内边距+边框) − 8(4×2 间距)) / 5，
   反解 119.7 → 视口 ≈ **651px**。实测 660 仍等分、640 差 2.6px、601 差 12.4px。
   去掉图标后下限降到 77 + 24 = **101px** → 601px 的格宽 109.8px 仍有余量 8.8px。
   取 700 而非贴着 651，是为了给字体渲染留缓冲（阈值上贴着走，亚像素差就会破相等）。
   ⚠ 图标是 `display:none`，`.tab` 的 `gap:6px` 会随之失效（gap 只在可见项之间生效），
     所以不需要额外把 gap 归零。 */
@media (max-width: 700px) {
  .tab-bar.top-nav .tab .ti { display: none; }
}


/* v8.9：右栏升级为 640px 完整详情面板（8 列周期表内嵌，覆盖层降级小屏专用）。
   三栏是否点亮由 .workbench.with-detail 控制（applyViewDisplay 按视图切换）：
   「异动流」展开右栏；宽表视图自动收起，中栏拿回全宽。
   断点 1320 → 1360：180 + 640 + 间距 32 = 852，1360 − 48(页边距) − 852 = 460px
   是开启详情时中栏的最窄值 —— 轻卡片三指标各 ~140px，可读。 */
@media (min-width: 1360px) {
  /* v8.11：首列（180px 侧栏）已退场 —— 只剩「中栏 + 右栏」两列，
     中栏因此比改造前宽 180px（616 → 796px @1500）。 */
  .workbench.with-detail {
    grid-template-columns: minmax(0, 1fr) 640px;
    /* v8.46：stretch —— 左右两栏底边对齐（用户要求）。
       原基类 align-items:start 让两栏各自独立生长，底边不齐。
       stretch 让矮栏拉伸到与高栏同高：detail-col 内 overflow-y:auto 兜底滚动。
       ⚠ v8.48d：用户最终选择回到 v8.48b 状态（stretch 对齐）——
         v8.48c 的 sticky+恒定高度解耦方案已撤销，高度随左栏变化的特性随之回来。 */
    align-items: stretch;
  }
  .workbench.with-detail .detail-col {
    /* v8.47：flex 列 —— stretch 把 detail-col 拉到与左栏同高后，可见卡片
      （.detail-card / .detail-empty）默认仍是内容高，底边悬空（用户截图实测）。
       改 flex 列 + 子项 flex:1 0 auto，让可见卡片填满列高；内容超高时自然生长、
       由 overflow-y 兜底滚动（flex-basis:auto 不压缩内容）。 */
    display: flex; flex-direction: column;
    overflow-y: auto;
    scrollbar-width: thin;
    padding-right: 2px;
  }
  /* v8.47c（用户反馈）：只有选中币种的大卡（--grow）拉伸对齐左栏底边；
     空态的提示框与「查询任意币种」卡被一起拉伸出大片空白，恢复内容高不参与拉伸。 */
  .workbench.with-detail .detail-col > .detail-card--grow { flex: 1 0 auto; }
  /* v8.47f（用户反馈）：拉伸后的多余高度**均匀注入周期表行距**，而不是堆在卡片底部。
     做法：内嵌周期表子卡改 flex 列并 flex:1 吃掉全部剩余空间，行（grid 容器）
     flex:1 + align-items:center 逐行均分 —— 视觉语义是「表格更舒展」，
     而不是「底部缺了一块内容」。行距保底 8px 不变（flex:1 0 auto 不压缩）。 */
  .workbench.with-detail .detail-col > .detail-card--grow { display: flex; flex-direction: column; }
  .workbench.with-detail .detail-col > .detail-card--grow > .detail-table-card {
    flex: 1 0 auto; display: flex; flex-direction: column;
  }
  .workbench.with-detail .detail-col > .detail-table-card > .coin-period-row,
  .workbench.with-detail .detail-col > .detail-card--grow > .detail-table-card > .coin-period-row {
    flex: 1 0 auto; align-items: center;
  }
}

@media (max-width: 600px) {
  /* ⚠ v8.27/v8.31：移动端导航沉到视口底部（position:fixed）。
     它的祖先链里不能出现 backdrop-filter / filter / transform，
     否则会变成 fixed 的包含块、底栏的 bottom 会算到卡片底边而不是视口底。
     v8.27 导航曾在 .top-bar 内（有 backdrop-filter）；v8.31 起两者是平级兄弟，
     该隐患已消除 —— 保留这条注释是为了防止将来有人把导航再塞回带模糊的卡片里。
     同时解除状态卡吸顶：移动端导航已在底部，顶部状态卡无需常驻。 */
  .top-bar { position: static; padding: 8px var(--sp-3); gap: var(--sp-3); }
  /* 移动端层 1 只留「胶囊 + 环 + 导出」：更新时间移入详情浮层（点胶囊可见） */
  .tb-stamp { display: none; }
  .status-pop { width: min(340px, calc(100vw - 24px)); left: -4px; }
  .status-toast { top: 12px; font-size: var(--fs-sm); }
  /* 导航沉到屏幕底部：拇指可达，5 项仍可横向滑动 */
  .tab-bar.top-nav {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    z-index: 40; border-radius: 0; margin: 0;
    border-left: none; border-right: none; border-bottom: none;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0px));
    /* 桌面那条「收紧到内容宽 + 居中」在底栏必须撤销：
       底栏要 left/right 贴边、横向滚动，居中会把首个标签顶出滚动区。
       v8.47g（用户反馈 440px 溢出）：未溢出时 space-evenly 均匀铺开；
       真溢出时 space-evenly 自动退化为顶格排列 + 横向滚动兜底。 */
    width: auto; max-width: none;
    justify-content: space-evenly;
  }
  body { padding-bottom: 92px; }
  /* v8.27：右下悬浮组让开底部固定导航（92px 留白 + 12px 呼吸） */
  .fab-stack { bottom: calc(104px + env(safe-area-inset-bottom, 0px)); }
  /* v8.10e：分组标签与竖分隔线在底栏不显示（v8.11 起 DOM 已整体移除，规则保留以防回退）。 */
  .tab-bar.top-nav .tab-group-label,
  .tab-bar.top-nav .tab-group-sep { display: none; }
  /* 触控目标补到 44px（原 7px 纵向内边距算下来只有 ~31px，低于触控标准） */
  .tab-bar.top-nav .tab {
    min-height: 44px; display: flex; align-items: center; justify-content: center;
    /* v8.47g（用户反馈 440px 底栏溢出 30px）：内边距 16→8、字号 12.5→12。
       实测 5 标签总宽 454 → ~362px：440/414/390 视口完整放下；
       ≤389px 窄机再加一档 5px 内边距（下方嵌套媒体查询）保 375 完整；
       320px 古董机型仍走横向滑动兜底（flex:none 保证标签不被压瘪）。 */
    padding: 0 8px; font-size: 12px;
    /* v8.34/35：撤销桌面那条「等分铺满」——底栏是横向滚动条，一屏只露 ~3.5 格，
       等分在滚动语境里没有意义，且等分后每格约 74px 装不下「资金流 · CVD」+ 内边距。
       移动端保持内容宽（flex:none 阻止被压缩），超宽部分靠底栏横滚。 */
    flex: none;
    width: auto;
  }
  @media (max-width: 389px) {
    .tab-bar.top-nav .tab { padding: 0 5px; }
  }
}

/* ===== 右栏：标的详情（零新增请求，全部来自 DATA）===== */
.detail-empty {
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px;
  padding: var(--sp-4); color: var(--text-3); font-size: 12px; line-height: 1.85;
}
.detail-card {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; padding: var(--sp-4);
}
.detail-sym { font-size: 16px; font-weight: 600; color: var(--text); letter-spacing: .3px; }
.detail-sub { font-size: 11.5px; color: var(--text-3); margin-top: 3px; }
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); margin: var(--sp-4) 0; }
.detail-k { font-size: 11.5px; color: var(--text-3); }
.detail-v { font-size: 14px; font-weight: 600; font-family: var(--mono); margin-top: 2px; color: var(--text); }
.detail-v.up { color: var(--up); }
.detail-v.down { color: var(--down); }
/* v8.9：右栏内嵌周期表 —— 640 面板去内边距后 ~570px 可用（实测 getBoundingClientRect）。
   全局 .coin-period-row 的 min-width:620 只在 ≤600px 移动端媒体查询内，桌面本可收缩。
   ⚠ v-al9.7（用户截图实证排版错乱）：**列轨道数必须与 periodTableHTML 输出的 9 格严格一致**。
     8 轨装 9 格时，第 9 格（价格变化率）会被浏览器自动换到「第二行第 1 列」——
     表头读成「周期 / 价格变化率」两行、每行数据也多出一行（3m / +0.40% 叠列），
     且行高从 ~29px 涨到 56px。
   ⚠ v-al9.8（P1-8）：轨道已收敛为 `--pt-cols-panel`，与卡片轨道**并列定义在同一 :root 块**
     （该块紧邻 `.coin-period-row`，搜 `--pt-cols-` 即可）。改条数时两值并列可见，
     不会再出现「只改了一处」；改完跑 `node scripts/verify-period-cols.mjs` 验证。 */
.detail-col .coin-period-row {
  font-size: 11px;
  grid-template-columns: var(--pt-cols-panel);
  column-gap: 8px;
  /* v8.13：行距 5px → 8px —— 右栏内容偏短时，中栏还很长，底部会留出大片空白；
     周期表是右栏最长的区块，放宽行距既提升可读性，也把面板高度撑起来。 */
  padding: 8px 0;
}
.detail-col .coin-events-label { margin: var(--sp-3) 0 var(--sp-2); }
.detail-col .coin-period-row.head { font-size: 9.5px; white-space: nowrap; }
.detail-col .coin-periods { margin-bottom: 0; }
.detail-btn {
  display: block; width: 100%; text-align: center; padding: 10px;
  font-size: 12.5px; font-weight: 600; cursor: pointer; font-family: inherit;
  background: var(--bg-elevated); color: var(--text);
  border: 1px solid var(--border-hover); border-radius: 9px;
  transition: color .18s, border-color .18s;
}
.detail-btn:hover { border-color: var(--accent); color: var(--accent); }
.detail-tip { font-size: 11px; color: var(--text-3); line-height: 1.75; margin-top: var(--sp-3); }

/* 选中态：左侧强调色条。
   ⚠ 与「点击复制」的命中区刻意分离 —— 点符号仍复制（COPY_SELECTOR 优先），
     只有点卡片/行的空白处才触发选中。 */
.coin-card.is-selected { border-color: var(--accent); box-shadow: inset 3px 0 0 0 var(--accent); }
.alert-row.is-selected { box-shadow: inset 3px 0 0 0 var(--accent); }
.alert-row[data-sym], .coin-card[data-sym] { cursor: pointer; }

/* ===== 覆盖层（完整周期表按需展开）===== */
.overlay {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(4, 7, 14, .74);
  display: flex; align-items: flex-start; justify-content: center;
  padding: var(--sp-5) var(--sp-4); overflow-y: auto;
}
.overlay[hidden] { display: none; }
.overlay-panel {
  /* v8.32：720 → 960。用户反馈 COIN360 弹窗里的统计卡「老是换行」。
     实测根因是宽度不够：720 时面板内容 688、4 列后每卡仅 163px，
     扣掉卡片左右各 24px 内边距 → .cc-value 只剩 113px 可写。
     而值字号是 24px（--fs-num-xl），"+$137.2K" 这类字符串要 120~150px，
     于是 word-break:break-all 把数值从中间劈成两行（"+$137.2 / K"、"2.63 / %"）。
     独立量测（真实值 GPRO / 更长值两档）：
       720 → 卡 163 / 内容 113 → 换行 4/12（长值 6/12）
       860 → 卡 198 / 内容 150 → 换行 0/12
       960 → 卡 223 / 内容 175 → 换行 0/12（余量 +25px/卡）
     取 960 而非贴着阈值 860，留出「数值位数再长一位」的余量。
     ⚠ 视口 >1100px 时面板恒为 960（`width:100%` 只在更窄屏才起作用），
       4 列每卡恒 223px；≤1100px 媒体查询会降到 3 列、≤700px 降到 2 列、
       ≤600px 单列 —— 各档都比 720 宽，只增不减。 */
  width: 100%; max-width: 960px; margin: auto;
  background: var(--bg); border: 1px solid var(--border-hover); border-radius: 14px;
}
.overlay-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border);
}
.overlay-title { font-size: 13px; font-weight: 600; color: var(--text); }
.overlay-body { padding: var(--sp-4); }
/* 覆盖层打开时锁背景滚动，避免滚动穿透 */
body.overlay-open { overflow: hidden; }

/* ============================================================
   v8.2 视图设计规范：页头 / 概览条 / 数据表 / 空态
   —— 10 个视图共用同一套组件，改这里即覆盖全部页面。
      对应《视图设计手稿》第二、三节的组件规范。
   ============================================================ */

/* ① 视图页头（三段式）：标题 + 口径副标题 + 右侧元信息。
      现状只有标题一段，10 页里 7 页没有口径说明 —— 本次最该补的一处。 */
.section-title { align-items: flex-end; }
.section-title > div { min-width: 0; }
.view-sub {
  font-size: 11.5px; color: var(--text-3); line-height: 1.7;
  margin-top: 3px; max-width: 66ch;
}
.view-meta { font-size: 11.5px; color: var(--text-3); white-space: nowrap; }

/* ② 概览条：2–4 个关键数字，沿用 .summary-card 的「标签在上 / 数值在下」指标网格语言。
      ⚠ 高矮与字号刻意小于聚合看板的三张主卡 —— 概览条是"扫一眼"，不该与主卡抢注意力。 */
.view-kpi {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--sp-3); margin-bottom: var(--sp-4);
}
.view-kpi > div {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; padding: var(--sp-3) var(--sp-4);
}
.view-kpi .kpi-k { font-size: 11.5px; color: var(--text-3); }
.view-kpi .kpi-v {
  font-size: 17px; font-weight: 600; font-family: var(--mono);
  margin-top: 3px; color: var(--text); font-variant-numeric: tabular-nums;
}
.view-kpi .kpi-v.up { color: var(--up); }
.view-kpi .kpi-v.down { color: var(--down); }
.view-kpi .kpi-v.neutral { color: var(--neutral); }
.view-kpi .kpi-v.dim { color: var(--text-3); }

/* ③ 数据表规范：斑马纹 + 悬停行 + 表头强化。
      ⚠ 粘性表头不在此实现：页面滚动下要真吸顶，卡片必须变成纵向滚动容器
        （overflow-x:auto + overflow-y 会被计算为 auto），那会改变既有滚动交互，
        属于交互决策而非视觉修正，需单独确认后再做。 */
.coin-table-card .coin-row.header { position: sticky; top: 0; z-index: 2; letter-spacing: .3px; }
/* v8.44（任务 ③）：宽屏（≥900px，overflow-x:clip 已解除 sticky 困住）表头吸附视口时
   贴吸顶堆叠下沿（--stack-h 125px）。
   ⚠ 必须放在上一行 top:0 基准之后 —— 同特异性、后者胜；放前面会被基准覆盖回 top:0。 */
@media (min-width: 900px) {
  .coin-table-card .coin-row.header { top: var(--stack-h); }
}
.coin-row { transition: background .14s; }
.coin-row:not(.header):nth-child(even) { background: rgba(255,255,255,.016); }
.coin-row:not(.header):hover { background: var(--bg-elevated); }

/* ④ 空态：统一形态。现状各视图空态文案与容器各不相同。 */
.view-empty {
  background: var(--bg-card); border: 1px dashed var(--border-hover);
  border-radius: 12px; padding: var(--sp-5); text-align: center;
  color: var(--text-3); font-size: 12px; line-height: 1.85;
}
.view-empty b { display: block; color: var(--text-2); font-size: 13px; font-weight: 500; margin-bottom: 4px; }

/* ============================================================
   v8.3 中栏改「异动流」轻卡片 —— 严格按《视图设计手稿》三栏形态 A：
      每张卡只给三个关键指标（OI 变化率 / 量能倍数 / 价格变化率），
      完整的 10 行周期表**不再挤在中栏**，改由右栏「展开完整周期表」按需查看。
      这样中栏只回答一个问题：现在哪个标的在动。
   ============================================================ */
/* 单列堆叠。手稿即为单列：宽屏双列会把三个指标压进 ~430px，扫读反而变差。 */
.coin-cards { grid-template-columns: minmax(0, 1fr); }
/* v8.11：导航腾出的 180px 让中栏达到 ~796px —— Top10 卡片回到双列，
   一屏 5 张 → 10 张（≥1440px 才够每张 ~360px，窄了会挤压三指标）。 */
@media (min-width: 1440px) {
  .coin-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* v8.19（用户要求）：强度榜卡片加高，使其**底边与右栏详情面板底边对齐**。
     实测（1500px 宽）：卡片固定 105px，而右栏面板高度随视口变化 ——
       视口 900 → 面板 836（左栏反超 28px）；950 → 886（差 22）；
       ≥1080 → 941（内容封顶，差 78）。
     固定加高只会在矮屏上「过冲」，所以高度必须跟着视口走：
       面板高 = min(内容高 941px, 100vh − 64px)   ← max-height 封顶
       网格顶边距视口顶恒为 482px（筛选栏 + 3 张汇总卡 + 小节标题，高度稳定）
       右栏面板顶边距视口顶 191px
       → 单卡高 = (面板高 − (482 − 191) − 4×12px 间隙) ÷ 5 行
                = (min(941px, 100vh − 64px) − 339px) / 5
     用 min-height 而非 height：风险最小 —— Top10 行数或卡片内容变化时卡片自然生长，
     不会被裁切；视口过矮（算式结果 < 内容高 105px）时自动退化为「不生效」，与改前一致。
     ⚠ 只在 ≥1440px 生效：只有双列布局才是 5 行，单列是 10 行，该公式不适用。
     ⚠ 若日后改动面板内容（941）或网格上方区块（482 / 191），这两个常数需重新实测。 */
  #coinCards .flow-card { min-height: calc((min(941px, 100vh - 64px) - 339px) / 5); }
}

.flow-card {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; padding: var(--sp-4);
  /* v8.19：卡片被 min-height 撑高后，把内容**纵向居中** ——
     否则多出的高度全堆在底部，看起来像"卡片没铺满"，而不是"卡片更舒展"。 */
  display: flex; flex-direction: column; justify-content: center;
  transition: border-color .15s;
}
.flow-card:hover { border-color: var(--border-hover); }
/* v8.10d：可点容器统一给手型光标 —— 去掉 title 提示泡后，可点性靠光标 + hover 表达。
   （符号 .csym 自带 cursor:pointer 用于「点击复制」，两者语义不冲突。） */
.flow-card, .coin-card, .alert-row, .coin-row { cursor: pointer; }
.flow-card.is-selected { border-color: var(--accent); box-shadow: inset 3px 0 0 0 var(--accent); }
.flow-head {
  display: flex; justify-content: space-between; align-items: baseline;
  flex-wrap: wrap;   /* v8.43：溢出时整个元素换行，而非文字内部劈开 */
  gap: var(--sp-3); margin-bottom: var(--sp-4);
}
.flow-sym { font-size: 15px; font-weight: 600; color: var(--text); letter-spacing: .3px; white-space: nowrap; }
.flow-score { font-size: 11.5px; font-weight: 400; color: var(--text-3); margin-left: 7px; white-space: nowrap; }
/* v8.41：评分理由标签 —— 弱于评分数字两级，accent 微底提示"这是解释不是数据"。 */
.flow-reason {
  font-size: 10.5px; font-weight: 500; color: var(--accent);
  background: var(--accent-dim); border-radius: 4px;
  padding: 1px 6px; margin-left: 6px; white-space: nowrap;
}
/* v8.42：在榜持续性标签 —— purple 区分理由（accent）/评分（灰），语义="榜位时间维度"。 */
.flow-streak {
  font-size: 10.5px; font-weight: 500; color: var(--purple);
  background: var(--purple-dim); border-radius: 4px;
  padding: 1px 6px; margin-left: auto; white-space: nowrap;
}
.flow-tag { font-size: 11.5px; color: var(--text-3); white-space: nowrap; }
.flow-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3); }
.fm-k { font-size: 11.5px; color: var(--text-3); }
.fm-v {
  font-size: 15px; font-weight: 600; font-family: var(--mono);
  margin-top: 2px; color: var(--text); font-variant-numeric: tabular-nums;
}
.fm-v.up { color: var(--up); }
.fm-v.down { color: var(--down); }

/* v8.40：卡片「摘要行」—— 规模/资金/方向三锚点压缩为一行小字。
   判断层（三指标）之上补事实锚点：增量金额（钱的流向）、持仓价值（盘子多大）、
   方向文字（增持/减持，红绿只暗示方向、文字免去脑内合成）。
   字号 11px / --text-3：刻意比指标行弱两级 —— 锚点信息，不应抢主数值的视觉。 */
.flow-foot {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  margin-top: var(--sp-3); padding-top: var(--sp-2);
  border-top: 1px dashed var(--border);
  font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums;
}
.flow-foot .ff-dir { font-weight: 600; }
.flow-foot .ff-dir.up { color: var(--up); }
.flow-foot .ff-dir.down { color: var(--down); }
.flow-foot .ff-sep { opacity: .45; }
/* ===== v-sg1.0（T02/D1+D2）：flow-card 行级标注角标 =====
   拥挤=橙（--warn 同族 41.8% 回测警示色系）、追高=灰（中性）——配色纪律：禁涨跌红绿。
   点形小标记（8px）：flow-foot 追加、不新增行、不挤占主数值；
   判定依据收在 tooltip（SIGNAL_NOTES.crowded/chase 模板），零命中零元素。 */
.flow-risk {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  margin-left: 6px;
  vertical-align: middle;
  flex: 0 0 auto;
}
.flow-risk.crowded { background: #ffaa00; box-shadow: 0 0 0 2px rgba(255,170,0,.18); }
.flow-risk.chase { background: #9aa3ad; box-shadow: 0 0 0 2px rgba(154,163,173,.16); }
.fm-v.dim { color: var(--text-3); }
/* v8.48d：信号档 chip 已随「回到 v8.48b」再次移除（v8.48c 曾恢复，用户最终选择 v8.48b 状态）。 */
/* 量能色阶：原规则是 .coin-card 作用域（`.coin-card .vol-hot`），轻卡片需要一份同值定义。
   刻意用暖橙/冷蓝，与涨跌红绿分开，避免"放量"被当成"上涨"。 */
.fm-v.vol-extreme { color: var(--vol-extreme); }
.fm-v.vol-hot { color: var(--vol-hot); }
.fm-v.vol-warm { color: var(--vol-warm); }
.fm-v.vol-cool { color: var(--vol-cool); }
.fm-v.vol-cold { color: var(--vol-cold); }

/* v3.21：订阅按钮反馈高亮（收在按钮自身，不插元素、位置零变动） */
/* v3.62c：flash 颜色随状态（订阅态与 hd-watch-on 金色叠加；取消态为中性灰）—— 原固定蓝色与金色订阅态不一致 */
.mini-btn.hd-flash { border-color: var(--border-hover); color: var(--text); }
#hdWatch { min-width: 100px; text-align: center; }

/* v3.29：OI 快照少量缺失的低调提示（≤3% 用它；高占比仍走 error-banner 大警示） */
.soft-banner {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-elevated);
  color: var(--text-3);
  font-size: 12.5px;
  padding: 10px 14px;
  line-height: 1.6;
}

/* v3.44：档位质量徽章（回测校准 v1）—— 强（绿）/待验（黄）/无效（灰）/反向（红） */
.flow-q { font-size: 9.5px; font-weight: 700; padding: 1px 6px; border-radius: 10px; margin-left: 6px; white-space: nowrap; }
.q-strong { background: rgba(46,160,67,.22); color: #7ee787; border: 1px solid #2ea043; }
.q-mid    { background: rgba(210,153,34,.22); color: #e3b341; border: 1px solid #d29922; }
.q-weak   { background: rgba(139,148,158,.18); color: #8b949e; border: 1px solid #30363d; }
.q-rev    { background: rgba(248,81,73,.22); color: #ffa198; border: 1px solid #f85149; }

/* v3.63：机会等级徽章样式已随功能移除 */

/* v3.62b：热力图下钻卡订阅按钮——订阅态金色（★ 取消订阅），与全景表星标同色系 */
.hd-watch-on { background: rgba(227,179,65,.18) !important; color: #e3b341 !important; border-color: #e3b341 !important; }
