/* ============================================================
   bazicn 门户 · 文章排版
   源文件：tools/content/assets/article.css（门户仓库内相对路径）
   产物：   build/article.css（由 build-content.mjs 逐字节拷贝）
   ⚠ 本文件是**公开分发**的（切换到生产后 /article.css 可直接 curl）。
     注释里不要写服务器绝对路径与内部仓库名 —— 那等于把构建机的目录结构
     写进公网可读的文件。守卫 check-article-page.mjs [3b] 对此有断言。
   引用：   <link rel="stylesheet" href="/article.css">（排在 styles.css 之后）
   ------------------------------------------------------------
   🔴 两条硬纪律，守卫 check-article-page.mjs [3] 机械断言：
     1. **零颜色字面量**。全部 var(--color-*)。半透明一律走
        color-mix(in oklab, <token> N%, transparent)，不写 rgb()/rgba()/hsl()。
        （transparent 是关键字不是字面量；oklab 混合在浅色宣纸底上
        比 sRGB 插值少一层灰浊。）
     2. **所有 font-size 的计算值 ≥ 0.625rem（10px）**。10px 是全站字号硬下限，
        clamp() 的第一个参数同样受这条约束。

   字号刻度就地定义在 --article-* 里，集中一处、附理由。
   tokens.css 目前只导出了 --text-2xs(11px) / --text-3xs(10px) 两级
   （它们是 W5 为"状态标签"这类极小字加的），没有正文刻度可用；
   本文件不去 tokens.css 里补 —— 那份是设计真源的落地副本，
   门户这边手改会与真源静默分叉。刻度先在这里落地并跑一段时间，
   等它稳定了再走 design 真源提升为全站 token。

   ══ 中文长文排版的取值与理由（不要照搬英文 prose）══════════════

   [A] 正文 16px，不就近取 14px。
       汉字是**全字面框**排布：一个字撑满 em 方框，笔画从边到边。
       14px 下宋体/衬线体的横画只有约 1 物理像素，在非 HiDPI 屏上会掉进
       亚像素渲染变灰、变糊；而拉丁小写字母的 x-height 只占 em 的 ~0.5，
       同样 14px 的拉丁文实际墨迹比汉字小得多，所以英文 prose 的 14px
       经验值不能平移过来。ziwei 的阅读区裁决同为 16px 取整。

   [B] 行高 1.9（正文），不是拉丁 prose 惯用的 1.5–1.65。
       同样源于全字面框：汉字之间**没有**拉丁文那种由 x-height 以下、
       ascender 以上的空白自然形成的行内留白，字块贴着字块。
       中文段落的"气口"只能靠 line-height 让出来。
       1.9 × 16px = 30.4px 行距，对应约 0.9 个字高的行间空白 —— 低于 1.75
       会出现"上下行笔画黏连"的观感，高于 2.0 则一行一行散开、丢失段落块感。
       标题反过来收紧（1.35/1.5）：字号大、字重高，同样的比例会显得松垮。

   [C] 行宽用 em（≈字数）而不是 ch。
       ch 是 "0" 的宽度，对等宽拉丁有意义，对汉字毫无意义（1 汉字 = 1em）。
       36em ≈ 每行 36 个汉字，落在中文阅读舒适区 30–40 字之间。

   [D] 中西文混排间距。
       正确做法是在中文与拉丁/数字之间插入约 1/8 em 的间隙。
       CSS 的 text-autospace 正是干这个的，但支持面还很窄 ⇒ 这里**声明它**
       作为渐进增强，并把回退纪律写进作者文档：**md 源里中英之间手写半角空格**
       （所谓"盘古之白"）。绝不用全局 letter-spacing 冒充 —— 那会把汉字之间
       也一起撑开，破坏汉字最重要的均匀字距，且让标点更松。

   [E] 标点挤压。
       中文全角标点自带左右各半个字宽的空白，连续标点（」、）与行首行尾的
       标点会留出难看的空洞。text-spacing-trim: normal 是规范给的解法
       （行首行尾及相邻标点收掉多余的半角），同样按渐进增强声明：
       不支持的浏览器只是维持现状，不会错位。

   [F] 禁则处理靠浏览器默认的 CJK 换行规则（。，、」等不许出现在行首）。
       不设 word-break: break-all —— 那会把拉丁单词从中间劈开。
       长 URL 单独用 overflow-wrap 处理。

   [G] 段落用**段间距**而不是首行缩进 2em。
       纸媒的首行缩进依赖固定行宽与高密度排版；屏幕上行宽是流动的、
       段落之间还夹着标题/引用/代码块，缩进的段首识别度明显弱于空行。
   ============================================================ */

.article-page {
  /* ── 字号刻度（全部 ≥ 0.625rem） ───────────────────────── */
  --article-h1: clamp(1.75rem, 1.15rem + 2.6vw, 2.5rem);
  --article-h2: clamp(1.3rem, 1.06rem + 1.05vw, 1.55rem);
  --article-h3: 1.125rem;
  --article-text: 1rem;          /* [A] */
  --article-lede: 1.0625rem;
  --article-code: 0.9375rem;     /* 等宽拉丁字面小，15px 与正文 16px 视觉等重 */
  --article-meta: 0.8125rem;
  --article-kicker: var(--text-2xs);

  /* ── 行高 ─────────────────────────────────────────────── */
  --article-leading: 1.9;        /* [B] */
  --article-leading-tight: 1.35;
  --article-leading-code: 1.7;   /* 代码行内多为拉丁，1.9 会散 */

  /* ── 度量 ─────────────────────────────────────────────── */
  --article-measure: 36em;       /* [C] ≈ 36 个汉字 */
  --article-rhythm: 1.2em;       /* 段间距：约 0.63 行，line-height 已提供大量呼吸 */
  --article-anchor-offset: 5.25rem; /* 粘性页眉 min-height 4.25rem + 1rem 余量 */

  padding-block: clamp(1.75rem, 1rem + 3vw, 3.25rem) clamp(3rem, 2rem + 4vw, 5rem);
}

/* 页面级 smooth scroll 由 styles.css 打开（html { scroll-behavior: smooth }）。
   点目录锚点时它是好事，但对前庭不适用户是明确的伤害源，这里补上退出口。 */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

/* ── 版面骨架 ─────────────────────────────────────────────
   窄屏单列，DOM 顺序即视觉顺序：标题 → 目录 → 正文 → 上下篇。
   宽屏把目录移到右栏并 sticky。用 grid-template-areas 而不是
   flex + order：order 会让 Tab 焦点顺序与视觉顺序分叉。 */
.article-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    'head'
    'rail'
    'prose'
    'pager';
  gap: 0;
}

.article-head { grid-area: head; }
.rail { grid-area: rail; }
.prose { grid-area: prose; }
.article-pager { grid-area: pager; }

@media (min-width: 62rem) {
  .article-grid {
    grid-template-columns: minmax(0, 1fr) 14rem;
    grid-template-areas:
      'head  rail'
      'prose rail'
      'pager rail';
    column-gap: clamp(2rem, 4vw, 4rem);
  }

  /* 契约明确允许 outline 为空（无标题短文、以及只有 h3 没有 h2 的文章）——
     那时模板不输出 <nav class="rail">，但上面这条规则仍然无条件留出 14rem 的
     目录列 + 最多 4rem 的 column-gap，桌面端会是一条永远空着的右栏、正文整体
     偏左。守卫 [9] 的锚点断言在这两种情形下 railIds=[] 与 h2Ids=[] 相等，
     全绿，所以它不会被发现。
     用 :has() 退回单列。不支持 :has() 的浏览器会把整条规则判为无效并忽略，
     行为退化成今天的样子 —— 渐进增强，不是新的失败面。 */
  .article-grid:not(:has(.rail)) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      'head'
      'prose'
      'pager';
    column-gap: 0;
  }
}

/* ── 文章头 ──────────────────────────────────────────────
   标题不挂 .zf：65 字显示体子集覆盖不了任意文章标题，挂上去的效果是
   标题里的字**静默**掉回退宋体（观感只是"有点不匀"，肉眼极难发现）。
   所以文章页的标题统一用 --font-serif —— 与首页有一处系统性的字体差异，
   这是取舍不是 bug。要消掉只能扩子集，而对任意标题不可行。 */
.article-head,
.list-head {
  max-width: var(--article-measure);
  margin-block-end: clamp(1.75rem, 1rem + 2.5vw, 2.75rem);
}

.article-kicker {
  margin: 0 0 0.75rem;
  font-family: var(--font-sans);
  font-size: var(--article-kicker);
  line-height: var(--text-2xs--line-height);
  letter-spacing: 0.22em;
  color: var(--color-gold-ink);
}

.article-head h1,
.list-head h1 {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--article-h1);
  line-height: var(--article-leading-tight);
  font-weight: 600;
  color: var(--color-ink);
  /* 中文标题不设负 letter-spacing：汉字字面本就顶满 em，收紧会让笔画相碰。 */
  letter-spacing: 0.01em;
  text-wrap: balance;
}

.article-lede {
  margin: 1rem 0 0;
  font-family: var(--font-serif);
  font-size: var(--article-lede);
  line-height: 1.85;
  color: var(--color-text-secondary);
}

.article-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.25rem;
  margin: 1.25rem 0 0;
  padding: 0;
  list-style: none;
  font-family: var(--font-sans);
  font-size: var(--article-meta);
  color: var(--color-text-muted-ink);
  /* 日期与阅读时长里的数字要等宽对齐，否则列表页一列日期会左右跳。 */
  font-variant-numeric: tabular-nums;
}

.article-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.9rem 0 0;
  padding: 0;
  list-style: none;
}

.article-tags li,
.post-tags li,
.tag-cloud li {
  font-family: var(--font-sans);
  font-size: var(--article-meta);
  line-height: 1.6;
  padding: 0.1rem 0.55rem;
  border: 1px solid var(--color-stroke);
  border-radius: var(--radius-chip);
  background: var(--color-gold-wash);
  color: var(--color-text-secondary);
}

/* ── 正文 ────────────────────────────────────────────────
   守卫切正文区靠的就是 <article class="prose">…</article> 这一对标签，
   改类名要同步改 check-article-page.mjs。 */
.prose {
  max-width: var(--article-measure);   /* [C] */
  font-family: var(--font-serif);
  font-size: var(--article-text);      /* [A] */
  line-height: var(--article-leading); /* [B] */
  color: var(--color-text);
  /* [D] 渐进增强：不支持的浏览器忽略本行，回退到"作者在 md 里手写半角空格"。 */
  text-autospace: normal;
  /* [E] 渐进增强：行首行尾与相邻全角标点的多余半角收掉。 */
  text-spacing-trim: normal;
  /* [F] 长 URL / 长英文串不撑破版心，但不劈开正常单词。 */
  overflow-wrap: break-word;
}

.prose > :first-child { margin-block-start: 0; }
.prose > :last-child { margin-block-end: 0; }

.prose p {
  margin-block: var(--article-rhythm);  /* [G] */
}

.prose h2,
.prose h3 {
  font-family: var(--font-serif);
  font-weight: 600;
  color: var(--color-ink);
  letter-spacing: 0.01em;
  text-wrap: pretty;
  /* 粘性页眉会盖住锚点跳转的落点，靠 scroll-margin 让开。 */
  scroll-margin-top: var(--article-anchor-offset);
}

.prose h2 {
  margin-block: 2.4em 0.9em;
  font-size: var(--article-h2);
  line-height: var(--article-leading-tight);
  padding-block-end: 0.4em;
  border-block-end: 1px solid var(--color-stroke);
}

.prose h3 {
  margin-block: 1.9em 0.7em;
  font-size: var(--article-h3);
  line-height: 1.5;
}

.prose ul,
.prose ol {
  margin-block: var(--article-rhythm);
  padding-inline-start: 1.4em;
}

.prose li { margin-block: 0.4em; }
.prose li::marker { color: var(--color-gold); }

.prose a {
  color: var(--color-gold-ink);
  text-decoration: underline;
  /* 中文下划线离基线太近会压到"横"画，往下推一点并变细。 */
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
}

.prose a:hover { color: var(--color-gold-dark); }

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

/* 中文**不用斜体**：中文字体几乎都没有真正的意大利体，
   浏览器只能做机器倾斜（faux italic），笔画会糊、竖画歪斜。
   md 里的 *强调* 改用色彩 + 轻微字重来表达。 */
.prose em {
  font-style: normal;
  font-weight: 600;
  color: var(--color-gold-ink);
}

/* 引用块：不缩小字号（缩小的汉字更难读），不倾斜（同上）。
   靠一条鎏金细线 + 缩进 + 降一级墨色来分层。 */
.prose blockquote {
  margin-block: 1.6em;
  margin-inline: 0;
  padding: 0.2em 0 0.2em 1.15em;
  border-inline-start: 2px solid var(--color-gold-light);
  color: var(--color-text-secondary);
  font-style: normal;
}

.prose blockquote p { margin-block: 0.7em; }

/* 行内代码：等宽栈末尾必须有中文回退，否则代码里的汉字会掉到默认宋体，
   与前后拉丁字符的行高/基线对不齐。 */
.prose code {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas,
    'Noto Sans Mono CJK SC', 'Source Han Mono SC', monospace;
  font-size: 0.9em;
  padding: 0.12em 0.35em;
  border: 1px solid var(--color-stroke);
  border-radius: var(--radius-chip);
  background: color-mix(in oklab, var(--color-night-lighter) 70%, transparent);
  /* 行内代码被折行会读不出来；短标识符不折，长的交给外层容器滚动。 */
  word-break: keep-all;
}

.prose pre {
  margin-block: 1.6em;
  padding: 1rem 1.1rem;
  border: 1px solid var(--color-stroke);
  border-radius: var(--radius-control);
  background: var(--color-night-light);
  /* 代码块横向自己滚，页面 body 绝不允许出现横向滚动条。 */
  overflow-x: auto;
  tab-size: 2;
  line-height: var(--article-leading-code);
}

.prose pre code {
  font-size: var(--article-code);
  padding: 0;
  border: 0;
  background: none;
  white-space: pre;
}

.prose img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-control);
}

/* 表格：命理内容里全是干支/星曜表，横向溢出很常见。 */
.prose table {
  display: block;
  overflow-x: auto;
  width: 100%;
  border-collapse: collapse;
  font-size: var(--article-meta);
  line-height: 1.7;
}

.prose th,
.prose td {
  border: 1px solid var(--color-stroke);
  padding: 0.45em 0.7em;
  text-align: start;
}

.prose th {
  background: var(--color-gold-wash);
  color: var(--color-ink);
  font-weight: 600;
}

.prose hr {
  margin-block: 2.4em;
  border: 0;
  border-block-start: 1px solid var(--color-stroke);
}

/* ── 进度轨（目录） ───────────────────────────────────────
   🔴 这里**没有** display:none。目录由构建期生成、默认可见、链接是真锚点。
   rail.js 只加 aria-current。JS 挂掉 ⇒ 少一个高亮，目录照常能用。
   历史反模式（明令禁止）：默认隐藏 + JS 起来才显示 —— 那让 JS 失败 = 目录消失。

   ── rail.js 的判据（说明文字放在这里，因为那边有 800 B gzip 预算）──
   视口上方 28% 处画一条线，**最后一个越过它的标题**就是当前章节；
   滚到底时强制点亮最后一节（末节太短时可能永远越不过那条线）。
   刻意**不用** IntersectionObserver：正文是 markdown 产出的扁平片段，
   标题外面没有 <section> 包裹，IO 只能观察标题元素本身 ——
   于是"两个标题之间"的绝大部分滚动区间里没有任何元素相交，高亮会空掉。
   rAF 节流 + 只读 getBoundingClientRect（读之间不写样式，不触发强制重排）。 */
.rail {
  align-self: start;
  margin-block-end: clamp(1.75rem, 1rem + 2vw, 2.5rem);
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--color-stroke);
  border-radius: var(--radius-control);
  background: color-mix(in oklab, var(--color-surface) 70%, transparent);
}

@media (min-width: 62rem) {
  .rail {
    position: sticky;
    top: var(--article-anchor-offset);
    max-height: calc(100vh - var(--article-anchor-offset) - 2rem);
    overflow-y: auto;
    padding: 0;
    border: 0;
    border-inline-start: 1px solid var(--color-stroke);
    border-radius: 0;
    padding-inline-start: 1.1rem;
    background: none;
  }
}

.rail-title {
  margin: 0 0 0.6rem;
  font-family: var(--font-sans);
  font-size: var(--text-2xs);
  line-height: var(--text-2xs--line-height);
  letter-spacing: 0.2em;
  color: var(--color-text-muted-ink);
}

.rail-list {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: rail;
}

.rail-list li {
  counter-increment: rail;
  margin-block: 0.15rem;
}

.rail-list a {
  display: block;
  padding: 0.3rem 0 0.3rem 1.9rem;
  position: relative;
  font-family: var(--font-serif);
  font-size: var(--article-meta);
  line-height: 1.65;
  color: var(--color-text-secondary);
  text-decoration: none;
  border-radius: var(--radius-chip);
}

/* 序号用 counter 而不是 <ol> 默认标记：默认标记吃不到 tabular-nums，
   两位数章节会把文字挤右一格。 */
.rail-list a::before {
  content: counter(rail, decimal-leading-zero);
  position: absolute;
  inset-inline-start: 0;
  font-family: var(--font-sans);
  font-size: var(--text-3xs);
  line-height: 2.2;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

.rail-list a:hover { color: var(--color-ink); }

/* 当前章节。aria-current 既是样式钩子也是给读屏的真实语义 ——
   不要退化成 class="is-active"（那对读屏什么都不是）。 */
.rail-list a[aria-current='true'] {
  color: var(--color-gold-dark);
  font-weight: 600;
}

.rail-list a[aria-current='true']::before {
  color: var(--color-gold);
}

.rail-list a:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 2px;
}

/* ── 上下篇 ──────────────────────────────────────────────── */
.article-pager {
  display: grid;
  gap: 0.9rem;
  margin-block-start: clamp(2.5rem, 2rem + 3vw, 4rem);
  max-width: var(--article-measure);
}

@media (min-width: 40rem) {
  .article-pager { grid-template-columns: 1fr 1fr; }
}

.pager-cell {
  display: block;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--color-stroke);
  border-radius: var(--radius-control);
  background: color-mix(in oklab, var(--color-surface) 60%, transparent);
  text-decoration: none;
}

.pager-cell:hover { border-color: var(--color-gold-light); }
.pager-empty { border-style: dashed; opacity: 0.35; }

.pager-label {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--text-2xs);
  line-height: var(--text-2xs--line-height);
  letter-spacing: 0.18em;
  color: var(--color-text-muted-ink);
}

.pager-title {
  display: block;
  margin-block-start: 0.35rem;
  font-family: var(--font-serif);
  font-size: var(--article-text);
  line-height: 1.6;
  color: var(--color-ink);
}

/* 「下一篇」在网格右列时右对齐，形成一对相向的指示。 */
@media (min-width: 40rem) {
  .pager-cell[rel='next'] { text-align: end; }
}

/* ── 列表页 ─────────────────────────────────────────────── */
.post-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(1rem, 0.6rem + 1.5vw, 1.6rem);
}

.post-card {
  padding: clamp(1.1rem, 0.8rem + 1vw, 1.6rem);
  border: 1px solid var(--color-stroke);
  border-radius: var(--radius-card);
  background: color-mix(in oklab, var(--color-surface) 72%, transparent);
}

/* featured 只加视觉权重，**不改数组顺序** ——
   重排会让列表顺序与 prev/next 分叉。 */
.post-card-featured {
  border-color: var(--color-gold-light);
  box-shadow: inset 3px 0 0 var(--color-gold);
}

.post-kicker {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0.5rem;
  font-family: var(--font-sans);
  font-size: var(--text-2xs);
  line-height: var(--text-2xs--line-height);
  letter-spacing: 0.18em;
}

.post-category { color: var(--color-gold-ink); }

.post-featured {
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius-chip);
  background: var(--color-gold-wash);
  color: var(--color-gold-dark);
  letter-spacing: 0.1em;
}

.post-title {
  margin: 0;
  font-family: var(--font-serif);
  font-size: clamp(1.2rem, 1.05rem + 0.7vw, 1.45rem);
  line-height: 1.45;
  font-weight: 600;
}

.post-title a {
  color: var(--color-ink);
  text-decoration: none;
}

.post-title a:hover {
  color: var(--color-gold-dark);
  text-decoration: underline;
  text-underline-offset: 0.22em;
}

.post-desc {
  margin: 0.6rem 0 0;
  max-width: var(--article-measure);
  font-family: var(--font-serif);
  font-size: var(--article-text);
  line-height: 1.85;
  color: var(--color-text-secondary);
}

.post-meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0.75rem 0 0;
  font-family: var(--font-sans);
  font-size: var(--article-meta);
  color: var(--color-text-muted-ink);
  font-variant-numeric: tabular-nums;
}

.post-dot { color: var(--color-stroke-strong); }

.post-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.post-empty {
  font-family: var(--font-serif);
  font-size: var(--article-text);
  line-height: var(--article-leading);
  color: var(--color-text-secondary);
}

.tag-cloud {
  margin-block-start: clamp(2rem, 1.5rem + 2vw, 3rem);
  padding-block-start: 1.25rem;
  border-block-start: 1px solid var(--color-stroke);
}

.tag-cloud-title {
  margin: 0 0 0.75rem;
  font-family: var(--font-sans);
  font-size: var(--text-2xs);
  line-height: var(--text-2xs--line-height);
  letter-spacing: 0.2em;
  color: var(--color-text-muted-ink);
}

.tag-cloud ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
