/* 字体声明、共用 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;
  --theme-fg-transition: var(--theme-transition);
  /* 整页共用一套栅格与纵向节拍。左缘只有一条，所有停都挂在同一个 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; }
}
/* overflow-x: hidden 不是为了裁东西——页面本来就没有横向溢出。Mac 上的 Chrome
   只要根滚动容器横向"可滚"（overflow-x 为默认的 visible），触控板双指横滑就会
   把整页横向拉出去、松手弹回，滚到 logo 放大那段时尤其扎眼。根上设 hidden，视口
   横向就不再可滚，这个回弹随之消失；纵向回弹不受影响。不用
   overscroll-behavior-x: none：它会连双指横滑前进/后退一起关掉 */
html { scroll-behavior: auto; overflow-x: hidden; }
/* 滚动条。Mac 和手机上它是悬浮的、平时看不见，这两行对它们没有效果；Windows 上的
   Chrome/Edge 却一直占着右边一条，默认是浅灰的，压在黑、绿、黑三屏旁边很扎眼。
   改成细条，颜色跟着旁边那一屏走：这里是首屏的黑，之后由 portal.js 换 */
html { scrollbar-width: thin; scrollbar-color: rgba(241, 243, 238, 0.35) #000; }
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; visibility: hidden; }
/* 瞬时显隐用（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-fg-transition);
}
.nav-links {
  display: flex;
  gap: 1.8rem;
  /* 与全站小标签同一款 IBM Plex Mono。原来这里是 Syne 800，是首页上唯一一处
     用到它的地方，等于为三个词多出第四款字体 */
  font-family: var(--mono);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.nav-links a {
  color: hsl(var(--nav-fg-hsl, 0 0% 100%) / 0.62);
  text-decoration: none;
  transition: color var(--theme-fg-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) {
  /* 手机上导航也固定在顶上、三个词都显示，和电脑端一样。原来改成跟着页面滚走，
     是因为没有底色的固定导航会一路压在正文上（黑字压黑字、白字压白图），读不出来
     也点不准。现在滚过开场后给它垫一条当前这一停的底色：停着的时候和页面同色、
     看不出来，滚动时正文从它底下过去。对开屏左半是黑底，那一屏不垫
     （.over-split，见 scope.js），站标切成灰白 */
  .topnav { padding: 1.1rem var(--side); transition: background-color var(--theme-transition), opacity 0.4s ease; }
  /* 带上 .scrolled：上面电脑端的 .topnav.scrolled { background: none } 优先级更高，
     只写 .topnav 会在一滑动时被它清掉 */
  .topnav, .topnav.scrolled { background: hsl(var(--theme-bg)); }
  /* 导航底色永远等于它正下方那一屏：iPhone 的 Safari 会把贴着顶边的固定元素的底色
     延伸进状态栏，导航透明或颜色不对，状态栏就是一条和页面割裂的色带（实测 iOS 26
     模拟器）。首屏还没被绿屏盖住时（portal.js 加 .over-hero）是首屏的黑，字用白 */
  .topnav.over-hero { background: #000; --nav-fg-hsl: 0 0% 100%; --nav-fg: #FFFFFF; }
  /* 对开屏左黑右白，导航底也分成两半。状态栏只能是一种颜色：Safari 取底色（background-color），
     渐变拿不到颜色就退回整页底色，所以垫一层实心黑，让它稳定取黑（和左半、和灵动岛一致） */
  .topnav.over-split { background: linear-gradient(90deg, #000 50%, #F1F3EE 50%) #000; }

  /* 触摸热区扩到 44px（iOS/Android 的最小可靠指尖尺寸）。这几个链接的实际
     字号是照排版定的——站标 17px 高、CONTACT 19px 高、正文里的方括号链接 20px 高——
     直接加 padding 会把导航条撑高、把 .link 的下划线推离文字，所以热区
     用一个透明的 ::after 撑出来：只吃事件，不占布局，视觉尺寸一个像素不动。
     父级 .brand / .nav-links 是 pointer-events: auto，伪元素继承得到 */
  .topnav .brand, .nav-links a, .link { position: relative; }
  .topnav .brand::after,
  .nav-links a::after,
  .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;
  /* 不是 svh。svh 是"地址栏露着"的小视口；手机一往下滑地址栏就收起，视觉视口
     长到 lvh，而 hero 还钉在 svh 上，底下于是露出下一节 60–100px 的荧光绿边——
     看着像页面没铺满、露出了后一页的边。lvh 取"地址栏收起"的大视口：地址栏在时
     hero 比视口高一点、只是底部裁掉一截，收起后正好齐平，两种状态都不露边。
     不用 dvh 是因为它随地址栏逐帧改高，整篇文档高度会跟着抖。
     桌面上 lvh = svh = vh，这一改对桌面无影响。 */
  height: 100lvh;
  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 { /* 解码符号：Plex Mono 缺的字按 Mac 上的样子取（Menlo），没有 Menlo 的系统用
         base.css 里的 Senxe Decode（与 Menlo 同形）。不用 --mono 的整条栈：那里的
         Consolas 排在前面，Windows 就会取它 */
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, "Senxe Decode", monospace;
  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 + what ---------- */
.portal { position: relative; } /* 260vh scroll track set from JS while the zoom is live */
/* 减弱动效的电脑端（<html>.calm，见 index.html 的内联脚本）：不做 60 倍推镜，和手机
   一样让荧光绿那一页直接盖着首屏滑上来——首屏在 .portal 里 sticky 钉住，.portal 多出
   一屏高，酸液页上提一屏叠在后半段。全是原生滚动，没有画面跟着滚动距离逐帧变 */
@media (min-width: 768px) {
  .calm .portal, .slide-layout .portal { height: 200lvh; }
  .calm #what, .slide-layout #what { margin-top: -100lvh; }
}

/* 派生色。主题变量由 portal.js 写在 <body> 上，派生量必须定义在 body 的后代上
   才会跟着换（写在 :root 上会永远停在 CSS 的初值）。#acid-pour 挂着 .acid 类，
   涌出层克隆同样拿得到 */
.stop, .acid {
  --rule: hsl(var(--theme-fg) / 0.25);
  --soft: hsl(var(--theme-fg) / 0.72);
}

.acid {
  /* 宽度容器：首屏标题按它的内容宽度（cqw）排，而不是 100vw。100vw 把 Windows 常驻的
     滚动条也算进去，标题会冲进右边距。涌出层 #acid-pour 也挂着 .acid，两层量的是同一个宽 */
  container-type: inline-size;
  min-height: 100vh;
  min-height: 100lvh; /* lvh 而非 svh，理由同 .hero：手机地址栏收起后不露下一节的边 */
  padding: 0;
  display: block;
  /* 背景由 .stop 统一绘制（= 当前主题色）。不在这里写死：portal.js 在重叠期
     会把它切成 transparent 藏起真身，那个机制要求背景确实由本元素绘制 */
  color: var(--bg);
  position: relative; /* 传送门重叠期：盖在 hero（z 0/1）之上；低于 topnav（z 10） */
  z-index: 2;
}
.acid ::selection { background: var(--bg); color: var(--accent); }

/* 首屏：一句标题，四行。定高一屏。THE REFLEX 那一行是全句的重心，放大到撑满整个
   宽度；另外三行一样大，平分剩下的高度。原来四行一样大，字号被屏高卡住，最长一行
   也只占七成宽，右边空出一大块绿 */
.acid-inner {
  position: relative;
  height: 100vh;
  height: 100svh;
  padding: 4.5rem var(--side) var(--side); /* 上侧让开导航条 */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.what-head {
  --wd: calc(100vw - 2 * var(--side));        /* 舞台宽（不支持 cqw 的浏览器用它） */
  --hs: calc(100svh - 4.5rem - var(--side));  /* 舞台高 */
  /* THE REFLEX 在 Anton 里宽 4.12em，按宽度撑满。但不超过舞台高度的 56%：
     又宽又矮的窗口里，否则另外三行会被挤得太小。原来是 46%，Windows 笔记本缩放 125%、
     150% 后视口偏扁（1536×730、1280×600），46% 先卡住，右边空出近 200px；这些尺寸
     需要 52%–55%。Mac 的视口不扁，按宽度撑满，不受影响 */
  --big: min(calc(var(--wd) / 4.14), calc(var(--hs) * 0.56 / 0.93));
  font-family: "Anton", var(--font);
  font-weight: 400;
  /* 另外三行平分剩下的高度。最长的 WE'RE GROWING 宽 5.847em，5.9 是宽度兜底，
     竖长的窗口里按宽度收 */
  font-size: min(calc((var(--hs) - 0.93 * var(--big)) / 2.79), calc(var(--wd) / 5.9));
  /* Anton 的大写字高约 0.86em，行高 0.93 让行与行之间只隔一道细缝，四行读成一块 */
  line-height: 0.93;
  text-transform: uppercase;
  letter-spacing: 0;
}
@supports (width: 1cqw) {
  .what-head { --wd: calc(100cqw - 2 * var(--side)); }
}
.wh-line { display: block; white-space: nowrap; }
.wh-big { font-size: var(--big); }

/* 宣言后的紧凑双栏：左侧提出方向，右侧解释研究与入口。 */
.what-foot {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 2rem var(--col-gap);
  align-items: start;
  padding: clamp(5rem, 7vw, 7rem) var(--side);
  scroll-margin-top: 6rem;
}
.what-lede {
  grid-column: 1 / 8;
  font-family: var(--font);
  font-weight: 400;
  font-size: clamp(2.25rem, 4.8vw, 4.5rem);
  line-height: 1.08;
  letter-spacing: -0.025em;
}
.what-copy {
  grid-column: 8 / -1;
  min-width: 0;
}
.what-text {
  font-size: clamp(1.05rem, 1.35vw, 1.3rem);
  line-height: 1.5;
  max-width: 30em;
  color: var(--soft);
}
.what-copy .link {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 44px;
  margin-top: 1.25rem;
}
.what-link-arrow { width: 1rem; height: 1rem; flex: none; }
.what-copy .link:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}

/* 等宽小标签与方括号链接：全站只有这一种小字 */
.label, .link {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.6;
}
.link {
  justify-self: start;
  margin-top: 2rem;
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s ease;
}
.link:hover { border-bottom-color: currentColor; }

/* 传送门涌出层：酸液页的克隆（荧光底 + 黑字），固定在视口，
   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)：两层因此由同一个变量解析，
     不可能再有舍入误差 */
  background: hsl(var(--theme-bg));
}
#acid-pour .pour-veil {
  position: absolute;
  inset: 0;
  background: var(--accent); /* 与斜杠同色的荧光纱：初始无缝盖住内容，随 zoom
                                淡出，黑字像从绿场里凝结出来，无颜色跳变 */
  pointer-events: none;
}
.sr-only {
  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; }
.shell a { color: inherit; }
.stop {
  position: relative;
  background: hsl(var(--theme-bg));
  color: hsl(var(--theme-fg));
  transition: background-color var(--theme-transition), color var(--theme-fg-transition);
}
/* 这些元素直接取主题色，需要与父区段同步；继承 color 的元素随父级过渡。 */
.what-text, .ask-text, .ask-name, .colophon {
  transition: color var(--theme-fg-transition);
}
.gate-mark { transition: background-color var(--theme-transition); }
.signoff-letters { transition: fill var(--theme-fg-transition); }

/* 一块 = 一屏 = 一件事。6 栏网格，内容从第 1 栏起：左缘和绿屏标题、灰白那屏、对开屏
   同一条（原来从第 2 栏起，五步这一屏比别的屏往里缩了约一栏半） */
.stop-inner {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 0 var(--col-gap);
  align-content: center;
  min-height: 100svh;
  padding: calc(var(--beat) * 2) var(--side);
}
.title {
  grid-column: 1 / -1;
  font-family: "Anton", var(--font);
  font-weight: 400;
  font-size: clamp(2.75rem, 7.4vw, 8.5rem);
  line-height: 0.92;
  text-transform: uppercase;
}

/* 整屏对开。左半黑底（神经元），右半灰白（软件），一句标题在中缝处断开、
   两半各一行，放电和墨线横穿两半（scope.js 画）。两个半边的颜色写死，不跟主题走：
   这一屏本身就是"两个世界"，换停时它不该跟着翻色 */
.authority { position: relative; display: grid; grid-template-columns: 1fr 1fr; min-height: 100svh; }
.auth-half { position: relative; padding: 4.5rem var(--side) var(--side); }
.auth-in { background: #000; color: #F1F3EE; }
.auth-out { background: #F1F3EE; color: #000; }
.auth-title {
  font-family: "Anton", var(--font);
  font-weight: 400;
  font-size: 6.2vw;
  line-height: 0.9;
  text-transform: uppercase;
}
.auth-foot { position: absolute; left: var(--side); bottom: var(--side); opacity: 0.7; }
.auth-dots { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.topnav.over-split .brand svg { fill: #F1F3EE; } /* 见 scope.js navTick */
/* 脚注只剩一个 Schematic：标题已经说清两半各是谁，不再放图例 */
.auth-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem 1.6rem; right: var(--side); }
.auth-foot > span { display: inline-flex; align-items: center; gap: 0.55rem; white-space: nowrap; }


/* 门控：五道门横跨整行、五等分，一级比一级高（越往后的门越难），
   门名站在自己那一级上。台阶之间不留缝，连成一整道。下一步（Gate 0）那一级
   用斜杠的荧光色，其余用 30% 的灰白——再暗就融进黑底，看不出是台阶 */
.gates {
  --step: clamp(1.75rem, 4.2vh, 3.25rem);
  grid-column: 1 / -1;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  align-items: end;
  margin-top: calc(var(--beat) * 1.5);
}
.gate { display: flex; flex-direction: column; gap: 0.55rem; }
.gate:nth-child(1) { --n: 1; }
.gate:nth-child(2) { --n: 2; }
.gate:nth-child(3) { --n: 3; }
.gate:nth-child(4) { --n: 4; }
.gate:nth-child(5) { --n: 5; }
.gate-mark { /* --g：台阶长出来的比例，--lo：门名的透明度，都由 gates.js 写，缺省 1 */
  height: calc(var(--step) * var(--n) * var(--g, 1));
  margin-top: 0.35rem;
  background: hsl(var(--theme-fg) / 0.3);
}
.gate.is-next .gate-mark { background: var(--accent); }
.gate-id, .gate-name { opacity: var(--lo, 1); }
.gate.is-next .gate-id { color: var(--accent); }
.gate-name { /* 桌面上一律两行：五个门名等高，每一级上的字一样多 */
  font-family: "Anton", var(--font);
  font-size: clamp(1.4rem, 2.6vw, 2.9rem);
  line-height: 0.95;
  text-transform: uppercase;
}

/* 验证路径与联系入口组成同一屏，主标题只留给 Five steps。 */
.contact-overview {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: calc(clamp(1.5rem, 4svh, 2rem) + 1.25rem);
  --entry-space: 5.5rem;
  --exit-space: 2.5rem;
  padding-block: var(--entry-space) var(--exit-space);
}
.contact-overview .stop-inner { min-height: 0; padding-block: 0; }
.gates-block .gates {
  --step: clamp(0.75rem, 2.3svh, 1.5rem);
  margin-top: 3rem;
}
.contact-overview .ask { min-height: 0; padding-block: 0; }
/* 首句只取正文里最亮的颜色，不再反白：这一屏的强调留给下面的按钮 */
.ask-lead { color: hsl(var(--theme-fg)); }
/* 正文是六段叠在同一格里（五道门的判据 + 联系文案），格高取最长那段，切换时
   版面不跳。静态全景只显示最后一段 */
.ask-stage { grid-column: 1 / 4; display: grid; }
.ask-stage > .ask-text { grid-column: 1; grid-row: 1; }
.ask-gate { display: none; }
.ask-text {
  margin: 0;
  font-size: clamp(1.05rem, 1.35vw, 1.3rem);
  line-height: 1.45;
  color: var(--soft);
  max-width: 32em;
}
/* 按钮放在正文下面，左缘和标题、Gate 0 对齐（原来放在第 5 栏，和上面五等分的门对不上） */
.ask-contact {
  grid-column: 1 / -1;
  grid-row: 2;
  margin-top: 1.75rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 0.8rem;
}
/* 全页唯一的行动入口：斜杠的荧光色 + Anton，和"Gate 0 · Next"同一个颜色。
   选择器要带上 .ask-contact：.shell a { color: inherit } 比单个类名优先，会把黑字盖成灰白 */
.ask-contact .ask-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  min-height: 44px;
  padding: 0.55em 0.75em 0.5em;
  background: var(--accent);
  color: #000;
  font-family: "Anton", var(--font);
  font-size: clamp(1.35rem, 1.9vw, 1.9rem);
  line-height: 1;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 0.2s ease;
}
.ask-contact .ask-btn:hover { background: #F1F3EE; }
.ask-contact .ask-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.ask-btn-arrow { width: 0.7em; height: 0.7em; flex: none; }   /* Anton 没有 ↗，用 SVG 画一个同样粗的 */
.ask-name { color: var(--soft); }

/* 五道门随滚动逐级讲解。gates.js 确认这一屏放得进视口后才在 #contact 上加
   .gates-live：轨道拉长，总览屏 sticky 钉住，每段滚动讲一道门。没有这个类时
   （无脚本、减弱动效、视口太矮）下面几条都不生效，就是原来的静态全景 */
.gates-live .gates-track { height: calc(100svh + 6 * 62svh); }
.gates-live .contact-overview { position: sticky; top: 0; height: 100svh; min-height: 0; }
.gates-live .ask-gate { display: block; }
/* 正文切换：往下滑时旧段往上退、新段从下面升上来，往回滑方向相反。
   不可见的几段只用透明度藏：留在无障碍树里，读屏按顺序读得到每道门的判据 */
.gates-live .ask-stage > .ask-text {
  opacity: 0;
  transform: translateY(0.9em);
  pointer-events: none;
  user-select: none;
  transition: opacity 0.25s ease, transform 0.5s cubic-bezier(.2, .7, .2, 1), color var(--theme-fg-transition);
}
.gates-live .ask-stage > .ask-text.is-before { transform: translateY(-0.9em); }
.gates-live .ask-stage > .ask-text.is-on {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  user-select: auto;
  transition: opacity 0.35s ease 0.12s, transform 0.55s cubic-bezier(.2, .7, .2, 1) 0.08s, color var(--theme-fg-transition);
}
/* 正在讲的那一级亮成灰白。Gate 0 保持荧光绿：绿色标的是"下一步"，不随讲解移动 */
.gates-live .gate.is-active:not(.is-next) .gate-mark { background: hsl(var(--theme-fg)); }
/* Gate 4 充电放电用的方格和阈值红线（gates.js 画），压在台阶列表上面 */
.gates-live .gates { position: relative; }
.gate-lif { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }

/* 落款：与 hero 同一个字标，字母取当前前景色，斜杠恒为荧光 */
/* 前一屏居中留下的底部空间已计入间距；补足上下 padding 的差，
   并扣掉 SVG 顶部约 20 / 1311 宽度的透明区，避免重复叠加留白。 */
.signoff {
  --entry-exit-difference: 3rem;
  /* 100% 而不是 100vw：padding 的百分比按容器宽算，不含 Windows 的滚动条 */
  padding: max(0px, calc(var(--entry-exit-difference) - (100% - 2 * var(--side)) * 0.01515)) var(--side) var(--side);
}
.signoff-mark { display: block; width: 100%; height: auto; }
.signoff-letters { fill: hsl(var(--theme-fg)); }
.signoff-band { fill: var(--accent); }
.colophon { margin-top: 1rem; color: hsl(var(--theme-fg) / 0.5); }

/* 矮屏：Windows 笔记本缩放 125%、150% 后，浏览器视口常见 1536×730、1366×650、
   1280×600。五道门这一屏按宽度排版时比屏高多出 30–65px，gates.js 判定放不下，逐级
   讲解就整段关掉。这里按屏高收紧：标题、门名的字号改由屏高封顶，几处间距压小，
   让它放得进一屏。Mac 的浏览器视口都高于 800，不进这一段 */
@media (min-width: 768px) and (max-height: 800px) {
  .contact-overview { --exit-space: 1.5rem; gap: calc(clamp(1.25rem, 3svh, 2rem) + 0.75rem); }
  .gates-block .title { font-size: clamp(2.75rem, min(7.4vw, 12.4svh), 8.5rem); }
  .gates-block .gates { margin-top: 2rem; }
  .gate-name { font-size: clamp(1.4rem, min(2.6vw, 5svh), 2.9rem); }
  .ask-contact { margin-top: 1.25rem; }
}

/* ---------- 窄屏 ---------- */
@media (max-width: 900px) {
  .ask-stage { grid-column: 1 / -1; }
  .ask-contact { grid-column: 1 / -1; grid-row: auto; margin-top: 1.25rem; }
  .what-foot { grid-template-columns: minmax(0, 1fr); padding-block: 3.5rem 4rem; gap: 1.5rem; }
  .what-foot > * { grid-column: 1 / -1; }
  .what-lede { font-size: clamp(1.75rem, 5.8vw, 3rem); }
}
@media (max-width: 767px) {
  .contact-overview { --entry-space: 4.5rem; --exit-space: 2rem; min-height: 0; gap: 2.5rem; }
  .signoff { --entry-exit-difference: 2.5rem; }
  .ask-stage, .ask-contact { grid-column: 1 / -1; }
  #hero-logo { width: min(84vw, 420px); }
  /* 手机上没有推镜：荧光绿那一页直接盖着首屏滑上来。首屏在 .portal 里 sticky 钉住，
     .portal 多出一屏高，酸液页上提一屏叠在后半段，随手指从底下滑上来盖住首屏。
     全是原生滚动，不用逐帧计算 */
  /* 多出的 7.5rem：Safari 的浮动工具栏后面还会透出一截页面，绿屏若紧贴在首屏下面，
     停在首屏时它的顶边就从工具栏后面露出来。让出这一截，工具栏后面只剩黑 */
  .portal { height: calc(200lvh + 7.5rem); }
  #what { margin-top: -100lvh; }
  html { background: #000; }
  /* 手机首屏背景：电极点阵（mea.js 生成），替代 WebGL 流体。清晰、很暗的小方点，
     平时只剩一点灰白；闪的那一下瞬间点亮成荧光绿、放大，再用 0.9 秒暗回去 */
  #fluid-canvas { display: none; }
  .mea { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
  .mea i {
    position: absolute;
    width: 5px; height: 5px;
    margin: -2.5px 0 0 -2.5px;
    background: #F1F3EE;
    opacity: 0.16;
    transition: opacity 0.9s ease-out, background-color 0.9s ease-out, transform 0.9s ease-out;
  }
  .mea i.off { opacity: 0; }
  .mea i.hit { opacity: 1; background: #C9FA09; transform: scale(1.8); transition: none; }
  /* 荧光绿这一屏在手机上也定高一整屏、标题沉底，和电脑端一样（原来不定高，正文
     紧跟在标题下面，一滑到这里整页就提前切成灰白，荧光绿一次都露不出来）。
     一词一行：两词一行时字号被屏宽卡住，标题只占屏幕下方三成。一词一行才能像
     电脑端那样铺满整屏。THE、REFLEX 两行放大到 REFLEX 撑满屏宽（它宽 2.58em），
     其余六行平分剩下的高度；最宽的 GROWING 是 3.37em，3.4 是宽度兜底 */
  .acid-inner { height: 100svh; padding-top: 5rem; }
  .what-head {
    --hs: calc(100svh - 5rem - var(--side));
    --big: min(calc(var(--wd) / 2.6), calc(var(--hs) * 0.36 / 0.93));
    font-size: min(calc((var(--hs) - 2 * 0.93 * var(--big)) / 5.58), calc(var(--wd) / 3.4));
  }
  .wh-w { display: block; }
  .what-foot, .stop-inner { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .what-foot > *, .stop-inner > *, .title, .gates, .ask-stage, .ask-contact { grid-column: 1 / -1; }
  .stop-inner { padding: calc(var(--beat) * 2.5) var(--side); }
  .title { font-size: clamp(2.5rem, 13vw, 4rem); }
  /* 对开在手机上也保持左右两半、整屏一张，和电脑端一样。原来改成上下两半：两半
     之间没有交点，放电越过中缝变成墨线、红线横在交点上这件事就没了，而且两半
     加起来比一屏还高，放电和墨线从来不在同一屏。标题在半屏宽里自己折行 */
  .auth-foot > span { white-space: normal; }
  .auth-title { font-size: 11vw; }
  /* 门控改竖排：台阶横过来，一道门一行，条一行比一行长，右端连成台阶 */
  .gates { grid-template-columns: minmax(0, 1fr); align-items: stretch; row-gap: 1.5rem; }
  .gate { gap: 0.35rem; }
  .gate-mark { width: calc(20% * var(--n) * var(--g, 1)); height: 0.75rem; } /* 竖排时台阶往右长 */
  .gate-name { font-size: 1.75rem; }
  .gate-br { display: none; } /* 竖排时一行放得下，不必折 */
  /* 五道门在手机上也钉住逐门讲解（gates.js）。要钉得住，整屏得压进一屏以内：
     门号和门名排成一行，横条在下面；标题小一号。Gate 4 那一条加高到三格，
     画充电放电的小方格 */
  .gates-live .gates-block .title { font-size: 11vw; }
  /* 五行共用列表的两栏（subgrid）：第一栏宽度取最长的标签 GATE 0 · NEXT，五个门名
     落在同一条竖线上。原来每行各自一个网格、第一栏按自己的标签宽，门名一行一个起点。
     不支持 subgrid 的浏览器退回固定栏宽 */
  .gates-live .gates-block .gates {
    margin-top: 1.75rem;
    row-gap: 0.85rem;
    grid-template-columns: max-content minmax(0, 1fr);
    column-gap: 1.5rem; /* 0.75rem 时 GATE 0 · NEXT 的 NEXT 紧贴着门名 */
  }
  .gates-live .gate {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: 7.25rem minmax(0, 1fr);
    grid-template-columns: subgrid;
    /* 子网格用自己的间距，不继承列表的：这里不写，就会沿用上面 .gate 的 0.35rem，
       NEXT 紧贴门名 */
    column-gap: 1.5rem;
    row-gap: 0.3rem;
    align-items: baseline;
  }
  .gates-live .gate-mark { grid-column: 1 / -1; margin-top: 0; }
  .gates-live .gate-name { font-size: 1.35rem; }
  /* 用 nth-child 不用 last-child：gates.js 把方格层追加在列表末尾，Gate 4 就不再是最后一个 */
  .gates-live .gate:nth-child(5) .gate-mark { height: 2.25rem; }
}

/* 可收起的本地动效操作台，仅由 ?tune=1 打开。 */
.scope-controls {
  position: fixed; z-index: 10000; right: 1rem; top: 5rem;
  width: min(308px, calc(100vw - 2rem)); max-height: calc(100svh - 6rem);
  overflow: auto; background: #181a17; color: #f1f3ee;
  border: 1px solid #50544b; border-radius: 8px;
  box-shadow: 0 8px 32px #0005;
  font: 13px/1.45 system-ui, sans-serif; font-variant-numeric: tabular-nums;
}
.scope-controls summary { padding: 12px 16px; cursor: pointer; font-weight: 600; }
.scope-controls[open] summary { border-bottom: 1px solid #3d4039; }
.scope-controls-body { padding: 12px 16px 14px; }
.scope-controls-note, .scope-controls-status { color: #b6bcb0; font-size: 11px; }
.scope-controls-note { margin-bottom: 14px; }
.scope-control { display: grid; grid-template-columns: 1fr 64px 28px; align-items: center; column-gap: 6px; margin-top: 11px; }
.scope-control input[type=number] { width: 64px; min-width: 0; padding: 3px 5px; border: 1px solid #62685b; border-radius: 3px; background: #252922; color: #fff; font: inherit; }
.scope-control-unit { color: #b6bcb0; font-size: 10px; }
.scope-control input[type=range] { grid-column: 1 / -1; width: 100%; accent-color: #c9fa09; margin: 6px 0 0; cursor: pointer; }
.scope-controls-live { margin-top: 13px; color: #c9fa09; font-size: 12px; }
.scope-controls-actions { display: flex; gap: 6px; margin-top: 12px; }
.scope-controls button { border: 1px solid #62685b; border-radius: 4px; padding: 7px 8px; background: #252922; color: #f1f3ee; cursor: pointer; font: inherit; font-size: 11px; }
.scope-controls button:hover { background: #3a4033; }
.scope-controls button:disabled { opacity: .5; cursor: default; }
.scope-controls :focus-visible { outline: 2px solid #c9fa09; outline-offset: 3px; }
.scope-controls-status { margin-top: 10px; }
.scope-controls textarea { width: 100%; margin-top: 10px; color: #fff; background: #252922; font: 11px/1.4 monospace; }

/* Keep the official nav and page clear of display cutouts in either orientation. */
.topnav { padding-left: max(var(--side), env(safe-area-inset-left)); padding-right: max(var(--side), env(safe-area-inset-right)); padding-top: max(1.6rem, env(safe-area-inset-top)); }
:root { --side: max(clamp(0.9rem, 1.6vw, 1.6rem), env(safe-area-inset-left), env(safe-area-inset-right)); }
@media (max-width: 767px) {
  .topnav { padding-top: max(1.1rem, env(safe-area-inset-top)); }
}
@media (hover: none), (pointer: coarse) {
  .topnav .brand, .nav-links a, .link { position: relative; }
  .topnav .brand::after, .nav-links a::after, .link::after {
    content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    min-width: 44px; min-height: 44px; width: 100%; height: 100%;
  }
}
a:focus-visible { outline: 2px solid currentColor; outline-offset: 5px; }
