@charset "UTF-8";
/* ==========================================================================
   SupermanCantFly · 静态博客样式表
   --------------------------------------------------------------------------
   目录
   01. 设计令牌 (Design Tokens)
   02. 基础重置与排版
   03. 布局容器
   04. 页头与导航
   05. 首页 Hero
   06. 文章卡片与列表
   07. 侧边栏
   08. 标签
   09. 文章正文 (prose)
   10. 归档页
   11. 页脚
   12. 404 与提示页
   13. 工具类 / 动效
   14. 打印样式
   15. 徽章 / 项目卡片 / 时间线 / 关于页列表
   ========================================================================== */

/* 00. 本地字体（零外部请求） ----------------------------------------------
   两个字体文件都随仓库分发（assets/fonts/），都是 SIL Open Font License 1.1：
     · Inter            可变字重 100–900，拉丁子集，48,256 B
     · JetBrains Mono   可变字重 100–800，拉丁子集，40,404 B
   都是**拉丁子集**：没有中文字形。这正是可以把 Inter 放在 --font-sans 首位的
   原因 —— 遇到汉字时浏览器按 cmap 自动回退到后面的系统 CJK 字体。
   font-display: swap 让首屏先用系统字体渲染，字体到位后再换，不阻塞阅读。
   字体策略与取舍见 DESIGN.md 第 19 节；来源与许可见 assets/vendor/LICENSES.md。 */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/inter-latin-wght-normal.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2");
}

/* 01. 设计令牌 ------------------------------------------------------------ */
/* 调色板直接采样自站长的头像（冬季日系插画：石板蓝天空 + 米白连帽衫 + 沙色格纹 +
   近黑头发）。每个色值后面的百分比是它在头像里的实际占比（canvas 取样）。
   设计说明见 DESIGN.md 第 3 节。 */
:root {
  color-scheme: light;

  /* 字体（详见第 00 节与 DESIGN.md 第 19 节）
     · Inter 放 --font-sans 首位：它是拉丁子集，没有中文字形，
       汉字会按 cmap 逐字回退到后面的系统 CJK，混排不受影响；
     · 宋体标题栈**保持原样**，不插 Inter —— 把无衬线拉丁塞进衬线标题，
       中英会一个几何无衬线、一个宋体，正是要避免的那种"混搭"；
     · 需要"拉丁 Inter + 中文宋体"时用 --font-latin-serif；
     · 末尾一律保留 system-ui / sans-serif 兜底，中文不会出豆腐块。 */
  --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
    "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC",
    "Source Han Sans SC", Roboto, Helvetica, Arial, system-ui, sans-serif;
  --font-serif: "Songti SC", "Noto Serif CJK SC", "Source Han Serif SC",
    "SimSun", serif;
  --font-latin-serif: "Inter", "Songti SC", "Noto Serif CJK SC",
    "Source Han Serif SC", "SimSun", serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono",
    "Cascadia Mono", Consolas, "Liberation Mono", Menlo, "Microsoft YaHei Mono",
    monospace;

  /* 纸（连帽衫的米白） */
  --bg: #e9e7df;
  --bg-subtle: #f2f1ec;
  --bg-elevated: #f7f6f2;
  --bg-inset: #e3e1d8;
  --surface-warm: #f4f2ec;

  /* 墨（头发） */
  --text: #1b1f26;
  --text-secondary: #3a4148;
  --text-muted: #4e565f;

  /* 发丝分隔线：1px 低对比；--hairline 只做装饰，--border-field 是表单控件那档 3:1 */
  --hairline: #d7d3c8;
  --graph: rgba(140, 122, 99, 0.1);
  --border: #dedbd2;
  --border-strong: #c6c2b6;
  --border-field: #8b8880;
  --mark: #d9cbb6;

  /* 图表（mermaid）画布底色：亮色主题用"纸"，暗色主题用墨色微抬 */
  --diagram-bg: #f2f1ec;

  /* 石板蓝（天空）—— 主色；浅色主题下压深到 #35485a 才够 4.5:1 */
  --accent: #35485a;
  --accent-hover: #23323f;
  --accent-soft: rgba(80, 100, 120, 0.1);
  --accent-contrast: #f2f1ec;
  --sky: #c8d8e4;
  --sky-soft: #e2e7e6;

  /* 沙 / 卡其（围巾）—— 强调色，只做装饰与点缀 */
  --sand: #b4a08c;
  --sand-deep: #8c7a63;
  --sand-soft: rgba(180, 160, 140, 0.22);

  --shadow-sm: 0 1px 2px rgba(27, 31, 38, 0.05);
  --shadow-md: 0 10px 24px -18px rgba(27, 31, 38, 0.5), 0 2px 6px -3px rgba(27, 31, 38, 0.07);
  --shadow-lg: 0 30px 56px -34px rgba(27, 31, 38, 0.42);

  /* 卡片不做圆角，只留 2px；杂志不靠圆角，靠边框与层次 */
  --radius-sm: 2px;
  --radius: 3px;
  --radius-lg: 2px;
  --radius-full: 2px;
  --radius-pill: 999px;

  /* 正文列宽：688 − 2×24(padding) = 640px 内容宽，按 16px 字号正好 40 汉字/行 */
  --content-width: 688px;
  --container-width: 1120px;
  --header-height: 64px;

  --dur: 0.22s;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* 深色主题：墨色底（#141519 / #1b1f26，直接取头发），不是简单反色 */
[data-theme="dark"] {
  color-scheme: dark;

  --bg: #141519;
  --bg-subtle: #1b1f26;
  --bg-elevated: #1f242c;
  --bg-inset: #171b21;
  /* 悬停底色：亮色是"更暖"，暗色必须是"更亮"才对得上。
     原来写成 #191a1c（比卡片底色 #1f242c 更暗、色相还跳到暖棕），
     暗色下 hover 反而像被按下去，与亮色语义相反。 */
  --surface-warm: #232a33;

  --text: #eceae3;
  --text-secondary: #c3c7cd;
  --text-muted: #9aa1a9;

  /* 暗色下 --border 刻意比 --hairline 重一档：发丝线负责"分隔"，
     卡片外框负责"成形"。两者同值时卡片在墨底上会糊成一片（这就是原来
     暗色"发灰"的原因）。--border-strong 再抬一档，hover 才有可见的变化。 */
  --hairline: #2b313a;
  --graph: rgba(180, 160, 140, 0.08);
  --border: #39414c;
  --border-strong: #4a535f;
  --border-field: #6a727c;
  --mark: #4a4136;
  --diagram-bg: #1b1f26;

  --accent: #9db2c6;
  --accent-hover: #c8d8e4;
  --accent-soft: rgba(157, 178, 198, 0.13);
  --accent-contrast: #161a1f;
  --sky: #8ea3b8;
  --sky-soft: #262b34;

  --sand: #b4a08c;
  --sand-deep: #cdbba4;
  --sand-soft: rgba(180, 160, 140, 0.16);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.45);
  --shadow-md: 0 10px 26px -18px rgba(0, 0, 0, 0.8), 0 2px 6px -3px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 30px 56px -34px rgba(0, 0, 0, 0.9);
}

/* 无 JS 时跟随系统偏好 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #141519;
    --bg-subtle: #1b1f26;
    --bg-elevated: #1f242c;
    --bg-inset: #171b21;
    --surface-warm: #232a33;
    --text: #eceae3;
    --text-secondary: #c3c7cd;
    --text-muted: #9aa1a9;
    --hairline: #2b313a;
    --graph: rgba(180, 160, 140, 0.08);
    --border: #39414c;
    --border-strong: #4a535f;
    --border-field: #6a727c;
    --mark: #4a4136;
    --diagram-bg: #1b1f26;
    --accent: #9db2c6;
    --accent-hover: #c8d8e4;
    --accent-soft: rgba(157, 178, 198, 0.13);
    --accent-contrast: #161a1f;
    --sky: #8ea3b8;
    --sky-soft: #262b34;
    --sand: #b4a08c;
    --sand-deep: #cdbba4;
    --sand-soft: rgba(180, 160, 140, 0.16);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.45);
    --shadow-md: 0 10px 26px -18px rgba(0, 0, 0, 0.8), 0 2px 6px -3px rgba(0, 0, 0, 0.5);
    --shadow-lg: 0 30px 56px -34px rgba(0, 0, 0, 0.9);
  }
}

/* 02. 基础重置与排版 ------------------------------------------------------ */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-height) + 16px);
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.75;
  color: var(--text);
  background-color: var(--bg);
  /* 围巾格纹的抽象：两组正交的极淡细线，只当隐线，不参与信息表达 */
  background-image:
    repeating-linear-gradient(90deg, var(--graph) 0 1px, transparent 1px 64px),
    repeating-linear-gradient(0deg, var(--graph) 0 1px, transparent 1px 64px);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
  /* clreq 6.3.2：中文标点挤压。Chrome 123+ 支持；不支持的浏览器整条声明无效，不影响布局 */
  text-spacing-trim: trim-start;
}

/* 标题一律系统衬线（宋体系）—— 杂志感的主要来源，也是和绝大多数中文技术博客
   拉开差距的地方。层级靠字号与留白拉开，不靠堆加粗。 */
h1, h2, h3, h4, h5, h6 {
  margin: 0 0 12px;
  font-family: var(--font-serif);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.005em;
  color: var(--text);
  text-wrap: balance; /* 老浏览器忽略，降级为普通换行 */
}

p { margin: 0 0 20px; text-wrap: pretty; }

a {
  color: var(--accent);
  text-decoration: none;
  transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
/* 链接下划线：offset 与 thickness 都调过；hover 只把颜色压实、把线加粗，不做位移 */
a:hover {
  color: var(--accent-hover);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

img, svg, video { max-width: 100%; height: auto; }

/* sprite 图标：<use> 引用的都是 24 画布线性图标，尺寸由这里统一决定 */
.i {
  flex: none;
  width: 1em;
  height: 1em;
  overflow: visible;
}

ul, ol { padding-left: 1.4em; }

hr {
  border: 0;
  border-top: 1px solid var(--hairline);
  margin: 2.5rem 0;
}

code, kbd, samp, pre { font-family: var(--font-mono); }

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

/* 可见焦点样式（无障碍） */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

::selection { background: var(--sand-soft); color: var(--text); }

.skip-link {
  position: absolute;
  left: 12px;
  top: -60px;
  z-index: 200;
  padding: 12px 16px;
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 600;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: 12px; color: var(--accent-contrast); text-decoration: none; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* 03. 布局容器 ------------------------------------------------------------ */
.container {
  width: 100%;
  max-width: var(--container-width);
  margin-inline: auto;
  padding-inline: 24px;
}

/* 非对称两栏：正文列 + 更窄的边注栏 */
.layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 268px;
  gap: 4rem;
  align-items: start;
  padding-block: 3rem 4rem;
}

.layout-main { min-width: 0; }

/* 04. 页头与导航 ---------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg); /* 不支持 color-mix 的浏览器回退为不透明背景 */
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(150%) blur(12px);
  -webkit-backdrop-filter: saturate(150%) blur(12px);
  border-bottom: 1px solid var(--hairline);
}

@supports not (backdrop-filter: blur(4px)) {
  .site-header { background: var(--bg); }
}

.header-inner {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: var(--header-height);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-right: auto;
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
}
.brand:hover { color: var(--text); text-decoration: none; }

/* 站点标志：<img> 直接引用根目录的 favicon.svg —— 页头 / 页脚标志与浏览器
   标签页上的图标从此是同一个文件，不可能再对不上。
   用 <img> 而不是 sprite 的 <use>：file:// 下 Chrome / Firefox 会拒绝
   跨文档的 SVG <use> 外部引用，双击打开时页头会只剩文字（图标 sprite
   本身就受这条限制）。<img> 在同目录下不受影响，两种打开方式一致。
   圆角与阴影交给 SVG 自己的圆角方块（rx=9 / 64），
   CSS 不再叠一层 2px 圆角，避免把图标的圆角切掉。 */
.brand-mark {
  flex: none;
  display: block;
  width: 32px;
  height: 32px;
  transition: transform var(--dur) var(--ease);
}
.brand:hover .brand-mark { transform: translateY(-1px) rotate(-3deg); }

.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-name { font-family: var(--font-serif); font-size: 16px; letter-spacing: 0.02em; }
.brand-tagline {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-muted);
  letter-spacing: 0.12em;
}

.site-nav ul {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav a {
  position: relative;
  display: block;
  padding: 8px 12px;
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
}
/* 当前栏目：不用色块，压一条 2px 的沙色短横线 */
.site-nav a::after {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 2px;
  height: 2px;
  background: var(--sand-deep);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.site-nav a:hover {
  color: var(--text);
  text-decoration: none;
}
.site-nav a[aria-current="page"] { color: var(--text); font-weight: 600; }
.site-nav a[aria-current="page"]::after { opacity: 1; }

.header-actions { display: flex; align-items: center; gap: 8px; }

.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
    background-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.icon-button:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--bg-subtle);
}
.icon-button:active { transform: scale(0.95); }
.icon-button svg { width: 18px; height: 18px; }

/* 主题图标切换 */
.theme-toggle .icon-moon { display: none; }
[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
[data-theme="dark"] .theme-toggle .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: none; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: block; }
}

.nav-toggle { display: none; }

@media (max-width: 720px) {
  .nav-toggle { display: inline-flex; }

  .site-nav {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    padding: 8px 12px 16px;
    background: var(--bg-elevated);
    border-bottom: 1px solid var(--hairline);
    box-shadow: var(--shadow-md);
    /* 折叠态不用 display:none：那会让面板里的链接无法聚焦。
       用 max-height 折叠 —— 不可见、不可点，但仍然可以 Tab 到，
       一旦焦点进入就靠 :focus-within 展开，焦点环不会落在隐形内容上。 */
    display: block;
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    pointer-events: none;
  }
  .site-nav.is-open,
  .site-nav:focus-within { max-height: 480px; opacity: 1; pointer-events: auto; }
  .site-nav.is-open { animation: nav-in 0.18s var(--ease); }
  .site-nav ul { flex-direction: column; align-items: stretch; gap: 4px; }
  .site-nav a { padding: 12px 14px; font-size: 1rem; }
  .site-nav a::after { left: 0; right: auto; width: 20px; bottom: 8px; }
  .site-nav a[aria-current="page"] { background: var(--bg-subtle); }
}

@keyframes nav-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* 阅读进度条 */
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 120;
  height: 2px;
  width: 0;
  background: linear-gradient(90deg, var(--sand-deep), var(--accent));
  transition: width 0.1s linear;
  pointer-events: none;
}

/* 05. 首页 Hero ----------------------------------------------------------- */
/* 左右非对称：左边是刊头文字，右边是头像插画卡片（像杂志的扉页插图）。 */
.hero {
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--hairline);
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, var(--sky-soft), transparent 62%),
    linear-gradient(180deg, var(--bg-subtle), var(--bg));
}
.hero > .container { position: relative; z-index: 2; }

/* 刊眉：小字距的一行小注，杂志扉页的常见做法。
   2026-10 精修：右侧补一条引满整栏的发丝线，做成真正的"刊头横线"
   （与 .year-label::after、.section-head 的下边线是同一套版式语言）。
   原来只有左边一小段 16px 的沙色短线，孤零零地挂在文字前面，
   既不像装饰也不像分隔，属于典型的"多余装饰"。 */
.hero-kicker {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 24px;
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
}
.hero-kicker::before {
  content: "";
  flex: none;
  width: 16px;
  height: 2px;
  background: var(--sand);
}
.hero-kicker::after {
  content: "";
  flex: 1 1 auto;
  min-width: 24px;
  height: 1px;
  /* 比 --hairline 重一档：hero 顶部还压着 --sky-soft 的渐层，
     发丝线在那层浅绿灰上几乎看不见，等于白写的装饰 */
  background: var(--border-strong);
}

.hero-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  align-items: center;
  gap: 4rem;
  /* 上 64 / 下 48：把首屏的重心往上提，让刊头更靠近页头，
     底部留出的 48 交给下面的版面（原来 72/56 让整块悬在中间） */
  padding-block: 4rem 3rem;
  position: relative;
  z-index: 2;
}
.hero-text { min-width: 0; }

.hero-title {
  margin: 0 0 16px;
  font-size: 30px;
  line-height: 1.16;
  letter-spacing: -0.01em;
}

.hero-subtitle {
  max-width: 560px;
  margin: 0 0 32px;
  color: var(--text-secondary);
  font-size: 18px;
  line-height: 1.8;
}

/* 头像插画卡片：细边框 + 极轻阴影 + 微微倾斜，不切成圆形头像 */
.hero-figure { margin: 0; }
.hero-portrait {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  box-shadow: var(--shadow-md);
  transform: rotate(-1.1deg);
  transition: transform 0.5s var(--ease), box-shadow var(--dur) var(--ease);
}
.hero-portrait:hover { transform: rotate(0deg); box-shadow: var(--shadow-lg); }
.hero-portrait img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 32%;
  filter: saturate(0.94);
}
/* 卡片下沿压一条 2px 的沙色短线，呼应围巾 */
.hero-portrait::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--sand), var(--sand-deep));
}
.hero-figure figcaption {
  margin-top: 12px;
  padding-left: 12px;
  border-left: 2px solid var(--sand);
  color: var(--text-muted);
  font-family: var(--font-serif);
  font-size: 13px;
  line-height: 1.7;
}

/* 徽章与按钮之间拉开一档（32px）：徽章是"技术关键词"这种次要信息，
   按钮是行动入口，两者原来只隔 24px，在首屏里糊成一团 */
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 32px; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
    background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
  text-decoration: none;
  color: var(--text);
}
.btn svg { width: 16px; height: 16px; }
.btn .i { color: var(--sand-deep); }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}
.btn-primary .i { color: var(--accent-contrast); opacity: 0.8; }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-contrast); }

/* 窄屏：头像移到文字上方，尺寸收小，不再承担版面重心 */
@media (max-width: 860px) {
  .hero-inner {
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
    padding-block: 3rem 2.5rem;
  }
  .hero-figure { order: -1; width: 200px; }
  .hero-portrait { transform: rotate(-1.6deg); }
}
@media (max-width: 480px) {
  .hero-figure { width: 160px; }
}

/* 06. 文章卡片与列表 ------------------------------------------------------ */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--hairline);
}
.section-title {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0;
  /* 区块标题抬到 24px：原来与卡片标题（20px）只差一档，
     1312 宽的版面里"最新文章"和它下面的卡片标题看起来一样大，
     层级立不住。现在读作 页面标题 36 > 区块标题 24 > 卡片标题 20 > 正文 16 > 元信息 13。 */
  font-size: 24px;
  letter-spacing: 0.01em;
}
.section-title::before {
  content: "";
  flex: none;
  width: 12px;
  height: 2px;
  background: var(--sand);
}
.section-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
}
.section-link:hover { color: var(--accent); }
.section-link .i { width: 14px; height: 14px; }

.post-list {
  display: grid;
  /* 卡片间距对齐项目卡（24px）：原来 16px，卡片内边距却是 24px，
     间距比内边距还小，一排卡片看上去是"粘"在一起的 */
  gap: 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 卡片不做圆角、不堆阴影：一条外框 + 一枚沙色的"书脊线"。
   2026-10 精修：书脊线原来是 left:14px 的 1px 细线，逼得左内边距只能写 32px，
   卡片左右不对称（32 / 24），文字与线之间还空着 18px。
   现在把线贴到卡片左沿、加粗到 2px —— 它与站内 .badge / .lede /
   .sidebar-quote / .hero-figure figcaption 的"沙色竖线"是同一套语言，
   内边距也随之回到左右对称的 24px。 */
.post-card {
  position: relative;
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.post-card::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--sand);
  transition: background-color var(--dur) var(--ease);
}
.post-card:hover {
  border-color: var(--border-strong);
  background: var(--surface-warm);
}
.post-card:hover::before { background: var(--sand-deep); }

.post-card-title {
  margin: 0 0 12px;
  font-size: 20px;
  line-height: 1.35;
}
.post-card-title a { color: var(--text); }
.post-card-title a:hover { color: var(--accent); text-decoration: none; }

.post-card-title a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-sm);
}

.post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin: 0 0 16px;
  color: var(--text-muted);
  font-size: 13px;
}
.post-meta .dot { color: var(--border-strong); }
.meta-item { display: inline-flex; align-items: center; gap: 4px; }
.meta-item .i { width: 13px; height: 13px; color: var(--sand-deep); }

.post-card-excerpt {
  margin: 0 0 20px;
  color: var(--text-secondary);
  font-size: 16px;
  line-height: 1.7;
}

.post-card-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.read-more {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}
.read-more .i {
  width: 15px;
  height: 15px;
  transition: transform var(--dur) var(--ease);
}
.read-more:hover .i { transform: translateX(3px); }

/* 卡片整体可点击（标题链接铺满），标签与"阅读全文"需浮在其上 */
.post-card .tag-list { position: relative; z-index: 1; }

/* 搜索与筛选 */
.filter-bar {
  margin-bottom: 24px;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
}

/* 搜索框与标签筛选之间压一条发丝线：两件事（找文章 / 按主题过滤）在视觉上分开，
   否则输入框、状态行、两行标签堆在一个白块里，分不清哪个是主体 */
.filter-bar .tag-cloud {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
}

.search-field { position: relative; display: flex; align-items: center; }
.search-field svg {
  position: absolute;
  left: 12px;
  width: 17px;
  height: 17px;
  color: var(--text-muted);
  pointer-events: none;
}
.search-input {
  width: 100%;
  padding: 8px 40px;
  border: 1px solid var(--border-field);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text);
  font-size: 16px;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.search-input::placeholder { color: var(--text-muted); }
.search-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.search-clear {
  position: absolute;
  right: 6px;
  display: none;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
}
.search-clear:hover { color: var(--text); background: var(--bg-subtle); }
.search-clear.is-visible { display: inline-flex; align-items: center; justify-content: center; }
.search-clear svg { width: 15px; height: 15px; }

.filter-status {
  margin: 16px 0 0;
  color: var(--text-muted);
  font-size: 13px;
}

.no-results {
  display: none;
  padding: 2.5rem 1.5rem;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  text-align: center;
  color: var(--text-muted);
}
.no-results.is-visible { display: block; }
.no-results strong { display: block; margin-bottom: 8px; color: var(--text); font-size: 18px; }

/* 07. 侧边栏（边注栏） ---------------------------------------------------- */
.layout-side {
  position: sticky;
  top: calc(var(--header-height) + 1.5rem);
  display: grid;
  gap: 24px;
}

/* 侧栏不做卡片：只用发丝线与留白分隔，像杂志页边的注 */
.sidebar-card {
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
}
.sidebar-card:first-child { padding-top: 0; border-top: 0; }

.sidebar-title {
  margin: 0 0 12px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* 作者位：文字优先。
   头像只出现一次——Hero 右侧那幅装框插画；侧栏这里不再重复同一张图，
   改用一枚小号的刊头 kicker + 姓名开场，避免同页两张一模一样的头像。 */
.author-box { text-align: left; }
.author-kicker {
  margin: 0 0 8px;
  color: var(--sand-deep);
  font-family: var(--font-serif);
  font-size: 12px;
  letter-spacing: 0.18em;
}
.author-name { margin: 0 0 4px; font-size: 18px; }
.author-role {
  margin: 0 0 12px;
  color: var(--text-muted);
  font-size: 13px;
  letter-spacing: 0.04em;
}
.author-bio { margin: 0 0 16px; color: var(--text-secondary); font-size: 14px; text-align: left; }

/* 边注引文：中文不做斜体，改用沙色竖线 + 引号图标 */
.sidebar-quote {
  margin: 0 0 16px;
  padding-left: 12px;
  border-left: 2px solid var(--sand);
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.8;
}
.sidebar-quote .i { width: 16px; height: 16px; color: var(--sand-deep); }

.stat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
}
.stat { text-align: left; }
.stat-value {
  display: block;
  font-family: var(--font-serif);
  font-size: 20px;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.stat-label { display: block; font-size: 12px; color: var(--text-muted); letter-spacing: 0.05em; }

.sidebar-list { margin: 0; padding: 0; list-style: none; }
.sidebar-list li + li { margin-top: 12px; }
.sidebar-list a { font-size: 14px; font-weight: 500; }
.sidebar-list .sidebar-date {
  display: block;
  margin-top: 4px;
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 400;
}
.sidebar-list .project-link { display: inline-flex; align-items: center; gap: 8px; }
.sidebar-list .project-link .i { width: 15px; height: 15px; color: var(--text-muted); }

/* 08. 标签 ---------------------------------------------------------------- */
.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 标签做成"关键词"而不是胶囊：不用圆角、不用大色块 */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.6;
  text-decoration: none;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
    background-color var(--dur) var(--ease);
}
.tag:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
  text-decoration: none;
}
.tag .tag-count { color: var(--text-muted); font-size: 12px; }

.tag-filter {
  cursor: pointer;
  font-family: inherit;
}
.tag-filter[aria-pressed="true"] {
  color: var(--accent-contrast);
  background: var(--accent);
  border-color: var(--accent);
}
.tag-filter[aria-pressed="true"] .tag-count { color: var(--accent-contrast); opacity: 0.75; }

.tag-cloud { display: flex; flex-wrap: wrap; gap: 8px; }
.tag-cloud .tag { padding: 4px 12px; font-size: 14px; }
/* 标签索引页顶部的整片标签云（原来靠行内 style 写 margin-bottom） */
.tag-cloud-page { margin-bottom: 24px; }

.tag-section {
  padding-top: 32px;
  margin-top: 32px;
  border-top: 1px solid var(--hairline);
}
/* 标签云下面那条"共 22 个标签"原来紧贴着第一个分组的标题，
   看起来像一句没写完的话。首组补回同一条分隔线 + 同样的 32/32 呼吸，
   与组间节奏一致。 */
.tag-section:first-of-type {
  margin-top: 32px;
  padding-top: 32px;
  border-top: 1px solid var(--hairline);
}
.tag-section[hidden] { display: none; }

.tag-section-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 16px;
  font-size: 18px;
}
.tag-section-title .tag-count {
  padding: 4px 8px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 500;
}

/* 09. 文章正文 ------------------------------------------------------------ */
.article-wrap {
  max-width: var(--content-width);
  margin-inline: auto;
  padding: 3rem 24px 4rem;
}

.post-header {
  margin-bottom: 32px;
  padding-bottom: 24px;
  border-bottom: 1px solid var(--hairline);
}

.post-eyebrow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 16px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sand-deep);
}

.post-title {
  margin: 0 0 16px;
  font-size: 24px;
  line-height: 1.3;
}

.post-header .post-meta { font-size: 14px; margin-bottom: 0; }

/* 导语：杂志的首段，字号放大一档，左侧压一条沙色细线 */
.lede {
  margin: 24px 0 32px;
  padding-left: 20px;
  border-left: 2px solid var(--sand);
  color: var(--text-secondary);
  font-family: var(--font-serif);
  font-size: 18px;
  line-height: 1.8;
}

.prose { font-size: 16px; }

/* 章节编号用 CSS 计数器生成（§ 01 / § 02 …），不写死在 HTML 里，
   增删小节时编号不会错位。 */
.prose { counter-reset: sec; }

.prose h2 {
  counter-increment: sec;
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 48px 0 16px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
  font-size: 24px;
  scroll-margin-top: calc(var(--header-height) + 20px);
}
.prose h2::before {
  content: "§ " counter(sec, decimal-leading-zero);
  flex: none;
  color: var(--sand-deep);
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.06em;
}
.prose h3 {
  margin: 32px 0 12px;
  font-size: 18px;
  scroll-margin-top: calc(var(--header-height) + 20px);
}
.prose h4 { margin: 24px 0 8px; font-size: 16px; }

.prose ul, .prose ol { margin: 0 0 20px; }
.prose li { margin-bottom: 8px; }
.prose li > ul, .prose li > ol { margin-top: 8px; }
.prose ul > li::marker { color: var(--sand-deep); }

.prose strong { font-weight: 700; color: var(--text); }

.prose a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--accent) 34%, transparent);
  text-underline-offset: 4px;
  transition: color var(--dur) var(--ease), text-decoration-color var(--dur) var(--ease);
}
.prose a:hover { text-decoration-color: var(--accent); text-decoration-thickness: 2px; }

/* 提示块：四种语义各有一套"左边框 + 底色 + 图标"，
   对应文档站的 Note / Tip / Warning / Info。图标全部来自 assets/icons.svg，不用 emoji。
   四色都取自本站调色板，靠**色相**区分，而不是靠饱和度堆叠：
     说明 .note           石板蓝 --accent        （正文里最常见的"补充一句"）
     技巧 .note-tip       沙     --sand          （围巾色，站内的强调色）
     提示 .note-info      天光   --sky           （天空色的浅档）
     注意 .note-warning   墨     --text          （最重的一档，一眼看到）
   亮/暗主题共用同一批令牌，所以两套主题下四种提示块的相对轻重一致。 */
.prose .note {
  display: flex;
  gap: 12px;
  margin: 24px 0;
  padding: 16px 20px;
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--text-secondary);
}
.prose .note-tip {
  border-left-color: var(--sand);
  background: var(--sand-soft);
}
.prose .note-info {
  border-left-color: var(--sky);
  background: var(--sky-soft);
}
.prose .note-warning {
  border-left-color: var(--text);
  background: var(--bg-inset);
}

.prose .note-ic {
  flex: none;
  margin-top: 4px;
  line-height: 1;
  color: var(--accent);
}
.prose .note-tip .note-ic { color: var(--sand-deep); }
.prose .note-info .note-ic { color: var(--accent); }
.prose .note-warning .note-ic { color: var(--sand-deep); }
.prose .note-ic .i { display: block; width: 18px; height: 18px; }

.prose .note-body {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 16px;
}
.prose .note-body > *:last-child { margin-bottom: 0; }
.prose .note-title {
  margin: 0 0 8px;
  font-family: var(--font-serif);
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
}

/* 引用块：不再填底色。
   原来它和 .toc 都是"浅底 + 左边框 + 2px 圆角"的方块，一篇文章里
   目录与引用前后脚出现时几乎分不出谁是谁。现在只有 .toc 是"浮起的纸"
   （浅底 + 外框），引用退成"一条沙色竖线 + 无底"，版面上少一层灰块，
   也让 .lede / .sidebar-quote 的沙色竖线语言第一次真正统一。 */
.prose blockquote {
  margin: 24px 0;
  padding: 16px 20px;
  border-left: 2px solid var(--sand);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: none;
  color: var(--text-secondary);
}
.prose blockquote p:last-child { margin-bottom: 0; }

.prose code {
  padding: 4px 8px;
  border: 1px solid var(--hairline);
  border-radius: 2px;
  background: var(--bg-inset);
  color: var(--text);
  font-size: 14px;
}

/* 代码块：顶部 header（左语言名 · 右复制），文本区独立横向滚动。
   语言名写死在 HTML 的 .code-lang 里（可维护：改语言就改那一个词），
   .code-block[data-lang] 同时给脚本用来拼无障碍标签。 */
.code-block {
  position: relative;
  margin: 24px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-inset);
  overflow: hidden;
}

.code-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 36px;
  padding: 4px 8px 4px 16px;
  border-bottom: 1px solid var(--hairline);
  background: var(--bg-subtle);
}
.code-lang {
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 24px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.prose pre {
  margin: 0;
  padding: 16px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  font-size: 14px;
  line-height: 24px;
  tab-size: 2;
}
/* 已经被 .code-block 包住的 pre 不再自带边框与底色，避免"框里套框" */
.code-block > pre {
  border: 0;
  border-radius: 0;
  background: none;
}
.prose pre code {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  font-size: inherit;
  color: var(--text);
  white-space: pre;
}

.code-copy {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 28px;
  padding: 4px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-muted);
  font-size: 12px;
  font-family: var(--font-sans);
  cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
    background-color var(--dur) var(--ease);
}
.code-copy .i { width: 13px; height: 13px; }
/* 两个图标叠在同一个按钮里，靠 .is-copied 切换：复制 → 对勾 */
.code-copy .ic-done { display: none; }
.code-copy.is-copied .ic-done { display: block; }
.code-copy.is-copied .ic-copy { display: none; }
.code-copy:hover {
  color: var(--text);
  border-color: var(--accent);
  background: var(--bg-elevated);
}
.code-copy.is-copied { color: var(--accent); border-color: var(--accent); }

/* 表格：只有横向 1px 分隔线，没有竖线、没有斑马纹、没有底色
   （对齐文档站的表格：表头 14px / w600 / padding 0 8px 8px 0，
   单元格 padding 8px 8px 8px 0）。
   唯一的本站差异是表头仍用宋体 —— 和 h2/h3 保持同一套"杂志"语气。 */
.prose table {
  width: 100%;
  margin: 24px 0;
  border-collapse: collapse;
  font-size: 14px;
  line-height: 1.6;
}
.prose th, .prose td {
  padding: 8px 8px 8px 0;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  text-align: left;
  vertical-align: top;
}
.prose thead th {
  padding: 0 8px 8px 0;
  background: none;
  font-family: var(--font-serif);
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.prose tbody tr:last-child td { border-bottom: 0; }
.prose td[data-numeric] { font-variant-numeric: tabular-nums; }

/* 宽表格的横向滚动容器：375px 下由它自己滚，绝不撑破页面。
   tabindex/role 由 HTML 写在 <div class="table-scroll"> 上会更严谨，
   但那是 20+ 个 tab 停靠点，收益不抵成本，这里只保证不溢出。 */
.table-scroll {
  max-width: 100%;
  margin: 24px 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
.table-scroll table { margin: 0; min-width: 420px; }

/* 目录：编号用 01 / 02 的前导零，同样由计数器生成 */
.toc {
  margin: 2rem 0;
  padding: 20px 24px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  counter-reset: toc;
}
.toc-title {
  margin: 0 0 12px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.toc ol { margin: 0; padding: 0; list-style: none; }
.toc li { margin-bottom: 8px; font-size: 14px; counter-increment: toc; }
.toc li::before {
  content: counter(toc, decimal-leading-zero);
  margin-right: 8px;
  color: var(--sand-deep);
  font-family: var(--font-mono);
  font-size: 12px;
}

/* 文章页脚 */
.post-footer {
  margin-top: 3rem;
  padding-top: 24px;
  border-top: 1px solid var(--hairline);
}
.post-footer .tag-list { margin-bottom: 1.5rem; }
.post-footer .post-nav-label { color: var(--text-muted); }

.post-nav {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}
.post-nav a {
  display: block;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text);
  text-decoration: none;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.post-nav a:hover {
  border-color: var(--border-strong);
  background: var(--surface-warm);
  color: var(--text);
  text-decoration: none;
}
.post-nav .post-nav-label {
  display: block;
  margin-bottom: 4px;
  font-size: 12px;
  letter-spacing: 0.08em;
}
.post-nav .post-nav-title { font-family: var(--font-serif); font-size: 16px; font-weight: 600; }
.post-nav .is-next { text-align: right; }

/* 页脚"延伸阅读"：横排三条 */
.related {
  margin-top: 32px;
  padding-top: 24px;
  border-top: 1px solid var(--hairline);
}
.related-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 24px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}
.related-list li { display: flex; flex-direction: column; gap: 4px; }
.related-list a { font-family: var(--font-serif); font-size: 16px; font-weight: 600; }
.related-list .related-meta { color: var(--text-muted); font-size: 13px; }

/* 10. 归档页 -------------------------------------------------------------- */
.page-head {
  padding: 48px 0 24px;
  border-bottom: 1px solid var(--hairline);
  background: linear-gradient(180deg, var(--bg-subtle), var(--bg));
}
.page-title {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0 0 8px;
  font-size: 24px;
}
.page-title::before {
  content: "";
  flex: none;
  width: 12px;
  height: 2px;
  background: var(--sand);
}
.page-desc { max-width: 640px; margin: 0; color: var(--text-secondary); }

.year-group { margin-bottom: 40px; }

.year-label {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 16px;
  font-family: var(--font-mono);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--text);
}
.year-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hairline);
}
.year-label .year-count {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
}

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

.archive-item {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  gap: 8px 16px;
  /* 16px：归档一页要扫完 6~20 条，条目之间太挤会把标题与摘要压成一片 */
  padding: 16px 0;
  border-bottom: 1px solid var(--hairline);
  transition: background-color var(--dur) var(--ease);
}
.archive-item:hover { background: var(--bg-subtle); }

.archive-date {
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  /* 与标题首行做视觉对齐；2px 不在 4px 网格上（审计会判越界），用 4px */
  padding-top: 4px;
}
.archive-title {
  font-family: var(--font-serif);
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
}
.archive-title:hover { color: var(--accent); text-decoration: none; }
.archive-summary { margin: 4px 0 0; color: var(--text-muted); font-size: 14px; }

@media (max-width: 560px) {
  .archive-item { grid-template-columns: 1fr; gap: 4px; }
}

/* 11. 页脚 ---------------------------------------------------------------- */
.site-footer {
  margin-top: auto;
  padding: 40px 0 32px;
  border-top: 1px solid var(--hairline);
  background-color: var(--bg-subtle);
  color: var(--text-muted);
  font-size: 14px;
  /* 页脚压满格纹，作为整本站的"纸张"收尾 */
  background-image:
    repeating-linear-gradient(90deg, var(--graph) 0 1px, transparent 1px 64px),
    repeating-linear-gradient(0deg, var(--graph) 0 1px, transparent 1px 64px);
}

.footer-grid {
  display: grid;
  grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr));
  gap: 2rem;
  margin-bottom: 2rem;
}
.footer-brand { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.footer-brand .brand-mark { width: 28px; height: 28px; }
.footer-brand strong { color: var(--text); font-family: var(--font-serif); font-size: 16px; }
.footer-about { margin: 0; max-width: 340px; }
.footer-heading {
  margin: 0 0 12px;
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.footer-links { margin: 0; padding: 0; list-style: none; }
.footer-links li { margin-bottom: 8px; }
.footer-links a { color: var(--text-secondary); }
.footer-links a:hover { color: var(--accent); }

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-top: 24px;
  border-top: 1px solid var(--hairline);
}
.footer-bottom p { margin: 0; }

@media (max-width: 720px) {
  .footer-grid { grid-template-columns: 1fr; gap: 24px; }
}

/* 回到顶部 */
.back-to-top {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 90;
  width: 42px;
  height: 42px;
  box-shadow: var(--shadow-md);
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease),
    visibility var(--dur) var(--ease);
}
.back-to-top.is-visible { opacity: 1; visibility: visible; transform: translateY(0); }

@media print { .back-to-top { display: none !important; } }

/* 12. 404 与提示页 -------------------------------------------------------- */
.center-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 62vh;
  padding: 4rem 24px;
  text-align: center;
}
.center-code {
  margin: 0;
  font-family: var(--font-serif);
  font-size: clamp(4rem, 16vw, 7rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--accent); /* 不支持 background-clip:text 时的回退颜色 */
  background: linear-gradient(135deg, var(--accent), var(--sand-deep));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.center-title { margin: 8px 0; font-size: 24px; }
.center-desc { max-width: 480px; margin: 0 auto 24px; color: var(--text-secondary); }
.center-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: center; }

/* 13. 工具类 / 动效 ------------------------------------------------------- */
.muted { color: var(--text-muted); }
.is-hidden { display: none !important; }

/* 项目页的分组说明：原来靠行内 style 写负边距（会破坏 4px 网格），现在收进类里 */
.section-desc { margin: 0 0 24px; color: var(--text-secondary); font-size: 16px; }

@media (max-width: 1120px) {
  .layout { grid-template-columns: minmax(0, 1fr) 248px; gap: 3rem; }
}
@media (max-width: 960px) {
  .layout { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
  .layout-side { position: static; }
}

@media (max-width: 480px) {
  .container { padding-inline: 16px; }
  .article-wrap { padding: 32px 16px 48px; }
  /* 窄屏卡片内边距收到 16px；书脊线仍然贴左沿（第 06 节），
     不能在窄屏又退回"线内缩 12px + 左内边距 28px"那套不对称的老写法 */
  .post-card { padding: 16px; }
  .prose { font-size: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .post-card:hover, .btn:hover, .post-nav a:hover { transform: none; }
}

/* 14. 打印样式 ------------------------------------------------------------ */
@media print {
  :root {
    --bg: #fff;
    --bg-subtle: #fff;
    --bg-elevated: #fff;
    --bg-inset: #f5f5f5;
    --text: #000;
    --text-secondary: #222;
    --text-muted: #444;
    --border: #bbb;
    --border-strong: #888;
    --accent: #000;
    --shadow-sm: none;
    --shadow-md: none;
    --shadow-lg: none;
  }

  html { font-size: 12pt; }

  body {
    background: #fff !important;
    color: #000;
    line-height: 1.6;
  }

  .site-header,
  .site-footer,
  .post-nav,
  .toc,
  .code-copy,
  .back-to-top,
  .progress-bar,
  .skip-link,
  .filter-bar,
  .hero-actions,
  .layout-side { display: none !important; }

  .article-wrap,
  .container { max-width: none; padding: 0; }

  .prose { font-size: 12pt; }

  .prose a { color: #000; text-decoration: underline; }
  .prose a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.82em;
    color: #555;
    word-break: break-all;
  }

  .prose h2, .prose h3, .prose h4 { page-break-after: avoid; break-after: avoid; }
  .prose pre, .prose blockquote, .prose table, .post-card { page-break-inside: avoid; break-inside: avoid; }

  .post-header { border-bottom: 1px solid #000; padding-bottom: 1rem; }
  .post-title { font-size: 20pt; }
  .prose h2 { border-top: 0; padding-top: 0; }
  .prose h2::before, .toc li::before { color: #444; }
  .hero-kicker { color: #444; }
}

/* 15. 徽章 / 项目卡片 / 时间线 / 关于页列表 --------------------------------- */
.badge-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  /* 与上一段（副标题）的间隔由 .hero-subtitle 的 margin-bottom 给出，
     与下一行按钮的间隔由 .hero-actions 的 margin-top 给出 —— 不再两头各写一半 */
  margin: 0;
  padding: 0;
  list-style: none;
}
/* 徽章不是胶囊：左侧压一条沙色细线，做成"标签条" */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px;
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--sand);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
}
.badge svg { width: 14px; height: 14px; color: var(--sand-deep); }

/* 项目卡片（2026-09 重做）
   原来 3 列 × 288px、卡片内边距被 .atropos-inner > .project-card 清零，
   一行 4~5 条中文要点在 300px 宽的列里要折 10 行，文字几乎贴着边框 —— 又挤又碎。
   现在：两列大卡（≥720px）、24px 内边距、要点用沙色圆点、
   语言标签改成 GitHub 风格的"圆点 + 名称"、页脚按钮贴住卡片底部。 */
.project-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 720px) {
  .project-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* 卡片按内容高度排布，不再被 grid 拉成同一行里最高的那张。
   原来 align-items 默认 stretch + 卡片 height:100%，于是一行里两张卡
   内容差一倍时，短的那张下半截是一大块空白（实测 EPF-Unlocker 那行
   右卡底部空出约 170px），看上去像"洞"。现在空白回到卡片之间，
   卡片边框勾出的始终是实实在在的内容。 */
.project-grid { align-items: start; }
.project-grid > .card-3d { height: auto; }
.project-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: auto;
  padding: 24px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
/* 悬停时卡片顶沿亮起一条"天光 → 沙色"渐变，作为卡片唯一的彩色装饰 */
.project-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, var(--sky), var(--sand));
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.project-card:hover {
  border-color: var(--border-strong);
  background: var(--surface-warm);
}
.project-card:hover::before { opacity: 1; }
.project-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--hairline);
}
.project-name {
  margin: 0;
  /* 项目名大多是 `starmerx_listing`、`EPF-Unlocker` 这类拉丁标识符，
     括注里才出现中文。用 --font-latin-serif：拉丁走 Inter，中文仍是宋体，
     不再让 SimSun 的拉丁字形去渲染仓库名。 */
  font-family: var(--font-latin-serif);
  /* 18px：项目名要与卡片正文（14px）拉开一档，
     否则整张卡片"什么都差不多大" */
  font-size: 18px;
  font-weight: 600;
  line-height: 1.4;
  overflow-wrap: anywhere;
}
.project-name a { color: var(--text); }
.project-name a:hover { color: var(--accent); text-decoration: none; }
/* 语言：圆点 + 名称，比带边框的小方块更轻，也不再和标题抢注意力 */
.lang-tag {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.lang-tag::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--sand-deep);
}
.project-desc {
  margin: 16px 0 0;
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.7;
}
.project-points {
  /* flex:1 吃掉卡片里剩余的高度，页脚因此统一贴底（同一行卡片的按钮对齐）；
     这样也避免用 margin-top:auto 撑出非 4 倍数的高度（审计会判越界） */
  flex: 1 1 auto;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.7;
}
.project-points li {
  position: relative;
  padding-left: 16px;
  margin-bottom: 8px;
}
.project-points li:last-child { margin-bottom: 0; }
.project-points li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--sand);
}
.project-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
  font-size: 14px;
}
/* 页脚动作用小按钮，而不是一行不带边框的蓝字 */
.project-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 28px;
  padding: 4px 12px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 500;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.project-link:hover {
  border-color: var(--border-strong);
  background: var(--bg-elevated);
  color: var(--accent);
  text-decoration: none;
}
.project-link svg { width: 14px; height: 14px; }
.project-link .i { width: 14px; height: 14px; }
.project-group { margin-bottom: 48px; }
.project-group:last-of-type { margin-bottom: 0; }

.timeline {
  margin: 0;
  padding: 0 0 0 1.4rem;
  list-style: none;
  border-left: 1px solid var(--hairline);
}
.timeline li { position: relative; padding-bottom: 24px; }
.timeline li:last-child { padding-bottom: 0; }
.timeline li::before {
  content: "";
  position: absolute;
  left: calc(-1.4rem - 4px);
  top: 0.4rem;
  width: 8px;
  height: 8px;
  border-radius: 1px;
  background: var(--sand-deep);
}
.timeline-date {
  display: block;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--sand-deep);
}
.timeline p { margin: 4px 0 0; color: var(--text-secondary); font-size: 16px; }

.info-list { margin: 0; padding: 0; list-style: none; }
.info-list li {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--hairline);
  font-size: 16px;
}
.info-list li:last-child { border-bottom: 0; }
.info-label { flex: none; width: 6.2em; color: var(--text-muted); }
.info-value { flex: 1 1 auto; min-width: 0; color: var(--text-secondary); overflow-wrap: anywhere; }

@media (prefers-reduced-motion: reduce) {
  .project-card:hover { transform: none; }
}
@media print {
  .project-card { break-inside: avoid; page-break-inside: avoid; box-shadow: none; }
}

/* ---- 16. 分组标题里的计数（项目页 / 标签页共用） ---- */
.section-title .tag-count {
  margin-left: 0.7em;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--text-muted);
  white-space: nowrap;
}

/* ==========================================================================
   17. 3D 升级：Hero 星图背景 + Atropos 卡片景深
   --------------------------------------------------------------------------
   本节是后加的，只追加、不改动上面的任何分节。
   设计令牌全部沿用第 01 节（--accent / --bg / --border / --radius / --shadow-*），
   所以主色一换成"石板蓝 #35485a + 沙色 #b4a08c"，
   hero3d.js 里 THREE.Color(accent).lerp(bg, …) 算出来的星图与连线自动跟着走。
   hero3d.js 里的 FALLBACK 常量已同步为同一组值（脚本兜底路径用）。
   依赖的第三方文件只有两个，都是本地自带（无 CDN、无外部请求）：
     · assets/vendor/three.min.js     three.js r160   MIT（只被 hero3d.js 使用）
     · assets/vendor/atropos.min.css  Atropos 2.0.2  MIT（本节的 .atropos-* 规则）
   ========================================================================== */

/* ---- 17.1 Hero 的 3D 背景 ------------------------------------------------- */
/* .hero 已经是 position:relative + overflow:hidden，这里只负责铺满，不参与布局。
   层序：.hero::before（浅天色渐层）在最下，canvas 是 z-index:0，
   遮罩是 z-index:1，内容 .hero > .container 是 z-index:2。 */
.hero-3d {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none; /* 绝不挡住文字选择与点击 */
}
/* 内容层压在背景之上 */
.hero > .container {
  position: relative;
  z-index: 2;
}

/* 可读性遮罩：从正文一侧的不透明背景色渐隐到透明，
   保证两套主题下标题与正文都维持 AA 对比度；纯装饰，不接收指针事件。
   右侧留得比左边透，让星图在头像卡片背后仍然看得见。 */
.hero-3d-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  pointer-events: none;
  background:
    linear-gradient(100deg,
      var(--bg) 2%,
      color-mix(in srgb, var(--bg) 80%, transparent) 30%,
      color-mix(in srgb, var(--bg) 34%, transparent) 62%,
      color-mix(in srgb, var(--bg) 14%, transparent) 100%),
    linear-gradient(180deg,
      transparent 62%,
      color-mix(in srgb, var(--bg) 50%, transparent) 100%);
}
@media (max-width: 860px) {
  .hero-3d-veil {
    background:
      linear-gradient(180deg,
        color-mix(in srgb, var(--bg) 62%, transparent) 0%,
        color-mix(in srgb, var(--bg) 36%, transparent) 55%,
        color-mix(in srgb, var(--bg) 58%, transparent) 100%);
  }
}

/* ---- 17.2 Atropos 卡片景深 ------------------------------------------------ */
/* 卡片外面多包了两层（.atropos > .atropos-rotate > .atropos-inner），
   只用来做 3D 变换，不改变卡片自身的任何观感 */
.card-3d {
  display: block;            /* <li> 在 grid 里，显式 block 更稳妥 */
  height: 100%;
  --atropos-shadow: 0 20px 40px -24px rgba(27, 31, 38, 0.42);
  --atropos-glow: rgba(255, 255, 255, 0.12);
}
[data-theme="dark"] .card-3d {
  --atropos-shadow: 0 26px 52px -24px rgba(0, 0, 0, 0.9);
  --atropos-glow: rgba(200, 216, 228, 0.05);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .card-3d {
    --atropos-shadow: 0 26px 52px -24px rgba(0, 0, 0, 0.9);
    --atropos-glow: rgba(200, 216, 228, 0.05);
  }
}
/* 卡片里的文字本来就不该被拖选（照抄 vendor/atropos.min.css 里
   .atropos-rotate-touch 的写法），否则倾斜时容易误选 */
.card-3d .atropos-rotate {
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
}

/* 阴影由 CSS 自己画（Atropos 的 shadow/highlight 都关掉了），
   内联在 .atropos-rotate 里，贴住卡片的直角（本站圆角只有 2px） */
.card-3d .atropos-shadow {
  display: block;
  z-index: 0;
  inset: 6px 10px;
  border-radius: var(--radius-sm);
  background: var(--atropos-shadow);
  filter: none;
  opacity: 0;
}

/* 高光：含蓄一点，跟着主题走 */
.card-3d .atropos-highlight {
  display: block;
  background-image: radial-gradient(circle at 50%, var(--atropos-glow), transparent 55%);
  /* .atropos-highlight 的 z-index:0 会盖住 .project-card 的自动层叠上下文，
     这里压到卡片之下 —— 卡片自身是不透明背景，高光只会从边缘透出来一点点 */
  z-index: -1;
  opacity: 0;
}
.card-3d.is-tilting .atropos-shadow { opacity: 1; }
.card-3d.is-tilting .atropos-highlight { opacity: 1; }

/* 卡片自己的 box-shadow 交给 atropos-shadow 表现，避免两层阴影打架 */
.card-3d .post-card,
.card-3d .project-card { box-shadow: none; }
.card-3d .post-card:hover,
.card-3d .project-card:hover {
  border-color: var(--border-strong);
  /* 悬停位移交给 3D 变换（activeOffset），不再叠加 translateY */
  transform: none;
}
/* 曾经这里写着 .card-3d .atropos-inner > .project-card { padding: 0 }，
   把卡片内边距清零后文字几乎贴住边框（实测 padding 0px、正文距边框 1px），
   这就是"3D 卡片太紧凑"的根因。卡片自己带边框与底色，内边距必须留着。 */
.card-3d > .atropos { height: 100%; }

/* 首页搜索筛选给卡片加 .is-hidden（display:none）。属性现在挂在外层 <li> 上，
   但为了避免老浏览器 / 兜底路径下漏掉，被隐藏时连内层卡片一起收干净 */
.card-3d.is-hidden .post-card,
.card-3d:has(.is-hidden) .post-card { display: none; }

/* 景深：首页文章卡片的标题里铺了一整张 .post-card-title a::after 点击热区，
   一旦给标题加 3D 位移就会把那个热区的包含块改掉、毁掉「整卡可点」，
   所以标题不做位移，改成给元信息、摘要与页脚做位移 */
[data-atropos-offset] { will-change: transform; }

/* reduced-motion：一条兜底规则，保证任何情况下都没有 3D 位移 */
@media (prefers-reduced-motion: reduce) {
  .card-3d .atropos-scale,
  .card-3d .atropos-rotate,
  .card-3d [data-atropos-offset] { transform: none !important; }
  .card-3d .atropos-shadow,
  .card-3d .atropos-highlight { opacity: 0 !important; }
}

/* ---- 17.3 打印：装饰一律不出现 ------------------------------------------- */
@media print {
  .hero-3d,
  .hero-3d-veil,
  .hero-portrait,
  .card-3d .atropos-shadow,
  .card-3d .atropos-highlight { display: none !important; }
}

/* ==========================================================================
   18. 触达目标与响应式标题档位（对齐 DESIGN.md 第 1 / 4 / 5 节）
   --------------------------------------------------------------------------
   18.1 触达目标 ≥24×24（WCAG 2.2 SC 2.5.8）
        句子行文里的内联链接是唯一豁免项：给它们加 padding 会撑开行距、
        改变换行。所以这里只处理目录、侧栏、页脚、列表与卡片里那些
        "块状"链接 —— inline-flex 撑到 24px 高，文字仍然垂直居中，
        不加 padding、不改行距。
   ========================================================================== */
.section-link,
.toc li a,
.sidebar-list a,
.footer-links a,
.archive-title,
.project-name a,
.post-card-title a,
.project-link,
.read-more,
.related-list a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}
.related-list a { align-items: flex-start; }

/* 18.2 响应式标题档位
   全部取自字号令牌（12/13/14/16/18/20/24/30/36/44）。这里不用 vw 插值：
   插值会算出 30.4px / 35.2px 这种不在模数上的字号。
   档位：窄屏 24 → ≥600px 30 → ≥960px 36；Hero 标题窄屏 30 → ≥960px 44。 */
@media (min-width: 600px) {
  .post-title,
  .page-title { font-size: 30px; }
}
@media (min-width: 960px) {
  .hero-title { font-size: 44px; }
  .post-title,
  .page-title { font-size: 36px; }
}

/* ==========================================================================
   19. 标题锚点（h2 / h3 的 "#" 链接）
   --------------------------------------------------------------------------
   对齐文档站的做法：<a class="heading-anchor" href="#id" aria-label="本节链接"
   tabindex="-1">#</a>。
     · tabindex="-1" —— 整页几十个标题，如果都进 Tab 序列，键盘用户要按几十次
       才能走过正文；锚点本身仍是可点击、可被读屏定位的链接；
     · aria-label —— 读屏念的是"本节链接"，而不是"井号"；
     · 24×24 最小尺寸 —— 它不在 <p>/<li> 里，属于审计里"块状目标"那一类，
       必须满足 WCAG 2.2 SC 2.5.8；
     · 默认 opacity:0，hover 标题或键盘聚焦时才出现，不干扰阅读。
   ========================================================================== */
.prose .heading-anchor {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  margin-left: 8px;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-weight: 500;
  text-decoration: none;
  opacity: 0;
  transition: opacity var(--dur) var(--ease), color var(--dur) var(--ease);
}
.prose h2:hover > .heading-anchor,
.prose h3:hover > .heading-anchor,
.prose .heading-anchor:focus-visible { opacity: 1; }
.prose .heading-anchor:hover {
  color: var(--accent);
  text-decoration: none;
}

/* ==========================================================================
   20. 图表（mermaid）与灯箱
   --------------------------------------------------------------------------
   20.1 图表容器
        .diagram 是 <figure>，一条边框把它和正文分开；顶栏沿用代码块的
        .code-bar 语言（左"mermaid"语言标识 + 标题，右工具按钮）。
        未渲染时 .mermaid 里的内容就是**图表源码**，按等宽代码块呈现 ——
        这就是无 JS / mermaid 加载失败时的降级，不会是空白。
   20.2 灯箱
        全屏遮罩里放大看同一张图，支持 ±缩放 / 重置 / Esc / 点背景关闭。
        节点由 JS 在第一次打开时创建，关闭后即刻移除 —— 页面上不留隐藏的
        巨大 DOM，也不会给审计留下不可见的字号/间距。
   ========================================================================== */

.diagram {
  margin: 24px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  overflow: hidden;
}

.diagram-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  min-height: 36px;
  padding: 4px 8px 4px 16px;
  border-bottom: 1px solid var(--hairline);
  background: var(--bg-subtle);
}
.diagram-lang {
  flex: none;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 24px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.diagram-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  line-height: 24px;
  font-weight: 500;
  color: var(--text-secondary);
}
.diagram-tools {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
/* author 样式里的 display 会盖掉 UA 的 [hidden]{display:none}，必须显式写一条：
   没有 JS 时工具条整体不出现，图表区只剩"源码 + mermaid 语言标识" */
.diagram-tools[hidden] { display: none; }
.diagram-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 28px;
  padding: 4px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--font-sans);
  font-size: 12px;
  cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.diagram-btn .i { width: 13px; height: 13px; }
.diagram-btn:hover:not(:disabled) { color: var(--text); border-color: var(--accent); }
.diagram-btn:disabled { opacity: 0.45; cursor: default; }
.diagram-btn .ic-done { display: none; }
.diagram-btn.is-copied .ic-done { display: block; }
.diagram-btn.is-copied .ic-copy { display: none; }
.diagram-btn.is-copied { color: var(--accent); border-color: var(--accent); }

.diagram-body { position: relative; }

/* 未渲染：源码原样显示（无 JS 时的降级形态，带上面的 mermaid 语言标识） */
.diagram .mermaid {
  margin: 0;
  padding: 16px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  background: var(--bg-inset);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.7;
  white-space: pre;
  tab-size: 2;
}
/* 已渲染：mermaid 画的 SVG 居中，底色换成图表专用画布色 */
.diagram .mermaid[data-processed="true"] {
  padding: 16px;
  background: var(--diagram-bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  white-space: normal;
  text-align: center;
}
.diagram .mermaid[data-processed="true"] svg {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
}
.diagram-body[data-zoomable] { cursor: zoom-in; }
.diagram-body[data-zoomable]::after {
  content: "点击放大";
  position: absolute;
  right: 12px;
  bottom: 12px;
  padding: 4px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-muted);
  font-size: 12px;
  line-height: 20px;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease);
}
.diagram-body[data-zoomable]:hover::after,
.diagram-body[data-zoomable]:focus-visible::after { opacity: 1; }

.diagram-caption {
  margin: 0;
  padding: 8px 16px 12px;
  border-top: 1px solid var(--hairline);
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1.7;
}

/* ---- 20.2 灯箱 ---- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(20, 21, 25, 0.78);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.lightbox-panel {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 1120px;
  max-height: 100%;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.lightbox-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  min-height: 44px;
  padding: 8px 8px 8px 16px;
  border-bottom: 1px solid var(--hairline);
  background: var(--bg-subtle);
}
.lightbox-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 14px;
  line-height: 24px;
  font-weight: 500;
  color: var(--text-secondary);
}
.lightbox-tools { display: inline-flex; align-items: center; gap: 4px; }
.lightbox-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  min-height: 32px;
  padding: 4px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.lightbox-btn:hover:not(:disabled) { color: var(--text); border-color: var(--accent); }
.lightbox-btn:disabled { opacity: 0.45; cursor: default; }
.lightbox-zoom {
  min-width: 56px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.lightbox-stage {
  flex: 1 1 auto;
  min-height: 0;
  padding: 16px;
  overflow: auto;
  overscroll-behavior: contain;
  background: var(--diagram-bg);
  cursor: grab;
}
.lightbox-stage svg { display: block; max-width: none; height: auto; }

@media (max-width: 480px) {
  .diagram-bar { padding: 4px 8px 8px 12px; }
  .lightbox { padding: 12px; }
  .lightbox-stage { padding: 8px; }
}

@media print {
  .heading-anchor,
  .diagram-tools,
  .lightbox { display: none !important; }
}
