/* ============================================================================
   web/static/css/v2.css —— ★ 新站增量样式（第 1 批 · 全站重建）

   ## 定位
     新站（`web/templates/v2/`）**专用**。与旧 `app.css` **物理隔离**。

   ## 加载顺序（见 `v2/base.html`）
     app.css      ← `.topbar` / `.nav-drop` 骨架（**共用，不改**）
     public.css   ← `--pub-*` 尺寸令牌 + `.pub-*` 组件（**共用，不改**）
     v2.css       ← 本文件：只放**新站增量**，可覆盖上面两者

   ## 铁律（违反 = 做错事）
     ① ⛔ **不修改** app.css / public.css（旧页依赖它们，动了 = 改旧页）
     ② ⛔ 不复制 app.css 的旧类名（旧类一律不复用 —— F-判-2 = 乙）
     ③ ✅ 新站尺寸令牌：顶栏高用 `--v2-topbar-h`（由 v2/_scripts.html 实测写入）
        ⚠️ 新旧变量名必须分开，否则互相干扰（旧页用 `--topbar-h`）
   ============================================================================ */

/* ---------------------------------------------------------------------------
   ① 新站令牌：只补充"新站独有"的变量
      ⚠️ 尺寸真源仍是 public.css 的 `--pub-*`（不复制、不重定义）
   --------------------------------------------------------------------------- */
:root {
  /* 顶栏实测高度（v2/_scripts.html 写入；兜底 = 公开层基准 60.8px）
     ★ 名字带 v2 前缀 ⇒ 与旧页的 `--topbar-h`（38px）互不干扰 */
  --v2-topbar-h: var(--pub-topbar-h, 60.8px);
  /* VCP 页固定头高度（新站专用；旧页叫 `--vcp-head-h`） */
  --v2-vcp-head-h: 0px;
}

/* ---------------------------------------------------------------------------
   ② sticky 吸顶偏移（新站自己的规则，不覆盖旧页的）
   --------------------------------------------------------------------------- */
.v2-sticky {
  position: sticky;
  top: var(--v2-topbar-h, 0px);
  z-index: 90;
}

/* ---------------------------------------------------------------------------
   ③ 组件库（对应 v2/_ui.html 的 7 个宏）
      规格来源：plan §四·六（实测自 stageanalysis.net）
      字体尺度：14px 基准 / 16px @≥1200px（§4.6.2）
   --------------------------------------------------------------------------- */

/* --- 3.1 卡片：纯文字单列（F-判-8 = 乙，无缩略图） --- */
.v2-card {
  margin-top: 2em;
  padding-top: 2em;
  border-top: 1px solid #eee;
}
.v2-card:first-of-type {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;          /* ★ 首项无上边框（plan §4.6.1） */
}
.v2-card-date {
  font-size: 0.8125em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #8a8a90;
}
.v2-card-title {
  font-size: 1.375em;
  line-height: 1.2;
  margin: 0.325em 0;
}
.v2-card-title > a {
  color: inherit;          /* ★ 标题继承色，不是蓝色（§4.6.1） */
  text-decoration: none;
}
.v2-card-title > a:hover {
  text-decoration: underline;
}
.v2-card-summary {
  font-weight: 300;        /* ★ 细体 300（§4.6.1） */
  line-height: 1.5;
  margin-bottom: 1em;
}
.v2-card-more {
  color: #0076ff;
  text-decoration: underline;   /* ★ 带下划线的蓝链接（§4.6.1） */
}

/* --- 3.2 表格（§4.6.1.2 · F-判-1 = 甲 自定） --- */
.v2-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875em;
}
.v2-table th,
.v2-table td {
  padding: 0.75rem;
  text-align: left;
  border-bottom: 1px solid #eee;
}
/*: ★★ 表头吸顶（2026-09-30 批 7-b 修正 —— **VPS 实测事故：表头错位 61px**）
    ## 事故
      会员区 RPS 表：Tab 条下面空 61px，且**表头跑到第一行数据下面**
      （实测 `theadTh.top=294 > firstTr.top=281`），多出一条假横线。
    ## 根因
      `position: sticky; top: var(--v2-topbar-h)` = **61px**（顶栏高）。
      · **不在滚动容器里**时：sticky 相对**视口/最近滚动祖先** ⇒ 61px 正确
        （吸在顶栏之下，不被顶栏盖住）。
      · **在 `.v2-table-scroll`（限高滚动容器）里**时：sticky 相对**容器**
        ⇒ `top: 61px` 就变成"离容器顶 61px" ⇒ **表头被往下推 61px**。
    ## 修法：两种场景分开写
      · 默认（不在 v2-table-scroll 内）⇒ 保留 `top: var(--v2-topbar-h)`（不变）
      · 在 `.v2-table-scroll` 内 ⇒ `top: 0`（贴容器顶吸顶）
      ⚠️ 用后代选择器提权重（0,2,1 > 0,1,1）⇒ 压得住上面那条默认规则。
   -------------------------------------------------------------------- */
.v2-table thead th {
  position: sticky;
  top: var(--v2-topbar-h, 0px);   /* ★ 默认：吸在**页面顶栏**之下 */
  z-index: 5;
  background: #eee;
  font-weight: 600;
  white-space: nowrap;
}
/*: 在限高滚动容器内 ⇒ 贴**容器顶**吸顶（见上方事故说明） */
.v2-table-scroll .v2-table thead th {
  top: 0;
}
.v2-table tbody tr:nth-child(even) {
  background: #f9f9f9;            /* ★ 斑马纹 */
}
.v2-table th.num,
.v2-table td.num {
  text-align: right;              /* ★ 数字列右对齐 */
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.v2-empty {
  text-align: center;
  color: #8a8a90;
  padding: 2em 0.75rem;
}

/* --- 3.3 按钮（全站只有 2 种） --- */
.v2-btn {
  display: inline-block;
  padding: 0.5em 1.25em;
  border-radius: 4px;
  font-size: 0.9375em;
  line-height: 1.4;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
}
.v2-btn--solid {
  background: #0076ff;
  color: #fff;
}
.v2-btn--solid:hover { background: #0062D6; }
.v2-btn--outline {
  background: #fff;
  color: #0076ff;
  border-color: #0076ff;
}
.v2-btn--outline:hover { background: #f0f7ff; }
.v2-btn--sm { padding: 0.25em 0.75em; font-size: 0.8125em; }

/* --- 3.4 输入框 --- */
.v2-input {
  height: 34px;
  padding: 0 0.75em;
  border: 1px solid #d8d8dc;
  border-radius: 4px;
  font-size: 0.9375em;
}

/* --- 3.5 分页器 --- */
.v2-pager {
  margin-top: 3em;
  display: flex;
  align-items: center;
  gap: 1em;
}
.v2-pager a {
  color: #0076ff;
  text-decoration: underline;
}
.v2-pager-cur { color: #8a8a90; font-size: 0.875em; }

/* --- 3.5b 分页器（★ 页码数字版 · 右下角）-------------------------------
   用户裁定 2026-09-27：新站 RPS 页照参照站截图那种分页条
     `Previous | 1 | 2 | 3 | Next`，**右下角**对齐。
   规格来源：截图实测 ⇒ 无边框小方块 · 当前页实底 · 两侧文字链接。
   ⚠️ 类名 `.v2-pgnum*` 与上面的 `.v2-pager*` 分开（互不干扰）；
      `.v2-pager-wrap` 只负责**靠右**，分页条本身仍由 `.v2-pager-num` 布局。
   -------------------------------------------------------------------- */
.v2-pager-wrap {
  margin-top: 2em;
  display: flex;
  justify-content: flex-end;      /* ★ 右下角（用户裁定） */
}
.v2-pager-num {
  display: flex;
  align-items: center;
  gap: 0.25em;
  font-size: 0.875em;
}
.v2-pager-num a,
.v2-pager-num .v2-pgnum-side {
  padding: 0.35em 0.7em;
  border-radius: 3px;
  color: #0076ff;
  text-decoration: none;          /* 分页条不加下划线（截图无） */
  cursor: pointer;
  white-space: nowrap;
}
.v2-pager-num a:hover { background: #f2f7ff; }
/* 页码数字：等宽小方块 */
.v2-pgnum {
  min-width: 2em;
  text-align: center;
}
/* 当前页：实底高亮、不可点 */
.v2-pgnum-cur {
  background: #0076ff;
  color: #fff;
  font-weight: 600;
  cursor: default;
}
.v2-pgnum-cur:hover { background: #0076ff; }   /* 当前页不因 hover 变浅 */
/* `Previous` / `Next`：首页/末页时灰化不可点（**仍在原位**，不跳位） */
.v2-pgnum-side { color: #0076ff; }
.v2-pgnum-off { color: #c2c2c8; cursor: default; }
.v2-pgnum-off:hover { background: transparent; }
.v2-pgnum-gap { color: #b0b0b6; padding: 0 0.2em; }

/* --- 3.5e 新站页面上留白（★ 用户裁定 2026-09-27：RPS 页取一半）----------
   ## 现状
     `public.css:140` `.pub-content { padding-top: var(--pub-content-pt) }`
       · 桌面 = **40px**（`public.css:91`）
       · 手机档 = `--pub-topbar-h-sm`（`public.css:441`）
   ## 用户裁定
     `Relative Strength` 离顶栏太远 ⇒ **这块留白取一半**（40px → 20px）。
   ## ★ 再收一次（同日第 5 版）
     用户看真站后再要求：H1「再往上挪点」⇒ 20px → **12px**。
     （与正文拉开差距由 `.v2-page-h1` 的 margin-bottom 负责，不是靠上留白。）
   ⚠️ **只作用于新站**：靠 `.v2-content--tight` 这个新站专属类，
      挂在 `v2/base.html` 的 `<main class="pub-content">` 上。
      铁律①（不改 public.css / app.css）⇒ 这里用**覆盖**而不是改源文件。
   ⚠️ 为什么不用 `padding-top: calc(var(--pub-content-pt) / 2)`：
      手机档 `--pub-content-pt` = `--pub-topbar-h-sm`（一个"顶开被 sticky 顶栏
      遮住"的占位值），半减它会让内容**钻到顶栏下面**。
      ⇒ 桌面直接给 12px；手机档单独给一个安全值（见媒体查询）。
   -------------------------------------------------------------------- */
.v2-content--tight { padding-top: 12px; }
@media (max-width: 700px) {
  /* 手机档：原值 = 顶栏高（占位，不能半减）⇒ 收一半但留够 ≥ 顶栏 */
  .v2-content--tight { padding-top: calc(var(--pub-topbar-h-sm) / 2); }
}

/* --- 3.5c 表格内的锁定格（RPS250 每行的 🔒 链接）------------------------
   ## ★ 为什么这里的选择器这么长（2026-09-27 真事故：锁整列渲染成蓝色）
     第一版只写 `.v2-cell-lock { color: #b0b0b6 }`（权重 0,1,0）**压不住**
     `public.css:143` 的 `.pub-body a { color: var(--pub-accent) }`（权重 0,2,1）
     ⇒ 浏览器取**链接蓝**，锁整列是蓝的（用户实测反馈）。
   ⇒ 修法 = **与 `.pub-body a` 同权重对齐后再加一层类**（`.pub-body .pub-table
     .v2-cell-lock` = 0,3,0 > 0,2,1），**不用 `!important`**。
     ⚠️ 权重算术：0,2,1 的"1"是 `.pub-body` 里的元素选择器 `a` 带来的；
        所以必须让新选择器也**以元素结尾**或在类数上超过它 ⇒ 这里 3 个类
        + `a`（`.pub-body a.v2-cell-lock` 亦等价），稳胜。
   ⚠️ 另一条被压的：`public.css:424` 的 `.pub-table td { text-align: left }`
      ⇒ 锁贴在单元格左边缘。居中另见 §3.5d。

   ## 颜色/字符约定
     · 字符 = `&#128274;&#65038;`（U+1F512 + U+FE0E，强制单色）
       —— 不加 FE0E 会被 Chrome 渲染成橙色彩色 emoji，`color` 失效；
     · 表格是**白底** ⇒ 用灰化色 `#b0b0b6`（纯白锁在白底上看不见）；
     · **任何时候都是灰**（用户裁定 2026-09-27）：**不要**加 `:hover` 变色
       —— 第一版 hover 变蓝，用户看到整列蓝，误判成"没生效"，来回一轮。
     · 点击照常跳订阅页（`<a href>`），只是不再有颜色反馈。
   -------------------------------------------------------------------- */
.pub-body .pub-table .v2-cell-lock,
.pub-body .pub-table a.v2-cell-lock {
  color: #b0b0b6;
  text-decoration: none;
  cursor: pointer;
  font-size: 1em;
}

/* --- 3.5d 锁定列：格子**水平居中** -------------------------------------
   为什么需要：`public.css:424` 给所有 `.pub-table td` 定了 `text-align: left`
   ⇒ 锁贴左。这里按**列定位**而不是给每个 td 加类，免得模板里重复。
   ⚠️ `th` 也要一起居中，否则表头与格子里的锁对不齐。
   ⚠️ 类名从 `.v2-rps-table` 改为通用 `.v2-lock-col`（用户裁定 2026-09-27 =
      甲：类名**去页面化**，任何页面的"最后 1 列是锁列"都能复用）。
   -------------------------------------------------------------------- */
.v2-lock-col th:last-child,
.v2-lock-col td:last-child {
  text-align: center;
}
/* ★ 2026-09-30：锁列**不再固定为最后一列**（用户裁定：锁从 RPS250 换到 RPS20）
   ⇒ `td:last-child` 不再适用 ⇒ 改用**专用类** `.v2-lock-cell`（挂在锁格上）
   ⚠️ 为什么要提权重：`public.css:424` 给 `.pub-table td` 定了
      `text-align: left`（0,1,1）⇒ 本规则（0,2,1，两前缀+元素）压得住
   ⚠️ 保留上面 `:last-child` 规则：其它页面/旧用法仍可能用"最后列是锁"

   ★ 2026-09-30 追加（用户裁定）：**RPS 四列全部居中**
     · `v2-rps-num` 挂 RPS50/120/250 的 `th` 与 `td`
     · `v2-lock-cell` 挂 RPS20 的 `th` 与 `td`（锁格）
     ⇒ 四列统一居中，**彻底不依赖 `:last-child`**（列序变也不失效）
   -------------------------------------------------------------------- */
.pub-table.v2-lock-col .v2-lock-cell,
.pub-table.v2-lock-col .v2-rps-num,
.v2-lock-col td.v2-lock-cell,
.v2-lock-col td.v2-rps-num,
.v2-lock-col th.v2-rps-num {
  text-align: center;
}

/* --- 3.6 非会员提示框（§4.6.7） --- */
.v2-gate {
  margin: 2em 0;
  padding: 1.25em 1.5em;
  border: 1px solid #e5e5e7;
  border-left: 3px solid #0076ff;
  border-radius: 4px;
  background: #fafafa;
}
.v2-gate-text { margin: 0 0 0.75em; }

/* --- 3.7 免责声明（F-判-5 = 甲） --- */
.v2-disclaimer {
  margin-top: 2.5em;
  font-size: 0.8125em;
  color: #8a8a90;
  line-height: 1.5;
}

/* --- 3.7b 新站页面导语（★ 用户裁定 2026-09-27：多段正文）----------------
   ## 行距 / 段距（用户明确要求"注意行距段距"）
     · 行高 1.7（长段英文的可读性区间）
     · **段间距 1em**（用 `p + p` 而不是给每段加 margin —— 这样**首段不带
       上边距**，紧贴 H1 的既有间距，不会顶出一块多余空白）
     · 前两段加粗：第 1 段是"是什么"的定义句（当 lead 用）；第 2 段按用户
       裁定与第 1 段样式一致。⚠️ 用 `:nth-child(-n+2)`，将来段落增减时
       不必改选择器。
   ⚠️ 不动 `public.css` 的 `.pub-lead`（公开层共用，铁律①）⇒ 本类
      **与 `pub-lead` 同时挂**（继承它的颜色）。
   ⚠️ 类名从 `.v2-rs-lede` 改为通用 `.v2-lede`（用户裁定 = 甲：去页面化，
      新站各页共用同一套行距/段距，避免各写一份而漂移）。
   -------------------------------------------------------------------- */
.v2-lede {
  line-height: 1.7;
  text-align: left;
}
.v2-lede p { margin: 0; }
.v2-lede p + p { margin-top: 1em; }        /* ★ 段距（首段不带） */
.v2-lede p:nth-child(-n+2) { font-weight: 600; }

/* --- 3.7b2 新站页面 H1 间距（★ 用户裁定 2026-09-27）---------------------
   用户要求：H1 **再往上挪**（离顶栏更近）**且**与下面的正文**拉开差距**。
   这两件事方向相反 ⇒ 必须分开调：
     · 上留白：由 `.v2-content--tight` 给（见 §3.5e）
     · 下边距：H1 自己的 margin-bottom（`public.css` 未定义 `.pub-page-title`
       的 margin ⇒ 吃浏览器默认 `1em` 左右）⇒ 这里显式给足。
   ⚠️ `.v2-lede` 的首段没有上边距（见下）⇒ 这段间距**全部**来自 H1 的
      margin-bottom，调一个地方即可，不会两处叠加。
   ⚠️ 类名从 `.v2-rs-h1` 改为通用 `.v2-page-h1`（用户裁定 = 甲：去页面化，
      新站每个页面共用同一套间距，避免各页各写一份、迟早漂移）。
   -------------------------------------------------------------------- */
.v2-page-h1 {
  margin-top: 0;
  margin-bottom: 1em;        /* ★ 与正文的间距（用户裁定 2026-09-27：1em） */
}

/* --- 3.7c 导语末段 / 免责声明（★ 用户裁定 2026-09-27）--------------------
   ★ 两条裁定：
     ① **段距与其它段完全一致**（"他跟第二段的距离等于第二段和第一段的
        距离"）⇒ 所以本段是 `.v2-lede` 的**子元素**，靠 `p + p` 的
        `margin-top: 1em` 自动对齐；这里**不再**写自己的 margin-top
        （曾写 1.6em ⇒ 视觉上"多一个回车"）。
     ② **字号与颜色回到正文**（此前是小号灰字 0.8125em / #8a8a90）。
   ⚠️ 为什么不直接改 `.v2-disclaimer`：那是组件库共用类（其它页的
      `ui.disclaimer()` 在用），改它 = 改别页 ⇒ 用**局部类**。
   ⚠️ 选择器多带 `.v2-lede` 前缀（0,2,0）是为**压过**同文件里更早定义的
      规则，也避免将来有人给 `p + p` 加样式时被反压。
   -------------------------------------------------------------------- */
.v2-lede p.v2-lede-note {
  font-size: 1em;            /* ★ 回正文（原 0.8125em） */
  color: inherit;            /* ★ 回正文色（原 #8a8a90 灰） */
  /* ★ 不加粗（用户裁定 2026-09-27）：导语前两段是 `font-weight: 600`，
     免责声明段要**正常字重**。
     ⚠️ 选择器 `p.v2-lede-note`（0,2,1）**权重高于** `p:nth-child(-n+2)`
        （0,2,0）⇒ 不依赖书写顺序，放在前面也稳。
     ⚠️ 为什么不用 `font-weight: normal`：`400` 与 `normal` 等价，写数字更明确
        （且将来若把 `600` 改成别的数字，对比一眼可见）。 */
  font-weight: 400;
  /* margin-top 由 `.v2-lede p + p` 统一给 1em ⇒ 这里刻意不写 */
}

/* --- 3.7d 末段里的下划线短语（用户裁定：这句带下划线）-------------------
   ⚠️ 用 `<u>` 元素（语义就是"下划线"），**不**改文字颜色
      —— 它不是链接，别让人误以为可点。
   -------------------------------------------------------------------- */
.v2-lede p.v2-lede-note u {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* --- 3.7e 文章区（观察名单页 · ★ 用户裁定 2026-09-27 第一步）-------------
   结构（甲：策略标题 + 介绍 = **页面级**，文章卡片是其下的条目）：
     .v2-art-section   整节容器
       .v2-art-strategy  策略名（H3）
       .v2-art-intro     策略介绍（复用 `.v2-lede`，故这里只管间距）
       .v2-art-list > .v2-art-item
         .v2-art-title     文章标题（<a>）
         .v2-art-summary   摘要行（小字）
         .v2-art-more      Read More（<a>）

   ⚠️ 链接颜色**不用自己写**：`public.css:143` 的 `.pub-body a` 已给品牌蓝
      ⇒ 这里只调**字号/间距/字重**，颜色继承（避免又一个"权重打不过"的坑）。
   ⚠️ 卡片条目之间用**分隔线**（截图风格：一条细线 + 大留白），不是方框卡片
      —— 与 `.pub-card`（有边框）区分开，故新起 `.v2-art-*` 类名。
   -------------------------------------------------------------------- */
.v2-art-section { margin-top: 2.5em; }
.v2-art-strategy {
  margin: 0 0 0.5em;
  font-size: 1.25rem;
}
.v2-art-intro { margin-bottom: 1.5em; }

.v2-art-list { margin: 0; padding: 0; list-style: none; }
.v2-art-item {
  padding: 1.5em 0;
  border-top: 1px solid #eee;      /* ★ 条目分隔线（首条也有 ⇒ 与介绍段分界） */
}
/* ★ 2026-10-02（用户裁定）：博客卡片**文字顶左边框** —— 补回左右内边距。
   ## 根因
     卡片同时挂了 `.pub-card`（public.css，**有边框** + `padding:20px`）
     和 `.v2-art-item`（本文件，**后加载** + `padding:1.5em 0`）
     ⇒ 后者把左右 padding 覆盖成 **0** ⇒ 文字贴到边框上。
   ## 只选「**两个类同时有**」（`.pub-card.v2-art-item`）
     ⇒ 不影响纯分隔线列表（`watchlist.html` 只用 `.v2-art-item`）
   ⚠️ 不改 `public.css`（**冻结文件**）。 */
.pub-card.v2-art-item { padding-left: 20px; padding-right: 20px; }
/* ★ 列表卡片的标题：**上间距归零**（用户裁定 2026-09-30）
   ## 为什么
     卡片原结构是「日期行 → 标题」；`.v2-post-h3` 的 `margin-top: 1.75em`
     是**为紧跟日期行**而设的。日期行删掉后，这个上间距就成了
     卡片顶部一块**冤枉的空白**（用户截图指出）。
   ## 值
     `1.75em × 1.05rem(=16.8px)` ≈ **29.4px** ⇒ 归零后标题整体上移约 29.4px
   ## ⚠️ 只作用于 `.v2-art-item` 内（作用域限定）
     `.v2-post-h3` **是共用类**（详情页「成为会员」标题、观察名单卡片也用）
     ⇒ **绝不能改全局** ⇒ 用 `.v2-art-item` 前缀覆盖
     ⚠️ 权重：`.v2-art-item .v2-post-h3`（0,2,0）> `.v2-post-h3`（0,1,0）✅ */
.v2-art-item .v2-post-h3 { margin-top: 0; }
.v2-art-title {
  display: block;
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.35;
  text-decoration: none;           /* 标题不像正文链接那样带下划线 */
}
.v2-art-summary {
  margin: 0.5em 0 0;
  color: #6b7280;                  /* = `.pub-muted` 的灰，保持全站一致 */
}
.v2-art-more {
  display: inline-block;
  margin-top: 0.75em;
  text-decoration: underline;      /* 截图里 Read More 是带下划线的链接 */
}

/* --- 3.7f 文章详情页（`/v2/watchlist/<n>`）-----------------------------
   ⚠️ 本步（试写）用到的：返回链接 / 小节标题 / 数据表 / 报告区。
   ⚠️ 小节标题层级：H1（页码标题）→ `.v2-post-h2` → `.v2-post-h3`，
      尺寸递减、间距递减，保证"分组表格"读起来有层次。
   ⚠️ 数据表用 `.pub-table` + 本类只补**数字列右对齐**与**首列不换行**：
      `public.css:424` 给所有 `.pub-table td` 定的 `text-align: left`
      ⇒ 数值右对齐要**提权重**（`.pub-table` 前缀）才压得住。
   -------------------------------------------------------------------- */
.v2-post-back { margin: 0 0 1em; }
.v2-post-body { margin-top: 1.5em; }

/*: ★★ 报告/文章正文的 Markdown 排版（2026-10-15 · A 方案）
   ## 背景（用户反馈「简报报告很烂，还没旧版好看」）
     报告正文 `| md | safe` 渲染出裸 `<h2>/<table>/<ul>…`，而本文件对
     `.v2-post-body` **只给过 `margin-top`** ⇒ 表格无边框、单元格 padding≈1px、
     段落样式还分两套 ⇒ 挤成一坨。
     旧版把同一段 md 挂 `.report-md`（`app.css:440-474` 有完整排版）⇒ 好看。
   ## 做法：给 `.v2-post-body` 补子元素样式，数值以旧 `.report-md` 为基线
     ⚠️⚠️ **选择器一律带 `.pub-shell` 前缀**（不能用裸 `.v2-post-body xxx`）：
        `v2/base.html:143` 把所有 v2 内容包在 `<div class="pub-shell">` 里，
        而本文件已有 `.pub-shell h2`(0,1,1) / `.pub-shell h2~p`(0,1,2)
        （`v2.css:935/942/949-951`）⇒ **会盖掉**同权重的朴素规则。
        加前缀后特异性 **(0,2,1)** ⇒ 稳压之（同 `v2.css:1182/1196/1212` 的先例）。
     ⚠️ 组合选择器**必须逐条写全前缀**（`h1,h2` 这种写法会让 `h2` 变全局裸选择器！）
     ✅ 只影响新站报告/博客正文（旧站无 `.pub-shell` ⇒ 零误伤）
   ## 与旧版 `.report-md` 的差异（4 处有意增强，见 docs/plan_report_body_typography.md §3.3）
     ① 表格单元格 `overflow-wrap:anywhere`（长中文/URL 在格内换行）
     ② 表头 `white-space:nowrap`（不折行）
     ③ 列表项 `li{margin:.15em}`（旧版没给，长列表贴太紧）
     ④ `p{max-width:none}`（压掉 `.pub-shell h2~p` 的 76ch，段距/行距整篇统一）
   -------------------------------------------------------------------- */
.pub-shell .v2-post-body {
  line-height: 1.7;
  overflow-wrap: break-word;   /* 正文长句/超长词断行 */
}
/* 标题：统一间距/行高（覆盖 `.pub-shell h1/h2/h3` 的默认值） */
.pub-shell .v2-post-body h1,
.pub-shell .v2-post-body h2,
.pub-shell .v2-post-body h3,
.pub-shell .v2-post-body h4 {
  margin: 1.1em 0 0.5em;
  line-height: 1.3;
}
.pub-shell .v2-post-body h2 {
  font-size: 1.15rem;
  border-bottom: 1px solid var(--line);   /* ★ 压 `.pub-shell h2`（v2.css:935） */
  padding-bottom: 4px;
}
.pub-shell .v2-post-body h3 { font-size: 1.02rem; }
/*: ★ 正文里的 `# `（h1）——**降级为文档内标题**（2026-10-15 · 批 2）
   ## 为什么要处理
     页标题已是 `<h1 class="pub-page-title">`（`report.html:36`，含代码+名称），
     而部分报告正文**以 `# xxx研究报告` 开头**（实测 4/175 篇）⇒ 渲染出
     **第二个 `<h1>`** ⇒ 一页两个"主标题"，且字号与页标题打架。
   ## 做法（**纯 CSS**，不改模板/渲染器）
     正文 h1 视作"文档/章节名"：比页 H1 小、无下边框、颜色降一档，
     让它**不与页标题争主标题**（视觉上并入 h2 层级，但保留 h1 语义）。
   ## ⚠️ 为什么不是 `:first-child` 单独处理
     正文里 `#` 可能不止一个（非首行也出现）⇒ 统一降级更稳，避免漏。
   -------------------------------------------------------------------- */
.pub-shell .v2-post-body h1 {
  font-size: 1.2rem;                    /* 仅略大于 h2(1.15rem)，明显小于页 H1 */
  border-bottom: 0;                     /* 去掉页 H1 才该有的重下划线 */
  color: #374151;                       /* 与 h3 同色一档（不再当主标题） */
  padding-bottom: 0;
}
/* 段落：段距统一 + `max-width:none` 压掉 `.pub-shell h2~p` 的 76ch
   （否则 h2 之后的段落会比其它段落窄、行高也不同 ⇒「行距分两套」） */
.pub-shell .v2-post-body p {
  margin: 0.5em 0;
  max-width: none;
}
/* 列表 */
.pub-shell .v2-post-body ul,
.pub-shell .v2-post-body ol {
  margin: 0.4em 0 0.6em;
  padding-left: 1.5em;
}
.pub-shell .v2-post-body li { margin: 0.15em 0; }
/* 表格：**每格有框 + 表头灰底 + 有内边距**（旧 `.report-md` 同款） */
.pub-shell .v2-post-body table {
  border-collapse: collapse;
  width: 100%;
  margin: 10px 0;
  font-size: 0.88rem;
}
.pub-shell .v2-post-body th,
.pub-shell .v2-post-body td {
  border: 1px solid var(--line);
  padding: 5px 8px;
  text-align: left;
  overflow-wrap: anywhere;     /* 格内长文本换行（不撑破列） */
}
.pub-shell .v2-post-body th {
  background: #eef1f5;
  white-space: nowrap;         /* 表头不折行 */
}
/* 引用 / 代码 / 分隔线 / 加粗 */
.pub-shell .v2-post-body blockquote {
  margin: 0.6em 0;
  padding: 6px 12px;
  border-left: 3px solid var(--accent);
  background: #f0f5fb;
  color: #333;
}
.pub-shell .v2-post-body pre {
  background: #10151c;
  color: #d6e2ec;
  border-radius: 6px;
  padding: 10px;
  overflow-x: auto;
}
.pub-shell .v2-post-body code {
  background: #eef1f5;
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.85em;
}
.pub-shell .v2-post-body pre code {
  background: none;
  padding: 0;
  color: inherit;
}
.pub-shell .v2-post-body strong { font-weight: 700; }
.pub-shell .v2-post-body hr {
  border: none;
  border-top: 1px solid var(--line);
  margin: 14px 0;
}

.v2-post-h2 {
  margin: 2em 0 0.75em;
  font-size: 1.35rem;
}
.v2-post-h3 {
  margin: 1.75em 0 0.5em;
  font-size: 1.05rem;
  color: #374151;
}
/* 首个小节标题紧跟在导语后 ⇒ 不要额外 2em 的上边距顶出空洞 */
.v2-post-lead { margin-bottom: 0; }
.v2-post-lead + .v2-post-h2 { margin-top: 1.5em; }

/* 数据表：**左对齐**（用户裁定 2026-09-27）
   ⚠️ 曾写成 `td:not(:first-child) { text-align: right }`（那是给**数字表**
      设计的）⇒ 表内容改成文字后右对齐很怪（用户截图反馈）⇒ 已移除该规则。
   `public.css:424` 的 `.pub-table td { text-align: left }` 就是我们要的
   ⇒ **不需要任何覆盖**，这里只保留"首列不换行"。
   ⚠️ 留着注释是为了防止将来有人再"顺手"加回右对齐。 */
.pub-table.v2-post-table td:first-child { white-space: nowrap; }

/* --- 3.7g Fundamental Fact Sheet 卡片（★ 用户裁定 2026-09-27）-----------
   设计（用户裁定 Q1 = 甲 / Q2 = 固定三段）：
     · 指标网格（6 个固定槽位：市值 / PE / Forward PE / ROE / 净利率 / 营收增速）
     · 业务结构（一行占比）
     · 三段固定要点：增长 / 业务催化剂 / 风险
   ⚠️ 为什么用"固定槽位"而不是整篇渲染：报告 9586 字、且将来会有 143 篇
      ⇒ 槽位固定才能自动化生成 + 逐字段翻译（用户要求"翻译回填"）。
   ⚠️ 视觉上用一个**带边框的整体卡片**（`.pub-card` 是现成的公开层卡片样式）
      包住这三块，而不是再自造一套"卡中卡"。
   -------------------------------------------------------------------- */
.v2-fact-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75em 1.25em;
  margin: 0.5em 0 1.25em;
}
.v2-fact-cell { margin: 0; }
.v2-fact-label {
  display: block;
  font-size: 0.8125em;
  color: #6b7280;
}
.v2-fact-value {
  display: block;
  font-size: 1.125em;
  font-weight: 600;
  color: #111827;
}
/* 手机档：3 列太挤 ⇒ 2 列 */
@media (max-width: 700px) {
  .v2-fact-grid { grid-template-columns: repeat(2, 1fr); }
}
/* --- 3.7h 卡片内分段容器（★ 用户裁定 2026-09-27 第 3 版）----------------
   ## 为什么又改结构（前两版都失败的根因）
     第 1 版：`margin-top: 1.5em`（标题） vs 列表 `margin-bottom: 0.5em`
              ⇒ 折叠取 max = 1.5em
     第 2 版：只靠标题 `margin-top: 2.5em`、列表 `margin-bottom: 0`
              ⇒ 数字按折叠算是对的，但**视觉仍不够**（用户截图反馈
                "还是拉不开"）
     ⚠️ 共同点：两版都把间距挂在**裸兄弟**上 ⇒ 只要需求是"再大一点"，
        就必须去猜折叠后的**有效值**（还要担心祖先块的 padding 不参与
        折叠：本卡片父级 `.pub-card` 有 `padding: 20px`，上边距只能
        自己挣，不能靠外层）。
     第 3 版（本版）：**结构改成包一层 `<section>`**，用
              `.v2-fact-sec + .v2-fact-sec { margin-top: … }`（值见下方「值」节）
     为什么这样就不受折叠困扰：相邻兄弟块之间，折叠只发生在两块**相接
        边界**（上一块的下边距 vs 下一块的上边距）。上一块**内部**是
        `.v2-fact-h`/`ul`，它们的 margin 被包在块里（块自身 `margin: 0`
        且无 padding ⇒ 内部 margin 会"溢出"合并到块的边界上，但由于块
        没有下边距，取到的就是内部那个值）—— 关键收益是**只调一个数**：
        要多大写多大，不用再算 max()。
   ## 值（★ 用户裁定 2026-09-27 第 5 版 = **24px**）
     沿革：1.5em/24px（折叠后，嫌窄）→ 2.5em/40px（还是拉不开）
        → 3.25em/52px（**太宽**）→ 2.25em/36px（仍宽）
        → **本版 `1.5em` = 24px**（用户明确给出目标数值）
     ⚠️ 为什么回到"和第 1 版一样"的值却不是重复犯错：第 1 版的 24px 是
        **折叠后的意外结果**（1.5em 标题上边距 vs 0.5em 列表下边距取 max），
        而现在的 24px 是**显式单点给定**（不参与折叠）⇒ 值可控、不会再变。
     📌 现在这是**唯一旋钮**：要调只改这一行的数字，不用再管折叠。
   ## 其它
     · 标题放 `<section>` 里、`<ul>` 是标题的**兄弟**（同级子元素）⇒
       `<ul>` 的 `padding-left: 1.25em` 不会把标题一起推右（老问题）。
     · 本类只新增选择器，`.v2-art-*`（列表页）与旧页零影响。
   -------------------------------------------------------------------- */
.v2-fact-sec { margin: 0; }
.v2-fact-sec + .v2-fact-sec { margin-top: 1.5em; }
/* 首个分段（增长）紧随业务概述 ⇒ 其自身不给上边距（间距由 `.v2-fact-mix` 给） */
.v2-fact-sec:first-of-type { margin-top: 0; }
/* 兜底：若将来有人又把标题/列表写成裸兄弟（没包 `<section>`），
   这里按"标题即分段起点"补一个等效的上边距 ⇒ 视觉不会忽然塌掉。
   ⚠️ 值必须与上面 `.v2-fact-sec + .v2-fact-sec` **保持一致**（两处一起改）。
   ⚠️ 用子选择器限定"父级里没有 `.v2-fact-sec`"（即标题是裸兄弟）才生效，
      避免与上面正常路径叠加。不用 `:has()` 是为了兼容更老的渲染核。 */
.v2-fact-h:not(:first-child) { margin-top: 1.5em; }
.v2-fact-sec .v2-fact-h { margin-top: 0; }

.v2-fact-mix { margin: 0 0 1.25em; font-size: 1em; }
/* ⚠️ 上一版写 `color: #4b5563`（浅灰）⇒ 用户反馈"颜色不对，应像其他一样黑"
   ⇒ **不写 color**，继承正文色（`.pub-body` 默认）。字号仍是正文（1em）。 */
/* 口径说明（★ 用户裁定：网格下**一行**小字，不逐格贴时间标签） */
.v2-fact-basis {
  margin: 0 0 1.25em;
  font-size: 0.8125em;
  color: #8a8a90;
}
.v2-fact-points { margin: 0; padding-left: 1.25em; }
.v2-fact-points li { margin-bottom: 0.5em; }
.v2-fact-points li:last-child { margin-bottom: 0; }
/* 段内小标题（增长 / 业务催化剂 / 风险）—— 比 `.v2-post-h3` 更轻
   ★ 标题与列表的间距（`0.4em`）：这是**段内**间距，与段间距无关，
     不要为了"拉开段落"去加大它（会变成标题孤零零吊在中间） */
.v2-fact-h {
  margin: 0 0 0.4em;
  font-size: 0.9375em;
  font-weight: 600;
  color: #374151;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.v2-post-report { margin-top: 1em; }

/* --- ★ Blog 详情页的 CTA 卡（2026-09-30 用户裁定）----------------------
   需求：**保留**现有的 `.pub-card` 框结构（不改成 `.pub-subscribe`），
        只把底色换成 watchlist 订阅框的那个灰。
   ⚠️ 灰值来源：`public.css:50` 的 `--pub-subscribe-bg = #F5F5F7`
      ⇒ 这里**直接引用同一个 CSS 变量**（不写死色值）
        ⇒ 将来若调整该变量，两处同步（避免"两处维护同一常量"漂移）
   ⚠️ 该变量定义在 `public.css`（冻结文件）⇒ 我们**只读不写**
      （铁律：动 public.css 会破坏 `docs/frozen_old_pages.txt` 基线，
        上轮实测 preflight 863→862）
   -------------------------------------------------------------------- */
/* ⚠️ 权重加固：`.pub-card`（public.css:397）也是 `background: var(...)`（0,1,0）
   ⇒ 虽然 v2.css 在其后加载（源码顺序可胜），但**加一层 `.pub-body` 前缀**
      （0,2,0）彻底避免"谁后加载"的隐式依赖 —— 本仓有过同类踩坑
      （见 v2.css:1376 的 `.pub-body a.v2-plan-cta` 事故记录） */
.pub-body .v2-cta-card {
  background: var(--pub-subscribe-bg, #F5F5F7);
}

/* --- 3.8 锁定态（对应旧 .lock-badge 的等价物，**新站自己的类名**） --- */
.v2-locked {
  cursor: not-allowed;
  color: #b0b0b6;
}
.v2-locked::after {
  content: " \1F512\FE0E";       /* ★ U+FE0E 强制文字渲染，防彩色 emoji */
}

/* --- 3.9 Tab 条（RPS 页等用；**新站自己的类名**，不复用旧 .rps-tab） --- */
.v2-tabs {
  display: flex;
  gap: 0.25em;
  margin: 1.25em 0 1em;
  border-bottom: 1px solid #e5e5e7;
}
.v2-tab {
  display: inline-block;
  padding: 0.5em 1em;
  color: #555;
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.v2-tab:hover { color: #0076ff; }
/* --- 3.9b 表内搜索框（RPS 页两个 Tab；照 StageAnalysis 的 `Search:` 样式）---
   ## 用途
     在**当前已加载的行**上做**纯前端**过滤（不重查库、不改 URL）。
     Tab① 的行由滚动加载（批 4）**动态追加** ⇒ 过滤脚本要能看见新行
     （见模板里 `filter()` 每次读取实时 `rows`，而非缓存 NodeList）。
   ## 为什么不用 datatables.js
     本仓不引入重型前端依赖（铁律）；这里十几行原生 JS 足够。
*/
.v2-searchbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;   /* ★ 右对齐（与 StageAnalysis 一致）*/
  gap: 0.5em;
  margin: 0.75em 0 0.5em;
}
.v2-searchbar label { color: #333; }
.v2-searchbar input[type="search"],
.v2-searchbar input[type="text"],
.v2-tabs-search input[type="search"] {
  width: 16em;
  max-width: 60%;
  padding: 0.3em 0.5em;
  border: 1px solid #d6d6d9;
  border-radius: 2px;
  font: inherit;
  color: #111;
  background: #fff;
}
.v2-searchbar input:focus,
.v2-tabs-search input:focus { outline: none; border-color: #0076ff; }
.v2-searchbar .v2-search-count { color: #777; margin-right: auto; }

/* --- 3.9c ★ 2026-10-17：搜索框与 Tab **同一行**（用户裁定）--- */
.v2-tabs--search { align-items: center; flex-wrap: wrap; }
.v2-tabs-search {
  margin-left: auto;              /* 推到最右（与「板块 RPS」同一行） */
  display: flex;
  align-items: center;
  gap: 0.5em;
  padding-bottom: 0.35em;
}
.v2-tabs-search label { color: #333; }
.v2-tabs-search input[type="search"] { width: 15em; }
.v2-tabs-search .v2-search-go { padding: 0.3em 0.9em; font-size: 0.95em; }
@media (max-width: 640px) {
  /* 手机上放不下同一行：搜索占满一整行（仍紧跟 Tab，不另起区块） */
  .v2-tabs-search { margin-left: 0; width: 100%; padding-bottom: 0.6em;
                    flex-wrap: wrap; }
  .v2-tabs-search input[type="search"] {
    flex: 1 1 8em; width: auto; max-width: none; min-width: 0;   /* ★ 允许收缩 */
  }
}

/* --- 3.9d 搜索等待遮罩（提交后显示，页面跳转即消失）--- */
.v2-wait {
  position: fixed; inset: 0; z-index: 300;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.55);
}
.v2-wait[hidden] { display: none; }
.v2-wait-box {
  background: #fff; border: 1px solid #e5e5e7; border-radius: 8px;
  padding: 14px 22px; color: #333; font-size: 0.95rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.v2-tab.on {
  color: #111;
  font-weight: 600;
  border-bottom-color: #0076ff;
}

/* --- 3.10 横向滚动容器（表格装不下时；新站自己的类名） -------------------
   ## ⚠️⚠️ 2026-09-30 批 7-b 修正（**VPS 实测事故：表头错位 + 一大块空白**）
      ## 事故现象（用户截图）
        Tab 条下面空一大块（约 61px），然后才出现表头；
        且表头位置**比第一行数据还低**（渲染错乱），多出一条假横线。
      ## 根因（真 Chrome 实测数据）
        旧版只写了 `overflow-x: auto`。**浏览器 CSS 规范**：
          当一个方向是 `visible`、另一个不是时 ⇒ `visible` **被强制算成 `auto`**
        ⇒ 本 div 变成**滚动容器**。
        而 `.v2-table thead th` 是 `position: sticky; top: var(--v2-topbar-h)`
        （= 61px，给"吸顶在顶栏之下"用的）⇒ 在滚动容器里就按**容器顶 + 61px**
        定位 ⇒ 表头被**往下推 61px** ⇒ 容器顶部留白 + 表头压到数据行位置。
        实测：`theadTh.top=317`、`box.top=256` ⇒ 差 **61**（正好 = 顶栏高）；
              `firstTr.top=303` < 表头 317 ⇒ 数据行跑到表头**上面**。
      ## 修法（用户裁定 = 甲）：**把它变成"真的"滚动容器**
        既然浏览器已经把它当滚动容器了，就**顺势给它限高 + 明确的 overflow-y**
        ⇒ sticky 表头**在容器内吸顶**（这本就是 sticky 的设计意图）
        ⇒ 不再有 61px 错位；滚动时列名一直可见（3600 行长列表很需要）。
      ## ⚠️ 配套改动（必须一起做）
        `web/templates/v2/member/rps.html` 的滚动加载监听**这个 div 的 scroll**
        ⇒ 限高后它**真的会滚** ⇒ 监听依然有效（且比"页面滚到底"更精准）。
      ## 高度怎么定
        `70vh`：视口高度的 70%。理由：① 顶栏 + 上方标题/Tab 约占 25vh；
        ② 留 70vh 给表格既够看，又保证**会出现滚动条**（触发加载）；
        ③ 用 `vh` 而非固定 px ⇒ 大小屏都合理。
   ## 🔴🔴 `.table-scroll` 保持**原样**（不加限高）
      它是**新旧共用的类**：
        · 旧站：`_vcp_*` / `browse` / `reports_list` / `admin_*` 等 **20+ 个模板**
        · 新站：`v2/rps.html`（公开版表格）、`v2/_ui.html`（`ui.table` 宏）
      ⇒ 给它加 `max-height` 会**改旧页外观**（违反「旧页一行不动」铁律）；
        也会**误伤公开版 RPS 页**。
      ⇒ 拆成两条规则：`.table-scroll` 只保留横向滚动（原样），
        **限高只给 `.v2-table-scroll`**（新站会员/后台专用）。
   -------------------------------------------------------------------- */
.table-scroll {
  overflow-x: auto;          /* ← 这一行**保持不变**（新旧共用，别动） */
}
.v2-table-scroll {
  overflow-x: auto;
  overflow-y: auto;          /* 明确纵向滚动（见上：不加会被浏览器强制成 auto） */
  max-height: 70vh;          /* 限高 ⇒ 成为真滚动容器 ⇒ sticky 表头在容器内吸顶 */
}

/*: ★ 会员区 Tab② 板块表：**成分数过少**的板块行（灰色行）。
    ## 规则来源（照旧版，用户裁定 P5 = 照旧版逻辑）
      成分数 < `config.RPS_MIN_SECTOR_MEMBERS`（默认 10）⇒ 不参与排序、
      一律排最后（**服务端**排的）+ 本类给"弱化"视觉。
    ## ⚠️ 为什么不复用旧类 `.sec-small`
      旧类在 `app.css`（**冻结的骨架文件**）⇒ 铁律：不依赖旧样式
      （Q5-b 风格铁律：外观归 v2）⇒ 用 v2 自己的类名。 */
.v2-sec-small {
  color: #9aa0a6;
}
.v2-sec-small a { color: #9aa0a6; }

/* ---------------------------------------------------------------------------
   ★ VCP 页新增类（2026-10-15 · plan_legacy_style_restore 批 A）
   ## 来源：照旧站 `.vcp-head-holder/.vcp-sticky-head/.vcp-tabs/.vcp-tab/-tab-n`
      （`app.css:365-406`）的**数值**，改用 v2 自己的类名（⛔ 不复用旧类）。
   ## 铁律：H1 + 筛选条 + Tab 条 **同一个 fixed 块**（照旧 `vcp.html:30-73`）
      ⇒ 滚动时整块不动（用户 2026-09-19 要求"完全不动"）。
      做法：`placeholder(fixed 块的兄弟占位)` + `fixed 块` + 页内 JS 实测高度写变量。
   --------------------------------------------------------------------------- */
.v2-vcp-head-holder { display: block; }
.v2-vcp-head-holder {
  /* 占位高度 = fixed 块实测高度（页内 JS 写 `--v2-vcp-head-h`） */
  height: var(--v2-vcp-head-h, 0px);
}
.v2-vcp-sticky-head {
  position: fixed;
  top: var(--v2-topbar-h, 0px);
  left: 0; right: 0;
  z-index: 50;                     /* < 顶栏 100；> 正文 */
  background: var(--bg, #f5f6f8);
  padding-top: var(--v2-vcp-head-gap, 18px);
  padding-bottom: 2px;
  /* ★★ 需求 11（2026-10-15）：**头与内容左对齐**（旧站 `app.css:385-387` 同思路）
     旧站：`padding-left/right: max(16px, calc((100vw - 1080px)/2 + 16px))`
       —— 旧站 `main{max-width:1080px; margin:auto; padding:0 16px}` ⇒ 一行算式即可。
     新站：内容在 `main > .pub-container(1280) > .pub-shell（再窄）` ⇒ **算不准**
       ⇒ 兜底用容器算式（与 `.pub-container` 对齐），**并由页内 JS 实测覆盖**（更准）。 */
  padding-left: var(--v2-vcp-head-pad);
  padding-right: var(--v2-vcp-head-pad);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .06);
}
.v2-vcp-sticky-head h1 { margin: 0 0 4px; font-size: 1.35rem; }   /* ★ 需求 11：照旧站 `h1{font-size:1.35rem}`(实测 21.6px) */
.v2-vcp-sticky-head form { margin: 4px 0 2px !important; }
/*: 分节小标题（VCP exit tab 的「出池/原有/新进」；照旧站 `.v2-vcp-sub` 语义） */
.v2-vcp-sub {
  margin: 14px 0 4px; font-size: 1.02em;
  padding-bottom: 2px; border-bottom: 1px solid #eee;
}
.v2-vcp-tabs {
  display: flex; flex-wrap: wrap; gap: 4px;
  border-bottom: 2px solid var(--line, #e2e4e8); margin: 4px 0 12px;
}
.v2-vcp-tab {
  display: inline-block; padding: 6px 14px; text-decoration: none;
  color: var(--muted, #7a808a); border: 1px solid transparent;
  border-bottom: none; border-radius: 6px 6px 0 0;
  margin-bottom: -2px; font-size: 0.95em; white-space: nowrap;
}
.v2-vcp-tab:hover { color: var(--fg, #23272f); background: #f4f6f8; }
.v2-vcp-tab.on {
  color: var(--fg, #23272f); font-weight: 600; background: var(--card, #fff);
  border-color: var(--line, #e2e4e8); border-bottom: 2px solid var(--card, #fff);
}
.v2-vcp-tab-n {
  display: inline-block; min-width: 1.5em; text-align: center;
  margin-left: 5px; padding: 0 5px; border-radius: 9px;
  background: #eef0f3; color: var(--muted, #7a808a); font-size: 0.85em;
  font-weight: 600;
}
.v2-vcp-tab.on .v2-vcp-tab-n {
  background: color-mix(in srgb, var(--accent, #0076ff) 16%, #fff);
  color: var(--accent, #0076ff);
}
@supports not (background: color-mix(in srgb, red 10%, #fff)) {
  .v2-vcp-tab.on .v2-vcp-tab-n { background: #e8f0fa; }
}
/* 表格 ↔ 卡片互跳（照旧 `.vcp-*`；避开 fixed 头） */
.v2-vcp-card, tr[id^="v2-row-"] {
  scroll-margin-top: calc(var(--v2-topbar-h, 0px) + var(--v2-vcp-head-h, 0px)
                          + var(--v2-vcp-head-gap, 18px) + 12px);
}
.v2-vcp-card.v2-vcp-flash { box-shadow: 0 0 0 3px rgba(0, 118, 255, .45); }
tr.v2-vcp-flash > td { background: #fffbe6; }
.v2-vcp-jump { color: inherit; text-decoration: none;
               border-bottom: 1px dotted var(--muted, #7a808a); }
.v2-vcp-jump:hover { border-bottom-color: var(--accent, #0076ff);
                     color: var(--accent, #0076ff); }
.v2-vcp-back { float: right; font-weight: 400; text-decoration: none;
               color: var(--muted, #7a808a); }
.v2-vcp-back:hover { color: var(--accent, #0076ff); }
/* 池历史浮层（照旧 `.vcp-modal`：遮罩 + 居中盒子） */
.v2-vcp-modal { position: fixed; inset: 0; z-index: 200; display: flex;
                align-items: center; justify-content: center; }
.v2-vcp-modal[hidden] { display: none; }
.v2-vcp-modal-mask { position: absolute; inset: 0; background: rgba(0,0,0,.45); }
.v2-vcp-modal-box { position: relative; background: #fff; border-radius: 8px;
                    box-shadow: 0 8px 30px rgba(0,0,0,.22);
                    width: min(760px, 92vw); max-height: 80vh;
                    display: flex; flex-direction: column; }
.v2-vcp-modal-head { display: flex; align-items: center; gap: 8px;
                     padding: 10px 14px; border-bottom: 1px solid #eee; }
.v2-vcp-modal-head strong { flex: 1 1 auto; }
.v2-vcp-modal-x { border: 0; background: none; font-size: 1.4em; line-height: 1;
                  cursor: pointer; color: #888; }
.v2-vcp-modal-body { padding: 12px 14px; overflow: auto; }

/* ---------------------------------------------------------------------------
   ★ 观察名单 / 报告表：**命中公式彩色胶囊**（2026-10-15 · 需求 1+2）
   ## 来源：旧站选股页「命中公式」列（`app.css:294-306`）的数值
      `.tag{border-radius:10px;padding:1px 8px;font-size:.78rem;color:#fff}`
      `.tag-fml{display:inline-block;margin:1px 3px 1px 0;background:#495057}`
      `.f-kdzd:#1c7ed6 / .f-yxfz:#7048e8 / .f-sxhc:#e8590c / .f-vcp:#0c8599 / .f-manual:#868e96`
      多公式**竖排**：`td.fml .tag-fml{display:block;width:fit-content;margin:2px 0}`
   ## 规则 B：**只抄外观**（内容仍用策略名，不露裸代号）
   --------------------------------------------------------------------------- */
.v2-tag-fml {
  display: inline-block;
  margin: 1px 3px 1px 0;
  padding: 1px 8px;
  border-radius: 10px;
  font-size: 0.78rem;
  line-height: 1.5;
  color: #fff;
  background: #495057;          /* 未登记策略的兜底（同旧站） */
  white-space: nowrap;
}
.v2-f-kdzd   { background: #1c7ed6; }   /* 口袋支点 */
.v2-f-yxfz   { background: #7048e8; }   /* 月线翻转 */
.v2-f-sxhc   { background: #e8590c; }   /* 顺向火车轨 */
.v2-f-vcp    { background: #0c8599; }   /* 形态分析 */
.v2-f-manual { background: #868e96; }   /* MANUAL */
/* 命中公式列：同格多个 ⇒ 竖排（照旧 `td.fml`） */
.v2-cell-fml { white-space: normal; }
.v2-cell-fml .v2-tag-fml { display: block; width: fit-content; margin: 2px 0; }

/* ---------------------------------------------------------------------------
   ★ 报告详情页两栏（2026-10-15 · plan_legacy_style_restore 需求 3）
   ## 照旧站 `.report-page/.report-body/.notes-rail`（`app.css:606-628/776-800`）
      · 正文在左（`.v2-report-body`）、**笔记在右**（`.v2-notes-rail`）
      · 笔记栏 `position: sticky` ⇒ **不随正文滚动**
      · 正文**卡片内滚动**（`max-height:calc(100vh-240px); overflow-y:auto`）
        —— ★ 这**变更**了 `plan_report_body_typography.md §4.1`（当时裁定"不做"）
   ## Q8 定案：内容区放宽到 1560px，**只作用于本页**（⛔ 不动全站 v2-content--tight）
   ## 移动端：窄屏折成单栏（笔记移到正文下方）
   --------------------------------------------------------------------------- */
.v2-report-page {
  display: flex;
  gap: clamp(12px, 2vw, 24px);
  align-items: flex-start;
  width: 100%;
}
.v2-report-body {
  flex: 1 1 auto;
  min-width: 0;
  align-self: flex-start;      /* ⚠️ 不能 stretch：否则笔记栏 sticky 失效 */
}
/* ★ 正文**卡片内滚动**（照旧 `.report-md`）：长报告不把整页拉长、右栏 sticky 才稳 */
.v2-report-body .v2-post-body {
  max-height: calc(100vh - 240px);
  min-height: 320px;
  overflow-y: auto;
  padding-right: 6px;           /* 给滚动条留空，文字不贴边 */
}
/* ★★ 批次 C（2026-10-16 · R4）：笔记 rail 与内层 —— 数值 1:1 照旧站
   `app.css:776-780`（.notes-rail / .notes-rail-inner）。
   ⚠️ 这两个类**只被报告页用**（`v2/member/report.html`）⇒ 直接改写、无需作用域。
   · 旧站 `top: 62px`（其顶栏高度 + 12）⇒ 照抄，滚动时吸附偏移与旧站一致
   · 旧站**内层没有 overflow/max-height**（不滚动，靠固定高 min(56vh,760px)） */
.v2-notes-rail {
  flex: 0 0 auto;
  display: flex;
  align-self: stretch;
  width: clamp(230px, 24vw, 320px);
  min-width: 230px;
}
.v2-notes-rail-inner {
  position: sticky;             /* ★ 不随正文滚动 */
  top: 62px;                    /* = 旧站 `.notes-rail-inner` */
  align-self: flex-start;
  width: 100%;
  display: flex;
  height: min(56vh, 760px);     /* 旧站固定高（不是 max-height + 滚动） */
}
/* H1 内的「← 观察名单」：★ 批次 B（R10）改为**行内**紧跟标题 + 旧站 `small` 类
   （`app.css:292 .small{font-size:.8rem}` / `app.css:488 a.small{color:var(--accent)}`）
   ⇒ 本文件不再需要专用类（旧 `.v2-report-back` 的 float 与去下划线**已删除**）。 */
/* Q8：本页内容区放宽到 1560px（只作用本页；不动全站容器） */
@media (min-width: 1600px) {
  .v2-content--tight:has(.v2-report-page) { max-width: 1560px; }
}
/* 移动端：折成单栏（笔记在正文下方）
   ★ 批次 C（R4）：**照旧站**——旧站手机档并没有重置笔记栏（只重置了追问栏 `.chat-rail`，
   见 `app.css:735-742`）⇒ 笔记栏仍 `width: clamp(230px,24vw,320px)`（390px 下 = 230px）、
   内层仍 sticky + 固定高。故这里**只**把两栏折成单栏，不再覆盖 rail 宽度/sticky。 */
@media (max-width: 900px) {
  .v2-report-page { flex-direction: column; }
  .v2-report-body .v2-post-body { max-height: none; overflow-y: visible; }
}

/* ---------------------------------------------------------------------------
   §3.11 后台表单控件（★ 2026-09-30 批 9：新后台「用户管理」）
   ⚠️ 新站**自己的类名**（Q5-b 风格铁律）—— 不复用旧 `card narrow` /
      `linklike` / `chk` / `perms-edit`（那些在 app.css，冻结骨架文件）。
   --------------------------------------------------------------------------- */
.v2-input {
  display: inline-block;
  padding: 0.35em 0.6em;
  border: 1px solid #d0d0d6;
  border-radius: 4px;
  font: inherit;
  font-size: 0.9375em;
  color: inherit;
  background: #fff;
}
.v2-input--sm { font-size: 0.875em; padding: 0.2em 0.45em; }
.v2-input:focus { outline: 2px solid #0076ff; outline-offset: -1px; }

.v2-btn--link {
  background: none;
  border: 0;
  padding: 0.15em 0.3em;
  color: #0076ff;
  cursor: pointer;
  font: inherit;
  font-size: 0.9375em;
  text-align: left;
}
.v2-btn--link:hover { text-decoration: underline; }
/* ★ 2026-10-22：表格里的「股票名」外链（雪球 / Yahoo Finance）。
   不复用 .v2-btn--link：那个带 padding / font-size，会把名称列挤宽。 */
.v2-quote-link { color: #0076ff; text-decoration: none; }
.v2-quote-link:hover { text-decoration: underline; }
.v2-danger { color: #c0392b; }

/*: 后台创建用户表单（纵向排列，与 v2 组件一致） */
.v2-console-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6em 1.2em;
  align-items: flex-end;
  margin: 1em 0;
}
.v2-console-form > label {
  display: flex;
  flex-direction: column;
  gap: 0.25em;
  font-size: 0.9375em;
}

/*: 用户行内的小表单（角色 / 操作按钮组）—— 横排 */
.v2-inline-form {
  display: inline-flex;
  align-items: center;
  gap: 0.25em;
  margin: 0 0.4em 0.3em 0;
}
.v2-console-ops { white-space: nowrap; }

/*: 权限矩阵折叠区（照旧版 `details.perms-edit` 的用途，类名换 v2） */
.v2-perms-edit { margin-top: 0.4em; }
.v2-perms-edit summary { cursor: pointer; }
.v2-chk {
  display: block;
  margin: 0.2em 0;
  font-size: 0.9375em;
}

/*: ★ 观察名单「入选策略」列的小标签（批 2）
    ## 为什么自己写（不复用旧 `.tag`）
      旧 `.tag` / `.tag-fml` 在 `app.css`（冻结骨架）⇒ 新站不依赖旧样式。
    ## 样式
      浅底圆角小标签，多个策略并排（`KDZD`+`YXFZ` 这种多策略命中会出现多个）。 */
.v2-tag {
  display: inline-block;
  padding: 0.1em 0.5em;
  margin: 0.1em 0.15em 0.1em 0;
  border-radius: 3px;
  background: #eef1f5;
  color: #44403c;
  font-size: 0.8125em;
  white-space: nowrap;
}

/*: 分页条（观察名单；居中一行） */
.v2-pager {
  display: flex;
  gap: 0.75em;
  justify-content: center;
  align-items: center;
  margin: 1em 0;
}

/* ============================================================================
   ★★ Phase 1 批次 C（2026-10-16 · R4）：报告页笔记面板 = 旧站 1:1
   ## 真源：`app.css:781-829`（.report-notes / .notes-list / .note-* / .notes-form*）
      ＋ DOM：`_report_notes_panel.html:11-33`（h2 + 副标题 + 历史链接 → 列表 → 表单）
   ## 铁律：只复制**外观与结构**；类名仍用新站前缀；接口仍走新站
      `/member/<market>/notes/api/*`（⛔ 不复用旧蓝图/旧模板）
   ## 作用域：`body:has(.v2-report-page)` —— 这些类本来就只被报告页用，
      前缀是双保险，同时稳压 `.pub-shell h2` 等基础规则的特异性
   ---------------------------------------------------------------------------- */

/* 白卡（旧站 `.report-notes`：bg / border / radius10 / padding 12-14 / shadow） */
body:has(.v2-report-page) .v2-notes {
  margin: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 14px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .08);
}
/* 标题行：主标题 + 副标题 + 右侧「该市场笔记历史 →」（旧站 `.report-notes h2`） */
body:has(.v2-report-page) .v2-notes-h2 {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  margin: 0 0 8px;
  font-size: 1.05rem;                  /* = 旧站全局 `h2`（app.css:201） */
  line-height: normal;                 /* ★ 压 `.pub-shell h2` 的 1.45（旧站 h2 未设行高） */
}
body:has(.v2-report-page) .v2-notes-sub { color: var(--muted); font-size: .8rem; }
body:has(.v2-report-page) .v2-notes-h2 a { margin-left: auto; }
/* 错误行（旧站 `.notes-error`） */
body:has(.v2-report-page) .v2-notes-error { color: #c53030; font-size: .8rem; margin: 4px 0; }
/* 列表（旧站 `.notes-list`：纵向、可滚动、间距 8、min-height 60） */
body:has(.v2-report-page) .v2-note-list {
  flex: 1 1 auto;
  overflow-y: auto;
  min-height: 60px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
}
body:has(.v2-report-page) .v2-note-loading { color: var(--muted); font-size: .8rem; }
/* 单条（旧站 `.note-item`） */
body:has(.v2-report-page) .v2-note-item {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 10px;
  background: #fbfcfe;
}
/* 条目头（旧站 `.note-head` / `.note-price` accent / `.note-by` 右对齐） */
body:has(.v2-report-page) .v2-note-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  font-size: .75rem;
  color: #718096;
}
body:has(.v2-report-page) .v2-note-price { color: var(--accent); }
body:has(.v2-report-page) .v2-note-by { margin-left: auto; }
/* 正文（旧站 `.note-body`） */
body:has(.v2-report-page) .v2-note-body {
  margin-top: 5px;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: .86rem;
  line-height: 1.5;
}
/* 操作行（旧站 `.note-acts` ＋ 两种按钮：白底描边 / 无边框蓝字） */
body:has(.v2-report-page) .v2-note-acts { margin-top: 6px; display: flex; gap: 10px; }
body:has(.v2-report-page) .v2-note-acts button {
  font-size: .78rem;
  padding: 2px 8px;
  cursor: pointer;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  color: #2d3748;
}
body:has(.v2-report-page) .v2-note-acts button:hover { background: #edf2f7; }
body:has(.v2-report-page) .v2-note-acts .v2-note-link {
  border: none;
  background: transparent;
  padding: 2px 2px;
  color: #3182ce;
}
body:has(.v2-report-page) .v2-note-acts .v2-note-link--danger { color: #c53030; }
/* 行内编辑框（旧站 `.note-edit`） */
body:has(.v2-report-page) .v2-note-edit {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: .86rem;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
}
/* 表单（旧站 `.notes-form` / textarea / `.notes-form-foot` / 实心按钮右下） */
body:has(.v2-report-page) .v2-notes-form {
  flex: 0 0 auto;
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
body:has(.v2-report-page) .v2-notes-form textarea {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: .9rem;
  padding: 8px 10px;
  resize: vertical;
  min-height: clamp(130px, 15vh, 250px);
  border: 1px solid var(--line);
  border-radius: 6px;
}
body:has(.v2-report-page) .v2-notes-foot { display: flex; align-items: center; gap: 8px; }
body:has(.v2-report-page) .v2-notes-foot button {
  margin-left: auto;
  padding: 5px 14px;
  cursor: pointer;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--accent);
  color: #fff;
}
body:has(.v2-report-page) .v2-notes-foot button:disabled { opacity: .55; cursor: default; }
/* ★ 批次 B（R2，2026-10-16）：`.v2-report-block` 包装已从模板移除
   （`report.html`：卡片直接放在 `.v2-report-body` 下，卡片成为唯一块、首行=正文）
   ⇒ 其原样式（`margin-top:1.5em` / 相邻块 `border-top`）**已删除**。 */

/*: 笔记历史的批量删除工具条（批 5） */
.v2-nh-tools {
  display: flex;
  gap: 0.5em;
  align-items: center;
  margin: 0.5em 0 0.75em;
}

/*: ★ VCP 卡片（批 6）—— 外观 v2，信息照旧（七块）
    ⛔ 不用旧 `.card` / `.vcp-card` / `.data`（旧站在 app.css 里） */
.v2-vcp-card {
  margin: 1.25em 0;
  padding: 1em 1.25em;
  border: 1px solid #e8e8ec;
  border-radius: 6px;
  background: #fff;
}
.v2-vcp-head {
  margin: 0 0 0.5em;
  font-size: 1em;
  line-height: 1.6;
}
.v2-vcp-head > * { margin-right: 0.4em; }
.v2-vcp-status { margin: 0 0 0.75em; }
.v2-vcp-sub { margin: 1em 0 0.35em; }
.v2-vcp-tier {
  margin: 1.5em 0 0.5em;
  font-size: 1.0625em;
  padding-bottom: 0.25em;
  border-bottom: 1px solid #eee;
}
/*: 档位徽标（A / B / C）—— 颜色区分 */
.v2-tier-a { background: #e6f4ea; color: #1e7e34; }
.v2-tier-b { background: #eef2ff; color: #3349a8; }
.v2-tier-c { background: #f2f2f4; color: #55555c; }
.v2-tag-ok { background: #e6f4ea; color: #1e7e34; }
.v2-tag-warn { background: #fff4e5; color: #a35b00; }

/* ---------------------------------------------------------------------------
   ④ G0 自检块（仅示范用，正式上线前删除本段）
   --------------------------------------------------------------------------- */
.v2-g0-check {
  margin-top: 3em;
  padding: 1.25em 1.5em;
  border: 1px dashed #c9c9cf;
  border-radius: 6px;
  background: #fafafa;
}

.v2-g0-check h2 {
  margin-top: 0;
  font-size: 1.125em;
}

.v2-g0-check ul {
  margin: 0.75em 0 0;
  padding-left: 1.25em;
}

.v2-g0-check li {
  margin: 0.35em 0;
}

.v2-g0-check code {
  font-size: 0.9em;
  background: #eee;
  padding: 1px 5px;
  border-radius: 3px;
}

/* ============================================================================
   §3.6 法务页排版（terms / privacy）—— 用户 2026-09-27 法务正文定稿

   ## 为什么需要
     法律正文段落长、编号多，README 式排版在 1280px 容器里**行长过长**
     （一行 150+ 字符 ⇒ 阅读疲劳、易漏读条款）。故：
        · `.v2-legal-*` 系列**只作用于法务页**，不改任何现有页面；
        · 正文宽限制在 76ch（≈ 中文 38 字）—— 排版学公认的舒适区间；
        · 标题层级 h2/h3 加大上间距 ⇒ 让"第 N 条"在长滚动中可定位。

   ## 与既有类的分工
      · `.pub-page-title`（public.css）—— H1，本文件**不覆盖**；
      · `.pub-muted` —— 日期行；本文件只加 `.v2-legal-meta` 的间距微调。
   ============================================================================ */

/*: 正文列宽 —— 长条款的可读性上限（76 字符 ≈ 中文 38 字） */
.v2-legal-meta {
  margin: -4px 0 20px;
  font-size: 0.875em;
}

/*: 章节标题（"1. 教育目的…"）：上方大留白 ⇒ 长滚动中的视觉锚点 */
.pub-shell h2 {
  margin: 2em 0 0.6em;
  font-size: 1.25em;
  line-height: 1.4;
}

/*: 子节标题（"11.1 有限的冷静期…"）：次一级留白 */
.pub-shell h3 {
  margin: 1.5em 0 0.5em;
  font-size: 1.0625em;
  line-height: 1.45;
}

/*: 正文段落 —— 限宽 + 行高，避免满屏长行 */
.pub-shell h1 ~ p,
.pub-shell h2 ~ p,
.pub-shell h3 ~ p {
  max-width: 76ch;
  line-height: 1.7;
  margin: 0.7em 0;
}

/*: 编号子段（6.4.1 / 8.1.1 / 9.2.1）—— 缩进 + 略小字号，制造层级 */
.v2-legal-sub {
  max-width: 76ch;
  margin: 0.35em 0 0.35em 1.4em;
  font-size: 0.95em;
  line-height: 1.65;
}

/*: 列表（PIPA 权利、Cookie 分类、定义表）—— 与段落同样的限宽 */
.pub-shell .v2-legal-ul,
.pub-shell .v2-legal-ol {
  max-width: 76ch;
  margin: 0.7em 0;
  padding-left: 1.5em;
  line-height: 1.7;
}

.pub-shell .v2-legal-ul li,
.pub-shell .v2-legal-ol li {
  margin: 0.4em 0;
}

/*: 分隔线（Privacy §16 之后） */
.v2-legal-hr {
  max-width: 76ch;
  margin: 2.5em 0 1.25em;
  border: 0;
  border-top: 1px solid #e5e7eb;
}

/*: 「以英文版本为准」声明 —— 略小、灰色，不当正文 */
.v2-legal-prevail {
  max-width: 76ch;
  font-size: 0.875em;
  color: #6b7280;
  line-height: 1.6;
}

/*: 小屏：取消 limit 与缩进，避免窄屏下右侧过空 */
@media (max-width: 640px) {
  .pub-shell h2 { margin-top: 1.5em; font-size: 1.125em; }
  .pub-shell h3 { margin-top: 1.25em; font-size: 1em; }
  .v2-legal-sub { margin-left: 0.8em; }
}

/* ============================================================================
   §4 订阅页（`v2/subscription.html`）—— 定价卡 · FAQ 折叠
   ----------------------------------------------------------------------------
   ## ★ 零 JS 的关键：单选钮与卡片是**兄弟**关系
     控制器（4 个 `<input type="radio" name="v2mkt">`）放在 `.v2-plan-grid`
     **之前**，卡片放在网格里 ⇒ 用 `~`（后续兄弟）选择器就能让**一次点击
     同时改变 Pro 与 Premium 两个价格**。
     为什么不能把 radio 放进卡片内部：那样它只能控制自己卡内的元素
     （`~` 不出父级），会出现「Pro 选 4 市场、Premium 还显示单市场」这种
     结算无法处理的组合。市场范围是**全页唯一的一个选择**。
     CSS 的 `:checked` 只能作用于「后继兄弟」，所以顺序必须是：
       radios → grid
     ⚠️ 顺序错了**不会报错**，只会静默不生效 ⇒ 改模板时不要挪动 radios。

   ## 2026-09-28 定稿：`:checked` 只用于**市场行**（第 8 题后也改 JS）
      周期开关（月/年）已改为 `<button role="switch">` + `[aria-checked]`（照 TIKR）。
      ⇒ 本节以下的 `:checked` 说明**只适用于市场 radio**。 */

/* --- 4.0 ★ TIKR 像素级配色令牌（2026-09-28 重做） --------------------------
   ## 取值来源：**本站商标**（`web/static/img/logo/valmentum-mark-vector.svg`）
      · 左臂 藏青  #0A2046
      · 右臂 青绿  #0EB2AA   ← ★ 用户裁定「用 V 字右臂那个绿」
      · 圆心 柠檬绿 #CDD620
     与 `app.css` 的 `--pub-brand-navy / --pub-brand-teal` **同源同值**；
     这里再抽一层「订阅页专用」变量，是为了让 §4 全区**零散写 hex**，
     将来换色只改这 7 行（照 TIKR 的 `#699C97 / #D9EFED / #262626` 一一映射）。

   ## ★ 映射表（TIKR 原值 → 我方）
     TIKR `#699C97`（高亮卡 2px 边 + 角标字） ⇒ `--v2-plan-hi`      = #0EB2AA
     TIKR `#D9EFED`（"Most Popular" 横条底）   ⇒ `--v2-plan-hi-soft` = #E3F7F5
     TIKR `#262626`（高亮卡按钮底）            ⇒ `--v2-plan-solid`   = #0A2046
     TIKR `#333333`（高亮按钮 hover）          ⇒ `--v2-plan-solid-hi`= #15305F
   ⛔ 其余数值（圆角 9.6px / 内边距 20px / 间距 32px / 价格 36px…）
      一律照抄 TIKR 实测，**一个像素都不改**。 */

:root {
  --v2-plan-hi:        #0EB2AA;   /* = TIKR #699C97 的对应物（V 右臂青绿） */
  --v2-plan-hi-soft:   #E3F7F5;   /* = TIKR #D9EFED 的对应物（同色相提亮） */
  --v2-plan-solid:     #0A2046;   /* = TIKR #262626（改用品牌藏青） */
  --v2-plan-solid-hi:  #15305F;   /* = TIKR #333333（藏青加深） */
  --v2-plan-ink:       #44403c;   /* TIKR stone-700：名字 / 价格 / 小字 */
  --v2-plan-ink-sub:   #57534e;   /* TIKR stone-600：副标题 */
  --v2-plan-line:      #d6d3d1;   /* TIKR stone-300：卡片 & 按钮边框 */
  --v2-plan-radius:    9.6px;     /* TIKR rounded-[.6em] */
  --v2-plan-pad:       20px;      /* TIKR p-5 */
  --v2-plan-gap:       32px;      /* TIKR gap-8 */
  --v2-plan-ctl-h:     40px;      /* TIKR 按钮 h-10 */
}

/* --- 4.1 控制器（视觉隐藏，仍可聚焦） --- */
.v2-plan-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- 4.2 卡片网格 ----------------------------------------------------------
   TIKR：`flex gap-8` + 外层 `max-w` ⇒ 三卡一行、**间距 32px**、容器居中限宽。
   ★ 用 grid 而不用 flex，是因为我们要 `1fr` 三等分（TIKR 靠 flex-basis 等分，
     效果等价但 grid 在窄屏换行时更可控）。 */
.v2-plan-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--v2-plan-gap);
  max-width: 1100px;
  margin: 2em auto 1em;
  align-items: stretch;
}
@media (min-width: 900px) {
  /* ★ 3 张卡一行（Free / Pro / Premium） */
  .v2-plan-grid { grid-template-columns: repeat(3, 1fr); }
}

/* --- 4.3 单张卡片（★ 严格照 TIKR 实测 class 串）----------------------------
   TIKR 原串（2026-09-28 从线上 HTML 逐字抓取）：
     `flex flex-col items-center gap-8 p-5 bg-white rounded-[.6em]
      border-[1px] border-solid border-stone-300 relative flex-1`
   ⇒ gap:32px · padding:20px · r:9.6px · 1px #d6d3d1 · **`relative`（角标靠它定位）**
   ⚠️ 不用 `overflow:hidden`：角标是 absolute 且自带 `rounded-t` 即可压住圆角。
   ★ 内边距减 1px 补偿 2px 边框 ⇒ 三卡内容基线仍严格对齐。 */
.v2-plan-card {
  position: relative;                 /* ★ 角标 absolute 的定位锚（TIKR `relative`） */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--v2-plan-gap);
  border: 1px solid var(--v2-plan-line);
  border-radius: var(--v2-plan-radius);
  padding: var(--v2-plan-pad);
  background: var(--pub-card-bg);
  text-align: center;          /* ★ TIKR `text-center`：连功能清单也居中 */
}

/*: ★ 推荐档（Premium）—— TIKR `!border-[#699C97] !border-[2px]`
   ★ 我方换品牌青绿 = logo V 字右臂色（`--v2-plan-hi`）。 */
.v2-plan-card--featured {
  border: 2px solid var(--v2-plan-hi);
  padding: calc(var(--v2-plan-pad) - 1px);
}

/*: 满宽角标横条 —— TIKR 原串逐字：
     `absolute top-0 left-0 flex justify-center items-center p-2
      bg-[#D9EFED] rounded-tl-lg rounded-tr-lg w-full`
   ⇒ **绝对定位 + 满宽**，**完全不占流**
   🔴 这正是上一版 Premium 整体下沉 32px 的根因：
      旧版用「负 margin 参与 flex 流」⇒ 角标占掉一个 `gap:32px`
      ⇒ 把 Premium 的档位名往下推。改成 absolute 后三卡首行**严格齐平**。
   ★ 我方底色/字色换品牌青绿（浅底 `--v2-plan-hi-soft` / 字 `--v2-plan-hi`）。 */
.v2-plan-flag {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  margin: 0;                           /* ★ `<p>` 默认 `margin: 1em 0` ⇒ 会顶下 14px */
  padding: 8px;                        /* TIKR `p-2` */
  border-radius: var(--v2-plan-radius) var(--v2-plan-radius) 0 0;   /* rounded-t */
  background: var(--v2-plan-hi-soft);
  color: var(--v2-plan-hi);
  font-size: 14px;                     /* TIKR `text-sm` */
  font-weight: 700;                    /* TIKR `font-[700]` */
  line-height: 1.2;
  text-align: center;
}

/*: ★ 卡内块（名字 + 副标题）—— TIKR 原串（`grep` 原文）：
     `mt-8 flex flex-col justify-center items-center gap-2 p-5`
   ⇒ **margin-top:32px · gap:8px · padding:20px**
   ## 🎯 卡顶 → 名字墨迹 = 73px 的分解（实测 TIKR）
     卡边框 1 + 卡 padding 20 + head margin-top 32 + head padding 20 = 73 ✓
   ## 🔴 两个曾经的错（2026-09-28 修）
     ① `margin-top` 误写 `calc(32px + 33px)` = 65px —— **`+33` 是凭空造的**（TIKR 无）
     ② head 的 **`padding: 20px`（`p-5`）整个漏抄**
     两错叠加 ⇒ 实际 `20 + 65 + 0 = 85px`，**比 TIKR 的 73px 更远**
     （用户原话「你越改离框越远」）
   ⚠️ 角标是 absolute **不占流** ⇒ 三卡**不需要**额外让出角标高。 */
.v2-plan-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;                            /* TIKR `gap-2` */
  width: 100%;
  margin-top: 22px;                    /* ★ 2026-09-28 v5：用户裁定 73px → **63px**
                                           （TIKR 是 `mt-8`=32px ⇒ 这处是**有意偏离 TIKR**）
                                           ⚠️ 只动这一处：卡 padding（p-5）与 head padding（p-5）
                                              都会影响左右边距/可用宽度，不能为凑高度去改。 */
  padding: 20px;                       /* ★ TIKR `p-5`（旧版整个漏抄） */
}

/*: 档位名 —— TIKR `text-2xl md:text-3xl font-[700] text-stone-700`
   ⇒ 小屏 24px / **md+ 30px** · 700 · `#44403c`
   ## 🔴 必须用 `.pub-shell .v2-plan-name`（特异性 0,2,0）
     因为上面有 `.pub-shell h2 { margin: 2em 0 .6em; font-size: 1.25em }`（0,1,1）
     —— 那是给**条款页章节标题**用的，但档位名也是 `.pub-shell` 里的 `<h2>`
     ⇒ **会被误伤**：实测 `margin: 40px` + `font-size: 20px`（把 30px 压回 20px）。
     提高特异性后条款页不受影响，定价卡也不被误伤。 */
.pub-shell .v2-plan-name {
  margin: 0;
  font-size: 24px;
  line-height: 30px;
  font-weight: 700;
  color: var(--v2-plan-ink);
}
@media (min-width: 768px) {                    /* TIKR `md:` 断点 */
  .pub-shell .v2-plan-name { font-size: 30px; line-height: 36px; }
}
/*: 副标题 —— TIKR `text-base md:text-sm font-[500] text-stone-600 min-h-[60px]`
   ⇒ **小屏 16px / md+ 14px**（桌面反而更小，照抄）· 500 · `#57534e` · min-h:60px
   ## 🔴 同 `.v2-plan-name` 的特异性问题
     上面有 `.pub-shell h2 ~ p { max-width: 76ch; line-height: 1.7; margin: .7em 0 }`（0,1,2）
     ⇒ **也会误伤副标题**：实测 `margin: 9.8px`、`line-height: 23.8px`、`max-width: 686px`。
     提高特异性到 `.pub-shell .v2-plan-tagline`（0,2,0）后修复。 */
.pub-shell .v2-plan-tagline {
  margin: 0;
  min-height: 60px;                    /* TIKR `min-h-[60px]`：三卡对齐的关键 */
  font-size: 16px;
  line-height: 20px;                   /* ★ TIKR `text-sm` 的行高 */
  font-weight: 500;
  max-width: none;                     /* ★ 覆盖 `.pub-shell h2 ~ p` 的 76ch */
  color: var(--v2-plan-ink-sub);
}

/* ★ 2026-09-30：`.pub-shell h2 ~ p`（0,1,2）给 `.pub-note` 加了 `max-width: 76ch`
   ⇒ 实测压到 686px ⇒ 英文免责句（~105 字符）被折成两行
   ⚠️ 同一坑 `v2.css:939` 已记载（`.v2-plan-tagline`）⇒ 同一种解法：覆盖 none */
.pub-shell .pub-note {
  max-width: none;
}

/* ★ 2026-09-30 用户裁定（订阅框的两处问题，**都在这里覆盖回来**）
   ## 背景：`.pub-shell *` 系列规则（v2.css）在 public.css **之后**加载
      ⇒ 权重相同或更高时，**v2.css 赢** ⇒ 把 `.pub-subscribe` 的设计值压掉：
      ① `.pub-shell h2`（0,1,1）覆盖 `.pub-subscribe h2 { margin: 0 0 10px }`
         ⇒ 标题 margin-top 变成 **2em（≈34px）**
         ⇒ 框内"**上面空得多**"（28px padding + 34px margin = 62px）
      ② `.pub-shell h2 ~ p`（0,1,2）覆盖 `.pub-subscribe p` 的
         `margin: 0 auto 20px` + `max-width: 720px`
         ⇒ 两行小字**贴左**（auto 没了）且**变窄**（686px）
   ## 修法：把这三条值覆盖回来（只动 v2.css，**不碰 public.css**）
      ⇒ 标题上方回到 28px（与下方对称）· 小字 720px 居中
   -------------------------------------------------------------------- */
.pub-shell .pub-subscribe h2 {
  margin: 0 0 10px;
}
.pub-shell .pub-subscribe p {
  margin: 0 auto 20px;
  max-width: 720px;
}
@media (min-width: 768px) {
  .pub-shell .v2-plan-tagline { font-size: 14px; }
}

/*: ⚠️ 2026-09-28 v3 删除：此处原有**重复的第二份**
   `.v2-plan-flag` / `.v2-plan-name` / `.v2-plan-tagline`（旧版残留）。
   它有三宗罪（一次删净才全部解决）：
     ① `.v2-plan-flag { align-self: stretch; margin: calc(1px - 20px) … }`
        ⇒ 角标**重新参与 flex 流** ⇒ 高亮卡被推下 `gap:32px`（第 2 题：角标漂移）
     ② 第二份 `.v2-plan-name` **没有 `@media` 断点** ⇒ 盖掉第一份的
        `@media (min-width:768px){ font-size:30px }` ⇒ 字号停在 20px（第 3 题）
     ③ 第二份 `.v2-plan-tagline` 同样缺断点
   现在全页**只有一份**定义，都在上面 §4.3 里。 */

/* --- 4.4 卡内市场分段按钮（★ 2026-09-28 v2：**每卡独立**，不再联动）-------
   ## 🔴 与上一版的根本区别
     上一版真 radio 放在 `.v2-plan-grid` **之前**（全页唯一）⇒ 点 Pro 的
     「4 markets」，Premium 也跟着变 —— **用户裁定这是错的**：卡片必须能各选各的。
   ## 现在：每卡自己的 radio 组（`name="v2mkt-pro"` / `"v2mkt-prem"`）
     · radio 放在**卡片内部**、分段按钮 label **之前**
     · 价格由**卡内**兄弟选择器驱动（见 §4.5）
     · 周期（月/年）仍是**全页唯一** —— 整页概念，TIKR 也是全页一个开关
   ## 零 JS、不用 `:has()`：全部 `~`（后继兄弟）+ 后代选择器 */
.v2-plan-mkt {
  display: flex;                       /* 两等分横排 */
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
}
/*: ★ Free 卡的**静态市场标签**（第二轮问题 2，用户裁定：与 Pro/Premium 同高）
   ## 与真市场行的区别（三处）
     ① 只有一格 ⇒ `flex: 0 0 auto`（不参与两等分），宽度由文字撑开
     ② 四角全圆（`border-radius: 6px`）—— 它不是分段按钮的一半
     ③ 居中（`justify-content: center`）
   ## 高度
     仍由 `.v2-plan-mkt-opt` 的 `min-height: var(--v2-plan-ctl-h)`（40px）保证
     ⇒ **与 Pro/Premium 的市场行严格同高** ⇒ 三卡 head 等高
   ## 不可点
     它是 `<span>` 不是 `<label>`（无 `data-target`）⇒ JS 不绑事件 ⇒ 天然不可点。
     ⚠️ 复用 `.is-on` 的选中态样式（深藏青底白字），与 Pro 的「已选」视觉一致。 */
.v2-plan-mkt--fixed {
  justify-content: center;
}
.v2-plan-mkt--fixed .v2-plan-mkt-opt {
  flex: 0 0 auto;                      /* ★ 不撑满（与 Pro 的两等分相反） */
  padding: 4px 12px;
  border-radius: 6px;                  /* ★ 四角全圆（单格，不是分段按钮） */
  cursor: default;                     /* ★ 不可点 */
}
/*: 🔴 必须**再次**覆盖分段规则 `:first-of-type` / `:last-of-type`（见 §4.4 下方）
   ## 为什么（实测根因）
     `.v2-plan-mkt-opt:first-of-type { border-radius: 6px 0 0 6px }` 与
     `.v2-plan-mkt-opt:last-of-type { border-radius: 0 6px 6px 0; border-left: 0 }`
     的特异性都是 **(0,2,0)**，与上面 `.v2-plan-mkt--fixed .v2-plan-mkt-opt` **相同**
     ⇒ CSS 层叠**后者（源码顺序更靠后的分段规则）胜出** ⇒ Free 的框变成
       **左边两角尖、右边两角圆**、且 `border-left-width: 0`（左边框被去掉）。
   ## 修法：把特异性提到 (0,3,1) ⇒ 稳压分段规则
     ① 四角 `border-radius: 6px`
     ② 补回左边框（单格不该有「共用中缝」的写法） */
.v2-plan-mkt--fixed .v2-plan-mkt-opt:first-of-type,
.v2-plan-mkt--fixed .v2-plan-mkt-opt:last-of-type {
  border-radius: 6px;
  border-left: 1px solid var(--v2-plan-line);
}
/*: 真 radio：视觉隐藏但可聚焦。
   ⚠️ 2026-09-28 v2：市场 radio 已**提到卡片直属**（见 §4.5 结构说明），
      所以这里同时覆盖「卡内直属」与「旧版放在 `.v2-plan-mkt` 内」两种位置。 */
.v2-plan-mkt input,
.v2-plan-card > input.v2-plan-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.v2-plan-mkt-opt {
  flex: 1 1 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: var(--v2-plan-ctl-h);    /* 40px（与 TIKR 按钮 h-10 同高） */
  margin: 0;                           /* ★ `<label>` 默认 `margin: 10px 0 4px` ⇒ 会顶 14px */
  padding: 4px 10px;
  border: 1px solid var(--v2-plan-line);
  background: #fff;
  color: var(--v2-plan-ink);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.3;
  cursor: pointer;
  user-select: none;
  transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
/*: 分段按钮语言：左半只圆左角、右半只圆右角，中间**共用一条边** */
.v2-plan-mkt-opt:first-of-type { border-radius: 6px 0 0 6px; }
.v2-plan-mkt-opt:last-of-type {
  border-radius: 0 6px 6px 0;
  border-left: 0;                      /* 去掉双线 */
}
.v2-plan-mkt-opt:hover { background: #f5f5f4; }

/*: ★ 选中态 —— 由 **`.is-on`** 驱动（★ 第 8 题 方案 C，照 TIKR 的纯 JS 机制）
   🔴 为什么不再用 `:checked ~`：
     ① 会让浏览器为「显示获得焦点的 radio」而**滚动页面**（用户报的「跳回顶端」）
     ② TIKR 全页**零 `:checked`、零 `:has()`** ⇒ 纯 JS 才与它同构
   ⚠️ `.is-on` 由页内 JS 维护（点击/键盘 + 首屏同步），见模板里的市场切换脚本。 */
.v2-plan-mkt-opt.is-on {
  background: var(--v2-plan-solid);
  border-color: var(--v2-plan-solid);
  color: #fff;
  font-weight: 600;
}
.v2-plan-mkt-opt:focus-visible {
  outline: 2px solid var(--v2-plan-hi);
  outline-offset: 2px;
}

/*: ★ 2026-09-28 v3 删除：`.v2-plan-mkt--locked`（Free 卡的**假市场行**）。
   它是上一版为了「凑三卡等高」造的**纯视觉假控件** —— TIKR 卡片里**根本没有市场行**。
   现在 Free 卡不渲染市场行（第 4 题 B2），这 4 条规则随之作废。
   ⚠️ 三卡对齐由「每一行都对齐」的结构保证（见 §第 4 题 A1/A2/A3），不靠假控件凑高。 */

/* --- 4.5 价格（★ 2026-09-28 v2：卡内独立市场 × 全页周期）-------------------
   TIKR 实测：价格块 `pb-[19px] flex flex-col justify-center items-center gap-0`
              价格 `font-[700] text-stone-700 text-4xl` = **36px / 700 / #44403c**
              单位 `font-[400] text-stone-700 text-sm` = **14px / 400**
   ⚠️ 价格与单位在 TIKR 里是**同一个 flex 块、gap:0** ⇒ 我们用一个 `.v2-plan-pricebox`
      包住两者，避免被卡片外层 `gap:32px` 拆开（上一版用负 margin 硬凑，很脆）。
   ★ 2026-09-28 v3：**按钮也在这个块内部**（照 TIKR，见 §第 4 题 A1）
   ⚠️ `pb-[19px]`：TIKR **只有付费卡有**，Free 卡没有 ⇒ 用 `--paid` 修饰类区分。 */
.v2-plan-pricebox {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;                          /* TIKR `gap-0` */
  width: 100%;
}
.v2-plan-card--pro > .v2-plan-pricebox,
.v2-plan-card--prem > .v2-plan-pricebox {
  padding-bottom: 19px;            /* TIKR `pb-[19px]`（仅付费卡；Free 卡无此类） */
}
.v2-plan-price {
  display: none;                   /* ★ 基类隐藏，只有状态矩阵命中才 block */
  margin: 0;
  font-size: 36px;                 /* TIKR `text-4xl` */
  line-height: 40px;               /* ★ 实测 TIKR 价格盒高 40px（旧版 1.1 ⇒ 39.6，差 0.4） */
  font-weight: 700;                /* TIKR `font-[700]` */
  letter-spacing: -0.01em;
  color: var(--v2-plan-ink);       /* TIKR stone-700 = #44403c */
}
/*: 单位行（USD / month）—— TIKR 14px / 400，紧贴价格（同块 gap:0） */
.v2-plan-per {
  display: none;
  margin: 0;
  font-size: 14px;                 /* TIKR `text-sm` */
  line-height: 20px;               /* ★ 实测 TIKR 单位盒高 20px（旧版未写 ⇒ normal 22.86，差 2.86） */
  font-weight: 400;                /* TIKR `font-[400]` */
  color: var(--v2-plan-ink);       /* TIKR stone-700（与价格同色，非次级灰） */
}

/*: ★ 删除线原价 —— 年付态下显示「原月付价」（照 TIKR `<span class="line-through">`）
   TIKR 原串：`<span class="line-through"> $54.95</span> USD/month`
   ⇒ 大字是**年付折月价**，小字是**被划掉的月付原价**，顾客一眼看到省了多少。 */
.v2-plan-was {
  text-decoration: line-through;
}

/*: ⚠️ 2026-09-28 v4 删除：`.v2-plan-save`（"Save 20% compared with monthly billing."）
   用户裁定删（第二轮 A 项）：TIKR 四卡都没有这一行；
   且**默认年付**后它会永远显示、撑高卡片；删除线已经表达「省了多少」，功能重复。 */

/*: ★★ 状态矩阵（4 组价格 × 2 档，**每组恰好显示 1 个**）
   ## 两个正交维度
     · **市场**（1 / 4）—— **卡内** radio（`#v2mkt-pro-*` / `#v2mkt-prem-*`）
     · **周期**（月 / 年）—— **全页** radio（`#v2cyc-*`，在 grid 之前）

   ## 🔴🔴 关键结构约束（本版经"真实选择器引擎"验证，非目测）
     `~` 只能选**同父的后继兄弟**，跨不过父块边界。
     若把市场 radio 放进 `.v2-plan-mkt`、价格放进 `.v2-plan-pricebox`：
         <div class="v2-plan-card--pro">
           <div class="v2-plan-mkt"><input id="v2mkt-pro-1"></div>      ← radio 在此
           <div class="v2-plan-pricebox"><p class="…-1 …-m">$19.99</p></div>
         </div>
     ⇒ `#v2mkt-pro-1 ~ .v2-plan-pricebox` 与 `#v2mkt-pro-1 ~ …p` **都不成立**
       （radio 的兄弟只有 `.v2-plan-mkt` 内的元素）。
     ★⇒ **解决**：把市场 radio 提到**卡片直属**（与 `.v2-plan-mkt`
        `.v2-plan-pricebox` 平级），**label 仍留在 `.v2-plan-mkt` 内**：
            <div class="v2-plan-card--pro">
              <input id="v2mkt-pro-1"><input id="v2mkt-pro-4">   ← 卡片直属
              <div class="v2-plan-mkt"><label for="v2mkt-pro-1">…</label></div>
              <div class="v2-plan-pricebox"><p class="…-1 …-m">$19.99</p></div>
            </div>
        ⇒ `#v2mkt-pro-1:checked ~ .v2-plan-pricebox .v2-plan-price-1.v2-plan-price-m`
          **成立**（pricebox 是 radio 的后继兄弟；价格是它后代）。
        ⇒ label 高亮同理：`#v2mkt-pro-1:checked ~ .v2-plan-mkt [for=…]` **成立**。
     ⚠️ 模板顺序铁律：**radio 必须在 `.v2-plan-mkt` 与 `.v2-plan-pricebox` 之前**。 */
.v2-plan-price, .v2-plan-per { display: none; }

/* ---- Pro 卡 ---- */
/* 月 × 1 */
.pub-shell[data-cycle="monthly"] .v2-plan-card--pro[data-mkt="1"]
  .v2-plan-price-1.v2-plan-price-m,
.pub-shell[data-cycle="monthly"] .v2-plan-card--pro[data-mkt="1"]
  .v2-plan-per-1.v2-plan-per-m { display: block; }
/* 月 × 4 */
.pub-shell[data-cycle="monthly"] .v2-plan-card--pro[data-mkt="2"]
  .v2-plan-price-4.v2-plan-price-m,
.pub-shell[data-cycle="monthly"] .v2-plan-card--pro[data-mkt="2"]
  .v2-plan-per-4.v2-plan-per-m { display: block; }
/* 年 × 1 */
.pub-shell[data-cycle="annual"] .v2-plan-card--pro[data-mkt="1"]
  .v2-plan-price-1.v2-plan-price-a,
.pub-shell[data-cycle="annual"] .v2-plan-card--pro[data-mkt="1"]
  .v2-plan-per-1.v2-plan-per-a { display: block; }
/* 年 × 4 */
.pub-shell[data-cycle="annual"] .v2-plan-card--pro[data-mkt="2"]
  .v2-plan-price-4.v2-plan-price-a,
.pub-shell[data-cycle="annual"] .v2-plan-card--pro[data-mkt="2"]
  .v2-plan-per-4.v2-plan-per-a { display: block; }

/* ---- Premium 卡 ---- */
.pub-shell[data-cycle="monthly"] .v2-plan-card--prem[data-mkt="1"]
  .v2-plan-price-1.v2-plan-price-m,
.pub-shell[data-cycle="monthly"] .v2-plan-card--prem[data-mkt="1"]
  .v2-plan-per-1.v2-plan-per-m { display: block; }
.pub-shell[data-cycle="monthly"] .v2-plan-card--prem[data-mkt="2"]
  .v2-plan-price-4.v2-plan-price-m,
.pub-shell[data-cycle="monthly"] .v2-plan-card--prem[data-mkt="2"]
  .v2-plan-per-4.v2-plan-per-m { display: block; }
.pub-shell[data-cycle="annual"] .v2-plan-card--prem[data-mkt="1"]
  .v2-plan-price-1.v2-plan-price-a,
.pub-shell[data-cycle="annual"] .v2-plan-card--prem[data-mkt="1"]
  .v2-plan-per-1.v2-plan-per-a { display: block; }
.pub-shell[data-cycle="annual"] .v2-plan-card--prem[data-mkt="2"]
  .v2-plan-price-4.v2-plan-price-a,
.pub-shell[data-cycle="annual"] .v2-plan-card--prem[data-mkt="2"]
  .v2-plan-per-4.v2-plan-per-a { display: block; }

/*: Free 卡：$0 永远显示（无市场/周期维度） */
.v2-plan-price--free, .v2-plan-per--free { display: block; }

/*: 年付省幅说明 `.v2-plan-save` 已删除（见上方 v4 说明），规则一并移除。 */

/* --- 4.5b 周期切换 = **Switch**（★ 2026-09-28 由分段按钮改为 TIKR 的开关）---
   TIKR 实测：轨道 40×24 全圆角；滑块 20×20 白，选中 `translateX(20px)`；
   左 label「Monthly」/ 右 label「Annual」，未选那个 `opacity-.5`，选中 `font-700`。
   ## 零 JS 实现
     轨道由 `#v2cyc-*:checked ~` 着色，滑块靠 `:checked ~ … ::after` 位移。
     ⚠️ **必须不用 `:has()`**（本仓既有约束，见 §3.x）。
   ## 结构约定（模板必须照做，否则静默失效）
     `<label for="v2cyc-monthly">Monthly</label>`
     `<span class="v2-plan-switch"></span>`      ← 靠 `~` 着色/位移
     `<label for="v2cyc-annual">Annual …</label>`
     顺序：两个 radio → `.v2-plan-cycle`（内含上述三件） */
.v2-plan-cycle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;                    /* ★ 实测 TIKR `gap-2` = 8px（旧版 12px 是错的） */
  margin: 1.5em 0 0;
  cursor: pointer;             /* ★ TIKR 父 div 确有 cursor-pointer */
}
.v2-plan-cycle-opt {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;             /* ★ 实测 14px（旧版 15px 错） */
  line-height: 14px;           /* ★ 实测 TIKR `leading-none`（旧版 1.3 错） */
  font-weight: 500;            /* 未选中 500 */
  color: rgb(2, 8, 23);
  cursor: pointer;
  user-select: none;
  background: none;
  border: 0;
  padding: 0;
  opacity: .5;                 /* ★ TIKR 未选态 `opacity-50` */
  transition: opacity .12s ease, color .12s ease;
}
.v2-plan-cycle-opt:hover { opacity: .8; }
/*: 选中那个：700 + stone-800 + 不透明（照 TIKR 换 class 后的结果）
   ⚠️ 两段文字 `for` 都指向同一个 button ⇒ 用 `:first-of-type / :last-of-type` 区分左右。
   🔴 `#v2cyc` 在 `.v2-plan-cycle` **内部** ⇒ 不能用 `~ .v2-plan-cycle`（跨父块，静默失效）。
      改用 shell 上的 `[data-cycle]` 从外往下选。 */
.pub-shell[data-cycle="monthly"] .v2-plan-cycle .v2-plan-cycle-opt:first-of-type,
.pub-shell[data-cycle="annual"]  .v2-plan-cycle .v2-plan-cycle-opt:last-of-type {
  font-weight: 700;
  color: rgb(41, 37, 36);      /* TIKR stone-800 */
  opacity: 1;
}

/*: 开关本体：轨道 44×24（★ 实测 TIKR `h-6 w-11` = 24×44 / `border-2`） */
.v2-plan-switch {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  width: 44px;                 /* TIKR `w-11`（旧版 40px 偏窄） */
  height: 24px;                /* TIKR `h-6` */
  padding: 0;
  border: 2px solid transparent;   /* TIKR `border-2 border-transparent` */
  border-radius: 9999px;       /* TIKR `rounded-full` */
  background: #d6d3d1;         /* 关：TIKR `data-[state=unchecked]:bg-input` 灰 */
  cursor: pointer;             /* TIKR `cursor-pointer` */
  transition: background-color .16s ease;
}
/*: 选中 ⇒ 轨道变品牌青绿（TIKR 是自家橙 `data-[state=checked]:bg-primary`） */
#v2cyc[aria-checked="true"] { background: var(--v2-plan-hi); }

/*: 滑块 20×20 白（★ 实测 TIKR `h-5 w-5` + `shadow-lg`） */
.v2-plan-switch-knob {
  display: block;
  width: 20px;
  height: 20px;
  border-radius: 9999px;
  background: #fff;
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -4px rgba(0, 0, 0, .1);
  transition: transform .16s ease;
}
/*: 年付选中 ⇒ 滑块右移 20px（TIKR `data-[state=checked]:translate-x-5`） */
#v2cyc[aria-checked="true"] .v2-plan-switch-knob { transform: translateX(20px); }

/*: 省幅徽标（跟在 Annual 选项后）—— 保留我方「Save 20%」 */
.v2-plan-cycle-badge {
  margin-left: 2px;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--v2-plan-hi-soft);
  color: var(--v2-plan-hi);
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}

/* --- 4.6 卡片小字（TIKR：12px / 500 / #44403c / margin-top 12px） ---
   ⚠️ 2026-09-28：原 `.v2-plan-soon`（4 市场「即将推出」免责栏）**已删除**
      —— 用户裁定「卡内删干净，免责挪到页脚法律小字区」。 */
.v2-plan-note {
  margin: 12px 0 0;                /* ★ TIKR `mt-3` = 12px */
  font-size: 12px;                 /* ★ TIKR `text-xs` */
  font-weight: 500;                /* TIKR `font-[500]` */
  line-height: 1.5;
  color: var(--v2-plan-ink);       /* TIKR stone-700 */
}

/* --- 4.7 功能清单 + ★ tick mark（2026-09-28 v2，照 TIKR 逐字实测）----------
   ## TIKR 原串（从线上 DOM 抓取，非目测）
     `<ul class="flex flex-col gap-4">`
       `<li class="flex gap-2 items-center">`
         `<svg width="18" height="18" viewBox="0 0 48 48" class="text-orange-300">`
           `… 实心圆 48×48 + 白色对勾 stroke-width:4 …`
         `<p class="text-base font-[400] text-stone-700">…</p>`
   ⇒ 列表 `gap:16px` · 行 `flex` + `gap:8px` + 垂直居中
      图标 **18×18** · 文字 **16px / 400 / #44403c**
   ## ★ 颜色：我方换品牌青绿
     TIKR 用 `text-orange-300` = `rgb(253 186 116)` = `#FDBA74`（TIKR 自家品牌橙）。
     **用户裁定「乙」：几何/尺寸 100% 照抄，色相换成我方商标 V 字右臂青绿
     `#0EB2AA`（`--v2-plan-hi`）** ⇒ 不引入外来色。
   ## 图标实现在模板内联（不引 iconify 依赖，守 .clinerules「不引外部库」） */

/*: ★ 2026-09-28 v3：清单块外包一层 `div.w-full`（照 TIKR）
   TIKR 结构：`<div class="mt-2 w-full"><hr…><span>标题</span><div class="py-5"><ul…>`
   ⚠️ 付费卡带 `mt-2`（8px），Free 卡**没有**。 */
.v2-plan-featwrap {
  width: 100%;
}
/*: ★ 2026-09-28 v4（第二轮问题 1）：Free 的清单块上移 3px ⇒ 与付费卡清单**严格同行**
   ## 为什么需要（实测）
     Free 价格块 = 142px（多一行 `note`：mt12 + h18）
     付费价格块 = 131px（有 `pb-[19px]`）
     ⇒ 差 11px；付费清单块有 `mt-2`（8px）吸收 8px ⇒ **净差 3px**
   ## 为什么用负 3px（三个约束下的唯一解）
     · 用户要求「0px 对齐」
     · 用户要求「保留 note」（⇒ 不能删 note 来配平）
     · 用户要求「不要动 pro premium 卡片」（⇒ 不能改付费卡的 pb）
     ⇒ **只能让 Free 的清单块自己上移 3px**
   ## 副作用
     Free 的 note 与清单块之间视觉间距从 32px 变 29px（其余一律不动）
   ⚠️ TIKR 自己也没做到 0px（它 Free/付费的清单块是 816/815，差 1px）；
      我们是**主动**做到 0px（用户已授权偏离 TIKR 微调）。 */
.v2-plan-featwrap:not(.v2-plan-featwrap--paid) {
  margin-top: -3px;
}
.v2-plan-featwrap--paid {
  margin-top: 8px;                 /* TIKR `mt-2` —— 付费卡的原值，**不动** */
}

/*: ★ 分隔线 —— **独立 `<hr>`**（照 TIKR `horizontal-rule mb-4 ml-[-20px]`）
   TIKR 实测：`w=285.5`（= 卡的 padding box 宽）、`ml=-20px`、`mb=16px`、`bw=1px`
   ## 机制
     `<hr>` 的 containing block 是本 div（宽 = 卡 padding box 宽 245.5）
     CSS `width: calc(100% + 40px)` ⇒ 245.5 + 40 = 285.5 ✓
     HTML `margin-left: -20px` ⇒ 从 20 左移到 0（相对本 div）⇒ 横跨到**边框内沿**
   ⚠️ 旧版把 `border-top` 加在 `<ul>` 上 ⇒ 线只有内容宽，左右各短 20px（错位根因）。 */
.v2-plan-rule {
  width: calc(100% + 40px);        /* ★ TIKR `horizontal-rule` */
  margin: 0 0 16px -20px;          /* TIKR `mb-4 ml-[-20px]` */
  border: 0;
  border-top: 1px solid #e2e8f0;   /* TIKR 实测普通卡线色 #E2E8F0 */
}
/*: 高亮卡那条线换品牌青绿（TIKR `is-most-popular` = 自家青绿 #699C97） */
.v2-plan-rule--hi {
  border-top-color: var(--v2-plan-hi);
}

.v2-plan-feats {
  display: flex;
  flex-direction: column;
  gap: 16px;                       /* TIKR `gap-4` */
  width: 100%;
  margin: 0;
  padding: 0;                      /* ★ 分隔线的间距已由 .v2-plan-rule 的 mb 负责 */
  list-style: none;
  text-align: left;                /* ★ 勾 + 文字左对齐（TIKR 是 `flex` 横排，非居中） */
}
.v2-plan-feat {
  display: flex;
  gap: 8px;                        /* TIKR `gap-2` */
  align-items: center;             /* TIKR `items-center` */
  margin: 0;
  font-size: 16px;                 /* TIKR `text-base` —— 🔴 上一版写成 14px，偏小 */
  font-weight: 400;                /* TIKR `font-[400]` —— 上一版 500，偏重 */
  line-height: 1.5;
  color: var(--v2-plan-ink);       /* TIKR stone-700 */
}
/*: ★ tick mark 容器 —— 18×18，颜色 = 品牌青绿 */
.v2-plan-tick {
  flex: 0 0 auto;
  display: inline-flex;
  width: 18px;                     /* TIKR `width="18"` */
  height: 18px;
  color: var(--v2-plan-hi);        /* ★ 乙：TIKR 的 #FDBA74 换品牌青绿 */
}
.v2-plan-tick svg { display: block; width: 18px; height: 18px; }
/*: 不含项（`data-off`）：整行压灰 + 勾也压灰 */
.v2-plan-feat[data-off] { color: #a8a29e; }        /* TIKR stone-400 */
.v2-plan-feat[data-off] .v2-plan-tick { color: #d6d3d1; }  /* stone-300 */

/* --- 4.8 卡片按钮（★ 2026-09-28 v3：逐字实测 + hover 实测）----------------
   TIKR 原串（MHTML 逐字）：
     普通卡：`border border-input bg-background hover:bg-accent text-accent-foreground
              visited:text-accent-foreground hover:text-accent-foreground h-10 px-4 py-2 mt-3`
     高亮卡：`h-10 px-4 py-2 mt-3 bg-[#262626] text-white hover:bg-[#333333]`
   ## 实测 computed（viewport 1440）
     h 40 · pad 8px 16px · mt 12px · radius 6px · fs 14px · lh 20px · fw 500
     普通卡：bg #fff · border 1px #E2E8F0 · color #0F172A
     高亮卡：bg #262626 · border 0 · color #fff
   ## ★ hover 实测（hover() 后读 computed，不是读 CSS 源码）
     普通卡：#fff → #F1F5F9        （旧版误写 #f5f5f4，偏黄暖，是错的）
     高亮卡：#262626 → #333333     （文字保持白）
   ## 位置：按钮在**价格块内部**（TIKR 的 `pb-[19px]` 那个 div）
     ⇒ 三卡按钮必然同 y（由「价格块 + mt-3」决定，不受卡片 gap 累积影响，见 §第 4 题 A1） */
.v2-plan-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: center;                  /* 不被卡片 `align-items:center` 拉伸通栏 */
  box-sizing: border-box;
  height: 40px;                        /* TIKR `h-10`（用 height，不用 min-height） */
  margin: 12px 0 0;                    /* TIKR `mt-3` = 12px（在价格块内部） */
  padding: 8px 16px;                   /* TIKR `px-4 py-2` */
  border: 1px solid #e2e8f0;           /* TIKR `border-input`（实测 #E2E8F0，旧版误用 --v2-plan-line） */
  border-radius: 6px;                  /* TIKR `rounded-md` */
  background: #fff;                    /* TIKR `bg-background` */
  color: #0f172a;                      /* TIKR `text-accent-foreground`（实测 #0F172A） */
  font-size: 14px;                     /* TIKR `text-sm` */
  font-weight: 500;                    /* TIKR `font-medium` */
  line-height: 20px;                   /* ★ TIKR `text-sm` 的行高（旧版 1.3 错） */
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color .15s, color .15s;   /* TIKR `transition-colors` */
}
/*: 🔴 必须用 `.pub-body a.v2-plan-cta` 把颜色抢回来
   `public.css:143` 有 `.pub-body a { color: var(--pub-accent) }`（品牌蓝 #0076FF，特异性 0,1,1）
   ⇒ 会盖掉上面 `.v2-plan-cta` 的 `color`（0,1,0）⇒ 实测按钮文字变成**蓝色**。
   ⚠️ 同一个坑 `public.css:283` 的注释里已经记载过（`a.pub-cta` 遇到过）
   —— 我这次又踩了，写在这里防止再犯。 */
.pub-body a.v2-plan-cta,
.pub-body a.v2-plan-cta:link,
.pub-body a.v2-plan-cta:visited {
  color: #0f172a;                      /* TIKR `text-accent-foreground` */
}
/*: ★ hover 实测：TIKR `bg-accent` = `#F1F5F9`（旧版误写 `#f5f5f4` stone-100，偏黄暖） */
.pub-body a.v2-plan-cta:hover { background: #f1f5f9; color: #0f172a; }
/*: 高亮卡按钮 → 深底白字、**无边框**（TIKR `bg-[#262626] hover:bg-[#333333]`，实测 border 0）
   ⇒ 色相换品牌藏青 `--v2-plan-solid` / hover `--v2-plan-solid-hi`（用户已定的唯一例外） */
.v2-plan-cta--solid {
  border: 0;                           /* ★ TIKR 高亮卡实测 border: 0px */
  background: var(--v2-plan-solid);
  padding: 9px 17px;                   /* 补偿去边框的 1px（保持 40px 总高与视觉留白一致） */
  color: #fff;
}
.pub-body a.v2-plan-cta--solid,
.pub-body a.v2-plan-cta--solid:link,
.pub-body a.v2-plan-cta--solid:visited { color: #fff; }
.pub-body a.v2-plan-cta--solid:hover {
  background: var(--v2-plan-solid-hi);
  color: #fff;
}
  border-color: var(--v2-plan-solid-hi);
}

/* --- 4.9 （已删除）页脚法律小字 `.v2-plan-legalbar` ---------------------------
   🔴 2026-09-28 v3：**整块删除**（用户裁定第 6 题）。
   原内容两块：① 卡片下方四段（USD/税 · 自动续订 · email 订阅说明 · 市场覆盖）
              ② FAQ 下方单段（"These plans are described here for information…"）
   ## 删除理由（用户原话）
     「记下第 6 条，把你写的这些垃圾全部拿走」
     「这是以后没问题的时候的页面，不是现在过渡页面，现在我不开放注册！」
   ⇒ 本页按**正式上线的目标态**设计：其中第 3、4 段写的是**临时运营状态**，
     上线时全部作废；留着既破坏与 TIKR 的像素级对齐，内容也会变成错的。
   ⚠️ 模板 `subscription.html` 里对应两块已同步删除，两边必须一起动。 */

/* --- 4.10 FAQ 折叠（原生 `<details>`，零 JS） --- */
.v2-faq { margin: 1em 0 0; }
.v2-faq-item { border-bottom: 1px solid #e5e7eb; }
.v2-faq-q {
  display: block;
  position: relative;
  padding: 14px 28px 14px 0;
  font-size: 1em;
  font-weight: 600;
  line-height: 1.4;
  cursor: pointer;
  list-style: none;            /* ★ 去掉默认三角（各浏览器不一致） */
}
.v2-faq-q::-webkit-details-marker { display: none; }
.v2-faq-q::after {
  content: "+";
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 1.25em;
  font-weight: 400;
  color: #6b7280;
}
.v2-faq-item[open] > .v2-faq-q::after { content: "\2212"; }   /* − */
.v2-faq-item[open] > .v2-faq-q { color: #111827; }
.v2-faq-a {
  padding: 0 40px 16px 0;
  font-size: 0.9375em;
  line-height: 1.7;
  color: #374151;
}
.v2-faq-a p { margin: 0 0 0.75em; }
.v2-faq-a p:last-child { margin-bottom: 0; }
.v2-faq-a ul { margin: 0.5em 0 0.75em; padding-left: 1.3em; }
.v2-faq-a li { margin: 0.3em 0; }

/* --- 4.10b 页头（★ 2026-09-28 新增，照 TIKR）---------------------------
   TIKR：H1 30px / 48px（lg）· font-800 · `#1c1917` · 居中
         副标题 16px / 18px · `#57534e` · 居中                              */
.v2-plan-hero {
  max-width: 760px;
  margin: 0 auto 1em;
  text-align: center;
}
.v2-plan-hero .v2-plan-h1 {
  margin: 0 0 12px;
  font-size: 30px;
  line-height: 1.25;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: #1c1917;                 /* TIKR stone-900 */
}
.v2-plan-hero .v2-plan-lead {
  margin: 0;
  font-size: 16px;
  line-height: 1.5;
  font-weight: 400;
  color: var(--v2-plan-ink-sub);
}
@media (min-width: 1024px) {
  .v2-plan-hero .v2-plan-h1 { font-size: 48px; line-height: 1.15; }
}

/* --- 4.10 小屏堆叠（★ 2026-09-28：TIKR 是 `flex-col md:flex-row`）-------
   ⚠️ 旧版只有 640px 断点且只改字号；现在补 900px（单列）与 640px（收紧内边距）。 */
@media (max-width: 900px) {
  .v2-plan-grid { max-width: 480px; gap: 20px; }
  .v2-plan-hero .v2-plan-h1 { font-size: 28px; }
}
@media (max-width: 640px) {
  :root {
    --v2-plan-pad: 16px;
    --v2-plan-gap: 20px;
  }
  .v2-plan-hero .v2-plan-h1 { font-size: 24px; }
  .v2-plan-price { font-size: 30px; }
  .v2-plan-feats { font-size: 13px; }
  .v2-faq-a { padding-right: 8px; }
}

/* ============================================================================
   §5 手机 / 平板顶栏（★ 2026-09-28，照 stageanalysis.net 实测重写）
   ----------------------------------------------------------------------------

   ## 参照站实测（2026-09-28 从 stageanalysis.net 真机抓取）
     · 断点：**992px**（Bootstrap md）—— 992 以上桌面、991 以下折叠
       ⚠️ **我方用 1024px**（用户 2026-09-28 裁定，覆盖 iPad 横屏）
     · 收起态：`logo`（左） + `MENU ☰`（右，按钮 99.8×48 · padding 9px 10px）
       MENU 文字：14px / 500 / 字距 1.4px / uppercase / `#333`
       打开后：三条杠变 ✕（文字保留）
     · 展开态：**下拉撑开内容**（不是全屏遮罩），nav 高 500px
       菜单项：390×52 · `padding: 16px 32px` · 14px / 500 / 字距 1.5px / uppercase / `#000`
       CTA（Subscribe）：326×41 · `border-radius: 20px` · 实心品牌蓝 `#0076FF`
                         文字 14px / 500 / 字距 1.75px / uppercase
     · 参照站**没有 Login 等价物可用**（我方 v2 无 login 路由 ⇒ 不做该按钮）

   ## ★★ 桌面（≥1241px）零影响（用户 2026-09-28 明确要求）
     本节**全部**规则关在 `@media (max-width: 1295px)` 内 ⇒ 桌面逐属性不变。
     `.v2-nav-open` / `.v2-drop-open` 这两个类**只在 media 内被引用**
     ⇒ 桌面上加不加它们都**没有任何效果**。

   ## 断点为什么是 1230（二分实测，不是拍脑袋）
     我方顶栏**实际占宽 = brand 244.6 + nav 921.2 = 1165.8px**（外加左右内边距）。
     二分扫描实测：**1231px 是「第一个不换行」的宽度**
       · vw=1230 → 顶栏 111px（两行 ❌）
       · vw=1231 → 顶栏 61px（一行 ✓）
     ⇒ 断点取 **1230**：1231 及以上是桌面（放得下），1230 及以下折叠。
     ⚠️ 取 1229 会让 **1230** 落在「desktop 但两行 111px」的坏状态。
     ⚠️ 参考 stageanalysis.net：它断点 **992px**，因为**它菜单短**
        （≈739px vs 我方 921px —— 它是 `Blog`/`Learn`/`Video` 短词，
          我方是 `RELATIVE STRENGTH`/`PATTERN ANALYSIS` 英文全称）
        ⇒ 两者临界点不同，但**逻辑一致：撑不住就折叠**。

   ## 为什么写在 v2.css 而不动 public.css
     `public.css` 是**冻结的旧站共用文件**（preflight 有「旧页文件零改动」门禁）。
     本次改动**全部**落在 v2.css / 新站模板 ⇒ 旧站零影响。
   ============================================================================ */

/* --- 5.1 MENU 按钮（默认隐藏 ⇒ 桌面看不见） --- */
.v2-menu-btn {
  display: none;                       /* ★ 桌面隐藏（= 参照站 `lg:hidden` 的等价物） */
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  height: 48px;                        /* 参照站实测 48 */
  padding: 9px 10px;                   /* 参照站实测 9px 10px */
  margin: 0 0 0 auto;                  /* 贴右 */
  border: 0;
  background: none;
  color: #333;                         /* 参照站实测 rgb(51,51,51) */
  font: inherit;
  cursor: pointer;
}
/*: MENU 文字 —— 参照站实测 14px / 500 / 字距 1.4px / uppercase */
.v2-menu-btn-text {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 1.4px;
  line-height: 20px;
  text-transform: uppercase;
}
/*: 三条杠（纯 CSS 画，不引图标库）—— 22×2，间距 7px */
.v2-menu-btn-bars,
.v2-menu-btn-bars::before,
.v2-menu-btn-bars::after {
  display: block;
  width: 22px;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
  transition: transform .2s ease, background-color .2s ease;
}
.v2-menu-btn-bars { position: relative; }
.v2-menu-btn-bars::before { content: ""; position: absolute; top: -7px; left: 0; }
.v2-menu-btn-bars::after  { content: ""; position: absolute; top: 7px; left: 0; }
/*: 打开态 → 淡出中杠、上下两杠交叉成 ✕ */
.v2-menu-btn[aria-expanded="true"] .v2-menu-btn-bars { background: transparent; }
.v2-menu-btn[aria-expanded="true"] .v2-menu-btn-bars::before {
  transform: translateY(7px) rotate(45deg);
}
.v2-menu-btn[aria-expanded="true"] .v2-menu-btn-bars::after {
  transform: translateY(-7px) rotate(-45deg);
}

/* --- 5.1b ★ 顶栏 CTA 稍作收敛（2026-10-03 用户提出）---------------------
   ## 为什么
     2026-10-02 加了「EN / 中文」语言胶囊（70px）后，菜单需要宽度从 1166
     涨到 1227px ⇒ 折叠断点从 1231 涨到 1295 ⇒ **1280/1366 屏被提前折叠**。
     用户提议「把 LOGIN / SUBSCRIBE 缩小一点」⇒ 实测**能省 57px**。
   ## 数值（实测，见 §5.2 断点注释）
     高 41 → **34**px；左右内边距 24/22 → **16/14**；字号 14 → **12**；
     字距 1.75 → **1.2**
     ⇒ nav 宽 952 → **895**（省 57）⇒ 需要宽度 1197 → **1140**
   ⚠️ 只作用于**顶栏内**的 CTA（`.topbar--public .pub-nav a.pub-cta`），
      页面正文里的 `.pub-cta`（如 RPS 页底部订阅块）**不受影响**。
   ⚠️ 用 (0,4,1) 稳赢 `public.css` 的 `.topbar--public .pub-nav a.pub-cta`(0,3,1)
      —— 注意后者**同特异性但 public.css 先加载**，本例靠"更高特异性"取胜。 */
.topbar--public .pub-nav a.pub-cta {
  height: 34px;
  padding: 0 14px 0 16px;
  font-size: 12px;
  letter-spacing: 1.2px;
}

/* --- 5.2 折叠态（≤1240px）—— ★ 1241 及以上 = desktop ----------------
   ## ★★ 2026-10-03 断点两次调整：1230 ⇒ 1295 ⇒ **1140**
     ## 第一次（1230 ⇒ 1295）：语言胶囊挤宽了菜单
       2026-10-02 加了「EN / 中文」胶囊（70px）⇒ nav 宽 921⇒982
       ⇒ 临界从 1231 涨到 1295，而断点还是 1230
       ⇒ **1231~1290 变成"两行"**（用户实机反馈「菜单变两行」）。
     ## 第二次（1295 ⇒ 1140）：用户提议「缩小 LOGIN / SUBSCRIBE」
       缩小 CTA（见 §5.1b）后实测：
         · nav 宽 982 ⇒ **895**（省 87）
         · 需要宽度 1216 ⇒ **1240**
       二分实测：
         · ≥1241px ⇒ 顶栏 **61px（一行）** ✅  ← 含 1280 / 1366 / 1440 / 1920
         · ≤1240px ⇒ 顶栏 111px（两行）❌ ⇒ 必须折叠
       ⇒ 断点取 **1240**（实测临界 1231 + 9px 余量）：1241 及以上必一行、1240 及以下必汉堡。
     ## ★ 收益
       折叠阈值 1295 ⇒ 1240（**-55px**）⇒
       **1280 / 1366 等常见笔记本屏恢复完整菜单**（此前被提前折叠成汉堡）。
     ## ⚠️ 维护约定
       改**任何**顶栏项（增删菜单项 / 改字号 / 改内边距 / 改 CTA 尺寸）
       ⇒ **必须重测临界并同步本断点**（preflight 有断言守住 1240）。
   ⚠️ 桌面（≥1241px）逐属性不变：本节全部规则都关在 media 内。 */
@media (max-width: 1240px) {
  /* ① 顶栏允许换行 ⇒ 展开的导航能落到第二行（收起时只有 logo+MENU，仍是一行） */
  .topbar--public {
    flex-wrap: wrap;
    align-items: center;
  }
  /* ② MENU 按钮出现 */
  .v2-menu-btn { display: inline-flex; }
  /* ③ logo 收窄（参照站手机 logo 210px vs 桌面 245px；我方等比缩） */
  .topbar--public .brand .pub-logo { height: 34px; }
  .topbar--public .brand { flex: 0 1 auto; min-width: 0; }

  /* ④ 桌面导航：收起；展开时长成**下拉面板**（撑开内容，非全屏遮罩）
        🔴 必须 `flex-basis: 100%`（否则它和 logo/MENU 挤在同一行、被压到只剩 1/3 宽：
           实测 nav 只有 142.9px、子项 x=214.6 ⇒ 菜单跑到右侧一小条）
        ⚠️ `order: 10` 只改顺序，不改行；**必须配合顶栏 `flex-wrap: wrap`** */
  .topbar--public .pub-nav {
    display: none;
    order: 10;                         /* 落到 logo/MENU 那一行**之后** */
    flex: 0 0 100%;                    /* ★ 独占一整行 */
    width: 100%;
    max-width: 100%;
    height: auto;                      /* 覆盖桌面 60px */
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0;
  }
  .topbar--public .pub-nav.v2-nav-open { display: flex; }

  /* ⑤ 菜单项 —— 参照站实测 52px 高 / padding 16px 32px / 14px 500 字距 1.5px */
  .topbar--public .pub-nav > a,
  .topbar--public .pub-nav .pub-nav-label {
    height: auto;
    padding: 16px 32px;
    font-size: 14px;
    letter-spacing: 1.5px;
    line-height: 20px;
  }
  /*: 项间极细分隔线 */
  .topbar--public .pub-nav > a + a,
  .topbar--public .pub-nav .nav-drop + a,
  .topbar--public .pub-nav > a + .nav-drop {
    border-top: 1px solid #f3f4f6;
  }

  /* ⑥ 下拉（市场 / 后台管理 / 用户名）：手机上默认收起，点标签展开
        ⚠️ 桌面靠 `:hover` 显示；触屏没有 hover ⇒ 必须显式控制 */
  .topbar--public .nav-drop-menu {
    position: static;
    display: none;
    min-width: 0;
    padding: 0 0 8px;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }
  .topbar--public .nav-drop.v2-drop-open .nav-drop-menu { display: block; }
  .topbar--public .nav-drop-menu a,
  .topbar--public .nav-drop-menu .is-locked {
    display: block;
    padding: 12px 32px 12px 48px;      /* 缩进一档表示层级 */
    font-size: 14px;
    letter-spacing: 1.5px;
  }
  /*: 去掉标签后的 ▾（手机上标签本身可点，箭头会误导） */
  .topbar--public .pub-nav .pub-nav-label::after { content: ""; }

  /* ⑦ Subscribe —— 参照站实测 326×41 / radius 20px / 全宽胶囊
        ⚠️ 用 `.topbar--public .pub-nav a.pub-cta`（0,3,0）压过桌面那条（0,2,0） */
  .topbar--public .pub-nav a.pub-cta {
    width: auto;
    min-height: 41px;
    margin: 12px 32px 16px;
    padding: 11px 22px;
    border-radius: 20px;
    justify-content: center;
  }

  /* ⑧ 语言切换：一行、与菜单项同缩进 */
  .topbar--public .lang-switch {
    display: block;
    padding: 12px 32px 0;
    font-size: 14px;
    letter-spacing: 1.5px;
  }
}

/* --- 5.3 手机（≤700px）：再收一点内边距，避免长菜单行溢出 --------------- */
@media (max-width: 700px) {
  .topbar--public .pub-nav > a,
  .topbar--public .pub-nav .pub-nav-label { padding: 15px 20px; }
  .topbar--public .nav-drop-menu a,
  .topbar--public .nav-drop-menu .is-locked { padding: 11px 20px 11px 36px; }
  .topbar--public .pub-nav a.pub-cta { margin: 10px 20px 14px; }
  .topbar--public .lang-switch { padding: 10px 20px 0; }
}

/* ★ 2026-10-02（用户裁定）：**下拉项必须竖排**（每项一行）。
   ## 现象
     市场下拉改成 `CN/HK/JP/US` 后，被排成**一行**（`CN HK JP` 横着、`US` 折行）。
   ## 根因（★ 实测 Playwright 量出 `display:inline-flex`）
     `public.css` 的「公开层导航链接」规则：
         `.topbar--public .pub-nav a { display: inline-flex; ... }`  ← (0,2,1)
     它匹配**所有** `pub-nav` 里的 `<a>`，**包括下拉项** ⇒ 下拉项变 inline ⇒ 横排。
     （`app.css` 的 `display:block` 与 v2 的窄屏规则都被它压住 / 不在桌面命中。）
   ## 修法
     加一条**不在任何 media 里**的规则，选择器写到 **`.topbar--public .pub-nav
     .nav-drop-menu a`（0,3,1）**⇒ 稳赢 `public.css` 的 (0,2,1)，且**桌面/窄屏都生效**。
     ⚠️ 不改 `public.css` / `app.css`（冻结文件）。 */
.topbar--public .pub-nav .nav-drop-menu a,
.topbar--public .pub-nav .nav-drop-menu .is-locked {
  display: block;
}

/* ★ 2026-10-02（用户裁定）：让「市场 / 后台管理 / 用户名」这几个**下拉标签**的
   左右留白，与普通菜单项（BLOG / WATCHLIST…）**完全一致**。
   ## 为什么它们原来多留白（`app.css` 两处，实测 Playwright 量过）
     ① `.nav-drop > .nav-drop-label::after { content: " ▾" }` ⇒ 多一个**箭头**
     ② `.nav-drop { margin-right: 12px }`                      ⇒ 多 12px 右边距
        （普通项没有这条 —— 它们只靠 `padding 9.75px` 分隔）
     ③ `letter-spacing / padding` 两边**本来就一样**（不是它们的问题）
   ## 修法
     用更高特异性**在本文件覆盖**（⛔ 不改冻结的 `app.css`）：
       · 去掉箭头（`content: none`）
       · 去掉容器多余右边距（`margin-right: 0`）⇒ 留白与普通项一致
     ✅ 三个下拉（市场 / 后台管理 / 用户名）**统一**处理。
     ⚠️ 去箭头后**仍靠 hover 展开**（`app.css` 的 `:hover` / `:focus-within` 不变）。 */
.topbar--public .pub-nav .nav-drop { margin-right: 0; }
.topbar--public .pub-nav .nav-drop > .nav-drop-label::after { content: none; }

/* ★ 2026-10-02（用户裁定）：**无权限的市场要灰化**。
   ## 现象（实测 Playwright：`is-locked` 的 color 与正常项**同为 rgb(51,51,51)**）
     `app.css` 有 `.nav-drop-menu .is-locked { color: rgba(35,39,47,.38) }`
     —— 但被 `public.css` 的 `.topbar--public .pub-nav a`（**更高特异性**）压住
     ⇒ 灰化**从未生效**（与之前 `display` 被压是同一套路）。
   ## 修法
     用 `.topbar--public .pub-nav .nav-drop-menu .is-locked`（0,3,1）**稳赢**
     `public.css` 的 (0,2,1) ⇒ 灰 + 默认光标（不可点感）+ 悬停不变色。
   ⚠️ 不改 `public.css` / `app.css`（冻结文件）。 */
.topbar--public .pub-nav .nav-drop-menu .is-locked {
  color: rgba(35, 39, 47, .38);
  cursor: default;
}
.topbar--public .pub-nav .nav-drop-menu .is-locked:hover {
  color: rgba(35, 39, 47, .38);
  background: transparent;
}

/* ============================================================================
   §6 锁图标 —— 内联 SVG（★ 2026-09-28 v6）
   ----------------------------------------------------------------------------

   ## 为什么换掉 emoji 字符（用户反馈「手机版锁头是奇怪的彩色」）
     原写法：`&#128274;&#65038;` = `U+1F512`（🔒）+ `U+FE0E`（**文字变体选择符**，
     要求渲染成单色）。**`U+FE0E` 的遵守度因平台而异**：
       · Windows Chrome：遵守 ⇒ 单色 ✓
       · **iOS Safari / Android Chrome：常被忽略 ⇒ 渲染成彩色 emoji** ❌
     ⇒ 与 CSS 无关（实测 `color` 已是目标色、`U+FE0E` 也在 DOM 里，
        纯粹是**字体层不认这个提示**）。

   ## 换成了什么
     内联 `<svg>`（**不是**字体、**不是** emoji）：
       `<svg class="v2-lock" viewBox="0 0 61 79" width="13" height="14">`
         `<mask id="v2-lock-hole"><rect fill="#fff"/><circle fill="#000"/></mask>`
         `<g mask="url(#v2-lock-hole)" fill="currentColor">`
           `<path …/>`  ← 锁梁（描边圆弧）
           `<rect …/>`  ← 锁体（圆角矩形）
     · **`fill="currentColor"`** ⇒ 颜色完全由 CSS `color` 决定
     · **`<mask>` 挖锁孔** ⇒ 真挖空，**任何底色都不露馅**（不依赖白色）
       ⚠️ 不能用「白色圆」代替：白底/蓝底切换时会露白点。

   ## 形状来源 & 验收（用户要求「一模一样」）
     参照站 stageanalysis.net 的 `.icon-lock`（icomoon 字体 `U+E900`）。
     我方**没有搬运它的字体**（版权 + 不引依赖），而是**照形状用几何复刻**，
     并做**逐像素 diff 验收**：
       · 初版白色圆方案：**1.44%**
       · 最终 mask 方案：**1.55%**（同量级；换来「真挖空、无颜色依赖」）
     其余差异来自抗锯齿 —— 在 14px 实际显示尺寸下**肉眼无差**。
     ⚠️ 参照站尺寸 13.13 × 14 ⇒ 我方取 **13 × 14**。

   ## 两处用法（颜色不同，靠 `currentColor` 自动区分）
     · `.pub-cta-lock`（订阅按钮内）→ 继承按钮色 ⇒ **白锁**（蓝底）
     · `.v2-cell-lock`（RPS250 表格格）→ `color: #b0b0b6` ⇒ **灰锁**（白底）
       ⚠️ 表格那个的 `color` 规则在 §3.5c，**不动**（选择器已解决过权重事故）。

   ## 改动范围（安全）
     · CSS 写在 v2.css ⇒ **旧站不加载本文件** ⇒ 旧站 4 处 emoji **不受影响**
     · 模板只改**新站** 6 处
     ⛔ `public.css` 一个字不动（冻结文件）
   ============================================================================ */
.v2-lock {
  display: inline-block;
  width: 13px;                         /* 参照站实测 13.13 */
  height: 14px;                        /* 参照站实测 14 */
  vertical-align: -2px;                /* 与文字基线对齐（视觉居中） */
  flex: 0 0 auto;
  fill: currentColor;
  pointer-events: none;                /* 点了应该走外层 <a>，不是图标 */
}

/*: 旧写法是 `<span class="pub-cta-lock">&#128274;&#65038;</span>`，
   现在里面放的是 `<svg class="v2-lock">` ⇒ 这个 span 只负责间距。 */
.pub-cta-lock {
  display: inline-flex;
  align-items: center;
  margin-right: 7px;
}

/*: 表格内的锁（RPS250 列）：链接里直接放 SVG，居中由 §3.5d 负责。 */
.v2-cell-lock .v2-lock {
  vertical-align: middle;
}

/* ---------------------------------------------------------------------------
   §3.5f 首页 hero（★ 2026-09-30 用户裁定：h1 分两行 + 副段居中放宽）

   ## 为什么需要这一段（根因，实测）
     v2.css §「正文段落」的
        `.pub-shell h1 ~ p, .pub-shell h2 ~ p, .pub-shell h3 ~ p`
         （特异性 0,1,2）给**所有**正文段落加了
        `max-width: 76ch` + `margin: .7em 0`
     ⇒ 它压过 `public.css:394` 的
        `.pub-hero p { max-width: 760px; margin: 0 auto 24px }`（0,1,1）
     ⇒ 后果①：`margin: 0 auto` 被 `margin: .7em 0` 覆盖 ⇒ 窄盒子**贴左**
               （`.pub-hero` 的 `text-align:center` 对块级窄盒子无效）
     ⇒ 后果②：76ch 太窄 ⇒ 每行只排 ~63 字符，长句被切成 3 行

   ## 修法（照本仓既有先例 v2.css:939 `.v2-plan-tagline`）
     用**更高特异性的类选择器**（0,2,0 > 0,1,2... 实为同级但源码在后）
     把设计值覆盖回来 —— ⛔ 不改 public.css（冻结文件）

   ## ⚠️ 为什么不用 `.pub-shell .pub-hero p`（0,2,1，特异性更高）
     本次要改的是**新站首页专用**的两处文案 ⇒ 用**新站专属类名**
     `.v2-hero-title` / `.v2-hero-lead`，与旧站及 RPS 页彻底隔离：
     其它页面的 `.pub-hero p`（如旧站首页 / RPS 页底）行为**完全不变**。
   --------------------------------------------------------------------------- */

/*: 主标题：两行（英文侧靠 msgstr 里的 `<span class="v2-hero-sub">`，
    见 i18n_zh_en.json）。中文侧 msgid 无该 span（中文是单行）⇒ 各自成立。
    ## 🔴 必须带 `.pub-shell` 前缀（特异性 0,2,0）
      上方 `.pub-shell h1 ~ p`（0,1,2）会命中 hero 的段落；
      裸类选择器只有 (0,1,0) ⇒ 一定输。详见下方 `.v2-hero-lead` 注释。 */
.pub-shell .v2-hero-title {
  /* 略收敛字距，长英文标题在窄屏更紧凑、不显得挤 */
  letter-spacing: -0.01em;
  text-wrap: balance;          /* 支持时自动均衡两行长度（不支持则忽略，无损） */
}

/*: 标题第二行（英文副题）——**小一档**（2026-09-30 用户裁定 A 档）。
    · `display: block` 负责换行（取代早先的 `<br>` ⇒ 换行与字号一次搞定）
    · 桌面：`1.75rem`(28px) ← h1 是 `2rem`(32px)，小 12.5%
    · 手机：见下方 media query ⇒ `1.25rem`(20px) ← 手机 h1 是 `1.5rem`
    ⚠️ 该 span 在**英文 msgstr** 里 ⇒ 中文站（走 msgid）**没有它**
      ⇒ 中文标题保持单行、不缩小（符合"先做英文站"的裁定）
    ⚠️ 需要 `.pub-shell` 前缀吗？—— 本规则**不设** max-width/margin，
      只设 font-size/display，上方 `hX ~ p` 只命中 `<p>`（不命中 h1 内 span）
      ⇒ 无冲突。但为与同区其它规则**写法一致**、并防将来新增 `.pub-shell span`
      规则压过来，仍带前缀（0,2,0）。

    ## 两个间距的层级（2026-09-30 用户裁定：A=32 / B=12 px）
      用户要：**A（H1↔副题）= 32px**、**B（副题↔副段）= 12px**（文字到文字）。

      ⚠️⚠️⚠️ **怎么量出来的（第六轮；前五轮全错，教训记这里）**
        前五轮我都**在纸上推公式**（margin + 行盒半留白）⇒ 每次都和用户实拍
        差 1.3~2.4 倍，用户直接质问"你会不会量啊"。
        根因：浏览器用**字体真实 ascent/descent**，且 `text-wrap: balance`
        会改变折行位置 ⇒ **纸上根本算不出来**。
        ⇒ 正确做法 = **真实浏览器实测**：
          ① Playwright 驱动**系统 Chrome**（`channel="chrome"`；本机 chromium
             装不上，见 handoff §五）开本地 Flask(:5099)；
          ② `document.createRange()` + `getClientRects()` 取**每一行文字**的
             `top/bottom` —— ⚠️ 不能取元素盒子（含行盒留白，会误导）；
          ③ A = `subOwn.firstTop − h1Own.lastBottom`
             B = `leadOwn.firstTop − subOwn.lastBottom`；
          ④ 以实测定基线，**1:1 线性反推**所需 margin，再实测复核。
        ★ 实测结果（Chrome，1280 与 390 两种视口）：**A/B 均为 32/12，两视口精确命中**。

      ⚠️ **手机与桌面的 em 必须分开写**（见下方 media query）：
        手机 h1 会折成 2 行、字号也不同 ⇒ 行盒不同 ⇒
        **同一个 em 值得不到同一个视觉结果**（第一版只在桌面命中，手机 A 变 50px）。

      ⚠️ **B 需要负 margin**：sub 与 lead 各自的行盒半留白合计已 ~13.8px，
        想让视觉值 B=12px 只能"倒扣" ⇒ `margin-top` 为负。这是有意为之，不是笔误。 */
.pub-shell .v2-hero-sub {
  display: block;
  font-size: 1.75rem;
  line-height: 1.3;            /* 略紧于 1.25 的 h1 行高，两行更成组 */
  margin-top: 1.0785em;        /* ★ A=32px（Chrome 实测反推） */
}

/*: 副段：**居中** + 放宽到合理上限（解决"每行太短"，又不宽过标题）。
    ## 🔴🔴 为什么必须写 `.pub-shell .v2-hero-lead`（实测踩坑，2026-09-30）
      本文件上方「正文段落」：
        `.pub-shell h1 ~ p, .pub-shell h2 ~ p, .pub-shell h3 ~ p`
          （特异性 **0,1,2**）设了 `max-width: 76ch` + `margin: .7em 0`
      ⇒ 第一版我写成**裸类** `.v2-hero-lead`（**0,1,0**）⇒ **输给 0,1,2**
        ⇒ 规则整段失效：仍是 76ch、仍被 `margin: .7em 0` 贴左
        ⇒ 用户实拍截图确认「还是句短，没有居中」。
      ⇒ 修法：**加 `.pub-shell` 前缀**（0,2,0）—— 与 `v2.css:933`
        `.pub-shell .v2-plan-tagline` / `.pub-shell .v2-plan-name` 同范式。
      ⚠️ 教训：`.pub-shell` 下的段落，覆盖规则**必须带该前缀**，
        否则 `hX ~ p` 永远赢。
    · `margin` 必须**逐个写全**（不能只写 `margin-inline: auto`）：
      因为对方的 `margin: .7em 0` 会重置全部四边 ⇒ 只改 inline 时
      `margin-top/bottom` 仍是对方的 `.7em`，且 `margin-left/right` 需 auto 才居中

    ## 928px（= 58rem）是怎么定的（2026-09-30 用户第二轮裁定）
      第一版放成 `64rem`(1024px) ⇒ 用户实拍：**副段比标题还宽**，要求"稍窄一些"。
      ⇒ 用 `fontTools` 量**真实 Inter 字体**（`inter-var-latin.woff2`）得：
        · H1 最宽行（32px / wght700）= **1197px**
        · 容器可用宽度 = 1280 - 2×24 = **1232px**
        · 58rem = **928px** ⇒ 比标题窄 **22%**（"稍窄"，收在标题之内）
          且单行仍可容 ~110 字符（不会退回原来的"句短"）
      ⚠️ 这是**桌面/平板**值；手机档在下方媒体 query 里放开为 `none`。

    ## 组内间距 B↓：副段与副题"是一伙的"（2026-09-30 第四轮裁定）
      用户原话：「这两段要靠近，看起来他们是一伙的，或者索性就改成他们是一伙的，
                 字号加黑不一样而已」+ 截图 ⇒ **副题 与 副段** 应成一个视觉块。
      目标层级（A = 组间、B = 组内）：
        H1（32px 大字）                      ← 第一行
          ↕ A ≈ 56px 【组间·大】              （由 `.v2-hero-sub` 的 margin-top 给）
        副题（28px 粗体）                     ← 第二行
          ↕ B ≈ 8px 【组内·小】 ★ 本轮收紧     ┐ 这两段"是一伙的"
        副段（16px 常规）                     ┘
      ⇒ 实现：把副段的 `margin-top` 从 `.7em`(11px) 收到 **`.5em`(8px)**（16px 字体）。
        ⚠️ 仍然是 `.pub-shell .v2-hero-lead`（0,2,0）—— 否则被上方
           `.pub-shell h1 ~ p { margin: .7em 0 }`（0,1,2）压回去（踩过两次）。
        ⚠️ `margin` 必须**四边写全**（简写会重置四边 ⇒ 只改 top 时要带上其余三边）。 */
.pub-shell .v2-hero-lead {
  max-width: 58rem;            /* = 928px（量自真实字体，比标题 1197px 窄 22%） */
  margin: -0.55em auto 24px;   /* ★ B=12px（Chrome 实测反推：负值抵消行盒留白） */
  line-height: 1.7;
}

/*: 小屏（≤700px，与 public.css:439 同断点）：
    · h1 字号由 public.css 的 1.5rem 接管（不动）
    · **副题跟着小一档** → `1.25rem`(20px)（用户裁定「手机跟着小」，2026-09-30）
    · 副段**取消限宽**⇒ 占满手机宽度（手机本来就只有 ~343px 内容区，
      再限宽会让每行短到 3~4 个词 ⇒ 反而难读）
    ⚠️ 这里同样要带 `.pub-shell` 前缀，否则压不住 0,1,2 的 76ch。 */
@media (max-width: 700px) {
  .pub-shell .v2-hero-sub { font-size: 1.25rem; margin-top: 1.5071em; }
  .pub-shell .v2-hero-lead { max-width: none; margin-top: -0.4875em; }
}


/* ============================================================================
   §40 ★ 顶栏 LOGIN（描边胶囊）—— 2026-10-01 用户裁定：像素级照抄参照站
   ----------------------------------------------------------------------------
   ## 参照站实测（本机 Chrome · 1440 视口）
     `<a class="main-nav__login-button"><span class="btn btn-outline btn-block">
       <i class="icon-lock mr-2"></i>Login</span></a>`
     胶囊：41px 高 · radius 20px · padding 11.5/22/12.5/24 · 14px/500/1.75px
     **与订阅钮唯一的差别 = 描边**（底透明 · 字/边 #0076FF）

   ## 为什么放 v2.css
     `public.css` 是**冻结文件**（改了 preflight FAIL）⇒ 用**覆盖式**新增；
     且**旧站不加载 v2.css** ⇒ 旧站完全不受影响。

   ## ⚠️ 为什么必须覆盖全部伪类
     本仓 BUG-A 血泪：`.pub-body a`（权重 0,2,1）的**链接蓝**会盖过普通类
     （0,1,0）⇒ 链接一旦被访问，`:visited` 生效 ⇒ 蓝底/蓝字看不见。
     ⇒ 逐个伪类显式声明（不用 `!important`）。
   ============================================================================ */
/*: ★★ 权重（2026-10-01 实测踩到：LOGIN 渲染成**实心蓝**）
    `public.css` 的 `.topbar--public .pub-nav a.pub-cta`（权重 **0,3,1**）含
    `background: var(--pub-cta-bg)` + `border: 0` ⇒ **压过**普通的
    `.pub-cta--outline`（0,1,0）⇒ 描边被吃掉、又变实心。
    ⇒ 用**同权重 + 追加一个类**（`.topbar--public .pub-nav a.pub-cta--outline`
      = 0,4,1 > 0,3,1）压回来。⛔ 不用 `!important`（本仓先例：提权重）。
    ⚠️ 且必须**同时**覆盖伪类（BUG-A：`.pub-body a` 蓝色会盖回来）。 */
.topbar--public .pub-nav a.pub-cta--outline {
  box-sizing: border-box;
  background: transparent;
  color: var(--pub-cta-bg);
  border: 1px solid var(--pub-cta-bg);
}
.topbar--public .pub-nav a.pub-cta--outline:link,
.topbar--public .pub-nav a.pub-cta--outline:visited,
.topbar--public .pub-nav a.pub-cta--outline:hover,
.topbar--public .pub-nav a.pub-cta--outline:focus,
.topbar--public .pub-nav a.pub-cta--outline:active {
  color: var(--pub-cta-bg);
  text-decoration: none;
}
/* 描边钮 hover：淡蓝底（参照站观感；实心钮另有 `.topbar--public ...:hover`） */
.topbar--public .pub-nav a.pub-cta--outline:hover {
  background: rgba(0, 118, 255, .08);
  border-color: var(--pub-cta-bg);
}
/* 非顶栏场景（万一将来别处用）：保留一份基础规则 */
.pub-cta--outline {
  box-sizing: border-box;
  background: transparent;
  color: var(--pub-cta-bg);
  border: 1px solid var(--pub-cta-bg);
}
.pub-cta--outline:hover { background: rgba(0, 118, 255, .08); }

/* ============================================================================
   §41 ★ 认证页组件（2026-10-01 · 新站自建 login/register/forgot/reset）
   ----------------------------------------------------------------------------
   ## 长相来源
     参照站三个认证页实测（`/login` `/register` `/forgot-password`）：
       · 大标题 H2 30px / 700 / 居中
       · 字段 label 16px / 700 / 必填后缀红 *
       · 输入框 368×39px · padding 8px 12px · border 1px #d1d5db · radius 4px
       · 提交按钮 39px · padding 8px 16px · radius 4px · bg #0076FF · 白字
       · 登录/注册页**无卡**；`forgot` 页**有卡**（浅边框 + 圆角）
   ## ⚠️ 输入框高度（用户裁定 Q11=乙）
     参照站是 39px；**我方用组件库既有的 34px**（`.v2-input`）。
     各认证页**允许**在自己的块里局部覆盖（`.v2-auth .v2-input { height: … }`）。
   ## ⚠️ 类名全是 v2 自己的
     ⛔ 不复用参照站的 `fui-*`（那是别人 WordPress 插件的类）
     ⛔ 不复用旧的 `.card` / `.narrow`（旧站类，铁律：新站不引旧类名）
   ============================================================================ */
.v2-auth {
  max-width: 368px;
  margin: 0 auto;                      /* 居中列 */
}
.v2-auth-h1 {
  margin: 0 0 1.25em;
  font-size: 1.875rem;                 /* 30px */
  font-weight: 700;
  text-align: center;
  color: #1f2937;
}
.v2-field { margin-bottom: 20px; }
.v2-label {
  display: block;
  margin-bottom: 4px;
  font-size: 1rem;                     /* 16px */
  font-weight: 700;
  color: #333;
}
/* 必填星号（红） */
.v2-label .v2-req { color: #d33; margin-left: 2px; }
.v2-auth .v2-input {
  width: 100%;
  /* 高度用组件库的 34px（见文件头 Q11 说明）—— 如需局部覆盖写在这里 */
}
.v2-submit {
  display: inline-block;
  height: 39px;
  padding: 8px 16px;
  border: 1px solid #0076FF;
  border-radius: 4px;
  background: #0076FF;
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  text-decoration: none;
}
.v2-submit:hover { background: #0062D6; border-color: #0062D6; }
/* 页底链接行（登录页的「忘记密码 / 需要账号」） */
.v2-auth-links {
  display: flex;
  justify-content: space-between;
  gap: 1em;
  margin-top: 1.75em;
  font-size: 0.9375em;
}
.v2-auth-links a { color: #0076FF; }
/* 错误条 */
.v2-form-error {
  margin: 0 0 1em;
  padding: 0.6em 0.85em;
  border-radius: 4px;
  background: #fdf2f2;
  border: 1px solid #f3c9c9;
  color: #b42318;
  font-size: 0.9375em;
}
/* 提示页的浅边框卡（照参照站 forgot 那种） */
.v2-card {
  padding: 1.5em 1.75em;
  border: 1px solid #e5e5e7;
  border-radius: 4px;
  background: #fff;
}
.v2-card .v2-auth-h1 { margin-top: 0; }
/* 页底小字（如「注册后会向你填写的邮箱发送验证信…」） */
.v2-auth-note {
  margin-top: 1.25em;
  font-size: 0.875em;
  color: #8a8a90;
}
/* 复选框行（Remember me / 同意条款） */
.v2-check {
  display: flex;
  align-items: center;
  justify-content: flex-start;   /* ★ 靠左（实测：曾整行居中 + 文字被挤换行）*/
  gap: 0.5em;
  font-size: 0.9375em;
  font-weight: 400;
  cursor: pointer;
  white-space: nowrap;           /* ★ 「Remember me」不许折行 */
  /* ⚠️ 覆盖 app.css:218 的全局 `label{display:block;margin:10px 0 4px}` */
  margin: 0;
  color: inherit;
}
/* ⚠️★ flex 里 checkbox 会**被拉伸**（实测成 250px 宽）⇒ 必须钉死尺寸 */
.v2-check input[type="checkbox"] {
  flex: 0 0 auto;
  width: 1em;
  height: 1em;
  margin: 0;
}
/* ★ 蜜罐字段（Q9=甲）：视觉隐藏但**仍在 DOM**（不是 type=hidden —— 机器人专挑它看）。
   ⚠️ 用 `position:absolute` 移出视口而不是 `display:none`：某些机器人会跳过
      `display:none` 的表单元素，留在 DOM 里更容易被"老实填"的脚本命中。 */
.v2-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}


/* ============================================================================
   §后台 console（批 C2-a · 2026-10-01）—— 看板 / 触发任务 / 后台首页
   ★ 风格铁律（Q10）：v2 皮肤、不抠像素、**避免横向滚动**
   ============================================================================ */

/* ---- 状态标签（照旧 admin 的 st-* 语义，用 v2 配色）---- */
.v2-tag-run      { background: #e3f2fd; color: #0062D6; }
.v2-tag-queued   { background: #f1f3f5; color: #555; }
.v2-tag-running  { background: #e3f2fd; color: #0062D6; }
.v2-tag-success  { background: #e6f4ea; color: #1e7e34; }
.v2-tag-failed   { background: #fdecea; color: #c0392b; }

/* ---- 计数小徽章 ---- */
.v2-chip {
  display: inline-block;
  margin-left: 0.4em;
  padding: 0.1em 0.5em;
  font-size: 0.8125em;
  border-radius: 999px;
  background: #f1f3f5;
  color: #444;
}
.v2-chip--ok  { background: #e6f4ea; color: #1e7e34; }
.v2-chip--err { background: #fdecea; color: #c0392b; }

/* ---- 危险按钮（终止任务）---- */
.v2-btn--danger {
  padding: 0.25em 0.75em;
  font-size: 0.8125em;
  border: 1px solid #c0392b;
  border-radius: 4px;
  background: #fff;
  color: #c0392b;
  cursor: pointer;
}
.v2-btn--danger:hover { background: #fdecea; }

/* ---- 看板：运行中 / 排队 ---- */
.v2-live-sec { margin: 1.5em 0; }
.v2-run-item {
  border: 1px solid #e5e5e7;
  border-radius: 6px;
  padding: 0.75em 1em;
  margin-bottom: 0.75em;
  background: #fff;
}
.v2-run-item--queued { background: #fafafa; }
.v2-run-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  align-items: center;
}
.v2-run-bar {
  height: 6px;
  margin: 0.5em 0 0.25em;
  border-radius: 3px;
  background: #eceef1;
  overflow: hidden;
}
.v2-run-bar > div { height: 100%; background: #0076FF; }
.v2-run-log {
  margin: 0.4em 0;
  padding: 0.4em 0.6em;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.8125em;
  background: #f7f8fa;
  border-radius: 4px;
  /* ★ 不让日志撑破页面（Q10）：单行超长 ⇒ 省略号，不横向滚动 */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.v2-run-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75em;
  align-items: center;
  margin-top: 0.4em;
}

/* ---- 后台首页：四市场入口卡 ---- */
.v2-console-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1em;
  margin: 1.25em 0;
}
@media (max-width: 900px) { .v2-console-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .v2-console-grid { grid-template-columns: 1fr; } }
.v2-console-card {
  display: flex;
  flex-direction: column;
  gap: 0.4em;
  padding: 1.1em 1em;
  border: 1px solid #e5e5e7;
  border-radius: 8px;
  background: #fff;
  text-decoration: none;
  color: inherit;
  transition: box-shadow .15s, transform .15s;
}
.v2-console-card:hover { box-shadow: 0 2px 10px rgba(0,0,0,.08); transform: translateY(-1px); }
.v2-console-big { font-size: 1.25em; font-weight: 700; }
.v2-console-card--cn { border-top: 3px solid #0076FF; }
.v2-console-card--us { border-top: 3px solid #1e7e34; }
.v2-console-card--hk { border-top: 3px solid #a35b00; }
.v2-console-card--jp { border-top: 3px solid #8e44ad; }

/* ---- 触发任务：每行一条预设（数据驱动，无横向滚动）---- */
.v2-preset-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75em;
  align-items: center;
  justify-content: space-between;
  padding: 0.85em 0;
  border-bottom: 1px solid #eceef1;
}
.v2-preset-row:last-child { border-bottom: 0; }
.v2-preset-info { display: flex; flex-direction: column; gap: 0.15em; min-width: 12em; flex: 1 1 16em; }
.v2-preset-form { display: flex; flex-wrap: wrap; gap: 0.5em; align-items: center; }
.v2-preset-field { display: inline-flex; align-items: center; gap: 0.3em; font-size: 0.875em; }

/* ---- 市场切换行 / 通用小卡 ---- */
.v2-market-switch { margin: 0.5em 0; }
.v2-cur { font-weight: 700; color: #0076FF; }
.v2-card {
  border: 1px solid #e5e5e7;
  border-radius: 8px;
  padding: 0.5em 1em;
  background: #fff;
}

/* ---- 日志框（★ Q10：内部处理 ⇒ 不撑破页面）---- */
.v2-logbox {
  max-height: 70vh;
  overflow: auto;
  padding: 0.75em 1em;
  margin: 0.75em 0;
  background: #1e1f22;
  color: #e6e6e6;
  border-radius: 6px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.8125em;
  line-height: 1.5;
  white-space: pre-wrap;      /* ★ 超长行**自动换行**，不产生整页横滚 */
  word-break: break-word;
}


/* ============================================================================
   §后台 console · 用户管理卡片（批 C2-c · 2026-10-01）
   ★ Q10/Q11：扔掉 8 列大宽表 ⇒ 一用户一卡，**无横向滚动**
   ============================================================================ */
.v2-user-card {
  border: 1px solid #e5e5e7;
  border-radius: 8px;
  padding: 0.9em 1.1em;
  margin-bottom: 0.85em;
  background: #fff;
}
.v2-user-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  align-items: center;
}
.v2-user-badges {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.35em;
  margin-left: auto;
}
.v2-tag-role { background: #eef2f7; color: #445; }
.v2-tag-err  { background: #fdecea; color: #c0392b; }
.v2-user-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em 1.1em;
  align-items: baseline;
  margin: 0.55em 0;
  padding: 0.5em 0.7em;
  background: #f7f8fa;
  border-radius: 6px;
}
.v2-user-email { font-weight: 600; color: #0062D6; }
.v2-user-email--warn { color: #a35b00; }
.v2-user-perms,
.v2-user-profile { margin: 0.4em 0; }
.v2-user-ops {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6em 1.1em;
  align-items: center;
  margin-top: 0.6em;
  padding-top: 0.6em;
  border-top: 1px solid #eceef1;
}
.v2-user-profile-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6em;
  align-items: center;
  margin-top: 0.4em;
}
.v2-inline-details { display: inline-block; }
.v2-inline-details > summary { cursor: pointer; }
/* ★ 操作按钮不换行折字（旧截图「Randomis...」截断的反面）*/
.v2-inline-form { display: inline-flex; align-items: center; gap: 0.35em; }
.v2-inline-form .v2-btn { white-space: nowrap; }
.v2-user-ops .v2-btn--link { white-space: nowrap; }

/* ============================================================================
   §用户管理 · 筛选/排序/视图（2026-10-07 · plan_user_management.md · 甲+乙）
   ★ Q10/Q11 仍成立：无横向滚动（工具栏 wrap、卡片竖排）
   ============================================================================ */
.v2-user-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em 0.75em;
  align-items: center;
  margin: 0.5em 0 1em;
  padding: 0.6em 0.8em;
  background: #f7f8fa;
  border-radius: 6px;
}
.v2-user-toolbar #uq { flex: 1 1 240px; min-width: 180px; }
.v2-view-toggle { display: inline-flex; gap: 0.5em; align-items: center; }
.v2-view-toggle [data-view] { white-space: nowrap; }

/* 创建用户表单默认折叠 */
.v2-user-create > summary { cursor: pointer; display: inline-block; }
.v2-user-create[open] > summary { margin-bottom: 0.6em; }

/* 紧凑行（默认视图）：一屏数十人 */
.v2-user-compact {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em 1em;
  align-items: center;
}
.v2-user-compact strong { min-width: 6em; }

/* 两视图显隐：默认紧凑（根是 div.v2-user-card；D7） */
.v2-view--compact .v2-user-card-body { display: none; }
.v2-view--compact .v2-user-card.is-open .v2-user-card-body { display: block; }
.v2-view--compact .v2-user-card.is-open .v2-user-compact { border-bottom: 1px solid #eceef1; padding-bottom: 0.4em; }
.v2-view--card .v2-user-compact { display: none; }


/* ---- 运行健康（批 C3）：一市场一卡，无横滚 ---- */
.v2-health-deploy { margin: 1em 0; }
.v2-health-card {
  border: 1px solid #e5e5e7;
  border-radius: 8px;
  padding: 0.9em 1.1em;
  margin-bottom: 0.8em;
  background: #fff;
}
.v2-health-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  align-items: center;
  margin-bottom: 0.5em;
}
.v2-health-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5em 1.5em;
}
@media (max-width: 620px) { .v2-health-grid { grid-template-columns: 1fr; } }
.v2-health-grid > div { display: flex; flex-direction: column; gap: 0.1em; }
.v2-health-path { margin: 0.5em 0 0; word-break: break-all; }

/* ---- VCP 手动出池链接（批 C4；仅 admin 渲染）---- */
.v2-vcp-manual-exit {
  margin-left: 0.6em;
  color: #c0392b;
  text-decoration: none;
  white-space: nowrap;
}
.v2-vcp-manual-exit:hover { text-decoration: underline; }

/* ============================================================================
   §4.11 选市场浮层（`subscription.html` · P4c，plan_stripe.md §9.8）
   ----------------------------------------------------------------------------
   ★ 零依赖：原生 JS 翻 `hidden`；这里只管长相。
   ⚠️ 用 `.v2-*` 命名空间 + 令牌，**不碰** public.css/app.css（冻结文件）。
   ============================================================================ */
.v2-market-modal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 23, 42, 0.45);
  padding: 20px;
}
.v2-market-modal[hidden] { display: none; }
.v2-market-modal__box {
  background: #fff;
  border-radius: 10px;
  padding: 24px;
  width: 100%;
  max-width: 420px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.25);
}
.v2-market-modal__title { margin: 0 0 8px; font-size: 1.25rem; }
.v2-market-modal__hint { margin: 0 0 16px; color: #57534e; font-size: 0.9rem; }
.v2-market-modal__opts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 14px;
}
.v2-market-opt {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid #d6d3d1;
  border-radius: 6px;
  padding: 10px 12px;
  cursor: pointer;
  font-size: 0.95rem;
}
.v2-market-opt:hover { border-color: #a8a29e; }
.v2-market-modal__err { color: #b91c1c; font-size: 0.85rem; margin: 0 0 10px; }
.v2-market-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.v2-btn {
  min-height: 38px;              /* 本仓手机规范：按钮 ≥38px */
  padding: 8px 18px;
  border-radius: 6px;
  border: 1px solid #0a2046;
  background: #0a2046;
  color: #fff;
  font-size: 0.95rem;
  cursor: pointer;
}
.v2-btn--ghost { background: #fff; color: #0a2046; }

/* ============================================================================
   §4.12 未验证邮箱提示条（`v2/base.html` · 2026-10-02 用户裁定）
   ----------------------------------------------------------------------------
   ★ 顶栏正下方一行；已登录 + status='pending' 时显示。
   ⚠️ 不遮挡内容（正常流，不是 fixed）；配色用暖黄（提示而非错误）。
   ============================================================================ */
.v2-verifybar {
  background: #fef3c7;              /* amber-100 */
  color: #92400e;                   /* amber-800 */
  border-bottom: 1px solid #fcd34d; /* amber-300 */
  font-size: 0.875rem;
  padding: 8px 20px;
  text-align: center;
}
.v2-verifybar a {
  color: #92400e;
  font-weight: 700;
  text-decoration: underline;
  margin-left: 8px;
}
/*: ★ 2026-10-02：提示条里的「重发」按钮（裸 button，样式像链接） */
.v2-verifybar-btn {
  background: none;
  border: 0;
  padding: 0;
  margin-left: 8px;
  color: #92400e;
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}

/* ★ 2026-10-02 首页重构：小标题下方的那句定位说明。
   ## 🔴 为什么会被\"无端端折行\"（用户 2026-10-02 反馈，实测根因）
     本句是 `<h2 class="v2-home-h2">` 的**后续兄弟 `<p>`** ⇒ 命中本文件
       `.pub-shell h1 ~ p, .pub-shell h2 ~ p, .pub-shell h3 ~ p
        { max-width: 76ch }`（0,1,2）
     ⇒ 块宽被限死在 **719px**，而本句**单行需要 1196px**
       ⇒ 必然折成 2 行（实测行宽 706 / 486）。
   ## 修法
     `max-width: 1200px` ⇒ 一行放下（1196 < 1200），且窄于容器可用宽 1312px。
     ⚠️ 必须带 `.pub-shell` 前缀（(0,1,2) 的那条压得很死），裸类 (0,1,0) 必输。
     ⚠️ 只影响**首页第二屏**这句；hero 的三句另有独立类（`.v2-hero-lead`）。 */
.pub-shell .v2-home-lead {
  margin: 0.5em 0 1.75em;
  font-size: 0.9375em;
  line-height: 1.6;
  max-width: 1200px;            /* ★ 一行放下（原被 76ch=719px 限死） */
}


/* ============================================================================
   §41 ★ 2026-10-02 首页首屏（用户裁定）
   ----------------------------------------------------------------------------
   ## 用户要的三件事（大白话原话）
     ① 「把截图1的部分就放大到占领整个首页第一页」
        ⇒ 首屏**只有那四行**（大标题／副题／说明句／蓝色按钮），撑满整屏、垂直居中
     ② 「截图2那里开始第二页，然后现在标题字体都太小，应该学 rps 页里面的风格」
        ⇒ 功能块从**第二屏**开始；小标题字号加大
     ③ 「第一个卡片框的上沿不见了」 + 「每个卡片里的文字里卡片框的右边框都特远」
        ⇒ 见 §41c / §41d

   ## 🔴 为什么全部要带 `.pub-shell` 前缀
     `public.css`（**冻结文件，不许改**）里：
        · `.pub-hero { text-align:center; padding:48px 0 32px }`   (0,1,0)
        · `.pub-hero h1 { font-size: 2rem }`                       (0,1,2)
        · `.pub-hero p  { max-width:760px; margin:0 auto 24px }`   (0,1,1)
     而本文件更早还有
        `.pub-shell h1 ~ p, …{ max-width:76ch; margin:.7em 0 }`    (0,1,2)
     ⇒ 裸类选择器 (0,1,0) 全输 ⇒ **必须 `.pub-shell <class>` 或更高**。

   ## 🔴 为什么用新类名 `v2-hero--full` 而不是改 `.pub-hero`
     ⚠️ **RPS 页底部也有 `.pub-hero`**（订阅 CTA 那块）。
     直接给 `.pub-hero` 加 `100vh` ⇒ **RPS 页会被撕出一个整屏空洞**。
     ⇒ 首屏态只在**首页**启用（`home.html` 上加类），影响面精确可控。

   ## 字号怎么定的（**实测，不是拍脑袋**）
     用户硬约束（2026-10-02）：「**这两句不能因为太大了换行**」
       T1 = `Valmentum™ — Where Momentum Meets Value.`
       T2 = `Momentum leads the search. Fundamentals make the case.`
     用户 2026-10-02 第二轮：「容器」放宽（= 丙/乙 方案）⇒ 取 **乙：文字区 1200 → 1240px**

     实测（真实 Chrome，`white-space:nowrap` 量单行宽）：

       | 容器 | 可用文字宽 | T1 上限  | T2 上限 |
       |------|-----------|----------|---------|
       | 1200 | 1152px    | 50px(97%)| 40px(96%)|
       | 1240 | 1192px    | 52px(98%)| 42px(97%)|
       | 1280 | 1232px    | 54px(98%)| 44px(99%)|

     ⇒ **取容器 1240px**：`T1 = 52px`、`T2 = 40px`（都留 2~7% 余量 ⇒ 不折行）。
     ⚠️ 为什么不用 1280：1920 屏两侧只剩 320px；1240 更稳，且 1366 屏仍放得下
        （1366 − 1240 = 126px 余量）。

   ## 用户第二轮的三条反馈（2026-10-02，全部实测确认）
     原话：「字号明显可以更大」「要往上拉起码150px」
           「三句话之间的留白太小」「整个页面上半部分是空的，下面3句话挤在一起」

     实测（1440×1080，改前）：
       · 四行只占 y=511~746 ⇒ **上方空 390px、下方空 392px**（居中却显空）
       · 句间**文字到文字**的实际留白：H1→SUB = **−39px**（行盒重叠！）、
         SUB→LEAD = **−3px**（几乎贴上）、LEAD→CTA = 29px
       ⇒ 根因①：`justify-content:center` 把\"内容本身很矮\"的块垂直居中 ⇒ 必然上下大空
         根因②：§3.5f 的 `margin-top: 1.0785em` / `-0.55em` 是给**单行**算的
                （用 `createRange` 反推的\"视觉 A=32px\"）⇒ 多行/换字号后**全塌**
     ⇒ 修法：① 改**靠上排**（`flex-start` + 顶部留白）
             ② 句间留白改**显式正值 `margin-top`**，不再用\"反推负值\"。
   ============================================================================ */

/*: §41a 首屏容器：★ **不再强求一屏高**（用户 2026-10-02 第五轮裁定）
    ## 用户最终口径（大白话原话）
      「我明白了，现在我不要求你那三句话一定是第一页，因为这个**很难统一**，
        那就那三句话**提高一点**，**相互行距放宽**，
        然后 **JOIN TODAY 按钮下留多一点空间**，才到 From momentum to conviction」

    ## 为什么放弃 `100vh`（四轮试错的最终结论）
      | 版本 | 做法 | 结果 |
      |------|------|------|
      | 1 | `100vh`+`center`+旧负 margin | 上下各空 390px、三句挤 |
      | 2 | `flex-start`+`100vh` | 上空 118 / 下空 600 ⇒ 小窗口第二屏闯进首屏 |
      | 3 | 固定 `padding-top:176px` | 窗口越矮越糟（第二屏仍被顶进） |
      | 4 | `100vh`+`center` | 小窗口（600px）内容挤、大窗口仍显空 |
      ⇒ **根因**：**窗口高度因人而异**，任何\"撑满一屏\"的做法都无法统一。
      ⇒ **用户已同意放弃该目标** ⇒ 改\"**自然排布 + 固定留白**\"，稳定、可预期。

    ## 本版做法（稳定，与窗口高度无关）
      · 首屏容器：**不设高度**（不 flex、不 vh）⇒ 高度由内容 + 上下 padding 决定
      · `padding-top: 6rem`(96px) —— 用户要的\"**提高一点**\"（比默认 48px 更高一些）
      · 三句**行距放宽**（见下方 §41b）
      · **JOIN TODAY 之下留大段空白**（`padding-bottom: 10rem` = 160px）
        ⇒ 之后才出现 `From momentum to conviction`
    ⚠️ 这样**任何人、任何窗口**看到的都是同一套间距（不随窗口高度伸缩）。 */
.pub-shell .pub-hero.v2-hero--full {
  display: block;               /* 不再 flex（原为居中/靠上而设） */
  padding: 6rem 0 10rem;        /* ★ 上 96px（\"提高一点\"）/ 下 160px（按钮下大留白） */
  text-align: center;           /* 沿用 public.css:392 的居中 */
  box-sizing: border-box;
}
/*: 容器放宽（同 §41a2 的理由：默认只有 1000px 可用） */
.pub-shell .v2-hero--full > * {
  margin-left: auto;
  margin-right: auto;
}

/*: §41a2 首屏文字区**放宽**（用户 2026-10-02 选「乙」）。
    ## 🔴 关键：真凶是 `app.css:199 main { max-width:1080px }`
      实测逐层量宽（1920 视口，改前）：
        BODY           width=1920
        MAIN.pub-content  width=**1080**  maxw=**1080px**  ⚠️← **卡在这里**
        DIV.pub-container width= 1048  maxw=1360px（我改的已生效，但被上层的 main 卡住）
        DIV.pub-shell     width= 1000
      ⇒ `app.css` 是**会员区样式表**，它的通用 `main { max-width:1080px }`
        把**新站公开页**也一起限宽了（v2.css 在其后加载，但那条是元素选择器、
        默认样式里没被别的规则顶掉 ⇒ 直接生效）。
      ⚠️ 踩坑记录：我第一版只放宽 `.pub-container` ⇒ **无效**（仍 1000px），
        因为**父级 main 更窄**；CSS 里子元素不可能超出父宽。
    ## 修法（**两层都要放宽**）
      只放宽**首页 body 类**下的 `main.pub-content` 与 `.pub-container`：
        · `main.pub-content`：1080 → 1416px（含左右 16px padding ⇒ 可用 1384）
        · `.pub-container`   ：1280 → 1360px（含左右 24px padding ⇒ 可用 1312）
      取 1360 为**实际约束**（更窄），可用 **1312px** ⇒ 52px + 40px 都不折行。
      ⚠️ 其它页不加 `.v2-body--home` ⇒ `main` 仍是 1080px、`.pub-container`
         仍是 1280px ⇒ **完全不变**（会员区/公开其它页零影响）。
      ⚠️ 用 body 类是因为 `main` / `.pub-container` 都是 `.pub-hero` 的**祖先**，
         CSS 没有\"父选择器\"。 */
body.v2-body--home main.pub-content {
  max-width: 1416px;
}
body.v2-body--home .pub-container {
  max-width: 1360px;
}

/*: §41b 首屏字号 + **句间留白**（★ 用户第 3 条：\"三句话距离没有拉开\"）
    ## 留白用\"显式正值\"（不再用 §3.5f 的反推负值）
      改前实测：H1→SUB = −39px、SUB→LEAD = −3px（挤在一起）
      ⇒ 用户要\"拉开\" ⇒ **明显加大**（不是 24/22px 那种\"看不出来\"的量）：
           H1 → SUB   = **48px**（视觉值）
           SUB → LEAD = **36px**（视觉值）
           LEAD → CTA = **40px**（视觉值）
      ⚠️ 视觉隙 = `margin-top` + 两行行盒的半留白 ⇒ **纸上算不出**，
         先用 `em` 给一组，再**实测反推**（见下方实测值）。
      ⚠️ `H1→SUB` 的\"实测负值\"是**测量口径**问题：英文 h1 里**嵌了**
         `.v2-hero-sub` 的 `<span>`（msgstr 如此）⇒ `createRange` 量 h1 末行时
         其实量到的是副标题那行 ⇒ 负值不代表真的重叠。以视觉为准。 */
.pub-shell .v2-hero--full .v2-hero-title {
  font-size: 3.25rem;           /* 52px（原 40px）★ 容器放宽后不折行 */
  line-height: 1.15;
  margin: 0;
}
.pub-shell .v2-hero--full .v2-hero-sub {
  display: block;               /* 独占一行 */
  font-size: 2.5rem;            /* 40px（原 32px）★ 不折行 */
  line-height: 1.25;
  margin-top: 0.8em;            /* ★ 视觉 H1→SUB ≈ 48px（实测反推） */
}
/*: 说明句：★ 放宽 + 加大与按钮的间距（用户 2026-10-02 第六轮）
    ## 两个问题的实测根因
      ① 三句偏移/孤字：说明句被 `§3.5f` 的 `max-width: 58rem`(928px) 限住，
         而 H1 块宽 **1312px** ⇒ 两行排不下 ⇒ **第 3 行只剩 \"potential move.\"**
         （实测行宽：864 / 895 / **137**（孤字））
      ② 按钮太靠上：`lead → CTA` 实测间距仅 **22px**。
    ## 修法
      · `max-width: 70rem`(1120px) ⇒ 两行排完、更饱满（且仍窄于 H1 的 1312）
      · 按钮 `margin-top: 2.4em` ⇒ 视觉间距 ≈ 48px
    ⚠️ 本句仍**允许折 2 行**（用户只要求前两句不折）。
    ⚠️ 必须带 `.pub-shell` 前缀：`.pub-shell h1 ~ p { max-width: 76ch }`(0,1,2)
      会命中本句，裸类 (0,1,0) 压不过。 */
.pub-shell .v2-hero--full .v2-hero-lead {
  font-size: 1.1875rem;         /* 19px（原 17px） */
  line-height: 1.7;
  max-width: 70rem;             /* ★ 1120px（原 928px）⇒ 2 行、不孤字 */
  margin: 1.6em auto 0;         /* ★ 视觉 SUB→LEAD ≈ 36px（实测反推） */
}
/*: 按钮：★ 往"下"放（用户 2026-10-02）
    · 实测改前 `lead → CTA` = **22px**（太挤）
    · 目标 ≈ **48px** ⇒ `margin-top: 2.4em`(约 46px)
    ⚠️ `public.css:394 .pub-hero p { margin: 0 auto 24px }` 给的是 24px
       ⇒ 用 `.pub-shell` 前缀 + 更高特异性覆盖。 */
.pub-shell .v2-hero--full .v2-hero-lead + .pub-cta {
  margin-top: 3.2em;
}

/*: §41b2 第二屏的小标题（`From momentum to conviction`）。
   ⚠️ 现状只有 20px —— 被本文件的正文段落规则压过 `public.css:395` 的 1.75rem。
   ⇒ 显式放大到 2rem(32px)，与 RPS 页 H1 同档（`--pub-page-title` 设计值）。 */
.pub-shell .v2-home-h2 {
  font-size: 2rem;
  line-height: 1.25;
  margin: 0 0 0.5em;
}

/*: §41c 第一张卡的上边框（用户：「第一个卡片框的上沿不见了」）
   ## 根因（实测）
     `v2.css:52` 的 `.v2-card:first-of-type { margin-top:0; padding-top:0;
     border-top:0 }` 是**旧的\"无边框清单\"设计**；而 `v2.css:2358` 又给了
     `.v2-card { padding:1.5em 1.75em; border:1px solid #e5e5e7; … }`
     ⇒ 两套设计叠加：只有**第一张**缺上边框，2~4 张有 ⇒ 看着像\"断了\"。
   ## 修法
     给首张补回与其余卡**完全相同**的上边框与上内边距。
     ⚠️ 必须比 `v2.css:52`（0,2,1）更具体 ⇒ 用 `.pub-shell .v2-card:first-of-type`
        （0,3,1）稳赢，且**本条在文件最后** ⇒ 也压过 2358 行的同特异性规则。 */
.pub-shell .v2-card:first-of-type {
  margin-top: 0;
  padding-top: 1.5em;                        /* = 2358 行的 padding（1.5em 1.75em） */
  border-top: 1px solid #e5e5e7;             /* = 2358 行的 border 颜色 */
}

/*: §41d 卡内正文占满卡宽（用户：「文字里卡片框的右边框都特远」）
   ## 根因（实测：卡宽 1000px，正文只有 751px ⇒ 右侧空 232px）
     本文件`.pub-shell h1 ~ p, .pub-shell h2 ~ p, .pub-shell h3 ~ p`
        { max-width: 76ch }（0,1,2）
     命中卡片里的 `<p class="v2-card-summary">`（它是 `h3` 的后续兄弟）
     ⇒ 正文被限死在 76ch（≈751px），而卡片是**满宽** ⇒ 右边一大片空。
   ## 修法
     卡片内正文 `max-width:none` ⇒ 撑满卡内宽度（≈968px）。
     ⚠️ 选择器 (0,3,0) > (0,1,2) ⇒ 稳赢，无需 !important。 */
.pub-shell .v2-card .v2-card-summary {
  max-width: none;
  /* ★ 2026-10-02 用户裁定：卡内正文的**段落换行要保留**。
     ## 为什么需要它
       卡片正文由 `ui.card(summary=...)` 传入，**只能是一个字符串**
       （宏里的 `{{ summary }}` 会**转义 HTML** ⇒ 不能用 `<br>`/`<p>`）。
       而 RPS 卡 / VCP 卡各有**两段原文**（`rps.html:97+98`、`vcp.html:48+49`）
       ⇒ 模板里用 `~ '\n\n'` 拼接（见 `home.html`）。
     ## 修法
       `white-space: pre-line` —— 保留**换行符**（`\n`）为换行，
       但**折叠多余空格**（不像 `pre` 那样连缩进空格都保留）。
       ⇒ 不污染文案（msgid 仍是原文），也不改共享宏 `_ui.html`。 */
  white-space: pre-line;
}

/*: §41e ★ 卡片标题放大（用户 2026-10-02：「Find the leaders / Identify the
    strong momentums 等，这些标题都太小」）
   ## 根因（实测，又是同类\"通用规则压专用类\"）
     卡片标题是 `<h3 class=\"v2-card-title\">` ⇒ 同时命中两条：
       · `v2.css:63  .v2-card-title { font-size: 1.375em }`   (0,1,0)
       · `v2.css:913 .pub-shell h3   { font-size: 1.0625em }`  (0,1,1)  ← 赢
     ⇒ 实际渲染 **1.0625em（≈17px）**，设计值 1.375em（22px）**从未生效**。
   ## 修法
     用 `.pub-shell .v2-card .v2-card-title`（0,3,0）稳赢 ⇒ 放大到 **1.5em**（24px），
     与 RPS 页的层级感一致（页内 h2 32px / 卡标题 24px / 正文 16px）。
     ⚠️ 不改 `v2.css:63`（那是组件库基础值，别处 `ui.card` 也在用）；
        本条只作用于**新站 `.pub-shell` 内**的卡片。 */
.pub-shell .v2-card .v2-card-title {
  font-size: 1.5em;             /* ★ 24px（原被压到 17px） */
  margin: 0 0 0.5em;
}
/*: 同理：卡内\"阅读更多\"链接也放大一档（原来跟正文同号、偏小） */
.pub-shell .v2-card .v2-card-more {
  font-size: 1.0625em;
}


/* ============================================================================
   §42 ★ 顶栏语言切换 = 胶囊分段控件（2026-10-02 用户裁定）
   ----------------------------------------------------------------------------
   ## 用户要求（大白话）
     「把 .ai 站的中英文选择改成这种样子的（**胶囊 + 选中项实心高亮**），
       不过风格用我们自己的，像订阅页里那个 monthly billing 和 annual billing
       中间那个 toggle 的颜色什么的，我们只有两个选择，**EN / 中文**」
   ## 与订阅页那个\"开关\"的区别（**不是照搬**）
     订阅页是 **switch 轨道 + 滑块**（两态互斥、有滑动动画）；
     用户要的是**分段控件（segmented）**：两格并排，**选中的那格实心高亮**。
     ⇒ **只借配色**：`--v2-plan-hi`(#0EB2AA，品牌青绿) + 白字（见 v2.css:1009）。
   ## 结构（模板 `_topbar.html` 两处，未登录①/已登录②）
     <span class="v2-lang-switch">
       <span class="v2-lang-opt is-on">EN</span>      ← 当前语言（不可点）
       <a    class="v2-lang-opt" href=...>中文</a>     ← 可切
     </span>
     ⚠️ 零 JS：当前语言用 `<span>`（无 href）⇒ 天然\"选中态\"。
   ## ⚠️ 铁律（§-1 三个域名）
     本控件**只在 `.ai` 且未登录/已登录时渲染**；`.ca` / `maimaistock.com`
     是封闭站 ⇒ 模板里 `{% if not lang_closed %}` 直接不输出（**已保持**）。
   ## ⚠️ 为什么用新类名 `.v2-lang-switch` 而不是复用旧 `.lang-switch`
     旧 `.lang-switch`（`public.css`/`app.css`）是**文字链接**样式
     （下划线、蓝色、`.small` 字号）⇒ 复用会带一堆要覆盖的旧规则。
     ⇒ 新类名 + 独立样式，**旧类不再被引用**（`_topbar.html` 已全部替换）。
   ============================================================================ */

/*: 外壳胶囊：★ 2026-10-02 用户裁定「太肥大」⇒ **去掉灰底外壳**，只留一条极窄的缝。
   ## 改前（肥大根因）
     `padding:2px` 灰底 + 每格 `min-width:38px` + `padding:3px 8px`
     ⇒ 两层内边距叠加 ⇒ 视觉上一个\"大灰饼\"。
   ## 🔴 又一个\"通用规则压专用类\"（第 6 次）
     `public.css:188 .topbar--public .pub-nav a { … height:100% … }`（**0,2,1**）
     压过裸类 `.v2-lang-opt`（0,1,0）⇒ 每格被**拉高到顶栏高度（60px）**。
     实测：`.v2-lang-opt` 高度 60px（应为 18px）⇒ 外壳也被撑成 80×60。
     ⇒ 本节所有选择器**都加 `.topbar--public .pub-nav` 前缀**（0,3,x）稳赢。
   ## 改后
     · 灰底**取消**（`background: transparent`）
     · 外壳 `padding:0`、`gap:2px` ⇒ 只剩两格本身
     · 每格 `min-width:30px` / `height:18px` / `padding:0 7px` ⇒ 紧致
     · 选中格仍是**青绿实心**（`--v2-plan-hi`）⇒ 一眼看出当前语言
   ⚠️ 保留 `role=group` + `aria-label`（无障碍不变）。 */
.topbar--public .pub-nav .v2-lang-switch {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 0;
  height: auto;                  /* 覆盖 `.pub-nav a` 系规则的 height:100% */
  margin-left: 10px;
  border-radius: 999px;
  background: transparent;       /* ★ 取消灰底（原 #f0f0f2 太肥） */
  vertical-align: middle;
  align-self: center;            /* ★ 不被顶栏 flex 拉伸 */
}
/*: 每一格（EN / 中文）—— 未选中：灰字、透明底；选中：青绿实心白字 */
.topbar--public .pub-nav .v2-lang-opt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;               /* ★ 38 → 30（收紧） */
  height: 18px;                  /* ★ 钉死高度（原来被顶栏拉成 60px） */
  padding: 0 7px;                /* ★ 3px 8px → 0 7px（高度由 height 定） */
  border-radius: 999px;
  font-size: 12px;
  line-height: 1;
  font-weight: 600;
  letter-spacing: normal;        /* 覆盖顶栏菜单的 letter-spacing */
  text-transform: none;
  color: #6b7280;                /* 未选：中灰 */
  background: transparent;
  text-decoration: none;         /* 覆盖 `.pub-body a` 的蓝色/下划线 */
  cursor: pointer;
  user-select: none;
  box-sizing: border-box;
  transition: background-color .14s ease, color .14s ease;
}
.topbar--public .pub-nav .v2-lang-opt:hover { color: #374151; }
/*: 选中格：**品牌青绿实心 + 白字**（= 订阅页 toggle 的 `--v2-plan-hi`） */
.topbar--public .pub-nav .v2-lang-opt.is-on {
  background: var(--v2-plan-hi);
  color: #fff;
  cursor: default;
}
.topbar--public .pub-nav .v2-lang-opt.is-on:hover { color: #fff; }

/*: 手机档（`v2.css` §5.2 断点 = **1140px**）：
   ★★ 2026-10-02 用户实机反馈（iPhone 12 Pro Max / Edge）：
     ① 「语言选择放在菜单的中间去了」—— 实测 `pill.x=182`（菜单宽 365）
        ⇒ 胶囊**水平居中**、不在左边，看着像\"浮在中间\"。
        根因：菜单是 `flex-direction: column` + `align-items: stretch`，
        而我桌面写的 `align-self: center` 在这里让它**不拉伸、居中**。
     ② 「怎么点 EN 都是中文」—— 实测每格只有 **28×18px**（远小于
        Apple 建议的 **44×44px** 触控目标）⇒ **手指点不中**（点在胶囊空白处，
        而 `.v2-lang-switch` 外壳本身不是链接）。
   ⇒ 修法：手机上
        · 外壳 `align-self: stretch` + 左对齐（不再居中）
        · 两格**加大到 40px 高**、左右各留内边距 ⇒ 好点
        · 整块与菜单项同宽，视觉上就是一行\"语言：EN / 中文\" */
@media (max-width: 1240px) {
  .topbar--public .pub-nav .v2-lang-switch {
    align-self: stretch;            /* ★ 不再居中（原 center 造成\"浮中间\"） */
    justify-content: flex-start;    /* ★ 靠左 */
    margin: 0;
    padding: 8px 32px;              /* 与菜单项左右对齐（见 §5.2 ⑤ 的 32px） */
    background: transparent;
  }
  .topbar--public .pub-nav .v2-lang-opt {
    min-width: 56px;                /* ★ 28 → 56px（触控目标更宽） */
    height: 36px;                   /* ★ 18 → 36px（更接近 44px 建议值） */
    padding: 0 14px;
    font-size: 14px;                /* 与菜单项字号一致（14px） */
  }
}


/* ============================================================================
   §报告表列宽（2026-10-07 · plan_watchlist_reports_tab Q22）
   ★ 列宽由**模板 colgroup 的百分比**决定（合计 100%）⇒ 绝不横向溢出。
   本类只负责：fixed 布局 + 单元格换行/按钮不撑破列。
   ============================================================================ */
.v2-rtable { table-layout: fixed; width: 100%; }
.v2-rtable th, .v2-rtable td { vertical-align: top; }
/* 表头不换行（短列名不被裁成两行）*/
.v2-rtable th { white-space: nowrap; }
/* 摘要单元格：正常换行、不撑破列 */
.v2-cell-sum { white-space: normal; word-break: break-word; line-height: 1.5; }
/* Tab1 名称列：**不格内换行**（用户 2026-10-07）*/
.v2-nowrap-cell { white-space: nowrap; }
/* fixed 布局下名称不换行且不撑破 */
.v2-rtable .v2-nowrap-cell { overflow: hidden; text-overflow: ellipsis; }
/* ★ 板块/行业名：**最多 2 行**（用户 2026-10-07）——超出省略 */
.v2-rtable td.v2-industry {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* ★ 第一列的策略标签：逐个可换行、不溢出、不与邻列重叠 */
.v2-rtable td .v2-tag { display: inline-block; margin: 0 0.25em 0.2em 0; }
/* 窄列内的按钮：不撑破列（过长则截断，不引发横滚） */
.v2-rtable td, .v2-rtable th { overflow-wrap: anywhere; }
.v2-rtable .v2-inline-form { max-width: 100%; }
/* ★ 报告表 Report/Deep 两列：按钮**正常字号**（与 View 一致）+ **格内居中** */
.v2-rtable td.v2-cell-act {
  text-align: center;
  white-space: nowrap;
}
/* 格内所有可见元素（裸 <a> / form 包裹的按钮 / 徽章）**与裸 <a> 同构**
   ⇒ View（a）与 Run（form>button）逐像素对齐（用户 2026-10-07，要"一模一样构造"）*/
.v2-rtable td.v2-cell-act > a,
.v2-rtable td.v2-cell-act > span {
  display: inline;
  vertical-align: baseline;
  margin: 0;
}
/* ⚠️ form 必须还原为 inline（它默认/本仓是 inline-flex + margin ⇒ 会推偏）*/
.v2-rtable td.v2-cell-act > form.v2-inline-form,
.v2-rtable td.v2-cell-act > form {
  display: inline;
  margin: 0;
  padding: 0;
  gap: 0;
}
/* 按钮/链接还原为普通文字大小（不要缩小、不要深色大块、不要换行）*/
.v2-rtable td.v2-cell-act .v2-btn {
  display: inline;
  min-height: 0;
  padding: 0;
  margin: 0;
  font-size: inherit;      /* ★ 与表格/View 同字号 */
  line-height: inherit;
  border: 0;
  background: none;
  color: #0076ff;
  vertical-align: baseline;
  white-space: nowrap;
}
.v2-rtable td.v2-cell-act .v2-btn:hover { text-decoration: underline; }
/* form 内隐藏的 input 不影响布局 */
.v2-rtable td.v2-cell-act form input[type="hidden"] { display: none; }
/* ★ 本表**永不出横向滚动**（用户 2026-10-07 要求） */
.v2-rtable-scroll { overflow-x: hidden; }

   一收件人一行（无横向滚动）；来源以彩色小标签区分（env / 默认 / 库）。 */
.v2-mail-list {
  list-style: none;
  margin: 0.75em 0 0;
  padding: 0;
}
.v2-mail-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5em;
  padding: 0.5em 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.v2-mail-addr {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.95em;
}
.v2-mail-tag {
  display: inline-block;
  padding: 0.1em 0.5em;
  border-radius: 3px;
  font-size: 0.8em;
  font-weight: 600;
  line-height: 1.6;
}
.v2-mail-tag--env { background: #e7f0ff; color: #1a4bd6; }
.v2-mail-tag--default { background: #fff3e0; color: #b25d00; }
.v2-mail-tag--db { background: #e6f7ec; color: #1a7f43; }




/* ============================================================================
   ★★ 需求 8/9/10/11（2026-10-15 · plan_legacy_style_restore · 重做版）
     用户总原则：「**必须跟旧站一模一样**」
     ★★ 事故教训（8ceb57ab 已 revert）：**只改「外观」，⛔ 绝不改「布局机制」**
        （上次破坏了 flex 收缩/滚动容器/吸顶 ⇒ 表格被撑到 1836px ⇒ 撑破页面）
     以下数值来自**旧站本机实测**（Playwright + Chrome 量出）：
       VCP/picks 表：border 1px + padding 6px 8px + 表头 rgb(238,241,245) + 14.4px
       报告正文卡：白底 + 1px + 圆角8 + padding 18px 22px，宽 1180
       报告页：main 1560（正文 1180 + 笔记 320）
   ============================================================================ */

/* ---------------------------------------------------------------------------
   需求 8：**表格外观**照旧站（只改视觉）
   ## ⛔⛔ 铁的边界（上次翻车点）
      · **不动** width / table-layout / overflow / position 任何**布局**属性
      · **不动** 外层 `.v2-table-scroll`（保持 flex 里的收缩行为）
      · **保留** `.v2-table thead th` 的 sticky（上次删了它 = 破坏行为）
   --------------------------------------------------------------------------- */
.v2-table--legacy th,
.v2-table--legacy td {
  border: 1px solid var(--line, #e2e4e8);
  padding: 6px 8px;
  font-size: 0.9rem;
}
.v2-table--legacy thead th { background: #eef1f5; }
/* 旧站无斑马纹 ⇒ 取消（纯视觉） */
.v2-table--legacy tbody tr:nth-child(even) { background: transparent; }

/* --- ★ 2026-10-17 笔记历史列宽（用户裁定）---
   ① 名称列至少 4 个汉字宽（原来窄到"一字一行"）；
   ② 多出来的宽度从「笔记」列匀出（auto 布局下自动让出）；
   ③ 列头一律不换行（「笔记价格」「报告」不再竖排）。
   ⚠️ 该表同时用于「笔记历史」页与「观察名单 → 笔记历史」Tab（同 id）。 */
#v2-nh-table th { white-space: nowrap; }
#v2-nh-table th:nth-child(3),
#v2-nh-table td:nth-child(3) {
  min-width: calc(4em + 16px);     /* 4 个汉字 + 左右内边距（8px×2） */
  width: calc(4em + 16px);
}
#v2-nh-table th:nth-child(4),
#v2-nh-table td:nth-child(4) {
  width: 1%;                       /* 收缩到列头宽度（「笔记价格」4 字） */
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   需求 9：报告正文**白底卡片**（旧站实测 `.report-md`）
      白底 / border 1px / radius 8px / padding 18px 22px
      ⚠️ 只加**视觉**；max-height/overflow-y 保持需求 3 已有的（内滚）
      ⚠️ 作用域限**报告详情页** ⇒ ⛔ 不影响博客
   --------------------------------------------------------------------------- */
.v2-report-body .v2-post-body {
  background: #fff;
  border: 1px solid var(--line, #e2e4e8);
  border-radius: 8px;
  padding: 18px 22px;
  line-height: 1.7;
}
.v2-report-body .v2-post-body::-webkit-scrollbar { width: 10px; }
.v2-report-body .v2-post-body::-webkit-scrollbar-thumb { background: #c3ccd8; border-radius: 5px; }
.v2-report-body .v2-post-body::-webkit-scrollbar-track { background: transparent; }

/* ---------------------------------------------------------------------------
   需求 10：报告页**宽 1560**（旧站 `main{max-width:1560px; padding:0 16px}`；
   正文 1180 + 笔记 320）
   ## ⚠️ 真正的墙是**内层** `.pub-container`（public.css:135 max-width:1280）
      ⇒ 必须放开**那一层**（`:has()` 只对报告页生效）
   ## ★ 连**内边距**也照旧站（旧站 16px；新站 24px）⇒ 总宽 1560、内容 1528 才完全一致
   --------------------------------------------------------------------------- */
.pub-content:has(.v2-report-page) > .pub-container {
  /* ★ 旧站 `main{max-width:1560px; padding:0 16px; box-sizing:border-box}`
     ⇒ **内容宽 = 1528**。新站多一层 `.pub-container` ⇒ 这里直接把内容宽钉成 1528。 */
  max-width: 1528px;
  padding-left: 0;
  padding-right: 0;
}
.v2-report-body { max-width: 1180px; }          /* 旧站 `.report-body{max-width:1180px}` */
@media (max-width: 900px) {
  .pub-content:has(.v2-report-page) > .pub-container { max-width: 100%; }
  .v2-report-body { max-width: 100%; }
}


/* ============================================================================
   ★★★ Phase 1 批次 A（2026-10-16）：报告页「照旧站」像素级覆盖
   ## 依据：docs/plan_report_page_parity.md（裁定 R1–R10）
     把会员区报告页（`/member/<market>/report/<code>`）在**报告页范围内**
     按旧站报告页（`/_legacy/reports/<id>`）的**实测渲染结果**对齐。
   ## 作用域（★ 关键）
     一律以 `.pub-content:has(.v2-report-page)` 为根 ⇒ **只命中报告页**；
     顶栏/底栏与其它页面**零影响**（用户裁定 R6）。
   ## 红线
     · ⛔ 不改 `app.css` / `public.css` 源文件，也不改全局令牌（`--pub-text` / `--pub-body-lh`）
     · 数值全部来自旧站真源，逐条注明出处
   ## 验收
     `~/.qv-verify/rerun.sh`（度量）+ `~/.qv-verify/visual_diff.py`（机器目视）
     批次 A 前基线：硬差异 **500**（2026-10-16 口径）
   ---------------------------------------------------------------------------- */

/* A1–A3 页面级三条（正文色 / 行高 / 字体/ 底色）—— ★★ **已上移到公共底座**
      （见文件末尾「会员区公共底座」`body.v2-body--legacy`；R11=甲）。
      报告页现在靠模板挂 `v2-body--legacy` 生效，本处不再重复定义。 */

/* A4 H1 → ★★ **已上移到公共底座 D8**（`body.v2-body--legacy h1.pub-page-title`） */

/* A5 元信息行 → ★★ **已上移到公共底座 D9**（`body.v2-body--legacy p.v2-art-meta`） */

/* A5b 状态胶囊（元信息行里的 success/failed）= 旧站 `.tag` + `.st-*`
      （`app.css:294` `border-radius:10px; padding:1px 8px; font-size:.78rem; color:#fff`
        ＋ `app.css:307-310` 的实心底色）
      新站 `.v2-tag`（`v2.css:1113`）是**圆角 3px + 浅底深字** ⇒ 把元信息行的行盒顶高
      1.56px（实测 旧 19px vs 新 20.56px，进而让整卡文字下移、机器目视热力图出现成片重影）。
      ⚠️ 只作用于**元信息行内**的胶囊 ⇒ 不影响观察名单/VCP 等页的 `.v2-tag`。 */
body:has(.v2-report-page) p.v2-art-meta .v2-tag {
  display: inline;                  /* ★ 旧站 `.tag` 未设 display ⇒ 默认 inline（不撑行盒） */
  margin: 0;
  padding: 1px 8px;
  border-radius: 10px;
  background: #868e96;              /* 兜底 = 旧站 `.st-queued`（app.css:307） */
  color: #fff;
  font-size: .78rem;
  white-space: normal;
}
body:has(.v2-report-page) p.v2-art-meta .v2-tag-ok { background: var(--ok); }   /* = .st-success */
body:has(.v2-report-page) p.v2-art-meta .v2-tag-err { background: var(--err); } /* = .st-failed */

/* A6 表格上下间隔 = 旧站**同款机制**（★ 批次 B 改为"照搬旧站 JS"，见 `report.html` 内联脚本）：
      旧站 `base.html:267-290` 给"装得下"的 `.table-scroll` 加 `fits` ⇒ `overflow:visible`
      （`app.css:285`）⇒ 表格 `margin:10px 0` **穿透**外层 div；装不下则保留 `overflow:auto` 横向滚动。
      新站原先没有这段 JS ⇒ 所有表格的 div 都成了 BFC、多吃 20px（5 个表共 +100px 内容高）。
      ⇒ 本批**照搬该 JS**（页面内联），因此这里**不再**搬动 margin（声明与旧站逐字一致）。 */

/* A7 卡片内表格 = 旧站口径（裁定 R7：4 处"有意增强"全部回退）
      旧站 `th,td` 继承 `.report-md` 的 `overflow-wrap:break-word`（app.css:442/461）且**不设**
      `white-space`（表头可折行）；新站 v2.css:561/565 是 `anywhere`/`nowrap` ⇒ 改变列宽、
      实测让 1 行多折一行（+23.9px）。 */
.pub-content:has(.v2-report-page) .v2-post-body th { white-space: normal; }
.pub-content:has(.v2-report-page) .v2-post-body th,
.pub-content:has(.v2-report-page) .v2-post-body td { overflow-wrap: break-word; }

/* A8 列表项间距 = 旧站口径（旧站 `.report-md` 未给 `li` 边距；新站 v2.css:548 给了 `.15em`，
      实测 `ol` 高 +11.9px） */
.pub-content:has(.v2-report-page) .v2-post-body li { margin: 0; }

/* ============================================================================
   ★★ Phase 1 批次 B（2026-10-16 · R2/R10）：结构对齐
   ## 模板侧（`v2/member/report.html`）
     · 去掉 `section.v2-report-block` + `h2`（mode 改徽标进元信息行）
     · 删英文版提示分支；H1 内链接改行内 `small`
   ## 这里只补"卡片与元信息行的关系"
   ---------------------------------------------------------------------------- */

/* B1 卡片**紧接**元信息行：旧站 `.report-md` 无上边距（`app.css:440-445` 只给 padding），
      而新站 `v2.css:479 .v2-post-body{margin-top:1.5em}` ⇒ 本页压回 0。 */
.pub-content:has(.v2-report-page) .v2-report-body > .v2-post-body { margin-top: 0; }

/* B2 多份报告（简版 + 深研）时，卡片之间留白（旧站是两列，新站上下排列 ⇒ 取页面常规块距） */
.pub-content:has(.v2-report-page) .v2-report-body > .v2-post-body ~ .v2-post-body {
  margin-top: 24px;
}

/* B3 手机档（≤900px）= 旧站 `app.css:735-742`
      · 旧站 `main { padding: 0 16px }` 是**唯一**的左右留白；新站在手机档还多一层
        `.pub-shell { padding:16px; margin:12px 0 24px; background:card-bg; border-radius:12px }`
        （`public.css:446-450`，只在手机档生效）⇒ 实测内容左边距 32px vs 旧站 16px（卡片窄 32px ⇒
        折叠更多行 ⇒ 卡片高 +958px）。⇒ 报告页内把这一层**归零**（旧站没有这层）。
      · 旧站手机档 `.report-page { display: block }`（新站是 `flex-direction:column`）⇒ 折叠成 block。 */
@media (max-width: 900px) {
  .pub-content:has(.v2-report-page) .v2-report-page { display: block; }
  /* ⚠️ `.pub-shell` 归零 与 `main` 手机档宽度：★★ 已上移到公共底座（D6/D7） */
}

/* ============================================================================
   ★★★ 会员区公共底座（`.v2-body--legacy`，2026-10-16 · 裁定 R11=甲 / R12）

   ## 为什么有这一段
     报告页当时把 7 条"整页级"差异逐页 scoped 覆盖；本次要做 watchlist / 笔记历史 /
     形态分析，若继续逐页复制 = 7×N 条重复且迟早漂移 ⇒ 抽成公共底座，**一次生效**。
   ## 怎么挂（R12：只给范围内的页挂）
     页面模板加 `{% block body_class %} v2-body--legacy{% endblock %}`
     （`v2/base.html:79` 已支持该 block ⇒ 不改共享 base）。
     ⛔ 未挂的页（RPS / 博客 / console / 后台 / 公开层）**一行都不受影响**。
   ## 底座内容（D1–D7，全部来自旧站真源）
     D1 正文色 `#23272f`（app.css:4 `--text`）
     D2 行高 `normal`（旧站 body 未设；新站 public.css:63/130 给了 22.8571px）
     D3 字体去掉 `InterVar`（旧站只加载 app.css ⇒ 无 @font-face；实测同串宽度数字 169.94→163.00）
     D4 页面底色 `#f5f6f8`（app.css:3 `--bg`；新站 pub-body 是白底 public.css:132）
     D5 表格 `fits` JS（见 `v2/_scripts.html` ⓪，照 `base.html:267-290`）
     D6 手机档 `.pub-shell` 归零（旧站无这层；public.css:446-450 会多 16px 边距与白底）
     D7 `main` 宽度（旧站 `body.page-report main{max-width:1560px}` app.css:607；
        新站被通用 `main{max-width:1080px}` app.css:199 卡住 ⇒ 901px 以上放开）
   ## ⛔ 底座不含
     顶栏 / 底栏 / 任何"某页才有的组件样式"（报告卡片、笔记面板、检索素材区、VCP 卡片…）
     —— 那些仍留在各自页面作用域里。
   ---------------------------------------------------------------------------- */
body.v2-body--legacy {
  color: #23272f;                  /* D1 */
  line-height: normal;             /* D2 */
  background: #f5f6f8;             /* D4 */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
               "Microsoft YaHei", "Hiragino Sans GB", Arial, sans-serif;   /* D3 */
}

@media (min-width: 901px) {
  /* ⚠️ D7 **修正（2026-10-16 · P2 实测）**：旧站各页宽度**不一样** ——
       报告页 `body.page-report main{max-width:1560px}`（app.css:607）；
       其它页用通用 `main{max-width:1080px}`（app.css:199）。
     ⇒ 底座**不再统一写 1560**（那会把观察名单撑宽 200px：实测旧 1048 vs 新 1232），
       改为：底座只把 `.pub-container` 的 24px 内边距归零，宽度交给 main
       （app.css 的 1080 + 它自带的 `padding:0 16px` ⇒ 内容 = 1080-32 = 1048 ✅ 与旧站一致）；
       报告页另在自己的作用域里放开到 1560。 */
  body.v2-body--legacy .pub-container { padding-left: 0; padding-right: 0; }
}
/* ⚠️ 上面这条**不能只写在 ≥901px 里**：390 档实测内容 x=32（main 16 + `.pub-container`
   手机档 16），而旧站只有 main 的 16px ⇒ 底座在**所有宽度**都把容器内边距归零。 */
body.v2-body--legacy .pub-container {
  padding-left: 0;
  padding-right: 0;
}

/* D12 手机档表格 = 旧站 `app.css:286`：`.table-scroll table.data { min-width: 900px }`
   ⇒ 窄屏**靠横滚**而不是压列（实测 390 档旧站表格宽 900、新站 326）。 */
body.v2-body--legacy .v2-table-scroll table { min-width: 900px; }
/* ⚠️ 卡片里的表格**不能**被上面那条撑到 900px（旧站卡片内表格是自然宽度 520） */
body.v2-body--legacy .v2-vcp-card table,
body.v2-body--legacy .v2-vcp-card .v2-table-scroll table { min-width: 0; }

/* D8 H1（2026-10-16 · P2 补）：旧站 `h1{font-size:1.35rem}`（app.css:200）+ 浏览器默认
   `margin:.67em`；新站是 `.pub-page-title{font-size:1.75rem}`（public.css:395）
   ⇒ 所有挂底座的页统一压回旧站（报告页原先的 A4 规则已并入这里）。 */
body.v2-body--legacy h1.pub-page-title {
  font-size: 1.35rem;
  line-height: normal;             /* ★ P2 实测：不改行高 ⇒ h1 高 25px vs 旧站 30px */
  margin: 0.67em 0;
}

/* D9 元信息/统计行（2026-10-16 · P2 补）：旧站这些行就是普通 `p.muted` ——
   16px（admin 行 `.muted.small` = 12.8px）/ 行高 normal / 满宽 / 默认 16px 边距 / `--muted #7a808a`。
   新站被 `v2.css:1250-1256`（`.pub-shell h1 ~ p{max-width:76ch;line-height:1.7;margin:.7em 0}`）
   + `.pub-small`（14px）压成窄行并折行（实测 676.9px 宽、行高 23.8px）。 */
body.v2-body--legacy p.v2-art-meta,
body.v2-body--legacy p.v2-stat-line {
  max-width: none;
  line-height: normal;
  margin: 16px 0;
  font-size: 16px;
  color: #7a808a;                  /* = app.css:4 --muted */
}
body.v2-body--legacy p.v2-admin-stat-line,
body.v2-body--legacy p.v2-nh-count-line {
  max-width: none;
  line-height: normal;
  margin: 12.8px 0;                /* = `.muted.small` 的 .8rem 默认段距 */
  font-size: .8rem;                /* = 旧站 `.small{font-size:.8rem}`（app.css:292） */
  color: #7a808a;
}

/* D10 筛选条（2026-10-16 · P2 补）= 旧站 `.filter`（app.css:483-486）：
      `display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap` +
      `label{margin:0}` / `input,select{width:auto}` / `button{margin:0}`；
      旧站 form 自身没有外边距。 */
body.v2-body--legacy form.v2-filter-form {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  flex-wrap: wrap;
  margin: 0;
}
body.v2-body--legacy form.v2-filter-form label { margin: 0; }
/* ★ P2 实测：新站 `.v2-console-form > label{display:flex;flex-direction:column;gap:.25em}`
   （`v2.css:1083-1086`）把"文字在上、控件在下" ⇒ 整条 55.8px；旧站 label 是 `display:block`
   且 `select` 为 inline-block ⇒ 同行 ⇒ 35px。 */
body.v2-body--legacy form.v2-filter-form > label {
  display: block;                  /* = 旧站 `app.css:218 label{display:block}` */
  margin: 0;
}
body.v2-body--legacy form.v2-filter-form input,
body.v2-body--legacy form.v2-filter-form select {
  width: auto;
  display: inline-block;           /* ★ 旧站 `select` 是 inline-block ⇒ 与 label 文字同行，整条 35px；
                                      新站 `.v2-input` 让它成块级 ⇒ 文字在上、下拉在下 ⇒ 整条 55.8px */
  padding: 7px 9px;                /* = app.css:219 */
  border: 1px solid var(--line);
  border-radius: 6px;
  font: inherit;
}
body.v2-body--legacy form.v2-filter-form button {
  margin: 0;
  padding: 8px 16px;               /* = app.css:221 的 `button` */
  border: 0;
  border-radius: 6px;
  min-height: 0;                   /* 压掉 `.v2-btn` 的 38px 最小高 */
  font: inherit;
}

/* D13 H1 内的「手动加股」胶囊（2026-10-16 · P2 目视发现 · 一律照旧站）
      旧站：`<h1>Screening results <a class="add-stock-btn">＋手动加股</a></h1>`
      （`browse.html:6`；样式 `app.css:573-577`：inline-block / margin-left:12 / padding 4px 14px /
       1px 边框 accent / 圆角 999px / .85rem / vertical-align:middle）
      新站：`<details>` 放进 `<h1>`（弹层机制保留 ⇒ 功能不回退）。 */
body.v2-body--legacy h1.pub-page-title details.v2-user-create { display: inline-block; }
body.v2-body--legacy h1.pub-page-title details.v2-user-create > summary {
  display: inline-block;
  margin-left: 12px;
  padding: 4px 14px;
  border: 1px solid var(--accent);
  color: var(--accent);
  border-radius: 999px;
  font-size: .85rem;
  line-height: normal;
  cursor: pointer;
  list-style: none;
  background: none;
  vertical-align: middle;
}
body.v2-body--legacy h1.pub-page-title details.v2-user-create > summary::-webkit-details-marker {
  display: none;
}
body.v2-body--legacy h1.pub-page-title details.v2-user-create > summary:hover {
  background: var(--accent);
  color: #fff;
}

/* D14 实心按钮 = 旧站 `button`（`app.css:221-222`：`background:var(--accent); color:#fff;
      border:0; border-radius:6px; padding:8px 16px`）。
      ⚠️ 新站有个既有覆盖问题：`.v2-btn`（`v2.css:3235-3243`，深藏青 `#0a2046`）写在
        `.v2-btn--solid`（`v2.css:150-153`，蓝色 `#0076ff`）**之后** ⇒ solid 的蓝色被吃掉，
        实测筛选按钮是深藏青而旧站是亮蓝。这里按旧站压回 accent。 */
body.v2-body--legacy .v2-btn--solid,
body.v2-body--legacy .v2-btn:not(.v2-btn--link):not(.v2-btn--ghost):not(.v2-btn--outline) {
  background: var(--accent);
  color: #fff;
  border: 0;
  border-radius: 6px;
  padding: 8px 16px;
  min-height: auto;                /* 旧站按钮没有 min-height（新站 `.v2-btn` 有 38px） */
  font: inherit;
}

/* ============================================================================
   D19 表格**列宽机制** = 旧站（auto 布局 + 两处 min-width）
      ★ 2026-10-16 用户反馈「摘要列太窄、有些列太宽」⇒ 实测新站**每一列都 149.6px**
        （= 1048/7）⇒ 根因：`.v2-rtable{table-layout:fixed}`（`v2.css:3661`）
        在 **R13 拆掉 colgroup 之后退化成"等宽分列"**。
      旧站真源（`app.css:286-289`）：auto 布局 + `td.td-name{min-width:110}` +
        `td.td-summary{min-width:300}` + `td.td-summary{overflow-wrap:anywhere}`。
   ---------------------------------------------------------------------------- */
body.v2-body--legacy .v2-table--legacy { table-layout: auto; }
body.v2-body--legacy .v2-table--legacy th.v2-cell-name,
body.v2-body--legacy .v2-table--legacy td.v2-cell-name { min-width: 110px; }
body.v2-body--legacy .v2-table--legacy th.v2-cell-sum,
body.v2-body--legacy .v2-table--legacy td.v2-cell-sum {
  min-width: 300px;
  width: 300px;                    /* 旧站摘要列实测就是 300（auto 布局下它的"内容宽"上限） */
  max-width: 300px;
  overflow-wrap: anywhere;
}
/* 旧站行业列**不做 2 行截断**（`.v2-rtable td.v2-industry` 的 line-clamp 是 fixed 布局的补丁） */
body.v2-body--legacy .v2-table--legacy td.v2-industry {
  display: table-cell;
  -webkit-line-clamp: none;
  overflow: visible;
}
/* ★ 2026-10-20 用户裁定：观察名单行业列 **6 个汉字宽**，超出换行 */
body.v2-body--legacy .v2-table--legacy th.v2-cell-industry,
body.v2-body--legacy .v2-table--legacy td.v2-industry {
  width: calc(6em + 16px);
  min-width: calc(6em + 16px);
  max-width: calc(6em + 16px);
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* ============================================================================
   §报告表列宽（2026-10-19 用户改判）
   名称/行业各 **5 个汉字宽**；报告/深研缩窄；摘要吃剩余。
   ⚠️ 仅 `v2-rtable--reports`（观察名单 Tab2）用 fixed+colgroup；
      其他 legacy 表仍保持 D19 的自动列宽。
   ============================================================================ */
body.v2-body--legacy .v2-table--legacy.v2-rtable--reports {
  table-layout: fixed;
}
.v2-rtable--reports .v2-col-fml { width: 7.5em; }
.v2-rtable--reports .v2-col-code { width: calc(5em + 16px); }
.v2-rtable--reports .v2-col-name { width: calc(5em + 16px); }
.v2-rtable--reports .v2-col-industry { width: calc(5em + 16px); }
.v2-rtable--reports .v2-col-stage { width: calc(3em + 16px); }
.v2-rtable--reports .v2-col-report { width: calc(3.5em + 16px); }
.v2-rtable--reports .v2-col-deep { width: calc(3.5em + 16px); }
/* 摘要列吃掉剩余；覆盖 legacy 的 300px 固定宽 */
body.v2-body--legacy .v2-rtable--reports th.v2-cell-sum,
body.v2-body--legacy .v2-rtable--reports td.v2-cell-sum {
  width: auto;
  min-width: 0;
  max-width: none;
}
/* 名称列：取消 legacy 的 110px 最小宽（否则 5em 失效） */
body.v2-body--legacy .v2-rtable--reports th.v2-cell-name,
body.v2-body--legacy .v2-rtable--reports td.v2-cell-name {
  min-width: 0;
}
/* 名称/行业：窄列内**自动换行**（用户 2026-10-19：不要省略号/溢出） */
.v2-rtable--reports td.v2-cell-name,
.v2-rtable--reports td.v2-industry {
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
}
/* 深研表头允许英文换行（Deep research）；中文仍是 2 字 */
.v2-rtable--reports th.v2-cell-deep { white-space: normal; }


/* ============================================================================
   D18 小字说明行 = 旧站 `p.muted.small`（`.small` = `.8rem` = 12.8px，muted `#7a808a`）
      ★ 由**目视**发现：VCP 页的「Tier definition / Today's pool changes / N stocks」等说明行
        新站是 14px（`.pub-small`），旧站是 12.8px ⇒ 视觉上"新站正文更大"。
      只在 VCP 页生效（其它页已各自对齐过）。
   ---------------------------------------------------------------------------- */
body.v2-body--legacy:has(.v2-vcp-head-holder) p.pub-small {
  font-size: .8rem;
  color: #7a808a;
  line-height: normal;
  margin: 6px 0;
}


/* ============================================================================
   D17 单票卡片 = 旧站 `.card.vcp-card`
      真源：`app.css:202`（`.card{background:var(--card);border:1px solid var(--line);
      border-radius:8px;padding:16px}`）+ `_vcp_card.html:11` 的内联
      `style="margin:12px 0;padding:12px"`（**覆盖** padding 为 12px）。
      标题 `<h3 style="margin:0 0 8px">`：旧站**没有 h3 规则** ⇒ 浏览器默认 `1.17em`；
      新站写了 `17px` + 显式行高 ⇒ 这里压回默认值。
   ---------------------------------------------------------------------------- */
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-card {
  background: #fff;
  border: 1px solid #e2e4e8;       /* = app.css:3 `--line` */
  border-radius: 8px;
  padding: 12px;                   /* = 旧卡片内联样式 */
  margin: 12px 0;
}
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-card h3 {
  margin: 0 0 8px;
  font-size: 1.17em;               /* = 浏览器默认 h3（18.72px） */
  line-height: normal;
}
/* ★ 标签列不折行：旧站卡片模板里有 **30 处 `class="nowrap"`**（`_vcp_card.html:112/115/122…`），
   新站模板 **0 处** ⇒ 实测旧 th/td 是 nowrap、新站会折行（也解释 P5 里"来源未定位"那项）。
   ⇒ 用等价规则给"表头 + 首列（标签列）"加 nowrap；`.nowrap` 是 app.css:246 的**通用工具类**。 */
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-card th,
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-card td:first-child {
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   D16 形态分析（VCP）壳 = 旧站 `app.css:365-406` + `vcp.html:30-60`
      用 `:has(.v2-vcp-head-holder)` 限定**只有 VCP 页**（不影响 watchlist/报告页）
   ---------------------------------------------------------------------------- */

/* 头部 H1：旧站 `.vcp-sticky-head h1{margin:0 0 4px}`（比其它页的 .67em 更紧） */
body.v2-body--legacy:has(.v2-vcp-head-holder) h1.pub-page-title { margin: 0 0 4px; }

/* 头部三下拉表单：旧站 `form.inline{margin:8px 0}` + `.vcp-sticky-head form{margin:4px 0 2px}` */
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-sticky-head form {
  display: inline;
  margin: 4px 0 2px;
}
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-sticky-head form label {
  display: block;                  /* 旧站 label 是 block，但 select 为 inline-block ⇒ 每个标签一行 */
  margin: 0;
  font-size: .8rem;                /* = 旧站 `label.small`（`.small` = .8rem） */
}
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-sticky-head form select {
  width: auto;
  display: inline-block;
  padding: 7px 9px;                /* = app.css:219 */
  border: 1px solid var(--line);
  border-radius: 6px;
  font: inherit;
}

/* Tab 条：旧站 `.vcp-tabs` */
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  border-bottom: 2px solid var(--line);
  margin: 4px 0 12px;
}
/* Tab 项：旧站 `.vcp-tab` */
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-tab {
  display: inline-block;
  padding: 6px 14px;
  color: var(--muted);
  text-decoration: none;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: 6px 6px 0 0;
  margin-bottom: -2px;
  font-size: .95em;
  white-space: nowrap;
}
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-tab:hover {
  color: var(--text);
  background: #f4f6f8;
}
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-tab.on {
  color: var(--text);
  font-weight: 600;
  background: var(--card);
  border-color: var(--line);
  border-bottom: 2px solid var(--card);
}
/* Tab 计数徽标：旧站 `.vcp-tab-n`（0 不显示 —— 由模板控制） */
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-tab-n {
  display: inline-block;
  min-width: 1.5em;
  text-align: center;
  margin-left: 5px;
  padding: 0 5px;
  border-radius: 9px;
  background: #eef0f3;
  color: var(--muted);
  font-size: .85em;
  font-weight: 600;
}
body.v2-body--legacy:has(.v2-vcp-head-holder) .v2-vcp-tab.on .v2-vcp-tab-n {
  background: color-mix(in srgb, var(--accent) 16%, #fff);
  color: var(--accent);
}


/* D15 空态行 = 旧站 `.empty`（`app.css:293`：muted 色 / `padding:18px 0 !important`）
      —— 笔记历史页的 JS 已输出 `<td class="v2-empty">`，只需补样式。
      ⚠️ 旧站那条还写了 `text-align:center`，但被更高特异性的 `table.data td{text-align:left}`
        （`app.css:242`）压掉 ⇒ **旧站实际渲染是左对齐**。按"抄渲染结果"这里用 left。 */
body.v2-body--legacy .v2-empty {
  text-align: left;
  color: var(--muted);
  padding: 18px 0 !important;
}

/* ---------------------------------------------------------------------------
   D11 列表表格 + 公式胶囊（2026-10-16 · P2 实测）：旧站 `table.data`（`app.css:240-244`）
      · 单元格字号**继承 body（16px）** —— 新站 `.v2-table--legacy` 写了 `.9rem` → 实测 14px
      · `table.data` 白底 `var(--card)`；th 700 / 非 sticky / 允许折行 / 无 anywhere
      · 表格滚动包装：旧站 `.table-scroll`（`app.css:247`）**只有横向滚动、没有限高**
      · 公式胶囊：旧站 `.tag`（`app.css:294`）没有 line-height、没有 nowrap
   --------------------------------------------------------------------------- */
body.v2-body--legacy .v2-table--legacy { font-size: inherit; }   /* 表格元素：旧站 table.data 未设字号 ⇒ 继承 16px */
/* ⚠️ 单元格**保持 .9rem**（14.4px）—— 旧站 `app.css:242` `table.data th, td{font-size:.9rem}`，
   新站 `.v2-table--legacy th,td{font-size:.9rem}`（v2.css:3779）本来就对 ⇒ 不要改它。 */
body.v2-body--legacy .v2-table--legacy {
  background: var(--card);
  margin: 8px 0;
}
body.v2-body--legacy .v2-table--legacy th {
  font-weight: 700;
  position: static;                /* 旧站表头不吸顶 */
  white-space: normal;
  overflow-wrap: normal;
}
body.v2-body--legacy .v2-table--legacy td { overflow-wrap: normal; }
body.v2-body--legacy .v2-table-scroll {
  max-height: none;                /* 旧站没有限高内滚 */
  overflow-x: auto;
  overflow-y: visible;
}
body.v2-body--legacy .v2-tag,
body.v2-body--legacy .v2-tag-fml {
  line-height: normal;
  white-space: normal;
  overflow-wrap: normal;
}

@media (max-width: 900px) {
  /* D6：旧站没有 `.pub-shell` 这一层（手机档它会加 16px 边距 + 白底 + 圆角）⇒ 归零 */
  body.v2-body--legacy .pub-container > .pub-shell {
    padding: 0;
    margin: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
  }
  /* D7 手机档：旧站 `body.page-report main{max-width:100%}`（app.css:736） */
  body.v2-body--legacy main.pub-content { max-width: 100%; }
}

/* ============================================================================
   ★★ Phase 1 批次 D（2026-10-16 · R5/R8）
   ## D1 时间显示：见 `v2/_scripts.html`（修 `new Date(raw)` 漏 'Z' 的时区 bug + 照旧站格式）
   ## D2 页尾「本次检索素材」折叠区（R8，仅 admin）
      真源：`_report_notes.html`（DOM）+ `app.css:202-203`（.card）/`:209-217`（.notes-panel）
            /`app.css:428-430`（.logbox）
      类名用新站前缀；作用域限报告页。
   ---------------------------------------------------------------------------- */
body:has(.v2-report-page) .v2-material {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0;                    /* 旧站 `.notes-panel{padding:0}`：内边距挪给 summary */
  margin-top: 18px;
}
body:has(.v2-report-page) .v2-material > summary {
  padding: 12px 16px;
  cursor: pointer;
  list-style-position: inside;
  border-bottom: 1px solid transparent;
}
body:has(.v2-report-page) .v2-material[open] > summary { border-bottom-color: var(--line); }
body:has(.v2-report-page) .v2-material > summary:hover { background: #f7f9fc; }
body:has(.v2-report-page) .v2-material > p,
body:has(.v2-report-page) .v2-material > pre { margin: 10px 16px; }
body:has(.v2-report-page) .v2-material > p:first-of-type { margin-top: 12px; }
body:has(.v2-report-page) .v2-material-meta,
body:has(.v2-report-page) .v2-material-note { color: var(--muted); font-size: .8rem; }
body:has(.v2-report-page) .v2-material-urls { font-size: .8rem; }
body:has(.v2-report-page) .v2-logbox {
  background: #10151c;
  color: #d6e2ec;
  padding: 12px;
  border-radius: 8px;
  overflow-x: auto;
  font-size: .8rem;
  line-height: 1.5;
  min-height: 200px;
  white-space: pre-wrap;
  margin-bottom: 16px;             /* 旧站 `.notes-panel > pre.logbox` */
}

/* B4 main 宽度（**报告页专属**）：旧站 `body.page-report main{max-width:1560px}`（app.css:607）
      盖住通用 `main{max-width:1080px}`（app.css:199）。
      ★ 2026-10-16（P2 实测）：这条**不能放公共底座** —— 旧站其它页（如观察名单）用的是通用
      1080（内容 1048）。⇒ 这里只给报告页放开到 1560（配合 `> .pub-container{max-width:1528}`）。 */
@media (min-width: 901px) {
  main.pub-content:has(.v2-report-page) { max-width: 1560px; }
}


