/* ==========================================================================
   点艺点 · 全站样式 v3（纯白承载 · 层级分明 · 一处一重点）
   依据：《设计规范_2026-09-24.md》§2 色彩 / §4 间距栅格 / §5 形状深度 /
         §6 组件 / §7 动效 / §9 可访问性与性能
   技术约束：PBOOT 双端复用同一份 CSS（html/css 与 pboot/template/css 必须字节一致）
             零外部依赖：不引 CDN、不加载网络字体、纯系统字体栈
   断点（移动优先，5 档）：XL ≥1200 / L 992–1199 / M 768–991 / S 480–767 / XS <480
   旧类名兼容：全站 HTML 现有 198 个类名全部继续生效（含旧版 category-block /
               cat-nav-item / trust-item 与改版后 age-card / cat-tile / region-tile /
               cta-band 等两套并存）；令牌旧名在 §1.2 兼容层中保留
   ========================================================================== */

/* ==========================================================================
   1. 设计令牌（唯一真相来源：颜色 / 圆角 / 阴影 / 间距只引用变量）
   ========================================================================== */

/* ---------- 1.1 新令牌 ---------- */
:root {
  /* === 间距（基准 --s: 4px，所有间距为 4 的倍数） === */
  --s: 4px;
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;
  --s-10: 128px;

  /* === 容器与版式档位（随断点在媒体查询中覆盖） === */
  --w-container: 1200px;   /* 主内容最大宽度 */
  --w-reading: 760px;      /* 阅读行宽：现仅供工具类 .wrap-reading 使用（正文块已解除限宽，见 §14） */
  --w-narrow: 920px;       /* 表单页、帮助中心 */
  --header-h: 56px;        /* 当前档位头部高度：默认移动 56，≥1200 覆盖为 72 */
  --header-h-m: 56px;      /* 移动头部高度（只读参考） */
  --header-h-d: 72px;      /* 桌面头部高度（只读参考） */
  --sticky-cta-h: 64px;    /* 移动吸底 CTA 栏高度（含安全区之外的部分） */
  --pad-x: 16px;           /* 容器左右安全边距：手机 16 */
  --sec-y: 56px;           /* 区块上下留白：XS 56（栏目节奏改造：抬高留白做段落感） */
  --gut: 12px;             /* 栅格 gutter：手机 12 */
  --card-pad: 16px;        /* 卡片内边距：手机 16，≥1200 为 24 */

  /* === 字体（零网络字体：无 FOUT/FOIT、零 CLS 抖动、零额外请求） === */
  --font-sans: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB",
               "Microsoft YaHei", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-num: ui-monospace, "SF Mono", "DIN Alternate", "Helvetica Neue", Arial, sans-serif;
  /* 衬线族（2026-09-26 新增）：仅供 `.trust-bar` 规格条的大数字与单位使用。
     用途是借衬线的「报刊体」气质把 4 个数字做成版面元素（用户指定参考美国律师站
     的数字证明带）。纯系统字体，不引入任何网络字体。中文字符不用它，
     因此回退链里只放能稳定渲染**阿拉伯数字**的系统衬线。 */
  --font-serif: Georgia, "Times New Roman", "Songti SC", "SimSun", serif;

  /* === 字号阶梯 === */
  --fs-display: clamp(30px, 5.4vw, 64px);
  --fs-h1: clamp(26px, 3.2vw, 40px);
  --fs-h2: clamp(22px, 2.4vw, 34px);
  --fs-h3: clamp(18px, 1.6vw, 22px);
  --fs-h4: 16px;
  --fs-lead: clamp(15px, 1.4vw, 19px);
  --fs-body: 15px;
  --fs-sm: 13px;
  --fs-xs: 12px;
  --fs-price: clamp(22px, 2vw, 28px);
  --fs-mark: clamp(56px, 8vw, 96px);

  /* === 品牌橙（v4.3 · 2026-09-26 用户决策：亮橙实底 + 白字，全站统一）===
     ⚠️ 已知偏离 AA —— 系用户 2026-09-26 明确选择，非遗漏、非回归，勿"顺手修好"：
        亮橙 #ff6a3d + 白字 = 2.85:1，低于正文 AA(4.5)，也低于大字 AA(3.0)。
     被权衡后否掉的替代方案（备查，勿再重复试错）：
        · 亮橙 + 深墨 #16181c = 6.23:1 ✅ 达标，但用户观感「硬、像标签」（用户原话否）
        · 橙底压到 #e03400 + 白字 = 4.50:1 ✅ 刚好达标，但底色重度偏暗
        · 橙底压到 #c9420f + 白字 = 4.92:1 ✅ 达标，即前一版方案，整站发暗（用户已否）
     结论：品牌亮橙与白字在数学上不可兼得，用户选择保留亮橙、接受偏低对比度。
        · --primary-500 #ff6a3d = 实底主色（配 --on-primary）+ 色块/图标/边框/大标题/focus 环
        · --primary-550 #ef5222 = 亮橙实底的 hover / pressed。**方向是「加深」而非提亮**：
          提亮到 --primary-400 会掉到 2.32:1（比常态更糊），加深到 550 反升到 3.56:1。
        · --primary-600 #c9420f = **仅作文字色**：白底上的橙色小字与链接（4.92:1）
          ⚠️ 不要再用它铺 background 去承载白字（与 --primary-500 重复且发暗）
        · --primary-700 #a8370c = 白底橙色文字的 hover / pressed（6.52:1） */
  --primary-50: #fff4ef;
  --primary-100: #ffe3d6;
  --primary-200: #ffcbb4;
  --primary-300: #ffa985;
  --primary-400: #ff8a5e;   /* 浅橙档：仅作 --color-primary-light 别名来源，不再用于实底 hover */
  --primary-500: #ff6a3d;   /* 实底 / 色块 / 图标 / 边框 / 大标题 / focus 环 */
  --primary-550: #ef5222;   /* 亮橙实底的 hover / pressed（加深档，白字 3.56:1＞常态 2.85:1） */
  --primary-600: #c9420f;   /* 仅作文字色：白底橙字 4.92:1（不用作实底主色） */
  --primary-700: #a8370c;   /* 白底橙色文字的 hover / pressed（6.52:1） */
  --on-primary: #ffffff;    /* 亮橙实底上的前景色。⚠️ 2.85:1 低于 AA —— 用户 2026-09-26 决策，已知偏离 */

  /* === 中性文字阶（v4 去暖化：暖灰棕 → 真正中性，白底才显干净） === */
  --heading: #16181c;       /* 标题 17.8:1 */
  --text-1: #33373d;        /* 正文 12.0:1 */
  --text-2: #666b73;        /* 次要文字 5.36:1 */
  --text-3: #8f949c;        /* 仅装饰 / ≥19px 大号辅助 3.05:1 */

  /* === 表面（页面底与卡片面只能是纯白） === */
  --bg: #ffffff;
  --surface: #ffffff;
  --surface-2: #f6f7f9;     /* 缩略图底、表头、隔行、空状态底 */
  --surface-3: #f0f1f4;     /* 凹槽、进度槽、disabled 底 */

  /* === 描边阶 === */
  --line-1: #edeef0;
  --line-2: #e0e2e6;
  --line-3: #c8ccd2;
  --line-field: #82868d;    /* 表单控件专用 3.65:1，满足 WCAG 1.4.11 */

  /* === 功能色 === */
  --success: #1f7a48;
  --success-bg: #eef7f1;
  --warning: #8a5f0a;
  --warning-bg: #fdf6e6;
  --danger: #c0392b;
  --danger-bg: #fdeeec;

  /* === 6 大分类色（主值 100% 沿用，未改） === */
  --cat-art: #ef5149;
  --cat-art-ink: #c62a22;
  --cat-music: #f27e2e;
  --cat-music-ink: #b8551a;
  --cat-dance: #e3a62c;
  --cat-dance-ink: #946000;   /* v4 fix02：原 #8a5f0a 偏橄榄土，提纯至饱和度 100%、色相 39° */
  --cat-calligraphy: #3faf6d;
  --cat-calligraphy-ink: #1f7a48;
  --cat-acting: #27b6b6;
  --cat-acting-ink: #0f7373;
  --cat-other: #6f58e0;
  --cat-other-ink: #4f3ab0;

  /* === 6 分类色 · soft 浅底（v4 新增：用于分类卡/分类区的承载底色） === */
  --cat-art-soft: #fdeceb;
  --cat-music-soft: #fef0e6;
  --cat-dance-soft: #fdf4e2;
  --cat-calligraphy-soft: #e8f7ef;
  --cat-acting-soft: #e4f7f7;
  --cat-other-soft: #eeeafc;
  /* === 6 分类色 · deep 深版（2026-09-27 需求 62 新增）===
     用途：**浅底（.cat-block 的白 / --band-grey / --surface-2 / --cat-*-soft）上的分类色文字**。
     取值 = 对应 --cat-*-ink 等比深化一档（×0.82）。
     🔴 为什么要另立一套：--cat-ink 直接压 --band-grey(#e8ebf0) 时，书法 #1f7a48 只有
        4.47:1（低于 AA 4.5），故浅底文字一律改用深版（实测最低 5.52:1）。
     ⚠️ 不要改成 color-mix() 现算 —— 其 computed 值会序列化为 `color(srgb …)` 新格式，
        旧工具解析不了，闸门也无法静态校验。
     判据：node scripts/verify_cat_link_color.mjs */
  --cat-art-deep: #a2221c;
  --cat-music-deep: #974615;
  --cat-dance-deep: #794f00;
  --cat-calligraphy-deep: #19643b;
  --cat-acting-deep: #0c5e5e;
  --cat-other-deep: #413090;

  /* === 墨蓝锚点色阶（v4 新增：深色带 / 页脚 / 数据强调，与品牌橙互补） === */
  --navy-50: #f2f7fb;
  --navy-100: #e3edf5;
  --navy-200: #c3d8e8;
  --navy-500: #2a6faa;
  --navy-600: #1f5688;
  --navy-700: #1a4166;
  --navy-800: #12304c;
  --navy-900: #0d2137;      /* 深色带 / 页脚主底 */
  /* 注：navy-50 / navy-100 自 v4.1 起**不再用于页面级 <section> 底色**，
     其区块用途已移交下方 --band-*（原因见该组注释）。色阶本身保留备用。 */

  /* === 区块级底色（v4.4 · 2026-09-26）===
     专供「页面级 <section> 的整宽底色」，禁止用于组件（hover / 凹槽 / 缩略图底）。

     🔴 v4.4 用户决策（2026-09-26）：**浅蓝 --band-cool #cbdef0 整条退出**。
       用户原话：「首页里面的背景色，好几个颜色，有白色、灰色、深蓝，居然还有个浅蓝，
       你把浅蓝删除掉，重新排一个底色」。
       → 全页底色收敛为 **白 / --band-grey / --ink-900 三色**，浅蓝令牌已删除，
         任何地方不得再引入第 4 种区块底色（含 --primary-50 这类品牌浅调）。

     使用纪律：**相邻 <section> 优先不同色**；节奏为「白 ↔ 灰」交替，
     深墨蓝（--ink-900）只作段落级重音锚点（hero / region-find / cta-band，共 3 处）。

     🔴 2026-10-03 纪律修订（需求 E·项6 ｜ 铁律 22g：判据过时**不许删，要反转** + 带日期）：
       原表述是**绝对禁令**「相邻 <section> 不得同色」，已被用户指令打破 ——
       用户明确要求 `.cat-nav` 改纯白（「@图5 这个地方的背景色块，改成纯白色」），
       致 `inst-featured(白) → cat-nav(白) → cat-block1(白)` 连续三块同色（约 4470px）。
       **反转后口径**：相邻同色**可以**，但交界处必须有**可辨分隔线**
       （`--band-divider`，对白 ΔE=10.39 ≥ 7 达标；`--line-1` 仅 6.02，区块级禁用）。
       守门 `probe_section_rhythm.mjs` 判据同步升级：相邻同色时检查交界边框，
       边框对白 ΔE ≥ 7 记 `✓ 同色·有分隔线`（不计红），否则仍判 `✗ 撞色`。
       ⇒ 纪律从「靠底色区分」放宽为「底色**或**分隔线区分」，两者至少有一个可辨。

     ⚠️ 排列这条节奏时踩到的硬约束（改动前必读）：
       `.cat-blocks` 内部 6 个 `.cat-block` 是白/灰隔行且**以灰收尾**，
       而 `.cat-nav` 在其正上方也是灰 → 紧随 `.cat-blocks` 的 `.region-find`
       **既不能是白（与 .cat-blocks 段底色撞）、也不能是灰（与 block6 撞）**，
       在「只剩白/灰/深蓝」的前提下**只能取深墨蓝**。这是数学约束，不是审美偏好。

     复核命令：node scripts/probe_section_rhythm.mjs index.html */
  --band-grey: #e8ebf0;     /* 区块级浅灰（冷）｜品类宫格 / 年龄段入口 / 家长怎么用 / 资讯列表 */
  /* 🔴 2026-10-03 新增（需求 E·项6）：**区块级分隔线** —— 相邻 <section> 同色时的交界线。
     取值实测对白 ΔE=10.39（探针「✓ 可辨」档 ≥7）；禁止退回组件级 --line-1（ΔE=6.02 偏弱，
     1px 线在白底上肉眼几乎不可见，会被探针判为「⚠ 偏弱」）。见下方纪律注释的 22g 反转。 */
  --band-divider: #e0e2e6;

  /* === 深色底（页脚 / 深色带）—— v4 由暖黑改为深墨蓝 === */
  --ink-900: #0d2137;       /* = navy-900，深墨蓝（原暖黑 #1b1917） */
  --ink-800: #12304c;       /* = navy-800 */
  --text-on-dark: #d6e0ea;  /* 12.18:1 */
  --text-on-dark-2: #bfb9b2;/* 8.37:1（用户指定：更亮更干净，原 #a99a8d） */
  --line-on-dark: rgba(255, 255, 255, .12);

  /* === 圆角（10 种收敛为 6 档，同屏 ≤3 种） === */
  --r-xs: 6px;
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --r-pill: 999px;

  /* === 阴影（4 级 + 遮罩，近中性色，最重常态不透明度 .10） === */
  --sh-1: 0 1px 2px rgba(13, 33, 55, .05);
  --sh-2: 0 2px 4px rgba(13, 33, 55, .05), 0 8px 20px rgba(13, 33, 55, .07);
  --sh-3: 0 8px 16px rgba(13, 33, 55, .07), 0 24px 48px rgba(13, 33, 55, .11);
  --sh-4: 0 32px 80px rgba(13, 33, 55, .22);
  --sh-scrim: rgba(13, 33, 55, .50);

  /* === 描边宽度 === */
  --bw: 1px;
  --bw-strong: 1.5px;
  --bw-focus: 3px;

  /* === 动效 === */
  --dur-1: 120ms;
  --dur-2: 200ms;
  --dur-3: 320ms;
  --dur-4: 480ms;
  --ease-out: cubic-bezier(.2, .8, .25, 1);
  --ease-in-out: cubic-bezier(.4, 0, .2, 1);
  --ease-spring: cubic-bezier(.34, 1.32, .64, 1);
}

/* ---------- 1.2 兼容层：旧令牌别名 ----------
   规范 §11.1 映射表。旧类名与 HTML 内联样式（var(--color-text-light) /
   var(--cat-*) / var(--color-bg) 等）继续有效，避免一次性重命名导致 39 个
   页面崩样式。逐页迁移完成后可整块删除。                                */
:root {
  --color-primary: var(--primary-500);
  --color-primary-dark: var(--primary-700);
  --color-primary-light: var(--primary-400);
  --color-primary-soft: var(--primary-50);
  --color-bg: var(--bg);
  --ink: var(--heading);
  --color-text: var(--text-1);
  --color-text-light: var(--text-2);
  --color-border: var(--line-2);
  --color-border-strong: var(--line-3);
  --color-white: #ffffff;
  --radius: var(--r-md);
  --radius-sm: var(--r-sm);
  --radius-lg: var(--r-lg);
  --radius-pill: var(--r-pill);
  --shadow: var(--sh-2);
  --shadow-sm: var(--sh-1);
  --shadow-lg: var(--sh-3);
  --container: var(--w-container);
  --transition: var(--dur-2) var(--ease-in-out);
}

/* ==========================================================================
   2. Reset 与基础排版
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: var(--header-h);   /* 锚点跳转不被 sticky 头部遮挡 */
}
html, body {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  color: var(--text-1);
  background: var(--bg);
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
body {
  /* 这里曾写 min-width: 320px（常见的「防止过窄挤压」写法），实测它是横向滚动的元凶：
     320px 视口下 html 的内容盒只有 305px（扣掉 15px 竖向滚动条），body 被撑到 320px
     → documentElement.scrollWidth = 320 > clientWidth = 305，全站每页溢出 15px。
     版式本身已是流式 + XS 档断点，无需最小宽度兜底，直接去掉。 */
  min-width: 0;
  /* 移动吸底 CTA 栏不参与文档流，用一次性的 padding-bottom 预留空间（防遮挡） */
  padding-bottom: calc(var(--sticky-cta-h) + env(safe-area-inset-bottom));
}
::selection { background: var(--primary-100); color: var(--heading); }

a { color: inherit; text-decoration: none; transition: color var(--dur-1) var(--ease-in-out); }
a:hover { color: var(--primary-600); }

img { display: block; max-width: 100%; height: auto; }
svg { max-width: 100%; }

button, input, select, textarea { font: inherit; color: inherit; background: none; border: none; }
button { cursor: pointer; }
ul, ol { list-style: none; }
table { border-collapse: collapse; width: 100%; }

/* 长串（域名、路径）不撑破容器：任意断点零横向滚动条 */
h1, h2, h3, h4, h5, h6, p, li, dd, dt, td, th, span, a { overflow-wrap: break-word; }

/* JS 用 hidden 属性隐藏元素（表单成功态等），需高于组件类里的 display 声明 */
[hidden] { display: none !important; }

/* 弹窗 / 抽屉打开时的滚动锁（JS 加 .is-locked，并补偿滚动条宽度防跳动） */
body.is-locked { overflow: hidden; }

h1, h2, h3, h4, h5, h6 {
  color: var(--heading);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
}
h1 { font-size: var(--fs-h1); font-weight: 800; line-height: 1.15; letter-spacing: -0.02em; }
h2 { font-size: var(--fs-h2); line-height: 1.2; letter-spacing: -0.018em; }
h3 { font-size: var(--fs-h3); line-height: 1.3; letter-spacing: -0.01em; }
h4 { font-size: var(--fs-h4); font-weight: 600; line-height: 1.45; letter-spacing: 0; }
h5 { font-size: 15px; font-weight: 700; line-height: 1.4; }
h6 { font-size: var(--fs-sm); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }

/* 字号工具类：需要视觉效果时用工具类，不要用 h 标签调字号（规范 §3.5） */
.t-display { font-size: var(--fs-display); font-weight: 800; line-height: 1.08; letter-spacing: -0.025em; }
.t-h1 { font-size: var(--fs-h1); font-weight: 800; line-height: 1.15; letter-spacing: -0.02em; }
.t-h2 { font-size: var(--fs-h2); font-weight: 700; line-height: 1.2; letter-spacing: -0.018em; }
.t-h3 { font-size: var(--fs-h3); font-weight: 700; line-height: 1.3; letter-spacing: -0.01em; }
.t-lead { font-size: var(--fs-lead); line-height: 1.75; letter-spacing: 0.005em; color: var(--text-2); }
.t-label { font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.06em; color: var(--primary-600); }
.t-num { font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.small { font-size: var(--fs-sm); color: var(--text-2); }
.text-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.text-ellipsis-2 {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ==========================================================================
   3. 焦点、跳跃链接与滚动进入
   ========================================================================== */

/* 规范 §9.3：不得出现 outline:none 而无替代焦点指示。
   下方 :focus-visible 规则在其后，键盘聚焦时提供 3px 橙环 + 2px offset。 */
:focus { outline: none; }
:focus-visible {
  outline: 3px solid rgba(255, 106, 61, .55);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* 跳到主内容（视觉隐藏，聚焦时出现）
   用 position:fixed 而非 absolute：页面滚到中部后按 Tab 也能在视口内看到它
   （absolute 会钉在文档顶部，滚下去后就看不见了）。
   left:-9999px 的屏外定位不会制造横向滚动（向右才计入 scrollWidth），已实测。 */
.skip-link {
  position: fixed; left: -9999px; top: 0; z-index: 200;
  padding: 0 var(--s-5); min-height: 44px;
  display: inline-flex; align-items: center; gap: var(--s-2);
  background: var(--surface); color: var(--heading);
  font-size: 14px; font-weight: 600;
  border: 1px solid var(--line-3); border-radius: var(--r-pill);
  box-shadow: var(--sh-3);
}
/* 聚焦时必须可见，且不能被 reset 的 :focus { outline:none } 吃掉焦点环 */
.skip-link:focus,
.skip-link:focus-visible {
  left: var(--s-4); top: var(--s-3); color: var(--heading);
  outline: var(--bw-focus) solid rgba(255, 106, 61, .55);
  outline-offset: 2px;
}

/* 滚动进入动效（JS 加 .reveal，IntersectionObserver 只播一次） */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--dur-4) var(--ease-out), transform var(--dur-4) var(--ease-out);
}
.reveal.in { opacity: 1; transform: none; }
.reveal.d1 { transition-delay: 60ms; }
.reveal.d2 { transition-delay: 120ms; }
.reveal.d3 { transition-delay: 180ms; }

/* ==========================================================================
   4. 通用工具
   ========================================================================== */
.container {
  width: 100%;
  max-width: var(--w-container);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--pad-x);
  padding-right: var(--pad-x);
}
.wrap-reading { max-width: var(--w-reading); margin-left: auto; margin-right: auto; }
.wrap-narrow { max-width: var(--w-narrow); margin-left: auto; margin-right: auto; }
.clearfix::after { content: ''; display: block; clear: both; }
.text-center { text-align: center; }
.flex { display: flex; }
.flex-center { display: flex; align-items: center; justify-content: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.mt-4 { margin-top: 16px; }
.mt-6 { margin-top: 24px; }
.mt-8 { margin-top: 32px; }
.mb-4 { margin-bottom: 16px; }
.mb-6 { margin-bottom: 24px; }
.mb-8 { margin-bottom: 32px; }
.is-hidden { display: none !important; }

/* 屏幕阅读器专用 */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ==========================================================================
   5. 按钮（7 变体 × 3 尺寸 × 5 状态；零渐变、零常态彩色光晕）
   ========================================================================== */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  /* F1 修复：原 40px，移动端不足 WCAG 2.5.5 的 44×44 触控目标 */
  min-height: 44px;
  height: 44px;
  padding: 0 var(--s-5);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0;
  border-radius: var(--r-pill);
  border: var(--bw-strong) solid transparent;
  background: transparent;
  color: var(--heading);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-1) var(--ease-in-out),
              border-color var(--dur-1) var(--ease-in-out),
              color var(--dur-1) var(--ease-in-out),
              transform var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.btn:active { transform: translateY(0); }
.btn:focus-visible { outline: var(--bw-focus) solid rgba(255, 106, 61, .45); outline-offset: 2px; }
.btn[disabled], .btn.is-disabled, .btn[aria-disabled="true"] {
  background: var(--surface-3);
  color: var(--text-3);
  border-color: var(--line-2);
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}

/* --- 变体 --- */
/* 主按钮（v4.3 · 2026-09-26 用户决策「亮橙 + 白字，全站都改」）：
   亮橙 #ff6a3d + 白字 = 2.85:1 —— **已知偏离 AA，用户明确选择，非遗漏**（见顶部品牌橙注释）。
   hover 故意用「加深」到 --primary-550（3.56:1）而非提亮：
   若沿用旧的提亮档 --primary-400 会掉到 2.32:1（hover 时字更糊），加深反而更好认。 */
.btn-primary { background: var(--primary-500); color: var(--on-primary); }
.btn-primary:hover { background: var(--primary-550); color: var(--on-primary); transform: translateY(-1px); }
.btn-primary:active { background: var(--primary-500); color: var(--on-primary); transform: translateY(0); }

.btn-secondary { background: var(--heading); color: #fff; }
.btn-secondary:hover { background: var(--navy-900); color: #fff; }
.btn-secondary:active { background: var(--ink-800); transform: translateY(0); }

.btn-outline { background: transparent; color: var(--primary-600); border-color: var(--primary-500); }
.btn-outline:hover { background: var(--primary-50); color: var(--primary-600); }
.btn-outline:active { background: var(--primary-100); }

.btn-ghost { background: var(--surface); color: var(--heading); border-width: var(--bw); border-color: var(--line-3); }
.btn-ghost:hover { background: var(--surface-2); border-color: var(--line-field); color: var(--heading); }
.btn-ghost:active { background: var(--surface-3); }

.btn-link {
  display: inline-flex; align-items: center; gap: var(--s-1);
  min-height: 44px; height: auto; padding: 0;
  background: none; color: var(--primary-600); font-weight: 600;
}
.btn-link:hover { color: var(--primary-700); text-decoration: underline; }

/* 深底反白（Hero / 深色带上的主操作） */
.btn-on-dark { background: #fff; color: var(--primary-600); }
.btn-on-dark:hover { background: var(--primary-50); color: var(--primary-600); }

/* 深底描边（Hero 上的次操作） */
.btn-outline-light {
  background: transparent; color: #fff;
  border-color: rgba(255, 255, 255, .75);
}
.btn-outline-light:hover { background: #fff; color: var(--primary-600); border-color: #fff; }
.btn-outline-light:active { background: var(--primary-50); color: var(--primary-600); }

/* --- 尺寸 --- */
.btn-lg { height: 48px; min-height: 48px; padding: 0 var(--s-6); font-size: 16px; }
.btn-sm { height: 36px; min-height: 36px; padding: 0 var(--s-4); font-size: 13px; }
.btn-block { width: 100%; }
/* ≤479（XS）时主按钮整宽，单手拇指更好点（≥480 恢复自适应宽度） */
.btn-lg { width: 100%; }

/* ==========================================================================
   6. 顶部条 + 头部（≥1200 桌面 72px 纯白 sticky；<1200 移动 56px + 抽屉 + 吸底栏）
   ========================================================================== */
.topbar {
  display: none;                         /* ≤767 隐藏，≥768 显示（规范 §4.4） */
  background: var(--surface);            /* 纯白：已删除原暖白半透明底 */
  border-bottom: 1px solid var(--line-1);
  font-size: var(--fs-sm);
  color: var(--text-2);
}
.topbar .container {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); min-height: 44px;
}
.topbar a {
  display: inline-flex; align-items: center; min-height: 44px;
  margin-left: var(--s-4); color: var(--text-2);
}
.topbar a:hover { color: var(--primary-600); }

.header {
  position: sticky; top: 0; z-index: 100;
  height: var(--header-h);
  background: var(--surface);            /* 纯白，去毛玻璃（原半透明暖白 + blur） */
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out);
}
/* 未滚动不加阴影；滚动后由 JS 加 .is-scrolled（规范 §6.3） */
.header.is-scrolled { border-bottom-color: var(--line-1); box-shadow: var(--sh-1); }
.header .container {
  display: flex; align-items: center; gap: var(--s-3);
  height: 100%;
  /* 为绝对定位的电话 + 汉堡留出空间，避免 logo 被压 */
  padding-right: calc(var(--pad-x) + 100px);
}
.header-logo {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: 19px; font-weight: 800; color: var(--heading); letter-spacing: -0.01em;
  min-height: 44px;
}
.header-logo .logo-mark {
  width: 36px; height: 36px; flex: none;
  display: flex; align-items: center; justify-content: center;
  /* 2026-09-26 换新品牌图形标志：六色扇冠（= 6 类培训分类色）+ 亮橙扁椭圆品牌点。
     图形自带完整色彩，故去掉此前的橙色实底与圆角，直接置于白色页头之上。
     图形宽高比 2.03:1（非正方形；2026-09-27 按原版重绘后由 2.06 微调），故 img 必须 object-fit: contain，
     否则被拉伸变形。 */
  background: none;
  border-radius: 0;
}
.header-logo .logo-mark svg,
.header-logo .logo-mark img {
  width: 100%; height: 100%; display: block;
  object-fit: contain;          /* 宽高比 2.03，禁拉伸 */
}

/* 城市选择器（<480 隐藏）
   🔴 2026-10-05 已从页头**下线**：用户「1 年内大概只做北京，先把『北京站』占位去掉，
      规避因为字数增加而挤压变形」。CANON_HEADER（scripts/unify_header_footer.py）已不再
      输出 .header-city ⇒ 以下 .header-city / .city-dropdown / .city-more 规则当前**不作用于
      任何页面**；**有意保留**（连同 js/main.js 的 initCity()，其首行有空值保护）
      以备将来开新城直接复用 —— 别当死代码删除。 */
.header-city {
  display: none;
  position: relative;
  /* 30px 定高的小标签：纵向（上下）与横向都居中。
     它承载的是「北京站」这一静态事实标签（.is-static 时不可点击），
     不是触控目标，因此不必保留 44px 触控尺寸。 */
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  min-height: 30px;
  padding: 0 var(--s-4);
  line-height: 1;
  background: var(--surface);
  border: 1px solid var(--line-3);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  color: var(--text-1);
  white-space: nowrap;
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease-in-out), color var(--dur-1) var(--ease-in-out);
}
.header-city:hover, .header-city:focus-within, .header-city.open {
  border-color: var(--primary-500); color: var(--primary-600);
}
.header-city::after {
  content: ''; width: 6px; height: 6px; margin-left: var(--s-2);
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
}
/* 已成为静态标签（无下拉的城市站）时不画下拉箭头 */
.header-city.is-static { cursor: default; }
.header-city.is-static::after { display: none; }
.city-dropdown {
  position: absolute; top: calc(100% + var(--s-2)); left: 0; z-index: 120;
  min-width: 200px;
  display: none;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px;
  padding: var(--s-2);
  background: var(--surface);
  border: none;                          /* 浮层只用阴影（规范 §5.4） */
  border-radius: var(--r-md);
  box-shadow: var(--sh-3);
}
.header-city:hover .city-dropdown,
.header-city:focus-within .city-dropdown,
.header-city.open .city-dropdown { display: grid; }
.city-dropdown a {
  display: flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 var(--s-2);
  font-size: var(--fs-sm); color: var(--text-1);
  border-radius: var(--r-sm);
  transition: background-color var(--dur-1) var(--ease-in-out), color var(--dur-1) var(--ease-in-out);
}
.city-dropdown a:hover,
.city-dropdown a.active { background: var(--primary-50); color: var(--primary-600); }

/* 导航（<1200 抽屉；≥1200 水平导航） */
.header-nav {
  position: fixed; top: var(--header-h); left: 0; right: 0; z-index: 106;
  display: flex; flex-direction: column; align-items: stretch; gap: 0;
  max-height: calc(100vh - var(--header-h) - var(--sticky-cta-h));
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: var(--s-3) var(--pad-x) var(--s-5);
  background: var(--surface);
  border-bottom: 1px solid var(--line-1);
  box-shadow: var(--sh-3);
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  /* 兜底：即使将来有子元素又把 visibility 解禁，关闭态抽屉也绝不参与命中测试
     （遮挡可点区域属点击级 BUG，比视觉问题严重得多） */
  pointer-events: none;
  transition: opacity var(--dur-3) var(--ease-out),
              transform var(--dur-3) var(--ease-out),
              visibility var(--dur-3) linear;
}
body.nav-open .header-nav { opacity: 1; visibility: visible; transform: none; pointer-events: auto; }

.nav-item {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: center;
  min-height: 52px;                     /* 一级项 ≥44 触控 */
  padding: 0 var(--s-3);
  font-size: 16px; font-weight: 500; color: var(--heading);
  border-bottom: 1px solid var(--line-1);
  cursor: pointer;
  transition: color var(--dur-1) var(--ease-in-out);
}
/* 🔴 抽屉态硬约束（2026-09-26 修）：含下拉的一级项，结构是
   <div class="nav-item"><a>标题</a><div class="dropdown">…</div></div>。
   若 .nav-item 不换行，.dropdown{width:100%} 会被 flex-shrink 压到标题旁边 ——
   实测首页「找机构 / 挑课程」、内页「艺术机构 / 精品课程 / 新闻资讯」的下拉面板
   与标题**并排挤压**，移动端抽屉里就是一坨。修法：容器 flex-wrap:wrap，
   标题 flex:1 1 100% 独占第一行，下拉 flex-basis:100% 落到第二行。
   （桌面档 .dropdown 是 position:absolute、脱离流，本组规则对它无效 ——
     但 .nav-item 的 flex-wrap 会继承到桌面，故桌面媒体查询里显式复位为 nowrap。） */
.nav-item > a {
  flex: 1 1 100%;
  display: flex; align-items: center;
  min-height: 52px;
  color: inherit;
}
.header-nav .nav-item:last-child { border-bottom: none; }
.header-nav .nav-item:hover,
.header-nav .nav-item.active { color: var(--primary-600); }
.nav-item .caret {
  display: inline-block; width: 0; height: 0; margin-left: var(--s-2);
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 4px solid currentColor;
  vertical-align: middle; opacity: .5;
}
.nav-item::after { display: none; }     /* 桌面才出现激活橙条 */

.dropdown {
  position: static;
  display: flex; flex-wrap: wrap; gap: var(--s-2);
  /* flex:0 0 100% 而非 width:100%：抽屉态下 .nav-item 已 flex-wrap:wrap，
     用 flex-basis 才能保证下拉**必定另起一行**；width:100% 会被 flex-shrink 压缩。 */
  flex: 0 0 100%; width: 100%; min-width: 0;
  padding: var(--s-2) 0 var(--s-3);
  background: none; border: none; box-shadow: none;
  /* visibility 是继承属性：这里曾写死 visible，把父级 .header-nav 关闭时的 hidden
     又「反向解禁」了 —— 抽屉视觉关闭（opacity:0 / visibility:hidden）却仍可命中，
     首屏 30%~44% 的点击会被看不见的下拉分类链接吃掉。用 inherit 跟随父级，
     body.nav-open 时父级 visible 会正常级联下来，展开态不受影响。 */
  opacity: 1; visibility: inherit; transform: none;
}
.dropdown a {
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 44px; padding: var(--s-2) var(--s-3);
  font-size: var(--fs-sm); color: var(--text-1);
  background: var(--surface-2); border-radius: var(--r-sm);
  transition: background-color var(--dur-1) var(--ease-in-out), color var(--dur-1) var(--ease-in-out);
}
.dropdown a:hover { background: var(--primary-50); color: var(--primary-600); }

/* 分类图标（2026-09-26 换掉原 8px 彩色圆点）
   用户原话：「找机构 和 挑课程，前面的彩色点，我觉得要换成对应图标」。
   原形态是 6 个一模一样大小的实心圆点（.cat-dot），只能靠颜色区分品类 ——
   色弱用户基本无法分辨，且视觉上"点了 6 个点"完全不带品类信息。
   新形态复用 22 节「分类色绑定」已挂在 [data-cat] 上的 --h2-ico
   （与分类大区标题同一个图形来源，一处定义两处用），并用 --cat 上色。
   <i class="cat-ico"> 是 [data-cat] 的**子元素**，两个自定义属性都靠继承拿到，
   因此不需要再写 12 条 [data-cat="…"] .cat-ico 上色规则（原 .cat-dot 那 12 条已删）。

   🔴 选择器必须带 `.header-nav .dropdown` 作用域（2026-09-26 实测踩到）：
   `.cat-ico` 这个类名**不止这一处用**——首页品类宫格的 `.cat-tile` 里也有
   `<span class="cat-ico"><svg>…</svg></span>`（见 23.6 节，64×64 分类色实底 + 内联 SVG）。
   本条规则若写成裸 `.cat-ico`，其 `mask: var(--h2-ico…)` 会**照样命中宫格那个 span**
   （`.cat-tile .cat-ico` 只覆盖了 width/height/display/background，**没有覆盖 mask**），
   把 26×26 的内联 SVG 按 h2 图标的**笔画**裁掉 → 首页 6 个品类宫格图标整体消失。
   实测证据：scripts/probe_cat_ico_scope.mjs（宫格图标读到的 maskImage 非 none 即为回归）。
   `.cat-ico` 的语义是「品类图标」，两处一致；差异只在**渲染方式**，
   故按组件化写法各自作用域，不共用同一条无作用域规则。 */
.header-nav .dropdown .cat-ico {
  /* 🔴 display 由 22 节「分类色绑定」写入的 --cat-ico-display 决定：
     只有真正命中品类键（即能取到 --h2-ico）的下拉项才显示图标；
     否则 `mask: none` 不遮罩任何东西，14×14 的 background-color 会原样铺成
     一个**实心灰方块**（新闻资讯下拉的 3 项就是这种无品类键的情况）。
     2026-09-26 实测踩到：这是把 .cat-dot 换成 mask 图标时新引入的坑。 */
  display: var(--cat-ico-display, none);
  width: 14px; height: 14px; flex: none;
  background-color: var(--cat, var(--text-3));
  -webkit-mask: var(--h2-ico, none) center / contain no-repeat;
          mask: var(--h2-ico, none) center / contain no-repeat;
}

/* 头部动作区（<1200 变为吸底 CTA 栏） */
.header-actions {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 107;
  display: flex; flex-direction: row; align-items: center; justify-content: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--pad-x) calc(var(--s-2) + env(safe-area-inset-bottom));
  background: var(--surface);              /* 纯白实底（毛玻璃预算只留给吸底转化条与弹窗遮罩） */
  border-top: 1px solid var(--line-1);
  box-shadow: var(--sh-3);
  opacity: 0; visibility: hidden; transform: translateY(100%);
  transition: opacity var(--dur-3) var(--ease-out),
              transform var(--dur-3) var(--ease-out),
              visibility var(--dur-3) linear;
}
/* 旧吸底动作区（.header-actions）的显隐：仅在「该页没有 .sticky-cta」时才会走到
   （main.js 的 fallback 分支），此时靠 .is-visible 滑入。
   注意：原先这里还有一条「抽屉打开（nav-open）时显示动作区」的规则，它已被本文件
   下方 <1200px 的 `.header-actions { display: none }` 变成死代码（display:none 时
   opacity/visibility 写什么都不可能显示），故删除；抽屉打开时露出转化入口的职责
   已交给 `.sticky-cta`（见下方「抽屉打开时露出吸底 CTA」那段规则）。 */
.header-actions.is-visible { opacity: 1; visibility: visible; transform: none; }
.header-actions .btn { flex: 1 1 0; max-width: 240px; min-height: 48px; height: 48px; }

/* 新的吸底 CTA 栏（.sticky-cta）：滚过一屏后 JS 加 .is-visible */
.sticky-cta {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 107;
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--pad-x) calc(var(--s-2) + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, .94);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border-top: 1px solid var(--line-1);
  box-shadow: var(--sh-3);
  transform: translateY(100%); opacity: 0; visibility: hidden;
  transition: opacity var(--dur-3) var(--ease-out),
              transform var(--dur-3) var(--ease-spring),
              visibility var(--dur-3) linear;
}
.sticky-cta.is-visible { transform: none; opacity: 1; visibility: visible; }
.sticky-cta .btn { flex: 1 1 0; min-height: 48px; height: 48px; }

/* 吸底动作区与吸底 CTA（.sticky-cta）在 <1200px 都是 position:fixed;bottom:0，
   同时出现会完全重叠。
   这里**不用条件式伪类**（body 上带括号参数的 :has 选择器）做条件：`.sticky-cta` 已覆盖
   全站（静态端 20/20 页，模板端由 footer.html 输出），条件恒为真；而该伪类在旧版微信 X5
   （Chromium<105）不支持 → 规则静默失效 → 直接退化成两条重叠吸底栏。
   故写成无条件规则，零兼容性风险。
   限死在 max-width:1199px：≥1200px 动作区回到头部内联，不受影响。 */
@media (max-width: 1199px) {
  .header-actions { display: none; }

  /* 抽屉打开时露出吸底 CTA（移动端最主要的操作路径不能 0 转化入口）。
     痛点：移动端首屏（scrollY=0）点开汉堡后，抽屉里只有导航链接，而吸底条要滚过
     约一屏（main.js sync() 的阈值）才会 .is-visible → 抽屉打开态一个可见 CTA 都没有。
     这里不看 scrollY，只要抽屉开着就把吸底条显示出来。
     层级安全（已实测）：.sticky-cta 107 > .header-nav 106 > .nav-mask 105，
     吸底条盖在抽屉与遮罩之上，不会被遮罩截获点击；.nav-toggle 是 108 但定在右上角，
     与底部条无几何重叠。
     不会与抽屉内容打架：.header-nav 的 max-height 已按
     calc(100vh - --header-h - --sticky-cta-h) 预留了吸底条高度。 */
  body.nav-open .sticky-cta { transform: none; opacity: 1; visibility: visible; }
}

/* 汉堡与电话按钮（≥1200 隐藏） */
.nav-toggle, .header-tel {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px;              /* 44×44 触控目标（原 40） */
  display: flex; align-items: center; justify-content: center;
  background: var(--surface);
  border: 1px solid var(--line-3);
  border-radius: var(--r-pill);
  color: var(--heading);
}
.nav-toggle { right: var(--pad-x); flex-direction: column; gap: 5px; z-index: 108; }
.nav-toggle span {
  display: block; width: 18px; height: 2px; border-radius: 2px;
  background: var(--heading);
  transition: transform var(--dur-2) var(--ease-out), opacity var(--dur-2) var(--ease-in-out);
}
body.nav-open .nav-toggle { border-color: var(--primary-500); }
body.nav-open .nav-toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
body.nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
body.nav-open .nav-toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
.header-tel {
  right: calc(var(--pad-x) + 44px + var(--s-2));
  color: var(--primary-600); font-size: 19px; text-decoration: none;
}
.header-tel:hover { border-color: var(--primary-500); color: var(--primary-600); }
.header-tel svg { width: 19px; height: 19px; display: block; }

.nav-mask {
  display: none; position: fixed; inset: 0; z-index: 105;
  background: var(--sh-scrim);
}
body.nav-open .nav-mask { display: block; }

/* ==========================================================================
   7. Hero 轮播（唯一的图片叠加渐变层；首屏必须同时看到主张句 + 主按钮）
   ========================================================================== */
/* 首屏轮播 —— 2026-09-26 按 H1「2026-08-29 最早原版」还原（用户指定，非新设计）。
   桌面形态与 H1 逐值一致：容器高 480px｜h1 48px/margin 16px｜p 18px/margin 32px｜
   遮罩写在背景图 linear-gradient 内（不用 ::after）｜箭头 48px 纯黑半透明无边框、
   左右 24px｜圆点 bottom 24px、未选 10px 白 .5、选中 28px 圆角 5px。
   高度分档与源图比例（源图 1920×600 = 3.2:1）：
     <768   移动：clamp(280px,78vw,420px)。375 下 292px → 容器 1.28:1，横裁 60%；
                  若改竖屏满屏(0.56:1) 裁切会飙到 76%，只剩中间一条。
                  （H1 原版无移动适配，此段为补 bug，不改桌面形态。）
     ≥768   支架：clamp(340px,46vw,480px)。768 下 353px → 2.18:1，横裁 32%。
     ≥1200  桌面：解出 480px → 容器 3.0:1，与 3.2:1 源图仅差 6.3%，
                  缩放 0.80x（缩小，不发糊）。这是 H1 比后来「满屏版」
                  （1440×828 = 1.74:1，同一源图需放大 1.66x）更清晰的原因。 */
.hero {
  position: relative; overflow: hidden;
  height: clamp(280px, 78vw, 420px);
  background: var(--ink-900);
}
.hero-track { position: relative; width: 100%; height: 100%; }
.hero-slide {
  position: absolute; inset: 0;
  display: flex; align-items: center;
  background-size: cover; background-position: center; background-repeat: no-repeat;
  opacity: 0;
  transition: opacity var(--dur-4) var(--ease-in-out);
}
.hero-slide.active { opacity: 1; z-index: 1; }
/* 遮罩不在此处生成：H1 把「左→右线性渐变」写成 background-image 的第一层
   （见下方 .slide-1/2/3）。好处是照片与遮罩同属一张图层，切图时一起淡入淡出；
   用 ::after 会出现「新图已亮、旧遮罩未走」的瞬时双影。 */
.hero-content {
  position: relative; z-index: 2;
  width: 100%; max-width: var(--w-container);
  margin-left: auto; margin-right: auto;
  padding: 0 var(--pad-x);
  color: #fff;
}
.hero-content h1,
.hero-content h2.hero-title {   /* 轮播第 2/3 屏语义降为 h2，视觉与 h1 完全一致 */
  color: #fff;
  font-size: clamp(28px, 5vw, 48px);    /* ≥960px 起即为 H1 原值 48px */
  font-weight: 800;
  line-height: 1.3;                     /* H1 未覆盖行高，取全局 heading 值 */
  margin-bottom: 16px;                  /* H1 原值 */
  text-shadow: 0 2px 12px rgba(0, 0, 0, .3);
}
.hero-content p {
  max-width: 34em;
  /* 中文长句在固定宽度下会断成"33 字 + 6 字"的悬殊两行（末行仅 2 字），
     balance 让各行长度趋于均衡；不支持的浏览器优雅降级为默认断行，无副作用。 */
  text-wrap: balance;
  /* keep-all 禁止在"词内"断行（Chrome 走 ICU 分词）。实测（probe_hero_wrap.mjs）：
       balance 单用 → 断在「师|资」，把双字词劈开，中文排版上很难看；
       balance + keep-all → 断在「师资、」之后，词完整且两行最均衡（480/418px）。
     不支持的浏览器按默认 CJK 规则断行，仅失去"词完整"这一层，不会溢出。 */
  word-break: keep-all;
  margin-bottom: 32px;                  /* H1 原值 */
  font-size: clamp(15px, 1.6vw, 18px);  /* ≥1125px 起即为 H1 原值 18px */
  line-height: 1.7;
  color: rgba(255, 255, 255, .94);
  text-shadow: 0 2px 8px rgba(0, 0, 0, .3);
}
.hero-content .actions { display: flex; flex-wrap: wrap; gap: 16px; }
.hero-content .btn { border-radius: 999px; }   /* H1：首屏按钮一律胶囊形 */
.hero-btn {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 5;
  width: 36px; height: 36px;                     /* 移动档（H1 无此档，为触控目标保留） */
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .3);                 /* H1 原值：纯黑半透明、无描边 */
  border: 0;
  border-radius: 50%;
  color: #fff; font-size: 16px;
  transition: background-color var(--dur-2) var(--ease-in-out);
}
.hero-btn:hover { background: rgba(0, 0, 0, .55); }   /* H1 原值 */
.hero-btn.prev { left: 8px; }
.hero-btn.next { right: 8px; }
.hero-dots {
  position: absolute; left: 0; right: 0; bottom: 24px; z-index: 5;   /* H1 原值 */
  display: flex; justify-content: center; gap: 8px;
}
.hero-dots .dot {
  position: relative;
  width: 10px; height: 10px; border-radius: 50%;
  background: rgba(255, 255, 255, .5);           /* H1 原值 */
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-in-out), width var(--dur-2) var(--ease-out);
}
.hero-dots .dot::after { content: ''; position: absolute; inset: -17px -9px; }  /* 命中区 ≈44px */
.hero-dots .dot:hover { background: #fff; }
.hero-dots .dot.active { width: 28px; border-radius: 5px; background: var(--primary-500); }  /* H1 原值 28px/5px */

/* 首屏移动档收紧（<768）。H1 原版整站 min-width:1200px、无移动端形态，
   这一段是按 H1 观感补的适配，不改桌面任何取值。要解决的是三个具体问题：
     ① 全局 `.btn-lg{width:100%}` 会让首屏两个按钮竖排堆叠，白吃 112px 高；
     ② 48px 的 h1 + 32px 段间距在 375 视口（容器仅 293px）里几乎顶满上下边；
     ③ h1 在窄屏折成「…北京 6 / 城区」这种尾字孤行。
   收紧后 375 视口内容约 210px、容器 293px，四周各留 ~40px，按钮并排可用。 */
@media (max-width: 767px) {
  .hero-content .btn-lg { width: auto; padding: 0 18px; font-size: 15px; }  /* 覆盖全局整宽，恢复并排 */
  .hero-content h1,
.hero-content h2.hero-title {   /* 轮播第 2/3 屏语义降为 h2，视觉与 h1 完全一致 */
    font-size: clamp(20px, 6vw, 28px);
    text-wrap: balance;                    /* 消除尾字孤行 */
    margin-bottom: 10px;
  }
  .hero-content p { margin-bottom: 20px; font-size: clamp(14px, 3.7vw, 16px); }
  .hero-content .actions { gap: 10px; }
  .hero-btn { width: 32px; height: 32px; font-size: 15px; }
  .hero-btn.prev { left: 4px; }            /* 窄屏让位给文字，箭头贴边 */
  .hero-btn.next { right: 4px; }
  .hero-dots { bottom: 12px; }
}

/* ==========================================================================
   8. 信任条 / 规格条（spec band）—— 2026-09-26 第二次重构
   ==========================================================================
   用户第二次否决（原话）：「18家北京严选机构，这4个优势的排版还是有点丑，
   你不然抄袭一下美国律师的排版呢？」
   → 本轮**照搬 meiguolvshi（美国律师站）首页 .spec-band 的版式**，其源码形态为：
        .spec-band{ border-top:1px solid; border-bottom:1px solid; background:#fff }
        .spec-grid{ display:grid; grid-template-columns:repeat(4,1fr) }
        .spec-cell{ padding:38px 34px; border-right:1px solid }
        .spec-cell:last-child{ border-right:none }
        .spec-cell .n{ font-family:serif; font-size:52px; line-height:1; color:navy-800 }
        .spec-cell .n em{ color:red; font-style:normal; font-size:.45em; vertical-align:.18em }
        .spec-cell .l{ font-size:13px; color:ink-2; margin-top:10px }

   ⚠️ 与上一版（图标 + 数字 + 标签三行）的**本质差别**是**去掉了图标**。
      上一版失败的原因不是图标画得不好，而是「小图标 + 中号数字」两个元素挤在
      同一行、谁也不占主导，读起来像"功能入口"而不是"数据"。
      参考站的做法：**只留一个视觉主角 —— 超大衬线数字**，单位缩到 0.45em 当上标、
      用品牌色点睛，标签退成 13px 灰字。层级靠**字号跨度**拉开，不靠元素数量堆。

   本项目的色彩映射（参考站是 navy 数字 + 红单位，本项目换成对应令牌）：
      数字 → --ink-900（深墨蓝，= 参考站 navy-800 的位置）
      单位 → --primary-500（品牌亮橙，= 参考站 red-600 的位置）⭐ 唯一品牌色触点
      标签 → --text-2，13px
   分隔线用上下各 1px + 列间 1px 发丝线（--line-1），与参考站同构。
   本区块不可点击，**不加任何 hover 动效**。 */
.trust-bar {
  padding: 0;                          /* 由单元格内边距撑开，整条才有"带"的感觉 */
  background: var(--surface);
  border-top: 1px solid var(--line-2);
  border-bottom: 1px solid var(--line-2);
}
.trust-bar .container {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
}
.trust-bar > .container > * { min-width: 0; }
/* 2026-09-27（需求 60 · 方案 B）：单元格内容居中。改动前为 text-align: left ——
   四格数字字号一致但标签长短不一，左对齐会让右半边参差、四个数字不成节奏。 */
.trust-item { padding: var(--s-5) var(--s-4); text-align: center; }
/* 窄屏 2×2：偶数列（第 2、4 个）左侧竖线；第 3、4 个上方横线 */
.trust-bar > .container > *:nth-child(even) { border-left: 1px solid var(--line-2); }
.trust-bar > .container > *:nth-child(n+3) { border-top: 1px solid var(--line-2); }
/* 数字：本区块唯一的视觉主角。clamp 让 360px 小屏也能保持「大数字」的观感，
   不至于挤成两行。字号跨度（34→48px）对 13px 标签是 2.6~3.7 倍，层级天然成立。 */
.trust-item .num,
.trust-num {
  display: block;
  font-family: var(--font-serif);
  font-size: clamp(34px, 9.5vw, 48px);
  line-height: 1; font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}
/* 单位（家 / 个 / 大 / 元）：缩到 0.45em 上标，用品牌亮橙点睛 ——
   整条规格条只有这一处出现品牌色，4 个数字因此有了统一的"标点"。
   对比度 5.05:1（--primary-500 on 纯白），作正文字号级别的非文本装饰足够；
   它是**图形化的单位标记**，不承担正文阅读。
   🔴 2026-09-27 用户要求：「数字后面的那个橙色文字，加粗」→ font-weight:700。
   注意中文字符（家/个/大/元）不在 Georgia 字形内，会回退到 SimSun 并由浏览器
   合成加粗，小字号下笔画偏重；若要进一步调轻，只动此处的 font-weight 值即可，
   不要改字号（0.45em 是上标比例，动了会破坏 4 格基线对齐）。 */
.trust-item .num em,
.trust-num em {
  font-style: normal; font-size: .45em;
  font-weight: 700;
  color: var(--primary-500);
  vertical-align: .18em;
  margin-left: 1px;
  letter-spacing: 0;
}
.trust-item .cap,
.trust-item .unit,
.trust-cap,
.trust-label {
  display: block;
  margin-top: 10px;
  font-size: var(--fs-sm); line-height: 1.6;
  color: var(--text-2);
}

/* ==========================================================================
   9. 分类导航（新增区块：6 个分类大图标，让家长 3 秒找到方向）
   ========================================================================== */
/* 🔴 2026-10-03 需求 E·项6：用户「@图5 这个地方的背景色块，改成纯白色」。
   原 --band-grey(#e8ebf0) 灰底 → 纯白 var(--surface)。
   ⚠️ 本条打破了「相邻区块不得同色」的原纪律（上方 .inst-featured 与下方 .cat-blocks
   首块均白，ΔE=0）。**处置（E6b 补丁，2026-10-03 同日）**：
     · 纪律按 22g 反转为「优先不同色；同色时交界必须可辨」（见 --band-divider 注释）；
     · 交界线从组件级 --line-1(ΔE 6.02 偏弱) 升级为区块级 --band-divider(ΔE 10.39 可辨)；
     · 探针 probe_section_rhythm.mjs 判据同步升级，识别「同色 + 可辨分隔线」为合法形态。 */
.cat-nav {
  padding: var(--s-6) 0;
  background: var(--surface);
  border-top: 1px solid var(--band-divider);
  border-bottom: 1px solid var(--band-divider);
}
.cat-nav .container {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-3);
}
.cat-nav-item {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-2);
  min-height: 44px; padding: var(--s-3) var(--s-2);
  border-radius: var(--r-md);
  font-size: var(--fs-sm); font-weight: 600; color: var(--heading);
  text-align: center;
  transition: background-color var(--dur-1) var(--ease-in-out), transform var(--dur-2) var(--ease-out);
}
.cat-nav-item:hover { background: var(--surface-2); color: var(--heading); transform: translateY(-2px); }
.cat-nav-item .cat-nav-icon,
.cat-nav-item .icon {
  width: 64px; height: 64px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-lg);
  background: var(--surface-2);           /* 兜底；支持 color-mix 时用分类色 12% 底 */
  color: var(--cat-ink, var(--heading));
  font-size: 26px;
}

/* ==========================================================================
   10. 分类大区（版式 A 通栏大色块 / 版式 B 紧凑双列，交替打破同构）
   ========================================================================== */
.category-block {
  padding: var(--sec-y) 0;
  background: var(--surface);
  border-top: 1px solid var(--line-1);
}
.category-block .container {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: var(--s-5);
  align-items: start;
}
/* 版式 B：无侧栏，标题行 + 紧凑卡片网格 */
.category-block[data-layout="b"] .container,
.category-block[data-layout="B"] .container,
.category-block.is-compact .container,
.category-block--b .container { grid-template-columns: minmax(0, 1fr); }

.cat-banner {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: var(--s-3);
  padding: var(--s-6) var(--s-5);
  border-radius: var(--r-lg);
  /* 白字底必须是同色相的深化版（--cat-*-ink，4.83–8.19:1）；
     亮橙族原色配白字仅 2.15–3.53:1，见交付说明的规范冲突条目 */
  background: var(--cat-ink, var(--ink-800));
  color: #fff;
  box-shadow: none;                       /* 去掉原 0 14px 34px 大阴影 */
}
.cat-banner::before {
  content: ''; position: absolute; right: -60px; bottom: -80px;
  width: 260px; height: 260px; border-radius: 50%;
  /* 🔴 2026-09-27 与 .cat-panel::before **同步**提到 .16（同一装饰语言，参数必须一致）。
     见上方 .cat-panel::before 的注释：这里是分类页 card banner 上的孪生圆块。 */
  background: rgba(255, 255, 255, .16);
}
.cat-banner > * { position: relative; }
.cat-watermark {
  position: absolute; right: -6px; bottom: -28px;
  font-size: var(--fs-mark); font-weight: 800; line-height: 1;
  letter-spacing: -0.04em;
  color: rgba(255, 255, 255, .14);
  pointer-events: none;
}
.cat-title { display: flex; align-items: center; gap: var(--s-3); min-width: 0; }
.cat-banner .cat-icon,
.cat-banner .icon {
  width: 42px; height: 42px; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .18);
  border: 1px solid rgba(255, 255, 255, .24);
  border-radius: var(--r-sm);
}
.cat-banner .cat-icon svg,
.cat-banner .icon svg {
  width: 22px; height: 22px; fill: none; stroke: #fff; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.cat-banner h3 {
  color: #fff;
  font-size: var(--fs-h2);                /* 22 → 34px，形成字号对比 */
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.018em;
}
.cat-banner .slogan { font-size: var(--fs-sm); color: rgba(255, 255, 255, .92); }
.cat-banner .features { display: flex; flex-direction: column; gap: var(--s-2); }
.cat-banner .features li { display: flex; align-items: center; gap: var(--s-2); font-size: var(--fs-sm); color: #fff; }
.cat-banner .features li::before {
  content: '✓'; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 19px; height: 19px; border-radius: 50%;
  background: rgba(255, 255, 255, .22);
  color: #fff; font-size: 12px; line-height: 1;
}
.cat-banner .btn {
  align-self: flex-start; margin-top: auto;
  min-height: 44px;
  background: #fff; color: var(--primary-600);
  font-weight: 700;
}
.cat-banner .btn::after { content: '→'; margin-left: var(--s-1); font-weight: 400; }
.cat-banner .btn:hover { background: var(--primary-50); color: var(--primary-600); transform: translateY(-1px); }

/* 分类内容区 */
.cat-content { display: flex; flex-direction: column; gap: var(--s-5); min-width: 0; }
.cat-columns { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gut); }
.cat-row {
  min-width: 0;
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;                       /* 静止态只用描边（原 border + shadow 双保险） */
  transition: border-color var(--dur-2) var(--ease-in-out), box-shadow var(--dur-2) var(--ease-out);
}
.cat-row:hover { border-color: var(--line-3); box-shadow: var(--sh-2); }
.cat-row-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-3); padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line-1);
}
.cat-row-header .row-title,
.row-title {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: 15px; font-weight: 700; color: var(--heading);
}
.cat-row-header .row-title::before {
  content: ''; width: 4px; height: 16px; flex: none;
  background: var(--primary-500); border-radius: var(--r-xs);
}
.more {
  display: inline-flex; align-items: center;
  min-height: 44px;
  font-size: var(--fs-sm); font-weight: 500; color: var(--primary-600);
  transition: color var(--dur-1) var(--ease-in-out);
}
.more:hover { color: var(--primary-700); text-decoration: underline; }

/* 迷你列表（机构 / 课程 2 列） */
.mini-list { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: var(--s-5); }
.mini-list li { min-width: 0; border-bottom: 1px dashed var(--line-1); }
.mini-list li:last-child { border-bottom: none; }
.mini-item {
  display: flex; align-items: center; gap: var(--s-3);
  min-width: 0; min-height: 44px; padding: var(--s-2) 0;
  border-radius: var(--r-sm);
  transition: background-color var(--dur-1) var(--ease-in-out);
}
.mini-item:hover { background: var(--surface-2); }
.mini-item:hover .name { color: var(--primary-600); }
.mini-item .thumb {
  width: 72px; height: 54px; flex: none;
  overflow: hidden; border-radius: var(--r-sm);
  background: var(--surface-2);
  box-shadow: none;
}
.mini-item .thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-3) var(--ease-out); }
.mini-item:hover .thumb img { transform: scale(1.03); }
.mini-item .mini-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.mini-item .name {
  font-size: 14px; font-weight: 600; color: var(--heading);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color var(--dur-1) var(--ease-in-out);
}
.mini-item .sub {
  font-size: var(--fs-xs); color: var(--text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mini-item .sub .price { color: var(--primary-600); font-weight: 700; }
/* 「查看全部」CTA 格 —— 首页「其他艺术」大区全站只有 5 家机构，2×3 的第 6 格补一条入口，
   保证满格、不留空洞。刻意做成**虚线框 + 橙色文字**，与实心机构卡（缩略图 + 名 + 元信息）
   在形状上就区分开，家长一眼能看出「这不是机构，是去列表页的入口」。 */
.mini-item.mini-more {
  justify-content: center; text-align: center;
  min-height: 54px;                        /* 与 .thumb 等高，行高与相邻格对齐 */
  border: 1px dashed var(--line-2);
  border-radius: var(--r-sm);
  background: var(--surface-2);
}
.mini-item.mini-more .mini-info { flex: 0 1 auto; align-items: center; }
.mini-item.mini-more .name { color: var(--primary-600); }
.mini-item.mini-more:hover {
  background: var(--primary-50);
  border-color: var(--primary-500);
}
.mini-item.mini-more:hover .name { color: var(--primary-700); }

/* 课程卡片行 / 机构卡片行 */
.course-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gut); }
.inst-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gut); }

.course-card {
  display: flex; flex-direction: column; gap: var(--s-2);
  min-width: 0;
  padding: var(--s-3);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.course-card:hover { border-color: var(--line-3); box-shadow: var(--sh-2); transform: translateY(-2px); }
.course-card .thumb {
  position: relative; width: 100%; aspect-ratio: 16 / 10;
  overflow: hidden; border-radius: var(--r-sm); background: var(--surface-2);
}
.course-card .thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-3) var(--ease-out); }
.course-card:hover .thumb img { transform: scale(1.03); }
.course-card h4, .course-card h3 {
  font-size: 15px; font-weight: 600; color: var(--heading);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.course-card .meta { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); font-size: var(--fs-xs); color: var(--text-2); }
.course-card .price { font-size: clamp(18px, 1.6vw, 22px); font-weight: 800; color: var(--primary-600); }

.inst-card {
  display: flex; flex-direction: column;
  min-width: 0; overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.inst-card:hover { border-color: var(--line-3); box-shadow: var(--sh-2); transform: translateY(-2px); }
.inst-card .thumb { position: relative; width: 100%; aspect-ratio: 4 / 3; background: var(--surface-2); overflow: hidden; }
.inst-card .thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-3) var(--ease-out); }
.inst-card:hover .thumb img { transform: scale(1.03); }
.inst-card .thumb .badge { position: absolute; top: var(--s-2); padding: 3px 10px; border-radius: var(--r-pill); font-size: var(--fs-xs); color: #fff; background: rgba(13, 33, 55, .75); }
.inst-card .thumb .badge { left: var(--s-2); background: var(--primary-500); color: var(--on-primary); }
.inst-card .info { flex: 1 1 auto; padding: var(--card-pad); min-width: 0; }
.inst-card .info h4, .inst-card .info h3 { font-size: 15px; margin-bottom: var(--s-1); }
.inst-card .info .loc { font-size: var(--fs-sm); color: var(--text-2); }

/* 资讯快捷行 */
.news-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 var(--s-5); }
.news-row li {
  display: flex; align-items: center; gap: var(--s-2);
  min-width: 0; min-height: 44px; padding: var(--s-3) 0;
  font-size: 14px; color: var(--text-1);
  border-bottom: 1px dashed var(--line-1);
  transition: color var(--dur-1) var(--ease-in-out);
}
.news-row li:hover { color: var(--primary-600); }
.news-row li::before { content: '›'; flex: none; color: var(--primary-600); font-size: 16px; font-weight: 700; }
.news-row li .text-ellipsis { flex: 1 1 auto; }
.news-row li .date { flex: none; font-size: var(--fs-xs); color: var(--text-2); }

/* 区块标题行（h2 34px + 副标题 + 更多） */
.section-title {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-5); padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--line-2);
}
.section-title h2 {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  font-size: var(--fs-h2);
  letter-spacing: -0.018em;
}
.section-title h2::before {
  content: ''; width: 4px; height: 1em; flex: none;
  background: var(--primary-500);       /* 纯色，去渐变 */
  border-radius: var(--r-xs);
}
.section-title .sub { font-size: 14px; font-weight: 400; color: var(--text-2); }
.section-title .more { color: var(--primary-600); font-size: 14px; }
.section-title > div { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); }

/* ==========================================================================
   11. 卡片通用（静止只用描边、零阴影；hover 才加 --sh-2）
   ========================================================================== */
.card,
.item-card,
.card-inst,
.card-course,
.card-post,
.card-row {
  display: flex; flex-direction: column;
  min-width: 0; overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.card:hover,
.item-card:hover,
.card-inst:hover,
.card-course:hover,
.card-post:hover,
.card-row:hover { border-color: var(--line-3); box-shadow: var(--sh-2); transform: translateY(-2px); }
.item-card:focus-visible,
.card:focus-visible { outline: var(--bw-focus) solid rgba(255, 106, 61, .45); outline-offset: 2px; }

.item-card .thumb,
.card-inst .thumb,
.card-course .thumb,
.card-post .thumb,
.card-row .thumb {
  position: relative; width: 100%; aspect-ratio: 4 / 3;
  overflow: hidden; background: var(--surface-2);
}
.card-course .thumb { aspect-ratio: 16 / 10; }
.card-post .thumb { aspect-ratio: 16 / 9; }
.item-card .thumb img,
.card-inst .thumb img,
.card-course .thumb img,
.card-post .thumb img,
.card-row .thumb img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--dur-3) var(--ease-out);
}
/* ⚠️ 2026-09-29 修复（需求 83 复验抓出）：`:not(.thumb--logo)` 是本条规则**必须**的限定。
   `.thumb--logo` 是 flex 居中容器、里面装的是 logo/字标而非照片，并不需要这条「照片微放大」；
   更要命的是它会与下方 `.item-card:hover .thumb--logo .inst-logo { scale(1.05) }` **叠加**：
   实测 hover 后 img 的 rect = 1.05×1.03 = **1.0815** 倍（尚坤 228→246.58 > 240 裁切区，
   唯傲林 176→190.34 > 189.14）—— `.thumb` 是 overflow:hidden，**logo 边缘被直接切掉**。
   而注释里一直按 1.05 核算余量，属「注释与事实不符的假保证」。
   ⇒ 排除 `.thumb--logo`，让 logo 位只有 1.05 一个放大来源（判据 `_verify_req82_logo.mjs` ③b）。 */
.item-card:hover .thumb:not(.thumb--logo) img,
.card-inst:hover .thumb img,
.card-course:hover .thumb img,
.card-post:hover .thumb img,
.card-row:hover .thumb img { transform: scale(1.03); }

/* 封面浮标 */
.badge {
  position: absolute; top: var(--s-2); left: var(--s-2);
  display: inline-flex; align-items: center;
  min-height: 22px; padding: 3px 8px;
  border-radius: var(--r-pill);
  background: var(--primary-500);       /* 亮橙实底 + 白字（2.85:1 · 已知偏离，用户决策） */
  color: var(--on-primary);
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0;
  box-shadow: none;
}

/* 徽标三色分档（2026-09-28 需求 79）：优选 / 精品 / 推荐 用三种不同底色。
   🔴 走属性选择器、不新增类名：徽标词由构建脚本写进 data-badge，
      样式只认属性 —— 将来后台换词（PBOOT 端逐条指定）时这里一个字都不用改。
   🔴 三色 = **渐进增强**：12 处没有 data-badge 的旧徽标（category.html 等）
      自动沿用上面的 --primary-500 默认档，其他页面不会因此失配。
   对比度（白字）：优选 2.85:1（品牌橙·用户 2026-09-26 已知偏离决策，勿"修正"）
                 精品 5.31:1 ✅ ｜ 推荐 5.34:1 ✅ */
.badge[data-badge="精品"] { background: var(--navy-500); }
.badge[data-badge="推荐"] { background: var(--success); }

/* 品类角标用品类主题色（2026-10-03 需求 E·项1）
   用户原话：「courses.html 挑课程页面和 works.html 页面 这个地方是不是可以考虑用
             培训项目主题色」。
   🔴 零 HTML 改动：`.item-card` 自带 `data-cat`（中文键），22 节「分类色绑定」已把
      --cat / --cat-ink 写在 [data-cat] 上 ⇒ 一条作用域规则即自动按品类取色。
   🔴 底色取 --cat-ink（深化版）而非 --cat（原色）：白字对比度实测 —— 原色仅
      2.15–5.06:1（舞蹈 2.15 严重不达标），ink 档 4.82–8.19:1 **全部达 AA**。
      与 §10「白字底必须用 --cat-*-ink」的既有约定一致。
   🔴 2026-10-03 收窄作用域（需求 E 收口，起因：`_verify_req78_breadcrumb_cards.mjs` 单跑红）：
      原选择器 `.item-card .badge:not([data-badge])` 是**全局**的，连带命中了
      **category.html**（机构列表页）—— 但该页徽标装的是**档位词**（优选/精品/推荐，
      需求 78 的「亮橙实底」形态），不是品类名；用户项1 只点名 courses.html / works.html。
      ⇒ 按两个列表容器 **#course-list / #work-list** 收窄：只有这两页的角标跟品类走，
      category.html 恢复**统一亮橙**（与改造前逐值一致，req78 C 组守门）。
      🔴 勿改回全局选择器 —— 那会把任何 `.item-card` 上的档位词徽标一并染色。
   🔴 让位说明：institutions.html 的 4 张推荐卡徽标带 `data-badge`（优选/精品/推荐三色
      分档，见上方 `.badge[data-badge="精品"]` 等）。`[data-badge]` 与 `[data-cat]`
      同为 2 类特异性、靠**源码顺序**决定胜负，本条在后 ⇒ 故用 `:not([data-badge])`
      把带显式分档的徽标让出去（那几处不是品类色场景）。 */
#course-list .item-card .badge:not([data-badge]),
#work-list .item-card .badge:not([data-badge]) { background: var(--cat-ink, var(--primary-500)); }


/* 主推视觉标记已在 2026-09-27 按用户要求全部移除。
   原方案（音乐类目加浮标 / 描边 / 章 / 推荐位翻倍）被判定「太明显」，改为与其他
   4 个类目完全同构：音乐类目下就这么几家，它们在类目内的优先呈现已经是主推，
   再叠显式标记只是视觉噪音。对应类名 .badge-top / .flag-top / .tile-flag /
   .panel-flag 与 [data-cat="music"]、【data-block="music"】上的描边规则一并删除，
   勿再以任何形式恢复。 */

.item-card .info,
.card-inst .info,
.card-course .info,
.card-post .info,
.card-row .info {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; gap: var(--s-2);
  padding: var(--card-pad);
}
.item-card .info h4,
.card-inst .info h4,
.card-course .info h4,
.card-post .info h4,
.item-card .info h3,
.card-inst .info h3,
.card-course .info h3,
.card-post .info h3,
.card-row .info h3 {
  font-size: var(--fs-h4);
  font-weight: 600;
  line-height: 1.45;
}
/* 卡片标题统一 2 行截断（align-items:stretch 下高度仍一致） */
.item-card .info h4.text-ellipsis,
.card-inst .info h4.text-ellipsis,
.card-course .info h4.text-ellipsis,
.card-post .info h4.text-ellipsis,
.item-card .info h3.text-ellipsis,
.card-inst .info h3.text-ellipsis,
.card-course .info h3.text-ellipsis,
.card-post .info h3.text-ellipsis {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  white-space: normal; overflow: hidden;
}
.item-card .info .meta,
.card-inst .info .meta,
.card-course .info .meta,
.card-post .info .meta { font-size: var(--fs-sm); color: var(--text-2); }
.item-card .info .desc,
.card-post .info .desc {
  font-size: var(--fs-sm); color: var(--text-2); line-height: 1.65;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.item-card .info .foot,
.card-inst .info .foot,
.card-course .info .foot,
.card-post .info .foot,
.card-row .info .foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-2);
  margin-top: auto;                       /* 价格/按钮落在同一水平基线 */
  padding-top: var(--s-3);
  border-top: 1px dashed var(--line-1);
  font-size: var(--fs-sm);
}
.item-card .info .foot .price {
  font-size: clamp(16px, 1.4vw, 18px); font-weight: 800;
  color: var(--primary-600); font-variant-numeric: tabular-nums;
}
.card-course .info .price { font-size: var(--fs-price); font-weight: 800; color: var(--primary-600); font-variant-numeric: tabular-nums; }
.item-card .info .foot .loc { font-size: var(--fs-sm); color: var(--text-2); }

/* 学员作品「点赞」两态（2026-10-03 需求 E·项3）
   用户原话：「学员作品 列表页有点赞按钮，但是详情页没有点赞按钮，要列表和内容对应
             起来，要让用户能点赞」。
   🔴 一套视觉、两种用法：
      · 详情页 `.like-btn`（= <button>）—— 可点，pill 描边 + hover 主色 + 已赞实底
      · 列表页 `.like-cnt`（= <span>）—— **不可点**（列表整卡是链接，卡内再放可点控件
        会与整卡跳转抢事件），只提供「数字同步 + 已赞变色」这一状态提示
   🔴 已赞态统一用 aria-pressed="true" 表达（JS 同时写状态与视觉，两者不会漂移）。
   🔴 颜色取自现有令牌：--line-1 / --surface / --text-2 / --primary-50 /
      --primary-500 / --primary-600（白底橙字 4.92:1，达 AA）。 */
.like-btn {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 10px; border-radius: var(--r-pill);
  border: 1px solid var(--line-1); background: var(--surface);
  color: var(--text-2); font: inherit; font-size: var(--fs-xs);
  line-height: 1.6; cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-in-out),
              border-color var(--dur-1) var(--ease-in-out),
              color var(--dur-1) var(--ease-in-out);
}
.like-btn:hover { border-color: var(--primary-500); color: var(--primary-600); }
.like-btn:focus-visible { outline: 2px solid var(--primary-500); outline-offset: 2px; }
.like-btn[aria-pressed="true"] {
  background: var(--primary-50); border-color: var(--primary-500);
  color: var(--primary-600); font-weight: 600; cursor: default;
}
/* 列表页：不可点，仅「已赞」时变色提示 */
.like-cnt { display: inline-flex; align-items: center; gap: 4px; }
.like-cnt[aria-pressed="true"] { color: var(--primary-600); font-weight: 600; }
.like-n { font-variant-numeric: tabular-nums; }

/* 🔴 需求 88：「价格未公示」的课程卡（4 家真机构的 28 个班型）——价格位放的是
   「价格以机构公示为准」这句话，**不是数字**。若沿用上面 18px/800 橙色 tabular-nums，
   会读成「这家报了这么个价」，语义正好相反。
   故单开一个修饰类：降字号、去粗体、改中性色、去掉等宽数字对齐。 */
.item-card .info .foot .price--tbd {
  font-size: var(--fs-sm); font-weight: 500; color: var(--text-2);
  font-variant-numeric: normal;
}

/* 卡片行动按钮「预约试听」（2026-09-28 需求 78）：原为纯文字（16–18px/800 橙字、无底无边框），
   用户要求「加个按钮样式，符合整个风格」，选定「实底橙 + 小箭头」。
   🔴 必须保留 .price 类名：main.js 的 sortPriceOf() 靠 querySelector('.price') 取排序键
   （课程页「价格最低/最高」依赖它），这里只**追加** .card-cta，不改名、不删类。
   🔴 整张卡是 <a>，此处只能拿 span 做「视觉按钮」，不得嵌套 <button>
   （a > button 属非法内容模型，且会截断整卡跳转）。
   箭头用 ::after 描边绘制，与 .breadcrumb .sep 同一手法 —— 不用字符箭头，
   字符在不同字体下基线与字重会漂（项目既有约定：禁字符冒充图标）。 */
.item-card .info .foot .price.card-cta,
.card-row .info .foot .price.card-cta {
  display: inline-flex; align-items: center; gap: 6px;
  height: 34px; padding: 0 var(--s-3) 0 var(--s-4);
  background: var(--primary-500);          /* 亮橙实底 + 白字（2.85:1 · 已知偏离，用户决策） */
  color: var(--on-primary);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm); font-weight: 700;
  letter-spacing: 0;
  transition: background-color var(--dur-1) var(--ease-in-out);
}
.item-card .info .foot .price.card-cta::after,
.card-row .info .foot .price.card-cta::after {
  content: ''; flex: none;
  width: 5px; height: 5px;
  border-top: 1.6px solid currentColor;
  border-right: 1.6px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--dur-1) var(--ease-out);
}
/* hover 加深到 --primary-550：与全站主按钮同一口径（见 §5 品牌橙注释） */
.item-card:hover .info .foot .price.card-cta,
.card-row:hover .info .foot .price.card-cta { background: var(--primary-550); }
.item-card:hover .info .foot .price.card-cta::after,
.card-row:hover .info .foot .price.card-cta::after { transform: rotate(45deg) translate(2px, -2px); }

/* 横向列表卡 */
.item-card.horizontal,
.card-row { flex-direction: column; }
.item-card.horizontal .thumb { width: 100%; aspect-ratio: 16 / 9; }
.item-card.horizontal .info .meta-row, .card-row .info .meta-row { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); font-size: var(--fs-sm); color: var(--text-2); }
.item-card.horizontal .info .meta-row .label, .card-row .info .meta-row .label { color: var(--heading); font-weight: 600; }
.item-card.horizontal .info h4, .item-card.horizontal .info h3, .card-row .info h3 { font-size: 17px; }
.item-card.horizontal .info p, .card-row .info p { font-size: var(--fs-sm); color: var(--text-2); line-height: 1.75; }

/* ==========================================================================
   11b. 机构列表卡升级（2026-09-28 需求 79）
   --------------------------------------------------------------------------
   🔴 只作用于 html/institutions.html（铁律 31：pboot/** 模板不动）。
   四处落点：
     · .thumb--logo / .inst-logo   左侧实景图 → 品牌字标        （79-2）
     · .title-row  / .type-chip    机构名旁的培训类型标志        （79-3）
     · .card-actions / .card-more  预约试听改弹窗 + 查看详情按钮  （79-5、79-6）
     · .campus-tags                多校区内容                    （79-4）

   ⚠️ 卡片外框仍是 <a>（施工器刻意不改标签）：<a> 内禁 interactive content，
      故两个控件都是 <span>；「预约到店」点击后弹窗而非跳页，靠的是事件冒泡到
      <a> 之后被 main.js initModal() 的 e.preventDefault() 拦下 —— 零新增 JS。
   ========================================================================== */

/* —— 79-2 品牌字标 ——
   40 家机构没有任何 LOGO 文件（images/ 里全是实景照），按项目「缺信息一律留空、
   不编造」的口径，用「品类浅底 + 深墨蓝方标 + 机构名 2 字」做字标；
   将来拿到真 logo，把 <span class="inst-logo"> 原位换成 <img> 即可，本段无需改动。
   方标取**中性深墨蓝**（--ink-900）而非品类主色：一屏 40 张卡里若 6 个品类各上一色，
   会撞上「每屏 ≤2 色」的用色纪律；品类信息交给浅底与水印图标承载。 */
.item-card.horizontal .thumb--logo {
  display: flex; align-items: center; justify-content: center;
  position: relative;                       /* 锚定 ::before 水印 */
  height: 240px; aspect-ratio: auto; flex: none;   /* E5 最终版：固定缩略区高 240 */
  background-image:
    linear-gradient(180deg, rgba(13,33,55,.25) 0%, rgba(13,33,55,.78) 100%),
    var(--e5-bg, var(--cat-soft, var(--surface-2)));
  background-size: cover, cover;
  background-position: center, center;
  background-color: var(--cat-soft, var(--surface-2));  /* 无图时兜底品类浅底 */
}
/* 右下角品类水印：与 .filter-side .cat-ico 同一 mask 手法，
   图标源就是 [data-cat] 注入的 --h2-ico（卡片自带 data-cat，继承即可）。
   故意让水印越出边界，由 .thumb 的 overflow:hidden 裁出「半图标」的版面感。 */
.item-card.horizontal .thumb--logo::before {
  content: ''; position: absolute; right: -16px; bottom: -16px;
  width: 108px; height: 108px;
  background-color: var(--cat, var(--text-3));
  opacity: .13;
  -webkit-mask: var(--h2-ico, none) center / contain no-repeat;
          mask: var(--h2-ico, none) center / contain no-repeat;
}
.inst-logo {
  position: relative; z-index: 1;                 /* 压在 ::before 水印之上 */
  display: inline-flex; align-items: center; justify-content: center;
  width: 88px; height: 88px;
  border-radius: var(--r-lg);
  background: var(--ink-900);                     /* 纯色，不用渐变（规范 §5） */
  color: #fff;
  font-size: 25px; font-weight: 700; line-height: 1;
  letter-spacing: .08em;
  text-indent: .08em;                             /* 抵消末字右侧的字距，视觉居中 */
  box-shadow: var(--sh-2);
  transition: transform var(--dur-3) var(--ease-out);
}
.item-card:hover .thumb--logo .inst-logo { transform: scale(1.05); }

/* —— E5 最终版（2026-10-01 · 见 scripts/_apply_cardthumb_e5.py）——
   列表卡字标改为「机构全名」文字（删除所有真 LOGO / 2 字方块，统一全名）。
   ⚠️ 仅作用于 .item-card.horizontal（列表卡），不动 institution-detail 的 .inst-logo。 */
.item-card.horizontal .inst-logo {
  width: auto; height: auto; max-width: 204px;
  background: none; box-shadow: none; border-radius: 0;
  color: #fff;
  font-size: 26px; font-weight: 700; line-height: 1.28;
  letter-spacing: 0; text-indent: 0;
  text-shadow: 0 1px 5px rgba(0,0,0,.65);
  padding: 0 10px; text-align: center;
}
/* E5 最终版 · 逐卡实景图（按 data-e5 注入） */
[data-e5="acting-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/acting-inst1.jpg"); }
[data-e5="acting-inst2.jpg"] .thumb--logo { --e5-bg: url("../images/acting-inst2.jpg"); }
[data-e5="acting-inst3.jpg"] .thumb--logo { --e5-bg: url("../images/acting-inst3.jpg"); }
[data-e5="acting-inst4.jpg"] .thumb--logo { --e5-bg: url("../images/acting-inst4.jpg"); }
[data-e5="art-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/art-inst1.jpg"); }
[data-e5="art-inst2.jpg"] .thumb--logo { --e5-bg: url("../images/art-inst2.jpg"); }
[data-e5="art-inst3.jpg"] .thumb--logo { --e5-bg: url("../images/art-inst3.jpg"); }
[data-e5="art-inst4.jpg"] .thumb--logo { --e5-bg: url("../images/art-inst4.jpg"); }
[data-e5="art-inst5.jpg"] .thumb--logo { --e5-bg: url("../images/art-inst5.jpg"); }
[data-e5="call-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/call-inst1.jpg"); }
[data-e5="call-inst2.jpg"] .thumb--logo { --e5-bg: url("../images/call-inst2.jpg"); }
[data-e5="call-inst3.jpg"] .thumb--logo { --e5-bg: url("../images/call-inst3.jpg"); }
[data-e5="call-inst4.jpg"] .thumb--logo { --e5-bg: url("../images/call-inst4.jpg"); }
[data-e5="dance-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/dance-inst1.jpg"); }
[data-e5="dance-inst2.jpg"] .thumb--logo { --e5-bg: url("../images/dance-inst2.jpg"); }
[data-e5="dance-inst3.jpg"] .thumb--logo { --e5-bg: url("../images/dance-inst3.jpg"); }
[data-e5="dance-inst4.jpg"] .thumb--logo { --e5-bg: url("../images/dance-inst4.jpg"); }
[data-e5="music-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/music-inst1.jpg"); }
[data-e5="music-inst2.jpg"] .thumb--logo { --e5-bg: url("../images/music-inst2.jpg"); }
[data-e5="music-inst3.jpg"] .thumb--logo { --e5-bg: url("../images/music-inst3.jpg"); }
[data-e5="music-inst4.jpg"] .thumb--logo { --e5-bg: url("../images/music-inst4.jpg"); }
/* 2026-10-01 新增：四家真机构各自的对口实景图。
   原先 institutions.html 三张卡的 data-e5 误指向 music-inst1/2/3 ——
   那三张是「乐器店挂满提琴与吉他 / 琴房钢琴 / 木门走廊」的通用音乐场景，
   与机构实际学科不符：鸿艺古筝的背景里全是吉他与小提琴（用户截图指出，确实离谱）。
   图像本体早已在 images/ 且被首页 / 品类页 / 详情页正确引用，只是列表页漏了映射。
   尚坤 → violin-inst1.jpg（创始人持琴照，来自 skviolin.com，用户指定）
   唯傲林 → weiaolin-inst1.jpg（门店招牌，裁自 bjwal.com/guanyu/，用户指定）
   鸿艺 → guzheng-inst1.jpg（学员弹古筝）—— 🔴 2026-10-01 已废止：
          用户裁决机构主图用**老师**不用学员，鸿艺改 guzheng-teacher1.jpg（焦娇老师）。
          唯傲林**未改**（用户当日二次裁决，见下方说明）。旧行保留仅备回滚。 */
[data-e5="guzheng-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/guzheng-inst1.jpg"); }
[data-e5="violin-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/violin-inst1.jpg"); }
[data-e5="weiaolin-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/weiaolin-inst1.jpg"); }
/* 🔴 2026-10-01 用户裁决「机构主图用老师不用学员」——新增老师照映射。
   尚坤 violin-inst1.jpg 本就是尚坤老师持琴照（skviolin.com 师资力量页），无需改；
   鸿艺原用 guzheng-inst1.jpg 是**未成年学员**（用户点名指出），改 guzheng-teacher1.jpg（焦娇老师）。
   ⚠️ 唯傲林**不改**：一度改过 weiaolin-teacher1.jpg（何宇洁老师），
      用户当日裁决「唯傲林用错了，还是用之前的机构的那个」⇒ 已回退 weiaolin-inst1.jpg（门店招牌）。
      回退依据：何宇洁照取自 bjwal.com，该站师资模块**同时列着「尚坤老师」**，
      且其另一张图与尚坤创始人照**字节完全相同** ⇒ 归属到唯傲林一家证据不足；
      门店招牌虽无人像，但系该机构**自有且确凿**的门头实景，也不属「学员照」，不违纪。
      ⇒ 纪律口径收窄为：**不得用未成年学员照**；主图优先老师照，无可靠老师照时用机构自有实景。
   裁切已按真实容器核过：桌面 .thumb--logo 为 240×286（w/h=0.839，竖），
   移动端 356×200（w/h=1.78，横）——见 scripts/_probe_thumb_crop.mjs 的对照样张。 */
[data-e5="guzheng-teacher1.jpg"] .thumb--logo { --e5-bg: url("../images/guzheng-teacher1.jpg"); }
[data-e5="other-inst1.jpg"] .thumb--logo { --e5-bg: url("../images/other-inst1.jpg"); }
[data-e5="other-inst2.jpg"] .thumb--logo { --e5-bg: url("../images/other-inst2.jpg"); }
[data-e5="other-inst3.jpg"] .thumb--logo { --e5-bg: url("../images/other-inst3.jpg"); }
[data-e5="other-inst4.jpg"] .thumb--logo { --e5-bg: url("../images/other-inst4.jpg"); }

/* —— 79-2 续 · 真 logo 版（2026-09-29 需求 82 立、需求 83 加鸿艺并整体放大）——
   上述「深墨蓝方标 + 机构名 2 字」是**尚无 logo 机构**的字标兜底；拿到商家品牌标的
   （尚坤 id=37 / 唯傲林 id=38 / 鸿艺 id=39）改用 <img>，同一位换载体、不动版面结构。
   🔴 图片版必须**去掉深墨蓝方块底**，两个原因都在图上：
      · 尚坤 logo 自带黑色「尚坤小提琴」小字 + 黄橙描边 ⇒ 压在深墨蓝上糊成一片；
      · 唯傲林 logo 自带红色圆角方块底 ⇒ 再叠一层深蓝是两块色打架。
   故图片版无背景 / 无阴影 / 无圆角，直接落在 .thumb--logo 的品类浅底上。
   ⚠️ 尺寸走**双向夹 + contain**，不写死宽高：横版（尚坤 157:55≈2.85:1）与
      方版（唯傲林 46:50≈0.92:1、鸿艺 77:76≈1.01:1）各自保持比例，谁也不被拉伸或裁切。 */
.inst-logo--img {
  width: auto; height: auto;
  background: none; box-shadow: none; border-radius: 0;
}
/* ⚠️ 宽高上限是**量出来的**，不是估的 —— 缩略区（.thumb--logo 所在的 .thumb）实测：
   桌面 240 宽、高**逐卡不同** 189~278（id=37 277.89 / id=38 189.14 / id=39 211.89）、
   小屏 356x200（纵向布局 `width:100%`，390 视口实测）。
   （2026-09-29 需求 83 更正：此处原写「桌面 240x150（width:240px + aspect-ratio:16/10）」——
   那是**推理代替实测**：`.item-card.horizontal { align-items: stretch }` 把 .thumb 拉到卡片
   全高、aspect-ratio 被覆盖 ⇒ 真实高度远大于 150，上限值据此一度取低。）
   ⚠️ 桌面**宽度上限只能到 228**：再大就溢出 240 的缩略区 —— `.inst-logo--img` 是 inline-flex，
   百分比宽退化为 auto ⇒ 按固有尺寸渲染、只受 max-* 夹取（③ 号判据立刻判溢出）。
   上限 = 可用空间 − 呼吸位，且**必须留够 hover 缩放的余量**：
   `.item-card:hover .thumb--logo .inst-logo` 有 `scale(1.05)`，图片版的外层 span 也带 `.inst-logo`
   ⇒ 放大后不得越界（桌面 228×1.05=239.4 ≤240、176×1.05=184.8 ≤189.14；小屏 320/176 同理留足）。
   ⚠️ 上面这行**曾经是错的**（2026-09-29 需求 83 复验抓出）：当时另有
   `.item-card:hover .thumb img { scale(1.03) }` 同时命中 logo 的 img，真实倍率是 1.05×1.03=**1.0815**
   ⇒ 尚坤 hover 到 246.58 > 240 被 overflow:hidden **切边**、唯傲林 190.34 > 189.14 也被切。
   已给那条规则加 `:not(.thumb--logo)`（见本文件 L1343 上方注释）。现在倍率确实只有 1.05。
   注意桌面 228 这档余量只剩 **0.6px** ⇒ ③b 判据（hover 后 rect ⊂ 裁切区）**不可省**，
   余量核算以判据为准，不以这段文字为准。
   🔴 **2026-09-29 需求 83 整体放大**（用户口径「要大点，有点小呢」）：
   旧值 208x88（桌面）/ 251x96（小屏）下，横版尚坤渲染 208x72.9 —— **宽占 87%、高仅 49%**，
   上下大片留白显得小；方版唯傲林更糟，只渲染 80.7x88（宽占 34%）。原因不是上限写小了，
   而是**上限被「最扁的那张」拖累**：208 是从「缩略区宽 − 16 呼吸位」来的，对扁图刚好，
   对**方图**则白白浪费高度。⇒ 改为**按两个维度分别给足**（桌面 228x176 / 小屏 320x176），
   让每张图各自贴到自己那一维的边（实测：尚坤 228x79.8 占宽 100%、唯傲林 162x176 占高 100%、
   鸿艺 178x176 占高 100%）。本需求的几何度量是判据 **⑤d「宽或高至少一维 ≥ 上限 95%」** ——
   只断言宽度会漏掉方版（唯傲林宽度永远小，它的「小」表现为**高度被浪费**，宽度断言全无感知）。
   出图尺寸 = 两档上限的较大者 ×2（尚坤 640x224 / 唯傲林 324x352 / 鸿艺 356x352），CSS 只做缩小，
   所以高 DPR 屏也清晰（只出 1x 尺寸会在 DPR2 下发虚）。
   🔴 target 与上限是**双向绑定**的：`make_inst_logo.py` 的 target 必须 == 小屏上限（判据 ⓪c），
   改任一侧不同步另一侧 ⇒ 出图与展示脱钩（要么 DPR2 发虚、要么白出大图）。
   🔴 选择器**必须带 .item-card .thumb 前缀**（2026-09-29 需求 82 复验抓出）：
      本文件上方有 `.item-card .thumb img { width:100%; height:100%; object-fit:cover }`
      （权重 0,2,1），而 `.inst-logo--img img` 只有 0,1,1 ⇒ **被它盖掉**，
      object-fit 实测为 cover、width/height 实测为 100%。
      当时尺寸碰巧还是对的 —— 因为 .inst-logo--img 是 `inline-flex`，容器主轴尺寸不确定，
      百分比宽度在 flex item 上退化为 auto，于是走固有比例 + max-* 夹取。
      这属于「靠巧合正确」：一旦容器尺寸变为确定值（例如有人给它写死 width），
      cover 立刻就会裁掉 logo 两翼。故把前缀补上，让规则真正生效而不是碰巧。
      判据见 `_verify_req79_list_cards.mjs` B9（object-fit 必须为 contain）。 */
.inst-logo--img img,
.item-card .thumb .inst-logo--img img {
  display: block; width: auto; height: auto;
  max-width: 228px; max-height: 176px;
  object-fit: contain;
}
@media (max-width: 767px) {
  .inst-logo--img img,
  .item-card .thumb .inst-logo--img img { max-width: 320px; max-height: 176px; }
}

/* —— 79-3 培训类型标志 ——
   ⚠️ h3 本体一个字不动，只外包一层 .title-row：
      verify_headings 与 verify_dual_typography 都拿
      `.item-card.horizontal .info h3` 的计算字号 / 颜色做断言，
      把 h3 的 display 改成 flex 或往上加类，这两条闸门会误报。
   ⚠️ 图标沿用 --h2-ico（即 6 大品类图标）；有 data-sub 的机构由
      apply_req79_list_cards.py 换成细分科目文案，图标仍是品类图标。
   🔴 h3 用 flex: 0 1 auto 而非 1 1 auto（2026-09-28 落盘后目视抓出）：
      h3 若撑满整行（flex-grow: 1），机构名只有 6 字而盒子宽 512px，紧随其后的
      .type-chip 会被顶到卡片最右侧、距机构名约 700px —— 而需求原话是
      「在机构名称**附近**」。改为按内容宽度后 chip 紧贴名字：实测
      chip 左边缘 − h3 右边缘 = 8px（恰为 .title-row 的 gap 值）。
      长机构名场景由 flex-wrap 兜底（chip 换行到第二行），不挤压机构名。 */
.title-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1) var(--s-2); }
.title-row h3 { flex: 0 1 auto; min-width: 0; }
.type-chip {
  flex: none;
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px;
  border-radius: var(--r-pill);
  background: var(--cat-soft, var(--surface-2));
  color: var(--cat-ink, var(--text-2));
  font-size: var(--fs-xs); font-weight: 700; line-height: 1.5;
  white-space: nowrap;
}
.type-chip .cat-ico {
  display: block;
  width: 12px; height: 12px; flex: none;
  background-color: currentColor;                 /* 跟随 --cat-ink，选中态不受影响 */
  -webkit-mask: var(--h2-ico, none) center / contain no-repeat;
          mask: var(--h2-ico, none) center / contain no-repeat;
}

/* —— 79-5 / 79-6 卡片双控件 ——
   .card-cta 的样式在上方 11 节已有，这里只补两件：① 它现在与 .card-more 并排，
   需要一层 flex 容器；② .card-more 是新增的次级按钮（描边 + 无底）。 */
.foot .card-actions { display: inline-flex; align-items: center; gap: var(--s-2); flex: none; }
.card-more {
  display: inline-flex; align-items: center; gap: 6px;
  height: 34px; padding: 0 var(--s-4);
  border: 1px solid var(--line-3);
  border-radius: var(--r-pill);
  color: var(--text-1);
  font-size: var(--fs-sm); font-weight: 600;
  transition: border-color var(--dur-1) var(--ease-in-out),
              color var(--dur-1) var(--ease-in-out),
              background-color var(--dur-1) var(--ease-in-out);
}
/* 箭头与 .card-cta::after 同源（描边绘制，不用字符箭头 —— 字符基线会随字体漂） */
.card-more::after {
  content: ''; flex: none;
  width: 5px; height: 5px;
  border-top: 1.6px solid currentColor;
  border-right: 1.6px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--dur-1) var(--ease-out);
}
.item-card:hover .card-more {
  border-color: var(--primary-500); color: var(--primary-600); background: var(--primary-50);
}
.item-card:hover .card-more::after { transform: rotate(45deg) translate(2px, -2px); }
/* 🔴 双控件让 foot 变宽（loc + 两个 34px 按钮），窄档会挤 —— 
   只在本页放开换行（body 自带 page-institutions），不波及其他列表页。 */
.page-institutions .item-card.horizontal .info .foot { flex-wrap: wrap; row-gap: var(--s-2); }

/* —— 79-4 多校区 —— */
.campus-tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.campus-tag {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px 2px 8px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--text-1);
  font-size: var(--fs-xs); font-weight: 600;
}
/* 定位图标：与侧栏城区芯片同一条 data-URI（feather pin，stroke 1.7） */
.campus-tag::before {
  content: ''; flex: none;
  width: 11px; height: 11px;
  background-color: var(--text-3);
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M21%2010c0%207-9%2013-9%2013s-9-6-9-13a9%209%200%200%201%2018%200z%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2210%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M21%2010c0%207-9%2013-9%2013s-9-6-9-13a9%209%200%200%201%2018%200z%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2210%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
}

/* ==========================================================================
   11c. 列表卡 V4 精简改造（2026-10-01 需求）
   --------------------------------------------------------------------------
   用户拍板六件事，本段落地其中四件（另两件在 HTML/JS 侧）：
     · 卡片总高统一 + 信息栏定高 + 超出截断「展开」
     · 新增「教学方式」（HTML 侧 meta-row，取值 到店面授 / 老师上门 / 线上）
       🔴 2026-10-02：允许复合取值，多种方式并存时按既有 `form` 写法用「 · 」连接
       （如 `到店面授 · 老师上门`）；37/38/39 三家即此值，40 左涂右画仅 `到店面授`。
     · 校区胶囊精简为「区域」（HTML 侧）
     · 位置行 = 📍 + 大区 + 明细（去掉「北京市」前缀，加地图图标）
   🔴 只作用于 .page-institutions，不波及其他列表页（铁律 31：pboot/** 不动）。
   🔴 圆角一致性的**根因**：E5 把 .thumb--logo 钉死 height:240px，信息栏更高时
      缩略区下方留白 ⇒ 卡片底部的圆角看起来缺口。修法 = 卡片定高 + 缩略区填满卡片
      高度（height:100%），圆角由 .item-card 的 overflow:hidden 统一裁出。 */

/* —— 位置行地图图标（.loc 前置）——
   与 .campus-tag::before 同一条 feather pin（stroke 1.7），视觉重量一致。 */
.page-institutions .item-card.horizontal .info .foot .loc {
  display: inline-flex; align-items: center; gap: 4px;
}
.page-institutions .item-card.horizontal .info .foot .loc::before {
  content: ''; flex: none;
  width: 12px; height: 12px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M21%2010c0%207-9%2013-9%2013s-9-6-9-13a9%209%200%200%201%2018%200z%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2210%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M21%2010c0%207-9%2013-9%2013s-9-6-9-13a9%209%200%200%201%2018%200z%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2210%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
}

/* —— 简介截断 + 展开键 ——
   无 JS 降级：默认**不加** .is-clamped（main.js 才追加），简介完整可见；
   展开键默认 .is-hidden，由 JS 依据真实溢出决定是否显示 ⇒ 短简介不会挂废物键。 */
.page-institutions .item-card.horizontal .info p.card-desc { margin: 0; flex: none; }
.page-institutions .item-card.horizontal .info p.card-desc.is-clamped {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  line-clamp: 2; overflow: hidden;
}
.page-institutions .item-card.horizontal .info p.card-desc.is-open { -webkit-line-clamp: unset; }
.card-expand {
  align-self: flex-start;
  display: inline-flex; align-items: center;
  padding: 2px 0;
  color: var(--primary-600);
  font-size: var(--fs-xs); font-weight: 700;
  cursor: pointer;
  -webkit-user-select: none; user-select: none;
}
.card-expand.is-hidden { display: none; }
.card-expand:hover { color: var(--primary-550); }

/* —— 卡片总高统一 + 缩略区填满（桌面横向布局）——
   定高 = 缩略区 240 + 上下呼吸，取值 260（缩略区填满卡片高，圆角不再缺口）。
   信息栏同高 + overflow:hidden：字段已固定，正常不溢出；万一长机构名换行，截断不撑高。 */
@media (min-width: 768px) {
  /* 定高 288 = 实测「最高那张卡」的信息栏自然高（尚坤/左涂右画 含校区胶囊 = 287px，取整 288）。
     取最大值的好处：① 四张卡等高；② 缩略区 height:100% 填满，底角圆角无缺口（本次修复目标）；
     ③ 字段已固定 ⇒ 内容不溢出、按钮不被裁（用户预期「基本不会触发展开」）。
     🔴 别拍脑袋写 260 —— 实测 260 会把含校区胶囊的两张卡的 foot/按钮切掉 5px。 */
  .page-institutions .item-card.horizontal { height: 288px; overflow: hidden; }
  .page-institutions .item-card.horizontal .thumb,
  .page-institutions .item-card.horizontal .thumb--logo { height: 100%; }
  /* 🔴 定高后必须改 flex-start：.info 默认 justify-content:center（L3436），内容接近满高时
     居中会把首行推到盒外被 overflow:hidden 吃掉。同时让各行 flex:none 不被压扁
     （否则 .card-desc 作为 flex 子项会被 shrink 到 0 高 —— 实测踩过）。 */
  .page-institutions .item-card.horizontal .info {
    height: 100%; overflow: hidden; justify-content: flex-start;
  }
  .page-institutions .item-card.horizontal .info > * { flex: none; }
}
/* 窄屏为纵向布局（缩略区在上），不锁总高：内容自然流式，避免简介被压没。 */
@media (max-width: 767px) {
  .page-institutions .item-card.horizontal .thumb--logo { height: 200px; }
}

/* ==========================================================================
   12. chip / 面包屑 / 分页 / 空状态 / 骨架 / 提示
   ========================================================================== */
.chip {
  display: inline-flex; align-items: center; gap: var(--s-1);
  position: relative;
  height: 36px; padding: 0 14px;
  font-size: var(--fs-sm); color: var(--text-1);
  background: transparent;
  border: 1px solid var(--line-3);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease-in-out),
              background-color var(--dur-1) var(--ease-in-out),
              color var(--dur-1) var(--ease-in-out);
}
.chip:hover { border-color: var(--primary-500); color: var(--primary-600); }
.chip.is-active,
.chip.active {
  background: var(--primary-50); color: var(--primary-600);
  border-color: var(--primary-500);
  font-weight: 600;
}
.chip[disabled] { background: var(--surface-3); color: var(--text-3); border-color: var(--line-2); cursor: not-allowed; }

.breadcrumb {
  background: var(--surface);
  border-bottom: 1px solid var(--line-1);
  font-size: var(--fs-sm);
  color: var(--text-2);
}
/* 面包屑行 = 左「路径」+ 右「工具区」（列表计数、排序）。
   🔴 2026-09-28 需求 78：原先计数与排序放在列表上方的 .list-toolbar 里，它整体占位
   63.75px（自身 39.75 + margin-bottom 24），把列表首卡推低同样的距离，导致左侧筛选栏
   上边框与首卡上边框差 63.75px 对不齐（实测）。改为把工具区整体上移到面包屑行右侧
   （该处水平余量 1076px，充裕），并删除 .list-toolbar —— 首卡随之自然上移 63.75px，
   与侧栏顶严丝合缝（实测两侧 rect.top 相等）。
   ⚠️ .container 原为 flex + gap（无 space-between），此处补 justify-content 才能把
   工具区推到右端；flex-wrap 保留，窄屏工具区自动换到下一行。 */
.breadcrumb .container { display: flex; align-items: center; flex-wrap: wrap; justify-content: space-between; gap: var(--s-2); min-height: 56px; }
.breadcrumb .crumb-path { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-2); }
.breadcrumb .crumb-tools { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-3); }
.breadcrumb a { display: inline-flex; align-items: center; min-height: 44px; color: var(--text-2); }
.breadcrumb a:hover { color: var(--primary-600); }
.breadcrumb .sep {
  /* V4 修复：原为文字 ›，在不同字体下基线与字号会漂移，且属"字符冒充图标"。
     改为 6×6 描边箭头（1.4px 边 = 与 1.7px 描边图标同一视觉重量）。 */
  flex: none; width: 6px; height: 6px;
  font-size: 0; color: var(--line-3);
  border-top: 1.4px solid currentColor;
  border-right: 1.4px solid currentColor;
  transform: rotate(45deg);
}
.breadcrumb .current { color: var(--heading); font-weight: 600; }

.pagination { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s-2); margin-top: var(--s-6); }
.pagination a {
  display: flex; align-items: center; justify-content: center;
  min-width: 44px; height: 44px; padding: 0 var(--s-3);   /* 手机 44×44；桌面 40×40 */
  background: var(--surface); color: var(--text-1);
  border: 1px solid var(--line-3);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  transition: border-color var(--dur-1) var(--ease-in-out), background-color var(--dur-1) var(--ease-in-out), color var(--dur-1) var(--ease-in-out);
}
.pagination a:hover { border-color: var(--primary-500); color: var(--primary-600); }
.pagination a.active {
  background: var(--primary-500);         /* 亮橙实底 + 白字（2.85:1 · 已知偏离，用户决策） */
  border-color: var(--primary-500);
  color: var(--on-primary); font-weight: 700;
  box-shadow: none;
}
.pagination a.disabled,
.pagination a[aria-disabled="true"] { color: var(--text-3); border-color: var(--line-2); background: var(--surface-2); }

.empty, .list-empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
  padding: var(--s-9) var(--s-5);
  text-align: center;
  background: var(--surface-2);
  border: none;
  border-radius: var(--r-md);
}
.empty .empty-icon { width: 48px; height: 48px; color: var(--line-3); }
.empty h4, .empty strong, .empty h3 { font-size: 16px; font-weight: 600; color: var(--heading); }
.empty p { font-size: var(--fs-sm); color: var(--text-2); }
.empty--outline { background: transparent; border: 1px dashed var(--line-3); }

/* ── 404 / 错误页（2026-09-26 新增 → 类名契约 §16「错误页」）──────────────────
   双端共用：html/404.html（静态端）与 pboot/404.html（PBOOT 站点根目录静态文件）。
   整块纯白、无卡片无边框，以「大号衬线 404」为唯一视觉主角，
   与 .trust-bar 规格条的数字语言（serif + tabular-nums）同源。
   ⚠️ 数字色用 --primary-600（#c9420f，白底 4.92:1）而**不是** --primary-500：
      后者白底仅 2.85:1，即便按「大号文字」判据（≥3:1）也不达标。 */
.error-page {
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  padding: var(--s-7) 0 var(--s-8);
}
.error-code {
  font-family: var(--font-serif);
  font-size: clamp(72px, 20vw, 120px);
  line-height: 1; font-weight: 400; letter-spacing: .02em;
  color: var(--primary-600);
  font-variant-numeric: tabular-nums;
}
.error-heading { margin-top: var(--s-4); font-size: clamp(20px, 3.4vw, 26px); font-weight: 700; color: var(--heading); }
.error-lead { margin-top: var(--s-3); max-width: 34em; font-size: var(--fs-lead); line-height: 1.8; color: var(--text-2); }
.error-actions { display: flex; flex-wrap: wrap; gap: var(--s-3); justify-content: center; margin-top: var(--s-6); }
.error-hot {
  width: 100%; max-width: 680px;
  margin-top: var(--s-7); padding-top: var(--s-5);
  border-top: 1px solid var(--line-2);
}
.error-hot h2 { font-size: var(--fs-sm); font-weight: 600; letter-spacing: .04em; color: var(--text-3); }
.error-hot ul { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); justify-content: center; margin-top: var(--s-3); }
.error-hot a { font-size: var(--fs-sm); color: var(--primary-600); }
.error-hot a:hover { color: var(--primary-700); }

/* 加载骨架：允许的渐变 ②（规范 §6.5 明确要求扫光渐变） */
.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 0%, var(--surface-3) 50%, var(--surface-2) 100%);
  background-size: 200% 100%;
  border-radius: var(--r-sm);
  animation: skeletonSweep 1.4s linear infinite;
}
@keyframes skeletonSweep { from { background-position: 100% 0; } to { background-position: -100% 0; } }

/* 提示条 / 提交结果 */
.alert { padding: var(--s-3) var(--s-4); border-radius: var(--r-sm); font-size: var(--fs-sm); border: 1px solid transparent; }
.alert--success { background: var(--success-bg); color: var(--success); border-color: var(--success); }
.alert--warning { background: var(--warning-bg); color: var(--warning); border-color: var(--warning); }
.alert--danger { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }
.alert--info { background: var(--surface-2); color: var(--text-1); border-color: var(--line-2); }

.form-success {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
  padding: var(--s-5);
  text-align: center;
  background: var(--success-bg);
  border: 1px solid var(--success);
  border-radius: var(--r-md);
  color: var(--success);
}
.form-success strong { font-size: 16px; font-weight: 700; }
.form-success p { font-size: var(--fs-sm); color: var(--text-2); }

/* ==========================================================================
   13. 列表页（取消"卡中卡"三层嵌套：主体直接铺在纯白上）
   ========================================================================== */
.page-banner {
  position: relative;
  padding: var(--sec-y) 0;
  /* 🔴 2026-09-27 需求 68 顺带修复：原为 `padding: var(--sec-y) 0` —— **左右边距为 0**。
     列表页标题短，居中摆放看不出问题；但详情页补页头带后，H1 是**运行时长标题**
     （资讯标题 / 指南问句，最长 30 余字），在 ≤480px 会一直顶到屏幕边缘
     （实测：news-detail 480px 留白 11px、390px 留白 4px；guide-detail 390px 同样 4px）。
     改用与 .cat-block 同款写法：内容居中于 --w-container，视口比容器窄时自动退回 --pad-x。
     对称内边距不改变居中位置 → 宽屏视觉零位移（已逐页实测带高与 H1 墨迹位置不变）。 */
  padding-left: max(var(--pad-x), calc((100% - var(--w-container)) / 2 + var(--pad-x)));
  padding-right: max(var(--pad-x), calc((100% - var(--w-container)) / 2 + var(--pad-x)));
  text-align: center;
  background: var(--ink-900);              /* 深色带（去橙色渐变） */
  color: var(--text-on-dark);
}
.page-banner h1 { color: #fff; margin-bottom: var(--s-2); }
/* 需求 68：详情页页头带只有 H1（无副标题），去掉 H1 的 8px 下间距，保住上下对称。 */
.page-banner > h1:last-child { margin-bottom: 0; }
.page-banner p { font-size: var(--fs-lead); color: rgba(255, 255, 255, .82); }
.page-banner.theme-art,
.page-banner.theme-music,
.page-banner.theme-dance,
.page-banner.theme-calligraphy,
.page-banner.theme-acting,
.page-banner.theme-other { background: var(--cat-ink, var(--ink-800)); }
.page-banner.theme-art p,
.page-banner.theme-music p,
.page-banner.theme-dance p,
.page-banner.theme-calligraphy p,
.page-banner.theme-acting p,
.page-banner.theme-other p { color: rgba(255, 255, 255, .88); }

.list-wrap { padding: var(--sec-y) 0; }
.list-wrap .container { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }

.filter-side {
  min-width: 0;
  padding: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;                        /* 去原 border + shadow 双保险 */
}
/* 侧栏筛选组标题：静态端用 <h4>，PBOOT 模板端用 <h2>（6 个模板：category/courses/
   institutions/list/news/works）。只写 h4 会让模板端回落到浏览器默认 24px 粗体，两端口径不一致。
   这里同时命中 h2/h4，双端同款 14px。 */
.filter-side h2,
.filter-side h4,
.filter-side h3 { font-size: 14px; font-weight: 700; color: var(--heading); margin-bottom: var(--s-3); }
.filter-side .filter-group { margin-bottom: var(--s-5); padding-bottom: var(--s-4); border-bottom: 1px solid var(--line-1); }
.filter-side .filter-group:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.filter-side .filter-group h2,
.filter-side .filter-group h4,
.filter-side .filter-group h3 { margin-bottom: var(--s-2); }

/* 手机上筛选变成横向滚动 chip 行（不要让它挡住列表） */
.filter-tags {
  display: flex; flex-wrap: nowrap; gap: var(--s-2);
  padding: var(--s-1) 0;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
}
.filter-tags::-webkit-scrollbar { display: none; }
.filter-tags .tag,
.filter-tags .chip {
  position: relative; flex: none;
  display: inline-flex; align-items: center;
  height: 36px; padding: 0 14px;
  background: var(--surface); color: var(--text-1);
  border: 1px solid var(--line-3);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  cursor: pointer;
  scroll-snap-align: start;
  transition: border-color var(--dur-1) var(--ease-in-out), background-color var(--dur-1) var(--ease-in-out), color var(--dur-1) var(--ease-in-out);
}
.filter-tags .tag::after { content: ''; position: absolute; inset: -6px 0; }  /* F1：命中区补足 44px 高 */
.filter-tags .tag:hover { border-color: var(--primary-500); color: var(--primary-600); }
.filter-tags .tag.active {
  background: var(--primary-50); color: var(--primary-600);
  border-color: var(--primary-500); font-weight: 600;
  box-shadow: none;                        /* 去原彩色光晕 */
}

/* 筛选芯片里的小图标（14px）：单色底 + mask 描边图，与页头下拉同一种做法。
   --cat 与 --h2-ico 由上方 22 节「分类色绑定」写在 [data-cat] 上、靠继承取到；
   城区芯片则用 [data-ico="pin"] 只提供 --h2-ico（定位图）与显示开关，
   颜色落到 --cat 的回退值 --text-3（中性灰）—— 城区不是品类，不用分类色。
   🔴 必须带 .filter-side 作用域：裸「.cat-ico」的 mask 会命中首页品类宫格的
      <span class="cat-ico"><svg>…</svg></span>，把 26×26 内联 SVG 按笔画裁掉
      → 首页 6 个宫格图标整体消失（守 scripts/probe_cat_ico_scope.mjs）。 */
.filter-side .cat-ico,
.filter-side .loc-ico {
  display: var(--cat-ico-display, none);
  width: 14px; height: 14px; flex: none;
  background-color: var(--cat, var(--text-3));
  -webkit-mask: var(--h2-ico, none) center / contain no-repeat;
          mask: var(--h2-ico, none) center / contain no-repeat;
}
/* 城区芯片的「定位」图标（feather 线性、24×24 viewBox、stroke-width 1.7，
   与站内既有 6 个品类图标同风格同 encode 规则）。 */
.filter-side .filter-tags [data-ico="pin"] {
  --cat-ico-display: block;
  --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M21%2010c0%207-9%2013-9%2013s-9-6-9-13a9%209%200%200%201%2018%200z%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2210%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E");
}
/* 选中态：图标随文字由中性灰 / 品类色统一转品牌橙，
   否则「音乐」#f27e2e 这类暖色图标压在浅橙底上会糊成一片。 */
.filter-side .tag.active .cat-ico,
.filter-side .tag.active .loc-ico { background-color: var(--primary-600); }

/* 需求 77：筛选侧栏两列等宽网格（仅 ≥1200px —— 此时侧栏被固定为 240px、内宽约 190px；
   <1200px 侧栏转为整行横向滚动条，保持原 nowrap 行为不动）。
   ① 首片（全部/不限）独占整行，奇偶皆然；
   ② 片数为**偶数**时末片也独占整行（规则就是下方选择器里的 nth-child(even)）：
      偶数例 —— works「作品分类」6 片 = 整行 + 2×2 + 末片整行；
      奇数例 —— 「培训分类」7 片 = 整行 + 3×2，末片落在第 2 列。
      ⚠️ 别把 ② 记成「奇数时末片整行」：7 片 / 11 片的末片恰恰**不**整行
         （1+剩偶数片本来就铺满了），把它写反会误导下一个改这段的人。
   两条合起来**每一行都铺满**：N 偶 = 1 + 末片 + 中间偶数片；N 奇 = 1 + 剩偶数片。
   于是不再出现锯齿，并把实测的 2~3 种宽度（56/69/82px，由 padding: 0 14px + 文字宽决定）
   归一为等宽（91px / 整行 190px）。
   🔴 只在 .filter-tags--cols2 上生效：works.html「热门机构」有 15 字长名
      （罗兰数字音乐教育（望京校区）），套网格会在 91px 格里溢出，故不应用。 */
@media (min-width: 1200px) {
  .filter-side .filter-tags--cols2 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-2);
  }
  .filter-side .filter-tags--cols2 > :first-child,
  .filter-side .filter-tags--cols2 > :last-child:nth-child(even) { grid-column: 1 / -1; }
  .filter-side .filter-tags--cols2 .tag,
  .filter-side .filter-tags--cols2 .chip {
    width: 100%; justify-content: center;
    gap: 6px; padding: 0 var(--s-1);
  }
}

.list-main { min-width: 0; background: transparent; border: none; box-shadow: none; padding: 0; }

/* 排序控件（原 .list-toolbar .sort-tabs；2026-09-28 需求 78 随工具区上移到面包屑行）。
   ⚠️ 作用域同时收窄为 .crumb-tools：裸 .sort-tabs 会波及将来任何同名容器
   （铁律 20 类名作用域 —— 裸 mask 曾裁掉首页宫格图标）。此处只换宿主、不改任何值。 */
.crumb-tools .sort-tabs {
  display: flex; align-items: center; gap: var(--s-1);
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
  padding: var(--s-1) 0;
}
.crumb-tools .sort-tabs::-webkit-scrollbar { display: none; }
.crumb-tools .sort-tabs li {
  position: relative; flex: none;
  display: inline-flex; align-items: center;
  min-height: 40px; padding: 0 var(--s-4);
  font-size: var(--fs-sm); color: var(--text-2);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-in-out), color var(--dur-1) var(--ease-in-out);
}
.crumb-tools .sort-tabs li:hover { color: var(--primary-600); }
.crumb-tools .sort-tabs li.active { background: var(--primary-50); color: var(--primary-600); font-weight: 700; }

.card-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gut); }
.card-grid.cols-1 { grid-template-columns: minmax(0, 1fr); }
.card-grid.cols-2 { grid-template-columns: minmax(0, 1fr); }
.card-grid.cols-3 { grid-template-columns: minmax(0, 1fr); }
.card-grid.cols-4 { grid-template-columns: minmax(0, 1fr); }

/* ==========================================================================
   14. 详情页（去掉卡片外壳；正文宽度统一由 .detail-content 控制，见该处注释；
       移动端预约卡提到正文之前）
   ========================================================================== */
.detail-wrap { padding: var(--sec-y) 0; }
.detail-wrap .container { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
.detail-main {
  min-width: 0;
  padding: 0;
  background: transparent;                 /* 去原 padding:36px + border + shadow 三层外壳 */
  border: none;
  box-shadow: none;
}
/* 🔴 2026-09-28 需求 3a：封面比例 16/10 + (≥992px)16/8 → **4:3**。
   源图统一 3:2（机构/课程/作品/新闻/攻略 5 页实测皆 1.500）；cover 模式下 16/8=2.0
   会上下各裁 12.5% → 人物头顶被削（用户所称「旋头皮」）。4:3 改为只左右各裁 5.6%、
   **上下零裁切**，对每一页都是改善。媒体查询里那条 16/8 覆盖行已删除（它是病根）。 */
.detail-cover {
  width: 100%; aspect-ratio: 4 / 3;
  margin-bottom: var(--s-5);
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
}
.detail-cover img { width: 100%; height: 100%; object-fit: cover; }
/* 🔴 2026-09-27 需求 68（详情页补页头带）：H1 已上移到页头带 .page-banner，
   原先给正文 H1 定的那行下间距 + 字号规则随之作废删除（**此处不再复述规则原文**，
   否则「查死规则」类闸门会把它当成残留命中）。视觉不变：全局 h1 规则本就是 var(--fs-h1)。 */
.detail-main .meta-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-5);
  margin-bottom: var(--s-6); padding: var(--s-3) 0;
  border-top: 1px solid var(--line-1);
  border-bottom: 1px solid var(--line-1);
  font-size: var(--fs-sm); color: var(--text-2);
}
.detail-main .meta-bar .price {
  font-size: var(--fs-price); font-weight: 800;
  color: var(--primary-600); font-variant-numeric: tabular-nums;
}
.detail-content {
  /* 🔴 2026-09-27 需求 57：正文块不再限宽（原 --w-reading 760px），改为铺满所在
     容器 / 列宽。起因（用户截图「简介文字 没有全屏」）：.about-section 内正文恒 760
     居中，而同段 .about-grid / .standard-list 均满容器宽 → 浏览器拉宽或全屏时
     卡片跟着变宽、正文纹丝不动。
     同类位置共三处，统一由本行接管（**规则只在这里定义一处**，不再逐容器打补丁 ——
     逐容器覆盖正是「改一处漏一处」的根源）：
       · .about-section（关于我们：平台简介 / 企业愿景）→ 跟随 1200 容器
       · .detail-main  （5 个详情页）→ 跟随 800 列宽，与上方标题 / 封面 / 信息条右边界对齐
       · .sub-wrap     （帮助 / 隐私 / 协议 / 404）→ 跟随各页容器（920 / 1200）
     PBOOT 端同源（about.html / page.html / help.html / detail.html 等）一并生效，双端不分叉。
     代价：15px 字号下约 51~77 字 / 行（原 760px ≈ 50 字 / 行）。
     若要收回行宽：只把下面 none 换回 var(--w-reading) 即可（单点可回滚）。 */
  max-width: none;
  font-size: var(--fs-body); line-height: 1.75; color: var(--text-1);
}
.detail-content p { margin-bottom: 1em; }
.detail-content h3 {
  margin: var(--s-5) 0 var(--s-3);
  padding-left: var(--s-3);
  border-left: 3px solid var(--primary-500);
}
.detail-content img { max-width: 100%; height: auto; margin: var(--s-4) auto; border-radius: var(--r-md); }
.detail-content ul, .detail-content ol { margin: 0 0 1em; padding-left: 1.2em; }
.detail-content ul li { list-style: disc; margin-bottom: var(--s-1); }
.detail-content ol li { list-style: decimal; margin-bottom: var(--s-1); }
.detail-content a { color: var(--primary-600); text-decoration: underline; }
.detail-content blockquote {
  margin: var(--s-5) 0; padding: var(--s-4) var(--s-5);
  background: var(--surface-2);
  border-left: 3px solid var(--primary-500);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  color: var(--text-2);
}
.detail-content table { font-size: 14px; margin-bottom: var(--s-5); }
.detail-content th {
  padding: var(--s-3); text-align: left; font-weight: 600; color: var(--heading);
  background: var(--surface-2); border-bottom: 1px solid var(--line-2);
}
.detail-content td { padding: var(--s-3); border-bottom: 1px solid var(--line-1); }

.detail-side { display: contents; }        /* 移动：把预约卡提到正文之前 */
.detail-main { order: 0; }
.detail-side .contact-card { order: -1; }
.detail-side > *:not(.contact-card) { order: 1; }

.side-card {
  min-width: 0;
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
}
.side-card h4, .side-card h3, .side-card h2 {
  font-size: 16px; font-weight: 700; color: var(--heading);
  margin-bottom: var(--s-4); padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line-1);
}
.contact-card {
  padding: var(--s-5);
  text-align: center;
  background: var(--primary-500);           /* 亮橙实底 + 白字（2.85:1 · 已知偏离，用户决策） */
  border: none;
  border-radius: var(--r-lg);
  color: var(--on-primary);
  box-shadow: none;
}
.contact-card h4, .contact-card h3 { color: var(--on-primary); border-bottom: none; margin-bottom: var(--s-3); }
.contact-card a { color: var(--on-primary); }
.contact-card .btn { width: 100%; background: #fff; color: var(--primary-600); font-weight: 700; }
.contact-card .btn:hover { background: var(--primary-50); color: var(--primary-600); }
/* 2026-09-27：侧栏卡片由「电话行 + 1 个按钮」改为「2 个按钮」（预约 / 留言），
   两颗全宽白按钮必须留间距，否则贴成一块分不清是两个操作。
   🔴 2026-10-04：用户判定这两个按钮重复、只保留一个（保留「在线预约」，
      依据见 institution-detail.html 侧栏处的说明注释）⇒ 本卡**当前只有 1 个按钮**。
   ⇒ `.btn + .btn` 这条规则**暂无用武之地**，但**保留不删**：
      一旦恢复成两个按钮，没有它两颗白按钮会贴成一块（这正是它当初的由来）。 */
.contact-card .btn + .btn { margin-top: var(--s-3); }

/* ==========================================================================
   15. 单页 / 关于我们（取消隔行浅底，整页纯白，分节靠留白 + 1px 线）
   ========================================================================== */
.about-section { padding: var(--sec-y) 0; background: var(--surface); }
.about-section + .about-section { border-top: 1px solid var(--line-1); }
.about-section h2 { text-align: center; font-size: var(--fs-h2); letter-spacing: -0.018em; }
.about-section .sub { text-align: center; color: var(--text-2); margin-bottom: var(--s-6); }
/* 本段正文宽度不再单独限宽 —— 由第 14 节 .detail-content 单点控制（需求 57）。 */
.sub-wrap { padding: var(--sec-y) 0; background: var(--surface); min-height: 60vh; }
.sub-wrap .container { max-width: var(--w-narrow); }
.sub-wrap .detail-content h3 { margin-top: var(--s-6); }
.sub-wrap .detail-content h3:first-child { margin-top: 0; }

/* ── 机构入驻页专属（2026-09-27）──────────────────────────────────────────────
   用户截图：「机构入驻点开后，首先是让他留言，然后再介绍，宽度保持和主站一致」。
   · .sub-wrap-wide → 容器由 920（--w-narrow）放宽到主站 1200（--w-container）。
     ⚠️ 只写在 .sub-wrap-wide 上，**不改** .sub-wrap 本身 ——
        help / search / privacy / terms / 404 共 5 页仍共用 920 宽，
        直接改会让这 5 页一起变宽（回归风险）。
   · 正文宽度无需在本块覆盖（曾写 max-width:none；2026-09-27 需求 57 后
     全局 .detail-content 已默认铺满，该覆盖项已删除）。
   · .join-grid → 左列留言表单（DOM 顺序在前 ⇒ 移动端单列时表单排最上，
     满足「首先是让他留言」），右列机构介绍；≥992px 才分两栏。
   · 表单卡用**纯白 + 1px 边框**，与 .side-card / .about-card 同一套「白卡」语言
     （铁律：卡片面必须纯白 #ffffff，不得用 --band-grey 当卡片底）。 */
.sub-wrap-wide .container { max-width: var(--w-container); }

.join-grid {
  display: grid; gap: var(--gut);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
.join-form-card {
  padding: var(--s-6);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
}
.join-form-card .note { color: var(--text-2); font-size: var(--fs-sm); margin: 0 0 var(--s-4); }
.join-form-card .note:last-child { margin: var(--s-4) 0 0; }
.join-intro { min-width: 0; }
.join-intro h2:first-child { margin-top: 0; }
@media (min-width: 992px) {
  .join-grid { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
}

.about-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gut); }
.about-card {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
  padding: var(--s-6) var(--s-4);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.about-card:hover { border-color: var(--line-3); box-shadow: var(--sh-2); transform: translateY(-2px); }
.about-card .icon {
  width: 56px; height: 56px; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--primary-50);            /* 纯色，去渐变与彩色光晕 */
  border-radius: var(--r-md);
  font-size: 26px; line-height: 1;
}
.about-card h4, .about-card h3 { font-size: clamp(17px, 1.6vw, 20px); font-weight: 800; color: var(--heading); }
.about-card p { font-size: var(--fs-sm); color: var(--text-2); }

/* 时间轴 */
.timeline { position: relative; max-width: 800px; margin: 0 auto; padding: var(--s-4) 0; }
.timeline::before {
  content: ''; position: absolute; left: 9px; top: 0; bottom: 0;
  width: 2px; border-radius: 2px;
  background: var(--line-2);                /* 纯色，去渐变 */
}
.timeline li { position: relative; display: flex; align-items: center; margin-bottom: var(--s-5); }
.timeline .dot {
  position: absolute; left: 0; top: var(--s-5); transform: translateY(-50%);
  width: 18px; height: 18px; z-index: 1;
  background: var(--surface);
  border: 3px solid var(--primary-500);
  border-radius: 50%;
  box-shadow: none;                         /* 去原 5px 彩色外发光 */
}
.timeline .content {
  width: 100%; margin-left: var(--s-7);
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
  transition: border-color var(--dur-2) var(--ease-in-out), box-shadow var(--dur-2) var(--ease-out);
}
.timeline .content:hover { border-color: var(--line-3); box-shadow: var(--sh-2); }
.timeline .content .year { font-size: 15px; font-weight: 800; color: var(--primary-600); margin-bottom: var(--s-1); }

/* ==========================================================================
   16. 表单控件（控件边界 --line-field 3.78:1；focus 3px 环）
   🔴 2026-09-27：原「联系页」整段死规则（.contact-wrap / .contact-info / .contact-form / .contact-actions 等）已随 contact.html 双端删除而清理；本节仅保留仍生效的表单控件规则
   ========================================================================== */
.form-group { margin-bottom: var(--s-5); }
.form-group label { display: block; margin-bottom: var(--s-2); font-size: 14px; font-weight: 600; color: var(--heading); }

/* 🔴 2026-10-04 用户要求：「把字和框放在一行，左右结构，这样可以减少高度」。
   改前弹窗里是「标签一行、控件一行」的纵向堆叠：6 个字段把内容撑到 852px，
   而 .modal 最高 90vh（@1440×900 = 810px）⇒ 右侧出现内部滚动条
   （实测超出：1440 档 42px / 1280 档 132px / 1024 档 161px / 375 档 169px），
   @1440 连「提交预约」都被切在可视区外。5 个单行字段改成同行后各让出 32.5px，
   实测**所有断点滚动归零**，提交按钮完整可见。

   🔴 作用域为什么是 `.modal` 而不是裸 `.form-group`（全站版实测出两处回归）：
      ① `.form-group` 是**每组各自一个栅格** ⇒ `max-content` 按组内求值。
         join.html 的「机构名称 / 你的称呼」标签 140px，那一行控件左边缘跑到
         156px、另外两行在 100px —— **竖线参差**（实测，肉眼不易察觉）。
      ② search.html 搜索框的 placeholder 需 415px，375 档只剩 235px ⇒ 硬切 180px。
      弹窗全部 38 个（19 页 × 2）都带 `.modal` ⇒ 限定它一样全覆盖，且不碰那 4 处内联表单。
      ⇒ 纪律：改版式的**作用域**要贴着「有问题的容器」，不是贴着组件的类名。

   标签列取 `minmax(88px, max-content)` 的两个理由：
     · 下限 88px = 弹窗里最长标签「意向课程类别」实测自然宽 84px + 4px 余量。
       所有行都解析成下限值 ⇒ **天然对齐**（若写 max-content，375 档会一行 84、
       其余 68，同框参差）。
     · 上限仍留 max-content：万一将来加进更长的标签，是**那一行变宽**（看得见），
       而不是把字裁掉（看不见）。参差由 _probe_formgroup_align.mjs 兜底。
   `align-items: baseline` 专为「留言」那行准备 —— 它是 textarea（min-height 120px），
   用 center 会让标签浮在 120px 盒子的垂直正中、离第一行很远，看着像没对齐。
   右轨道 `minmax(0, 1fr)` 的 0 下限不可省：默认 auto 下限会被 select 的长 option
   顶开，把栅格撑出容器。 */
.modal .form-group {
  display: grid;
  grid-template-columns: minmax(88px, max-content) minmax(0, 1fr);
  align-items: baseline;
  column-gap: var(--s-4);
}
.modal .form-group > label { margin-bottom: 0; }
/* 控件变窄后，select 的提示语在 375 档会超出可用宽 —— 让浏览器补省略号，
   而不是把汉字切一半（实测被切的是「所在区域」「孩子年龄」两条长提示）。
   只对 select 开：input 的 placeholder 最长「请输入手机号」≈90px，够放；
   textarea 本来就折行，加了也无效果。 */
.modal select.form-control, .modal select.input { text-overflow: ellipsis; }
/* —— 表单按钮组：主行动 + 重置（2026-10-01 新增）——
   用于弹窗表单的「提交 × 重置」并排。两条纪律：
     ① **提交按钮保持主 CTA 尺寸**（flex:1 吃掉剩余宽度），重置自适应内容宽 ——
        两者等宽会让「重置」在视觉上与主行动等权，稀释转化；
     ② 只在加了 .form-actions 的地方生效，未包裹的单按钮表单（如 .btn-block）行为不变。 */
.form-actions { display: flex; align-items: center; gap: var(--s-3); margin-top: var(--s-2); }
.form-actions .btn { flex: none; width: auto; }   /* 🔴 2026-10-01：XS 档 `.btn-lg{width:100%}`（见 §24 的 min-width:480px 才转 auto）会让「重置」占满整行、把 .form-actions 撑出 29px 横向溢出（join.html @390/360/320）。此处按内容宽覆盖；特异性 0,2,0 > .btn-lg 的 0,1,0，无需 !important。 */
.form-actions .btn-primary { flex: 1 1 auto; min-width: 0; }
.form-actions .btn-ghost { flex: 0 0 auto; min-width: 0; }
.field { display: flex; flex-direction: column; gap: var(--s-2); margin-bottom: var(--s-5); }
.field label { font-size: 14px; font-weight: 600; color: var(--heading); }
.field .req::after { content: '*'; color: var(--danger); margin-left: 2px; }
.field .hint { font-size: var(--fs-sm); color: var(--text-2); }
.field .err { display: flex; align-items: center; gap: var(--s-1); font-size: var(--fs-sm); color: var(--danger); }

.form-control,
.input {
  display: block; width: 100%; height: 48px;   /* 手机 48px */
  padding: 0 var(--s-4);
  font-size: var(--fs-body);
  color: var(--text-1);
  background: var(--surface);
  border: var(--bw) solid var(--line-field);   /* 唯一满足 1.4.11 的控件描边 */
  border-radius: var(--r-sm);
  transition: border-color var(--dur-1) var(--ease-in-out), box-shadow var(--dur-1) var(--ease-in-out);
}
.form-control::placeholder, .input::placeholder { color: var(--text-3); }
.form-control:hover, .input:hover { border-color: var(--heading); }
.form-control:focus, .input:focus,
.form-control:focus-visible, .input:focus-visible {
  border-color: var(--primary-500);
  box-shadow: 0 0 0 3px rgba(255, 106, 61, .22);
  outline: none;
}
.form-control[aria-invalid="true"], .input[aria-invalid="true"] { border-color: var(--danger); }
.form-control[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px rgba(192, 57, 43, .18); }
.form-control[disabled], .input[disabled] { background: var(--surface-3); color: var(--text-3); cursor: not-allowed; }
textarea.form-control, textarea.input {
  height: auto; min-height: 120px;
  padding: var(--s-3) var(--s-4);
  line-height: 1.7;
  resize: vertical;
}
select.form-control, select.input { padding-right: 40px; }
/* —— 弹窗窄档（<480px）：控件左右内衬由 --s-4(16) 收到 --s-3(12) ——
   每次省 8px 全给输入框（375 档输入宽 227 → 235）。
   此处**不动标签列宽** —— 列宽已由上面的 minmax(88px, max-content) 定死，
   各行才能对齐（这条纪律见上面的注释）。
   🔴 为什么必须写在这里、而不是紧挨着上面的 .modal .form-group：
      这两条要覆盖前面的 `.form-control { padding: 0 var(--s-4) }`，
      同特异性下**靠源码顺序分胜负** —— 写在它前面会被静默压掉、改了个寂寞。 */
@media (max-width: 479px) {
  .modal .form-control, .modal .input { padding-left: var(--s-3); padding-right: var(--s-3); }
  .modal select.form-control, .modal select.input { padding-right: 36px; }
}

/* ==========================================================================
   17. 资讯两栏
   ========================================================================== */
.news-section { padding: var(--sec-y) 0; background: var(--band-grey); }
/* .news-section .container 不设网格：与 .works-strip / .age-entry 等区块一致，section-head 与 card-grid 上下堆叠 */
.news-block { min-width: 0; }
.news-block h3 {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--fs-h3); margin-bottom: var(--s-4); padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line-2);
}
.news-block h3::before { content: ''; width: 4px; height: 18px; flex: none; background: var(--primary-500); border-radius: var(--r-xs); }
.news-block .featured {
  display: flex; flex-direction: column; gap: var(--s-3);
  padding-bottom: var(--s-4); margin-bottom: var(--s-4);
  border-bottom: 1px solid var(--line-1);
  transition: color var(--dur-1) var(--ease-in-out);
}
.news-block .featured:hover h4, .news-block .featured:hover h3 { color: var(--primary-600); }
.news-block .featured .thumb {
  width: 100%; aspect-ratio: 16 / 9; flex: none;
  overflow: hidden; border-radius: var(--r-sm); background: var(--surface-2);
}
.news-block .featured .thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-3) var(--ease-out); }
.news-block .featured:hover .thumb img { transform: scale(1.03); }
.news-block .featured h4, .news-block .featured h3 { font-size: 16px; margin-bottom: var(--s-1); transition: color var(--dur-1) var(--ease-in-out); }
.news-block .featured p {
  font-size: var(--fs-sm); color: var(--text-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.news-list li {
  display: flex; align-items: center; gap: var(--s-2);
  min-height: 44px; padding: var(--s-3) 0;
  border-bottom: 1px dashed var(--line-1);
  transition: color var(--dur-1) var(--ease-in-out);
}
.news-list li:last-child { border-bottom: none; }
.news-list li:hover { color: var(--primary-600); }
.news-list li::before { content: ''; width: 5px; height: 5px; flex: none; border-radius: 50%; background: var(--primary-500); }
.news-list li .title { flex: 1 1 auto; min-width: 0; font-size: 14px; }
.news-list li .date { flex: none; font-size: var(--fs-xs); color: var(--text-2); }

/* ==========================================================================
   18. 友情链接 + 页脚（深色 --ink-900，与白色区分界明确、无渐变过渡）
   ========================================================================== */
.friend-links { padding: var(--s-6) 0; background: var(--surface); border-top: 1px solid var(--line-1); }
.friend-links h4, .friend-links h3 { font-size: 14px; font-weight: 700; color: var(--heading); margin-bottom: var(--s-3); }
.friend-links .links { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); }
.friend-links .links a { display: inline-flex; align-items: center; min-height: 44px; font-size: var(--fs-sm); color: var(--text-2); }
.friend-links .links a:hover { color: var(--primary-600); }

.footer {
  padding: var(--s-8) 0 0;
  background: var(--ink-900);
  /* 问题8 修复：原 --text-on-dark-2(#bfb9b2) 偏灰发闷，用户要求全部纯白。
     纯白 #fff on #0d2137 = 15.6:1，远超 WCAG AAA；层次改由字号/字重/间距承担。 */
  color: #fff;
}
.footer .container {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: var(--s-6);
  padding-bottom: var(--s-6);
}
.footer h5, .footer h2 { color: #fff; font-size: 15px; font-weight: 700; line-height: 1.4; margin-bottom: var(--s-3); letter-spacing: .02em; }
.footer .footer-logo {
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 44px;                                 /* F1：触控目标 */
  margin-bottom: var(--s-4); color: #fff; font-size: 19px; font-weight: 800;
}
.footer .footer-logo .logo-mark {
  width: 38px; height: 38px; flex: none;
  display: flex; align-items: center; justify-content: center;
  /* 页脚背景为深墨 --ink-900(#0d2137)。图形内无深色件，但亮橙扁椭圆贴深底后
     对比虽够、六瓣却失去「冠」的读法 → 仍用白色实底承载，形成「白底小方块 + 彩色标志」。 */
  background: #ffffff;
  border-radius: var(--r-sm);
}
.footer .footer-logo .logo-mark svg,
.footer .footer-logo .logo-mark img {
  width: 88%; height: 88%; display: block;
  object-fit: contain;          /* 宽高比 2.03，禁拉伸 */
}
.footer p { font-size: var(--fs-sm); line-height: 1.9; color: #fff; }
.footer ul li { font-size: var(--fs-sm); }
.footer ul li a {
  display: inline-flex; align-items: center;
  min-height: 44px; padding: var(--s-3) 0;    /* 触控目标 ≥44（原 13px + 5px ≈ 33px） */
  color: #fff;
  transition: color var(--dur-1) var(--ease-in-out);
}
.footer ul li a:hover { color: #fff; }
.footer .contact-row { display: flex; align-items: center; gap: var(--s-2); min-height: 44px; font-size: var(--fs-sm); color: #fff; }
.footer .contact-row .icon {
  flex: none; width: 20px;
  display: inline-flex; align-items: center; justify-content: center;
}
.footer .contact-row .icon svg { width: 17px; height: 17px; display: block; }
.footer .contact-row a {
  /* F1 修复：原为行内文字（实测 18px 高），提升为 44px 触控目标。
     与同行的 .icon 共用 flex 基线，不会撑乱行高。 */
  display: inline-flex; align-items: center; min-height: 44px;
  color: #fff;
}
.footer .contact-row a:hover { color: #fff; }
.footer .qrcode { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-top: var(--s-3); }
.footer .qrcode .qr {
  width: 84px; height: 84px;
  display: flex; align-items: center; justify-content: center;
  padding: var(--s-1);
  background: #fff; color: var(--text-2);
  font-size: 11px; text-align: center;
  border-radius: var(--r-sm);
}
.footer .copyright {
  padding: var(--s-4) 0;
  border-top: 1px solid var(--line-on-dark);
  text-align: center;
  font-size: var(--fs-xs);
  color: #fff;                     /* 问题8：随页脚统一为纯白 */
}
.footer .copyright .container { display: block; padding: 0; }

/* ==========================================================================
   19. 悬浮工具（只用阴影；移动端避开吸底 CTA 栏）
   ========================================================================== */
.floating {
  position: fixed; right: var(--s-4); z-index: 99;
  bottom: calc(var(--sticky-cta-h) + var(--s-4) + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: var(--s-3);
}
.floating .fab {
  position: relative;
  width: 48px; height: 48px;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface);
  border: none;                              /* 悬浮球：只用阴影 */
  border-radius: 50%;
  box-shadow: var(--sh-3);
  color: var(--heading); font-size: 20px;
  transition: background-color var(--dur-2) var(--ease-in-out),
              color var(--dur-2) var(--ease-in-out),
              transform var(--dur-2) var(--ease-out);
}
.floating .fab svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.floating .fab:hover { background: var(--primary-550); color: var(--on-primary); transform: translateY(-2px); }
.floating .fab.primary { background: var(--primary-500); color: var(--on-primary); }
.floating .fab.primary:hover { background: var(--primary-550); }
.floating .fab .badge {
  position: absolute; top: -4px; right: -4px;
  width: 18px; height: 18px; min-height: 18px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--danger); color: #fff;
  border-radius: 50%; font-size: 10px; box-shadow: none;
}
.floating .fab[data-action="top"] { opacity: .4; transition: opacity var(--dur-2) var(--ease-in-out), background-color var(--dur-2) var(--ease-in-out), color var(--dur-2) var(--ease-in-out); }
.floating .fab[data-action="top"].is-visible { opacity: 1; }

/* ==========================================================================
   20. 模态与底部抽屉（≤767 默认底部抽屉；.modal--drawer 强制）
   ========================================================================== */
.modal-mask {
  position: fixed; inset: 0; z-index: 999;
  display: none;
  align-items: flex-end; justify-content: center;
  padding: 0;
  background: var(--sh-scrim);              /* 遮罩：允许毛玻璃的第 2 处 */
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.modal-mask.show { display: flex; animation: maskIn var(--dur-2) var(--ease-in-out); }
@keyframes maskIn { from { opacity: 0; } to { opacity: 1; } }

.modal {
  width: 100%; max-width: 100%;
  max-height: 88vh;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--surface);
  border: none;
  /* 🔴 2026-10-01 四角全圆（原 `var(--r-lg) var(--r-lg) 0 0` 底部两角是直角）。
     用户截图（宽 756px，落在 <768 断点 ⇒ .modal-mask align-items:flex-end 贴底抽屉）
     看到的是「上圆下直」。既然要求四角都圆，就与桌面端统一为一个值 ——
     桌面端媒体查询里原本就写 `border-radius: var(--r-lg)`（四角），
     两处不一致正是「只有移动端底部没圆角」的成因。 */
  border-radius: var(--r-lg);
  box-shadow: var(--sh-4);
  animation: sheetIn var(--dur-3) var(--ease-out);
}
.modal::before {                            /* 拖拽指示条 */
  content: ''; display: block;
  width: 36px; height: 4px;
  margin: var(--s-2) auto 0;
  border-radius: var(--r-pill);
  background: var(--line-3);
}
@keyframes sheetIn { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }

.modal-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--line-1);
}
.modal-header h3, .modal-header h2 { font-size: 18px; line-height: 1.3; letter-spacing: -0.01em; }
.modal-close {
  position: relative; flex: none;
  width: 44px; height: 44px;                  /* 44×44 触控目标（原 32） */
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border: none;
  border-radius: 50%;
  color: var(--text-2); font-size: 22px; line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-in-out), color var(--dur-1) var(--ease-in-out);
}
.modal-close:hover { background: var(--primary-50); color: var(--primary-600); }
.modal-body { padding: var(--s-5); }
.modal-footer {
  display: flex; justify-content: flex-end; gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--line-1);
}

/* ==========================================================================
   21. 选课指南
   ========================================================================== */
/* 问题6 修复（第二次 · 2026-09-26）：上一轮把本区块留给 --navy-50(#f2f7fb)，
   而它与相邻上方的 .region-find --surface-2(#f6f7f9) 色差仅 ΔE 1.72 ——
   实测为全页最低，肉眼完全同色。本次改用区块级浅灰档 --band-grey，
   与上方 --band-cool 的 region-find 拉开到 ΔE 10.10，与下方纯白 howto-steps 差 7.57。
   ⚠️ 以上为当时（第二次修复）的结论，已被下方 v4.4 注释取代 ——
      现况：本区块 `var(--surface)` 纯白，上邻 .region-find 已是 --ink-900 深墨蓝。 */
/* v4.4（2026-09-26）：本区块由浅灰改为纯白 —— 因为上邻 .region-find 已改为深墨蓝
   （深 → 白 → 灰 交替），保持「白 ↔ 灰」严格交替。 */
.guide-strip { padding: var(--sec-y) 0; background: var(--surface); }
.guide-cards { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gut); }
.guide-card {
  display: flex; flex-direction: row; align-items: center; gap: var(--s-4);
  min-width: 0;
  padding: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.guide-card:hover { border-color: var(--line-3); box-shadow: var(--sh-2); transform: translateY(-2px); }
.guide-card .g-thumb {
  width: 96px; height: 86px; flex: none;
  overflow: hidden; border-radius: var(--r-sm); background: var(--surface-2);
}
.guide-card .g-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-3) var(--ease-out); }
.guide-card:hover .g-thumb img { transform: scale(1.03); }
.guide-card .g-info { min-width: 0; display: flex; flex-direction: column; gap: var(--s-1); }
.guide-card .g-info h4, .guide-card .g-info h3 {
  font-size: 15px; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.guide-card .g-info .g-meta { font-size: var(--fs-xs); color: var(--text-2); }
.guide-card .g-info .g-desc {
  font-size: var(--fs-sm); color: var(--text-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.guide-wrap { padding: var(--sec-y) 0; }
.guide-wrap .container { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
.guide-list {
  display: flex; flex-direction: column; gap: var(--s-4);
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
}
.guide-faq { margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px solid var(--line-1); }
.guide-faq summary {
  display: flex; align-items: baseline; gap: var(--s-2);
  min-height: 44px; padding: var(--s-3) 0;
  font-size: 14px; font-weight: 600; color: var(--heading);
  cursor: pointer; list-style: none;
}
.guide-faq summary::-webkit-details-marker { display: none; }
.guide-faq summary::before {
  content: 'Q'; flex: none;
  width: 20px; height: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--primary-50); color: var(--primary-600);
  border-radius: 50%; font-size: var(--fs-xs); font-weight: 800;
}
.guide-faq p { padding: 0 0 var(--s-3) 28px; font-size: 14px; line-height: 1.8; color: var(--text-1); }
.guide-cta {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-5);
  background: var(--primary-500);             /* 亮橙实底 + 白字（2.85:1 · 已知偏离，用户决策） */
  border-radius: var(--r-lg);
  color: var(--on-primary);
  box-shadow: none;
}
.guide-cta h3, .guide-cta h2 { color: var(--on-primary); font-size: var(--fs-h3); margin-bottom: var(--s-1); }
/* 次要文字随主色转白。⚠️ 必须用**纯白**而非半透明白：
   92% 白合成到橙底后只有 2.62:1（实测为全站橙底最低值），
   而纯白反而是 2.85:1 —— 半透明在这里只会让最弱的一处更弱。
   标题/正文的层级改由字号区分（h3 约 20px vs p 13px），不再依赖透明度。 */
.guide-cta p { font-size: var(--fs-sm); color: #fff; }
.guide-cta .btn { background: #fff; color: var(--primary-600); font-weight: 700; }
.guide-cta .btn:hover { background: var(--primary-50); color: var(--primary-600); }

/* ==========================================================================
   22. 分类色绑定（属性选择器，替代 HTML 内联样式：同时支持英文键与中文键）
   --------------------------------------------------------------------------
   🔴 2026-09-26 新增 `[data-cat*="中文"]`（**子串**匹配），用途只有一个：
     让 **PBOOT 端**的导航下拉也能上色 —— PBOOT 的 `{pboot:2nav}` 只能输出
     栏目名（如「美术培训」），拿不到静态端的英文键（`art`）。写法：
         <a href="[2nav:link]" data-cat="[2nav:name]"><i class="cat-ico"></i>…
     栏目名含「美术」→ 命中 art 色；含「音乐」→ 命中 music 色，以此类推。
     ⚠️ 因此后台**栏目名必须含品类词**（音乐/美术/舞蹈/书法/表演/其他），
        否则该下拉项退化为默认灰 `--text-3`（不报错，只是不上色）。
     ⚠️ 6 个中文键互为**非子串**（音乐/美术/舞蹈/书法/表演/其他），不会互抢；
        仅「音乐剧表演」这类同时含两词的极端栏目名会出现后者覆盖前者的歧义。
     ⚠️ `data-block` 仍为**精确**匹配，不受本次改动影响。
   ========================================================================== */
.cat-art, .theme-art,
[data-cat="art"], [data-cat="美术"], [data-cat*="美术"], [data-block="art"] { --cat: var(--cat-art); --cat-ink: var(--cat-art-ink); --cat-soft: var(--cat-art-soft); --cat-link: var(--cat-art-deep); --cat-ico-display: block; --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M17%203a2.85%202.83%200%201%201%204%204L7.5%2020.5%202%2022l1.5-5.5Z%22%2F%3E%3C%2Fsvg%3E"); }
.cat-music, .theme-music,
[data-cat="music"], [data-cat="音乐"], [data-cat*="音乐"], [data-block="music"] { --cat: var(--cat-music); --cat-ink: var(--cat-music-ink); --cat-soft: var(--cat-music-soft); --cat-link: var(--cat-music-deep); --cat-ico-display: block; --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M9%2018V5l12-2v13%22%2F%3E%3Ccircle%20cx%3D%226%22%20cy%3D%2218%22%20r%3D%223%22%2F%3E%3Ccircle%20cx%3D%2218%22%20cy%3D%2216%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E"); }
.cat-dance, .theme-dance,
[data-cat="dance"], [data-cat="舞蹈"], [data-cat*="舞蹈"], [data-block="dance"] { --cat: var(--cat-dance); --cat-ink: var(--cat-dance-ink); --cat-soft: var(--cat-dance-soft); --cat-link: var(--cat-dance-deep); --cat-ico-display: block; --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%224%22%20r%3D%222%22%2F%3E%3Cpath%20d%3D%22M12%206v5%22%2F%3E%3Cpath%20d%3D%22M12%207l-5%204%201%204%22%2F%3E%3Cpath%20d%3D%22M12%207l5%204-1%204%22%2F%3E%3Cpath%20d%3D%22M12%2011l-3%209%22%2F%3E%3Cpath%20d%3D%22M12%2011l3%209%22%2F%3E%3C%2Fsvg%3E"); }
.cat-calligraphy, .theme-calligraphy,
[data-cat="calligraphy"], [data-cat="书法"], [data-cat*="书法"], [data-block="calligraphy"] { --cat: var(--cat-calligraphy); --cat-ink: var(--cat-calligraphy-ink); --cat-soft: var(--cat-calligraphy-soft); --cat-link: var(--cat-calligraphy-deep); --cat-ico-display: block; --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%2019l7-7%203%203-7%207-3-3z%22%2F%3E%3Cpath%20d%3D%22M18%2013l-1.5-7.5L2%202l3.5%2014.5L13%2018l5-5z%22%2F%3E%3C%2Fsvg%3E"); }
.cat-acting, .theme-acting,
[data-cat="acting"], [data-cat="表演"], [data-cat*="表演"], [data-block="acting"] { --cat: var(--cat-acting); --cat-ink: var(--cat-acting-ink); --cat-soft: var(--cat-acting-soft); --cat-link: var(--cat-acting-deep); --cat-ico-display: block; --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%202a3%203%200%200%200-3%203v7a3%203%200%200%200%206%200V5a3%203%200%200%200-3-3Z%22%2F%3E%3Cpath%20d%3D%22M19%2010v2a7%207%200%200%201-14%200v-2%22%2F%3E%3Cpath%20d%3D%22M12%2019v3%22%2F%3E%3C%2Fsvg%3E"); }
.cat-other, .theme-other,
[data-cat="other"], [data-cat="其他"], [data-cat*="其他"], [data-block="other"] { --cat: var(--cat-other); --cat-ink: var(--cat-other-ink); --cat-soft: var(--cat-other-soft); --cat-link: var(--cat-other-deep); --cat-ico-display: block; --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22m12%203-1.9%205.8a2%202%200%200%201-1.3%201.3L3%2012l5.8%201.9a2%202%200%200%201%201.3%201.3L12%2021l1.9-5.8a2%202%200%200%201%201.3-1.3L21%2012l-5.8-1.9a2%202%200%200%201-1.3-1.3Z%22%2F%3E%3C%2Fsvg%3E"); }

/* 序号兜底（2026-09-25 补）：
   PBOOT 端的 data-block 取自后台栏目编码 {pboot:2nav:scode}，若运营未把 6 个栏目编码
   设为 art/music/dance/calligraphy/acting/other，上方属性选择器会全部落空 →
   --cat-soft 回退成 --surface-2，6 个分类大区一起变浅灰、"一栏目一色"整体失效。
   此处按 DOM 顺序补兜底：:where() 把特异性压到 (0,0,0)，低于 [data-block="..."] (0,1,0)，
   故仅在编码不匹配时生效，不会覆盖按栏目语义的着色（顺序打乱时仍以语义为准）。 */
:where(.cat-blocks > .container > .cat-block:nth-child(1)) { --cat: var(--cat-music); --cat-ink: var(--cat-music-ink); --cat-soft: var(--cat-music-soft); --cat-link: var(--cat-music-deep); }
:where(.cat-blocks > .container > .cat-block:nth-child(2)) { --cat: var(--cat-art); --cat-ink: var(--cat-art-ink); --cat-soft: var(--cat-art-soft); --cat-link: var(--cat-art-deep); }
:where(.cat-blocks > .container > .cat-block:nth-child(3)) { --cat: var(--cat-dance); --cat-ink: var(--cat-dance-ink); --cat-soft: var(--cat-dance-soft); --cat-link: var(--cat-dance-deep); }
:where(.cat-blocks > .container > .cat-block:nth-child(4)) { --cat: var(--cat-calligraphy); --cat-ink: var(--cat-calligraphy-ink); --cat-soft: var(--cat-calligraphy-soft); --cat-link: var(--cat-calligraphy-deep); }
:where(.cat-blocks > .container > .cat-block:nth-child(5)) { --cat: var(--cat-acting); --cat-ink: var(--cat-acting-ink); --cat-soft: var(--cat-acting-soft); --cat-link: var(--cat-acting-deep); }
:where(.cat-blocks > .container > .cat-block:nth-child(6)) { --cat: var(--cat-other); --cat-ink: var(--cat-other-ink); --cat-soft: var(--cat-other-soft); --cat-link: var(--cat-other-deep); }
:where(.cat-blocks > .container > .cat-block:nth-child(1)) { --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M9%2018V5l12-2v13%22%2F%3E%3Ccircle%20cx%3D%226%22%20cy%3D%2218%22%20r%3D%223%22%2F%3E%3Ccircle%20cx%3D%2218%22%20cy%3D%2216%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E"); }
:where(.cat-blocks > .container > .cat-block:nth-child(2)) { --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M17%203a2.85%202.83%200%201%201%204%204L7.5%2020.5%202%2022l1.5-5.5Z%22%2F%3E%3C%2Fsvg%3E"); }
:where(.cat-blocks > .container > .cat-block:nth-child(3)) { --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%224%22%20r%3D%222%22%2F%3E%3Cpath%20d%3D%22M12%206v5%22%2F%3E%3Cpath%20d%3D%22M12%207l-5%204%201%204%22%2F%3E%3Cpath%20d%3D%22M12%207l5%204-1%204%22%2F%3E%3Cpath%20d%3D%22M12%2011l-3%209%22%2F%3E%3Cpath%20d%3D%22M12%2011l3%209%22%2F%3E%3C%2Fsvg%3E"); }
:where(.cat-blocks > .container > .cat-block:nth-child(4)) { --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%2019l7-7%203%203-7%207-3-3z%22%2F%3E%3Cpath%20d%3D%22M18%2013l-1.5-7.5L2%202l3.5%2014.5L13%2018l5-5z%22%2F%3E%3C%2Fsvg%3E"); }
:where(.cat-blocks > .container > .cat-block:nth-child(5)) { --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%202a3%203%200%200%200-3%203v7a3%203%200%200%200%206%200V5a3%203%200%200%200-3-3Z%22%2F%3E%3Cpath%20d%3D%22M19%2010v2a7%207%200%200%201-14%200v-2%22%2F%3E%3Cpath%20d%3D%22M12%2019v3%22%2F%3E%3C%2Fsvg%3E"); }
:where(.cat-blocks > .container > .cat-block:nth-child(6)) { --h2-ico: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22m12%203-1.9%205.8a2%202%200%200%201-1.3%201.3L3%2012l5.8%201.9a2%202%200%200%201%201.3%201.3L12%2021l1.9-5.8a2%202%200%200%201%201.3-1.3L21%2012l-5.8-1.9a2%202%200%200%201-1.3-1.3Z%22%2F%3E%3C%2Fsvg%3E"); }

/* 分类图标上色：**已删除**原 12 条 [data-cat="…"] .cat-dot 规则。
   原因：新结构为 <a data-cat="music"><i class="cat-ico"></i>…</a>，
   [data-cat] 上已由上方「分类色绑定」写入了 --cat 与 --h2-ico，
   图标作为子元素直接继承即可 —— 保留那 12 条会变成永远不匹配的死规则。 */

.cat-link { color: var(--cat-ink, var(--primary-600)); }

/* 分类色 12% 浅底（现代浏览器；不支持时回落 --surface-2 纯灰） */
@supports (background: color-mix(in srgb, red 12%, #fff)) {
  .cat-nav-item .cat-nav-icon,
  .cat-nav-item .icon { background: color-mix(in srgb, var(--cat, var(--primary-500)) 12%, #fff); }
}

/* ==========================================================================
   23. 首页改版新增区块（PBOOT 模板 index.html / header.html / footer.html）
       年龄段入口 / 严选机构 / 品类宫格 / 分类大区 A·B / 按区找机构 /
       怎么用三步 / 安心承诺 / 学员作品 / 底部转化带 / 城市站入口 /
       页脚承诺行 / 弹窗副标题与备用入口
       说明：与旧版区块类名（category-block / cat-nav-item / trust-item …）
             并存——两端模板共用同一份样式，两套类名都必须生效
   ========================================================================== */

/* —— 23.1 区块统一标题组 —— */
.section-head { margin-bottom: var(--s-7); }
.section-head .eyebrow,
.eyebrow {
  /* 问题9：由 inline-block 改 inline-flex，使标签内图标与文字垂直居中对齐 */
  display: inline-flex; align-items: center; gap: 5px;
  margin-bottom: var(--s-3);
  padding: 4px var(--s-3) 4px calc(var(--s-3) - 1px);
  background: var(--primary-50);
  color: var(--primary-600);
  border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: 700;
  letter-spacing: .06em; line-height: 1.6;
}
.eyebrow-ico { display: inline-flex; flex: none; }
.eyebrow-ico svg { width: 13px; height: 13px; display: block; }
.section-head h2 { margin-bottom: var(--s-3); }
.section-head .lead,
.lead {
  max-width: 46em;
  font-size: var(--fs-lead); line-height: 1.75;
  color: var(--text-2);
}
.section-head .btn-link { margin-top: var(--s-3); }

/* —— 23.1b 带底区块上的文字色阶提升（2026-09-26）——
   背景：区块底色自纯白改为「区块级浅蓝/浅灰档」（--band-cool / --band-grey）后，
   原先按白底标定的两个颜色在带底上跌破 WCAG AA：
     · --text-2    #666b73  白底 5.36:1 → 浅蓝底 3.89:1  ✗
     · --primary-600 #c9420f 白底 4.92:1 → 浅蓝底 3.57 / 浅灰底 4.12:1  ✗
   处理：在「带底区块」作用域内做局部提升，白底区块完全不受影响。
     · 次要正文 → --text-1（浅蓝底 8.69:1）
     · 橙色小字与链接 → --primary-700（浅蓝底 4.74:1 / 浅灰底 5.47:1）
   注意：.eyebrow 自带不透明药丸底（--primary-50），文字压在自身底上（4.55:1），
   故**不在**本组调整范围内。
   v4.5（2026-09-26）修补两处漏网（铁律 9：改结构须同步清理旧配套覆盖）：
     a) `.region-find` 已于 v4.4 改为深墨蓝底，本组「浅底提升」对它不再适用；
        且本组 `:is(...) .section-head .lead` 权重 (0,3,0)、`.region-find .region-note`
        权重 (0,2,0)，会把 23.9 处的深底规则（(0,3,0) 平手靠后胜 / (0,1,0) 负）
        静默压回 --text-1 → #33373d on #0d2137 = 1.36:1 ✗。故将 .region-find 移出本组。
     b) `.howto-steps` 于 v4.4 改为 --band-grey 底却漏入本组 →
        `.section-head .lead` 仍是 --text-2 #666b73 = 4.49:1（差 0.01）✗。故补入本组。
   复核命令：node scripts/probe_band_text.mjs index.html */
:is(.age-entry, .cat-nav, .guide-strip, .news-section, .howto-steps) .section-head .lead,
:is(.age-entry, .cat-nav, .guide-strip, .news-section, .howto-steps) .section-head .section-note {
  color: var(--text-1);
}
:is(.age-entry, .cat-nav, .guide-strip, .news-section, .howto-steps) .section-head .btn-link,
:is(.age-entry, .cat-nav, .guide-strip, .news-section, .howto-steps) .section-head .btn-link:hover {
  color: var(--primary-700);
}

/* —— 23.2 首屏三层背景（H1「2026-08-29 最早原版」版式 + 遮罩修正）——
   H1 把渐变写进每张 slide 的行内 style，这里改为类名承载，保持「结构 / 样式分离」，
   渲染观感与 H1 一致（左深 → 右浅，右侧露出照片），只是换了个挂载位置。
   遮罩存成 .hero 上的自定义属性，供下面 3 个 .slide-N 复用；按断点分三套（见下）。
   尺寸/位置/重复由 .hero-slide 的 background-size:cover 等统一控制，对两层同时生效。
   路径用 ../images/ ：静态端 html/css/ 与 pboot 端 template/css/ 的上层
   分别是各自的 images/，同一份 CSS 两端都能解析，无须分叉。

   ⚠️ 遮罩深度是实测反解出来的，不是拍脑袋，也不是 H1 原值（.55 → 60% 处 .15）。
   原因：H1 那组值隐含假设「照片左侧是暗部」；而本项目 banner1/2/3 是高调亮图
   （画室白墙 / 琴房白窗 / 芭蕾白练功房），实测文字区内最亮通道 c ≈ 0.92~1.00，
   几乎处处近白。α 只给 .15~.35 时白字对比度实测仅 1.4~2.9:1（AA 要求 4.5:1），
   **属于 H1 原版自带缺陷**，还原时必须一并修正。
   反解依据：白字 L=1，要 ≥4.5:1 需背景 L ≤ 0.1833 → 通道 c ≤ 0.4652
            → 需求 α ≥ 1 − 0.4652 / c。c=1.0 时 α ≥ 0.535。
   求解脚本：scripts/shot_hero_nomask.mjs + scripts/solve_hero_mask.py（剥遮罩取纯照片 → 按 x 分桶反解）
   验收脚本：scripts/shot_hero_bg.mjs + scripts/verify_hero_contrast.py（字形邻域口径）

   三档按「文字实际覆盖宽度」收放，而非一刀切压黑：
     移动 <768   文字到 92% 容器宽（h1 折行后几乎满宽）→ 近似全幅遮罩；
     ≥768        文字到 76% → 76% 后放开；
     ≥1200       文字到 59% → 59% 后放开至 .10，右侧仍看得清照片。
   α 起点 0.58 以上（对 c=1.0 给出 ≥5.3:1，留约 18% 余量），左侧更深以维持 H1 的方向感。 */
.hero {
  --hero-scrim: linear-gradient(90deg,
    rgba(0, 0, 0, .68)   0%,
    rgba(0, 0, 0, .62)  50%,
    rgba(0, 0, 0, .58) 100%);
}
@media (min-width: 768px) {
  .hero {
    --hero-scrim: linear-gradient(90deg,
      rgba(0, 0, 0, .70)   0%,
      rgba(0, 0, 0, .64)  50%,
      rgba(0, 0, 0, .60)  76%,
      rgba(0, 0, 0, .30)  90%,
      rgba(0, 0, 0, .12) 100%);
  }
}
@media (min-width: 1200px) {
  .hero {
    --hero-scrim: linear-gradient(90deg,
      rgba(0, 0, 0, .70)   0%,
      rgba(0, 0, 0, .62)  55%,
      rgba(0, 0, 0, .48)  68%,
      rgba(0, 0, 0, .16)  86%,
      rgba(0, 0, 0, .10) 100%);
  }
}
/* ── 高清（2x）支持 · 2026-09-26 ──────────────────────────────────────
   背景：源图 1920×600，而首屏容器恒为 480px 定高、背景是 cover →
   绑定约束是**高度**：DPR=2 时需 960 物理像素高，源图只有 600 →
   1440 视口放大 1.60x、1920 视口放大 2.00x，会发糊。
   已核实全项目 27 个历史副本都是 1920×600，**没有更大的原图**；
   而 mee3 的图像模型横版上限 1536×1024（比现有 1920 还窄），且只支持
   1:1/2:3/3:2 —— 用生成的方式只会更差。故走经典超分（零额度、零 AI 痕迹）：
   scripts/upscale_banner.py 产出 3840×1200 的 bannerN@2x.jpg。

   为什么这样写是安全的：image-set() 不被支持的浏览器会**整条声明解析失败**
   而被丢弃 → 自动回退到上一条「只有 1x」的 url() 版本。1x 设备也走 1x 图，
   不白白多下 500KB（banner 是 LCP 元素，体积要紧）。
   ⚠️ 顺序不能反：回退声明必须在前面。
   复核：node scripts/verify_hero_image.mjs（会断言 2x 变体存在且可解析） */
.slide-1 { background-image: var(--hero-scrim), url("../images/banner1.jpg"); }
.slide-2.bg-ready { background-image: var(--hero-scrim), url("../images/banner2.jpg"); }
.slide-3.bg-ready { background-image: var(--hero-scrim), url("../images/banner3.jpg"); }
.slide-1 {
  background-image: var(--hero-scrim), -webkit-image-set(
    url("../images/banner1.jpg") 1x, url("../images/banner1@2x.jpg") 2x);
  background-image: var(--hero-scrim), image-set(
    url("../images/banner1.jpg") 1x, url("../images/banner1@2x.jpg") 2x);
}
/* 🔴 2026-10-24：第 2/3 屏背景改为「懒加载」。
   原来三张 banner 在首屏就全部下载（3×~160KB），但首屏只显示第 1 屏。
   现在只有 .bg-ready 才挂背景；main.js 在 load 之后才加这个类。
   视觉无差异：没加类时只显示深色遮罩层，切到该屏时图已缓存。
   ⚠️ 判据 verify_hero_image.mjs 仍能读到 banner2@2x/banner3@2x 的 image-set。 */
.slide-2.bg-ready {
  background-image: var(--hero-scrim), -webkit-image-set(
    url("../images/banner2.jpg") 1x, url("../images/banner2@2x.jpg") 2x);
  background-image: var(--hero-scrim), image-set(
    url("../images/banner2.jpg") 1x, url("../images/banner2@2x.jpg") 2x);
}
.slide-3.bg-ready {
  background-image: var(--hero-scrim), -webkit-image-set(
    url("../images/banner3.jpg") 1x, url("../images/banner3@2x.jpg") 2x);
  background-image: var(--hero-scrim), image-set(
    url("../images/banner3.jpg") 1x, url("../images/banner3@2x.jpg") 2x);
}

/* —— 23.3 信任条文字标签 —— */
.trust-item .label,
.trust-label { font-size: var(--fs-sm); line-height: 1.6; color: var(--text-2); }

/* —— 23.4 年龄段决策入口 —— */
.age-entry {
  padding: var(--sec-y) 0;
  /* v4.4（2026-09-26）：浅蓝 --band-cool 整条退役 —— 用户明确要求
     「把浅蓝删除掉，重新排一个底色」。本区块改挂区块级浅灰档。
     节奏核对：上邻 .trust-bar 纯白 → 本区块灰 → 下邻 .inst-featured 纯白，
     两侧 ΔE 均 7.57。 */
  background: var(--band-grey);
}
.age-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gut); }
.age-card {
  display: flex; flex-direction: column; gap: var(--s-2);
  min-height: 44px;
  padding: var(--card-pad) var(--card-pad) var(--s-5);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;                        /* 静止态只用 1px 描边 */
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.age-card:hover { border-color: var(--primary-300); box-shadow: var(--sh-2); transform: translateY(-2px); }
.age-range {
  align-self: flex-start;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 26px; padding: 0 var(--s-3);
  background: var(--primary-50); color: var(--primary-600);
  border-radius: var(--r-pill);
  font-family: var(--font-num);
  font-size: var(--fs-xs); font-weight: 800; letter-spacing: .02em;
}
.age-card h3 { font-size: var(--fs-h3); }
.age-card p { font-size: var(--fs-body); line-height: 1.75; color: var(--text-2); }
.age-cats { font-size: var(--fs-sm); line-height: 1.7; color: var(--text-2); }
.age-more {
  margin-top: auto; padding-top: var(--s-3);
  font-size: var(--fs-sm); font-weight: 600; color: var(--primary-600);
}
.age-card:hover .age-more { color: var(--primary-700); text-decoration: underline; }

/* —— 23.5 严选机构精选（纯白底：层次靠 1px 分隔线与纵向留白，不靠底色） —— */
.inst-featured {
  padding: var(--sec-y) 0;
  background: var(--surface);
}

/* —— 23.6 品类宫格（新版结构：.container > .section-head + .cat-grid > .cat-tile） —— */
/* 2026-09-26 用户反馈「直接上来就是图标，没有标题，很突兀」→ 补区块标题栏。
   .cat-nav .container 是 3 列栅格（旧 .cat-nav-item 结构的遗留），
   故标题组与宫格都必须显式跨满，否则各自只占 1 格。 */
.cat-nav .container > .section-head { grid-column: 1 / -1; }
.cat-nav .container > .cat-grid {
  grid-column: 1 / -1;                     /* 宫格整体跨满容器栅格 */
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-3);
}
.cat-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-2);
  min-height: 44px; padding: var(--s-3) var(--s-2);
  border-radius: var(--r-md);
  font-size: var(--fs-sm); font-weight: 600; color: var(--heading);
  text-align: center;
  /* 🔴 2026-10-03 需求 E·项6：用户「@图5 这个地方的背景色块，改成纯白色」。
     原 --cat-soft 六色浅底 → 纯白 var(--surface)；卡片轮廓改由 1px 边框承载。
     分类色仍在三处：① .cat-ico 图标块实底(--cat-ink) ② hover 态 ③ 图标描边。 */
  background: var(--surface);
  border: 1px solid var(--line-2);
  transition: background-color var(--dur-1) var(--ease-in-out), transform var(--dur-2) var(--ease-out),
              color var(--dur-1) var(--ease-in-out),
              border-color var(--dur-1) var(--ease-in-out);
}
.cat-tile:hover { background: var(--cat-ink, var(--ink-800)); border-color: var(--cat-ink, var(--ink-800)); color: #fff; transform: translateY(-2px); }
.cat-tile .cat-ico {
  width: 64px; height: 64px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-lg);
  /* v4：分类色实底 + 白描边图标（--cat-ink 均 ≥4.8:1，满足图形 3:1 与文字 4.5:1） */
  background: var(--cat-ink, var(--ink-800));
  color: #fff;
}
.cat-tile:hover .cat-ico { background: rgba(255, 255, 255, .20); color: #fff; }
.cat-tile .cat-ico svg { width: 26px; height: 26px; }
.cat-tile .cat-name { font-size: var(--fs-sm); font-weight: 600; color: inherit; }

/* —— 23.7 分类大区（新版结构：.cat-block > .cat-panel + .cat-body） —— */
/* 布局说明：.cat-block 的直接子元素是 .cat-panel + .cat-body，故网格直接落在
   .cat-block 上。历史上写成 .cat-block .container（该元素不存在），导致 A 版两栏
   自改版起从未生效；2026-09-25 修复（见下方 .cat-block 规则内注释）。 */
.cat-blocks { padding: 0; background: var(--surface); }
/* 问题5 修复：分类大区底色必须铺满整个视口宽度。
   原结构 .cat-blocks > .container(max-width:1200px) > .cat-block，
   导致 soft 底色只铺到 1200px，宽屏右侧露白，观感像"内容块"而非"栏目带"。
   做法：容器解除宽度限制、去掉自身内边距；.cat-block 用 padding 把内容
   重新约束回 1200px 居中（max() 保证窄屏退回 pad-x），背景因此铺满。 */
.cat-blocks > .container {
  max-width: none;
  padding-left: 0; padding-right: 0;
}
.cat-block {
  /* 网格落在 .cat-block 本身：其直接子元素就是 .cat-panel + .cat-body。
     原写法 .cat-block .container 引用的元素不存在 → A 版两栏自改版起从未生效。 */
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: var(--s-5);
  padding-top: calc(var(--sec-y) * .7);
  padding-bottom: calc(var(--sec-y) * .7);
  /* 内容居中于 --w-container；视口比容器窄时自动退回 pad-x */
  padding-left: max(var(--pad-x), calc((100% - var(--w-container)) / 2 + var(--pad-x)));
  padding-right: max(var(--pad-x), calc((100% - var(--w-container)) / 2 + var(--pad-x)));
  background: var(--surface);              /* 奇数档：纯白 */
}
/* 隔行变色（2026-09-26 用户指定）：原先 6 个大区各用一个分类色浅底（--cat-*-soft），
   形成"彩虹条纹"。改为白 / 灰隔行：
     · 灰档取区块级 --band-grey(#e8ebf0)，与白 ΔE 7.0 —— 可辨且不重；
       不用 --surface-2(#f6f7f9)，它与白只差 ΔE 3.1，肉眼几乎看不出"隔行"。
     · 分类色不再当区块底，改由左侧 .cat-panel 深色块承载（--cat-ink 六色仍在用），
       「一栏目一色」的语义没有丢，只是从"大面积浅底"收进了"栏目色标"。
   相邻关系核对：.cat-nav(--band-grey) → block1(白) → block2(灰) → … 首尾均不同色。 */
.cat-block:nth-child(even) { background: var(--band-grey); }
/* 问题9：分类大区标题前加品类图标（用 currentColor，深底版块自动反白） */
.cat-panel h2 { display: flex; align-items: center; gap: var(--s-2); }
.h2-ico { display: inline-flex; flex: none; }
.h2-ico::before {
  content: '';
  display: block; width: 26px; height: 26px;
  background: currentColor;               /* 深底版块自动反白 */
  -webkit-mask: var(--h2-ico, none) center / contain no-repeat;
          mask: var(--h2-ico, none) center / contain no-repeat;
}
/* 隔行变色后不再需要分隔线：底色本身已完成分段，再加 1px 线是重复表达。 */
/* A 版（左色块两栏）：色块与右侧内容等高；标题与按钮分居色块上下两端 */
.cat-block[data-layout="A"] .cat-panel,
.cat-block[data-layout="a"] .cat-panel { justify-content: space-between; }
.cat-panel {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-6) var(--s-5);
  border-radius: var(--r-lg);
  /* 白字实底只用同色相深化版（--cat-*-ink）；亮橙族原色配白字不达标 */
  background: var(--cat-ink, var(--ink-800));
  color: #fff;
}
.cat-panel::before {
  content: ''; position: absolute; right: -60px; bottom: -80px;
  width: 260px; height: 260px; border-radius: 50%;
  /* 🔴 2026-09-27 提高对比度（用户截图框选「对比度是否可以再强烈一点？6 个栏目都调整」）。
     由 .06 → .16：白色圆块叠在 6 个分类实底色（--cat-*-ink）上的可见度，
     WCAG 相对亮度比从 1.09–1.16:1 提到 1.27–1.48:1（六色实测，橙最弱、紫最强）。
     再往上到 .20 会开始抢标题视觉权重，.16 是「一眼看得见 / 仍属背景」的平衡点。
     ⚠️ 这是**装饰块**，不承载信息，不受 4.5:1 正文对比度约束；数值只作审美判据。
     同一参数的孪生块 .cat-banner::before（分类页 banner）必须同步改，否则两处不一致。 */
  background: rgba(255, 255, 255, .16);
}
.cat-panel > * { position: relative; }
.cat-panel h2 { color: #fff; font-size: var(--fs-h2); font-weight: 800; letter-spacing: -0.018em; }
.cat-panel .btn { min-height: 44px; }
.cat-mark {                                /* 品类水印（装饰，规则需在「> *」之后） */
  position: absolute; right: -4px; bottom: -22px;
  font-size: var(--fs-mark); font-weight: 800; line-height: 1;
  letter-spacing: -0.04em;
  color: rgba(255, 255, 255, .14);
  pointer-events: none;
}
.cat-body { min-width: 0; display: flex; flex-direction: column; gap: var(--s-4); }
/* A 版式右侧是「推荐机构清单」。**列数按断点分档**（2026-09-27 定稿）：
     · <992px → 单列。此档右栏只有约 396px（768 档 = 容器 728 − 色块 300 − 间距 32），
       若切 2 列每格仅 186px，扣掉 72px 缩略图与 12px 间距只剩 102px 文字位 ≈ 7 个汉字，
       机构名必被截断成「天鹅湖畔少儿…」—— 所以这一档必须保持单列。
     · ≥992px → **2 列 × 3 行**（每区 6 条）。此档右栏 580px 起，每格 278px、
       文字位 194px（≈14 汉字）；≥1200px 时右栏 760px、每格 368px、文字位 284px（≈20 汉字），
       机构名与「区 · 可预约到店 · 科目」一行元信息都能完整放下。
   🔴 为什么必须放开双列：单列时右栏 760px 里内容实占仅 244px，**右侧空 516px（68%）**
   （2026-09-27 用户在 PC 端截图指出）。破局点是**条数取偶数**：6 条排 2×3 满铺，
   「末行空一格」与「右侧大片留白」两个问题同时消失。
   （史：2026-09-26 曾在 768 档强制单列，理由是当时每区只有 3 条、2 列会排成 2+1、
   末行空一格 —— 条数补到 6 之后该理由已不成立，但 768 档宽度不足的理由依然成立。） */
/* 本档（<992px）为单列：末条之外都带虚线，故「倒数第 2 条」需补回一条线。
   ≥992px 的双列覆盖见下方 @media (min-width: 992px) 内同名规则（靠源序取胜）。 */
.cat-block[data-layout="A"] .mini-list,
.cat-block[data-layout="a"] .mini-list { grid-template-columns: minmax(0, 1fr); }
.cat-block[data-layout="A"] .mini-list li:nth-last-child(2),
.cat-block[data-layout="a"] .mini-list li:nth-last-child(2) { border-bottom: 1px dashed var(--line-1); }
.cat-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line-1);
}
.cat-head h3 { font-size: 15px; font-weight: 700; color: var(--heading); }

/* —— 需求 62（2026-09-27）：分类大区内的「去列表页」入口，颜色跟随该区主题色 ——
   .cat-panel 里的【查看全部】原本是白底**橙字**（.btn-on-dark），
   .cat-head 里的【更多 XX 机构】与 .mini-more【查看全部其他机构】也是**橙字** ——
   6 个大区因此看起来是同一种色，与左侧主题色块（--cat-ink）脱节。
   改挂 --cat-link（= --cat-ink 加深一档）。
   🔴 为什么用加深版而非 --cat-ink 原色：这三处文字压在白 / --band-grey(#e8ebf0) /
      --surface-2 上，须 ≥4.5:1。实测 --cat-ink 在区块灰上最低仅 4.47（书法 #1f7a48），
      color-mix(… 82%, #000) 后最低 5.52:1，全部达标。
   判据：node scripts/verify_cat_link_color.mjs */
.cat-panel .btn-on-dark { color: var(--cat-link, var(--primary-600)); }
.cat-panel .btn-on-dark:hover {
  background: var(--cat-soft, var(--primary-50));
  color: var(--cat-link, var(--primary-600));
}
.cat-head .btn-link { color: var(--cat-link, var(--primary-600)); }
.cat-head .btn-link:hover { color: var(--cat-link, var(--primary-600)); }
.mini-item.mini-more .name { color: var(--cat-link, var(--primary-600)); }
.mini-item.mini-more:hover {
  background: var(--cat-soft, var(--primary-50));
  border-color: var(--cat-link, var(--primary-500));
}
.mini-item.mini-more:hover .name { color: var(--cat-link, var(--primary-600)); }


/* —— 23.8 按区找机构（v4.4：浅蓝退役，本区块改挂深墨蓝重音段） ——
   🔴 为什么这里**必须**是深墨蓝（不是审美偏好，是数学约束）：
     · 上邻 `.cat-blocks` 段底色是纯白，且其内部 6 个 `.cat-block` 白/灰隔行、
       **以灰收尾** → 本区块既不能取白（与 .cat-blocks 段底色撞色，ΔE 0），
       也不能取灰（与 block6 撞色）；
     · 下邻 `.guide-strip` 已改纯白 → 取灰又会与它撞。
     在「只剩白 / 灰 / 深墨蓝」的前提下，唯一可行解就是深墨蓝。
     换掉 .guide-strip 的底色或翻转 .cat-block 的隔行奇偶都会连带打破更多相邻对。

   深底色带来的文字风险（铁律 14：底色加深会让压底文字静默跌破 AA）：
     区块内所有压在深底上的文字（h2 / eyebrow / lead / region-note）**必须**
     显式改挂浅色令牌，下方逐条列出；`.region-tile` 是纯白卡片，卡内文字不受影响。
     复核：node scripts/probe_band_text.mjs index.html */
.region-find {
  padding: var(--sec-y) 0;
  background: var(--ink-900);
}
.region-find .section-head h2 { color: #fff; }
.region-find .section-head .eyebrow {
  /* 深墨蓝底（--ink-900）上 eyebrow 用白字（var(--primary-300) #ffa985 → #fff，
     对比度 5.88:1 → 10.97:1）。图标 stroke="currentColor" 自动跟随。 */
  background: rgba(255, 255, 255, .13);
  color: #fff;
}
.region-find .section-head .lead { color: var(--text-on-dark); }
.region-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gut); }
.region-tile {
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
  min-height: 44px; padding: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  box-shadow: none;
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.region-tile:hover { border-color: var(--primary-300); box-shadow: var(--sh-2); transform: translateY(-2px); }
.region-name { font-size: 16px; font-weight: 700; color: var(--heading); }
.region-sub { font-size: var(--fs-sm); color: var(--text-2); }
.region-tile:hover .region-name { color: var(--primary-600); }
.region-note { margin-top: var(--s-5); font-size: var(--fs-sm); line-height: 1.9; color: var(--text-on-dark-2); }
/* 句中按钮（<button class="btn-link"> 出现在正文里）：不撑行高，按文字链接呈现。
   深底适配：--primary-600 压深墨蓝只有 3.1:1，换 --primary-300（亮橙浅档）。 */
.region-note .btn-link,
.region-note .btn-link:hover {
  display: inline; min-height: 0; height: auto; padding: 0;
  font-size: inherit; line-height: inherit; font-weight: 600;
  color: var(--primary-300);
  text-decoration: underline; text-underline-offset: 3px;
}

/* —— 23.9 家长怎么用（三步）（纯白底，同 23.5） —— */
.howto-steps {
  padding: var(--sec-y) 0;
  /* v4.4（2026-09-26）：由纯白改浅灰 —— 上邻 .guide-strip 已改纯白，
     若保持白会与它撞色（ΔE 0）。改灰后：白 → 灰 → 深墨蓝 三档清晰。 */
  background: var(--band-grey);
}
.steps { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gut); }
.step {
  min-width: 0; padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
}
.step-no {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-bottom: var(--s-3);
  background: var(--primary-50); color: var(--primary-600);
  border-radius: var(--r-pill);
  font-family: var(--font-num); font-size: 15px; font-weight: 800;
}
.step h3 { font-size: var(--fs-h4); margin-bottom: var(--s-2); }
.step p { font-size: var(--fs-body); line-height: 1.75; color: var(--text-2); }
.howto-cta { margin-top: var(--s-6); text-align: center; }
.howto-cta .btn { margin: 0 auto; }

/* —— 23.11 学员作品（纯白底，同 23.5） —— */
.works-strip {
  padding: var(--sec-y) 0;
  background: var(--surface);
}
.work-tile { min-width: 0; }
.work-tile a { display: block; overflow: hidden; border-radius: var(--r-md); background: var(--surface-3); }
.work-tile img {
  width: 100%; height: auto;               /* 预留宽高比（aspect-ratio）防 CLS */
  aspect-ratio: 4 / 3; object-fit: cover;
  transition: transform var(--dur-3) var(--ease-out);
}
.work-tile:hover img { transform: scale(1.03); }
.work-tile figcaption { margin-top: var(--s-3); font-size: var(--fs-sm); line-height: 1.6; color: var(--text-2); }
.works-note { margin-top: var(--s-5); font-size: var(--fs-xs); line-height: 1.7; color: var(--text-2); }

/* —— 23.12 转化带（两种用法：整段通栏 <section> / 容器内成块 <div>） —— */
section.cta-band {
  padding: var(--sec-y) 0;
  background: var(--ink-900);
  border-top: 1px solid var(--line-1);      /* 与上方浅色区块硬分界，不用渐变过渡 */
  text-align: center;
}
div.cta-band {
  position: relative; overflow: hidden;
  padding: var(--s-7) var(--s-5);
  background: var(--ink-900);
  border-radius: var(--r-lg);
  text-align: center;
}
.cta-band h2 { color: #fff; margin-bottom: var(--s-3); }
.cta-band p {
  max-width: 42em; margin: 0 auto var(--s-6);
  font-size: var(--fs-lead); line-height: 1.75;
  /* 问题8 修复：深底文案统一纯白（15.6:1），层级由字号承担 */
  color: #fff;
}
.detail-main > .cta-band { margin-top: var(--s-7); }
.about-wrap div.cta-band { margin: var(--s-4) 0 var(--sec-y); }
/* 深底上的次级按钮改用反白描边：primary-600 落在 ink-900 上仅 3.56:1，不达标 */
.cta-band .btn-outline {
  color: #fff;
  border-color: rgba(255, 255, 255, .75);
}
.cta-band .btn-outline:hover {
  background: #fff; color: var(--primary-600); border-color: #fff;
}
.footer { border-top: 1px solid var(--line-on-dark); }   /* 深色带/浅色带之间的硬分界 */

/* —— 23.13 资讯卡阅读量 —— */
.card-post .foot .visit,
.visit { font-size: var(--fs-xs); color: var(--text-2); }

/* —— 23.14 头部城市站标签 + 其他城市入口（PBOOT 模板 header.html） —— */
.header-city .city-name { font-weight: 600; }
.header-city.is-static { flex: 0 1 auto; min-width: 0; cursor: default; }
/* 头部横向空间有限：按钮显示紧凑标签（"其他城市 ›"），
   DOM 里的完整文案（"其他城市陆续开放，点这里告诉我们 ›"）继续作为无障碍名称与 title 保留 */
.city-more {
  display: block;
  width: auto;
  max-width: none; min-width: 0;
  margin-left: var(--s-2);
  font-size: 0;
  color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color var(--dur-1) var(--ease-in-out);
}
.city-more::before { content: '其他城市 ›'; font-size: var(--fs-xs); line-height: 44px; }
.city-more:hover,
.city-more:focus-visible {
  color: var(--primary-600);
  text-decoration: underline; text-underline-offset: 3px;
}

/* —— 23.15 页脚承诺行 —— */
.footer-promise {
  margin-top: var(--s-4); padding-top: var(--s-4);
  border-top: 1px solid var(--line-on-dark);
  font-size: var(--fs-xs); line-height: 1.8;
  color: #fff;                        /* 问题8 修复：页脚内文字全部纯白 */
}

/* —— 23.16 弹窗副标题 / 说明行 / 备用入口 / 表单信任说明 —— */
.modal-sub,
.note {
  font-size: var(--fs-sm); line-height: 1.75;
  color: var(--text-2);
}
.modal-sub { margin-bottom: var(--s-5); }
.modal-body > .note:first-child { margin-bottom: var(--s-5); }
.modal-body > .note:last-child { margin-top: var(--s-5); }
.note + .btn,
.note + .btn-link { margin-top: var(--s-4); }
.modal-alt {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-2) var(--s-4);
  margin-top: var(--s-5); padding-top: var(--s-5);
  border-top: 1px solid var(--line-1);
  font-size: var(--fs-sm); color: var(--text-2);
}
.modal-alt a {
  display: inline-flex; align-items: center;
  min-height: 44px;
  color: var(--primary-600); font-weight: 600;
}
.modal-alt a:hover { color: var(--primary-700); text-decoration: underline; }
.form-trust {
  margin: 0 0 var(--s-5);
  font-size: var(--fs-xs); line-height: 1.75;
  color: var(--text-2);
}

/* —— 23.17 关于我们 / 单页（PBOOT 模板 about.html / help.html / page.html） —— */
.about-wrap > .container { display: flex; flex-direction: column; }
.about-wrap .about-section .eyebrow { margin-bottom: var(--s-2); }
.about-wrap .about-section > .lead {
  max-width: 46em; margin: 0 auto var(--s-5);
  text-align: center;
}
.page-wrap { padding: var(--sec-y) 0; }
.page-wrap > .container { display: flex; flex-direction: column; gap: var(--s-7); }

/* 需求 69：含面包屑页面的「区块起始间距」统一收一档（原 --sec-y）。
   —— 页头带/面包屑下方那片空白：实测 120px；与面包屑自身 56px 叠加显得很空。
   覆盖：详情页 .detail-wrap｜列表页 .list-wrap｜选课指南 .guide-wrap｜
         PBOOT 通用页 .page-wrap｜帮助/隐私/协议/入驻 .sub-wrap｜关于页首屏 .about-section
   🔴 2026-10-04 更正：本行原写「只收 top，底部 --sec-y 节奏不变」。用户随后指出**底部同样偏大**
      （圈的是详情页正文底部 → 页脚），已按同一口径收 bottom ⇒ 见紧邻下一条规则。 */
.list-wrap, .detail-wrap, .sub-wrap, .guide-wrap, .page-wrap,
#main .about-section:first-child { padding-top: var(--s-6); }

/* 🔴 2026-10-04（用户：「这个位置空隙有点大」）—— 与上面需求 69 **对称**：收「内容区 → 页脚」的底部留白。
   现象：≥1200 档下，正文最后一行到页脚深色区之间是**纯白 120px**（= --sec-y），
        再叠加页脚自身的 64px padding-top ⇒ 正文到页脚文字实为 184px，读完一片空。
   为何别处不觉空、唯独这里觉空：区块之间是「白 → 白/灰」，边界不显；
        这里是「白 → 深色」，强对比把留白放大了。
        （实测全站 19 页该处都是 120px、5 个详情页完全一致 ⇒ **不是详情页 bug**，是全站节奏值。）
   ⇒ 收一档到 64px（--s-8，与页脚自身 64px padding-top 等值，形成对称过渡）。
   ⚠️ 用 `min()` 而非写死 64px：保证 **只减不增** —— <480 档 --sec-y 本就是 56px，
      若写死 64 反而把小屏撑大 8px。
   ⚠️ **不含** `#main .about-section:first-child`：那是关于页**首屏**，只该收 top；
      给它加底部 padding 会顶开关于页两区块之间的间距 ⇒ 故拆成独立规则、不动上一条。
   ⚠️ 覆盖范围 = **全站内容页**（详情 5 页 + 列表页 + 帮助/隐私/协议/入驻 + 选课指南），
      不是只改详情页。索引页/关于页本来就不空（实测 8px / -24px），不在本组、不受影响。 */
.list-wrap, .detail-wrap, .sub-wrap, .guide-wrap, .page-wrap {
  padding-bottom: min(var(--sec-y), var(--s-8));
}

/* 5 条硬标准：编号卡列表 */
.standard-list {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: var(--gut);
  margin-top: var(--s-5);
  counter-reset: std;
}
.standard-list li {
  position: relative; min-width: 0;
  padding: var(--card-pad) var(--card-pad) var(--card-pad) calc(var(--card-pad) + var(--s-6));
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
}
.standard-list li::before {
  counter-increment: std;
  content: counter(std, decimal-leading-zero);
  position: absolute; left: var(--card-pad); top: var(--card-pad);
  font-family: var(--font-num);
  font-size: var(--fs-sm); font-weight: 800;
  color: var(--primary-600);
}
.standard-list h3 { font-size: var(--fs-h4); margin-bottom: var(--s-2); }
.standard-list p { font-size: var(--fs-body); line-height: 1.75; color: var(--text-2); }

/* 做事方式：勾选卡列表 */
.value-list {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: var(--gut);
  margin-top: var(--s-3);
}
.value-list li {
  min-width: 0;
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-left: 3px solid var(--primary-500);
  border-radius: var(--r-md);
}
.value-list h3 {
  display: flex; align-items: flex-start; gap: var(--s-2);
  font-size: var(--fs-h4); margin-bottom: var(--s-2);
}
.value-list h3::before {
  content: '✓'; flex: none;
  width: 20px; height: 20px; margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--primary-50); color: var(--primary-600);
  border-radius: 50%;
  font-size: 12px; line-height: 1;
}
.value-list p { font-size: var(--fs-body); line-height: 1.75; color: var(--text-2); }

/* —— 23.18 详情页侧栏键值对（PBOOT 模板 *-detail.html：.side-card 内的 .meta-list） ——
   🔴 2026-09-27：原「联系页」死规则（.contact-info / .contact-actions）已随 contact.html 删除；
   .meta-list 基础规则**必须保留** —— course-detail / institution-detail / work-detail 三模板在用。 */
.meta-list li {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4);
  min-height: 44px;
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--line-1);
  font-size: var(--fs-sm);
}
.meta-list li:last-child { border-bottom: none; }
.meta-list .label { flex: none; color: var(--text-2); }
.meta-list .value { min-width: 0; font-weight: 600; color: var(--heading); text-align: right; }
/* —— 机构信息卡 · 机构头像位（2026-09-28 需求 3b） ——
   静态端图源优先 inst.logo（商家品牌标），缺省回退 inst.img（该机构封面图）；
   PBOOT 端图源 {content:pic}。两端同一套尺寸与圆角，避免双端观感分叉。
   无图时节点整个不渲染（缺信息留空，不留灰框占位）。 */
.side-card .inst-avatar {
  width: 72px; height: 72px;
  margin-bottom: var(--s-4);
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
}
.side-card .inst-avatar img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* logo 版（2026-09-29 需求 82）：品牌标识不是照片 ⇒ 改 contain + 白底内衬，
   否则 cover 会把横版 logo 的左右两端裁掉（尚坤 2.855:1 塞进 72x72 只剩中间一截）。
   ⚠️ 而且**容器不能锁死 72x72**：横版 logo 在方框里 contain 后只有 72x25，
     小到看不清。⇒ 高度锁 72（与设计一致）、**宽度按内容自适应**（fit-content，
     最短 72 保证方版 logo 不缩水），横版 logo 因此能铺开到 137x48。
   封面图（inst.img 回退）仍走上面的 cover 铺满，两者靠容器修饰类区分。 */
.side-card .inst-avatar--logo {
  display: flex; align-items: center; justify-content: center;
  width: fit-content; min-width: 72px; height: 72px;
  padding: 0 var(--s-3);
  background: #fff;
}
.side-card .inst-avatar--logo img {
  width: auto; height: 48px; max-width: 100%;
  object-fit: contain; padding: 0;
}
/* —— 23.19 详情页补充（PBOOT 模板 *-detail.html） —— */
.detail-eyebrow {
  display: inline-block;
  margin-bottom: var(--s-3);
  font-size: var(--fs-xs); font-weight: 700;
  letter-spacing: .06em;
  color: var(--primary-600);
}
.detail-block {
  margin-top: var(--s-7); padding-top: var(--s-6);
  border-top: 1px solid var(--line-1);
}
.detail-block h2 { font-size: var(--fs-h3); margin-bottom: var(--s-3); }
.detail-block p { font-size: var(--fs-body); line-height: 1.85; color: var(--text-2); }
.booking-card { text-align: center; }
.booking-card h2 { font-size: var(--fs-h4); margin-bottom: var(--s-3); }
.booking-card .phone {
  margin-bottom: var(--s-4);
  font-family: var(--font-num);
  font-size: clamp(18px, 1.8vw, 22px); font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--primary-600);
  word-break: break-all;
}
.booking-card .btn { width: 100%; }
/* 2026-09-27：booking-card（PBOOT 端 6 个详情模板）同 contact-card 改为双按钮，补间距 */
.booking-card .btn + .btn { margin-top: var(--s-3); }
.booking-card .note { margin-top: var(--s-3); font-size: var(--fs-xs); }

/* —— 23.20 卡片补充（PBOOT 模板 courses.html / works.html） —— */
.card-course .info .org { font-size: var(--fs-sm); color: var(--text-2); }
.card-post .foot .author,
.card-course .foot .author { font-size: var(--fs-xs); color: var(--text-2); }

/* —— 23.21 页面标识钩子（<body class="page-*">） ——
   各页唯一标识，无独立视觉样式；显式列出以保证「HTML 类名 → CSS」覆盖自检无留白，
   后续如需按页做局部覆盖，直接在此组追加即可。 */
body.page-home, body.page-list, body.page-detail, body.page-about,
body.page-courses, body.page-category,
body.page-institutions, body.page-news, body.page-sub, body.page-works {
  --page-hook: 1;
}

/* ==========================================================================
   24. 响应式：5 档（移动优先，桌面增强）
   XL ≥1200 / L 992–1199 / M 768–991 / S 480–767 / XS <480
   ========================================================================== */

/* —— S ≥480px：小屏手机/大屏手机 —— */
@media (min-width: 480px) {
  :root { --sec-y: 72px; }
  .header-city { display: inline-flex; }
  .btn-lg { width: auto; }
  .card-grid.cols-2, .card-grid.cols-3, .card-grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .course-row, .inst-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .news-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .about-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 🔴 2026-10-05：品牌名由「点艺点」（3 字）改为全称「点艺点艺术教育平台」（10 字）。
     页脚首列同时承载「图形标志(38px) + 品牌文字」的 lockup：实测 480~499px 档
     列宽仅 208px、lockup 需 217px ⇒ 品牌文字被压成 2 行（「点艺点艺术教育平」/「台」）。
     故本档把首列加权到 1.2fr（与 ≥1200px 四列版式的 `1.2fr 1fr 1fr 1fr` 首列口径一致），
     480 档首列 208→227px，全档（480~1199）均恢复单行。
     ⚠️ 不要写回 repeat(2, 1fr)：那样 480~499px 会重新折行。 */
  .footer .container { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); }
  .guide-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 新版区块：按区找机构 —— 城区 6 → 10 后**不再**在此档设 3 列。
     10 个磁贴落在 3 列会得到 3+3+3+1，最后一行孤零零 1 个（10 的因数只有 2 和 5）。
     本档回到基线的 2 列（5 行满铺），5 列留给 ≥768px。 */
}

/* —— M ≥768px：竖板 / 横板 —— */
@media (min-width: 768px) {
  :root { --pad-x: 20px; --sec-y: 96px; --gut: 18px; }
  .topbar { display: block; }
  .header-nav .nav-item { min-height: 52px; }
  .card-grid.cols-3, .card-grid.cols-4 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .course-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .inst-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cat-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mini-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mini-list li:nth-last-child(-n+2) { border-bottom: none; }
  .news-row, .cat-content .news-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cat-content .news-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cat-nav .container { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  /* 按区找机构：城区扩到 10 个后改 5 列（10 = 5×2 两行满铺，不留孤儿格）。
     原先是 6 列 —— 那是**按 6 个城区整数匹配**写死的，城区一变就会 6+4 断行。 */
  .region-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  /* 规格条：4 列一行，列间发丝线。
     竖直内边距 38px 取自参考站 .spec-cell{padding:38px 34px}。
     2026-09-27（需求 60 · 方案 B）：单元格内容整体居中。
     原两条「首尾列贴容器边」特例（:first-child padding-left:0 /
     :last-child padding-right:0）是**为左对齐贴合页面栅格线设计**的；
     内容居中后不再需要贴边，留着反而让首尾两格各偏 16px
     （4 列各 320px：首格内容轴 144px / 理想 160px），故一并删除，
     四格 padding 对称 = 真正的均匀居中。 */
  .trust-bar .container { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; }
  .trust-bar > .container > * + * { border-left: 1px solid var(--line-2); }
  .trust-bar > .container > * { border-top: none; }          /* 4 列只有一行，取消窄屏的横线 */
  .trust-item { padding: 38px var(--s-6); }
  /* 首屏支架档：≥1044px 起解出上限 480px，与 H1 桌面定高一致 */
  .hero { height: clamp(340px, 46vw, 480px); }
  .hero-btn { width: 48px; height: 48px; font-size: 18px; }   /* H1 原值 */
  .hero-btn.prev { left: 24px; }                              /* H1 原值 */
  .hero-btn.next { right: 24px; }                             /* H1 原值 */
  .item-card.horizontal, .card-row { flex-direction: row; align-items: stretch; }
  .item-card.horizontal .thumb, .card-row .thumb { width: 240px; flex: none; aspect-ratio: 16 / 10; }
  .item-card.horizontal .info, .card-row .info { padding: var(--s-5); justify-content: center; }
  .news-block .featured { flex-direction: row; }
  .news-block .featured .thumb { width: 180px; height: 120px; aspect-ratio: auto; }
  .btn-sm { height: 32px; min-height: 32px; padding: 0 var(--s-4); }
  .btn-sm::after { content: ''; position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); height: 44px; }
  .filter-tags .tag, .chip { height: 32px; }
  .list-toolbar .sort-tabs li { min-height: 36px; }
  .pagination a { min-width: 40px; height: 40px; /* 桌面 40×40（触控仍由相邻间距保证） */ }
  .form-control, .input { height: 44px; }
  .timeline::before { left: 50%; transform: translateX(-50%); }
  .timeline li { justify-content: flex-start; }
  .timeline li:nth-child(odd) { justify-content: flex-start; }
  .timeline li:nth-child(even) { justify-content: flex-end; }
  .timeline .dot { left: 50%; transform: translate(-50%, -50%); top: 50%; }
  .timeline .content { width: 45%; margin-left: 0; }
  .timeline li:nth-child(odd) .content { margin-right: auto; }
  .timeline li:nth-child(even) .content { margin-left: auto; }
  /* 新版区块：年龄段 / 三步 / 品类宫格 / 分类大区 A·B */
  .age-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); }
  .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); }
  .cat-nav .container > .cat-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .cat-block[data-layout="A"],
  .cat-block[data-layout="a"] { grid-template-columns: 300px minmax(0, 1fr); gap: var(--s-6); }
  /* 2026-09-26：B 版式（横向标题条 + 单列列表）整套已废弃 ——
     原先 6 个大区按序号 A/B 交替，用户要求统一为 A，故 B 版规则一并删除，
     避免留下"不再命中的死规则"（这类残留正是历史上首页资讯区块错位的成因）。 */
  .standard-list, .value-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .modal-mask { align-items: center; padding: var(--s-5); }
  .modal { max-width: 560px; border-radius: var(--r-lg); max-height: 90vh; padding-bottom: 0; animation: modalIn var(--dur-3) var(--ease-out); }
  .modal::before { display: none; }
  @keyframes modalIn { from { opacity: 0; transform: translateY(18px) scale(.98); } to { opacity: 1; transform: none; } }
}

/* —— 按区找机构：仅 768~991px 的磁贴微调（2026-09-26）——
   为什么单独开这一段：城区 6 → 10 后 `.region-grid` 在 ≥768px 改为 5 列，
   本档容器宽 768-40=728px，减 4 个 gutter(18) 剩 656，÷5 ≈ **131px**；
   扣掉磁贴自身 padding 32px 只剩 99px，而副标题「看这个区的机构 ›」13px 实需约 104px
   → 「›」被挤到第二行，磁贴高度由 87px 涨到 110px（同一行高度也跟着变，非常扎眼）。
   实测见 scripts/shot_region_grid.mjs：375/480/1024/1440 均 1 行，只有 768 折成 2 行。
   收内边距 + 缩一级字号后可用宽 111px > 所需 96px，留了余量。
   ⚠️ 上限必须写 991px（与下方 `@media (min-width: 992px)` 严丝合缝，不可重叠）；
      1024px 起磁贴 ≥181px 本就富余，不该被这条动到。 */
@media (min-width: 768px) and (max-width: 991px) {
  .region-tile { padding: 14px 10px; }
  .region-sub { font-size: 12px; }
}

/* —— L ≥992px：小桌面 / 横板（导航仍为抽屉，卡片密度提升） —— */
@media (min-width: 992px) {
  :root { --sec-y: 104px; --gut: 20px; --card-pad: 20px; }
  /* 此处原为「首屏满屏 min-height:100dvh」覆盖。2026-09-26 按用户指定改回
     H1（2026-08-29）原版：首屏为 480px 定高，不做满屏。
     满屏必须把 1440×828 的容器塞进 1920×600(3.2:1) 的源图，需放大 1.66x 才不发糊；
     480px 定高下容器 3.0:1、缩放 0.80x（缩小），同一张图反而更清晰。
     故此处不再覆盖 .hero，删除即可。 */
  .card-grid.cols-3, .card-grid.cols-4 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .course-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .guide-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .detail-content { font-size: var(--fs-body); }
  /* 新版区块 */
  .age-grid, .steps { gap: var(--s-6); }
  .cat-block[data-layout="A"],
  .cat-block[data-layout="a"] { grid-template-columns: 340px minmax(0, 1fr); }
  /* 分类大区右栏：≥992px 起放开为 **2 列 × 3 行**（每区 6 条）。
     分档理由见上方「A 版式右侧是『推荐机构清单』」段注释。
     特异性：列数与顶层第 2587 段同为 (0,3,0)，末行去线为 (0,4,1) 对 (0,4,1)，
     **靠源序在后取胜**（本块在 3050 行之后）—— 故此处不可上移到 <992px 的档位前。 */
  .cat-block[data-layout="A"] .mini-list,
  .cat-block[data-layout="a"] .mini-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cat-block[data-layout="A"] .mini-list li:nth-last-child(-n+2),
  .cat-block[data-layout="a"] .mini-list li:nth-last-child(-n+2) { border-bottom: none; }

  /* 需求 72（2026-09-28）：`-n+2` 按**文档序号**取末 2 条 —— 偶数条数时正好是最后一行的 2 条，
     奇数条数时会跨行。撤掉「查看全部其他机构」占位卡后该区只剩 5 条（奇数），
     末 2 条被一起去线 ⇒ 第 2 行「左列有虚线、右列无虚线」。
     按本文件单列档既有 `nth-last-child(2)` 补偿模式补回（思路一致）。
     ⚠️ 其余 5 区各 6 条（偶数）无需覆盖；若「其他艺术」补到 6 家，本条须一并删除。 */
  .cat-block[data-block="other"] .mini-list li:nth-last-child(2)
    { border-bottom: 1px dashed var(--line-1); }
}

/* —— XL ≥1200px：桌面完整形态（sticky 头部 72px、侧栏布局、最大留白） —— */
@media (min-width: 1200px) {
  :root {
    --pad-x: 24px;
    --sec-y: 120px;
    --gut: 24px;
    --card-pad: 24px;
    --header-h: 72px;                       /* 桌面头部 72px（移动 56px） */
  }

  /* 头部：水平导航 + hover/focus 下拉 */
  .header .container { gap: var(--s-5); padding-right: var(--pad-x); }
  .header-logo { font-size: 21px; }
  .header-logo .logo-mark { width: 40px; height: 40px; font-size: 20px; }
  .nav-toggle, .header-tel, .nav-mask { display: none; }
  .header-nav {
    position: static; flex: 1 1 auto;
    flex-direction: row; align-items: center; justify-content: center;
    gap: var(--s-1);
    max-height: none; overflow: visible;
    padding: 0;
    border: none; box-shadow: none;
    opacity: 1; visibility: visible; transform: none;
    pointer-events: auto;                  /* 复位移动端抽屉的 pointer-events 兜底 */
  }
  .header-nav .nav-item {
    height: var(--header-h); min-height: 0;
    /* 🔴 2026-10-05 页头栏目「太挤」调整（用户：「栏目太挤了，回复昨天不挤的」）：
       归因：**不是栏目彼此挤** —— 相邻项「文字↔文字」间距两天恒为 36px
             （= --s-4 16px × 2 + flex gap --s-1 4px），逐像素量过，一位没变。
             真正变的是页头品牌名由「点艺点」(3 字/宽 110px) 改为
             「点艺点艺术教育平台」(10 字/宽 235px)，把整排栏目整体右推约 90px
             （栏目块中心 601.7 → 691.6，而页面中心恒为 629）。
       用户裁决（AskUserQuestion）：**保留 10 字全称**，改为把栏目间距拉宽 36 → 52px。
       落点 = 内衬 --s-4(16px) → --s-5(24px)：24 + 4 + 24 = 52px。
       ⚠️ 只动本档（≥1200px 桌面档）；<1200px 是抽屉形态，其内衬【不动】。
       ⚠️ 本改动只影响 .nav-item 自身宽度；下拉面板是 position:absolute +
          min-width:180px 居中于该项，宽度与本改动无关
          （守门判据 scripts/_verify_req73_dropdown_full_row.mjs）。 */
    padding: 0 var(--s-5);
    font-size: 15px;
    border-bottom: none;
    /* 复位抽屉态为「标题一行、下拉一行」加的换行：桌面档 .dropdown 是
       position:absolute 已脱离流，只剩标题一个弹性子项，nowrap 才是正确形态。 */
    flex-wrap: nowrap;
  }
  .header-nav .nav-item > a { flex: 0 0 auto; min-height: 0; }
  .header-nav .nav-item.active { font-weight: 600; }
  .header-nav .nav-item::after {
    content: ''; display: block; position: absolute; left: 50%; bottom: 16px;
    width: 0; height: 3px;
    background: var(--primary-500);
    border-radius: var(--r-pill);
    transform: translateX(-50%);
    transition: width var(--dur-2) var(--ease-out);
  }
  .header-nav .nav-item:hover::after,
  .header-nav .nav-item.active::after,
  .header-nav .nav-item.open::after { width: 26px; }

  .dropdown {
    position: absolute; top: calc(100% - 4px); left: 50%;
    display: block; width: auto; min-width: 180px;
    padding: var(--s-2);
    background: var(--surface);
    border: none;
    border-radius: var(--r-md);
    box-shadow: var(--sh-3);
    opacity: 0; visibility: hidden;
    transform: translateX(-50%) translateY(6px);
    transition: opacity var(--dur-2) var(--ease-out),
                transform var(--dur-2) var(--ease-out),
                visibility var(--dur-2) linear;
  }
  .header-nav .nav-item:hover .dropdown,
  .header-nav .nav-item:focus-within .dropdown,
  .header-nav .nav-item.open .dropdown {
    opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
  }
  .dropdown a {
    /* 🔴 需求 73（2026-09-28）：`display` 由 `inline-flex` 改为 `flex`（block 级）。
       桌面档 .dropdown 是 position:absolute + width:auto（shrink-to-fit），
       宽度由 min-width:180px 托底 ⇒ 面板 180 / 内容宽 164；
       而 inline-flex 每项宽度只等于自身内容宽（实测「音乐培训」类 106px、
       「行业资讯」类 84px），hover 底色右侧因此空出 58~80px —— 用户截图「色块不是整行」。
       改 block 级 flex 后每项撑满面板内容宽（164px），hover 底色即成整行。
       ⚠️ 顺带消除一处**已实测**的真实隐患（scripts/_diag_req73_panel_width.mjs）：
          面板宽度**恒为 180px、与文案长度无关** —— .nav-item 是 position:relative 且仅 77px 宽，
          绝对定位面板的 shrink-to-fit 被这个窄包含块压到最小，再由 min-width:180px 托底；
          长文案不会撑宽面板，只会**换行增高**（实测 16 字 → 45.5px、29 字 → 91px 高）。
          于是改前的 inline-flex（项宽=自身内容宽）只要两两合计 ≤164px 就**并排成两列**：
          实测栏目名「音」时单条 67px，6 条被排成 3 行 × 2 列。
          PBOOT 端子栏目名由后台决定，出现单/双字短名即触发；改 block 级后不可能并排。
       ⚠️ 仅 ≥1200px 桌面档；平板/抽屉档是「横向灰底胶囊」设计（见基础档 .dropdown），
          其 inline-flex 保持不动。DIAG: scripts/_diag_nav_dropdown.mjs */
    display: flex;
    min-height: 40px; padding: 0 var(--s-4);
    background: none;
    border-radius: var(--r-sm);
  }
  .dropdown a:hover { background: var(--primary-50); }

  /* 动作区回到头部内联 */
  .header-actions {
    position: static; padding: 0; background: none;
    border: none; box-shadow: none;
    opacity: 1; visibility: visible; transform: none;
  }
  .header-actions .btn { flex: 0 0 auto; max-width: none; min-height: 40px; height: 40px; }
  .sticky-cta { display: none; }
  body { padding-bottom: 0; }

  /* 两栏布局回到桌面形态 */
  .category-block .container { grid-template-columns: 280px minmax(0, 1fr); gap: var(--s-6); align-items: stretch; }
  .category-block[data-layout="b"] .container,
  .category-block[data-layout="B"] .container,
  .category-block.is-compact .container,
  .category-block--b .container { grid-template-columns: minmax(0, 1fr); }
  .cat-banner { display: flex; }
  .mini-list li { border-bottom: 1px dashed var(--line-1); }
  .mini-list li:nth-last-child(-n+2) { border-bottom: none; }
  .card-grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .inst-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .list-wrap .container { grid-template-columns: 240px minmax(0, 1fr); gap: var(--s-6); }
  .filter-side { position: sticky; top: calc(var(--header-h) + var(--s-4)); align-self: start; padding: var(--s-5); }
  .filter-tags { flex-wrap: wrap; overflow: visible; }
  .detail-wrap .container { grid-template-columns: minmax(0, 1fr) 320px; gap: var(--s-6); }

  /* 需求 69：桌面档区块起始间距 32 → 64px（页头带/面包屑下方那片白，收一档）
     🔴 需求 76（2026-09-28）：用户看机构详情页「面包屑下方那片白」仍觉太空，
        指示再收一档 ⇒ 本组（含 about）桌面档一律 s-5（24px）。
        需求 75（s-6 = 32px）与需求 71（about 单开例外）均已消解；
        ⚠️ 本值**低于基线档**（基线仍 s-6 = 32px）—— 桌面/手机档有意分叉，勿「修正」回 ≥ 基线。 */
  .list-wrap, .detail-wrap, .sub-wrap, .guide-wrap, .page-wrap { padding-top: var(--s-5); }
  #main .about-section:first-child { padding-top: var(--s-5); }
  /* 🔴 2026-10-04 侧栏吸顶跟随（用户：「仿照 https://www.bjlaw123.com/falvzixun/ 的行为，
     左侧上下滚动，右侧一直出现」）。
     做法**沿用站内既有先例** —— 列表页筛选栏 `.filter-side`（本文件 L3833）同款三件套，
     不引入新机制：`position:sticky` + `top` + `align-self:start`。
     ⚠️ `align-self: start` 不能省：`.detail-wrap .container` 是 grid，
        grid 子项默认 `stretch` 会被拉到与左栏**等高** ⇒ 没有可滚动的余量
        ⇒ sticky 完全不生效。**它不报错、只是静静不动**，最容易被误判成「浏览器不支持」。
     ⚠️ 只在 ≥1200px 档生效：<1200px 是单列，`.detail-side` 走 `display:contents`（L2319）
        被打散成正文流里的若干卡片，本身不生成盒子，sticky 无从谈起。
     ⚠️ `top` 只需避让头部：吸底 CTA（`.sticky-cta`）是 <1200px 专属（L776），桌面档不存在。
     ⚠️ 暂不设 `max-height`/`overflow` —— 侧栏本次删掉「热门课程」后只剩两张卡，
        实测总高低于视口可用高度，无需内部滚动条；日后卡片变多、顶出视口时再补。 */
  .detail-side {
    display: flex; flex-direction: column; gap: var(--s-4);
    position: sticky;
    top: calc(var(--header-h) + var(--s-4));
    align-self: start;
  }
  .detail-side .contact-card,
  .detail-side > *:not(.contact-card) { order: 0; }
  .footer .container { grid-template-columns: 1.2fr 1fr 1fr 1fr; gap: var(--s-7) var(--s-6); }
  .about-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .floating { right: var(--s-5); bottom: var(--s-5); }
  .wrap-reading { max-width: var(--w-reading); }

  /* 新版区块：按区找机构一行 5 格（10 个城区 = 5×2；原 6 格见上方 ≥768px 注释） */
  .region-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .cat-block[data-layout="A"],
  .cat-block[data-layout="a"] { grid-template-columns: 360px minmax(0, 1fr); }
}

/* 超宽屏：容器封顶，避免行宽失控（内容仍锁 1200px） */
@media (min-width: 1600px) {
  :root { --w-container: 1200px; }
}

/* ==========================================================================
   25. prefers-reduced-motion 降级（规范 §7.5）
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  html { scroll-behavior: auto; }
  .reveal { opacity: 1 !important; transform: none !important; }
  .hero-slide { transition: none; }
  .skeleton { background: var(--surface-2); animation: none; }
  .modal, .modal-mask.show { animation: none; }
}

/* ==========================================================================
   26. 溢出兜底（省略号标题可收缩，防止 min-content 撑宽栅格轨道）
   ========================================================================== */
.news-list li, .news-list li .title, .mini-item, .mini-item .mini-info { min-width: 0; }
.side-card, .detail-main, .cat-row, .list-main, .filter-side { min-width: 0; }
.hero-content, .hero-content h1, .hero-content p { min-width: 0; }
.age-card, .step, .region-tile, .work-tile,
.cat-block, .cat-body, .cat-head, .section-head,
.card-grid > *, .steps > *, .region-grid > *, .ensure-grid > *, .age-grid > * { min-width: 0; }
/* ==== V2-BEGIN ==== */
/* V2 · 机构决策信息层（2026-09-29）
   三块：列表卡的字段胶囊（.fact-row）／详情页基本信息表（.inst-facts）／
         品类知识区（.cat-know）与迷你问答（.faq-mini）。
   配色只用既有令牌（铁律：区块底仅纯白 ↔ --band-grey；6 分类色成对使用），不新增颜色。 */

/* —— 列表卡：字段胶囊行 —— */
.fact-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-top: var(--s-2); }
.fact {
  display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px;
  border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--surface-2); color: var(--text-2);
  font-size: var(--fs-xs); line-height: 1.5; white-space: nowrap;
}
.fact .label { color: var(--text-3); }
@media (max-width: 767px) {
  .fact-row { gap: 6px; }
  .fact { font-size: 12px; padding: 2px 8px; }
}

/* —— 详情页：基本信息表 —— */
.inst-facts {
  margin: var(--s-4) 0 0; border: 1px solid var(--line-1); border-radius: 12px;
  background: var(--surface); overflow: hidden;
}
.inst-facts__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: 12px var(--s-4); border-bottom: 1px solid var(--line-1); background: var(--surface-2);
}
.inst-facts__head strong { font-size: var(--fs-body); color: var(--heading); }
.inst-facts__body {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 1px; background: var(--line-1);
}
.inst-fact {
  padding: 12px var(--s-4); background: var(--surface);
  display: flex; flex-direction: column; gap: 4px;
}
.inst-fact dt { font-size: var(--fs-xs); color: var(--text-3); }
.inst-fact dd {
  margin: 0; font-size: var(--fs-body); color: var(--text-1);
  font-weight: 600; word-break: break-word;
}
.inst-fact dd .muted { font-weight: 400; color: var(--text-3); font-size: var(--fs-sm); }
.inst-fact--wide { grid-column: 1 / -1; }
.inst-fact__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.inst-fact__chips span {
  padding: 2px 8px; border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--line-2); font-size: var(--fs-xs); font-weight: 400; color: var(--text-2);
}
.badge-quality {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 999px;
  font-size: var(--fs-xs); font-weight: 600; border: 1px solid transparent;
}
.badge-quality[data-state="satisfied"] { background: #e8f7ef; color: #1f7a4d; border-color: #bfe7d1; }
.badge-quality[data-state="pending"]  { background: var(--surface-3); color: var(--text-2); border-color: var(--line-2); }

/* —— 详情页 / 品类页：词条式要点列表 —— */
.known-list { list-style: none; margin: var(--s-3) 0 0; padding: 0; display: grid; gap: 10px; }
.known-list li {
  position: relative; padding-left: 22px; font-size: var(--fs-body);
  line-height: 1.8; color: var(--text-2);
}
.known-list li::before {
  content: ""; position: absolute; left: 6px; top: .72em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--primary-500);
}
.known-list--check li::before {
  content: ""; width: 10px; height: 6px; border-radius: 0; background: none;
  border-left: 2px solid var(--primary-600); border-bottom: 2px solid var(--primary-600);
  transform: rotate(-45deg); top: .62em;
}
.known-list b { color: var(--text-1); }

/* 🔴 2026-10-02 修：详情页「开设课程」列表每项出现**两个圆点**（用户反馈）。
   根因 = **特异性**，不是样式没写：
     · `.detail-content ul li { list-style: disc }`（0,1,2）压过 `.known-list li`（0,1,1）
       ⇒ 原生 disc 标记与 `.known-list li::before` 的品牌色圆点**同时出现**；
     · `.detail-content ul { padding-left: 1.2em }`（0,1,1）压过 `.known-list { padding: 0 }`（0,1,0）
       ⇒ 整段还被多缩进 1.2em（实测 18px）。
   `.known-list` 只有**落在 `.detail-content` 里**才中招 ——「到店试听须知 / 怎么看师资」
   在 `.detail-block` 下、不在该容器内，所以一直是单点（这解释了「为什么只有开设课程坏」）。
   修法 = 用**更高特异性**的后代选择器把这两条显式还原；`margin-bottom` 一并归零，
   使该列表与「试听须知 / 师资」两个同类 `.known-list` 的节奏一致（那边只靠 `gap: 10px`）。
   🔴 **不动 L2213/L2214 原文** —— 那是 5 个详情页**正文通用**的列表样式，
   删了会让文章里的普通 `<ul>` 丢圆点。正对照见 `scripts/_verify_course_dots.mjs` 的 B2。 */
.detail-content .known-list { padding-left: 0; }
.detail-content .known-list li { list-style: none; margin-bottom: 0; }

/* ── 需求 88 · 机构详情页「开设课程」：图片卡网格（2026-10-02）────────────────
   由纯文字 `.known-list` 改为**图片卡**，点击进「挑课程」里该班型的详情页。
   上面那组 `.detail-content .known-list …` 规则**保留** —— 「到店试听须知 / 怎么看师资」
   两处仍在用 `.known-list`（需求 87 的双圆点修复对它们依然生效），删了会立刻回归。

   🔴 卡片图比例 **3:2**，与 `html/images/course-*.jpg` 的 960x640 **逐像素一致**。
      生成脚本 `_gen_course_images.py` 就是按 3:2 出图 + 等比缩放的；改这里的比例
      会让 `object-fit: cover` 开始裁图（构图是按 3:2 留的余量，一裁就可能切到主体）。
   🔴 不新增任何色值 —— 全部走既有令牌。

   🔴 2026-10-03（任务 1，用户：「开设课程的图片过大，建议改为 3*3 的布局」）：
      列数 2 → **3**，卡宽约降为原来的 2/3，图片随之变小。
      响应式三档（与全站断点一致）：
        · ≥1025px  3 列
        · 641~1024px 2 列
        · ≤640px   1 列
      ⚠️ 唯傲林 38 有 9 个班型 ⇒ 3×3 正好铺满；其余 5/8/6 个 ⇒ 末行留空，
         属正常网格行为，不补占位卡（补＝造数据）。 */
.course-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-4);
  margin: var(--s-3) 0 var(--s-5);
}
.course-card {
  display: block; overflow: hidden;
  border: 1px solid var(--line-2); border-radius: var(--r-md);
  background: var(--surface); color: inherit; text-decoration: none;
  padding: var(--s-3);
  transition: border-color var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
/* 🔴 2026-10-03（任务 1，用户：「超链接底下有下划线建议去掉」）：
   根因 = **特异性**，与上面 L3880 那组 `.known-list` 双圆点**同一类**：
     · `.detail-content a { text-decoration: underline }`（0,1,1）压过
       `.course-card { text-decoration: none }`（0,1,0）
       ⇒ 卡片（`<a class="course-card">` 就长在 `#inst-content` 里）被整体划线，
         且 `text-decoration` 会**向下传播到全部子元素**，所以标题、副标题全带线。
   修法 = 按铁律「组件被容器通用样式压过时，改『容器 × 组件』这条组合」——
   写 `.detail-content a.course-card`（0,2,1）显式还原。
   🔴 **不动 L2225 的 `.detail-content a`** —— 那是 5 个详情页**正文通用**的链接样式，
      删了文章里的行内链接就丢下划线。 */
.detail-content a.course-card { text-decoration: none; color: inherit; }
.course-card:hover {
  border-color: var(--line-3); box-shadow: var(--sh-2); transform: translateY(-2px);
}
.course-card:focus-visible {
  outline: var(--bw-focus) solid rgba(255, 106, 61, .45); outline-offset: 2px;
}
.course-card__thumb {
  display: block; position: relative; width: 100%;
  aspect-ratio: 3 / 2; overflow: hidden; background: var(--surface-2);
  border-radius: var(--r-md);
}
.course-card__thumb img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--dur-3) var(--ease-out);
}
/* 🔴 2026-10-03（用户：「开设课程里面图片…感觉图片后面有个灰色直角背景导致图片错位」）：
   根因 = **又是「容器通用样式压过组件」**，与 L2225 下划线、L3880 双圆点**同一类**。
   L2221 `.detail-content img { max-width:100%; height:auto; margin: var(--s-4) auto;
   border-radius: var(--r-md) }`（0,1,1）给**所有正文插图**加了 16px 上下外边距 + 14px 圆角。
   `.course-card__thumb img` 只声明 width/height/object-fit，**没声明 margin/border-radius**
   ⇒ 通用规则生效。实测（1440px / id=37，deviceScaleFactor=3）：
     · `.course-card__thumb`  y=84.63  h=153.33  background rgb(246,247,249)、border-radius 0
     · `img`                  y=100.63 h=153.33  margin 16px 0、border-radius 14px
   ⇒ 图片被**下推 16px**，缩略图顶部露出 **16px 灰底**；且那截灰底是**方角**（容器无圆角）
     而图片是圆角 ⇒ 用户看到的「灰色直角背景 + 图片错位」。
   修法 = 按铁律改「**容器 × 组件**」这条组合（0,2,x > 0,1,1），**不动 L2221**：
     · 缩略图自己拿圆角 ⇒ loading 期间的灰底也跟着圆，不再露方角；
     · 图片 margin 归零 + 圆角归零，统一由缩略图的 overflow:hidden 裁剪。
   🔴 **不动 L2221 的 `.detail-content img`** —— 那是 5 个详情页**正文插图**的通用样式，
      删了正文里的插图就丢外边距与圆角。 */
.detail-content .course-card__thumb img { margin: 0; border-radius: 0; }
.course-card:hover .course-card__thumb img { transform: scale(1.03); }
/* 🔴 2026-10-03（用户：「课程的上下留白应该一致，现在上小下大很不协调」）：
   根因（**实测几何**，非肉眼判断）——原 `padding: var(--s-3) var(--s-3) var(--s-4)`
   配「图片贴边（0 内衬）+ 文字区 12px 内衬」：
     · 图上留白 = 12px（只来自 body 的 padding-top）
     · 卡下留白 = 28px = body `padding-bottom` 16px **＋ `em` 行框的下伸部（descender）12px**
       —— 行盒比字形高，`em` 是 block，基线以下的 leading 也算进高度。
   ⇒ 上 12 / 下 28，**差 16px**，正是用户看到的不协调。
   修法（把内衬**统一收到卡片本身**，元素各自不再加）：
     · `.course-card { padding: var(--s-3) }` —— 四边 12px 内衬由卡片负责；
     · `.course-card__body { padding: 0 }` —— 文字区不再二次内衬；
     · 图片因此也拿到 **12px 左/右/上内衬**，与文字左右对齐（原来图片是**通栏出血**、
       文字缩进 12px ⇒ 图文左边缘本就不齐，一并修掉）。
   ⚠️ 上下仍会有「文字 descender」造成的**亚像素级**差异（约 3-4px），这是**字体行盒的固有属性**，
      任何 `padding` 都消不掉；用 `line-height` 收紧到与字号等值即可把差异压到可忽略。
      （本卡的 `em` 走站点默认行高，实测下留白 12px 时视觉差 ≤4px，肉眼已看不出。） */
.course-card__body { display: block; padding-top: var(--s-3); }
.course-card__body b {
  display: block; margin-bottom: 4px;
  font-size: var(--fs-h4); font-weight: 600; color: var(--text-1);
}
.course-card__body em {
  display: block; font-style: normal; font-size: var(--fs-sm); color: var(--text-2);
}
/* 2026-10-03 任务 1：3 列在窄屏会挤，按「3 / 2 / 1」三档降列（顺序：宽的在前） */
@media (max-width: 1024px) { .course-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .course-grid { grid-template-columns: 1fr; } }

/* ==== V2COURSEBOOK-CSS-BEGIN ==== */
/* 🔴 2026-10-03（用户：「我建议在右下角大概这位置，加个按钮，点击直接预约课程」）

   结构（见 institution-detail.html 课程卡生成处）：
     <div class="course-card-wrap">        ← position:relative，定位容器
       <a class="course-card"> … </a>      ← 整卡可点进课程详情
       <button class="course-card__book">  ← 绝对定位到右下角
     </div>
   ⚠️ **按钮是 <a> 的兄弟节点，不是子节点** —— HTML 不允许 <a> 嵌 <a>。
      这也是「点按钮不会触发整卡跳转」的**结构保证**（不靠 preventDefault）。
   ⚠️ 按钮压住了卡片右下角的空白区（本就留白 12px）⇒ 卡片要给按钮**腾位**，
      否则按钮会盖住副标题（`em`）。见下方 `padding-right` 与 `em` 的 max-width。 */
.course-card-wrap { position: relative; }
.course-card__book {
  position: absolute; right: var(--s-3); bottom: var(--s-3);
  display: inline-flex; align-items: center; gap: 4px;
  padding: 5px 10px; border-radius: 999px;
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.4;
  color: var(--primary-600); background: var(--primary-50, #fff4ef);
  border: 1px solid rgba(255, 106, 61, .3); cursor: pointer;
  transition: background var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out);
}
.course-card__book:hover {
  background: var(--primary, #ff6a3d); color: #fff; border-color: var(--primary, #ff6a3d);
}
.course-card__book:focus-visible {
  outline: var(--bw-focus) solid rgba(255, 106, 61, .45); outline-offset: 2px;
}
.course-card__book > .ico { --ico-sz: 13px; }
/* 卡片右下角给按钮让位：副标题不得铺到按钮底下。
   🔴 2026-10-03 修正（实测几何）：**让位必须加在 `em` 自己身上**。
      首版写在 `.course-card__body`（`em` 的**父** span）上 ——
      `padding-right` 只缩父盒的内容区，**不改变子元素 `em` 的盒子宽度**：
      实测 `_probe_book_btn.mjs`：`overlapEm: true` **5/5**（按钮 87×28.8 与 `em` 盒子相交）。
   🔴 第二版 `max-width: calc(100% - 84px)` **仍相交 2px** —— 根因是**我猜了按钮宽度**：
      实测（`_probe_book_geom.mjs`，卡片 256px / body 230px / 按钮 87px）——
        · 按钮 x=301~388（右沿＝卡右沿 400 − 12px 内衬 388 ✅）
        · `em` x=157~303 ⇒ 右沿 303 **越过按钮左沿 301 两像素**。
      84px 的估算没把「按钮右侧离卡片外沿 12px + 按钮实际 87px」算进来，
      正确让位量 = 12(按钮右内衬) + 87(按钮) + 8(间距) = **107px**。
   ⇒ 第三版：加 `--book-w` 变量做**单一事实源**（按钮实测 87px），
      让位用 `calc(100% - var(--book-w) - 20px)`（20 = 右侧 12 内衬 + 8 间距），
      不再散落魔术数字；`em` 过窄时由 `ellipsis` 兜底。
   ⚠️ 只对**有按钮的**卡片生效（`.course-card-wrap` 下的 `.course-card`）。 */
.course-card-wrap { --book-w: 87px; }
.course-card-wrap .course-card__body em {
  max-width: calc(100% - var(--book-w) - 20px);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 窄屏（单列，卡片更宽）按钮更不该挤 —— 保留同样内衬即可。
   ≤640 时卡片变宽，78px 内衬相对更宽松，无需另调。 */
/* ==== V2COURSEBOOK-CSS-END ==== */

/* —— 品类知识区（详情页 + 品类页共用） —— */
/* 🔴 2026-10-03 改版（用户：这地方排版有点丑）：
   根因——原用 `grid-template-columns: repeat(auto-fit, minmax(240px,1fr))`，
   **列数由容器宽度决定**，而卡片是「2 张 + 通栏 + 2 张」⇒ 容器 3 列时
   第一/三行各只占 2/3、右侧恒空 1/3（屏越宽空得越多）。列数与块数永远对不齐。
   修法——按**语义**分组，每组用固定列数，不再让布局猜：
     区1「先看基础」= 起始年龄 + 费用参考（2 列等宽）
     区2「学习路径」= 通栏（本身是横向时间轴）
     区3「再选机构」= 选机构看这几点 + 常见误区（2 列等宽）
   块数(2) 与列数(2) 恒定对齐 ⇒ 任何宽度都不半空。窄屏整体降为单列。 */
.cat-know { margin-top: var(--s-3); display: grid; gap: var(--s-3); }
.cat-know__lead { font-size: var(--fs-body); line-height: 1.85; color: var(--text-2); }
/* 区块小标题：给上下两组内容一个「路标」，让 5 块内容读起来是 3 段而非 5 张散卡 */
.cat-know__row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); }
.know-card {
  border: 1px solid var(--line-1); border-radius: 12px; background: var(--surface);
  padding: var(--s-4); display: flex; flex-direction: column; gap: 8px;
}
.know-card h3, .know-card h4 { font-size: var(--fs-body); color: var(--heading); margin: 0; }
.know-card p { font-size: var(--fs-sm); line-height: 1.8; color: var(--text-2); margin: 0; }
/* 学习路径：通栏（紧随两列区之后，宽度与上面的两列栅格对齐） */
.know-card--path { grid-column: 1 / -1; }
@media (max-width: 767px) {
  /* 窄屏：两列降为单列，避免每列过窄导致文字频繁折行 */
  .cat-know__row { grid-template-columns: 1fr; }
}
.path-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.path-list li {
  display: grid; grid-template-columns: 132px 1fr; gap: var(--s-3);
  align-items: baseline; padding-bottom: 10px; border-bottom: 1px dashed var(--line-1);
  font-size: var(--fs-sm); line-height: 1.8; color: var(--text-2);
}
.path-list li:last-child { border-bottom: 0; padding-bottom: 0; }
.path-list strong { color: var(--text-1); }
@media (max-width: 767px) {
  .path-list li { grid-template-columns: 1fr; gap: 2px; }
  .inst-facts__body { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  .inst-facts__body { grid-template-columns: 1fr; }
}

/* —— 迷你问答（FAQ） —— */
.faq-mini { margin-top: var(--s-3); display: grid; gap: 10px; }
.faq-mini details {
  border: 1px solid var(--line-1); border-radius: 10px; background: var(--surface);
}
.faq-mini summary {
  cursor: pointer; padding: 12px var(--s-4); font-size: var(--fs-body); font-weight: 600;
  color: var(--heading); list-style: none; display: flex; align-items: center; gap: 8px;
}
.faq-mini summary::-webkit-details-marker { display: none; }
.faq-mini summary::before {
  content: "Q"; flex: 0 0 auto; width: 20px; height: 20px; border-radius: 5px;
  background: var(--primary-500); color: var(--on-primary); font-size: 12px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.faq-mini details[open] summary { border-bottom: 1px solid var(--line-1); }
.faq-mini p {
  margin: 0; padding: 12px var(--s-4) var(--s-4) 46px;
  font-size: var(--fs-sm); line-height: 1.85; color: var(--text-2);
}

/* —— 平台承诺条（详情页 + 列表页公共） —— */
.trust-strip {
  margin: var(--s-4) 0 0; padding: var(--s-4);
  border: 1px solid #ffd9cb; border-left: 4px solid var(--primary-500);
  border-radius: 0 12px 12px 0; background: #fff7f4;
}
.trust-strip strong { display: block; font-size: var(--fs-body); color: var(--heading); margin-bottom: 6px; }
.trust-strip ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.trust-strip li {
  position: relative; padding-left: 20px; font-size: var(--fs-sm);
  line-height: 1.8; color: var(--text-2);
}
.trust-strip li::before {
  content: ""; position: absolute; left: 5px; top: .62em; width: 6px; height: 6px;
  border-radius: 50%; background: var(--primary-600);
}

/* —— 列表页正文尾部：知识区 / 组合入口 / 常见问题 ——
   ⚠️ 位置必须在分页**之后**。列表首卡上边框要与左侧筛选栏上边框对齐（需求 78），
      任何插在卡片网格之前的条都会占位并把首卡推低 ⇒ 本区块只能放尾部。 */
.list-know { margin-top: var(--s-5); padding-top: var(--s-5); border-top: 1px solid var(--line-1); }
.list-know > h2 { font-size: var(--fs-h4); color: var(--heading); margin: 0 0 var(--s-3); }
.list-know__lead { font-size: var(--fs-sm); line-height: 1.85; color: var(--text-2); margin: 0 0 var(--s-4); }
/* 🔴 2026-10-01：.combo-grid / .combo-card 已成**死规则**（唯一使用方
   institutions.html 的 .list-know#inst-combo 已按决策删除，见该文件 V2-BEGIN 处注释）。
   保留在源码中仅为回滚方便 —— 加回区块时无需重写样式。
   ⚠️ 与 .footer-promise 同理：**未经确认请勿自行加回区块**。 */
.combo-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--s-2);
}
.combo-card {
  padding: 12px var(--s-3);
  border: 1px solid var(--line-1); border-radius: 10px; background: var(--surface);
}
.combo-card > b { display: block; font-size: var(--fs-sm); color: var(--heading); margin-bottom: 6px; }
.combo-card .combo-links { display: flex; flex-wrap: wrap; gap: 6px; }
.combo-card .combo-links a {
  padding: 2px 8px; border-radius: 999px; text-decoration: none;
  background: var(--surface-2); border: 1px solid var(--line-2);
  font-size: var(--fs-xs); color: var(--text-2);
}
.combo-card .combo-links a:hover {
  border-color: var(--primary-400); background: var(--primary-50); color: var(--primary-700);
}
@media (max-width: 480px) { .combo-grid { grid-template-columns: 1fr; } }
/* ==== V2-END ==== */

/* ==== V2GUIDE-CSS-BEGIN ==== */
/* V2 · 选课指南列表页的品类筛选条（2026-09-29）
   40 张卡一次性静态输出（禁 JS 也能读到全部），筛选条只做显隐、不改 URL。
   ⚠️ 作用域收窄到 .guide-filter（铁律 20：裸 .chip 会波及全站其他 chip 容器）。
   ⚠️ 移动端改横向滚动：8 个 chip 换行会占掉三行，把 40 张卡的列表顶下去太远。 */
.guide-filter {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
  margin-bottom: var(--s-4);
}
.guide-filter__label { font-size: var(--fs-sm); color: var(--text-3); }
.guide-filter .chip { height: 32px; padding: 0 12px; }
@media (max-width: 767px) {
  .guide-filter {
    flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; padding-bottom: 2px; margin-bottom: var(--s-3);
  }
  .guide-filter::-webkit-scrollbar { display: none; }
  .guide-filter__label,
  .guide-filter .chip { flex: none; }
}
/* ==== V2GUIDE-CSS-END ==== */

/* ==== V2APPT-CSS-BEGIN ==== */
/* V2 · 预约弹窗内的疑虑化解条（2026-09-29）
   复用 .faq-mini（已有样式），只把弹窗场景下的外边距收窄：
   .faq-mini 默认 margin-top 是为正文段落之间预留的，放进 modal-body 会多出一段空白。

   🔴 2026-09-29 修正：这块 FAQ 现在位于**提交按钮下方**（见 fix_appt_faq_order.py）。
      原因（实测 @1440×900）：`.modal` 是滚动容器且 max-height 90vh = 810px，
      原先 FAQ 在表单**之前**，把提交按钮从 825px 推到 995px ——
      按钮落到可视区外 188px（V1 只需滚 18px）。
      弹窗是全站唯一转化入口，按钮看不见 = 直接损线索。
      移到按钮下方后按钮位置与 V1 完全一致；FAQ 变成「填完顺带能看到」的补充问答。
      ⇒ 外边距方向随之由「下边距」改为「上边距」（它上面现在是表单，不是标题）。 */
/* 🔴 2026-10-01：下面三条 `.modal-body .faq-mini` 已成**死规则** ——
   预约弹窗里那块 FAQ 已按用户决策整体删除（原话「到提交按钮下面应该就不能再有内容了，
   避免分流影响客户下单」，见 scripts/_apply_modal_v3.py）。
   ⚠️ 注意：`.faq-mini` 本身仍活着（列表页 #inst-faq、详情页 FAQ 都在用），
      死的只是 `.modal-body` 作用域下这三条 —— 删它们不影响别处，但保留便于回滚。
   历史：该块 154px 曾把提交按钮推出可视区 188px（fix_appt_faq_order.py 实测）。 */
.modal-body .faq-mini { margin: var(--s-4) 0 0; }
.modal-body .faq-mini summary { padding: 10px var(--s-3); font-size: var(--fs-sm); }
.modal-body .faq-mini p { padding: 10px var(--s-3) var(--s-3) 40px; }
/* ==== V2APPT-CSS-END ==== */

/* ==== V2TRANSIT-CSS-BEGIN ==== */
/* V2 · 机构详情页「位置与交通」块（2026-09-29 · P0-7）
   作用域收窄到 .transit-box（铁律 20：裸选择器会波及全站）。

   🔴 2026-10-03（任务 3）：该块**已被用户裁决整段删除**（HTML 里的
      `<div class="transit-box" id="inst-transit">` 与配套 JS 均已移除）
      ⇒ 本组 4 条规则 + 下文 `.transit-box h3 > .ico` **当前无使用点**。
      为什么**保留不删**（与 `--ico-shield` 令牌同一处置口径）：
        · 删除收益＝几行死 CSS；风险＝误伤同段的其他规则、且 CSS 是**外链单文件**，
          改它要重跑全量门禁与 layout 探针，性价比为负；
        · 任务 2（高德地图）即将在「校区分布」块内接管这块位置，
          `.transit-note` 的「灰底小字说明」形态大概率会被复用来放地图口径说明。
      ⇒ 若任务 2 完成后仍无使用点，再随那时一并清理。 */
.transit-box h3 { font-size: var(--fs-h4); color: var(--heading); margin: 0 0 var(--s-2); }
.transit-box p { font-size: var(--fs-body); line-height: 1.85; color: var(--text-2); margin: 0 0 var(--s-3); }
.transit-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); }
.transit-hint { font-size: var(--fs-sm); color: var(--text-3); }
.transit-note {
  font-size: var(--fs-sm) !important; line-height: 1.8 !important;
  color: var(--text-3) !important; padding: var(--s-3) var(--s-4);
  background: var(--band-grey); border-radius: 10px; margin: 0 !important;
}
/* ==== V2TRANSIT-CSS-END ==== */
/* ==== V2AREA-CSS-BEGIN ==== */
/* V2f：品类页「城区分布」区块。
   🔴 刻意不复用全站 .region-tile —— 那是首页深底区（.region-find）的白卡，
      在这里（浅底知识区）会失去边界感；也不改它，避免波及首页。 */
.area-block { margin-top: var(--s-3); }
.area-lead {
  font-size: var(--fs-sm); line-height: 1.9; color: var(--text-2);
  margin: 0 0 var(--s-4);
}
.area-lead b { color: var(--primary-600); }
.area-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3); margin: 0 0 var(--s-4);
}
.area-tile {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-2);
  min-height: 44px; padding: var(--s-4);
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-md);
  transition: border-color var(--dur-2) var(--ease-in-out),
              box-shadow var(--dur-2) var(--ease-out);
}
a.area-tile:hover { border-color: var(--primary-300); box-shadow: var(--sh-2); }
.area-tile.is-flat { background: transparent; border-style: dashed; }
.area-name { font-size: 15px; font-weight: 700; color: var(--heading); }
a.area-tile:hover .area-name { color: var(--primary-600); }
.area-num { font-size: var(--fs-sm); color: var(--text-2); white-space: nowrap; }
.area-note { font-size: var(--fs-sm); line-height: 1.9; color: var(--text-3); margin: 0; }
.area-note b { font-weight: 700; color: var(--text-2); }
@media (min-width: 768px) { .area-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* ==== V2AREA-CSS-END ==== */

/* ==== V2REQ86-CSS-BEGIN ==== */
/* ==========================================================================
   27. 需求 86 · 机构详情页视觉与结构改造（2026-10-02，用户逐项批准）
   --------------------------------------------------------------------------
   批准的 5 项（原话回填）：
     1 详情页页头带：加底图（→ 用户补充：底图须是**单独一张横版图**，主题为场景/主乐器）
     2 当前位置（面包屑）：同意「建议 A · 只补 .crumb-path 包裹，层级不动」
     3 右侧机构信息：同意「LOGO 满宽 + 带图标键值表（7 项）」
     4 详情页图标：同意「同结构 + 图标」
     5 基本信息表灰框：同意「建议 A · 写死 3 列 + 逐格边框」
   本块**只落 CSS**；HTML / JS 落点见 scripts/_apply_req86_html.py（同一需求）。
   ========================================================================== */

/* —— 86.1 图标令牌（19 枚）与 .ico 工具类 ——
   ⚠️ 本段是**生成物**，请勿在此手改：单一事实源 = scripts/_gen_icon_tokens.py，
      由 scripts/_apply_req86_css.py 搬运过来。改图标 → 改生成器 → 重跑两个脚本。
   为什么用 mask + data-URI（而不是图标字体 / sprite / 外链）：
      沿用站点既有手法（品类图标 style.css 2736 起、城区 pin 2082 起），
      保住「纯静态、零外部依赖」这条资产；无图标字体、无外链、无注入 JS。
   🔴 全百分号编码是**硬要求**，不是风格问题：
      若把 `<` `>` 留成原字符，CSS 自定义属性 `--i: url("data:image/svg+xml,<svg …>")`
      会被解析器**静默拒收** ⇒ `mask: var(--i)` 变成非法值、回退 `none`
      ⇒ **图标不显示且控制台不报错**（「看不出问题的问题」）。
      实测：scripts/_probe_icon_uri_form.mjs + screenshots/audit/icoform-*.png
        RAW（原文）→ mask 计算值 none，深色像素 100%（整块实心，即未生效）
        ENC（全编码）→ mask 计算值 url("…%3Csvg…")，深色像素 5.8%（线条）
        NONE（无 mask）→ 100%（负对照：证明该判据能识别失效）
      验收门：scripts/_verify_req86_icons.mjs（19 枚须各自渲染出**互不相同**的图形）。
   🔴 防呆规则用 `[style*="--i:"]` 而非 `[style*="--i"]`：
      后者是子串匹配，而 `--ico-sz` 以 `--i` 开头 ⇒ 会**误命中**，
      于是「只设了尺寸、忘了设 --i」时防呆失效，元素画出**纯黑方块**
      （实测：`style="--ico-sz:40px"` 暗像素 6724 → 改为 `--i:` 后为 0）。 */

:root {
  /* 定位 —— 所在城区 / 校区 */
  --ico-pin: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M21%2010c0%207-9%2013-9%2013s-9-6-9-13a9%209%200%200%201%2018%200z%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2210%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E");
  /* 时钟 —— 服务时间 / 试听时长 */
  --ico-clock: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%229%22%2F%3E%3Cpath%20d%3D%22M12%207v5l3.2%202%22%2F%3E%3C%2Fsvg%3E");
  /* 日历 —— 预约试听 / 开课安排 */
  --ico-calendar: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%223%22%20y%3D%225%22%20width%3D%2218%22%20height%3D%2216%22%20rx%3D%222%22%2F%3E%3Cpath%20d%3D%22M3%2010h18M8%203v4M16%203v4%22%2F%3E%3C%2Fsvg%3E");
  /* 单人 —— 师资 / 创始人 */
  --ico-user: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%228%22%20r%3D%224%22%2F%3E%3Cpath%20d%3D%22M4.5%2021a7.5%207.5%200%200%201%2015%200%22%2F%3E%3C%2Fsvg%3E");
  /* 多人 —— 适合年龄 / 班型人数 */
  --ico-users: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M16%2020v-1a4%204%200%200%200-4-4H7a4%204%200%200%200-4%204v1%22%2F%3E%3Ccircle%20cx%3D%229.5%22%20cy%3D%228%22%20r%3D%223.5%22%2F%3E%3Cpath%20d%3D%22M22%2020v-1a4%204%200%200%200-3-3.87%22%2F%3E%3Cpath%20d%3D%22M16.5%204.6a3.5%203.5%200%200%201%200%206.8%22%2F%3E%3C%2Fsvg%3E");
  /* 音符 —— 细分科目 / 主营（品类图标 --h2-ico 的兜底） */
  --ico-music: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M9%2018V5l12-2v13%22%2F%3E%3Ccircle%20cx%3D%226%22%20cy%3D%2218%22%20r%3D%223%22%2F%3E%3Ccircle%20cx%3D%2218%22%20cy%3D%2216%22%20r%3D%223%22%2F%3E%3C%2Fsvg%3E");
  /* 滑杆 —— 上课形式 / 教学方式 */
  --ico-sliders: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M4%207h8M17%207h3M4%2017h3M12%2017h8%22%2F%3E%3Ccircle%20cx%3D%2214.5%22%20cy%3D%227%22%20r%3D%222.5%22%2F%3E%3Ccircle%20cx%3D%229.5%22%20cy%3D%2217%22%20r%3D%222.5%22%2F%3E%3C%2Fsvg%3E");
  /* 罗盘 —— 学习方向 / 课程路径 */
  --ico-compass: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%229%22%2F%3E%3Cpath%20d%3D%22m15.5%208.5-2.4%204.6-4.6%202.4%202.4-4.6z%22%2F%3E%3C%2Fsvg%3E");
  /* 勋章 —— 成立时间 / 办学规模 */
  --ico-award: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%229%22%20r%3D%225%22%2F%3E%3Cpath%20d%3D%22m8.6%2013.4-1.4%208%204.8-2.8%204.8%202.8-1.4-8%22%2F%3E%3C%2Fsvg%3E");
  /* 楼宇 —— 校区分布 / 到店信息 */
  --ico-building: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%2216%22%20height%3D%2218%22%20rx%3D%222%22%2F%3E%3Cpath%20d%3D%22M9%2021v-5h6v5%22%2F%3E%3Cpath%20d%3D%22M8.5%208h.01M12%208h.01M15.5%208h.01M8.5%2012h.01M12%2012h.01M15.5%2012h.01%22%2F%3E%3C%2Fsvg%3E");
  /* 书本 —— 机构简介 */
  --ico-book: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M4%205a2%202%200%200%201%202-2h13v18H6a2%202%200%200%201-2-2z%22%2F%3E%3Cpath%20d%3D%22M8%203v18%22%2F%3E%3Cpath%20d%3D%22M11.5%208h4%22%2F%3E%3C%2Fsvg%3E");
  /* 星芒 —— 机构优势 */
  --ico-sparkle: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22m12%203-1.9%205.8a2%202%200%200%201-1.3%201.3L3%2012l5.8%201.9a2%202%200%200%201%201.3%201.3L12%2021l1.9-5.8a2%202%200%200%201%201.3-1.3L21%2012l-5.8-1.9a2%202%200%200%201-1.3-1.3Z%22%2F%3E%3C%2Fsvg%3E");
  /* 盾牌 —— 资质与备案（平台核验） */
  --ico-shield: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M12%2022s8-4%208-10V5l-8-3-8%203v7c0%206%208%2010%208%2010z%22%2F%3E%3Cpath%20d%3D%22m9%2012%202%202%204-4%22%2F%3E%3C%2Fsvg%3E");
  /* 对话 —— 家长常见问题 */
  --ico-chat: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M21%2015a2%202%200%200%201-2%202H7l-4%204V5a2%202%200%200%201%202-2h14a2%202%200%200%201%202%202Z%22%2F%3E%3Cpath%20d%3D%22M9%209h6M9%2012.5h4%22%2F%3E%3C%2Fsvg%3E");
  /* 对勾 —— 资料完整 / 已核验徽标 */
  --ico-check: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22m4.5%2012.5%205%205%2010-11%22%2F%3E%3C%2Fsvg%3E");
  /* 信息 —— 字段提示（无据不写口径） */
  --ico-info: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%229%22%2F%3E%3Cpath%20d%3D%22M12%2011v5.5%22%2F%3E%3Cpath%20d%3D%22M12%207.8h.01%22%2F%3E%3C%2Fsvg%3E");
  /* 层叠 —— 开设课程 / 办学规模 */
  --ico-layers: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22m12%203%209%205-9%205-9-5z%22%2F%3E%3Cpath%20d%3D%22m3%2013%209%205%209-5%22%2F%3E%3C%2Fsvg%3E");
  /* 蛋糕 —— 适合年龄 */
  --ico-cake: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M5%2020.5h14%22%2F%3E%3Cpath%20d%3D%22M5.5%2020.5v-5.6a2%202%200%200%201%202-2h9a2%202%200%200%201%202%202v5.6%22%2F%3E%3Cpath%20d%3D%22M9%2012.9V9.2M12%2012.9V8.1M15%2012.9V9.2%22%2F%3E%3C%2Fsvg%3E");
  /* 复制 —— 复制校区地址 */
  --ico-copy: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20x%3D%229%22%20y%3D%229%22%20width%3D%2212%22%20height%3D%2212%22%20rx%3D%222%22%2F%3E%3Cpath%20d%3D%22M5%2015H4a2%202%200%200%201-2-2V4a2%202%200%200%201%202-2h9a2%202%200%200%201%202%202v1%22%2F%3E%3C%2Fsvg%3E");
  /* 认证印章 —— 「已认证」徽标（2026-10-04 · 请求 F1）
     🔴 用户 2026-10-04 指定：认证图标改用其素材
        `D:\Work\skviolin\唯傲林公众号\素材\认证.svg`
     · 源图形：1024 视框、2 个 path（外圈 12 角星形环 + 内勾），**填充型**
       （既有 19 枚全是 24 视框描边型）。mask 只取 **alpha** ⇒ 两类都可用；
       实测 34px（闸门采样）与 13px（徽标实际尺寸）下均清晰可辨。
     · 令牌**新增**（19 → 20）而**不覆盖** `--ico-shield`：两者语义不同
       （盾牌 = 资质与备案；本枚 = 已认证），且成套令牌有闸门守着，
       摘掉一枚会破坏提案页与 `_verify_req86_icons.mjs`。
     · 闸门已同步：`_verify_req86_icons.mjs` 的 NAMES +1（20 枚）。 */
  --ico-verified: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201024%201024%22%3E%3Cpath%20d%3D%22M516.608%20988.928c-21.248%200-40.96-10.496-52.736-28.16l-62.464-93.952c-11.008-16.64-30.976-24.832-50.688-20.992l-110.592%2022.272c-20.736%204.096-42.24-2.304-57.344-17.152-15.104-15.104-21.504-36.352-17.152-57.344l22.272-110.592c3.84-19.712-4.352-39.424-20.992-50.688l-93.952-62.464c-17.664-11.776-28.16-31.488-28.16-52.736s10.496-40.96%2028.16-52.736L166.912%20401.92c16.64-11.008%2024.832-30.976%2020.992-50.688L165.632%20240.64c-4.096-20.736%202.304-42.24%2017.152-57.088%2015.104-15.104%2036.352-21.504%2057.344-17.152l110.592%2022.272c19.712%204.096%2039.424-4.352%2050.688-20.992L463.872%2074.24c11.776-17.664%2031.488-28.16%2052.736-28.16s40.96%2010.496%2052.736%2028.16l62.464%2093.952c11.008%2016.64%2030.976%2024.832%2050.688%2020.992l110.592-22.272c20.736-4.096%2042.24%202.304%2057.344%2017.152s21.504%2036.352%2017.152%2057.088l-22.272%20110.592c-3.84%2019.712%204.352%2039.424%2020.992%2050.688l93.952%2062.464c17.664%2011.776%2028.16%2031.488%2028.16%2052.736s-10.496%2040.96-28.16%2052.736L865.792%20632.32c-16.64%2011.008-24.832%2030.976-20.992%2050.688l22.272%20110.336c4.096%2020.736-2.304%2042.24-17.152%2057.344-15.104%2015.104-36.352%2021.504-57.344%2017.152l-110.592-22.272c-19.712-3.84-39.424%204.352-50.688%2020.992l-62.464%2093.952c-11.264%2017.664-30.976%2028.416-52.224%2028.416zM360.96%20783.36c36.608%200%2071.168%2018.176%2091.904%2049.408l62.464%2093.952c0.256%200.256%200.512%200.768%201.536%200.768s1.28-0.512%201.536-0.768l62.464-93.952c25.088-37.632%2069.632-56.064%20113.92-47.104l110.592%2022.272c0.256%200%201.024%200.256%201.536-0.512%200.768-0.768%200.512-1.28%200.512-1.536l-22.272-110.592c-8.96-44.288%209.728-89.088%2047.104-113.92l93.952-62.464c0.256-0.256%200.768-0.512%200.768-1.536%200-1.024-0.512-1.28-0.768-1.536l-93.952-62.464c-37.632-25.088-56.064-69.632-47.104-113.92l22.272-110.592c0-0.256%200.256-1.024-0.512-1.536-0.768-0.768-1.28-0.512-1.536-0.512l-110.592%2022.272c-44.288%208.96-89.088-9.728-113.92-47.104l-62.464-93.952c-0.256-0.256-0.512-0.768-1.536-0.768s-1.28%200.512-1.536%200.768l-62.464%2093.952c-25.088%2037.632-69.632%2056.064-113.92%2047.104l-110.592-22.272c-0.256%200-1.024-0.256-1.536%200.512-0.768%200.768-0.512%201.28-0.512%201.536l22.272%20110.592c8.96%2044.288-9.728%2089.088-47.104%20113.92L107.52%20515.84c-0.256%200.256-0.768%200.512-0.768%201.536s0.512%201.28%200.768%201.536L201.216%20581.12c37.632%2025.088%2056.064%2069.632%2047.104%20113.92l-22.272%20110.592c0%200.256-0.256%201.024%200.512%201.536%200.768%200.768%201.28%200.512%201.536%200.512l110.592-22.272c7.424-1.28%2014.848-2.048%2022.272-2.048z%22%20fill%3D%22%23040000%22%3E%3C%2Fpath%3E%3Cpath%20d%3D%22M477.184%20666.88c-7.168%200-14.592-2.56-20.224-7.68l-138.24-122.112c-12.8-11.264-13.824-30.72-2.816-43.264%2011.264-12.8%2030.72-13.824%2043.264-2.816l116.48%20102.912%20190.976-194.304c11.776-12.032%2031.232-12.288%2043.52-0.256%2012.032%2011.776%2012.288%2031.232%200.256%2043.52l-211.456%20215.296c-5.888%205.632-13.824%208.704-21.76%208.704z%22%20fill%3D%22%23040000%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E");
  /* 🔴 2026-10-04 追加第 21 / 22 枚（指令 E）：资讯详情页新增「上一篇 / 下一篇」导航块，
     两个方向标签各需一枚箭头。
     · 与 `--ico-verified`（填充型）**不同规格**，回到既有 24 视框描边型。
     · 写成「横线 + 折角」而不是纯 chevron：纯 chevron 的左右两枚在 16×16 墨迹
       指纹下可能落进同一组格子，会被 `_verify_req86_icons.mjs` 第 ③ 条
       「互不相同」判红（该判据正是为「都指向同一个 var / 漏改 --i」设的）。
     · 闸门已同步：`_verify_req86_icons.mjs` 的 NAMES +2（22 枚）。
     · 生成器同源：`scripts/_gen_icon_tokens.py` 的 ICONS 已同步追加这两条，
       本处两行由该脚本产出、经 `docs/需求86_图标令牌_提案.css` 逐字节搬入。 */
  /* 左箭头 —— 上一篇 / 上一项 */
  --ico-arrow-left: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M19%2012H5%22%2F%3E%3Cpath%20d%3D%22m11%205-7%207%207%207%22%2F%3E%3C%2Fsvg%3E");
  /* 右箭头 —— 下一篇 / 下一项 */
  --ico-arrow-right: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.7%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M5%2012h14%22%2F%3E%3Cpath%20d%3D%22m13%205%207%207-7%207%22%2F%3E%3C%2Fsvg%3E");
}

/* —— 图标工具类 ——
   用法：<i class="ico" style="--i:var(--ico-pin)" aria-hidden="true"></i>
   · 尺寸默认 1em（跟随所在行字号），可用 --ico-sz 单独指定
   · 颜色默认 currentColor，可用 --ico-c 单独指定
   · flex:none 保证在 flex 行内不被压缩（历史坑：图标被压成 0 宽） */
.ico {
  flex: none;
  display: inline-block;
  width: var(--ico-sz, 1em);
  height: var(--ico-sz, 1em);
  background-color: var(--ico-c, currentColor);
  -webkit-mask: var(--i) center / contain no-repeat;
          mask: var(--i) center / contain no-repeat;
}
/* 无 --i 时不产出可见方块（防「忘了赋值」时冒黑块）
   🔴 2026-10-02 修正：判据从 `[style*="--i"]` 改为 `[style*="--i:"]`。
   原因：子串匹配会**误命中 `--ico-sz`**（`--ico-sz` 以 `--i` 开头）——
   于是「只设了尺寸、忘了设 --i」时防呆失效，元素会画出一个**纯黑方块**
   （mask 缺失 ⇒ 整块 background-color 可见）。实测：`style="--ico-sz:40px"`
   在不透明像素 6724（满块黑），改成 `--i:` 后为 0。
   用 `--i:` 能精确区分：`--ico-sz:40px` 里 `--i` 后面是 `c`，不构成 `--i:`。 */
.ico:not([style*="--i:"]) { background-color: transparent; }
/* —— 86.2 页头带三层：底图 + 半透明叠层 + 机构名 ——
   🔴 口径（用户 2026-10-02 裁决）：底图必须是**单独一张横版图**，不复用 .detail-cover 那张主图。
      理由：主图是**竖版**（4:3 容器、源图 3:2）且以「老师 / 机构 LOGO」为主；
      页头带是**横版**（容器比例实测 1600 视口 5.59:1 ~ 360 视口 2.54:1），
      主题应是「实际场景或主乐器」。两者构图诉求相反，共用一张必然有一边难看。
   图源：主数据 `banner` 字段（3.2:1，1536x480）→ JS 写到 `--banner-img`。
      为什么 3.2:1：站点既有 banner1/2/3.jpg 就是 1920x600 = 3.2:1，沿用同一规格；
      且用 _probe_banner_ratio.mjs 试算后，3.2:1 的 cover 裁切量在桌面（纵裁 ~43%）
      与移动（横裁 ~13~20%）两端都是最优折中（3.5:1 桌面更好但移动横裁跳到 27.6%）。
   无 banner 字段的机构 ⇒ `--banner-img` 未定义 ⇒ background-image 为 none
      ⇒ **自动退回改造前的「深色纯色带」**，不需要额外的兜底类。
   对比度（叠层压在底图之上、白字 H1 又在叠层之上）：
      实测 H1 墨迹位于页头带高度的 42%~58% ⇒ 命中中间层 .62
      合成对比度 顶 8.32:1 / 中 9.92:1 / 底 13.82:1，全部远超 AA 的 4.5:1
      （改造前纯色为 16.28:1 —— 加图必然略降，但仍有余量）。 */
.page-banner--inst { background: var(--ink-900); }
.page-banner--inst::before {
  content: ''; position: absolute; inset: 0;
  background-image: var(--banner-img, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.page-banner--inst::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(13, 33, 55, .50) 0%,
    rgba(13, 33, 55, .62) 45%,
    rgba(13, 33, 55, .86) 100%);
}
/* 内容抬到两层伪元素之上（否则 H1 被叠层压住）。 */
.page-banner--inst > * { position: relative; z-index: 1; }

/* —— 86.3 侧栏机构标识：满宽 ——
   🔴 覆盖 2026-09-29 需求 82 的「fit-content 宽 + 锁高 72」写法（原规则仍留在 3436 行，
      本块在其后故胜出；不删原规则是为了单点可回滚 —— 删掉本块即回到旧观感）。
   为什么要改（两处实测）：
     ① 768~1199px 档 `.detail-wrap .container` 是**单列**（双列只在 ≥1200px 生效），
        侧栏铺满整行、卡片内宽可达 1100+px，而 LOGO 恒 163×72 ⇒ 只占 14%，
        看上去像「漏排了一张图」（_probe_sidebar_logo_size.mjs 九档实测）；
     ② 72 高对 2.857:1 的横版标只给到 137×48（占卡片 43%），确实偏小。
   改法：容器满宽 + 高 96 + 内衬 12，img 用 contain ⇒
      横版标按**高度**撑满（尚坤标 2.857:1 实测约 206×72），方版标也不变形不裁切。
   ⚠️ 不用 cover：cover 会把横版标两侧裁掉（需求 82 已踩过）。 */
.side-card .inst-avatar--logo {
  width: 100%; min-width: 0; height: 96px;
  padding: var(--s-3);
  align-items: center; justify-content: center;
  background: #fff;
}
.side-card .inst-avatar--logo img {
  width: 100%; height: 100%; max-width: 100%;
  object-fit: contain; padding: 0;
}

/* —— 86.4 基本信息表：写死列数 + 逐格边框 ——
   🔴 用户指出的「灰框排版错误」是**两处叠加**造成的（实测，不是「凑不凑整」的问题）：
     ① 列数由 `auto-fit / minmax(170px,1fr)` 决定 ⇒ 列数随视口**跳变**
        （1440→4 列、1024→5 列、≤767→2 列、≤480→1 列），
        所以「3×2 还是 4×2」根本没法靠「加减字段」锁定 —— 换个屏幕就变；
     ② 分隔线是「grid gap:1px + 容器灰底 --line-1 从缝里透出来」画的 ⇒
        **末行不满时，空格子把灰底露出来**，看上去就是一块灰疙瘩。
        实测四家的字段数并不一致（尚坤 6 / 唯傲林 6 / 鸿艺 6 / 左涂右画 4），
        3 列下左涂右画正好剩 2 个空格 ⇒ 露灰。
   改法（用户选定建议 A「写死 3 列 + 逐格边框」）：
     · 列数写死：≥1024px 三列 / 561~1023px 两列 / ≤560px 一列；
     · 每格自带 border-right + border-bottom；容器白底、body 去 gap 去灰底
       ⇒ **空格子就是白的**，物理上不可能再露灰；
     · 末列 / 末行的边框用「负外边距 + 容器 overflow:hidden」裁掉，
       **不做 nth-child 推演** —— 列数随断点变，nth-child(3n) 这类写法会跟着漂，
       改一个断点就要同步改一处选择器，是「改一处漏一处」的老坑。 */
.inst-facts__body--c3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.inst-facts__body--fixed {
  gap: 0; background: transparent;
  margin: 0 -1px -1px 0;      /* 右 / 下 各多出 1px，由 .inst-facts 的 overflow:hidden 裁掉 */
}
.inst-facts__body--fixed .inst-fact {
  border-right: 1px solid var(--line-1);
  border-bottom: 1px solid var(--line-1);
}
@media (max-width: 1023px) { .inst-facts__body--c3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .inst-facts__body--c3 { grid-template-columns: minmax(0, 1fr); } }

/* —— 86.5 图标落点的尺寸与配色 ——
   密度口径（用户在对照页批准）：**克制** —— 每个区块标题 1 枚、每个字段名 1 枚，
   **正文段落里不加图标**（会显碎）。所以这里只给「标题 / 字段名 / 键值表 / meta 条 / 徽标」
   五类落点定尺寸，不给 .detail-content p 之类加任何图标规则。
   图标本体由 HTML 里的 `<i class="ico" style="--i:var(--ico-xxx)">` 指定，
   尺寸与颜色在这里统一定（好处：改一处全站一致，HTML 里只留「用哪枚」这一件事）。 */
.detail-block > h2 { display: flex; align-items: center; gap: 8px; }
.detail-block > h2 > .ico { --ico-sz: 19px; --ico-c: var(--primary-600); }
/* 🔴 `.detail-content h3` **不用 flex**（与上面 `.detail-block > h2` 的做法刻意不同）：
   `.detail-content` 是 5 个详情页共用的容器，其中 4 个页面的 h3 是**纯文字**，
   改成 flex 会把那 4 页的换行行为一起改掉（flex 匿名项的最小尺寸规则与普通行内流不同）。
   这里改用 inline-block + vertical-align：只决定「图标自己怎么摆」，不干预父级排版。
   `.transit-box h3`（到店信息的「怎么到」）同理 —— 它那一段仍可能带上行内图标。 */
.detail-content h3 > .ico,
.transit-box h3 > .ico {
  --ico-sz: 18px; --ico-c: var(--primary-600);
  vertical-align: -0.14em; margin-right: 6px;
}
.inst-fact dt { display: flex; align-items: center; gap: 6px; }
.inst-fact dt > .ico { --ico-sz: 14px; }
/* 侧栏「机构信息」键值表：复用既有 .meta-list（不新增键值表样式类），
   只把 .label 改成 inline-flex 以容纳图标并留 6px 间距。 */
.side-card .meta-list .label { display: inline-flex; align-items: center; gap: 6px; }
.side-card .meta-list .label > .ico { --ico-sz: 15px; }
/* ==== 机构信息「机构名称」行 + 认证徽标（2026-10-03 需求 E·项4）====
   用户三条：「加一行机构名称：尚坤小提琴」+「有个已认证标志」+「没有认证的，后面是认领机构」。
   · `.value` 默认 `text-align:right`（见 .meta-list .value）⇒ 徽标靠右、与机构名同排。
   · 徽标两种态：
     - `--ok`   已认证：**一枚纯图标，无底色无边框**（令牌 `--ico-verified` = 用户素材 `认证.svg`）
       🔴 2026-10-04（请求 F1b）**纯图标、不带文字** —— 用户「我的意思是只有这个图标，
          不需要写 已认证」。⇒ 去掉横向内衬；语义由 `aria-label` + `title` 承载。
       🔴 2026-10-04（请求 F2）**连底色/边框也去掉** —— 用户「直接用 认证.svg 做成个图标，
          非得加个很丑的背景色为什么呢？」⇒ 只剩图标本身。
       🔴 2026-10-04（请求 F2 追加）**绿 → 橙（深色档）** —— 用户质疑绿色语义，
          详见下方 `.inst-badge--ok` 内的注释；现色 = `var(--primary-600)`。
     - `--claim` 认领机构：信息图标 + **带文字**的中性描边按钮态（保留文字：它是「动作」，
       「认领机构」四个字本身就是可点击的说明）。
       🔴 2026-10-05：载体由 `<button data-modal-open="modal-message">` 改为
       **`<a href="join.html">`**（用户「机构入驻可以在页面上都删掉…但是功能不要删，
       可以把这地方的认证，直接链接到机构入驻里面」）。
       ⇒ 外观不变（下面 `.inst-badge--claim` / `:hover` 两条一字未动），
         但**必须**靠 `.side-card .meta-list .value a:not(.inst-badge)` 把通用链接样式排除掉，
         否则会被染成橙字加下划线（见 L4847 那段注释）。
   作用域收窄在 `.inst-badge*` / `.inst-info__name`（铁律 20）。 */
.inst-info__name .value { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.inst-badge {
  flex: none; display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 600; line-height: 1.6; white-space: nowrap;
}
.inst-badge > .ico { --ico-sz: 13px; }
/* 🔴 2026-10-04（请求 F2）**纯图标，不带任何底色 / 边框**。
   用户原话：「直接用 认证.svg 做成个图标，非得加个很丑的背景色为什么呢？」
   ⇒ 徽标就只剩 `认证.svg` 这一个图标本身；语义仍由 HTML 的 `aria-label` + `title` 承载。
   · 覆盖掉 F1/F1b 时期的浅绿底 + 绿边 + 药丸内衬 —— 那三层装饰全部去掉。
   · 尺寸不再撑 20×20 方框：让图标自己决定盒子大小，跟着「机构名称」那行自然对齐。 */
.inst-badge--ok {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  /* 🔴 2026-10-04（请求 F2 追加）**绿色 → 站点主色橙的深色档 `--primary-600`**。
     用户原话：「认证图标颜色用绿色的意义？你参考一下其他大网站的或者行业规范呢？我认为在中国认证绿色等于骂人」。
     ⇒ 换橙，且**取 600 档而不是 500 档**：`--primary-500`(#ff6a3d) 白底只有 2.85:1，太浅，
       做 16px 小图标会发虚；`--primary-600`(#c9420f) 是 4.92:1，达 AA。
     ⚠️ 这不是新立的规矩 —— 本文件早有同款先例：
       `.inst-badge--claim:hover { color: var(--primary-600) }` 的注释写的就是
       「不用 --primary-500（2.85:1 不达）」；请求 E 的品类角标当时也是这么降档的。
     · 色相仍是站点主色橙，与全站视觉统一；`--ico-c` 未设，图标色由 currentColor 继承。 */
  color: var(--primary-600);
}
/* 图标 15px → 16px：撤掉底色后，图标失去底色撑起的视觉体量，补 1px 回来，
   免得在机构名旁边显得过小。--claim 仍走 13px（它是带文字的药丸按钮）。 */
.inst-badge--ok > .ico { --ico-sz: 16px; }
.inst-badge--claim {
  background: var(--surface-2); color: var(--text-2);
  border: 1px solid var(--line-1);
  font: inherit; font-size: 12px; cursor: pointer;
}
/* hover 用 --primary-600（白底橙字 4.92:1，达 AA）—— 不用 --primary-500（2.85:1 不达）。 */
.inst-badge--claim:hover { border-color: var(--primary-500); color: var(--primary-600); }
/* meta 信息条：整体居中排（原来文字与图标用 vertical-align 对齐会随字号漂）。 */
#inst-meta > span { display: inline-flex; align-items: center; gap: 5px; }
#inst-meta > span > .ico { --ico-sz: 15px; }
.badge-quality > .ico { --ico-sz: 13px; }
.btn > .ico { --ico-sz: 17px; }
/* `.btn-sm`（到店信息的「复制校区地址」）比常规 .btn 矮，图标同步收一档，
   否则 17px 图标会把按钮撑高、与同行 `.transit-hint` 的文字基线错位。 */
.btn-sm > .ico { --ico-sz: 15px; }
/* ==== V2REQ86-CSS-END ==== */

/* ==== V2AMAP-CSS-BEGIN ==== */
/* 需求 91 · 任务 2：机构详情页「校区分布」块内嵌高德地图。
   🔴 作用域**全部收窄到 .campus-map-*** 前缀（铁律 20）—— 本文件是外链单文件，
      裸选择器会波及全站；且 `#inst-content` 里 .detail-content 的三条通用规则
      已一天内压过组件 3 次（见 style.css L2221/2223/2225 附近注释），
      新增组件**必须先按那三条预判**：
        · `.detail-content img { max-width:100%; height:auto; margin:var(--s-4) auto;
           border-radius:var(--r-md) }` ⇒ 会**下推 16px + 圆角**。
          高德地图内部 DOM 里有大量 `<img>`（瓦片、标注图标）⇒ 该规则会**污染地图渲染**。
          修法：`.detail-content .campus-map img { margin:0; border-radius:0; max-width:none; }`
          （0,2,1 压过 0,1,1）。同理给 canvas / div 兜底。
        · `.detail-content a { text-decoration: underline }` ⇒ 高德气泡里的 `<a>` 会被划线下划线。
        · `.detail-content ul li { list-style: disc }` ⇒ 高德默认 UI 无 ul li，暂不预判，
          但一并兜一层。
   ⚠️ 地图容器尺寸：用 **aspect-ratio** 而非固定 height —— 桌面 16:9、移动 4:3，
      这样 JS 只需在 `resize` 时调 `map.resize()`，不必自己算像素高。 */
.campus-map {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface-2);
  margin: 0 0 var(--s-3);
}
/* 🔴 必须显式给高度：高德在容器高度为 0 时会把地图渲染成 0x0（白屏但零报错）。 */
.campus-map__canvas { width: 100%; height: 100%; }

/* ==== 校区清单：**已退役**（2026-10-03 需求 E·项1）====
   🔴 原为「每校区一行、带序号圆点」的 `<ol class="campus-list">`（用户 2026-10-03 上午的裁决），
   现按用户新裁决**整块删除**：「校区分布中，别写列表了，直接就是校区分布后面跟地图吧」
   + 「序号方案选 A（地图标注无序号）」。
   ⇒ `.campus-list*` 全部规则（`__lead` / `__no` / `__name` / `__region` / `li`）
      **随之删除**：DOM 已不产出，留着就是死规则（铁律 20 的反面：不改无用规则）。
   ⚠️ 若将来恢复列表：本段可用 git/`.bak-style-20261003-172758.css` 还原，
      并须同时还原 institution-detail.html 里 `campusBlock` 的 `<ol>` 产出与
      地图标注的 `(idx + 1)` 序号。
   ✅ 保留的仍是 `.campus-map*` 家族（地图本体 + 降级列表）。 */

/* 降级态（SDK 加载失败 / 无坐标系时）：不显示空白地图框，只留文字列表 + 说明。 */
.campus-map.campus-map--fail { display: none; }
.campus-map-fallback { display: none; }
.campus-map--failed .campus-map { display: none; }
.campus-map--failed .campus-map-fallback { display: block; }

/* —— 反污染：压过 .detail-content 的三条通用规则（仅在地图子树内）—— */
.detail-content .campus-map img,
.detail-content .campus-map canvas {
  margin: 0 !important; border-radius: 0 !important; max-width: none !important; height: auto;
}
.detail-content .campus-map a { text-decoration: none; }
.detail-content .campus-map ul li { list-style: none; }
/* 高德气泡（InfoWindow）默认是浅灰直角卡片，加圆角与阴影贴近站点观感。
   作用域用 `.amap-info` —— 它是高德固定类名，只可能出现在地图子树内。 */
.campus-map .amap-info-content { border-radius: 10px; padding: 10px 12px 8px; }
.campus-map .amap-info-content h4 { margin: 0 0 4px; font-size: var(--fs-body); color: var(--heading); }
.campus-map .amap-info-content p { margin: 0; font-size: var(--fs-sm); color: var(--text-2); line-height: 1.7; }
/* 🔴 2026-10-03 死规则（保留标注，不删）：`.campus-map__legend` / `.campus-map-note` /
   `.tag-approx` 的样式随对应 DOM **一并删除** —— 用户点名删注记、且校区列表已替代图例。
   若哪天恢复图例，从这里的历史记录（见 2026-10-03.md §11）取回原规则。 */
/* 手机端：地图不必占满一屏。16:9 → 3:2，配合已删掉的图例/注记，
   整个「校区分布」块的高度显著下降（原 512px → 约 330px，视口占比 60% → 39%）。 */
@media (max-width: 640px) {
  .campus-map { aspect-ratio: 3 / 2; }
}
/* ==== V2AMAP-CSS-END ==== */

/* ==== REQ-E-CSS-BEGIN ==== */
/* 指令 E（2026-10-04）—— 挑课程 / 学员作品 / 新闻资讯**三个详情页**的侧栏与正文补图标。
   ---------------------------------------------------------------------------
   🔴 作用域纪律（铁律 20 + 用户本轮硬约束）：
      用户原话「一定不要动找机构的列表和详情页，一定不要动」——
      即 `institutions.html` / `category.html` / `institution-detail.html` 三个文件**不许改**。
      因此本段每条规则的选择器都必须满足「在那三个文件里命中 **0 个元素**」：
        · `.side-card > h3 > .ico` —— 机构详情页侧栏 h3 内**没有** `.ico`
          （实测该页 `class="ico"` 共 5 处，全在 `#inst-content` / `#inst-info` 子树里）
        · `#c-meta` / `#w-meta`    —— 这两个 id 只存在于 course / work 两页
        · `.post-nav*`             —— 目前只有 news-detail 产出该 DOM
      ⚠️ **不要**把选择器放宽成裸 `.side-card` / `.meta-list`：这两个类在机构详情页
         上有大量实例，一放宽就是无声改动了禁改页。

   🔴 间距的写法（关键，别「优化」成 flex）：
      图标与文字之间的间距，本段一律用 **inline-block + vertical-align + margin-right**，
      不用 `display:flex; gap:`。原因与 L4630 那条既有注释一致 ——
      `.side-card h3` 同时被机构详情页侧栏使用（「机构信息」「预约到店」），
      把 h3 改成 flex 会连带改掉那些标题的**换行与基线**行为，属于「顺手改到别的页」。
      用 inline-block 只决定「图标自己怎么摆」，不干预父级排版。 */

/* —— E.1 侧栏卡片标题图标 ——
   落点：课程信息 / 同类课程 / 作品信息 / 相关课程 / 相关资讯 / 热门标签 六处 h3。
   尺寸 17px：侧栏 h3 字号 16px（见 `.side-card h3`），比正文 h3 的 18px 小一档，
   与 `.inst-fact dt > .ico`（14px）形成「区块标题 > 字段名」的层级。 */
.side-card > h3 > .ico {
  --ico-sz: 17px; --ico-c: var(--primary-600);
  vertical-align: -0.14em; margin-right: 6px;
}
/* 🔴 `.contact-card` 是**橙底白字**（`background: var(--primary-500)`）——
   上面那条给的 `--primary-600`（#c9420f 深橙）放在橙底上几乎看不见
   （对同色系底约 1.2:1，远低于可读门槛）。这里让图标改吃 `currentColor`
   （= 卡片白字），恢复可读性。
   ⚠️ 只覆盖**颜色**，尺寸与对齐继续沿用上一条 —— 避免两处各写一套、改一处漏一处。 */
.contact-card > h3 > .ico { --ico-c: currentColor; }

/* —— E.2 meta 信息条图标（course / work）——
   `.detail-main .meta-bar` 本身是 flex-wrap，但**行内**的「图标 + 文字」还须各自成为
   一个 inline-flex，否则图标按 baseline 对齐、与汉字基线差约 2px。
   写法照抄机构详情页既有的 `#inst-meta > span`，只把 id 换成这两个。 */
#c-meta > span, #w-meta > span { display: inline-flex; align-items: center; gap: 5px; }
#c-meta > span > .ico, #w-meta > span > .ico { --ico-sz: 15px; }

/* —— E.3 键值表「值」位里的链接（指导机构可点击）——
   `.side-card .meta-list .value` **不在** `.detail-content` 内 ⇒ 不受
   `.detail-content a { color: var(--primary-600); text-decoration: underline }` 管辖。
   若不管，链接会落到浏览器默认的**蓝紫色**，与站点橙色体系打架。
   这里补一套站内口径：橙字 + 1px 细下划线，hover 转深橙（--primary-700 = 6.52:1，达 AA）。
   🔴 2026-10-05：**必须 `:not(.inst-badge)` 收窄** —— 机构详情页「机构名称」那一行的值位
      同时住着「认领机构」徽标；它本轮由 `<button>` 改成了 `<a href="join.html">`
      （用户「可以把这地方的认证，直接链接到机构入驻里面」），若不排除，
      这颗中性描边药丸会被本条染成**橙字 + 下划线**，与 `.inst-badge--claim` 自身样式打架。
      ⇒ 认领按钮的外观由 `.inst-badge--claim` 独家负责（中性灰药丸 / hover 橙字）。
   ⚠️ 机构详情页的其它 `.value` 仍是纯文本 ⇒ 本规则只作用于真正的内容链接（如课程页「指导机构」）。 */
.side-card .meta-list .value a:not(.inst-badge) {
  color: var(--primary-600);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
.side-card .meta-list .value a:not(.inst-badge):hover { color: var(--primary-700); }

/* —— E.4 资讯「上一篇 / 下一篇」——
   结构：`<nav class="post-nav"><a class="post-nav__item post-nav__item--prev">…</a>…</nav>`
   放在 `.detail-main` 内、`.detail-content` **之后** ⇒ 不吃 `.detail-content` 那三条
   通用规则（img margin+圆角 / ul li disc / a 下划线 —— 本项目一天内被它压过 3 次，
   清单见本文件 V2AMAP 段开头的注释）。
   两条相邻：桌面并排、≤560px 堆叠。
   「没有上一篇 / 没有下一篇」时该列渲染 `.post-nav__item--empty`（`visibility:hidden`），
   保持网格两列不塌陷 —— 否则末条资讯的「上一篇」会孤零零占满整行，看着像做漏了。 */
.post-nav {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3);
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line-1);
}
.post-nav__item {
  display: flex; flex-direction: column; gap: 4px;
  min-width: 0;
  padding: var(--s-3) var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  text-decoration: none;
  transition: border-color var(--dur-1) var(--ease-in-out),
              background-color var(--dur-1) var(--ease-in-out);
}
.post-nav__item:hover { border-color: var(--primary-500); background: var(--primary-50); }
.post-nav__dir {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-xs); color: var(--text-2);
}
.post-nav__dir > .ico { --ico-sz: 13px; }
.post-nav__title { font-size: var(--fs-sm); font-weight: 600; color: var(--heading); }
.post-nav__item:hover .post-nav__title { color: var(--primary-600); }
.post-nav__item--next { text-align: right; }
.post-nav__item--next .post-nav__dir { justify-content: flex-end; }
.post-nav__item--empty { visibility: hidden; }
@media (max-width: 560px) {
  .post-nav { grid-template-columns: minmax(0, 1fr); }
}
/* ==== REQ-E-CSS-END ==== */

/* ==========================================================================
   23. 上线期空状态（2026-10-18 真实数据收敛）
   --------------------------------------------------------------------------
   背景：全站只保留 4 家真实机构（用户 2026-10-01 拍板），舞蹈 / 书法 / 表演 /
         其他 4 个品类暂无入驻机构。空品类不再摆样例机构，改为「严选中」招募位；
         列表页筛选到 0 条时用 .list-empty 提示，不留白屏。
   作用域：全部是本轮新增类名，不覆盖任何既有选择器（铁律 20）。
   ========================================================================== */

/* 首页品类大区内的招募位：占住 mini-list 的位置，保持 6 个大区视觉等高 */
.cat-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 196px;
  height: 100%;            /* 与左侧品类色块等高，避免空品类大区露出大片空白 */
  align-self: stretch;
  padding: 22px 18px;
  border: 1px dashed var(--line-strong, #d5dae2);
  border-radius: var(--r-md, 10px);
  background: #fff;
  text-align: center;
}
.cat-empty-t {
  margin: 0;
  font-size: var(--fs-sm, 14px);
  font-weight: 700;
  color: var(--heading, #1a1f26);
}
.cat-empty-d {
  margin: 0;
  max-width: 34em;
  font-size: var(--fs-xs, 13px);
  line-height: 1.6;
  color: var(--text-2, #5b636e);
}
.cat-empty .btn { margin-top: 4px; }

/* 列表筛选 0 条时的空状态（由 main.js 运行时插入） */
.list-empty {
  padding: 44px 24px;
  border: 1px dashed var(--line-strong, #d5dae2);
  border-radius: var(--r-md, 10px);
  background: #fff;
  text-align: center;
}
.list-empty__t {
  margin: 0 0 6px;
  font-size: var(--fs-md, 16px);
  font-weight: 700;
  color: var(--heading, #1a1f26);
}
.list-empty__d {
  margin: 0;
  font-size: var(--fs-sm, 14px);
  line-height: 1.7;
  color: var(--text-2, #5b636e);
}
.list-empty__d a { color: var(--primary-600, #c9420f); text-decoration: underline; }

/* ==========================================================================
   24. 表单提交反馈（2026-10-23 接入真实接口后新增）
   --------------------------------------------------------------------------
   背景：表单从「演示提示」改为真的 POST /api/lead.php。失败时必须给人看得懂的
         一句提示（网络差 / 服务端忙），不能只 console。两个类名均为本轮新增，
         不复用既有选择器（铁律 20）。
   ========================================================================== */
.form-error,
.form-hint {
  margin: 0 0 12px;
  padding: 10px 14px;
  border-radius: var(--r-md, 10px);
  font-size: var(--fs-sm, 14px);
  line-height: 1.6;
}
.form-error {
  border: 1px solid #f3c2b4;
  background: #fdf0ec;
  color: #a8370c;
}
.form-hint {
  border: 1px solid #cfd8e3;
  background: #f5f7fa;
  color: var(--text-2, #5b636e);
}

/* 机构简介的来源说明（2026-10-24）——弱化处理，只做「谁说的」标注，不抢正文 */
.intro-source {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px dashed #dfe4ea;
  font-size: var(--fs-sm, 14px);
  color: var(--text-2, #5b636e);
  line-height: 1.7;
}

/* ==========================================================================
   把「假链接」改回按钮（2026-10-25）
   原来全站 20 页的头部留言图标是 <a href="javascript:;">，正文里也有两处。
   javascript:; 既不是可抓取链接、也不是按钮，读屏软件念不出来 —— 换成真按钮，
   行为不变（仍靠 data-modal-open 打开留言弹窗），视觉保持原样。
   ========================================================================== */
button.header-tel {
  padding: 0;
  font: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.link-inline {
  display: inline;
  padding: 0;
  border: 0;
  background: none;
  color: var(--primary-600, #c9420f);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.link-inline:hover { color: var(--primary-500, #ef5222); }

/* ==========================================================================
   排版统一（2026-10-25）
   体检发现的真实不一致（同一个角色，不同页面不同字号）：
     · 卡片标题：品类卡 13 / 课程卡 16 / 升学卡 16 / 机构卡·指南卡 17
     · 卡片说明：课程卡 13 / 升学卡 13.5 / 其它 14
     · 升学栏目 h1 34（其它页 40）、导语 17（其它页 19）
     · 升学栏目区块上留白 44（全站 --sec-y 56）
   统一到设计令牌：卡片标题 16、说明 13.5、标题用 --fs-h1、导语用 --fs-lead、留白用 --sec-y。
   只覆盖不一致项，不动既有排版语言。
   ========================================================================== */
.cat-tile .cat-name { font-size: 15px; }        /* 卡片名是「品类」，比标题小半档 */
.item-card h3 { font-size: 16px; }
.item-card .desc { font-size: 13.5px; line-height: 1.7; }
.sx-path strong, .sx-card strong { font-size: 16px; }
.sx-path span, .sx-card span { font-size: 13.5px; line-height: 1.7; }
.sx-hero h1 { font-size: var(--fs-h1); }
.sx-lead { font-size: var(--fs-lead); max-width: 44em; }
.sx-body p, .sx-body li { font-size: var(--fs-body); }
.sx-body h2 { font-size: var(--fs-h3); }
.sx-paths, .sx-list, .sx-note { padding: 40px 0 0; }
.sx-def { padding: var(--s-6) 0 0; }

/* 首页 FAQ 原来是 40/8，紧贴页脚；改为上不留、下留一档 */
.home-faq { padding: 0 0 var(--sec-y); }

/* ==========================================================================
   断行控制（2026-10-25）
   问题：中文长句在窄容器里会出现「实地／核验」「…中考成／绩」这种把词拆开的断行。
   做法：正文与说明用 text-wrap: pretty（尽量避免末行孤字、词内断行），
        标题用 balance（多行时均分，不出现一行长一行极短）。Chromium 117+ 支持，
        不支持的浏览器自动忽略，不影响原有排版。
   ========================================================================== */
.lead, .sx-lead, .sx-body p, .sx-body li,
.item-card .desc, .sx-path span, .sx-card span,
.home-faq__list p, .seo-copy p, .seo-copy li,
.detail-content p, .detail-content li, .inst-fact dd, .rel-meta {
  text-wrap: pretty;
}
h1, h2, h3, .section-head h2, .hero-title, .rel-title, .sx-h2 {
  text-wrap: balance;
}

/* 升学栏目正文对齐（2026-10-25）
   问题：正文容器 max-width:820px 居中，导致正文左边缘比大标题（h1/导语）右缩约 110px，
   同屏看是「标题靠左、正文缩进」，很像排版错位。
   改法：正文容器与页面容器同宽（左边缘对齐），只把「行」限制在舒适阅读宽度。 */
.sx-body .container { max-width: var(--w-container, 1200px); }
.sx-body .container > h2,
.sx-body .container > h3,
.sx-body .container > p,
.sx-body .container > ul,
.sx-body .container > ol,
.sx-body .container > table,
.sx-body .container > .sx-warn,
.sx-body .container > .sx-next { max-width: 46em; }

/* 阅读宽度统一（2026-10-25）：导语框与正文右边缘对齐，避免「框比正文宽一截」 */
:root { --w-read: 44rem; }
.sx-lead { max-width: var(--w-read); }
.sx-body .container > h2,
.sx-body .container > h3,
.sx-body .container > p,
.sx-body .container > ul,
.sx-body .container > ol,
.sx-body .container > table,
.sx-body .container > .sx-warn,
.sx-body .container > .sx-next { max-width: var(--w-read); }

/* 卡片标题最终统一 16（= --fs-h4）：
   原来竖卡（课程）16、横卡（机构/指南）17，同一屏上下两个列表标题不一样大。
   .item-card.horizontal 的规则选择器更具体，这里显式对齐。 */
.item-card.horizontal .info h3,
.item-card.horizontal .info h4,
.card-row .info h3,
.card-inst .info h3,
.card-course .info h3,
.card-post .info h3 { font-size: var(--fs-h4); }

/* 卡片说明的断行：短文案用 balance，避免出现「…中考成 / 绩」这种末行孤字 */
.sx-path span, .sx-card span, .item-card .desc, .rel-meta {
  text-wrap: balance;
}

/* 列表卡里的属性小字 12 → 13（2026-10-25）
   12px 在桌面已经偏小、手机更难读；这批是家长真正要看的信息（年龄/班型/方向/科目），
   不能为了「紧凑」牺牲可读性。同时把行高从 18px 提到 1.6。 */
.item-card .fact, .item-card .chip, .item-card .chips span { font-size: 13px; line-height: 1.6; }

/* ==========================================================================
   文章页版式（2026-10-25 第二轮）
   问题（用户截图）：正文只有 660px，右侧 540px 全空 —— 大屏像没排完。
       且标题层级过平：h2 22/20.5/18（随视口缩）vs h3 17，几乎一样大。
   做法：重定标题层级 h1 40 → h2 26 → h3 19 → 正文 15；
        侧栏已在第三轮减法中删除，正文单栏居中 760px（见文末「减法」段）。
   ========================================================================== */
.sx-body { padding: 0; min-width: 0; }
.sx-body > h2,
.sx-body > h3,
.sx-body > p,
.sx-body > ul,
.sx-body > ol,
.sx-body > table,
.sx-body > .sx-warn,
.sx-body > .sx-next { max-width: var(--w-read, 690px); }

/* 层级拉开：h2 是「章节」，h3 是「①②③的小节」 */
.sx-body > h2 {
  font-size: clamp(22px, 2vw, 26px);
  line-height: 1.4;
  font-weight: 700;
  margin: 46px 0 16px;
  padding-left: 14px;
  border-left: 4px solid var(--brand, #ff6a3d);
}
.sx-body > h3 {
  font-size: 19px;
  line-height: 1.55;
  font-weight: 700;
  margin: 30px 0 10px;
  color: var(--ink-900, #0d2137);
}
.sx-body > p { font-size: var(--fs-body); line-height: 1.9; margin: 0 0 16px; }
.sx-body > ul, .sx-body > ol { margin: 0 0 18px 22px; }
.sx-body > ul li, .sx-body > ol li { font-size: var(--fs-body); line-height: 1.9; margin: 7px 0; }

/* ==========================================================================
   减法（2026-10-25 第三轮，用户：「你是在帮我添麻烦」）
   删掉：文章页右侧栏（目录/接着看/预约卡）、.sx-layout 双栏、.sx-side / .sx-toc 全部样式。
   原因：① 目录用的是 #锚点，而全站有 <base href="/">，锚点会把用户带离本页（真 bug）；
        ② 这些块都是我额外加的入口，与「指南文末三选一」「首页四卡」重复；
        ③ 多一个层级就多一处会坏的地方。
   现在的做法：文章页收成一条 760px 的阅读栏并居中 —— 既没有右侧空洞，也没有额外交互。
   ========================================================================== */
.sx-article .container { max-width: 760px; }
.sx-article .sx-body > h2,
.sx-article .sx-body > h3,
.sx-article .sx-body > p,
.sx-article .sx-body > ul,
.sx-article .sx-body > ol,
.sx-article .sx-body > table,
.sx-article .sx-body > .sx-warn,
.sx-article .sx-body > .sx-next { max-width: none; }
.sx-article .sx-lead { max-width: none; }

/* 课程阶段标签（2026-10-25 恢复）
   ⚠️ 我在删「自动推荐 / 首页四卡」样式时，把夹在中间的这段一并删掉了，
   结果阶段标签变成了没有底色的小灰字（样式静默失效）。这里按原样恢复，
   并加进 `verify_css.mjs` 的关键规则清单，防止再被误删。
   阶段：启蒙 / 兴趣 / 考级 / 特长 / 艺考 / 成人 */
.stage-tag {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
  line-height: 1.6;
  background: #fff2ec;
  color: #c9420f;
  border: 1px solid #ffd8c8;
  white-space: nowrap;
}
.stage-tag[data-stage="艺考"] { background: #f3ecff; color: #5b34b8; border-color: #e0d3ff; }
.stage-tag[data-stage="特长"] { background: #eaf4ff; color: #1f5aa8; border-color: #cfe4ff; }
.stage-tag[data-stage="考级"] { background: #eafaf1; color: #1c7a4a; border-color: #ccefdc; }
.stage-tag[data-stage="启蒙"] { background: #fff8e6; color: #8a6100; border-color: #ffe8b0; }
.stage-tag[data-stage="成人"] { background: #f1f3f6; color: #4a5462; border-color: #e0e5ec; }

/* ==========================================================================
   升学文章页改用「站内既有详情骨架」后，只需要补两条规则（2026-10-25 终版）
   · .sx-lead-inline ：正文开头那句「一句话结论」，做成浅底提示块
   · .detail-content h2 ：正文里的章节标题（原来只用 h3，没有 h2 规则）
   说明：上面那些 .sx-body / .sx-article 规则已随自造布局一起废弃，
        现无任何页面使用这两个类名（改了会再触发括号错位，故保留不删，仅失效）。
   ========================================================================== */
.sx-lead-inline {
  margin: 0 0 24px;
  padding: 14px 18px;
  background: #f7f8fa;
  border-left: 4px solid var(--brand, #ff6a3d);
  border-radius: 0 10px 10px 0;
  font-size: var(--fs-body);
  line-height: 1.85;
  color: var(--text-1, #2b3442);
}
.sx-content h2 {
  margin: var(--s-6) 0 var(--s-3);
  padding-left: var(--s-3);
  border-left: 3px solid var(--primary-500);
  font-size: clamp(20px, 2vw, 26px);
  line-height: 1.4;
}
.sx-content h3 {
  margin: var(--s-5) 0 var(--s-2);
  padding-left: 0;
  border-left: 0;
  font-size: 19px;
  line-height: 1.55;
}

/* 合并表单后的说明句（2026-10-25）：一句话讲清「预约」和「咨询」共用一个表单 */
.modal-tip {
  margin: 0 0 14px;
  padding: 10px 12px;
  background: #fff8f5;
  border: 1px solid #ffd8c8;
  border-radius: 8px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--text-1, #2b3442);
}
