/* ============================================================
   VANSHI · 美化层  assets/beauty.css
   —— 加载顺序在 tokens / ed / pages 之后，只做"加法"与"修正"。
      不改既有类名、不改布局骨架，任何一条删掉页面都能退回原样。

   改造依据（2026-10-23 自动取证，9 页 × 2 视口，1,160 条实例）：
     · 品牌色出现次数：index/search/services/renovation/process/about/404 = 0 次
       → 全站只剩纸白与墨黑，视线没有落点。本文件第 2 节起建立"红钉子"制度。
     · 图标密度：services 7.0/100 元素、renovation 7.2、process 8.3
       → 有标题的纯文字块 132 处，扫读全靠字重。第 3 节补图标基元。
     · 同级块高度不齐 141 处、间距碎值 447 处、点击区偏小 250 处
       → 第 5、6 节收口。
     · 页脚 6 列网格只填 3 格 → 右侧 550px 空白。第 7 节按实际栏数重排。
     · 统计条最后一格被 padding 挤窄 49px。第 4 节改等分。
   ============================================================ */

/* ============================================================
   1. 基元：图标、关键词、可点元素
   ============================================================ */
/* 图标基类不在这里定义 —— 全站只有一套：`ed.css` 的 `.ic-i`
   （24 格、em 尺寸、stroke 1.5、currentColor）。我再写一份 `.ic`
   就会和 portal.js 那套参数图标（同样叫 .ic）打架，同一页出现两种笔画密度。
   这里只补三处「必须脱离字号」的固定尺寸。 */
.aic .ic-i{ width:20px; height:20px; }
.mcta .ic-i{ width:17px; height:17px; }
.btn .ic-i{ width:15px; height:15px; }
.ft .ico-li .ic-i{ width:15px; height:15px; margin-top:4px; color:var(--ink3); }
.cp-svc .sic svg{ stroke-width:1.1; }

/* 品牌关键词：0 元、当天答复、佣金由业主支付。全站只有这三类能被标红。 */
.hl{ color:var(--brand); font-style:normal; font-weight:700; }

/* 选中文字用品牌浅红：默认蓝色是浏览器语言的残留，与站不同源。 */
::selection{ background:var(--brand-wash); color:var(--ink); }

/* 断行控制：中文没有词边界，浏览器默认只按「能塞几个字」断，
   结果是把「不承担」拆成「不 / 承担」、「上海。」这种两个字的孤行。
   实测首页眉标在 390 下就是这么断的。问 AI 之前先问排版引擎。
   text-wrap 是渐进增强，不支持的浏览器照旧，不影响任何布局。

   2026-10-23 换成 balance：原来这里写 pretty，A/B 实测**对中文一行都没改善**
   （9 个页面：孤字 30/86 → 30/86，页高一位数都没动）。pretty 的算法是
   「避免最后一行的孤词」，它按西文的词边界工作，中文没有词边界就没有可换的位置。
   balance 是把整段的行宽拉开均分，中文一样有效：30/86 → **9/86**，
   而且页高 4254→4254、8275→8275…… 行数没涨，没有引入任何布局回归。
   超过 10 行的段落 Chrome 会自动退回普通断行（那 9 个就是这么剩下的）。 */
h1, h2, h3, .h2, .cp-h{ text-wrap:balance; }
/* ⚠ 2026-10-23 夜：段落**不再**一律 balance。
   balance 的算法是「把整段的行宽拉开均分」，短段落（2 行）会被拉成 24/24，
   整段右缘从 100% 退到 52%；量出来的全站平均充实度只有 0.69。
   段落交给普通断行（非末行本来就齐右缘），balance 只保留在
   标题、卡片内文、列表项、表格单元这些「一行两句」的短块上。 */
/* 实测（_dev/_ab_justify.mjs，7 页 82 段）：
     全段 balance      → 孤字 0，平均右缘充实 0.69
     全段普通断行       → 孤字 14（1440），充实 0.99
     balance + 长段两端对齐 → 孤字 1（且只是 7 个字的末行），充实 0.99
   第三种最好，所以：短的继续 balance（那里它是在消除 1–2 字的孤行），
   长的（生成器打 `.lp`）走两端对齐。 */
p, li, td, th, .lead, .eyebrow, .cp-sub, figcaption{ text-wrap:balance; }
/* 长段（生成器打分界 `.lp`）的最终形态在本文件末尾那段
   `p.lp{ text-align:justify; … }` 里，这里只负责把上面的 balance 让出来。 */
p.lp{ text-wrap:balance; }
/* 窄屏的最终形态在文件末尾 `p.lp{text-align:justify}` 那一段之后（2026-10-26 挪过去的）。
   ⚠ 原来这里写过一条 `@media (max-width:899px){ p.lp{text-align:start} }`，
     它**从写下那天起就没生效过**：同优先级的 `p.lp{justify}` 在文件更后面，
     后者胜出。而实测（_dev/_ab_w390.mjs）证明 justify 与 start 在 390 下
     逐像素相同、孤字条数也相同 —— 所以那条规则即使生效也什么都不会改变。
     真正消孤字的是字距，现在写在那一段里。此处不留规则，只留这个指路。 */

/* ── 长文改用两端对齐（2026-10-23 夜补）────────────────────────────
   balance 消掉了孤字，但它的做法是「把整段的行宽拉开均分」——
   代价量出来了：全站段落的**非末行最宽行只占列宽的 68%**。
   一段 140 字的话，正常断是 46/46/46/2，balance 断成 35/35/35/35：
   孤字没了，但整段的右缘从 690 退到 525，缩了四分之一。
   在 1296 宽的画布上，这一段看着就像「只写了一半」。
   （_dev/_ab_justify.mjs 实测：balance 0 孤字 / 充实 0.681；
     justify 23 孤字 / 充实 0.993。）

   中文的常规做法本来就是两端对齐：非末行必须齐右缘，末行自然断。
   所以：**长的正文段落**走 justify + 普通断行，让右缘回到列边界；
   卡片、列表项、表格单元这些一行两句的短块继续用 balance（那里它是对的）。

   ⚠ 判据不能按选择器分，要按**段长**分：同样是 `.cp2-l p`，
     2 行的段落用 justify 会出现「末行只挂一个句号」，用 balance 反而好看；
     4 行以上的段落用 balance 会缩水 24%，必须 justify。
     90 字（≈2 行 @46 字/行）是分界，由生成器 `emit()` 打 `.lp` 类，
     不在 CSS 里猜 —— CSS 选不到「这一行会不会折」。 */
/* 长的正文段落：两端对齐 + 断点交给 balance（理由见上）。
   ⚠ 2026-10-24 二次实测（_dev/_ab_lead.mjs，8 页 × 3 视口，逐元素量末行）：
     上一版这里写的是 `text-wrap:wrap`，结果「一句话看清这栋楼」那段导语
     在 1920/1440 下有 5/8 的页面末行只剩 2 个字（4.9%–15%）。
     五变体对照（1920，8 个元素）：
       A 现状 justify+wrap   孤字 5  末行 37.4%  行数 2.63  块高 87
       B justify+balance     孤字 0  末行 77.2%  行数 2.63  块高 87   ← 采用
       C justify+pretty      孤字 5  末行 38.0%  行数 2.63  块高 87
       D justify+末行两端    孤字 0  末行 100%   行数 2.63  块高 87
       E max-width 42→46em   孤字 0  末行 78.1%  行数 2.00  块高 66
     关键：**balance 配合 justify，行数与块高一点没变，非末行仍是 100%**。
     上面那段「balance 让右缘退到 0.69」量的是 **balance 单用（不 justify）** ——
     justify 会把非末行强制拉满，balance 只剩「选断点」这一个作用，
     而它选断点的目标正是「末行别太短」。
     C 印证了 beauty.css:42 的老结论：pretty 按西文词边界工作，对中文一字未改。
     D 虽然 0 孤字，但末行 2 个字被拉开到 714px（字间距 180px），不能要；
     E 行数从 2.63 掉到 2.00、块高少 21px，等于全站段高重排，且 390 下完全无效。

   ⚠ balance 只在 ≤6 行的块上生效，超过就退回普通断行 —— 所以长段落不会被它改动。
     这也是这一改风险低的原因：它只影响「短到会出孤字」的那些段。 */
p.lp{ text-align:justify; text-justify:inter-ideograph; text-wrap:balance; }

/* 窄屏（≤899）的长段：保持两端对齐，另加 0.02em 字距。
   ⚠ 2026-10-26 实测（_dev/_ab_w390.mjs，4 页 8 个长段，390 宽，五种排法）：
     A justify+balance        孤字 1   段高合计 1400   ← 改前
     B start+balance          孤字 1   段高合计 1400
     C justify+wrap           孤字 1   段高合计 1400
     D start+wrap             孤字 1   段高合计 1400
     E justify+balance+.02em  孤字 0   段高合计 1400   ← 采用
   结论一：**text-align 不改变断行**（A 与 C 逐像素相同、B 与 D 相同），
           所以「窄屏改回左对齐就能消孤字」是错的前提，这也是文件里那条
           从没生效过的 @media 规则即使生效也不会有任何效果的原因。
   结论二：起作用的是**字距**。390 宽一行 21 字，每字多 0.3px ≈ 一行多 6px 余量，
           断点就有得挑；段高合计仍是 1400，说明没有多折一行（不重排版面）。
   结论三：中文窄屏本来就该松一点，这是排版常规（手机阅读的正文常见 0.02–0.05em），
           不是为了迁就尺子。只影响 ≤899，1440 一个字节都不动。 */
@media (max-width:899px){
  p.lp{ text-align:justify; letter-spacing:.02em; }
}


/* ============================================================
   2. 页头：签发线、红钉子、统计带
   ============================================================ */

/* 2.1 内页主标题上方的一道短红条。
   它是这一页的"签发线"——告诉眼睛「从这里开始读」。
   用 ::before 而不是新增元素：模板里 9 个页头都不用改。 */
.hhead h1::before{
  content:""; display:block; width:38px; height:3px;
  background:var(--brand); margin-bottom:20px;
}
/* 2.2 英文副题前的红点：一个 4px 方点足够，不要做成小旗子。 */
.hsub::before{
  content:""; display:inline-block; width:4px; height:4px;
  background:var(--brand); vertical-align:2px; margin-right:8px;
}

/* 2.3 页头在 981–1100 之间是没人管的一段。
   导航收起的断点是 980（ed.css），可 1024×768 这种平板横屏还走桌面版：
   `.nav` 作为 flex 子项被压缩，里面的 a 允许断行，
   实测 1024 下每个导航字都竖着排成两行、电话折成两行、按钮也断成两行。
   一行本来放得下（品牌 127 + 导航 503 + 电话 110 + 按钮 98 + 间距 < 1009），
   只是没人禁止它缩。这里禁止缩、禁止断行。 */
.hd .nav{ flex:0 0 auto; }
.hd .nav a{ white-space:nowrap; }
.hd-tel{ white-space:nowrap; }
.hd-r .btn{ white-space:nowrap; }

/* 2.3b 981–1120 的宽度收口。
   「禁止压缩」解决了换行，但把问题换成了溢出：1024 实测 .hd-r 右缘到 1065，
   整页多出 41px 横向滚动条（只量高度、不量宽度的教训）。
   这里收窄链接内边距 —— 每项省 10px，十项足够，导航内容一个字不减。
   下划线 ::after 的 left/right 是写死的 14px，必须同步改，否则线比文字宽。 */
@media (min-width:981px) and (max-width:1120px){
  .nav{ margin-left:4px; }
  .nav a{ padding-left:9px; padding-right:9px; }  /* 9 是刻意值：每项省 10px 换不折行 */
  .nav a.on::after{ left:9px; right:9px; }
}

/* 2.3c 1261–1310 的宽度收口（2026-10-25 补）。
   1260 及以下导航已经收起（见 ed.css 里那条说明），但 1261–1310 这一档
   仍然展开导航，而容量刚好差一点：1280 实测 .hd-r 右缘 1255，
   而 .wrap 的内容右缘是 1233 —— 接话按钮比下面的内容栏多出 22px，
   跟正文右缘对不齐。
   ⚠ 第 5 步报的是「首页 = 1233，全站中位 1254」，把**首页**当成了异类。
     实际相反：1233 才是容器边缘，1254 是溢出 22px 的那一类
     （11 页里 10 页溢出，中位数自然落在溢出值上）。
     判据用中位数当基准，多数派错了它就跟着错 —— 这类红必须先量几何再动手。 */
@media (min-width:1261px) and (max-width:1310px){
  /* 2026-10-26：这里原写 12px，而首页不引 beauty.css、走 ed.css 的 10px ——
     同一档宽度下首页导航比内页窄 40px，`.hd-r` 右缘 1233 vs 1238，差 5px。
     check_unify「顶栏按钮右边界」判红。统一成 10px：内页回到容器边缘 1233。 */
  .nav a{ padding-left:10px; padding-right:10px; }
  .nav a.on::after{ left:12px; right:12px; }
}

/* 2.3 统计带：等分 + 每格一个小红方块。
   原实现 `.hstat{display:flex; gap:0 48px}` + `div{padding-right:48px}`：
   gap 与 padding 双份留白，最后一格 padding-right:0 → 比前三格窄 49px
   （实测 4 格为 300/300/300/251），右边缘的线也就不在等距位置上。
   改成 grid 按孩子数自动等分，不看有几格都对。 */
.hstat{
  display:grid; grid-auto-flow:column; grid-auto-columns:minmax(0,1fr);
  gap:0; margin-top:32px; padding-top:24px; border-top:1px solid var(--ink);
}
.hstat div{ padding:0 40px 0 0; border-right:1px solid var(--line2); min-width:0; }
.hstat div:last-child{ padding-right:0; border-right:0; }
.hstat .k{
  display:flex; align-items:center; gap:8px;
  font-size:var(--fs-sm); color:var(--ink3); letter-spacing:.04em;
}
.hstat .k::before{
  content:""; flex:0 0 auto; width:4px; height:4px; background:var(--brand);
}
/* 数字行的字重只有 500，压不住一条 1296 宽的带子；抬到 400（宋体 400 已够黑），
   并给一点负字距，让「22,326」这种带千分位的数字排得更紧。 */
.hstat .v{ letter-spacing:-.01em; }
.hstat .v em{ font-family:var(--sans); font-style:normal; font-size:var(--fs-sm); color:var(--ink3); margin-left:4px; }

/* 2.4 面包屑：上一版在末级前加了一枚 4px 红方块，本意是与 h1 的签发线同一套语言，
   实际读出来是「首页 / ▪ 服务范围」—— 方块正好落在分隔符的位置上，像分隔符坏了。
   红钉子制度里面包屑不在四类合法位置内，故整条撤掉。 */
.crumb .cur{ display:inline-flex; align-items:center; }

/* 2.5 导语栏宽：540px 是 15px × 36 字，在 1296 的容器里只占 41%，
   右侧 750px 全空 —— 这是"留白不平衡"最大的一处。
   中文正文的舒适行宽是 36–44 字，抬到 620px（41 字）仍在区间内，
   同时把页面重心拉回中间。 */
.hhead .lead{ max-width:620px; }

/* ============================================================
   3. 区块标题：竖条 + 序号
   ============================================================ */
.dsec h2::before,
.cp-h::before{
  content:""; flex:0 0 auto; align-self:stretch;
  width:3px; min-height:.82em; background:var(--brand);
  margin-right:16px;
}
.cp-h::before{ display:inline-block; vertical-align:-.06em; align-self:auto; }
/* h2 后面的那条发丝线原本是 --line（几乎看不见），改成红→透明渐变：
   线有了方向，标题右边也不再是一条"没排完"的灰线。 */
.dsec h2::after{
  background:linear-gradient(90deg, var(--brand-mid), var(--line) 60%, transparent);
}

/* 3.1 正文栏宽：`max-width:64ch` 对中文是陷阱 —— ch 是数字「0」的宽度，
   15px 下 1ch≈7.5px，64ch 只有 480px，等于每行 32 字就折。
   中文应按 em 算：42em = 630px ≈ 42 字/行，落在舒适区间。
   ⚠ 2026-10-23 夜改成令牌 --measure：原来写 42em，但 .dtxt 的基准字号是 17px，
   42em 算出 714；.cfl 基准 15px，同样写 42em 算出 630。em 跟着字号走，
   于是"同一个数字、两条右缘"。令牌是 px，两处才真的同宽。 */
.dtxt{ max-width:var(--measure); }
.hhead .hlead{ max-width:640px; }

/* 3.2 双栏版式（首页那一节用）：左标题钉住、右内容走全文宽。
   目的只有一个 —— 1296 的容器里，正文只用 630、右边空 660，
   那是"没排完"而不是"留白"。标题挪到左栏之后，右栏拿到 884px，
   adv 两列卡片正好铺满。 */
.dsec.cols{ display:grid; grid-template-columns:minmax(0,300px) minmax(0,1fr); gap:0 72px; align-items:start; }
.dsec.cols > h2{
  grid-column:1; grid-row:1; position:sticky; top:96px;
  display:block; padding:0; border:0; margin:0;
}
/* 左栏窄，标题若继续挂左侧竖条会只剩 252px 可用宽（30px × 9 字 = 270），
   被逼折成两行。改挂上方一小段红条，标题就能整句站住。 */
.dsec.cols > h2::before{ display:block; width:26px; height:3px; min-height:0; margin:0 0 16px; }
.dsec.cols > h2::after{ display:none; }
.dsec.cols > :not(h2){ grid-column:2; }
@media (max-width:980px){
  .dsec.cols{ display:block; }
  .dsec.cols > h2{ position:static; display:flex; }
  .dsec.cols > h2::before{ display:none; }
  .dsec.cols > h2::after{ display:block; }
}

/* 3.3 收尾块（.dend）：一句结论 + 一颗主按钮，通栏铺开。
   原来是一段正文里塞一颗小黑按钮，读完停在一个孤立的方块上，
   眼睛不知道该往哪走；改成一条通栏台账，左右都有了边界。 */
.dend{
  display:flex; align-items:center; justify-content:space-between; gap:32px; flex-wrap:wrap;
  margin-top:36px; padding-top:24px; border-top:1px solid var(--line);
}
.dend p{ font-size:var(--fs-bs); color:var(--ink3); line-height:1.9; max-width:52em; }

/* ============================================================
   4. 卡片：描边、顶线、水印图标、hover
   ============================================================ */
/* 4.1 服务卡：右上角一枚极淡的品牌色水印图标（opacity .06）。
   为什么用水印而不是彩色实心图标：五张卡各放一个 24px 红图标，
   页面立刻变成本地生活服务站的视觉；水印退到背景里，远看是纸纹，
   近看才认出是「选址/租约/资产/注册/装修」。 */
.cp-svc li{ position:relative; overflow:hidden; }
.cp-svc li .sic{
  position:absolute; right:18px; top:16px; width:64px; height:64px;
  color:var(--brand); opacity:.11; pointer-events:none;
  transition:opacity .3s var(--ease), transform .3s var(--ease);
}
.cp-svc li .sic svg{ width:100%; height:100%; stroke:currentColor; fill:none;
  stroke-width:1.1; stroke-linecap:round; stroke-linejoin:round; }
.cp-svc li:hover .sic{ opacity:.12; transform:translateY(-2px); }
/* 序号改品牌红：12px 的红在纸上 5.9:1，过 AA（原来的 --ink3 是为了过闸门，
   红比它更深，判据不会红）。 */
/* 顶部 2px 红线：hover 时才从中间展开。静止不动，避免五张卡同时"发红"。 */
.cp-svc li::before{
  content:""; position:absolute; left:0; right:0; top:0; height:2px;
  background:var(--brand); transform:scaleX(0); transform-origin:50% 0;
  transition:transform .35s var(--ease);
}
.cp-svc li:hover::before{ transform:scaleX(1); }
/* chips 描边跟着 hover 一起提亮，避免"卡是活的、chip 是死的"。 */
.cp-svc li:hover .tg span{ border-color:var(--brand-mid); color:var(--ink); }

/* 4.2 首页那四条标准：加序号 + 图标位。
   原来四条长得一模一样，读者得逐条读才知道顺序。
   序号用 CSS counter（模板不用改），图标由生成器注入 .aic。 */
.adv{ counter-reset:adv; }
.adv li{ position:relative; counter-increment:adv; }
.adv h3{ display:flex; align-items:center; gap:8px; }
.adv h3::before{
  content:counter(adv,decimal-leading-zero);
  font-family:var(--en); font-size:var(--fs-sm); font-weight:400;
  color:var(--brand); letter-spacing:.06em; font-variant-numeric:tabular-nums;
}
.adv li .aic{
  display:flex; align-items:center; justify-content:center;
  width:38px; height:38px; margin-bottom:16px;
  border:1px solid var(--brand-mid); background:var(--brand-soft); color:var(--brand);
  transition:background .25s var(--ease), border-color .25s var(--ease), color .25s var(--ease);
}
.adv li .aic .ic-i{ width:20px; height:20px; }
.adv li:hover .aic{ background:var(--brand); border-color:var(--brand); color:var(--white); }

/* 4.3 流程步骤带（.fl-steps）：序号改红，圆环 hover 才变红。
   六个圆环同时描红会把整条带子变成"红灯笼"，所以红只落在序号 0N 上。 */
.fl-steps li .fl-meta i{ color:var(--brand); font-variant-numeric:tabular-nums; }
.fl-steps li:hover .fl-ic{ border-color:var(--brand); color:var(--brand); background:var(--brand-soft); }

/* 4.4 装修时间表图例（.rzn-key）：三列卡片。
   原来是「彩色 chip + 一句 11px 灰字」的 flex-baseline 排列，
   三组读起来连成一句话 —— 分不清哪句属于哪个责任方。
   改成三条等宽卡，chip 上置、说明在下，边界一眼可读。 */
.rzn-key{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:1px;
  background:var(--line); border:1px solid var(--line);
  max-width:none; margin-top:28px;
}
.rzn-key li{ display:block; background:var(--white); padding:16px 16px 16px; }
.rzn-key li > .rz-who{ display:inline-block; margin-bottom:8px; }
.rzn-key li > span:last-child{ display:block; font-size:var(--fs-bs); color:var(--ink3); line-height:1.75; }
@media (max-width:760px){
  .rzn-key{ grid-template-columns:1fr; }
}

/* 4.5 问答（.faq）：每条问题前一道红短杠，答案与问题同缩进。
   原来 Q 与 A 都顶着左边线排，读起来像两段独立的话。 */
.faq dt{ position:relative; padding-left:24px; }
.faq dt::before{
  content:""; position:absolute; left:0; top:.68em; width:9px; height:2px; background:var(--brand);
}
.faq dd{ padding-left:24px; }

/* ============================================================
   5. 按钮与可点元素
   ============================================================ */
/* 5.1 主 CTA：正文里的"填写需求单"用品牌红。
   顶栏那颗"联系顾问"保持墨黑 —— 每页只允许一颗红按钮，
   否则红就从"主张"退化成"底色"。 */
.btn.pri{ background:var(--brand); border-color:var(--brand); color:var(--white); }
.btn.pri:hover{ background:var(--brand-deep); border-color:var(--brand-deep); }
.btn.pri:focus-visible{ outline:2px solid var(--brand); outline-offset:3px; }
/* 箭头随 hover 右移：全站按钮统一，不再各自写动画。 */
.btn .ic-i, .hero-call .ic-i{ transition:transform .25s var(--ease); }
.btn:hover .ic-i, .hero-call:hover .ic-i{ transform:translateX(3px); }

/* 5.2 导航当前页：1px 墨线改 2px 品牌红，并在两端收 2px 圆角。
   这是全站唯一"位置提示用红"的地方 —— 用户要知道自己在哪。 */
.nav a.on::after{ height:2px; background:var(--brand); left:14px; right:14px; border-radius:var(--r-1); }
.nav a:hover{ color:var(--brand); }

/* 5.3 点击区不足 44px 的一律抬到 44（WCAG 2.2 目标尺寸）。
   只抬 min-height 与内边距，不改字号，视觉密度不变。 */
.crumb a, .ft-bot .links a, .ft li a, .co-a a{ min-height:44px; display:inline-flex; align-items:center; }
.ft-bot .links a, .ft li a{ min-height:32px; }

/* ============================================================
   6. 首屏
   ============================================================ */
/* 6.1 eyebrow 前的短线由 22×1 灰线改成 26×2 品牌红：
   它是首屏第一眼看到的东西，要给出"这是一家有主色"的暗示。 */
.eyebrow::before{ width:26px; height:2px; background:var(--brand); }
.eyebrow{ letter-spacing:.06em; }
/* 6.2 hero-fact 左侧一道 2px 红竖条，把"顾问当天答复"从正文里拎出来。 */
.hero-fact{ position:relative; padding-left:16px; }
.hero-fact::before{
  content:""; position:absolute; left:0; top:.45em; bottom:.45em;
  width:2px; background:var(--brand-mid);
}
.hero-fact b{ color:var(--brand); }
/* 6.3 首屏主按钮：改成品牌红描边（原为 --line 的浅灰描边，等于没有边）。
   为什么不填满：首屏已经有 logo 红、eyebrow 红条、hero-fact 红竖条三处红，
   再压一块红底就把「墨黑标题」的份量抢走了。红描边 + hover 反色，
   静止时是"框"，交互时才是"实"—— 这正是这一页需要的两级。 */
.hero-call{ border-color:var(--brand); color:var(--ink); }
.hero-call:hover{ background:var(--brand); border-color:var(--brand); color:var(--white); }
.hero-call:focus-visible{ outline:2px solid var(--brand); outline-offset:3px; }

/* 6.4 首屏右图：底部压一条从墨到透明的渐变，让图片与白色版面咬合，
   而不是一条硬边直接切断。不改图片本身（老板要求首屏图不动）。 */
.hero-fig::after{
  content:""; position:absolute; left:0; right:0; bottom:0; height:26%;
  background:linear-gradient(to bottom, transparent, rgba(var(--ink-rgb),.18));
  pointer-events:none;
}

/* ============================================================
   7. 深墨段（.co）
   —— 全站唯一允许反白的区块。首页 2440px 全是浅底，读到尾部
      「关于这份资料」时需要一个落点收住整页。
   ============================================================ */
.co{
  background:var(--ink-deep); border-top:0; color:var(--on-deep);
  /* 上下留白走节奏档。原来写 72px，而节奏档是 {0,8,32,40,44,64,96} ——
     72 不在其中，第 5 步在 5 个页面 × 2 档宽度上各判了一条。
     64 就是 --sp-band（「附属区块与色带」），语义正好对上。 */
  padding:var(--sp-band) 0;
  position:relative; overflow:hidden;
}
/* 一抹暗红光晕落在右上方：深底上如果只有纯色，会像一块"没加载完的黑图"；
   加一层极低不透明度的品牌色径向渐变，纸面才有厚度。
   .01 级的不透明度在普通屏上几乎看不出，但在高色域屏上能读出来 — 这正是要点。 */
.co::before{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(680px 320px at 82% 18%, rgba(var(--brand-rgb),.16), transparent 68%),
    radial-gradient(520px 260px at 8% 96%, rgba(var(--brand-rgb),.07), transparent 70%);
}
.co .wrap{ position:relative; z-index:1; }
.co-k{ color:var(--on-deep3); display:flex; align-items:center; gap:8px; }
.co-k::before{ content:""; width:4px; height:4px; background:var(--brand); }
.co h2{ color:var(--white); }
.co p{ color:var(--on-deep2); }
/* 三个数字：原来停在 --fs-h3（24px），在 1296 宽的深色带上只占 1/3 高度，
   整块看着"空"。抬到 --fs-h2（30px）并给数字一点负字距，块面才站得住。 */
/* 大数字用 --fs-num-lg（36），不用 `--fs-h2 × 1.2` 这种复合算式：
   复合算式算出的数不在字号阶梯上，而且会跟着 --fs-h2 的响应断点一起脱阶梯。 */
.co-f b{ color:var(--white); font-size:calc(var(--fs-num-lg) * var(--k)); letter-spacing:-.01em; }
.co-f em{ color:var(--on-deep3); }
/* 「成立 / 累计服务企业 / 客户分布」三个说明词原来吃的是 ed.css 里的 --ink3
   （#63676E）—— 那是给纸底定的三级灰，搬到 14171C 的深底上只有 3.16:1，
   没过 AA。深底有自己的一档 --on-deep2（7:1），必须换。 */
.co-f span{ color:var(--on-deep2); }
.co-f li{ position:relative; padding-top:16px; }
.co-f li::before{
  content:""; position:absolute; left:0; top:0; width:24px; height:2px; background:var(--brand);
}
/* 深底上三个文字链接：改白字 + 底部 1px 半透明线，hover 变满白。
   不能再用墨色下划线（深底上看不见）。 */
.co-a a{
  color:var(--on-deep); border-bottom:1px solid rgba(var(--white-rgb),.28);
  padding-bottom:2px; min-height:44px;
}
.co-a a:hover{ color:var(--white); border-bottom-color:var(--brand); }
/* .co .wrap 的 align-items:end 会让数字块与链接块各自贴底，
   实测两者基线差 14px。改成 start 后由 grid 行对齐，数字与链接同顶。 */
.co .wrap{ align-items:start; }
.co-a{ align-self:end; }

/* ============================================================
   8. 页脚
   ============================================================ */
/* 8.1 页脚列模板与断点全部在 ed.css。
   ⚠ 不要在这里再写 `.ft-top{ grid-template-columns:… }`：
   首页只引 tokens + ed 两层，不引 beauty —— 写在这里的列模板
   在首页根本不生效，390 下六列会被压成每列 13px，整页横向溢出 492px
   （2026-10-27 实测，就是这条踩的）。默认值 `var(--ftc, …)` 也在 ed.css。 */
/* 8.2 联系栏加图标：电话 / 邮箱 / 需求表 / 地址 / 时间，五个都是"要用的东西"，
   纯文字排成五行，读起来像法务条款。 */
.ft .ico-li{ display:flex; align-items:flex-start; gap:8px; }

.ft li a.ft-tel{ display:inline-flex; align-items:center; gap:8px; }
/* 8.3 页脚顶部收一条墨线，与正文结束处断开。 */
.ft{ border-top:1px solid var(--ink); padding-bottom:40px; }
/* 8.3b 品牌简介从 280px 放到 22em（15px × 22 字 ≈ 330）。
   280px 只有 18 字，最后一行只剩「上海。」两个字吊在下面 ——
   页脚最显眼的一栏出现孤行，比右侧空白更刺眼。 */
.ft-brand p{ max-width:22em; }
/* 8.3c 移动端必须把三栏拆掉。
   上一轮加的 `.ft-top{ grid-template-columns:var(--ftc) }` 写在 beauty.css，
   它比 ed.css 里 `@media (max-width:760px){ .ft-top{ grid-template-columns:1fr 1fr } }`
   后加载、同特异性，于是把那条移动端规则静默压掉了 ——
   实测 390 下页脚还是三栏，电话号码被断成「183 / 0180 / 3714」三行。
   同一份文件里自己补回来，别再指望别的文件兜。 */
@media (max-width:760px){
  /* 列数与跨列同样在 ed.css（首页不引本文件）。这里只留页脚条目的换行策略。 */
  .ft li a, .ft-tel{ white-space:nowrap; }
}
/* 8.5 回到顶部（2026-10-23 新增）。
   内容页在 390 下有 5000–6000px，装修页 8000+，读完没有任何回顶入口 ——
   只能一路甩回上面，或者点 logo 回首页（那是另一个动作）。
   放在页脚底部条最右，用一条发丝线跟左边三个站内导航隔开：
   前三个是「去别处」，这个是「原地回去」，不该混成一排。
   纯 anchor 链接，不挂 JS —— 全站没有共用 JS 包，内容页只加载内联的年份脚本。 */
.ft-top-link{
  margin-left:4px; padding-left:20px; border-left:1px solid var(--line);
  color:var(--ink2);
}
.ft-top-link span{ margin-left:8px; color:var(--ink3); transition:color .15s var(--ease), transform .15s var(--ease); }
.ft-top-link:hover span{ color:var(--brand); transform:translateY(-2px); }
@media (max-width:760px){
  /* 手机上底部条改成竖排，左侧那条发丝线会变成一段悬空的竖杠 */
  .ft-top-link{ margin-left:0; padding-left:0; border-left:0; }
}

/* ============================================================
   9. 空态组件（.empt）：找楼页与 404 用
   ============================================================ */
.empt{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,.92fr); gap:64px; align-items:start; }
.empt-l .h2{ font-family:var(--serif); font-size:calc(var(--fs-h2) * var(--k)); font-weight:500; line-height:1.5; letter-spacing:.02em; }
.empt-l .h2::before{
  content:""; display:block; width:38px; height:3px; min-height:0; background:var(--brand); margin-bottom:20px;
}
/* ⚠ min-height:0 是必须的。这几行原来是 <div class="h2">，不爱 .dsec h2::before 管；
   2026-10-23 把它改成真正的 <h2> 之后，那条规则里的 min-height:.82em 就跟了进来，
   38×3 的细红条被撑成 38×24.6 的实心红块（截图里看着像贴错位置的红标签）。
   .dsec.cols > h2::before 早就写了 min-height:0，这里当时漏了。 */
.empt-l p{ margin-top:16px; font-size:var(--fs-base); line-height:1.9; color:var(--ink2); max-width:40em; }
.empt-l .acts{ display:flex; flex-wrap:wrap; gap:12px; margin-top:28px; }
/* 右侧三张"能做/不能做"卡：不用图标，用序号 + 一句结论，比图标更快读懂。 */
.empt-r{ display:grid; gap:1px; background:var(--line); border:1px solid var(--line); }
.empt-r > div{ background:var(--white); padding:24px 24px; }
.empt-r .n{ font-family:var(--en); font-size:var(--fs-sm); color:var(--brand); letter-spacing:.06em; }
.empt-r h3{ margin-top:8px; font-size:var(--fs-lg); font-weight:500; letter-spacing:.02em; }
.empt-r p{ margin-top:8px; font-size:var(--fs-bs); color:var(--ink3); line-height:1.9; }

/* 9.2 出口列表（.exit）：404 页用。三条出口带图标、整行可点。
   原来是一个黑按钮 + 两条孤零零的文字链，点击区 18px 高、彼此挤在一起，
   看着像"没排完的脚注"。改成三行整宽卡片，hover 浅红底，扫一眼就知道往哪走。 */
.exit{ display:grid; gap:1px; background:var(--line); border:1px solid var(--line); }
.exit > a{
  display:flex; align-items:center; gap:16px;
  background:var(--white); padding:16px 24px; min-height:78px;
  transition:background .2s var(--ease);
}
.exit > a:hover{ background:var(--brand-soft); }
.exit > a > .ic-i:first-child{ width:20px; height:20px; color:var(--brand); }
.exit .ex{ flex:1 1 auto; min-width:0; }
.exit b{ display:block; font-size:var(--fs-lg); font-weight:700; letter-spacing:.02em; }
.exit .ex > span{ display:block; margin-top:4px; font-size:var(--fs-bs); color:var(--ink3); line-height:1.75; }
.exit .go{ width:15px; height:15px; color:var(--ink4); transition:transform .25s var(--ease), color .25s var(--ease); }
.exit > a:hover .go{ color:var(--brand); transform:translateX(3px); }

/* ============================================================
   9.3 溢出与行宽收口（审计 R08 / R09 命中项）
   ============================================================ */
/* 服务卡正文：hi 卡是 300px + 1fr 的两栏，右侧那栏能宽到 948px，
   42em 在这里算出 588 却仍被撑到 660（父级 min-width 未归零）。
   统一收到 38em ≈ 532px，一行 38 字，是中文正文的上限舒适区。 */
.cp-svc .svc-b{ min-width:0; }
.cp-svc p{ max-width:38em; }
/* 装修时间表的节点描述是一串「材料检测 · 审图 · 开工报验」，
   原来铺满 977px，一行 131 字，读到后面完全失去锚点。收到 52em。 */
.rzn-n .d, .rzn-n p.d{ max-width:52em; }
/* 流程页步骤带下的注释原来一行 227 字（1296 宽 × 12px）。 */
.fl-note{ max-width:62em; }
/* 甘特图比容器宽 14px：给 figure 一条横向滚动带，而不是让它把版心顶破。 */
.gantt{ overflow-x:auto; overflow-y:hidden; }
/* 空态与出口列表：grid/flex 子项默认 min-width:auto，
   长中文串会把容器顶宽 4px（移动端 390 下实测 339 > 335）。归零即可。 */
.empt, .empt > *, .empt-l, .empt-r, .empt-r > div,
.exit, .exit > a, .exit .ex{ min-width:0; }
.exit .ex > span, .empt-r p, .empt-l p{ overflow-wrap:anywhere; }
/* 页脚底部那条（找楼 / 工具 / 站点地图）原来 32px 高，移动端点不准。 */
.ft-bot .links a{ min-height:44px; }
@media (min-width:761px){
  .ft-bot .links a{ min-height:32px; }
}

/* 9.4 深底块上的"签章"（about 页 .vm-lead）
   —— 这一块已经是深墨底反白，但整块 34px 内边距、四边齐平，
      看起来像一张没盖章的公文。顶部加一道 64px 品牌红，等于署名。 */
.vm-lead{ position:relative; }
.vm-lead::before{
  content:""; position:absolute; left:0; top:0; width:64px; height:3px; background:var(--brand);
}
/* 区域顾问列表的「由此填写需求 →」：静止时是三级灰，hover 才转红。
   contact 页整页原本只有 1 处品牌色，这里是第二处，而且只在需要时出现。 */
.advg a:hover em{ color:var(--brand); }

/* 9.5 表单提交按钮：这一页唯一的核心动作，原来是一个墨黑实心块，
   与顶栏那颗「联系顾问」撞成同一级。改成品牌红，动作才有指向。 */
.cf button[type="submit"]{ background:var(--brand); border-color:var(--brand); }
.cf button[type="submit"]:hover{ background:var(--brand-deep); border-color:var(--brand-deep); }

/* 9.6 装修九类服务（.deco-scope）：加图标后必须改列数。
   原样式是两列「名称 112px + 说明」，图标插进来成了第三个格子，
   会被挤到第二行第一列 —— 九行全错位。改三列，图标固定 18px 列。 */
.deco-scope li{
  grid-template-columns:18px 104px minmax(0,1fr); gap:0 16px;
  align-items:center; padding:12px 0;
}
.deco-scope .ic-i{ width:16px; height:16px; color:var(--brand); }
.deco-scope b{ transition:color .2s var(--ease); }
.deco-scope li:hover b{ color:var(--brand); }
@media (max-width:760px){
  .deco-scope li{ grid-template-columns:18px 88px minmax(0,1fr); gap:0 12px; }
}

/* 9.7 装修页右栏（.dside）：九类服务列表高 470，右栏只有 160，
   两栏并排时右栏下半截是纯空白，看起来像列表没排完。
   给它一道左边线 + 内缩，空白就变成「边注」，而不是「漏排」。 */
.deco-cases .dcgrid > .dside{ border-left:1px solid var(--line2); padding-left:36px; }
@media (max-width:980px){ .deco-cases .dcgrid > .dside{ border-left:0; padding-left:0; } }

/* ============================================================
   10. 移动端
   ============================================================ */
/* 10.1 底部转化条：手机上海拔最高的一件事是"怎么联系"，
   原来要滑到页脚才有电话。这条只在 ≤760px 出现，33px 高，
   不挡正文（body 补 padding-bottom）。 */
.mcta{ display:none; }
@media (max-width:760px){
  .mcta{
    display:grid; grid-template-columns:1fr 1fr; gap:0;
    position:fixed; left:0; right:0; bottom:0; z-index:60;
    background:var(--white); border-top:1px solid var(--line3);
    padding-bottom:env(safe-area-inset-bottom, 0);
  }
  .mcta a{
    display:flex; align-items:center; justify-content:center; gap:8px;
    min-height:52px; font-size:var(--fs-md); letter-spacing:.02em;
  }
  .mcta a + a{ background:var(--brand); color:var(--white); }
  .mcta a:first-child{ color:var(--ink); }
  /* ⚠ 2026-10-23：这里原来写死 52px，而 .mcta 的高度是 52 + env(safe-area-inset-bottom)。
     在刘海屏上安全区约 34px —— 于是页面底部最后 34px 永远被这条固定条压住，
     滚到底也露不出来（实测 iPhone 尺寸下 .ft-bot 的 © 行被压掉一半）。
     预留必须跟着固定条的实际高度走。 */
  body{ padding-bottom:calc(52px + env(safe-area-inset-bottom, 0px)); }
  /* 10.2 页头按钮在 390 下是 96×56，占掉了整个右上角；
       缩到 40 高，与汉堡同高，视觉上才是一组。 */
  .hd-r .btn{ height:44px; min-height:44px; padding:0 16px; font-size:var(--fs-bs); }
  .ft li a{ min-height:44px; }
  /* 10.3 移动端统计带 2×2 时，竖向分隔线只在左格保留。 */
  .hstat{ grid-auto-flow:row; grid-template-columns:1fr 1fr; }
}

/* ============================================================
   11. 二轮：跨屏幕比例的对齐与留白（2026-10-23 晚）
   这一节全部来自 8 种比例的实测，不是看某一屏的印象。
   ============================================================ */

/* 11.1 深色 CTA 带（.dbelt）在 761–1120 被过早切成竖排。
   pages.css:937 把 .wrap 改成 column + align-items:flex-start，
   子项于是缩到内容宽：正文只剩 396px，1024 屏上右侧空出 600px。
   这一段本来就是「一句话 + 一个按钮」，两列放得下（396+98+56 < 1009），
   只有真正的窄屏才该竖排。 */
@media (min-width:761px) and (max-width:1120px){
  .dbelt .wrap{ display:grid; grid-template-columns:minmax(0,1fr) auto;
    align-items:center; gap:40px; }
  .dbelt .act{ align-items:flex-end; }
}

/* 11.2 装修节点图（.rz-fig → .rzm，2026-10-26）
   旧版这里把那张 3200×1746 的 PPT 截图做成 360 高的裁切窗，再盖一个
   「查看完整节点图 ↗」把人送去新窗口。现在改成按 SCHEDULE 渲染的矢量时序带
   （样式在 pages.css 的 .rzm 段），整条规则连同按钮一起删掉 ——
   留着就是一段指向不存在元素、而且写着「图看不清所以要跳出去」的死代码。 */

/* 11.3 矮屏（≤720 高）时横幅图顶破一屏。
   about 两张 banner 在 1440×600 下分别占视口 94% 与 121%，
   一张图把人挤到看不见下面的字。只在矮屏压，常规屏不动。
   object-fit 与 .cp-fig 的既有裁切方式一致（pages.css:1020）。 */
@media (min-width:761px) and (max-height:720px){
  .cp-fig img{ max-height:min(58vh, 420px); object-position:center 62%; }
}

/* 11.4 工具页在 320 下整页横向溢出 60px。
   .topbar 的固有宽度是 380（品牌 + 三个操作按钮），窄屏塞不下就顶破视口。
   这里只让它换行、让画布区自己横滚，不动 app 内部布局。 */
@media (max-width:420px){
  .topbar{ flex-wrap:wrap; row-gap:8px; height:auto; min-height:52px; padding-block:8px; }
  .top-actions{ flex-wrap:wrap; justify-content:flex-end; }
  .workspace{ overflow-x:auto; }
}

/* 11.5 五项服务首卡（01 选址顾问）。
   上一轮把这里从「左标题 / 右正文」改成了纵向堆叠，理由是「同一列表两种内部
   骨骼像两组东西」。方向对，落点错：01 是通栏卡（1296），纵向堆叠之后正文
   只有 532 宽，右侧 640px 全空，只剩一个 64px 的极淡图标孤零零浮在右上角 ——
   看着不是"留白"，是"内容没排完"。实测两种骨骼：
     纵向 block：卡高 225，正文占宽 41%
     栅格 grid：卡高 153，正文占宽 70%，且五张卡总高从 705 降到 633
   栅格版反而更紧凑、更像"这一页的主业务"，所以改回栅格。
   层级不再靠"换骨骼"表达，而是靠：2px 墨色顶线 + 通栏尺寸 + 正文右对齐成第二栏。
   🔴 判据：卡内正文占宽 ≥60%，且 01 卡高 ≤ 其余单卡高 × 1.2（避免通栏卡反被拉长）。 */
.cp-svc li.hi{
  display:grid; grid-template-columns:minmax(0,260px) minmax(0,1fr);
  gap:48px; align-items:start; grid-template-rows:auto;
  padding:24px 32px 28px; border-top:2px solid var(--ink);
}
.cp-svc li.hi .svc-b{ margin-top:0; padding-top:2px; }
/* ⚠ 上面这条的选择器与 pages.css 的移动端回退同权（都是 .cp-svc li.hi），
   而 beauty.css 在 pages.css 之后加载 —— 不加这一段，≤980 的单栏回退会被
   顶掉：实测 900 下正文列只剩 447（每行 28 字），手机更窄。
   凡是"在美化层重写某个组件的骨骼"，都必须把该组件的断点回退一起抄过来。 */
@media (max-width:980px){
  .cp-svc li.hi{ grid-template-columns:minmax(0,1fr); gap:12px; padding:24px 24px 24px; }
}
@media (max-width:760px){
  .cp-svc li.hi{ padding:20px 20px 24px; gap:8px; }
}

/* 11.6 空态双栏（.empt）在手机上没切单栏 —— 这是我上一轮留下的硬伤。
   `.empt{ 1fr / .92fr }` 写了双栏，却只在桌面成立：
   390 下左栏被压到 210px，正文每行 8 个字；右栏卡片挤成 155px。
   找楼页与 404 都用这个组件，两页同时中招。 */
@media (max-width:760px){
  .empt{ grid-template-columns:minmax(0,1fr); gap:36px; }
  .empt-l p{ max-width:none; }
}

/* 11.7 只有「标题 + 一段话 + 一个小按钮」的区块，整段内容挤在左侧。
   services 的「装修由乐企装承接」实测内容右缘只到 41%，右侧 800px 全白，
   一整段 291px 高的区块读起来像没排完。
   这两处（services / about 各一个 .cp-acts）里，按钮本来就该落在右端：
   给它加一条通栏顶线、按钮右对齐，段落宽度不变，但整段占满了版心。 */
@media (min-width:761px){
  .cp-acts{
    margin-top:24px; padding-top:24px; border-top:1px solid var(--line);
    justify-content:flex-end;
  }
}

/* 11.8 侧栏信息块（.cp-rail：整段委托 / 施工团队 / 公司全称 …）
   在 ≤980 会从「右侧窄栏」变成「通栏」，可每一行还是「标签在上、值在下」，
   一行内容只占半幅，通栏之后右边整个空掉（审计上 about 与 renovation 各判 900px）。
   通栏时改成规格表：标签在左、值右对齐，横线贯穿整幅。 */
@media (max-width:980px){
  .cp-rail > div:not(.cp-rail-h){
    display:grid; grid-template-columns:auto minmax(0,1fr);
    gap:4px 24px; align-items:baseline;
  }
  .cp-rail > div:not(.cp-rail-h) span{ margin:0; }
  .cp-rail > div:not(.cp-rail-h) b{ margin-top:0; text-align:right; }
}

/* ============================================================
   13. 内容页 · 行业参照（第三方口径）

   为什么单独做视觉标记：本库数据与第三方机构数据是两套口径，
   混排会被读成「同一份统计」。所以参照区用左竖条 + 浅底，
   与本库的 .ctab 形成明确区分，读者一眼能看出「这一段是别人说的」。
   ============================================================ */

/* 13.1 参照区：不做整块容器。
   原先加了一条贯穿整块的灰竖线，实测正好压在小节标题的红竖条上 ——
   红竖条是本站在用的「这里是我们的判断」信号，不能被引用的内容盖掉；
   而且灰线让标题左缘与其他小节差 3px。
   所以只用两件事区分：机构标签（灰底小标）+ 表头浅灰底。左缘与其他小节完全对齐。 */
.extsec{ position:relative; }

/* 13.2 区块小标：写明是哪个机构、哪一期。这里必须能独立读出，
   不能依赖正文上下文 —— 引用块被单独截屏时也要说明出处。 */
.ext-tag{
  display:inline-block; margin:0 0 16px; padding:4px 8px;
  font-size:12px; line-height:1.5; letter-spacing:.02em;
  color:var(--ink3);
  background:var(--paper2);
  border-left:2px solid var(--line);
}

/* 13.3 参照表：表头用中性底，与 .ctab 的墨色表头区分开。 */
.extab th{
  background:var(--paper2);
  color:var(--ink2);
  font-weight:700;
  border-bottom-color:var(--line);
}
.extab td{ font-variant-numeric:tabular-nums; }

/* 13.4 口径对照表（每页末段共用）：四列在窄屏会挤，给最小宽度并允许横滚。 */
.scopetab{ margin:16px 0 16px; }
.scopetab td:first-child{ white-space:nowrap; }

/* 13.5 资讯页分类标题：数量标注右对齐。
   注意这里不能用 .side —— ed.css 第 684 行把 .side 定义成了二栏布局的侧栏
   （display:flex + position:sticky），套在 h2 里会把「5 篇」挤成下一行的竖排块。
   .dsec h2 .side 有自己的覆盖所以没事，.aip-cat h2 没有。这是本轮实测踩到的。 */
.aip-cat h2{ display:flex; align-items:baseline; gap:16px; }
.aip-cat h2 .aip-n{
  margin-left:auto; font-family:var(--sans);
  font-size:var(--fs-sm); font-weight:400;
  color:var(--ink3); letter-spacing:.06em;
}
/* 分类导航与上方描述之间要留出呼吸；原来紧贴 loc 文字，读起来是一整块 */
.ins-cats{ margin:24px 0 0; }

/* 13.6 内容页表格在窄屏一律横滚。
   ⚠ 不能写 `table{display:block; overflow-x:auto}` —— 试过，table 一旦变成
   block 就失去表格布局，宽度被压回 100%，第三列直接被裁掉（390 下实测
   「风机盘管或」「有经验的服」都被切断，而且没有任何滚动提示）。
   正确做法是外面套一层滚动容器，table 保持 display:table。
   ⚠ 本节改完后,下面 @media 里还留着一条 `.scopetab,.extab{display:block}`
   的老规则,与本节结论直接冲突 —— 探针实测 390 下这两类表 display 仍是
   block、宽度被压回 335、scrollLeft 恒为 0（横滚功能等于不存在，但肉眼
   看着「表格在容器里」,不复核就发现不了）。本轮已删,统一走 .tscroll。

   滚动提示：光有横滚不告诉用户,视觉上就是「表格被右边切掉」。用
   background-attachment:local/scroll 做滚动阴影 —— 遮罩贴内容走、阴影
   钉在容器边缘,滚到右端右影自动消失、滚到左端左影消失;不溢出时两影
   都不出现,所以不需要 JS 判断、也不会给短表挂一条假提示。
   ⚠ 遮罩色必须是 .tscroll 实际所在的底色,写错会在表头边上留一道灰边。 */
.dmain .tscroll{
  overflow-x:auto; -webkit-overflow-scrolling:touch;
  background-color:var(--paper);
  background-image:
    linear-gradient(to right, var(--paper) 40%, rgba(250,250,249,0)),
    linear-gradient(to left, var(--paper) 40%, rgba(250,250,249,0)),
    radial-gradient(farthest-side at 0 50%, rgba(20,23,28,.20), rgba(20,23,28,0)),
    radial-gradient(farthest-side at 100% 50%, rgba(20,23,28,.20), rgba(20,23,28,0));
  background-repeat:no-repeat;
  background-size:38px 100%, 38px 100%, 15px 100%, 15px 100%;
  background-position:0 0, 100% 0, 0 0, 100% 0;
  background-attachment:local, local, scroll, scroll;
}

@media (max-width:760px){
  .dmain .tscroll > table{ width:auto; min-width:100%; }
  .dmain .tscroll > table th,
  .dmain .tscroll > table td{ white-space:nowrap; }
  /* 末列是说明性文字，nowrap 会把它撑出半屏；给它折行权并留下限 */
  .dmain .tscroll > table th:last-child,
  .dmain .tscroll > table td:last-child{ white-space:normal; min-width:9em; }
  /* 首列钉住：横滑到右端时「行首标签」不能跟着滚走，否则看到的是
     「甲级 / 乙级」两列数却不知道每行量的是什么（实测右滑截图就是这个
     观感）。sticky 必须给不透明底色，否则后面的格子会从字缝里透出来。
     th 用 paper2 与表头带一致，td 用 paper 与正文底一致。 */
  .dmain .tscroll > table th:first-child,
  .dmain .tscroll > table td:first-child{
    position:sticky; left:0; z-index:2;
    /* 没有边线时，被钉住的标签和「从下面滑过去」的数值会连成一句
       读成「标准层面积 .500 m²」（实测截图就是这样，数值被从中间切断）。
       给一条竖线 + 右向投影 + 14px 内留白：留白是关键那一步 —— 只画线
       而不留空，被切一半的数字仍紧贴标签，读起来还是一个词。 */
    padding-right:16px;
    box-shadow: inset -1px 0 0 rgba(20,23,28,.16), 16px 0 14px -14px rgba(20,23,28,.34);
  }
  .dmain .tscroll > table th:first-child{ background:var(--paper2); }
  .dmain .tscroll > table td:first-child{ background:var(--paper); }
}

/* 2026-10-23：手机上不再靠横滚，改成折行收进视口。
   760 那套（首列 sticky + 滚动阴影 + 末列折行）在平板宽度是对的，
   但到 390 就撑不住了：实测本页三张表滚出 101 / 0 / 335px，
   4 列那张「数据来源」要滑两屏才看完，第三列只剩半个「1–」印在右边缘 ——
   读者第一眼看到的是「表格坏了」，不是「可以滑」。
   放开折行后三张表全部 0 溢出，页高只从 5448 长到 5658（+3.9%）：
   表格变高一截，换掉整整两屏的横滚，这笔划得来。
   🔴 判据：≤560 下 .tscroll 的 scrollWidth 必须等于 clientWidth（零横滚）。 */
@media (max-width:560px){
  .dmain .tscroll > table th,
  .dmain .tscroll > table td{ white-space:normal; }
  .dmain .tscroll > table th:last-child,
  .dmain .tscroll > table td:last-child{ min-width:0; }
  /* 矩阵型表格（等级 × 指标，三列同质）本来就放不下 —— 实测 36 张里只有
     building-grade 这一张仍溢出 63px。它横滚是对的，所以首列吸附与滚动阴影
     照旧保留（760 那套不动），只是不再靠横滚去救本来能折行的普通表。 */
}

@media (max-width:760px){
  /* 12px 是中文可读下限（--fs-xs 已与 --fs-sm 同值）；这里原来单独压到 11，
     同一类说明文字在手机上比桌面还小，属于反过来。 */
  .ext-tag{ font-size:var(--fs-sm); }
}

/* 13.7 资讯页分类区（2026-10-23）。
   原来是「标题 → 副标题 → 15 行列表」一路竖排，列表本身横跨 1,200px，
   每行只有一个标题贴左、一个箭头贴右，中间空出八百多像素 —— 1440 下
   整页读起来像站点地图，不像一个栏目。
   改成桌面两栏：左栏放分类名与一句话说明，右栏放条目；条目加两位序号，
   让 5–6 行的组也有节奏。窄屏维持原来的竖排（两栏在 760 以下会把标题
   挤成竖排单字，得不偿失）。 */
@media (min-width:1001px){
  .aip-cat{
    display:grid;
    grid-template-columns:258px minmax(0,1fr);
    /* 两行：第一行只装分类名，第二行吃掉剩余高度。
       不写这一句时行高由「撑满两行的列表」反推分配，第一行被拉到
       近百像素，副标题就飘到标题下方很远的位置（实测截图如此）。 */
    grid-template-rows:auto minmax(0,1fr);
    column-gap:56px;
    align-items:start;
  }
  .aip-cat > h2{ grid-column:1; grid-row:1; }
  /* 两栏之后数量不该甩到 258px 栏的最右边 —— 离标题太远，读起来像另一栏
     的内容。改成紧贴标题。三个分类名都是四字，数量都是个位数，能对齐。 */
  .aip-cat > h2 > .aip-n{ margin-left:12px; }
  .aip-cat > .aip-cat-sub{ grid-column:1; grid-row:2; max-width:20em; }
  .aip-cat > .aip-list{ grid-column:2; grid-row:1 / span 2; margin-top:0; }
  .ins-feed{ display:block; }
}
/* 序号列：用 CSS 计数器生成，不动 DOM —— 条目是从 contentIndex 直出的，
   在模板里插一个序号 <i> 只为了排版，属于把版式的东西写进数据管线。
   count 定宽左对齐，标题才会齐头；用 sans 是为了和衬线标题拉开层次。
   ⚠ a 原本是 space-between 两端对齐，多出序号这一项之后标题会被推到
   中间（三个 flex 项平分空档）。改成 flex-start + 箭头 margin-left:auto，
   无论有没有序号，标题都贴左、箭头都贴右。 */
.aip-list{ counter-reset:aip; }
.aip-list li{ counter-increment:aip; }
.aip-list a{ justify-content:flex-start; gap:0; }
.aip-list a::before{
  content:counter(aip,decimal-leading-zero);
  flex:none; width:2.9em; margin-right:8px;
  font-family:var(--sans); font-size:var(--fs-sm); font-weight:400;
  /* 2026-11-02：--ink4 → --ink3。tokens 里写死了 --ink4 在纸底只有 2.8:1，
     只配做分隔线与图标描边；这里是 12px 正文级数字，AA 要 4.5:1。 */
  color:var(--ink3); letter-spacing:.08em; font-variant-numeric:tabular-nums;
}
.aip-list a span{ margin-left:auto; padding-left:20px; }
/* 窄屏撤掉序号：2.9em 的序号列在 390 下把标题挤成两行，
   行高从 56 涨到 83（实测），换来的「顺序感」不值这 27px。 */
@media (max-width:760px){
  .aip-list a::before{ content:none; }
  .aip-list a span{ padding-left:12px; }
}

/* ============================================================
   12. 打印
   ============================================================ */
@media print{
  .mcta, .cp-svc li .sic, .hero-fig::after{ display:none !important; }
  .co, .co h2, .co p, .co-f b{ background:none !important; color:#000 !important; }
}

/* ============================================================
   14. 键盘焦点：全站统一一种环
   —— 之前各处各写一条：.hero-call 有、.est-f 靠 focus-within 变下划线、
      .fv input 只把边框从 --line 变 --ink4、.sform-r1 input 直接 outline:none
      且没有任何替代。键盘用户 Tab 到搜索框时，屏幕上什么都不会发生。
      统一成一条 2px 品牌红 + 2px 留白：在浅底、深底、图片上都看得见。
      ⚠ 放在这里（beauty.css 最后）是有意的：ed.css 里那些
      `.sform-r1 input{outline:none}` 与这条同权（0,1,1），靠层叠顺序取胜。
      再加 !important 就盖住了 .hero-call 自己的红环，反而多一层重复。
   ============================================================ */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible{
  outline:2px solid var(--brand);
  outline-offset:2px;
  border-radius:var(--r-1);
}
/* 深墨段里用白环：红环压在 #14171C 上只有 1.9:1，等于没有 */
.co a:focus-visible,
.co button:focus-visible,
.co input:focus-visible{ outline-color:var(--on-deep); }

/* ══ 交互状态补全（2026-10-26 · DEEP-1026 判据 D）════════════════════════
   来源：M3 State Layers「Hover +8% / Focus +10% / Press +10%」、
         Apple HIG Buttons「Always include a pressed state — without one,
         users may think the button isn't responding」。
   实测（audit_states，3 页 400+ 个可点元素）：hover 76.1%、active **11.0%**。
   缺 active 的不是零散页面，是**页头导航**（4.7 万页每页都有）——
   按下时视觉上毫无反应。缺 hover 的是页脚列标题右侧的「14 个区 / 18 条线」出口
   与 logo。

   为什么用 opacity 而不是颜色：这些元素原本已经有各自的 hover 配色（品牌红 / 墨色），
   再动颜色会和设计语言打架；按下反馈只需要「一瞬的压暗」，opacity 是最不侵入的做法。
   0.62 是在纸底（#FAFAF9）上肉眼可辨、又不至于像禁用态的值。 */
.nav a:active,
.ft li a:active,
.ft-more:active,
.logo:active,
.car-t:active,
.chip:active,
.gal-btn:active { opacity:.62; }

/* hover 补齐：页脚出口与 logo */
.ft-more:hover{ color:var(--ink); text-decoration:underline; text-underline-offset:3px; }
.logo:hover{ opacity:.85; }
.car-t:hover img{ opacity:.86; }
