/* ============================================================
   material.css —— 「纸在案上」物质系统
   ------------------------------------------------------------
   此前的问题（诊断，不是审美偏好）：
     · 底色 #f7f2e7 与卡片 #fffdf7 几乎同色 —— 纸浮在纸上，没有层次
     · 卡片是 1px 描边 + 2px 圆角的矩形 —— 宣纸不长这样
     · 所有区块等距、居中、成网格 —— 每一处都"正确"，于是没有一处有记忆点
     · 数字只是衬线字 —— 没有尺、没有秤、没有厚度
   现实里的宣纸：有毛边、有帘纹、有厚度、会叠放、会被盖朱印、会洇墨、
   摊在昏暗作坊的木案上被灯照亮。这一层把 UI 换回这些东西。
   本文件最后加载，只做覆盖与新增，不动既有结构。
   ============================================================ */

/* ---------- ① 案面与纸：拉开反差，才有"纸摊在案上"这件事 ---------- */
:root{
  --desk:        #221e19;   /* 案面：老木 / 石板，暖褐近黑 */
  --desk-2:      #2b2520;
  --desk-3:      #3a332b;
  --sheet:       #fbf7ee;   /* 纸：比原来更亮，是被灯照着的那张 */
  --sheet-2:     #efe6d2;   /* 下面那张 */
  --on-desk:     #ece4d4;   /* 案面上的字 */
  --on-desk-soft:#a99e8d;
  --on-desk-faint:#7f7666;
  --sheet-line:  #ded5c1;
}

body{ background:var(--desk); color:var(--on-desk); }

/* 案面的光：上方一盏灯，四周压暗。真实作坊就是这样照明的 */
body::before{
  content:""; position:fixed; inset:0; z-index:0; pointer-events:none;
  background:
    radial-gradient(112% 78% at 50% 4%,  rgba(255,236,200,.115), transparent 62%),
    radial-gradient(96% 74% at 50% 104%, rgba(0,0,0,.5),         transparent 68%);
}
/* 原底纹是 multiply（压暗），在深色案面上会整片糊掉 —— 改成 overlay 只留纤维感 */
.paper-photo{ opacity:.20; mix-blend-mode:overlay; }
.grain-tile,
#paper-grain{ opacity:.16; mix-blend-mode:overlay; }

/* 长卷页有自己的夜景与光照（body.is-journey 已设纯色底），
   案面那盏灯和底纹 blend 都不能参与 */
body.is-journey::before{ display:none; }
body.is-journey .paper-photo,
body.is-journey #paper-grain{ mix-blend-mode:multiply; opacity:.12; }

/* ---------- ② 毛边：宣纸是捞出来的，四边不齐 ----------
   clip-path 会连 box-shadow 一起裁掉，所以外阴影改由
   祖先 .grid 的 drop-shadow 投出 —— 它会沿着毛边轮廓走。 */
:root{
  --deckle-1:polygon(0.7% 1.3%, 13% .3%, 27% 1.5%, 42% .4%, 57% 1.7%, 72% .3%, 87% 1.5%, 99.3% .7%,
    99.1% 14%, 99.7% 31%, 98.7% 47%, 99.6% 63%, 98.8% 78%, 99.4% 91%, 99.2% 99.1%,
    87% 99.5%, 71% 98.5%, 55% 99.6%, 39% 98.4%, 23% 99.5%, 9% 98.6%, .9% 99.4%,
    .4% 85%, 1.1% 69%, .2% 53%, 1.2% 37%, .4% 21%, .8% 9%);
  --deckle-2:polygon(1.4% .6%, 15% 1.5%, 30% .3%, 45% 1.4%, 60% .4%, 75% 1.6%, 90% .5%, 99.5% 1.2%,
    98.9% 16%, 99.6% 33%, 98.6% 49%, 99.5% 65%, 98.7% 80%, 99.3% 93%, 99.0% 98.7%,
    89% 99.4%, 73% 98.6%, 57% 99.5%, 41% 98.3%, 25% 99.4%, 11% 98.5%, 1.1% 99.3%,
    .3% 87%, 1.0% 71%, .2% 55%, 1.1% 39%, .5% 23%, 1.0% 11%);
  --deckle-3:polygon(.5% 1.0%, 12% 1.7%, 26% .4%, 40% 1.3%, 55% .3%, 70% 1.5%, 85% .4%, 99.4% 1.0%,
    99.2% 15%, 98.8% 32%, 99.6% 48%, 98.7% 64%, 99.5% 79%, 98.9% 92%, 99.3% 98.9%,
    88% 99.6%, 72% 98.4%, 56% 99.4%, 40% 99.6%, 24% 98.5%, 10% 99.4%, .7% 99.2%,
    .2% 86%, 1.2% 70%, .3% 54%, 1.0% 38%, .3% 22%, .6% 10%);
  --deckle-4:polygon(1.1% 1.6%, 14% .4%, 29% 1.4%, 44% .3%, 59% 1.5%, 74% .5%, 89% 1.4%, 99.2% .5%,
    99.5% 17%, 98.9% 34%, 99.6% 50%, 98.6% 66%, 99.4% 81%, 98.8% 94%, 99.1% 98.8%,
    86% 99.3%, 70% 99.6%, 54% 98.4%, 38% 99.4%, 22% 98.6%, 8% 99.3%, 1.0% 99.5%,
    .5% 88%, 1.3% 72%, .4% 56%, 1.0% 40%, .2% 24%, .9% 12%);
  --deckle-5:polygon(.9% .4%, 16% 1.2%, 31% .5%, 46% 1.6%, 61% .6%, 76% 1.3%, 91% .3%, 99.6% 1.5%,
    98.8% 13%, 99.5% 30%, 98.7% 46%, 99.4% 62%, 98.6% 77%, 99.5% 90%, 99.0% 99.3%,
    90% 99.6%, 74% 98.5%, 58% 99.5%, 42% 98.6%, 26% 99.4%, 12% 98.4%, .8% 99.6%,
    .3% 84%, 1.1% 68%, .2% 52%, 1.3% 36%, .6% 20%, 1.2% 8%);
  --deckle-6:polygon(1.6% 1.1%, 17% .5%, 32% 1.6%, 47% .6%, 62% 1.4%, 77% .4%, 92% 1.2%, 99.4% .9%,
    99.0% 18%, 99.6% 35%, 98.8% 51%, 99.5% 67%, 98.7% 82%, 99.4% 95%, 99.2% 98.6%,
    85% 99.5%, 69% 98.4%, 53% 99.6%, 37% 98.5%, 21% 99.3%, 7% 98.7%, 1.2% 99.2%,
    .2% 89%, 1.0% 73%, .5% 57%, 1.2% 41%, .4% 25%, 1.1% 13%);
}

/* ---------- ③ 卡片变身"一张纸" ---------- */
.card{
  position:relative;
  background:var(--sheet);
  color:var(--ink);
  border:0; border-radius:0;
  padding:24px 26px;
  clip-path:var(--deckle, var(--deckle-1));
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.85),      /* 纸的边在灯下的高光 */
    inset 0 -1px 0 rgba(36,42,46,.07);
  transition:transform .35s var(--ease);
}
.card:hover{ transform:translateY(-3px); }
/* 帘纹：竹帘留下的横纹（每 5px）+ 帘丝线（每 27px）。这是捞纸的物证 */
.card::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:
    repeating-linear-gradient(180deg, rgba(109,122,112,.055) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg,  rgba(109,122,112,.045) 0 1px, transparent 1px 27px);
}
/* 每张纸的毛边与摆放角度都不同 —— 六个循环，避免整齐 */
.grid > .col:nth-child(6n+1) > .card{ --deckle:var(--deckle-1); rotate:-.45deg; }
.grid > .col:nth-child(6n+2) > .card{ --deckle:var(--deckle-2); rotate:.38deg; }
.grid > .col:nth-child(6n+3) > .card{ --deckle:var(--deckle-3); rotate:-.28deg; }
.grid > .col:nth-child(6n+4) > .card{ --deckle:var(--deckle-4); rotate:.5deg; }
.grid > .col:nth-child(6n+5) > .card{ --deckle:var(--deckle-5); rotate:-.52deg; }
.grid > .col:nth-child(6n+6) > .card{ --deckle:var(--deckle-6); rotate:.3deg; }
.card{ rotate:-.3deg; }
/* 外阴影：clip-path 会裁掉自身阴影，改由祖先沿毛边轮廓投出 */
.grid{ filter:drop-shadow(0 11px 20px rgba(0,0,0,.46)); }
.card--flat{
  background:rgba(236,228,212,.075); color:var(--on-desk);
  clip-path:none; rotate:0deg;
  border:1px dashed rgba(236,228,212,.32);
  box-shadow:none;
}
.card--flat::after{ display:none; }
.card--flat .card__label{ color:var(--seal-soft); }
.card--flat .src{ color:var(--on-desk-faint); }
.card--flat .src{ border-bottom-color:rgba(236,228,212,.3); }

/* ---------- ④ 案面上的文字 ---------- */
.section__head h3{ color:var(--on-desk); }
.section__head .sub{ color:var(--on-desk-soft); }
.chapter-head__no{ color:var(--seal); opacity:.9; }
.chapter-head__txt h2{ color:var(--on-desk); }
.chapter-head__txt .kicker{ color:var(--seal-soft); }
.chapter-head__txt .lede{ color:var(--on-desk-soft); }

/* 四联：让四个数字卡片挤进一行，而不是 3+1 */
.grid--four > .col{ flex:1 1 235px; }

/* ---------- ⑤ 引文：也是一张纸，不再只是浅色底 ---------- */
.quote{
  position:relative;
  background:var(--sheet); color:var(--ink);
  border-left:0;
  padding:26px 30px 22px 34px;
  clip-path:var(--deckle-3);
  rotate:.35deg;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.85);
}
.quote::before{ left:12px; top:-10px; color:var(--seal); opacity:.32; }
.quote--classic{
  background:var(--sheet);
  border-left:4px solid var(--seal);
}
.quote cite{ color:var(--ink-soft); }
.quote--classic cite{ color:var(--seal); }

/* ---------- ⑥ 页头 / 页脚 / 按钮：案上的木与漆 ---------- */
.site-head{
  background:rgba(26,22,18,.84);
  border-bottom:1px solid rgba(236,228,212,.10);
}
.brand__text b{ color:var(--on-desk); }
.brand__text i{ color:var(--on-desk-faint); }
.mainnav a{ color:var(--on-desk-soft); }
.mainnav a:hover{ background:rgba(236,228,212,.07); color:var(--on-desk); }
.mainnav a.is-active{ background:var(--on-desk); color:var(--desk); }
.mainnav a.is-active em{ color:var(--seal); opacity:1; }
.mainnav__mini{ opacity:.8; }
.btn{
  background:rgba(236,228,212,.06);
  border-color:rgba(236,228,212,.24);
  color:var(--on-desk);
}
.btn:hover{ background:rgba(236,228,212,.13); color:var(--on-desk); }
.btn--solid{ background:var(--seal); border-color:var(--seal); color:#fff; }
.btn--solid:hover{ background:#ad5544; color:#fff; }
.btn--ghost{ color:var(--on-desk-soft); }
.progress{ background:rgba(236,228,212,.16); }
.progress i{ background:var(--seal); }
.site-foot{ border-top:1px solid rgba(236,228,212,.10); }
.foot-line{ color:var(--on-desk-soft); }
.foot-meta{ color:var(--on-desk-faint); }

/* ---------- ⑦ 图表 / 工作台：保住浅色纸面，并锁回墨色字 ---------- */
.chart{ background:var(--sheet); border-color:var(--sheet-line); color:var(--ink); }
.chart__cap{ color:var(--ink-faint); border-top-color:var(--sheet-line); }
.sheet-frame{ background:#ddd6c4; border-color:#c9c0ab; color:var(--ink); }
.cue{ color:var(--ink-soft); border-left-color:var(--sheet-line); }
.roll{ border-top-color:var(--sheet-line); border-bottom-color:var(--sheet-line); }
.roll__item{ border-left-color:var(--sheet-line); }

/* 墨线分隔：中间带一点朱，两端淡出，像一笔扫过 */
.rule-ink{
  height:2px; border:0; opacity:1;
  background-image:linear-gradient(90deg,
    transparent, rgba(236,228,212,.10) 10%, rgba(236,228,212,.42) 44%,
    rgba(156,74,60,.62) 52%, rgba(236,228,212,.30) 62%,
    rgba(236,228,212,.08) 90%, transparent);
}

/* ============================================================
   ⑧ 实物组件：印章 / 墨滴 / 纸叠 / 毫米尺 / 标签 / 照片
   —— 这一节是"与现实结合"的主体
   ============================================================ */

/* 朱印：边缘不方、盖歪一点、压得不匀 */
.seal-stamp{
  display:inline-flex; align-items:center; justify-content:center;
  min-width:46px; padding:7px 12px;
  font-family:var(--font-serif); font-size:13px; letter-spacing:.12em; line-height:1.15;
  text-align:center;
  color:#fff8ee; background:var(--seal);
  border-radius:5px 3px 6px 4px / 4px 6px 3px 5px;
  rotate:-2.4deg; opacity:.94;
  box-shadow:inset 0 0 0 2px rgba(255,248,238,.26);
}
.seal-stamp--zhu{
  background:transparent; color:var(--seal);
  box-shadow:inset 0 0 0 2px var(--seal);
}
.seal-stamp--lg{ min-width:70px; padding:13px 18px; font-size:20px; }
.seal-stamp--sm{ min-width:0; padding:3px 8px; font-size:11px; letter-spacing:.08em; }

/* 洇墨：一滴墨落在纸上的样子，用作分节标记 */
.inkblot{
  flex:0 0 auto; width:66px; height:52px;
  background:radial-gradient(58% 66% at 44% 42%,
    #14171a 0%, #23282c 50%, rgba(36,42,46,.40) 74%, transparent 100%);
  border-radius:54% 46% 62% 38% / 48% 60% 40% 52%;
  filter:blur(.4px);
}
.inkblot--seal{
  background:radial-gradient(58% 66% at 44% 42%,
    #8d3f33 0%, #a85344 50%, rgba(156,74,60,.40) 74%, transparent 100%);
}

/* 纸叠侧边：一刀纸是有厚度的，不是一行字 */
.sheet-stack{ position:relative; height:20px; margin:8px 0 12px; }
.sheet-stack i{
  position:absolute; left:0; right:0; height:3px;
  background:linear-gradient(180deg, #fffdf7 0%, #e5dac2 100%);
  border-bottom:1px solid rgba(36,42,46,.28);
}

/* 毫米尺：纤维长度这类数字，该有尺子量 */
.ruler{ display:block; width:100%; height:auto; }
.ruler text{ font-family:var(--font-num); font-size:9px; fill:#6d7a70; }

/* 宣纸包装标签：仿印刷的红框标签 */
.paper-label{
  position:relative;
  border:2px solid var(--seal);
  padding:14px 18px;
  background:var(--sheet); color:var(--ink);
  font-family:var(--font-sans); letter-spacing:.08em;
  box-shadow:inset 0 0 0 1px rgba(156,74,60,.22);
}
.paper-label::after{
  content:""; position:absolute; inset:5px;
  border:1px dashed rgba(156,74,60,.32); pointer-events:none;
}
.paper-label__k{ font-size:11px; letter-spacing:.22em; color:var(--seal); }
.paper-label dl{ margin:8px 0 0; display:flex; flex-wrap:wrap; gap:4px 18px; font-size:13px; }
.paper-label dt{ color:var(--tan); font-size:11.5px; letter-spacing:.1em; }
.paper-label dd{ margin:0; }

/* 纪实照片：一张冲洗出来的相片 —— 白边、暗角、略微放歪 */
.photo{
  position:relative; overflow:hidden;
  margin:0; padding:9px 9px 6px;
  background:var(--sheet);
  rotate:-.3deg;
  /* 照片是可移动的实物：没影子就和底下的纸糊成一张。
     这里没有 clip-path，box-shadow 不会被自己裁掉（卡片那边才要 filter）。 */
  box-shadow:0 13px 26px rgba(0,0,0,.40), inset 0 1px 0 rgba(255,255,255,.85);
}
.photo img{ width:100%; display:block; filter:saturate(.86) contrast(1.05); }
.photo::after{
  content:""; position:absolute; inset:9px 9px 26px; pointer-events:none;
  background:radial-gradient(120% 100% at 50% 42%, transparent 42%, rgba(20,16,12,.44) 100%);
  box-shadow:inset 0 0 70px rgba(20,16,12,.34);
}
.photo__cap{
  font-size:var(--fs-xs); color:var(--ink-faint);
  padding:7px 2px 2px; letter-spacing:.06em;
}
.grid > .col:nth-child(3n+1) > .photo{ rotate:-.65deg; }
.grid > .col:nth-child(3n+2) > .photo{ rotate:.5deg; }
.grid > .col:nth-child(3n+3) > .photo{ rotate:-.3deg; }

/* 出血：这一屏不是"一张照片"，是"整屏都是这张照片"。
   ------------------------------------------------------------
   整部作品读下来，页面一直是"案面上摊着纸卡"；
   到了这里把案面撤掉，只留一样东西铺满屏幕 —— 一次呼吸。
   三个细节决定它成不成立：
     · 图要能被推着走（img 比容器高 16%，下缘留出视差的余量），
       否则整屏死图 = 一张壁纸；
     · 底部要压一道从暗到透明的渐隐，图注才读得清，
       也才不会像"图没铺满"；
     · 图注要回到正文那一列（而不是贴屏幕边），
       它才像这本书的图注，不像网页的水印。 */
.photo--bleed{
  width:100vw; margin:0 calc(50% - 50vw);
  height:clamp(320px, 72vh, 640px);
  rotate:0deg; padding:0; overflow:hidden;
  background:#14110e; box-shadow:none;
}
.photo--bleed img{
  position:absolute; left:0; right:0; top:-8%;
  width:100%; height:116%;
  object-fit:cover; object-position:center 52%;
  /* scale 而不是 transform:scale —— transform 这一格被视差占着，
     每帧会被 translate3d 整条覆盖掉；scale 是独立属性，两者可以叠加。 */
  scale:1.5;
  filter:saturate(.94) contrast(1.05) brightness(.94);
  will-change:transform;
}
.photo--bleed::after{
  inset:0;
  background:
    linear-gradient(to top, rgba(10,9,8,.74) 0%, rgba(10,9,8,.26) 26%, rgba(10,9,8,.06) 56%),
    radial-gradient(120% 100% at 50% 38%, transparent 30%, rgba(10,9,8,.50) 100%);
  box-shadow:none;
}
.photo--bleed .photo__cap{
  position:absolute; left:0; right:0; bottom:0; z-index:2;
  max-width:var(--maxw); margin:0 auto;
  padding:0 28px 20px;
  color:rgba(247,242,231,.80); font-size:12px; letter-spacing:.18em;
}

/* 手写体：题跋、口述引文用楷 */
.hand{ font-family:"STKaiti","KaiTi","Kaiti SC","楷体",var(--font-serif); }

/* 案上的分隔：一段留白 + 一枚墨点，替代千篇一律的等距 */
.desk-break{
  display:flex; align-items:center; gap:14px;
  margin:34px 0; color:var(--on-desk-faint);
  font-size:11px; letter-spacing:.3em;
}
.desk-break::before,
.desk-break::after{
  content:""; flex:1 1 auto; height:1px;
  background:linear-gradient(90deg, transparent, rgba(236,228,212,.18), transparent);
}

@media (max-width:720px){
  .card{ padding:16px 18px; }
  .quote{ padding:16px 18px; }
  .grid{ filter:drop-shadow(0 6px 12px rgba(0,0,0,.4)); }
}
@media (prefers-reduced-motion: reduce){
  .card, .quote{ rotate:0deg; }
}

/* ============================================================
   2026-10-21 · 整体 UI 修订
   ------------------------------------------------------------
   诊断（全站长图，1280×900）：
   ① 全站 56 个 .card，每一块字都装在一张纸上 → 读起来像"剪贴簿"，
      不像一份文档。真正该是纸的只有：引文、大数字、图注；正文应直接落在案面上。
   ② 每张纸 ±0.5° 的随机角度 + rgba(0,0,0,.46) 的重投影，在长页面里累加成噪声。
   ③ 段落之间 62px 的间距，全站节奏偏挤；章节标题与正文只差 3 级，层级不清。
   ④ 大数字（.stat__val）本来是这份作品的"证据"，却只有 38px，不如一张卡片的标题显眼。

   这一版只做四件事：**收噪声、拉节奏、立层级、放数字**。不新增组件。
   ============================================================ */

/* ---------- ① 节奏 ---------- */
.section{ margin-bottom:92px; }
.section__head{ margin-bottom:36px; }
.section__head h3{ font-size:26px; letter-spacing:.03em; }
.section__head .sub{ font-size:13.5px; letter-spacing:.02em; }
/* 章节标题上方一道朱砂短杠：不占版面，但让每一节有"起头" */
.section__head::before{
  content:""; display:block; width:28px; height:2px;
  background:var(--seal); opacity:.7; margin-bottom:15px;
}
/* 章节头（有 hero 的无所谓，无 hero 的那种靠留白立住） */
.chapter-head{ margin-bottom:64px; }

/* ---------- ② 收噪声：投影、角度、帘纹 ---------- */
.grid{ filter:drop-shadow(0 7px 14px rgba(0,0,0,.30)); }
.grid > .col:nth-child(6n+1) > .card{ rotate:-.22deg; }
.grid > .col:nth-child(6n+2) > .card{ rotate:.18deg; }
.grid > .col:nth-child(6n+3) > .card{ rotate:-.14deg; }
.grid > .col:nth-child(6n+4) > .card{ rotate:.24deg; }
.grid > .col:nth-child(6n+5) > .card{ rotate:-.26deg; }
.grid > .col:nth-child(6n+6) > .card{ rotate:.15deg; }
.card{ rotate:-.15deg; }
.card::after{
  background:
    repeating-linear-gradient(180deg, rgba(109,122,112,.042) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg,  rgba(109,122,112,.034) 0 1px, transparent 1px 27px);
}
.quote{ rotate:.2deg; }

/* ---------- ③ 纸面内部：先给标题一个位置 ---------- */
.card{ padding:26px 30px 24px; }
.card__label{
  font-size:11.5px; letter-spacing:.24em;
  margin-bottom:10px;
}
.card p{ line-height:1.86; }
.card p + p{ margin-top:.7em; }

/* ---------- ④ 数字：这份作品的证据，就该大 ---------- */
.stat__val{ font-size:46px; letter-spacing:-.01em; }
.stat__key{ font-size:12px; letter-spacing:.2em; margin-top:4px; }
.stat__note{ font-size:11.5px; line-height:1.7; }
.stat-row{ gap:26px; }

/* ---------- ⑤ 引文：留白比装饰重要 ---------- */
.quote{ padding:30px 34px 26px 38px; }
.quote p{ font-size:17px; line-height:1.9; }

/* ---------- ⑥ 页头：压低声音，让"卷 / 天时 / 匠心"自己站住 ---------- */
.mainnav a{ letter-spacing:.16em; }
.mainnav a.is-active{ box-shadow:inset 0 -2px 0 var(--seal); }

/* ---------- ⑦ 页脚：三行压成一行半 ---------- */
.site-foot__inner{ padding-top:28px; padding-bottom:34px; }
.foot-line{ font-size:12.5px; opacity:.85; }
.foot-meta{ font-size:11px; letter-spacing:.04em; }

@media (max-width:720px){
  .section{ margin-bottom:62px; }
  .section__head{ margin-bottom:26px; }
  .stat__val{ font-size:36px; }
  .card{ padding:20px 22px 18px; }
}

/* ---------- ⑧ 投影收成一道窄边 ----------
   0 7px 14px / .30 在近黑的案面上读成一圈灰雾（阴影比桌面还亮），
   整组卡片像被一圈光晕包着。改成 3px/7px 的窄影：只在纸边下方压一道暗，
   纸才"落"在案上，而不是"浮"在光里。 */
.grid{ filter:drop-shadow(0 3px 7px rgba(0,0,0,.55)); }
.card::after{
  background:
    repeating-linear-gradient(180deg, rgba(109,122,112,.030) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg,  rgba(109,122,112,.024) 0 1px, transparent 1px 27px);
}

/* ---------- ⑨ 出处：从"一行灰字"变成真正的脚注 ----------
   数据都有出处，这是这份作品的立身之本；但原来它是一串 11px 的灰字，
   挤在纸的下沿、还带一条虚线下划 —— 读起来像标签，不像注。
   改成脚注：上面一道发丝线、字号再降半档、字距放开、行高收到 1.6。 */
.card .src, .quote .src{
  display:block; margin-top:16px; padding-top:10px;
  border-top:1px solid rgba(36,42,46,.13);
  border-bottom:0; font-size:10.5px; line-height:1.65;
  letter-spacing:.03em; color:var(--ink-faint);
}
/* 只有"案面上、纸面之外"的出处是浅色；
   纸里的出处永远是墨色脚注 —— 否则纸卡落在深色案面里，脚注会跟着变浅、读不见 */
.on-desk .src{
  border-top-color:rgba(236,228,212,.16); color:var(--on-desk-faint);
}
.on-desk .card .src{
  border-top-color:rgba(36,42,46,.13); color:var(--ink-faint);
}
/* 键值表的数字统一等宽，右对齐才好对列 */
.kv dd, .kv b{ font-variant-numeric:tabular-nums; }
.kv__r{ padding:7px 0; }

/* ---------- ⑩ 纸面里的标题与图注 ---------- */
.card h4{
  font-size:16px; letter-spacing:.06em; margin-bottom:8px;
}
.card h4 + p{ margin-top:0; }
/* 题注：图下面那半句说明，字号比正文小一档、颜色退一档 */
.card .cap, .cap{
  margin-top:10px; font-size:11.5px; line-height:1.6;
  letter-spacing:.04em; color:var(--ink-faint);
}

/* ---------- ⑪ 表格：71 条文献挤成一块灰墙 ----------
   这是"文献·出处"那一页的主体。原来整张表没有行线、没有表头区分、
   字号和正文一样 —— 71 行读下来会串行。这里只加三样：
   行发丝线、表头小字距、数字等宽。 */
.card table{
  width:100%; border-collapse:collapse;
  font-size:12.5px; line-height:1.62;
}
.card th{
  text-align:left; font-weight:500; color:var(--seal);
  font-size:10.5px; letter-spacing:.2em;
  padding:0 12px 8px 0;
  border-bottom:1px solid rgba(36,42,46,.28);
  white-space:nowrap;
}
.card td{
  padding:7px 12px 7px 0; vertical-align:top;
  border-bottom:1px solid rgba(36,42,46,.075);
}
.card tbody tr:hover td{ background:rgba(156,74,60,.055); }
.card td:first-child, .card th:first-child{ width:52px; color:var(--ink-faint); font-variant-numeric:tabular-nums; }

/* ---------- ⑫ 排版：把"一排卡片"变成"一个栅格" ----------
   这是这一轮最关键的一处。原来一排卡片的宽度是**按内容算的**：
   `.stat-row > *{ flex:1 1 168px }` 没有 `min-width:0`，
   flex 项的 min-content 会把卡片顶宽 —— 于是「1050 年」那张因为多了一行备注
   和一枚徽标，比旁边三张明显宽，右端永远参差。
   排版的第一件事是让同一行的元素服从同一套列宽，不是让内容决定宽度。

   顺带解决两件事：
   · 同一行的卡片等高（flex 默认 stretch，但 .card 自己要撑满）；
   · 脚注（.src）用 margin-top:auto 贴到卡片底部 —— 一行的出处落在同一条基线上。
     这是"排"与"堆"的区别。 */
.stat-row{ gap:24px; align-items:stretch; }
.stat-row > *{ flex:1 1 0; min-width:0; }
.card{ min-width:0; }
.stat-row > .card{ height:100%; display:flex; flex-direction:column; }
.stat-row > .card .stat__note{ margin-top:auto; padding-top:10px; }
.grid{ gap:24px; }
.grid > .col{ display:flex; min-width:0; }
.grid > .col > .card{ width:100%; }
.card:has(> .src){ display:flex; flex-direction:column; }
.card:has(> .src) > .src{ margin-top:auto; }
/* 纸面上不要出现"两行之间贴在一起"：统一段落间距 */
.card p + p{ margin-top:.62em; }
.card > * + *{ margin-top:0; }

/* ---------- ⑬ 行内等高：`.grid` 还留着 align-items:flex-start ----------
   上一条只改了 .stat-row，`.grid` 的原始规则是 `align-items:flex-start` ——
   于是同一行里三张卡的高度各按自己的内容走，底部一条锯齿。
   改成 stretch：一行以最高的那张为准，其余纸铺满 —— 案面上摊开的纸本来就该齐边。 */
.grid{ align-items:stretch; }
.grid > .col > .card{ height:100%; }

/* ---------- ⑭ 键值表：标签被挤成 88px，词都断了 ----------
   实测「道工序 + 操作 + 工程」被压成三行，「生产周期」断成「生产周 / 期」——
   因为 `.kv dt{ flex:0 0 88px }` 是死宽。卡片的列宽会变，键列不能写死。
   改成 grid：键列按内容取宽（有上限），值列吃掉剩下的。 */
.kv__r{
  display:grid; grid-template-columns:minmax(64px,max-content) minmax(0,1fr);
  column-gap:14px; align-items:baseline;
}
.kv dt{ flex:none; white-space:normal; }
.kv dd{ margin:0; }

/* ⑭ 修正：grid 的 max-content 在这里没生效（dt 被压在 64px 下限上继续断词）。
   改回 flex，但这次把键列设成**不可断行**、值列吃掉剩余 ——
   键名本来就不该折行（"生产周期"断成"生产周/期"是硬伤），
   值可以折（"18 + 108 + 手工操作"折了也不影响读）。 */
.kv__r{ display:flex; align-items:baseline; column-gap:0; gap:14px; }
.kv dt{ flex:0 0 auto; white-space:nowrap; }
.kv dd{ flex:1 1 0; min-width:0; }

/* ⑮ 上一条表格样式把"键值表"也一起改坏了 ——
   `.card td:first-child{ width:52px }` 本来是给文献表的序号列定的，
   可第 ② 篇里「权威口径」那张卡用的是**同样的 table 结构**（左键右值），
   52px 的序号列把「道工序 + 操作工程」压成三行。
   改成 width:1% + nowrap：列宽由内容决定，不再折行。 */
.card td:first-child, .card th:first-child{
  width:1%; white-space:nowrap;
}
.card table td.num{ text-align:right; font-variant-numeric:tabular-nums; }

/* ---------- ⑯ 列宽：宽窄两栏根本没铺满 ----------
   `.col--wide{flex:2 1 480px}` + `.col--narrow{flex:0 1 340px}`：
   basis 480+340=820，容器 1124、gap 24 —— 剩 280 只有 wide 在长（grow:2），
   narrow 的 grow 是 0，于是整行只占 1047，**右边缘比容器内缩 77px**。
   长页里每一处"宽图 + 窄注"的段落都缺这一截，右边缘永远对不齐。
   改成按比例分（basis 0）：宽 2 份、窄 1 份，加起来永远等于容器。 */
.col{ flex:1 1 0; }
.col--wide{ flex:2 1 0; }
.col--narrow{ flex:1 1 0; }

/* ---------- ⑰ 六张数字挤成一行 ----------
   `flex:1 1 0` 让六张卡等分之后每张只剩 165px，而数字是 46px 的等宽衬线 ——
   「1400–1550」直接折行、还被卡片裁掉。
   数字卡到底几列合适，取决于这一排有几张，flex 判不出来 —— 交给栅格：
   默认 auto-fit（215px 起，3~4 张自然铺满），六张的那一排显式标 3 列。
   栅格的行高天然等高，⑬里那些 height:100% 的补丁可以退场了。 */
.stat-row{ display:grid; grid-template-columns:repeat(auto-fit,minmax(215px,1fr)); gap:24px; align-items:stretch; }
.stat-row--six{ grid-template-columns:repeat(3,minmax(0,1fr)); }
.stat-row > .card{ height:100%; }
@media (max-width:720px){
  /* 原来写死两列，390 宽下每张只剩 147px、内宽 103，
     而「1400–1550」在 32px 下就要 158px —— 数字直接冲出纸外。
     交给 auto-fit：窄到放不下就自己降成一列。 */
  .stat-row--six{ grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); }
}

/* ⑰ 补：数字不许折行。六张那排里「1400–1550」被拆成两行，其他都一行 ——
   一排数字里混着"一行/两行"，比字号小更难看。数字与单位整体不换行，
   六列的那排单独降一档字号去适配。 */
.stat__val{ white-space:nowrap; }
.stat-row--six .stat__val{ font-size:34px; }
.stat-row--six .stat__val small{ font-size:.46em; }

/* ---------- ⑱ 卡片只能有一种材质：虚线框退场 ----------
   案面上原来并存两套卡片外壳：
     · .card        —— 宣纸：毛边（clip-path）、帘纹、投影、微微歪着放；
     · .card--flat  —— 透明底 + 1px 虚线框 + 无投影、无毛边、不歪。
   两套放在同一屏里像没对齐的两个图层：虚线在成品语境里读作"占位/待定"，
   而旁边就是一张有毛边的纸，观者会以为这块还没做完。
   更实际的问题：.card--flat 一律把文字设成浅色（案面用色），
   可它里面不少子元素还带着浅色区的 var(--ink-soft) 行内样式 ——
   craft 首屏那张「24 道工序」注解就成了**深字压深底**，等于隐形。
   一张纸就够了。批注与正文的差别交给一道朱线 —— 这套语汇
   .quote--classic 已经在用了（纸是内容，朱线是批注），不必再造第二种外壳。 */
.card--flat{
  background:var(--sheet); color:var(--ink);
  /* 批注纸都是"宽而扁"的条：正卡那六套毛边按百分比定义，横向量是 1%、
     竖向也是 1% —— 落到 1069×130 的条上就是左右各抖 10px、上下只抖 1.3px，
     边角被啃掉一大块，看着像缺角。单独一套：横向 0.3%、竖向 2%。 */
  clip-path:polygon(
    0.35% 1.8%, 9% .5%, 19% 2.0%, 30% .6%, 41% 2.2%, 52% .4%, 63% 1.9%, 74% .6%, 85% 2.1%, 94% .5%, 99.65% 1.6%,
    99.4% 13%, 99.78% 26%, 99.3% 39%, 99.7% 52%, 99.35% 65%, 99.75% 78%, 99.4% 90%,
    99.6% 98.4%, 93% 99.6%, 82% 98.4%, 71% 99.7%, 60% 98.5%, 49% 99.6%, 38% 98.4%, 27% 99.6%, 16% 98.5%, 6% 99.7%,
    0.5% 98.6%, 0.3% 86%, 0.78% 72%, 0.35% 58%, 0.8% 44%, 0.3% 30%, 0.7% 16%, 0.34% 6%);
  rotate:.24deg;
  border:0;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.85);
  /* 这张纸多数不在 .grid 里，拿不到祖先的 drop-shadow；
     而 box-shadow 会被自己的 clip-path 裁掉 —— 只能自己在 filter 里投。
     filter 在 clip-path 之后生效，影子会沿着毛边轮廓走。 */
  filter:drop-shadow(0 10px 17px rgba(0,0,0,.42));
}
/* 朱线不能画在 border 上：毛边 clip-path 会把左缘切掉 5~10px，
   4px 的 border-left 正好整根被裁掉（实测第一条就是这么消失的）。
   改成纸面内侧的一道短线 —— 手写在纸边上的批注记号，长短不齐也合理。 */
.card--flat::before{
  content:""; position:absolute; left:13px; top:15px; bottom:15px; width:3px;
  background:var(--seal); opacity:.92; pointer-events:none; z-index:1;
}
.card--flat > *{ position:relative; z-index:1; }
.card--flat::after{ display:block; }              /* 帘纹回来 */
.card--flat:hover{ transform:translateY(-3px); }
.card--flat .card__label{ color:var(--seal); }
.card--flat .src{ color:var(--ink-faint); border-top-color:rgba(36,42,46,.13); }

/* ---------- ⑲ 等高的代价：短内容的纸不该被拉成一整页 ----------
   `.grid > .col > .card{height:100%}` 让同一行的纸齐底，多数情况是对的 ——
   三张数字卡、三张并列的说明卡，齐底才像一排。但有两处差别太大：
   「1050 年」旁边那张一句半的注解被拉到 372px（下半张全空），
   「你的这一程」旁边那张被拉到 712px（空了 437px）。
   空到这种程度，纸就不再是纸，是一个边框。
   所以给这两类纸一个"按内容收边"的口子：只收边、不居中 ——
   顶边仍然对齐（标题还在一条水平线上），底边参差。
   案面上摊着大小不一的纸本来就是常态，毛边也在，不齐反而正常。 */
.grid > .col > .card--hug{
  height:auto; align-self:flex-start;
}

/* ---------- ⑳ 断行：末行不留孤字 ----------
   这些纸都很窄，一列只有十几个字，行末经常甩出半个词：
   「距泾县县 / 城约 17 公里」「青弋江 69.37 km、徽水河 54.91 / km」。
   text-wrap:pretty 会让浏览器在算行的时候避开"末行只剩一点"的断法，
   代价只是排版多算一遍 —— 静态页面无所谓。 */
.card p, .card li, .card dd, .card td,
.quote p, .quote cite, .section__head .sub{ text-wrap:pretty; }

/* ---------- ㉑ 窄屏：⑯ 把 basis 改成 0，换行就再也不会发生了 ----------
   `.grid{flex-wrap:wrap}` 什么时候换行，是由 flex item 的 **basis** 决定的：
   原来 `.col{flex:1 1 320px}` / `.col--wide{flex:2 1 480px}`，
   两列基础宽 320+480 > 容器就换行。⑯ 为了让宽窄栏严格按比例铺满，
   把 basis 全改成 0 —— 1280 下是对的，但浏览器从此认为"这一行放得下"，
   窄屏两列永远并排挤在一起：390 宽实测 页面 scrollWidth 482，
   「2.20mm @30°SR」直接冲出纸外。
   flex 只管 1280 的事，窄屏另开一条：一列到底。 */
@media (max-width:900px){
  /* 720 不够：768 的平板仍然两列并排（ch2 的「mm @30°SR」还超 22px，
     src 的引文纸被压到 145px 宽、每行只剩两个字）。
     正好正文列宽本来也不该超过 700px —— 900 以下一律一列到底。 */
  .col, .col--wide, .col--narrow{ flex:1 1 100%; }
  .grid--four > .col{ flex:1 1 235px; }       /* 四数字仍可两列，235 兜底 */
  .grid > .col > .card{ height:auto; }        /* 单列了就不必为了齐底拉高 */
}
@media (max-width:720px){
  /* 数字卡：46px 的数字 + small 单位在窄纸里放不下，降一档 */
  .stat__val, .stat-row--six .stat__val{ font-size:32px; }
  .stat__val small{ font-size:.56em; }
}

/* ---------- ㉒ 换章：一张纸从下往上抹过去 ----------
   Awwwards 上那批"单一叙事"作品（Inkwell 的 cinematic framework、
   Siena Film Foundation 的 filmic structure）把章节切换当镜头切，不当页面跳。
   本案所有内容都摊在一张案面的纸上，最贴切的镜头就是：
   **把一张新纸从案面下缘推上来，压过旧的，再抽走，露出新的一页。**
   —— 这不是装饰。原来的换章是硬切 + scrollTo(0,0)，
      观者会同时看到"旧页消失、滚动条跳回顶部"两件事，出戏。

   实现要点：
   · 纸是 fixed 覆盖层，靠 transform 移动（合成层，不触发布局）；
   · 「盖满」那一刻才渲染新章并跳回顶部 —— 硬切发生在观者看不见的时候；
   · 用两段 transition（is-closing 推入 / is-open 抽出），中间不能合并成一段，
     否则纸会匀速滑过整个屏幕，像幻灯片而不是"推上来再抽走"；
   · 纸的前缘要有一道阴影，它才像一张有厚度的纸，而不是一块色板。 */
.page-veil{
  position:fixed; inset:-6px 0; z-index:140; pointer-events:none;
  transform:translate3d(0,101%,0);
  background-color:var(--sheet);
  background-image:
    repeating-linear-gradient(180deg, rgba(109,122,112,.055) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg,  rgba(109,122,112,.045) 0 1px, transparent 1px 27px);
  /* 推入时前缘在上边，阴影要朝上；抽出时前缘在下边，阴影改朝下（见 .is-open） */
  box-shadow:0 -18px 46px rgba(0,0,0,.52);
  will-change:transform;
}
/* 纸过处留一枚印：既是品牌，也告诉观者"这一下是换章，不是加载" */
.page-veil::after{
  content:"纸";
  position:absolute; left:50%; top:46%;
  width:64px; height:64px; margin:-32px 0 0 -32px;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-serif); font-size:31px; line-height:1;
  color:var(--paper); background:var(--seal); border-radius:2px;
  opacity:0;
}
.page-veil.is-closing{
  transform:translate3d(0,0,0);
  transition:transform .30s cubic-bezier(.55,.02,.42,1);
}
.page-veil.is-closing::after{ opacity:.92; transition:opacity .24s .16s; }
.page-veil.is-open{
  transform:translate3d(0,-101%,0);
  box-shadow:0 18px 46px rgba(0,0,0,.52);
  transition:transform .36s cubic-bezier(.52,0,.36,1), box-shadow .12s linear;
}
.page-veil.is-open::after{ opacity:0; transition:opacity .16s; }

@media (prefers-reduced-motion: reduce){
  .page-veil{ display:none; }
}

/* ============================================================
   ㉔ 版心 · 页边书耳（mrail）
   ------------------------------------------------------------
   线装书的书口上刻着鱼尾与书名，翻到哪一页，眼睛都不离书脊。
   这一页动辄五六千像素，顶栏只能说"还在第二章"，
   "哪一段"没有地方回答 —— 于是在左侧页边放一条竖排的书耳。

   三条约束决定了它的形状：
     · 位置：右侧容易和滚动条打架，左侧是书口的位置；
     · 竖排：横排小字放页边会被读成"批注"，竖排才读成"书口"；
     · 只显示当前一节：四到六字的竖排标题并排会互相压字，
       所以平时只有刻度，只有当前那一节浮出名字。
   只在正文页出现：长卷（3D）没有 .section，导航自动收起。
   ============================================================ */
.mrail{
  display:none;
  position:fixed; left:12px; top:50%; z-index:40;
  transform:translate3d(0,-50%,0);
  pointer-events:none;
}
.mrail ol{
  position:relative;
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column; align-items:flex-start; gap:13px;
}
/* 书口那道竖线：刻度是从它身上长出来的鱼尾，不是几根浮空的短横 */
.mrail ol::before{
  content:""; position:absolute; left:0; top:-6px; bottom:-6px; width:1px;
  background:rgba(236,228,212,.15);
}
.mrail li{ position:relative; }
.mrail button{
  display:block; position:relative;
  width:26px; height:1px; padding:0; border:0; background:none;
  cursor:pointer; pointer-events:auto;
  /* 点击区只有 1px 高，必须撑开一个看不见的命中区 */
  outline-offset:6px;
}
.mrail button::before{
  content:""; position:absolute; left:-10px; top:-8px; right:-8px; bottom:-8px;
}
.mrail button i{
  display:block; height:1px; width:14px;
  background:rgba(236,228,212,.42);
  transition:width .30s var(--ease), background-color .30s var(--ease);
}
.mrail li.is-on button i{ width:26px; background:var(--seal); }
.mrail button em{
  position:absolute; left:32px; top:50%;
  writing-mode:vertical-rl; text-orientation:mixed;
  font-style:normal; font-size:11px; line-height:1;
  letter-spacing:.22em; white-space:nowrap;
  color:rgba(236,228,212,.66);
  opacity:0;
  transform:translate3d(-4px,-50%,0);
  transition:opacity .28s var(--ease), transform .28s var(--ease);
}
.mrail li.is-on button em{ opacity:1; transform:translate3d(0,-50%,0); }
.mrail button:hover i{ background:var(--seal-soft); width:22px; }
.mrail button:focus-visible i{ background:var(--seal-soft); }

/* 1280 时内容区两侧各留 50px，竖排 11px 的字刚好塞得下（标签右缘 ≈ 45px < 78px）。
   再窄就没有页边可以写字了。 */
@media (min-width:1280px){
  .mrail.is-on{ display:block; }
}
@media (min-width:1280px) and (prefers-reduced-motion: reduce){
  .mrail button em{ transition:none; }
}

/* ============================================================
   ㉕ 页末翻页（pageturn）
   ------------------------------------------------------------
   读完一章没有出口：六千像素滚到底只有一句免责声明，
   想继续读得滚回顶栏 —— 书不会这样，书在页脚翻页。

   形状上刻意不做成两个按钮：按钮是"操作"，翻页是"位置"。
   所以这里是一行小字（上一页/下一页）+ 一行大字（哪一章），
   指向哪边就向右/左对齐，和书页的左右页对应。
   分隔线不用自己加 —— 页脚那条 border-top 就是这条线。
   ============================================================ */
.pageturn{
  max-width:var(--maxw); margin:0 auto;
  padding:44px 28px 34px;
  display:flex; align-items:flex-start; gap:26px;
}
.pageturn a{
  flex:1 1 0; min-width:0;
  display:block; padding:14px 0 4px;
  text-decoration:none; color:inherit;
  outline-offset:4px;
}
.pageturn__pad{ flex:1 1 0; }
.pageturn__k{
  display:block; font-size:11px; letter-spacing:.3em;
  /* --on-desk-faint 在案面上几乎看不见；翻页的"上一页/下一页"是路标，不是脚注 */
  color:rgba(236,228,212,.50);
  transition:color .3s var(--ease);
}
.pageturn b{
  display:block; margin-top:9px;
  font-family:var(--font-serif); font-weight:400;
  font-size:clamp(19px, 2.1vw, 26px); line-height:1.32;
  color:var(--on-desk);
  transition:color .3s var(--ease), transform .3s var(--ease);
}
.pageturn b em{
  font-style:normal; font-size:.58em; letter-spacing:.22em;
  color:var(--seal-soft); margin-right:.55em; vertical-align:.12em;
}
.pageturn__n{ text-align:right; }
.pageturn a:hover .pageturn__k{ color:var(--seal-soft); }
.pageturn a:hover b{ color:var(--seal-soft); }
.pageturn__p:hover b{ transform:translate3d(-5px,0,0); }
.pageturn__n:hover b{ transform:translate3d(5px,0,0); }

@media (max-width:640px){
  .pageturn{ padding:30px 18px 26px; gap:16px; }
  .pageturn b{ font-size:17px; }
  .pageturn__k{ letter-spacing:.2em; font-size:10.5px; }
}

/* ============================================================
   ㉖ 寿命尺（.lifer）
   ------------------------------------------------------------
   纸是浅色的，尺是墨色的，所以这一块必须放在纸卡里 ——
   它是"图表"，不是"照片"。
   三个决定它读得懂的结构：
     · 三列固定：[名字 88px][轨道 1fr][数值 120px]。
       刻度与游标都按"轨道"那一列定位（见 .lifer__over），
       否则百分比对不上，游标会偏到名字上；
     · 数值列要给够宽度，1050 这个数字不能换行；
     · 失效的纸不做删除线（像表格），只压到 22% 不透明度 + 变灰：
       它还在那儿，只是不在了 —— 这才是这条轴想说的话。
   ============================================================ */
.lifer{ --lifer-l:88px; --lifer-r:120px; }

.lifer__head{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:14px; margin-bottom:20px; flex-wrap:wrap;
}
.lifer__now{ display:flex; align-items:baseline; gap:5px; }
.lifer__now b{
  font-family:var(--font-num); font-weight:400; line-height:1;
  font-size:46px; color:var(--seal);
  font-variant-numeric:tabular-nums; letter-spacing:.01em;
}
.lifer__now i{
  font-style:normal; font-size:13px; letter-spacing:.22em; color:var(--tan);
}
.lifer__state{ font-size:13px; color:var(--ink-soft); letter-spacing:.06em; }
.lifer__state.is-alert{ color:var(--seal); }

.lifer__plot{ position:relative; }
.lifer__over{
  position:absolute; left:var(--lifer-l); right:var(--lifer-r);
  top:0; bottom:0; pointer-events:none;
}
.lifer__axis{ position:absolute; left:0; right:0; top:0; height:16px; }
.lifer__tick{
  position:absolute; top:0; transform:translateX(-50%);
  font-size:11px; letter-spacing:.06em; color:var(--ink-faint);
  white-space:nowrap;
}
.lifer__rows{ position:relative; padding-top:20px; }
.lifer__row{ display:flex; align-items:center; height:36px; }
.lifer__name{
  flex:0 0 var(--lifer-l); font-size:13px; color:var(--ink);
  letter-spacing:.04em;
}
.lifer__track{
  position:relative; flex:1 1 auto; height:10px; min-width:0;
  background:linear-gradient(to bottom, transparent 44%, rgba(36,42,46,.10) 44%, rgba(36,42,46,.10) 56%, transparent 56%);
}
.lifer__bar{
  position:absolute; left:0; top:50%; height:6px; margin-top:-3px;
  border-radius:2px;
  transition:opacity .3s var(--ease);
}
.lifer__end{
  position:absolute; top:50%; width:9px; height:9px; margin:-4.5px 0 0 -4.5px;
  border-radius:50%;
  transition:opacity .3s var(--ease);
}
.lifer__val{
  flex:0 0 var(--lifer-r); text-align:right;
  font-size:12px; color:var(--ink-soft); white-space:nowrap;
  padding-left:12px; letter-spacing:.02em;
  transition:color .3s var(--ease);
}
.lifer__row.is-gone .lifer__bar,
.lifer__row.is-gone .lifer__end{ opacity:.18; }
.lifer__row.is-gone .lifer__name,
.lifer__row.is-gone .lifer__val{ color:var(--ink-faint); opacity:.55; }

.lifer__cur{
  /* 往下多伸 32px，正好够到滑杆那条线 ——
     游标和拇指必须是一根东西，中间断开就变成两个指示器 */
  position:absolute; top:14px; bottom:-32px; width:1px;
  background:var(--seal); opacity:.5;
  transition:left .06s linear;
}

.lifer__range{
  /* 滑杆必须和轨道同宽同位：滑到中间 = 轴上的中间。
     左右各让出与 [名字][数值] 相同的宽，拇指才落在刻度正上方。 */
  /* width:auto 对 <input type=range> 无效 —— 它有自己的默认宽（≈129px），
     必须显式算宽度，否则滑杆会缩成一小段，拇指落在轨道之外。 */
  display:block;
  width:calc(100% - var(--lifer-l) - var(--lifer-r));
  margin:22px 0 0 var(--lifer-l);
  -webkit-appearance:none; appearance:none;
  background:transparent; height:20px;
  accent-color:var(--seal); cursor:ew-resize;
}
/* 自己画拇指：原生拇指又大又灰，和朱色游标不是一套语言；
   12px 的小圆也让"滑杆行程"更接近轨道长度（误差≈半个拇指） */
.lifer__range::-webkit-slider-runnable-track{
  height:2px; background:rgba(36,42,46,.16); border-radius:1px;
}
.lifer__range::-webkit-slider-thumb{
  -webkit-appearance:none; width:12px; height:12px; margin-top:-5px;
  border:0; border-radius:50%; background:var(--seal); cursor:ew-resize;
}
.lifer__range::-moz-range-track{ height:2px; background:rgba(36,42,46,.16); }
.lifer__range::-moz-range-thumb{
  width:12px; height:12px; border:0; border-radius:50%; background:var(--seal);
}
.lifer__hint{
  font-size:11.5px; color:var(--ink-faint);
  margin:10px 0 0; letter-spacing:.05em;
}

@media (max-width:640px){
  .lifer{ --lifer-l:62px; --lifer-r:78px; }
  .lifer__now b{ font-size:34px; }
  .lifer__name{ font-size:11.5px; }
  .lifer__val{ font-size:10.5px; padding-left:8px; }
  .lifer__tick{ font-size:9.5px; }
  .lifer__row{ height:32px; }
}
@media (prefers-reduced-motion: reduce){
  .lifer__cur, .lifer__bar, .lifer__end, .lifer__val{ transition:none; }
}

/* ============================================================
   ㉗ 一百零八 · 点阵（.dots）
   ------------------------------------------------------------
   108 个点，点亮的才有名字。缺口本身就是内容 ——
   所以空点不能画得太淡（看不见就等于没画），
   也不能画得太重（会和真信息抢注意力）：
   1px 描边、无填充，摊在纸上刚好是一条"没被说完的话"。
   ============================================================ */
.dots__head{
  display:flex; align-items:baseline; gap:14px; flex-wrap:wrap;
  margin-bottom:18px;
}
.dots__n b{
  font-family:var(--font-num); font-weight:400; font-size:40px; line-height:1;
  color:var(--seal); font-variant-numeric:tabular-nums;
}
.dots__n i{ font-style:normal; font-size:13px; color:var(--ink-faint); margin-left:5px; }
.dots__say{ font-size:13.5px; color:var(--ink-soft); letter-spacing:.04em; }

/* 27 列 × 4 行 = 108（刚好整除，不留半行）；格子宽度封顶，
   否则 18 列在大屏上会被拉成 45px 的"圆按钮"，一排空圈看着像占位符。 */
.dots__grid{
  display:grid; grid-template-columns:repeat(27, minmax(0, 1fr));
  gap:8px; max-width:820px;
}
.dots__dot{
  width:100%; aspect-ratio:1/1; border-radius:50%;
  border:1px solid rgba(36,42,46,.30); background:transparent;
  box-sizing:border-box;
  transition:background .3s, border-color .3s;
}
.dots__dot.is-known{ border-color:transparent; }

.dots__legend{
  display:flex; flex-wrap:wrap; gap:8px 20px;
  margin-top:16px; font-size:12px; color:var(--ink-soft);
}
.dots__key{ display:inline-flex; align-items:center; gap:6px; }
.dots__key i{ width:8px; height:8px; border-radius:50%; display:inline-block; }
.dots__key b{ font-weight:400; color:var(--ink-faint); font-variant-numeric:tabular-nums; }

.dots__note{
  font-size:12px; line-height:1.9; color:var(--ink-faint);
  margin:16px 0 10px;
}
@media (max-width:640px){
  .dots__grid{ grid-template-columns:repeat(12, minmax(0, 1fr)); gap:5px; }
  .dots__n b{ font-size:32px; }
}

/* ============================================================
   ㉘ 首屏的钩子与去处（.cover__hook / .cover__act）
   ------------------------------------------------------------
   原来首屏读完只有"标题 + 一段 lede + 一行元信息"，
   既不知道该不该信，也不知道下一步点哪儿 ——
   评委/读者的第一屏不该是一次"介绍"，该是一次"选择"。
   钩子只放两个已核实的数（1050 年 / 3~4 个月），不放形容词。
   ============================================================ */
.cover__hook{
  margin:22px 0 0; max-width:34em;
  font-size:14.5px; line-height:1.9;
  color:var(--on-desk); opacity:.86;
  padding-left:14px; border-left:2px solid var(--seal);
}
.cover__act{
  display:flex; flex-wrap:wrap; gap:12px; margin-top:26px;
}
.cover__act .btn{
  display:inline-flex; align-items:baseline; gap:8px;
  text-decoration:none;
}
.cover__act .btn span{
  font-size:11px; letter-spacing:.08em; opacity:.62;
}
@media (max-width:900px){
  .cover__hook{ font-size:13.5px; margin-top:18px; }
  .cover__act{ margin-top:20px; }
}

/* ============================================================
   ㉙ 水一节的注解行（.water-note）
   ------------------------------------------------------------
   两条引文各占一列，注解与三个数字退回整版宽度单独成行 ——
   所以这里要自己带上下间距，不能再靠 .col 的内部流。
   ============================================================ */
.water-note{
  margin:24px 0 0; max-width:62em;
  font-size:13px; line-height:1.95;
  color:var(--on-desk); opacity:.72;      /* 这行落在案面上，不在纸上 —— 用纸上的墨色会看不见 */
}
.water-note + .stat-row{ margin-top:20px; }
@media (max-width:900px){
  .water-note{ margin-top:18px; font-size:12.5px; }
}

/* ============================================================
   ㉚ 工序长卷（.roll）
   ------------------------------------------------------------
   两条线并行：皮料线、草料线。段宽按"月数 / 总月数"定位，
   所以横轴是真实的时间，不是六个等宽的格子 ——
   等宽的格子会让人以为"配浆"和"摊晒一年"一样长。
   游标不是滑杆画出来的，是直接在板上按下、拖动：
   滑杆的拇指有宽度，行程比轨道短一截，两端的月份会落不准。
   ============================================================ */
.roll{ margin-top:4px; }
.roll__card{ padding:28px 30px 26px; }
@media (max-width:640px){
  /* 一个月在窄屏只有十来个像素，卡片内边距省下的都是时间轴 */
  .roll__card{ padding:22px 16px 20px; }
}
.roll__head{
  display:flex; align-items:baseline; gap:16px; flex-wrap:wrap;
  margin-bottom:18px;
}
.roll__m b{
  font-family:var(--font-num); font-weight:400; font-size:40px; line-height:1;
  color:var(--seal); font-variant-numeric:tabular-nums;
}
.roll__m i{ font-style:normal; font-size:13px; color:var(--ink-faint); margin-left:6px; }
.roll__now{
  flex:1; min-width:11em;
  font-size:13px; line-height:1.85; color:var(--ink-soft);
}

.roll__board{
  display:flex; gap:10px; position:relative;
  padding:2px 0 6px; outline:none;
  touch-action:none; cursor:ew-resize;
  -webkit-user-select:none; user-select:none;
}
/* 焦点框必须看得见，但不能画成一条虚线 —— 虚线在长卷上会被误读成刻度线 */
.roll__board:focus-visible{ outline:1px solid rgba(156,74,60,.34); outline-offset:8px; }
.roll__labels{ flex:0 0 44px; }
.roll__lname{
  height:26px; margin-bottom:10px; display:flex; align-items:center;
  font-size:11.5px; letter-spacing:.12em; color:var(--ink-faint);
}
.roll__lanes{ position:relative; flex:1; min-width:0; }
.roll__lane{
  position:relative; height:26px; margin-bottom:10px;
  background:rgba(36,42,46,.045); border-radius:2px;
}
/* 段底色的"淡"与填充的"实"是两层：到过的段留着颜色，
   正在走的段才亮起来 —— 读得出"已经过去多久" */
.roll__seg{
  position:absolute; top:0; bottom:0; border-radius:2px; overflow:hidden;
  box-shadow:1px 0 0 rgba(255,255,255,.7);   /* 一像素缝：只剩十来个像素的月也要分得开 */
}
.roll__ghost{
  position:absolute; left:0; top:0; bottom:0; right:0;
  background:var(--c); opacity:.2;
}
.roll__fill{
  position:absolute; left:0; top:0; bottom:0; width:0;
  background:var(--c); opacity:.9;
}
.roll__seg.is-done .roll__fill{ opacity:.42; }
.roll__seg.is-live .roll__fill{ opacity:.92; }
.roll__segname{
  position:absolute; left:8px; top:50%; transform:translateY(-50%);
  font-size:11.5px; letter-spacing:.05em; white-space:nowrap;
  color:var(--ink); opacity:.55;
}
/* 段被填满之后底下的颜色是深的，名字要翻成纸白才读得出 */
.roll__seg.is-live .roll__segname{ opacity:1; color:#f8f3e9; }
.roll__seg.is-done .roll__segname{ opacity:.86; color:#f8f3e9; }

.roll__cur{
  position:absolute; top:-4px; bottom:-16px; width:1px;
  background:var(--seal); pointer-events:none;
}
.roll__cur::before{
  content:''; position:absolute; top:-3px; left:-3px;
  width:7px; height:7px; border-radius:50%; background:var(--seal);
}

.roll__axis{ position:relative; height:18px; margin:0 0 0 54px; }
.roll__tick{
  position:absolute; top:0; transform:translateX(-50%);
  font-size:10.5px; color:var(--ink-faint);
  font-variant-numeric:tabular-nums; white-space:nowrap;
}

.roll__panel{
  margin-top:18px; padding-top:16px;
  border-top:1px solid rgba(36,42,46,.13);
}
.roll__judge{
  margin:0; font-size:13.5px; line-height:1.95; color:var(--ink);
  min-height:2.7em;
}
.roll__judge b{
  font-weight:400; color:var(--seal); letter-spacing:.06em;
  margin-right:8px;
}
.roll__judge span.is-off{ color:var(--ink-faint); }
.roll__ops{
  list-style:none; margin:12px 0 0; padding:0;
  display:flex; flex-wrap:wrap; gap:7px;
}
.roll__ops li{
  font-size:12px; letter-spacing:.04em; padding:3px 9px 3px 8px;
  color:var(--ink-soft); background:rgba(36,42,46,.055);
  border-left:2px solid var(--c); border-radius:2px;
}
.roll__note{
  margin:16px 0 0; font-size:12px; line-height:1.9; color:var(--ink-faint);
}
@media (max-width:640px){
  /* 读数与描述在窄屏必须上下排：横排时"12 月"旁边的字会被挤成一条细柱 */
  .roll__head{ flex-direction:column; align-items:flex-start; gap:6px; }
  .roll__now{ min-width:0; font-size:12.5px; }
  .roll__m b{ font-size:30px; }
  .roll__labels{ flex:0 0 34px; }
  .roll__lname{
    font-size:10px; letter-spacing:0; white-space:nowrap;
    height:22px; margin-bottom:8px;
  }
  .roll__lane{ height:22px; margin-bottom:8px; }
  .roll__segname{ display:none; }
  .roll__axis{ margin-left:42px; }
  .roll__hint{ font-size:10.5px; }
  .roll__judge{ font-size:13px; }
  .roll__ops li{ font-size:11.5px; padding:3px 7px 3px 6px; }
}
.roll__hint{
  margin:10px 0 0 54px; font-size:11px; letter-spacing:.08em;
  color:var(--ink-faint); opacity:.85;
}
@media (max-width:640px){ .roll__hint{ margin-left:38px; } }


/* 两处交互之间的路：只在"走到那一段"时才出现，平时不占位置 */
.roll__jump{
  display:inline-block; margin-top:16px;
  font-size:12.5px; letter-spacing:.04em; text-decoration:none;
  color:var(--seal); border-bottom:1px solid rgba(156,74,60,.34);
  padding-bottom:2px;
}
.roll__jump:hover{ border-bottom-color:var(--seal); }
/* 回声：观者在打浆台定过的数，走到配浆/抄造时接回来说一句。
   左侧那道朱线是"引用"的意思，不是警示 —— 所以只用 2px、低透明度。 */
.roll__echo{
  margin:16px 0 0; padding:2px 0 2px 12px;
  border-left:2px solid rgba(156,74,60,.28);
  font-size:12.5px; line-height:1.95; color:var(--ink-soft);
}
.roll__echo b{ color:var(--seal); font-weight:500; }
/* 今页那句"去试一张" */
.xlink{
  margin:0; font-size:13.5px; line-height:2; color:var(--ink-soft);
}
.xlink a{
  color:var(--seal); text-decoration:none;
  border-bottom:1px solid rgba(156,74,60,.34); padding-bottom:1px;
}
.xlink a:hover{ border-bottom-color:var(--seal); }

/* 导言页「四处可以上手」：一份清单，不是四张卡 —— 它是指路，不该比正文更重。
   朱线由 .card--flat 的 ::before 给，所以左边距要让开 13+3px。 */
.hands{ padding:4px 28px 4px 30px; }
.hands__row{
  display:grid; grid-template-columns:42px minmax(0,1fr) 22px; gap:0 12px;
  align-items:start; padding:18px 0 17px;
  text-decoration:none; color:inherit;
  border-top:1px solid var(--line-soft);
}
.hands__row:first-child{ border-top:0; padding-top:12px; }
.hands__row:last-child{ padding-bottom:8px; }
.hands__no{ font-size:11px; letter-spacing:.16em; color:var(--tan); padding-top:3px; }
.hands__body b{ display:block; font-size:16px; font-weight:400; line-height:1.6; color:var(--ink); }
.hands__body i{
  display:block; margin-top:4px; font-style:normal;
  font-size:11.5px; letter-spacing:.14em; color:var(--seal);
}
.hands__body em{
  display:block; margin-top:8px; font-style:normal;
  font-size:13px; line-height:1.95; color:var(--ink-soft);
}
.hands__go{
  font-size:15px; line-height:1.6; color:var(--seal); opacity:.6;
  transition:opacity .18s ease, transform .18s ease;
}
.hands__row:hover .hands__go{ opacity:1; transform:translateX(3px); }
.hands__row:hover .hands__body b{ color:var(--seal); }
/* 焦点框用实线：虚线落在清单的横线上会被当成分隔线 */
.hands__row:focus-visible{ outline:1px solid rgba(156,74,60,.45); outline-offset:2px; }
@media (max-width:640px){
  .hands{ padding:2px 16px 2px 24px; }
  .hands__row{ grid-template-columns:32px minmax(0,1fr) 16px; gap:0 8px; padding:16px 0; }
  .hands__body b{ font-size:15px; }
  .hands__body i{ font-size:11px; letter-spacing:.1em; }
  .hands__body em{ font-size:12.5px; line-height:1.9; }
}
@media (max-width:640px){
  .roll__jump{ font-size:12px; }
  .xlink{ font-size:13px; }
}

/* ---------- 长卷的加载态 ----------
   长卷要 14.5 MB 的贴图与模型，取完之后还要一两秒装配。
   这段时间原来是纯空白 —— 读者不知道是慢、是坏、还是自己点错了。
   底色与长卷的 #0e1113 一致：不是"盖一层遮罩"，是"山谷还没画出来"。
   层级 40 < 顶栏的 50，所以导航仍然可用，随时能退出去。 */
.loadveil{
  position:fixed; inset:0; z-index:40;
  display:flex; align-items:center; justify-content:center;
  background:#0e1113;
}
.loadveil__inner{ text-align:center; padding:0 24px; max-width:520px; }
.loadveil__k{
  margin:0 0 14px; font-size:11px; letter-spacing:.34em;
  color:rgba(214,205,186,.45);
}
.loadveil__t{ margin:0; font-size:15px; line-height:2; color:rgba(238,231,218,.82); }
.loadveil__bar{
  display:block; width:150px; height:1px; margin:22px auto 0;
  background:rgba(238,231,218,.14); overflow:hidden;
}
.loadveil__bar i{
  display:block; width:38%; height:100%; background:rgba(196,120,96,.9);
  animation:loadsweep 1.15s ease-in-out infinite;
}
@keyframes loadsweep{
  0%{ transform:translateX(-100%); }
  100%{ transform:translateX(365%); }
}
@media (prefers-reduced-motion: reduce){
  .loadveil__bar i{ animation:none; width:100%; opacity:.45; }
}

/* ---------- ㉕ 口播提示（js/engine/voice.js） ----------
   只在一件事上说一句：本机没装中文语音，所以播放只有画面与字幕。
   用固定的深色小条压在底部 —— 它是"系统状态"，不是作品内容，
   所以不套宣纸外壳（那张皮是给内容用的）。 */
.voicehint{
  position:fixed; left:50%; bottom:26px; z-index:45;
  transform:translate(-50%,14px);
  max-width:min(86vw,480px);
  padding:11px 18px;
  border-radius:2px;
  background:rgba(26,22,18,.94);
  color:rgba(238,231,218,.9);
  font-size:13px; line-height:1.75; letter-spacing:.02em;
  text-align:center;
  box-shadow:0 10px 30px rgba(0,0,0,.32);
  opacity:0; pointer-events:none;
  transition:opacity .32s ease, transform .32s ease;
}
.voicehint.is-on{ opacity:1; transform:translate(-50%,0); }
@media (prefers-reduced-motion: reduce){
  .voicehint{ transition:opacity .2s linear; transform:translate(-50%,0); }
}

