/* ============================================================================
   非洲观察官网 — 样式
   ----------------------------------------------------------------------------
   单文件、零依赖、零外链字体（离线可跑、首屏无第三方请求）。

   【配色：与会员站（frontend/src/styles.css）**同一套色板】**
   ----------------------------------------------------------------------------
   原来这里是「近黑底 + 暖砂文字 + 黄铜强调」，与会员站的绿完全两套语言。
   现在整份色板来自会员站的 :root，**变量名也照抄**（--bg / --panel / --ink /
   --ink-2 / --ink-3 / --line / --accent / --accent-2 / --accent-ink），
   这样以后改主题只需要改一处，而且改的时候不会因为「官网的 --ink 是背景色、
   会员站的 --ink 是文字色」这种同名不同义而搞错。

   ⚠️ 三个**必须理解**的映射（两站结构不同，逐元素抄不过来）：
     ① 会员站 --ink 是**文字色**，官网原来 --ink 是**背景色** —— 名字相同、语义
        相反。所以这一版不是"把值换掉"，而是"把语义摆正"：
        `--ink` 在两边都指最深的那一层文字。
     ② 官网原来 --brass（黄铜）承担了"主色"角色（eyebrow、区块标题竖条、名次
        数字、按钮底色）。它对应会员站的 `--accent`（主题绿）；而 --brass-2
        （更亮的黄铜，用在数字大字与链接 hover）对应 `--accent-ink`（深绿）——
        在浅底上"更亮"要反过来变成"更深"才能保住对比度。
     ③ 风险三档色不在会员站的 :root 里，而在它的 `--lv-hot / --lv-mid / --lv-low`
        （需求 TOP30-M 定义的档位色）。这里直接取那三个值 —— 包括
        **中风险刻意用琥珀不用绿**：这套界面里绿色读出来是"安全/正常"，
        拿来标中风险会和"低"混。这条口径两边必须一致。

   为什么浅色底比深色底更需要小心：
     · 深色下靠"颜色深浅叠加"就能分层，浅色下必须靠**描边 + 阴影**。所以
       `.qr` 加了 box-shadow、`.qr__img` 加了描边（白图在白卡上否则没有边界）。
     · `.brief__rank` 原来 `opacity: .82` 是为了压住黄铜的亮度；绿在浅底上直接
       降透明度会发脏，改成不降。
     · 页眉原来是深色半透明，现在改成**白色半透明** —— 保持 blur 效果不变。

   第一轮只动了颜色（+ 层级阴影）。第二轮加了动效与移动端适配。第三轮把
   字体改成「粗大硬朗」（黑体 + 700 + 收紧字距 + 方角），并把会员登录入口
   整块移除 —— 见下方 :root 里的字体注释。栅格与断点始终没动。

   ⚠️ 三轮改动里唯一**同时**碰颜色和形状的只有 `--r`（10px → 3px）：
   配色仍与会员站一致，圆角是刻意偏离，理由写在 `--r` 旁边。
   ========================================================================== */

:root {
  /* —— 底色与层级 —— */
  --bg:        #f4f6f8;   /* 页面底（会员站 --bg） */
  --panel:     #ffffff;   /* 卡片 / 交替区块底（会员站 --panel） */
  --hover:     #f7f9fb;   /* 白卡 hover（比 --bg 更淡，避免整块变灰） */

  /* —— 文字 —— */
  --ink:       #1f2329;   /* 主文字（会员站 --ink） */
  --ink-2:     #5a6068;   /* 次级文字（会员站 --ink-2） */
  --ink-3:     #9aa0a8;   /* 弱化文字 / 占位（会员站 --ink-3） */

  /* —— 描边 —— */
  --line:      #e8eaed;   /* 标准描边（会员站 --line） */
  --line-2:    #f0f2f4;   /* 更弱的分隔（区块内部细线） */

  /* —— 主色：主题绿 —— */
  --accent:     #18a058;  /* 会员站 --accent */
  --accent-2:   #e8f6ee;  /* 浅绿底：hover / 高亮（会员站 --accent-2） */
  --accent-ink: #14703f;  /* 深绿字（会员站 --accent-ink） */

  /* —— 风险三档：与会员站 --lv-hot / --lv-mid / --lv-low 同值 —— */
  --risk-high: #cf3a24;
  --risk-mid:  #b26b06;
  --risk-none: #6b7280;

  --shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 6px 18px rgba(0, 0, 0, .05);

  /* ══════════════════════════════════════════════════════════════════
     字体：粗大、硬朗 —— 对齐安保公司的专业形象
     ------------------------------------------------------------------
     ① **全站单一字族，不再用衬线**。原来标题走 `--serif`（Georgia /
        Songti / 宋体），那是"书卷气"：文学、学术、杂志的调性，靠的是
        笔画粗细对比和末端的小衬线。安保要的是「力量、条理、不含糊」，
        衬线的收笔恰恰在削弱这一点。所以 `--serif` **整个删掉**，
        标题与正文都落在 `--sans` 上，靠**字重**而不是字族做层级。
     ② 英文栈把 Helvetica / Arial 提到最前：笔画直、字腔方、末端平切。
        原来打头的 `-apple-system`（SF Pro）笔画末端带圆角、字腔偏圆，
        是 iOS 的亲和力语言，不是安保的语言。
        中文回落 PingFang SC / 微软雅黑 —— 黑体本身就是方头平尾。
     ③ 字重只用 400 / 600 / 700 三档，**上限就是 700**。
        写 800/900 会怎样：Windows 的微软雅黑只有 Regular + Bold 两档，
        浏览器为了凑出 800 会做"合成加粗"（把字形沿轮廓描粗一圈），
        中文字笔画立刻粘连、小字发糊 —— 看起来更重，实际更难读。
        所以"再粗一档"要靠**放大字号 + 收紧字距**，不是堆 font-weight。
     ══════════════════════════════════════════════════════════════════ */
  --sans: "Helvetica Neue", Helvetica, Arial, "PingFang SC",
          "Hiragino Sans GB", "Microsoft YaHei", "Source Han Sans SC",
          "Noto Sans CJK SC", sans-serif;

  /* 三档字重，语义化命名：以后整体"再粗/再细一档"只改这三行 */
  --w-black: 700;   /* 主标题 / 区块标题 / 卡片标题 / 大数字 / 小标签 */
  --w-bold:  600;   /* 条目标题 / 按钮 / 导航 / 次级强调 */
  --w-body:  400;   /* 正文 */

  --wrap: 1180px;
  --gut: 28px;
  /* 圆角：硬朗的关键一半在"方"。
     原来 10px 是照会员站 `.btn` 抄的（为了配色统一），但 10px 的圆角
     读出来是"线上产品的柔和感"；安保要的是**方头方脑的标牌感**。
     3px 保留一点点边缘抗锯齿，视觉上仍是直角。
     ⚠️ 这是形状层面的有意偏离，不是漏改 —— 配色仍与会员站一致。 */
  --r: 3px;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* iOS 横屏会自动放大正文（它觉得 12px 太小），一放大版式就全乱了。
     显式声明 100% 关掉这个"贴心的"自动调整文字大小。 */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* ⚠️ 锚点跳转必须补 scroll-margin-top。
   页眉是 `position: sticky` 的 68px 高，点导航「服务 / 每日评估」跳过去时，
   目标区块的标题正好**压在页眉底下** —— 看起来像"跳歪了"，其实是被盖住了。
   这个是手机上尤其明显的老问题（手机上还有个抽屉收起的过程，观感更差）。 */
section[id] { scroll-margin-top: 78px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  /* 拆成长属性而不是 `font:` 简写：简写里混 var() 一旦变量名打错，
     整条声明会**静默失效**（字体退回 Times），而长属性只会丢一处。 */
  font-family: var(--sans);
  font-size: 16px;
  font-weight: var(--w-body);
  /* 行高 1.65 → 1.62：硬朗感一半来自"密"。留 1.62 是中文正文的可读下限，
     再紧长段落就开始串行。 */
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* 数字等宽：分数、日期、条数在列表里上下对齐才像"数据"，不像"文案" */
  font-variant-numeric: tabular-nums;
}

a { color: inherit; text-decoration: none; }
/* `height: auto` 是必须的：只写 max-width: 100% 时，如果 <img> 带了 height 属性
   （或父级用 height 拉伸），浏览器会保持原高度 ⇒ 图被压扁。
   宽高比交给浏览器按内在比例算，这是图片自适应唯一的正确写法。 */
img { display: block; max-width: 100%; height: auto; }
/* 标题基线：700 + 负字距。负字距是"硬"的另一个来源 —— 字挨得紧才显得
   有秩序、有压迫感；松散的字距读起来是"从容"，那是另一类品牌气质。 */
h1, h2, h3 { margin: 0; font-weight: var(--w-black); letter-spacing: -.02em; }
p { margin: 0; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gut); }

/* 载入中：先藏内容避免 FOUC，配置到位后由 JS 摘掉 is-loading */
.app.is-loading main,
.app.is-loading .ftr { opacity: 0; }
main, .ftr { opacity: 1; transition: opacity .28s ease; }

/* ── 页眉 ───────────────────────────────────────────────────────────── */
.hdr {
  position: sticky; top: 0; z-index: 40;
  /* 深色版是 rgba(10,16,22,.82)；浅色版换成白。透明度与 blur 保持不变，
     这样"内容从页眉底下滚过去"的观感与原来一致。 */
  background: rgba(255, 255, 255, .86);
  backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--line);
}
.hdr__in {
  display: flex; align-items: center; gap: 26px;
  height: 68px;
}
/* ⚠️ 品牌名是**后台可配**的，长度不受控（实测有人把它填成
   「利剑安保 · Sharp Sword Security」这种 26 字符的）。原来这里是 `flex: 0 0 auto`
   —— 不许收缩 —— 于是 390px 窄屏上「品牌名 + 语言切换 + 右侧按钮」直接撑出
   450px，页面横向溢出（而品牌名短的时候一切正常，所以这个 bug 藏了很久）。
   现在改成可收缩 + 省略号：宁可把品牌名截尾，也不能让整页横向滚动。
   （会员登录按钮已下线，右侧那组只剩语言与汉堡，但这条纪律不能撤 ——
     后台可配字段的"长度不受控"是长期约束，不是这一次的偶然。） */
.brand { display: flex; align-items: center; gap: 11px; flex: 0 1 auto; min-width: 0; }
.brand__mark { width: 26px; height: 26px; color: var(--accent); flex: 0 0 auto; }
/* `gap: 4px` 是 2026-10-30 加的：原来两行的 line-height 都是 1.1，
   行盒**紧贴**（实测 b 的 bottom 与 i 的 top 完全相等，gap = 0），
   17.5px 的品牌名和 10.5px 的副标题字面只差 2px 左右，看起来是"粘"在一起的。
   折算下来实际视觉空隙 ≈ 1.4px（两行各自的半行距） + 4px = 5.4px，
   约等于品牌名的 31%，既拉开又不散 —— 副标题仍是"从属"，不会被读成第二行标题。
   ⚠️ 行高本身**不能加大**：`.hdr__in` 是固定高 68px，品牌块只有 30.8px，
      加行高会让文字在 68px 里往上浮、和 26px 的盾牌图标不再视觉居中。
      用 gap 只加"两行之间"这一处，块高 30.8 → 34.8，仍远小于 68，页眉高度不变。 */
.brand__txt {
  display: flex; flex-direction: column; gap: 4px;
  line-height: 1.1; min-width: 0;
}
.brand__txt b {
  font-family: var(--sans);
  font-size: 17.5px; font-weight: var(--w-black);
  /* 品牌字用**正**字距（.01em）：其余标题一律负字距，唯独它放开一点点，
     让"标识"与"标题"区分开 —— 全站都紧到极限会变成一个声音。 */
  letter-spacing: .012em; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brand__txt i {
  font-style: normal; font-size: 10.5px; font-weight: var(--w-bold);
  color: var(--ink-3);
  letter-spacing: .15em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.nav { display: flex; gap: 24px; margin-left: auto; }
.nav a {
  font-size: 13.5px; font-weight: var(--w-bold); color: var(--ink-2);
  letter-spacing: .04em;
  padding: 4px 0; border-bottom: 1px solid transparent;
  transition: color .18s, border-color .18s;
}
/* hover 的下划线做到 2px：1px 在浅底上几乎看不见，2px 才有"选中"的力度。
   ⚠️ 不用 `border-bottom-width: 2px` —— 那会让盒子长高 1px，
   hover 的瞬间整条导航往下一抖。用 inset 阴影叠出一条，总厚度 2px 而盒子不变。 */
.nav a:hover {
  color: var(--ink);
  border-bottom-color: var(--accent);
  box-shadow: inset 0 -1px 0 var(--accent);
}

/* 右侧一组（语言切换 + 汉堡）**不许收缩**：按钮被压窄会换行，比品牌名截尾更难看 */
.hdr__act { display: flex; align-items: center; gap: 18px; flex: 0 0 auto; }
.lang { display: flex; align-items: center; gap: 7px; font-size: 13px; }
.lang a {
  color: var(--ink-3); font-weight: var(--w-bold); letter-spacing: .04em;
  padding: 2px 1px; transition: color .18s;
}
.lang a:hover { color: var(--ink); }
/* 当前语言（13px 小字）同样用深绿，理由见 .eyebrow 的注释 */
.lang a.is-on { color: var(--accent-ink); font-weight: var(--w-black); }
.lang__sep { color: var(--line); }

/* 按钮：底色/描边/文字对齐会员站 `.btn` 与 `.btn.primary`。
   ⚠️ `.btn--ghost`（白底 + 描边）随会员登录按钮一起下线了 ——
   它当时只有那一个使用者，留着就是死样式。 */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  border-radius: var(--r); cursor: pointer;
  font-size: 13.5px; font-weight: var(--w-bold); letter-spacing: .05em;
  transition: background .18s, border-color .18s, color .18s, filter .18s;
}
.btn svg { width: 15px; height: 15px; }
/* 会员站 `.btn.primary`：--accent 底 + 白字 + 同色描边。
   字重 700、字距 .06em、内边距放大一号 —— 首屏主按钮要"沉"，
   它必须比正文重一档才压得住版心。 */
.btn--solid {
  padding: 14px 26px;
  background: var(--accent); color: #fff;
  font-weight: var(--w-black); letter-spacing: .06em;
  border: 1px solid var(--accent);
}
.btn--solid:hover { filter: brightness(1.06); }

/* ── 首屏 ───────────────────────────────────────────────────────────── */
/* 背景不设色 ⇒ 透出 body 的 --bg。深色版靠底色分层，浅色版靠这层淡网格线
   提供"纸面"质感（线色用 --line，在 #f4f6f8 上可辨但不抢字）。 */
.hero { position: relative; overflow: hidden; border-bottom: 1px solid var(--line); }
.hero__grid {
  position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: 68px 68px;
  mask-image: radial-gradient(ellipse 78% 68% at 22% 34%, #000 22%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse 78% 68% at 22% 34%, #000 22%, transparent 78%);
}
.hero__in {
  position: relative;
  /* 只改上下内边距，**不要**写 `padding: 104px 0 96px` ——
     那会把 .wrap 的左右 28px 一起清零，首屏文字比下方区块左移一格。
     曾经就是这么写的，肉眼看着"好像有点歪"，只有量出 123 vs 151 才发现。 */
  padding-top: 104px;
  padding-bottom: 96px;
  /* 刻意**不**收窄这个容器：首屏文字的左边界必须和下面各区块对齐（都走 .wrap）。
     曾经给它 max-width: 880px，配合 .wrap 的 margin:auto 结果首屏整块被居中，
     比其它段落内缩了 120px，看起来像对不齐。收窄交给标题自身的 ch 宽度。 */
}
.eyebrow {
  font-size: 11.5px; font-weight: var(--w-black);
  letter-spacing: .2em; text-transform: uppercase;
  /* ⚠️ 小号绿字一律用 --accent-ink（深绿），**不要**用 --accent。
     实测对比度：#18a058 在白底上 3.38:1（低于正文 AA 的 4.5:1），
     #14703f 是 6.14:1。会员站也是这个口径 —— 亮绿只做按钮底/图标/描边，
     要写字就用深绿（见它的 .pill-btn:hover / .more-btn:hover）。 */
  color: var(--accent-ink); margin-bottom: 20px;
}
/* 首屏主标题：56px → 64px、400 → 700、字族 serif → sans、行高 1.16 → 1.14。
   「粗大」的全部手段都在这一行：**放大 + 加粗 + 收紧**，三者同时做才成立 ——
   只放大不收紧会显得稀松，只加粗不放大像"加粗的正文"。 */
.hero__title {
  font-size: clamp(34px, 5.6vw, 64px);
  font-weight: var(--w-black);
  line-height: 1.14;
  letter-spacing: -.025em;
  /* 23ch：字号变大后每行容字变少，放宽一点避免标题被切成三行 */
  max-width: 23ch;
  /* 断行交给浏览器均衡。不写的话 64px 下会断成
     「让每一支出海队伍都有可靠的 / 安全底座」= 13 字 + 4 字，
     第二行右边空一大片，整块重心往左上坠；balance 会摊成 9 / 8。
     不支持的浏览器直接忽略这一行 —— 退化成现状，不会更糟。 */
  text-wrap: balance;
  color: var(--ink);
}
.hero__sub {
  margin-top: 24px; max-width: 46ch;
  font-size: 17.5px; line-height: 1.62; color: var(--ink-2);
}
.hero__cta { margin-top: 38px; }

/* ── 数字条 ─────────────────────────────────────────────────────────── */
/* 白底条：与首屏（灰）和下面的服务区（灰）形成一明一暗的节奏，
   这正是会员站"灰底上铺白卡"的同一手法。 */
.stats { border-bottom: 1px solid var(--line); background: var(--panel); }
.stats__in {
  display: grid; grid-template-columns: repeat(3, 1fr);
  /* 不写 padding: 0 —— 那会清零 .wrap 的左右 28px，整条数字比其它区块左移一格 */
}
.stat { padding: 32px 0; border-right: 1px solid var(--line-2); padding-left: 26px; }
.stat:first-child { padding-left: 0; }
.stat:last-child { border-right: 0; }
.stat__v {
  font-size: 34px; font-weight: var(--w-black);
  line-height: 1.02; letter-spacing: -.03em;
  /* 34px 深绿数字：大字 AA 门槛 3:1，深绿 6.14:1 远超要求；
     不用 --accent（亮绿）是因为这个字号还不够"大字"到可以放宽对比度。 */
  color: var(--accent-ink);
  display: block; margin-bottom: 8px;
}
/* 标签做全大写 + 700 + .1em 字距 —— 这是"铭牌"的排版语法，
   和上面的粗数字配在一起就是一块仪表盘，而不是三句说明。 */
.stat__l {
  font-size: 12px; font-weight: var(--w-bold);
  color: var(--ink-3); letter-spacing: .1em; text-transform: uppercase;
}

/* ── 通用分区 ───────────────────────────────────────────────────────── */
.sec { padding: 86px 0; border-bottom: 1px solid var(--line); }
.sec__head {
  display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap;
  margin-bottom: 44px;
}
.sec__title {
  font-size: 31px; font-weight: var(--w-black);
  line-height: 1.18; letter-spacing: -.022em;
  position: relative; padding-left: 20px;
  /* 与首屏标题同一口径：窄屏上「每日评估 · 今日前三」会断行，
     不加 balance 会断出孤字。单行标题不受影响。 */
  text-wrap: balance;
  color: var(--ink);
}
/* 竖条 3px → 4px、并拉满字高：区块标题的"锚"要够重，
   否则 31px 的粗字旁边一根 3px 细线会被压得看不见。 */
.sec__title::before {
  content: ""; position: absolute; left: 0; top: .04em;
  width: 4px; height: .96em; background: var(--accent);
}
.sec__meta {
  font-size: 13px; font-weight: var(--w-bold);
  color: var(--ink-3); letter-spacing: .04em;
}

/* ── 服务卡 ─────────────────────────────────────────────────────────── */
/* 1px gap + 底色 --line ⇒ 卡片之间的"缝"就是描边，不需要每张都写 border */
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--line); }
.card {
  background: var(--panel); padding: 34px 28px 32px;
  transition: background .2s;
}
.card:hover { background: var(--hover); }
.card__ico {
  width: 34px; height: 34px; color: var(--accent);
  margin-bottom: 20px;
}
.card__t {
  font-size: 20px; font-weight: var(--w-black);
  line-height: 1.3; letter-spacing: -.015em;
  margin-bottom: 10px; color: var(--ink);
}
.card__d { font-size: 15.5px; color: var(--ink-2); line-height: 1.66; }

/* ── 每日评估前三 ───────────────────────────────────────────────────── */
.sec--briefs { background: var(--panel); }
.briefs { display: grid; gap: 1px; background: var(--line); }
.brief {
  display: grid;
  grid-template-columns: 84px 1fr;
  gap: 26px;
  background: var(--panel);
  padding: 30px 26px 30px 0;
  transition: background .2s;
}
.brief:hover { background: var(--hover); }
.brief__rank {
  /* 名次数字 40px → 44px + 700 + 行高 .92：序号是这一屏的视觉重音，
     tabular-nums 让 01/02/03 的数字宽度一致，右对齐时不会参差。
     ⚠️ 颜色从 --accent（亮绿）改成 --accent-ink（深绿），三条理由：
        ① **统一**：首屏数字条 `.stat__v` 本来就是深绿，同一页上两套大数字
           两种绿，属于"看起来都对、但说不出哪里乱"的那类不统一；
        ② **硬朗**：亮绿读出来轻快，深绿才沉、才压得住 700 的粗字；
        ③ **对比度**：3.38:1 → 6.14:1，44px 虽够大字门槛，但没理由停在及格线上。
     而它**不降透明度**：绿在浅底上降透明度只会让字发灰、看着像没渲染完。 */
  font-size: 44px; font-weight: var(--w-black);
  /* ⚠️ 数字的字距**不能**跟标题一样收到 -.035em：tabular-nums 下 "1" 本身
     就在格子里居中、左右留白多，再压负字距会让 "01" 的两位几乎粘在一起，
     远看像 "01" 糊成一团。-.01em 只是把多余的空气收掉。 */
  line-height: .92; letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
  color: var(--accent-ink);
  text-align: right;
}
.brief__body { min-width: 0; }
.brief__tags {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 12px;
}
/* 方角 + 700 + 更紧的字距：风险徽章是"标记"不是"药丸"，
   圆角会把它读成标签化的 UI 组件。 */
.pill {
  font-size: 10.5px; font-weight: var(--w-black);
  letter-spacing: .12em; text-transform: uppercase;
  padding: 4px 8px; border-radius: 0;
  border: 1px solid currentColor;
  white-space: nowrap;
}
.pill--high { color: var(--risk-high); }
.pill--mid  { color: var(--risk-mid); }
.pill--none { color: var(--risk-none); }
.brief__theme {
  font-size: 10.5px; font-weight: var(--w-black);
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-3);
}
.brief__t {
  font-size: 21px; font-weight: var(--w-black);
  line-height: 1.38; letter-spacing: -.015em;
  margin-bottom: 10px;
  color: var(--ink);
}
.brief__t a { transition: color .18s; }
.brief__t a:hover { color: var(--accent-ink); }
.brief__d { font-size: 15.5px; color: var(--ink-2); line-height: 1.66; }
.brief__f {
  margin-top: 14px; display: flex; align-items: center; gap: 14px;
  font-size: 12px; font-weight: var(--w-bold);
  color: var(--ink-3); letter-spacing: .05em;
}
.brief__f .dot { color: var(--line); }
.briefs__empty {
  padding: 40px 0; color: var(--ink-3); font-size: 15.5px;
  border-top: 1px solid var(--line);
}

/* ── 关于 ───────────────────────────────────────────────────────────── */
.about { display: grid; grid-template-columns: 220px 1fr; gap: 40px; }
.about__r p { font-size: 16px; line-height: 1.72; color: var(--ink-2); max-width: 64ch; }

/* ── 联系：二维码 ───────────────────────────────────────────────────── */
/* ⚠️ 这一块的底**必须留灰**（不设 background），二维码卡片才是白的。
   第一版把这里也设成 --panel 白，结果「白底 + 白卡 + 1px #e8eaed 描边」——
   实测截图上卡片的四条边**完全看不见**，两个二维码像直接飘在页面上。
   根部原因：--line 在白底上的明度差太小，会员站的卡片边界从来不靠描边，
   靠的是「白卡 vs 灰底」这一档明度差。所以这里跟会员站用同一个手法。 */
.qrwrap { display: flex; gap: 26px; flex-wrap: wrap; }
.qr {
  background: var(--panel); border: 1px solid var(--line);
  box-shadow: var(--shadow);
  padding: 22px; width: 232px;
  display: flex; flex-direction: column; align-items: center;
  transition: border-color .2s;
}
.qr:hover { border-color: var(--accent); }
/* ⚠️ 白底 + 内边距是必须的：二维码需要静区(quiet zone)，贴在深色底上扫不出来。
   这里**不加**描边 —— 二维码是白图、卡片是白底，静区本来就该与卡片白融成一片；
   加一圈 --line 反而在图上画了个框，看着像图被裁过。 */
.qr__img {
  background: #fff; padding: 12px; border-radius: 0;
  width: 100%; aspect-ratio: 1/1;
  display: flex; align-items: center; justify-content: center;
}
.qr__img img { width: 100%; height: 100%; object-fit: contain; }
.qr__img--none {
  color: var(--ink-3); font-size: 12px; letter-spacing: .06em; text-align: center;
}
.qr__l {
  margin-top: 16px; font-size: 13px; font-weight: var(--w-bold);
  color: var(--ink-2);
  text-align: center; letter-spacing: .03em;
}
/* 12.5px 的小链接 → 深绿；下划线仍用亮绿，hover 时露出来 */
.qr__go {
  margin-top: 10px; font-size: 12.5px; font-weight: var(--w-bold);
  letter-spacing: .03em;
  color: var(--accent-ink);
  border-bottom: 1px solid transparent; transition: border-color .18s;
}
.qr__go:hover { border-bottom-color: var(--accent); }

.contact-lines {
  list-style: none; margin: 38px 0 0; padding: 26px 0 0;
  border-top: 1px solid var(--line);
  display: flex; gap: 34px; flex-wrap: wrap;
}
.contact-lines li { font-size: 15px; color: var(--ink-2); }
/* 这一条作用于 app.js 生成的 .contact-lines__k。
   原来 CSS 里写的是 `.contact-lines dt`，而 JS 造的是 <div class="contact-lines__k">，
   选择器根本不匹配 —— 于是有人在 JS 里塞了一行内联 style 兜住。内联样式会盖掉
   以后所有 CSS 改动，所以这次把它收回样式表，JS 里那行删掉。 */
.contact-lines dt,
.contact-lines__k {
  color: var(--ink-3); font-size: 11px; font-weight: var(--w-black);
  letter-spacing: .14em;
  text-transform: uppercase; margin-bottom: 4px;
}

/* ── 页脚 ───────────────────────────────────────────────────────────── */
/* 页脚只有一句话版权与备案，**没有任何链接** ——
   本轮下线会员登录时逐条核过：`.ftr__cp / .ftr__note / .ftr__icp`
   三个都是纯文本节点（JS 用 textContent 写入，后台填 URL 也不会变成 <a>）。 */
.ftr { padding: 34px 0 44px; }
.ftr__in { display: flex; gap: 22px; flex-wrap: wrap; align-items: baseline; }
.ftr__cp { font-size: 12.5px; font-weight: var(--w-bold); color: var(--ink-3); }
.ftr__note, .ftr__icp { font-size: 12px; color: var(--ink-3); opacity: .85; }

/* ════════════════════════════════════════════════════════════════════
   动效层：滚动渐显 + 首屏视差
   --------------------------------------------------------------------
   两条硬纪律：

   ① **只动合成层属性**。这里用 `opacity` + `translate`，两者都只走合成，
      不触发 layout / paint（改 width / top / margin 会每帧重排，一屏就能掉到
      20fps）。为什么渐显用 `translate` 而**不用** `transform`？
      因为卡片/按钮的 hover 反馈要用 `transform: translateY(-2px)`，
      同一个属性被"渐显"和"hover"两处写，后者必被前者压掉
      （`html.anim [data-reveal]` 特异性 0-2-1，`.card:hover` 只有 0-2-0）——
      hover 抬升会静默失效。拆成两个独立属性后互不干扰、可叠加，
      这正是 CSS 独立变换属性（translate / scale / rotate）存在的意义。

   ② **脚本没跑 / 系统要求减少动态 ⇒ 内容必须照常可见**。
      「初始透明」只在 `html.anim` 下生效，而 `anim` 是 JS 主动加的。
      JS 被拦、被 CSP 挡、浏览器太老 —— 没有这个类，`[data-reveal]`
      就是普普通通的元素，正常显示。反过来做（CSS 先藏、等 JS 来放）
      的写法就是"JS 一挂整页空白"。
   ════════════════════════════════════════════════════════════════════ */
html.anim [data-reveal] {
  opacity: 0;
  translate: 0 14px;
  /* --rd 由 JS 按顺序写入（第 2/3 项错开 70ms），没写就是 0ms。
     延迟只加在 opacity / translate 上；后面几个属性是给 hover 用的，
     **必须一起列在这里** —— 否则这条 transition 会把它们的选择器覆盖掉
     （`transition` 是整条覆盖，不是合并）。 */
  transition:
    opacity   .52s cubic-bezier(.22, .61, .36, 1) var(--rd, 0ms),
    translate .52s cubic-bezier(.22, .61, .36, 1) var(--rd, 0ms),
    background-color .2s ease,
    box-shadow .24s ease,
    border-color .2s ease,
    transform .24s ease;
}
html.anim [data-reveal].is-in {
  opacity: 1;
  translate: 0 0;
}

/* 视差层：只在 JS 判定"宽屏 + 有鼠标 + 没要求减少动态"时才挂这个类。
   will-change 常驻会平白占一张首屏大小的合成层纹理，所以由 JS 动态开关。 */
html.has-parallax .hero__grid { will-change: transform; }

/* ════════════════════════════════════════════════════════════════════
   悬停反馈
   --------------------------------------------------------------------
   整块包在 `(hover: hover) and (pointer: fine)` 里：触屏设备的 `:hover`
   会被"记住"—— 手指点过的卡片一直保持抬起状态，看起来像卡住了。
   触屏的反馈交给下面的 `@media (hover: none)` 用 `:active` 做。
   ════════════════════════════════════════════════════════════════════ */
@media (hover: hover) and (pointer: fine) {
  /* 卡片在 1px 缝的网格里，**不能**整体抬升：一抬就把网格底下的 --line
     露出来一道，看着像错位。所以卡片只加阴影（阴影落在缝上反而让"卡片浮起来"），
     位移交给里面的图标 —— 小面积、廉价、不破坏栅格。 */
  .card:hover { z-index: 1; box-shadow: 0 2px 12px rgba(0, 0, 0, .055); }
  .card__ico { transition: transform .24s ease, color .18s ease; }
  .card:hover .card__ico { transform: translateY(-1px); color: var(--accent-ink); }

  /* 条目行同理（也是 1px 缝网格）：只让名次数字变深色 + 标题下划线伸出来。
     常态已是深绿，hover 再深一档到 --ink（近黑）—— 反馈仍成立，
     而且比"深绿 ↔ 亮绿"的跳变更克制。 */
  .brief:hover .brief__rank { color: var(--ink); }
  .brief__rank { transition: color .2s ease; }

  /* 二维码是独立的浮动卡片，抬升 2px 最自然（它没有网格约束） */
  .qr:hover {
    transform: translateY(-2px);
    box-shadow: 0 3px 8px rgba(0, 0, 0, .07), 0 14px 30px rgba(0, 0, 0, .07);
  }

  /* 主按钮：抬升 1px + 绿色投影（`.btn--ghost` 已随会员登录下线，这里只剩实心按钮） */
  .btn--solid:hover {
    transform: translateY(-1px);
    box-shadow: 0 6px 16px rgba(24, 160, 88, .22);
  }
}

/* 触屏：用 :active 给即时反馈（点下去有反应，比 hover 更符合手指的预期） */
@media (hover: none) {
  .card:active,
  .brief:active,
  .mnav__a:active { background: var(--hover); }
  .btn:active { filter: brightness(.95); }
}

/* ════════════════════════════════════════════════════════════════════
   移动端导航（≤900px 的抽屉）
   --------------------------------------------------------------------
   原来窄屏是 `.nav { display: none }` —— 手机上**完全没有站内导航**，
   五个板块一个都跳不过去。这一节补一个汉堡 + 抽屉。
   抽屉内容由 app.js 从配置里的 nav 填充（文案零硬编码）。
   ════════════════════════════════════════════════════════════════════ */
.navbtn {
  display: none;              /* 桌面端由 .nav 承担导航，这个按钮不出现 */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  /* 44×44 是 iOS HIG / Material 共同的触控目标下限。视觉上只是三条细线，
     但**点击区必须是 44** —— 手指没有像素级精度。 */
  width: 44px; height: 44px;
  /* 负外边距抵消按钮内部留白，让三条线的视觉右边缘和 .wrap 对齐 */
  margin-right: -11px;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--r);
  color: var(--ink-2);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.navbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.navbtn__b {
  display: block;
  width: 20px; height: 2px; border-radius: 1px;
  background: currentColor;
  transition: transform .26s cubic-bezier(.22, .61, .36, 1), opacity .16s ease;
}
/* 三条线 → ✕：中间那条淡出并收缩，上下两条各转 45° 并向中心平移 7px
   （gap 5 + 线高 2 = 相邻线中心距 7px，正好重合到中线） */
.navbtn.is-open .navbtn__b:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.navbtn.is-open .navbtn__b:nth-child(2) { opacity: 0; transform: scaleX(.4); }
.navbtn.is-open .navbtn__b:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.mnav {
  position: absolute; left: 0; right: 0; top: 100%;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 14px 28px rgba(0, 0, 0, .07);
  /* ⚠️ **不能用 hidden 属性**：hidden ⇒ display:none ⇒ 过渡直接不生效
      （display 不是可过渡属性，元素根本没机会画第一帧）。
      这里用 visibility 承担"可交互性"开关：
        · 关：visibility 延迟到淡出结束才变 hidden（0s + .24s 延迟）
        · 开：延迟归零，立刻可见
      只写 opacity:0 是不够的 —— 面板里的链接仍会被 Tab 键和读屏摸到，
      那是典型的可访问性 bug。JS 另外还设了 aria-hidden 双保险。 */
  visibility: hidden;
  opacity: 0;
  transform: translateY(-8px);
  transition:
    opacity .24s ease,
    transform .24s cubic-bezier(.22, .61, .36, 1),
    visibility 0s linear .24s;
  /* 面板比屏幕还高时自己滚，不把页面顶走 */
  max-height: calc(100vh - 69px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.mnav.is-open {
  visibility: visible; opacity: 1; transform: none;
  transition-delay: 0s;
}

.mnav__in { display: flex; flex-direction: column; padding: 4px 0; }
.mnav__a {
  display: flex; align-items: center;
  min-height: 48px;              /* 触控目标 ≥44，行高给 48 更好按 */
  padding: 0 var(--gut);
  font-size: 15.5px; font-weight: var(--w-bold);
  letter-spacing: .01em;
  color: var(--ink-2);
  border-bottom: 1px solid var(--line-2);
  transition: color .16s, background-color .16s;
}
.mnav__a:last-child { border-bottom: 0; }
.mnav__foot {
  display: flex; align-items: center; gap: 18px;
  padding: 12px var(--gut) 16px;
  border-top: 1px solid var(--line);
}
.mnav__lang {
  display: inline-flex; align-items: center;
  min-height: 44px;
  padding: 0 2px;
  font-size: 14px; font-weight: var(--w-bold); color: var(--ink-3);
}
.mnav__lang.is-on { color: var(--accent-ink); font-weight: var(--w-black); }

/* 面板展开时页眉自己的下边框不要和面板顶边叠成两条灰线 */
html.nav-open .hdr { border-bottom-color: transparent; }

/* 桌面端一律不渲染抽屉（JS 也会在转宽屏时自动收起，这里是兜底） */
@media (min-width: 901px) {
  .mnav { display: none; }
}

/* ── 窄屏 ───────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .nav { display: none; }
  .navbtn { display: inline-flex; }
  .hdr__act { margin-left: auto; }
  .cards { grid-template-columns: 1fr; }
  .about { grid-template-columns: 1fr; gap: 18px; }
  .stat { padding-left: 16px; }
  .stat__v { font-size: 28px; }
}

@media (max-width: 620px) {
  :root { --gut: 18px; }
  /* ⚠️ 必须写 padding-top / padding-bottom 两个长属性。
     写 `padding: 68px 0 62px` 会把 `.wrap` 的左右 18px 一起清零 ——
     首屏文字直接贴到屏幕边缘，而**桌面端有同样的坑**（那边注释里写过），
     这个窄屏版本是照着抄下来的，一直没人发现：截图里首屏文字左右都顶着边，
     看起来只是"手机排版就这样"。 */
  .hero__in { padding-top: 68px; padding-bottom: 62px; }
  .sec { padding: 56px 0; }
  .stats__in { grid-template-columns: 1fr; }
  .stat { border-right: 0; border-bottom: 1px solid var(--line-2); padding-left: 0; }
  .stat:last-child { border-bottom: 0; }
  /* 名次列 44 → 52px：数字放大到 30px 后，两位 tabular 数字约需 33px，
     再留出右对齐的空隙；窄了会把 01 挤到正文上。 */
  .brief { grid-template-columns: 52px 1fr; gap: 16px; padding-right: 0; }
  .brief__rank { font-size: 30px; }
  .brief__t { font-size: 18.5px; }
  .qr { width: 100%; flex-direction: row; gap: 18px; padding: 16px; }
  .qr__img { width: 128px; flex: 0 0 auto; }
  .qr__l { margin-top: 0; text-align: left; }
  .qr__txt { display: flex; flex-direction: column; align-items: flex-start; }
  .brand__txt i { display: none; }
  /* 窄屏把右侧那组收紧一点：gap 18→10。
     品牌名是可配的、可能很长，收紧右侧才轮到它多显示一两个字。 */
  .hdr__act { gap: 10px; }
  .contact-lines { gap: 18px; }

  /* 触控目标：页眉里只剩语言切换一个可点项（会员登录已下线）。
     页眉有 68px，给语言项 44px 完全放得下。 */
  .lang a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 2px; }
  /* 正文里的按钮同样抬到 44：手指没有像素级精度，40 以下就明显容易点空 */
  .btn--solid { min-height: 44px; }
  /* 正文行高略放大：手机屏上 1.62 偏挤，1.72 更耐读（英文长句尤其明显） */
  .brief__d, .card__d, .about__r p { line-height: 1.72; }
  /* 小标签在手机上不再缩小 —— 再小就不是"硬朗"而是"看不清"了 */
  .pill, .brief__theme, .contact-lines__k, .stat__l { font-size: 11px; }
}

/* 极窄屏（iPhone SE 375 / 320 级）：把语言切换收进抽屉，把省下的 ~76px
   全部让给品牌名 —— 品牌名是后台可配的、长度不受控，390px 以下它最先被挤爆。
   抽屉 .mnav__foot 里本来就是语言切换的一份拷贝，功能不丢。 */
@media (max-width: 440px) {
  .hdr .lang { display: none; }
  .hdr__act { gap: 6px; }
  .brand__mark { width: 24px; height: 24px; }
}

@media (max-width: 380px) {
  :root { --gut: 16px; }
  .hero__title { font-size: 30px; }
  .sec__title { font-size: 25px; padding-left: 17px; }
  /* 44px 列宽放 26px 的两位数字刚好（tabular 约 29px），再窄就压字 */
  .brief { grid-template-columns: 44px 1fr; gap: 12px; }
  .brief__rank { font-size: 26px; }
  .qr__img { width: 104px; }
  .contact-lines { gap: 14px; }
}

/* ════════════════════════════════════════════════════════════════════
   系统要求减少动态效果
   --------------------------------------------------------------------
   JS 侧已经做了两件事（不加 .anim 类、视差直接 disable），但 CSS 仍要兜底：
   用户可能在**页面打开之后**才去改系统设置，此时 .anim 类已经在 <html> 上了。
   如果只靠 JS，那些已经挂了 .anim 的元素会永久停在 opacity:0 —— 整页空白。
   ════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; }
  html.anim [data-reveal] { opacity: 1 !important; translate: none !important; }
  html.has-parallax .hero__grid { will-change: auto; transform: none !important; }
}

/* ════════════════════════════════════════════════════════════════════
   打印 / 导出 PDF
   --------------------------------------------------------------------
   打印时**不会**滚动，所以 IntersectionObserver 永远不放行首屏以外的元素
   —— 那些区块会带着 opacity:0 被打出来，纸上一片空白（整页截图同样会这样）。
   这条规则必须存在：客户把方案页另存 PDF 是真实场景。
   ════════════════════════════════════════════════════════════════════ */
@media print {
  html.anim [data-reveal] { opacity: 1 !important; translate: none !important; }
  .hdr { position: static; }
  .navbtn, .mnav { display: none !important; }
  /* 白底省墨，也让二维码的静区保持纯白可扫 */
  body { background: #fff; }
  .card, .brief, .qr { box-shadow: none !important; }
}
