/* 字体声明、共用 token 与盒模型重置在 base.css，index.html 先引它再引本文件 */
:root {
  --hairline: #2A2D26;
  --text: #FFFFFF;
  --muted: #8E948A;
  --muted-strong: #BFC4B9; /* 正文用的浅灰：--muted 在纯黑上只有 6.8:1，
                              等宽字小字号下笔画太细。这个是 11.6:1 */
  /* 三停主题。用 HSL 三元组而非 hex：可以写 hsl(var(--theme-fg) / .6) 取半透明，
     这正是 springsummer.dk 用三元组的原因。--theme-bg / --theme-fg 由
     portal.js 的滚动 tick 写到 <body> 上，过渡交给 CSS */
  /* 精度不是讲究：取整到 72 96% 51% 会解析成 rgb(202,250,10)，与 --accent
     的 #C9FA09 差 1 个单位。传送门交接时两层要像素一致，这 1 个单位就是它
     整套机制唯一的破口 */
  --volt: 72.199 96.016% 50.784%;  /* = #C9FA09 */
  --bone: 84 17.241% 94.314%;      /* = #F1F3EE，与 logo 同色 */
  --ink:  0 0% 0%;
  --theme-bg: var(--volt);
  --theme-fg: var(--ink);
  --theme-transition: 0.4s ease-out; /* 实测原版就是 .4s ease-out */
  /* 整页共用一套栅格与纵向节拍。左缘只有一条，所有停都挂在同一个 6 栏上——
     停之间换的是颜色，不是版式，页面才读成一张纸而不是三张 */
  /* 左右留白按 Getty 的比例：它的 --sideMargin 是 2rem（其 rem 基准 10px = 20px），
     INDEX 那页实测左右各约 1%。原先 3.5rem = 3.7%，字带四周空太多 */
  --side: clamp(0.9rem, 1.6vw, 1.6rem);
  --col-gap: clamp(0.75rem, 1.6vw, 1.75rem);
  --beat: clamp(2.25rem, 5.5vh, 4rem);
}
@media (prefers-reduced-motion: reduce) {
  /* 颜色照变（那是信息，不是装饰），只是不做动画 */
  :root { --theme-transition: 0s; }
}
html { scroll-behavior: auto; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.7;
  font-variant-numeric: tabular-nums;
  font-optical-sizing: auto; /* Fraunces 是可变字：按字号自动取 opsz */
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
::selection { background: var(--accent); color: var(--bg); }

/* ---------- top nav ---------- */
.topnav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.6rem 1.5rem;
  transition: background 0.35s ease, opacity 0.4s ease;
}
/* hero 之外导航坐在三停的主题色上，颜色跟着主题走。hero 是纯黑，用初始值即可 */
.topnav.scrolled { background: none; }
/* 导航显隐由 portal.js 切 .nav-hide 类接管：滚过酸液页底部才淡入。
   脚本挂了（类永远不会被加）退回常显的导航 */
.topnav.nav-hide { opacity: 0; }
/* 瞬时显隐用（intro 命中帧与导航同帧露面）：摘掉过渡，不被补成 0.4s 渐变 */
.topnav.nav-instant { transition: none; }
.topnav { pointer-events: none; }
.topnav .brand, .topnav .nav-links { pointer-events: auto; }
.topnav .brand svg {
  height: 17px; width: auto; display: block;
  fill: var(--nav-fg, var(--text));
  transition: fill var(--theme-transition);
}
.nav-links {
  display: flex;
  gap: 1.8rem;
  /* 与 brief.html 列表标题同款 Syne 800。Syne 本身就宽，字距从 .28em 收到 .12em，
     否则全大写 + 宽字 + 宽字距会散得读不成一个词 */
  font-family: "Syne", var(--font);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.nav-links a {
  color: hsl(var(--nav-fg-hsl, 0 0% 100%) / 0.62);
  text-decoration: none;
  transition: color var(--theme-transition);
}
.nav-links a:hover { color: hsl(var(--nav-fg-hsl, 0 0% 100%)); }
/* 导航压在哪一停上就取那一停的前景色。scrolled 由 portal.js 在滚过 hero 后加，
   正好是导航离开纯黑、进入三停的时刻 */
.topnav.scrolled { --nav-fg-hsl: var(--theme-fg); --nav-fg: hsl(var(--theme-fg)); }
@media (max-width: 1080px) {
  .nav-links { gap: 1.15rem; letter-spacing: 0.1em; }
}
@media (max-width: 767px) {
  .nav-links a:not(:last-child) { display: none; }
  /* 手机上正文是通栏的，固定导航又没有底色：站标和 CONTACT 会一路压在正文、
     配图和满幅字带上（黑字压黑字、白字压白图），读不出来也点不准。窄屏改成
     跟着页面滚走的静态头——hero 那一屏照样看得到，联系方式在页脚还有一份 */
  .topnav { position: absolute; }

  /* 触摸热区扩到 44px（iOS/Android 的最小可靠指尖尺寸）。这几个链接的实际
     字号是照排版定的——站标 17px 高、CONTACT 19px 高、页脚链接 20px 高——
     直接加 padding 会把导航条撑高、把 .foot-link 的下划线推离文字，所以热区
     用一个透明的 ::after 撑出来：只吃事件，不占布局，视觉尺寸一个像素不动。
     父级 .brand / .nav-links 是 pointer-events: auto，伪元素继承得到 */
  .topnav .brand, .nav-links a, .foot-link { position: relative; }
  .topnav .brand::after,
  .nav-links a::after,
  .foot-link::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    min-width: 44px; min-height: 44px;
    width: 100%; height: 100%;
  }
}

/* ---------- hero ---------- */
.hero {
  height: 100vh;
  height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  position: sticky; /* pinned inside .portal while the slash zoom runs */
  top: 0;
  overflow: hidden;
}
#fluid-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  opacity: 0;
  transition: opacity 1.4s ease; /* 由 fluid.js 在 intro 的荧光斜杠出现后置 1 */
}
.hero-inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3.2rem;
  padding: 0 6vw;
}
#hero-logo {
  width: clamp(320px, 74vw, 1100px);
  height: auto;
  display: block;
  overflow: visible;
}

/* typewriter intro: initial states only when the autoplay branch is active.
   Markup default (no-JS / reduced-motion / mobile) is the complete wordmark. */
#caret { opacity: 0; }
#caret.blink { animation: caret-blink 0.85s steps(1, end) infinite; }
.dc { font-family: var(--mono); font-size: 293px; fill: #C9FA09; }
@keyframes caret-blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } }
/* .preintro 首帧防闪（机制见 index.html <head> 内联脚本）：脚本链跑到之前，
   由渲染阻塞的 CSS 直接画出 intro 起始态——字标部件全隐、色带全隐、导航全隐。
   band 用 opacity 预藏而不换裁剪多边形：CSS 里 url() 引用 SVG clipPath 在
   Safari 上不可靠；intro.js 写入零面积 reveal 裁剪后再把 opacity 交还。 */
.preintro #g-s, .preintro #g-e1, .preintro #g-n, .preintro #g-e2,
.preintro #g-x, .preintro #g-xs-j { opacity: 0; }
.preintro #g-band { opacity: 0; }
.preintro .topnav { opacity: 0; }

/* ---------- portal + acid page ---------- */
.portal { position: relative; } /* 260vh scroll track set from JS while the zoom is live */
.acid {
  min-height: 100vh;
  min-height: 100svh; /* CSS 基线 = 正好一屏，inner 的 sticky 没有活动范围、等同静态：
                         reduced-motion 与无脚本时字幅就静止在满屏态。开了动效时
                         portal.js 把它抬到 170svh，那 70svh 就是压扁的行程 */
  padding: 0; /* 盖掉 section 的 4.5rem：inner 必须从页顶开始，交接才无缝 */
  display: block; /* 块流：sticky 的字幅按普通文档流定位即可，
                     原先的 flex 是为居中排版留的，现在无用 */
  /* 背景由 .stop 统一绘制（= 当前主题色）。不在这里写死：portal.js 在重叠期
     会把它切成 transparent 藏起真身，那个机制要求背景确实由本元素绘制 */
  color: var(--bg);
  position: relative; /* 传送门重叠期：盖在 hero（z 0/1）之上；
                         低于 topnav（z 10）。reduced-motion 无重叠、无影响 */
  z-index: 2;
}
.acid-inner { /* 字幅的定屏舞台：整屏就这一块字，没有别的元素 */
  position: sticky;
  top: 0;
  pointer-events: none; /* 舞台盒定屏期间盖着下方的 foot 块：不挡 foot 里的链接 */
  height: 100vh;
  height: 100svh; /* 定高而非 min-height：字幅按盒高铺满，盒子得先有确定的高 */
  width: 100%;
  max-width: none;
  margin: 0 auto;
  padding: 4.5rem var(--side) 0; /* 上侧让开导航条，下侧不留：
                                                     静止态字幅要吃到视口底边 */
}
/* 大标题字带 —— 移植自 gehry.getty.edu 的 TitleStretch 组件。
   三个要件缺一不可：
   1) 标题是 <path> 轮廓不是文字，svg 上 preserveAspectRatio="none"，
      viewBox 被非等比拉伸铺满盒子——字体做不到这种拉伸；
   2) scaleY 只从 1 往下走，合成层在 scale 1 附近栅格化后缩小，不会糊；
   3) transform-origin 钉在顶边（原版就是 center top），压扁读作"往下塌"。
   与原版一致：字带自始至终满幅，只压纵向，落定后独占一行。*/
.what-word {
  position: relative;
  height: 100%;
  margin: 0;
  pointer-events: none; /* 满屏盒盖在 foot 块上方：不挡其下方的链接点击 */
  --band: 0.275; /* 字带的终态高度（占舞台的比例）。同时就是 scaleY 的终值，JS 直接读
                    这个变量——只此一处定义，无脚本时也靠它把字带排到正确大小。
                    轨道长度也由它推出：(2 - band) 屏，与原版同一套几何。
                    写成无单位小数而不是 27.5%：百分比用处要 × 100%，而窄屏那条
                    规则要把它乘进一个长度里（见 max-width: 767px 块），小数两边都能用 */
}
/* 无脚本 / reduced-motion 的基线：字带直接排在终态高度上，不动 */
.what-neural { position: absolute; left: 0; top: 0; width: 100%; height: calc(var(--band) * 100%); }
.what-neural svg { display: block; width: 100%; height: 100%; }
/* JS 接管后：字带撑满舞台等着被压回 --band。起始态必须落在 CSS 上——传送门的
   涌出层是 .acid-inner 的深拷贝，JS 写的内联样式拷不过去，只有选择器对克隆
   同样生效，否则交接会跳一帧 */
.js-stretch .what-neural {
  height: 100%;
  transform-origin: center top;
  transform: scaleY(1);
  will-change: transform;
  backface-visibility: hidden;
}
/* 字带下方的正文块 —— 版式取自 Getty 的 .archiveHeader__bottom：
   六栏网格 / 左 1 栏 meta / 第 2 栏留空 / 右 4 栏正文 / 首行大缩进 */
.acid-foot {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--col-gap);
  padding: 0 var(--side) var(--beat);
  /* 上边距要先补掉一段恒定的重叠：字带落定后底边在 pad + band×(视口−pad)，
     而本块顶边落在 band×视口，两者相减恒为 −pad×(1−band) = −4.5rem×0.725
     ≈ 52px，与视口高无关。补掉之后再给 5rem 的真实留白 */
  margin-top: calc(4.5rem * 0.725 + 5rem);
}
.foot-meta {
  grid-column: 1 / 2;
  font-family: var(--mono); /* 等宽小字 vs 衬线正文 —— 原版这处对比的来源 */
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 2;
  color: inherit;
  max-width: none;
}
.foot-body { grid-column: 3 / 7; }
.foot-body p {
  font-size: clamp(1.05rem, 1.5vw, 1.35rem);
  line-height: 1.4;
  text-indent: min(12.5rem, 24%); /* 原版 text-indent:20rem（其 rem 基准 10px）*/
  color: inherit;                 /* 盖掉全局 p 的 --muted：那是黑底上的浅灰 */
  max-width: 33em;                /* 页边距收紧是为了字带满幅；正文行宽必须单独管。
                                     用 em 不用 ch：Fraunces 的数字 0 很宽，
                                     62ch 解析成 949px，等于没限制 */
}
.foot-body p + p { margin-top: 0.75rem; }
.foot-link {
  display: inline-block;
  margin-top: 2rem;
  text-indent: 0; /* 取消段落缩进，原版同样单独归零 */
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s ease;
}
.foot-link:hover { border-bottom-color: currentColor; }
@media (max-width: 900px) {
  .acid-foot { grid-template-columns: 1fr; margin-top: calc(4.5rem * 0.725 + 3rem); }
  .foot-meta, .foot-body { grid-column: 1 / -1; }
  .foot-meta { margin-bottom: 1.75rem; }
  .foot-body p { text-indent: 0; }
}
/* 窄屏上收尾节的正文比字带给它留的空档高：字带压完之后 sticky 还钉在顶上，
   余下的行程只有 (1 − band) 屏 ≈ 0.725 屏，而这一块正文（四段 + 邮箱）在
   390px 宽下要 690px 上下，装不下。装不下的部分就从钉住的字带里穿过去，
   两层字叠在一起谁也读不出来。给这一块补上本节底色（与 .stop 同一个 token，
   连主题过渡都一致）并抬到字带之上：正文照旧从下方升起，只是升到字带那里把它
   盖住，而不是穿过去。

   下限收在 768px：≤767px 起 portal.js 的 stretchOn/finOn 已排除移动端，字带不再
   sticky、直接静止在 --band 的终态，正文正常流在它下面，本来就不会相撞。那时这块
   不透明色板反而成了新缺陷——它是横着盖上去的，会把 LIVING PROOF. 齐腰切一刀
   （实测 360×640 切掉 45px）。所以它只在"仍有钉住的字带、但栅格已单列"的
   768–900px 这一段成立 */
@media (min-width: 768px) and (max-width: 900px) {
  .fin-foot, .fin .colophon {
    position: relative;
    z-index: 3;
    background: hsl(var(--theme-bg));
  }
}

.acid p { color: rgba(11, 12, 10, 0.72); }
.acid ::selection { background: var(--bg); color: var(--accent); }

/* ---------- 收尾页字带(#contact)----------
   与 .acid 完全同一套几何,只换类名与配色:黑底上字带用浅色(--theme-fg)。
   压扁动画由 portal.js stretchTick 驱动(--band 同 0.275,JS 读它推出行程) */
.fin {
  min-height: 100vh;
  min-height: 100svh; /* 基线同 .acid:无脚本/reduced-motion 时字带静止在满屏态,
                         开动效时 portal.js 把本节抬到 (2 - band) svh */
  padding: 0;
  display: block;
  color: hsl(var(--theme-fg));
  position: relative;
  z-index: 2;
}
.fin-inner { /* 定屏舞台,同 .acid-inner */
  position: sticky;
  top: 0;
  pointer-events: none; /* 同 .acid-inner：不挡 fin-foot 里的邮件链接 */
  height: 100vh;
  height: 100svh;
  width: 100%;
  max-width: none;
  margin: 0 auto;
  padding: 4.5rem var(--side) 0;
}
.fin-word {
  position: relative;
  height: 100%;
  margin: 0;
  pointer-events: none; /* 同 .what-word：满屏盒不挡 foot 里的邮件链接 */
  --band: 0.275;
}
.fin-band { position: absolute; left: 0; top: 0; width: 100%; height: calc(var(--band) * 100%); }
.fin-band svg { display: block; width: 100%; height: 100%; }
.js-stretch .fin-band {
  height: 100%;
  transform-origin: center top;
  transform: scaleY(1);
  will-change: transform;
  backface-visibility: hidden;
}

/* ---------- 窄屏：字带舞台不再定屏 ----------
   两个字带节的结构是「sticky 的满屏舞台 + 紧跟其后的正文块，同属一个 section」。
   舞台钉在视口顶上的这段时间里，正文块是从下方一路升上来的，升到舞台高度以内
   就会盖到字带上——桌面上正文只占 300px 上下，(1 − band) 屏的空档装得下，永远
   撞不到；窄屏上同一块正文换行后要 445px，空档只有 464px 甚至更少，于是正文
   直接压进字带里（实测 360×640 重叠 33px、360×600 重叠 45px，字与字叠成一团）。

   注意这跟压扁动画无关：把 portal.js 的 stretchOn 关掉之后重叠一模一样，因为
   .acid-inner / .fin-inner 的 position: sticky 与 height: 100svh 写在基线 CSS 里，
   不挂 .js-stretch。所以窄屏要动的是舞台本身：改成普通流、收成字带那么高，
   正文就正常排在它下面，两者不可能相交。

   字带高度仍旧只由 --band 推出，与桌面终态同一个式子：--band ×（一屏 − 上边距）。
   桌面上这个高度是 sticky 舞台里 27.5% 的位置，这里把它直接当成盒高 */
@media (max-width: 767px) {
  .acid-inner, .fin-inner { position: static; height: auto; }
  .what-word, .fin-word { height: calc((100svh - 4.5rem) * var(--band)); }
  .what-neural, .fin-band { height: 100%; }
}
.fin-foot { /* 复用 .acid-foot 的栅格与重叠补偿,正文按黑底降透明 */
  padding-bottom: calc(var(--beat) * 0.5);
}
.fin .foot-body p { color: hsl(var(--theme-fg) / 0.72); }
.fin .colophon { padding: 0 var(--side); }
.fin ::selection { background: var(--accent); color: var(--bg); }

/* what 节为压扁行程抬高的 min-height(172.5svh)在正文块之后剩一截纯空绿的
   死尾:链接到节尾的空档 = 72.5svh − 379px(379 = foot 的 margin-top 132 +
   盒高 307 − padding-bottom 60)。把 how 上提盖掉它,链接与 bone 底之间只留
   3rem 缝:M = −(72.5svh − 379) + 48 = 26.7rem − 72.5svh,缝宽随视口高恒定。
   .acid 有 z-index 2,提上来的 how 必须压过它,死尾区才露出 bone 底而不是
   被绿盖住。移动端 foot 堆叠变高、死尾不存在,整段不适用。
   必须挂在 .js-stretch 上:死尾是 JS 抬高 min-height 撑出来的,reduced-motion
   与无脚本时 stretchOn 为假、js-stretch 不加、#what 保持 CSS 的 100svh 基线,
   那时没有死尾可盖,这 −138px 会直接吃掉正文末段和 ARCHITECTURE BRIEF 链接
   (z-index 3 还保证它盖在上面) */
@media (min-width: 768px) {
  .js-stretch #how { margin-top: calc(26.7rem - 72.5svh); position: relative; z-index: 3; }
}

/* 传送门涌出层：酸液页的克隆（荧光底 + 黑字），固定在视口，
   clip-path 就是真实斜杠路径本身（JS 逐帧按 logo 的 transform 重算）——
   内容从斜杠"里面"透出来，不新增任何形状 */
#acid-pour {
  position: fixed;
  inset: 0;
  z-index: 2; /* 盖过 hero（z 0/1）、低于 topnav（z 10） */
  margin: 0;
  min-height: 0;
  pointer-events: none;
  clip-path: circle(0px at 50% 50%);
  background: none; /* 底图移到缩放层上，随内容一起缩小 */
}
#acid-pour .pour-cut { position: absolute; inset: 0; }
#acid-pour .pour-zoom {
  position: absolute;
  inset: 0;
  will-change: transform;
  /* 直接取当前主题色而不是写 var(--accent)：两层因此由同一个变量解析，
     不可能再有舍入误差（底图时代这里要靠 cover 随 scale 一起缩放才对得上） */
  background: hsl(var(--theme-bg));
}
#acid-pour .pour-veil {
  position: absolute;
  inset: 0;
  background: var(--accent); /* 与斜杠同色的荧光纱：初始无缝盖住内容，随 zoom
                                淡出，黑字像从绿场里凝结出来，无颜色跳变 */
  pointer-events: none;
}
.sr-only { /* 只给读屏：表格需要 caption，视觉上那句话是多余的 */
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- 一页三停 ---------- */
/* 背景由每个 .stop 自己绘制而不是 .shell 统一绘制：portal.js 在重叠期把
   #what 的 background 切成 transparent 来藏起真身、只露出涌出层，那个机制
   要求背景确实挂在本元素上。相邻两停同时过渡到同一颜色，观感与整块过渡一致 */
.shell { position: relative; }
.stop {
  position: relative;
  background: hsl(var(--theme-bg));
  color: hsl(var(--theme-fg));
  transition: background-color var(--theme-transition), color var(--theme-transition);
}
.stop-inner {
  width: 100%;
  padding: calc(var(--beat) * 1.5) var(--side) calc(var(--beat) * 1.5);
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 0 var(--col-gap);
  align-content: start;
}
/* 每一块都占整数列：内部再开 6 栏的子块（.evidence / .signoff）轨道与间隙
   与父级相同，因此逐列对齐 */
.stop-inner > * { grid-column: 1 / 7; }
.stop-head { grid-column: 1 / 6; }
.stop-head {
  font-family: "Anton", var(--font);
  font-size: clamp(3rem, 7.2vw, 6.2rem); /* Anton 窄得多，同等分量需要更大的字号 */
  font-weight: 400;                      /* Anton 只有一个字重 */
  line-height: 0.98;
  letter-spacing: 0;                     /* Syne 的 -0.035em 会让 Anton 的字母粘连 */
  margin-bottom: var(--beat);
}
/* 正文继承主题前景色；次要文字用同色降透明度，不引入第四个颜色变量 */
.shell p { color: inherit; max-width: 68ch; }
.shell a { color: inherit; }

/* 证据图：默认图左文右。how 节 .merge 变体文左图右，此节没有大标题，
   kicker 之后直接是下沉首字的正文 */
.evidence {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--col-gap);
  align-items: start;
  margin: 0 0 calc(var(--beat) * 1.25);
}
.evidence-media { grid-column: 1 / 4; }
.evidence img { display: block; width: 100%; height: auto; }
.evidence-text { grid-column: 4 / 7; }
#how .merge .evidence-text { grid-column: 1 / 4; margin: 0; }
#how .merge .evidence-media { grid-column: 4 / 7; }
/* Getty 式首字（gehry.getty.edu）：巨型实心字母作为块级元素位于整段正文
   上方，正文从下一行起整宽排布。Anton 只有一个字重，用同色描边加粗笔画 */
.init {
  display: block;
  font-family: "Anton", var(--font);
  font-size: clamp(6rem, 10vw, 10.4rem);
  line-height: 0.8;
  margin-bottom: 0.18em;       /* 与下方正文拉开距离 */
  color: hsl(var(--theme-fg)); /* 正文是 80% 透明黑，首字要纯黑 */
  -webkit-text-stroke: 0.025em hsl(var(--theme-fg));
  paint-order: stroke fill;
}
.cap-key { /* 图注：现在挂在图片下方，间距在上方 */
  display: block;
  margin-top: 1.1rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
/* 右栏现在是两段实文而不是一句图注，字号按正文给 */
.evidence-text p {
  font-size: clamp(0.98rem, 1.25vw, 1.08rem);
  line-height: 1.5;
  color: hsl(var(--theme-fg) / 0.8);
  max-width: 33em; /* 同上：行宽与页边距解耦，用 em 不用 ch */
}
.evidence-text p + p { margin-top: 1rem; }
/* 两处强调（how 节黑底白字 / 收尾节白底黑字）都反相跟随当前停的主题色：
   portal.js 滚过停中线时改写 body 上的 --theme-bg/--theme-fg，.stop 背景与这里
   一起过渡（同一条 --theme-transition），强调色始终是该停背景的反相 */
.evidence-text .hl,
.foot-body .hl-inv {
  background: hsl(var(--theme-fg));
  color: hsl(var(--theme-bg));
  padding: 0.08em 0.32em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  transition: background-color var(--theme-transition), color var(--theme-transition);
}
@media (max-width: 900px) {
  .evidence { grid-template-columns: 1fr; }
  .evidence-media, .evidence-text { grid-column: 1 / -1; }
  .evidence-text { margin-top: 1.25rem; }
  /* 移动端 merge 单列：文字在上（DOM 顺序），图在下；margin-top 移到图上 */
  #how .merge .evidence-text { grid-column: 1 / -1; margin-top: 0; }
  #how .merge .evidence-media { grid-column: 1 / -1; margin-top: 1.25rem; }
}

/* 收尾 */
.colophon { font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: hsl(var(--theme-fg) / 0.5); }
@media (max-width: 767px) {
  #hero-logo { width: min(84vw, 420px); }
  /* 窄屏塌成单列：栅格没有意义了，但左缘仍与停 1 一致 */
  .stop-inner { grid-template-columns: 1fr; }
  .stop-inner > *, .stop-head { grid-column: 1 / -1; }
}
