/* IBM Plex Mono — self-hosted (latin subset) so the intro decode never swaps fonts mid-animation */
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/ibm-plex-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("assets/ibm-plex-mono-500.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("assets/ibm-plex-mono-600.woff2") format("woff2");
}
/* Syne — what 页大标题的展示字体（logo 同款先锋几何风），自托管 latin 子集 */
@font-face {
  font-family: "Syne";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("assets/syne-800.woff2") format("woff2");
}
:root {
  --bg: #000000;
  --hairline: #2A2D26;
  --text: #FFFFFF;
  --muted: #8E948A;
  --muted-strong: #BFC4B9; /* 正文用的浅灰：--muted 在纯黑上只有 6.8:1，
                              等宽字小字号下笔画太细。这个是 11.6:1 */
  --accent: #C9FA09;
  --font: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
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;
  -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;
}
.topnav.scrolled {
  background: var(--bg);
}
/* 导航显隐由 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(--text); }
.nav-links {
  display: flex;
  gap: 1.8rem;
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
}
.nav-links a { color: var(--muted); text-decoration: none; transition: color 0.2s ease; }
.nav-links a:hover { color: var(--accent); }
@media (max-width: 1080px) {
  .nav-links { gap: 1.15rem; letter-spacing: 0.2em; }
}
@media (max-width: 767px) {
  .nav-links a:not(:last-child) { display: none; }
}

/* ---------- 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(--font); font-size: 293px; fill: #C9FA09; }
@keyframes caret-blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } }

/* ---------- portal + acid page ---------- */
.portal { position: relative; } /* 260vh scroll track set from JS while the zoom is live */
.acid {
  min-height: 100vh;
  min-height: 100svh; /* 正好一屏：交接时 inner 就占满视口，之后正常滚走。
                         inner 的 sticky 因此没有活动范围，等同静态 */
  padding: 0; /* 盖掉 section 的 4.5rem：inner 必须从页顶开始，交接才无缝 */
  display: flex;
  align-items: flex-start;
  /* 凝胶质感底图叠在荧光绿上。attachment: fixed 以视口为参照——涌出层
     （fixed 全视口）与真身（170vh 高）的背景因此像素一致，p→1 互换无跳变 */
  background: var(--accent) url("assets/acid-bg.webp") center / cover no-repeat fixed;
  color: var(--bg);
  position: relative; /* 传送门重叠期：盖在 hero（z 0/1）之上；
                         低于 topnav（z 10）。reduced-motion 无重叠、无影响 */
  z-index: 2;
}
.acid-inner { /* 编辑海报式：整页负空间，内容垂直居中 */
  position: sticky;
  top: 0;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  max-width: none;
  margin: 0 auto;
  padding: 4.5rem clamp(1.5rem, 4vw, 3.5rem) clamp(2rem, 5vh, 3.5rem);
}
.acid-inner h1 { /* Syne 800 展示标题：logo 同款先锋几何 */
  font-family: "Syne", var(--font);
  font-size: clamp(3.4rem, 6.8vw, 6.6rem);
  font-weight: 800;
  line-height: 1.24; /* 给 g 的下伸部留位：否则会贴上下面 neural 的黑块 */
  letter-spacing: 0;
  margin-bottom: 0;
  text-shadow: 0 0 0.9em var(--accent), 0 0 0.22em var(--accent); /* 荧光晕：把黑字从底图的深色凝胶块上托起来 */
}
.acid-inner h1 em { font-style: normal; }
.acid-inner h1 .hl { /* 反相强调：纯黑块 + 荧光字，从酸液底上切出来 */
  background: var(--bg);
  color: var(--accent);
  padding: 0 0.16em;
  text-shadow: none; /* 块内是荧光字，不再需要外圈光晕 */
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.acid p { color: rgba(11, 12, 10, 0.72); }
.acid ::selection { background: var(--bg); color: var(--accent); }

/* 传送门涌出层：酸液页的克隆（荧光底 + 黑字），固定在视口，
   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;
  /* cover 对元素自身盒子（= 视口）计算，随 scale 变换一起放大缩小；
     scale 归 1 时与真身的 fixed+cover 背景像素一致 */
  background: var(--accent) url("assets/acid-bg.webp") center / cover no-repeat;
}
#acid-pour .pour-veil {
  position: absolute;
  inset: 0;
  background: var(--accent); /* 与斜杠同色的荧光纱：初始无缝盖住内容，随 zoom
                                淡出，黑字像从绿场里凝结出来，无颜色跳变 */
  pointer-events: none;
}
/* ---------- deck：what 页之后，每屏一节，正常往下滚 ---------- */
.deck { position: relative; background: #000; }
/* 每节各占满一屏，一节只讲一件事。原先机制 / 联系 / 页脚三块挤在同一屏，
   再用 space-between 摊开——富余没有去处，只会在两处各堆成一个几百 px 的洞 */
.deck .slide { min-height: 100vh; min-height: 100svh; }
/* 末节多兜 1px：上游 .portal 的 260vh 和 .acid 的 100svh 会算出小数高度，
   滚到底时顶部会漏出约 0.6px 的酸液绿（2x 屏上是一条实的物理像素线） */
.deck .slide:last-child {
  min-height: calc(100vh + 1px);
  min-height: calc(100svh + 1px);
}
.slide {
  position: relative;
  display: flex;
  align-items: center;
  background: #000;
}
.slide-inner {
  position: relative; /* 抬到 .slide-ghost 之上 */
  z-index: 1;
  width: 100%;
  max-width: 78rem; /* 全 deck 统一，逐节之间左边距不能跳 */
  margin: 0 auto;
  padding: 6rem clamp(1.5rem, 5vw, 4rem);
}
/* 出血层：只裁它自己，不能裁 .slide —— 裁掉超出视口的部分后，
   矮窗口下的正文就永远够不着了。
   不设 z-index：避免生成层叠上下文，否则 mix-blend-mode 会被隔离 */
.slide-bleed {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
/* 版式底图：黑屏上的幽灵网点手。invert 把黑网点翻白、screen 让白底透掉。
   贴右下角、往下出血：原先挂在右上角被视口右缘切掉一半，既不像出血也不像
   配图；改成居中当底纹又会被中缝线一劈两半、边缘压在 "bounded" 上。落在
   正文下方的空场里既填了空，又正好待在 bounded 那半边——握有硬件权限的一侧 */
.slide-ghost {
  position: absolute;
  right: -7vw;
  bottom: -16vh;
  width: min(46vw, 600px);
  filter: invert(1);
  mix-blend-mode: screen;
  opacity: 0.11;
  pointer-events: none;
  user-select: none;
}
p { color: var(--muted); max-width: 68ch; }
p + p { margin-top: 1rem; }

/* ---------- 03 · 机制屏：提议 | 决定 ---------- */
/* 中缝那条通高细线就是这一屏在讲的东西本身——bounded 说的是边界，两栏
   之间就得真有一条界。.slide-inner 是 78rem 居中盒且左右内边距对称，
   所以整节的 50% 恰好落在两个 1fr 之间的 gap 正中 */
#how::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  background: linear-gradient(to bottom, transparent, var(--hairline) 16%, var(--hairline) 84%, transparent);
  pointer-events: none;
}
#how .slide-inner { padding-bottom: clamp(6rem, 14vh, 9rem); } /* 给钉在屏底的页脚让位 */
.mech {
  display: grid;
  /* minmax(0,·) 是必须的：网格项默认 min-width:auto，Syne 的 "bounded" 比
     半栏还宽时会把右栏顶开（实测 453 / 564），右栏正文就压到中缝线上了 */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2.8rem, 7.2vw, 6.4rem); /* 原来是 gap+1px+gap 三段，中缝列删掉后
                                        间距加倍，两栏位置才不动 */
  align-items: start;
}
.mech-verb { /* Syne 二次出场：把两种性质喊出来，与 what 页大标题同一嗓门 */
  display: block;
  font-family: "Syne", var(--font);
  font-weight: 800;
  /* 上限 4.2rem 不是随手取的：再大 "bounded" 就超过半栏宽度，会把等分列顶歪 */
  font-size: clamp(2.4rem, 5.4vw, 4.2rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  color: var(--text);
  margin-bottom: 1.6rem;
}
.mech-col.living .mech-verb { color: var(--accent); }
.mech-col p {
  font-size: 18px; /* 15px 在这个字体 + 黑底下读不动 */
  line-height: 1.7;
  color: var(--muted-strong);
  max-width: 36ch;
}
.mech-col code {
  font-family: var(--font);
  color: var(--text);
  background: rgba(201, 250, 9, 0.12);
  padding: 0 0.3em;
}




/* 页脚：邮箱 + 署名并排的一条小字带。邮箱不值得单开一屏——独占整屏时
   它得放到 54px 才撑得住，而它本来就只是一行联系方式。
   两项都靠左：分到两端的话右端那项正好落在网点手上，14px 小字压在halftone
   网点上读起来发糊。邮箱自带下划线，不需要再加分隔符。
   脱离正文流钉在屏底，左右用与 .slide-inner 相同的内边距值，
   那条 hairline 才和正文左缘严丝合缝 */
.contact-foot {
  position: absolute;
  left: clamp(1.5rem, 5vw, 4rem);
  right: clamp(1.5rem, 5vw, 4rem);
  bottom: clamp(2rem, 5vh, 3.5rem);
  max-width: calc(78rem - 2 * clamp(1.5rem, 5vw, 4rem));
  margin: 0 auto;
  display: flex;
  gap: 2rem;
  align-items: baseline;
  font-size: 14px;
  color: var(--muted);
}
.foot-mail {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid rgba(201, 250, 9, 0.4);
  padding-bottom: 0.2em; /* 下划线让开 j 和 @ 的降部，否则线从字里穿过去 */
  transition: border-color 0.2s ease;
}
.foot-mail:hover { border-bottom-color: var(--accent); }

/* 矮窗口（笔记本 720p 一类）：收紧留白与字号，让每一节仍能一眼读完，
   不必为了读完一节而在节内再滚一段 */
@media (min-width: 768px) and (max-height: 780px) {
  .slide-inner { padding-top: 5rem; padding-bottom: 4rem; }
  .mech-verb { font-size: clamp(1.9rem, 4.4vw, 3.2rem); margin-bottom: 1.1rem; }
}

@media (max-width: 767px) {
  /* 窄屏内容本来就更高，不再强撑整屏，按内容高度排 */
  .deck .slide, .deck .slide:last-child { min-height: 0; }
  .acid-inner h1 { font-size: min(2.1rem, 9.2vw); } /* 保证最长行 "for robot hands." 不断行 */
  #hero-logo { width: min(84vw, 420px); }
  .slide-inner { padding: 5.5rem 1.5rem; }
  .slide-ghost { width: 74vw; right: -16vw; bottom: -8vh; opacity: 0.09; }
  /* 机制屏：两栏塌成上下两块，中缝那条竖线转成横线 */
  #how::before { display: none; }
  .mech { grid-template-columns: 1fr; gap: 0; }
  .mech-col + .mech-col {
    margin-top: 2.2rem;
    padding-top: 2.2rem;
    border-top: 1px solid var(--hairline);
  }
  .mech-col p { max-width: none; }
  /* 页脚回到正文流末尾。#how 必须一并退回 block——.slide 是 display:flex
     （默认 row），页脚一旦从 absolute 变回 static 就会成为 .slide-inner
     旁边的 flex 项，两块并排横放 */
  #how { display: block; }
  .contact-foot {
    position: static;
    max-width: none;
    margin: 0 1.5rem 4rem; /* 页脚在 .slide-inner 之外，左右缩进要自己补上 */
    flex-direction: column; /* 一行摆不下署名 + 邮箱，拆成两行 */
    gap: 0.7rem;
  }
}
