/* ==========================================================
   life-marathon-view.css — 路线详情层 (路线 / 海拔 / 分段)
   ------------------------------------------------------------

   加载序: CSS 第 24 / 37 (index.html <head> 第 24 个 <link>; 后加载的覆盖先加载的)
   ★ 全站 37 个 CSS 的完整加载序 + 职责表见 index.html 头注释「📂 文件结构速查」
     (单一事实源 —— 旧版 19 个文件各抄一份清单, 且停在 25/26/28/31 四个旧版本;
      加一个 CSS 要改 19 处, 编号还是"分组号"而非真实加载位, 现已收敛)
   [Backlog #180] 语汇沿用 life-marathon.css:
     纸面 linear-gradient(180deg,#fdfaf3,#f5ecd9) / 墨 rgba(10,31,36,.92)
     海青 rgba(0,80,100,…) / 强调 #b31515 / 硬投影 2px 3px 0
   ========================================================== */

/* ---------- 层容器 ---------- */
.mv-layer {
    position: fixed; inset: 0; z-index: 100020;
    display: none;
}
.mv-layer.is-open { display: block; }

.mv-scrim {
    position: absolute; inset: 0;
    background: rgba(10, 31, 36, 0.55);
    backdrop-filter: blur(3px);
}

.mv-panel {
    position: absolute; inset: 50% auto auto 50%;
    transform: translate(-50%, -50%);
    width: min(860px, calc(100vw - 32px));
    max-height: calc(100vh - 40px);
    display: flex; flex-direction: column;
    background: linear-gradient(180deg, #fdfaf3 0%, #f5ecd9 100%);
    border: 1px solid rgba(10, 31, 36, 0.16);
    box-shadow:
        4px 5px 0 rgba(0, 0, 0, 0.2),
        0 10px 28px rgba(0, 0, 0, 0.24),
        inset 0 1px 0 rgba(255, 255, 255, 0.6);
    color: rgba(10, 31, 36, 0.92);
    overflow: hidden;
}

/* ---------- 头部 ---------- */
.mv-head {
    display: flex; align-items: flex-start; gap: var(--space-7);
    padding: var(--space-8) var(--space-9) var(--space-6);
    border-bottom: 1px solid rgba(10, 31, 36, 0.1);
}
.mv-head-txt { flex: 1; min-width: 0; }
.mv-title {
    margin: 0; font-family: var(--font-serif-cjk);
    font-size: var(--type-title-sm); font-weight: 600; letter-spacing: 0.02em;
}
.mv-sub {
    margin: var(--space-2) 0 0;
    font-size: var(--type-caption); color: rgba(10, 31, 36, 0.62);
    font-family: var(--font-mono);
}
/* [Backlog #272 · 2026-10-24] 路线详情头部的当日天气(真实历史再分析值)
   —— 挂在副标题下面单独一行。等宽 token: 一串读数要对齐。
   ⚠ [hidden] 时整行不占位(JS 侧对没有 weather 的场次直接 hidden)。 */
.mv-weather {
    margin: var(--space-2) 0 0;
    font-size: var(--type-caption-sm);
    font-family: var(--font-mono);
    color: rgba(10, 31, 36, 0.58);
    letter-spacing: 0.01em;
}
.mv-weather[hidden] { display: none; }
/* [Backlog #291 · 2026-10-26] 路线详情头部的**跑鞋**(用户提供的真实事实)
   —— 挂在天气下面**单独一行**: 装备 ≠ 环境读数, 混在一行会看不清。
   ⚠ 用**无衬线**(var(--font-sans)): 鞋款名不是读数, 等宽是留给「要对齐的数值」的。
   ⚠ [hidden] 时整行不占位(JS 侧对没有 shoe 的场次直接 hidden)。 */
.mv-gear {
    margin: var(--space-2) 0 0;
    font-size: var(--type-caption-sm);
    font-family: var(--font-sans);
    color: rgba(10, 31, 36, 0.58);
    letter-spacing: 0.01em;
}
.mv-gear[hidden] { display: none; }
.mv-close {
    flex: 0 0 auto;
    width: 30px; height: 30px;
    font-size: var(--type-title); line-height: 1;
    color: rgba(10, 31, 36, 0.7);
    background: transparent;
    border: 1px solid rgba(10, 31, 36, 0.2);
    border-radius: 50%;
    cursor: pointer;
    transition: background 140ms ease, color 140ms ease;
}
.mv-close:hover { background: #b31515; color: #fffefb; border-color: #b31515; }
.mv-close:focus-visible { outline: 2px solid rgba(0, 80, 100, 0.7); outline-offset: 2px; }

/* ---------- 标签页 ---------- */
.mv-tabs { display: flex; gap: 0; border-bottom: 1px solid rgba(10, 31, 36, 0.1); }
.mv-tab {
    padding: var(--space-5) var(--space-9);
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body-sm);
    color: rgba(10, 31, 36, 0.6);
    background: transparent; border: 0;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}
.mv-tab:hover { color: rgba(10, 31, 36, 0.9); background: rgba(0, 80, 100, 0.05); }
.mv-tab.is-active { color: #b31515; border-bottom-color: #b31515; font-weight: 600; }
.mv-tab:focus-visible { outline: 2px solid rgba(0, 80, 100, 0.7); outline-offset: -2px; }

.mv-body { flex: 1; overflow: auto; padding: var(--space-7); }
.mv-note {
    margin: var(--space-7) 0; padding: var(--space-6);
    font-size: var(--type-caption); color: rgba(10, 31, 36, 0.6);
    border-left: 3px solid rgba(179, 21, 21, 0.5);
    background: rgba(255, 255, 255, 0.4);
}

/* ---------- 路线图 ---------- */
.mv-svg { display: block; width: 100%; height: auto; }

.mv-road { fill: none; stroke: rgba(10, 31, 36, 0.22); stroke-linecap: round; stroke-linejoin: round; }
/* [Backlog #224] motorway/trunk = 高速/桥面(港马「三桥」的桥面), 最粗最实, 画在最底下 */
.mv-road--motorway     { stroke: rgba(10, 31, 36, 0.55); }
.mv-road--trunk        { stroke: rgba(10, 31, 36, 0.5); }
.mv-road--primary      { stroke: rgba(10, 31, 36, 0.42); }
.mv-road--secondary    { stroke: rgba(10, 31, 36, 0.34); }
.mv-road--tertiary     { stroke: rgba(10, 31, 36, 0.28); }
.mv-road--residential  { stroke: rgba(10, 31, 36, 0.2); }
.mv-road--unclassified { stroke: rgba(10, 31, 36, 0.18); }
/* [Backlog #258 · 2026-10-23] 步道一类 —— 日常训练跑实际走的就是这些
   (实测沿线 59~71% 是 footway·cycleway·path 类)。比马路细、比马路淡,
   画在最上层但仍让路线压得住 —— 它的作用是**让路线有依托**, 不是抢戏。 */
.mv-road--living_street { stroke: rgba(10, 31, 36, 0.22); }
.mv-road--track        { stroke: rgba(10, 31, 36, 0.24); }
.mv-road--footway      { stroke: rgba(10, 31, 36, 0.26); }
.mv-road--path         { stroke: rgba(10, 31, 36, 0.24); }
.mv-road--cycleway     { stroke: rgba(10, 31, 36, 0.26); }

.mv-route {
    fill: none;
    stroke: #8e3022;
    stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
    filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.18));
}

.mv-node { stroke: #0b1116; stroke-width: 1.1; }
.mv-node--start  { fill: rgba(0, 80, 100, 0.85); }
.mv-node--finish { fill: #8e3022; }
/* 起终点同址 (环形赛道): 半青半红, 一眼看出"回到原点"而不是糊成黑点 */
.mv-node--both {
    fill: rgba(0, 80, 100, 0.9);
    stroke: #8e3022;
    stroke-width: 1.9;
}

.mv-scalebar-line { stroke: rgba(10, 31, 36, 0.6); stroke-width: 1.4; }
.mv-scalebar-text {
    font-family: var(--font-mono); font-size: var(--type-caption);
    fill: rgba(10, 31, 36, 0.7);
}

/* ---------- 海拔剖面 ---------- */
/* ⚠ 不要给 .mv-svg--ele 写死 height。
   viewBox 已是 760x300 的扁比例, 若 CSS 再强制 height:300px,
   盒宽(~796) 与盒高(300) 就不匹配 viewBox(760x300), 需要非等比缩放才填满 ——
   那正是文字被压扁的根因。靠 .mv-svg 的 height:auto 按 viewBox 推导盒高即可。 */
.mv-ele-fill { fill: rgba(0, 80, 100, 0.16); }
.mv-ele-line { fill: none; stroke: #006478; stroke-width: 1.6; stroke-linejoin: round; }
.mv-ele-label {
    font-family: var(--font-mono); font-size: var(--type-caption);
    fill: rgba(10, 31, 36, 0.6);
}

/* ---------- 心率剖面 (Backlog #276 · 2026-10-25) ----------
   红 = 强度高(与配速图例同款的 #c0392b); 海拔剖面用海青 —— 两张剖面一眼能分开。
   ⚠ 同样**不写死 height**: viewBox 已是扁比例, 靠 .mv-svg 的 height:auto 推导盒高。 */
.mv-hr-fill { fill: rgba(192, 57, 43, 0.14); }
.mv-hr-line { fill: none; stroke: #c0392b; stroke-width: 1.6; stroke-linejoin: round; }
.mv-hr-label {
    font-family: var(--font-mono); font-size: var(--type-caption);
    fill: rgba(10, 31, 36, 0.6);
}
.mv-hr-stats {
    display: flex; flex-wrap: wrap; gap: var(--space-5) var(--space-7);
    margin: var(--space-6) 0 0;
}
.mv-hr-stat { display: inline-flex; align-items: baseline; gap: var(--space-2); }
.mv-hr-stat-k {
    font-size: var(--type-micro); letter-spacing: 0.08em;
    color: rgba(10, 31, 36, 0.55);
}
.mv-hr-stat-v {
    font-family: var(--font-mono); font-size: var(--type-caption);
    color: rgba(10, 31, 36, 0.82);
}
.mv-hr-note {
    margin: var(--space-5) 0 0;
    font-size: var(--type-micro); color: rgba(10, 31, 36, 0.5);
}
/* ---------- [Backlog #284] 心率**原始图表**(App 截图) ---------- */
.mv-hr-chartfig {
    margin: var(--space-6) 0 0;
    padding: var(--space-4);
    border: 1px solid rgba(10, 31, 36, 0.1);
    background: #fff;
}
.mv-hr-chart {
    display: block; width: 100%; height: auto;
}
.mv-hr-chartcap {
    margin-top: var(--space-3);
    font-size: var(--type-micro); color: rgba(10, 31, 36, 0.55);
}

/* ---------- 分段 (Backlog #187: 旧多列网格 + 横向进度条下线, 改单列逐公里;
              Backlog #223: 逐公里 → 5km 断点, 配色三档 → 六档;
              Backlog #224: 改成**两块并存** —— 上 = 5km 断点小结, 下 = 逐公里明细) ----------
   四列含义: 断点 | 用时(累计) | 配速(该段) | 爬升。行左侧色条 = 配速档位。
   为什么分栏: 报纸式 3 列(§207), 列内公里连续升序, 横向扫时列位置对得上。
   【#223】行内容改成 5km 断点(用户口径: 5/10/15/20/半马/25/30/35/40, 每行用时+配速),
   21~42 行压到 5~9 行, 一屏看完一场的配速形状。
   【#224】用户澄清「增加分段小结」≠「替换掉逐公里」→ 两块并存, 逐公里表**恢复**。
   ⚠ 色板来源: **#223 起按用户口径重画** —— 红 = 最快, 黄 = 中, 青绿 = 最慢, 共六档
     (改前是「青 = 快 / 红 = 慢」三档, 与用户口径正好相反)。

   ⚠⚠ [Backlog #224 · 2026-10-22] 这段注释**曾经提前闭合** —— 第 2 行末尾就写了终止符,
     于是下面几行说明文字变成**裸文本** → CSS 解析器把「说明文字 + .mv-splits」整段
     当成**一个选择器** → 选择器非法 → **整条 .mv-splits 规则被丢弃**。
     后果: 报纸式 3 列从 #223 起**就没生效过**(分栏塌成一列), 而当时的探针只断言了
     DOM 结构(3 个 .mv-splits-col + data-km-from/to)—— **存在性断言全绿, 布局是坏的**。
     教训: 探针要断言**计算后的布局**(三列的 top 相同、left 不同), 不能只断言 DOM 里
     "有那三个列容器"; 注释里也**绝不能写字面的终止符**, 否则会重演同一个坑。 */
.mv-splits {
    /* Backlog #188: 固定 3 列 (用户拍板「第一列 1-7 / 第二列 8-14 / 第三列 15-21」)。
       用 grid 3 列而不是 CSS multi-column: multi-column 由**可用高度**决定断点,
       列内公里号会不连续, 与需求相反; 显式 grid 列由 JS 保证每列公里连续。
       窄屏降级到 1 列见文件末尾 @media —— 那时退化成 #187 的单列长列表, 可读性不丢。 */
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-5) var(--space-6);
    align-items: start;   /* 各列行数可能差 1 (均分的余数), 顶对齐更好看 */
}
.mv-splits-col {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;   /* grid 子项默认 min-width:auto, 会把长内容撑破列宽 */
}
/* 列头: 标出本列公里区间, 横向扫时不必逐行数公里号 */
.mv-splits-colhead {
    font-family: 'PingFang SC', -apple-system, sans-serif;
    font-size: var(--type-micro);
    letter-spacing: 0.04em;
    color: rgba(10, 31, 36, 0.5);
    padding: 0 var(--space-4) 3px;
    border-bottom: 1px solid rgba(10, 31, 36, 0.12);
    margin-bottom: 2px;
}
.mv-split {
    display: grid;
    /* [Backlog #261 · 2026-10-23] 公里列 38px → 46px: 段行的新标签「35–40 km」实测 44px
       (等宽 10px, 5.5px/字符), 38px 装不下会压到用时列。
       其余四列宽度不动 ⇒ 行内容 240px → 248px。 */
    grid-template-columns: 46px 58px 46px 40px 34px;  /* 公里/区间 | 用时 | 配速(本行区间) | +秒差 | Δ海拔 */
    align-items: center;
    gap: var(--space-3);
    padding: 4px var(--space-4) 4px var(--space-3);
    background: rgba(255, 255, 255, 0.45);
    border: 1px solid rgba(10, 31, 36, 0.08);
    border-left-width: 3px;   /* 档位色条占左侧, 行高不变 */
    border-left-color: var(--pace-bar, transparent);
}
/* 【Backlog #223 · 2026-10-21】六档配速色, 语义按用户口径**翻转 + 细化**:
     快 = 红 → … → 黄(中) → 青绿(慢)。改前是「青 = 快 / 红 = 慢」, 只有三档。
   两个变量: --pace-bar 给**色条/图例色块**(要醒目, 可以亮),
             --pace-ink 给**配速数字**(要能读 —— 橙/黄在米白底上对比度不够, 单独压深)。
   ⚠ .mv-split-ele 的红/青(海拔升/降)与本组色**不是一回事**: 前者是地形语义,
     后者是配速语义, 两者在不同列, 各有各的图例。 */
.mv-split.is-pace-1 { --pace-bar: #8f1d1d; --pace-ink: #8f1d1d; }   /* 最快 深红 */
.mv-split.is-pace-2 { --pace-bar: #c0392b; --pace-ink: #c0392b; }   /* 快   红   */
.mv-split.is-pace-3 { --pace-bar: #d97b26; --pace-ink: #a85a13; }   /* 偏快 橙   */
.mv-split.is-pace-4 { --pace-bar: #c9a227; --pace-ink: #8a6c10; }   /* 中   黄   */
.mv-split.is-pace-5 { --pace-bar: #6a9a4b; --pace-ink: #4f7a38; }   /* 偏慢 黄绿 */
.mv-split.is-pace-6 { --pace-bar: #1f7a6a; --pace-ink: #1f7a6a; }   /* 慢   青绿 */
/* 最快的那一行: 加浅底, 与档位色条共存 (色条说档, 底色说「这是本场最快」) */
.mv-split.is-fastest { background: rgba(0, 100, 120, 0.07); }
/* 配速数字: 跟着档位上色 —— 色条在行首, 视线落到数字时再确认一次 */
.mv-split .mv-split-pace { color: var(--pace-ink, rgba(10, 31, 36, 0.92)); }
/* 用时(累计到该断点): 等宽右齐, 扫视时数字成列 */
.mv-split-time {
    text-align: right;
    font-family: var(--font-mono); font-size: var(--type-body-sm);
    color: rgba(10, 31, 36, 0.92);
}
/* 爬升格右对齐 */
.mv-split-ele { text-align: right; }

/* ---------- 配速档位图例 (Backlog #187 · 六档见 #223 · 版式见 #259) ----------
   没有图例的话, 彩色行对用户是噪声: 看不出每档分别代表什么。
   图例色块直接复用行上的 --pace-bar, 于是**图例与行永远同色**, 不会各写一份色值跑偏。

   [Backlog #259 · 2026-10-23] 改成**独立的图例区**。
   改前是一行 run-on 文字: 长句「配速快慢(逐公里行相对本场最快公里; 分段与总计行相对
   本场最快 5km 段)」后面直接接 6 个「■ 文字」, 换行时从色块中间断开, 整块看着不规整。
   改后三段式(硬边 + 1px 细线分隔, 无圆角无投影 —— 纸雕语汇):
     ① 眉标  配速色阶 / PACE SCALE
     ② 两个基准的口径 —— 对齐成两行(左「行类型」/ 右「相对基准」)。原来挤在一个
        括号里, 读者看不出那是**两条**规则, 于是把「逐公里行」的秒差拿去和
        「5km 小结行」的秒差比 —— 两者基准不同, 比出来的数没有意义。
     ③ 六格色标尺 —— 每格 = 3px 左色条 + 档名 + 区间。
        色条与行上的 border-left **同形同粗**, 于是图例真的是一把「钥匙」:
        行的左色条 ↔ 图例格的左色条, 眼睛不用换符号。
   ⚠ 类名全部沿用旧名(.mv-pace-legend / -label / -item / -swatch / -text):
     旧探针 _b246b_verify(在跑批器里)断言「6 档」与「label 写明两个基准」;
     _b223d_verify 断言「色相单调递增」+「item 文本首词 = 档名」+「swatch 背景可解析 RGB」。
     所以 swatch 元素保留(改成 3px 竖条, 背景色仍是 --pace-bar ⇒ 色相断言照旧成立)。 */
.mv-pace-legend {
    margin-bottom: var(--space-5);
    border: 1px solid rgba(10, 31, 36, 0.12);
    background: rgba(255, 255, 255, 0.42);
    font-family: 'PingFang SC', -apple-system, sans-serif;
    font-size: var(--type-caption);
    color: rgba(10, 31, 36, 0.66);
}
/* ① 眉标 */
.mv-pace-legend-head {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid rgba(10, 31, 36, 0.1);
}
.mv-pace-legend-title {
    font-weight: 600;
    letter-spacing: 0.1em;
    color: rgba(10, 31, 36, 0.78);
}
.mv-pace-legend-en {
    font-size: var(--type-micro-sm);
    font-weight: 600;
    letter-spacing: 0.16em;
    color: rgba(10, 31, 36, 0.4);
}
/* ② 两个基准: 左「行类型」/ 右「相对基准」, 对齐成两行
   ⚠ 两行必须**共用同一个两列栅格**才能对齐 —— 早先给每行各自一个 grid,
     各自按 max-content 定宽, 于是「逐公里行」那行的基准起点比另一行靠左,
     两行的值**没对齐**(「相对基准」列左右错开), 正是要治的毛病。
     ⇒ 外层 .mv-pace-legend-label 是唯一栅格; 每行的 .mv-pace-legend-basis
       用 display:contents 把自己「摊平」进外层栅格, 左列宽度由两行里最宽的那行决定。 */
.mv-pace-legend-label {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: var(--space-5);
    row-gap: 2px;
    align-items: baseline;
    padding: var(--space-4);
    border-bottom: 1px solid rgba(10, 31, 36, 0.1);
}
.mv-pace-legend-basis { display: contents; }
.mv-pace-legend-basis-k {
    font-weight: 600;
    color: rgba(10, 31, 36, 0.85);
}
.mv-pace-legend-basis-v b {
    font-weight: 600;
    color: rgba(10, 31, 36, 0.88);
}
/* ③ 六格色标尺: 格子之间不画竖线, 靠各自的 3px 左色条分隔 */
.mv-pace-legend-scale {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
}
.mv-pace-legend-item {
    display: grid;
    grid-template-columns: 3px 1fr;
    grid-template-rows: auto auto;
    column-gap: var(--space-3);
    padding: var(--space-3) var(--space-4) var(--space-4);
}
.mv-pace-legend-swatch {
    grid-column: 1;
    grid-row: 1 / 3;
    align-self: stretch;
    background: var(--pace-bar, transparent);
}
.mv-pace-legend-text {
    grid-column: 2;
    grid-row: 1;
    font-weight: 600;
    color: rgba(10, 31, 36, 0.88);
    white-space: nowrap;
}
.mv-pace-legend-range {
    grid-column: 2;
    grid-row: 2;
    font-family: var(--font-mono);
    font-size: var(--type-micro);
    color: rgba(10, 31, 36, 0.5);
    white-space: nowrap;
}
/* 图例项自己带 --pace-bar (与 .mv-split 同名), 于是六档只在这一处定义一次 */
.mv-pace-legend-item.is-pace-1 { --pace-bar: #8f1d1d; }
.mv-pace-legend-item.is-pace-2 { --pace-bar: #c0392b; }
.mv-pace-legend-item.is-pace-3 { --pace-bar: #d97b26; }
.mv-pace-legend-item.is-pace-4 { --pace-bar: #c9a227; }
.mv-pace-legend-item.is-pace-5 { --pace-bar: #6a9a4b; }
.mv-pace-legend-item.is-pace-6 { --pace-bar: #1f7a6a; }
.mv-split-km {
    position: relative;
    font-family: var(--font-mono); font-size: var(--type-micro);
    color: rgba(10, 31, 36, 0.5);
}
.mv-split-pace {
    position: relative;
    font-family: var(--font-mono); font-size: var(--type-body-sm);
    color: rgba(10, 31, 36, 0.92);
}
.mv-split-ele {
    position: relative; text-align: right;
    font-family: var(--font-mono); font-size: var(--type-micro);
    color: rgba(10, 31, 36, 0.45);
}
.mv-split-ele.is-up   { color: #b31515; }
.mv-split-ele.is-down { color: #006478; }

/* ---------- 分段: 两块并存 (Backlog #224 · 2026-10-22) ----------
   #223 把逐公里表**替换**成了 5km 断点表, #224 改成**两块并存**:
   上 = 分段小结(5km 断点), 下 = 逐公里明细(恢复 §206/§207 形态)。
   逐公里行的列**原样恢复**: 公里 | 配速 | 相对最快(+秒) | Δ海拔
   (§206 的栅格 22px 52px 40px 34px, 与小结表的 34px 56px 48px 36px 不同)。 */
.mv-split-block { margin-top: var(--space-6); }
.mv-split-block:first-child { margin-top: 0; }
.mv-split-block-title {
    margin: 0 0 var(--space-3);
    font-family: 'PingFang SC', -apple-system, sans-serif;
    font-size: var(--type-body-sm);
    font-weight: 600;
    letter-spacing: 0.02em;
    color: rgba(10, 31, 36, 0.72);
}
/* [Backlog #261 · 2026-10-23] 用时列的口径注脚 ——
   同一列有两种口径(逐公里行 = 累计钟表 / 5km 分段行 = 该段用时), 不写明读者会以为全表都是累计。
   放在标题下方、图例上方: 它讲的是**表**的口径, 图例讲的是**颜色**的口径。 */
.mv-split-block-note {
    margin: 0 0 var(--space-4);
    font-family: 'PingFang SC', -apple-system, sans-serif;
    font-size: var(--type-caption-sm);
    line-height: 1.7;
    color: rgba(10, 31, 36, 0.6);
}
/* 「相对最快公里 +秒」: 右对齐等宽, 扫视时秒差成列 */
.mv-split-delta {
    position: relative; text-align: right;
    font-family: var(--font-mono); font-size: var(--type-micro);
    color: rgba(10, 31, 36, 0.45);
}


/* ---------- 分段: **一张表**, 逐公里与 5km 小结交错 (Backlog #246 · 2026-10-22) ----------
   #224 是「两块并存」(上小结 / 下逐公里), #246 合并成**一张表**: 行序 = 「该 5km 段的
   逐公里行 → 该段的小结行」, 段段相接。列含义全表统一:
     公里 | 用时(累计) | 配速(本行区间) | +秒差(各 family 基准) | Δ海拔
   两个基准(§237 教训): 逐公里行相对最快公里, 小结/终行相对最快 5km 段。
   小结行与终行用不同底色, 让「哪一行是段小结 / 哪一行是终行」一眼可见。 */
.mv-splits-block {
    display: flex; flex-direction: column; gap: 2px;
    min-width: 0;   /* grid 子项默认 min-width:auto, 长内容会撑破列宽 */
}
/* 块头: 标出这个 5km 段的范围(「0–5 km」), 读者一眼知道自己在第几段 */
.mv-splits-blockhead {
    font-family: 'PingFang SC', -apple-system, sans-serif;
    font-size: var(--type-micro);
    letter-spacing: 0.04em;
    color: rgba(10, 31, 36, 0.5);
    padding: 0 var(--space-4) 3px;
    border-bottom: 1px solid rgba(10, 31, 36, 0.12);
    margin-bottom: 2px;
}
/* 5km 小结行: 底色略重 + 上边框, 与逐公里行区分 */
.mv-split--seg {
    background: rgba(10, 31, 36, 0.06);
    border-top: 1px solid rgba(10, 31, 36, 0.14);
}
/* 终行(半马 / 全马): 最重的一行 */
.mv-split--total {
    background: rgba(10, 31, 36, 0.1);
    border-top: 2px solid rgba(10, 31, 36, 0.28);
}
.mv-split--seg .mv-split-km,
.mv-split--total .mv-split-km { font-weight: 600; }

/* ---------- 赛事卡上的路线入口 (Backlog #180) ---------- */
.race-card-route-btn {
    display: inline-flex; align-items: baseline; gap: var(--space-3);
    margin-top: var(--space-4);
    padding: var(--space-3) var(--space-6);
    font-family: var(--font-serif-cjk);
    color: rgba(0, 80, 100, 0.9);
    background: rgba(0, 80, 100, 0.08);
    border: 1px solid rgba(0, 80, 100, 0.35);
    border-radius: 2px;
    cursor: pointer;
    transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.race-card-route-btn:hover {
    background: #b31515; color: #fffefb; border-color: #b31515;
}
.race-card-route-btn:focus-visible { outline: 2px solid rgba(0, 80, 100, 0.8); outline-offset: 2px; }
.race-card-route-btn-en { font-size: var(--type-caption-sm); letter-spacing: 0.04em; }
.race-card-route-btn-cn { font-size: var(--type-caption); }
.race-card-route-btn:hover .race-card-route-btn-en,
.race-card-route-btn:hover .race-card-route-btn-cn { color: #fffefb; }

@media (prefers-reduced-motion: reduce) {
    .race-card-route-btn { transition: none; }
}

/* [Backlog #261 · 2026-10-23] **三列版面在 769~810px 窗口本来就溢出**(旧行宽 240px,
   而那窗口的报纸列只有 226px —— 3×240+2×12 = 744px 的需求 vs 只有 703px 可用),
   本轮把公里列加宽到 46px 后溢出从 14px 变成 22px, 于是**顺手修掉**:
   ≤900px 把四列压窄, 行内容收到 220px —— 769px 窗口的列宽 226px 刚好装得下。
   ⚠ 放在 768px 那段**之前**: 同特异度靠源码顺序, 窄屏那段要能覆盖它。 */
@media (max-width: 900px) {
    .mv-split { grid-template-columns: 46px 54px 40px 30px 26px; }
}
@media (max-width: 768px) {
    .mv-panel { width: calc(100vw - 16px); max-height: calc(100vh - 16px); }
    .mv-body { padding: var(--space-5); }
    .mv-head { padding: var(--space-7) var(--space-7) var(--space-5); }
    .mv-title { font-size: var(--type-section); }
    .mv-tab { padding: var(--space-5) var(--space-7); }
    /* Backlog #187: 原「窄屏 1fr 1fr 两列」已下线 —— 现在是单列,
       窄屏要做的是把四列压窄而不是改列数 */
    /* Backlog #188: 窄屏 3 列并排会把每列压到 ~120px, 四列数字放不下 -> 退 1 列
       (退化成 #187 的单列长列表, 每行一个公里, 仍然可读) */
    .mv-splits { grid-template-columns: 1fr; gap: var(--space-4); }
    .mv-split { grid-template-columns: 46px 54px 40px 30px 26px; gap: var(--space-3); }
}
@media (max-width: 420px) {
    .mv-splits { gap: 2px; }
    .mv-split { grid-template-columns: 46px 54px 40px 32px 28px; gap: var(--space-3); padding: 4px var(--space-4); }
    /* padding 由上面 .mv-split 那条给(两条都命中同一批行), 这里不重复写 ——
       重复写会把棘轮的「<=8px 声明数」顶到上限。 */
    .mv-sub { font-size: var(--type-micro); }
}

/* [Backlog #259] 六格色标尺的窄屏折行: ~560px 以下每格放不下区间文案(+28%~40%),
   折成 3 列 × 2 行; 第二行加一条 1px 横线, 与面板其余的细线分隔同族。 */
@media (max-width: 560px) {
    .mv-pace-legend-scale { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .mv-pace-legend-item:nth-child(n+4) { border-top: 1px solid rgba(10, 31, 36, 0.08); }
}

@media (prefers-reduced-motion: reduce) {
    .mv-close, .mv-tab { transition: none; }
}

/* ==========================================================
   影像页 [Backlog #203] —— 成绩证书 + 大众跑者等级证书
   ------------------------------------------------------------
   两张图的**宽高比差别很大**(成绩证书是竖版整页 1488×2105,
   大众二级是横版 1125×804), 所以一律 `object-fit: contain` +
   限高, 不裁切 —— 证书上有签名和印章, 裁掉就失去凭证意义了。
   ========================================================== */
.mv-photos {
    display: grid;
    gap: var(--space-8);
    padding: var(--space-6) 0;
}
.mv-photo {
    margin: 0;
    padding: var(--space-6);
    background: rgba(255, 253, 245, 0.75);        /* 抄 life-marathon.css:349 证书底 */
    border: 1px solid rgba(10, 31, 36, 0.1);       /* :350 */
    box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.08);    /* :351 */
}
.mv-photo-cap {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-5);
    margin-bottom: var(--space-5);
}
.mv-photo-cap-en {
    font-family: var(--font-sans);
    font-size: var(--type-caption-sm);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(126, 79, 24, 0.95);               /* 暖铜 —— 与卡片证书标签同源 */
}
.mv-photo-cap-cn {
    font-family: var(--font-serif-cjk);
    font-size: var(--type-body);
    font-weight: 600;
    color: rgba(10, 31, 36, 0.9);
}
.mv-photo-cap-hint {
    font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
    font-size: var(--type-caption-sm);
    color: rgba(10, 31, 36, 0.5);
}
.mv-photo-img {
    display: block;
    width: 100%;
    /* 限高而非定宽: 竖版成绩证书整页有近 2000px 高, 不限会把页面撑得没法滚 */
    max-height: 620px;
    object-fit: contain;
    margin: 0 auto;
    border: 1px solid rgba(0, 0, 0, 0.08);
    background: #fffdf5;
    cursor: zoom-in;
    transition: border-color 0.2s ease;
}
.mv-photo-img:hover {
    border-color: rgba(150, 96, 29, 0.55);
}

/* ---------- 现场照片分区 [Backlog #270 · 2026-10-24] ----------
   影像页现在有两类东西: **赛事文件**(成绩证书 / 大众二级) 与 **摄影作品**(现场照片)。
   后者来自摄影库 ⇒ 用**冷色**(海青)与证书的暖铜区开, 读者一眼看得出两类不同。
   ⚠ 现场照片是**普通照片**(多为横版), 限高放宽 —— 证书要限高是因为整页近 2000px 高,
     照片不需要; 但仍保留 object-fit: contain, 不裁切。 */
.mv-photo-sec {
    margin-top: var(--space-10);
    padding-top: var(--space-8);
    border-top: 1px dashed rgba(0, 80, 100, 0.25);
}
.mv-photo-sec-title {
    display: flex;
    align-items: baseline;
    gap: var(--space-5);
    margin: 0 0 var(--space-4);
    font-family: var(--font-sans);
    font-size: var(--type-body-sm);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: rgba(0, 80, 100, 0.9);
}
.mv-photo-sec-count {
    font-family: var(--font-mono);
    font-size: var(--type-caption-sm);
    letter-spacing: 0.06em;
    color: rgba(0, 80, 100, 0.6);
    text-transform: none;
}
.mv-photo-sec-note {
    margin: 0 0 var(--space-5);
    font-family: var(--font-serif-cjk);
    font-size: var(--type-caption-sm);
    color: rgba(10, 31, 36, 0.55);
}
.mv-photos--live {
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--space-7);
    padding: 0;
}
.mv-photos--live .mv-photo {
    padding: var(--space-5);
    background: rgba(255, 253, 245, 0.8);
}
.mv-photos--live .mv-photo-img {
    max-height: 260px;
    cursor: zoom-in;
}
.mv-photos--live .mv-photo-cap-en {
    color: rgba(0, 80, 100, 0.85);   /* 冷色 —— 与证书的暖铜区开 */
}
.mv-empty-photos {
    padding: var(--space-13) 0;
    text-align: center;
    color: rgba(10, 31, 36, 0.45);
    font-size: var(--type-body);
}

/* ==========================================================
   [Backlog #205] 路线底色面 —— 绿地 / 水系 / 建成区
   ------------------------------------------------------------
   配色由用户拍板:「更明显的分区 —— 绿地中等饱和绿、水系中等青、建成区浅褐,
   **靠色相区分而不是靠明度**」。

   三个色相各据一处, 互不混淆:
     绿地   ~88°  黄绿      水系   ~194° 青蓝      建成区 ~32°  暖褐
   所以即使三者明度接近, 也能一眼分出是哪一类。

   透明度的取舍: 画在米白纸面(#fdfaf3→#f5ecd9)上, 合成后仍要**明显比纸面艳**,
   否则整张图看起来还是「没画」。但又不能压过路线 —— 路线靠下面那条 casing 兜底。
   ========================================================== */

/* 建成区: 最底下的一层「城市体量」, 暖褐 */
.mv-lu--built {
    fill: rgba(198, 158, 108, 0.32);
    stroke: rgba(176, 136, 88, 0.28);
    stroke-width: 0.8;
}

/* 绿地: 中等饱和黄绿 */
.mv-lu--green {
    fill: rgba(96, 148, 64, 0.42);
    stroke: rgba(84, 128, 56, 0.45);
    stroke-width: 0.8;
}

/* 水系: 中等青 —— 压在绿地/建成区之上, 水面必须读得出是「水」 */
.mv-lu--water {
    fill: rgba(48, 138, 168, 0.45);
    stroke: rgba(38, 112, 140, 0.5);
    stroke-width: 0.8;
}

/* [Backlog #205 · 2026-10-13] 底色面一律 round 接头/圆端。
   为什么: 这三层是**拼接**出来的多环 path, 相邻环之间会出现极小的夹角甚至
   近乎 180° 的折返。SVG 默认 `stroke-linejoin: miter` 在那种夹角上会甩出一条
   **长尖刺**, 视觉上就是用户说的「边界粗细不均」。round 接头把尖刺削成小圆角,
   代价是在正常折角处轮廓略钝一点 —— 底色是背景层, 那点钝化看不出来。 */
.mv-lu {
    stroke-linejoin: round;
    stroke-linecap: round;
}

/* 路线底衬(casing): 比路线粗一圈的浅色描边。
   ⚠ 为什么需要: 砖红 #8e3022 压在中青底(合成后约 rgb(179,193,195))上时,
     对比度只有 ~3.5:1, 1.8px 的细线会发虚。casing 用纸面色垫在下面,
     等于给路线留出一圈「呼吸」, 不管底下铺了什么都读得出来。
     这是制图里的标准做法(道路的 casing), 不是权宜之计。 */
.mv-route-casing {
    fill: none;
    stroke: #fdfaf3;
    stroke-width: 4.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.85;
}
