/* ═══════════════════════════════════════════════════════════════
   SPHARX 设计体系 v30 · 冷白克制版
   语言参考 MiMo：近黑胶囊、轻字重大字号、大圆角细描边卡片、
   非对称编辑式网格、单一平色强调。底色换为自有冷白。
   仅试点页加载（base.njk 依 designSystem 变量切换），不影响其余页面。
   ═══════════════════════════════════════════════════════════════ */

/* ── 令牌 ── */
:root {
  /* 冷白不等于纯灰。每一档中性色都带一点蓝相，页面才和英雄区那台
     #3b5cff 的极光共用同一个色温——差 3–6 个色阶，读起来只是"更干净" */
  --bg: #f4f7fc;
  --surface: #ffffff;
  --band: #edf1f9;
  --line: #e2e7f1;
  --line-strong: #ccd4e2;

  --ink: #171a24;
  --ink-2: #575f70;
  --ink-3: #929aac;

  --acc: #3b5cff;
  --acc-ink: #2b46c9;
  --acc-soft: rgba(59, 92, 255, .07);

  --r-xs: 12px;
  --r-sm: 12px;
  --r-md: 12px;
  --r-lg: 12px;
  /* 全站按钮、胶囊、卡片仍是同一个 12。只有英雄带用 24：那是 MiMo 实测的
     数（静息 1260×480、border-radius 24px，窄屏 12），带子是这一轮要对齐的
     对象，而它比任何卡片都大一个量级——大圆角配大面板是同一个尺度的事 */
  --r-xl: 24px;
  --pill: 12px;

  /* 影子的颜色是夜空蓝（22,32,64）而不是近黑（16,20,28）：
     落在冷白底上，边缘读起来是"阴影"而不是"脏" */
  --sh: 0 1px 2px rgba(22, 32, 64, .05);
  /* 白卡落在冷白底上，顶边那条比底边亮才读得出厚度。inset 高光只加在
     卡片族（--sh-card），按钮和胶囊仍然用 --sh，否则小控件会发虚 */
  --sh-card: 0 1px 2px rgba(22, 32, 64, .05), inset 0 1px 0 rgba(255, 255, 255, .9);
  /* 深色面板（#14161b）落在冷白上，接触影要比浅色卡更实才压得住；顶缘那
     条冷蓝内高光和浅色卡片的受光逻辑是同一套，只是光源色换成夜空的蓝。
     它们静息就带投影但没有 hover 态，所以单独一档——不带 --sh-hover
     那圈只在悬停时才有的蓝环 */
  --sh-dark: 0 18px 40px -22px rgba(22, 32, 64, .42),
             0 3px 10px -4px rgba(22, 32, 64, .22),
             inset 0 1px 0 rgba(109, 132, 255, .16);
  /* 悬停阴影分三层：贴着边缘的接触影、抬起来落在桌面上的投影，
     再加一圈极淡的蓝色环境光——卡片浮起时像被自己的极光照着 */
  --sh-hover: 0 2px 6px -2px rgba(22, 32, 64, .07), 0 16px 32px -14px rgba(22, 32, 64, .14),
              0 0 0 1px rgba(59, 92, 255, .05);
  --sh-pop: 0 16px 44px rgba(22, 32, 64, .12);

  --sans: "MiSans", "PingFang SC", "HarmonyOS Sans SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", Menlo, Consolas, monospace;
  /* 粒子标识专用：靠笔画粗细对比出颗粒层次，等宽体拼出来是条码。
     拉丁走 Georgia，中文必须显式点思源宋体——它和 Georgia 同属高对比衬线，
     交给通用 serif 兜底会掉到宋体，笔画对比度差一档 */
  --mark-serif: Georgia, "Iowan Old Style", "Times New Roman", "Noto Serif SC",
    "Source Han Serif SC", "Songti SC", "SimSun", serif;

  --wrap: 1180px;
  --ease: cubic-bezier(.2, .7, .2, 1);
  /* MiMo 全站揭示用的同一条 expo-out：opacity/transform .8s */
  --ease-expo: cubic-bezier(.16, 1, .3, 1);
  --nav-h: 68px;
}

/* ── 基础 ── */
*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
/* 锚点跳转给 sticky 导航让位：导航 68 + 16 呼吸，点「一行命令安装」
   之后 #install 顶边不再藏进导航条底下 */
[id] { scroll-margin-top: calc(var(--nav-h) + 16px); }

body {
  margin: 0;
  /* 横幅被 scrub 拉宽时会短暂超出视口，clip 不留滚动条也不建滚动容器 */
  overflow-x: clip;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.75;
  letter-spacing: .002em;
  -webkit-font-smoothing: antialiased;
}

/* ── 首页光标：纯黑线条火箭 ──
   头朝左上（↖），热点钉在弹头上，所以「指哪儿」还是「打哪儿」，
   点按钮不会偏出一枚火箭的距离。白色描边底画在 SVG 里：显影后的
   照片、深色终端块、品牌蓝上，纯黑线条会整个读不出来。
   PNG 兜底给 Safari（它不吃 SVG 光标），64 那档给高 DPI。
   只覆盖有 cursor:pointer 的元素，输入框留给 UA 的 I 型光标 */
body.page-home,
body.page-home :is(a, button, summary, label, .btn, .copy-btn, .nav-burger) {
  cursor: url('/img/cursor-rocket.svg') 6 6,
          url('/img/cursor-rocket-32.png') 6 6,
          url('/img/cursor-rocket-64.png') 12 12,
          auto;
}

img, svg { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
h1, h2, h3, h4 { margin: 0; font-weight: 500; letter-spacing: -.018em; line-height: 1.22; }
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

.mono { font-family: var(--mono); font-variant-ligatures: none; }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 28px; }

::selection { background: rgba(59, 92, 255, .16); }

:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}

/* ── 顶部发布条 ── */
.strip {
  background: var(--ink);
  color: rgba(255, 255, 255, .82);
  font-size: 12.5px;
  letter-spacing: .01em;
}
.strip-inner {
  min-height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
  text-align: center;
}
.strip b { color: #fff; font-weight: 500; }
.strip a {
  color: #fff;
  border-bottom: 1px solid rgba(255, 255, 255, .34);
  padding-bottom: 1px;
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.strip a:hover { border-bottom-color: #fff; }
.strip-dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: #4ade80;
  /* 「在线」这件事第一次有光——黑条上 5px 的实心点不发光就会被读成一个像素 */
  box-shadow: 0 0 8px 1px rgba(74, 222, 128, .55);
  flex: none;
}

/* ── 导航：纯白 + 细描边，不做玻璃拟态 ── */
.nav {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  transition: border-color .28s var(--ease), box-shadow .28s var(--ease);
}
/* components.js 在 scrollY>30 时挂 .scrolled。之前没有对应样式，那个类是
   空转的：导航飘在内容之上却和页面顶部毫无区别。静息态只有一条 1px 细线，
   滚动态补一层很浅的投影，把它读成「浮在页面上的一层」，不动高度也不上模糊 */
.nav.scrolled {
  border-bottom-color: rgba(26, 29, 35, .10);
  box-shadow: 0 8px 20px -14px rgba(22, 32, 64, .28);
}
.nav-inner {
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.brand {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--ink);
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
}
.brand-sup {
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: .04em;
  color: var(--ink-3);
}

.nav-right { display: flex; align-items: center; gap: 4px; }
/* 定高 + 只在水平方向留白：字号不同的项（CTA）不会把基线顶歪 */
.nav-right > a,
.nav-dropdown > a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 38px;
  padding: 0 13px;
  border-radius: var(--pill);
  font-size: 14.5px;
  line-height: 1;
  color: var(--ink-2);
  transition: color .18s var(--ease), background .18s var(--ease);
}
.nav-right > a:hover,
.nav-dropdown > a:hover { color: var(--ink); background: var(--band); }
/* 按下时把底色再压深一档：只有 hover 的导航点下去没有回弹，
   在触屏上就是「不知道有没有点中」 */
.nav-right > a:active,
.nav-dropdown > a:active { background: rgba(26, 29, 35, .07); }
.nav-right > a.nav-cur,
.nav-dropdown > a.nav-cur { color: var(--ink); font-weight: 500; }

.nav-cta {
  margin-left: 8px;
  padding: 0 16px;
  border-radius: var(--pill);
  background: var(--ink);
  color: #fff !important;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: .06em;
  transition: background .18s var(--ease), box-shadow .18s var(--ease), transform .18s var(--ease);
}
/* #1a1d23 → #000 肉眼基本看不出差别，等于没有 hover。
   黑胶囊能改的只有「离面板多远」，所以用抬升 + 投影做出反馈 */
.nav-cta:hover {
  background: #000;
  transform: translateY(-1px);
  box-shadow: 0 5px 14px -6px rgba(22, 32, 64, .5);
}
.nav-cta:active { transform: translateY(0); box-shadow: none; }

.nav-dropdown { position: relative; display: flex; }
.nav-dropdown > a::after {
  content: '';
  width: 7px; height: 7px;
  margin-top: -2px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  opacity: .5;
}
.nav-dropdown-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translate(-50%, -4px);
  min-width: 186px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-pop);
  opacity: 0;
  visibility: hidden;
  transition: opacity .22s var(--ease-expo), transform .22s var(--ease-expo), visibility .22s;
}
.nav-dropdown:hover .nav-dropdown-menu,
.nav-dropdown:focus-within .nav-dropdown-menu {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}
.nav-dropdown-menu a {
  display: block;
  padding: 9px 12px;
  border-radius: var(--r-xs);
  font-size: 14px;
  color: var(--ink-2);
  transition: background .16s var(--ease), color .16s var(--ease);
}
.nav-dropdown-menu a:hover { background: var(--band); color: var(--ink); }
.nav-dropdown-menu a:active { background: rgba(26, 29, 35, .07); }

.nav-burger { display: none; }

/* ── 按钮：近黑胶囊为主 ── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 46px;
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: var(--pill);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: .005em;
  cursor: pointer;
  transition: background .18s var(--ease), border-color .18s var(--ease),
              color .18s var(--ease), transform .18s var(--ease);
}
.btn::after {
  content: '→';
  font-family: var(--mono);
  font-size: 15px;
  transition: transform .22s var(--ease);
}
.btn:hover::after { transform: translateX(4px); }
.btn:active { transform: translateY(1px); }

.btn-solid {
  background: var(--ink);
  color: #fff;
  box-shadow: 0 2px 8px -4px rgba(22, 32, 64, .4);
  transition: background .18s var(--ease), border-color .18s var(--ease),
              color .18s var(--ease), transform .18s var(--ease),
              box-shadow .18s var(--ease);
}
/* #171a24 → #000 看不出差别，主按钮的 hover 靠抬升和一层冷蓝环境光说话 */
.btn-solid:hover {
  background: #000;
  transform: translateY(-1px);
  box-shadow: 0 8px 18px -8px rgba(22, 32, 64, .55), 0 6px 20px -10px rgba(59, 92, 255, .45);
}

.btn-ghost {
  background: var(--surface);
  border-color: var(--line-strong);
  color: var(--ink);
  transition: background .18s var(--ease), border-color .18s var(--ease),
              color .18s var(--ease), transform .18s var(--ease),
              box-shadow .18s var(--ease);
}
.btn-ghost:hover { border-color: var(--ink); background: var(--band); }

/* 无箭头的按钮变体 */
.btn-bare::after { content: none; }

.btn-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 14.5px;
  color: var(--acc);
}
.btn-link::after {
  content: '→';
  font-family: var(--mono);
  transition: transform .2s var(--ease);
}
.btn-link:hover::after { transform: translateX(4px); }

/* ── 尾行治理 ──
   自动换行的正文最容易收在两个字上，一行里孤零零挂个尾巴；两行标题也会
   一行长一行短。pretty 管正文不出孤字，balance 管标题两行等长。
   作者自己写过 <br> 的（.sec-title / .sec-note / .sub-title / .sub-lede /
   .about-intro p）一律不进这里——那儿的断行是刻意的，balance 会把对句拆散。
   两者都是渐进增强，不支持的浏览器按原样排版 */
/* 上一版只写了 p 标签，漏掉了 div/li/dd 这些同样自动换行的正文：
   .product-desc 是 div、.feat-list 是 li、规格值是 dd，全都没被覆盖 */
.cell p, .about-vision-card p, .about-team-card p, .about-phi-card p,
.news-feature p, .news-card p, .band p,
.product-desc, .feed-sum, .feat-list li, .lang-note, .sub-fine,
.feat-desc, .jobs-bullets li,
.news-detail-body p, .news-detail-body li { text-wrap: pretty; }
/* 窄列里的短标签（一两行就完事）用 balance 而不是 pretty：pretty 只管
   不产生孤行，掰不动「11 字 + 2 字」这种分配。实测 390 下 .stat-label
   的尾行从 36–47px 抬到 69–83px */
.cell h3, .news-feature h2, .news-card h2, .band h2,
.sec-title, .feat h3, .about-team-card h3, .jobs-card h3,
.stat-label, .spec dd, .cell-meta span, .cert-dl dd, .layer b, .layer span,
.sku-attr dd, .jobs-col-h,
.news-detail-title, .news-detail-body h2 { text-wrap: balance; }

/* ── Section 原语 ── */
.sec { padding: clamp(64px, 5.4vw, 96px) 0; }
.sec-tight { padding-top: clamp(8px, 1vw, 16px); }
.sec-soft { background: var(--surface); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }

.sec-head { max-width: 660px; margin-bottom: clamp(34px, 3.6vw, 56px); }
.sec-head-center { margin-left: auto; margin-right: auto; text-align: center; }

/* 编号 kicker：一行有分量的引导标签，不是需要眯眼看的微标。
   前导短线在区块揭示时从左抽出，用全站同一条 expo-out */
.sec-idx {
  display: inline-flex;
  align-items: center;
  gap: clamp(10px, 1vw, 14px);
  margin-bottom: clamp(16px, 1.5vw, 22px);
  font-family: var(--mono);
  font-size: clamp(13px, 1.05vw, 16px);
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--acc-ink);
  text-transform: uppercase;
}
.sec-idx::before {
  content: '';
  width: clamp(26px, 2.8vw, 44px); height: 2px;
  border-radius: 2px;
  background: var(--acc);
  transform-origin: left center;
}
.js [data-reveal].in .sec-idx::before {
  animation: v30-rule .62s var(--ease-expo) .14s both;
}
@keyframes v30-rule { from { transform: scaleX(0); } }

.sec-title {
  font-size: clamp(28px, 3.4vw, 40px);
  font-weight: 500;
  letter-spacing: -.024em;
  color: var(--ink);
  margin-bottom: clamp(14px, 1.4vw, 20px);
}
/* 引导段跟标题一个量级，不是脚注。左边一道强调细杆把它读成编者按；
   居中的区块没有左边界可挂，就不挂 */
.sec-note {
  font-size: clamp(16px, 1.35vw, 21px);
  font-weight: 400;
  line-height: 1.85;
  letter-spacing: .004em;
  color: var(--ink-2);
}
.sec-head:not(.sec-head-center) .sec-note {
  padding-left: clamp(16px, 1.5vw, 22px);
  border-left: 2px solid rgba(59, 92, 255, .26);
}

/* ── Hero ── */
/* 首屏排法：文案在正常流里，动效带在文案下方，两块地谁也不压谁。
   字标高度由 innerHeight 封顶又居中在卡片中段，所以「不滚动就能把字样
   看全」靠的是字标自己的尺，不是把卡片缩小的理由 */
.hero { padding: clamp(14px, 1.6vw, 22px) 0 clamp(40px, 4.6vw, 68px); text-align: center; }
.hero-copy {
  max-width: 900px;
  margin: 0 auto var(--copy-gap);
  padding: 0 var(--art-inset);
}
.hero-title {
  margin: 0 0 var(--title-mb);
  /* 字号同时被宽和高采两把尺：视口矮下去的时候标题得跟着收。
     收的理由不是审美，是几何——标题每多一行高，下面的带子就少一截 */
  font-size: var(--hero-fs);
  font-weight: 300;
  letter-spacing: -.038em;
  line-height: 1.14;
}
/* 占位行撑住高度，打字行绝对定位叠上去：逐字打出时布局零位移 */
.hero-line { display: block; position: relative; text-align: center; }
.hero-line-sizer { visibility: hidden; white-space: nowrap; user-select: none; }
.hero-line-typing {
  position: absolute;
  inset: 0;
  white-space: nowrap;
}
.hero-line-strong { font-weight: inherit; }
/* 没有 JS 就没有打字机，占位行是唯一能看见标题的机会 */
html:not(.js) .hero-line-sizer { visibility: visible; }
.hero-cursor {
  margin-right: -.5em;
  animation: v30-blink 1s step-end infinite;
}
@keyframes v30-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
/* 打完字光标闪两下就退场，不能常驻。退场走全站那条 --ease，
   平直的 linear 淡出读起来像「被抹掉」而不是「退下去」 */
.hero-cursor.out {
  animation: none;
  opacity: 0;
  transition: opacity .26s var(--ease);
}
.hero-lede {
  max-width: 620px;
  margin: 0 auto 12px;
  font-size: clamp(15.5px, 1.5vw, 18px);
  font-weight: 300;
  line-height: 1.95;
  color: var(--ink-2);
  letter-spacing: .004em;
}
.hero-lede b { font-weight: 500; color: var(--ink); }
/* 打完字才放行按钮，与 MiMo 的 heroButtonsFadeIn 同一条 */
.js.type-ready .hero-actions {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .45s var(--ease-expo), transform .45s var(--ease-expo);
}
.js.type-ready .hero-actions.in { opacity: 1; transform: none; }
.hero-cta { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; }
.hero-legal {
  margin: 0;
  font-size: 11px;
  letter-spacing: .09em;
  color: var(--ink-3);
}
.hero-specs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 24px;
  font-size: 11.5px;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.hero-specs span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 16px;
  line-height: 2;
}
.hero-specs span + span { border-left: 1px solid var(--line); }
.hero-specs b { color: var(--ink); font-weight: 500; font-size: 13px; }

/* 影院横幅：开场就是近乎满幅的圆角卡，向下滑时 scrub 只补最后那点
   gutter 并把圆角收平。
   stage 不给左右 padding——JS 用 stage.clientWidth 算居中偏移，
   两边留白改由卡片自身宽度承担，否则 margin 会算错半个 gutter。
   底图是勇气号收工前那张自拍抽出来的 260×100 冷灰像素，按 100% 100% 铺：
   不裁切、不缩放失真，粒子那层和它严格同比例，卡片拉多大都不错位。
   边框去掉——画布的坐标原点是 padding box，留着 1px 边框会让两套
   坐标系差一个边框宽度，画的东西和像素底就会整体偏移 */
.hero-stage {
  --art-inset: clamp(14px, 2.2vw, 34px);
  --hero-fs: clamp(40px, min(5.2vw, 8.2vh), 72px);
  --title-mb: clamp(12px, 2vh, 20px);
  --copy-gap: clamp(16px, 2.2vh, 26px);
  --art-h: 480px;
  position: relative;
  padding: 0;
  /* 给带子的行程预留落点。scrub 的原点在顶边，行程走满时视觉盒长到
     100vh——比布局盒多出 (100vh − 480)，这段超出量正好盖住卡下方的注脚
     （实测 1440×900 上 y=700：注脚三行压在照片上、还和 591~1071 的字标
     撞成一团）。MiMo 靠每帧改布局把注脚推开，我们不能改（那是 157.6ms
     的重栅格），所以把这段距离一次性写死成下外边距：静息时超出量藏在
     首屏线以下看不见，行程走完后带子底边和注脚的间距恒定 36px，
     滚起来就是 MiMo 那条「带子顶着一屏走、注脚紧跟其后」的观感 */
  margin-bottom: max(0px, calc(100vh - var(--art-h) + 8px));
}
/* 静息几何照 MiMo 实测的尺：高度优先。它的带子在 1440／1920／2560 三档上
   都是 1260×480，宽随容器但封顶 1260，高度永远是 480——大屏上它不铺满，
   是一块居中的板。之前我们是宽度优先（100% 减 gutter，封顶 2000，高由
   3:1 反推），2560 上算出来 2000×667，比 MiMo 宽 59%。这一轮改成同一把尺：
   封顶 1260、高度写死在 .hero-art-shader 上，1440 落点 left 正好 90、
   2560 正好 650，和 MiMo 一个像素都不差。
   第三把尺 (100vh − fold-cost) 早就不在了；高度固定之后首屏仍然兜得住：
   1440×900 上带子顶边 381、底边 861，离首屏线还差 39px */
.hero-art {
  position: relative;
  width: min(calc(100% - var(--art-inset) * 2), 1260px);
  max-width: none;
  margin: 0 auto;
  background: var(--surface);
  box-shadow: var(--sh);
  overflow: hidden;
  isolation: isolate;
  touch-action: pan-y;
  transform-origin: top center;
  /* 滚动 scrub 只往这上面写 transform: scale(sx, sy)。必须常驻合成层，
     改了才走合成器而不是重栅格整张卡片（底下压着三层几百万像素的画布，
     重栅格单笔实测 157.6ms）。行程是滚轮期间反复发生的，所以不按需加卸
     ——切换 will-change 本身要挂/摘一层，代价比一直挂着大。
     原点是 top center：MiMo 的顶边严格锁文档（实测 top = 397 − y，
     一个像素的视差都没有）。默认原点会把缩放的一半增量摊到顶边上，
     行程走满 420px 时顶边就多跑 210px，那条「锁文档」的直线就断了 */
  transform: scale(var(--scrub-sx, 1), var(--scrub-sy, 1));
  will-change: transform;
  /* 非等比缩放会把圆角拉成椭圆，所以这里除回各自的缩放率：视觉上半径
     恒等于 --r-xl × 档位，实测 MiMo 的 r 是一条随行程降到底的斜线
     （1440×900 上每 20px 滚动 −1.77，走完正好 0）。
     档位由 JS 写在 --scrub-r 上，只有 4 档：改 border-radius 会重新
     栅格 clip 路径，逐帧跟着 p 走等于把 scrub 变成每帧重画 */
  border-radius: calc(var(--r-xl) * var(--scrub-r, 1) / var(--scrub-sx, 1))
               / calc(var(--r-xl) * var(--scrub-r, 1) / var(--scrub-sy, 1));
}
/* 没有 JS 就没有「刮开」这件事，那就直接摆成品：整幅照片常驻 */
html:not(.js) .hero-art { background: var(--surface) url('/img/hero-band.jpg') center / cover no-repeat; }
/* 底图是勇气号照片的点阵化。格距、刮痕、字标用的是同一个网格单位，
   整张图由 JS 按设备像素一次算成 ImageData：让浏览器替我们放大方块，
   得到的就是「糊 JPEG」；自己按整数格铺，得到的才是「LED 点阵」 */
.hero-art-base {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}
/* 白纱：常驻半透，刮开处才显影。触屏同样要刮，所以不再有 hover 开关。
   两层都写成 100% 而不是 JS 逐帧钉像素：滚动 scrub 拉长卡片时，位图由
   CSS 盒自己跟着长，一帧一次样式写入都省下来（静息和停手时画布本来就是
   按卡幅开的，100% 与像素值最多差一格的内缩边，被 overflow 裁掉） */
.hero-mask {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  /* 浓淡由 JS 逐帧写在这层的 opacity 上：合成器属性，改了不重画画布 */
  will-change: opacity;
  pointer-events: none;
  display: block;
}
/* 高度由 stage 上的 --art-h 定（桌面 480、窄屏 240）。MiMo 在 1024×768 上也是
   968×480（窄屏不砍高，
   只砍宽），在 ≤900 那一档换成 240，所以带子的比例是随视口变的：静息幅
   从 2.625（桌面）到 2.0（1024）到 1.43（手机）。
   比例既然不再固定，底图就不能再按 CSS 拉伸——#heroBase 把 hero-band.jpg
   降采样成格表的时候走 cover 裁切（长边对齐、多余裁掉），换视口裁的是
   两边而不是把勇气号压扁。
   这一层还要把外层的缩放整个抵消掉：外层 scale 是双轴不等速的（MiMo 实测
   宽 +0.667/px、高 +1.55/px），这里除回各自的率，净缩放正好是 1。字标
   因此一个像素都不长，只是被越来越宽的带子围起来——这正是 MiMo 的做法：
   它的画布每帧按带子的 CSS 尺寸重开，字号却是 18vw（视口没动，字就不动），
   画在画布中心，于是字恒定、位置随带子中心走。净缩放为 1 还有个白赚的
   好处：画布的 CSS 尺寸和位图尺寸严格 1:1，行程里不掉分辨率 */
.hero-art-shader {
  position: relative;
  width: 100%;
  height: var(--art-h);
  z-index: 2;
  transform: scale(calc(1 / var(--scrub-sx, 1)), calc(1 / var(--scrub-sy, 1)));
}
/* 底图与白纱这层的净缩放取 max(sx, sy)：位图按等比放大到刚好盖满带子，
   超出的一侧由外层 overflow 裁掉——这正是 MiMo 那张 object-fit: cover
   的图在同一段行程里做的事。取 max 而不是 sy 是因为绝大多数视口 sy 更大，
   但 3440×1080 这种超宽带子反过来是 sx 更大，取错那把尺会露出白边。
   写在 custom property 上是一帧三笔属性写入、五个元素重算样式，
   比逐层写 transform 少两笔，也不用把比例算在 JS 里 */
.hero-art {
  --scrub-m: max(var(--scrub-sx, 1), var(--scrub-sy, 1));
}
.hero-art-base,
.hero-mask {
  transform: scale(calc(var(--scrub-m) / var(--scrub-sx, 1)),
                   calc(var(--scrub-m) / var(--scrub-sy, 1)));
}
@media (max-width: 900px) {
  /* 窄屏按 MiMo 实测：带子 334×240、r12，而且完全没有行程（JS 里
     matchMedia 同一条线门禁）。改 --r-xl 而不是直接写 border-radius，
     因为 JS 从 --r-xl 读半径基准。没有行程就没有超出量，预留的外边距
     必须一起清零，否则手机上会多出 600 多的死白 */
  .hero-art { --r-xl: 12px; }
  .hero-stage { --art-h: 240px; margin-bottom: 0; }
}
/* 无 JS 时的静态标识：粒子画布一启动就淡出 */
.hero-art-static {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transition: opacity .5s var(--ease-expo);
}
.hero-art-static text { font-family: var(--mark-serif); font-weight: 400; fill: var(--ink); }
/* 无 JS 的静态标独占整张卡，两行锁定按墨框居中 */
.hero-art-static .mark-lat { font-size: 70px; }
.hero-art-static .mark-cjk { font-size: 98px; }
.hero-art.live .hero-art-static { opacity: 0; }
.hero-art-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  touch-action: pan-y;
}
/* 卡下方的说明区。必须自带定位层：行程走满时卡片的视觉盒比布局盒长出
   420px（1440×900 上视觉底边到文档 1281、注脚在 889~1040），而 .hero-art
   是 position:relative，静态流里的文字排在定位元素之后 —— 实测这三行
   整个压在照片底下（elementFromPoint 命中 canvas#heroWord）。终端卡
   早就靠同一招活下来的（z-index: 2）。 */
.hero-foot {
  position: relative;
  z-index: 2;
  padding-top: clamp(18px, 2vw, 28px);
}

/* 终端：深色圆角卡，与居中文案形成错位 */
.hero-term {
  position: relative;
  z-index: 2;
  max-width: 640px;
  margin: clamp(34px, 3.4vw, 52px) auto 0;
  text-align: left;
}
.term {
  background: #14161b;
  border-radius: var(--r-md);
  box-shadow: var(--sh-dark);
  overflow: hidden;
  text-align: left;
}
.term-bar {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 13px 16px;
  border-bottom: 1px solid rgba(255, 255, 255, .07);
}
.term-bar i { width: 9px; height: 9px; border-radius: 50%; background: rgba(255, 255, 255, .17); }
.term-title {
  margin-left: 8px;
  font-size: 11.5px;
  letter-spacing: .06em;
  color: rgba(255, 255, 255, .38);
}
.term-body {
  padding: 20px 22px 24px;
  font-size: 13px;
  line-height: 2.05;
  color: #c8ccd4;
  white-space: pre-wrap;
  overflow-x: auto;
}
.term-body .t-p { color: #7b8cff; }
.term-body .t-ok { color: #4ade80; }
.term-body .t-cursor {
  display: inline-block;
  width: 7px; height: 14px;
  margin-left: 4px;
  vertical-align: -2px;
  background: rgba(255, 255, 255, .5);
  animation: v30-blink-term 1.1s steps(1) infinite;
}
/* 名字不能和标题打字机那枚 v30-blink 撞：同名会静默覆盖前一条定义，
   两处光标看着像同步闪，其实是被终端这条改了节拍 */
@keyframes v30-blink-term { 50% { opacity: 0; } }

/* ── 产品双卡 ── */
.products {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}
.products.three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.product {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(26px, 3vw, 36px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  transition: border-color .22s var(--ease-expo), transform .22s var(--ease-expo),
              box-shadow .22s var(--ease-expo);
}
.product:hover {
  border-color: var(--line-strong);
  box-shadow: var(--sh-hover);
  transform: translateY(-2px);
}
.product-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 18px;
}
.product-badge {
  align-self: flex-start;
  margin-bottom: 18px;
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  font-size: 11px;
  letter-spacing: .08em;
  color: var(--ink-3);
}
.product-top .product-badge { align-self: center; margin-bottom: 0; }
/* 头行右肩原来是空的：把这张卡最硬的那两个数字挪上来，
   空档变成信息，下面的规格表也就不用再重复一行 */
.product-figure {
  font-size: 11px;
  letter-spacing: .1em;
  color: var(--ink-3);
  white-space: nowrap;
}
.product-figure b {
  font-weight: 500;
  font-size: 15px;
  letter-spacing: 0;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.product h3 { font-size: 26px; font-weight: 500; letter-spacing: -.02em; }
.product-en {
  margin: 6px 0 16px;
  font-size: 11px;
  letter-spacing: .16em;
  color: var(--ink-3);
}
/* flex:1 让正文吃掉两卡行数差（RT 4 行 / OS 3 行），把「了解详情」链接与
   规格表压到同一基线，两卡左右齐平 */
.product-desc { flex: 1; font-size: 15px; line-height: 1.9; color: var(--ink-2); }
.product-more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 18px 0 0;
  font-size: 14px;
  font-weight: 500;
  color: var(--acc);
}
.product-more::after {
  content: '→';
  font-family: var(--mono);
  transition: transform .2s var(--ease);
}
.product-more:hover::after { transform: translateX(4px); }

.product-spec {
  margin: 24px 0 0;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 11px;
}
.product-spec div {
  display: grid;
  grid-template-columns: 104px 1fr;
  gap: 14px;
  align-items: baseline;
  font-size: 12.5px;
}
.product-spec dt { color: var(--ink-3); letter-spacing: .06em; }
.product-spec dd { margin: 0; color: var(--ink); }

/* ── 安装面板：深色块 ── */
.install {
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
  gap: clamp(28px, 4vw, 56px);
  margin-top: 20px;
  padding: clamp(28px, 3.4vw, 44px);
  background: #14161b;
  border-radius: var(--r-lg);
  box-shadow: var(--sh-dark);
  color: #c8ccd4;
}
.install h3 { font-size: 25px; font-weight: 500; color: #fff; margin-bottom: 14px; }
.install p { font-size: 14.5px; line-height: 1.9; color: rgba(255, 255, 255, .62); }
.install .sec-idx { color: rgba(255, 255, 255, .76); }
.install .sec-idx::before { background: #6d84ff; }
.install code {
  padding: 1px 6px;
  border-radius: var(--r-xs);
  background: rgba(255, 255, 255, .08);
  font-size: 12.5px;
  color: #e6e9ef;
}
.install-cmds { display: grid; gap: 10px; align-content: start; }
.install-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  background: rgba(255, 255, 255, .045);
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: var(--r-sm);
}
.install-os { flex: none; width: 104px; font-size: 10.5px; letter-spacing: .12em; color: rgba(255, 255, 255, .38); }
.install-row code {
  flex: 1;
  min-width: 0;
  padding: 0;
  background: none;
  font-size: 12.5px;
  line-height: 1.75;
  color: #dfe3ea;
  /* break-all 会抢在 <wbr> 前面把 URL 从单词中间劈开（agentrt/rele
     ases 那种读法）。断点交给标记里的 <wbr>，只在整段塞不下时才兜底断 */
  overflow-wrap: break-word;
}

.copy-btn {
  display: inline-grid;
  place-items: center;
  width: 30px; height: 30px;
  flex: none;
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: var(--r-xs);
  background: rgba(255, 255, 255, .05);
  color: rgba(255, 255, 255, .6);
  cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease), transform .18s var(--ease);
}
.copy-btn:hover { background: rgba(255, 255, 255, .12); color: #fff; }
/* 复制成功只换图标太快，指尖看不清「刚刚按到了」。补一档回弹 */
.copy-btn:active { transform: scale(.92); }
/* 同一颗图标换状态不许换笔画：1.4 的复制框和 1.8 的勾并排看，
   点一下就像有人把线换粗了。UI 图标统一 1.5 */
.copy-btn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.5; }
.copy-btn .ic-check { display: none; }
.copy-btn.copied { color: #4ade80; border-color: rgba(74, 222, 128, .34); }
.copy-btn.copied .ic-copy { display: none; }
.copy-btn.copied .ic-check { display: block; }

/* ── 架构：层叠行 + 侧栏统计 ── */
.arch {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: clamp(24px, 3.4vw, 48px);
  /* 右栏靠 align-items:stretch 撑到与左侧 stack 等高，内部统计再纵向铺满，
     两栏底边咬合 */
  align-items: stretch;
}
.stack {
  padding: 8px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
}
.layer {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 16px;
  padding: 14px 4px 14px 15px;
  border-bottom: 1px solid var(--line);
  /* 每层左边一道轨，连起来就是这根脊柱；越靠近底座越实，
     一眼读得出「压在上面」的重量。只用平色步进，不做渐变 */
  border-left: 2px solid rgba(59, 92, 255, .1);
  font-size: 14px;
}
.stack .layer:nth-child(2) { border-left-color: rgba(59, 92, 255, .16); }
.stack .layer:nth-child(3) { border-left-color: rgba(59, 92, 255, .23); }
.stack .layer:nth-child(4) { border-left-color: rgba(59, 92, 255, .3); }
.stack .layer:nth-child(5) { border-left-color: rgba(59, 92, 255, .38); }
.stack .layer:nth-child(6) { border-left-color: rgba(59, 92, 255, .47); }
.stack .layer:nth-child(7) { border-left-color: rgba(59, 92, 255, .56); }
.layer:last-child { border-bottom: none; }
.layer b { font-size: 12px; font-weight: 500; letter-spacing: .04em; color: var(--acc); }
.layer.os { border-left-color: rgba(20, 22, 27, .46); }
.layer.os b { color: var(--ink); }
.layer span { color: var(--ink-2); }
.layer i {
  font-style: normal;
  font-size: 13.5px;
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
/* 这八行是全页信息密度最高的表，之前却零反馈。hover 走行表同一套语言：
   点亮本行轨段、说明轻推、数字转主色——曲线与时长按全站统一 */
.layer { transition: border-left-color .22s var(--ease); }
.layer span, .layer i { transition: translate .22s var(--ease), color .18s var(--ease); }
.layer:hover { border-left-color: var(--acc); }
.layer:hover span { translate: 3px 0; }
.layer:hover i { color: var(--acc); }
.layer-gap {
  padding: 9px 4px 9px 15px;
  border-bottom: 1px solid var(--line);
  border-left: 2px solid rgba(20, 22, 27, .12);
  font-size: 11px;
  letter-spacing: .05em;
  color: var(--ink-3);
  text-align: center;
}

/* 左侧层表很高，右栏原来 align-content:start 把统计挤在顶部、下方大片死白。
   改成两行 1fr + foot auto：四张统计纵向撑开填满 stack 高度，// 注释钉到底，
   两栏底边咬合 */
.arch-side { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: 1fr 1fr auto; gap: 12px; }
.stat {
  display: grid;
  grid-template-columns: 1fr;
  align-content: center;
  gap: 7px;
  padding: 20px 18px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--sh);
}
.stat-num {
  font-size: clamp(34px, 3vw, 46px);
  font-weight: 300;
  letter-spacing: -.04em;
  line-height: 1.02;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.stat-label { font-family: var(--mono); font-size: 11.5px; letter-spacing: .03em; line-height: 1.6; color: var(--ink-3); }
.arch-foot { grid-column: 1 / -1; margin-top: 4px; font-size: 11.5px; line-height: 2; color: var(--ink-3); }

/* ── 编号行（核心技术 / 动态 / 联系方式 / 职位） ──
   序号那一栏原来是 92px 的纯死白，行与行靠一条通栏 hairline 分开，整块
   读起来是一张表。现在把这条槽位变成轨道：1px 竖轨串起每行的节点方块，
   分隔线从轨道右侧起画，行于是有了「列表」的骨架。hover 不再整行铺底色
   ——那层 band 会把卡片的跟手冷光盖掉——改成点亮本行的轨段、节点和序号 */
.rows {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  overflow: hidden;
}
.row {
  position: relative;
  display: grid;
  /* 名称列与说明列各自成栏。原来标题贴左、标签钉右，中间一条 600px 的死
     通道，一行信息被劈成两截，扫读要在两头之间跳一次。现在按「字段名 →
     值」排：左栏名称+标签，右栏整段说明，阅读宽度一次给足 */
  grid-template-columns: 62px minmax(0, 226px) minmax(0, 1fr) auto;
  grid-auto-rows: min-content;
  align-items: start;
  column-gap: 30px;
  row-gap: 9px;
  padding: 22px 26px 22px 52px;
  /* 分隔线不用 border-bottom：它只能通栏。画成一条背景带，起点让开轨道 */
  background-image: linear-gradient(var(--line), var(--line));
  background-size: calc(100% - 78px) 1px;
  background-position: 52px 100%;
  background-repeat: no-repeat;
}
/* 竖轨：每行各画自己那一段，相邻段首尾相接就是一根连续的线；
   末行只画到节点中心，不然线会吊在最后一项下面 */
.row::before {
  content: '';
  position: absolute;
  left: 27px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line-strong);
  transition: background-color .22s var(--ease);
}
.row:last-child::before { bottom: auto; height: 34px; }
/* 末行的分隔带正好压在容器自己的描边上，看着像 2px 粗，撤掉 */
.rows > .row:last-child { background-image: none; }
/* 节点：压在轨上的实心小圆点。原来的空心方块在窄屏读起来像没画完的
   复选框，实心点在任意尺寸都是「列表节点」。中线对齐标题第一行的光学中线 */
.row::after {
  content: '';
  position: absolute;
  left: 24px;
  top: 30px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--line-strong);
  transition: background-color .22s var(--ease), transform .26s var(--ease-expo);
}
/* 位移只作用在真正成框的格子上：.row-body 已经 display:contents，
   它自己不再有盒子 */
.row > *, .row-body b, .row-body small { transition: translate .22s var(--ease); }
a.row { cursor: pointer; }
a.row:hover::before { background: var(--acc); }
a.row:hover::after {
  background: var(--acc);
  transform: scale(1.25);
}
a.row:hover .row-no { color: var(--acc); }
a.row:hover .row-body b, a.row:hover .row-body small { translate: 4px 0; }
a.row:hover .row-body b { color: var(--acc); }
a.row:hover .row-tag { color: var(--ink-2); border-color: var(--line-strong); }
.row-no {
  grid-column: 1;
  grid-row: 1 / span 2;
  padding-top: 3px;
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--ink-3);
  transition: color .18s var(--ease);
}
/* 说明文字和名称分列两栏，标签落在名称正下方，三者在左栏对齐成一竖 */
.row-body { display: contents; }
.row-body b {
  grid-column: 2;
  grid-row: 1;
  font-size: 16px;
  font-weight: 500;
  letter-spacing: -.01em;
  line-height: 1.5;
  color: var(--ink);
  transition: color .18s var(--ease);
}
.row-body small {
  grid-column: 3;
  grid-row: 1 / span 2;
  font-size: 14px;
  line-height: 1.8;
  color: var(--ink-2);
}
.row-date {
  grid-column: 2;
  grid-row: 2;
  padding-top: 2px;
  font-size: 12px;
  white-space: nowrap;
  color: var(--ink-3);
}
/* 标签原来是行尾一枚悬空的灰字，标题换不换行都会让它上下漂。
   归到名称栏下方做成胶囊，右栏于是能整幅铺给说明 */
.row-tag {
  grid-column: 2;
  grid-row: 2;
  justify-self: start;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  font-size: 10.5px;
  line-height: 1.45;
  letter-spacing: .08em;
  white-space: nowrap;
  color: var(--ink-3);
  transition: color .18s var(--ease), border-color .18s var(--ease);
}
.row-arrow { grid-column: 4; grid-row: 1 / span 2; font-size: 15px; color: var(--acc); }

/* 动态行：日期和类别是「索引」，标题才是内容，所以把索引挪到左栏，
   标题+摘要在右栏整幅铺开——和规格行正好左右相反 */
.rows--meta-left .row { grid-template-columns: 62px minmax(0, 132px) minmax(0, 1fr); column-gap: 26px; }
.rows--meta-left .row-body b { grid-column: 3; }
.rows--meta-left .row-body small { grid-column: 3; grid-row: 2; }
.rows--meta-left .row-date { grid-column: 2; grid-row: 1; padding-top: 5px; }
.rows--meta-left .row-tag { grid-column: 2; grid-row: 2; justify-self: start; }

/* ── 型号对照：PRO / AIR 等宽双卡 ──
   两个型号是「比较」关系，竖着排两行要读者自己上下对照着记。改成左右双卡，
   四组属性同名同序；两卡在同一根 grid 行里等高，属性行各自又按 1fr 均分，
   于是左右两边的横线严格落在同一高度上，扫一眼就能对上 */
.sku-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.sku {
  display: grid;
  grid-template-rows: auto 1fr;
  padding: 26px 28px 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  transition: border-color .22s var(--ease-expo), transform .22s var(--ease-expo),
              box-shadow .22s var(--ease-expo);
}
.sku:hover { border-color: var(--line-strong); box-shadow: var(--sh-hover); transform: translateY(-2px); }
.sku-head {
  display: grid;
  gap: 7px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line);
}
.sku-kicker { font-size: 11px; letter-spacing: .14em; color: var(--acc); }
.sku-name { margin: 0; font-size: 20px; font-weight: 500; letter-spacing: -.02em; color: var(--ink); }
.sku-tag {
  justify-self: start;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  font-size: 10.5px;
  line-height: 1.45;
  letter-spacing: .08em;
  white-space: nowrap;
  color: var(--ink-3);
}
.sku-dl { display: grid; grid-auto-rows: 1fr; margin: 0; }
.sku-attr {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr);
  gap: 16px;
  align-content: center;
  padding: 13px 0;
  border-bottom: 1px solid var(--line);
}
.sku-attr:last-child { border-bottom: 0; padding-bottom: 4px; }
.sku-attr dt { padding-top: 3px; font-family: var(--mono); font-size: 11px; letter-spacing: .06em; color: var(--ink-3); }
.sku-attr dd { margin: 0; font-size: 14px; line-height: 1.75; color: var(--ink-2); }

/* ── 首页动态：头条 / 编号行 / 紧凑档 ──
   三档靠字号和留白拉开，不靠边框花样：头条用 22px 标题 + 完整摘要，
   中间三条沿用带脊柱的行表，尾巴压成一行一条——日期和版本号钉两端，
   只有标题会被截断 */
.feed > * + * { margin-top: 16px; }
.feed-hero {
  display: block;
  padding: 26px 28px 28px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  transition: border-color .22s var(--ease-expo), transform .22s var(--ease-expo),
              box-shadow .22s var(--ease-expo);
}
a.feed-hero:hover {
  border-color: var(--line-strong);
  box-shadow: var(--sh-hover);
  transform: translateY(-2px);
}
.feed-meta {
  display: block;
  font-size: 11px;
  letter-spacing: .1em;
  white-space: nowrap;
  color: var(--ink-3);
}
/* 头条就是行表里的 01，编号从它开始往下排，不再凭空档一档 */
.feed-no { color: var(--acc-ink); font-weight: 700; }
.feed-title {
  display: block;
  margin: 13px 0 11px;
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -.02em;
  line-height: 1.45;
  color: var(--ink);
  transition: color .18s var(--ease);
}
a.feed-hero:hover .feed-title { color: var(--acc); }
.feed-sum { font-size: 14.5px; line-height: 1.9; color: var(--ink-2); }
.feed-tight {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  overflow: hidden;
}
.feed-tight-item {
  position: relative;
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  padding: 12px 26px;
  font-size: 13.5px;
  color: var(--ink-2);
  background-image: linear-gradient(var(--line), var(--line));
  background-size: calc(100% - 52px) 1px;
  background-position: 26px 100%;
  background-repeat: no-repeat;
  transition: background-color .16s var(--ease), color .16s var(--ease);
}
.feed-tight-item:last-child { background-image: none; }
a.feed-tight-item:hover { background-color: var(--band); color: var(--ink); }
.ft-date { font-size: 11.5px; letter-spacing: .04em; color: var(--ink-3); }
.ft-title {
  /* 窄屏放开单行截断：「Capability Folding 单平面架构：IPC 从 12 精简至 4」
     在 390 下按 nowrap+ellipsis 会被吃掉 36% 的宽度，丢的正是尾部那个量化
     卖点。两行封顶，超出才省略——行高必须显式写，line-clamp 靠它算高度 */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: normal;
  line-height: 1.45;
  transition: translate .22s var(--ease);
}
a.feed-tight-item:hover .ft-title { translate: 3px 0; }
.ft-tag {
  font-size: 10.5px;
  letter-spacing: .08em;
  text-align: right;
  color: var(--ink-3);
}

/* ── 加入我们 ── */
.join { display: grid; gap: 24px; }
.join-lead {
  max-width: 620px;
  font-size: clamp(19px, 2.2vw, 26px);
  font-weight: 300;
  line-height: 1.75;
  letter-spacing: -.015em;
  color: var(--ink);
}
.join-lead b { font-weight: 500; }
.about-h { font-size: 17px; font-weight: 500; margin-bottom: 14px; }
.join-qr {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 20px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--sh);
}
.join-qr img { width: 128px; height: 128px; border-radius: var(--r-xs); }
.join-qr-text b { display: block; font-size: 15px; font-weight: 500; margin-bottom: 6px; }
.join-qr-text small { font-size: 13.5px; line-height: 1.8; color: var(--ink-2); }
/* 宽屏下职位与二维码并排：QR 卡按内容取高、与职位列表垂直居中对齐，
   不再被拉伸到等高而在卡内产生空重 */
@media (min-width: 1001px) {
  .join-cols { display: grid; grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr); gap: 24px; align-items: center; }
}

/* ── 关于页 ── */
/* 简介与 .sec-note 同一套节奏：左对齐、由 accent 竖线起头，
   不再用居中窄栏——每行都短换行会把正文读成诗 */
.about-intro {
  max-width: 820px;
  padding-left: clamp(18px, 2.2vw, 30px);
  border-left: 2px solid var(--acc);
  font-size: clamp(15.5px, 1.6vw, 18px);
  font-weight: 300;
  line-height: 2.05;
  color: var(--ink-2);
  letter-spacing: .004em;
}
.about-intro p + p { margin-top: 22px; }
.about-intro b { font-weight: 500; color: var(--ink); }

.about-vision-grid,
.about-team-grid,
.about-certs-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
.about-vision-card,
.about-team-card,
.about-cert {
  padding: 26px 26px 28px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  transition: border-color .22s var(--ease-expo), transform .22s var(--ease-expo),
              box-shadow .22s var(--ease-expo);
}
.about-vision-card:hover,
.about-team-card:hover,
.about-cert:hover {
  border-color: var(--line-strong);
  box-shadow: var(--sh-hover);
  transform: translateY(-2px);
}
.about-vision-idx { font-size: 11px; letter-spacing: .12em; color: var(--ink-3); }
.about-vision-card h3 { font-size: 19px; font-weight: 500; margin: 12px 0 10px; }
.about-vision-card p { font-size: 14px; line-height: 1.85; color: var(--ink-2); }

/* ── 发展历程：一条竖轨串起五个里程碑 ──
   原来是「日期 / 胶囊 / 正文」三栏悬空的账目，五个点之间没有任何表示时间
   的连线，胶囊里那几个字（公司成立、架构确立…）其实是每一条的标题，却被
   压成一枚灰边胶囊。现在把阶段名提成左栏的粗体标题，日期压在它上面，正文
   统一从同一条左缘起读；轨段和节点沿用行表那套语言，末节点填成主色，
   表示「走到了今天」 */
.about-tl { position: relative; margin: 0; padding-left: 0; list-style: none; }
.about-tl-item {
  position: relative;
  display: grid;
  grid-template-columns: 134px minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: 28px;
  align-items: center;
  padding: 16px 0 16px 30px;
  /* 分隔带和行表一样从轨的右侧起画，不通栏 */
  background-image: linear-gradient(var(--line), var(--line));
  background-size: calc(100% - 30px) 1px;
  background-position: 30px 100%;
  background-repeat: no-repeat;
}
.about-tl-item:last-child { background-image: none; }
/* 轨段：每段各画自己那一格，末段只画到节点中心 */
.about-tl-item::before {
  content: '';
  position: absolute;
  left: 4.5px;
  top: 0;
  bottom: 0;
  width: 1.5px;
  background: var(--line-strong);
}
.about-tl-item:last-child::before { bottom: auto; height: 50%; }
/* 节点：圆点压在轨的中线上，末节实心 accent */
.about-tl-item::after {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  translate: 0 -50%;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--line-strong);
}
.about-tl-item:last-child::after { background: var(--acc); border-color: var(--acc); }
/* 日期单独占一行，压在轨上方；主行只剩标签＋描述，基线才咬得住 */
.about-tl-date {
  grid-column: 1;
  grid-row: 1;
  align-self: end;
  padding-bottom: 3px;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.about-tl-stage {
  grid-column: 1;
  grid-row: 2;
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -.015em;
  color: var(--ink);
}
.about-tl-body { grid-column: 2; grid-row: 2; }
.about-tl-body b { display: block; font-size: 16px; font-weight: 500; margin-bottom: 6px; }
.about-tl-body p { font-size: 15px; line-height: 1.8; color: var(--ink-2); }

/* 团队卡：头像与姓名/职位在同一基线上成组，中间一条分隔线，
   描述压到卡片底部——原来竖排在左上角，下半截整块空掉 */
.about-team-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto auto;
  column-gap: 14px;
  row-gap: 16px;
  align-items: center;
}
.about-team-avatar {
  grid-column: 1;
  grid-row: 1 / span 2;
  align-self: center;
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  box-shadow: var(--sh);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 18px;
  font-weight: 500;
}
.about-team-card h3 {
  grid-column: 2;
  grid-row: 1;
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -.015em;
}
.about-team-role {
  grid-column: 2;
  grid-row: 2;
  margin-top: 2px;
  font-size: 11.5px;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.about-team-card p {
  grid-column: 1 / -1;
  grid-row: 3;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 14.5px;
  line-height: 1.9;
  color: var(--ink-2);
}

.about-phi-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.about-phi-card {
  padding: 26px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
}
.about-phi-code { font-size: 11px; letter-spacing: .1em; color: var(--acc); }
.about-phi-card h3 { font-size: 18px; font-weight: 500; margin: 12px 0 10px; }
.about-phi-card p { font-size: 14px; line-height: 1.85; color: var(--ink-2); }

.about-cert-img-link { display: block; border-radius: var(--r-sm); overflow: hidden; border: 1px solid var(--line); }
.about-cert-img { width: 100%; display: block; }
.about-cert h3 { font-size: 16px; font-weight: 500; margin: 16px 0 12px; }
.about-cert-dl { display: grid; gap: 7px; font-size: 12.5px; }
.about-cert-dl div { display: grid; grid-template-columns: 74px 1fr; gap: 10px; }
.about-cert-dl dt { color: var(--ink-3); }
.about-cert-dl dd { margin: 0; color: var(--ink-2); }

/* 证书只有一张，三等分网格于是留出三分之二的死白——整块读起来像没渲染完。
   改成横卡：左边把 A4 原件立成缩略图（点开新标签看高清图），右边铺开说明与
   编号。四段用 space-between 撑到和图等高，右栏于是没有下半截的空档。
   证书号/信用代码这类长 mono 串在 12.5px 下要约 150px，标签列给到 78px，
   值才能对齐成一条竖线 */
.about-certs-grid { grid-template-columns: minmax(0, 1fr); }
.about-cert {
  display: grid;
  grid-template-columns: 208px minmax(0, 1fr);
  gap: clamp(24px, 3vw, 42px);
  align-items: stretch;
  padding: 28px;
}
.about-cert-meta {
  display: grid;
  align-content: space-between;
  gap: 18px;
}
.about-cert-meta h3 { margin: 0; font-size: 18px; font-weight: 500; letter-spacing: -.015em; }
.about-cert-note { font-size: 14px; line-height: 1.85; color: var(--ink-2); }
.about-cert-dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 30px; font-size: 12.5px; }
.about-cert-dl div { display: grid; grid-template-columns: 78px minmax(0, 1fr); gap: 10px; }
.about-cert-dl dt { color: var(--ink-3); }
.about-cert-dl dd { margin: 0; color: var(--ink-2); overflow-wrap: anywhere; }
.about-cert-open { display: inline-flex; align-items: center; gap: 5px; font-size: 13.5px; color: var(--acc); }
.about-cert-open i { font-style: normal; display: inline-block; transition: translate .22s var(--ease); }
.about-cert-open:hover i { translate: 3px -3px; }

/* ── 子页骨架（产品 / 内核 / SDK 共用）──
   和首页同一套语言：冷白底、细描边、单一平色强调，深色终端窗做视觉重音。
   所有网格用 auto-fit + minmax，窄屏自己收列，不靠断点硬切。 */
.sub-hero { padding: clamp(40px, 4.6vw, 72px) 0 clamp(30px, 3.2vw, 46px); }
.sub-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 58px);
  align-items: center;
}
/* 子页 kicker 就是首页 .sec-idx 的那一套：同一字号、同一强调色、同一根前导线。
   之前它是 11px 灰微标，首页 kicker 升过一档之后，产品页和首页摆在一起
   差了一整个量级，看着像两代设计 */
.sub-kicker {
  display: inline-flex;
  align-items: center;
  gap: clamp(10px, 1vw, 14px);
  margin-bottom: clamp(16px, 1.5vw, 22px);
  font-family: var(--mono);
  font-size: clamp(13px, 1.05vw, 16px);
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--acc-ink);
  text-transform: uppercase;
}
.sub-kicker::before {
  content: '';
  width: clamp(26px, 2.8vw, 44px); height: 2px;
  border-radius: 2px;
  background: var(--acc);
  transform-origin: left center;
  /* 首屏之上，不用等揭示观察器——进场就抽出来 */
  animation: v30-rule .62s var(--ease-expo) .14s both;
}
.sub-title {
  font-size: clamp(34px, 4.6vw, 58px);
  font-weight: 300;
  letter-spacing: -.034em;
  line-height: 1.14;
  margin-bottom: 18px;
}
.sub-lede {
  max-width: 520px;
  font-size: clamp(15.5px, 1.6vw, 17.5px);
  font-weight: 300;
  line-height: 1.95;
  color: var(--ink-2);
  /* 和 .sec-note 同一道编者按杆：不加字号（首页 hero-lede 才 18px，
     子页引导段不能压过它），只借左边一条强调细杆把它从正文里拎出来 */
  padding-left: clamp(14px, 1.3vw, 20px);
  border-left: 2px solid rgba(59, 92, 255, .26);
}
.sub-lede b { font-weight: 500; color: var(--ink); }
.sub-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 26px; }
/* 下边距必须显式归零：全局 p 有 1em 底边距，别的页 .sub-fine 是左栏末子
   元素所以被折叠掉，product 页它后面还跟着 .sub-actions，那 12px 就露出来 */
.sub-fine { margin: 14px 0 0; font-size: 12px; letter-spacing: .03em; color: var(--ink-3); }

/* 规格条 */
.spec-bar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 12px;
  margin: 0;
}
.spec-bar .spec { padding: 14px 16px; }
.spec dt {
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-3);
}
.spec dd { margin: 6px 0 0; font-size: 14px; font-weight: 500; color: var(--ink); }

/* 数字带 */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; }

/* 终端窗：页面里唯一的深色块 */
.shot {
  background: #14161b;
  border-radius: var(--r-md);
  box-shadow: var(--sh-dark);
  overflow: hidden;
}
.shot-bar {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 15px;
  border-bottom: 1px solid rgba(255, 255, 255, .07);
  font-family: var(--mono);
  font-size: 11.5px; letter-spacing: .04em;
  color: rgba(255, 255, 255, .4);
}
.shot-dots { display: inline-flex; gap: 6px; margin-right: 4px; }
.shot-dots i { width: 9px; height: 9px; border-radius: 50%; background: rgba(255, 255, 255, .17); }
.shot-ext { margin-left: auto; color: rgba(255, 255, 255, .26); }
.shot pre {
  margin: 0;
  padding: 18px 20px 20px;
  font-family: var(--mono);
  font-size: 12.6px;
  line-height: 1.95;
  color: #c8ccd4;
  white-space: pre;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.shot .t-p { color: #7b8cff; }
.shot .t-ok { color: #4ade80; }
.shot .t-hl { color: #fff; }
.shot .t-dim { color: rgba(255, 255, 255, .42); }
.shot .t-warn { color: #fbbf24; }

/* 交替特性行：左文案右终端，flip 换边 */
.feat {
  display: grid;
  grid-template-columns: minmax(0, .86fr) minmax(0, 1.14fr);
  gap: clamp(24px, 3.4vw, 54px);
  align-items: start;
  padding: clamp(30px, 3.4vw, 48px) 0;
  border-top: 1px solid var(--line);
}
.feat.first { border-top: none; padding-top: 0; }
.feat.flip > .feat-copy { order: 2; }
.feat-top {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--mono);
  font-size: 11px; letter-spacing: .14em;
  color: var(--ink-3);
}
.feat-rule { flex: 1; height: 1px; background: var(--line); }
.feat-copy h3 {
  font-size: clamp(21px, 2.4vw, 29px);
  font-weight: 500; letter-spacing: -.026em; line-height: 1.32;
  margin: 14px 0 12px;
}
.feat-desc { font-size: 15.5px; line-height: 1.95; color: var(--ink-2); }
.feat-desc code {
  padding: 1px 6px;
  border-radius: var(--r-xs);
  background: var(--band);
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink);
}
.feat-list { margin: 16px 0 0; padding: 0; list-style: none; display: grid; gap: 9px; }
.feat-list li {
  position: relative;
  padding-left: 18px;
  font-size: 14px; line-height: 1.75; color: var(--ink-2);
}
.feat-list li::before {
  content: '';
  position: absolute; left: 0; top: .78em;
  width: 7px; height: 1px; background: var(--line-strong);
}
.feat-tags { display: flex; flex-wrap: wrap; gap: 8px; margin: 20px 0 0; padding: 0; list-style: none; }
/* 容器本来就有 gap:8px，是给胶囊行写的；样式挂在 li 上，
   模板里就不用再逐个补 .tag，14 处标签行才有一个真源 */
.tag, .feat-tags li {
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  background: var(--surface);
  font-family: var(--mono);
  font-size: 10.5px; line-height: 1.5; letter-spacing: .1em;
  color: var(--ink-2);
}

/* 小卡格：列数交给内容自己算（auto-fit + 最小可读宽度），
   不再靠 repeat(3) 配一条 1000px 硬切——断点上沿那一段
   （1001–1180px）三列只剩 300px，正是卡片最挤的地方 */
.cell-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; }
.cell-grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.cell-grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.cell {
  /* 竖排成 flex，meta 才能被推到卡片底边——同一行的胶囊
     落在同一根线上，一行卡片才有收尾 */
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 24px 24px 26px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  transition: border-color .22s var(--ease-expo), transform .22s var(--ease-expo),
              box-shadow .22s var(--ease-expo);
}
.cell:hover { border-color: var(--line-strong); box-shadow: var(--sh-hover); transform: translateY(-2px); }
.cell-idx {
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-3);
}
.cell h3 { font-size: 17px; font-weight: 500; margin: 12px 0 9px; }
.cell p { font-size: 14px; line-height: 1.85; color: var(--ink-2); }
/* 末段的下边距一律归零，底部空隙交给卡片自己的 padding。
   否则「有 meta 胶囊」的卡末段 mb=0、「没胶囊」的 mb=14，同一网格里
   两种卡的底边对不齐 */
.cell p:last-child { margin-bottom: 0; }
.cell-grid ~ .cell { margin-top: 16px; }
/* meta 从通栏分隔线下的灰字改成胶囊，与行表标签同一档圆角 */
/* meta 从通栏分隔线下的灰字改成胶囊，与行表标签同一档圆角。
   外间距由 <p> 承担（margin-top:auto 把每行卡片的胶囊推到同一条底线上），
   描边与内边距交给内层 span，胶囊才不会被撑成通栏长条 */
.cell-meta { margin: 0; padding-top: 16px; margin-top: auto; }
.cell-meta span {
  display: inline-block;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .08em;
  white-space: nowrap;
  color: var(--ink-3);
}
/* 核心技术六格此前完全同形。每格右下角压一枚大号幽灵序号：
   默认只有描边（z-index:-1 落在文字之下、白底之上），hover 填实成
   主色冷蓝——序号成为这组卡片的视觉锚，也补上 hover 的反馈层次 */
#capabilities .cell { overflow: hidden; counter-increment: cell; }
#capabilities .cell-grid { counter-reset: cell; }
#capabilities .cell::after {
  content: "0" counter(cell);
  position: absolute;
  z-index: -1;
  right: 6px;
  bottom: -34px;
  font-family: var(--mono);
  font-size: clamp(96px, 9vw, 132px);
  font-weight: 700;
  letter-spacing: -.05em;
  line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1px rgba(59, 92, 255, .18);
  pointer-events: none;
  transition: color .22s var(--ease), -webkit-text-stroke-color .22s var(--ease);
}
#capabilities .cell:hover::after {
  color: var(--acc-soft);
  -webkit-text-stroke-color: var(--acc);
}

/* ── 仓库域：伞仓横卡 + 五个域卡 ──
   原来六个 .row 是一张等高的表，伞仓（协调一切的那一层）和叶子域读起来
   一个分量。伞仓抽出来做跨整幅的横卡，左文案右进仓指引；五个域沿用首页
   的 .cell 语言。5 格在 3 列里必然空半格，末卡跨两列补上，不留缺口 */
.repo-hub {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(24px, 3vw, 48px);
  align-items: center;
  margin-bottom: 16px;
  padding: 26px 28px 28px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  transition: border-color .22s var(--ease-expo), transform .22s var(--ease-expo),
              box-shadow .22s var(--ease-expo);
}
.repo-hub:hover { border-color: var(--line-strong); box-shadow: var(--sh-hover); transform: translateY(-2px); }
.repo-hub-title { display: block; margin: 12px 0 10px; font-size: 20px; font-weight: 500; letter-spacing: -.02em; color: var(--ink); }
.repo-hub-main p { max-width: 74ch; font-size: 14px; line-height: 1.85; color: var(--ink-2); }
.repo-hub-aside { display: grid; justify-items: end; gap: 12px; text-align: right; }
.repo-hub-tag {
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  font-family: var(--mono);
  font-size: 11px; letter-spacing: .07em; white-space: nowrap;
  color: var(--ink-3);
}
.repo-hub-go { font-size: 13.5px; color: var(--acc); }
.repo-hub-go i { font-style: normal; display: inline-block; transition: translate .22s var(--ease); }
a.repo-hub:hover .repo-hub-title { color: var(--acc); }
a.repo-hub:hover .repo-hub-go i { translate: 3px -3px; }
.repo-grid .cell:last-child { grid-column: span 2; }

/* 招聘详情页：外壳复用 .cell（悬停/冷光/揭示都已挂上），内部只补两栏职责
   与要求、投递胶囊和编号锚点滚动避让 */
.jobs-list { display: flex; flex-direction: column; gap: 16px; }
.jobs-card { display: flex; flex-direction: column; gap: 20px; }
.jobs-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
/* 这里原来写 margin-top:6px 把全站 .cell h3 的 12px 压掉一半，jobs 卡
   的标题比其他 9 页矮一截。删掉覆盖，让它跟其他卡片同尺 */
.jobs-apply {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  padding: 9px 16px; border: 1px solid var(--acc); border-radius: var(--pill);
  font-size: 13.5px; color: var(--acc); text-decoration: none; white-space: nowrap;
  transition: background .22s var(--ease-expo), color .22s var(--ease-expo);
}
.jobs-apply i { font-style: normal; display: inline-block; transition: translate .22s var(--ease); }
.jobs-apply:hover { background: var(--acc); color: var(--acc-ink); }
.jobs-apply:hover i { translate: 3px 0; }
.jobs-body { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 32px; }
.jobs-col-h { font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 10px; }
.jobs-bullets { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.jobs-bullets li { position: relative; padding-left: 16px; font-size: 14px; line-height: 1.75; color: var(--ink-2); }
.jobs-bullets li::before { content: ""; position: absolute; left: 0; top: .72em; width: 6px; height: 1px; background: var(--line-strong); }
.jobs-stack { margin-top: 0; }
/* 岗位锚点（#sys-01 等）从首页职位行跳进来，落在 sticky nav 下面会遮标题 */
.jobs-card[id] { scroll-margin-top: calc(var(--nav-h) + 20px); }
@media (max-width: 760px) {
  .jobs-body { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .jobs-head { flex-direction: column; }
  .jobs-apply { align-self: stretch; justify-content: center; }
}

/* 语言绑定卡：卡片外壳 + 内嵌终端窗，代码就是主角。2×2 比 3+1 稳 */
.lang-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.lang-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  overflow: hidden;
  transition: border-color .22s var(--ease-expo), transform .22s var(--ease-expo),
              box-shadow .22s var(--ease-expo);
}
.lang-card:hover { border-color: var(--line-strong); box-shadow: var(--sh-hover); transform: translateY(-2px); }
.lang-head { display: flex; align-items: baseline; gap: 10px; padding: 18px 20px 12px; }
.lang-head h3 { font-size: 18px; font-weight: 500; }
.lang-badge {
  margin-left: auto;
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .08em; color: var(--ink-3);
}
.lang-card .shot { border-radius: 0; box-shadow: none; }
.lang-card .shot pre { padding: 15px 18px 17px; font-size: 11.8px; line-height: 1.85; }
.lang-note { padding: 15px 20px 0; font-size: 13.5px; line-height: 1.8; color: var(--ink-2); }
.lang-link { display: inline-flex; align-items: center; gap: 7px; padding: 14px 20px 18px; font-size: 13.5px; font-weight: 500; color: var(--acc); }
.lang-link::after { content: '→'; font-family: var(--mono); transition: transform .2s var(--ease); }
.lang-link:hover::after { transform: translateX(4px); }

/* 平台胶囊 + 命令条 */
.switch {
  display: inline-flex; gap: 4px;
  padding: 4px;
  background: var(--band);
  border: 1px solid var(--line);
  border-radius: var(--pill);
}
.sw-opt {
  height: 34px;
  padding: 0 15px;
  border: none;
  border-radius: var(--pill);
  background: transparent;
  font-size: 13px; color: var(--ink-2);
  cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease), transform .18s var(--ease);
}
/* 之前只有 .on 一档：非选中项 hover 什么都不变，按下去也不缩，
   切换器读起来像三个印上去的标签而不是三个按钮 */
.sw-opt:not(.on):hover { background: rgba(26, 29, 35, .06); color: var(--ink); }
.sw-opt:not(.on):active { transform: scale(.97); }
.sw-opt.on { background: var(--ink); color: #fff; box-shadow: 0 2px 6px -2px rgba(22, 32, 64, .35); }
.cmd {
  display: flex; align-items: center; gap: 10px;
  margin-top: 14px;
  padding: 9px 9px 9px 16px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
}
.cmd-os {
  flex: none;
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: .12em; color: var(--ink-3);
}
.cmd code {
  flex: 1; min-width: 0;
  font-family: var(--mono);
  font-size: 12.5px; line-height: 1.65; color: var(--ink);
  overflow-wrap: break-word;
}
.cmd.swap code { animation: v30-swap .34s var(--ease); }
@keyframes v30-swap { from { opacity: 0; transform: translateY(4px); } }
.cmd .copy-btn { border-color: var(--line-strong); background: var(--band); color: var(--ink-2); }
.cmd .copy-btn:hover { border-color: var(--ink); background: var(--ink); color: #fff; }

/* 收尾行动带 */
.band {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(22px, 3vw, 44px);
  align-items: center;
  padding: clamp(28px, 3.4vw, 44px);
  background: #14161b;
  border-radius: var(--r-lg);
  box-shadow: var(--sh-dark);
}
.band h2 { font-size: clamp(22px, 2.6vw, 32px); font-weight: 500; letter-spacing: -.026em; color: #fff; margin-bottom: 10px; }
.band p { font-size: 14.5px; line-height: 1.9; color: rgba(255, 255, 255, .6); }
.band p b { font-weight: 500; color: #fff; }
.band-cta { display: flex; flex-wrap: wrap; gap: 10px; }
.band .btn-ghost { background: transparent; border-color: rgba(255, 255, 255, .26); color: #fff; }
.band .btn-ghost:hover { border-color: #fff; }

/* ── 深底描边水印 ──
   等宽大字、只描边不填色、从右上角溢出后被板子裁掉半截，读作印在面板上的
   厂标而不是又一行标题。描边压在 13% 白：正文压在它上面时，字底对比几乎不变。 */
.band,
.install { position: relative; overflow: hidden; }
.band > div,
.install > div { position: relative; z-index: 1; }
.band-mark,
.install-mark {
  position: absolute;
  z-index: 0;
  font: 700 clamp(56px, 8.2vw, 116px)/1 var(--mono);
  letter-spacing: -.045em;
  white-space: nowrap;
  color: rgba(255, 255, 255, .022);
  -webkit-text-stroke: 1px rgba(255, 255, 255, .13);
  pointer-events: none;
  user-select: none;
}
/* 收尾带：右上角溢出，被板子裁掉半截 */
.band-mark {
  top: clamp(-30px, -2.1vw, -10px);
  right: clamp(-10px, -.5vw, -2px);
}
/* 安装面板：整块板子的右半边是两条命令卡。水印放右上角会横穿卡片边界——
   压成正文时 13% 描边几乎看不见，压到卡片自身的底色和描边上就变成一道杂影。
   所以宽屏下先在板子底下垫一条空槽，再把水印从左下角溢出去：它只落在空槽里，
   既不动正文也不碰卡片 */
@media (min-width: 1001px) {
  .install { padding-bottom: clamp(72px, 7vw, 104px); }
  .install-mark {
    top: auto;
    right: auto;
    /* 底边收进板内 2px：让 y 的下伸部完整收尾，不再被板缘切断。
       横向仍从左角溢出，出画方向只剩一边，读起来才是故意的 */
    bottom: 2px;
    left: -22px;
    font-size: clamp(60px, 7vw, 96px);
  }
}

/* ── ROCK Ⅲ：E-Ink 设备与性格卡 ──
   电子墨水是反射式显示，不发光，所以这里刻意用哑光灰底 + 纯黑眼睛，
   不做任何屏幕发光/渐变效果。 */
.rk-device {
  display: grid;
  justify-items: center;
  padding: clamp(30px, 3.4vw, 50px) 20px clamp(24px, 2.8vw, 36px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
}
/* 机身 = 一层 10px 实色边框。不额外套元素也能读出厚度：外圆角 16、
   内容圆角自然收到 6，比原来「一张灰纸贴在白卡上」像硬件得多 */
.rk-screen {
  position: relative;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: clamp(16px, 1.8vw, 22px);
  width: 100%;
  max-width: clamp(268px, 23vw, 324px);
  aspect-ratio: 4 / 3;
  background: #eceef2;
  border: 10px solid #23262c;
  border-radius: 16px;
  box-shadow: 0 22px 36px -26px rgba(22, 32, 64, .6);
}
/* 上边框里嵌一颗传感器。硬件感就是靠这种不多余的小东西立起来的 */
.rk-screen::before {
  content: '';
  position: absolute;
  top: -6px; left: 50%;
  width: 4px; height: 4px;
  margin-left: -2px;
  border-radius: 50%;
  background: #4a5160;
}
/* e-ink 是雾面，所以玻璃高光只给内沿一道很浅的亮线，不做渐变 */
.rk-screen::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 6px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7);
  pointer-events: none;
}
.rk-eyes { display: flex; align-items: center; gap: 30px; }
/* 屏幕放大之后眼睛要跟着涨，不然 15×32 在一块 344px 的面板上像两个像素点 */
.rk-eyes i {
  width: 17px;
  height: 38px;
  background: var(--ink);
  border-radius: 9px;
  animation: rk-blink 5.4s var(--ease) infinite;
}
.rk-eyes.rk-sleepy i { height: 11px; border-radius: 6px; }
.rk-eyes.rk-wide i { width: 21px; height: 48px; }
.rk-eyes.rk-half i { height: 20px; border-radius: 8px; }
/* 性格卡里眼睛高低不同，固定行高才不会把标题挤错位 */
.rk-eyes-sm { gap: 15px; height: 26px; align-items: center; }
.rk-eyes-sm i { width: 10px; height: 22px; border-radius: 5px; }
.rk-eyes-sm.rk-wide i { height: 26px; width: 13px; }
.rk-mood { font-size: 11px; letter-spacing: .12em; color: #5b6270; }
/* 颈和底座跟机身同色，三段才是一件东西而不是三个图形 */
.rk-neck { width: 24px; height: 16px; background: #23262c; }
.rk-base {
  width: 156px; height: 13px;
  background: #23262c;
  border-radius: 999px 999px 6px 6px;
  box-shadow: 0 16px 22px -16px rgba(22, 32, 64, .55);
}
.rk-caption { margin-top: 22px; font-size: 10.5px; letter-spacing: .12em; color: var(--ink-3); }
.rk-persona { display: grid; justify-items: start; }
.rk-persona .rk-eyes { margin-top: 4px; }
@keyframes rk-blink {
  0%, 91%, 100% { transform: scaleY(1); }
  94%, 96% { transform: scaleY(.1); }
}

/* ── 新闻：非对称编辑式网格 ── */
#news-list { padding-top: clamp(18px, 2.2vw, 36px); }

.news-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.42fr) minmax(0, 1fr);
  gap: 20px;
  align-items: start;
}
.news-feature,
.news-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  overflow: hidden;
  transition: border-color .22s var(--ease-expo), transform .22s var(--ease-expo),
              box-shadow .22s var(--ease-expo);
}
.news-feature:hover,
.news-card:hover {
  border-color: var(--line-strong);
  box-shadow: var(--sh-hover);
  transform: translateY(-2px);
}
.news-feature-visual,
.news-card-visual {
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--band) url('/img/aurora-mesh.svg') center / cover no-repeat;
}
.news-card-visual { aspect-ratio: 4 / 3; }
/* 整页唯一一处成幅的图，hover 时让它缓慢推近 6%——1.2s 的 expo 出去，
   读起来像镜头在呼吸，不是图标在弹。卡片已经 overflow:hidden，
   推出去的部分被圆角收住，不会漏成直角 */
.news-feature-visual,
.news-card-visual {
  transition: transform 1.2s var(--ease-expo);
}
.news-feature:hover .news-feature-visual,
.news-card:hover .news-card-visual { transform: scale(1.06); }
.news-feature-body { padding: 26px 28px 30px; }
.news-card-body { padding: 18px 20px 22px; }
.news-meta { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.news-date { font-size: 11.5px; letter-spacing: .04em; color: var(--ink-3); }
.news-kind {
  padding: 3px 10px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  font-size: 10.5px; letter-spacing: .06em; color: var(--ink-3);
}
.news-feature h2,
.news-card h2 { font-size: 21px; font-weight: 500; letter-spacing: -.018em; line-height: 1.5; margin-bottom: 10px; }
.news-card h2 { font-size: 16px; margin-bottom: 7px; }
.news-feature p,
.news-card p { font-size: 14.5px; line-height: 1.85; color: var(--ink-2); }
.news-card p { font-size: 13.5px; }

.news-side { display: grid; gap: 16px; }

.news-more { margin-top: 26px; text-align: center; }

/* 详情 */
.news-detail { padding: clamp(50px, 6vw, 92px) 0 clamp(42px, 5vw, 76px); }
.news-detail .wrap { max-width: 760px; }
.news-back { display: inline-flex; align-items: center; gap: 7px; font-size: 14px; color: var(--ink-2); transition: color .18s var(--ease), transform .18s var(--ease); }
.news-back:hover { color: var(--acc); transform: translateX(-2px); }
.news-detail-head { padding: 26px 0 30px; border-bottom: 1px solid var(--line); margin-bottom: 34px; }
.news-detail-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.news-detail-meta .mono { font-size: 12px; color: var(--ink-3); }
.news-detail-title {
  font-size: clamp(26px, 3.6vw, 40px);
  font-weight: 500;
  letter-spacing: -.026em;
  line-height: 1.35;
}
.news-detail-body { font-size: 16px; line-height: 2; color: var(--ink-2); }
/* 正文小标题此前是 21px 光板，和站内 20+ 处 `01 / FEATURES` 的 mono 眉标
   语言不通。用 counter 补上序号与前导细线，不动正文 HTML */
.news-detail-body { counter-reset: ndsec; }
.news-detail-body h2 {
  display: flex; align-items: center; gap: 12px;
  margin: 40px 0 14px; color: var(--ink);
  font-size: 21px; font-weight: 500; letter-spacing: -.018em;
}
.news-detail-body h2::before {
  counter-increment: ndsec;
  content: "0" counter(ndsec);
  font-family: var(--mono); font-size: 11px; font-weight: 400;
  letter-spacing: .12em; color: var(--acc);
}
.news-detail-body h2::after {
  content: ''; flex: 1; height: 1px; background: var(--line);
}
/* 首段是摘要不是正文，给它 about-intro 同一套 accent 竖线 */
.news-detail-body p:first-of-type {
  margin-bottom: 26px; padding-left: 18px;
  border-left: 2px solid var(--acc);
  color: var(--ink); font-weight: 400;
}
.news-detail-body p { margin-bottom: 18px; }
.news-detail-body ul { margin: 0 0 18px; padding-left: 22px; }
.news-detail-body li { margin-bottom: 8px; }
.news-detail-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin-top: 44px; padding-top: 26px; border-top: 1px solid var(--line);
}
.nd-nav { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; }
.nd-prev, .nd-next { display: block; min-width: 0; }
.nd-next { text-align: right; }
.nd-nav-k {
  display: block; margin-bottom: 5px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; color: var(--ink-3);
}
.nd-nav-t {
  display: block; font-size: 14.5px; line-height: 1.6; color: var(--ink-2);
  text-wrap: pretty;
  transition: color .18s var(--ease);
}
.nd-prev:hover .nd-nav-t, .nd-next:hover .nd-nav-t { color: var(--acc); }

/* ── 404 ── */
.notfound { padding: clamp(90px, 12vw, 190px) 0 clamp(80px, 10vw, 160px); text-align: center; }
.notfound .wrap { max-width: 560px; }
.nf-code { font-size: clamp(56px, 7vw, 88px); line-height: 1; letter-spacing: -.02em; color: var(--ink-3); }
.nf-title { margin: 18px 0 12px; font-size: clamp(24px, 3vw, 34px); font-weight: 500; letter-spacing: -.026em; }
.nf-lede { font-size: 15px; line-height: 1.95; color: var(--ink-2); }
.nf-actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-top: 30px; }

/* ── 页脚 ── */
/* 品牌列 + 四栏链接架构 + 底部 mono 状态栏：让页脚说 v30 的语言，
   而不是八条小链接平铺在一大片横向死白里 */
.foot {
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding: clamp(48px, 5vw, 76px) 0 30px;
}
.foot-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 40px 32px;
  flex-wrap: wrap;
}
.foot-brand { flex: 1 1 240px; max-width: 300px; }
.foot-brand .brand { font-size: 22px; }
.foot-slogan {
  margin: 14px 0 0;
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.foot-about {
  margin: 18px 0 0;
  font-size: 13.5px;
  line-height: 1.85;
  color: var(--ink-2);
}
.foot-cols {
  flex: 2 1 auto;
  display: grid;
  grid-template-columns: repeat(4, minmax(96px, 1fr));
  gap: 26px 28px;
}
.foot-col { display: flex; flex-direction: column; gap: 11px; }
.foot-col-h {
  margin: 0 0 3px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .16em;
  color: var(--ink-3);
}
.foot-col a {
  align-self: flex-start;
  font-size: 14px;
  color: var(--ink-2);
  transition: color .18s var(--ease);
}
.foot-col a:hover { color: var(--ink); }
.foot-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px 24px;
  flex-wrap: wrap;
  margin-top: clamp(34px, 3.6vw, 52px);
  padding-top: 22px;
  border-top: 1px solid var(--line);
  font-size: 11.5px;
  letter-spacing: .02em;
  line-height: 1.9;
  color: var(--ink-3);
}
.foot-bar address { font-style: normal; }
.foot-bar a { color: var(--ink-3); transition: color .18s var(--ease); }
.foot-bar a:hover { color: var(--ink); }

@media (max-width: 900px) {
  .foot-brand { flex-basis: 100%; max-width: none; }
  .foot-cols { flex-basis: 100%; grid-template-columns: repeat(4, 1fr); gap: 22px; }
}
@media (max-width: 640px) {
  .foot-cols { grid-template-columns: 1fr 1fr; gap: 26px 20px; }
  .foot-bar { flex-direction: column; align-items: flex-start; }
}

/* ── 滚动进度条 ──
   这是全站唯一一条贯穿视口的动态元素，所以它是一束光而不是一条线。
   .progress 本身只做定位、不缩放：条体缩放在 b 上，走珠在 i 上按 vw 平移。
   如果直接缩放父级，走珠的 translateX 会被再乘一次 scaleX，行程就短了一半 */
.progress {
  --p: 0;
  position: fixed;
  top: 0; left: 0;
  z-index: 80;
  height: 2px;
  width: 100%;
  pointer-events: none;
}
.progress b {
  display: block;
  height: 100%;
  width: 100%;
  transform-origin: 0 50%;
  transform: scaleX(var(--p));
  background: var(--acc);
  box-shadow: 0 0 14px 1px rgba(59, 92, 255, .35);
}
.progress i {
  position: absolute;
  left: 0; top: 50%;
  width: 6px; height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: #7b8cff;
  box-shadow: 0 0 12px 3px rgba(59, 92, 255, .55);
  transform: translateX(calc(var(--p) * 100vw));
}

/* ── 回到顶部 ──
   静息态用 opacity/visibility 收着，不用 display:none：那是硬切，
   按钮是「砸」进画面的。升起只走 8px，跟揭示同一条 expo-out */
.to-top {
  position: fixed;
  right: clamp(16px, 2vw, 28px);
  bottom: clamp(18px, 2.4vw, 30px);
  z-index: 50;
  display: grid;
  place-items: center;
  width: 42px; height: 42px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--sh);
  color: var(--ink-2);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  translate: 0 8px;
  pointer-events: none;
  transition: opacity .26s var(--ease-expo), translate .3s var(--ease-expo),
              visibility .26s, background .18s var(--ease),
              border-color .18s var(--ease), color .18s var(--ease),
              box-shadow .18s var(--ease);
}
.to-top.show { opacity: 1; visibility: visible; translate: 0 0; pointer-events: auto; }
.to-top svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.to-top:hover {
  border-color: var(--line-strong);
  color: var(--ink);
  box-shadow: var(--sh-hover);
}
.to-top:active { translate: 0 2px; }

/* ── 滚动揭示（渐进增强：无 JS 时内容始终可见）
   曲线照搬 MiMo 的 expo-out，但拍子收到电影一点：60px 升起到 34px、.8s
   到 .62s。60px 那一档在一屏只放两张卡的窄屏上是「整块从下面涌进来」，
   34px 才是「内容本来就在那里，只是刚好轮到它亮」。
   --d 是同一排卡片各自的等待时间（components.js 按 DOM 次序写 0/60/120…），
   没有它一排卡片是整块闪现，有了它才是一串跟着亮过去。
   卡片级元素尺度小，用 --rise 再收一档，避免整块弹跳。
   走 @keyframes + 独立的 translate，而不是 transition：原来那条 transition
   简写把卡片自己的 border-color / box-shadow / transform 过渡整条盖掉了，
   揭示过的卡片悬停时边框阴影是硬切、抬起却是 .8s 慢动作 */
@keyframes v30-rise {
  from { opacity: 0; translate: 0 var(--rise, 34px); }
  to { opacity: 1; translate: 0 0; }
}
.js [data-reveal] { opacity: 0; translate: 0 var(--rise, 34px); }
.js [data-reveal].in { animation: v30-rise .62s var(--ease-expo) var(--d, 0ms) forwards; }
/* 首页动态那批行是 JS 现插的，赶不上揭示观察器，进场由 main.js 打上
   .rows-enter + 每行 animation-delay。backwards 让等待的那几十毫秒停在
   升起前的位置，而不是先亮出来再跳回去 */
.js .rows.rows-enter .row,
.js .feed.rows-enter .feed-hero,
.js .feed.rows-enter .row,
.js .feed.rows-enter .feed-tight-item { --rise: 14px; animation: v30-rise .5s var(--ease-expo) backwards; }
.js .product[data-reveal],
.js .cell[data-reveal],
.js .stat[data-reveal],
.js .rk-device[data-reveal],
.js .about-vision-card[data-reveal],
.js .about-team-card[data-reveal],
.js .about-phi-card[data-reveal],
.js .about-cert[data-reveal],
.js .about-tl-item[data-reveal],
.js .news-more[data-reveal] { --rise: 28px; }

/* ── 卡片细微反馈：按压回音 + 跟手冷光 ────────────────────────
   抬起是 .22s 的事，按下去必须在 90ms 内给回音，不然「点了一下」这件
   事没有回执。:active 里整条 transition-duration 收到 .09s，松手时走的
   还是上面那条慢的——快进慢出，和按键开关一个手感。translateY(0) 是把
   悬停那 2px 收回来，再配 .995 缩放，读起来是「按进去」而不是「抖一下」。

   冷光写在 ::before：卡片自己的白底先铺，光落在白底和文字之间（z-index
   -1 配 isolation:isolate，否则这一层会掉到卡片背景之后，什么都看不见），
   字面不会被糊上一层蓝。位置由 JS 在 pointermove 里按帧合流写进
   --mx/--my：一帧最多一次，只动两个自定义属性，不触发布局；离开卡片就没有
   任何 rAF 在空转。
   卡片本身已经是纯白，所以这束光不能是白的——白上加白什么都读不出来。
   这里用极淡的一档冷蓝（约 9% 的主色），等价于「屏幕上多了一点亮」 */
.product,
.cell,
.lang-card,
.news-card,
.news-feature,
.about-vision-card,
.about-team-card,
.about-cert,
.repo-hub,
.sku,
.feed-hero,
.stat {
  position: relative;
  isolation: isolate;
}
.product::before,
.cell::before,
.lang-card::before,
.news-card::before,
.news-feature::before,
.about-vision-card::before,
.about-team-card::before,
.about-cert::before,
.repo-hub::before,
.sku::before,
.feed-hero::before,
.stat::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  /* 半径跟着视口收：写死 230px 时，390 视口下卡片只有 335px 宽，
     一束光盖掉近七成卡面，读起来是整张卡发灰而不是一束光 */
  background: radial-gradient(clamp(150px, 46vw, 230px) clamp(150px, 46vw, 230px)
    at var(--mx, 50%) var(--my, 50%),
    rgba(59, 92, 255, .09), rgba(59, 92, 255, 0) 70%);
  transition: opacity .3s var(--ease-expo);
}
/* 卡片族统一换成 --sh-card：白卡落在冷白底上，顶边多一条 inset 高光，
   底边留影——一片薄瓷立在桌面上的读法。按钮、胶囊、小控件不在这里，
   它们尺寸太小，加高光会发虚 */
.product, .cell, .lang-card, .news-card, .news-feature,
.about-vision-card, .about-team-card, .about-cert, .about-phi-card,
.repo-hub, .stack, .stat, .rows, .sku, .feed-hero, .feed-tight,
.join-qr, .rk-device { box-shadow: var(--sh-card); }

.product:hover::before,
.cell:hover::before,
.lang-card:hover::before,
.news-card:hover::before,
.news-feature:hover::before,
.about-vision-card:hover::before,
.about-team-card:hover::before,
.about-cert:hover::before,
.repo-hub:hover::before,
.sku:hover::before,
.feed-hero:hover::before,
.stat:hover::before,
.glow::before { opacity: 1; }
.product:active,
.cell:active,
.lang-card:active,
.news-card:active,
.news-feature:active,
.about-vision-card:active,
.about-team-card:active,
.about-cert:active,
.repo-hub:active,
.sku:active,
.feed-hero:active {
  transform: translateY(0) scale(.995);
  transition-duration: .09s;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js [data-reveal] { opacity: 1 !important; translate: none !important; animation: none !important; }
  .js [data-reveal] .sec-idx::before { animation: none !important; }
  .sub-kicker::before { animation: none !important; }
  .js .rows.rows-enter .row,
  .js .feed.rows-enter .feed-hero,
  .js .feed.rows-enter .row,
  .js .feed.rows-enter .feed-tight-item { animation: none !important; }
  a.feed-hero:hover { transform: none; }
  /* 头条图的推近也算动，一并停掉 */
  .news-feature:hover .news-feature-visual,
  .news-card:hover .news-card-visual { transform: none; }
  a.feed-tight-item:hover .ft-title { translate: none; }
  .js.type-ready .hero-actions { opacity: 1; transform: none; }
  .term-body .t-cursor { animation: none; }
  .rk-eyes i { animation: none; }
  .hero-cursor { animation: none; }
  .product:hover, .cell:hover, .about-vision-card:hover, .about-team-card:hover,
  .about-cert:hover, .news-feature:hover, .news-card:hover,
  .repo-hub:hover, .sku:hover { transform: none; }
  a.repo-hub:hover .repo-hub-go i { translate: none; }
  /* 按压那一档走的是同一组卡片，:hover 那条按不到 :active */
  .product:active, .cell:active, .lang-card:active, .news-card:active,
  .news-feature:active, .about-vision-card:active, .about-team-card:active,
  .about-cert:active, .repo-hub:active, .sku:active, .feed-hero:active { transform: none; }
  a.row:hover > *, a.row:hover .row-body b, a.row:hover .row-body small { translate: none; }
  .layer:hover span { translate: none; }
  /* 轨道的轨段、序号、标题照样变色，只有节点那一下回弹收掉 */
  a.row:hover::after { transform: none; }
  .cmd.swap code { animation: none; }
  /* 位移类反馈撤干净，颜色/底色/投影照旧变——reduced-motion 反对的是「动」，
     不是反对「点下去有回音」。这批是抬升、回弹和箭头平移 */
  .btn:hover::after, .btn-link:hover::after, .lang-link:hover::after,
  .btn:active, .btn-solid:hover, .nav-cta:hover, .nav-cta:active,
  .sw-opt:active, .copy-btn:active, .nav-burger:active,
  .news-back:hover { transform: none; }
  .nav { transition: none; }
  /* to-top 的升起/按压走的是独立的 translate 属性，不是 transform，
     上面那条按不到它 */
  .to-top, .to-top.show, .to-top:active { translate: none; }}

/* ── 响应式 ── */
@media (max-width: 1000px) {
  .arch, .install, .news-grid { grid-template-columns: minmax(0, 1fr); }
  .sub-hero-grid, .feat, .band { grid-template-columns: minmax(0, 1fr); }
  /* 单列后换边没有意义，文案永远在上 */
  .feat.flip > .feat-copy { order: 0; }
  .band { gap: 24px; }
  /* 单列之后正文铺满整块板子，水印收到一角，别和标题抢位置。
     安装面板的卡片此时排在下面，左下角反而更挤，跟收尾带一样回到右上 */
  .band-mark, .install-mark { font-size: clamp(48px, 9vw, 76px); top: -14px; }
  .install-mark { right: -8px; }
  /* 窄屏内容宽度不足 280×2，auto-fit 会直接退成单列——这里显式给列数，
     默认三列档退两列、四列档退两列、两列档保持两列 */
  .cell-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cell-grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 名称栏在 1000px 以下占掉太多阅读宽度，收到 172 并把栏间距压一档 */
  .row { grid-template-columns: 56px minmax(0, 172px) minmax(0, 1fr) auto; column-gap: 20px; }
  .products.three { grid-template-columns: minmax(0, 1fr); }
  .products, .about-vision-grid, .about-team-grid, .about-phi-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 760px) {
  /* 只改左右。写成 padding 简写会顺手把上下内边距清零——窄屏里动效带和
     注脚贴死（实测 .hero-art 底边到 .hero-lede 顶边 vgap＝0）就是这么来的：
     .hero-foot 的 padding-top 和这条同级，而媒体查询在文件更后面 */
  .wrap { padding-inline: 20px; }
  /* 详情页上下篇在窄栏里挤成两行糊字，改成上下排、右栏取消右对齐 */
  .nd-nav { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .nd-next { text-align: left; }
  .news-detail-foot { flex-direction: column; align-items: flex-start; }
  .nav-burger {
    display: inline-grid;
    place-items: center;
    gap: 5px;
    /* 40px 摸不到 iOS 的 44pt 下限。汉堡是窄屏唯一的导航入口，
       点不中的代价比别的控件高，抬到 44 并把透明外扩留白也算进命中区 */
    width: 44px; height: 44px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    cursor: pointer;
    transition: background .18s var(--ease), border-color .18s var(--ease), transform .18s var(--ease);
  }
  .nav-burger:hover { background: var(--band); border-color: var(--line-strong); }
  .nav-burger:active { transform: scale(.94); }
  .nav-burger i {
    display: block;
    width: 16px; height: 1.5px;
    background: var(--ink);
    transition: translate .24s var(--ease-expo), rotate .24s var(--ease-expo);
  }
  /* 开合都走 opacity/translate，不再 display:none↔flex：那是一帧硬切，
     菜单是「砸」出来的不是「落」下来的。visibility 保证收起时读屏和
     Tab 都进不去，pointer-events 关掉免得挡住英雄区的刮痕 */
  .nav-right {
    position: absolute;
    top: var(--nav-h); left: 0; right: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: 12px 20px 20px;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--sh-pop);
    opacity: 0;
    visibility: hidden;
    translate: 0 -8px;
    pointer-events: none;
    transition: opacity .22s var(--ease-expo), translate .26s var(--ease-expo), visibility .22s;
  }
  .nav.open .nav-right { opacity: 1; visibility: visible; translate: 0 0; pointer-events: auto; }
  .nav.open .nav-burger i:first-child { translate: 0 3.25px; rotate: 45deg; }
  .nav.open .nav-burger i:last-child { translate: 0 -3.25px; rotate: -45deg; }
  /* 折叠菜单要竖着排，桌面那套 flex 行布局在窄屏会把子项挤到右边 */
  .nav-dropdown { display: block; }
  .nav-right > a, .nav-dropdown > a { height: auto; padding: 12px 14px; font-size: 15.5px; }
  .nav-dropdown-menu {
    position: static;
    transform: none;
    opacity: 1;
    visibility: visible;
    box-shadow: none;
    border: none;
    background: var(--band);
    margin: 2px 0 6px;
  }
  .products, .about-vision-grid, .about-team-grid, .about-phi-grid, .about-certs-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  /* 窄屏证书卡退回上下两段，图限宽不放巨 */
  .about-cert { grid-template-columns: minmax(0, 1fr); padding: 20px; gap: 20px; }
  .about-cert-img-link { max-width: 208px; }
  .about-cert-meta { align-content: start; gap: 14px; }
  .about-cert-dl { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .cell-grid, .cell-grid.cols-2, .cell-grid.cols-4, .lang-grid { grid-template-columns: minmax(0, 1fr); }
  /* 对照卡在窄屏竖着叠，属性行的横向对齐已经没有意义，行高回到内容高 */
  .sku-grid { grid-template-columns: minmax(0, 1fr); }
  .sku { padding: 22px 20px 20px; }
  /* 单列网格里 span 2 会凭空长出第二列，窄屏把跨列收回来 */
  .repo-grid .cell:last-child { grid-column: auto; }
  .repo-hub { grid-template-columns: minmax(0, 1fr); gap: 18px; padding: 20px 18px 22px; }
  .repo-hub-aside { justify-items: start; text-align: left; grid-auto-flow: column; gap: 14px; }
  /* 窄屏退回单列：序号一栏、内容一栏，名称/说明/标签竖着排。
     标签不再隐藏——它已经归到内容栏里，是这行唯一的分类信息 */
  .row { grid-template-columns: 46px minmax(0, 1fr); column-gap: 14px; row-gap: 5px; padding: 18px 18px 18px 38px; }
  .row-no { grid-column: 1; grid-row: 1 / span 3; }
  .row-body b { grid-column: 2; grid-row: 1; }
  .row-body small { grid-column: 2; grid-row: 2; }
  .row-tag { grid-column: 2; grid-row: 3; }
  .row-arrow { display: none; }
  /* 动态行窄屏：日期排在摘要下面，类别胶囊和标题抢位置，撤掉 */
  .rows--meta-left .row { grid-template-columns: 46px minmax(0, 1fr); }
  .rows--meta-left .row-body b { grid-column: 2; grid-row: 1; }
  .rows--meta-left .row-body small { grid-column: 2; grid-row: 2; }
  .rows--meta-left .row-date { grid-column: 2; grid-row: 3; }
  .rows--meta-left .row-tag { display: none; }
  /* 窄屏轨道整体左移，槽位从 52 收到 38，节点和分隔线跟着挪 */
  .row { background-size: calc(100% - 56px) 1px; background-position: 38px 100%; }
  .row::before { left: 15px; }
  .row::after { left: 12px; top: 26px; }
  .row:last-child::before { height: 30px; }
  /* 紧凑档的标签列在窄屏没有落点，只留日期和标题 */
  .feed-tight-item { grid-template-columns: 78px minmax(0, 1fr); padding: 11px 18px; }
  .ft-tag { display: none; }
  .feed-hero { padding: 20px 18px 22px; }
  .feed-title { font-size: 19px; }
  /* 模块数是这栏唯一的量化信息，窄屏不再整列删掉：压窄标签列、
     字号各收一档，数字留在原位 */
  .layer { grid-template-columns: 52px minmax(0, 1fr) auto; gap: 10px; padding: 12px 2px 12px 11px; }
  .layer b { font-size: 11px; }
  .layer i { font-size: 12px; }
  /* 窄屏：日期与阶段留在轨旁，说明整行铺在下面 */
  .about-tl-item { grid-template-columns: minmax(0, 1fr); column-gap: 0; row-gap: 8px; }
  .about-tl-stage { grid-row: 2; }
  .about-tl-body { grid-column: 1; grid-row: 3; }
  .hero-title { letter-spacing: -.03em; }
  /* 指尖命中的东西在窄屏一律抬到 44 */
  .to-top { width: 44px; height: 44px; right: 16px; bottom: 16px; }
  /* 窄屏不展开，留白仍走卡片自身宽度，别让 stage 带左右 padding。
     发布条在窄屏会折成两行，stage 还多了 20 的上边距 */
  .hero-stage { padding: 20px 0 0; --art-inset: 16px; }
  .hero-term { margin-top: 24px; }
  .hero-specs span { padding: 0 12px; }
  .term-body { font-size: 11px; padding: 14px 14px 18px; }
  /* 窄屏里标签独占一行，命令才有整幅宽度 */
  .install-row { flex-wrap: wrap; gap: 6px 14px; }
  .install-os { width: 100%; }
  .join-qr { flex-direction: column; align-items: flex-start; }
  /* 子页窄屏：终端窗换行而不横滚，胶囊组与命令条都撑满一行 */
  .sub-hero { padding-top: 34px; }
  .sub-title { letter-spacing: -.028em; }
  .sub-actions .btn { flex: 1 1 auto; justify-content: center; }
  .shot pre { font-size: 11px; line-height: 1.85; padding: 14px 15px 17px; white-space: pre-wrap; overflow-x: hidden; }
  .cell { padding: 22px; }
  .switch { display: flex; width: 100%; }
  .sw-opt { flex: 1; padding: 0 8px; }
  .cmd { flex-wrap: wrap; padding: 12px 12px 12px 14px; gap: 8px; }
  .cmd code { flex-basis: 100%; order: 3; }
}
