/* ==========================================================================
 * VANSYE 博客 · 样式
 *
 * 这个文件的**规格来源是 `VANSYE-设计方案-v2.md`**，不是我的判断。
 * 里面每个颜色、字号、透明度都能在那份文档里找到对应的一行。
 * 改任何一个值之前先看那份文档第 9 节（禁止事项 12 条）和第 10 节（验收清单）。
 *
 * 主体原样来自 `原型/index.html` 的 <style> 块——那是按规格实现并反复调过的版本，
 * 重写一遍只会引入偏差。我只改两处：
 *   1. 字体从 Google Fonts 换成自托管（见下）
 *   2. 文末新增文章页（纸面）样式——规格 §6.1，原型里没有
 * ========================================================================== */

/* ── 字体：自托管，不用 Google Fonts ─────────────────────────────────────
 *
 * **原型是从 fonts.googleapis.com 加载的，那在大陆访问不了。**
 * 后果不是「慢一点」，是访客全部落到 fallback——而 Libre Caslon Display
 * 就是整个视觉身份（站名巨字、英文大标题），掉到 Georgia 就不是这个设计了。
 *
 * 两个拉丁字体是 OFL 授权，可自托管，加起来 62 KB。
 *
 * 中文**故意不自托管**：Noto Serif SC 全 CJK 十几 MB；做子集化又会在
 * 「出现没被子集进去的字」时静默掉字——那种 bug 只在特定汉字上出现，平时测不到。
 * 所以走系统栈（规格 §2 的 fallback 链本来就列了 STSong / SimSun），
 * 而规格自己留了「可整体换回思源黑体」这个开关。
 * ------------------------------------------------------------------------ */
@font-face{
  font-family:"Libre Caslon Display";
  src:url("/fonts/libre-caslon-display-400.woff2") format("woff2");
  font-weight:400; font-style:normal;
  /* swap：字体没到之前先用 fallback 显示。
     内容站不能为了字体让首屏文字消失几百毫秒 */
  font-display:swap;
}
@font-face{
  font-family:"Courier Prime";
  src:url("/fonts/courier-prime-400.woff2") format("woff2");
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Courier Prime";
  src:url("/fonts/courier-prime-700.woff2") format("woff2");
  font-weight:700; font-style:normal; font-display:swap;
}

/* ============ tokens（规格 v2 §1）============ */
:root{
  --purple:#7929FF;
  --paper:#F4F2F8;
  --white:#FFFFFF;
  /* 小号标签（≤0.8rem）的辅助色。**这个数是量出来的，不是挑出来的**：
     白色叠在 #7929FF 上，65% → 对比度 3.26，78% → 4.11，83% → 4.48，
     都不到 WCAG AA 小字要求的 4.5；阈值在 83.5%。取 84%（4.56，留一点余量）。
     规格 §1 从 65% 升到 78% 的方向是对的，只是没升够。
     **名字里带数字，就必须和值一致**——不然读规则的人会算出错的对比度 */
  --white-84:rgba(255,255,255,.84);
  --white-65:rgba(255,255,255,.65);
  --white-28:rgba(255,255,255,.28);
  --white-16:rgba(255,255,255,.16);
  --violet:#6A1FE8;
  /* 代码块底色。原来是硬写的 #FFF，抽成令牌好让夜间主题换掉它 */
  --code-bg:#FFFFFF;
  --ink:#2B1670;
  --ink-65:rgba(43,22,112,.65);
  --paper-hair:rgba(106,31,232,.18);

  --f-display:"Libre Caslon Display","Libre Caslon Text",Georgia,"Times New Roman",serif;
  --f-mono:"Courier Prime","Courier New",ui-monospace,monospace;
  --f-cn:"Noto Serif SC","Source Han Serif SC","STSong","SimSun",serif;

  --gut:clamp(1.5rem,6vw,7rem);
  /* 紫框只在**纸面**背后才看得见（它是紫的，紫底上等于不存在），
     所以这个值实际只影响文章页和首页 D 区。规格 §4 原值 clamp(14px,1.8vw,26px)
     在 1440px 下量到 25px，纸面被压得太窄——减到一半 */
  --frame:clamp(8px,1vw,14px);
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
  background:var(--purple);
  color:var(--white);
  font-family:var(--f-cn);
  font-size:clamp(1rem,.9vw + .8rem,1.0625rem);
  line-height:1.9;
  -webkit-font-smoothing:antialiased;
  /* **不能写 hidden。** 按规范 overflow-x:hidden + overflow-y:visible 会把
     overflow-y 算成 auto，body 于是变成滚动容器——而滚动容器里的
     position:sticky 是相对它算的，页脚揭示直接失效。实测确认过：
     改之前 body 的计算值就是 x=hidden y=auto。
     clip 一样裁掉横向溢出（出血图要靠它），但**不建立滚动容器** */
  overflow-x:clip;
}
::selection{background:var(--white);color:var(--purple)}
.paper ::selection{background:var(--purple);color:var(--white)}
a{color:inherit;text-decoration:none}
a:focus-visible{outline:2px solid var(--white);outline-offset:4px}
.paper a:focus-visible{outline-color:var(--violet)}
img{display:block;max-width:100%;height:auto;user-select:none;-webkit-user-drag:none}

/* 滚动条：默认隐形，hover 显形 */
html{scrollbar-width:thin;scrollbar-color:var(--white-28) transparent}
::-webkit-scrollbar{width:.25rem;height:.25rem}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--white-28);border-radius:1rem}

/* ============ 全局装置（§4）============ */
.frame{position:fixed;inset:0;z-index:200;pointer-events:none;border:var(--frame) solid var(--purple)}
.grain{position:fixed;inset:0;z-index:190;pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.72' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:11rem 11rem;mix-blend-mode:color-burn;opacity:.16}

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

/* mono 标签原子 */
.mono{font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.2em;font-size:.72rem;font-weight:400}
.mono-s{font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.16em;font-size:.64rem;font-weight:400}
.rule{border:0;height:1px;background:var(--white-28)}
.tick{border:0;width:2.4rem;height:1px;background:var(--white)}

/* ============ A 导航 ============ */
.topbar{display:flex;align-items:flex-start;justify-content:space-between;gap:1.5rem;
  padding-top:clamp(1.6rem,4vh,2.8rem);padding-bottom:1.2rem;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:.75rem}
/* 导航栏的 VA 用加粗版（icon-va-sm）：花体衬线在 37px 下笔画会掉进亚像素，
   页脚印章 4.4rem 有空间，用原版更秀气 */
.brand img{width:2.3rem;height:2.3rem;flex:none}
.brand-name{display:flex;flex-direction:column;gap:.28rem}
.brand-name b{font-family:var(--f-mono);font-weight:700;font-size:.9rem;letter-spacing:.24em}
.brand-name span{font-family:var(--f-mono);font-size:.56rem;letter-spacing:.3em;color:var(--white-84)}
.topnav{display:flex;gap:clamp(1.2rem,3.2vw,3rem);padding-top:.5rem;flex-wrap:wrap}
.topnav a{font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.2em;font-size:.72rem;
  color:var(--white-84);transition:color .15s}
.topnav a:hover{color:var(--white);text-decoration:underline;text-underline-offset:.35em;text-decoration-thickness:1px}

/* ============ B Hero ============ */
.hero{position:relative;overflow:hidden}
.hero-stage{position:relative;min-height:min(88vh,60rem)}
/* Hero 图版：白墨 + alpha 通道，紫色由页面背景透上来。
   不加 filter、不加 mix-blend-mode —— 半调网点已经是「非黑即白」，
   再叠 screen 只会把紫色洗成淡薰衣草色。 */
.hero-art{position:absolute;z-index:1;top:0;right:calc(-1 * var(--gut) - 6vw);
  width:min(72vw,68rem);height:min(82vh,56rem);object-fit:cover;object-position:48% 48%;
  opacity:.74;
  -webkit-mask-image:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.55) 22%,#000 52%);
  mask-image:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.55) 22%,#000 52%);
  pointer-events:none;will-change:transform}
.hero-body{position:relative;z-index:2;padding-top:clamp(2.5rem,9vh,6rem)}
.wordmark{font-family:var(--f-display);font-weight:400;font-size:clamp(4rem,15.5vw,15.5rem);
  line-height:.8;letter-spacing:.01em;user-select:none;}
.hero-sub{display:flex;flex-direction:column;gap:1.15rem;margin-top:clamp(1.6rem,4.5vh,2.8rem)}
.tagline{font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.19em;
  font-size:clamp(.72rem,1.05vw,.85rem);line-height:2;color:var(--white)}
.cta{display:inline-flex;align-items:baseline;gap:.9em;width:max-content;
  font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.2em;font-size:.78rem;
  padding-bottom:.55em;border-bottom:1px solid var(--white-28);transition:border-color .15s}
.cta .arr{transition:transform .15s}
.cta:hover{border-color:var(--white)}
.cta:hover .arr{transform:translateX(.35em)}
.hero-tag{position:absolute;z-index:2;left:0;bottom:clamp(1.2rem,5vh,3rem);
  display:flex;flex-direction:column;gap:.45rem;color:var(--white-84)}
.hero-meta{display:flex;justify-content:space-between;gap:1rem 2rem;flex-wrap:wrap;
  padding-block:1.15rem;color:var(--white-84)}
.hero-meta .g{display:flex;gap:2rem;flex-wrap:wrap}

/* ============ C 精选图版 ============ */
.plates{padding-top:clamp(4rem,10vh,7rem);padding-bottom:clamp(4rem,12vh,8rem)}
.sec-head{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;
  padding-bottom:1.1rem;border-bottom:1px solid var(--white-28)}
.sec-head .mono{color:var(--white)}
.sec-head .mono-s{color:var(--white-84)}
.plate{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(2rem,5vw,5rem);align-items:center;
  padding-block:clamp(4.5rem,13vh,9rem);border-bottom:1px solid var(--white-28);min-height:55vh}
.plate:last-of-type{border-bottom:0}
.plate-txt{display:flex;flex-direction:column;align-items:flex-start;gap:1.5rem}
.plate-eyebrow{font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.2em;
  font-size:.72rem;color:var(--white-84)}
.plate-title{font-family:var(--f-cn);font-weight:300;font-size:clamp(1.9rem,4.2vw,3.3rem);
  line-height:1.18;letter-spacing:.04em;color:var(--white);text-wrap:balance}
.plate-abs{max-width:42ch;color:var(--white-84);font-size:.95rem;line-height:1.9}
.plate-go{font-family:var(--f-mono);letter-spacing:.2em;font-size:.72rem;color:var(--white-84);transition:color .15s}
/* 默认图版：展示完整标本 */
.plate-art{position:relative;display:flex;align-items:center;justify-content:center;
  min-height:clamp(14rem,30vh,24rem)}
.plate-art img{width:auto;max-width:100%;max-height:clamp(14rem,34vh,26rem);
  opacity:.62;transition:opacity .2s;pointer-events:none}
/* 满铺图版：内容区采用 Hermes 的画框、过扫描、局部颗粒和暗角。
   图是白墨 + alpha，容器背景的紫直接透上来 —— 饱和度由结构保证，
   不像 mix-blend-mode:screen 那样会把 #7929FF 洗成淡薰衣草色。 */
.plate-art--full{display:block;isolation:isolate;aspect-ratio:666/574;min-height:0;
  overflow:hidden;background:var(--purple)}
.plate-art--full img{position:absolute;inset:0;z-index:1;width:100%;height:100%;
  max-width:none;max-height:none;object-fit:cover;object-position:50% 48%;
  transform:scale(1.06);margin:0!important;opacity:.82}
.plate-art--full::before{content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.72' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:11rem 11rem;mix-blend-mode:color-burn;opacity:.22}
.plate-art--full::after{content:"";position:absolute;inset:0;z-index:3;pointer-events:none;
  background:radial-gradient(120% 92% at 50% 45%,transparent 48%,var(--purple) 100%);
  opacity:.72}
.plate-line{position:absolute;inset:14px;z-index:4;border:1px solid var(--white-16);
  pointer-events:none}
.plate-mark{position:absolute;z-index:5;left:26px;right:26px;bottom:22px;
  display:flex;justify-content:space-between;gap:1rem;font-family:var(--f-mono);
  text-transform:uppercase;letter-spacing:.14em;font-size:.58rem;color:var(--white-84);
  line-height:1.3;pointer-events:none}
.plate-art--upright{aspect-ratio:4/5;background:var(--purple)}
.plate-art--upright img{object-fit:cover;object-position:50% 46%;transform:scale(1.03)}
.plate-art--upright::after{
  background:radial-gradient(92% 106% at 50% 45%,transparent 42%,var(--purple) 100%);
  opacity:.72}
.plate-art--runner{aspect-ratio:4/3;background:var(--purple)}
.plate-art--runner img{object-fit:cover;object-position:54% 46%;transform:scale(1.05)}
.plate-art--runner::after{
  background:radial-gradient(112% 92% at 48% 50%,transparent 40%,var(--purple) 100%);
  opacity:.7}
.plate:nth-of-type(even) .plate-art{order:-1}
.plate:nth-of-type(even) .plate-art img{margin-right:auto}
.plate:nth-of-type(odd) .plate-art img{margin-left:auto}
.plate:hover .plate-title{text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.18em;
  text-decoration-color:var(--white-65)}
.plate:hover .plate-art img{opacity:.82}
.plate:hover .plate-art--full img{opacity:.95}
.plate:hover .plate-go{color:var(--white)}

/* 横向过渡图版：在紫色档案与纸面索引之间形成一段连续地层。
   居中窄条而非通栏 —— 源图仅 332px 宽，通栏 1600px 是 4.8 倍放大，
   收到 900px 后降到 2.7 倍，网点才不会稀到能数出来。 */
.spine-bridge{position:relative;isolation:isolate;overflow:hidden;
  width:min(900px,100% - var(--gut) * 2);margin-inline:auto;aspect-ratio:1100/656;
  background:var(--purple)}
.spine-bridge>img{position:absolute;inset:0;z-index:1;width:100%;height:100%;
  object-fit:cover;object-position:50% 46%;transform:scale(1.04);opacity:.8;
  -webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 9%,#000 91%,transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0%,#000 9%,#000 91%,transparent 100%)}
.spine-bridge::before{content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.72' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:11rem 11rem;mix-blend-mode:color-burn;opacity:.24}
.spine-bridge::after{content:"";position:absolute;inset:0;z-index:3;pointer-events:none;
  background:linear-gradient(180deg,var(--purple) 0%,transparent 18%,transparent 76%,var(--purple) 100%),
    radial-gradient(110% 92% at 50% 48%,transparent 44%,var(--purple) 100%);opacity:.76}
.spine-bridge-line{position:absolute;z-index:4;inset:14px;border-block:1px solid var(--white-16);
  pointer-events:none}
.spine-bridge-meta{position:absolute;z-index:5;left:26px;right:26px;bottom:1.4rem;
  display:flex;justify-content:space-between;gap:1rem;font-family:var(--f-mono);
  text-transform:uppercase;letter-spacing:.16em;font-size:.58rem;color:var(--white-84)}

/* ============ D 索引（§5-D）============
 * 索引表的配色**跟着所在底色走，不写死**。
 *
 * 这一套样式有三个使用现场：首页 D 区（纸面反转）、归档页（紫底）、
 * 关于页的「做过的事」（紫底）。第一版把颜色直接写成 --violet / --ink，
 * 那只对纸面成立——另外两处于是把深紫的字印在紫底上。实测对比度：
 *
 *     .n  图版号   --violet  on #7929FF   1.20
 *     .d  日期     --ink-65  on #7929FF   1.83
 *     .t  标题     --ink     on #7929FF   2.46
 *     td  分隔线   --paper-hair          1.03
 *
 * 也就是**完全看不见**。而 DOM 里字一个不少，PageRenderTest 全绿——
 * 「查字符串在不在 HTML 里」这种断言对配色错误天生没有分辨力。
 *
 * 默认值取**紫底**那一套（body 的默认背景就是紫），纸面是需要显式声明的例外。
 * 这样将来任何页面复用 .idx-table，拿到的默认配色都和它所在的底色配对，
 * 忘了声明的后果是「对了」而不是「看不见」 */
:root{
  --idx-key:var(--white);        /* 图版号、区块标题 */
  --idx-text:var(--white);       /* 文章标题 */
  --idx-dim:var(--white-84);     /* 日期、板块、统计条 */
  --idx-hair:var(--white-28);    /* 行分隔线 */
}
.paper{
  background:var(--paper);color:var(--ink);
  --idx-key:var(--violet);
  --idx-text:var(--ink);
  --idx-dim:var(--ink-65);
  --idx-hair:var(--paper-hair);
}
.index{padding-block:clamp(3rem,9vh,6rem)}
.index .sec-head{border-color:var(--idx-hair)}
.index .sec-head .mono{color:var(--idx-key)}
.index .sec-head .mono-s{color:var(--idx-dim)}
/* 字号：作者反馈「文字太小」。行高和列宽跟着放，否则数字列会挤成两行 */
.idx-table{width:100%;border-collapse:collapse;margin-top:.4rem;font-variant-numeric:tabular-nums}
.idx-table td{padding:.95rem .5rem;border-bottom:1px solid var(--idx-hair);vertical-align:baseline}
.idx-table tr:hover .t{text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.2em}
/* `.n`（编号列）已经从 idxRow 里删掉，规则跟着删——留着一条没有元素命中的规则，
   下一个人会以为那一列还在，然后去找它为什么不显示。
   日期成了首列，所以用 --idx-key（亮色）而不是 --idx-dim：
   它现在是这一行的定位点，不是附属信息 */
.idx-table .d{font-family:var(--f-mono);font-size:.82rem;letter-spacing:.1em;color:var(--idx-key);width:12.5ch;white-space:nowrap}
.idx-table .t{font-family:var(--f-cn);font-weight:400;font-size:1.06rem;color:var(--idx-text)}
.idx-table .c{font-family:var(--f-mono);font-size:.72rem;letter-spacing:.16em;color:var(--idx-dim);text-align:right;width:9.5ch;white-space:nowrap}
.idx-more{display:inline-flex;gap:.9em;margin-top:2rem;font-family:var(--f-mono);
  text-transform:uppercase;letter-spacing:.2em;font-size:.78rem;color:var(--idx-key);
  padding-bottom:.5em;border-bottom:1px solid var(--idx-hair);transition:border-color .15s}
.idx-more:hover{border-color:var(--idx-key)}

/* ============ E 页脚：整页面板（隐藏页）============
 * 一整屏高，默认看不见，正文滚完才被揭开。观感是抽屉被拉开，
 * 而不是「滚到底看见一个矮页脚」。
 *
 * 为什么必须一整屏：作者要的是 Hermès 那个效果——最后一屏是一个完整的面。
 * 而 100svh 也正好是揭示能成立的**上限**：页脚比视口高的话，
 * sticky bottom:0 会把它顶部推到视口以上，顶部就永远看不见。
 * 用 svh 不用 vh：移动端地址栏收起时 vh 变大，页脚跟着长就超过视口了。
 *
 * 另外两个前提：
 *   ① body 不能是滚动容器（见上面 overflow-x 那条）。
 *   ② 压在上面的每一层必须有自己的不透明背景（见下面盖板那一段）。
 *
 * z-index:0 是必须的：sticky 元素在 DOM 里排在正文**之后**，
 * 不显式压低的话它按文档顺序画在正文上面，正文就盖不住它了。
 *
 * 版式：右半整块给人物图（出血），左半克制。理由在 layout.html 的片段注释里。 */
.foot{
  position:sticky; bottom:0; z-index:0; overflow:hidden;
  min-height:100svh;
  display:flex; flex-direction:column; justify-content:space-between;
  /* **不在这里加 padding。** 加了的话 stage 从内边距之后才开始，
     人物图就顶不到页脚上沿，上缘出血没了（实测图顶停在 y=40）。
     纵向间距由 .foot-base 自己出 */
}

/* 盖住页脚的那些层。**用排除法而不是列举法**：
 * 漏掉一个新的 body 子元素，排除法的后果是「它也不透明」（正确），
 * 列举法的后果是「它是透明的，页脚从第一屏透出来」（坏的）。
 * 让忘记声明的那一侧是安全的。
 *   .chrome-wrap  里面是 fixed 的紫框和噪点，给了 z-index 就会被压到正文下面
 *   .progress     文章页顶部的 fixed 进度条，z-index 210，同上
 *   script        没有渲染框，给背景没意义 */
body > :not(.foot, .chrome-wrap, .progress, script){ position:relative; z-index:1; }

/* **默认必须看不见揭示页。**
 * 页脚是 sticky 的，从第一屏就钉在视口底部，全靠盖板压住。而短页面
 * （比如只有一篇文章的归档页）的盖板不到一屏高，压不住——实测那时候
 * scroll=0 就露出了 479/528px，等于「揭示页默认是打开的」，效果全没了。
 *
 * 所以紧挨页脚的那一层至少要一屏高。「紧挨页脚的那一层」没法用
 * 「前一个兄弟」选择器表达（CSS 没有这个），但 :has(+ .foot) 可以：
 * 选中「下一个兄弟是 .foot 的那一个」。每页正好命中一个，
 * **不用改任何模板**——首页命中 main、文章页命中 .wrap，
 * 都是本来就该负责遮挡的那一层。 */
body > :not(.foot):has(+ .foot){ min-height:100svh; }

/* 盖板的底色画在一个**满视口宽**的伪元素上，不能用元素自己的 background。
 *
 * 第一版就是用 background 写的，而文章页的盖板是 `body > .wrap`——
 * 它有 max-width:1500px + margin-inline:auto。视口比 1500 宽的时候
 * 盖板两侧盖不住，z-index:0 的紫页脚就从缝里透出来：
 *
 *     视口 1440   缝   0px   （盖板 1440，正好填满）
 *     视口 1600   缝  50px
 *     视口 1920   缝 210px
 *     视口 2560   缝 530px
 *
 * **我上一轮只量了 1440 —— 1440 < 1500，盖板刚好填满视口，
 * 这个 bug 一点痕迹都没有。** 挑测量宽度的时候没去看 max-width 是多少，
 * 于是挑了一个唯一看不见问题的宽度。以后量这类东西要跨过所有断点取样。
 *
 * left:50% + width:100vw + translateX(-50%)：伪元素的中心对齐盖板的中心，
 * 而盖板是居中的，所以它满视口宽且左右对称。溢出由 body 的 overflow-x:clip 裁掉。
 * z-index:-1 让它落在盖板自己的内容下面，但仍在盖板的层叠上下文里，
 * 所以还是压在页脚（根上下文的 z-index:0）之上。 */
body > :not(.foot, .chrome-wrap, .progress, script)::before{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  top:0; bottom:0; left:50%; width:100vw; transform:translateX(-50%);
  background:var(--purple);
}
body.paper > :not(.foot, .chrome-wrap, .progress, script)::before{ background:var(--paper); }
/* 人物图：右半，上缘和右缘出血（构图三律 §3.3「图永远被裁切」）。
   object-position:left top —— 裁掉右边和下边，脸留在框里。
   线条色和透明纸面是预处理烧进 PNG 的，见 blog/target/make-figure.py */
.foot-figure{
  position:absolute; z-index:0; top:0; right:0;
  width:52%; height:100%;
  object-fit:cover; object-position:left top;
  pointer-events:none; user-select:none;
}

/* stage：图在这里面出血，底栏在它外面。overflow:hidden 负责裁 */
.foot-stage{
  position:relative; flex:1; overflow:hidden;
  display:flex; align-items:center;
}
/* 图的左边缘是一条**硬的直边**——这在这套设计里是对的（§3.3「图永远被裁切」，
   首页的图版块也是硬边）。但光有硬边会读成「贴歪了一张图」，
   所以补一条发丝线，把它变成一条有意的图版边界，和首页 .plate-art 的
   内嵌发丝线是同一个手法。不用渐变淡出：§9 第 1 条禁止任何渐变 */
.foot-stage::after{
  content:""; position:absolute; z-index:1; pointer-events:none;
  top:0; bottom:0; right:52%; width:1px; background:var(--white-28);
}

/* **必须写 width:100%。**
   .wrap 带 margin-inline:auto，而 auto 外边距会取消 flex 的 stretch——
   于是这两块都缩成 fit-content 并且居中了：实测 .foot-panel 只有 482px、
   .foot-base 691px，左列跑到了视口中间。加上 width:100% 才回到
   「满宽、上限 1500、居中」这个 .wrap 本来的意思 */
.foot-panel{ position:relative; z-index:1; width:100%; }
/* 左列宽度**必须跟着图的起点算**，不能只写一个固定上限。
   图从视口 48% 处开始，而 wrap 的左边距是 var(--gut)（宽屏 6vw）。
   第一版写死 min(30rem,100%)：1440 下没事（列 86–566、图 691），
   但 1024 下 gut 只有 61px，列变成 61–541 而图从 492 开始——**压了 49px**，
   实测有 9 个文字块落在图上。
   calc(48vw - var(--gut) - 2rem) 保证列永远在图左边留出 2rem 的空隙。 */
.foot-col{
  width:min(30rem, calc(48vw - var(--gut) - 2rem));
  display:flex; flex-direction:column; gap:clamp(1.8rem,5.5vh,3.4rem);
}

.foot-id{ display:flex; align-items:flex-start; gap:1rem; }
.foot-id img{ width:2.1rem; height:auto; }
.foot-id span{ color:var(--white-84); line-height:1.75; }

.foot-tagline{
  font-family:var(--f-display); font-weight:400;
  font-size:clamp(1.55rem,3.2vw,2.5rem); line-height:1.14;
  letter-spacing:.01em; color:var(--white);
}

/* 索引：索引寄存器的行结构，四行。编号是版式的一部分，不是图版号 */
.foot-index{ display:flex; flex-direction:column; }
.foot-index a{
  display:flex; align-items:baseline; gap:1.2rem;
  padding-block:.78rem; border-bottom:1px solid var(--white-28);
  font-family:var(--f-mono); text-transform:uppercase;
  letter-spacing:.16em; font-size:.7rem;
  color:var(--white-84); transition:color .15s;
}
.foot-index a:first-child{ border-top:1px solid var(--white-28); }
.foot-index .n{ flex:none; color:var(--white); font-variant-numeric:tabular-nums; }
.foot-index .t{ flex:1; }
.foot-index .r{ flex:none; }
.foot-index a:hover{ color:var(--white); }
.foot-index a:hover .t{ text-decoration:underline; text-underline-offset:.3em; }

.foot-seal{ display:flex; align-items:center; gap:1.1rem; }
.foot-seal img{ width:3.2rem; height:auto; }
.foot-seal span{ color:var(--white-84); line-height:1.75; }

.foot-base{
  position:relative; z-index:1; width:100%;
  display:flex; justify-content:space-between; align-items:baseline; gap:.6rem 2rem;
  flex-wrap:wrap;
  padding-block:1.15rem clamp(1.8rem,4.5vh,3.2rem);
  margin-inline:auto;
  border-top:1px solid var(--white-28); color:var(--white-84);
}
.foot-base a:hover{ color:var(--white); text-decoration:underline; text-underline-offset:.35em; }

/* 窄屏：两栏并排放不下，改成上下叠。
 *
 * **图不能垫在文字底下。** 第一版就是那么做的（整幅、opacity .28），
 * 而那样文字落在线条最密处的对比度只有 3.39（`--white-84`）／4.14（纯白），
 * 都不到 4.5。而且**降低透明度救不回来**：薰衣草线条比紫底亮，
 * 越淡只是越接近紫底，中间那一段永远压不到 4.5 以下的亮度差。
 * 所以窄屏改成「图在上、字在下」，两者不重叠——
 * 图还完整看得见，文字回到纯紫底上（4.56）。
 */
@media (max-width:1023px){
  /* **窄屏不做揭示。**
   * 揭示成立的前提是页脚正好一屏。而窄屏上同样的内容要竖着排，
   * 实测页脚被顶到 871–876px（视口 844），390×667 更是超出 115px——
   * 页脚一超过视口，sticky bottom:0 就把顶部推到视口外，
   * 上面那一段（人物图和身份牌）永远看不见。
   *
   * 硬压内容去凑一屏是可以做到的（砍掉印章、收紧间距），但那很脆：
   * 以后任何一处加字都会重新超出，而超出的表现是「页脚顶部莫名消失」，
   * 不会有任何报错。所以这里退回普通排布：页脚照旧是一整屏的面板，
   * 只是不再钉在视口底部，而是正常排在文档末尾。
   * 「默认看不到」照样成立——它在首屏之下。 */
  .foot{ position:static; }
  /* **盖板的 min-height 要留着。** 我一度在这里把它重置成 0，理由是
     「页脚已经不是 sticky 了，撑高盖板没意义」——错的：短页面（手机上
     只有一条的归档/随笔/LAB）内容不到一屏，静态页脚就直接排进了首屏，
     实测默认露出 222/355/227px，「默认看不到」又没了。
     盖板至少一屏高，页脚才一定在首屏之下。 */

  .foot-stage{ flex-direction:column; align-items:stretch; }
  /* position:static 把图放回文档流，它就成了上面那一条带。
     **高度必须用 svh，不能用百分比。** 百分比高度要求容器有确定高度，
     而 .foot-stage 的高度是 flex:1 算出来的——实测 34% 直接没解析，
     图退回内在高度 537px 并溢出，矩形上边跑到了 -299。
     32svh 是确定值：页脚共 100svh，底栏约 9svh，文字还剩约 59svh。 */
  .foot-figure{
    position:static; flex:none; width:100%; height:32svh;
    object-position:center 18%;
  }
  /* 发丝线从「竖在两栏之间」转成「横在图和文字之间」，作用不变：
     把裁切边说成有意的图版边界 */
  .foot-stage::after{ top:32svh; left:0; right:auto; width:100%; height:1px; }
  .foot-panel{ flex:1; display:flex; align-items:center; }
  .foot-col{ width:100%; }
  .foot-tagline{ font-size:clamp(1.4rem,7vw,2rem); }
}

/* ============ 响应式 ============ */
@media (max-width:767px){
  .frame{display:none}
  .hero-art{right:-34vw;top:auto;bottom:13vh;width:122vw;height:58vh;object-position:48% 50%;
    opacity:.4;-webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 30%);
    mask-image:linear-gradient(90deg,transparent 0%,#000 30%)}
  .hero-stage{min-height:92vh}
  .plate{grid-template-columns:1fr;gap:1.8rem;min-height:0;padding-block:3.6rem}
  .plate:nth-of-type(even) .plate-art{order:0}
  .plate-art{aspect-ratio:2/1}
  .plate-art--full{aspect-ratio:4/3}
  .plate-art--full img{object-position:50% 48%;transform:scale(1.06)}
  .plate-art--upright{aspect-ratio:5/6}
  .plate-art--upright img{object-position:50% 45%;transform:scale(1.03)}
  .plate-art--runner{aspect-ratio:4/3}
  .plate-art--runner img{object-position:54% 46%;transform:scale(1.05)}
  .plate-line{inset:10px}
  .plate-mark{left:18px;right:18px;bottom:15px;gap:.6rem;font-size:.52rem;
    letter-spacing:.08em}
  .spine-bridge{aspect-ratio:4/3}
  .spine-bridge>img{object-position:52% 48%;transform:scale(1.1)}
  .spine-bridge-line{inset:10px}
  .spine-bridge-meta{bottom:1rem;left:18px;right:18px;gap:.35rem;font-size:.52rem}
  .idx-table .c{display:none}
  .topnav{gap:1.1rem}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{transition-duration:.01ms!important;animation-duration:.01ms!important}
}

/* ==========================================================================
 * 文章页（规格 §6.1）—— 原型里没有这一页，以下按规格新写
 *
 * 「读者呆最久的地方」，所以整页翻成纸面。紫框保留（§6.1 第一条）。
 * ========================================================================== */

/* 整页纸面。加在 <body> 上，于是 ::selection 的反色规则（.paper 那条）也一起生效 */
body.paper{ background:var(--paper); color:var(--ink); }

/* 纸面上的导航要换色 —— 紫底那套是白色系，直接搬过来在纸上看不见 */
.paper .brand-name b,
.paper .brand-name span,
.paper .topnav a{ color:var(--violet); }
.paper .topnav a{ opacity:.78; }
.paper .topnav a:hover{ opacity:1; }
.paper .rule{ background:var(--paper-hair); }
.paper .tick{ background:var(--violet); }

/* ── 标本签（§6.1 头部，**左对齐不居中**）───────────────────────────── */
.spec-label{ padding-top:clamp(2.5rem,7vh,5rem); }
/* eyebrow：PLATE 012 — 2026.03.08 — LAB — 9 MIN */
.spec-label .eyebrow{
  font-family:var(--f-mono); text-transform:uppercase;
  letter-spacing:.2em; font-size:.72rem; color:var(--violet);
}
.spec-label h1{
  font-family:var(--f-cn); font-weight:300;
  font-size:clamp(1.8rem,4vw,2.6rem); line-height:1.15;
  color:var(--ink); margin-top:1.1rem; max-width:22ch;
}
.spec-label .dek{
  font-size:.95rem; color:var(--ink-65); margin-top:1rem; max-width:52ch;
}
.spec-label hr{ border:0; height:1px; background:var(--paper-hair); margin-top:clamp(2rem,5vh,3rem); }

/* ── 正文（§6.1 中段）──────────────────────────────────────────────────
 * 段宽 68ch、行高 1.9 —— 这两个数字是规格 §2 字号表里写死的，不要「顺手调大一点」
 * -------------------------------------------------------------------- */
.prose{ max-width:68ch; padding-block:clamp(2.5rem,6vh,4rem); }
.prose > * + *{ margin-top:1.4em; }
.prose p{ color:var(--ink); }

/* h2 = mono 编号 + 换行 + 宋体标题。
   规格说「文章是线性的，编号成立」——所以编号是 CSS 计数器自动排的，
   不是作者手写的。作者手写的话，插一节就得全改一遍 */
.prose{ counter-reset:h2; }
.prose h2{
  font-family:var(--f-cn); font-weight:400; font-size:1.35rem;
  line-height:1.35; color:var(--ink); margin-top:2.6em;
}
.prose h2::before{
  counter-increment:h2;
  content:counter(h2,decimal-leading-zero);
  display:block; margin-bottom:.5rem;
  font-family:var(--f-mono); font-size:.7rem; letter-spacing:.2em;
  color:var(--violet);
}
.prose h3{
  font-family:var(--f-cn); font-weight:400; font-size:1.12rem;
  color:var(--ink); margin-top:2em;
}

/* 链接：下划线 25% → hover 变实。两种地面同规则（§6.1） */
.prose a{
  color:var(--violet);
  text-decoration:underline;
  text-decoration-color:rgba(106,31,232,.25);
  text-underline-offset:.22em;
  transition:text-decoration-color .15s;
}
.prose a:hover{ text-decoration-color:var(--violet); }

/* 行内代码：mono + violet + 发丝边框，**无底色**（§6.1） */
.prose :not(pre) > code{
  font-family:var(--f-mono); font-size:.86em; color:var(--violet);
  border:1px solid var(--paper-hair); padding:.05em .35em;
  background:none;
}

/* 代码块：白底 + 发丝边框 + mono violet。
   **不要彩色语法高亮**（§6.1 明文，§9 第 6 条列为禁止事项）——
   所以这里既不引 highlight.js 也不给 .language-* 上颜色。
   「最多用透明度分层」那句留给以后真需要时做，现在一档都不分 */
.prose pre{
  background:var(--code-bg); border:1px solid var(--paper-hair);
  padding:1.1rem 1.2rem; overflow-x:auto;
  /* 直角。规格 §9 第 2 条：圆角 >4px 一律禁止 */
  border-radius:0;
}
.prose pre code{
  font-family:var(--f-mono); font-size:.8rem; line-height:1.75;
  color:var(--violet); white-space:pre;
}

/* 引用块：左 2px 实线，**不用斜体**（中文斜体是伪斜，§6.1） */
.prose blockquote{
  border-left:2px solid var(--violet); padding-left:1.2rem;
  color:var(--ink-65); font-style:normal;
}

/* 表格：索引寄存器的密度，无竖线 */
/* 正文表格要能**自己横向滚动**，不然它把整个文档撑宽。
   实测 390 视口下一张 markdown 表格的 min-content 是 373px，
   加上左边距 scrollWidth 变成 397 —— 整页出现横向滚动条，
   而 body 的 overflow-x:clip 只是把溢出裁掉、并不阻止文档变宽。
   这是内容驱动的溢出：表格是 markdown 渲染出来的，模板包不到它，只能用 CSS 兜。

   display:block + width:max-content + max-width:100% + overflow-x:auto 是
   这件事的标准写法：display:block 让它成为滚动容器，而 thead/tbody/tr/td
   的 display 没动，浏览器会生成匿名表格盒，**内部的列对齐照旧**。 */
.prose table{
  display:block; width:max-content; max-width:100%; overflow-x:auto;
  border-collapse:collapse; font-size:.92rem;
}
.prose th,.prose td{
  text-align:left; padding:.55rem .8rem .55rem 0;
  border-bottom:1px solid var(--paper-hair);
}
.prose th{
  font-family:var(--f-mono); text-transform:uppercase;
  letter-spacing:.16em; font-size:.64rem; font-weight:400; color:var(--violet);
}
.prose td{ color:var(--ink); }

/* 图：占满正文宽。图注见下 */
.prose img{ width:100%; margin-block:1.8em; }
.prose figcaption,
.prose .fig{
  font-family:var(--f-mono); text-transform:uppercase;
  letter-spacing:.16em; font-size:.64rem; color:var(--ink-65);
  margin-top:-1em;
}

.prose ul,.prose ol{ padding-left:1.4rem; }
.prose li + li{ margin-top:.5em; }
.prose li::marker{ color:var(--violet); }
.prose hr{ border:0; height:1px; background:var(--paper-hair); margin-block:2.4em; }

/* ── 文末（§6.1 倒数第二条）：居中眼球印章 + END OF PLATE 012 ──────────
 * 规格 §9 第 5 条禁止居中排版，**印章是明文列出的例外之一** */
.plate-end{ text-align:center; padding-block:clamp(3rem,8vh,5rem); }
.plate-end img{ width:2rem; margin-inline:auto; opacity:1; }
.plate-end span{
  display:block; margin-top:.9rem;
  font-family:var(--f-mono); text-transform:uppercase;
  letter-spacing:.2em; font-size:.64rem; color:var(--ink-65);
}

/* ── prev / next：两行索引寄存器样式（§6.1 最后一条）────────────────── */
.plate-nav{ border-top:1px solid var(--paper-hair); }
.plate-nav a{
  display:flex; gap:1.2rem; align-items:baseline;
  padding:1.1rem 0; border-bottom:1px solid var(--paper-hair);
}
.plate-nav .dir{
  font-family:var(--f-mono); text-transform:uppercase; letter-spacing:.2em;
  font-size:.64rem; color:var(--violet); flex:none; width:5.5rem;
}
/* 日期列，和索引寄存器同一个位置、同一个宽度——邻居行就是索引行的一种。
   **宽度必须和 .idx-table .d 一致（12.5ch）。**
   第一版写的是 7ch：`2026.08.16` 是 10 个字符，加 .1em 字距根本塞不进去，
   而 flex:none 配固定宽度不会把盒子撑开——溢出的字直接压到标题上，
   页面上是「2026.08.低空略过…」。DOM 里两段文字都对，测试全绿，
   **只有截图能看出来**。 */
.plate-nav .d{
  font-family:var(--f-mono); font-size:.78rem; letter-spacing:.1em;
  color:var(--ink); flex:none; width:12.5ch; white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
.plate-nav .t{ font-family:var(--f-cn); color:var(--ink); }
.plate-nav a:hover .t{ text-decoration:underline; text-underline-offset:.22em; }

/* ==========================================================================
 * 文章页夜间主题
 *
 * 三个状态：跟系统（默认）／强制亮／强制暗。手动选择记在 localStorage，
 * 由 <head> 里那段同步脚本在首屏之前贴到 <html data-theme> 上。
 *
 * ── 为什么用 light-dark() 而不是 @media (prefers-color-scheme) ──
 * 用媒体查询的话，同一份「亮值→暗值」的映射要写两遍：
 * 一遍在 `@media (prefers-color-scheme:dark) html:not([data-theme=light])` 里，
 * 一遍在 `html[data-theme=dark]` 里（不然手动开关在亮系统上不生效）。
 * 两份映射会分岔——加一个令牌只改了一处，那个令牌就只在一半情况下变。
 * light-dark() 把两个值写在同一行，三个状态全部由 color-scheme 决定，
 * 映射**只有一份**。实测过 light-dark() 在自定义属性里可用，
 * 且四种组合（系统亮/暗 × 无覆盖/两种覆盖）都解析正确。
 *
 * ── 为什么裹在 @supports 里 ──
 * 自定义属性在**解析时**接受任何词法，`light-dark()` 不被支持时
 * 是在**取值时**才失效——而取值时失效的声明会变成 unset，
 * `background` 于是变透明，比任何一套主题都糟。
 * @supports 保证老浏览器根本看不到这一段，拿到的是 :root 里的亮色值。
 *
 * ── 为什么只覆盖 body.paper，不覆盖 :root ──
 * `.paper` 这个类首页 D 区那条纸面横条也在用。覆盖 :root 会把它一起翻黑，
 * 而要的是「文章页」。页脚同理不受影响：它用的是 --purple / --white 那一套。
 *
 * 深色值都是量过的（底 #130B26）：
 *     正文 #EFEAFF          16.21   AAA
 *     辅助 62% #EFEAFF       6.61   AA
 *     链接/标签 #C4AEFF      9.83   AAA
 *     代码块 #1C1239 与正文面的分离度 1.08 —— 和亮色下 #FFF 印在 #F4F2F8 上
 *     （1.09）是同一个量级，都靠发丝边框划边界，观感一致
 * ========================================================================== */
@supports (color: light-dark(#000, #fff)) {
  body.paper{
    /* 默认跟系统。两个值写在一行里，改一个能立刻看见另一个 */
    color-scheme: light dark;
    --paper:      light-dark(#F4F2F8,              #130B26);
    --ink:        light-dark(#2B1670,              #EFEAFF);
    --ink-65:     light-dark(rgba(43,22,112,.65),  rgba(239,234,255,.62));
    --violet:     light-dark(#6A1FE8,              #C4AEFF);
    --paper-hair: light-dark(rgba(106,31,232,.18), rgba(196,174,255,.24));
    --code-bg:    light-dark(#FFFFFF,              #1C1239);
  }
  /* 手动覆盖。只改 color-scheme，上面那一份映射自己会跟着走 */
  html[data-theme="light"] body.paper{ color-scheme: light; }
  html[data-theme="dark"]  body.paper{ color-scheme: dark;  }
}

/* 主题开关：三个 mono 词，当前那个高亮。
   放在标本签那一行的右端，**只在文章页出现**——
   放进全站导航的话，它在另外四个页面上什么都不做，那是个假控件 */
.spec-head{
  display:flex; justify-content:space-between; align-items:baseline;
  gap:1rem 1.5rem; flex-wrap:wrap;
}
.theme-switch{ display:inline-flex; gap:1em; flex:none; }
.theme-switch button{
  /* 按钮要长得像 mono 标签，不像按钮 —— 但**必须是 button**：
     div 加 onclick 键盘上按不到，而这是个真控件 */
  background:none; border:0; padding:0; cursor:pointer;
  font:inherit; letter-spacing:inherit; text-transform:inherit;
  color:var(--ink-65); transition:color .15s;
}
.theme-switch button:hover{ color:var(--violet); }
.theme-switch button[aria-pressed="true"]{
  color:var(--violet);
  text-decoration:underline; text-decoration-thickness:1px; text-underline-offset:.3em;
}
.theme-switch button:focus-visible{ outline:2px solid var(--violet); outline-offset:3px; }

/* ── 纸面下边缘的巨字（§6.1 之外，作者要的 Hermès 效果）────────────────
 * 巨大的 wordmark 印在纸面最底部，紫色（相对纸面是反色），
 * 被纸面的下边缘裁掉下半截。overflow:hidden 配一个小于字高的 height
 * 就是那个「被裁掉」；height 用 em，所以跟着字号一起缩放，
 * 不会在小屏上变成「裁太多只剩一条」。
 *
 * 不用 mix-blend-mode 做自动反色：这个字整块都在纸面上，不跨边界，
 * 用混合模式等于为一个不会发生的情况付代价，而且混合模式在 sticky
 * 层叠上下文里的行为要额外验。直接给颜色更可控。 */
.edge-mark{
  margin-top:clamp(2rem,6vh,4rem);
  font-size:clamp(3.4rem,17vw,17rem);
  /* 字是**完整**的，不裁。第一版用 overflow:hidden 真把下半截切掉了，
     那是误解——Hermès 那个字本身是完整的，只是和下面的品牌色同色、
     又紧贴内容面的下边缘，看起来才像被切断。
     line-height:.78 已经让盒子小于字高，字整个露在外面。

     要「贴住」纸面下缘，控制的是**盒子高度**，不是外边距。
     试过 margin-bottom:-.03em，没用——.wrap 没有下内边距也没有边框，
     子元素的下外边距会**和父元素的合并**、跑到 .wrap 外面去拉页脚，
     纸面自己的下缘一点没动，间隔还是 7px。

     行盒高 .78em，而全大写的墨迹底边就是基线，基线在盒顶下方约 .75em
     （半行距 (0.78-1.0)/2 加上 Caslon 约 .86em 的 ascent）。
     .75em 会让盒底正好落在基线上——但那样在边界处仍然看得出一条发丝缝
     （抗锯齿的最后一行像素落在纸面上）。压到 .71em，字的最后一小截落到
     纸面之外、印在同色的紫上，两边就真正连成一片。
     **要往下挪就调这一个数**：越小 = 字露出纸面越多 = 交界处越连续。
     overflow 保持 visible：基线以下本来没有墨（全大写没有降部），不会被切到。 */
  line-height:.78;
  height:.71em;
}
.edge-mark span{
  display:block;
  font-family:var(--f-display); font-weight:400;
  letter-spacing:.01em; text-align:center;
  color:var(--purple); white-space:nowrap; user-select:none;
  transform:translateY(-0.0323em);
}

/* ── 阅读进度条（F-09：顶部 1px 发丝线）─────────────────────────────
 * 放在紫框之上（z-index 高于 .frame 的 200），否则被框盖住看不见 */
.progress{
  position:fixed; top:0; left:0; height:1px; width:0;
  background:var(--violet); z-index:210;
  /* 不做 transition：进度条要跟手，加过渡会「拖在后面」 */
}

/* ── 代码块 COPY 按钮（F-09：mono .6rem、78% 白 → 纸面上是 violet）──── */
.prose pre{ position:relative; }
.copy{
  position:absolute; top:.5rem; right:.5rem;
  font-family:var(--f-mono); text-transform:uppercase; letter-spacing:.16em;
  font-size:.6rem; color:var(--violet);
  background:#FFF; border:1px solid var(--paper-hair);
  padding:.25rem .5rem; cursor:pointer; border-radius:0;
  opacity:0; transition:opacity .15s;
}
.prose pre:hover .copy,
.copy:focus-visible{ opacity:1; }

/* 404（§6.6）：允许居中的页面 */
.notfound{ text-align:center; padding-block:clamp(6rem,20vh,12rem); }
.notfound img{ width:min(34rem,70vw); margin-inline:auto; opacity:.45; }
.notfound h1{
  font-family:var(--f-display); font-weight:400;
  font-size:clamp(2rem,7vw,4.5rem); line-height:.9; margin-top:2rem;
}
.notfound p{ margin-top:1.2rem; color:var(--white-65); }
.notfound a{
  display:inline-block; margin-top:2rem;
  font-family:var(--f-mono); text-transform:uppercase; letter-spacing:.2em;
  font-size:.72rem; text-decoration:underline; text-underline-offset:.3em;
  text-decoration-color:var(--white-28);
}
.notfound a:hover{ text-decoration-color:var(--white); }

/* 移动端：紫框在 <768px 关闭（§4 第一条） */
@media (max-width:767px){
  .frame{ display:none; }
}

/* 动效白名单之外一律不做，并且尊重系统设置（§7 第 3 条） */
@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{ transition:none !important; animation:none !important; }
}

/* ==========================================================================
 * 内页（规格 §6.2–6.5）—— 全部用第 4 节的工具箱，不造新排场
 * ========================================================================== */

/* ── 归档页 §6.2：索引寄存器，**一张插画都没有** ─────────────────────
 * 「密度反差就是设计」。这一页看起来「空」是故意的，别加图 */
.index--page{ padding-block:clamp(2.5rem,7vh,5rem); }
.stratum{ margin-top:clamp(2.5rem,6vh,4rem); }
/* 组头：STRATUM 2026 + 发丝线延伸到行尾 */
.stratum-head{ display:flex; align-items:center; gap:1.2rem; margin-bottom:.6rem; }
.stratum-head .mono{ flex:none; color:var(--idx-key); }
.stratum-head .rule{ flex:1; }

/* ── 随笔页 §6.4：日期 + 一段文字，发丝线分隔，无图 ────────────────── */
.notes-page{ padding-block:clamp(2.5rem,7vh,5rem); }
.note-row{
  display:flex; gap:clamp(1.2rem,4vw,3rem); align-items:baseline;
  padding-block:1.6rem; border-bottom:1px solid var(--white-28);
}
.note-date{ flex:none; width:6.5rem; color:var(--white-84); font-variant-numeric:tabular-nums; }
.note-body{ display:flex; flex-direction:column; gap:.5rem; max-width:52ch; }
.note-title{ font-family:var(--f-cn); font-weight:300; font-size:1.25rem; line-height:1.35; }
.note-abs{ font-size:.95rem; color:var(--white-84); }
.note-row:hover .note-title{ text-decoration:underline; text-underline-offset:.22em; }

/* ── LAB §6.3：栅格，格子 28% 白发丝边框，直角 ──────────────────────
 * hover 时**只有边框变白，背景不变**（规格明文）——加背景色就变成卡片了 */
.lab-page{ padding-block:clamp(2.5rem,7vh,5rem); }
.lab-grid{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(20rem,1fr));
  gap:1px; margin-top:clamp(2rem,5vh,3rem);
}
.lab-cell{
  display:flex; flex-direction:column; gap:.9rem;
  border:1px solid var(--white-28); padding:clamp(1.4rem,3vw,2rem);
  transition:border-color .2s;
}
.lab-cell:hover{ border-color:var(--white); }
.lab-no{ color:var(--white-84); }
.lab-name{ font-family:var(--f-cn); font-weight:300; font-size:1.4rem; line-height:1.25; }
.lab-desc{ font-size:.95rem; color:var(--white-84); }
.lab-tags{ display:flex; flex-wrap:wrap; gap:.4rem; margin-top:.2rem; }
/* 「边框 pill 直角」——规格原话。pill 是形状的名字，直角是明确要求（§9 第 2 条） */
.lab-tag{ border:1px solid var(--white-28); padding:.18rem .5rem; color:var(--white-84); }
.lab-go{ margin-top:auto; color:var(--white-84); }
.lab-cell:hover .lab-go{ color:var(--white); text-decoration:underline; text-underline-offset:.3em; }

/* ── 关于页 §6.5 ───────────────────────────────────────────────────── */
.about-top{ position:relative; padding-block:clamp(2.5rem,7vh,5rem); }
/* 马骨 P-02：右缘出血、opacity .5、在文字下层（构图三律） */
.about-art{
  position:absolute; top:0; right:calc(var(--gut) * -1);
  width:min(42vw,34rem); opacity:.5; z-index:0; pointer-events:none;
}
.about-body{ position:relative; z-index:1; max-width:65ch; }
.about-body .mono{ display:block; color:var(--white-84); margin-bottom:1.5rem; }

/* 通栏地图横条：高约 30vh，opacity .28，上下发丝线夹住 */
.about-band{ margin-block:clamp(2rem,5vh,3.5rem); }
.about-band img{
  width:100%; height:30vh; object-fit:cover; object-position:center 40%;
  opacity:.28; margin-block:1.5rem;
}

.about-projects,.about-links{ padding-block:clamp(2rem,5vh,3.5rem); }

/* 紫底上的正文（关于页和友链用）——文章页那套是纸面色，搬过来在紫底上看不见 */
.prose--purple{ max-width:65ch; }
.prose--purple p,.prose--purple li,.prose--purple td{ color:var(--white); }
.prose--purple h2,.prose--purple h3{ color:var(--white); }
.prose--purple h2::before{ color:var(--white-84); }
.prose--purple a{
  color:var(--white); text-decoration:underline;
  text-decoration-color:var(--white-28); text-underline-offset:.22em;
  transition:text-decoration-color .15s;
}
.prose--purple a:hover{ text-decoration-color:var(--white); }
.prose--purple :not(pre) > code{ color:var(--white); border-color:var(--white-28); }
.prose--purple pre{ background:none; border-color:var(--white-28); }
.prose--purple pre code{ color:var(--white-84); }
.prose--purple blockquote{ border-left-color:var(--white); color:var(--white-65); }
.prose--purple th{ color:var(--white-84); }
.prose--purple th,.prose--purple td{ border-bottom-color:var(--white-28); }
.prose--purple hr{ background:var(--white-28); }
.prose--purple li::marker{ color:var(--white-84); }
/* 紫底上的 COPY 按钮 */
.prose--purple .copy{ color:var(--white-84); background:var(--purple); border-color:var(--white-28); }

@media (max-width:767px){
  .about-art{ display:none; }
  .note-row{ flex-direction:column; gap:.6rem; }
  .note-date{ width:auto; }
}

/* ── 纸面页上的页脚：**翻回紫底** ──────────────────────────────────────
 *
 * 这是一处**规格没写、我做的判断**，理由要说清楚。
 *
 * 发现过程：文章页截图看起来页脚是空的。查计算样式才知道
 * `.foot` 没有自己的背景（透明 → 继承纸面），而里面的字是 --white-84——
 * **白字印在 #F4F2F8 上，等于看不见。** 测试全绿，因为它们只查
 * 「字符串在不在 HTML 里」，而字确实在，只是看不见。
 *
 * 为什么选「页脚翻回紫」而不是「给页脚一套纸面配色」：
 *
 *   页脚里有两张**白线透明 PNG**（眼球印章、足迹）。它们在纸面上一样消失，
 *   而规格 §3.2 明文禁止「在 PS 里把线稿染成紫色再导出——颜色永远由 CSS 给，
 *   同一张图才能横跨紫底和纸面使用」。用 CSS 给白线图上色只能靠 filter 反相，
 *   而反相之后白线变黑线，那是**另一种处理**，不是同一张图换个地面。
 *
 *   翻回紫底不需要动任何图，而且读起来是「正文结束，回到档案馆的地面」。
 *
 * 代价：规格 §1 说「紫→纸的反转是大节奏，一页最多翻一次」，
 * 这样文章页就是紫框 → 纸面 → 紫页脚。**我认为页脚是页面之外的一条带，
 * 不算正文地面的第二次翻转**——但这是我的判断，规格 §6.1 里没有提页脚。
 * 你要是觉得该严格按「只翻一次」，那就得给页脚做纸面配色 + 给两张图做反相处理。
 * ------------------------------------------------------------------------ */
body.paper .foot{
  background:var(--purple);
  color:var(--white);
  /* 原来这里有 margin-top:clamp(3rem,8vh,6rem)，**改成揭示式之后必须去掉**：
     margin 在盖板元素的背景之外，于是那一段变成一条透光的缝，
     纸面还没结束就先看见底下的紫页脚了。
     间距改由盖板自己的 padding-bottom 出（见下一条），
     这样它落在盖板的背景**里面**。
     紫带的上边界仍然是硬边，不做渐变（规格 §9 第 1 条：任何渐变都禁止） */
}
/* 原来这里给 .wrap 加过 padding-bottom 撑间距。**改成巨字之后必须去掉**：
   那个字要紧贴纸面的下边缘才能被裁出「切断」的效果，中间垫一段就成了
   一个完整的字浮在面里。间距现在由 .edge-mark 自己的 margin-top 出 */
