/* ============================================================
   二级页设计语言（r20，2026-09-11）
   ------------------------------------------------------------
   适用页：dev-list / research-list / dev-detail / research-detail / gallery。
   本文件只在上述五页引入（紧跟 style.css 之后），首页不受影响。

   为什么单独一个文件：style.css 里同一组件的定义散落多处且互相覆盖
   （.section-intro 2 处 · .detail-quiet 2 处 · .eyebrow 3 处），改一处不知
   最终值是哪一处。这里把「二级页」收敛成唯一一份权威定义，加载在最后，
   以同优先级覆盖旧值；旧定义暂不删除，避免与首页共用规则互相牵连。

   设计目标（改前实测 → 改后）：
     · 页头节拍   列表 top121 / 详情 top92 / 摄影 top148  → 统一 --page-top
     · 页名尺度   列表 58px / 摄影 128px                  → 统一 --fs-page-title
     · 条目标题   详情 34px（被当正文读）                 → --fs-article-title
     · 回首页     .section-more @top190 / .detail-home @top92 → 统一 .page-back
     · 尾部计数   摄影页 1200 宽 vs 瀑布流 1310           → 同一 --sides 轴线
     · 页脚       只有摄影页有字标                         → 五页统一
   ============================================================ */

:root {
 /* ---------- 页头节拍：五页共用一套 ---------- */
 --page-top: clamp(
  84px,
  7.6vw,
  108px
 ); /* 页头首元素距视口顶（固定导航 71px + 呼吸） */
 --page-head-gap: clamp(10px, 1.2vw, 16px); /* kicker → 页名 */
 --page-title-gap: clamp(14px, 1.6vw, 20px); /* 页名 → 导语 */
 --page-intro-gap: clamp(20px, 2.4vw, 30px); /* 导语 → 筛选行 / 列表 */
 /* r25：操作区 → 结果 → 计数是同"组"内的三段，原来全部用 --page-intro-gap
    （20~30px），等于把筛选行当成又一段导语——读完导语、读完筛选、再读结果，
    节拍一样长，组内松散。组内收一档，导语与列表之间保持大档。 */
 --filter-gap: clamp(16px, 2vw, 22px);

 /* ---------- 标题尺度：两档，各有明确角色 ----------
     页名（开发 / 科研 / 摄影作品）：短标签，撑得起大字号；
     条目名（详情页 h1）：可能是一句 30+ 字的长标题，必须能折成三行不炸。
     改前两档分别是 58/128 与 34，差距 3.8 倍，看起来是两套设计。 */
 --fs-page-title: clamp(2.5rem, 5.4vw, 4.5rem);
 --fs-article-title: clamp(1.65rem, 2.9vw, 2.5rem);

 /* ---------- 行与线：列表 / 卡片 / 详情共用 ----------
    r25：原来这里自造了"第三份边线"——--line（#ccd7e2）再乘 62% 透明度。
    实测合成后浅色 ≈ #dbe2eb、暗色 ≈ #222220，与 --border-subtle（#d9e2ea / #26241f）
    相差不到 1%，却让全站边线有了三个来源（--border-subtle / --border-strong / --page-rule）。
    直接指向语义档位：细线用 subtle、卡片与控件描边用 strong。 */
 --page-rule: var(--border-subtle);
 --row-pad-y: clamp(20px, 2.1vw, 28px);
 --row-lift: -2px; /* hover 抬升统一为 -2（改前卡片 -3、科研行 -2） */
}

/* ============================================================
   1. 页头：同一顶部留白、同一行结构
   ============================================================ */

/* 列表页（.section 自带 clamp(80px,11vw,150px) 上距）*/
.page-list .list-section,
/* 详情页页眉栏（r19 的 92px 是为避开固定导航手写的）*/
.page-list > .detail-section-bar,
/* 摄影页 hero（自带 clamp(112px,9vw,148px)）*/
.gallery-hero {
 padding-top: var(--page-top);
}

/* 页头行：kicker 左 + 返回右。
   改前列表页是 align-items:flex-end，返回链接贴着巨大页名的基线；
   顶端对齐后，返回链接与 kicker 同一水平线，视觉上是一枚"面包屑"。 */
.page-list .list-section .section-head {
 align-items: flex-start;
 margin-bottom: 0;
}

.page-head .kicker,
.detail-section-bar .kicker {
 margin-bottom: var(--page-head-gap);
}

/* 摄影页的 kicker 叫 .eyebrow：归到同一外观与同一点位。
   inline-flex 会在行盒里留出基线空隙，实测比列表页 kicker 低 3px。 */
.gallery-hero .eyebrow {
 display: flex;
 margin-bottom: var(--page-head-gap);
}

/* ---------- 摄影页首屏文案的可读兜底 ----------
   html.motion-on .fx 把文案压到 opacity:0，只有 intro:gallery 时间线会放行；
   该时间线要等动效系统注册 + 照片墙就绪，实测会偶发没跑到（改前 1600px 下
   「摄影作品」大标题 op0 —— 页面标题整个看不见）。
   保险做在时间线窗口之后：正常时时间线已写内联 opacity，这条动画只是重复一次
   终态；时间线缺席时 1.2s 后自行显现。代价是这两个元素不再参与语言切换后的
   重播入场（可见性优先于重播）。
   reduce-motion 下 motion-static 用 opacity:1 !important 覆盖，不受影响。 */
html.motion-on .gallery-hero .fx {
 animation: fd-gallery-text-visible 0.5s ease 1.2s forwards;
}

@keyframes fd-gallery-text-visible {
 to {
  opacity: 1;
 }
}

/* ---------- kicker 的可见性冲突 ----------
   同一枚 .kicker 被两套入场系统同时声明为隐藏：
     style.css  html.js .section-head .kicker { opacity: 0 }   权重 (0,3,1)
     motion.css .m-head .kicker           { opacity: 0 }   权重 (0,2,0)
   放行端分别是 .section-head.in-view（(0,4,1)）与 .m-head-in（(0,2,0)）。
   motion 接管后 .section-head 的入场已归 .m-head 管（motion.css 里
   html.motion-on .section-head.reveal 直接放行），.in-view 属于另一套已退役的
   路径、可能永远不来；而 .m-head-in 的权重又低于 style.css 那条隐藏规则。
   结果：改前实测 20 次里 3 次 kicker 停在 opacity: 0 / translateY(14px)，
   列表页的「Development」「Research」整个看不见。
   入场既然归 motion 管，就让 motion 的终态说话。 */
html.motion-on .page-list .section-head .kicker {
 opacity: 1;
 transform: none;
 /* 连过渡一起去掉：.m-head .kicker 上挂着 opacity/transform 各 0.5s 的过渡，
     在被节流/不可见标签页里过渡不会推进，计算值会一直停在起始值 0——
     实测同一次全站自检里 20 次有 3 次如此。终态既然已经是可见，就不需要过渡。 */
 transition: none;
}

/* ============================================================
   2. 页名 / 条目标题
   ============================================================ */

.page-title {
 margin: 0;
 font-family: var(--font-display);
 font-weight: 800;
 font-size: var(--fs-page-title);
 line-height: 1.03;
 letter-spacing: var(--ls-cjk, 0.015em);
}

/* 摄影页大标题回归同一档（改前 clamp(3rem,9vw,8rem) → 1600px 下 128px） */
.gallery-hero h1 {
 font-family: var(--font-display);
 font-weight: 800;
 font-size: var(--fs-page-title);
 line-height: 1.03;
 letter-spacing: var(--ls-cjk, 0.015em);
}

/* 详情页 h1：这是"条目标题"不是"页名"，单独一档，但仍然是有力的字号 */
.detail-head h1 {
 font-size: var(--fs-article-title);
 line-height: 1.32;
 letter-spacing: var(--ls-tight);
 margin: 0;
 text-wrap: pretty;
}

/* 详情页阅读列：改前 72ch / 68ch 两份定义，这里定死 66ch */
.detail-quiet {
 max-width: 66ch;
}

/* 详情页：页眉栏 → 标题 的间距与列表页 kicker→页名 同源 */
/* 页眉栏与正文列同宽：改前栏 72ch、正文 68ch，页眉「Development」和标题
   左右都不在一条线上。 */
.page-list > .detail-section-bar {
 max-width: 66ch;
 margin-bottom: 0;
}

#detail-root {
 padding-top: var(--page-title-gap);
}

/* 详情页：标题在上、日期/类型在下。改前 meta 压在标题之上，读标题前要先跨过
   一行元信息；编辑式标题应当先立住，元信息跟在其后作为落款。 */
.detail-head .detail-meta {
 margin: var(--page-head-gap) 0 0;
}

/* ============================================================
   3. 导语：一处定义，两处引用（改前 .section-intro 两份 + .gallery-intro 一份）
   ============================================================ */

.page-list .section-intro,
.gallery-hero .gallery-intro {
 max-width: 58ch;
 margin: var(--page-title-gap) 0 0;
 font-size: clamp(1.05rem, 1.3vw, 1.2rem);
 line-height: 1.7;
 /* 用色阶而不是 opacity：同一条 opacity 在浅底上的衰减远大于深底 */
 color: var(--text-aux);
 opacity: 1;
}

.gallery-hero .gallery-intro {
 margin-top: var(--page-title-gap);
}

/* ============================================================
   4. 返回上层的链接：五页同一个 .page-back
   ============================================================ */

.page-back {
 flex-shrink: 0;
 font-family: var(--font-accent);
 font-size: var(--text-xs);
 letter-spacing: var(--ls-wide);
 line-height: 1.4;
 color: var(--text-aux);
 white-space: nowrap;
 padding-bottom: 3px;
 border-bottom: 1px solid var(--page-rule);
 transition:
  color 0.2s ease,
  border-color 0.2s ease;
}

.page-back:hover,
.page-back:focus-visible {
 color: var(--accent);
 border-bottom-color: var(--accent);
}

/* 列表页的返回链接：r42 起 .section-head::after（旧 r4 渐变短线）已移除，
   页头行只剩标题与返回链接两个子项；order:2 与 auto 外边距让它照旧归右端
   （与摄影页的绝对定位 right、详情页页眉栏一致）。此处保留这条下划线。 */
.page-list .list-section .section-head .section-more {
  order: 2;
  border-bottom: 1px solid var(--page-rule);
}

/* 摄影页没有页头行容器，返回链接绝对定位到 hero 右上，与 kicker 同一水平线 */
.gallery-hero .page-back {
 position: absolute;
 top: calc(var(--page-top) + 2px);
 right: var(--sides);
}

/* r47：详情页页眉返回链接与列表/摄影统一到右侧页边轴；kicker 仍留在
   66ch 阅读列内。≤560px 媒体查询里它恢复随栏排布，手机端不变。 */
@media (min-width: 561px) {
 .page-list {
  position: relative;
 }

 .page-list > .detail-section-bar .page-back {
  position: absolute;
  top: calc(var(--page-top) + 2px);
  right: var(--sides);
 }
}

/* r47：手机端返回链接的视觉文字位置不动，用伪元素把触控热区补到 ≥44px
   （下划线与排版都不位移）；阅读末尾的 .detail-back-link 同一份处理。 */
@media (max-width: 860px) {
 .page-back,
 .detail-back-link {
  position: relative;
 }

 .page-back::before,
 .detail-back-link::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  min-width: 48px;
  min-height: 44px;
  transform: translate(-50%, -50%);
 }
}

/* ============================================================
   5. 筛选行 + 列表起点：同一节拍
   ============================================================ */

.page-list .list-filters,
.gallery-filters {
 margin: var(--page-intro-gap) 0 0;
 gap: 14px 26px;
 align-items: center;
}

.page-list #list-root > .list-items {
  margin-top: var(--filter-gap);
}

/* 列表页筛选条不在 #list-root 之后重建时（首页等）保持原样；此处只约束二级页 */
.page-list #list-root > .list-filters + .list-items {
  margin-top: var(--filter-gap);
}

/* ============================================================
   r17 首批静态内容（构建期生成，见 scripts/build-static-first-batch.mjs）
   ------------------------------------------------------------
   两种语言的首批条目同时存在于 HTML 中，按 html[lang] 择一显示：
   无 JS / JS 未执行时也能直接读到内容，且不会先显示中文再换成英文。
   pages.js 首次渲染若状态完全匹配会直接接管这些节点，不做清空重建。
   ============================================================ */
#list-root > .static-batch {
  display: none;
}

html:not([lang="en"]) #list-root > .static-batch[data-batch-lang="zh"],
html[lang="en"] #list-root > .static-batch[data-batch-lang="en"] {
  display: block;
}

/* 静态批次内的列表仍按二级页的轴线与间距排布（选择器需穿过 .static-batch） */
.page-list #list-root > .static-batch > .list-items {
  margin-top: var(--filter-gap);
}

.page-list #list-root > .static-batch > .list-filters + .list-items {
  margin-top: var(--filter-gap);
}

/* 摄影页瀑布流起点：年份操作区 → 照片墙，与列表页同一档组内节拍 */
.masonry {
  padding-top: var(--filter-gap);
}

/* ============================================================
   6. 尾部计数：与列表 / 瀑布流同一条 --sides 轴线
   改前 .gallery-foot max-width:1200px —— 内容宽 1152，瀑布流内容 1310，
   左右各错开 55px，肉眼可见没对齐。
   ============================================================ */

/* 结果 → 计数：同组收尾，与操作区 → 结果同一档 */
.list-foot {
  margin-top: var(--filter-gap);
}

.gallery-foot {
 max-width: none;
 margin: 0;
 padding: 0 var(--sides) clamp(72px, 9vw, 120px);
}

.page-gallery .masonry {
 padding-bottom: clamp(24px, 3vw, 40px);
}

/* 计数与列表行使用同一档字号 / 色阶 */
.list-count,
.list-end {
 color: var(--text-aux);
 opacity: 1;
}

.list-end {
 letter-spacing: var(--ls-caps);
}

/* ============================================================
   7. 行 / 卡片：分隔线、内距、hover 抬升统一
   ============================================================ */

.page-list .entry-card,
.page-list .entry-row {
 border-top: 1px solid var(--page-rule);
}

.page-list .entry-card:last-child,
.page-list .entry-row:last-child {
 border-bottom: 1px solid var(--page-rule);
}

.list-items .entry-card {
 padding: var(--row-pad-y) clamp(18px, 2.2vw, 26px);
}

/* hover 抬升：改前卡片 -3px、科研行 -2px，统一到 --row-lift */
.page-list .entry-card:hover,
.page-list .entry-card:focus-within,
.page-list .entry-row:hover,
.page-list .entry-row:focus-within {
 transform: translateY(var(--row-lift));
}

.page-list .entry-card:hover,
.page-list .entry-card:focus-within {
 box-shadow: var(--shadow-1);
}

/* ============================================================
   8. 页脚：五页统一带 FANG DIAN 字标
   ============================================================ */

.site-footer {
 padding-top: clamp(44px, 6vw, 76px);
}

/* 字标在暗色下靠描边成形；两主题都保证至少 3:1 的可见轮廓 */
.footer-mark {
 -webkit-text-stroke: 1px color-mix(in srgb, var(--ink) 34%, transparent);
}

.footer-row {
 margin-top: clamp(24px, 3.4vw, 40px);
}

/* ============================================================
   9. 极窄屏：页头节拍收一档，避免页名与返回链接挤在一行
   ============================================================ */

@media (max-width: 560px) {
 :root {
  --page-top: 76px;
  --page-title-gap: 12px;
  --page-intro-gap: 18px;
 }

 .page-list .list-section .section-head,
 .page-list > .detail-section-bar {
  flex-wrap: wrap;
  gap: 10px 16px;
 }

 .gallery-hero .page-back {
  position: static;
  display: inline-block;
  margin-bottom: var(--page-head-gap);
 }
}

@media (prefers-reduced-motion: reduce) {
 .page-list .entry-card:hover,
 .page-list .entry-row:hover {
  transform: none;
 }
}

/* ============================================================
   10. 三级页（条目详情）：research-detail / dev-detail
   ------------------------------------------------------------
   三级页是"档案层"：一列、一行元信息、若干具名字段。它比二级栏目页更安静，
   但必须共用同一套 token 与同一套行/线语言——字段组就是二级页的"行"。

   改前实测（`verify-secondary-pages.mjs` 的 role-mismatch 断言，1600px 深色）：
     · 小标题三套：12px/ls1.68/灰/opacity.75/大写 · 13px/ls2.08/近白/opacity.6/大写
       · 15px/ls normal/灰/opacity1/不放大写
     · 右边缘两条：h1/li/a 在 1119，正文段落因 `max-width:62ch` 缩到 1089（差 30px）
     · 正文字号两种：字段段 17px ／ 流程项 15.2px（0.95rem）
     · 同一 detail-facts 里有的字段有条目分隔线（flow li）、有的没有（p）
   ============================================================ */

/* ---------- 10.1 小标题收敛成一套 ---------- */
.detail-fact h2,
.detail-links h2,
.detail-onward h2 {
 margin: 0 0 var(--page-head-gap);
 font-family: var(--font-accent);
 font-size: var(--text-2xs);
 font-weight: 600;
 line-height: 1.5;
 letter-spacing: var(--ls-caps);
 text-transform: uppercase;
 color: var(--text-aux);
 opacity: 1; /* 色阶承担层级，不再用 opacity 稀释 */
}

/* ---------- 10.2 字段组 = 二级页的行：同一条 hairline、同一套内距 ---------- */
.detail-facts {
 gap: 0;
 /* 详细内容与首屏（概览/入口）之间拉开一档：上面回答"要不要点开"，下面才是"详细读" */
 margin-top: clamp(32px, 4vw, 48px);
}

.detail-fact {
 padding: var(--row-pad-y) 0;
 border-top: 1px solid var(--page-rule);
}

/* r48：末字段的"收尾线"退役 —— 它与下方 .detail-onward 的开场线在单列语境里
   构成两条平行 hairline（手机端实测相距 41px，中间是无内容的空带，读起来像
   渲染残留而非分节）。分节只保留一条线：继续浏览上方的开场线。
   双列断点原本就把末字段底线归零（见下方 min-width:1000px 的 border-bottom:0），
   本条让单列与双列一致；科研详情没有 .detail-facts，行为不变。 */

/* ---------- 10.3 一种正文字号、一条右边缘 ----------
   `.detail-fact p` 原来是 62ch，套在 66ch 的列里等于右边缘缩进 30px；
   列本身就是度量，正文不该再自己收一次。 ---------- */
.detail-fact p,
.detail-flow li {
 font-size: var(--fs-body);
 line-height: 1.85;
 max-width: none;
}

.detail-fact p {
 margin: 0;
}

/* ---------- 10.4 功能流程：与字段组同一套线，收尾让字段自己收 ---------- */
.detail-flow {
 gap: 0;
}

.detail-flow li {
 padding: var(--space-item) 0;
 border-top: 1px solid var(--page-rule);
}

.detail-flow li:last-child {
 /* r48：收尾线已随 .detail-fact:last-child 一并退役，末尾分节交给
    .detail-onward 的开场线，避免两条线叠在一起 */
 border-bottom: 0;
}

.detail-flow-no {
 font-size: var(--text-2xs);
 letter-spacing: var(--ls-wide);
}

/* ---------- 10.5 类型 tag 与技术栈 chip 统一成同一枚 pill ---------- */
.entry-tag {
 padding: 3px 10px;
 color: var(--text-aux);
 border-color: var(--page-rule);
 opacity: 1;
}

/* 选择器要用 .detail-fact .entry-stack：`<p class="entry-stack">` 也是 `.detail-fact p`
   的匹配对象，10.3 那条 `margin: 0`（(0,1,1)）会盖掉这里的 (0,1,0)，本意要写得比它
   更具体才不会随源码顺序漂移。 */
.detail-fact .entry-stack {
 margin-top: var(--page-head-gap);
}

.entry-stack span {
 padding: 3px 10px;
 border: 1px solid var(--page-rule);
 border-radius: 999px;
 background: none;
 color: var(--text-aux);
}

/* ---------- 10.6 元信息：日期与精度标签也走色阶 ---------- */
.detail-meta .item-date,
.detail-meta .item-date-type {
 color: var(--text-aux);
 opacity: 1;
}

.detail-meta .item-date-type {
 color: var(--text-faint);
}

/* ---------- 10.7 收尾两节：相关入口 / 继续浏览 ---------- */
.detail-links {
 margin-top: var(--page-intro-gap);
}

.detail-onward {
 margin-top: clamp(40px, 5vw, 64px);
 padding-top: var(--space-group);
 border-top: 1px solid var(--page-rule);
}

.detail-onward a {
 padding: var(--space-item) 0;
 border-bottom: 1px solid var(--page-rule);
}

.detail-onward a:last-child {
 border-bottom: 0;
}

/* ---------- 10.8 页眉 kicker 与二级页同一字距 ---------- */
.detail-kicker {
 letter-spacing: var(--ls-caps);
}

/* ---------- 10.9 空态与页底留白 ---------- */
.detail-missing {
 padding: var(--space-section) 0;
 color: var(--text-aux);
}

/* 缺失/无效 ID：一句说明 + 一枚明确的返回列表按钮 */
.detail-missing p {
 margin: 0;
}

.detail-missing-go {
 margin-top: var(--page-intro-gap);
}

/* ---------- 10.10 首屏：著录行 → 简明概览 → 入口行 ----------
   改前首屏是「标题 → 日期/类型 → 摘要/是什么」，著录信息（期刊卷期、专利号）
   被当成正文的一节，唯一的外链入口还压在全文最后。现在著录信息随元数据上提，
   概览用全站导语同一档，入口紧跟在概览之后。 */

/* 科研条目的著录信息：既是识别条目的必要元数据，也回答了"发表在哪" */
.detail-record {
 margin: var(--page-head-gap) 0 0;
 font-family: var(--font-accent);
 font-size: var(--text-sm);
 line-height: 1.6;
 color: var(--text-aux);
 /* 专利号叠加法律状态是长串（CN119924773A · 授权 2025-11-21（CN119924773B）），
    窄屏允许任意位置断开，不撑破阅读列 */
 overflow-wrap: anywhere;
}

/* r45：科研著录结构化定义列表。字段名弱、字段值正常，
   桌面两列对齐（dt 固定宽 + dd 自适应），手机收为上下堆叠。
   比单行字符串更易扫读，长专利号和日期自然折行。 */
.detail-record-list {
 margin: 0;
 display: grid;
 gap: 6px 0;
}
.detail-record-row {
 display: grid;
 grid-template-columns: 96px minmax(0, 1fr);
 gap: 8px 14px;
 align-items: baseline;
}
.detail-record-row dt {
 margin: 0;
 font-family: var(--font-accent);
 font-size: var(--text-2xs);
 letter-spacing: var(--ls-caps);
 text-transform: uppercase;
 color: var(--text-aux);
 opacity: 0.7;
}
.detail-record-row dd {
 margin: 0;
 font-size: var(--text-sm);
 line-height: 1.6;
 color: var(--ink);
 overflow-wrap: anywhere;
}
@media (max-width: 560px) {
 .detail-record-row {
  grid-template-columns: 1fr;
  gap: 2px 0;
 }
 .detail-record-row dt {
  margin-top: 6px;
 }
}

/* r45：首页科研紧凑模式：隐藏摘要后收紧上下内距，
   让访客能更快连续扫描多项成果。列表页不受影响。 */
.entry-row.is-compact {
 padding: 14px 0;
}
.entry-row.is-compact .entry-body h3 {
 margin-bottom: 4px;
}
.entry-row.is-compact .entry-venue {
 margin-bottom: 0;
}

/* 概览复用 .section-intro 的度量与字号（58ch），只把上间距对上页眉节奏 */
.page-list .detail-lead {
 margin-top: var(--page-title-gap);
}

.detail-link-row {
 display: flex;
 flex-wrap: wrap;
 align-items: center;
 gap: 12px;
}

/* 入口是阅读页上的动作行，不必沿用首页 hero 那一档 52px 主按钮；
   <button> 不继承 body 字体，需显式继承，否则与相邻的 <a> 按钮字体不一致。 */
.detail-links .button {
 min-height: 46px;
 padding: 0 22px;
 font-family: inherit;
 font-size: var(--text-nav);
}

.detail-links .button.is-copied {
 border-color: var(--ok, #2ebb6b);
 color: var(--ok, #2ebb6b);
}

.detail-links .button.is-failed {
 border-color: var(--warn, #d97706);
 color: var(--warn, #d97706);
}

/* 复制反馈对读屏可读、对视觉隐藏（视觉反馈由按钮文案本身承担） */
.detail-copy-status {
 position: absolute;
 width: 1px;
 height: 1px;
 overflow: hidden;
 clip-path: inset(50%);
 white-space: nowrap;
}

/* 复制失败的手动退路：一块只读框，用户可以自己选中复制。
   不设 display（hidden 属性才生效），不自动消失，只能手动关闭。
   flex-basis:100% 让它独占一行，不与按钮挤在同一行。 */
.detail-copy-fallback {
  flex-basis: 100%;
  /* r25：改前 2px —— 退路块紧贴按钮行下沿，看起来像按钮的残影而不是
     "另一条路"。与页眉同档（10~16px），让"按钮失败 → 手动复制"读成两段。 */
  margin-top: var(--page-head-gap);
}

.detail-copy-manual-label {
 margin: 0 0 8px;
 font-size: var(--text-2xs);
 color: var(--text-aux);
}

.detail-copy-manual-row {
  display: flex;
  /* 与 .detail-link-row 同一档（12px），输入框与"关闭"是并列的两个控件 */
  gap: 12px;
  align-items: center;
}

/* r25：手动复制框并入控件层（L1 主控件，与列表页的 .filter-select 同级）——
   改前透明底 + --page-rule 描边，在阅读页里像一个"没画完的框"；
   现在用与 select 同一套 --ctl-* ：实底 + strong 描边 + 同半径、同横向内距。
   纵向内距保留 10px（输入框需要更高的可点/可选中高度），不做机械等高。 */
.detail-copy-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px var(--ctl-pad-x);
  border: 1px solid var(--ctl-border);
  border-radius: var(--ctl-radius);
  background: var(--ctl-surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-xs);
}

/* r26：手动复制区的「关闭」原为 .button.secondary —— 与同一区域的主操作
   「复制链接」同为 999px 胶囊、同 ink 描边、同字号，两个按钮长得一样，
   主次只能靠位置分辨；失败面板里读者第一眼看到的是一枚"和复制一样重"的按钮。
   改为中性次按钮：透明底 + --ctl-border 描边 + --ctl-radius 圆角，
   与 .copy-email / .filter-reset 同一套控件语言；文字降到 --text-muted（≥5.8）。
   纵向内距保留 12px 让它与相邻输入框（47px）等高，手机上更有可点面积，
   按钮行的层级差由"表面/描边/颜色"承担，而不是靠缩小操作面积。 */
.detail-copy-close {
  padding: 12px var(--ctl-pad-x);
  border: 1px solid var(--ctl-border);
  border-radius: var(--ctl-radius);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--text-xs);
  line-height: 1.4;
  white-space: nowrap;
  cursor: pointer;
  transition:
    border-color var(--dur-hover) var(--ease-out),
    background-color var(--dur-hover) var(--ease-out),
    color var(--dur-hover) var(--ease-out);
}
.detail-copy-close:hover,
.detail-copy-close:focus-visible {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--ctl-border));
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--accent);
}

/* 与二级页同一档页底留白（原来固定 90px，不随视口变化） */
.detail-quiet {
 padding-bottom: clamp(72px, 9vw, 120px);
}

/* ============================================================
   r40：开发详情的阅读排版（章节轨道 / 步骤示意 / 读完之后）
   ------------------------------------------------------------
   为什么只做在开发详情：科研详情是"档案层"的短条目（著录 + 摘要 + 入口），
   加章节轨道只会把短内容摊开；HTML 里两页的 data-page 本就不同，
   所以这里全部用 html[data-page="dev-detail"] 限定，不波及科研页。

   双列不是"把正文压窄"：正文度量仍是原来的 66ch（16px 基准下 696.67px 盒宽、
   内部 648.7px），双列只是把容器再加宽一条章节名轨道（15ch + 24px 间距），
   于是正文列宽度与改动前逐像素一致，长出来的部分全给章节名。

   断点定在 1000px 的依据（本机实测）：此时容器（15ch+24px+66ch = 879px）的
   左右留白 60.5px 与页眉/页脚的 --sides（6vw = 60px）同一条轴线；
   再窄就会出现"详情列比页眉内容还宽"的错轴（940px 时错 26px），
   所以窄于 1000px 一律回到"标题在上、正文在下"的单列节奏。
   ============================================================ */

:root {
 /* 详情正文度量：与 .detail-quiet 既有 max-width 同值，改这里两处一起变 */
 --detail-measure: 66ch;
 --detail-rail: 15ch; /* 章节名轨道（EN "ABOUT THE PROJECT" 单行放得下） */
 --detail-rail-gap: 24px;
 --flow-gap: 32px; /* 横排步骤之间的空档，连接线画在这段空档里 */
}

html[data-page="dev-detail"] .detail-quiet {
 /* 窄屏（<1000px）不加宽：容器仍是既有的 66ch，正文度量与改动前逐像素一致。
    加宽只发生在下面的双列断点里。 */
 max-width: var(--detail-measure);
}

/* ---------- 详情正文体：段落归一个容器，段数变化不影响分列 ---------- */
.detail-fact-body > p + p {
 /* 1.85 行距下，段间距取不到一行：读得出换段，又不把长说明切成碎片 */
 margin-top: 0.9em;
}

@media (min-width: 1000px) {
 /* 加宽 = 正文度量 + 章节名轨道 + 列间距；除这条轨道外，正文列宽度不变 */
 html[data-page="dev-detail"] .detail-quiet {
  max-width: calc(
   var(--detail-measure) + var(--detail-rail) + var(--detail-rail-gap)
  );
 }

 /* ---------- 章节 = 左章节名 + 右正文 ---------- */
 html[data-page="dev-detail"] .detail-facts {
  row-gap: clamp(28px, 3.6vw, 44px);
 }

 html[data-page="dev-detail"] .detail-fact {
  display: grid;
  grid-template-columns: var(--detail-rail) minmax(0, 1fr);
  column-gap: var(--detail-rail-gap);
  /* 章节名与正文首行同一条基线：小名大字并排时，这是唯一看着对齐的对法 */
  align-items: baseline;
  padding: 0;
  /* 10.2 的"每个字段一条 hairline"在双列里会变成表格横线；
     双列靠留白分节，只在首节上方保留一条开场线 */
  border-top: 0;
 }

 html[data-page="dev-detail"] .detail-fact:first-child {
  padding-top: var(--row-pad-y);
  border-top: 1px solid var(--page-rule);
 }

 /* （r48）末字段底线已从 10.2 移除，这里不再需要抵消 */

 html[data-page="dev-detail"] .detail-fact h2 {
  /* 基线对齐已经承担了"名与文同起"，这里不再额外下压 */
  margin: 0;
 }

 html[data-page="dev-detail"] .detail-fact .entry-stack {
  margin-top: 0;
 }

 /* ---------- 流程：紧凑横向步骤 ----------
   仍是同一个 <ol>、同一份 flow 数据、同一个 DOM 顺序：
   只是宽屏把"一行一步"折成"一列一步"，编号在上、步骤在下，
   步骤之间画一段装饰线（::before 空内容，不进无障碍树）。 */
 html[data-page="dev-detail"] .detail-flow {
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  column-gap: var(--flow-gap);
  row-gap: 0;
 }

 html[data-page="dev-detail"] .detail-flow li {
  position: relative;
  grid-template-columns: minmax(0, 1fr);
  gap: 6px;
  padding: 0;
  border: 0;
  /* 步骤是辅助信息，横排后归到 --text-sm；正文段落不受影响 */
  font-size: var(--text-sm);
  line-height: 1.5;
 }

 html[data-page="dev-detail"] .detail-flow li + li::before {
  content: "";
  position: absolute;
  top: 8px;
  right: calc(100% + 4px);
  width: 24px;
  height: 1px;
  background: linear-gradient(
   90deg,
   transparent,
   color-mix(in srgb, var(--text-faint) 80%, transparent) 30%,
   color-mix(in srgb, var(--text-faint) 80%, transparent) 70%,
   transparent
  );
  pointer-events: none;
 }
}

/* ============================================================
   r40：读完之后 —— 上一篇 / 下一篇 + 返回列表
   ------------------------------------------------------------
   详情排序是"日期倒序 + 同年人工 sortRank"，部分条目只有年份，
   所以邻项只说"上一篇 / 下一篇"，不说"更新 / 更早"。
   ============================================================ */

.detail-onward-list {
 list-style: none;
 margin: 0;
 padding: 0;
}

/* 整块是一枚链接；方向标签固定一列，标题占满其余宽度并自然折行（不省略） */
.detail-onward a {
 display: flex;
 flex-wrap: wrap;
 align-items: baseline;
 justify-content: flex-start;
 gap: 4px 16px;
}

.detail-onward-item a {
 border-bottom: 1px solid var(--page-rule);
}

.detail-onward-item:last-child a {
 border-bottom: 0;
}

.detail-onward-cap {
 flex: 0 0 auto;
 min-width: 6.5em;
 display: inline-flex;
 align-items: baseline;
 gap: 8px;
 font-family: var(--font-accent);
 font-size: var(--text-2xs);
 letter-spacing: var(--ls-wide);
 color: var(--text-aux);
}

/* 长英文标题完整显示：折行而不省略（原来标题与箭头两端对齐，长标题只能被挤） */
.detail-onward-title {
 flex: 1 1 16rem;
 min-width: 0;
}

/* 阅读末尾的安全出口：与页眉那枚 .page-back 同一套外观，同一份 safeListReturn */
.detail-back-row {
 margin: var(--space-group) 0 0;
}

.detail-back-link {
 display: inline-flex;
 align-items: baseline;
 gap: 8px;
 padding-bottom: 3px;
 border-bottom: 1px solid var(--page-rule);
 font-family: var(--font-accent);
 font-size: var(--text-xs);
 letter-spacing: var(--ls-wide);
 color: var(--text-aux);
 text-decoration: none;
 transition:
  color 0.2s ease,
  border-color 0.2s ease;
}

.detail-back-link:hover,
.detail-back-link:focus-visible {
 color: var(--accent);
 border-bottom-color: var(--accent);
}
