/* ============================================================================
   显然考研 · 官网样式
   视觉母题：刻度 · 显影 · 闭环
   配色体系：墨 · 纸 · 品牌蓝 · 状态三色（客户色板）
   V4.0：冷色科技风——页面底 #F2F4FA + 纯白卡片 + 品牌蓝 #4B74FF，
         文字级强调用 #2F4FD6；结构沿用 V3.0 不动（灰白底 + 纯白卡片浮起、
         无衬线标题、胶囊形状、高信息密度）。
   约束：正文与数据均为服务端渲染直出，样式层不得依赖 JS 才能阅读。
   ============================================================================ */

/* ── 1. 设计令牌 ───────────────────────────────────────────────────────────── */
:root {
  /* 墨 —— 深藏青。只作最深一档文字与图标色。 */
  --ink-900: #070D2F;
  --ink-800: #111A3F;
  --ink-700: #1B2550;
  --ink-500: #363B4D;
  --ink-100: #DDE1EC;

  /* 纸（V4.1：从"冷灰"改成"明确偏蓝"。
     客户色板里品牌蓝占色块总面积的**一半**，V4.0 只把蓝用在按钮与细线上，
     等于把 50% 的配额用成了 3%。这里先把底色整体转蓝，
     再在首屏/页头/末屏交给它三块实心面积。
     #EDF2FD 对 #FFFFFF 是 1.12:1，比原来的 #F2F4FA（1.10:1）还略高，
     所以"转蓝"没有牺牲白卡的浮起。 */
  --paper: #EDF2FD;      /* 页面底：明确偏蓝的浅蓝 */
  --paper-2: #FFFFFF;    /* 卡片 / 输入框 / 顶栏：纯白 */
  --paper-3: #E3E9FC;    /* 交替带：比页面底再深一档的浅蓝 */
  --paper-4: #D3DCF7;    /* 页脚压底 */
  --line: #D8E0F5;
  --grid-line: rgba(47, 79, 214, .07);

  /* 品牌（客户色板主色 #4B74FF） */
  --brand: #4B74FF;      /* 品牌原色：装饰线、渐变高光端、色块、色点——它本身对比度只有 4.01:1，不可承载文字 */
  --brand-light: #8FA9FF;/* 亮化品牌色：仅限深底 */
  --brand-050: #EDF1FF;  /* 品牌淡底 */

  /* 实心品牌蓝带（首屏 / 内页页头 / 末屏 CTA）三档。
     ⚠️ 带体不能用品牌原色：白字压 #4B74FF 只有 4.01:1，不达 AA。
     品牌原色只留在渐变的高光端与装饰线上——它出现在大色块里，但不承担对比度。

     V4.7 整体压深一档。原先的 #3B5BE8 / #2237A8 是照着"品牌蓝要亮"定的，
     但实测（按渐变 + 极光合成出的真实底色算，不是按兜底实色算）：
       白字压最亮端只有 5.44:1，首屏 h1 最差角 6.98:1 —— 达标但余量薄；
       更要紧的是内页页头的面包屑/导语（13px，`--on-brand-2` 压在最亮端加柔光上）
       只有 3.84:1，**实际不达 AA**（4.5），而旧脚本拿 backgroundColor 比对，漏报了。
     压深后：
       白字 @band-1 = 6.96:1（内页页头，也就是整条带子上最亮的点）
       白字 @band-3 = 12.6:1（首屏最深端）
       内页页头面包屑 = 5.96:1（原 3.84:1，从"不达标"变成"过线"）
     --band-2 还兼作"白底胶囊/按钮上的文字色"，压深对它同样是加分（9.63 → 10.79:1）。 */
  --band-1: #2F4CCB;
  --band-2: #1B2F9E;
  --band-3: #142682;
  --on-brand-1: #FFFFFF;   /* 带上的主文字 */
  --on-brand-2: #ECF1FF;   /* 带上的次级文字。压深后：在 #2F4CCB 上 6.16:1（原 4.83:1） */

  /* 语义状态（客户色板三辅色只做色点，标签一律"浅底 + 深字"）。
     三辅色原值 #FE830F / #EE4545 / #2AB977 对白底分别只有 2.48 / 3.77 / 2.53:1，
     都不可承载文字，所以另配深字色；-dot 才是色板原色。 */
  --ok: #17795A;      --ok-050: #E6F7EF;      --ok-dot: #2AB977;
  --warn: #A85B00;    --warn-050: #FFF3E4;    --warn-dot: #FE830F;
  --danger: #C0392B;  --danger-050: #FDECEC;  --danger-dot: #EE4545;

  /* 文字 */
  --text-1: #070D2F;     /* 白底 19.0:1 */
  --text-2: #363B4D;     /* 白底 11.1:1 */
  --text-3: #5C6478;     /* 白底 5.9:1，达 AA。色板给的 #7C8294 只有 3.83:1，不能用来写字 */
  --on-ink-1: #F2F5FF;
  --on-ink-2: #A9B2CC;

  /* 语义 */
  --accent: #2F4FD6;     /* 全站唯一的强调色。白底 6.54:1、在交替带 #E4E6EE 上 5.25:1，两处都达 AA，因此文字与按钮底色共用它 */
  --accent-weak: var(--brand-050);
  --surface: var(--paper);
  --surface-2: var(--paper-2);
  --rule: var(--line);
  --fg-1: var(--text-1);
  --fg-2: var(--text-2);
  --fg-3: var(--text-3);

  /* 字体 —— V3.0 起标题改无衬线。
     衬线是"文艺/出版"语汇，无衬线是"专业/工具"语汇；
     参照对象（研途）连标题都是 PingFangSC，本次跟随。
     衬线仅保留给印章与题字类图形。 */
  --font-sans: "Source Han Sans SC", "HarmonyOS Sans SC", "PingFang SC", system-ui, sans-serif;
  --font-serif: "Source Han Serif SC", "Songti SC", serif;
  --font-heading: var(--font-sans);

  /* 字号阶 —— V3.0 整体下调一档：
     首屏从整屏压到 440px，display 必须跟着收，否则一屏塞不下。 */
  --fs-display: clamp(1.875rem, 3.2vw, 3rem);
  --fs-h1: clamp(1.625rem, 2.4vw, 2.375rem);
  --fs-h2: clamp(1.375rem, 1.9vw, 1.75rem);
  --fs-h3: 1.125rem;
  --fs-body: 1rem;
  --fs-sm: .9375rem;
  --fs-caption: .8125rem;
  --lh-body: 1.75;

  /* 间距 */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;
  --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px; --sp-10: 128px;
  /* V3.0：区块纵向留白砍掉约一半，把信息密度提上来（9599px → 目标 4500px 量级）。
     白卡本身已经靠"底色差 + 阴影"划出边界，不再需要大留白来分隔。 */
  --section-y: clamp(20px, 2.4vw, 32px);

  /* 圆角 —— 参照对象卡片只有 4–6px，标签/按钮是胶囊。
     大圆角（16px+）是 SaaS 语汇，这里刻意不用。 */
  --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-full: 999px;

  /* 阴影（冷色）。卡片浮起全靠这里，所以比 V2 更明显。 */
  --sh-1: 0 1px 2px rgba(24, 39, 75, .06), 0 8px 24px rgba(24, 39, 75, .06);
  --sh-2: 0 4px 10px rgba(24, 39, 75, .08), 0 18px 44px rgba(24, 39, 75, .10);

  /* 动效 */
  --ease-out: cubic-bezier(.22, .61, .36, 1);
  --ease-spring: cubic-bezier(.34, 1.32, .64, 1);
  /* 呼吸：正弦式往返，两端无"刹车感"，长周期才不躁 */
  --ease-breath: cubic-bezier(.45, 0, .55, 1);
  --dur-fast: 150ms;
  --dur: 280ms;
  --dur-slow: 520ms;
  --dur-breath: 11s;

  /* 栅格 */
  --container: 1200px;
  --container-wide: 1440px;
  --measure: 42rem;

  /* 冷灰：表头底、表格 hover 行 */
  --tint: #EDF2FE;
  --tint-2: #E4EBFC;
}


/* 交替带（原「深色轨」位置）。
   V4.0：页面底已压到 #F2F4FA，交替带只需再深一档；
   带内分隔线与斑马纹也要跟着加深，否则在冷灰底上几乎看不见。 */
.band-tint {
  --surface: var(--paper-3);
  --rule: #CBD6F2;      /* #D8E0F5 在 #E3E9FC 上几乎看不见，必须再深一档 */
  --tint: #E6ECFC;
  --tint-2: #D9E2FA;
  position: relative;
  isolation: isolate;
}

/* 纸带底光：品牌蓝与强调蓝两组极缓漂移的径向光。
   周期 26s，位移幅度控制在 3% 以内——"呼吸"只在余光里被察觉，
   一旦能被盯着看就不再是呼吸，而是闪烁。

   浅底上的 alpha 必须比深底低一个量级：同样 .17 的品牌蓝在冷灰纸上会明显"脏"，
   这里的取值是实机对比后的结果，不是按比例拍的。

   注意：这里刻意不用 overflow: hidden 来给底光"裁边"。
   overflow 会创建滚动容器，使本区块内的 position: sticky（.split > .aside）
   静默失效——吸附不再生效，且不抛任何错，极难排查。
   改为 inset: 3%：一来径向渐变本就在 68%/70% 处淡出为全透明，
   二来给 drift 的 scale(1.08) 留出余量，保证缩放后仍不越出本区块。 */
.band-tint::before {
  content: "";
  position: absolute;
  inset: 3%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(38% 42% at 22% 18%, rgba(75, 116, 255, .10), transparent 68%),
    radial-gradient(34% 40% at 84% 78%, rgba(47, 79, 214, .07), transparent 70%);
  animation: drift 26s var(--ease-breath) infinite;
}

/* 蓝带上的柔光改成白色微光：在实心蓝底上再叠一层蓝色光是无效操作。
   注意 ::before 的 z-index: -1 并非"沉到父元素背景之下"——
   它压在父元素自己的背景之上、内容之下，所以渐变底色仍然可见，微光叠在它上面。
   首页首屏不用这一层（它有自己的极光 + 星场 + 渐隐网格，见下方 section.band-hero），
   这里只服务末屏与内页页头。 */
.band-cta::before, section.page-hero::before {
  content: "";
  position: absolute;
  inset: 3%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(46% 58% at 6% 0%, rgba(255, 255, 255, .18), transparent 66%),
    radial-gradient(40% 54% at 97% 86%, rgba(143, 169, 255, .26), transparent 68%);
  animation: drift 26s var(--ease-breath) infinite;
}

/* ── 2. 基础 ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--text-1);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  line-height: 1.32;
  margin: 0 0 var(--sp-4);
  letter-spacing: .01em;
}

/* 大字号需要收紧字距：字号越大，字间空隙在视觉上越被放大，
   保持 +.01em 会让大标题显得松散。小标题维持原值不变。 */
h1 { font-size: var(--fs-h1); letter-spacing: -.012em; }
h2 { font-size: var(--fs-h2); letter-spacing: -.008em; }
h3 { font-size: var(--fs-h3); }

p { margin: 0 0 var(--sp-5); }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

img { max-width: 100%; height: auto; display: block; }

/* 选中色走品牌淡底，避免浏览器默认蓝在冷白纸与冷灰纸上跳出来 */
::selection { background: var(--accent-weak); color: var(--text-1); }

/* 数字严格对齐——数据感的基础，不依赖特殊字体 */
.num, table td.n, .data-value, .score {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ink-900); color: var(--on-ink-1); padding: var(--sp-3) var(--sp-5);
}
.skip:focus { left: 0; }

/* ── 3. 布局 ───────────────────────────────────────────────────────────────── */
.container { width: 100%; max-width: var(--container); margin: 0 auto; padding: 0 var(--sp-5); }
.container-wide { max-width: var(--container-wide); }
.measure { max-width: var(--measure); }

.band { background: var(--surface); color: var(--fg-1); padding: var(--section-y) 0; }

/* 首屏横幅：目标 420–480px。上下留白必须比普通区块更紧，
   只要放开就会立刻退回"整屏大标题"，密度随之崩掉。 */
.band-hero { padding-top: var(--sp-7); padding-bottom: var(--sp-6); }

/* ── 实心品牌蓝带（V4.1）──────────────────────────────────────────────────
   客户色板里品牌蓝占色块总面积的**一半**；V4.0 只把它用在按钮与细线上，
   等于把 50% 的配额用成了 3%。这里把三块最大的面积真的交给它：
   首页首屏、内页页头、首页末屏 CTA。

   ⚠️ 带体用 --band-1 → --band-2 的渐变，不用品牌原色：
   白字压 #4B74FF 只有 4.01:1，不达 AA；渐变最亮端是 5.46:1，全带安全。
   品牌原色仍留在渐变高光与装饰线上——大色块里有它，但它不承担对比度。

   ⚠️ 深底上的每一个组件都要单独覆盖。最危险的是**主按钮**：
   它默认就是品牌蓝底，放在蓝带上会直接消失（蓝底蓝按钮）。 */
.band-hero, .band-cta, section.page-hero {
  position: relative;
  isolation: isolate;
  color: var(--on-brand-1);
  /* 刻度网格改成白色 10%：蓝底上原来的蓝色网格线完全不可见 */
  --grid-line: rgba(255, 255, 255, .10);
  --rule: rgba(255, 255, 255, .28);
  --accent: var(--on-brand-1);
  --accent-weak: rgba(255, 255, 255, .16);
  --fg-1: var(--on-brand-1);
  --fg-2: var(--on-brand-2);
  --fg-3: var(--on-brand-2);
}

/* 末屏与内页页头：安静的蓝带，不加星场（13 个内页页头若都上星场，
   首屏特效会因重复而失效），但有两条动态：
     ① 星座网络横带（弱强度，见上方 section.page-hero .net）
     ② 光带横扫——与首屏连线上的短划流光同一动作语汇（"线流光"）
   底色先落一个实色再叠渐变：一来渐变不被支持时不至于变成透明白底，
   二来对比度检测脚本读的是 backgroundColor，只写渐变会被读成透明。 */
.band-cta, section.page-hero {
  background-color: var(--band-1);
  background-image:
    linear-gradient(105deg, transparent 0 46%, rgba(255, 255, 255, .10) 50%, transparent 54% 100%),
    linear-gradient(105deg, transparent 0 47%, rgba(255, 255, 255, .06) 50%, transparent 53% 100%),
    linear-gradient(105deg, transparent 0 48%, rgba(143, 169, 255, .10) 50%, transparent 52% 100%),
    linear-gradient(135deg, var(--band-1) 0%, var(--band-2) 100%);
  /* 三层宽度不同 → 同一个 background-position 动画下它们处在**不同位置**，
     于是任意时刻屏上有一到两条光带，而不是整块一起平移 */
  background-size: 460px 100%, 760px 100%, 1180px 100%, 100% 100%;
  background-repeat: no-repeat, no-repeat, no-repeat, no-repeat;
  animation: beamSweep 13s linear infinite;
}
/* ⚠️ background-position 的起止必须**同为百分比**：长度与百分比之间不能插值，
   写成 -460px → 200% 会直接跳变，而不是扫过。 */
@keyframes beamSweep {
  from { background-position: -100% 0, -100% 0, -100% 0, 0 0; }
  to   { background-position: 200% 0, 200% 0, 200% 0, 0 0; }
}

/* ── 首页首屏背景：深空 · 极光 · 星场 · 渐隐网格（V4.3）──────────────────
   全站只有这一处上特效。四层结构（从下到上）：
     ① 深空斜向渐变 --band-1 → --band-2 → --band-3（#2F4CCB → #1B2F9E → #142682）
        白字最亮端 6.96:1、最深端 12.6:1，整条带子都安全
     ② 极光：三处大半径柔光，只做纵深、不承载信息
     ③ 细网格：32px，用 mask 从上到下渐隐 —— 网格"有尽头"才像空间，
        铺满整屏只会像一张表格
     ④ 星场：六层星点 tile（220px 与 130px 两种密度），140s 极缓漂移

   全部是 CSS 渐变与伪元素，零图片、零 JS：爬虫与无 JS 环境看到的是同一张背景。

   ⚠️ 星场刻意不做"闪烁"：项目自身的动效纪律是"呼吸只在余光里被察觉"，
      星点一旦能被盯着看闪，就变成噪点。这里只做整体极缓位移。
   ⚠️ 柔光的 alpha 必须压住：它会把白字的底提亮，而对比度脚本读的是
      backgroundColor，提亮它检测不出来——这类问题只能靠控制 alpha 来防。 */
section.band-hero {
  --grid-line: rgba(255, 255, 255, .07);   /* 网格由 48px 加密到 32px，alpha 跟着降 */
  /* 兜底实色必须跟着渐变的最深端走（对比度脚本读的是这个值，
     所以它越接近真实底色，脚本的估算就越不容易偏乐观） */
  background-color: var(--band-3);
  background-image:
    radial-gradient(58% 76% at 86% 4%, rgba(143, 169, 255, .30), transparent 62%),
    radial-gradient(46% 66% at 2% 100%, rgba(75, 116, 255, .32), transparent 66%),
    radial-gradient(32% 54% at 68% 120%, rgba(94, 212, 173, .07), transparent 70%),
    linear-gradient(135deg, var(--band-1) 0%, var(--band-2) 56%, var(--band-3) 100%);
}
/* ⑤ 文字区压暗（V4.7）：首屏文字列上方盖一层深色光池。
   起因：V4.4 加了星座网络之后，h1 的矩形里穿过了 6 条连线（含 6 段流动的
   亮短划，alpha .9）和 1 个亮节点，细网格在标题高度处 mask 强度还有 0.71。
   白字压在中蓝上 6.98:1 本身达标，但底下有亮线来回穿过时读起来就是"糊"——
   这是**干扰**问题，不是颜色问题，所以解法是给文字区一层更深的底，
   而不是去改文字颜色（改文字只能加 13%，让背景让路能加 35%）。

   ⚠️ 必须用 ::after + z-index: -1，不能放进元素自己的 background-image：
   同为 z-index: -1 时按树序绘制 ::before（细网格）→ .net（星座）→ ::after（本层）
   → 正文。放在这个位置，压暗层才盖得住网格与星座线；
   若写进 background-image，它会落在所有伪元素之下，星座线反而显得更刺眼。
   也**不能**用 z-index: auto —— 绝对定位 + z-index auto 会落到"定位后代"那一步，
   绘制在正文**之上**，等于把文字本身压暗。 */
section.band-hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image: radial-gradient(88% 120% at 52% 34%, rgba(12, 20, 62, .34), transparent 72%);
}
/* ③ 细网格 + 向上渐隐 */
section.band-hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 32px 32px;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, .52) 58%, transparent 94%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, .52) 58%, transparent 94%);
}
/* ④ 星座网络（V4.4）：节点 + 连线 + 沿连线流动的信号。
   标记抽在 Pages/Shared/_ConstellationNet.cshtml 里，以**内联 SVG** 直出；
   这里有 16 个调用点（首屏、末屏、15 个内页页头），坐标只有一份来源。
   强度由调用处的 CSS 决定，不在 partial 里分叉：
     首屏 全强度；内页页头压低透明度、藏掉流光（见下方 section.page-hero）

   为什么不用 JS 粒子网络（plexus）：项目纪律是"内容不得依赖 JS 才能阅读"，
   而画布背景对爬虫与无 JS 环境是一片空白；SVG 服务端直出，两边看到的是
   同一张图，动画只是渐进增强，且静置时 CPU 成本≈0。

   两条动画：
     · 节点：错峰呼吸（opacity .30 → 1）。错峰靠 --i 做**负延迟**，
       一屏里永远有明暗不同的节点，不会整体同频闪
     · 连线：画两遍同一段线段——一层极淡实线（"关系存在"）
       + 一层短划流光沿走向位移（"关系在动"）。
       这样不需要 JS 维护拓扑，也不需要真正的连线增删

   ⚠️ 所有元素透明度都压得很低：它在白字底下。连线每亮一分，
      白字的实际可读性就降一分——而对比度脚本读的是 backgroundColor，
      这种下降它检测不出来，只能靠控制透明度来防。 */
.net {
  position: absolute; inset: 0; z-index: -1;
  width: 100%; height: 100%;
  pointer-events: none;
}
.net .c-edge { stroke: rgba(190, 212, 255, .15); stroke-width: 1; }
.net .c-spark {
  stroke: rgba(216, 232, 255, .9); stroke-width: 1.4; stroke-linecap: round;
  stroke-dasharray: 24 300;
  animation: edgeFlow 13s linear infinite;
  animation-delay: calc(var(--i) * -1.15s);
}
.net .c-node {
  fill: #DCE7FF;
  animation: nodePulse 7s var(--ease-breath) infinite;
  animation-delay: calc(var(--i) * -.46s);
}
@keyframes edgeFlow {
  from { stroke-dashoffset: 324; }
  to   { stroke-dashoffset: 0; }
}
@keyframes nodePulse {
  0%, 100% { opacity: .30; }
  50%      { opacity: 1; }
}

/* 内页页头与末屏 CTA 里的同一个网络：**同一张图，弱一档**。
   页头只有约 170px 高，viewBox 的 slice 会只露出中间那条横向带
   （y ≈ 155–305），读起来正好是"一条网络横带"，不是首屏的缩小版。
   藏掉流光：这两处已经有光带横扫（见下），再叠流动的短划就成两层噪声了。
   ⚠️ 选择器必须把 .band-cta 单独列出来——它**不在** section.page-hero 里面，
   只写后者的那次改动，末屏会拿到全强度，在 249px 高的带子上糊成一片。 */
section.page-hero .net, .band-cta .net { opacity: .45; }
section.page-hero .net .c-spark, .band-cta .net .c-spark { display: none; }
section.page-hero .net .c-node, .band-cta .net .c-node { animation-duration: 9s; }

/* 主按钮在蓝带上必须反白。
   ⚠️ 选择器必须带 `section.`：`.band-hero .button.primary` 与 `.button.primary`
   同为 (0,2,0) 特异性，而组件规则写在本节之后，会**静默**把这里的覆盖吃掉
   （蓝底 + 蓝按钮 = 按钮直接消失，且不报错）。
   多加一个元素选择器把特异性抬到 (0,3,1) 才稳定。 */
section.band-hero .button.primary,
section.band-cta .button.primary,
section.page-hero .button.primary {
  background: #FFFFFF; color: var(--band-2);
  box-shadow: 0 4px 16px rgba(10, 20, 60, .28);
}
section.band-hero .button.primary:hover,
section.band-cta .button.primary:hover,
section.page-hero .button.primary:hover { background: #E6EDFF; color: #1A2C8C; }

/* 幽灵按钮改成"玻璃描边"：白底幽灵按钮在蓝带上会变成一块刺眼的白斑。
   ⚠️ 而且**不能给它半透明白底**：白字压在 12% 白上只有 4.34:1，不到 AA
   （半透明白底会把底色调亮，等于把自己的对比度削掉）。
   直接透明 + 白描边，白字回到 5.46:1。 */
section.band-hero .button.ghost,
section.band-cta .button.ghost,
section.page-hero .button.ghost {
  background: transparent;
  border-color: rgba(255, 255, 255, .55);
  color: var(--on-brand-1);
}
section.band-hero .button.ghost:hover,
section.band-cta .button.ghost:hover,
section.page-hero .button.ghost:hover {
  background: rgba(12, 22, 64, .22); border-color: #FFFFFF;
}

/* eyebrow 胶囊在蓝带上必须反白底。
   同理：白字压在 16% 白的胶囊上只有 4.05:1，不到 AA。
   改成"白底 + 文字级蓝字"，白底上的 --band-2（#1B2F9E）有 10.79:1，余量很足。 */
section.band-hero .eyebrow,
section.band-cta .eyebrow,
section.page-hero .eyebrow {
  background: #FFFFFF;
  color: var(--band-2);
}

/* 内页页头：面包屑、标题、导语、底边线 */
section.page-hero { border-bottom: 0; }
section.page-hero .breadcrumb,
section.page-hero .breadcrumb a,
section.page-hero .breadcrumb span { color: var(--on-brand-2); }
section.page-hero .breadcrumb a:hover { color: var(--on-brand-1); }
section.page-hero .lede { color: var(--on-brand-2); }

/* 首屏的刻度索引与免责说明 */
.band-hero .hero-index { border-top-color: rgba(255, 255, 255, .30); }
.band-hero .hero-index .idx .v { color: var(--on-brand-1); }
.band-hero .hero-index .idx .k { color: var(--on-brand-2); }
.band-hero .hint, .band-cta .hint { color: var(--on-brand-2); }

/* 末屏主标题下的品牌色线：在蓝带上改成一条白色渐隐线 */
.band-cta .hero-rule {
  background: linear-gradient(90deg, rgba(255, 255, 255, .9), rgba(255, 255, 255, .05));
  opacity: 1;
}


/* 刻度网格底纹：静态母题。透明度 7%，不超过 8% 以免显脏。
   ⚠️ V4.3 起改由伪元素承载。原先它直接写在本元素的 `background-image` 上，
   而 `.band-hero` / `.band-cta` 也在**同一个元素**上写 `background-image` ——
   两者同特异性、`.band-grid` 位置在后，于是**渐变被网格整条静默替换**：
   首页首屏与末屏因此一直是一块平的 #3B5BE8，渐变从未渲染过（零报错）。
   挪到 ::after 之后，渐变与网格互不干扰。
   这是本项目第五次踩「同特异性 + 位置在后 = 静默失效」。 */
.band-grid { position: relative; isolation: isolate; }
.band-grid::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 48px 48px;
}

.section-head { margin-bottom: var(--sp-5); }

/* 4.0 区块标题：居中双段式（V4.0 新增）
   参照对象（新东方在线考研）最容易被认出来的地方，就是区块标题居中、
   写成「主词 + 一句说明」两段——这是"目录式的商业站"与"编辑式长文"
   在**第一眼**上最大的差别。
   主词与说明同号，只靠字重与明度拉开层次（参照对象实测：34px / 600 / 居中）。

   ⚠️ 只用在通栏区块（.section-head.center）。.split 侧栏里的标题必须保持左对齐，
   窄栏里居中会直接散架。 */
.section-head.center {
  text-align: center;
  /* 居中标题块比正文宽一档（48rem / 768px）：主词 + 说明排成一行时
     需要比 42rem 的正文行宽更多的余地，否则两段会各占一行 */
  max-width: 48rem;
  margin: 0 auto var(--sp-6);
}
.section-head.center h2 {
  /* 比 h1 小一档、比正文 h2 大一档：居中的标题天然更"响"，
     用 --fs-h2 会撑不住 1200px 的宽度 */
  font-size: clamp(1.5rem, 2.2vw, 2rem);
  margin-bottom: var(--sp-3);
}
.section-head.center h2 .sub {
  margin-left: .5em;
  font-weight: 500;
  color: var(--fg-2);
}
.section-head.center p { margin-left: auto; margin-right: auto; }
/* .eyebrow 需可独立使用（首屏、安静段里不带 .section-head）
   V3.0：从"左侧竖条"改为"淡品牌色胶囊"——竖条是出版语汇，胶囊是标签语汇，
   后者更接近参照对象，且与 .tag / .button 形成同一套形状语言。 */
.eyebrow {
  display: inline-block;
  font-size: var(--fs-caption);
  letter-spacing: .08em;
  color: var(--accent);
  background: var(--accent-weak);
  border-radius: var(--r-full);
  padding: 3px var(--sp-3);
  line-height: 1.6;
  margin-bottom: var(--sp-3);
}
.section-head p { color: var(--fg-2); max-width: var(--measure); margin-bottom: 0; }

/* 等分网格一律 minmax(0, 1fr)。
   不写裸 1fr：1fr 的最小值是 auto（= 内容 min-content），
   表单控件与宽表格的固有宽度会撑开轨道，在 641–980px 这类"没塌缩但也不宽"的
   中间档位上产生横向溢出——只在断点处修 minmax 是堵不住的，基底必须就写对。 */
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-6); }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-6); }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-5); }

/* 3.1 编辑式双栏 —— 替代「小标签 + 标题 + 一段话 + 等分卡片」的幻灯片节奏。
   标题退到窄栏并随滚动停驻，正文占宽栏；读者的视线因此产生左右往复，
   而不是被一路向下推平。 */
.split {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}
/* 吸附偏移 = 顶栏高度 64px + 24px 呼吸。顶栏一改高这里必须同步，
   否则吸附时标题会贴着页头或被页头压住。 */
.split > .aside { position: sticky; top: 88px; }
.split > .aside h2 { margin-bottom: var(--sp-4); }
.split > .aside p { color: var(--fg-2); font-size: var(--fs-sm); margin-bottom: 0; }
.split > .aside > :last-child { margin-bottom: 0; }
/* 侧栏里的 .section-head 自带 96px 下边距，会把侧栏撑高、吸附时留出空档 */
.split > .aside .section-head { margin-bottom: 0; }
/* 镜像：侧栏换到右侧。列宽须同步翻转，否则窄栏会拿走 7fr 的宽列。 */
.split.flip { grid-template-columns: minmax(0, 8fr) minmax(0, 4fr); }
.split.flip > .aside { order: 2; }
/* 首屏变体：左 6 右 5、底对齐、不吸附——与内容页的"标题侧栏"区分开。
   右边给到 5fr 是为了让引文少换两行：首屏高度实际上由引文列决定，
   而不是由大标题决定（标题只有两行）。 */
.split.hero { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); align-items: end; }
.split.hero > .aside { position: static; }
.split.hero > .aside .lede { font-size: 1rem; margin-bottom: var(--sp-5); }
/* 首屏标题是 display 级别（最大 6rem），需要比 h1 收得更紧 */
.split.hero h1 { letter-spacing: -.022em; }

/* 3.2 错位：偶数项下沉，打断等分网格的机械感。
   下沉量用 clamp，窄屏自动收敛为 0，不产生横向溢出。 */
/* V3.0：下沉量整体砍掉约一半。错位在这里只为"打断机械感"，
   不需要制造空白；原来的 72/112px 在紧凑版式里会读成"内容没填满"。 */
.stagger { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-5); }
.stagger > *:nth-child(even) { margin-top: clamp(0px, 3vw, 40px); }
.stagger-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-5); }
.stagger-3 > *:nth-child(3n + 2) { margin-top: clamp(0px, 2vw, 24px); }
.stagger-3 > *:nth-child(3n + 3) { margin-top: clamp(0px, 4vw, 48px); }

/* 3.3 细品牌色线：分隔与"权威"的视觉锚点，替代厚重色块 */
.rule-gold {
  height: 1px; border: 0; margin: 0;
  background: linear-gradient(90deg, var(--brand), rgba(75, 116, 255, 0));
  opacity: .8;
}

/* 3.4 首屏：左标题 / 右索引，底部一条满幅基线
   V3.0：索引从 2×2 改为 4×1。两行索引要吃掉 200px，
   而首屏的目标高度只有 420–480px——索引占掉四成，横幅就不成立了。 */
.hero-index {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--rule);
  padding-top: var(--sp-4);
  margin-top: var(--sp-5);
}
.hero-index .idx { display: block; }
/* 刻度数字统一用墨色（米白底 17.1:1）。
   不要为了"金贵"改成品牌色：--brand 在浅底上只有 2.3:1，直接用会不可读；
   品牌色在这套设计里只做线与徽标，不做要读的数字。 */
.hero-index .idx .v {
  display: block;
  font-family: var(--font-heading); font-size: 2rem; line-height: 1;
  color: var(--fg-1);
  font-variant-numeric: tabular-nums;
}
.hero-index .idx .k {
  display: block; margin-top: var(--sp-2);
  font-size: var(--fs-caption); color: var(--fg-3); letter-spacing: .04em;
}
/* 三项刻度索引（如案例页的汇总）：仍走同一套视觉，只是列数不同 */
.hero-index.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* 版面关系的窄屏塌缩。三个组合类都要列出：.split.hero / .split.flip 的特异性
   都高于 .split，只写 .split 的话媒体查询根本打不赢，首屏会静默保持两栏。 */
@media (max-width: 980px) {
  .split, .split.flip, .split.hero { grid-template-columns: minmax(0, 1fr); gap: var(--sp-7); }
  .split > .aside { position: static; }
  .split.flip > .aside { order: 0; }
  .stagger, .stagger-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 三列错位在平板宽度下改成两列交替下沉，避免出现 200px 宽的卡片 */
  .stagger-3 > *:nth-child(n) { margin-top: 0; }
  .stagger-3 > *:nth-child(even) { margin-top: clamp(0px, 5vw, 48px); }
  .hero-index, .hero-index.cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .stagger, .stagger-3 { grid-template-columns: minmax(0, 1fr); }
  .stagger > *:nth-child(n), .stagger-3 > *:nth-child(n) { margin-top: 0; }
  .hero-index, .hero-index.cols-3 { grid-template-columns: minmax(0, 1fr); }
}

/* ── 4. 组件 ───────────────────────────────────────────────────────────────── */

/* 4.1 按钮 —— V3.0 改胶囊形（参照对象的按钮与标签全部是 999px） */
.button {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 11px var(--sp-5);
  border-radius: var(--r-full);
  font-size: var(--fs-sm);
  font-weight: 600;
  font-family: inherit;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.button:hover { text-decoration: none; }
.button.primary {
  background: var(--accent); color: #fff;
  box-shadow: 0 2px 8px rgba(47, 79, 214, .22);
}
.button.primary:hover { background: #2643B8; box-shadow: 0 4px 14px rgba(47, 79, 214, .32); }
/* 幽灵按钮在灰底上必须带白底，否则读不出"这是个按钮" */
.button.ghost { border-color: var(--rule); color: var(--fg-1); background: var(--surface-2); }
.button.ghost:hover { border-color: var(--accent); color: var(--accent); }

/* 4.2 卡片 —— V3.0 的"浮起"全靠这里：纯白卡片压在灰白底上。
   ⚠️ 不要给它加 hover 的 translateY 抬升：
   .card 通常同时带 .reveal，而 `.js .reveal.is-in { transform: none }` 与
   `.card:hover` 特异性相同且位于其后，会静默把位移吃掉——既不报错也不生效。 */
.card {
  background: var(--surface-2);
  border: 1px solid rgba(24, 39, 75, .07);
  border-radius: var(--r-md);
  padding: var(--sp-5);
  box-shadow: var(--sh-1);
  /* 顶边一道品牌色线：静止时长度 0，指针进入时自左向右显影。
     用 background-size 而非伪元素，避免与 .measured 的四角标抢占 ::before/::after。 */
  background-image: linear-gradient(90deg, var(--brand), rgba(75, 116, 255, 0));
  background-repeat: no-repeat;
  background-position: 0 0;
  background-size: 0 1px;
  transition: background-size 620ms var(--ease-out), border-color var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out);
}
.card h3 { margin-bottom: var(--sp-2); }
.card p:last-child { margin-bottom: 0; }

/* "高端"与"花哨"的分界：只在被看时才回应，不主动抢注意力 */
.card:hover { background-size: 100% 1px; box-shadow: var(--sh-2); }

/* 4.2b 细线清单：用于资质、凭证这类"条目即证据"的内容。
   做成表格式清单而非卡片网格，是去 PPT 化最省力也最有效的一步：
   卡片网格天然把并列关系强加给不等重的内容。
   V3.0：外壳改成一张白卡（与 .card 同一质感），内部保持细线分隔。
   圆角用首尾 li 单独处理，不用 overflow: hidden —— 少一处会破坏 sticky 的写法。 */
.hair-list {
  list-style: none; margin: 0; padding: 0;
  background: var(--surface-2);
  border: 1px solid rgba(24, 39, 75, .07);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}
.hair-list > li {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 7fr);
  gap: var(--sp-5);
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--rule);
  transition: background var(--dur) var(--ease-out);
}
.hair-list > li:first-child { border-top: 0; border-radius: var(--r-md) var(--r-md) 0 0; }
.hair-list > li:last-child { border-radius: 0 0 var(--r-md) var(--r-md); }
.hair-list > li:hover { background: var(--accent-weak); }
.hair-list .k { font-family: var(--font-heading); font-size: var(--fs-h3); font-weight: 600; }
.hair-list .k .sub { display: block; font-family: var(--font-sans); font-size: var(--fs-caption); color: var(--fg-3); margin-top: var(--sp-1); letter-spacing: .04em; font-weight: 400; }
.hair-list .v { color: var(--fg-2); font-size: var(--fs-sm); }
@media (max-width: 640px) {
  .hair-list > li { grid-template-columns: minmax(0, 1fr); gap: var(--sp-3); }
}

/* 4.2c 排版型封面 —— 零素材方案里的"图位"
   参照对象的美感约七成来自它那 69 张实拍图，我们没有素材，
   用「冷色块 + 刻度纹理 + 一个超大字符/序号」顶上。
   验收方法（《08》§七）：遮住卡片正文后，仍能凭封面的底色与字符区分出不同卡片。
   色相纪律不变：三档全部落在冷灰 / 品牌蓝 / 强调蓝之内，不引入第五色。
   ⚠️ 这里的 overflow: hidden 是安全的：封面内不含 sticky 元素，
   不会像给整段 .band 加 overflow 那样创建滚动容器、吃掉吸附。 */
.cover {
  position: relative;
  aspect-ratio: 16 / 4;
  border-radius: var(--r-sm);
  overflow: hidden;
  margin-bottom: var(--sp-4);
  background-color: var(--tint);
  background-image:
    repeating-linear-gradient(90deg, rgba(24, 39, 75, .06) 0 1px, transparent 1px 20px),
    linear-gradient(135deg, rgba(255, 255, 255, .92), rgba(255, 255, 255, 0));
}
.cover.tone-b { background-color: var(--accent-weak); }
.cover.tone-c { background-color: #E9EDFB; }
/* 案例卡的封面把分数直接做成主视觉：分数是有信息量的，
   不能像装饰序号那样压到 10% 透明度，必须实心强调色。
   margin-bottom 归零——.case-card 本身是 flex 且已有 gap，避免出现两道间距。 */
.cover.case-cover {
  display: flex; align-items: flex-end; gap: 6px;
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: 0;
}
.cover.case-cover .score {
  font-family: var(--font-heading); font-weight: 700;
  font-size: 2.5rem; line-height: .9; letter-spacing: -.03em;
  color: var(--accent); font-variant-numeric: tabular-nums;
}
.cover.case-cover .unit { font-size: var(--fs-caption); color: var(--fg-2); padding-bottom: 3px; }
.cover .glyph {
  position: absolute; right: var(--sp-3); bottom: -.24em;
  font-family: var(--font-heading); font-weight: 700;
  font-size: 4rem; line-height: 1;
  color: var(--ink-900); opacity: .1;
  font-variant-numeric: tabular-nums; letter-spacing: -.04em;
}
.cover .mark {
  position: absolute; left: var(--sp-3); top: var(--sp-3);
  width: 20px; height: 3px; border-radius: var(--r-full);
  background: var(--accent);
}

/* 四角测量框：对数据卡、凭证卡加角标 */
.measured { position: relative; }
/* 四角测量框改用橙：这是全站唯一一处"数据被测量"的记号，
   交给辅色里唯一被拉成"高瘦"形状的橙（见 §2.1 V4.1 配色比读数）。 */
.measured::before, .measured::after {
  content: ""; position: absolute; width: 12px; height: 12px;
  border-color: var(--warn-dot); opacity: .7; pointer-events: none;
}
.measured::before { top: 10px; left: 10px; border-top: 1px solid; border-left: 1px solid; }
.measured::after { bottom: 10px; right: 10px; border-bottom: 1px solid; border-right: 1px solid; }

/* 4.3 数据块：大数字 + 小号单位 */
.data-value {
  font-size: var(--fs-display);
  line-height: 1;
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--fg-1);
}
.data-unit { font-size: var(--fs-h3); font-weight: 400; color: var(--fg-2); margin-left: var(--sp-2); }
.data-label { color: var(--fg-2); font-size: var(--fs-sm); margin-top: var(--sp-4); }
.data-note { color: var(--fg-3); font-size: var(--fs-caption); margin-top: var(--sp-2); }

/* 4.4 数据带
   V2 全浅色后不再需要"深色数据带"这一层：数据带与普通段落只在底色上差一档，
   由 .band-tint 承担。因此本节没有独占规则，保留标题只为维持编号连续。 */

/* 4.5 占比条与环形仪表 */
.ratio { margin-bottom: var(--sp-5); }
.ratio-head { display: flex; justify-content: space-between; font-size: var(--fs-sm); color: var(--fg-2); }
.ratio-track {
  height: 6px; border-radius: var(--r-full); background: var(--rule); overflow: hidden; margin-top: var(--sp-3);
}
.ratio-fill {
  height: 100%; width: 0; border-radius: var(--r-full); background: var(--accent);
  transition: width 900ms var(--ease-out);
}
.is-in .ratio-fill { width: var(--ratio, 0%); }

.ring { width: 140px; height: 140px; }
.ring-track { fill: none; stroke: var(--rule); stroke-width: 8; }
.ring-fill { fill: none; stroke: var(--accent); stroke-width: 8; stroke-linecap: round; }

/* 4.6 表格 */
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
/* 表头降一档字号并加字距：15px 半粗的表头与正文同号时会"糊"在一起，
   而缩小 + 拉开字距能把表头明确区分为标签层，同时不多占高度 */
thead th {
  text-align: left; padding: var(--sp-4);
  border-bottom: 2px solid var(--accent);
  color: var(--fg-2); font-weight: 600; font-size: var(--fs-caption);
  letter-spacing: .06em; white-space: nowrap;
}
tbody td { padding: var(--sp-4); border-bottom: 1px solid var(--rule); color: var(--fg-2); vertical-align: top; }
tbody tr { transition: background var(--dur) var(--ease-out); }
tbody tr:nth-child(even) td { background: var(--tint); }
/* hover 高亮：台账表列多（最多 9 列），没有行高亮时横向扫视极易串行 */
tbody tr:hover td { background: var(--tint-2); }
/* 用 --surface-2 而不是 --paper-2：前者会随纸带档位变化，
   后者是固定值，一旦文章体落在冷灰纸带上，表头会与底色脱节 */
.article-body thead th { position: sticky; top: 0; background: var(--surface-2); }

/* 4.7 徽标 */
/* 徽标是"要被读到的文字"。品牌原色 --brand (#4B74FF) 对白底只有 4.01:1，
   达不到 AA，所以文字用 --accent (#2F4FD6, 6.54:1)，描边才用品牌原色。 */
/* 可信标记（985/211）改用绿：它是一个**已核验的正面事实**，
   不是品牌强调。品牌蓝留给"我们自己说的话"，绿留给"第三方能查到的事"。 */
.trust-mark {
  display: inline-block; font-size: var(--fs-caption); font-weight: 600;
  color: var(--ok); border: 1px solid #AEDCC6; background: var(--ok-050);
  border-radius: var(--r-full); padding: 2px var(--sp-2); letter-spacing: .04em;
}
.tag {
  display: inline-block; font-size: var(--fs-caption); color: var(--fg-2);
  border: 1px solid var(--rule); border-radius: var(--r-full);
  background: var(--surface-2);
  padding: 3px var(--sp-3); margin: 0 var(--sp-2) var(--sp-2) 0;
}
.tag-pending { color: var(--warn); border-color: #F3D5A8; background: var(--warn-050); }
.tag-ok { color: var(--ok); border-color: #AEDCC6; background: var(--ok-050); }
.tag-danger { color: var(--danger); border-color: #F0C0BB; background: var(--danger-050); }

/* ── 4.2b 三辅色的用法纪律（V4.2）─────────────────────────────────────────
   客户色板里三个辅助色合计约占色块面积的 1/4，但 V4.0/V4.1 只用在了几枚
   状态标签上。本轮把它们铺开成**一条贯穿全站的核验状态轴**，而不是装饰：

     绿 = 可核验 / 已取得        → 可信标记、已取得标签、可核验清单
     橙 = 进行中 / 申办中 / 待核验 → 时间轴、侧栏脊、证据框、测量角标
     红 = 明确不提供 / 尚未具备   → 边界标签（只用在说明"我们给不出"的地方）

   ⚠️ **纪律：三色只表示"核验状态"，不表示"分类"。**
   同一屏里出现两个不同颜色的标签，必须意味着两种不同的核验状态。
   一旦拿它们区分类别（按科目着色、按届别着色、按板块着色），
   纪律立刻破，页面会滑回促销感——这是这套配色唯一的失败模式。

   ⚠️ 三色原值（#FE830F / #EE4545 / #2AB977）对白底只有 2.48 / 3.77 / 2.53:1，
   一律**不能承载文字**。`-dot` 才是原值，文字一律用压深后的 --ok / --warn / --danger。 */

/* 编辑式侧栏的纵向脊：把散落在各页的 .aside 用同一根橙线串起来。
   这是"橙 = 纵向贯穿"最省成本也最连贯的落法。
   ⚠️ 窄屏的取消规则必须写在**本条之后**：媒体查询不增加特异性，
   同为 (0,2,0)，靠顺序决定胜负。写在前面的 @media 会被这条静默压掉。 */
.split > .aside {
  border-left: 2px solid var(--warn-dot);
  padding-left: var(--sp-5);
}
@media (max-width: 980px) {
  /* 窄屏塌缩成单列，侧栏不再是"纵向脊" */
  .split > .aside { border-left: 0; padding-left: 0; }
}

/* 「可核验清单」这类卡片用绿左条：它列的全是"你能自己去查到"的东西 */
.card.ok-list { border-left: 3px solid var(--ok-dot); }

/* 4.8 折叠面板：优先用原生 details，零 JS 也可展开。
   V3.0：从"一条条分割线"改为"一张张独立白卡"——与 .hair-list / .card 同质感。
   注意仍不设 overflow: hidden，圆角靠 summary 与面板自身承担。 */
.collapse {
  background: var(--surface-2);
  border: 1px solid rgba(24, 39, 75, .07);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  margin-bottom: var(--sp-3);
}
.collapse summary {
  cursor: pointer; padding: var(--sp-5); font-family: var(--font-heading);
  font-size: var(--fs-h3); list-style: none; display: flex; justify-content: space-between; gap: var(--sp-4);
}
.collapse summary::-webkit-details-marker { display: none; }
/* 用 ASCII 的 + 与 U+2212 真减号，而不是全角 ＋/－：
   全角符号在衬线标题旁偏大偏重，且与正文数字不同源 */
.collapse summary::after {
  content: "+"; color: var(--accent); font-family: var(--font-sans);
  font-size: 1.35rem; font-weight: 300; line-height: 1; flex: none;
}
.collapse[open] summary::after { content: "\2212"; }
.collapse .collapse-body { padding: 0 var(--sp-5) var(--sp-5); color: var(--fg-2); }
.collapse .collapse-body p:last-child { margin-bottom: 0; }

/* 4.9 时间轴（8 大类测试） */
.timeline { position: relative; display: grid; gap: var(--sp-6); }
.timeline-track { position: absolute; left: 15px; top: 8px; bottom: 8px; width: 2px; background: var(--rule); }
/* 时间轴是全站最长的一根纵向元素，按色板里橙的"高瘦"形状交给它 */
.timeline-line {
  position: absolute; left: 0; top: 0; width: 2px;
  background: linear-gradient(180deg, var(--warn-dot), rgba(254, 131, 15, .28));
  height: 0; transition: height 1200ms var(--ease-out);
}
.is-in .timeline-line { height: 100%; }
.timeline-item { position: relative; padding-left: var(--sp-9); }
/* 轴上的点用白底：V3.0 起页面底压深，走 --surface 会变成灰点，读成"未激活" */
.timeline-dot {
  position: absolute; left: 8px; top: 6px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--surface-2); border: 2px solid #F0C39A;
  transition: border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.is-in .timeline-item .timeline-dot { border-color: var(--warn-dot); background: var(--warn-dot); }
.timeline-item h3 { margin-bottom: var(--sp-2); }
.timeline-item .meta { font-size: var(--fs-caption); color: var(--fg-3); }

/* 4.10 七位一体闭环链
   取代原来的环形 SVG。环形图必须先撑到 340px 高，节点文字才读得清
   （viewBox 420 里 11px 的标签，缩到 240px 就只剩 6px），
   在"高信息密度"的版面上这是一笔不划算的开销。
   横向链只要 72px 高，而且更贴合「讲→练→测→评→答→督→专」的先后顺序。 */
.loop {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--sp-2);
}
/* 7 个节点用品牌淡底填充：闭环链是首页唯一一处"横向铺开的蓝"，
   实心品牌蓝压不住白字（4.01:1），所以用淡底 + 文字级蓝的写法。 */
.loop li {
  position: relative;
  background: linear-gradient(160deg, #F2F6FF 0%, #E4EBFF 100%);
  border: 1px solid rgba(75, 116, 255, .22);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  padding: var(--sp-3) var(--sp-2);
  text-align: center;
}
/* 节点之间的连接线：用伪元素画，不额外占列宽 */
.loop li::after {
  content: ""; position: absolute; top: 50%;
  right: calc(var(--sp-2) * -1); width: var(--sp-2); height: 1px;
  background: var(--rule);
}
.loop li:last-child::after { display: none; }
.loop .n {
  display: block; font-family: var(--font-heading); font-weight: 700;
  font-size: var(--fs-h3); line-height: 1.2;
  color: var(--accent);
}
.loop .r { display: block; margin-top: 2px; font-size: var(--fs-caption); color: var(--fg-3); }
@media (max-width: 980px) {
  .loop { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .loop { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 4.10b 带序号的卡片：序号是排版元素，不是图位。
   绝对定位压在右上角，不额外占一行业高——8 张测试卡因此省下约 570px。
   与 .cover 的区别：封面是"没有照片时的图位"，序号卡只是"给条目编号"。 */
.num-card { position: relative; padding-top: var(--sp-6); }
.num-card .num {
  position: absolute; top: var(--sp-3); right: var(--sp-4);
  font-family: var(--font-heading); font-weight: 700;
  font-size: 1.75rem; line-height: 1; letter-spacing: -.04em;
  color: var(--accent); opacity: .22;
  font-variant-numeric: tabular-nums; pointer-events: none;
}

/* 4.10c 七位一体环形（首页已改用 .loop，这里留给更宽的画面） */
.ring-diagram { width: 100%; max-width: 460px; margin: 0 auto; display: block; }
.ring-diagram .ring-track { stroke: var(--rule); stroke-width: 1.5; fill: none; }
.ring-diagram .ring-link { stroke: var(--accent); stroke-width: 2; fill: none; opacity: .5; }
.ring-diagram .node circle { fill: var(--surface-2); stroke: var(--rule); stroke-width: 1.5; }
.ring-diagram .node text {
  font-family: var(--font-heading); font-size: 22px; fill: var(--fg-1);
  text-anchor: middle; dominant-baseline: central;
}
.ring-diagram .node .node-label {
  font-family: var(--font-sans); font-size: 11px; fill: var(--fg-3);
}
.is-in .ring-diagram .node circle { animation: nodeIn var(--dur-slow) var(--ease-spring) both;
  animation-delay: calc(var(--i, 0) * 90ms); }
.is-in .ring-diagram .node text { animation: fadeIn var(--dur-slow) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 90ms + 120ms); }
.is-in .ring-diagram .ring-link {
  stroke-dasharray: var(--len, 1200); stroke-dashoffset: var(--len, 1200);
  animation: drawRing 1200ms var(--ease-out) 640ms both,
             ringBreath 12s var(--ease-breath) 2s infinite;
}

@keyframes nodeIn { from { opacity: 0; transform: scale(.55); } to { opacity: 1; transform: scale(1); } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes drawRing { from { stroke-dashoffset: var(--len, 1200); } to { stroke-dashoffset: 0; } }
/* 环形连线呼吸：只改描边透明度，不动几何，读图不受影响 */
@keyframes ringBreath { 0%, 100% { stroke-opacity: .26; } 50% { stroke-opacity: .68; } }

/* 4.11 留资表单 */
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-5); }
.field { display: flex; flex-direction: column; gap: var(--sp-2); }
.field.full { grid-column: 1 / -1; }
.field label { font-size: var(--fs-sm); color: var(--fg-2); }
.field .required::after { content: " *"; color: var(--accent); }
.field input, .field select, .field textarea {
  font: inherit; padding: var(--sp-4);
  border: 1px solid var(--rule); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--fg-1);
}
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--accent); outline: none;
}
.hint { font-size: var(--fs-caption); color: var(--fg-3); }
.honeypot { position: absolute; left: -9999px; }

/* 4.12 列表与案例卡 */
.case-card { display: flex; flex-direction: column; gap: var(--sp-3); }
.case-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4); }
.case-score { font-size: 2.25rem; font-weight: 700; line-height: 1; }
.case-school { font-size: var(--fs-h3); font-family: var(--font-heading); }
.case-quote {
  font-family: var(--font-heading); color: var(--fg-2);
  border-left: 2px solid var(--accent); padding-left: var(--sp-4); margin: var(--sp-3) 0 0;
}
.faculty-subject { font-size: var(--fs-caption); color: var(--accent); letter-spacing: .08em; }

/* ── 5. 页头 / 页脚 ────────────────────────────────────────────────────────── */
/* V3.0：顶栏改为"浮在页面之上的白卡"——纯白 92% + 一层浅影，
   与 .card 用同一套语汇（白面 + 细边 + 影），而不是靠一条分割线。 */
.site-header {
  position: sticky; top: 0; z-index: 50;
  /* 玻璃拟态：半透明白 + 加饱和的模糊。alpha 从 .92 降到 .78——
     .92 时模糊几乎看不出，和一块实心白板没有区别，白拿一层 GPU 开销。 */
  background: rgba(255, 255, 255, .78);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid rgba(75, 116, 255, .14);
  box-shadow: 0 1px 2px rgba(24, 39, 75, .04), 0 12px 32px rgba(24, 39, 75, .06);
}
.site-header .inner {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-6);
  height: 64px;
}
.brand { display: flex; align-items: center; gap: var(--sp-3); font-family: var(--font-heading); font-size: 1.25rem; color: var(--ink-900); }
.brand:hover { text-decoration: none; }
.brand .seal {
  width: 30px; height: 30px; display: grid; place-items: center;
  background: var(--accent); color: #fff; border-radius: var(--r-sm);
  font-family: var(--font-heading); font-size: 1rem;
}
.site-nav { display: flex; align-items: center; gap: var(--sp-5); font-size: var(--fs-sm); }
.site-nav a { color: var(--text-2); }
.site-nav a[aria-current="page"], .site-nav a:hover { color: var(--accent); }
.nav-toggle {
  display: none; background: transparent; border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: var(--sp-2) var(--sp-4); font: inherit; cursor: pointer;
}

/* 页脚：V2 起改用最深的一档冷灰纸压底，而不是深色轨。
   顶部加一条向品牌蓝淡出的细线，保留"收口"的仪式感。 */
.site-footer {
  background-color: var(--paper-4);
  background-image: linear-gradient(90deg, var(--brand), rgba(75, 116, 255, 0));
  background-repeat: no-repeat;
  background-position: 0 0;
  background-size: 100% 2px;
  color: var(--text-2);
  padding: var(--sp-7) 0 var(--sp-5);
}
.site-footer a { color: var(--text-2); }
.site-footer a:hover { color: var(--accent); }
.footer-grid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-7); }
.footer-grid h4 { color: var(--text-1); font-size: var(--fs-sm); letter-spacing: .06em; margin-bottom: var(--sp-4); }
.footer-grid ul { list-style: none; margin: 0; padding: 0; font-size: var(--fs-sm); }
.footer-grid li { margin-bottom: var(--sp-2); }
.footer-bottom {
  margin-top: var(--sp-6); padding-top: var(--sp-4);
  border-top: 1px solid #CFD5E4;
  font-size: var(--fs-caption); display: flex; flex-wrap: wrap; gap: var(--sp-4); justify-content: space-between;
}

/* 滚动进度条 */
.scroll-progress {
  position: fixed; top: 0; left: 0; height: 2px; width: 0;
  background: var(--accent); z-index: 60;
}

/* ── 6. 正文（承接内容页）─────────────────────────────────────────────────── */
/* V3.0：内页页头同样收一档。整屏高的页头一页只能讲一件事，
   与"高信息密度"是相反的取向。 */
.page-hero { padding: var(--section-y) 0 var(--sp-6); border-bottom: 1px solid var(--rule); }
.page-hero h1 { max-width: 20em; }
.page-hero .lede { color: var(--fg-2); max-width: var(--measure); font-size: 1.125rem; }
.breadcrumb { font-size: var(--fs-caption); color: var(--fg-3); margin-bottom: var(--sp-5); }
.breadcrumb a { color: var(--fg-3); }

.article-body { max-width: var(--measure); }
.article-body h2 { margin-top: var(--sp-8); padding-top: var(--sp-5); border-top: 1px solid var(--rule); }
.article-body h2:first-child { margin-top: 0; border-top: 0; padding-top: 0; }
.article-body ul, .article-body ol { padding-left: 1.4em; margin: 0 0 var(--sp-5); }
.article-body li { margin-bottom: var(--sp-2); }
/* 项目符号压到三级灰：正文已是二级灰，符号再深就会与文字抢注意力 */
.article-body li::marker { color: var(--fg-3); }
.article-body strong { color: var(--fg-1); }

/* 证据框（"怎么自行核验"）改用橙：它的语义就是"请你去核验"，
   正对辅色里"进行中 / 待核验"的那一色。左侧 3px 实条让它成为一条纵向记号。 */
.evidence-box {
  margin-top: var(--sp-8); padding: var(--sp-5) var(--sp-6);
  background: var(--warn-050); border-left: 3px solid var(--warn-dot);
  border-radius: var(--r-md);
  font-size: var(--fs-sm); color: var(--fg-2);
}
.evidence-box h3 { font-size: var(--fs-sm); letter-spacing: .06em; margin-bottom: var(--sp-3); }
.evidence-box ul { margin: 0; padding-left: 1.2em; }

/* ── 7. 显现动效（渐进增强）─────────────────────────────────────────────────
   默认可见；仅当 JS 可用（html.js）时才接管动画初始态。
   爬虫抓取到的原始 HTML 无 .js 类，因此内容始终可见。
   ──────────────────────────────────────────────────────────────────────── */
.reveal { opacity: 1; }

.js .reveal { opacity: 0; transform: translateY(16px); }
.js .reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}

/* 显影：从模糊到清晰 */
.js .develop { opacity: 0; filter: blur(6px); }
.js .develop.is-in {
  opacity: 1; filter: blur(0);
  transition: opacity 700ms var(--ease-out), filter 700ms var(--ease-out);
}

/* 数字计数：服务端直出终值，JS 只做视觉插值 */
.count { display: inline-block; }

/* ── 7.1 呼吸层 ──────────────────────────────────────────────────────────────
   规则：只让「光」和「尺度」呼吸，绝不让文字呼吸。
   文字透明度或字重的往复会直接损害可读性，是廉价动效最常见的翻车点。
   所有呼吸均为纯 CSS、无 JS 依赖，且不承载任何信息。
   ──────────────────────────────────────────────────────────────────────── */
@keyframes drift {
  0%, 100% { transform: translate3d(-2%, -1.5%, 0) scale(1);    opacity: .80; }
  50%      { transform: translate3d(2%, 1.5%, 0) scale(1.08); opacity: 1; }
}
@keyframes breath {
  0%, 100% { transform: scale(1);    opacity: .72; }
  50%      { transform: scale(1.06); opacity: 1; }
}
/* 光晕：给强调元素一层随呼吸收放的外光，不改变元素本身尺度 */
@keyframes haloPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(75, 116, 255, 0),    0 0 22px 0 rgba(47, 79, 214, .16); }
  50%      { box-shadow: 0 0 0 6px rgba(75, 116, 255, .10), 0 0 34px 2px rgba(47, 79, 214, .26); }
}

/* 印章：唯一允许"呼吸"的文字元素——它是图形，不是正文 */
.brand .seal, .login-card .seal { animation: breath var(--dur-breath) var(--ease-breath) infinite; }

/* 首屏主标题下的一条品牌色线：长度与透明度同步往返，制造"刻度在走动"的暗示 */
.hero-rule {
  height: 1px; border: 0; margin: var(--sp-5) 0 0;
  background: linear-gradient(90deg, var(--brand) 0%, rgba(75, 116, 255, .05) 62%, transparent 100%);
  transform-origin: left;
  animation: heroRule var(--dur-breath) var(--ease-breath) infinite;
}
@keyframes heroRule {
  0%, 100% { transform: scaleX(.72); opacity: .55; }
  50%      { transform: scaleX(1);   opacity: 1; }
}

/* 纸带主按钮：外光呼吸，提示"这里可以按"，但不动按钮本身 */
.band-tint .button.primary { animation: haloPulse 6.5s var(--ease-breath) infinite; }

/* ── 8. 微动效系统（V4.4）：点呼吸 · 线流光 · 信号传递 ──────────────────
   首屏星座网络给了三种动作，但它们不该只属于首屏。这里是同一套语汇在
   全站的其余落点——**只加在"点"和"线"上**：只有点与线才谈得上"关联在动"，
   往卡片和表格上堆这类动效只会变成噪音。

     线流光   → /assessments 测评时间轴（光带沿轴向下流）
     点呼吸   → 全部数据卡 .measured 的四角标（一处 CSS，全站自动生效）
     信号传递 → 首页七位一体闭环链（7 个节点依次亮起，像信号在闭环里流过）
                首页 8 张测试卡的序号（依次被点亮，读成"测试在依次执行"）

   ⚠️ 刻意**不**把星座网络复制到其它区块：首屏之所以是首屏，就因为它唯一；
      同一张图形出现第二次，第一次的份量就没了。这里呼应的是**动作**，不是图形。
   ⚠️ 只用 opacity / background-position 这类小面积重绘的属性，
      不用 width / height / top / margin，避免每帧触发整块重排。
   ⚠️ 四个落点全部由本文件末尾的 prefers-reduced-motion 兜底统一关停。 */

/* 闭环链：信号依次流过 7 个节点。
   周期 10.5s = 7 × 1.5s，每个节点延迟错开一格，所以任意时刻只有一个在亮。

   ⚠️ 只脉冲**描边与辉光**，不染整张卡片。第一版是给 ::before 铺一层 --brand
   再淡入淡出，但节点大字是 18px/700——按 WCAG 不算"大字"（要 ≥18.66px），
   仍需 4.5:1，而蓝底叠到 alpha .20 时只有 3.98:1。描边与辉光是装饰，
   不参与对比度，所以这样改既更亮眼也更安全。
   用 ::before 是因为它是第一个子元素，绘制顺序在文字之下、卡片底色之上。 */
.loop li::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit;
  border: 1px solid var(--brand); opacity: 0; pointer-events: none;
  box-shadow: 0 0 0 3px rgba(75, 116, 255, .13), 0 0 18px rgba(75, 116, 255, .40);
  animation: loopSignal 10.5s linear infinite;
  animation-delay: calc(var(--i) * -1.5s);
}
@keyframes loopSignal {
  0%, 100% { opacity: 0; }
  7%       { opacity: 1; }
  16%      { opacity: 0; }
}

/* 测试卡序号依次被点亮：与闭环链同一套"依次"节奏，不新增任何元素 */
.num-card .num {
  animation: numBreath 8s var(--ease-breath) infinite;
  animation-delay: calc(var(--i) * -.6s);
}
@keyframes numBreath {
  0%, 100% { opacity: .16; }
  50%      { opacity: .34; }
}

/* 测评时间轴：光带沿轴向下流动。
   ⚠️ background-position 的起止必须**同为百分比**：长度与百分比之间不能插值，
   写成 -64px → 100% 会直接跳变，而不是流动。 */
.timeline-track::after {
  content: ""; position: absolute; left: -2px; top: 0; width: 6px; height: 100%;
  border-radius: var(--r-full); pointer-events: none;
  background-image: linear-gradient(180deg, transparent 0%, rgba(254, 131, 15, .85) 50%, transparent 100%);
  background-size: 100% 64px;
  background-repeat: no-repeat;
  animation: axisFlow 6.5s linear infinite;
}
@keyframes axisFlow {
  from { background-position: 0 -100%; }
  to   { background-position: 0 100%; }
}

/* 数据卡四角标微呼吸：一处 CSS，全站所有 .measured 自动生效 */
.measured::before, .measured::after {
  animation: cornerBreath 7s var(--ease-breath) infinite;
}
@keyframes cornerBreath {
  0%, 100% { opacity: .40; }
  50%      { opacity: .95; }
}

/* ── 9. 无障碍降级 ─────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .js .reveal, .js .develop { opacity: 1 !important; transform: none !important; filter: none !important; }
  .is-in .ratio-fill { width: var(--ratio, 0%); }
  /* 呼吸层整体停摆：底光保留静态，不再漂移 */
  .band-tint::before { animation: none !important; opacity: 1; }
  .hero-rule { animation: none !important; transform: none; opacity: 1; }
  .band-tint .button.primary { animation: none !important; }
}

/* ── 9. 响应式 ───────────────────────────────────────────────────────────────
   本段只放"跨组件"的响应式规则；各版面关系（.split / .stagger / .hero-index）
   的塌缩写在它们的定义处（§3.1），就近维护。

   两条硬规则（踩过的坑，勿回退）：

   ① 塌缩后一律写 minmax(0, 1fr)，不写 1fr。
      1fr 的最小值是 auto，等于内容的 min-content；表单控件（input / select /
      textarea）与表格的固有宽度经常大于视口，轨道就收缩不下去，整页横向溢出。
      实测：/contact 在 375px 下曾溢出 28px（379px 宽的 input 撑开了单列轨道）。
      minmax(0, …) 等价于给每个网格项加 min-width: 0。

   ② 塌缩规则必须能打赢原规则。
      `.split.hero` 特异性是 (0,2,0)，只写 `.split` 是 (0,1,0)，媒体查询会静默失效。
      实测：首页首屏曾在 375px 下保持两栏（177px + 101px），大标题被挤成一条细柱。
      ∴ 塌缩时必须逐一列出全部组合类。

   新增或改动网格列时，务必在这里或 §3.1 同步，否则窄屏会静默错版。
   ────────────────────────────────────────────────────────────────────────── */
@media (max-width: 980px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ring-diagram { max-width: 360px; }
}

@media (max-width: 640px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }
  .form-grid { grid-template-columns: minmax(0, 1fr); }
  .footer-grid { grid-template-columns: minmax(0, 1fr); }
  .nav-toggle { display: inline-block; }
  .site-nav {
    display: none; position: absolute; top: 64px; left: 0; right: 0;
    flex-direction: column; align-items: flex-start; gap: var(--sp-4);
    background: var(--paper-2); border-bottom: 1px solid var(--line);
    padding: var(--sp-5);
  }
  .site-nav.open { display: flex; }
  .data-value { font-size: 2.5rem; }
  .ring-diagram { max-width: 300px; }
  /* 时间轴在窄屏收窄缩进：96px 左侧留白会吃掉 375px 视口的四分之一 */
  .timeline-item { padding-left: var(--sp-8); }
  .timeline-track { left: 11px; }
  .timeline-dot { left: 4px; }
  /* 宽表（案例台账 9 列）在窄屏靠容器横向滚动，需要惯性滚动 */
  .table-wrap { -webkit-overflow-scrolling: touch; }
  /* 页头上下留白在窄屏减半，否则 375px 首屏只剩标题看不见正文 */
  .page-hero { padding: var(--sp-7) 0 var(--sp-6); }
}
