/* wanctl product site
   视觉真源 docs/design/DESIGN.md。改这里之前先改那里。
   场景：上午，光线很足的桌面。笔记本上一条命令刚敲下去，旁边手机亮了一下。
   参照 design-dna/apple —— 单一强调色、负字距、除产品投影外零阴影、整幅明暗交替。 */

/* ── fonts ─────────────────────────────────────────────────────────── */
@font-face{
  font-family:'Schibsted Grotesk';font-style:normal;font-weight:400 800;font-display:swap;
  src:url(fonts/schibsted-var-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Schibsted Grotesk';font-style:normal;font-weight:400 800;font-display:swap;
  src:url(fonts/schibsted-var-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'JetBrains Mono';font-style:normal;font-weight:400 700;font-display:swap;
  src:url(fonts/jbmono-var-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* ── tokens ────────────────────────────────────────────────────────── */
:root{
  --canvas:#ffffff;
  --canvas-2:#f5f5f7;
  --ink:#1d1d1f;
  --ink-2:#6e6e73;      /* 白底 5.07:1，浅灰屏 4.66:1 */
  --ink-3:#86868b;      /* 3.62:1 —— 仅装饰，不承载文字 */
  --hairline:#d2d2d7;
  --blue:#0066cc;
  --blue-hi:#0071e3;
  --dark:#1d1d1f;
  --on-dark:#f5f5f7;
  --on-dark-2:#a1a1a6;

  --sans:'Schibsted Grotesk','PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif;
  --mono:'JetBrains Mono','PingFang SC',ui-monospace,SFMono-Regular,Menlo,monospace;

  --maxw:1440px;
  /* 侧边留白：窄屏 22px 就够，宽屏最多 56px。
     居中的海报永远够不到边，但两栏章节里的面板会一直顶到 --pad，
     只留 22px 会让它看着要掉出屏幕。超过 1552px 之后由内容锁接手。 */
  --pad:max(clamp(22px, 4vw, 56px), calc((100vw - var(--maxw)) / 2));
  --ease:cubic-bezier(.22,.61,.36,1);
  --z-nav:5;
}

*{box-sizing:border-box}
/* 吸顶导航 48px 高：不给滚动容器留出这一截，点导航跳过去时每一屏的顶
   都会被压在导航条底下。 */
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:48px}
body{
  margin:0;background:var(--canvas);color:var(--ink);
  font-family:var(--sans);font-size:17px;font-weight:400;line-height:1.47;
  letter-spacing:-.374px;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
a{color:var(--blue);text-decoration:none}
a:hover{text-decoration:underline}
:focus-visible{outline:2px solid var(--blue-hi);outline-offset:3px;border-radius:4px}

/* ── 整幅屏：颜色变化本身就是分隔线 ────────────────────────────────── */
.tile{padding:clamp(52px,6.4vw,88px) var(--pad)}

/* ── 两栏章节 ────────────────────────────────────────────────────────
   规则：海报（首屏、安装）居中，章节走左脊。主体落在哪一侧不是为了不单调，
   是**位置在说这东西属于谁** —— 首屏已经定了笔记本在左（控制端）、手机在右（你）。
     · 凭证是你的回答      → 主体在右
     · 设备表是 peers 的输出 → 主体在左（.flip）
     · 身份核对是两端之间的链路，不站边 → 保持整幅居中，顺便打断节拍
   两条纪律：文字列跑到哪边，段落自身永远左对齐（右对齐段落的锯齿左缘不可读）；
   两行的分栏比例故意不同，不做镜像 50/50。 */
.tile.split{
  display:grid;align-items:center;text-align:left;
  gap:clamp(34px,5vw,78px);
}
#decide.split{grid-template-columns:minmax(0,42fr) minmax(0,58fr)}
#fleet.split{grid-template-columns:minmax(0,61fr) minmax(0,39fr)}
.split.flip .see{order:-1}
.split .say{max-width:36rem}
.split .lead{margin-left:0;margin-right:0;max-width:40ch}
.split .panel{margin:0;max-width:100%}
/* 面板该不该长满自己那一栏，看它有没有东西填。
   凭证是五行短键值，拉宽只会在每行后面拖出一大片空 —— 封顶，贴外缘站。
   设备表有右对齐的 meta 列，宽度越大它越像一张真表 —— 让它长满。 */
#decide .panel{max-width:620px;margin-left:auto}

@media (max-width:900px){
  .tile.split{grid-template-columns:minmax(0,1fr)!important;gap:clamp(26px,4vw,38px)}
  .split.flip .see{order:0}      /* 窄屏一律先说话再给东西看 */
  .split .say{max-width:none}
  .split .lead{max-width:52ch}
  #decide .panel{max-width:100%;margin-left:0}
}
/* 首屏顶上已经有导航条了，不需要再来一整份上留白 ——
   省下来的高度给舞台，好让手机上那个蓝按钮落在折线以上（它是要人去点的东西）。 */
.tile.hero{
  padding-top:clamp(32px,3.6vw,52px);
  /* 首屏就只放首屏。窗口高一点，下一屏那块深色就会从底下探进来半截 ——
     那半截既读不出内容，又把「一屏一个主体」直接作废。48px 是导航条的高度。
     svh 而不是 dvh：dvh 会随移动端浏览器工具条的伸缩跳数，svh 取的是工具条
     展开时的高度，也就是「保证不被切到」的那一档。
     min-height 不是 height：窗口矮的时候内容照样自己撑开。 */
  min-height:calc(100vh - 48px);
  min-height:calc(100svh - 48px);
  display:flex;flex-direction:column;justify-content:center;
  /* 密文场（assets/field.js）垫在首屏底下。isolation 让首屏自成一个层叠上下文，
     画布的 z-index:-1 才是「在这一屏的内容底下」，而不是「在整页底下」。 */
  position:relative;isolation:isolate;
}
/* 首屏底下的两层，都铺满整幅 tile、都不接事件 —— 指针事件挂在 section 上，
   手机屏里那三个按钮、两个 CTA 照常能按。
     .backdrop  svg：只有那团蓝光，圆心在首屏正中（field.js 建）。
                甲方 09-05 拍板的那一处渐变例外，见 DESIGN.md §7.5 / §8。
     .field     画布，在光上面：那几圈线、沿圈流动的高光弧、划过才显形的字符都画在这里。
                没有 JS 两层都不存在，白底即原样。 */
.backdrop{position:absolute;inset:0;z-index:-2;width:100%;height:100%;display:block;pointer-events:none}
.field{position:absolute;inset:0;z-index:-1;width:100%;height:100%;display:block;pointer-events:none}
/* 上面那个 flex 会把子元素拉满宽（stretch），段落自身的居中靠 .center 的
   text-align，不受影响；舞台本来就有 margin:auto。 */

/* 三档配额，不是五段等权。之前每一段的上下内边距都是 88px，于是产品的命题
   （「你不点头，什么都不会跑」）和一张设备清单拿到完全相同的视觉权重。
     chapter 命题   —— 双倍空间，标题接近 hero 尺度，下面还有一条通栏的收尾
     （默认）二级   —— 消解疑虑的那一屏，维持原样
     minor   清单   —— 压紧，标题降一档；它只是在说「有哪些机器」 */
.tile.chapter{padding-top:clamp(72px,8.6vw,124px);padding-bottom:clamp(58px,6.4vw,88px)}
.tile.chapter h2{font-size:clamp(34px,4.8vw,52px);line-height:1.06;letter-spacing:-.3px}
.tile.minor{padding:clamp(38px,4.4vw,60px) var(--pad)}
.tile.minor h2{font-size:clamp(24px,2.6vw,32px)}
.tile.minor .lead{font-size:clamp(17px,1.35vw,18px);max-width:36ch}
.tile.dark{background:var(--dark);color:var(--on-dark)}
.tile.grey{background:var(--canvas-2)}

/* ── 排版阶：照搬苹果 ──────────────────────────────────────────────── */
h1{
  margin:0;font-size:clamp(36px,5.2vw,56px);font-weight:600;line-height:1.07;
  letter-spacing:-.28px;text-wrap:balance;
}
h2{
  margin:0;font-size:clamp(28px,3.8vw,40px);font-weight:600;line-height:1.1;
  letter-spacing:-.374px;text-wrap:balance;
}
.lead{
  margin:16px auto 0;font-size:clamp(18px,1.6vw,21px);font-weight:400;line-height:1.4;
  color:var(--ink-2);max-width:44ch;text-wrap:balance;
}
.dark .lead{color:var(--on-dark-2)}
.center{text-align:center}
.mono{font-family:var(--mono);letter-spacing:0}
/* 命令要能被逐字符读准：等宽处一律关连字，别让 -- 连成一道长划。 */
.mono,.term,.ask .what,.cmdline code,.party .fp,.ledger code,.rows .host{
  font-variant-ligatures:none;font-feature-settings:"liga" 0,"calt" 0;
}

/* ── 导航 ──────────────────────────────────────────────────────────── */
.nav{
  position:sticky;top:0;z-index:var(--z-nav);
  display:flex;align-items:center;gap:clamp(14px,2.4vw,30px);
  height:48px;padding:0 var(--pad);
  /* 没有下边框。吸顶条压在内容上要半透，那层 backdrop-filter 本身就是边界：
     深色 tile 从底下滑过去，这条 .86 的白就成了一道明显的浅带。停在顶部时
     它化进白底里 —— 一条横穿整幅的发丝线在那种时候只是把首屏切成两截。 */
  background:rgba(255,255,255,.86);backdrop-filter:saturate(180%) blur(20px);
  font-size:14px;letter-spacing:-.2px;
}
/* 标记 + 字标。标记是那个末笔上扬的 w（assets/mark.svg 同一条路径），
   currentColor 描边 —— 它跟着字标的颜色走，不引入第二种墨色。 */
/* 字标不跟着链接一起撑到 48：它的盒子高度是分数（17px 的行盒 25.4），撑满之后
   标记在 48 里居中和在 25.4 里居中差半个像素，1440 上实测 78 个像素不一致 ——
   那是全站唯一冻着的那一档。它去的又是当前这一页，够不着的代价近乎为零。 */
.nav .brand{
  display:flex;align-items:center;gap:8px;
  color:var(--ink);font-weight:600;font-size:17px;letter-spacing:-.4px;flex:none;
  /* 标记在自己那个 32 的 viewBox 里左右各留了 4.05 的边距（笔画 3.1、圆头），
     19px 下就是 2.4px —— 盒子压着内容锁，墨迹却比每一屏的内容往里缩 2.4px。
     拉回 2px 而不是 2.4：圆头本来就该比直边多探出去一点点才看着齐。
     **改了 .mk 的尺寸要重算这个数。** */
  margin-left:-2px;
}
.nav .brand:hover{text-decoration:none}
.mk{width:19px;height:19px;flex:none;display:block}
/* min-width:0 + overflow:hidden 是最后一道保险，不是版式的一部分。断点该掉的链接
   已经掉干净了（760 / 560 / 440 / 360 四档），正常情况下这里一个字也不会被裁。
   但访客的字号不归我们管：甲方那台 412px 的安卓实测把每个字渲宽 10%（字标 76.4 → 84），
   于是 388px 的一栏胀到 427，整条顶栏溢出，被挤出屏幕的正好是最右边的语言开关 ——
   中文访客第一个要按的那个键。没有这一行，链接栏拒绝收缩（nowrap 的 min-content
   就是它的下限），溢出只能由整条 header 承担；有了这一行，超出的部分在链接栏自己
   那里被裁掉，开关原地不动。**要被牺牲的永远是链接，不是开关。** */
/* align-self:stretch 让这一栏跟整条顶栏一样高（48），链接因此可以做成
   整条高的按压面而墨迹一动不动 —— 实测手机上每个链接只有 20.6~22.4px 高，
   而它们是这个站在手机上仅有的两个导航入口之一。栏被撑高之后上面那句
   overflow:hidden 也不再会把纵向探出的东西裁掉。 */
.nav .links{
  display:flex;align-items:center;align-self:stretch;
  gap:clamp(13px,2vw,26px);margin-left:auto;min-width:0;overflow:hidden;
}
.nav .links a{
  display:flex;align-items:center;align-self:stretch;
  color:var(--ink-2);white-space:nowrap;
}
.nav .links a:hover{color:var(--ink);text-decoration:none}
/* 不画边框：分隔线拿掉之后，一条描边胶囊会成为整条导航里最重的东西，
   而它只是个次级开关。悬停的浅灰底照样说明它能点。 */
.nav .lang{
  flex:none;white-space:nowrap;
  font:inherit;font-size:13px;color:var(--ink);background:none;cursor:pointer;
  border:0;border-radius:999px;padding:4px 11px;line-height:1.2;
  /* 把自己的内边距吐回去。描边还在的时候，那圈内边距的外沿就是边框、边框压着
     内容锁，看着是齐的；边框一拿掉，内边距成了不可见的空白，「中文」两个字
     就比内容锁往里缩了 11px —— 而首屏标题和每一屏的内容都是压着锁的。
     负外边距只挪盒子不挪墨迹，悬停那块浅灰底探进 --pad 的留白，正是它该在的地方。 */
  margin-right:-11px;
  position:relative;
  transition:background .16s var(--ease);
}
/* 按压面撑到 44×45，那块浅灰底一个像素都不动。
   直接给按钮加内边距会把悬停的底块在 48px 的横带里撑成一块厚板；
   透明的伪元素只管手指够不够得着。 */
.nav .lang::after{content:"";position:absolute;top:-11px;bottom:-11px;left:-3px;right:-3px}
.nav .lang:hover{background:var(--canvas-2)}
.nav .lang:active{transform:scale(.95)}

/* ── 按钮：pill 只给主 CTA，蓝色是唯一的「点我」信号 ───────────────── */
.btn{
  display:inline-block;font:inherit;font-size:17px;font-weight:400;
  background:var(--blue);color:#fff;border:0;border-radius:999px;
  padding:12px 24px;cursor:pointer;transition:background .18s var(--ease);
}
.btn:hover{background:var(--blue-hi);text-decoration:none}
.btn:active{transform:scale(.95)}
.link-arrow::after{content:" ›"}
.cta-row{display:flex;gap:26px;align-items:center;flex-wrap:wrap;margin-top:24px}
.center .cta-row{justify-content:center}

/* ── 双屏真实场景：唯一允许有阴影的地方 ────────────────────────────── */
.stage{
  position:relative;margin:clamp(22px,2.8vw,34px) auto 0;
  display:flex;align-items:flex-end;justify-content:center;
  gap:clamp(12px,2.6vw,44px);
  /* 首屏是个纵向 flex，横向 auto 外边距让舞台按内容宽收缩 —— 而笔记本是个 container
     （inline-size containment），它对「内容宽」的贡献是 0，舞台会缩到只剩手机、
     笔记本被挤成 0 宽。所以舞台明确铺满，两台机器由 justify-content 居中。 */
  width:100%;
  /* 舞台的尺子是手机的宽 --pw，笔记本按等高跟着它（DESIGN.md §6：两块屏高度大致相等）。
     手机取视口宽的 17%，再被两条线封顶：300px（2000 宽的屏上手机 300、舞台 1361，够大了），
     和折线 —— 手机高是宽的 2.07 倍，舞台顶在 ~400px，(100svh−400)/2.07 就是屏底不过折线的
     最大宽。1440×900 → 245；1536×864 → 224；1366×768 → 178；1920×1080 → 300。
     甲方 09-05 下午：184 的手机在 2000 宽的屏上「太小了」—— 之前它是个固定上限。
     地板 150：再小，屏里那份按 iOS 尺子排的字就读不了了（tools/phone-fit.mjs 量的）。 */
  --pw:clamp(150px,min(17vw,(100svh - 400px) / 2.07),300px);
}
/* 外壳是真机：两张带透明通道的渲染图（assets/iphone.webp · macbook.webp），屏幕那块
   在图里是个透明的洞。活界面（.screen）垫在壳底下，壳压在上面且不接事件 ——
   边框、圆角、灵动岛、刘海都是照片级的，屏里跑的仍然是活的产品界面。
   DESIGN.md §6 那句「渲染的就是产品自己」不变，变的只是壳从 CSS 画的换成了真的。
   屏那一层落在哪、圆角多大，是 tools/frames.py 按像素量出来的：矩形比洞每边多出几个
   源像素（洞边上那一两像素的抗锯齿灰边要压在屏底下，不是压在页面白底上），
   圆角取「不戳出壳外」和「不露出洞的圆角」之间的中点。**数字别手改，重新量。**
   <img> 带 width/height，加载前盒子就有尺寸，舞台不会跳。
   挂在 .laptop / .phone 上而不是 .stage 上：分享卡（tools/og.html）里的手机不在舞台里。 */
.laptop .shell,.phone .shell{display:block;width:100%;height:auto;position:relative;z-index:1;
                             pointer-events:none;user-select:none;-webkit-user-drag:none}
.laptop .screen,.phone .screen{position:absolute;z-index:0;overflow:hidden;display:flex;flex-direction:column}

/* 3.39 = 手机的高宽比 ÷ 笔记本的高宽比：这个宽度下两台机器一样高。放不下时笔记本收缩 */
.laptop{flex:0 1 calc(var(--pw) * 3.39);min-width:0;position:relative;container-type:inline-size;
        filter:drop-shadow(3px 5px 30px rgba(0,0,0,.22))}
/* 上角圆、下角方 —— MacBook 的屏就长那样。圆角量出来的可行区间是 0~52 源像素
   （壳宽 3984）即壳宽的 0~1.3%，.5cqw 在任何显示宽度下都落在区间里 */
.laptop .screen{left:8.986%;top:2.055%;width:82.028%;height:86.93%;
                background:#000;border-radius:.5cqw .5cqw 0 0}

.phone{
  /* 不再抬离地面：两台机器摆在同一张桌上就该共一条地平线，
     悬空 16px 是把它们从一张照片变成两张贴图。 */
  flex:0 0 auto;width:var(--pw);margin-bottom:0;position:relative;
  filter:drop-shadow(3px 5px 30px rgba(0,0,0,.22));
  /* 屏里的一切按壳的宽度定尺寸（cqw）：iOS 的状态栏、圆角、安全区都是屏宽的比例，
     手机放大缩小它们跟着走 —— 桌面 184、手机 230、分享卡 250 用的是同一份数。 */
  container-type:inline-size;
}
.phone .screen{
  left:3.636%;top:1.486%;width:92.727%;height:97.062%;
  background:var(--canvas-2);border-radius:10.28cqw;
  transition:background .45s var(--ease);
}
/* iOS 的状态栏：时间在灵动岛左边那只「耳朵」的正中，信号 / Wi-Fi / 电量在右边那只，
   整条跟岛的中线对齐；三栏的中间那栏就是岛的宽度，量出来的。
   9:41 —— 苹果所有渲染图上的那个时间，也正好是 DESIGN.md 第 1 节那句「上午」。
   字走系统栈：这是 iOS 自己的字不是我们的，苹果设备上看到的就是 SF；别处退到站点字体。
   尺寸按 iPhone 17 Pro（402pt 宽）的比例：时间 17pt、图标组 72×12pt、home 条 134×5pt。 */
.phone .sbar{
  position:absolute;left:0;right:0;top:6.99cqw;transform:translateY(-50%);
  display:grid;grid-template-columns:1fr 26.3cqw 1fr;align-items:center;
  pointer-events:none;transition:opacity .3s var(--ease);
}
.phone .clock{
  grid-column:1;text-align:center;color:var(--ink);
  font:600 3.9cqw/1 -apple-system,BlinkMacSystemFont,'SF Pro Text','Helvetica Neue',var(--sans);
  letter-spacing:-.02em;font-variant-numeric:tabular-nums;
}
.phone .sys{grid-column:3;justify-self:center;display:block;width:16.5cqw;height:auto;color:var(--ink)}
.phone .homebar{
  position:absolute;left:50%;bottom:2.1cqw;transform:translateX(-50%);
  width:30.7cqw;height:1.15cqw;border-radius:999px;background:var(--ink);
  transition:opacity .3s var(--ease);
}

/* 开场时手机是黑的，然后亮一下 —— DESIGN.md 第 1 节那句场景句。
   醒着是默认状态；.asleep 只由 JS 临时加，所以无 JS / 无头渲染看到的都是醒着的。
   睡着的时候状态栏和 home 条也一起灭：真机就是这样，黑屏上什么都没有。 */
.phone.asleep .screen{background:#000}
.phone.asleep .ask,.phone.asleep .sbar,.phone.asleep .homebar{opacity:0}
@keyframes phone-attn{0%{transform:translateY(0)}34%{transform:translateY(-5px)}100%{transform:translateY(0)}}
.phone.attn{animation:phone-attn .5s var(--ease)}

/* 笔记本屏内：终端 */
/* 字号按笔记本的宽算（.laptop 是 container）：壳 830 宽时 13px，1017 时 16px，508 时 8px */
.term{
  flex:1;padding:2.4cqw 2.6cqw;font-family:var(--mono);font-size:1.6cqw;
  line-height:1.75;color:#e6e6e6;letter-spacing:0;overflow:hidden;text-align:left;
}
.term .row{white-space:pre-wrap;word-break:break-word;min-height:1.1em}
.term .cmd{color:#fff;font-weight:700}
.term .out{color:#a1a1a6}
.term .wait{color:#5ac8fa}
.term .no{color:#ff6961}
.term .caret{display:inline-block;width:.55em;height:1.05em;vertical-align:-.16em;
             background:#fff;animation:blink 1.06s steps(2,end) infinite}
@keyframes blink{50%{opacity:0}}

/* 手机屏内：审批 —— 按 iOS 的尺子画。
   --pt 是「一个 iOS 点」：iPhone 17 Pro 的屏 402pt 宽，占壳宽的 92.2%，真比例是 .229cqw；
   这里放大一成到 .255：1440 下（手机 245 宽）17pt 的正文才够 10.6px。字号、按钮高、圆角、
   留白全用这一把尺，手机放大缩小，屏里的一切跟 iOS 保持同一个比例 ——
   甲方 09-05 下午那句「按钮比例不对」说的就是之前 13px 的字配 35px 的按钮：字是 iOS 的两倍大。
   版式是 iOS 的请求页：顶上导航标题（字标 + wanctl），然后是谁在请求（名字、短指纹、一句
   「想运行一条命令」），请求什么（一张分组卡：设备 / 命令），秒表，最底下三颗 50pt 的胶囊
   按钮压在 home 条的安全区上。屏底是 iOS 分组页那种浅灰（--canvas-2），卡片和次级按钮是白的
   —— 都是站点自己的 token，没有新颜色。
   上内边距 54pt = iOS 自己把内容摆在状态栏底下的那条线，下内边距 34pt = home 条的安全区。
   装不装得下由 tools/phone-fit.mjs 量。 */
.phone{--pt:.255cqw}
.ask{
  flex:1;display:flex;flex-direction:column;text-align:left;
  padding:calc(var(--pt)*54) calc(var(--pt)*20) calc(var(--pt)*34);
  transition:opacity .3s var(--ease);
}
@keyframes ask-rise{from{opacity:0;transform:translateY(7px)}to{opacity:1;transform:none}}
.ask.rise{animation:ask-rise .42s var(--ease) both}
/* 导航标题：字标 + 名字，17pt semibold 居中 —— iOS 内嵌标题的写法。所有状态都有它 */
.ask .ttl{
  flex:none;display:flex;align-items:center;justify-content:center;gap:calc(var(--pt)*5);
  height:calc(var(--pt)*44);font-size:calc(var(--pt)*17);font-weight:600;letter-spacing:-.02em;
}
.ask .ttl svg{display:block;width:calc(var(--pt)*19);height:calc(var(--pt)*19);flex:none}
/* 谁在请求：名字 22pt，底下是它的短指纹（设备端真会显示它，也把首屏和「端到端身份」那一屏
   系在一起），再一句「想运行一条命令」 */
.ask .who{margin-top:calc(var(--pt)*10)}
.ask .name{font-size:calc(var(--pt)*22);font-weight:600;line-height:1.2;letter-spacing:-.02em;overflow-wrap:anywhere}
.ask .fp{
  font-family:var(--mono);font-size:calc(var(--pt)*13);line-height:1.3;
  color:var(--ink-2);letter-spacing:0;word-break:break-all;margin-top:calc(var(--pt)*3);
}
.ask .from{font-size:calc(var(--pt)*17);line-height:1.3;color:var(--ink-2);margin-top:calc(var(--pt)*8)}
/* 请求什么：iOS 的分组卡，两行 —— 设备、命令。标签一栏 74pt，「Command」正好放得下 */
.ask .card{
  margin:calc(var(--pt)*14) 0 0;padding:0 calc(var(--pt)*16);
  background:var(--canvas);border-radius:calc(var(--pt)*20);
}
.ask .card .row{display:flex;gap:calc(var(--pt)*12);padding:calc(var(--pt)*11) 0;font-size:calc(var(--pt)*17);line-height:1.3}
.ask .card .row+.row{border-top:1px solid var(--hairline)}
.ask .card dt{flex:0 0 calc(var(--pt)*74);margin:0;color:var(--ink-2)}
.ask .card dd{flex:1;min-width:0;margin:0}
.ask .card dd.mono{font-family:var(--mono);font-size:calc(var(--pt)*15);letter-spacing:0}
/* 优先在空格处折行：break-all 会把 --epochs 拦腰断成 --epoc / hs */
.ask .what{word-break:normal;overflow-wrap:break-word}
.ask .what .tok,.kv dd.cmd .tok{white-space:nowrap}
.ask .what .tok.split,.kv dd.cmd .tok.split{white-space:normal;word-break:break-all}
/* 手机上的那份秒表。移动端终端在折线以下，这一份才是落地时看得见的活体证明。
   压在按钮上方，卡片和它之间的空由 margin-top:auto 吃掉。 */
.ask .pend{
  margin-top:auto;padding:calc(var(--pt)*14) 0;
  display:flex;align-items:center;justify-content:center;gap:calc(var(--pt)*6);
  font-size:calc(var(--pt)*15);color:var(--ink-2);letter-spacing:0;font-variant-numeric:tabular-nums;
}
.ask .pend .dot{
  width:calc(var(--pt)*7);height:calc(var(--pt)*7);border-radius:50%;background:var(--blue);flex:none;
  animation:pend-dot 2s var(--ease) infinite;
}
@keyframes pend-dot{0%,100%{opacity:1}50%{opacity:.28}}
.ask .acts{flex:none;display:flex;flex-direction:column;gap:calc(var(--pt)*10)}
/* iOS 的按钮：50pt 高的胶囊，17pt semibold 的字 */
.ask button{
  font:inherit;font-size:calc(var(--pt)*17);font-weight:600;letter-spacing:-.02em;line-height:1;
  height:calc(var(--pt)*50);border:0;border-radius:999px;padding:0 calc(var(--pt)*16);cursor:pointer;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:background .18s var(--ease);
}
/* 主按钮每 3.4 秒轻呼吸两下：告诉你「该你了」，不靠涟漪不靠阴影 */
@keyframes breathe{
  0%{transform:scale(1)}   6%{transform:scale(1.055)}  13%{transform:scale(1)}
  19%{transform:scale(1.055)}  26%{transform:scale(1)}  100%{transform:scale(1)}
}
.ask .yes.breathe{animation:breathe 3.4s var(--ease) 1.1s infinite}
.ask .yes.breathe:active{animation:none}
.ask button:active{transform:scale(.95)}
/* 层级按后果排，不按语气排。
   「一直允许」是一次持久授权 —— 这个产品里后果最重的动作 —— 之前它是三个里最轻的
   一行蓝字，而后果最轻的「拒绝」反倒是一块厚灰板。两个次级动作现在等重：
   浅灰屏底上的两颗白胶囊（iOS 26 起浅底上的次级按钮就是这个样子）。 */
.ask .yes{background:var(--blue);color:#fff}
.ask .yes:hover{background:var(--blue-hi)}
.ask .alt,.ask .no{background:var(--canvas);color:var(--ink)}
.ask .alt:hover,.ask .no:hover{background:#ececf0}
.ask .done{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--pt)*12)}
.ask .tickring{
  width:calc(var(--pt)*64);height:calc(var(--pt)*64);border-radius:50%;
  border:2px solid var(--blue);display:grid;place-items:center;color:var(--blue);
}
.ask .tickring svg{width:calc(var(--pt)*28);height:calc(var(--pt)*28)}
.ask .tickring.refused{border-color:var(--ink-2);color:var(--ink-2)}
.ask .doneword{font-size:calc(var(--pt)*17);color:var(--ink-2);text-align:center;letter-spacing:-.02em;
               max-width:calc(var(--pt)*300)}
.ask .idle{flex:1;display:grid;place-items:center;color:var(--ink-2);text-align:center;
           font-size:calc(var(--pt)*17);padding:0 calc(var(--pt)*8)}

/* ── 被放大的真实界面 ──────────────────────────────────────────────── */
.panel{
  text-align:left;max-width:700px;margin:clamp(38px,5vw,60px) auto 0;
  background:var(--canvas);color:var(--ink);border-radius:18px;overflow:hidden;text-align:left;
  filter:drop-shadow(3px 5px 30px rgba(0,0,0,.22));
}
/* 白卡压在近黑整幅屏上，色差本身就是抬升；那道投影在这儿肉眼为零，只是白烧一次重绘 */
.dark .panel{filter:none}
.panel .head{
  display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
  padding:19px 26px;border-bottom:1px solid var(--hairline);
}
.panel .head .t{font-size:17px;font-weight:600;letter-spacing:-.374px}
.panel .head .s{margin-left:auto;font-size:14px;color:var(--ink-2)}
.panel .body{padding:8px 0 12px}
/* 内边距挂在行上而不是容器上，分隔线才能和设备表一样通栏 —— 同一个组件不该有两套规矩 */
.kv{display:flex;gap:14px;padding:9px 26px;font-size:15px;margin:0}
.kv+.kv{border-top:1px solid var(--hairline)}
.kv dt{flex:0 0 104px;color:var(--ink-2);margin:0}
.kv dd{margin:0;min-width:0;overflow-wrap:anywhere}

.rows{list-style:none;margin:0;padding:0}
.rows li{display:flex;align-items:center;gap:13px;padding:14px 26px;font-size:17px}
.rows li+li{border-top:1px solid var(--hairline)}
.rows .dot{width:9px;height:9px;border-radius:50%;background:var(--blue);flex:none}
.rows .dot.off{background:var(--ink-3)}
.rows .name{font-weight:600;letter-spacing:-.374px}
.rows .host{font-family:var(--mono);font-size:14px;color:var(--ink-2);letter-spacing:0}
.rows .meta{margin-left:auto;font-size:14px;color:var(--ink-2);white-space:nowrap}

/* ── 端到端身份：两端对称，中间那道链路就是主体 ────────────────────
   默认状态就是终局（headless / 无 JS 下看到的是已钉住的真相）。
   JS 只在滚进视口时短暂加 .verifying 再摘掉，让它当着你的面钉一次。 */
.pin .body{padding:30px 26px 28px;text-align:center}
.party{display:flex;flex-direction:column;gap:5px;align-items:center}
.party .who{font-size:15px;font-weight:600;letter-spacing:-.374px}
.party .who em{font-style:normal;font-weight:400;color:var(--ink-2);margin-left:7px}
.party .fp{
  font-size:clamp(12px,1.45vw,15px);color:var(--ink);word-break:break-all;
  transition:color .55s var(--ease);
}
.wireline{display:flex;align-items:center;gap:16px;margin:22px 0}
.wire{
  flex:1 1 auto;height:2px;border-radius:1px;background:var(--hairline);
  position:relative;overflow:hidden;
}
.wire::after{
  content:"";position:absolute;inset:0;background:var(--blue);
  transform:scaleX(1);transition:transform .75s var(--ease);
}
/* 两端各自向中间画过来，在徽标处会合 —— 这才是「互相核对」的顺序 */
.wireline .wire:first-child::after{transform-origin:left}
.wireline .wire:last-child::after{transform-origin:right}
.state{
  flex:none;display:flex;align-items:center;gap:9px;
  font-size:14px;color:var(--ink-2);white-space:nowrap;
}
.state .ring{
  width:22px;height:22px;flex:none;border-radius:50%;
  border:1.5px solid var(--blue);color:var(--blue);
  display:grid;place-items:center;
  transition:opacity .3s var(--ease) .55s,transform .35s var(--ease) .55s;
}
.state b{font-weight:400}

.pin.verifying .party .fp{color:var(--ink-2)}
.pin.verifying .wire::after{transform:scaleX(0)}
.pin.verifying .state .ring{opacity:0;transform:scale(.5);transition-delay:0s}

.ledger{
  background:var(--canvas-2);border-top:1px solid var(--hairline);
  padding:15px 26px 17px;display:flex;flex-direction:column;gap:5px;text-align:left;
}
.ledger .cap{font-size:14px;color:var(--ink-2)}
.ledger code{font-size:15px;color:var(--ink);white-space:pre;overflow-x:auto;display:block;min-width:0}

/* ── 章节收尾：通栏的两行，讲策略的语义 ──────────────────────────────
   两列文字撑不满 1440，但这条能 —— 而且它讲的是首屏 demo 每一轮都在演、
   页面上却从没写出来的那件事。 */
.lesson{
  margin-top:clamp(30px,3.2vw,44px);padding-top:clamp(22px,2.3vw,30px);
  border-top:1px solid rgba(255,255,255,.14);grid-column:1 / -1;
}
.lesson .claim{margin:0 0 20px;font-size:clamp(19px,1.9vw,24px);font-weight:600;
               letter-spacing:-.374px;color:var(--on-dark)}
/* 表格自己收窄。撑满 1440 会把判定推到 600px 开外，
   于是刚在整页上治好的「两边空」在这一小块里原样复发。 */
.lesson ul{list-style:none;margin:0;padding:0;max-width:860px}
.lesson li{
  display:flex;align-items:baseline;gap:clamp(14px,2vw,28px);
  padding:13px 0;font-size:15px;
}
.lesson li+li{border-top:1px solid rgba(255,255,255,.09)}
.lesson .k{flex:0 0 clamp(88px,9vw,132px);color:var(--on-dark-2)}
.lesson code{flex:1 1 auto;min-width:0;color:var(--on-dark);letter-spacing:0;
             overflow-wrap:break-word}
.lesson .v{flex:0 0 auto;color:var(--on-dark-2);text-align:right;white-space:nowrap}

@media (max-width:760px){
  .lesson li{flex-wrap:wrap;gap:4px 14px;padding:12px 0}
  .lesson .k{flex:0 0 auto}
  .lesson code{flex:1 0 100%;order:3;font-size:13.5px}
  .lesson .v{margin-left:auto}
}

/* ── 安装 ──────────────────────────────────────────────────────────── */
/* 分段控件：选中项填蓝。蓝是这套系统里唯一的「这个是活的」信号，
   而这里选中与否直接决定下面那条命令，含糊不得。 */
.picker{display:flex;gap:30px;justify-content:center;flex-wrap:wrap;margin-top:28px}
.seg{display:inline-flex;gap:2px;background:var(--canvas-2);border-radius:999px;padding:3px}
.seg button{
  font:inherit;font-size:14px;letter-spacing:-.2px;white-space:nowrap;
  color:var(--ink-2);background:none;border:0;border-radius:999px;
  padding:6px 15px;cursor:pointer;
  transition:background .18s var(--ease),color .18s var(--ease);
}
.seg button:hover{color:var(--ink)}
.seg button.on{background:var(--blue);color:#fff}
.seg button.on:hover{background:var(--blue-hi);color:#fff}
.seg button:active{transform:scale(.95)}

.cmdline{
  /* 贴着内容走，不是固定宽。四条命令长短差一倍（44 ~ 86 字），
     钉死在最长那条的宽度上，短命令后面就是一大片空当。 */
  display:flex;align-items:stretch;width:fit-content;max-width:100%;margin:22px auto 0;
  border:1px solid var(--hairline);border-radius:999px;overflow:hidden;background:var(--canvas);
}
.dark .cmdline{background:#000;border-color:#3a3a3c}
.cmdline code{
  flex:0 1 auto;min-width:0;overflow-x:auto;white-space:nowrap;
  font-family:var(--mono);font-size:14px;letter-spacing:0;color:var(--ink);
  padding:14px 20px;text-align:left;
}
.dark .cmdline code{color:var(--on-dark)}
.cmdline button{
  flex:none;font:inherit;font-size:15px;background:var(--blue);color:#fff;border:0;
  padding:0 22px;cursor:pointer;transition:background .18s var(--ease);
}
.cmdline button:hover{background:var(--blue-hi)}
.cmdline button:active{transform:scale(.95)}
/* 「还有」这个信号。命令一行装不下的时候，末 24px 擦成透明，露出胶囊自己的底
   （深色屏下露出的就是黑）—— DESIGN.md 第 8 节禁的是装饰性渐变，这是遮罩：
   它不铺任何一层新颜色，只是把元素自己的末端擦掉。
   `.more` 由 app.js 在 scrollWidth > clientWidth 时挂上（issue #25 那一版把它
   挂在 ≤560 上，而 768 下英文那条 GitHub 命令照样有 138px 在框外，遮罩已经
   下线了）。量的是溢出本身，所以不溢出的宽度上它自动不存在。 */
.cmdline.more code{
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 24px),transparent);
          mask-image:linear-gradient(90deg,#000 calc(100% - 24px),transparent);
}

.note{
  margin:14px auto 0;max-width:52ch;font-size:15px;color:var(--ink-2);
  line-height:1.45;text-wrap:pretty;
}

/* ── 两扇门 ────────────────────────────────────────────────────────
   不用卡片：那道投影按 DESIGN.md 只归「产品渲染」。两扇门用发丝线分隔，
   读起来像一份规格表，而不是两张促销卡。 */
#relay .say{max-width:46rem}
#relay .lead{margin-left:0;max-width:52ch}
.doors{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(30px,4.4vw,72px);margin-top:clamp(34px,4vw,56px);
}
.door{border-top:1px solid var(--hairline);padding-top:20px;display:flex;flex-direction:column}
.door .k{font-size:14px;color:var(--ink-2)}
.door .addr{margin:9px 0 0;font-size:clamp(19px,1.7vw,22px);letter-spacing:-.2px;color:var(--ink)}
.door .addr a{color:var(--ink)}
.door .addr a:hover{color:var(--blue);text-decoration:none}
.door .d{margin:12px 0 0;font-size:16px;line-height:1.5;color:var(--ink-2);max-width:38ch}
.door .link-arrow{margin-top:18px;align-self:flex-start;font-size:16px}

@media (max-width:760px){
  .doors{grid-template-columns:minmax(0,1fr);gap:26px}
  #relay .say{max-width:none}
}

/* ── 页脚 ──────────────────────────────────────────────────────────── */
.foot{background:var(--canvas);border-top:1px solid var(--hairline);
      padding:40px var(--pad) 44px;font-size:14px;
      color:var(--ink-2);line-height:1.6}
/* 页脚链接跟随导航条的语法（灰→黑），不是蓝：它们是路标，不是号召 */
.foot .flinks{display:flex;flex-wrap:wrap;gap:9px 26px;margin-bottom:22px}
.foot .flinks a{color:var(--ink-2)}
.foot .flinks a:hover{color:var(--ink);text-decoration:none}
.foot .row{display:flex;gap:24px;flex-wrap:wrap;align-items:baseline}
.foot .row .end{margin-left:auto}

@media (max-width:760px){
  /* 手机上访客自己就是设备主人，先给他那块要按的屏。
     笔记本在这里退场，跟竖排是同一个断点：两块屏一旦并排放不下，被折线切掉的
     就是下面那台。以前退场挂在 ≤560，于是 561~760 这 200px 里既竖排又留着笔记本，
     首屏实测 1055~1062px 高（900 的窗口下多出 158），落地看到的是「一部手机
     加半台笔记本」—— HANDOFF §3.11 在 ≤560 修掉的正是这一条。
     故事全在手机屏上：谁在请求、要跑什么、等了多久、那三个按钮。 */
  .stage{flex-direction:column-reverse;align-items:center;gap:24px}
  .laptop{display:none}
  .stage{--pw:min(230px,62vw)}
  .phone{margin-bottom:0}
  /* 这里访客拿的就是手机，屏里的字得读得了：iOS 的尺子从 .255 放到 .27cqw，
     230 宽的手机上 17pt ≈ 10.6px，跟桌面 1440 那一档一样 */
  .phone{--pt:.27cqw}
  /* 手指用的宽度。链接撑到整条顶栏高之后还剩宽度这一头：中文的「安装」只有 28px
     宽，旁边 13px 就是下一条。左右各加 6px 的透明内边距把按压面做到 40，再用
     等量的负外边距把整栏拉回去 —— 最右那条链接的墨迹落点一个像素不动（这一栏
     是 margin-left:auto 靠右站的，右缘就是基准）。760 以下才加：桌面用鼠标，
     而 1440 那一档是冻着的。 */
  .nav .links{margin-right:-6px;gap:calc(clamp(13px,2vw,26px) - 12px)}
  .nav .links a{padding:0 6px}
  .nav .links a[data-opt]{display:none}
  .kv{flex-direction:column;gap:2px}
  .kv dt{flex:none}
  .rows li{flex-wrap:wrap;gap:8px 12px}
  .rows .meta{margin-left:0;width:100%}
  /* 窄屏上这句会换到第二行，margin-left:auto 让它孤零零吊在右边 */
  .foot .row .end{margin-left:0}
  .party .who em{margin-left:6px}
  .ledger code{font-size:13.5px;white-space:pre-wrap}
  .wireline{flex-direction:column;gap:12px;margin:20px 0}
  .wire{flex:none;width:100%;height:2px}
  .state{white-space:normal;text-align:left}
  /* 那两条线在宽屏上是两端之间的链路，细、短、夹着一句话；竖过来之后它们变成两道
     通栏的亮蓝横杠，成了这一屏最重的东西 —— 而蓝在这套系统里是「点我」的信号，
     它们不可点。窄屏上链路由上下两端的排列本身说明，横杠减掉。 */
  .wire{display:none}
  .wireline{gap:0;margin:18px 0}
  /* 收尾那两条命令原先在连字符处断开，`--format=csv` 被切成 `--` 和 `format=csv`，
     读起来像两个参数 —— 和 HANDOFF 3.5 在手机屏里修过的是同一个坑，同一个解法。 */
  .lesson code .tok{white-space:nowrap}
  /* 左对齐的中文段落在窄屏上不能用 balance：汉字可以在任意两字之间断开，于是 balance
     为了把几行拉齐会主动把行做短 —— 390 下实测一行只排到 13~14 字，而这一栏放得下 19 字，
     左对齐时右边就恒定空出一条一指宽的白，一栏看着像浮在另一栏里。
     居中的海报 lead（首屏、安全模型、安装）**不在此列**：那三处正是 balance 的本行，
     三行等长居中是它该有的样子，实测也确实比填满好看。宽屏一律不动。 */
  .split .lead:lang(zh),
  #relay .lead:lang(zh){text-wrap:pretty}
}

/* 窄屏放不下一行 51 字的指纹：断成两行等长的，看着是有意为之而不是被挤爆的 */
@media (max-width:499px){
  .party .fp{font-size:13.5px;max-width:26ch;margin:0 auto}
}

@media (max-width:560px){
  .panel .head .s{margin-left:0}
  .nav{gap:12px}
  .nav .links a[href="#decide"]{display:none}
  /* 安全是页内锚点，往下滚一屏就到；在这个宽度上它是这一栏里最先该让位的一条。 */
  .nav .links a[href="#private"]{display:none}
  .nav .brand{font-size:16px}

  /* ── 手机上的减法 ────────────────────────────────────────────────
     下面每一条都是「拿掉一件在 390px 上没有职务的东西」，不是加一套手机专用版式。 */

  /* 笔记本的退场已经挪到 ≤760（跟竖排同一个断点，理由记在那边）。
     中文的 lead 行高 1.6，比英文那三行高出 11px，正好把首屏顶到 850 —— 差 6px。
     底部内边距收 10px 补回来，两种语言都落在折线以内。 */
  .tile.hero{padding-bottom:42px}

  /* 这里曾经有一块把安装胶囊竖排的规则（#20 / HANDOFF 3.11）：命令摊开换行、
     复制键落到底下通栏、圆角 999 → 18。甲方看了真机之后要求撤回（issue #25）——
     换行之后那条命令不再是「一行装完」，而整页最重的蓝色大块落在了一个次级动作上。
     手机上照样用桌面那一份：单行、胶囊内横向滚、复制键在右。别再竖排回去。

     撤回之后要补的那个「还有」信号见下面 .cmdline.more —— 它现在不挂在这个
     断点上了。 */

  /* 两个分段控件各自居左居右都不是，是各自居中：实测左缘一个在 87 一个在 100，
     彼此错开 13px，也都不压内容锁（22）。窄屏改成两条通栏，共用同一对左右缘，
     顺便把两个选项做成一样宽的按压面。
     内边距 10 而不是 8：8 给出 36.6px（英文）高的按压面，而这两个键决定下面
     那条命令是哪一条，够不着就选错。10 给出 40.6，通栏的宽度本来就富余。 */
  .picker{flex-direction:column;align-items:stretch;gap:10px;margin-top:24px}
  .seg button{flex:1 1 0;padding:10px}
}

/* ── 顶栏的落链梯 ────────────────────────────────────────────────────
   一条横带装不下的时候，掉的永远是链接，语言开关一步都不让。窄下去的顺序是
   设备（760）→ 怎么工作、安全（560）→ GitHub（440）→ 文档（360），安装留到最后：
   访客来这个站就为那一件事。开关也从不掉：中文访客第一个要按的就是它。

   断点不是照着 iPhone 的分辨率排的，是照着「英文那一栏的自然宽度不超过视口的
   八成」排的 —— 留下的两成给访客自己的字号设置。甲方那台 412px 的安卓把每个字
   渲宽约 10%（实测：字标 76.4 → 84、GitHub 45.3 → 50），四条链接那一栏 388 胀到
   427，超出 412 十五个像素，被推出屏幕的正是最右边的开关（issue #25）。
   自然宽（英文，实测）：安全+安装+文档+GitHub 388、安装+文档+GitHub 322、
   安装+文档 266、安装 220 —— 每一档除以断点都落在 0.74 以下。 */
@media (max-width:440px){
  .nav .links a[href^="https://github.com"]{display:none}
}
@media (max-width:360px){
  .nav .links a[href="/docs/"]{display:none}
}

/* ── 中文 ────────────────────────────────────────────────────────────
   上面那套排版是给 Schibsted Grotesk 的拉丁字母调的，原样套到汉字上有三处不对：
     · 负字距是给大 x-height 的拉丁大字号调的，落到全宽方块的汉字上是把笔画互相压住；
     · 1.47 的行高对没有升降部、字面占满全宽的汉字太紧；
     · 一行里的汉字掉到系统 CJK 字体，而同一行的拉丁字母和数字仍留在 Schibsted，
       两者的 x-height 和笔画粗细都不同，于是每一句混排都像是拼起来的。
   lang 属性由 app.js 的 applyLang 翻（zh-CN / en），所以这一整块不需要 JS 配合。

   **每一条都挂在元素自己的 :lang(zh) 上，一条后代选择器都不写。**
   这份 CSS 有两个消费方，语言边界落的位置不一样：官网整页是 <html lang="zh-CN">，
   文档站是英文页里嵌一段 <article lang="zh-CN">（tools/docsite/build.py）。
   写成 `:lang(zh) body` 那种后代形式，在文档站上一条都不命中 —— body 的语言是 en ——
   于是中文文章拿到的是英文那套负字距和拼接字体。写成 `body:lang(zh)` 两处都命中。

   加新的负字距时：同时在下面补一条**多一个 :lang 伪类**的重置，
   靠权重取胜而不是靠源码顺序，否则 docs.css 后加载就会盖回去。 */
:root{
  /* 一行只有一种字体：系统 CJK 无衬线**连同它自己的那套拉丁**。
     macOS 走 -apple-system —— SF Pro 的拉丁配 PingFang 的汉字，苹果本来就是配套设计的；
     Windows 落到微软雅黑，Linux / Android 落到思源黑体。三条路各自都自带拉丁，
     所以「wanctl 0.3.4」这种混排里的字母和数字不会再是外来的。 */
  --sans-zh:-apple-system,BlinkMacSystemFont,'PingFang SC','Hiragino Sans GB',
            'Microsoft YaHei','Noto Sans CJK SC','Source Han Sans SC',sans-serif;
}

/* 字体和字距落在**声明了语言的那个元素**上，后代照常继承。
   为什么不能只换 --sans：body 上的 font-family:var(--sans) 是在 body 那里算完的，
   后代继承到的是算好的字体名单而不是 var() 引用，只改变量传不进英文页里的那段中文。
   为什么不写成命中每个中文元素的 :lang(zh){...}：那会把等宽块内部那些自己没有
   字体声明的 span（.tok）从等宽里拽出来 —— 声明永远强过继承。
   为什么这里不设行高：这条会命中侧栏里 lang="zh-CN" 的文章标题那类**行内**元素，
   给它们单独一个行高会让中英混排的列表行高不齐。行高只给块级正文，见下。 */
[lang]:lang(zh),
body:lang(zh){font-family:var(--sans-zh);letter-spacing:0}
/* body 单列一条：它自己声明了这三样，继承压不过它。 */
body:lang(zh){line-height:1.6}
/* 字标是品牌那一处例外：它只有拉丁，留在 Schibsted Grotesk 上，连同它的负字距。 */
.nav .brand:lang(zh){font-family:'Schibsted Grotesk',sans-serif}

/* 字距归零。逐条对上 app.css 里每一处负 letter-spacing，两处故意不在里面：
   .nav .brand（字标，见上）和 .door .addr（内容永远是 wanctl.z10.dev / docker compose up，
   等宽拉丁，那 -.2px 是给 JetBrains Mono 调的）。等宽的其余各处本来就是 0。
   h3~h6 官网没有，是给文档站备的：docs.css 故意把标题字距压到了 0,0,1
   （见那个文件里的说明），就等这几条来接管。 */
h1:lang(zh),
h2:lang(zh),
h3:lang(zh),
h4:lang(zh),
h5:lang(zh),
h6:lang(zh),
.tile.chapter h2:lang(zh),
.nav:lang(zh),
.ask .ttl:lang(zh),
.ask .name:lang(zh),
.ask .from:lang(zh),
.ask .card:lang(zh),
.ask .pend:lang(zh),
.ask button:lang(zh),
.ask .doneword:lang(zh),
.ask .idle:lang(zh),
.panel .head .t:lang(zh),
.rows .name:lang(zh),
.party .who:lang(zh),
.lesson .claim:lang(zh),
.seg button:lang(zh){letter-spacing:0}

/* 行高：正文 1.6（上面 body 那条给的），官网的展示级标题 1.2。
   文档站的标题不在此列 —— .dbody article h1 是 0,1,2，它的 1.12 该留着。
   等宽块（.term 1.75 / .ask .card .row 1.3 / .ask .fp 1.3）也不动，它们自己声明着。 */
h1:lang(zh),h2:lang(zh),.tile.chapter h2:lang(zh){line-height:1.2}
.lead:lang(zh),.note:lang(zh),.door .d:lang(zh){line-height:1.6}

/* 两扇门那两段收尾不留孤字（原先最后一行只剩一个「器。」）。
   实测 pretty 在 390 与 1440 上都不比默认换行差，所以它是白捡的。
   `.lead` 不在这里：它带着 text-wrap:balance，宽屏上 balance 更好，
   窄屏上反过来 —— 那条只在上面的窄屏媒体查询里翻。 */
.door .d:lang(zh){text-wrap:pretty}

/* 中文标题只在标点处换行。
   浏览器允许在任意两个汉字之间断开，于是「你不点头，什么都不会跑。」这一句实测三种
   换行模式全都难看：balance 断成「什 | 么」、pretty 断成「不 | 会」、默认换行留下孤字
   「跑。」。word-break:auto-phrase 在 zh-CN 上是**空操作**（实测断点一个都没动，
   Chrome 的分词换行目前只对日文生效），别加。
   办法是把每个分句包成 inline-block：盒子是原子的，断点只能落在分句之间；
   分句自己一行装不下时它照样会在内部换行，不会溢出。
   只有实测断得难看的标题才包 —— 目前是 #decide 和 #relay 两条。 */
.cl{display:inline-block}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:1ms!important;animation-iteration-count:1!important;
                       transition-duration:1ms!important}
}
