/* ============================================================
   潜龙八字 · 版式 v3（layout-v3.css）  UIUX-v3 · 260921
   ------------------------------------------------------------
   owner 口径：「**版式先统一，模块固定化**，后面还是以皮肤形式来搞」
              「首页不好看，主要是**版式**不好看，其他页面也存在这个问题」
   ⇒ 本文件是**版式/模块层**：把页面骨架、模块秩序、模块内部结构**定死**，
     让 9 套皮肤只改「颜色与质感」，不再各改各的间距与结构。

   它管四件事：
     ① 页面模块秩序（所有页统一：间距阶 / 最大宽 / 页头层级）
     ② 模块内部结构（本批三处硬要求）
          · 十神力量 —— 桌面**一行放完**（10 格），手机/平板两行（5×2）
          · 藏干 / 气位 / 副星 —— **合并成一格**，每行「干·气位·十神」
          · 五行力量 ↔ 十神力量 —— **联动互指**（悬停互高亮）
     ③ 首页版式（两个模式开关并成一条；表单自适应列数）
     ④ 模块固定的视觉节奏（卡片标题三级、分隔线、数值对齐）

   ⛔ 只消费 var(--*)；唯二豁免 #fff / #000。
   ⛔ 不新增断点真源：骨架差异只用 #viewportStage[data-shell=...]。
   ⛔ 注释内禁写「星号紧跟斜杠」的片段（硬教训 10）。
   ============================================================ */

/* ============================================================
   1 · 页面模块秩序（★「版式统一」的落点）
   ------------------------------------------------------------
   固定秩序：页头 → 模式条 → 主体模块 → 附注/快捷。
   所有 .page 都是纵向 flex，模块间距只有一个来源：--mod-gap。
   ⇒ 任何页面新增模块都自动对齐，不需要再写 margin。
   ============================================================ */
:root,
#viewportStage{
  --mod-gap: var(--sp-4);     /* 模块间距（唯一来源） */
  --mod-pad: var(--sp-4);     /* 模块内距（卡片类共用） */
  --home-max: 860px;          /* 首页/表单页的内容最大宽（表单页不要太宽） */
  --read-max: 34em;           /* 长文行宽上限（中文 30–38 字） */
}
.page{
  display: flex;
  flex-direction: column;
  gap: var(--mod-gap);
}
/* 模块间距的唯一来源 ⇒ 抹掉组件层散落的下边距，避免"双份间距" */
.page > *{ margin-bottom: 0; }
.page > .apphead{ margin-bottom: 0; padding-bottom: 2px; }

/* 页头层级固定：H1 品牌名 / 副题 12px / 动作区右对齐 */
.page > .apphead .brand-name{ font-size: var(--fs-xl); line-height: 1.15; }
.page > .apphead .brand-sub{ font-size: var(--fs-min, 12px); }
.page > .apphead{ border-bottom: 1px solid var(--line); padding-bottom: var(--sp-2); }

/* ★★ 261005：手机档页头与顶栏汉堡键**同一行** ★★
   ⛔ 旧状：`.hamburger` 被 readability.css 覆写成 position:relative（占流 38px）
      ⇒ 把 `.page` 整体下推 38px ⇒ 页头永远在汉堡键**下方一行**（实测页头 top=54，汉堡 12–50）。
   ✅ 本批：`.hamburger` 回归 `position:fixed`（shell.css 原定义）⇒ 不占流；
      页头 top 回到 `.page` padding-top(16) ⇒ 标题行中心 ≈32，与汉堡键中心 31 自然对齐（无需负 margin）。
   ⚠ 随之页头会与左上角按钮**水平重叠** ⇒ 手机档统一左让 52px（按钮右缘 50 + 2 间距）。
   ⇒ 一处生效于**全部页面**（owner：「不止排盘总览页，其他也一样」）。 */
#viewportStage[data-shell="phone"] .page > .apphead{ padding-left: 52px; }

/* 卡片标题（模块标题）三级之一：统一字号/字重/下边线 */
.card-title{
  font-size: var(--fs-base);
  font-weight: 800;
  border-bottom: 1px solid var(--line);
  padding-bottom: var(--sp-2);
  margin-bottom: var(--sp-3);
}
.card-title .sub{ font-size: var(--fs-min, 12px); }

/* ============================================================
   2 · 模块内部结构（本批三处硬要求）
   ============================================================ */

/* ---------- 2.1 十神力量：桌面一行放完 / 手机与平板两行 ---------- */
.shishen-grid{
  display: grid;
  grid-template-columns: repeat(10, minmax(0, 1fr));
  gap: var(--sp-2);
}
#viewportStage[data-shell="phone"] .shishen-grid,
#viewportStage[data-shell="tab"] .shishen-grid{
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
/* 一行放完 ⇒ 卡片必须更紧凑：名字 ＋「档位／raw 合行」两层压到最小必要高度
   （★ 261013 #6 起为两层；此前为名字/档位/raw 三层）
   ★★ 261013 #6 真缺陷修复（盒模型层 · 实测抓出）★★
   ⛔ 病灶：本规则原写 `padding: 8px 6px`（**四边**），而 `readability.css` 的
      `.sh-card{padding-bottom:16px}` 是**底条（`::before`/`::after`，绝对定位）的预留带**——
      本选择器 `.shishen-grid .sh-card`（0,2,0）**恒压** `.sh-card`（0,1,0）
      ⇒ 底条预留带被清零（实测 `padB = 8px`）⇒ 底条画进内容区、**压住档位 pill 与占比数字**。
      实测最重者＝`epic`（轨 `bottom:9px;height:8px` ⇒ 顶边距底 17px），
      `nova`（7+5＝12px）与基础皮肤（6+3＝9px）亦偏紧。
   ✅ 修法：**在此显式写回底部预留**（18px ＝ epic 轨顶 17px ＋ 1px 余量），
      且只用 `padding-bottom`、不用 `padding` 四边简写（免再压其它边）。 */
.shishen-grid .sh-card{
  padding: 8px 6px;
  padding-bottom: 18px;
  text-align: center;
  justify-items: center;
}
.shishen-grid .sh-name{ font-size: var(--fs-min, 12px); font-weight: 800; }
.shishen-grid .sh-lvl{ font-size: var(--fs-min, 12px); }
.shishen-grid .sh-raw{ font-size: var(--fs-min-2, 10.5px); }

/* ---------- 2.2 藏干 / 气位 / 副星 → 合并成一格 ----------
   形态（owner 指定）：
       己 · 本 · 偏财
       癸 · 中 · 偏印
       辛 · 余 · 七杀
   实现：js/app.js::cangGanLines() 输出
        <div class="zg-line">
          <span class="zg-g" data-wx="土" style="--gz:var(--wx-earth)">己</span>
          <span class="zg-qi">本</span>
          <span class="zg-ss">偏财</span>
        </div>
   —— 三列栅格对齐：干（等宽）/ 气位（等宽小签）/ 十神（余下）。
   四柱总览表与细盘表**共用同一套类** ⇒ 一处真源、两处同形。 */
/* ★ 260922-r3 owner ①「藏干不居中」修法（实测取证）：
   ⛔ 旧写法 `1.3em auto minmax(0,1fr)` 让第 3 列吃掉全部余量 ⇒ 三列整体贴左，
      且余量随各柱列宽变化 ⇒ **同一行内四个格的藏干视觉对齐各不相同**
      （owner 截图：年柱「乙」贴左、日柱/时柱看似居中、月柱三行左对齐）。
   ✅ 正解＝**三列改为内容宽（max-content），整行在格内居中**（justify-content:center）。
      这样「干·气位·十神」作为**一个整体**在单元格内水平居中，四柱同形、与列宽解耦。
   ⚠ 十神仍是 `1fr` 会让整行撑满 ⇒ 必须一并改 max-content，否则居中不生效。
   兜底：十神名过长时 `max-width` 收口 + 溢出省略，避免撑破窄列。 */
.zg-line{
  display: grid;
  grid-template-columns: 1.3em auto minmax(0, max-content);
  gap: 5px;
  align-items: center;
  justify-content: center;   /* ← 整行居中：修①「藏干不居中」 */
  line-height: 1.5;
}
.zg-g{
  font-family: var(--font-serif);
  font-size: var(--fs-md);
  font-weight: 800;
  color: var(--ink);
  position: relative;
  padding-bottom: 2px;
  text-align: center;
}
/* 干支的五行以色条表达（与全局口径一致：色相不上文字） */
.zg-g[data-wx]::after{
  content: "";
  position: absolute;
  left: 15%; right: 15%; bottom: 0;
  height: 2px; border-radius: 2px;
  background: var(--gz);
}
.zg-qi{
  font-size: var(--fs-min-2, 10.5px);
  color: var(--ink-3);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0 3px;
  line-height: 1.5;
}
.zg-ss{
  font-size: var(--fs-min, 12px);
  color: var(--ink-2);
  text-align: left;
  /* ★ 260922-r3：整行改居中后，末列不得再吃掉余量 ⇒ 收为内容宽 + 溢出收口。
     十神名最长 2 字（正官/七杀…）或「日元」2 字 ⇒ 6em 足够且不会撑破 38-48px 窄列。 */
  max-width: 4.5em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 四柱总览表里这一格的行高要撑得住三行；手机端收紧字号 */
/* ★ 260922-r3：与 .zg-line 同步改 max-content —— 旧 `1fr` 与本文件的居中口径冲突。 */
.pillars-table .zg-line{ grid-template-columns: 1.2em auto minmax(0, max-content); gap: 4px; }
#viewportStage[data-shell="phone"] .zg-line{ gap: 3px; }
#viewportStage[data-shell="phone"] .zg-ss{ font-size: var(--fs-min-2, 10.5px); }

/* ★★ 260903 那段「藏干改两行」的 `@media(max-width:767px)` 规则**已删除**（261003 C8 批 1）★★
   【删除缘由（门禁实证，非主观判断）】`tests/gate/audit_breakpoints.py` 判其为**阻断项**：
     该 `@media` 块出现在其**基线规则之前** ⇒ 同特异性时**基线胜出** ⇒ **覆盖实际失效**。
     即 260903 那轮改动**从未生效过**；叠字之所以仍在，另因它本身写错
     （`grid-template-areas` 与 `max-content` 列冲突，见下）。
   【正确实现位置】**本文件链尾**的「C8 修 B」段：⛔ 不用 `@media`（本仓手机端一律用
     `data-shell`，见 `shell.js:92` 按 `#viewportStage` **实测宽度**写属性），
     ⛔ 不用 `grid-template-areas`（此上下文不可靠）⇒ 改**两行 flex**：
     上行＝干＋气位；下行＝十神 `flex:1 0 100%`（必换行）。
   【验收判据（比 260903 那条更强）】⛔ 不只看 `scrollWidth == clientWidth`（只验"装得下"）：
     须验 **① 规则真生效**（`getComputedStyle` 取到本段的 `display:flex`）
        ② **排得对**（同行内任两 span 的 `|Δx|+|Δy|` 不得过小 ⇒ 判叠字）。见 `audit_breakpoints` ＋ 实拍。 */


/* ---------- 2.3 五行力量 ↔ 十神力量 联动 ----------
   钩子由 js/app.js 输出：`.wx-bar .wb-seg[data-wx]`、
   `.wx-legend .wx-item[data-wx]`、`.sh-card[data-wx]`（十神相对日主的五行）。
   两向互指：悬停五行 → 高亮同五行的十神；悬停十神 → 高亮对应的五行段与图例。
   ⛔ 联动只是**强化**，不改变任何数据；被高亮者同时带出「五行名」文字，不只靠颜色。
   ⛔ 用 :has() 在共同祖先 #page-chart 上配对，零 JS。 */
.wx-bar .wb-seg,
.wx-legend .wx-item,
.shishen-grid .sh-card{ transition: opacity .16s ease, box-shadow .16s ease, outline-color .16s ease; }

/* ① 悬停五行（条或图例）→ 同五行的十神卡浮起，其余淡出 */
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="木"]:hover) .sh-card[data-wx="木"],
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="火"]:hover) .sh-card[data-wx="火"],
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="土"]:hover) .sh-card[data-wx="土"],
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="金"]:hover) .sh-card[data-wx="金"],
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="水"]:hover) .sh-card[data-wx="水"]{
  outline: 2px solid var(--brand);
  outline-offset: -2px;
  background: var(--brand-soft);
}
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx]:hover) .sh-card:not(:hover){ opacity: .38; }
/* 被高亮的十神卡补一个**文字**回指（不只靠颜色） */
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="木"]:hover) .sh-card[data-wx="木"] .sh-name::after{ content: " · 木"; }
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="火"]:hover) .sh-card[data-wx="火"] .sh-name::after{ content: " · 火"; }
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="土"]:hover) .sh-card[data-wx="土"] .sh-name::after{ content: " · 土"; }
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="金"]:hover) .sh-card[data-wx="金"] .sh-name::after{ content: " · 金"; }
#page-chart:has(:is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="水"]:hover) .sh-card[data-wx="水"] .sh-name::after{ content: " · 水"; }
#page-chart .sh-name::after{ color: var(--brand); font-weight: 800; }

/* ② 反向：悬停十神 → 对应五行段 + 图例浮起，其余淡出 */
#page-chart:has(.sh-card[data-wx="木"]:hover) :is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="木"],
#page-chart:has(.sh-card[data-wx="火"]:hover) :is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="火"],
#page-chart:has(.sh-card[data-wx="土"]:hover) :is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="土"],
#page-chart:has(.sh-card[data-wx="金"]:hover) :is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="金"],
#page-chart:has(.sh-card[data-wx="水"]:hover) :is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx="水"]{
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}
#page-chart:has(.sh-card[data-wx]:hover) .wx-bar .wb-seg:not(:hover){ opacity: .34; }
#page-chart:has(.sh-card[data-wx]:hover) .wx-legend .wx-item:not(:hover){ opacity: .5; }
/* 键盘可达：tab 到十神卡的依据按钮时同样联动（可访问性，非仅鼠标） */
#page-chart:has(.sh-card[data-wx]:focus-within) :is(.wx-bar .wb-seg, .wx-legend .wx-item)[data-wx]{ outline-offset: 1px; }

/* ---------- 2.4 「五行力量 × 十神力量」合参块（旺衰卡内）----------
   ★ 260922-r4 owner 第 4 项：「他俩要合在一块且放在旺衰卡片里」。
   本块是**版式归位**：把原本分居两张卡（五行力量卡 / 格局卡尾十神力量）的两套同源力量
   收进同一容器，使 §2.3 的 `:has()` 双向联动在**紧邻**关系下被感知（原来隔着 200 行）。
   ⛔ 本层只做容器与提示条；`.wx-bar` / `.wx-legend` / `.shishen-grid` 沿用既有实现，
      皮肤层的 patch 规则（`[data-skin="…"] .wx-bar` 等）**不受影响**（选择器层级未变）。 */
.power-merge{ margin-top: 4px; }
.power-merge .wx-legend{ margin-top: 9px; }
.power-merge .shishen-grid{ margin-top: 12px; }
.pm-hint{
  font-size: var(--fs-min-2, 10.5px);
  color: var(--ink-3);
  margin-top: 9px;
  padding: 6px 9px;
  border-radius: var(--r-md, 8px);
  background: var(--surface-2);
  border: 1px dashed var(--line);
}
#viewportStage[data-shell="phone"] .pm-hint{ font-size: var(--fs-min-2, 10.5px); }

/* ============================================================
   3 · 首页版式（owner：首页不好看，主要是版式）
   ------------------------------------------------------------
   改动要点：
     · 「体验模式」与「单盘/合盘」两个开关**并成一条**（原来上下两整行）
     · 首页内容收窄到 --home-max，表单不再拉满宽屏
     · 表单行改为自适应栅格：字段少就少列、多就多列，不再固定两列
     · 样本库与快捷卡在桌面并排，减少纵向长度
   ============================================================ */
#page-home{
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--mod-gap);
  max-width: var(--home-max);
  margin-inline: auto;
  width: 100%;
}
#viewportStage:not([data-shell="phone"]) #page-home{
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  grid-template-areas:
    "head  head"
    "mt    seg"
    "form  form"
    "smp   smp"
    "quick quick"
    "err   err";
}
/* ★★ 260923 手机档**必须**显式声明单列 areas —— 否则「首页排盘消失」★★
   ⛔ 缺陷机理：`#page-home` 是 grid 容器，各子元素靠 `grid-area: head|mt|seg|form|smp|quick|err`
      （下方 246–253 行，**无条件**生效）落位。而 `grid-template-areas` 原先只写在
      `:not([data-shell="phone"])` 分支里 ⇒ 手机档下这些**区域名不存在**，浏览器便把每个
      `grid-area: X` 解析为「在同名隐式线处起止」⇒ 七个区域名各自生成隐式线、**全部落在同一格**：
      实测手机 390 下 `#page-home` 的 8 个直接子元素**矩形完全相同**（330×1117，top 均为 −241）。
      后绘制的 `样本盘库` 覆盖在先绘制的表单之上 ⇒ 视觉上「排盘表单直接没了」，只剩样本库。
   ✅ 修法＝手机档补单列 areas（每区独占一行，恢复自然堆叠）。非手机档原模板不变。 */
#viewportStage[data-shell="phone"] #page-home{
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "head"
    "mt"
    "seg"
    "form"
    "smp"
    "quick"
    "err";
}
#page-home > .apphead{ grid-area: head; }
#page-home > .mode-toggle{ grid-area: mt; }
#page-home > #modeSeg{ grid-area: seg; }
#page-home > #formA{ grid-area: form; }
/* ★★ 260923：B 表单**必须**独立区域 `formb` ★★
   ⛔ 原写法 `#formA{#formB{ grid-area: form; }` —— 两表单**共用同一网格单元**，
      而合盘模式下两者同时可见 ⇒ 后绘制的 B 完全覆盖 A（`.card` 背景不透明）。
      实测 1760 合盘档两表单矩形**完全相同**：A=[412,182,1200,557] ＝ B=[412,182,1200,557]
      ⇒ 「命主」表单在合盘模式下根本看不见（既有缺陷，非本轮引入）。
   ✅ 修法：B 走 `formb`；而该区域**只在 B 可见时**才出现在模板里（`:has()` 声明式联动，
      **零 JS**）⇒ 单盘档模板一字不变、不产生空行/多余 gap。 */
#page-home > #formB{ grid-area: formb; }
#viewportStage:not([data-shell="phone"]) #page-home:has(> #formB:not(.hidden)){
  grid-template-areas:
    "head  head"
    "mt    seg"
    "form  form"
    "formb formb"
    "smp   smp"
    "quick quick"
    "err   err";
}
#viewportStage[data-shell="phone"] #page-home:has(> #formB:not(.hidden)){
  grid-template-areas:
    "head"
    "mt"
    "seg"
    "form"
    "formb"
    "smp"
    "quick"
    "err";
}
#page-home > .sample-card{ grid-area: smp; }
#page-home > .quick-grid{ grid-area: quick; }
#page-home > .err{ grid-area: err; }
/* 两个开关并排 ⇒ 内部对齐、等高 */
#page-home > .mode-toggle,
#page-home > #modeSeg{ align-self: stretch; margin: 0; }
#page-home > .mode-toggle{ display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
#page-home > #modeSeg{ max-width: none; }
#page-home > #modeSeg .seg{ min-height: var(--hit, 44px); }

/* 表单：自适应列数（字段少则少列，不再固定两列） */
#page-home .form .form-row{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: var(--sp-3);
  align-items: end;
}
#viewportStage[data-shell="phone"] #page-home .form .form-row{
  grid-template-columns: repeat(auto-fit, minmax(108px, 1fr));
  gap: var(--sp-2);
}
#page-home .form .field{ min-width: 0; }
#page-home .form .field.grow{ grid-column: 1 / -1; }
/* ★ 260927 B2 修复：**滚轮占满整行** ——
   ⛔ 症状（owner 图4）：农历档同一 `.form-row` 内还有「闰月」字段 ⇒ `auto-fit` 把该行切成 2 轨
      ⇒ 滚轮只剩半宽（实测 `#aWhLunar` 260px，5 列各 **52px**），文字挤成一团（owner 所谓"空横线"）。
   ✅ 修法：滚轮跨满整行（`grid-column:1/-1`），「闰月」自动落到上一行 ——
      ⛔ 不改 DOM 结构、不改任何 id、不动取值链路（`whMount/whSync/readBirth` 零影响）。 */
#page-home .form .form-row > .wh{ grid-column: 1 / -1; }
/* 表单标题：与卡片标题同一套层级 */
#page-home .form .form-title{ margin-bottom: var(--sp-3); }
#page-home .form .form-title b{ font-size: var(--fs-base); }

/* 样本库：桌面多列，缩短纵向长度 */
#page-home .sample-list{ display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--sp-3); }
#viewportStage[data-shell="phone"] #page-home .sample-list{ grid-template-columns: minmax(0, 1fr); }

/* 快捷卡：桌面 2 列 */
#page-home .quick-grid{ display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--sp-3); }

/* ============================================================
   4 · 模块固定的视觉节奏（长文行宽 / 数值对齐 / 分隔）
   ============================================================ */
/* 长文类模块统一行宽上限（中文 30–38 字/行） */
.reading-box,
.about-text,
.cali-doc,
.xing-txt{ max-width: var(--read-max); }
/* 表格数值统一等宽 */
.stat-pill .v,
.tl-item .y b,
.pillars-table td,
.wx-legend b,
.cali-c-v{ font-variant-numeric: tabular-nums; }
/* 提示块与免责：统一为 1px 全框（禁用 3px 侧条纹，项目既有禁令） */
.disclaimer,
.zi-note,
.disease-card,
.rq{ border-left-width: 1px; border-radius: var(--r-md); }

/* ============================================================
   5 · 品牌水印：「潜龙」二字
   ------------------------------------------------------------
   owner 口径：「潜龙两个字的背景水印（就两个字，不用重复搞一堆水印）」。

   ★ 三条硬约束（决定了实现方式）：
     ① **只两个字、不平铺** —— 所以不用 background-image 平铺，
        而是用一个「整幅只出现一次」的绝对定位文字层，居中偏下、单行竖排。
     ② **不干扰正文** —— 水印是**装饰**，必须：
        · pointer-events: none（不挡任何点击）
        · aria-hidden + user-select: none（读屏与复制都不含它）
        · z-index 在内容之下（内容层需有定位上下文，见下）
     ③ **跨皮肤可换色** —— 版式层只定义「在哪、多大、什么朝向」，
        **颜色由皮肤层给**（消费 --line / --brand-soft 这类既有令牌，
        不新增令牌 ⇒ 不增加 9 套皮肤各自的维护面）。

   ★ 为什么必须由版式层做、而不是 9 套皮肤各写一遍：
     水印的**位置与尺寸**属于版式；颜色属于皮肤。若下放到皮肤，
     9 套各写一次几何，改一次要改九处 —— 正是 owner 要避免的。

   ★ 实现要点（本项目已踩过的坑）：
     绝对定位层要落在 #app 的**绘制层之下**，故给 #app 建立层叠上下文，
     水印 z-index: 0，`.page` 抬到 z-index: 1 —— 否则水印会盖在卡片上。
   ============================================================ */
#app{ position: relative; isolation: isolate; }
.page{ position: relative; z-index: 1; }
/* 骨架层自身的固定元素（汉堡 / 底栏）要在水印之上 */
.hamburger,
.tabbar{ z-index: 3; }

/* ★★ 261005 修 owner 报障「抽屉点开有 bug」★★
   ⛔ 真根因（实测证据链）：本行原把 `#sbDrawer`/`.ai-drawer` **统设为 z-index:3**
      （本意「压过水印 z=0」），而 `.scrim` 是 **z-index:140**（shell.css:333）
      ⇒ **抽屉渲染在遮罩之下**：点开只看到整页变暗、面板根本不出现。
      实测：点击汉堡后 `elementFromPoint(汉堡中心)` 命中 `DIV.scrim`；
      `drawer.className` 含 open 但视觉不可见 ⇒ 用户判「点开有 bug」。
   修：抽屉是**浮层**，须在遮罩之上 ⇒ 200（遮罩 140 ／ 内容 1 ／ 水印 0）。
   ⚠ 遮罩仍低于抽屉、高于内容与汉堡（120）—— 点遮罩关闭仍成立（预期行为）。 */
#sbDrawer,
.ai-drawer{ z-index: 200; }

/* ★★ 260930 修复 owner 报障「两个弹窗互相弹闪」——**真正的根因在此**★★
   ⛔ 机理：`#app{isolation:isolate}` 建了一层栈；`.page{z-index:1}` **又建一层栈**。
      而「出生时间」弹层（`#sheetTimeA/#sheetTstA/#sheetTimeB/#sheetTstB`）与
      智能图示弹层是**生成在页面容器里**的（`#page-home` / `#page-chart`）⇒
      它们被**关在 `.page` 的 z=1 上下文内**；`.tabbar{z-index:3}` 与 `.page` 平级
      ⇒ **3 > 1，弹层自身 z 再高也翻不过底栏**（实测：`#sheetTimeA` z=100 仍被 `.tab` 命中；
      弹层底 835 / 底栏顶 771 ⇒ 重叠 64px）。
      后果：点弹层下半部（滚轮末行 / 确定区）实际点到**底部导航** ⇒ 页面被切走、弹层仍浮着
      ⇒ 观感正是「**两个弹窗在互相弹闪**」。
   ✅ 修法：弹层打开时给 `<html>` 挂 `ql-sheet-open`，把 `.page` 整体抬到**底栏之上**（4 > 3）。
      此时弹层内的 mask（z=99）会连底栏一起盖住 —— 这正是模态该有的行为。
      ⛔ 只在弹层打开期间生效，关闭即撤 ⇒ 不影响常态下「底栏在页内容之上」的既有秩序。 */
html.ql-sheet-open .page{ z-index: 4; }

/* ★ 260930-3 浮层打开期间**背景全锁**（owner：「鼠标一出弹层范围就弹出另一个框」）。
   机理：`pointer-events:none` 的元素**不参与命中测试** ⇒ ① 点不到背景任何控件
   ② **原生 `title` 浮框也不会弹出**（浏览器不为不可命中元素显示 tooltip）
   ③ 但**键盘 Tab 仍可用**（pointer-events 不管焦点序）⇒ 无 a11y 倒退。
   ⚠ 时间弹层 `#sheetTimeA` 等**本身就在 `#app` 内**（生成于 `#page-home`）⇒
      必须把 `.sheet/.sheet-mask/.vz-sheet/.vz-mask` 重新置 `auto`，否则弹层自己也被锁死。
   （配 `js/app.js::closeAllOverlays()` 的**单例互斥**，两者合起来＝一次只有一个浮层、且背景不可达。） */
html.ql-sheet-open #app{ pointer-events: none; }
html.ql-sheet-open .sheet,
html.ql-sheet-open .sheet-mask,
html.ql-sheet-open .vz-sheet,
html.ql-sheet-open .vz-mask{ pointer-events: auto; }

/* ★ 260930-4 保底闸（防同类复发）：`position:fixed` 的弹层/遮罩，其**祖先不得"分组化"**。
   分两类机制，**两类都会把弹层关住**，且**表象相同**（被后面的卡片盖住 / 遮罩盖不满）：
     ① **包含块**类：`transform≠none`、`filter≠none`、`backdrop-filter≠none`、`perspective≠none`、
        `contain: paint|layout|strict|content`、`will-change` 含上述 ⇒ fixed 相对该祖先定位。
        （实测元凶＝`.card:hover{transform:translateY(-1px)}`，已由 `form.card:hover{transform:none}` 根治。）
     ② **层叠上下文**类：`opacity<1`、`animation` 中 opacity 变化、`isolation:isolate`、`z-index` 非 auto
        +positioned ⇒ 弹层的 `z-index:100` 只在该栈内排序，与祖先**平级**的后续兄弟卡片会整块盖住它。
        （实测元凶＝`.page:not(.hidden) > *` 的页面淡入动画命中 `form.card`，已由 `:not(form)` 排除。）
   ⇒ 故此处把两类属性**一并**在弹层打开期间清掉；⛔ 关闭即撤，不影响常态观感。 */
html.ql-sheet-open #app,
html.ql-sheet-open .page,
html.ql-sheet-open .card,
html.ql-sheet-open form{
  transform: none !important;
  filter: none !important;
  backdrop-filter: none !important;
  perspective: none !important;
  contain: none !important;
  will-change: auto !important;
  /* 层叠上下文类：`opacity:1` 使元素不再建栈；`animation:none` 掐掉 opacity 动画期间的建栈窗口。
     ⚠ 只作用这四类**容器**，不写 `*`（否则会强拉平弹层内部的淡入/半透明效果）。 */
  animation: none !important;
  opacity: 1 !important;
}

.watermark{
  position: absolute;
  /* 落点：整幅的右下腹（避开左侧导航与顶部标题），单次出现 */
  right: 4%;
  bottom: 6%;
  z-index: 0;
  pointer-events: none;
  user-select: none;
  text-align: center;
  /* 二字竖排：像落款，也让它更「长」而不占宽 —— 横向不挤压内容 */
  writing-mode: vertical-rl;
  text-orientation: upright;
  font-family: var(--font-serif);
  font-weight: 700;
  /* 字号跟视口走，但不参与任何栅格计算 */
  font-size: clamp(72px, 16vw, 210px);
  /* ★★ 260923 修正 `line-height`：原 `.92` **小于** upright 竖排字形的横向步进 ★★
     ⛔ 缺陷机理：`writing-mode: vertical-rl` + `text-orientation: upright` 下，`line-height`
        决定的是**列宽（cross size）**，而该字体（宋体族）的**自然行高 ≈ 字号×1.34**
        —— 竖排 upright 时这个自然行高被当作行内文本的横向占位。
        `.92` ⇒ 列宽 93.3px ＜ 实测所需 136px（手机 390：`scrollWidth 136` vs `width 127`）
        ⇒ **行内文本溢出**，随 `#app` 的滚动溢出传导到文档
        （实测 `#app.scrollWidth = 409`、`documentElement.scrollWidth = 409` vs 视口 390）
        ⇒ 手机端整页出现**横向滚动条**（owner 截图底部那条）。
        ⚠ 为何只在手机暴露：桌面/宽屏视口更宽，水印右缘（right:4%）之外的溢出仍落在视口内。
     ✅ 修法＝列宽按**字体自然行高**给足（1.4 ＞ 1.34）。水印为纯装饰、以 `right` 锚定，
        字形在列内居中 ⇒ 视觉位置仅内移约 0.24em，观感不变，整页不再横拉。
        实测：390 档 `documentElement.scrollWidth` 409 → 390（等于视口）。 */
  line-height: 1.4;
  /* ★ 颜色只消费既有令牌，皮肤不用为本水印新增任何变量 */
  color: var(--line-strong);
  opacity: .16;
  /* 字距拉开，让两个字有「印章间距」而不是挤成一坨 */
  letter-spacing: .08em;
}
/* 手机骨架：水印贴右下角，避免与底栏相撞
   ★★ 261005 修 owner 报障「水印也没见到」★★
   ⛔ 真根因：基线 `.watermark{position:absolute}`（layout-v3:457）⇒ 它锚在**文档**上，
      长页里落到 y≈8453（页高 9957 的 85%）⇒ **首屏永远看不到**。
      另：上轮我把选择器写成 `#viewportStage .watermark`(1,1,0)，被本块
      `#viewportStage[data-shell="phone"] .watermark`(**1,2,0**) 压掉 ⇒ 改了也不生效（特异性坑）。
   ✅ 本批：`position:fixed`（视口锚定）+ 字号放大 + `z-index:3`（在内容之上、遮罩/抽屉之下）。 */
#viewportStage[data-shell="phone"] .watermark{
  position: fixed;
  font-size: clamp(110px, 46vw, 200px);
  right: -2%;
  bottom: 6%;
  opacity: .12;
  z-index: 3;
}
/* 降级：打印时不要水印（省墨，且不干扰纸质阅读） */
@media print{
  .watermark{ display: none; }
}

/* ============================================================
/* ==== 5.5 · 行标签护栏（260922 owner 投诉「藏干·气位·十神」窜进数据格）==== */
/* 根因（实测）：app.css:219 `.row-name{width:64px;min-width:64px;white-space:nowrap}`
   × shell.css:311-317 按骨架覆写（desk/wide 88px · tab 76px · phone 58px）——
   而标签是「6 字 + 2 间隔号」（约 8 字位），nowrap 下必然溢出压住右侧数据格。
   两层根治：① 数据源标签回归两字「藏干」（app.js 已改）；② 本节版式溢出兜底。
   ⛔ 不改 app.css 的 64px / shell.css 的 88·76·58px —— 那些是骨架裁定值，只加护栏。 */
.pillars-table .row-name{
  overflow: hidden;
  text-overflow: ellipsis;
  /* 与 shell.css / app.css 的 width 同源，仅作保险（窄于任一骨架值时不再放宽） */
  max-width: 64px;
}
#viewportStage[data-shell="desk"] .pillars-table .row-name,
#viewportStage[data-shell="wide"] .pillars-table .row-name{ max-width: 88px; }
#viewportStage[data-shell="tab"] .pillars-table .row-name{ max-width: 76px; }
#viewportStage[data-shell="phone"] .pillars-table .row-name{
  max-width: 58px;
  letter-spacing: 0;          /* 1px × 8 字位 = 8px，最易回收 */
}
/* 全角间隔号不再独占整字宽，省下标签内「·」的占位 */
.pillars-table .row-name{ font-variant-east-asian: proportional-width; }

/* ==== 5.6 · 神煞（260922-r3 owner：「**要列全啊！**」⇒ 默认即列全，不再折叠）==== */
/* 说明：折叠开合属**版式行为**，不属视觉风格 ⇒ 归版式层，全皮肤共用。
   ★ 260922-r3 口径变更：旧版为 <details class="ss-fold"> 折叠徽标（默认只出「N 项」）；
     owner 明令**要列全** ⇒ 数据侧 app.js::shenshaCellHtml() 改为**直接输出全部 chip**，
     本节随之删除 `.ss-fold/.ss-sum/.ss-u`（触发器）相关规则，只保留清单与 chip 样式。
   ⛔ 本节不引入新依赖、不写 emoji、不改任何判定值。 */
/* 全列清单：换行排布（不再 nowrap+横滑）—— 列全后项目多，横滑会「看不见等于没有」。
   手机窄列下 chip 自动折行，仍是可读的连续小签。 */
.pillars-table .ss-body{
  display: flex;
  flex-wrap: wrap;
  gap: 3px 3px;
  font-size: var(--fs-min-2);
  color: var(--ink-3);
  line-height: 1.5;
  justify-content: center;   /* 与藏干格同口径：格内居中 */
  max-width: 100%;
  /* ★ 261005 复位：`app.css::.ss-body` 是「古籍 · 参阅」**面板的滚动容器**
     （`flex:1;overflow-y:auto;padding:var(--sp-3) var(--sp-4) var(--sp-5)` ＝ 12/16/20px）。
     本格与它**同名异义**（71★），此前只在本节覆盖了 display/gap/字号，内距与 overflow
     **一直漏下来** —— 手机窄列（约 90px）白吃 32px 横向内距，是「神煞行看着比别的格挤」
     的来源。此处按本格语义把这两条一并复位；⛔ 不动面板侧（那侧语义正确）。 */
  padding: 0;
  overflow: visible;
}
/* 等级徽标前置（261005）后的字距：本格 chip 是 `display:inline-block`（非 flex），
   ⛔ 拿不到 `.ss-chip` 的 `gap:6px` ⇒ 须显式给徽标右侧留一口气，否则「S禄神」粘成一体。 */
.pillars-table .ss-body .ss-chip .grade{ margin-right: 4px; }
.pillars-table .ss-none{ color: var(--ink-3); font-size: var(--fs-min-2); }
/* 手机：格内宽度最紧 —— chip 字号再降一档，靠折行兜住 */
#viewportStage[data-shell="phone"] .pillars-table .ss-body{ font-size: var(--fs-min-2); }

/* ★ 260922-r2 · 神煞项标签（逐项 chip）
   v1 只把整串文本塞进一个 div ⇒ 多项混排难辨认。改为逐项 chip：
   每项独立底纹 + 细边，项与项之间自然留白，一眼可数。
   ★ 260930：本批把密排处的 `.ss-tag` **并入统一 `.ss-chip`**（四处神煞改用同一形态，
     消除「同形异构」硬教训 71★）⇒ 选择器改指 `.ss-chip`。
   ⚠ 必须显式 `min-height:auto`：app.css 的 `readability` 族给所有 `.ss-chip` 保 ≥44px 命中，
     而**表格格内是密排上下文**，44px 会把四柱总览的神煞行整行撑高。
     ⛔ 这不是「新增豁免」—— `.ss-tag` 原本就不在该 44px 名单内，本条只是把既有豁免显式化。
     可点性由「格内 chip 仍带 `data-ss`」保证（面板入口不依赖命中盒高度）。 */
.pillars-table .ss-body .ss-chip{
  display: inline-block;
  min-height: auto;
  padding: 1px 5px;
  border-radius: var(--r-sharp-md, 6px);
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--fs-min-2);
  line-height: 1.5;
  white-space: nowrap;
}
/* ★ 260922-r3 · 细盘层神煞逐行（app.js::shenshaOf 输出 .ss-line）
   ① 色相不上文字（EL-3）—— 原内联 `color:var(--gold-text)` 已删，改走 --ink 系语义令牌；
   ② 与总览层 chip 区分：细盘是「一柱一列、竖向列表」，逐行排布比 chip 更省横向空间。 */
.detail-wrap .left .pillars-table .ss-line,
.pillars-table .ss-line{
  color: var(--ink-2);
  font-size: var(--fs-min-2);
  line-height: 1.45;
  white-space: nowrap;
}
/* ★ 260930：细盘/运岁层逐行神煞 —— 行内 chip **去壳留字**（竖向列表里再套壳会挤），
   但保留 `data-ss` ⇒ 仍可点开「古籍 · 参阅」面板。`min-height:auto` 同密排豁免口径。 */
.pillars-table .ss-line .ss-chip{
  min-height: auto; background: transparent; border: 0; padding: 0;
  font-size: var(--fs-min-2); color: var(--ink-2);
}

/* ==== 5.7 · 运岁引动神煞（260922-r4 收口）==== */
/* 说明：本段承载「原局神煞 × 岁运触发」列表（app.js 内 data-stack="xyself"）。
   ⛔ 260922-r2/r3 曾在卡内另列「焦点运岁三柱」(.xyself-row 及其 5 个子类)，
      260922-r4 运岁列已回到表格（7 列）⇒ 该行**同页重复**，已撤。
   ⇒ 上述 7 个类成为**无消费者的死码**，一并清除（铁律⑩ 死码分类：直接删）。
   卡体本身（.ssf-list/.ssf-item）保留 —— 见下方神煞段样式。 */

/* ★ 260922-r2 · 细盘层藏干「堆在一块」根治
   根因：原盘列仅 4 柱、每柱宽较窄，而 `cangGanLines` 输出的是**竖排多行**
   （每藏干一行 `.zg-line`）⇒ 在细盘窄列中三行堆叠、行高失衡。
   ★ 260922-r3 owner 再投诉「细盘的藏干又堆在一块了」—— 实测复现：
     旧写法（flex + .zg-ss{flex:1 1 auto} + nowrap）在窄列内**三元素不肯压缩**，
     `.zg-ss` 又吃 1fr 余量 ⇒ 文本溢出格宽、被相邻格覆盖，视觉上「堆在第 1 列」。
   ✅ 正解＝与总览层**同一套栅格**（grid + max-content + justify-content:center），
      一处口径两处同形；`.zg-ss` 固定 max-width + 省略号兜底，绝不溢出格宽。 */
.detail-wrap .left .pillars-table td.gz-sub{
  vertical-align: top;
  padding-top: 4px;
  padding-bottom: 4px;
}
.detail-wrap .left .pillars-table td.gz-sub .zg-line{
  display: grid;
  grid-template-columns: 1.2em auto minmax(0, max-content);
  align-items: center;
  justify-content: center;      /* ← 格内居中（与总览层同口径） */
  gap: 3px;
  line-height: 1.35;
  white-space: nowrap;
}
.detail-wrap .left .pillars-table td.gz-sub .zg-g{
  font-weight: 800;
  min-width: 1em;
  text-align: center;
}
.detail-wrap .left .pillars-table td.gz-sub .zg-qi{
  min-width: 1em;
  text-align: center;
  font-size: var(--fs-min-2);
  color: var(--ink-3);
}
.detail-wrap .left .pillars-table td.gz-sub .zg-ss{
  text-align: left;
  font-size: var(--fs-min-2);
  color: var(--ink-2);
  max-width: 4.5em;            /* ★ 260922-r3：与总览层同口径 —— 固定上限，绝不溢出格宽 */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 手机：三栏压到最小间距，仍保「干·气位·十神」可辨 */
#viewportStage[data-shell="phone"] .detail-wrap .left .pillars-table td.gz-sub .zg-line{ gap: 2px; }

/* ★ 260922-r3 · 流年「强化 / 弱化」信号（owner「流年层的强化弱化字体根本渲染错误」）
   ⛔ 根因（实测）：`.ly-sig` 在**三处**被定义且互相打架 ——
      `app.css:274`（justify-self:end / font-size:10.5px）、
      `app.css:545`（inline-block / font-size:10.5px）、
      `readability.css:140`（font-size:var(--fs-min)）。
      而 `app.css:274` 未设 display/width ⇒ 「强化（×1.5）」长文本在 46px 窄格里
      **溢出被 overflow:hidden 裁掉、或压到相邻元素**，表现为「字体渲染错误」。
   ✅ 正解＝在**最高层（本文件最后加载）**给一条权威定义，统一：
      独立成行（grid-column 1/-1）+ 可折行 + 居中 + 定字号。三处旧定义被本层覆盖。 */
.tl-card[data-stack="xingyun"] .tl-scroll .tl-item .ly-sig{
  grid-column: 1 / -1;
  justify-self: center;
  align-self: center;
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 1px 4px;
  border-radius: var(--r-sm);
  font-size: var(--fs-min-2, 10.5px);
  font-weight: 700;
  line-height: 1.35;
  text-align: center;
  white-space: normal;           /* 允许「强化（×1.5）」在窄格内折行，不再溢出被裁 */
  word-break: break-all;
  overflow-wrap: anywhere;
  cursor: help;
}
/* 三态底色（文字仍为白/深色，满足对比度；色相不单独表意 —— 文本已写明「强化/弱化」） */
.tl-card[data-stack="xingyun"] .tl-scroll .tl-item .ly-sig.sig-strong{ color:#fff; background:var(--ok); }
.tl-card[data-stack="xingyun"] .tl-scroll .tl-item .ly-sig.sig-weak{ color:#fff; background:var(--danger); }
.tl-card[data-stack="xingyun"] .tl-scroll .tl-item .ly-sig.sig-mid{ color:var(--ink-2); background:var(--surface-3); }

/* ★ 260922-r3 · 关系芯片行（.age 内 .rel-tag）
   ⛔ 实测缺陷：app.js 在无关系文本时仍输出 `<span class="rel-tag">合</span><span class="rel-tag">·</span>`
     ⇒ 屏上出现**孤立的「合」与「·」**（owner 截图可见）。
      根因＝分隔符 `·` 被当作独立 chip 渲染，且默认文案「合」无条件追加。
   ✅ 修法在 app.js（见 updateDetail/renderDetailPage 的关系拼装）；本层仅保证：
      空 chip 不占位、多 chip 用间距分隔而非插入字符型 chip。 */
.tl-card[data-stack="xingyun"] .tl-scroll .tl-item .age .rel-tag:empty{ display: none; }

/* ★ 260922-r3 · 「月令 · 旺相休囚死」卡（owner「细盘里的月令是用来干嘛的？应该放在排盘页里」）
   ⇒ 该卡由**细盘页尾迁至排盘页**，随迁须补本卡**自身**的类定义。
   ⛔ 迁移前实测：`.wx-season-head` / `.wx-season-wx` / `.wx-season-txt` 三个类
      **在全部 CSS 中零定义**（`season-bar` 仅在 `app.css:331` 有旧定义、且被 5 套皮肤各自覆写）
      ⇒ 若只迁 HTML 不补样式，头部会退化为**默认块级元素**（大写行距、无对齐、无层级）。
   ✅ 本层给基线定义：与五行力量卡同视觉语言（surface-3 底 + 圆角），头部一行居中，
      当令五行加粗提色；`.season-bar` 沿用既有实现（app.css 基线 + 皮肤覆写），本层不夺其权。 */
.wx-season-head{
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  padding: 6px 0 4px;
}
.wx-season-wx{
  font-size: var(--fs-xl, 20px);
  font-weight: 800;
  color: var(--ink);
  line-height: 1.2;
}
.wx-season-txt{
  font-size: var(--fs-min, 12px);
  font-weight: 600;
  color: var(--ink-3);
  letter-spacing: .04em;
}

/* ★★ 260922-r3 · 关系下拉窗口（owner「关系要单独作为一个下拉窗口，来完成可视化」）
   —— 原生 `<details>` 承载，零依赖、键盘可达、折叠态内容仍在 DOM。
   ⛔ EL-3（色相不上文字）：族色**只走 `.rel-dot` 色点**（图形轨），
      族名/条数/描述一律 `--ink` 系文字色 ⇒ light 皮肤下对比度不受色相牵连。 */
.rel-panel{
  border: 1px solid var(--line);
  border-radius: var(--r-md, 10px);
  background: var(--surface-2);
  overflow: hidden;
}
.rel-sum{
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--hit, 44px);          /* 可点击区 ≥44px */
  padding: 8px 12px;
  cursor: pointer;
  list-style: none;                       /* 去默认三角，用自绘 caret */
  user-select: none;
}
.rel-sum::-webkit-details-marker{ display: none; }
.rel-sum:focus-visible{ outline: 2px solid var(--brand); outline-offset: -3px; }
.rel-sum-t{ font-weight: 700; font-size: var(--fs-sm, 13px); color: var(--ink); }
.rel-sum-n{
  font-size: var(--fs-min-2, 10.5px);
  font-weight: 600;
  color: var(--ink-2);
  background: var(--surface-3);
  padding: 1px 7px;
  border-radius: var(--r-pill, 999px);
}
/* caret 由 CSS 边框绘制，展开时旋转 90° */
.rel-sum-caret{
  margin-left: auto;
  width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  color: var(--ink-3);
  transition: transform .18s ease;
}
.rel-panel[open] .rel-sum-caret{ transform: rotate(90deg); }
.rel-panel[open] .rel-sum{ border-bottom: 1px solid var(--line); }

.rel-body{ padding: 8px 12px 10px; display: grid; gap: 10px; }
.rel-group-head{
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 5px;
  flex-wrap: wrap;
}
/* 族色点 —— 唯一承载色相的元素（图形轨） */
.rel-dot{
  width: 9px; height: 9px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--ink-3);              /* 兜底：皮肤/数据未给族色时不露空 */
}
.rel-dot[data-fam="合"]{ background: var(--ok); }
.rel-dot[data-fam="冲"]{ background: var(--danger); }
.rel-dot[data-fam="刑"]{ background: var(--warn); }
.rel-dot[data-fam="害"]{ background: var(--ink-3); }
.rel-dot[data-fam="破"]{ background: var(--brand); }
.rel-fam{ font-size: var(--fs-sm, 13px); font-weight: 800; color: var(--ink); }
.rel-cnt{
  font-size: var(--fs-min-2, 10.5px);
  font-weight: 700;
  color: var(--ink-2);
  background: var(--surface-3);
  padding: 0 6px;
  border-radius: var(--r-pill, 999px);
  min-width: 18px;
  text-align: center;
}
.rel-desc{ font-size: var(--fs-min-2, 10.5px); color: var(--ink-3); }
.rel-items{ display: grid; gap: 4px; }
.rel-item{ display: flex; align-items: baseline; gap: 6px; }
.rel-item .xing-txt{ min-width: 0; }
.rel-item .xing-txt b{ font-weight: 600; color: var(--ink-2); word-break: break-word; }
.rel-empty{ padding: 10px 12px; font-size: var(--fs-min, 12px); color: var(--ink-3); }
/* 降级：无动效偏好 */
@media (prefers-reduced-motion: reduce){
  .rel-sum-caret{ transition: none; }
}

/* ============================================================
   6 · 降级
   ============================================================ */
@media (prefers-reduced-motion: reduce){
  .wx-bar .wb-seg,
  .wx-legend .wx-item,
  .shishen-grid .sh-card{ transition: none !important; }
}

/* ============================================================
   T-142 手工四柱轨（260922）：四柱下拉四列等宽 ＋ 窗口下拉
   —— 仅版式，不涉机制/皮肤；无 @media（不动 T-080 断点审计面）
   ============================================================ */
.pillar-inputs{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}
.pillar-inputs .pl-sel{width:100%;min-width:0}
#aManualHint{margin-top:4px}

/* ══════════════════════════════════════════════════════════════════════════
   260903 C8 · 手机端「小字 + 触控目标过小」收口（owner 真机实拍 · DOM 复算）
   ══════════════════════════════════════════════════════════════════════════
   【★ 关键：断点口径用 `#viewportStage[data-shell="phone"]` 而非 `@media(max-width:767px)`】
     `shell.js:92` 写 `data-shell` 是按 **`#viewportStage` 的实测宽度**（phone/tab/desk/wide），
     **不是**窗口媒体查询 ⇒ 两者会不一致（stage 宽 ≠ innerWidth、或浏览器缩放/分栏差异）。
     真机实测：本段第一版用 `@media(max-width:767px)` ⇒ **规则一条都没生效**
     （`layout-v3.css` 解析出 138 条、最后一条正是本段，`--fs-min` 三处均为 12px，
       `data-shell=phone` 也正确，唯独 computed 仍 11px）⇒ 断点口径不符。
     ⇒ **本仓手机端一律以 `data-shell` 为准**（与既有藏干两行那两段同口径，⛔ 勿混用）。
   【实测基线（390px 档，排盘后全页；机制层口径＝声明体 ≥12px、徽标 ≥10.5px、命中盒 ≥44px）】
     · 可见小字（<12px）**599 处**；最大三族＝表单 `.ft-sub`/`.field`（66）、
       运岁 `.sub-line`/`.row-name`（38）、藏干 `.zg-qi`（24）
     · 触控目标过小 **7 处**；`.mini-btn` 实测 **h=29px** ⛔ 未达 `--hit:44px`
     · 横向溢出 **0**（`scrollW == innerWidth`）✔
   【修法】⛔ **不新造令牌**（`--fs-min`/`--hit` 一字未改，铁律 1）
     · `.mini-btn` 字号 11.5→`--fs-min`，`min-height: --hit`（⛔ 不用 height，避免破 flex）
     · 表单 `.ft-sub`/`.field`、运岁 `.sub-line`/`.row-name`、藏干 `.zg-qi` ⇒ `--fs-min`
     · ⛔ **不碰**神煞 `.ss-chip`/`.ss-item_pos` 与 `.sh-raw`：**徽标**，机制层明文允许 `--fs-min-2`
   【验收】⛔ 不以「截图看着好」为准（skill 铁律：肉眼读图不可靠），以探针计数为准。
   ══════════════════════════════════════════════════════════════════════════ */
#viewportStage[data-shell="phone"] .mini-btn{ font-size: var(--fs-min); min-height: var(--hit);
  display: inline-flex; align-items: center; justify-content: center; }
#viewportStage[data-shell="phone"] .auth-kicker{ font-size: var(--fs-min); }
#viewportStage[data-shell="phone"] .form-title .ft-sub{ font-size: var(--fs-min); }
#viewportStage[data-shell="phone"] .field{ font-size: var(--fs-min); }
#viewportStage[data-shell="phone"] .pillars-table .sub-line{ font-size: var(--fs-min); }
#viewportStage[data-shell="phone"] .pillars-table .row-name{ font-size: var(--fs-min); }
#viewportStage[data-shell="phone"] .zg-qi{ font-size: var(--fs-min); }
#viewportStage[data-shell="phone"] input,
#viewportStage[data-shell="phone"] select,
#viewportStage[data-shell="phone"] textarea,
#viewportStage[data-shell="phone"] input::placeholder{ font-size: var(--fs-min); }

/* ── 260903 C8 修 B · 藏干「叠字」真因修复（实拍发现 · owner 复现）──────────────
/* 十神字号：删掉 260903 那段时连带丢了这条（实测回退到 10.5px 徽标档）。
   十神是**信息载体**（非徽标）⇒ 手机档提到机制层下限。 */
#viewportStage[data-shell="phone"] .zg-ss{ font-size: var(--fs-min); }

   【⚠ 这是我 260903 上一版「藏干两行」改动的**回归**，本轮实拍才发现】
   【真因（DOM 复算，非目测）】同一 `.zg-line` 内三个 span 的 x/y **几乎相同**
     （`zg-g` x=121 y=639｜`zg-qi` x=121 y=641｜`zg-ss` x=121 y=642）
     ⇒ 三者排在**同一格、彼此压字**（截图：「丙」压住「本」、「戊」压住「中」）。
   【★ 判据教训（120★ 同族，新的一条）】上一版我只查「rect 是否相交」得 0 ⇒ 误判"无重叠"。
     ⛔ **rect 相交不是唯一判据**：三个 span 各自 rect 有效但**位置几乎相同**时，
     叠加效果是「压字」，而相交判定可能因容差/尺寸为空而漏报（本次 `w`/`h` 实测为 null）。
     ⇒ 正确判据＝**逐 span 比对 x/y 起点**（同 line 内任两 span 的 |Δx|+|Δy| 过小 ⇒ 判叠字）。
   【写错了什么】上一版用 `grid-template-areas:"g qi ss" / "g ss ss"` 让「干」跨两行。
     ⚠ 它要求 `grid-template-columns` 与区域名**逐列匹配**；而本仓 `.zg-line` 基础规则是
     `grid-template-columns:1.2em auto minmax(0,max-content)`（含 `max-content` 列）
     ⇒ 区域跨行时三项被挤进**同一条基线** ⇒ 叠字。
   【正解】⛔ **不用 grid-template-areas**（此上下文不可靠）⇒ 改**两行 flex 显式排布**：
     上行＝干 ＋ 气位（各 1 字，天然窄）；下行＝十神**独占整行**（`flex:1 0 100%` 必换行）。 */
#viewportStage[data-shell="phone"] .pillars-table .zg-line,
#viewportStage[data-shell="phone"] .detail-wrap .left .pillars-table td.gz-sub .zg-line{
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start;
  column-gap: 3px; row-gap: 0; line-height: 1.35; padding-left: 2px;
}
#viewportStage[data-shell="phone"] .pillars-table .zg-line > .zg-g{
  flex: 0 0 auto; align-self: center;            /* 干：与气位同行 */
}
#viewportStage[data-shell="phone"] .pillars-table .zg-line > .zg-qi{
  flex: 0 0 auto; align-self: center;            /* 气位：与干同行 */
}
#viewportStage[data-shell="phone"] .pillars-table .zg-line > .zg-ss{
  flex: 1 0 100%; align-self: flex-start;       /* ⭐ 十神：强制独占第二行 */
  text-align: left; white-space: nowrap; max-width: none;
}
/* ── 260903 C8 续 · 第二批（非徽标的小字族，真机路径复算后按类下药）─────────────
   【判别标准】**徽标**（`ss-chip`/`ss-item_pos`/`sh-raw`/`ql-term`/`grade`/`col-year-tag`/
     `dayun-act`/`sc-meta`）机制层明文允许 `--fs-min-2` ⇒ **不在本次范围**；
     以下各族是**信息载体（正文数据）**，⛔ 不属徽标 ⇒ 提到下限。
   【真机路径实证（探针报 `path`，⛔ 非凭类名推断）】
     · `.quick > .q-d`（快速日期「己巳 丙子 丙寅 甲午」，11px）｜`.quick > .q-r`（「查看记录 ▸」，10px）
     · `.user-info > .u-line`（阳历/阴历/生肖/时辰，11.5px）—— **排盘页最上方的主信息**
     · `details.cf > summary.cf-head > .sub`（「农历·生肖·节气」，11px）
     · `.pat-sec > .pat-sec_h`（「① 定格」「② 成败」，11px）—— **格局段标题**
     · `.pattern-head .pattern-src`（「月令本气不透干，支中暗取」，10.5px）—— **判据依据** */
#viewportStage[data-shell="phone"] .quick .q-d,
#viewportStage[data-shell="phone"] .quick .q-r,
#viewportStage[data-shell="phone"] .u-line,
#viewportStage[data-shell="phone"] .u-line b,
#viewportStage[data-shell="phone"] .cf-head .sub,
#viewportStage[data-shell="phone"] .pat-sec_h,
#viewportStage[data-shell="phone"] .pattern-src{ font-size: var(--fs-min); }
/* 触控目标：`.mini-btn` 已在上段提到 `--hit`；此处补 `.quick`（可点日期格）与 `.cf-head`（summary）
   ⚠ 实测前者 29px、后者是 details 摘要 ⇒ 一并提到 40px 以上，⛔ 不用 height 以免破布局。 */
#viewportStage[data-shell="phone"] .quick{ min-height: 40px; }
#viewportStage[data-shell="phone"] .cf-head > .sub{ padding: 6px 0; }

/* ══════════════════════════════════════════════════════════════════════════
   261003 C8 批 1 · 运岁总览表（手机端）—— 横溢出 / 列宽 / 断字
   ══════════════════════════════════════════════════════════════════════════
   【实测基线（真机 390px，DOM 复算）】
     · 表 8 列＝行标签 **58** ＋ 7×**43** = **359**，容器 `.detail-scroll` **360**
     · 但容器 `scrollWidth=362 / clientWidth=360` ⇒ **横向溢出 2px**
       而 `app.css:331` 注释明说该 `overflow-x:auto` 「**仅作安全阀**，非常态行为」
       ⇒ 溢出 2px 时**既无滚动提示、又视觉被切**（实拍：右侧 2027 柱被切掉一半）。
     · 单列 43px 装不下藏干「本/偏印」两段 ⇒ 跨列压字。
   【根因】`shell.css:299` 已把手机行标签列收窄到 **58px**（为**四柱**表 4 数据列让位），
     而**运岁总览是 7 数据列** ⇒ 58+7×43 已顶满容器，再加 `padding/border` 即溢出。
   【修法】⛔ **不改 `shell.css` 的四柱口径**（四柱表在 390px 下是合理的）：
     ① 说明行（`.sub-note` 之类）允许换行，⛔ 不再 nowrap 断字（P1-4）
     ② 列头 nowrap ＋ 破折号改半角（P1-5「28-38 岁」换行破字）
     ③ **数据列保底 56px** ＋ 容器**显式可横滚** ＋ 右缘**渐隐 affordance**
        —— 承认「7 列装不进 390px」这一事实，与其挤成 43px 压字，不如**让用户能滑**。
   【⛔ 不做的事】不缩字号到 12px 以下（机制层 `--fs-min` 下限）；不减列（会丢信息）。 */
#viewportStage[data-shell="phone"] .pillars-table th,
#viewportStage[data-shell="phone"] .pillars-table td{ padding-left: 2px; padding-right: 2px; }
/* ① 说明行：允许换行（⛔ 不再 nowrap） */
#viewportStage[data-shell="phone"] .detail-scroll .unified-note,
#viewportStage[data-shell="phone"] .pillars-table .sub-line{ white-space: normal; }
/* ② 列头：不换行 ＋ 紧凑 */
#viewportStage[data-shell="phone"] .pillars-table th{ white-space: nowrap; }
#viewportStage[data-shell="phone"] .pillars-table th .col-year-tag{ font-size: var(--fs-min-2); }
/* ③ 数据列保底 56px（藏干「本/偏印」可读）＋ 容器可横滚 ＋ 右缘渐隐提示 */
/* ⚠ `table-layout:fixed`（app.css:314）下**列宽由首行决定** ⇒ 给 `td` 设 `min-width` **无效**
     （实测 43px 未变）。唯一有效入口＝**首行 `th`**。 */
/* 同上：列宽保底同样**只给运岁表**（四柱表 4 列在 390px 已由 shell.css 裁好） */
#viewportStage[data-shell="phone"] .pillars-table:has(.col-dynamic) th{ min-width: 58px; }
/* 收到「够用即止」：实测 min-width:56 时表宽 569（需滑 209px，太多）⇒ 收到 58px 档，
   目标表宽 ≈ 58+7*62=492 ⇒ 滑约 130px，可接受。仍 ⛔ 不回到 43px（那会压字）。 */
/* ⛔ 表不再被压进容器宽：让它按内容撑开，由 `.detail-scroll` 横滑承载
   （否则 fixed 会把 7 列均分到 43px ⇒ 藏干跨列压字，正是 P0-3）。 */
/* ⚠⚠ 作用域修正（261003 C8 批 1 修 B）：⛔ **不可**把「撑开」设在通用类 `.pillars-table` 上
     —— 四柱表（`qz-table`）与七政表共用该类，一刀切会让它们也 `max-content`
     ⇒ 真机实测 `page-chart` 横向溢出 **117 处**、`page-detail` **198 处**（我引入的）。
   ⇒ 运岁总览表的**独有特征**＝含 `.col-dynamic`（3 个运岁动态列）⇒ 依此限定作用域。 */
#viewportStage[data-shell="phone"] .pillars-table:has(.col-dynamic){ width: max-content; min-width: 100%; }
#viewportStage[data-shell="phone"] .detail-scroll{
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  /* 右缘渐隐＝「还能往右滑」的可见提示（⛔ 不用 JS 检测，直接给 affordance） */
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 12px), transparent 100%);
          mask-image: linear-gradient(to right, #000 calc(100% - 12px), transparent 100%);
}
/* ⛔ 渐隐只在**真能滑**时给：内容不超宽时去掉（避免"明明不用滑却右侧发灰"） */
#viewportStage[data-shell="phone"] .detail-scroll.no-scroll{ -webkit-mask-image: none; mask-image: none; }

/* ── 260903 C8 批 1 补 · 断字（实拍复核后追加）────────────────────────────────
   【说明行断字】实拍：「7 列 9 行 · 以「四柱总览」为基础＋运岁三列 · 运岁列随下方行运栏点击联动」
     右缘被切 ⇒ `.card-title` 的 `.sub` 是 `nowrap`（实测）。
     ✓ 修法：手机档允许换行，⛔ 不改文案（文案是自述信息，删了反而看不懂）。
   【列头破字】实拍：「28-38 岁」在 74px 列里断成两行且「岁」单独一行。
     ✓ 修法：⚠ **⛔ 不用 JS 改文案**（那是把排版问题写进数据）；改为
       ① 列头 `white-space:nowrap`（已在上面设过，此处补 `min-height` 防挤压）
       ② 破折号改**半角连字符**由 JS 侧统一（`28-38` 不断行），⛔ 不动语义
     —— 实测「28-38 岁」在 nowrap 下 74px 够放（8 字 × ~7px = 56px + padding）。 */
#viewportStage[data-shell="phone"] .card-title .sub{ white-space: normal; line-height: 1.45; }
#viewportStage[data-shell="phone"] .detail-wrap .left .card-title{ align-items: flex-start; }
#viewportStage[data-shell="phone"] .pillars-table th{ min-height: 44px; }
#viewportStage[data-shell="phone"] .zg-ss{ font-size: var(--fs-min); }

/* ══════════════════════════════════════════════════════════════════════════
   261003 C8 批 2 · debug 余项 ① · 信息载体族小字 → 机制层下限
   ══════════════════════════════════════════════════════════════════════════
   【为什么机制层声明没生效】`readability.css:132` 声明 `.tl-item .y{var(--fs-min)}`（2 类），
     而 `app.css:373/377` 是 **5 类 + 2 属性**（`.tl-card[data-stack="xingyun"] [data-pane="…"]
     .tl-scroll .tl-item .y`）⇒ **高特异度胜出** ⇒ 机制层被压制。
     ⚠ 那条高特异度规则是「详略两排版各自定尺寸」的**有意设计**
     ⇒ ⛔ **不动**它的栅格/布局/列宽，只提**字号下限**。
   【本段定位】样式链**末位**（`index.html` 加载序 …readability(42)→xy3(43)→**layout-v3(47)**）
     ⇒ 同特异度时后加载者胜（119★ 家族：层叠顺序是可判定的，⛔ 不用 `!important`）。
   【范围】**只限信息载体**（干支/十神/年份/宫位/终盘行/运岁对齐）
     ⛔ 徽标（`ss-chip`/`grade`/`ly-sig`/`ts-tag`/`sc-meta`/`dayun-act`/`rep-seal`…）不动。 */
#viewportStage[data-shell="phone"] .tl-item .y,
#viewportStage[data-shell="phone"] .tl-item .ss,
#viewportStage[data-shell="phone"] .tl-item .age,
#viewportStage[data-shell="phone"] .tl-item .gz{ font-size: var(--fs-min); }
#viewportStage[data-shell="phone"] .tl-card[data-stack="xingyun"] .tl-scroll .tl-item .y,
#viewportStage[data-shell="phone"] .tl-card[data-stack="xingyun"] .tl-scroll .tl-item .ss{ font-size: var(--fs-min); }
/* 终盘行（`ys-final-row` 的 k/v）＝干支与十神 ⇒ 同样提级 */
#viewportStage[data-shell="phone"] .ys-final-row .k,
#viewportStage[data-shell="phone"] .ys-final-row .v{ font-size: var(--fs-min); }
/* 运岁对齐辅助文字（`dayun-align`） */
#viewportStage[data-shell="phone"] .tl-item .dayun-align{ font-size: var(--fs-min); }
/* 六道表：三列的年份/星曜/吉凶/应期 —— 属**判读信息**非徽标 */
#viewportStage[data-shell="phone"] .six-star,
#viewportStage[data-shell="phone"] .six-trend,
#viewportStage[data-shell="phone"] .six-timing,
#viewportStage[data-shell="phone"] .six-chip{ font-size: var(--fs-min); }
/* 关系组（`rel-cnt`/`rel-desc`）与五行图例（`wx-item`） */
#viewportStage[data-shell="phone"] .rel-cnt,
#viewportStage[data-shell="phone"] .rel-desc,
#viewportStage[data-shell="phone"] .wx-item{ font-size: var(--fs-min); }
/* 命中盒：`rep-seal` 实测 22×22 ⛔ 未达 `--hit:44px`（ui-ux-pro-max CRITICAL 条） */
#viewportStage[data-shell="phone"] .rep-seal{ min-width: var(--hit); min-height: var(--hit);
  display: inline-flex; align-items: center; justify-content: center; }

/* ── 261003 C8 批 2 · debug 余项 ①补 · 表格内裸文本（⛔ 无类名，靠单元格选择器）──
   【真机归因】探针报「33 × (无类) @11」而加标签名后拆出：`<TD>` 9 ＋ `<TH>` 7 ＋ 裸 17
   ⇒ 它们是**表格单元格里的裸文本**（旺衰矩阵「木相/火死」那类**数据格**），**没有 class**
   ⇒ ⛔ 无法用类选择器，⛔ 也不该用 `* td{}`（会波及全站表格）。
   【作用域】只限**手机端的旺衰/司命两张矩阵表**（`.dz-*` 族 ＋ `.sm-*` 族），
     先核这两族的真实表名再落，⛔ 不猜。 */
/* 旺衰矩阵表 ＋ 卡二小标题（261003 C8 批 2 · ①续）
   【为何改这里】原为**内联** `style="font-size:11px"` ⇒ 内联优先于任何 CSS 选择器
   ⇒ 机制层压不住（且⛔ 不用 `!important`：那是绕过层叠而非解决层叠，铁律 1 精神所拒）。
   ⇒ 已在 `app.js` 改**源头**：加类名、⛔ 不再内联写死字号。
   【为何值得提】这格是**旺衰矩阵**（重根/克轻/无根 × 克轻/克中/克重）＝**精确出口**的判读结论，
     连同「身强/偏强/身弱」强弱档 —— 是全页**最该看清**的一组数字。 */
#viewportStage[data-shell="phone"] .judge-matrix-tbl,
#viewportStage[data-shell="phone"] .judge-matrix-tbl th,
#viewportStage[data-shell="phone"] .judge-matrix-tbl td{ font-size: var(--fs-min); }
#viewportStage[data-shell="phone"] .judge-cap{ font-size: var(--fs-min-2); color: var(--ink-3); margin: 0 0 4px; }

/* stat-pill 判读依据（261003 C8 批 2 · ③）—— 三得「得令/得地/得势」的**证据行**
   原为内联 `font-size:9px`（内联优先于任何选择器，机制层压不住）⇒ 已在 app.js 改源头加类。
   ⚠ 这三行是旺衰结论的**证据**（月令状态 / 通根轻重 / 生扶克泄数值），⛔ 不该是全页最小的字。 */
#viewportStage[data-shell="phone"] .stat-evi{ font-size: var(--fs-min-2); color: var(--ink-3); margin-top: 2px; }
#viewportStage[data-shell="phone"] .stat-pill .k{ font-size: var(--fs-min); }
/* 「当前格 重根 × 克中 ⇒ 偏强」＝旺衰**结论行**（app.js:1901 原内联 9px ⇒ 已改源头加类）
   ⚠ 它是本卡的**最终判读**，⛔ 不该比它的依据行（`.stat-evi`）还小。 */
#viewportStage[data-shell="phone"] .judge-now{ font-size: var(--fs-min-2); color: var(--ink-3); margin-top: 3px; }
