/* 官网首页样式（从 landing.html 内联 <style> 搬出，2026-09-24：让百度蜘蛛抓得完首页 HTML）。 */
:root {
  --bg: #ffffff;
  --bg-elevated: #f7f7f8;
  --ink: #0d0d0d;
  --ink-soft: #5d5d5d;
  --ink-muted: #7d7d7d;
  --line: rgba(13, 13, 13, 0.1);
  --line-strong: rgba(13, 13, 13, 0.18);
  /* 这组 token 描述的是 hero 里那张「App 截图」卡片的**浅色**内部，
     不是页面本身（页面底色见下面 body 的 --bm-ground）。所以强调色取亮面那支，
     与卡片里译文用的 #1d4ed8 同一个蓝。色值事实源 = brand.css */
  --accent: var(--bm-accent-on-light);
  --panel: #f7f7f8;
  --panel-border: rgba(13, 13, 13, 0.1);
  /* 顶栏高度的唯一事实源。hero 用负 margin 顶到导航底下、锚点用它躲开吸顶栏，
     而手机上导航会长出第二行标签条 —— 三处必须同步，所以只写这一个数。 */
  --nav-h: 64px;
}

* { box-sizing: border-box; }

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bm-ground);
  color: var(--ink);
  font-family: "Manrope", "Noto Sans SC", sans-serif;
  scroll-behavior: smooth;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  text-rendering: geometricPrecision;
  /* overflow-x: clip prevents horizontal bleed without creating a
     scroll context — preserves position: sticky on the nav, which
     overflow-x: hidden silently breaks. */
  overflow-x: clip;
}

/* No page scrollbar: this is a full-bleed marketing canvas and the OS bar
   cuts a strip out of the edge-to-edge video. Only the visual chrome is
   gone — wheel, trackpad, keyboard, and #anchor links all still scroll.
   Inner scrollers (.demo-text) are hidden too — see the rule down there. */
html,
body {
  scrollbar-width: none; /* Firefox + Chrome 121+ / Safari 18.2+ */
  -ms-overflow-style: none; /* legacy Edge */
}
html::-webkit-scrollbar,
body::-webkit-scrollbar {
  width: 0;
  height: 0;
}

.wrap {
  width: min(1152px, calc(100% - 64px));
  margin: 0 auto;
}

.nav {
  position: sticky;
  top: 0;
  z-index: 30;
  background: transparent;
  transition: background 0.25s ease, border-color 0.25s ease, backdrop-filter 0.25s ease;
  border-bottom: 1px solid transparent;
}

/* ── Invite banner (/invite.js) — rides on top of the sticky nav ── */
.bm-invite {
  background: rgba(var(--bm-accent-rgb), 0.14);
  border-bottom: 1px solid rgba(var(--bm-accent-rgb), 0.35);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
/* `.nav .wrap` is the nav's own 1fr/auto/1fr grid at nav height — the
   banner row lives inside <header class="nav"> too, so it must out-rank
   that rule (same specificity + own display/height) or it renders as a
   three-column grid with the × wrapped onto a second row. */
.nav .bm-invite-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  max-width: none;
  height: auto;
  margin: 0;
  padding: 10px 32px;
  font-size: 14px;
  line-height: 1.35;
  color: var(--bm-ink);
}
.bm-invite-gift { font-size: 18px; flex: none; }
.bm-invite-text { flex: 1 1 auto; min-width: 0; }
.nav .bm-invite-cta { flex: none; white-space: nowrap; min-height: 36px; padding: 8px 16px; font-size: 14px; }
.bm-invite-x {
  flex: none;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--bm-ink-soft);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.bm-invite-x:hover { background: var(--bm-surface); color: var(--bm-ink); }
@media (max-width: 640px) {
  .bm-invite-row { flex-wrap: wrap; row-gap: 8px; }
  .bm-invite-text { flex-basis: calc(100% - 30px - 28px - 24px); }
  .bm-invite-cta { margin-left: 30px; }
}

/* Codex-style scroll lock: after scrolling past the hero crown, nav
   turns solid dark with white text + a faint divider, then reverts
   to transparent the moment we scroll back near the top. */
.nav.scrolled {
  background: rgba(0, 0, 0, 0.86);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-bottom-color: rgba(255, 255, 255, 0.08);
}

.nav .wrap {
  width: 100%;
  max-width: none;
  height: var(--nav-h);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 20px;
  padding: 0 32px;
}

/* Nav lives over a vivid video — all text white with a soft shadow
   for legibility against any frame of the underlying liquid mix. */
.brand {
  color: #ffffff;
  font-size: 17px;
  font-weight: 650;
  line-height: 1;
  letter-spacing: 0;
  text-decoration: none;
  justify-self: start;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}

/* .brand links to the parent-company site (zcclabs.com, now live). */
a.brand { cursor: pointer; transition: opacity 0.18s ease; }
a.brand:hover { opacity: 0.78; }

.links {
  display: flex;
  align-items: center;
  justify-self: center;
  gap: 2px;
  color: rgba(255, 255, 255, 0.92);
  font-size: 14px;
  font-weight: 500;
  line-height: 23px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}

.links a {
  color: inherit;
  text-decoration: none;
  border-radius: 8px;
  padding: 6px 10px;
  transition: color 0.18s ease, background 0.18s ease;
  white-space: nowrap;
}

.links a:hover {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.08);
}

.nav-right {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-self: end;
}

.nav-contact,
.nav-login {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;
  padding: 6px 16px;
  border-radius: 999px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: opacity 0.18s ease, background 0.18s ease, border-color 0.18s ease;
}

.nav-contact {
  color: #ffffff;
  border: 1px solid rgba(255, 255, 255, 0.42);
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.nav-contact:hover {
  background: rgba(255, 255, 255, 0.18);
  border-color: rgba(255, 255, 255, 0.66);
}

/* White / ink in every nav state — over the aurora at the top of the
   home tab, scrolled past the hero, and on the Download / Pricing tabs
   (Zechang 2026-09-03; until then it was ink over the aurora and only
   flipped to white on scroll/tabs). Matches the secondary pages'
   nav-login in seo-common.css. */
.nav-login {
  color: var(--ink);
  border: 1px solid #ffffff;
  background: #ffffff;
}

.nav-login:hover { opacity: 0.82; }

.nav-ios { display: none; }

/* Aurora stage = wrapper around hero + demo-section that hosts ONE
   <video> covering the full vertical run. Codex's exact technique:
   single video stretched to fill the whole area, then content layers
   sit on top via z-index. No mask, no seam, no JS sync needed. */
.aurora-stage {
  position: relative;
  isolation: isolate;
  /* The video is scaled up 1.045 (overscan for the blur phase, see
     .aurora-video) — without a clip it bleeds ~27px past the stage and
     paints a bright sky band over the dark section below. `clip`, not
     `hidden`: hidden makes this a BFC, so .hero's -64px margin-top stops
     collapsing out and the hero's top 64px gets cut instead. Same reason
     the page root uses overflow-x: clip. */
  overflow: clip;
  background: var(--bm-aurora-fallback); /* fallback while video loads */
}

.aurora-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  pointer-events: none;
  /* Constant slight zoom: blur() samples past the video bounds and
     leaves a translucent edge fringe — the overscan keeps that fringe
     cropped out during the frosted phase. */
  transform: scale(1.045);
  filter: saturate(1.08) contrast(1.04) brightness(1) blur(0px);
  animation: aurora-focus-breathe 16s ease-in-out infinite;
}

/* Codex-style focus breathing, crisp-first: opens sharp, mid-cycle the
   liquid frosts over, then focuses back in. 0% and 100% share the
   crisp pose so the loop has no seam. All keyframes list the same
   four filter functions in the same order — mismatched lists
   interpolate discretely (snap). */
@keyframes aurora-focus-breathe {
  0%, 22% { filter: saturate(1.08) contrast(1.04) brightness(1) blur(0px); }
  38%, 56% { filter: saturate(1) contrast(1) brightness(1.03) blur(6px); }
  72%, 100% { filter: saturate(1.08) contrast(1.04) brightness(1) blur(0px); }
}

@media (prefers-reduced-motion: reduce) {
  .aurora-video { animation: none; }
}

/* ── 开场演示（2026-09-03，Zechang 定形）：不是盖住整页的浮层，而是首页顶栏下方的
     **第一段内容**——跟「下载」那类标签页一样顶栏照常在上面，只是页面内容不同。
     规则见 #tab-home 开头那块注释。高度 = 视口减顶栏，底 = 全站近黑 --bm-ground
     （Zechang 黑→白→黑→夜雾静图→黑一路试回来，2026-09-03），设备居中；手机看 iPhone 录屏、
     桌面看 macOS 录屏（macOS 素材未到位前桌面暂用 iPhone 那条）。
     --dev-w = 机身图的宽：手机端按段落高度反推（1470/3000 是机身图比例）、最多 84vw；
     桌面 423px（= 屏幕 380px，435 太大 315 太小，取中）。
     ⚠️ margin-bottom: var(--nav-h) 不是留白：.hero 的 margin-top 是 -nav-h（顶到透明
     导航底下用的），本段插在它前面后两者相邻折叠，正负相抵 = 0，hero 才不会被
     拽上来盖住本段底部。 ── */
.bm-opening {
  position: relative;
  height: calc(100vh - var(--nav-h));
  height: calc(100dvh - var(--nav-h));
  margin-bottom: var(--nav-h);
  background: var(--bm-ground);
  display: grid;
  grid-template-rows: 3.4em auto;
  row-gap: 14px;
  justify-items: center;
  align-content: center;
  overflow: hidden;
  overflow-anchor: none;
  /* 刻意没有任何 transition：本段不再有「收起」动画，它就是首屏那一屏静态内容
     （2026-09-20 起滚动完全归用户）。别把 height / padding / margin / opacity 的过渡
     加回来 —— 那是被否掉的两版自动收起留下的东西。 */
  /* 高度预算：视口 − 顶栏 − 字幕行（3.4em ≈ 2 行）− 行距 − 上下留白 */
  --dev-w: min(calc((100vh - var(--nav-h) - 150px) * 1470 / 3000), 84vw);
  --dev-w: min(calc((100dvh - var(--nav-h) - 150px) * 1470 / 3000), 84vw);
}
/* 两句字幕叠在同一格里，按视频时间轴（data-from / data-to，秒）轮流点亮：
   字幕流出那段先亮「专业名词一个不漏」（opening.terms），再亮「全球最快最准的翻译引擎」（opening.engine，09-13 起不再跟 Why 头格标题共用），Andy 出场换「被点名？Claude 一句话接住」。 */
.bm-opening-cap {
  grid-row: 1;
  grid-column: 1;
  align-self: end;
  margin: 0;
  padding: 0 20px;
  max-width: 720px;
  text-align: center;
  font-size: clamp(22px, 2.4vw, 32px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--bm-ink);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.45s ease, transform 0.45s ease;
  pointer-events: none;
}
.bm-opening-cap.is-on {
  opacity: 1;
  transform: none;
}
/* 中日韩默认逐字可断：桌面侧栏那条窄列里会断在词中间（实测「全球最快最准的翻 / 译软件」）。
   opening.terms / opening.engine 的中日韩文案把短语包进 .nb，只在短语之间换行。 */
.bm-opening-cap .nb { white-space: nowrap; }
/* 往下滑时越滑越糊（Zechang 2026-09-03 提，09-20 起成为本段与首屏之间唯一的衔接手段）：
   JS 按滑过本段的比例写 --bm-blur，滑完整段 = 14px。只糊手机和字幕，不糊整段——整段套
   filter 每帧重绘一屏太贵，手机会掉帧。 */
.bm-opening-stage,
.bm-opening-cap { filter: blur(var(--bm-blur, 0px)); }
.bm-opening-stage {
  grid-row: 2;
  grid-column: 1;
}
/* 机身 = Apple Design Resources 官方 iPhone 17 Pro Max 外框（Deep Blue；源 = 设计资源
   Bezel-iPhone-17.dmg 的 PNG 1470×3000，缩到 1000 宽、留 alpha → bezel-iphone-17-pro-max.webp
   33KB；许可写明用于展示跑在 Apple 平台上的软件界面，正是此用途）。换色 = 换这张图。
   开孔量自原图：屏幕 1320×2868 = 录屏像素一比一，落在 left 75 / top 66 →
   视频按百分比钉在开孔里、压在机身图下面，机身不透明部分盖住视频四角。
   之前试过 CSS 画的灰框（假）和不画框（没边界），都被否了（2026-09-03）。 */
.bm-opening-stage--phone {
  position: relative;
  width: var(--dev-w);
}
.bm-opening-bezel {
  position: relative;
  z-index: 2;
  display: block;
  width: 100%;
  height: auto;
  pointer-events: none;
}
.bm-opening-stage--phone .bm-opening-video {
  position: absolute;
  z-index: 1;
  left: 5.102%;
  top: 2.2%;
  width: 89.796%;
  height: 95.6%;
  border-radius: 14% / 6.5%;
}
/* macOS 录屏（1200×800 窗口 @2x）：窗口圆角 + 发丝边，宽度吃视口。 */
.bm-opening-stage--mac .bm-opening-video {
  width: min(86vw, calc((100vh - var(--nav-h) - 150px) * 3 / 2), 1200px);
  aspect-ratio: 3 / 2;
  border-radius: 12px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.12), 0 40px 100px rgba(0, 0, 0, 0.7);
}
.bm-opening-video {
  display: block;
  height: auto;
  background: #000;
}
/* 桌面（Zechang 2026-09-03 定形）：手机照旧放正中（跟手机端一样）、顶到段落上方、
   按「一屏内显示完整」反推大小（视口 − 顶栏 − 上下留白 = 机身高）；字幕不占栏，
   绝对定位挂在机身左侧、垂直居中于机身（右边贴机身留 56px，最宽 520px）。
   之前试过「字幕在上 + 手机居中」（900 高视口手机底部露不全）和「左右两栏」
   （手机被挤到右边），都被否了。段落仍至少一屏高、高度随内容走——收起不碰 height
   了（2026-09-20 起两端同走 transform），所以 auto 高度在这里没有任何顾虑。 */
@media (min-width: 769px) {
  .bm-opening {
    --dev-w: min(423px, calc((100vh - var(--nav-h) - 72px) * 1470 / 3000));
    --dev-w: min(423px, calc((100dvh - var(--nav-h) - 72px) * 1470 / 3000));
    height: auto;
    min-height: calc(100vh - var(--nav-h));
    min-height: calc(100dvh - var(--nav-h));
    padding: 28px 40px 44px;
    grid-template-rows: auto;
    grid-template-columns: 1fr;
    row-gap: 0;
    justify-items: center;
    align-content: start;
  }
  /* ⚠️ 包含块 = 网格格子而不是段落：网格里带显式格子位置的绝对定位子元素以那个格子为
     包含块。必须写成 1 / 2（两头都显式）——写 `grid-row: 1` 等于 `1 / auto`，auto 那头
     会延伸到容器 padding 边，格子就比手机那一行高 44px、宽 40px，居中全偏。两头钉死后
     top: 50% = 机身正中、right 里的 50% = 内容区正中，跟手机同一个中心。 */
  .bm-opening-cap {
    position: absolute;
    grid-row: 1 / 2;
    grid-column: 1 / 2;
    top: 50%;
    right: calc(50% + var(--dev-w) / 2 + 56px);
    width: min(520px, calc(50% - var(--dev-w) / 2 - 96px));
    transform: translateY(calc(-50% + 6px));
    padding: 0;
    max-width: none;
    text-align: left;
    font-size: clamp(24px, 3.2vw, 46px);
    line-height: 1.18;
  }
  .bm-opening-cap.is-on { transform: translateY(-50%); }
  .bm-opening-stage {
    grid-row: 1;
    grid-column: 1;
    align-self: start;
  }
}
@media (prefers-reduced-motion: reduce) { .bm-opening { display: none; } }

.hero {
  position: relative;
  z-index: 1;
  /* 顶到透明导航底下，再把内容压回导航下方 64px —— 两个数都从 --nav-h 推出来，
     手机上导航长高时不用再手工对第二个数。 */
  margin-top: calc(-1 * var(--nav-h));
  padding: calc(var(--nav-h) + 64px) 0 42px;
  min-height: 480px;
  text-align: center;
  background: transparent;
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Just a soft top vignette for nav text legibility — no bottom
     darken anymore (would create a visible band against the
     continuous video below). */
  background: linear-gradient(180deg, rgba(0, 14, 40, 0.18) 0%, rgba(0, 14, 40, 0) 24%);
}

.aurora-blob {
  position: absolute;
  border-radius: 50%;
  filter: blur(88px);
  opacity: 0.76;
  will-change: transform;
}

.aurora-blob--a {
  width: 720px;
  height: 720px;
  top: -260px;
  left: -170px;
  background: radial-gradient(circle, rgba(29, 78, 216, 0.88) 0%, rgba(62, 133, 235, 0.62) 42%, transparent 72%);
  animation: aurora-drift-a 13s ease-in-out infinite alternate;
}

.aurora-blob--b {
  width: 680px;
  height: 680px;
  top: -210px;
  right: -160px;
  background: radial-gradient(circle, rgba(247, 255, 255, 0.9) 0%, rgba(128, 224, 241, 0.56) 40%, transparent 72%);
  animation: aurora-drift-b 15s ease-in-out infinite alternate;
}

.aurora-blob--c {
  width: 820px;
  height: 820px;
  left: 50%;
  bottom: -470px;
  background: radial-gradient(circle, rgba(42, 112, 225, 0.62) 0%, rgba(114, 195, 235, 0.46) 46%, transparent 76%);
  animation: aurora-drift-c 18s ease-in-out infinite alternate;
}

.aurora-blob--d {
  width: 460px;
  height: 460px;
  top: 180px;
  left: 40%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.7) 0%, rgba(184, 234, 247, 0.42) 46%, transparent 76%);
  animation: aurora-drift-d 11s ease-in-out infinite alternate;
}

.aurora-blob--e {
  width: 560px;
  height: 560px;
  top: 120px;
  left: -120px;
  background: radial-gradient(circle, rgba(24, 55, 173, 0.5) 0%, rgba(44, 97, 210, 0.34) 44%, transparent 75%);
  mix-blend-mode: soft-light;
  animation: aurora-drift-e 17s ease-in-out infinite alternate;
}

@keyframes aurora-bg-flow {
  0% {
    background-position: 0% 0%, 100% 0%, 82% 72%, 48% 44%, 50% 50%;
    filter: saturate(1.16) contrast(1.03);
  }
  50% {
    background-position: 12% 8%, 86% 18%, 70% 62%, 56% 50%, 46% 54%;
    filter: saturate(1.24) contrast(1.05);
  }
  100% {
    background-position: 22% 14%, 78% 26%, 58% 56%, 42% 52%, 58% 46%;
    filter: saturate(1.18) contrast(1.04);
  }
}

@keyframes aurora-field-a {
  0% { transform: translate3d(-2%, -1%, 0) rotate(-4deg) scale(1.02); }
  100% { transform: translate3d(8%, 7%, 0) rotate(15deg) scale(1.12); }
}

@keyframes aurora-field-b {
  0% { transform: translate3d(3%, -2%, 0) rotate(5deg) scale(1); }
  100% { transform: translate3d(-8%, 7%, 0) rotate(-14deg) scale(1.14); }
}

@keyframes aurora-drift-a {
  0% { transform: translate(-30px, -12px) scale(1); }
  100% { transform: translate(190px, 126px) scale(1.16); }
}

@keyframes aurora-drift-b {
  0% { transform: translate(24px, -18px) scale(1.02); }
  100% { transform: translate(-210px, 150px) scale(1.18); }
}

@keyframes aurora-drift-c {
  0% { transform: translate(-54%, 24px) scale(1); }
  100% { transform: translate(-39%, -128px) scale(1.18); }
}

@keyframes aurora-drift-d {
  0% { transform: translate(-48px, -18px) scale(0.96); }
  100% { transform: translate(118px, 72px) scale(1.2); }
}

@keyframes aurora-drift-e {
  0% { transform: translate(-24px, 36px) scale(1.08); }
  100% { transform: translate(132px, -62px) scale(0.94); }
}

.hero-content {
  position: relative;
  z-index: 1;
  /* 与开场段反着走的模糊（Zechang 2026-09-21「主页那里也是模糊然后慢慢变清晰」）：
     开场那段 JS 把 (1 − p) × 14px 写在 :root 的 --bm-hero-blur 上（p = 滑过开场段的比例），
     滑到一半两边各 7px、滑到底开场 14px / 这里 0px——任何时刻两边加起来恒为 14px，
     焦点从手机交给首屏。只糊图标 + 标题 + 三颗胶囊这一小块，不糊 .hero 整段：天空本来
     就是云层视频 + 88px 光晕，再糊看不出差别，而整段套 filter 每帧重绘一屏、手机掉帧
     （与开场「只糊手机和字幕」同一条纪律）。开场被撤 / 减少动效下隐藏 / 变量没写 →
     回落 0px = 原样。 */
  filter: blur(var(--bm-hero-blur, 0px));
}

.hero-icon {
  display: block;
  width: 92px;
  height: 92px;
  margin: 0 auto 38px;
  border-radius: 20px;
  animation: hero-icon-breathe 7s ease-in-out infinite;
}

@keyframes hero-icon-breathe {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

.hero .hero-title {
  margin: 0;
  color: #ffffff;
  font-family: "Manrope", "Noto Sans SC", sans-serif;
  font-size: 54px;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.28);
}

.hero-tagline {
  display: block;
  margin: 22px auto 0;
  width: min(720px, 100%);
  color: rgba(255, 255, 255, 0.92);
  font-size: 22px;
  font-weight: 520;
  line-height: 1.25;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.25);
}

.hero-cta {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 30px;
}

.cta-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  gap: 8px;
  padding: 11px 22px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  background: var(--ink);
  color: #ffffff;
  font-family: inherit;
  font-size: 14px;
  font-weight: 650;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.cta-pill:hover {
  opacity: 0.86;
  transform: translateY(-1px);
}

.cta-pill svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

/* Hero pills carry a white platform glyph left of the label (Apple /
   phone / Windows, Zechang 2026-09-03). Same Simple Icons Apple path as
   the download tab's Mac button; the phone is hand-drawn (shell with
   screen + earpiece cut out). fill: currentColor = white on the ink pill. */
.hero-cta .cta-pill .cta-ico {
  width: 16px;
  height: 16px;
  fill: currentColor;
}


.demo-section {
  position: relative;
  z-index: 1;
  padding: 52px 0 78px;
  /* Minimal layout: video stays blue through the card + the invisible
     spacer below it (background reads big), fading to dark to meet the
     tagline-banner. ONE color (the banner's #020409 = rgb(2,4,9)) with
     only alpha ramping — no hue shift, no abrupt stop — so the fade is a
     single smooth curve with no visible seam line. The old 82%→93%→100%
     stops put a hard onset edge over the bright-blue video (looked like
     the dark band was "split twice"). Many eased stops ≈ smoothstep. */
  background: linear-gradient(180deg,
    rgba(var(--bm-ground-rgb), 0) 0%,
    rgba(var(--bm-ground-rgb), 0) 44%,
    rgba(var(--bm-ground-rgb), 0.04) 56%,
    rgba(var(--bm-ground-rgb), 0.12) 66%,
    rgba(var(--bm-ground-rgb), 0.26) 75%,
    rgba(var(--bm-ground-rgb), 0.46) 83%,
    rgba(var(--bm-ground-rgb), 0.68) 90%,
    rgba(var(--bm-ground-rgb), 0.86) 95%,
    rgba(var(--bm-ground-rgb), 0.96) 98%,
    rgba(var(--bm-ground-rgb), 1) 100%);
}

/* --- Minimal-layout toggle ---
   Hide the trust strip + demo heading; the invisible .demo-bg-spacer
   below the card keeps the aurora background big without visible content.
   Delete these two `display:none` rules to bring the filled composition
   (flags + "Try it now") back — the markup/CSS/i18n all still exist.
   The powered-by logo row that used to sit in this hero slot was removed
   2026-09-14 (Zechang), so the slot under the CTAs is free again. */
.demo-section-title { display: none; }
.demo-bg-spacer {
  /* Invisible block — adds height to the aurora-stage so the video covers
     a bigger area. Tune the height to grow/shrink the background. */
  height: 260px;
}

.demo-section .wrap {
  position: relative;
  z-index: 1;
}

/* Big positioning headline that sits right under the demo widget,
   introducing the showcase stories below (Codex's "更高效完成工作"
   equivalent). Solid dark base, generous vertical breathing room. */
.tagline-banner {
  background: var(--bm-ground);
  padding: 120px 0 100px;
  text-align: center;
}
.tagline-banner-title {
  margin: 0;
  font-family: "Manrope", "Noto Sans SC", sans-serif;
  font-size: clamp(40px, 6vw, 76px);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: #ffffff;
}

.demo-section-title {
  margin: 0 0 12px;
  color: #f8fafc;
  text-align: center;
  font-family: "Manrope", "Noto Sans SC", sans-serif;
  font-size: 28px;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.12;
}

.demo-section-sub {
  margin: 0 0 34px;
  color: rgba(248, 250, 252, 0.66);
  text-align: center;
  font-size: 16px;
  line-height: 1.5;
}

/* Single outer white card carries the visual weight. Everything
   inside is borderless — the user wanted "no lines anywhere", so the
   nested .mock just becomes a transparent layout container. */
.hero-media {
  width: min(760px, 100%);
  margin: 0 auto;
  /* Trimmed since the prompt copy below the mic was removed — keep a
     bit of room under the button so the card doesn't feel cramped,
     but no more empty floor than necessary. */
  padding: 20px 18px 40px;
  border-radius: 24px;
  /* Liquid glass: a translucent frosted panel over the dark hero base.
     Kept light-dominant so the dark text + grey controls inside stay
     readable, but clearly see-through — the blur + saturate pull the
     ink/video behind into a soft frost, and the bright top edge +
     inset highlight read as light catching real glass. */
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.66) 100%);
  -webkit-backdrop-filter: blur(30px) saturate(180%);
  backdrop-filter: blur(30px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow:
    0 18px 44px -18px rgba(6, 20, 55, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 -1px 2px rgba(255, 255, 255, 0.18);
}

.mock {
  background: transparent;
  text-align: left;
}

.mock-head {
  display: flex;
  justify-content: center;
  gap: 12px;
  padding: 6px 4px 18px;
  color: var(--ink-soft);
  font-size: 12px;
  line-height: 1.4;
}
/* Each demo-head state (#demo-head-idle / #demo-head-rec / #demo-head-done)
   should fill mock-head so its inner row centers against the full card
   width, not just against its content width. */
.mock-head > div { flex: 1; animation: demo-state-in 0.34s cubic-bezier(0.22, 1, 0.36, 1) both; }

.mock-body {
  display: grid;
  gap: 10px;
  padding: 0 4px;
}

.mock-chip {
  padding: 11px 13px;
  border-radius: 10px;
  background: #f7f7f8;
  color: var(--ink);
  font-size: 14px;
  line-height: 1.5;
}

/* Interactive demo */
#demo-interactive { display: none; }

/* ── Language pill — the app's "Auto-detect → X" chain capsule as a
   Liquid Glass capsule (surface from mobile.css .bm-glass light-theme
   tokens, wording from DesktopLangPicker), sitting at the TOP-RIGHT of
   the demo card (left → right 2026-09-01 per Zechang; a 2026-07-20
   top-right try was called back, this reverses that). Source (fixed
   auto-detect) + arrow + target + chevron; the transparent <select>
   stretched over the pill pops the native dropdown for the TARGET. ── */
#demo-head-idle {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.demo-lang-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  /* Same size spec as the app's .desktop-lang-btn (34px / 0 14px) so
     the pill reads as a quiet control, not a rival to the mic CTA. */
  height: 34px;
  padding: 0 14px;
  border-radius: 999px;
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.5);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  backdrop-filter: blur(16px) saturate(180%);
  border: 0.5px solid rgba(255, 255, 255, 0.5);
  box-shadow:
    0 2px 8px rgba(17, 24, 39, 0.06),
    0 1px 2px rgba(17, 24, 39, 0.05),
    inset 0 1px 1px rgba(255, 255, 255, 0.9);
}
/* Bright refractive rim: white specular glints concentrated at the
   top-left / bottom-right corners, masked to a 2px edge ring. */
.demo-lang-pill::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 2px;
  background: linear-gradient(135deg,
    rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 20%,
    rgba(255, 255, 255, 0) 80%, rgba(255, 255, 255, 1) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.demo-lang-pill:focus-within {
  box-shadow:
    0 0 0 2px rgba(37, 99, 235, 0.4),
    0 2px 8px rgba(17, 24, 39, 0.06),
    inset 0 1px 1px rgba(255, 255, 255, 0.9);
}
/* Recording: language frozen for the session — dimmed pill + lock,
   exactly the app's locked state. */
.demo-lang-pill.is-locked { opacity: 0.55; cursor: default; }
.demo-lang-pill-chevron,
.demo-lang-pill-lock { flex-shrink: 0; }
.demo-lang-pill-lock { opacity: 0.55; }
/* Chain face "Auto-detect → X", three pieces like the app's Web pill
   (workspace-overrides.css .desktop-lang-btn-chain): target at full
   weight is the lead, the fixed source steps back to 500 / 62%, the
   svg arrow one step further. Opacity not colours, so the locked
   (recording) pill inherits the same hierarchy. */
.demo-lang-pill-text {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 260px;
  overflow: hidden;
  white-space: nowrap;
}
.demo-lang-pill-src { font-weight: 500; opacity: 0.62; }
.demo-lang-pill-arrow { flex: none; opacity: 0.4; }
.demo-lang-pill.is-locked .demo-lang-pill-src { opacity: 0.78; }
.demo-lang-pill-select {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  border: none;
  opacity: 0;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  cursor: pointer;
}
.demo-lang-pill-select option { background: #ffffff; color: var(--ink); }
.demo-rec-meta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.demo-state {
  text-align: center;
  /* Each state (idle / loading / recording / done) fades + slides up when
     it's shown — the JS toggles display, which restarts this animation,
     so transitions feel smooth instead of snapping in abruptly. */
  animation: demo-state-in 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
}
/* ── Height reserve: the card opens at the size it will reach when the
   FIRST original line lands (measured: rec head + one-line transcript
   + stop controls = 210px card at desktop), so starting a recording
   doesn't pump the card height — idle → loading → empty recording →
   first line all render at one constant height, and the card grows
   for the first time only when the translation column appears.
   min-heights are per-state because only the recording state adds the
   stop-controls row below the body. ── */
#demo-idle,
#demo-loading,
#demo-error {
  min-height: 152px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
/* Optical centering inside the reserve. The mic rides 60px of top
   margin (= +30px visual center shift, margin participates in flex
   centering at half value) — Zechang tuned it well below true center
   (10px → 30px over two rounds). Margin, NOT transform: the
   hover/active scale owns the transform slot. */
#demo-idle .demo-mic-btn { margin: 60px 0 0; }
#demo-loading .demo-loader-svg { margin: 0 auto; }
#demo-rec { min-height: 60px; }
@keyframes demo-state-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.demo-mic-btn {
  position: relative;
  /* Trap the bloom pseudo (z-index: -1) inside this button so it sits
     behind the button's bg without leaking below ancestor layers. */
  isolation: isolate;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  border: none;
  /* Copied 1:1 from the macOS dark-mode home start button
     (.ln-idle-hero-start): flat brand blue + soft blue drop shadow +
     a breathing bloom. macOS uses #60a5fa in dark, #1d4ed8 in light;
     this demo sits on the dark hero, so it takes the dark value. */
  background: var(--bm-accent);
  color: #ffffff;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.12s ease, filter 0.12s ease, box-shadow 0.12s ease;
  margin: 18px 0 10px;
  box-shadow: 0 4px 16px rgba(37, 99, 235, 0.35);
}
/* macOS's bloom (.ln-rec-bloom) is a box-shadow spread, which renders as
   a SQUARE on Windows Chrome for a border-radius:50% box (long-standing
   compositor bug — Mac Chrome / Safari respect the radius). So the same
   glow is a blurred pseudo-element here: round on every browser,
   breathing in intensity to invite a tap. */
.demo-mic-btn::before {
  content: "";
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  background: rgba(96, 165, 250, 0.5);
  filter: blur(13px);
  z-index: -1;
  pointer-events: none;
  animation: demo-bloom 2.6s ease-in-out infinite;
}
/* 图标必须显式站到光晕上面。光晕是 z-index:-1 + blur + 无限动画的伪元素，
   移动端 WebKit 会因为那个动画把它提升成合成层，而没有自己图层的 SVG 就被压到
   底下 —— 按钮看着还在（蓝圆 + 呼吸光晕），中间那只白麦克风却没了。给图标一个
   正的 z-index，绘制层序在所有引擎里都不再有歧义。 */
.demo-mic-btn svg { position: relative; z-index: 1; }
.demo-mic-btn:hover { filter: brightness(1.08); transform: scale(1.05); }
.demo-mic-btn:active { transform: scale(0.93); box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3); }
@keyframes demo-bloom {
  0%, 100% { opacity: 0.42; transform: scale(0.9); }
  50%      { opacity: 0.85; transform: scale(1.08); }
}

.demo-prompt { color: var(--ink-soft); font-size: 15px; margin: 0; }

/* Loading state — BufferMind sun-rays SVG spins while the engine
   warms up; the existing opacity-graded rays create a chase trail
   out of the box, no extra strokes needed. */
.demo-loader-svg {
  display: block;
  /* Quiet in-between state, not a hero element — sized well below the
     mic CTA it replaces so the swap reads as a step forward.
     44px (was 36) compensates the real logo-mark geometry being smaller
     within the 96 viewBox (rays reach r28.8 vs the old loader's r36) —
     on-screen size is unchanged. */
  width: 44px;
  height: 44px;
  margin: 14px auto 8px;
  animation: demo-loader-spin 1.1s linear infinite;
}
@keyframes demo-loader-spin {
  to { transform: rotate(360deg); }
}

.demo-rec-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  background: var(--bm-danger);
  border-radius: 50%;
  margin-right: 4px;
  animation: demo-dot-pulse 1s ease-in-out infinite;
  vertical-align: middle;
}
@keyframes demo-dot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

/* Mobile-parity caption emphasis (mobile.css live feed): original bold
   black, translation bold brand-blue — unboxed text, no colored side
   borders. #1d4ed8 is the app's light-theme translation blue (mirrors
   the brand logo pair) — keep the hex identical to mobile.css. */
.demo-transcript-area {
  text-align: left;
  margin-bottom: 12px;
}
/* Hidden until it has text (toggled by JS) so the recording card starts
   compact and grows on demand as the transcript/translation stream in. */
.demo-transcript-area.is-empty { display: none; }
.demo-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin-bottom: 4px;
}
.demo-text {
  font-size: 15px;
  font-weight: 700;
  line-height: 1.6;
  color: var(--ink);
  max-height: 120px;
  overflow-y: auto;
  scrollbar-width: none; /* Firefox — non-inherited, must sit on the scroller */
  scroll-behavior: smooth;
  min-height: 28px;
  word-break: break-word;
}
.demo-translation-area .demo-text { color: var(--bm-accent-on-light); }
/* No scrollbar (2026-08-24 Zechang) — matches the app, where scrollbars
   are off everywhere (src/styles/auth.css). This page is standalone: it
   inlines all its CSS and never loads the app bundle, so the global hide
   doesn't reach here and the rule has to be repeated. Scrolling itself is
   untouched — the transcript still auto-scrolls as text streams in. */
.demo-text::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* Typewriter reveal (ported from the app's TypewriterTranslation): each
   newly revealed translation char fades in once. Opacity only — any
   transform would shift glyph layout mid-line. */
.demo-tw-char { animation: demo-tw-char-in 110ms ease-out both; }
@keyframes demo-tw-char-in {
  from { opacity: 0.15; }
  to { opacity: 1; }
}

/* Top padding sets how far the stop button sits below the transcript —
   only the recording state shows this block, so it's the safe knob for
   "button lower in the card" without touching the idle mic state. */
#demo-controls { padding: 60px 18px 12px; text-align: center; }
/* Circular red stop button — outer circle, inner white square (the
   classic "stop recording" icon, mirrors the mic button's geometry
   so the two actions share the same visual weight). */
.demo-stop-btn {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--bm-danger);
  color: #ffffff;
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: transform 0.15s, opacity 0.15s, box-shadow 0.15s;
  box-shadow: 0 6px 16px -4px rgba(239, 68, 68, 0.55);
}
.demo-stop-btn:hover { transform: scale(1.06); opacity: 0.95; }
.demo-stop-btn svg { width: 24px; height: 24px; display: block; }

.demo-cta-card {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 20px;
}
/* "Try again" now sits inline beside the "Try now" pill, vertically
   centred with it — drop the block/auto-margin from the shared rule. */
.demo-cta-card .demo-retry-link { margin: 0; display: inline-flex; }
.demo-cta-card p { color: var(--ink-soft); font-size: 14px; margin: 0 0 12px; }
.demo-cta-card .cta-pill {
  display: inline-flex;
  font-size: 14px;
  padding: 10px 24px;
}
.demo-retry-link {
  background: none;
  border: none;
  color: var(--accent);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  margin-top: 10px;
  display: block;
  margin-left: auto;
  margin-right: auto;
}
.demo-retry-link:hover { text-decoration: underline; }

/* In the done state, retry link parks at the RIGHT EDGE of the CTA
   row, vertically centred with the "立即试用" pill (2026-09-01
   Zechang: 从面板右下角往上提, 与立即试用对齐) — the centered CTA
   stays the visual anchor while "再试一次" stays an unobtrusive
   secondary action. Absolute keeps the pill dead-centre (an in-flow
   sibling would shove it left). */
#demo-done .demo-cta-card { position: relative; }
#demo-done #demo-retry {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  margin: 0;
  display: inline-block;
}
.demo-error-text { color: var(--bm-danger-on-light); font-size: 14px; text-align: center; margin: 24px 0; }

#demo-head-rec, #demo-head-done {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
/* Keep the pill anchored top-right across idle → recording (idle head
   is flex-end): visually swap sides so the locked pill stays put and
   the rec dot + timer take the left; DOM keeps pill first. */
#demo-head-rec { flex-direction: row-reverse; }

@media (max-width: 600px) {
  .demo-mic-btn { width: 52px; height: 52px; }
  .demo-text { max-height: 100px; font-size: 14px; }
  .demo-lang-pill { height: 30px; font-size: 13px; padding: 0 11px; }
  /* Height reserve, remeasured at phone widths (231px card). */
  #demo-idle, #demo-loading, #demo-error { min-height: 149px; }
  #demo-rec { min-height: 57px; }
}

/* ── Section (Summary / PiP / Call / Trust+FAQ) ── */
.section {
  padding: 84px 0;
}
.showcase-media {
  margin: 44px auto 0;
  width: min(1040px, 96%);
  border-radius: 26px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: var(--bm-ground-raised);
  box-shadow: 0 30px 90px -18px rgba(0, 0, 0, 0.55);
}
.showcase-media img, .showcase-media video { display: block; width: 100%; height: auto; }
.showcase-placeholder {
  aspect-ratio: 16 / 10;
  display: grid;
  place-items: center;
  color: rgba(248, 250, 252, 0.62);
  font-size: 14px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: repeating-linear-gradient(45deg, rgba(255,255,255,0.03) 0 12px, rgba(255,255,255,0.07) 12px 24px);
}
@media (min-width: 981px) {
  .showcase-split {
    display: grid;
    grid-template-columns: minmax(0, 0.7fr) minmax(0, 2.1fr);
    gap: 48px;
    align-items: center;
  }
  .showcase-split.reverse {
    grid-template-columns: minmax(0, 2.1fr) minmax(0, 0.7fr);
  }
  .showcase-split .showcase-text h2 {
    text-align: left;
    font-size: 28px;
    line-height: 1.2;
  }
  .showcase-split .showcase-text .sub {
    text-align: left;
    width: 100%;
    margin-left: 0;
    margin-right: 0;
    font-size: 16px;
    line-height: 1.55;
  }
  .showcase-split .showcase-media { margin: 0; width: 100%; }
}

/* ── Why BufferMind — tech-trust rows (translation engine / AI models).
   Reuses the showcase-split skeleton (narrow text left, wide media
   right). Since 2026-09-20 (Zechang「把这些背景全部去掉，只留下一个手机
   在那里，背景就是黑的」) the media side is the bare phone: no card, no
   aurora backdrop, no bleed crop — the official iPhone 17 Pro Max body
   stands alone on the page ground (--bm-ground), the same treatment as
   the download tab's .dl-card-phone. Screenshots = the ASC 黑底 set
   cropped to the bare phone slab with transparent corners (since
   2026-09-05, Zechang「和 ASC 新版截图那样」), pinned into the body's
   opening with the same geometry as the opening demo
   (.bm-opening-stage--phone): screen = 89.796% of the body width, inset
   5.102% from its left and 2.2% of the body height from top AND bottom.
   Layer order is DOM order: slab → bottom overlay (.shot-buttons) → body
   last, so the body's anti-aliased opening edge covers everything. The
   body is the only in-flow child, so the box IS the phone: every
   percentage below is relative to the body, and the old card's
   aspect-ratio ↔ screen-top coupling is gone. ── */
/* Nav "Intro" lands here (2026-09-01, was the demo card); the scroll
   margin keeps the headline clear of the sticky nav (桌面 = 24px；手机上
   导航多一行标签条，避让量跟着 --nav-h 一起长)。 */
#why { padding: 0; scroll-margin-top: calc(var(--nav-h) - 40px); } /* 纯锚点，不占高 */
/* FAQ is an h3 inside the trust section with no padding of its own —
   without this the anchor lands under the sticky nav. */
#faq { scroll-margin-top: calc(var(--nav-h) + 24px); }
.why-row { padding: 72px 0 0; }
/* 左右交替（2026-09-20，Zechang「交叉交叉地进场」）：第 2 / 4 / 6 行挂 .reverse。
   栅格列宽由上面 .showcase-split.reverse 对调；DOM 顺序不动（字先图后——
   手机上下叠的顺序、读屏顺序都靠它），桌面只用 order 把文字栏换到右列，
   写法与下载页 .dl-row.reverse 同款。
   进场方向跟着机身所在那一侧走：--why-slide 在下面 .why-reveal 里消费。
   ⚠️ 用变量、别再写一条 transform：多一条 (0,4,0) 的选择器会压过 .in-view
   那两条 (0,3,0)，机身就永远停在偏移位上。变量不分断点——手机上下叠时
   机身也一左一右交替滑入。 */
.why-row.reverse { --why-slide: -64px; }
@media (min-width: 981px) {
  .why-row.reverse > .showcase-text { order: 2; }
}
/* 两个类叠写 (0,2,0) 是有意的：≥981 那条 `.showcase-split .showcase-media
   { margin: 0; width: 100% }` 同分，靠书写顺序压过它——只写 .why-shot-card
   (0,1,0) 会被撑回整列宽。margin 只动左右（auto = 在栅格列里居中），上边距
   沿用 .showcase-media 的：手机上下叠时 44px，桌面并排时 0。
   机身宽度沿用卡片时代的数（367.5 / 445.45px 封顶），阴影仍挂在机身图上。 */
.showcase-media.why-shot-card {
  position: relative;
  width: min(49%, 367.5px);
  margin-left: auto;
  margin-right: auto;
  overflow: visible;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}
/* 头格 #why-engine 机身放大一档，跟下面五格拉开层级。 */
.showcase-media.why-shot-card--hero { width: min(60.137%, 445.45px); }
@media (max-width: 980px) {
  /* 上下叠时机身按栏宽走：没有卡片衬着，49% 的机身在 iPhone 上只有 170px。 */
  .showcase-media.why-shot-card { width: min(68%, 340px); }
  .showcase-media.why-shot-card--hero { width: min(78%, 400px); }
}
/* 屏幕贴图钉进开孔——数字与 .bm-opening-stage--phone / .dl-card-phone 同源，
   别单改一处。机身图在下面另设 position: relative 撑出盒子高度。 */
.why-shot-card img,
.why-shot-card .why-shot-video {
  position: absolute;
  left: 5.102%;
  top: 2.2%;
  width: 89.796%;
  height: auto;
  display: block;
}
/* ── 头格 #why-engine：全站唯一一格会动的。真机录屏 9.1s（与顶部开场
     同一条 take1：源片 12.0→25.70s 按 1.5 倍速），按下录音钮 → bar 展开 →
     第一段原文流出 → 译文跟上 → 第二段「Now, what happens…」也写满，停在
     Andy 面板滑进来之前那一帧（两段原文 + 译文全满）。2026-09-03 之前只剪到
     第一段写完就停，Zechang 要「整个两段播完」才改的——别再剪回一段。
     规格是量苹果产品页量出来的：滚到才播、不循环、preload=none，同一时刻
     页面上只有这一格在动。机身放大一档（上面 --hero 那条），跟下面五格拉开层级。 ── */
/* ⚠️ 选择器必须带 .why-shot-card：上面 .showcase-media video 那条
   (0,1,1) 会盖过裸 .why-shot-video (0,1,0) 的 width，把机身撑成满宽。 */
.why-shot-card .why-shot-video {
  background: #000;
  /* 视频没有 alpha 通道，机身圆角只能画在元素上。why-live.webp 的 alpha
     量出来圆角 = 71px @760 宽 → 横 71/760 = 9.34%、纵 71/1651 = 4.30%，
     两个方向算回来是同一个半径，所以随尺寸缩放永远是正圆角，跟另外五格
     烘进 alpha 的机身圆角对得上。 */
  border-radius: 9.34% / 4.30%;
  /* 阴影挂在机身图上（drop-shadow 吃机身 alpha），视频本身不再带 box-shadow */
}

/* ⚠️ 这条不能省：上面 `.why-shot-card img` 那条 display:block 的特异性
   (0,1,1) 压过 UA 样式表的 `[hidden]{display:none}`，于是 JS 给降级静图挂的
   hidden **完全不生效**——静图照样 absolute 定位、按 DOM 顺序压在 video
   上面，页面看着跟没改一样（实测踩到过，视频在播、但你看到的是那张图）。
   (0,2,0) 才盖得住。 */
.why-shot-card [hidden] { display: none; }

/* Hold-to-talk (why-chat) is still two layers — why-chat.webp = the slab
   with the buttons removed and plain white below the bubbles (760×1651),
   why-chat-buttons.webp = just the two buttons on white (748×195: 6px
   inset each side + feathered sides so it sits invisibly on the slab's
   white; tall enough to carry the capsules' full drop shadow). The card
   used to need this because the visible phone height varied with card
   width; now the whole phone shows, the strip simply sits on the
   screen's bottom edge (its bottom row = the slab's bottom row), so
   the composition equals the original screenshot. The two numbers
   are derived from the slab: left = 5.102% + 6px × (89.796% / 760),
   width = 748/760 × 89.796%. (Headphone-broadcast row needs no overlay
   any more: its sheet is baked into why-broadcast.webp.) */
.why-shot-card .shot-buttons {
  top: auto;
  bottom: 2.2%;
  left: 5.811%;
  width: 88.378%;
}
/* why-chat.webp 是重做过的底图（抠掉按钮、下面补白），补白时四角没留透明——
   纯白直角；按钮层也是白底直角。机身图在屏幕圆角和内圈之间那块楔形是透明的，
   其余五格底图的透明圆角（量过 71px@760）让它露出页面黑底、和机身同色；这格
   整机露出后白直角就从那块楔形里冒出来（2026-09-20 Zechang 截图指出）。
   不动素材，CSS 切回同心圆角：底图半径 = 上面视频那条同一个 71px（9.34%×760
   = 4.30%×1651 = 71，正圆）；按钮层往里偏了 6px，底部两角用 65px（8.69%×748
   = 33.3%×195）正好内切于底图那条弧，上面两角落在白底中间不用切。 */
#why-chat .why-shot-card img:first-child { border-radius: 9.34% / 4.30%; }
#why-chat .why-shot-card .shot-buttons { border-radius: 0 0 8.69% 8.69% / 0 0 33.3% 33.3%; }
/* The phone body — the only in-flow child, so it gives the box its
   height (position: relative; left/top reset because the img rule above
   sets 5.102%/2.2%, which would shift a relative box too). Last child
   in the DOM + z-index 1 → paints over slab and overlay; its alpha
   shapes the drop-shadow (near-invisible on the black ground, kept for
   the light-ground print/screenshot case). */
.why-shot-card .why-shot-bezel {
  position: relative;
  left: 0;
  top: 0;
  width: 100%;
  z-index: 1;
  pointer-events: none;
  filter: drop-shadow(0 26px 50px rgba(0, 0, 0, 0.55));
}
/* Scroll reveal（一次性）：机身从自己所在那一侧滑入（奇数行右、偶数行左，
   见 .why-row.reverse 的 --why-slide）；文字（.text-reveal 的每个子元素）
   照首开欢迎页那套由糊到清、微微上浮，子元素依次晚 90ms。桌面 Why 行
   文字和卡片并排 → JS 观察整行、卡片再晚 140ms，顺序永远「先读字再看图」；
   手机上下叠 → 文字栏、卡片各自进视口各自触发。
   隐藏态只挂在 JS 标记过的元素上——没有 JS、或用户开了减弱动态效果时
   不 arm，内容始终常显，绝不出现"永远看不见"。 */
.why-shot-card.why-reveal {
  opacity: 0;
  transform: translateX(var(--why-slide, 64px));
  transition:
    transform 0.7s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.7s ease;
}
.why-shot-card.why-reveal.in-view,
.in-view > .why-shot-card.why-reveal {
  opacity: 1;
  transform: none;
}
.in-view > .why-shot-card.why-reveal { transition-delay: 0.14s; }
.text-reveal > * {
  opacity: 0;
  transform: translateY(18px);
  filter: blur(10px);
  transition:
    opacity 0.8s ease,
    transform 0.9s cubic-bezier(0.22, 1, 0.36, 1),
    filter 0.8s ease;
}
.text-reveal.in-view > *,
.in-view > .text-reveal > * {
  opacity: 1;
  transform: none;
  filter: none;
}
.text-reveal > :nth-child(2) { transition-delay: 0.09s; }
.text-reveal > :nth-child(3) { transition-delay: 0.18s; }
.text-reveal > :nth-child(4) { transition-delay: 0.27s; }
.section h2 {
  margin: 0;
  text-align: center;
  font-family: "Manrope", "Noto Sans SC", sans-serif;
  font-size: 54px;
  line-height: 1.08;
  letter-spacing: 0;
  color: #f8fafc;
  font-weight: 560;
  /* CJK by default breaks at any character; keep-all makes it
     behave like Western text and only break at explicit spaces or
     bullets — keeps "免费 / 全平台" as atomic units instead
     of splitting "全平 / 台". */
  word-break: keep-all;
  overflow-wrap: anywhere;
}
/* Keep a phrase together on its own line (used in English titles where
   a long word like "window" would otherwise drop to the next row in
   the narrow showcase-text column). */
.section h2 .nb { white-space: nowrap; }
.section .sub {
  margin: 14px auto 0;
  text-align: center;
  color: rgba(248, 250, 252, 0.66);
  width: min(760px, 90%);
  font-size: 20px;
  line-height: 1.45;
}
.section h2.security-headline {
  font-size: 38px;
  line-height: 1.25;
  width: min(820px, 90%);
  margin: 0 auto;
}

.grid-2, .grid-3, .grid-4 { margin-top: 36px; display: grid; gap: 16px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.panel {
  border-radius: 22px;
  background: var(--bg-elevated);
  border: 1px solid var(--line);
  padding: 24px;
}
.panel h3, .panel .panel-title {
  margin: 0;
  font-family: "Space Grotesk", sans-serif;
  font-size: 30px;
  color: var(--ink);
}
/* The two feature-tab intro cards are <h2> for heading order (they sit
   right under the tab's h1) but wear the .panel h3 look. .section h2
   above also matches them; this puts back the h3 defaults it would
   otherwise override. Kept as a class override rather than :not() on
   .section h2 — raising that selector's specificity broke the narrow
   showcase headings (`.showcase-text h2` lost, words split mid-way). */
.panel .panel-title { text-align: left; line-height: normal; letter-spacing: normal; font-weight: 700; word-break: normal; overflow-wrap: normal; }
.panel p {
  margin: 10px 0 0;
  color: var(--ink-soft);
  font-size: 17px;
  line-height: 1.5;
}

.mini {
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.11);
  background: rgba(255, 255, 255, 0.055);
  padding: 20px;
}
.mini h3 { margin: 0; font-size: 16px; font-weight: 800; color: #f8fafc; }
.mini p { margin: 8px 0 0; color: rgba(248, 250, 252, 0.64); font-size: 14px; line-height: 1.45; }

/* Stat tiles — sole consumer today is the About tab's three glass
   tiles (rethemed to brand tokens when About became a tab, 2026-09-02;
   the old trust-row markup that used them is long gone). Collapses to
   one column via the 980px grid enumeration below. */
.stats {
  width: min(880px, 100%);
  margin: 64px auto 0;
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.stat { padding: 12px 20px; text-align: center; }
.stat strong {
  display: block;
  font-family: "Manrope", sans-serif;
  font-size: clamp(22px, 2.4vw, 30px);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 800;
  color: #f8fafc;
}
.stat span { display: block; margin-top: 8px; color: var(--bm-ink-soft); font-size: 14px; line-height: 1.5; }

.pricing-card {
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.11);
  background: rgba(255, 255, 255, 0.055);
  padding: 20px;
}
.pricing-card h3 { margin: 0; font-size: 20px; font-family: "Space Grotesk", sans-serif; color: #f8fafc; }
.price {
  margin-top: 8px;
  font-size: 34px;
  font-family: "Space Grotesk", sans-serif;
  letter-spacing: 0;
  color: #f8fafc;
}
.price small { font-size: 14px; color: rgba(248, 250, 252, 0.64); }
.pricing-list {
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
  color: rgba(248, 250, 252, 0.64);
  font-size: 14px;
  line-height: 1.8;
}

.faq-title {
  margin: 52px 0 0;
  color: #f8fafc;
  font-size: 22px;
  font-weight: 650;
  letter-spacing: 0;
}
.faq-list { margin-top: 16px; display: grid; gap: 10px; }
details {
  border: 1px solid rgba(255, 255, 255, 0.11);
  border-radius: 8px;
  padding: 14px 16px;
  background: rgba(255, 255, 255, 0.055);
}
summary { cursor: pointer; font-weight: 700; color: #f8fafc; }
details p { margin: 10px 0 0; color: rgba(248, 250, 252, 0.64); line-height: 1.5; }

/* FAQ disclosure — wears the sidebar's 「其他工具」 look (see
   workspace-overrides.css): native marker out, a chevron that turns 90°
   on the same 0.18s. The motion itself is faqFlow() at the bottom. */
.faq-list summary {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  list-style: none;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary::after {
  content: "";
  flex: none;
  margin: 5px 2px 0 auto;
  width: 7px;
  height: 7px;
  border-right: 2px solid rgba(248, 250, 252, 0.45);
  border-bottom: 2px solid rgba(248, 250, 252, 0.45);
  transform: rotate(-45deg);
  transition: transform 0.18s ease;
}
.faq-list details[open] summary::after { transform: rotate(45deg); }

/* ── Keyboard (2026-09-20 批 7): one focus ring for every control, and a
   skip link that is the first tab stop and only shows while focused. ── */
:where(a, button, select, summary, [tabindex]):focus-visible { outline: 2px solid var(--bm-accent); outline-offset: 3px; }
#content { outline: none; }
.skip-link {
  position: absolute; top: 10px; left: 16px; z-index: 1000;
  padding: 10px 16px; border-radius: 999px;
  background: #ffffff; color: var(--ink);
  font-weight: 700; font-size: 14px; text-decoration: none;
  transform: translateY(-200%); opacity: 0;
}
.skip-link:focus-visible { transform: none; opacity: 1; outline-offset: 2px; }

/* ── Footer ── */
.footer {
  padding: 48px 0 56px;
  background: var(--bm-ground);
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.5fr repeat(4, minmax(0, 1fr));
  gap: 16px;
}
.footer h2 {
  margin: 0;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: rgba(248, 250, 252, 0.55);
}
.footer .brand {
  color: #f8fafc;
}
.footer a, .footer p, .footer li {
  color: rgba(248, 250, 252, 0.62);
  text-decoration: none;
  font-size: 13px;
  line-height: 1.9;
  margin: 0;
}
.footer a:hover { color: #ffffff; }
.footer ul { list-style: none; margin: 8px 0 0; padding: 0; }
.copyright {
  margin-top: 28px;
  color: rgba(248, 250, 252, 0.48);
  font-size: 12px;
}

/* Lang dropdown matches the .nav-contact glass treatment so the nav
   reads as one cohesive translucent action row over the video. */
.lang-toggle {
  min-height: 34px;
  padding: 6px 30px 6px 14px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.42);
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #ffffff;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease;
  white-space: nowrap;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23ffffff' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
.lang-toggle:hover {
  background: rgba(255, 255, 255, 0.18);
  border-color: rgba(255, 255, 255, 0.66);
}
/* Native dropdown menu stays light — opening it pops out of the nav
   context so a white menu reads better than a translucent one. */
.lang-toggle option { background: #ffffff; color: var(--ink); }

/* Customizable select (Chrome/Edge 135+): base-select swaps the macOS
   native popup — which anchors the checked item OVER the pill and
   covers it — for an in-page popover anchored BELOW the pill. Browsers
   without support skip this whole block and keep the native menu.
   Structural twin lives in seo-common.css (subpages' dark picker) —
   port structural fixes (::picker-icon, anchor offset) to both. */
@supports (appearance: base-select) {
  .lang-toggle,
  .lang-toggle::picker(select) {
    appearance: base-select;
  }
  /* The pill already draws its own chevron via background-image — hide
     the extra icon base-select adds. */
  .lang-toggle::picker-icon { display: none; }
  .lang-toggle::picker(select) {
    margin-top: 6px;
    padding: 5px;
    border: 1px solid rgba(15, 23, 42, 0.1);
    border-radius: 14px;
    background: #ffffff;
    color: var(--ink);
    box-shadow: 0 14px 36px rgba(2, 6, 23, 0.22);
  }
  .lang-toggle option {
    padding: 7px 24px 7px 10px;
    border-radius: 9px;
    font-size: 13px;
    font-weight: 600;
    background: transparent;
  }
  .lang-toggle option:hover,
  .lang-toggle option:focus-visible {
    background: rgba(15, 23, 42, 0.07);
  }
}

/* CJK language overrides */
html.lang-zh body {
  font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
}
html.lang-zh h1,
html.lang-zh h2,
html.lang-zh .panel h3,
html.lang-zh .panel .panel-title,
html.lang-zh .pricing-card h3,
html.lang-zh .price {
  font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
  letter-spacing: 0.02em;
}
html.lang-zh .hero .hero-title {
  font-size: 54px;
  line-height: 1.05;
  font-weight: 500;
}
html.lang-zh .section h2 {
  font-size: 50px;
  line-height: 1.2;
  font-weight: 700;
}
html.lang-zh .section h2.security-headline {
  font-size: 36px;
  line-height: 1.35;
}
@media (min-width: 981px) {
  html.lang-zh .showcase-split .showcase-text h2 {
    font-size: 28px;
    line-height: 1.25;
  }
}
html.lang-zh .hero-tagline,
html.lang-zh .section .sub {
  line-height: 1.6;
  letter-spacing: 0.01em;
}
html.lang-zh .panel p,
html.lang-zh .mini p,
html.lang-zh details p,
html.lang-zh .pricing-list { line-height: 1.7; }
html.lang-zh .mini h3 {
  font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
  font-weight: 700;
  letter-spacing: 0.02em;
}
html.lang-zh summary {
  font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
}
html.lang-zh .stat strong {
  font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
  letter-spacing: 0;
}
html.lang-zh .cta-pill {
  font-family: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
  letter-spacing: 0.04em;
}
html.lang-zh .footer h2 { letter-spacing: 0.08em; }

/* ── Tablet ── */
@media (max-width: 980px) {
  /* 2026-09-02：这三个链接原先在 980 以下整组 display:none —— 但「价格」和
     「下载」自 #1290 标签化后就是首页的两个标签，隐藏等于手机上完全够不着。
     改成品牌行下面的第二行标签条：无 JS、始终可见，和标签化是同一个心智模型。
     .nav .wrap 的 height 仍然等于 --nav-h（两行靠 align-content 居中排进去），
     所以 hero 的负 margin 自动对得上，不用另外量高度。 */
  :root { --nav-h: 92px; }
  .nav .wrap {
    display: flex;
    flex-wrap: wrap;
    align-content: center;
    gap: 6px 20px;
    padding: 0 20px;
  }
  .brand { order: 1; margin-right: auto; }
  .nav-right { order: 2; }
  .links {
    order: 3;
    flex-basis: 100%;
    justify-content: flex-start;
    /* 负 margin 抵消链接自身的左内边距，让「简介」的**字面**左缘和 ZCC Labs
       上下对齐 —— 直接 flex-start 会因为 a 的 padding-left 右偏 10px。
       负量吃进 wrap 的 padding 里，不会撑出横向滚动。 */
    margin-left: -10px;
    gap: 4px;
  }
  .nav-contact { display: none; }
  .grid-2, .grid-3, .grid-4, .stats, .footer-grid {
    grid-template-columns: 1fr;
  }
}

/* ── Mobile (≤ 640px) ── */
@media (max-width: 640px) {
  .wrap { width: calc(100% - 32px); }
  :root { --nav-h: 82px; }
  .nav .wrap {
    width: 100%;
    max-width: none;
    position: relative;
    gap: 4px 16px;
    padding: 0 16px;
  }
  /* 第二行标签条：手机上是够到定价页 / 下载页的唯一入口，所以点击区给足。
     左内边距加大到 12px，抵消量也要跟着改到 -12px，否则和品牌名对不齐。 */
  .links { font-size: 15px; margin-left: -12px; }
  .links a { padding: 6px 12px; }
  .brand { font-size: 16px; }
  /* Keep lang dropdown + Log in visible on mobile — Contact stays
     hidden (handled by the ≤980 rule) so the right action group
     fits comfortably alongside the brand. */
  .nav-right { gap: 6px; }
  .nav-contact { display: none; }
  .nav-login {
    padding: 6px 12px;
    font-size: 12px;
    min-height: 30px;
  }
  .lang-toggle {
    min-height: 30px;
    padding: 4px 22px 4px 10px;
    font-size: 12px;
    background-position: right 8px center;
  }

  .hero { padding: calc(var(--nav-h) + 44px) 0 36px; min-height: 440px; }
  .hero-icon { width: 92px; height: 92px; border-radius: 20px; margin-bottom: 24px; }
  .hero .hero-title { font-size: 42px; }
  /* 中文那条 html.lang-zh .hero h1 (0,2,1) 在媒体查询外、压过上面 (0,1,1)——不在这儿
     再写一遍，中文站手机上就一直是桌面的 54px（2026-09-03 Zechang 说「小一号」才发现）。 */
  html.lang-zh .hero .hero-title { font-size: 46px; }
  .hero-tagline { font-size: 18px; line-height: 1.35; margin-top: 14px; }

  .hero-cta { margin-top: 24px; gap: 8px; }
  .cta-pill { padding: 12px 24px; font-size: 14px; }



  .aurora-blob { filter: blur(70px); }

  .demo-section { padding: 60px 0 52px; }
  .demo-section-title { font-size: 26px; }
  .demo-bg-spacer { height: 80px; }
  .demo-section-sub {
    width: min(280px, 100%);
    margin: 0 auto 28px;
    font-size: 14px;
  }
  .hero-media { width: 94%; padding: 16px; border-radius: 20px; }
  .mock-head { padding: 12px 14px; }
  .mock-body { padding: 14px; gap: 8px; }
  .mock-chip { padding: 10px 12px; font-size: 13px; line-height: 1.45; }
  .demo-mic-btn { width: 50px; height: 50px; margin: 8px 0 6px; }
  .demo-prompt { font-size: 14px; }
  .demo-text { font-size: 14px; max-height: 96px; }
  .demo-label { font-size: 10px; }
  .demo-transcript-area { margin-bottom: 10px; }

  .section { padding: 52px 0; }
  /* Slightly trimmed from 32 → 28px so multi-segment titles like
     "免费 · 全平台" comfortably fit one row at iPhone widths
     instead of cracking mid-word. */
  .section h2 { font-size: 28px; }
  /* Chinese desktop sets .section h2 to 50px, but that's huge on
     iPhones — match the Latin mobile size. */
  html.lang-zh .section h2 { font-size: 28px; }
  html.lang-zh .section h2.security-headline { font-size: 24px; }
  .section .sub { font-size: 15px; margin-top: 10px; line-height: 1.5; }
  .section h2.security-headline { font-size: 26px; }

  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .mini { padding: 14px; border-radius: 14px; }
  .mini h3 { font-size: 14px; }
  .mini p { font-size: 12.5px; line-height: 1.4; }

  .panel { padding: 20px; border-radius: 18px; }
  .panel h3, .panel .panel-title { font-size: 22px; }
  .panel p { font-size: 15px; line-height: 1.5; }

  .pricing-card { padding: 18px; border-radius: 16px; }
  .pricing-card h3 { font-size: 18px; }
  .price { font-size: 30px; }
  .pricing-list { font-size: 13px; line-height: 1.7; }

  details { padding: 12px 14px; border-radius: 12px; }
  summary { font-size: 14px; }
  details p { font-size: 13.5px; line-height: 1.5; }

  .footer { padding: 36px 0 40px; }
  .footer-grid { grid-template-columns: 1fr 1fr; gap: 22px 14px; }
  .footer-grid > div:first-child { grid-column: 1 / -1; }
}

@media (max-width: 380px) {
  .hero .hero-title { font-size: 38px; }
  html.lang-zh .hero .hero-title { font-size: 42px; }
  .grid-4 { grid-template-columns: 1fr; }
  .cta-pill { padding: 11px 20px; font-size: 13px; }
}

@media (prefers-reduced-motion: reduce) {
  .aurora-blob, .hero-icon, .demo-mic-btn, .demo-mic-btn::before, .demo-state, .mock-head > div, .demo-tw-char { animation: none !important; }
}

/* ── Route tabs (2026-09-01): "Download" / "Pricing" / "About" are tabs,
   not pages. `html[data-tab]` is the single switch — set before first
   paint by the inline script at the top of <body> from the URL, then
   toggled by tabRouter() at the bottom. Panels stay in the DOM so i18n /
   demo state survive a switch. Adding a tab = a `#tab-<name>` panel
   (with data-title) + its name in BM_TABS at the top of <body> + one
   line in the enumeration below (CSS cannot map an attribute value onto
   an id) + a ROUTES head entry in scripts/marketing-heads.mjs + a
   vercel.json rewrite + a sitemap.xml entry. A link may live in the top
   bar OR the footer — the router intercepts any `/<name>` anchor; only
   top-bar links get the aria-current highlight (About is footer-only on
   purpose, so it never highlights). ── */
.tab-panel:not(#tab-home) { display: none; }
html[data-tab] #tab-home { display: none; }
html[data-tab="download"] #tab-download,
html[data-tab="pricing"] #tab-pricing,
html[data-tab="about"] #tab-about { display: block; }
.links a[aria-current="page"] { color: #ffffff; }
.tab-panel.tab-enter { animation: tab-enter 0.36s cubic-bezier(0.22, 1, 0.36, 1) both; }
@keyframes tab-enter {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .tab-panel.tab-enter { animation: none; } }

/* ── Download tab: two ChatGPT-style rows (media card ↔ copy), one per
   platform family. The phone / Windows cards reuse the brand-blue
   gradient card from the landing "Why" rows; the Mac row is the "bare"
   variant (2026-09-03) — the real screenshot IS the media, no card
   behind it. ── */

.dl-main { padding-top: 24px; }

.dl-row {
  display: grid;
  gap: 36px;
  padding: 48px 0;
  align-items: center;
  /* Hero pills deep-link to a row (/download#mac · #ios · #windows);
     the top bar is sticky, so an anchored row must stop under it, not
     behind it. The row's own top padding supplies the breathing room. */
  scroll-margin-top: var(--nav-h);
}
/* Phone/tablet: copy always reads first, card follows. */
.dl-row .dl-card { order: 2; }

@media (min-width: 981px) {
  .dl-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 64px;
    padding: 72px 0;
  }
  .dl-row .dl-card { order: 0; }
  .dl-row.reverse .dl-card { order: 2; }
}

.dl-title {
  margin: 0;
  font-family: "Manrope", sans-serif;
  font-size: clamp(38px, 5vw, 68px);
  line-height: 1.04;
  letter-spacing: -0.025em;
  font-weight: 700;
  color: #f8fafc;
  text-wrap: balance;
  word-break: keep-all;
  overflow-wrap: anywhere;
}
/* zh titles size off the copy column (cqw) rather than the viewport:
   「下载 iPhone 或 iPad 版」 must stay on one line, and at the viewport-
   based 60px it is wider than the column. 9.3cqw = that string's width
   ratio (~10.5× font-size) with ~2% slack; both rows share it so the two
   titles never differ in size. First declaration = fallback where cqw
   is unsupported. */
.dl-copy { container-type: inline-size; }
html.lang-zh .dl-title {
  font-size: clamp(34px, 4.4vw, 60px);
  font-size: clamp(32px, 9.3cqw, 60px);
  line-height: 1.18;
  letter-spacing: 0;
}

.dl-desc {
  margin: 22px 0 0;
  color: var(--bm-ink-soft);
  font-size: clamp(16px, 1.5vw, 19px);
  line-height: 1.55;
}
html.lang-zh .dl-desc { line-height: 1.7; }
.dl-meta { margin-top: 14px; font-size: 15px; opacity: 0.85; }
.dl-meta.dl-region { margin-top: 6px; }

.dl-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 30px;
}

/* Pill buttons shared by the Download and Pricing tabs (white primary /
   glass ghost). Sits on the flat page ground, so no backdrop blur —
   unlike the nav pills that float over the aurora video. */
.pill-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 52px;
  padding: 14px 28px;
  border-radius: 999px;
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: opacity 0.18s ease, transform 0.18s ease, background 0.18s ease, border-color 0.18s ease;
}
.pill-btn svg { flex-shrink: 0; fill: currentColor; }
.pill-btn.primary { background: #ffffff; color: var(--ink); border: 1px solid #ffffff; }
.pill-btn.primary:hover { opacity: 0.86; transform: translateY(-1px); }
.pill-btn.ghost {
  color: #ffffff;
  border: 1px solid rgba(255, 255, 255, 0.42);
  background: rgba(255, 255, 255, 0.08);
}
.pill-btn.ghost:hover { background: rgba(255, 255, 255, 0.18); border-color: rgba(255, 255, 255, 0.66); }
.pill-btn.apple svg { width: 18px; height: 18px; }

.dl-more {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 24px;
  color: #f8fafc;
  font-size: 16px;
  font-weight: 600;
  text-decoration: none;
  opacity: 0.9;
}
.dl-more:hover { opacity: 1; text-decoration: underline; text-underline-offset: 4px; }
.dl-more svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ── Media: bare real screenshots (2026-09-03, Zechang「不用那个蓝色边框了，
   直接上真机的一张大图」— tried on the Mac row first, then「都换吧」). No
   gradient card, no offset crop: the screenshot itself is the media —
   rounded, hairline ring, deep shadow, so it reads as a window floating
   on the dark page. Desktop rows hand the shot the wider column (see
   .dl-row.wide) so it lands as ONE big image beside the 68px headline.
   Mac shot = app mid-recording (dark, Andy rail open, 1504×827 @2x);
   Windows shot = home page (light, 1440×900 @2x). ── */
.dl-card {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 40px 100px -24px rgba(0, 0, 0, 0.75), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.dl-card img {
  display: block;
  width: 100%;
  height: auto;
}
@media (min-width: 981px) {
  .dl-row.wide { grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr); }
  .dl-row.wide.reverse { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); }
}

/* Phone row: the screenshot sits in the official iPhone 17 Pro Max body
   — same bezel asset + opening geometry as the opening demo up top
   (.bm-opening-stage--phone), standing alone centered in its column.
   The body's alpha shapes the shadow (drop-shadow on the stage, so
   overflow must stay visible — the card's clip would cut it off). */
.dl-card-phone {
  width: min(100%, 340px);
  margin: 0 auto;
  overflow: visible;
  border-radius: 0;
  box-shadow: none;
  filter: drop-shadow(0 30px 60px rgba(0, 0, 0, 0.6));
}
.dl-phone-bezel {
  position: relative;
  z-index: 2;
  pointer-events: none;
}
.dl-card-phone .dl-phone-screen {
  position: absolute;
  z-index: 1;
  left: 5.102%;
  top: 2.2%;
  width: 89.796%;
  height: 95.6%;
  border-radius: 14% / 6.5%;
  background: #000;
}

/* ── App Store block: QR + button. The QR is hidden on phones — you are
   already holding the device it would open. ── */
.dl-store {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 22px;
  margin-top: 30px;
}
.dl-qr {
  display: block;
  width: 160px;
  height: 160px;
  padding: 12px;
  border-radius: 22px;
  background: #ffffff;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.35);
  flex-shrink: 0;
}
.dl-qr svg { display: block; width: 100%; height: 100%; }
.dl-store-side { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; }
.dl-scan { margin: 0; max-width: 240px; color: var(--bm-ink-soft); font-size: 14px; line-height: 1.45; }
@media (max-width: 700px) {
  .dl-qr, .dl-scan { display: none; }
}

.dl-legacy {
  margin: 0;
  padding: 12px 0 72px;
  text-align: center;
  color: var(--bm-ink-soft);
  font-size: 15px;
  line-height: 1.6;
}
.dl-legacy a { color: #f8fafc; text-decoration: underline; text-underline-offset: 3px; }

/* ── Pricing tab (2026-09-01): ChatGPT pricing skeleton — eyebrow /
   headline / sub / monthly·term segmented control / two big plan cards
   (Pro featured, Max). Both price sets live in the markup; the panel's
   data-period attribute decides which one shows, so the switch is pure
   CSS + one attribute flip. Prices are hand-written here AND in the
   four dictionaries (term notes) — bump all of them together with the
   Stripe / IAP catalog. ── */
.pr-main { padding: 64px 0 88px; }
.pr-head { text-align: center; }
.pr-eyebrow { margin: 0; color: var(--bm-ink-soft); font-size: 15px; font-weight: 500; letter-spacing: 0.01em; }
.pr-title {
  margin: 14px 0 0;
  font-family: "Manrope", sans-serif;
  font-size: clamp(48px, 7vw, 92px);
  line-height: 1;
  letter-spacing: -0.03em;
  font-weight: 700;
  color: #f8fafc;
}
html.lang-zh .pr-title { letter-spacing: 0; font-size: clamp(44px, 6.5vw, 84px); }
.pr-sub {
  margin: 22px auto 0;
  width: min(640px, 92%);
  color: var(--bm-ink-soft);
  font-size: clamp(16px, 1.6vw, 19px);
  line-height: 1.55;
}

/* Segmented control: glass track, white thumb (::before) sliding under
   the active half; keyed off the panel's data-period like the prices. */
.pr-seg {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  width: min(440px, 92%);
  margin: 40px auto 0;
  padding: 5px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.14);
}
.pr-seg::before {
  content: "";
  position: absolute;
  top: 5px; bottom: 5px; left: 5px;
  width: calc(50% - 5px);
  border-radius: 999px;
  background: #ffffff;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
[data-period="term"] .pr-seg::before { transform: translateX(100%); }
.pr-seg button {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 46px; padding: 0 16px;
  border: 0; border-radius: 999px; background: transparent;
  color: rgba(255, 255, 255, 0.72);
  font-family: inherit; font-size: 15px; font-weight: 600;
  cursor: pointer;
  transition: color 0.2s ease;
}
.pr-seg button[aria-checked="true"] { color: var(--ink); }
.pr-seg button svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.pr-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  width: min(960px, 100%);
  margin: 40px auto 0;
}
@media (min-width: 761px) { .pr-cards { grid-template-columns: 1fr 1fr; gap: 22px; } }
.pr-card {
  position: relative;
  padding: 32px 30px 34px;
  border-radius: 24px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.04);
}
.pr-card.featured {
  border-color: rgba(var(--bm-accent-rgb), 0.55);
  background:
    radial-gradient(120% 80% at 0% 0%, rgba(var(--bm-accent-rgb), 0.14), transparent 60%),
    rgba(255, 255, 255, 0.05);
}
.pr-badge {
  position: absolute; top: 22px; right: 22px;
  padding: 5px 11px;
  border-radius: 999px;
  background: rgba(var(--bm-accent-rgb), 0.18);
  border: 1px solid rgba(var(--bm-accent-rgb), 0.45);
  color: var(--bm-accent);
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em;
}
.pr-name { margin: 0; font-family: "Manrope", sans-serif; font-size: 34px; line-height: 1.1; font-weight: 700; letter-spacing: -0.01em; color: #f8fafc; }
.pr-tag { margin: 8px 0 0; color: var(--bm-ink-soft); font-size: 16px; }
.pr-price { margin-top: 34px; }
.pr-price > span { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.pr-amount {
  font-family: "Manrope", sans-serif;
  font-size: clamp(44px, 5vw, 60px);
  line-height: 1;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: #f8fafc;
  font-variant-numeric: tabular-nums;
}
.pr-per { color: var(--bm-ink-soft); font-size: 17px; }
.pr-note { margin: 10px 0 0; min-height: 22px; color: var(--bm-ink-soft); font-size: 14px; }
.pr-note strong { color: var(--bm-accent); font-weight: 700; }
/* Card CTA = the shared white pill, full width, chevron glyph. */
.pr-cta { display: flex; width: 100%; margin-top: 22px; min-height: 54px; }
.pr-cta svg, .ab-cta-row .pill-btn svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.pr-list { list-style: none; margin: 26px 0 0; padding: 0; display: grid; gap: 14px; }
.pr-list li { display: flex; align-items: flex-start; gap: 12px; color: rgba(248, 250, 252, 0.86); font-size: 15px; line-height: 1.45; }
.pr-list li svg { flex-shrink: 0; width: 18px; height: 18px; margin-top: 2px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.pr-list li.lead { font-weight: 700; color: #f8fafc; padding-bottom: 16px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); }
html.lang-zh .pr-list li { line-height: 1.6; }
/* Period swap: hide the inactive price set; the incoming one fades up. */
[data-period="monthly"] .pr-term, [data-period="term"] .pr-monthly { display: none; }
.pr-swap { animation: pr-swap 0.22s ease both; }
@keyframes pr-swap { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.pr-foot { margin: 28px auto 0; width: min(720px, 92%); text-align: center; color: var(--bm-ink-soft); font-size: 15px; line-height: 1.7; }
.pr-foot a { color: #f8fafc; text-decoration: underline; text-underline-offset: 3px; }

/* ── Compare table + billing FAQ (2026-09-20, 官网批 5) ──
   Same glass as the cards; the Pro column carries the accent tint so
   the eye lands where the card badge already points. Prices in the
   header follow the monthly ↔ term control through the same
   .pr-monthly / .pr-term switch as the cards. Under 760px each row
   stacks: the feature label on its own line, the three plan values
   side by side beneath it (Apple's compare-table shape), so nothing
   scrolls sideways at 375px. */
.pr-cmp { width: min(960px, 100%); margin: 72px auto 0; }
.pr-cmp-title { margin: 0; text-align: center; font-family: "Manrope", sans-serif; font-size: clamp(26px, 3vw, 34px); line-height: 1.15; font-weight: 700; letter-spacing: -0.02em; color: #f8fafc; }
.pr-cmp-sub { margin: 12px auto 0; width: min(560px, 100%); text-align: center; color: var(--bm-ink-soft); font-size: 16px; line-height: 1.55; }
.pr-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.pr-table {
  width: 100%;
  margin-top: 32px;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.04);
  overflow: hidden;
  font-size: 15px;
  line-height: 1.45;
}
.pr-table th, .pr-table td { padding: 16px 18px; text-align: left; vertical-align: top; border-top: 1px solid rgba(255, 255, 255, 0.09); }
.pr-table thead th { border-top: 0; vertical-align: bottom; padding-top: 24px; padding-bottom: 20px; }
.pr-table thead th:not(.pr-col-label), .pr-table tbody td { text-align: center; }
.pr-table tbody th { font-weight: 600; color: #f8fafc; }
.pr-table tbody td { color: rgba(248, 250, 252, 0.86); }
.pr-table .pr-col-label { width: 34%; }
.pr-table .pr-col-pro, .pr-table tbody td:nth-child(3) { background: rgba(var(--bm-accent-rgb), 0.07); }
.pr-plan { display: block; font-family: "Manrope", sans-serif; font-size: 22px; font-weight: 700; letter-spacing: -0.01em; color: #f8fafc; }
.pr-plan-price { display: block; margin-top: 6px; color: #f8fafc; font-weight: 600; font-variant-numeric: tabular-nums; }
.pr-plan-price .pr-amt { font-size: 20px; }
.pr-plan-price .pr-unit { color: var(--bm-ink-soft); font-size: 13px; font-weight: 500; }
.pr-plan-note { display: block; margin-top: 4px; color: var(--bm-ink-soft); font-size: 12.5px; font-weight: 500; }
.pr-ok { width: 20px; height: 20px; stroke: var(--bm-accent); fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; vertical-align: middle; }
.pr-dash { color: rgba(248, 250, 252, 0.35); }
.pr-fine { list-style: none; margin: 18px 0 0; padding: 0 6px; display: grid; gap: 6px; color: var(--bm-ink-soft); font-size: 13.5px; line-height: 1.6; }
.pr-fine a { color: #f8fafc; text-decoration: underline; text-underline-offset: 3px; }
.pr-faq { width: min(760px, 100%); margin: 64px auto 0; }
.pr-faq-list { margin-top: 20px; }
.pr-faq-list details p a { color: #f8fafc; text-decoration: underline; text-underline-offset: 3px; }
html.lang-zh .pr-table, html.lang-zh .pr-fine, html.lang-zh .pr-faq-list details p { line-height: 1.65; }
@media (max-width: 760px) {
  .pr-cmp { margin-top: 56px; }
  .pr-table, .pr-table thead, .pr-table tbody { display: block; }
  .pr-table tr { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pr-table tbody tr { border-top: 1px solid rgba(255, 255, 255, 0.09); }
  .pr-table th, .pr-table td { border-top: 0; padding: 12px 8px; }
  .pr-table thead .pr-col-label { display: none; }
  .pr-table thead th { padding: 18px 8px 14px; }
  .pr-table tbody th { grid-column: 1 / -1; padding: 14px 14px 4px; }
  .pr-table tbody td { padding-top: 6px; padding-bottom: 14px; font-size: 14px; }
  .pr-table .pr-col-label { width: auto; }
  .pr-plan { font-size: 19px; }
  .pr-plan-price .pr-amt { font-size: 17px; }
  .pr-plan-price .pr-unit { display: block; font-size: 12px; }
  .pr-fine { padding: 0 2px; }
}

/* ── About tab (2026-09-02): reuses the Pricing head skeleton (.pr-main
   / .pr-head), the .stats glass tiles, and the .dl-actions pill row —
   only the story prose and CTA heading are new. Reached from the footer
   "About" link — the top bar deliberately stays as-is. ── */
.ab-story { width: min(680px, 92%); margin: 0 auto; padding-top: 64px; }
.ab-story h2, .ab-cta h2 {
  font-family: "Manrope", sans-serif;
  letter-spacing: -0.02em;
  font-weight: 700;
  color: #f8fafc;
}
html.lang-zh .ab-story h2, html.lang-zh .ab-cta h2 { letter-spacing: 0; }
.ab-story h2 { margin: 0 0 18px; font-size: clamp(26px, 3vw, 34px); line-height: 1.15; }
.ab-story p { margin: 0 0 16px; color: var(--bm-ink-soft); font-size: 17px; line-height: 1.75; }
.ab-story p:last-child { margin-bottom: 0; }
.ab-story a { color: var(--bm-accent); text-decoration: none; }
.ab-story a:hover { text-decoration: underline; text-underline-offset: 3px; }
.ab-cta { text-align: center; padding-top: 88px; }
.ab-cta h2 { margin: 0; font-size: clamp(28px, 3.6vw, 44px); line-height: 1.1; }
.ab-cta p { margin: 14px 0 0; color: var(--bm-ink-soft); font-size: 16px; }
.ab-cta-row { justify-content: center; }
