/* ============================================================
   策略魔方 - Portfolio Cube — 视觉升级版
   设计系统：深蓝品牌 + 中性灰阶 + 涨红跌绿语义色
   ============================================================ */
:root {
  /* 品牌与语义色 */
  --brand-900: #0B1F3A;   /* 近黑深蓝（标题） */
  --brand-700: #12345E;
  --brand-500: #1D4ED8;   /* 品牌主色 */
  --brand-400: #3B82F6;
  --brand-100: #E8EEFB;   /* 品牌浅底 */
  --brand-50:  #F4F7FD;
  --accent:    #C9A227;   /* 金强调（点缀用） */

  --pos: #D92D20;
  --pos-soft: #FEF1F0;
  --neg: #0E9F6E;
  --neg-soft: #EAFBF3;

  /* 中性灰阶 */
  --bg: #F5F7FB;
  --surface: #FFFFFF;
  --surface-2: #F8FAFD;
  --border: #EDF1F7;
  --border-strong: #E1E7F0;
  --text: #1A2B45;
  --text-2: #46546A;
  --text-3: #6B7A92;
  --muted: #9AA7BC;
  --map-land: #DEE7F6;    /* 世界地图陆地底色 */
  /* 世界地图区域 hover / 选中色（2026-10-01 用户指定：由品牌蓝改为中性深灰蓝）。
     ⚠ 与 `--brand-500` 是**两套语义** —— 品牌蓝留给链接/按钮等交互控件，
       地图高亮改用这个中性色，避免整张图上大片品牌蓝。 */
  --map-hover: #475467;

  /* 品牌标识尺寸（2026-10-01，参照资管棱镜：图标尺寸对齐品牌文字）
     ------------------------------------------------------------
     品牌文字是两行：站名 16.5px + 英文副名 9.5px，`line-height: 1.42`。
     ⚠ 关键点：**行盒 ≠ 可见文字**。行距会在行盒上下各留一段空白，
       所以「按行盒对齐」会让图标比文字高出一圈（实测 36.91 vs 29.97，约 23%，肉眼明显）。
       参照站资管棱镜的 line-height 是 1.15 / 1.2（很紧），行盒≈墨水，按行盒对齐看不出问题；
       本站 1.42 较松，必须按**可见墨水**对齐，故拆成下面三个令牌：

         --brand-text-h   = 文字块「行盒」总高 = (16.5 + 9.5) × 1.42 = 36.91px
         --brand-ink-top  = 行盒顶 → 可见文字顶 = 3.71px
                            （16.5px 行的半行距 2.715 + 字形上沿低于字体 ascent 的 1px）
         --brand-ink-bottom = 可见文字底 → 行盒底 = 3.23px
                            （9.5px 行的下方留白）
         --brand-icon-h   = 36.91 − 3.71 − 3.23 = **29.97px**（= 可见文字的真实高度）

     图标取 --brand-icon-h（= 可见文字高度），再在 .brand-logo 里让它**上沿贴齐文字可见上沿**
     ⇒ 尺寸相等 + 上沿对齐 ⇒ **图形上/下沿与可见文字上/下沿同时对齐**（实测截图逐像素 0/0）。

     ⚠ 改字号 / 行高 / font-family 后，上式三个数字都要**重新实测**（量法见 website-UI.md §3.1.2）。
     ⚠ 页脚复用同一组令牌（页脚两行字号与顶栏逐项一致、且窄屏不隐藏副名）。 */
  --brand-text-h: calc((16.5px + 9.5px) * 1.42);
  --brand-ink-top: 3.71px;
  --brand-ink-bottom: 3.23px;
  --brand-icon-h: calc(var(--brand-text-h) - var(--brand-ink-top) - var(--brand-ink-bottom));

  /* 布局 */
  --radius: 0;
  --radius-sm: 0;
  --shadow-sm: none;
  --shadow-md: none;
  --shadow-lg: none;
  --font: Arial, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
  --num: "SF Mono", "JetBrains Mono", Consolas, "Roboto Mono", monospace;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: hidden; }
body {
  font-family: var(--font);
  /* ⚠ 2026-10-01 用户指定「全站 hero 区去除彩色光晕」：原这里还叠了一层
     `radial-gradient(1200px 500px at 85% -10%, rgba(29,78,216,0.05), transparent 60%)` 的
     固定定位蓝色光晕（贴在视口右上角，正好落在 hero 右上角外侧那圈）。
     它与 `.hero::after` / `.pf-hero::after` 是同一组「彩色光晕」，一并去除 ⇒ 背景回到纯 `--bg` 平色。 */
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.62;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
a { color: var(--brand-500); text-decoration: none; transition: color 0.15s; }
a:hover { color: var(--brand-700); }
b, strong { font-weight: 600; }

/* 滚动条 */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: #D9E1EC; border-radius: 0; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: #C9D4E4; }
::-webkit-scrollbar-track { background: transparent; }

/* ---------------- 顶栏 ---------------- */
.topbar {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px) saturate(1.4);
  border-bottom: 1px solid var(--border);
}
.topbar-inner {
  max-width: 1280px; margin: 0 auto; padding: 0 24px;
  height: 64px; display: flex; align-items: center; gap: 30px;
}
.brand { display: flex; align-items: center; gap: 12px; color: var(--text); }
/* 品牌标识（魔方矢量图标，2026-10-01 起替代原「黑底白字 ♞ 字符方块」）
   - 图形 = assets/img/icon-cube.svg —— **顶栏、页脚、favicon 共用的唯一来源**（改图形只改那个文件）。
   - 尺寸 = `--brand-icon-h`（见 :root 说明）= **品牌文字「可见墨水」的高度**（≈29.97px），
     不是文字块行盒高度 ⇒ **图形上/下沿与可见文字上/下沿对齐**（参照资管棱镜对 icon 的用法）。
     图标本身填满 viewBox（见 assets/img/icon-cube.svg 的 viewBox 说明），故元素盒即图形可见范围；
     再由 .brand 的 `align-items: center` 居中完成纵向定位。
   - 该元素是装饰性图形（相邻 .brand-text 已提供站名文字）⇒ `alt=""` + `aria-hidden="true"`，
     不参与无障碍命名（同资管棱镜给 SVG 标 aria-hidden 的做法）。
   ⚠ 单色纯黑（用户指定不用渐变）；**不做黑底/圆角/阴影**（全站硬规则）。
     注意：原先是「黑底 + 白字」，新图标是**黑色描边**——若保留黑底则图形与底色同色而不可见，故黑底必须去掉。
   ⚠ 图标按 img 引用外部 svg（不是内联），换来「一处图形、三处共用」；因固定纯黑，无需继承 currentColor。 */
.brand-logo {
  width: var(--brand-icon-h);
  height: var(--brand-icon-h);
  flex-shrink: 0;
  display: block;
  /* 纵向定位：让图标**上沿贴齐文字可见上沿**。
     尺寸已 = 文字墨水高度，故下沿随之同时对齐（实测上下偏差均 ≈0）。
     ⚠ 不用容器默认的 `align-items: center`：它按**行盒**居中，而文字上下留白不等
       （上 3.71 / 下 3.23），居中会让图标比墨水范围高 0.24px（亚像素、但既然要求对齐就做准）。
     页脚经 .footer-brand-link 复用同一类名，故此处一并生效，无需重复声明。 */
  align-self: flex-start;
  margin-top: var(--brand-ink-top);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.42; width: 70px; }
.brand-text b { font-size: 16.5px; letter-spacing: 0.6px; color: var(--brand-900); text-align: justify; text-align-last: justify; }
.brand-text small { font-size: 9.5px; color: var(--text-3); letter-spacing: 0.7px; font-weight: 400; text-align: left; }

/* 顶栏品牌魔方：hover 起转（2026-10-02 用户指定「鼠标移到 logo 区域的时候，魔方开始转动，每 3 秒转一圈」）
   ------------------------------------------------------------
   ⚠⚠ **起转与停转都由 JS 控制**（`app.js` 的 `initBrandSpin()`），此处只声明动画本身。
     原因：用户要求「**移开以后继续转，转到恢复最初状态为止**」——
     CSS 只有 `animation-play-state: paused` 一种停法，它**停在当前角度**，
     表达不了「跑完正在进行的这一圈、回到 0° 才停」（＝ iteration 边界对齐）。
     故 CSS 里**不能**写 `.brand:hover { animation-play-state: running }`：
     那样鼠标一移开样式立刻失效、动画被就地暂停，正是要避免的行为。
   ⚠ 时长 **3s** 即「每 3 秒一圈」；`linear` 保证匀速；`0° → 360°` 为正角度 = **顺时针**。
   ⚠ `transform-origin` 默认 `50% 50%`（图形中心），无需声明 —— 图标填满 viewBox，绕盒中心转即绕图形中心转。
   ⚠ **作用域只限顶栏**：页脚标识的父级是 `.footer-brand-link`（另一套类名），
     JS 也只选 `.brand .brand-logo`，故页脚图标不会跟着转。
   ⚠ 该元素已有 `align-self` / `margin-top` 参与纵向对齐，加动画**不影响布局**
     （animation / transform 都不参与布局计算）。
   ⚠ 注：旋转会让**轴对齐包围盒**变大（30px 元素转到 45° 时包围盒约 42px）——
     这是旋转的固有几何，不是布局变化；`.brand-text` 位置不受影响（flex 按 30px 计算），
     gap 12px 足够容纳，实测不重叠。 */
@keyframes brand-cube-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.brand-logo {
  animation: brand-cube-spin 3s linear infinite;
  animation-play-state: paused;          /* 默认静止在 0°（初始姿态）；起转/停转见 app.js */
}
/* 无障碍：系统开启「减弱动态效果」时不做该装饰性旋转（纯装饰、无信息依赖）。
   ⚠ 用 `animation: none` 一并清掉上面的 play-state 声明，避免留下「已定义但永不运行」的动画。
   ⚠ JS 侧 `initBrandSpin()` 里也有同样的判断并会提前退出，两处需一致。 */
@media (prefers-reduced-motion: reduce) {
  .brand-logo { animation: none; }
}
.main-nav { display: flex; align-items: center; }
.menu-link {
  display: flex; align-items: center;
  font-size: 15px; font-weight: 600; color: var(--text);
  padding: 10px 16px; border-radius: 0;
  transition: all 0.15s;
}
.menu-link:hover { color: var(--brand-500); }
/* 当前栏目的一级菜单标蓝（品牌主色 --brand-500，用户指定「颜色变成蓝色」）。
   ⚠ 与 hover/open 同为 --brand-500：三者都是「已激活」语义，取同色才不会出现
     「hover 当前项时它反而变暗」的错觉（hover 若沿用旧值 --brand-700 会比 active 更暗）。
     与「组合策略」下拉里 .drop-item.active 的蓝底白字共同构成两级选中态。 */
.menu-link.active { color: var(--brand-500); font-weight: 700; }
.menu { position: relative; }
.menu-btn {
  appearance: none; border: none; background: transparent; cursor: pointer;
  font-family: inherit; font-size: 15px; font-weight: 600; color: var(--text);
  display: flex; align-items: center; gap: 7px; padding: 10px 16px; border-radius: var(--radius-sm);
  transition: color 0.15s;
}
.menu-btn:hover, .menu.open .menu-btn { color: var(--brand-500); }
/* 一级菜单的「当前栏目」态：其下任一二级项高亮时由 updateTopbar() 反查加上（见 app.js） */
.menu-btn.active { color: var(--brand-500); }
.menu-btn svg { width: 12px; height: 12px; transition: transform 0.18s; }
.menu.open .menu-btn svg { transform: rotate(180deg); }
.dropdown {
  position: absolute; top: calc(100% + 10px); left: 0; min-width: 210px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 0;
  box-shadow: var(--shadow-lg); padding: 7px;
  opacity: 0; visibility: hidden; transform: translateY(-8px) scale(0.98);
  transform-origin: top left;
  transition: all 0.18s cubic-bezier(0.22, 1, 0.36, 1);
}
/* ⚠ hover 桥（2026-10-01 新增，修「从一级菜单滑到二级菜单时会跳一下」）：
   下拉框与按钮之间有 10px 物理间隙（top: calc(100% + 10px)）。鼠标穿过这段间隙时已不在
   .menu 的盒内，会触发 mouseleave ⇒ 收起（开始 0.18s 的淡出/上移），随即进入下拉框又重新展开
   —— 视觉上就是「弹窗先跳一下」。补一层**透明**的桥（高度 = 间隙）把这段路纳入 .menu，
   鼠标全程不离开 .menu，抖动消失。与 .fdd-wrap 的 padding-bottom 桥是同一思路。
   ⚠ 桥宽取下拉框宽度（left/right: 0），比按钮更宽 ⇒ 斜向下滑也不会漏；
     加在 .dropdown 上（而非 .menu::after）是因为它只在展开时才有意义。 */
.dropdown::before {
  content: ""; position: absolute; left: 0; right: 0; top: -10px; height: 10px;
}
.menu.open .dropdown { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
/* 键盘可达：Tab 聚焦一级菜单时同样展开（鼠标点击会 blur 释放，避免跳转后下拉仍挂着）。
   ⚠ 必须与 JS 的 blur 配合：只关 .open 而不 blur 的话，:focus-within 会让下拉关不掉。 */
.menu:focus-within .dropdown { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
.drop-item {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 14px; border-radius: var(--radius-sm);
  color: var(--text-2); font-size: 14px; font-weight: 500;
  transition: all 0.12s;
}
.drop-item:hover { color: var(--brand-700); }
.drop-item .dot { width: 9px; height: 9px; border-radius: 0; background: var(--border-strong); flex: none; transition: all 0.12s; }
.drop-item:hover .dot { background: var(--brand-400); }
.drop-item.active { background: var(--brand-100); color: var(--brand-700); font-weight: 600; }
.drop-item.active .dot { background: var(--brand-500); }
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 14px; font-size: 12.5px; color: var(--text-3); }
.src-badge {
  background: linear-gradient(120deg, var(--brand-100), #F2F6FF); color: var(--brand-700); font-weight: 600;
  padding: 5px 12px; border-radius: 0; font-size: 12px;
  border: 1px solid #E9EFF9;
}
.asof b { color: var(--text-2); font-weight: 700; }

/* ---------------- 汉堡按钮 + 移动端抽屉导航（≤720px，2026-10-01 新增） ----------------
   样式参照资管棱镜（其 css/mobile.css 的 .menu-toggle 与 css/main.css 的 .subnav），
   按本站令牌与硬约束落地：白底 + 浅边框、扁平直角、无阴影、无渐变装饰。
   · 汉堡按钮：三根 20×2 短横、无边框无底色，窄屏**贴右**（margin-left: auto）
   · 抽屉：顶栏正下方、整宽面板；一级菜单整行可点、二级菜单**内联展开**（不再是悬浮下拉）
   · 手风琴：同时只开一个一级菜单（由 app.js 的 closeAll 保证），否则连点几下会撑满整屏
   ⚠ 断点 720px 必须与 app.js 的 MOBILE_NAV_MQ 一致 —— 改一处必须同时改另一处，
     否则会出现「汉堡按钮已出现，但点一级菜单仍在跳页」的错位。
   ⚠ 桌面端 .menu-toggle 为 display: none（DOM 里它排在 .main-nav 之后，视觉/无障碍顺序不受影响；
     资管棱镜把按钮放在 .brand 之前、再用 flex order 调位 —— 本站追加在末尾即可，无需 order）。 */
.menu-toggle {
  display: none;
  flex-direction: column; align-items: center; gap: 5px;
  margin-left: auto;                 /* 吃掉全部剩余空间 ⇒ 按钮贴顶栏右侧 */
  padding: 9px; background: none; border: none; cursor: pointer;
  color: var(--text);
}
.menu-toggle span { display: block; width: 20px; height: 2px; background: currentColor; }

@media (max-width: 720px) {
  .menu-toggle { display: flex; }
  /* 抽屉：.main-nav 在窄屏脱离 flex 流（绝对定位），故只需让按钮 margin-left:auto 就能贴右。
     绝对定位的包含块是 .topbar（sticky 亦属定位元素），故 left/right: 0 即整宽。 */
  .main-nav {
    position: absolute; top: 64px; left: 0; right: 0;
    display: none;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: 4px 16px 16px;                       /* 左右 16px = 窄屏 .topbar-inner 的内边距 */
    max-height: calc(100vh - 64px); overflow-y: auto;
  }
  .main-nav.open { display: flex; }
  /* 一级菜单：整行可点、标题贴左箭头贴右（对齐资管棱镜的 .nav-link justify-content: space-between） */
  .menu-link, .menu-btn { width: 100%; justify-content: space-between; padding: 13px 10px; font-size: 15px; }
  .menu-link, .menu { border-bottom: 1px solid var(--border); }
  .main-nav > :last-child { border-bottom: none; }   /* 末项不画线（抽屉底部已有 padding 收尾） */
  /* 二级菜单：由悬浮下拉改为内联展开 —— 位置回到文档流、靠 display 切换。
     必须把桌面那套 opacity/visibility/transform 一并复位，否则会残留「透明但占位」的状态。 */
  .dropdown {
    position: static;
    display: none;
    min-width: 0; padding: 4px 0;
    background: var(--surface-2);
    border: none; box-shadow: none;
    opacity: 1; visibility: visible; transform: none;
    transition: none;
  }
  .menu.open .dropdown { display: block; }
  /* 悬浮桥只服务桌面的 hover 穿越；内联展开时它反而会盖住上方菜单项的下半部分点击区 */
  .dropdown::before { display: none; }
  .drop-item { padding: 11px 16px; font-size: 14px; }
}

/* ---------------- 布局 ---------------- */
.app { max-width: 1280px; margin: 0 auto; padding: 30px 24px 64px; min-height: calc(100vh - 240px); }
.breadcrumb {
  font-size: 12.5px; color: var(--text-3); margin-bottom: 20px;
  display: flex; gap: 7px; align-items: center;
}
.breadcrumb a:hover { color: var(--brand-500); }
.breadcrumb .sep { color: var(--border-strong); }
.breadcrumb .cur { color: var(--text-2); font-weight: 600; }

/* ---------------- Hero（列表页） ----------------
   ⚠ 2026-10-01 用户指定：**去除彩色光晕、完全使用纯白色**。原先这里有
     ① `background: linear-gradient(135deg, #FFFFFF, var(--brand-50))` —— 右下角泛淡蓝；
     ② `.hero::after` 一块 240px 的品牌蓝 radial-glow 压在右上角。
     两者都已删除，改为**纯白实底**（`--surface`）。
   ⚠ 不要再加回渐变/光晕 —— 本站「无渐变装饰」的硬约束在 hero 上同样是硬约束。 */
.hero {
  position: relative;
  padding: 30px 32px 26px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  margin-bottom: 30px;
}
.hero h1 { font-size: 30px; font-weight: 800; letter-spacing: 0.3px; color: var(--brand-900); }
.hero .desc { color: var(--text-2); margin-top: 8px; max-width: 860px; font-size: 14.5px; }
.hero-chips { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
.chip {
  font-size: 12px; font-weight: 600; padding: 5px 13px; border-radius: 0;
  background: var(--surface); border: 1px solid var(--border); color: var(--text-3);
  display: inline-flex; align-items: center; gap: 6px;
  box-shadow: var(--shadow-sm);
}
.chip b { color: var(--brand-700); font-weight: 700; }
/* 页面元信息行（对标资管棱镜「指数发布」页的 .meta-bar，2026-10-01 新增）：
   纯文字、**无底色、无边框、无内边距** —— 与 `.hero-chips`（白底小卡片式统计）是两种视觉语义，
   故另立一类，不要复用 .chip。用 flex + gap 是为将来并列多条元信息（如「更新频率」+「数据截至」）留结构。 */
.hero-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px;
  margin-top: 14px; font-size: 13px; color: var(--text-3);
}

/* ---------------- 卡片 / 区块 ---------------- */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
}
.section { margin-bottom: 28px; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 16px; gap: 12px; flex-wrap: wrap; }
.section-title { display: flex; align-items: center; gap: 10px; font-size: 17.5px; font-weight: 700; color: var(--brand-900); }
.section-title::before { content: ""; width: 4px; height: 18px; border-radius: 0; background: #475467; }
.section-sub { font-size: 12.5px; color: var(--muted); }
.copy-btn {
  appearance: none; border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text-2); font-family: inherit; font-size: 12px; font-weight: 600;
  padding: 4px 12px; cursor: pointer; border-radius: 0;
  transition: all 0.15s;
}
.copy-btn:hover { border-color: var(--brand-500); color: var(--brand-700); }
/* 嵌入副标题中的复制按钮：无边框、与文字统一格式（保留颜色与加粗） */
.section-sub .copy-btn {
  appearance: none; border: none; background: transparent; padding: 0 4px;
  color: var(--text-2); font-family: inherit; font-size: 12.5px; font-weight: 600;
  cursor: pointer; border-radius: 0; transition: color 0.15s; vertical-align: baseline;
}
.section-sub .copy-btn:hover { color: var(--brand-700); }
.copy-btn:active { transform: translateY(1px); }
.card-pad { padding: 24px 26px; }

/* ---------------- 组合卡片（列表页） ---------------- */
.cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(370px, 1fr)); gap: 22px; }
.pf-card {
  padding: 24px 26px 0; display: flex; flex-direction: column;
  position: relative; overflow: hidden;
  transition: transform 0.2s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.2s;
}
.pf-card::before {
  content: ""; position: absolute; left: 0; top: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--brand-500), var(--brand-400), transparent);
  opacity: 0; transition: opacity 0.2s;
}
.pf-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.pf-card:hover::before { opacity: 1; }
.pf-card .head { display: flex; align-items: center; gap: 10px; }
.pf-card .head h3 { font-size: 17.5px; font-weight: 700; color: var(--brand-900); }
.pf-card .head .code {
  font-size: 11px; color: var(--muted); font-weight: 700; letter-spacing: 0.5px;
  border: 1px solid var(--border); padding: 2px 8px; border-radius: 0; background: var(--surface-2);
}
.pf-card .head .status {
  margin-left: auto; font-size: 11px; color: var(--neg); background: var(--neg-soft);
  padding: 3px 10px; border-radius: 0; font-weight: 700;
}
.pf-card .meta { margin-top: 12px; font-size: 12px; color: var(--text-3); display: flex; flex-wrap: wrap; gap: 5px 16px; }
.pf-card .meta span b { color: var(--text-2); font-weight: 600; }
.pf-card .kpis {
  margin-top: 18px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
  border-top: 1px dashed var(--border); padding-top: 16px;
}
.pf-card .kpi { padding: 2px 4px; }
.pf-card .kpi .k { font-size: 11px; color: var(--muted); letter-spacing: 0.2px; }
.pf-card .kpi .v { font-size: 18px; font-weight: 700; margin-top: 3px; letter-spacing: -0.2px; }
.pf-card .foot {
  margin-top: 16px; border-top: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between; padding: 14px 0;
  font-size: 13px;
}
.pf-card .foot .link { color: var(--brand-500); font-weight: 600; display: inline-flex; align-items: center; gap: 6px; }
.pf-card .foot .link svg { width: 14px; height: 14px; transition: transform 0.15s; }
.pf-card:hover .foot .link svg { transform: translateX(4px); }
.pf-card .foot .dd { font-size: 12px; color: var(--muted); }

/* ---------------- 数值颜色 ---------------- */
.pos { color: var(--pos); }
.neg { color: var(--neg); }

/* ---------------- 表格 ---------------- */
.table-wrap { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: separate; border-spacing: 0; }
.tbl th {
  text-align: left; font-size: 13px; color: var(--text-3); font-weight: 700;
  padding: 13px 16px; background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  white-space: nowrap; letter-spacing: 0.2px;
}
.tbl th:first-child { border-radius: var(--radius-sm) 0 0 0; }
.tbl th:last-child { border-radius: 0 var(--radius-sm) 0 0; }
.tbl th.num { text-align: right; }
.tbl th[data-key] { cursor: pointer; user-select: none; }
.tbl th[data-key]:hover { color: var(--brand-500); }
/* 排序指示箭头（2026-10-01 用户指定：用 assets/img/sort-up.svg 取代 ▲ 字符）
   ⚠ 为什么用 `mask` + `background: currentColor` 而不是 `<img>`：
     `<img>` 的描边色是图片自带的，**不继承文字颜色** —— 表头 hover 变蓝、当前栏目选中态
     等都会失效。用 mask 取形、背景取 currentColor，箭头才能随文字一起变色。
   ⚠ 降序 = 把同一图形**旋转 180°**（升/降本就是同一个箭头，源件只给了向上的）。
   ⚠ 尺寸 11px + 左边距 4px = 总占位 15px，比原字符 `' ▲'`（空格 3.61 + ▲ 字宽 12.87 ≈ 16.5px）
     **略窄 1.5px** ⇒ 不会撑破任何一列的表头（两页的列宽都是按「表头 + 箭头」实测定的，
     变窄只会留出余量）。⚠ 若日后加大本图标，必须重新遍历实测两页表头是否溢出。 */
.sort-ico {
  display: inline-block;
  width: 11px; height: 11px;
  margin-left: 4px;
  background: currentColor;
  -webkit-mask: url(../img/sort-up.svg) center / contain no-repeat;
  mask: url(../img/sort-up.svg) center / contain no-repeat;
  vertical-align: middle;
  /* ⚠ 实测微调（逐像素量的，不是估的）：单靠 `vertical-align: middle` 会**偏低 1.5px** ——
     表头文字墨水行中心 y=23.5、图标 y=25。根因是 `middle` 的参照是**拉丁 x-height**
     （Arial 13px ≈ 6.75px ⇒ 中心在基线上方 3.4px），而表头是 **CJK**（字面更高、
     视觉中心更靠近 em 盒中心 ≈ 基线上方 4.5px）⇒ 差约 1.2~1.5px。
     用 `position: relative; top` 做显式上移（不用 `vertical-align: -7.4px` 那种魔法长度 ——
     一旦字号变了会整体错位，而 `middle` + 小修正始终近似居中）。
     ⚠ **改表头字号 / 行高 / 字体后必须重测这个偏移**。 */
  position: relative;
  top: -1.5px;
}
.sort-ico.desc { transform: rotate(180deg); }
.tbl td { padding: 13px 16px; font-size: 13px; border-bottom: 1px solid #F5F7FC; vertical-align: middle; }
.tbl tbody tr:last-child td { border-bottom: none; }
.tbl tbody tr { transition: background 0.1s; }
.tbl tbody tr:hover td { background: var(--brand-50); }
.tbl td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tbl td.tx { white-space: nowrap; }
.tbl .strong { font-weight: 600; }
.tbl .wbar { display: inline-block; width: 48px; height: 6px; border-radius: 0; background: #EEF1F6; vertical-align: middle; margin-left: 8px; overflow: hidden; }
.tbl .wbar i { display: block; height: 100%; background: linear-gradient(90deg, var(--brand-500), var(--brand-400)); border-radius: 0; }
.tbl .link-cell { color: var(--brand-500); font-weight: 600; }

/* ---------------- 港交所ETF 表格（列表页） ---------------- */
/* 列宽策略（2026-10-01）：table-layout: fixed + colgroup（列宽在 app.js 的 HKEX_COLS.w）。
   默认的 auto 布局下，窄列被长表头「钉」在原宽不让位，所有压缩都落到跟踪指数列上
   （1280→900px 视口时指数列 418→215px），且列宽随筛选后的内容变化而跳动。
   改为 fixed 后：① 跟踪指数独占剩余空间，拿到最大份额；② 长度确定的列按实测宽度定死、不留白；
   ③ 列宽与内容无关，排序/筛选不再引起跳动。min-width 保证窄屏下正确横向滚动而非压扁。
   ⚠ min-width 的用意是「防压扁」——即给**弹性列（跟踪标的）留出与宽屏同等的余量**，
     故它必须随固定列的总宽同步调整：2026-10-01 新增「管理模式」列（104px）后，
     固定列 733 → 837px，本值随 960 → **1064px**（= 960 + 104）。
     不改则窄屏下跟踪标的列只剩 960−837 = **123px**（原为 227px），实测 768px 视口
     首页 40 行中 32 行折行（改前同口径下仅少数）——即「多一列」的代价不该由弹性列独自承担。 */
.tbl-hkex { table-layout: fixed; min-width: 1064px; }

/* QDII基金 列表（#/qdii-fund）—— 复用 .tbl-hkex 的列宽策略，**只覆盖 min-width**。
   ⚠ 为什么不能沿用 1064px：两页的**固定列总宽**不同（QDII 869px vs 港交所ETF 837px），
     而 min-width 的用意是「给弹性列留出与宽屏同等的余量」⇒ 必须各自算。
     QDII 的弹性列是「基金简称」，其**全库最长内容为 280.9 + 内边距 32 = 312.9px**，
     故取 **869 + 313 = 1182px** —— 这是「**任意视口下都不折行**」的临界值。
     ⚠ 固定列合计会随加列变化：2026-10-01 首次建页为 765px（min-width 1078）；
       同日新增「申购限额」列（104px）后 765 → **869px**，本值随 **1078 → 1182px**
       （= 1078 + 104，加一列就同步加该列宽）。不加则窄屏下基金简称列被压到
       1182 以下、最长的 22 字简称会折行。
     港交所ETF 页的 min-width 1064px 会让弹性列留在 227px（< 其需要的 463px）⇒ 那里接受折行；
     QDII 这边既然算得出精确临界值，就取它、把折行消掉。 */
.tbl-qdii { min-width: 1182px; }

/* 互认基金 列表（#/mutual-fund）—— 同样复用 .tbl-hkex 的列宽策略，**只覆盖 min-width**。
   ⚠ 为什么又算一遍：三页的**固定列总宽**各不相同（港交所ETF 837 / QDII 869 / 互认 555），
     而 min-width 的用意是「给弹性列留出与宽屏同等的余量」⇒ 必须各自算。
   ⚠ 互认页的表头由用户指定为 6 列且**无「管理人」列**，故固定列比 QDII 少一列（QDII 有管理人 136px）。
     弹性列是「基金简称」，全库最长内容 **275.9px**（`东方汇理香港-亚太新动力股息M RMB HDG累积`）
     + 内边距 32 = **307.9px** ⇒ 取 555 + 308 + 3 余量 = **866px**，即「任意视口下都不折行」的临界值。
     ⚠ 改动任一固定列宽，本值必须同步（加一列就同步加该列宽）。
     ⚠ 2026-10-01 规模列币种经万得确认为人民币后，列头由「管理规模(亿港元)」缩短为「管理规模(亿元)」，
       该列 160 → 138px（固定列 577 → **555**）。
     ⚠⚠ **实测教训（两处，都已修正）**：① 量文本宽度时**必须带上 `font-weight:600`** ——
       名称列是加粗的，只按常规字重量会把 275.9 量成 275；② **resize 视口后必须等一次 reflow 再量**，
       否则 `getBoundingClientRect()` 仍返回旧布局的值，会得出「零折行」的假结论
       （上一版 884px 就是这样漏检的，实际窄屏下有 3 行折行）。
       量法：注入 `.tbl-mutual td.strong{white-space:nowrap}` 后用 `Range.getBoundingClientRect()` 逐页量。 */
.tbl-mutual { min-width: 866px; }

/* 全球投资 产品列表（#/global-invest）—— 同样复用 .tbl-hkex 的列宽策略，**只覆盖 min-width**。
   ⚠ 固定列总宽 = 代码 104 + 基金类型 122 + 管理人 138 + 成立日期 108 + 管理费率 105 + 管理规模 138 = **715**
     （弹性列是「基金简称」，7 列中只有它是变长的）。
   ⚠ 弹性列余量 = 本页**实际最长基金简称 + 内边距 32**（本页含 QDII，名称普遍长于互认，见下实测）。
   ⇒ min-width = 715 + 313 + 3 余量 = **1031px**，取整 **1032**，即「任意视口下都不折行」的临界值。
   ⚠ 实测（2026-10-02，注入 `.tbl-gi td.strong{white-space:nowrap}` 后用 `Range.getBoundingClientRect()`
     遍历全 418 行）：最长名称 **281px** = `华泰柏瑞南方东英新交所泛东南亚科技ETF联接A`（QDII），
     + 内边距 32 = **313px** ⇒ 715 + 313 + 3 = 1031。
     ⚠⚠ 第一版取 1024（按互认页「最长 275.9」类比估的），**实测有 1 行折行** —— 本页含 QDII，
     名称普遍比互认长（QDII 有「ETF联接A」这类后缀），必须**按本页实际最长值**重算，
     不能照搬互认页的数字。
   ⚠ 改动任一固定列宽（或增删列），本值必须同步重算并遍历全页实测折行。
   ⚠⚠ 量文本宽度时**必须带上 `font-weight:600`**（名称列加粗），且 **resize 后须等一次 reflow**
     —— 否则会得出「零折行」的假结论（互认页 884px 就是这样漏检的，见上方 §互认基金）。 */
.tbl-gi { min-width: 1032px; }

/* ---------------- 「全球投资」页投资区域：hover 弹出的二级筛选（2026-10-03 用户指定） ----------------
   用户原话：「参考资管棱镜这一页筛选器的展现方式：当我 hover 过去的时候，它会弹出一个二级筛选框」。
   ⚠ 改动前「投资区域」占 **4 行**（1 行一级 + 3 行二级各占一行，二级共 16 项横铺）；
     改为一级平铺 + hover 弹二级后压成 **1 行**，与「基金类型」「资产类型」等高、视觉齐整。
   ⚠ 类名沿用本区块命名空间（`.dd-*`，与 QDII 页的 `.fdd-*` 下拉并列、互不影响）。 */
.dd-wrap {
  position: relative;                 /* 弹层以「该一级按钮」为定位基准 */
  display: inline-flex; align-items: center;
  /* ⚠ **hover 桥**：按钮下缘到弹层之间留 4px，且这 4px 也属于 `.dd-wrap` 的命中区 ——
     鼠标从按钮下移到弹层的路径上不会离开 wrap、菜单不闪断（与 JS 的 150ms 延时收起双保险）。 */
  padding-bottom: 4px;
}
.dd-arrow {
  width: 12px; height: 12px; color: currentColor; opacity: 0.7;
  vertical-align: -2px; margin-left: 2px; flex-shrink: 0;
  transition: transform 0.15s;
}
.ftag.dd-btn { display: inline-flex; align-items: center; }
.ftag.dd-btn[aria-expanded="true"] .dd-arrow { transform: rotate(180deg); }
.dd-menu {
  position: absolute; top: 100%; left: 0;
  z-index: 200;                       /* 置顶：高于冻结表头(60)与顶栏(101)，避免被滚动后的元素盖住 */
  min-width: 220px; max-width: min(560px, 92vw);
  margin-top: 0; padding: 8px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: 0 6px 20px rgba(11, 31, 58, 0.10);   /* ⚠ 本站「扁平直角」硬约束：不加圆角，靠投影与描边表达层级 */
  /* ⚠⚠ **这里必须写 `flex`（不能写 `none`）** —— 初始隐藏由 HTML 的**内联** `style="display:none"`
     承担，JS 展开时用 `display = ''` 清除内联值、回落到本规则。
     若此处写 `none`，`display=''` 会回落到 `none` ⇒ **菜单永远打不开**
     （2026-10-03 实测踩到：`aria-expanded` 已变 true、箭头也转了，但 `display` 仍是 none）。
     这与资管棱镜 `#/product/new-index` 的做法一致（那边也是 CSS 给 flex、内联给 none）。 */
  display: flex;
  flex-wrap: wrap; gap: 4px; align-items: flex-start;
}
/* ⚠ 弹层内的按钮**显式给白底**：`.ftag` 默认已是 `--surface`，但弹层底色同为白 ——
   若无描边/底色区分，按钮与弹层会糊成一片、看不出「这是一个可点的选项」。
   故给弹层内按钮加极浅底色，hover / active 时再变蓝（复用 `.ftag` 既有规则）。 */
.dd-menu .ftag { background: var(--surface-2); }
.dd-menu .ftag:hover { background: var(--brand-50); color: var(--brand-700); }
.dd-menu .ftag.active { background: var(--brand-500); color: #fff; }
/* 基金代码列：用正文字体（Arial 栈）并加粗 —— 不用等宽字体（等宽会把 2800.HK 拉得太开、与整体不协调） */
.tbl-hkex td.code { font-family: var(--font); font-weight: 700; color: var(--text); }
.tbl-hkex td.idx { color: var(--text-2); }
/* 「互联互通ETF」小字标记（2026-10-01 用户指定：**蓝底白字**；同日再指定**字号缩小**降低视觉感）：
   紧贴基金名称后，字号与字重都低于主名（不抢主体）；实心品牌蓝底 + 白色字形成醒目标记。
   · `display: inline-block` + 上下 padding 1px —— 使底色成为规整色块；1px 上下内边距不足以
     撑高行盒（10px 字号 + 2px = 12px 内容高，仍低于 13px/1.5 的名称行高），**不会导致行高变化**
   · `border-radius: 0` 守本站「扁平直角」硬约束
   · `margin-left: 6px` 与名称留出气口；`nowrap` 保证标记不自断行
   · 2026-10-01 缩小：`font-size` 11px → **10px**、左右 padding 6px → **5px**（视觉更轻，不再与
     主名争夺注意力），标记宽度随之 77 → 67px
   ⚠ 标记宽度直接约束**名称列宽（HKEX_COLS 的 name.w）** —— 它是按含标记的最宽组合实测定的；
     改本处字号/内边距后必须重算列宽，否则名称列会折行（或白占空间、挤压跟踪标的列）。 */
.tbl-hkex .mk-conn {
  display: inline-block;
  margin-left: 6px; padding: 1px 5px;
  font-size: 10px; font-weight: 500; line-height: 1.45;
  color: #fff; background: var(--brand-500);
  letter-spacing: 0; white-space: nowrap; border-radius: 0;
}

/* ---------------- 筛选器（tagbar + 字母平铺下拉） ----------------
   参照资管棱镜「基金行政审批」页的筛选器实现（分组标签 + 字母平铺 + 悬停弹窗），
   视觉守本站硬约束：白底 + 浅边框、扁平直角、无阴影、无渐变装饰。
   ⚠ 类名用 .ftag* / .fdd-* 命名空间，与本站既有的 .tag（风险/状态徽标）隔离，避免样式串味。 */
.ftagbar {
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 16px;              /* 四边等值：筛选条留白须上下左右一致 */
  margin-bottom: 16px;
}
.ftag-row { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; margin-bottom: 12px; }
.ftag-row:last-child { margin-bottom: 0; }
.ftag-group { font-size: 12px; font-weight: 600; color: var(--muted); min-width: 52px; white-space: nowrap; }
.ftag {
  font-family: inherit; font-size: 12px; font-weight: 500; color: var(--text-2);
  background: var(--surface); border: none; padding: 4px 12px; cursor: pointer;
  border-radius: 0; white-space: nowrap; flex-shrink: 0;
  transition: background 0.15s, color 0.15s;
}
.ftag:hover { background: var(--brand-50); color: var(--brand-700); }
.ftag.active { background: var(--brand-500); color: #fff; font-weight: 600; }
.ftag-reset { font-family: inherit; font-size: 12px; color: var(--muted); background: none; border: none; padding: 4px 2px; cursor: pointer; }
.ftag-reset:hover { color: var(--brand-500); }
.ftag-summary { font-size: 12px; color: var(--muted); }
/* 「基金份额」三组之间的分隔竖线（2026-10-01 新增，#/mutual-fund 页）
   该行把 **币种 / 对冲 / 派息** 三组「两两一组」的层级显式画出来 —— 三组是
   「组内互斥、组间可多选」的关系，只靠按钮间距看不出分组边界，故加一道 1px 竖线。
   ⚠ 纯装饰（HTML 上带 aria-hidden），不承担语义；`align-self: stretch` 让它与标签等高。 */
.ftag-sep { width: 1px; align-self: stretch; background: var(--border-strong); margin: 0 5px; flex-shrink: 0; }

/* 关键词搜索行（2026-10-01 用户指定：置于筛选栏**下方**）
   样式对标资管棱镜「基金行政审批」页的 `.pt-search-row` + `.news-search`：
   输入框 30px 高 / 13px、放大镜图标内嵌（左侧 30px 内边距留位）、聚焦时描边品牌蓝。
   ⚠ 类名沿用本区块的 `.fsearch` 命名空间（同 `.ftag*`/`.fdd-*` 的做法），不引入通用 `.news-search`
   ⚠ 该行是筛选条的**最后一个 `.ftag-row`** —— 因此它自身 margin-bottom 归 0（:last-child），
     而上一行（清除/摘要）保留 12px 下边距，两行间距与其它行一致 */
.ftag-search-row .fsearch { flex: 0 1 460px; min-width: 220px; }
.fsearch { position: relative; }
.fsearch-icon {
  position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--muted); pointer-events: none;
}
.fsearch input {
  width: 100%; height: 30px; padding: 6px 10px 6px 30px;
  font-family: inherit; font-size: 13px; color: var(--text);
  border: 1px solid var(--border); background: var(--surface);
  border-radius: 0; outline: none;
}
.fsearch input:focus { border-color: var(--brand-500); }
.fsearch input::placeholder { color: var(--muted); }

/* 字母下拉：外层 padding-bottom 作 hover 桥 —— 按钮与菜单之间的间隙也计入悬停区，鼠标移动不中断 */
.fdd-wrap { position: relative; display: inline-block; vertical-align: middle; padding-bottom: 4px; }
.fdd-menu {
  position: absolute; top: 100%; left: 0; z-index: 200;   /* 高于吸顶顶栏(100) */
  padding: 8px; background: var(--surface);
  border: 1px solid var(--border-strong);
  display: block;
  max-width: calc(100vw - 16px);   /* 兜底：弹窗不超出视口（定位偏移由 JS 的 fitFilterMenu 计算） */
}
/* 字母弹窗：网格布局，列数由 JS 按 min(项数,3) 写内联（写死列数时空列仍占位、右侧多留白） */
.fdd-tabs { display: grid; gap: 4px; align-items: stretch; min-width: 0; max-height: 380px; overflow-y: auto; }
.fdd-tabs .fdd-sub { text-align: center; white-space: nowrap; }

/* ---------------- 分页条（样式对标资管棱镜基金审批页） ----------------
   ⚠ 分页条由 JS 渲染在 `.card` **之外**（照资管棱镜的做法）：卡片内是纯白，分页条放里面会与
     表格融为一体、看不出边界；移出后落在页面灰白底（--bg #F5F7FB）上，靠卡片自身的下边框分层。
     因此这里**不再加 border-top** —— 分页条紧贴卡片下方（无间隙），再加一条会与卡片下边框叠成 2px 双线。
   结构：共 N 条 · 第 x/y 页 + 上一页 / 页码 / 下一页，整块右对齐
   ⚠ 右侧内边距只有 1px（2026-10-01 用户指定）：让末位「下一页 ›」的右缘与**上方表格**的右缘齐平。
     这 1px 不是留白，而是**补偿卡片的 1px 右边框** —— 表格在卡片 content box 内（右缘 = 卡片内缘），
     而分页条是卡片的兄弟节点（右缘 = 卡片外缘），两者天然差 1px。资管棱镜同理不留右侧空间
     （其 `padding: 14px 0 14px 18px`，注释「右侧不留空间，分页信息贴最右」）。
     左侧仍留 16px：窄屏换行后仍有边距，且与卡片内单元格左内边距一致。 */
.pagination {
  display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap;
  gap: 6px; padding: 14px 1px 14px 16px;
}
/* 「共 N 条 · 第 x/y 页」：信息文字，不透明（曾试过 opacity: 0.5，实测对比度仅 1.50:1，
   疑似过淡，已按用户澄清撤回——用户要的是**区块底色**半透明，不是文字）。 */
.pg-info { font-size: 12px; color: var(--muted); margin-right: 10px; white-space: nowrap; }
.pg-btn {
  min-width: 32px; height: 28px; padding: 0 10px;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-2);
  font-family: inherit; font-size: 12.5px; border-radius: 0; cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.pg-btn:hover:not(:disabled):not(.active) { border-color: var(--brand-500); color: var(--brand-500); }
.pg-btn.active { background: var(--brand-500); border-color: var(--brand-500); color: #fff; font-weight: 600; cursor: default; }
.pg-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.pg-dots { color: var(--muted); padding: 0 2px; }

/* ---------------- 世界地图（跨境投资 → 全球投资，2026-10-01 新增） ----------------
   真实海陆边界（Natural Earth 1:50m）+ 区域裁剪多边形，图层结构见 app.js 的 worldMapSvgHtml()：
     ① .wm-ocean 海洋底 → ② .wm-grid 经纬网 → ③ .wm-land 陆地底色
     → ④ .wm-regions 区域高亮层（陆地 ∩ 区域，5 个 clipPath）→ ⑤ .wm-lakes 主要湖海
   ⚠ 2026-10-01 用户指定：**去掉海岸线的白色描边** —— 原最上层还有一层 `.wm-coast`
     （`stroke: var(--surface)` 白色 1px，沿每条海岸线描一圈）。已连同 CSS 一并删除，
     陆地与海洋的边界改由「陆地底色 vs 海洋底色」的色差直接体现。
   ⚠ 同日用户指定：区域 hover / 选中色由品牌蓝改为中性深灰蓝 `--map-hover`（#475467）。
   视觉守本站硬约束：扁平直角、无阴影、无渐变装饰。
   ⚠ SVG 内的字号是「画布单位」（viewBox 1200×540），随容器等比缩放，不随页面 px 体系变化。
   ⚠ .wm-land / .wm-lakes 必须 pointer-events: none —— 它们叠在区域之上，
     若参与命中测试，光标擦过陆地边界那一刻最上层命中的就不是区域了，会「高亮一闪即灭」。 */
.wm-scroll { overflow-x: auto; }                       /* 窄屏：地图保底 640px，横向滚动而非压扁 */
/* ⚠ 保底宽度必须加在 **.wm-stage**（而不是只加在 .wm-svg 上）：
   悬浮卡是用「相对 .wm-stage 的百分比」定位的。若只有 svg 设 min-width，窄屏下 .wm-stage 仍是
   容器宽度（341px）、svg 靠自己溢出，百分比就会按 341px 换算 ⇒ 卡片偏出左缘被裁掉。
   加在 stage 上后，stage 实际宽度 = 640px，百分比与 svg 的渲染宽度一致。 */
.wm-stage { position: relative; width: 100%; min-width: 640px; }
.wm-svg { display: block; width: 100%; height: auto; }
/* 海洋底色：让地图自成一块「图面」，陆地才与之形成可辨的对比（无底色时陆地浮在白卡上） */
.wm-ocean { fill: var(--surface-2); }
/* 经纬网：只提供坐标感，不承载数据 —— 故用极淡的线，避免与陆地争层级 */
.wm-grid line { stroke: #EFF3FA; stroke-width: 1; }
.wm-grid .wm-equator { stroke: #E3EAF6; }
/* 陆地底色（2026-10-01 用户指定：**去掉海岸线的白色描边**）。
   原实现把陆地拆成两层 —— `.wm-land` 铺底色、最上层 `.wm-coast` 再沿海岸线描一圈 1px 白线
   （目的是让高亮时陆地上的海岸线仍是干净的 1px 白线）。用户要求去掉这圈白描边 ⇒
   `.wm-coast` 这一层与它的样式一并删除，陆地↔海洋的边界改由底色色差直接体现。 */
.wm-land { fill: var(--map-land); pointer-events: none; }
/* 主要湖海（贝加尔湖、五大湖…）：用海色盖在陆地上层，避免内陆湖被填成陆地。
   里海不在此列 —— 它是陆地轮廓自带的空洞，天然留白。
   ⚠ pointer-events: none ⇒ 悬停到湖面上时命中的仍是其下方的区域，不会把悬浮卡打断。 */
.wm-lakes { fill: var(--surface-2); pointer-events: none; }
/* 区域高亮层：默认填「透明色」——不重绘底色（让底图透出来），hover/固定时换 `--map-hover`。
   ⚠ 2026-10-01 用户指定：高亮色由品牌蓝 `--brand-500` 改为中性深灰蓝 `#475467`。 */
.wm-region { cursor: pointer; outline: none; pointer-events: all; }
.wm-region use { fill: transparent; stroke: none; transition: fill 0.15s; }
/* 「无产品区域」灰显（用户 2026-10-02 指定：「5 大区域完整画出，非洲等空区域浅灰保留」）。
   ⚠ 默认是 `fill: transparent`（不重绘底色、让底图透出来）——灰显必须给一层**实色浅灰**，
     否则「无数据」与「未高亮」在视觉上完全一样，用户会以为地图漏画了。
   ⚠⚠ **本规则必须写在下面的 `:hover` 之前** —— 两者选择器权重相同（0,2,1），
     靠「后写者胜」决定；若放在 hover 之后，灰显区域 hover 时**不会被高亮**（点不动、没反馈）。
   ⚠ 用浅灰 `#EFF3F9`：全站最浅层级色，不抢底图轮廓、也不至于看不见。 */
.wm-region.no-data use { fill: #EFF3F9; }
.wm-region.no-data .wm-label { fill: var(--muted); }
.wm-region:hover use, .wm-region.on use, .wm-region:focus-visible use { fill: var(--map-hover); }
.wm-label {
  font-family: var(--font); font-size: 15px; font-weight: 700;
  fill: var(--text-3); text-anchor: middle; dominant-baseline: middle;
  pointer-events: none; transition: fill 0.15s;
}
.wm-region:hover .wm-label, .wm-region.on .wm-label, .wm-region:focus-visible .wm-label { fill: #fff; }
/* 悬浮卡：锚定在该区域的标签锚点上（left/top 由 JS 写百分比）
   ⚠ pointer-events: none —— 卡片只是信息展示，**不抢 hover**：光标即使落在卡片范围内，
     命中的仍是其下方的地图区域，不会出现「鼠标一移到卡片上就闪没」的抖动。
     代价是卡片不能滚动/点击，故 app.js 用 WM_TIP_MAX 限制列示条数。 */
.wm-tip {
  position: absolute; z-index: 5;
  transform: translate(-50%, calc(-100% - 14px));
  min-width: 170px; max-width: 280px;
  padding: 11px 13px;
  background: var(--surface); border: 1px solid var(--border-strong); border-top: 2px solid var(--brand-500);
  pointer-events: none;
}
.wm-tip[hidden] { display: none; }
/* 上方放不下时翻到锚点下方（由 JS 按实测高度判定并加 .below）——
   窄屏地图压到 ~290px 高时，「北美」这类靠上的锚点上方只剩不到 90px，浮在上方会被容器裁掉顶部 */
.wm-tip.below { transform: translate(-50%, 14px); }
.wm-tip-h { font-size: 13px; font-weight: 700; color: var(--brand-900); }
.wm-tip-d { font-size: 12px; line-height: 1.6; color: var(--text-3); margin-top: 5px; }
.wm-tip-list { list-style: none; margin: 9px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.wm-tip-list li { display: flex; gap: 8px; font-size: 12px; color: var(--text-2); }
.wm-tip-list .code { flex: none; font-weight: 700; color: var(--text); }
.wm-tip-empty, .wm-tip-more { margin-top: 9px; font-size: 12px; color: var(--muted); }

/* ---------------- 全球投资：产品列表（2026-10-02） ----------------
   ⚠ 2026-10-02 页面重构：原「按市场明细」（`.gi-row` / `.gi-group` / `.gi-chip` 等）
     与「全球市场产品」两块**已被 7 列产品列表取代**，相关样式（`.gi-hide` / `.gi-head` /
     `.gi-rows` / `.gi-row` / `.gi-group` / `.gi-g-label` / `.gi-items` / `.gi-chip` /
     `.gi-tag` / `.gi-tag-t` / `.gi-g-note` / `.gi-items-plain` / `.gi-empty`）
     已一并删除（grep 确认在 `app.js` 中零引用）。
     配套的 @media (max-width:720px) 窄屏堆叠规则同样删除 ——
     列表是表格，窄屏走 `.table-wrap` 横向滚动，不需要列转行。 */
/* 筛选按钮里的数量角标（基金类型 / 资产类型 / 投资区域各行都用）—— 让用户点之前就知道各类有多少只 */
.ftag .ftag-n { font-style: normal; margin-left: 5px; font-size: 11px; font-weight: 600; color: var(--muted); }
/* ⚠⚠ **active 时角标必须用 `inherit`，不能用品牌蓝** ——
   active 按钮是「品牌蓝实底 + 白字」，角标若仍取 `--brand-500` 就与底色**同色** ⇒
   角标等于消失（2026-10-03 实测：按钮底色与角标色都是 `rgb(29,78,216)`）。
   用 `inherit` 随按钮文字色（active 时为 #fff）、`opacity` 压低层级 ⇒ 既有层次又始终可见。
   ⚠ 教训：样式类的验收**不能只看「元素是否存在」**，要看「计算后的颜色对比」
     —— 上一轮验收查了 `.ftag-n` 的数量、没查可见性，故漏检此处。 */
.ftag.active .ftag-n { color: inherit; opacity: 0.82; }
/* ---------------- 详情页 Hero ----------------
   ⚠ 2026-10-01 用户指定：与列表页 `.hero` 一致 —— **去除彩色光晕、完全使用纯白色**。
     原 `.pf-hero::after`（280px 品牌蓝 radial-glow）已删除；`.pf-hero` 本身不带背景，
     继承 `.card`/`.hero` 的纯白实底。 */
.pf-hero {
  padding: 28px 30px 24px; margin-bottom: 24px;
  position: relative; overflow: hidden;
}
.pf-hero .head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pf-hero .head h1 { font-size: 25px; font-weight: 800; color: var(--brand-900); }
.pf-hero .head .en-name {
  display: block; font-size: 12.5px; font-weight: 400; color: var(--muted);
  letter-spacing: 0.4px; margin-top: 3px;
}
.pf-hero .head .code {
  font-size: 11.5px; color: var(--muted); font-weight: 700; letter-spacing: 0.8px;
  border: 1px solid var(--border); padding: 3px 9px; border-radius: 0; background: var(--surface-2);
}
.pf-hero .tags { display: flex; gap: 8px; margin-left: auto; flex-wrap: wrap; }
.tag { font-size: 11.5px; font-weight: 700; padding: 5px 12px; border-radius: 0; }
.tag.risk { background: var(--brand-100); color: var(--brand-700); border: 1px solid #E9EFF9; }
.tag.run { background: var(--neg-soft); color: var(--neg); border: 1px solid #DDF5ED; }
/* 策略介绍：字段名等宽对齐 */
.pf-hero .intro { margin-top: 16px; display: flex; flex-direction: column; gap: 9px; }
.pf-hero .intro-row { display: flex; align-items: baseline; gap: 12px; font-size: 13.5px; }
.pf-hero .intro-k { flex: none; width: 110px; font-weight: 700; color: var(--text-3); font-size: 13px; letter-spacing: 0.2px; }
.pf-hero .intro-v { color: var(--text); line-height: 1.75; }
.pf-hero .sub { margin-top: 14px; color: var(--text-2); font-size: 13.5px; }
.pf-hero .sub b { color: var(--text); font-weight: 700; }
.pf-hero .goal {
  margin-top: 14px; font-size: 13px; color: var(--text-3);
  background: linear-gradient(90deg, var(--brand-50), transparent);
  border-left: 3px solid var(--brand-500);
  padding: 11px 16px; border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.pf-hero .goal b { color: var(--text-2); }

/* KPI */
.kpi-strip { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; margin-bottom: 22px; }
.kpi-tile {
  padding: 18px 20px; position: relative; overflow: hidden;
  transition: transform 0.18s, box-shadow 0.18s;
}
.kpi-tile:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.kpi-tile::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: #475467;
}
.kpi-tile .k { font-size: 12px; color: var(--text-3); font-weight: 600; letter-spacing: 0.2px; }
.kpi-tile .v { font-size: 25px; font-weight: 400; margin-top: 5px; line-height: 1.15; letter-spacing: -0.3px; }
.kpi-tile .s { font-size: 11.5px; color: var(--muted); margin-top: 4px; }
.chip-strip { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 28px; }
.stat-chip {
  background: var(--surface); border: 1px solid var(--border); border-radius: 0;
  padding: 8px 15px; font-size: 12.5px; color: var(--text-3);
  display: inline-flex; align-items: baseline; gap: 8px;
  box-shadow: var(--shadow-sm);
  transition: all 0.15s;
}
.stat-chip:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.stat-chip b { font-size: 14.5px; color: var(--text); font-weight: 700; }

/* 图表 */
.chart-box { position: relative; height: 340px; padding: 6px 4px 0; }
.chart-box.tall { height: 300px; }
/* 首页散点图：高度 +1/3（340 → 453） */
.risk-box { height: 453px; }
.chart-fallback { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--muted); font-size: 13px; }
.chart-legend { display: flex; flex-wrap: wrap; gap: 18px; font-size: 12.5px; color: var(--text-2); margin-bottom: 12px; }
.chart-legend .lg { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; }
.chart-legend .sw { width: 10px; height: 10px; border-radius: 0; display: inline-block; }

/* 回撤信息 */
.dd-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 18px; }
.dd-tile { padding: 16px 18px; }
.dd-tile .k { font-size: 12px; color: var(--text-3); font-weight: 600; }
.dd-tile .v { font-size: 17px; font-weight: 700; margin-top: 4px; color: var(--brand-900); }
.dd-tile .s { font-size: 11.5px; color: var(--muted); margin-top: 3px; }

/* 资产配置 */
.alloc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
.donut-wrap { position: relative; height: 260px; }
.donut-center {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  pointer-events: none;
}
.donut-center .dv { font-size: 24px; font-weight: 800; color: var(--brand-900); }
.donut-center .dk { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.donut-legend { list-style: none; margin-top: 16px; display: flex; flex-direction: column; gap: 9px; }
.donut-legend li { display: grid; grid-template-columns: 10px 1fr auto; gap: 11px; align-items: center; font-size: 12.5px; color: var(--text-2); }
.donut-legend .pct { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }

/* 持仓表 */
.hl-bar { display: inline-block; min-width: 90px; height: 7px; border-radius: 0; background: #EEF1F6; overflow: hidden; vertical-align: middle; margin-left: 8px; }
.hl-bar i { display: block; height: 100%; background: linear-gradient(90deg, var(--brand-500), #60A5FA); border-radius: 0; }

/* 热力图 */
.heat-wrap { overflow-x: auto; }
.heat-grid { display: grid; gap: 3px; width: 100%; }
.heat-grid .yr { grid-column: 1; font-size: 13px; font-weight: 400; color: var(--text-3); display: flex; align-items: center; padding-right: 8px; white-space: nowrap; }
.heat-grid .mn { font-size: 13px; color: var(--muted); text-align: center; align-self: end; padding-bottom: 2px; }
.heat-cell {
  min-width: 0; height: 33px; border-radius: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 400; color: #fff;
  cursor: default; white-space: nowrap;
}
@media (max-width: 720px) {
  /* 手机端：保底宽度，横向滑动查看完整格子 */
  .heat-grid { min-width: 560px; }
  .heat-cell { font-size: 13px; }
}
.heat-legend { display: flex; align-items: center; gap: 8px; margin-top: 14px; font-size: 11.5px; color: var(--muted); }
.heat-bar { width: 190px; height: 10px; border-radius: 0; background: linear-gradient(90deg, #0E9F6E, #F5F7FB 50%, #D92D20); }
.heat-note { font-size: 11.5px; color: var(--muted); margin-top: 8px; }

/* 空状态 */
.empty {
  padding: 70px 20px; text-align: center; color: var(--muted);
  background: var(--surface); border: 1px dashed var(--border-strong); border-radius: var(--radius);
}
.empty .big { font-size: 42px; opacity: 0.45; }
.empty h3 { color: var(--text-2); font-size: 16px; margin: 14px 0 6px; }
.empty p { font-size: 13px; }

/* ---------------- 数据来源面板（2026-10-01 用户指定，参照资管棱镜） ---------------- */
/* 每个页面末尾统一追加一块数据来源说明，DOM 结构与资管棱镜一致：
     <div class="data-panel"><div class="data-source">数据来源：…</div></div>
   由 app.js 的 appendDataSourcePanel() 在**路由派发之后集中追加** ⇒ 每个页面天然都有，
   新页面不需要各自记得写（文案登记在 DATA_SOURCE_TEXT，未登记的页面用默认文案）。

   文字样式照搬资管棱镜：12px / 行高 1.8 / --muted，**无底色、无边框、无内边距**——
   内边距归零是为了让文字左缘与上方表格/卡片左缘严格对齐（.app 与 .card 本身均无内边距）。 */
.data-source {
  background: none;
  border: none;
  padding: 0;
  font-size: 12px;
  line-height: 1.8;
  color: var(--muted);
}
/* ⚠ 上外边距取 28px（= 本站 .section 的下外边距），**不是照搬资管棱镜的 -4px**：
   资管棱镜那个 -4px 是把文字上移、让字形墨迹顶对齐到它自己「原横线」的位置（该横线已在其
   2026-10-01 改版中删除），属它自己的历史像素校准；本站没有那条线，照搬没有意义。
   本站取 28px 的理由：本面板恒为 .app 的最后一个子元素，而各页收尾元素的下外边距并不一致
   （.section 为 28px；跨境专区占位页以 .card 收尾、下外边距为 0）——**相邻兄弟的外边距会折叠**，
   故取 28px ⇒ 无论前一元素是 0 还是 28px，最终间距都收敛到 28px，各页视觉一致。
   下间距不另给（资管棱镜给 20px）：本站 .app 自身有 64px 底部内边距（移动端 40px），已是
   全站统一的页面底部留白，够用。 */
.data-panel:has(> .data-source) { margin-top: 28px; }

/* ---------------- 页脚 ---------------- */
/* ============================================================
   页脚＝网站地图
   结构＝两级容器 + 底栏：① .footer-brand 品牌栏（Logo + 站名）
                        ② .footer-cols 栏目链接区（分类派生 + 相关站点）
                        ③ .footer-bottom 底栏版权
   ⚠ 2026-10-03 用户指定**删除品牌栏下的「数据来源：Wind，截至 yyyy-mm-dd」一行**，
     故 .footer-asof 规则一并移除（当时已无任何引用）。
   视觉守本站硬约束：白底 + 顶部分隔线、扁平直角、无阴影、无渐变装饰
   ============================================================ */
.footer { border-top: 1px solid var(--border); background: var(--surface); }
.footer-inner { max-width: 1280px; margin: 0 auto; padding: 32px 24px 24px; }
/* 两级容器：品牌信息与栏目信息本质不同 → 分容器，新列只在列区内换行，永不落到品牌下方 */
.footer-map { display: grid; grid-template-columns: 1fr auto; gap: 20px 14px; }
.footer-brand { display: flex; flex-direction: column; gap: 12px; }
.footer-brand-link { display: inline-flex; align-items: center; gap: 12px; align-self: flex-start; color: var(--text); }
/* 页脚标识：复用 .brand-logo 与**同一份图形**（assets/img/icon-cube.svg），
   尺寸同为 `--brand-icon-h` —— 页脚两行字号与顶栏逐项一致、且窄屏**不隐藏**副名，
   故与顶栏共用同一组令牌即可与文字墨水对齐。此处显式声明，避免被移动端对 .brand-logo 的覆盖改动。 */
.footer-brand-link .brand-logo { width: var(--brand-icon-h); height: var(--brand-icon-h); }
.footer-brand-text { display: flex; flex-direction: column; line-height: 1.42; width: 70px; }
.footer-brand-text b { font-size: 16.5px; letter-spacing: 0.6px; color: var(--brand-900); text-align: justify; text-align-last: justify; transition: color 0.15s; }
.footer-brand-text small { font-size: 9.5px; color: var(--text-3); letter-spacing: 0.7px; font-weight: 400; text-align: left; }
.footer-brand-link:hover .footer-brand-text b { color: var(--brand-500); }
/* .footer-asof 已于 2026-10-03 删除（全站页脚不再显示「数据来源…截至…」） */
.footer-cols { display: grid; grid-template-columns: repeat(3, 160px); gap: 20px 14px; }
.footer-group-title { font-size: 12.5px; font-weight: 600; color: var(--text-2); }
.footer-links { margin: 10px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.footer-links a { font-size: 12.5px; color: var(--text-3); }
.footer-links a:hover { color: var(--brand-500); }
.footer-bottom { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border); font-size: 11.5px; color: var(--muted); }
@media (max-width: 1100px) {
  /* 品牌块独占一行，与列区隔出距离 */
  .footer-map { grid-template-columns: 1fr; gap: 26px; }
  .footer-cols { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .footer-map { gap: 24px; }
  .footer-inner { padding: 26px 18px 20px; }
}

/* ---------------- 响应式 ---------------- */
@media (max-width: 1100px) {
  .kpi-strip { grid-template-columns: repeat(3, 1fr); }
  .alloc-grid { grid-template-columns: 1fr; }
  .dd-tiles { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .topbar-inner { gap: 12px; }
  .topbar-right .src-badge { display: none; }
  .cards-grid { grid-template-columns: 1fr; }
  .kpi-strip { grid-template-columns: repeat(2, 1fr); }
  .pf-card .kpis { grid-template-columns: repeat(2, 1fr); }
  .hero h1 { font-size: 24px; }
  .hero { padding: 24px 20px 20px; }
  .app { padding: 20px 16px 40px; }
  .card-pad { padding: 18px; }
  .dd-tiles { grid-template-columns: 1fr; }
}
