/* ==========================================================================
   酷酷科技官网 - 基础样式
   Header 参考 agnes-ai.com 的排版尺度（浅色侧取值）
   ========================================================================== */

/* ---------- 设计令牌 ---------- */
:root {
  /* 色板：与参考站点一致（Tailwind gray） */
  --gray-50: 243 243 243;
  --gray-100: 238 238 238;
  --gray-200: 223 223 223;
  --gray-300: 206 206 206;
  --gray-400: 159 159 159;
  --gray-500: 112 112 112;
  --gray-600: 80 80 80;
  --gray-700: 63 63 63;
  --gray-800: 38 38 38;
  --gray-900: 23 23 23;
  --gray-950: 10 10 10;

  --background-light: 255 255 255;
  --primary: 23 23 23;

  /* 布局尺度 */
  --header-height: 64px; /* h-16 = 4rem */
  --page-padding-x: 48px; /* lg:px-12 */
  --container-max: 1472px; /* max-w-8xl = 92rem */
  --content-max: 832px; /* 正文阅读宽度 */
  /* 服务页专用内容宽。服务页是单栏，不像产品页那样被三栏夹在中间，
     正文可以拿到整个框架宽度；但给满 1344px 的话，
     3 列卡片每列接近 440px，配上 220px 的最小高度会显得又扁又松。
     1128px 下卡片每列约 365px，是设计稿那种竖长比例。
     服务页与案例页共用这个值（两者都是单栏满宽页面）；
     产品页 / 联系页的三栏框架宽度不受它影响。 */
  --service-max: 1128px;
  --page-min-width: 1280px; /* 页面最小宽度：窄于此宽度出现横向滚动 */
  /* 产品页三栏：内容距 header 的距离与侧栏吸附点共用这一个值。
     两者必须相等，侧栏才会在进入视口的第一刻就吸附住，
     不会先跟着页面滚一段再停下。
     同时驱动 html 的 scroll-padding-top，保证书签跳转后标题与侧栏顶对齐。
     2026-10-18：54px → 14px。书签跳转 / 点击后标题的整体定位点上移 40px，
     侧栏吸附点、正文顶距与 scroll-padding-top 随同一令牌一起上移，
     三者仍然严格对齐（只改其一会出现「侧栏比标题高 40px」的错位）。 */
  --docs-sticky-gap: 14px;
  /* footer 兜底高度。footer 是 fixed 定位，页面底部要按它的高度补留白；
     真实高度由 main.js 实测后写进 .page-content 的 padding-bottom，
     这个令牌只是 JS 尚未执行时的兜底，避免首帧正文被压住。
     96px ≈ 上行(14+24+14) + 1px 分隔线 + 下行(12+18+12)。 */
  --footer-height: 96px;

  /* 标题字号：全站唯一一套。h1 / h2 / h3 各只有一个字号与行高，
     任何页面、任何区块都不再单独覆盖 —— 需要不同观感时改标签层级
     （h1 → h2 → h3），不要改字号。调整档位只动这六个值。 */
  --h1-size: 40px;
  --h1-line: 1.2;
  --h2-size: 26px;
  --h2-line: 1.35;
  --h3-size: 18px;
  --h3-line: 1.5;

  --spacing: 0.25rem;
  --rounded-xl: 0.75rem;

  /* 过渡 */
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --duration: 0.15s;

  /* header 阴影（滚动后显示） */
  --header-shadow: 0 1px 2px 0 rgb(0 0 0 / 4%), 0 2px 8px -2px rgb(0 0 0 / 6%);

  --font-sans: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
    system-ui, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;

  /* 等宽字体：首页右侧 IDE 动画（代码 / 行号 / 文件名）使用。
     原为像素字体 "Press Start 2P"（2026-09-27 按皇上要求去掉），
     改为系统等宽（Windows → Consolas），不再依赖 Google Fonts。 */
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono",
    monospace;
}

/* ---------- 重置 ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  /* 锚点跳转留出 header 高度 + 侧栏间距；窄屏下由 JS 写入实际值。
     与 .docs-side / .docs-toc 的 top 共用同一个令牌，
     书签跳转后标题落点才会与侧栏顶部对齐。 */
  scroll-padding-top: calc(var(--header-height-actual, var(--header-height)) + var(--docs-sticky-gap));
  /* 页面最小宽度：窄于此宽度时出现横向滚动条，内部保持桌面布局。
     同时挂在 body 上做防御，避免个别浏览器不把 html 的 min-width
     传给 body 的 flex 布局。 */
  min-width: var(--page-min-width);
  /* 隐藏原生滚动条，改用文件末尾 .ov-scroll 的浮层滚动条。
     原生滚动条会占据 15~17px 布局宽度：长页面出现、短页面消失，
     切换标签时内容宽度跳变，导航与正文就会左右位移。
     注意 scrollbar-width 会被继承，各滚动容器需自行声明所需值。 */
  scrollbar-width: none; /* Firefox */
}

html::-webkit-scrollbar {
  display: none; /* Chrome / Edge / Safari */
}

body {
  margin: 0;
  min-width: var(--page-min-width);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: rgb(var(--background-light));
  color: rgb(var(--gray-700));
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ==========================================================================
   Header
   fixed 固定在顶部；下方 1px 极淡描边，滚动后叠加淡阴影
   ========================================================================== */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
  /* fixed 元素不继承 body 的 min-width（它的包含块是视口），
     必须单独声明，否则页面横向滚动时 header 右侧会露出空缺 */
  min-width: var(--page-min-width);
  height: var(--header-height);
  background-color: rgb(var(--background-light));
  border-bottom: 1px solid rgb(var(--gray-500) / 5%);
  transition: box-shadow 0.25s var(--ease), background-color 0.25s var(--ease);
}

/* 滚动到 header 下方后显示淡阴影 */
.site-header.is-scrolled {
  box-shadow: var(--header-shadow);
}

.header-inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--page-padding-x);
  gap: 16px; /* gap-x-4 */
}

/* ---------- 左：logo + 公司名称 ---------- */
/* 左右两段等宽（flex:1 1 0），中间导航按内容占宽并真正居中；
   这样既保证元素间距相等，又让换行断点尽可能晚 */
.brand {
  order: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 0;
  min-width: 0;
}

.brand-logo {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  display: block;
}

/* 公司名称：与 logo 始终同一行，不换行、不折叠 */
.brand-name {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: rgb(var(--gray-900));
  white-space: nowrap;
}

/* ---------- 中：主导航（页签条） ---------- */
/* order 前移把导航放到左右两段之间；flex: 0 1 auto 按内容占宽，
   因此导航真正居中，且不会被压缩 —— 换行点可推到更窄 */
.site-nav {
  position: relative;
  z-index: 30; /* 低于语言下拉(40)，避免压住浮层 */
  order: 2;
  flex: 0 1 auto;
  display: flex;
  justify-content: center;
  align-items: stretch;
  height: 100%;
  min-width: 0;
}

.nav-list {
  display: flex;
  align-items: stretch;
  height: 100%;
  gap: 24px; /* gap-x-6 */
}

.nav-list > li {
  display: flex;
  align-items: stretch;
}

/* 页签：h-full + flex 居中 → 整条高度均可点击
   对应参考站点 .nav-tabs-item = "relative h-full flex items-center" */
.nav-link {
  position: relative;
  display: flex;
  align-items: center;
  height: 100%;
  font-size: 14px; /* text-sm */
  line-height: 20px; /* 1.25rem */
  font-weight: 500; /* font-medium */
  color: rgb(var(--gray-600));
  white-space: nowrap;
  transition: color var(--duration) var(--ease);
}

.nav-link:hover {
  color: rgb(var(--gray-800));
}

/* 激活态：文字加粗 + 颜色加深
   参考站点用 text-shadow 描边模拟加粗，此处用真实字重，
   并过渡 font-variation 以保证切换不跳动 */
.nav-link[aria-current="page"] {
  font-weight: 700;
  color: rgb(var(--gray-900));
}

/* 下划线：贴容器底边（absolute bottom-0），宽 100%，高 2px
   粗细对「激活」与「hover」两种状态同时生效（共用 height）
   - 激活：深色 #171717
   - hover（非激活）：浅灰 #DFDFDF
   （参考站点原为 1.5px，按皇上要求加粗至 2px） */
.nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background-color: transparent;
  transition: background-color var(--duration) var(--ease);
}

/* 非激活项 hover：浅灰线条 */
.nav-link:not([aria-current="page"]):hover::after {
  background-color: rgb(var(--gray-200));
}

/* 激活项：深色线条（选择器权重高于 hover 规则） */
.nav-link[aria-current="page"]::after {
  background-color: rgb(var(--primary));
}

/* 激活项被 hover 时保持深色线条 */
.nav-link[aria-current="page"]:hover::after {
  background-color: rgb(var(--primary));
}

/* ---------- 右：语言选择 ---------- */
/* 与左侧等宽（flex:1 1 0）+ 内容右对齐，保证导航居中 */
.header-right {
  order: 3;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex: 1 1 0;
  min-width: 0;
}

.lang-switch {
  position: relative;
  z-index: 40; /* 高于导航行，确保下拉浮在最上层 */
}

.lang-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 32px; /* h-8 */
  padding: 0 10px; /* px-2.5 */
  border-radius: var(--rounded-xl);
  font-size: 14px;
  font-weight: 500;
  color: rgb(var(--gray-900));
  white-space: nowrap;
  transition: background-color var(--duration) var(--ease);
}

.lang-trigger:hover,
.lang-trigger[aria-expanded="true"] {
  background-color: rgb(var(--gray-600) / 5%);
}

.lang-trigger .chevron {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  color: rgb(var(--gray-400));
  transition: transform var(--duration) var(--ease), color var(--duration) var(--ease);
}

.lang-trigger:hover .chevron {
  color: rgb(var(--gray-600));
}

/* 右箭头旋转 90° 成向下箭头 */
.lang-trigger .chevron {
  transform: rotate(90deg);
}

.lang-trigger[aria-expanded="true"] .chevron {
  transform: rotate(270deg);
}

/* 下拉菜单 */
.lang-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 160px;
  padding: 4px;
  background-color: rgb(var(--background-light));
  border: 1px solid rgb(var(--gray-500) / 12%);
  border-radius: var(--rounded-xl);
  box-shadow: 0 4px 16px -4px rgb(0 0 0 / 10%), 0 1px 2px 0 rgb(0 0 0 / 5%);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease),
    visibility var(--duration) var(--ease);
}

.lang-menu.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.lang-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 14px;
  color: rgb(var(--gray-700));
  text-align: left;
  transition: background-color var(--duration) var(--ease), color var(--duration) var(--ease);
}

.lang-option:hover {
  background-color: rgb(var(--gray-600) / 5%);
  color: rgb(var(--gray-900));
}

.lang-option[aria-selected="true"] {
  font-weight: 600;
  color: rgb(var(--gray-900));
}

.lang-option .check {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  opacity: 0;
  color: rgb(var(--primary));
}

.lang-option[aria-selected="true"] .check {
  opacity: 1;
}

/* ==========================================================================
   主内容区
   ========================================================================== */
/* padding-top 由 JS 按 header 实际高度写入（窄屏 header 会变高） */
.page-content {
  flex: 1 0 auto;
  padding-top: var(--header-height);
  /* footer 是 fixed，已脱离文档流；底部要补一段等量留白，
     否则最后一屏内容会被 footer 压住。
     实际高度由 main.js 实测后写在内联样式上（窄屏换行会变高），
     这里的令牌只是 JS 尚未执行时的兜底值，与 --header-height 同一套思路。 */
  padding-bottom: var(--footer-height);
}

/* 正文区容器：与 .header-inner 用**完全相同的宽度约束与内边距**，
   保证正文区与导航左右对齐。
   内容自身的宽度由内部元素控制（见 .prose 等），不在此限制。 */
.content-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  /* 顶部留白下放到各标签页（见 .page-panel）：
     首页没有标题区，需要比其余页更紧凑的顶部间距，
     放在共用容器上会连带改动服务 / 产品 / 联系我们。 */
  padding: 0 var(--page-padding-x) 66px;
}

/* 阅读宽度约束：加在需要限制行宽的内容块上（默认 832px）
   例：<div class="prose">…</div>
   注意：.content-inner 本身是「容器」（与导航同宽 1472px），
   不再限制内容宽度 —— 内容宽度一律由内部元素自己控制。 */
.prose {
  max-width: var(--content-max);
}

/* 页面切换：非激活页隐藏 */
.page-panel {
  display: none;
  /* 标签页顶部留白：默认 56px，供带标题区的页面使用 */
  padding-top: 56px;
}

/* 首页：标题区已移除，首个元素即展示动效，顶部收紧到 32px */
#page-home {
  padding-top: 32px;
}

/* 首页：内容在「header 到 footer 之间」的可视高度内垂直居中。
   - 可视带高度 = 100vh − header − footer，正好等于 .page-content 的内容盒
     （上下 padding 就是 JS 实测的 header / footer 高度）。所以把 .page-content
     改成纵向 flex、在内容盒内居中即可，不需要再自己算 100vh。
   - 仅在视口足够高时启用：舞台固定 600px + 上下间距 88px = 688px，
     低于 900px 视口时带高不足，硬居中会上下都溢出（顶部被 header 压住），
     此时保持原来的顶部对齐 + 正常滚动。
   - 居中后必须去掉两处固定留白，否则内容会明显偏下：
     #page-home 的 padding-top: 32px 与 .content-inner 的 padding-bottom: 66px。
   - 40px/48px 的舞台上下边距合计 88px，改成 44/44 让可见内容关于中线对称，
     总和不变，不会引起任何高度位移。 */
@media (min-height: 900px) {
  .page-content:has(#page-home.is-active) {
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* 内容万一超出（footer 换行变高等），退化为顶部对齐，避免顶部不可达 */
    justify-content: safe center;
  }

  .page-content:has(#page-home.is-active) > .content-inner {
    /* 必须显式给宽：.content-inner 带 margin: 0 auto，而按 flexbox 规范，
       交叉轴存在 auto 外边距时 align-items: stretch 不再生效，宽度会退化成
       fit-content（实测从 1440 缩到 940），首页整体变窄。 */
    width: 100%;
    padding-bottom: 0;
  }

  .page-content:has(#page-home.is-active) #page-home {
    padding-top: 0;
  }

  .page-content:has(#page-home.is-active) .gen-stage {
    margin-top: 44px;
    margin-bottom: 44px;
  }
}

/* 三栏文档页（产品 / 服务 / 联系我们）：顶部必须正好落在左右栏的 sticky
   吸附位置上（= header 高 + --docs-sticky-gap）。
   否则内容从 120px 起、吸附点在 88px，页面要先滚 32px 才把侧栏「吸」上去，
   表现出来就是侧栏先跟着滚一段才停住。
   两处共用同一个令牌，改动时不会漏掉另一头。
   服务页只占中间栏，左右两栏留空，同样需要这个顶距。 */
#page-products,
#page-services,
#page-contact {
  padding-top: var(--docs-sticky-gap);
}

.page-panel.is-active {
  display: block;
  animation: fade-in 0.28s var(--ease);
}

@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ==========================================================================
   标题字号：全站唯一一套
   h1 / h2 / h3 各只有一个字号与行高，任何页面、任何区块都不再单独覆盖。
   需要不同观感时改标签层级（h1 → h2 → h3），不要改字号。
   改档位只动 :root 里的 --h1-size / --h2-size / --h3-size（及对应行高）。

   这里刻意只声明 font-size 与 line-height：
   字重、颜色、字距、外边距仍由各区块规则决定 —— 服务页深色流程块里的 h3
   靠继承拿白色，此处若写死 color 会把它压成深色。

   改造前同一批标签共用了 12 种字号（服务 hero 44 / 案例·联系·下载 48 /
   服务区块 28 / 服务 CTA 26 / 服务卡片 21 / 服务步骤 17 / 联系卡片 21 /
   文档 32·20·16 / 案例卡片 16 …），现已全部清除。
   ========================================================================== */
h1 {
  font-size: var(--h1-size);
  line-height: var(--h1-line);
}

h2 {
  font-size: var(--h2-size);
  line-height: var(--h2-line);
}

h3 {
  font-size: var(--h3-size);
  line-height: var(--h3-line);
}

.content-inner h1 {
  margin: 0 0 8px;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: rgb(var(--gray-900));
}

.content-inner .eyebrow {
  margin: 0 0 10px;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  color: rgb(var(--gray-500));
}

.content-inner .lead {
  margin: 0 0 32px;
  font-size: 18px;
  line-height: 1.7;
  color: rgb(var(--gray-600));
}

.content-inner p {
  margin: 0 0 20px;
}

/* ==========================================================================
   展示动效：左侧 App UI 生成 / 右侧 IDE 代码输入
   —— 纯代码绘制（DOM + CSS + JS），无任何图片/视频
   —— 由 main.js 的单一时间轴驱动，约 15s 一轮循环
   ========================================================================== */
.gen-stage {
  display: grid;
  /* 左侧 App UI 为主角，右侧 IDE 占比减小 */
  grid-template-columns: 1.5fr 1fr;
  /* 单行 + 固定行高：防止内容把舞台顶开 */
  grid-template-rows: 100%;
  gap: 24px;
  height: 600px; /* 皇上指定：高度 600 */
  margin: 40px 0 48px;
}

.gen-pane {
  position: relative;
  min-width: 0;
  border-radius: 6px;
  background-color: rgb(var(--background-light));
  border: 2.5px solid rgb(var(--gray-900));
  overflow: hidden;
  /* 初始态直接隐藏，不依赖 JS 先补 class；避免首帧先把外框完整绘制出来。
     入场动画只在 .is-live 存在时挂载，循环时移除再添加即可重新播放。 */
  opacity: 0;
  transform: translateY(8px);
  animation: none;
}

@keyframes gen-pane-enter {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes gen-pane-exit {
  from {
    opacity: 1;
    transform: none;
  }
  to {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* 入场时序（由 CSS 动画自身控制）：
   0.0–0.5s  空白
   0.5–1.0s  右侧代码框渐显
   1.5–2.0s  左侧 UI 渐显 */
.gen-stage.is-live .gen-pane {
  animation-name: gen-pane-enter;
  animation-duration: 0.5s;
  animation-timing-function: var(--ease);
  animation-fill-mode: both;
}

.gen-stage.is-live .gen-pane--ide {
  animation-delay: 0.5s;
}

.gen-stage.is-live .gen-pane--app {
  animation-delay: 1.5s;
}

/* 循环末尾：两侧一起渐隐。
   必须用 !important 压过 .gen-stage.is-live .gen-pane--ide/--app 的
   animation-delay（权重 0,3,0 高于本条的 0,2,0），否则渐隐会被入场延迟
   推后 0.5s / 1.5s，在重置前根本来不及播放，表现为「直接消失」。 */
.gen-stage.is-fading-out .gen-pane {
  animation-name: gen-pane-exit !important;
  animation-duration: 0.5s !important;
  animation-delay: 0s !important;
  animation-timing-function: var(--ease) !important;
  animation-fill-mode: both !important;
}

/* 从后台恢复时不重播入场，直接恢复到可见状态。 */
.gen-stage.is-restored .gen-pane {
  animation: none;
  opacity: 1;
  transform: none;
}

/* --------------------------------------------------------------------------
   左侧：App UI
   -------------------------------------------------------------------------- */
.app-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 14px;
  gap: 12px;
  background-color: rgb(var(--gray-50) / 40%);
}

/* 顶部栏 */
.app-topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 44px;
  flex-shrink: 0;
  padding: 0 14px;
  border-radius: 10px;
  background-color: rgb(var(--background-light));
  border: 1px solid rgb(var(--gray-500) / 8%);
}

.app-dot {
  width: 18px;
  height: 18px;
  border-radius: 5px;
  background-color: rgb(var(--gray-900));
  flex-shrink: 0;
}

.app-title-bar {
  height: 8px;
  width: 96px;
  border-radius: 4px;
  background-color: rgb(var(--gray-200));
}

.app-topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
}

.app-chip {
  width: 52px;
  height: 20px;
  border-radius: 6px;
  background-color: rgb(var(--gray-100));
}

.app-avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background-color: rgb(var(--gray-300));
}

/* 主体：侧栏 + 主区 */
.app-body {
  display: flex;
  gap: 12px;
  flex: 1;
  min-height: 0;
}

.app-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 156px;
  flex-shrink: 0;
  padding: 16px 14px;
  border-radius: 10px;
  background-color: rgb(var(--background-light));
  border: 1px solid rgb(var(--gray-500) / 8%);
}

.app-side-item {
  height: 12px;
  border-radius: 6px;
  background-color: rgb(var(--gray-100));
}

.app-side-item:nth-child(1) {
  background-color: rgb(var(--gray-800));
  height: 14px;
}

.app-main {
  display: flex;
  flex-direction: column;
  gap: 16px;
  flex: 1;
  min-width: 0;
}

.app-h1 {
  height: 16px;
  width: 42%;
  border-radius: 5px;
  background-color: rgb(var(--gray-800));
}

.app-h2 {
  height: 10px;
  width: 62%;
  border-radius: 5px;
  background-color: rgb(var(--gray-200));
}

/* 统计卡片 */
.app-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  flex-shrink: 0;
}

.app-card {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  height: 92px;
  padding: 0 16px;
  border-radius: 10px;
  background-color: rgb(var(--background-light));
  border: 1px solid rgb(var(--gray-500) / 8%);
}

.app-card-label {
  height: 8px;
  width: 56%;
  border-radius: 4px;
  background-color: rgb(var(--gray-200));
}

.app-card-value {
  height: 16px;
  width: 40%;
  border-radius: 5px;
  background-color: rgb(var(--gray-900));
}

/* 图表 + 圆环 */
.app-panels {
  display: grid;
  grid-template-columns: 1fr 180px;
  gap: 12px;
  flex: 1 1 auto;
  min-height: 0;
}

.app-chart {
  display: flex;
  align-items: center;
  padding: 14px;
  border-radius: 10px;
  background-color: rgb(var(--background-light));
  border: 1px solid rgb(var(--gray-500) / 8%);
}

.app-chart svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* 折线：用 dash 偏移做“绘制”效果 */
.chart-line {
  stroke-dasharray: 340;
  stroke-dashoffset: 340;
}

.is-draw .chart-line {
  animation: chart-draw 1.5s var(--ease) forwards;
}

@keyframes chart-draw {
  to {
    stroke-dashoffset: 0;
  }
}

.chart-area {
  opacity: 0;
}

.is-draw .chart-area {
  animation: fade-in-up 0.7s var(--ease) 0.9s forwards;
}

.chart-dots circle {
  fill: rgb(var(--gray-900));
  opacity: 0;
  transform-origin: 252px 20px;
}

.is-draw .chart-dots circle {
  animation: dot-pop 0.4s var(--ease) 1.35s forwards;
}

@keyframes dot-pop {
  0% {
    opacity: 0;
    transform: scale(0);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes fade-in-up {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* 圆环进度 */
.app-ring {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background-color: rgb(var(--background-light));
  border: 1px solid rgb(var(--gray-500) / 8%);
}

.app-ring svg {
  width: 124px;
  height: 124px;
  transform: rotate(-90deg);
}

.ring-track {
  fill: none;
  stroke: rgb(var(--gray-100));
  stroke-width: 7;
}

.ring-value {
  fill: none;
  stroke: rgb(var(--gray-900));
  stroke-width: 7;
  stroke-linecap: round;
  /* 周长 = 2πr = 2 × π × 32 ≈ 201 */
  stroke-dasharray: 201;
  stroke-dashoffset: 201;
}

.is-ring .ring-value {
  animation: ring-fill 1.6s var(--ease) forwards;
}

@keyframes ring-fill {
  to {
    stroke-dashoffset: 36; /* 约 82% */
  }
}

/* 列表行 */
.app-rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex-shrink: 0;
}

.app-row {
  height: 34px;
  border-radius: 8px;
  background-color: rgb(var(--background-light));
  border: 1px solid rgb(var(--gray-500) / 8%);
}

/* 通用「生长」动画：用于所有用 data-grow 标记的元素
   横向条状元素用 scaleX 从左侧「长出来」，更像内容正在生成；
   卡片类（.app-card / .app-chart / .app-ring）用纵向淡入，避免文字被压扁。 */
[data-grow] {
  opacity: 0;
  transform: translateY(6px);
  transform-origin: center;
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
}

[data-grow].is-shown {
  opacity: 1;
  transform: none;
}

/* 条状元素：从左侧 0 宽长到满宽 */
.app-side-item[data-grow],
.app-h1[data-grow],
.app-h2[data-grow],
.app-row[data-grow] {
  transform: translateY(6px) scaleX(0.2);
  transform-origin: left center;
}

.app-side-item[data-grow].is-shown,
.app-h1[data-grow].is-shown,
.app-h2[data-grow].is-shown,
.app-row[data-grow].is-shown {
  transform: none;
}

/* --------------------------------------------------------------------------
   左侧重构：图片编辑器 UI
   —— 用画布、属性、图层与时间轴填满整个工作区
   —— 保持浅灰底 + 极淡蓝/粉/黄的彩色点缀
   -------------------------------------------------------------------------- */
.image-editor {
  --editor-ink: #30363d;
  --editor-muted: #8b949e;
  --editor-line: rgba(48, 54, 61, 0.12);
  --editor-soft: rgba(255, 255, 255, 0.78);
  --editor-blue: #b8d5ee;
  --editor-pink: #f2c8c2;
  --editor-yellow: #f4e2a9;
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  background: #f3f5f6;
  color: var(--editor-ink);
  font-family: "DM Sans", "PingFang SC", sans-serif;
}

.editor-topbar {
  display: flex;
  align-items: center;
  height: 48px;
  flex: 0 0 48px;
  padding: 0 16px;
  border-bottom: 1px solid var(--editor-line);
  background: rgba(255, 255, 255, 0.9);
}

.editor-brand,
.editor-document,
.editor-top-actions,
.editor-canvas-header,
.editor-canvas-footer,
.editor-status,
.timeline-head,
.inspector-title,
.inspector-section-title,
.inspector-row,
.adjust-row,
.layer-row {
  display: flex;
  align-items: center;
}

.editor-brand {
  gap: 9px;
  /* 品牌区按内容定宽、永不压缩：若允许收缩，内部 nowrap 的文字会向右
     溢出并压住右侧描述，所以这里不能用 min-width: 0。
     空间不足时统一由右侧描述截断来吸收。 */
  flex: 0 0 auto;
}

.editor-brand-mark {
  display: grid;
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
  place-items: center;
  border-radius: 7px;
  background: #30363d;
  color: #fff;
  font-size: 13px;
  font-weight: 700;
}

.editor-brand-name,
.editor-document,
.editor-top-actions,
.editor-canvas-header,
.editor-canvas-footer,
.editor-status,
.timeline-head,
.inspector-section-title,
.inspector-row,
.adjust-row,
.layer-row {
  font-size: 10px;
  letter-spacing: 0.03em;
}

.editor-brand-name {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.04em;
  white-space: nowrap;
  /* 标题永不压缩，空间不足时由右侧描述截断 */
  flex-shrink: 0;
}

.editor-document {
  gap: 8px;
  /* 与左侧品牌标题拉开距离，避免紧贴 */
  margin-left: 14px;
  min-width: 0;
  font-size: 13px;
  color: var(--editor-muted);
}

/* 文案较长：窄屏时截断而不是把标题栏撑破 */
.editor-document span + span {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.editor-document-dot,
.status-live {
  width: 7px;
  height: 7px;
  flex: 0 0 7px;
  border-radius: 50%;
  background: #98c7b1;
}

.editor-top-actions {
  gap: 8px;
  margin-left: auto;
}

.editor-action,
.editor-export {
  padding: 6px 10px;
  border: 1px solid var(--editor-line);
  border-radius: 6px;
  color: var(--editor-muted);
}

.editor-export {
  border-color: #30363d;
  background: #30363d;
  color: #fff;
}

.editor-workspace {
  display: grid;
  grid-template-columns: 62px minmax(0, 1fr) 194px;
  flex: 1 1 auto;
  min-height: 0;
  gap: 0;
}

.editor-tools {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 12px 7px;
  border-right: 1px solid var(--editor-line);
  background: rgba(255, 255, 255, 0.56);
}

.editor-tool {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 46px;
  height: 45px;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--editor-muted);
  font-size: 9px;
}

.editor-tool b {
  font-size: 16px;
  line-height: 16px;
  font-weight: 500;
}

.editor-tool em {
  font-style: normal;
  font-size: 8px;
}

.editor-tool.is-active {
  border-color: rgba(104, 146, 181, 0.28);
  background: rgba(184, 213, 238, 0.32);
  color: #49677d;
}

.editor-tool--bottom {
  margin-top: auto;
}

.editor-canvas-area {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  padding: 12px 14px 10px;
  background: #eef1f2;
}

.editor-canvas-header,
.editor-canvas-footer {
  justify-content: space-between;
  min-height: 20px;
  color: var(--editor-muted);
}

.editor-canvas-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 auto;
  min-height: 0;
  padding: 10px 0;
}

.editor-canvas {
  position: relative;
  width: min(100%, 540px);
  aspect-ratio: 1.42;
  overflow: hidden;
  border: 1px solid rgba(48, 54, 61, 0.18);
  border-radius: 5px;
  background: #dce5e7;
  box-shadow: 0 10px 22px rgba(48, 54, 61, 0.08);
}

.canvas-ruler {
  position: absolute;
  z-index: 3;
  display: flex;
  color: rgba(48, 54, 61, 0.42);
  font-size: 8px;
  pointer-events: none;
}

.canvas-ruler--top {
  top: 4px;
  right: 14px;
  left: 14px;
  justify-content: space-between;
}

.canvas-ruler--left {
  top: 20px;
  bottom: 14px;
  left: 4px;
  flex-direction: column;
  justify-content: space-between;
}

.canvas-grid {
  position: absolute;
  inset: 20px 14px 14px 22px;
  display: grid;
  grid-template-columns: 1.25fr 0.8fr;
  grid-template-rows: 1.12fr 0.72fr 0.42fr;
  gap: 9px;
  opacity: 0;
  transform: translateY(8px);
}

.canvas-grid.is-shown {
  animation: canvas-build 0.65s var(--ease) forwards;
}

@keyframes canvas-build {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.canvas-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  overflow: hidden;
  padding: 14px;
  border: 1px solid rgba(48, 54, 61, 0.13);
  border-radius: 7px;
  background: rgba(255, 255, 255, 0.76);
  color: #4e5963;
  opacity: 0;
  transform: translateY(7px) scale(0.98);
}

.canvas-panel.is-shown {
  animation: panel-build 0.58s var(--ease) forwards;
}

@keyframes panel-build {
  from { opacity: 0; transform: translateY(7px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

/* 主视觉卡片是画布上唯一的可交互元素：显示后才接收点击，
   避免「还没出现就能点到」；未显示时交给下层，不干扰其它演示。 */
.canvas-panel--hero {
  grid-row: 1 / span 2;
  background: linear-gradient(135deg, rgba(211, 231, 246, 0.8), rgba(255, 238, 228, 0.84));
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  pointer-events: none;
  transition: box-shadow 0.25s var(--ease), border-color 0.25s var(--ease);
}

.canvas-panel--hero.is-shown {
  pointer-events: auto;
}

.canvas-panel--hero:hover {
  border-color: rgba(70, 125, 153, 0.42);
  box-shadow: 0 6px 16px rgba(48, 54, 61, 0.12);
}

/* 键盘聚焦时给出明确的焦点环，不用 outline:none 抹掉可见焦点。 */
.canvas-panel--hero:focus-visible {
  border-color: rgba(70, 125, 153, 0.72);
  box-shadow: 0 0 0 2px rgba(70, 125, 153, 0.38), 0 6px 16px rgba(48, 54, 61, 0.12);
  outline: none;
}

.canvas-panel--hero:hover .canvas-panel-button,
.canvas-panel--hero:focus-visible .canvas-panel-button {
  background: #30363d;
}

.canvas-panel--mini {
  align-items: center;
  justify-content: center;
  background: rgba(244, 231, 218, 0.72);
}

.canvas-panel--stat {
  background: rgba(220, 234, 218, 0.72);
}

.canvas-panel--footer {
  grid-column: 1 / -1;
  flex-direction: row;
  align-items: center;
  justify-content: space-around;
  background: rgba(255, 255, 255, 0.64);
}

.canvas-panel-kicker {
  color: #6d8190;
  font-size: 9px;
}

.canvas-panel strong {
  color: #30363d;
  font-size: clamp(14px, 1.7vw, 22px);
  font-weight: 700;
}

.canvas-panel-line {
  display: block;
  width: 72%;
  height: 5px;
  border-radius: 4px;
  background: rgba(48, 54, 61, 0.22);
}

.canvas-panel-line--short {
  width: 48%;
  background: rgba(48, 54, 61, 0.12);
}

.canvas-panel-button {
  width: fit-content;
  margin-top: 5px;
  padding: 5px 9px;
  border-radius: 5px;
  background: rgba(48, 54, 61, 0.78);
  color: #fff;
  font-size: 9px;
}

.canvas-mini-icon {
  display: grid;
  width: 24px;
  height: 24px;
  place-items: center;
  border: 1px dashed rgba(48, 54, 61, 0.35);
  border-radius: 6px;
  color: #637f8e;
  font-size: 18px;
}

.canvas-panel--stat strong {
  color: #567563;
}

.canvas-panel--stat i {
  display: block;
  height: 5px;
  border-radius: 5px;
  background: rgba(48, 54, 61, 0.12);
}

.canvas-panel--stat i b {
  display: block;
  width: 68%;
  height: 100%;
  border-radius: inherit;
  background: #9ec8ae;
}

.canvas-panel--footer span {
  color: #82909a;
  font-size: 8px;
}

.is-resizing .canvas-panel--hero {
  animation: component-resize 1.15s var(--ease) forwards;
}

@keyframes component-resize {
  0% { transform: translateY(0) scale(1); }
  45% { transform: translateY(-2px) scale(1.025); }
  100% { transform: translateY(0) scale(1); }
}

.editor-canvas-label {
  position: absolute;
  right: 10px;
  bottom: 8px;
  padding: 5px 7px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.7);
  color: #53606a;
  font-size: 9px;
  opacity: 0;
}

.is-labeling .editor-canvas-label {
  animation: fade-in-up 0.55s var(--ease) forwards;
}

.canvas-resize-note {
  position: absolute;
  left: 24px;
  bottom: 8px;
  padding: 4px 6px;
  border-radius: 4px;
  background: rgba(255, 244, 213, 0.88);
  color: #927443;
  font-size: 8px;
  opacity: 0;
}

.canvas-resize-note.is-shown {
  animation: fade-in-up 0.55s var(--ease) forwards;
}

.editor-inspector {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
  overflow: hidden;
  border-left: 1px solid var(--editor-line);
  background: rgba(255, 255, 255, 0.7);
}

.inspector-title,
.inspector-section {
  padding: 12px 13px;
  border-bottom: 1px solid var(--editor-line);
}

.inspector-title,
.inspector-section-title,
.inspector-row {
  justify-content: space-between;
}

.inspector-title {
  font-size: 11px;
  font-weight: 700;
}

.inspector-section-title {
  margin-bottom: 12px;
  color: var(--editor-muted);
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.layer-preview {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-bottom: 12px;
  padding: 7px;
  border-radius: 6px;
  background: rgba(184, 213, 238, 0.28);
}

.layer-thumb {
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: 4px;
  background: linear-gradient(135deg, #c9e4f5, #f2c8c2 55%, #d6e5b7);
}

.layer-preview b,
.layer-preview em {
  display: block;
  overflow: hidden;
  max-width: 120px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.layer-preview b {
  font-size: 9px;
}

.layer-preview em {
  margin-top: 3px;
  color: var(--editor-muted);
  font-size: 8px;
  font-style: normal;
}

.inspector-row {
  margin-top: 8px;
  color: var(--editor-muted);
}

.inspector-row b,
.adjust-row em {
  color: var(--editor-ink);
  font-size: 9px;
  font-weight: 600;
  font-style: normal;
}

.inspector-slider,
.adjust-row i {
  position: relative;
  display: block;
  height: 4px;
  margin-top: 8px;
  border-radius: 4px;
  background: #e4e8ea;
}

.inspector-slider i {
  display: block;
  width: 68%;
  height: 100%;
  border-radius: inherit;
  background: #a8c6de;
}

.adjust-row {
  gap: 6px;
  margin: 9px 0;
  color: var(--editor-muted);
}

.adjust-row > span {
  width: 50px;
  flex: 0 0 50px;
}

.adjust-row i {
  flex: 1;
  margin-top: 0;
}

.adjust-row i b {
  display: block;
  width: 62%;
  height: 100%;
  border-radius: inherit;
  background: #e4c8b9;
}

.adjust-row em {
  width: 24px;
  text-align: right;
}

.editor-timeline {
  flex: 0 0 82px;
  padding: 10px 16px 7px;
  border-top: 1px solid var(--editor-line);
  background: rgba(255, 255, 255, 0.72);
}

.timeline-head {
  justify-content: space-between;
  margin-bottom: 7px;
  color: var(--editor-muted);
}

.timeline-ruler {
  display: flex;
  justify-content: space-between;
  height: 5px;
  margin-left: 78px;
  border-top: 1px solid var(--editor-line);
}

.timeline-ruler i {
  width: 1px;
  height: 5px;
  background: var(--editor-line);
}

.timeline-track {
  position: relative;
  height: 15px;
  margin-top: 5px;
  padding-left: 78px;
}

.timeline-track-name {
  position: absolute;
  left: 0;
  top: 2px;
  color: var(--editor-muted);
  font-size: 8px;
}

.timeline-track b,
.timeline-track i {
  position: absolute;
  top: 2px;
  height: 11px;
  border-radius: 3px;
}

.timeline-track b {
  left: 82px;
  width: 67%;
  background: rgba(184, 213, 238, 0.72);
}

.timeline-track i {
  left: 44%;
  width: 26%;
  background: rgba(242, 200, 194, 0.7);
}

.timeline-track--second {
  margin-top: 2px;
}

.timeline-track--second b {
  left: 18%;
  width: 48%;
  background: rgba(244, 226, 169, 0.72);
}

.timeline-track--second i {
  left: 62%;
  width: 22%;
  background: rgba(184, 213, 238, 0.5);
}

.editor-status {
  flex: 0 0 24px;
  gap: 6px;
  padding: 0 16px;
  border-top: 1px solid var(--editor-line);
  background: #30363d;
  color: rgba(255, 255, 255, 0.72);
}

.status-live {
  background: #a9d9bd;
}

.status-progress {
  display: block;
  width: 72px;
  height: 3px;
  margin-left: auto;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.2);
}

.status-progress i {
  display: block;
  width: 68%;
  height: 100%;
  border-radius: inherit;
  background: #c8e5d1;
}

.editor-status b {
  font-size: 9px;
  font-weight: 500;
}

.editor-status.is-rendering .status-progress i {
  animation: status-sweep 1.25s var(--ease) forwards;
}

@keyframes status-sweep {
  from { width: 68%; }
  50% { width: 92%; }
  to { width: 78%; }
}

.editor-canvas.is-commit .editor-canvas-label {
  animation: commit-pulse 0.85s var(--ease) forwards;
}

@keyframes commit-pulse {
  0% { opacity: 0.78; transform: translateY(0); }
  45% { opacity: 1; transform: translateY(-2px); background: rgba(200, 229, 209, 0.82); }
  100% { opacity: 0.9; transform: translateY(0); }
}

/* 构造动画：画布、组件、标签和面板依次被释放。 */
.editor-canvas.is-reveal,
.editor-canvas.is-labeling,
.editor-canvas.is-resizing {
  overflow: hidden;
}

.editor-inspector .is-shown {
  transform: none;
}

/* 窄屏下收窄属性栏，仍保留画布与时间轴的完整结构。
   【已停用】页面最小宽度锁定 1280px（--page-min-width），本断点不会生效。
   恢复方法：删掉下面那行 @media not all { 与本块结尾多出的那个 }。 */
@media not all {
@media (max-width: 900px) {
  .editor-workspace {
    grid-template-columns: 54px minmax(0, 1fr) 160px;
  }
  .editor-topbar {
    padding: 0 10px;
  }
  .editor-brand-name {
    font-size: 15px;
  }
  .editor-document {
    margin-left: 10px;
    font-size: 12px;
  }
  .editor-action {
    display: none;
  }
}
}

/* 更窄时隐藏文档名，保证品牌标题不被挤压。
   【已停用】同因，见上方 900px 断点说明。 */
@media not all {
@media (max-width: 560px) {
  .editor-document {
    display: none;
  }
  .editor-brand-name {
    font-size: 14px;
  }
}
}

/* --------------------------------------------------------------------------
   右侧：IDE（白底线条风格，无阴影、无拟物）
   -------------------------------------------------------------------------- */
.ide-shell {
  --pixel-ink: #171717;
  --pixel-muted: #858585;
  --pixel-line: #bdbdbd;
  --pixel-deep: #f2f2f2;
  --pixel-panel: #ffffff;
  --pixel-dark: #262626;
  --pixel-mid: #707070;
  --pixel-light: #dedede;
  --pixel-num: #4d7a58; /* 行号绿：低饱和，长时间看也不刺眼 */
  display: flex;
  flex-direction: column;
  /* 用绝对定位撑满 .gen-pane：grid 轨道高度无法作为 height:100% 的基准 */
  position: absolute;
  inset: 0;
  background-color: var(--pixel-deep);
  color: var(--pixel-ink);
  overflow: hidden;
  image-rendering: pixelated;
}

.ide-topbar {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 14px;
  height: 44px;
  flex-shrink: 0;
  padding: 0 14px;
  border-bottom: 3px solid var(--pixel-line);
  background: #ffffff;
  box-shadow: inset 0 -3px 0 rgba(23, 23, 23, 0.08);
}

.ide-lights {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

/* 像素风：窗口灯改为硬边实心像素块 */
.ide-lights i {
  width: 10px;
  height: 10px;
  border-radius: 0;
  border: 2px solid var(--pixel-dark);
  background-color: var(--pixel-light);
  box-shadow: 2px 2px 0 rgba(23, 23, 23, 0.25);
}

.ide-lights i:nth-child(2) {
  background-color: #ffffff;
}

.ide-lights i:nth-child(3) {
  background-color: var(--pixel-mid);
}

.ide-filename {
  font-size: 13px; /* 窗口标题栏文件名 */
  font-weight: 500;
  color: var(--pixel-ink);
  font-family: var(--font-mono);
  letter-spacing: 0.01em;
}

.ide-tabs {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

/* 像素风标签：沿用系统窗口按钮的红/绿色块，硬边不带圆角 */
.ide-tab {
  width: 24px;
  height: 10px;
  border-radius: 0;
  border: 2px solid var(--pixel-dark);
  box-shadow: 2px 2px 0 rgba(23, 23, 23, 0.25);
}

.ide-tab:nth-child(1) {
  background-color: #ff5f57; /* 关闭：红 */
}

.ide-tab:nth-child(2) {
  background-color: #28c840; /* 最大化：绿 */
}

/* 激活项用更重的像素投影区分，不覆盖上面的色块 */
.ide-tab.is-active {
  box-shadow: 3px 3px 0 rgba(23, 23, 23, 0.42);
}

/* 代码区：亮色像素终端，背景用极淡网格增加抽象层次 */
.ide-body {
  position: relative;
  display: flex;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background-color: var(--pixel-deep);
  background-image:
    linear-gradient(rgba(23, 23, 23, 0.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(23, 23, 23, 0.055) 1px, transparent 1px);
  background-size: 16px 16px;
}

.ide-body::before,
.ide-body::after {
  position: absolute;
  z-index: 0;
  display: block;
  width: 54px;
  height: 54px;
  content: "";
  pointer-events: none;
}

.ide-body::before {
  top: 24px;
  right: 18px;
  border: 3px solid rgba(23, 23, 23, 0.18);
  box-shadow: 8px 8px 0 rgba(23, 23, 23, 0.08);
}

.ide-body::after {
  right: 42px;
  bottom: 32px;
  border: 3px solid rgba(23, 23, 23, 0.16);
  box-shadow: -8px -8px 0 rgba(23, 23, 23, 0.07);
}

/* 代码字号与行高：改这两处即可整体缩放代码区 */
.ide-gutter,
.ide-code {

  --code-font: 18px; /* 11 → 13 → 16 → 18，继续加大 */
  --code-line: 30px; /* 行距收紧（字号18 / 行高30），必须与 main.js 的 LINE_H 一致 */
}

.ide-gutter {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  width: 56px;
  min-height: 0;
  padding: 16px 12px 16px 0;
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--code-font);
  line-height: var(--code-line);
  color: var(--pixel-num);
  user-select: none;
  border-right: 3px solid var(--pixel-line);
  background: rgba(255, 255, 255, 0.72);
  overflow: hidden;
}

.ide-gutter::before {
  position: absolute;
  top: 0;
  right: -3px;
  bottom: 0;
  width: 3px;
  background: var(--pixel-dark);
  opacity: 0.7;
  content: "";
}

/* 行号内层：与代码同步位移，保证行号始终对齐行 */
.ide-gutter-inner,
.ide-lines {
  will-change: transform;
}

.ide-code {
  position: relative;
  z-index: 1;
  flex: 1;
  min-width: 0;
  min-height: 0; /* 关键：防止被内容撑高，否则滚动定位失准 */
  padding: 16px 16px;
  /* 允许长行横向延伸：纵向裁掉，横向可滚 */
  overflow-x: auto;
  overflow-y: hidden;
  font-family: var(--font-mono);
  font-size: var(--code-font);
  line-height: var(--code-line);
  color: var(--pixel-ink);
  tab-size: 4;
}

/* 隐藏横向滚动条，保持画面干净（内容仍可滚动） */
.ide-code {
  scrollbar-width: none; /* Firefox */
}

.ide-code::-webkit-scrollbar {
  display: none; /* Chrome / Edge */
}

/* 代码行容器：整体向上位移，形成「逐行滚动」的生成感 */
.ide-lines {
  will-change: transform;
}

.ide-line {
  white-space: pre;
}

/* ---------- C++ 语法高亮 ----------
   亮底上的低饱和配色：整体仍是克制的黑白灰基调，
   只给关键字、类型、字符串、数字各加一种可区分的色相，
   保证在浅灰背景上对比度足够、长时间看不刺眼。 */
.tk-com {
  color: #8a8a8a;
  font-style: italic;
}

.tk-str {
  color: #2e8b57; /* 字符串：松绿 */
}

.tk-pre {
  color: #8a4fbf; /* 预处理指令：紫 */
}

.tk-key {
  color: #1f4fd8; /* 关键字：蓝 */
  font-weight: 700;
}

.tk-type {
  color: #0f7b8a; /* 类型：青 */
}

.tk-num {
  color: #c2571a; /* 数字：橙 */
}

/* 光标：紧跟在已输入文字之后，闪烁表示正在输入 */
.ide-caret {
  display: inline-block;
  width: 10px;
  height: 18px;
  margin-left: 3px;
  vertical-align: -3px;
  background-color: var(--pixel-dark);
  box-shadow: 2px 2px 0 rgba(23, 23, 23, 0.22);
  animation: caret-blink 1.05s steps(1) infinite;
}

.ide-caret.is-paused {
  opacity: 1;
  animation: none;
}

@keyframes caret-blink {
  0%,
  49% {
    opacity: 1;
  }
  50%,
  100% {
    opacity: 0;
  }
}

/* 状态栏 */
.ide-statusbar {
  display: flex;
  align-items: center;
  gap: 14px;
  height: 26px;
  flex-shrink: 0;
  padding: 0 14px;
  border-top: 2px solid rgb(var(--gray-900));
}

.ide-status-seg {
  width: 64px;
  height: 6px;
  border-radius: 0;
  background-color: rgb(var(--gray-300));
}

.ide-status-seg--short {
  width: 34px;
}

.ide-status-seg--right {
  margin-left: auto;
  width: 46px;
}

/* 窄屏：上下堆叠
   【已停用】页面最小宽度锁定 1280px（--page-min-width），本断点不会生效。
   恢复方法：删掉下面那行 @media not all { 与本块结尾多出的那个 }。 */
@media not all {
@media (max-width: 900px) {
  .gen-stage {
    grid-template-columns: 1fr;
    height: auto;
  }

  .gen-pane {
    height: 480px;
  }
}
}

/* ==========================================================================
   三栏文档布局（左 = 列表 / 中 = 正文 / 右 = 书签）
   布局以 agnes-ai.com 的文档页为底本（左 288px / 右 264px / 间距 48px），
   在其基础上做了本地调整：左右两栏各收到 240px / 200px，栏间距加大到 56px。
   两栏的文字都很短（最长条目 5 字、最长书签 8 字），收窄后仍有余量，
   腾出的宽度全部给中间正文 —— 1440 视口下正文由 712px 增至 776px。
   左右两栏 sticky，中间正文跟随页面滚动。
   配色未照搬参考站的深色主题，沿用本站自身的纯白极简 + gray 色板。
   ========================================================================== */
.docs-layout {
  display: grid;
  /* 中间栏用 minmax(0, 1fr)：允许收缩到内容宽以下，
     否则长链接 / 长单词会把整列撑破。 */
  grid-template-columns: 240px minmax(0, 1fr) 200px;
  gap: 56px;
  /* 关键：sticky 需要 grid item 高度等于内容高度，
     若被拉伸成整行高，sticky 就不会产生任何位移。 */
  align-items: start;
}

/* 单栏页面（服务页）：内容只有一篇，不需要左侧列表与右侧书签。
   这里把三栏网格压成单列，正文因此不再被锁在 776px 的中间列，
   而是拿到整个框架宽度 —— 服务页的卡片区要有这个宽度才排得下 3 列。

   这是与产品页 / 联系页各自独立的一套框架宽度：
   产品页 / 联系页仍是 240/1fr/200 的三栏，改这里不会波及它们。
   （正文自身再用 --service-max 兜一道最大宽，见下面的 max-width。） */
.docs-layout--single {
  grid-template-columns: minmax(0, 1fr);
}

.docs-layout--single > .docs-main {
  grid-column: 1;
  /* 框架给满、内容居中收窄：两侧留白由 margin auto 平分。
     比在网格里写死左右列宽更经得起视口变化 ——
     窄视口下留白自动压缩，不会像固定列那样把正文挤扁。 */
  max-width: var(--service-max);
  margin-inline: auto;
}

/* 中栏留空页（联系页）：不写左右两栏，正文仍钉在中间列。
   列宽与栏间距原样保留，正文宽度与产品页严格一致（776px），
   跨页切换时不跳变，左右两侧自然留白。
   （比塞两个空 <aside> 干净：空导航区会被读屏软件念出来。） */
.docs-layout--center > .docs-main {
  grid-column: 2;
}

/* 左右两栏吸附在 header 下方；顶距与 html 的 scroll-padding-top 保持一致，
   这样锚点跳转后标题落点与侧栏顶部对齐。 */
.docs-side,
.docs-toc {
  position: sticky;
  top: calc(var(--header-height-actual, var(--header-height)) + var(--docs-sticky-gap));
  /* 视口高度扣掉 header、侧栏顶距与底部留白，超出部分在栏内滚动。
     栏顶在 header + gap，这里必须同步减掉 gap，
     否则栏底会溢出视口，最下面几个链接永远看不到。 */
  max-height: calc(100dvh - var(--header-height-actual, var(--header-height)) - var(--docs-sticky-gap) - 24px);
  overflow-y: auto;
  /* 防止栏内滚到边界后继续带动整页滚动 */
  overscroll-behavior: contain;
  /* 栏宽固定，滚动条只占栏内宽度，不影响三栏整体布局；
     做成细的 + 透明轨道，接近悬浮观感。
     必须显式声明：html 上的 scrollbar-width: none 会被继承下来。 */
  scrollbar-width: thin;
  /* 默认完全透明，只有鼠标指向该栏时才显形 */
  scrollbar-color: transparent transparent;
}

/* 鼠标进入该栏：滚动条显形（Firefox 走 scrollbar-color） */
.docs-side:hover,
.docs-toc:hover {
  scrollbar-color: rgb(var(--gray-900) / 22%) transparent;
}

.docs-side::-webkit-scrollbar,
.docs-toc::-webkit-scrollbar {
  width: 6px;
}

.docs-side::-webkit-scrollbar-track,
.docs-toc::-webkit-scrollbar-track {
  background-color: transparent;
}

/* 滑块默认透明，但轨道仍占 6px ——
   栏内内容宽度不会因滚动条的显隐而跳动。 */
.docs-side::-webkit-scrollbar-thumb,
.docs-toc::-webkit-scrollbar-thumb {
  border-radius: 3px;
  background-color: transparent;
}

.docs-side:hover::-webkit-scrollbar-thumb,
.docs-toc:hover::-webkit-scrollbar-thumb {
  background-color: rgb(var(--gray-900) / 22%);
}

/* 指向滑块本身时再加深一档 */
.docs-side::-webkit-scrollbar-thumb:hover,
.docs-toc::-webkit-scrollbar-thumb:hover {
  background-color: rgb(var(--gray-900) / 38%);
}

/* ---------- 左栏：文档列表 ---------- */
.docs-group + .docs-group {
  margin-top: 32px; /* 参考站 lg:mt-8 */
}

/* 分组标题：对齐参考站 sidebar-group-header（pl-4 / mb-2.5 / font-semibold） */
.docs-group-title {
  margin: 0 0 10px;
  padding-left: 16px;
  font-size: 14px;
  line-height: 24px; /* 参考站 lg:leading-6 */
  font-weight: 600;
  color: rgb(var(--gray-900));
}

.docs-group-list {
  display: flex;
  flex-direction: column;
  gap: 1px; /* 参考站 space-y-px */
}

/* 行级项：一级文档项、折叠按钮共用这一套基础外观。
   用 flex 而不是 block —— 折叠按钮右侧要挂 chevron。
   左内边距设为 28px，相对分组标题（16px）保持 12px 层级缩进。 */
.docs-link {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 6px 12px 6px 28px; /* py-1.5 / pr-3 / pl-7 */
  border-radius: 12px;
  font-size: 14px;
  line-height: 24px; /* 参考站 lg:leading-6 */
  color: rgb(var(--gray-700));
  text-align: left;
  transition: background-color 0.15s var(--ease), color 0.15s var(--ease);
}

/* 行级 hover：淡灰底 + 文字加深。
   底色比参考站的 5% 深一档（8%）—— 本站侧栏比参考站窄，
   行内可点区域更小，需要更明确的指向反馈。 */
.docs-link:hover {
  background-color: rgb(var(--gray-600) / 8%);
  color: rgb(var(--gray-900));
}

/* 激活项：淡底色 + 深色文字 + 实加粗。
   这里用 font-weight 而非参考站的 text-shadow 伪加粗：
   侧栏每行文字都短、且左对齐，加粗只会让行尾向右延伸，
   不会带动其他元素位移，所以不必靠伪加粗来规避跳动。 */
.docs-link.is-current {
  background-color: rgb(var(--primary) / 10%);
  color: rgb(var(--primary));
  font-weight: 600;
}

.docs-link:focus-visible {
  outline: 2px solid rgb(var(--gray-900) / 35%);
  outline-offset: 2px;
}

/* ---------- 左栏：可折叠子分组 ---------- */
/* 子分组下属项（二级项）：在基础项 28px 基础上再压深一级缩进到 40px，保持清晰树状层次 */
.docs-link-sub {
  padding-left: 40px;
}

/* 折叠按钮：继承 .docs-link 的全部外观，这里只声明它自己的行为 */
.docs-subgroup-toggle {
  cursor: pointer;
}

.docs-link-label {
  flex: 1;
  min-width: 0;
}

/* chevron 容器：高度锁行高，让箭头与首行文字垂直居中 */
.docs-chevron-box {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  height: 24px;
  margin-right: -2px; /* 参考站 -mr-0.5 */
}

.docs-chevron {
  width: 12px; /* 参考站 size-3 */
  height: 12px;
  color: rgb(var(--gray-400));
  transition: transform 75ms var(--ease), color 0.15s var(--ease);
}

.docs-subgroup-toggle:hover .docs-chevron {
  color: rgb(var(--gray-600));
}

/* 展开时箭头由「指向右」转为「指向下」 */
.docs-subgroup.is-open .docs-chevron {
  transform: rotate(90deg);
}

/* 子列表：默认收起，展开时沿用上级同样的 1px 项间距 */
.docs-sublist {
  display: none;
}

.docs-subgroup.is-open .docs-sublist {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* ---------- 中栏：文档正文 ---------- */
.docs-main {
  min-width: 0;
}

.docs-article {
  display: none;
}

.docs-article.is-current {
  display: block;
  animation: fade-in 0.28s var(--ease);
}

/* 顶部层级标识（眉标）：指明所属上级分类（如「开始使用」），作为 h4 标题样式展示。
   比 h3（18px）稍小，设为 16px，颜色与正文各级标题保持一致（--gray-900）。 */
.docs-article h4,
.docs-category {
  margin: 0 0 8px;
  font-size: 16px;
  line-height: 22px;
  font-weight: 600;
  color: rgb(var(--gray-900));
  letter-spacing: 0.01em;
}

.docs-article h1 {
  margin: 0 0 22px;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: rgb(var(--gray-900));
}

/* 文档标题上挂的跳转链接（doc.titleUrl）：外观必须与纯文本标题完全一致，
   不能出现浏览器默认的蓝色与下划线，也不接管字号/字重。 */
.docs-article h1 > a {
  color: inherit;
  text-decoration: none;
  font: inherit;
  letter-spacing: inherit;
}

.docs-article h1 > a:visited,
.docs-article h1 > a:hover,
.docs-article h1 > a:focus,
.docs-article h1 > a:active {
  color: inherit;
  text-decoration: none;
}

.docs-lead {
  margin: 0 0 28px;
  font-size: 16px;
  line-height: 1.7;
  color: rgb(var(--gray-500));
}

.docs-article h2 {
  margin: 36px 0 18px;
  font-weight: 600;
  color: rgb(var(--gray-900));
}

/* 子分段标题（h3）：文章内部的第三级标题，比 h2 再收一档。
   2026-09-23 产品页标题置换后，h3 的角色从「文章名」变成「子分段标题」——
   旧的 h3 只作文章首行标题，不需要上间距，所以 margin-top 是 0；
   现在它上面总是有段落或列表，必须补上间距，否则会直接贴住上一段。
   上间距取 28px：与 h2 的 36px 保持一档落差，折叠后仍大于段落的 16px。 */
.docs-article h3 {
  margin: 28px 0 10px;
  font-weight: 600;
  color: rgb(var(--gray-900));
}

.docs-article p {
  margin: 0 0 16px;
  font-size: 16px;
  line-height: 1.8;
  color: rgb(var(--gray-600));
}

/* 全局重置把 ul 的 list-style 清掉了，正文列表要显式恢复 */
.docs-article ul {
  margin: 0 0 16px;
  padding-left: 20px;
  list-style: disc;
}

.docs-article li {
  margin-bottom: 6px;
  font-size: 16px;
  line-height: 1.8;
  color: rgb(var(--gray-600));
}

/* ---------- 文档配图（产品文档正文截图） ---------- */
/* 兜底规则：富文本编辑器（Tiptap）只保留 img，figure/figcaption 会被剥离。
   先给裸 img 一套能看的默认样式，保证结构被简化后也不会破版。
   作用域刻意收窄到「article 直接子元素」和「段落内的图片」——
   下载页的产品图标（.downloads-head > img）也是 article 的后代，
   若用 .docs-article img 会连带给它加上边框与底距，破坏图标行布局。 */
.docs-article > img,
.docs-article p > img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0 auto 24px;
  border-radius: 10px;
  border: 1px solid rgb(var(--gray-200));
  background: rgb(var(--gray-50));
}

/* 单图容器：默认占满阅读区 */
.docs-figure {
  margin: 0 0 24px;
}

/* 单图：按图片自身像素尺寸显示 —— 大图缩到阅读区宽度，小图保持原尺寸不放大
   （小截图强行拉满会糊）。配合 auto 外边距水平居中。
   height: auto 必须显式写：img 上的 height 属性是表现属性，
   若 CSS 不声明 height，宽度被 max-width 压窄后高度仍按属性值渲染，图片会被压扁。
   display: block 也必须显式写：img 默认是 inline，inline 元素的 auto 左右外边距
   不参与居中（会被无视），只有块级化后 margin: 0 auto 才生效 ——
   否则当容器比图片宽时（如 .is-compact 的 440px 容器装 400px 图）图会贴左。 */
.docs-figure img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  margin: 0 auto;
}

/* 图注：小一号、居中，与图片留 10px 间距 */
.docs-figure figcaption {
  margin-top: 10px;
  font-size: 14px;
  line-height: 1.6;
  color: rgb(var(--gray-500));
  text-align: center;
}

/* 窄图：竖版截图（手机比例），居中收窄，避免被拉到通栏高度 */
.docs-figure.is-narrow {
  max-width: 320px;
  margin-left: auto;
  margin-right: auto;
}

/* 紧凑图：中小尺寸截图居中显示，不强行放大 */
.docs-figure.is-compact {
  max-width: 440px;
  margin-left: auto;
  margin-right: auto;
}

/* 并排图：两张截图左右排列。统一高度、各自按原始比例出宽，整组水平居中 ——
   这样宽度不同的两张图也能等高对齐，不会一高一矮。 */
.docs-figure-row {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 20px;
  margin: 0 0 24px;
}

.docs-figure-row .docs-figure {
  margin: 0;
}

.docs-figure-row .docs-figure img {
  width: auto;
  height: 440px;
  max-width: 100%;
  margin: 0 auto;
}

/* 左图右文：竖版截图配说明文字并排，图定宽、文字占余下空间 */
.docs-figure-side {
  display: flex;
  align-items: flex-start;
  gap: 28px;
  margin: 0 0 24px;
}

.docs-figure-side .docs-figure {
  flex: 0 0 300px;
  max-width: 300px;
  margin: 0;
}

/* 图按原始像素出宽，不套用上面的 300px 定宽（大图仍受容器宽度约束）；
   配 .docs-figure img 的 width:auto 使用，图片既不被放大也不被额外压缩。 */
.docs-figure-side--natural .docs-figure {
  flex: 0 0 auto;
  max-width: 100%;
}

/* 图宽交给 .docs-figure img 统一控制（width:auto + max-width:100%），
   即「容器 300px、图片按原始像素出宽」—— 小图不会被拉伸放大。 */
.docs-figure-side .docs-figure img {
  height: auto;
}

.docs-figure-side-text {
  flex: 1 1 auto;
  min-width: 0;
}

.docs-figure-side-text p:last-child {
  margin-bottom: 0;
}

/* 导语 + 主图并排（图在左、文字在右）：底距比普通并排图多一档 */
.docs-figure-side--lead {
  margin-bottom: 28px;
}

.docs-figure-side--lead .docs-lead {
  margin: 0;
}

@media (max-width: 700px) {
  /* 窄屏：并排图与左图右文一律改为上下排列，图片按列宽收窄 */
  .docs-figure-row {
    flex-direction: column;
    align-items: center;
  }

  .docs-figure-row .docs-figure img {
    width: 100%;
    max-width: 320px;
    height: auto;
  }

  .docs-figure-side {
    flex-direction: column;
    gap: 16px;
  }

  .docs-figure-side .docs-figure {
    flex: 0 0 auto;
    margin: 0 auto;
  }
}

/* ---------- 右栏：书签 ---------- */
.docs-toc-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;
  color: rgb(var(--gray-700));
}

.docs-toc-icon {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
}

.docs-toc-list {
  display: flex;
  flex-direction: column;
}

.docs-toc-link {
  display: block;
  padding: 4px 0;
  font-size: 14px;
  line-height: 24px;
  color: rgb(var(--gray-600));
  transition: color 0.15s var(--ease);
}

/* 二级书签（h3）：比一级（h2）缩进一级，色阶也调浅一档 ——
   缩进 + 色阶共同表达层级。
   注意：h1（文章标题）不进书签（见 main.js 的 buildToc），
   所以书签里的「一级」是 h2，「二级」是 h3。
   必须排在 :hover 与 .is-current 之前：三者特异性相同，后写的胜出，
   否则这一档颜色会把悬停态和激活态一起盖掉。 */
.docs-toc-link.is-sub {
  padding-left: 20px;
  color: rgb(var(--gray-500));
}

.docs-toc-link:hover {
  color: rgb(var(--gray-900));
}

/* 当前所处章节：颜色加深 + 实加粗（与左栏用同一套激活语言） */
.docs-toc-link.is-current {
  color: rgb(var(--primary));
  font-weight: 600;
}

/* ==========================================================================
   浮层滚动条（页面级）
   原生滚动条参与布局，会占掉 15~17px；这里改成 position: fixed 的浮层，
   完全脱离布局流 —— 无论页面长短，内容宽度都恒定，切换标签不再左右位移。
   由 main.js 模块 5 驱动，内容不超出视口时自动隐藏。
   ========================================================================== */
.ov-scroll {
  position: fixed;
  top: 0;
  right: 3px;
  z-index: 60;
  width: 6px;
  height: 100vh;
  opacity: 0;
  transition: opacity 0.25s var(--ease);
  /* 轨道不吃指针事件，只有滑块可交互 */
  pointer-events: none;
}

.ov-scroll.is-visible {
  opacity: 1;
}

.ov-scroll-thumb {
  width: 100%;
  border-radius: 3px;
  background-color: rgb(var(--gray-900) / 22%);
  pointer-events: auto;
  transition: background-color 0.15s var(--ease);
}

.ov-scroll-thumb:hover {
  background-color: rgb(var(--gray-900) / 38%);
}

/* ==========================================================================
   Footer
   ========================================================================== */
/* ==========================================================================
   Footer（固定悬浮在底部）
   结构照 kukultd.com 的底部格式：上行 = logo + 页面导航，下行 = 版权 + 备案。
   两处按本站情况落地：
   1) 配色不用参考稿的 #070b14，改用站内深色块同源的 #17191d
      （与服务页「从需求到上线」区块一致）；
   2) 不做参考稿那层 hover 才展开的详情行 —— 本站只有 4 个页面，两行足够。
   fixed 定位后它脱离文档流，底部留白由 main.js 实测高度写进 .page-content。
   ========================================================================== */
.site-footer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  /* 与 header 同级：两者一顶一底，不会互相遮挡 */
  z-index: 30;
  /* fixed 元素的包含块是视口，不继承 body 的 min-width，
     必须单独声明，否则页面横向滚动时 footer 右侧会露出空缺
     （与 .site-header 同一个坑） */
  min-width: var(--page-min-width);
  color: rgb(var(--background-light));
  background-color: #17191d;
}

/* 与 .header-inner / .content-inner 用同一套宽度约束与内边距，
   保证页脚内容与页头、正文左右对齐。 */
.footer-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--page-padding-x);
}

/* ---------- 第一行：logo + 页面导航 ---------- */
/* 上行与展开行共用同一套 4 列网格（1.5fr + 3×1fr）：
   列宽、列间距完全一致，两行的内容才会逐列对齐 ——
   这是目标站点 footer 的做法，改用 flex 排「左 logo / 右导航」就对不上了。 */
.footer-top {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1fr;
  align-items: center;
  gap: 24px;
  padding: 14px 0;
}

.footer-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: inherit;
  text-decoration: none;
}

.footer-logo {
  flex: none;
  width: 24px;
  height: 24px;
}

.footer-brand-name {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* display: contents 让 <nav> 本身不生成盒子，
   它的三个链接直接成为 .footer-top 的 grid item，
   于是每个导航项正好落在第 2–4 列，与下行详情列一一对齐。 */
.footer-nav {
  display: contents;
}

/* 页脚导航不沿用 header 那套下划线激活态：footer 是常驻浮层，
   页面切换时它不参与重绘，标一个「当前页」反而容易与正文脱节。
   只保留 hover 提亮。 */
.footer-nav-link {
  font-size: 14px;
  line-height: 20px;
  color: #aeb3bb;
  text-decoration: none;
  transition: color var(--duration) var(--ease);
}

.footer-nav-link:hover {
  color: rgb(var(--background-light));
}

/* ---------- 悬停展开行：从底部向上展开 ----------
   这是目标站点 footer 的关键交互：
   - 收起态 max-height: 0，详情行完全折叠；
   - 鼠标进入 footer 后，详情行获得高度并把 footer 顶边向上推；
   - footer 的 bottom 始终为 0，所以视觉上就是「向上展开」。
   不用 display:none/block 切换，才能让 max-height + padding 有连续过渡。 */
.footer-detail {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1fr;
  gap: 24px;
  max-height: 0;
  padding: 0;
  overflow: hidden;
  border-top: 0 solid #2a2d33;
  transition: max-height 0.4s var(--ease),
              padding 0.4s var(--ease),
              border-top-width 0.4s var(--ease);
}

.site-footer:hover .footer-detail,
.site-footer:focus-within .footer-detail {
  /* 「服务」列 5 项是最高的一列，内容约 122px、含内边距约 155px；
     给到 180px 留足余量，同时上限别拉太远，展开过渡才不会「走一半就停住」。 */
  max-height: 180px;
  padding: 16px 0;
  border-top-width: 1px;
}

.footer-detail-col {
  min-width: 0;
}

/* 展开行没有列标题：标题与上行的导航项是同一批词，重复一遍纯属冗余。
   每列内容直接落在对应导航项的正下方。 */
.footer-detail-text,
.footer-detail-col a {
  display: block;
  margin: 0 0 8px;
  font-size: 12px;
  line-height: 18px;
  color: #8e949d;
  text-decoration: none;
  transition: color var(--duration) var(--ease);
}

.footer-detail-col a:hover {
  color: #ffffff;
}

.footer-detail-col a:last-child,
.footer-detail-text:last-child {
  margin-bottom: 0;
}

/* ---------- 第二行：版权 + 备案 ---------- */
.footer-bottom {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: center;
  gap: 24px;
  padding: 12px 0;
  border-top: 1px solid #2a2d33;
  text-align: center;
}

.footer-copyright {
  margin: 0;
  font-size: 12px;
  line-height: 18px;
  color: #8e949d;
}

.footer-record {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-width: 0;
}

.footer-record a,
.footer-privacy {
  font-size: 12px;
  line-height: 18px;
  color: #8e949d;
  text-decoration: none;
  transition: color var(--duration) var(--ease);
}

.footer-record a:hover,
.footer-privacy:hover {
  color: #aeb3bb;
}

.footer-privacy {
  justify-self: center;
}

.footer-divider {
  font-size: 11px;
  color: #3a3d43;
}

/* ==========================================================================
   响应式：窄屏
   宽度不足时：第一行 = [logo+公司名称] …… [语言选择]
               第二行 = 导航（独占一行）
   header 高度随之自动增高（由 JS 实测后顶开正文）

   【已停用】页面最小宽度锁定 1280px（--page-min-width）后，
   视口宽度不再等于可用宽度，以下断点（1024 / 700 / 480）全部失效，
   且在窄视口下会造成「1280px 宽容器 + 窄屏布局」的错乱，故整体停用。
   恢复方法：删掉下面这行 @media not all { 与本区块结尾多出的那个 }。
   ========================================================================== */
@media not all {
@media (max-width: 1024px) {
  :root {
    --page-padding-x: 32px;
  }
}

@media (max-width: 700px) {
  :root {
    --page-padding-x: 24px;
  }

  /* header 改为自动高度，容纳两行 */
  .site-header {
    height: auto;
    min-height: 64px;
  }

  /* 启用换行：品牌与语言选择占第一行，导航挤到第二行 */
  .header-inner {
    flex-wrap: wrap;
    align-content: center;
    padding-top: 14px;
    padding-bottom: 0;
    row-gap: 0;
  }

  /* 第一行：品牌贴左、语言选择贴右 */
  .brand {
    flex: 0 1 auto;
    order: 1;
  }

  .header-right {
    flex: 0 0 auto;
    order: 2;
  }

  /* 第二行：导航整行独占并居中 */
  .site-nav {
    order: 3;
    flex: 1 1 100%;
    width: 100%;
    height: 48px;
    margin-top: 4px;
    border-top: 1px solid rgb(var(--gray-500) / 5%);
  }

  /* 下划线保持贴容器底边，与参考站点一致 */
  .nav-link::after {
    bottom: 0;
  }
}

@media (max-width: 480px) {
  :root {
    --page-padding-x: 16px;
  }

  .nav-list {
    gap: 18px;
  }
}
}

/* ==========================================================================
   服务页（参考稿原文存于 docs/reference-service-page.html）
   参考稿是一份独立页面，自带一套配色；本站的做法是
   「内容与排版照参考稿，配色与令牌落站点体系」，只有一处例外：
   交付流程区的深色块按皇上要求原样引入（见下面的 .svc-block--process）。

   内容组织：标题区 → 服务卡片（3 列）→ 交付流程（4 步）→ 行动区。

   三条工程约束：
   1) 类名统一加 svc- 前缀。参考稿用的 .container / .grid / .card 是通用名，
      直接落地会波及其余页面。
   2) 每条规则都带 .svc-page 前缀。站点的 `.content-inner p` 特异性是 (0,1,1)，
      只写 `.svc-hero-lead`（0,1,0）压不住它，段落间距会由那条全局规则说了算；
      加一层变 (0,2,0)，稳定胜出。
   3) 参考稿用的是一组固定中性色（#e7e9ed / #8a919c / #17191d / #3a3d43 /
      #9da3ad / #aeb3bb），不属于本站的 gray 色板。照抄处一律写原值，
      并注明来自参考稿，日后要统一调整时便于定位。
   ========================================================================== */

/* 页面底部留白：行动区是浅灰块，紧接着的 footer 也是浅灰，
   不留出间距两块会糊在一起。 */
.svc-page {
  padding-bottom: 56px;
}

/* ---------- 整屏（服务页两屏翻页）----------
   服务页拆成两屏：标题区+服务卡片 / 交付流程+行动区。
   每屏锁死一屏高，配合 html.is-snap 的 scroll-snap 实现整页翻页。

   高度为什么要减掉 footer：header 与 footer 都是 fixed、都脱离文档流，
   正文区是从视口顶开始的。每屏 = 视口高 − footer 高，屏顶贴视口顶、
   屏底正好落在 footer 顶边；屏内再用 padding-top 让开 header，
   内容就不会被压住。两者相减，一屏真正可用的内容高度 = 100vh − header − footer。

   留白尺寸一律写成 clamp(下限, vh 值, 原值)：视口高时保持原本的疏朗，
   视口矮时自动收紧，于是任何分辨率下都压得进一屏，而字号不缩。 */
.svc-screen {
  scroll-snap-align: start;
  height: calc(100vh - var(--footer-height-actual, var(--footer-height)));
  padding-top: var(--header-height-actual, var(--header-height));
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* 整屏模式的开关：JS 在切到服务页时给 <html> 加 .is-snap。
   挂在 html 上是因为滚动容器就是它（document.scrollingElement）。
   其余页面没有 .svc-screen 这类 snap 目标，本来也吸不住，
   但滚轮一旦被 JS 接管，产品页那种长文档就没法正常滚了，
   所以必须按页开关，不能常驻。 */
html.is-snap {
  scroll-snap-type: y mandatory;
  /* 全局 html 为文档书签预留了 header + sticky gap；
     整屏翻页的屏顶本身就是视口顶，不能再叠加 118px，
     否则 scrollIntoView 会把第二屏停在错误位置。 */
  scroll-padding-top: 0;
}

/* 每屏自己让开 header，页面级的顶部留白要归零 —— 否则第一屏会被整体推下去，
   第二屏的吸附位置也跟着错位。padding-top 是 JS 写在行内样式上的
   （syncHeaderHeight 按实测高度写入），只能用 !important 压过。 */
html.is-snap .page-content {
  padding-top: 0 !important;
}

html.is-snap #page-services {
  padding-top: 0;
}

/* 底部余量交给 .page-content 的 padding-bottom（= footer 实测高）：
   少了它，文档总高会差一屏底，第二屏就吸不到顶部。
   而 .svc-page 自己那段留白必须去掉，否则文档多出一截、
   最后一屏还能继续往下滚。 */
html.is-snap .svc-page {
  padding-bottom: 0;
}

/* .content-inner 底部还留着 96px（footer 还是静态元素时的老留白，
   fixed 化之后已与 .page-content 的 padding-bottom 重复）。
   普通页面多出这 96px 只是能多滚一点，无伤大雅；
   但整屏模式下文档总高必须精确等于「两屏 + footer 高」，
   否则最后一屏吸不到顶部，所以这里必须归零。
   只覆盖 bottom，左右内边距要保留 —— 那是与导航对齐的依据。 */
html.is-snap .content-inner {
  padding-bottom: 0;
}

/* ---------- 标题区 ---------- */
.svc-page .svc-eyebrow {
  margin: 0 0 12px;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: rgb(var(--gray-500));
}

.svc-page .svc-hero-title {
  margin: 0 0 clamp(10px, 1.4vh, 16px);
  font-weight: 700;
  letter-spacing: -0.03em;
  color: rgb(var(--gray-900));
}

.svc-page .svc-hero-lead {
  max-width: 660px;
  margin: 0;
  font-size: 17px;
  line-height: 1.75;
  color: rgb(var(--gray-600));
}

/* hero 的下内边距决定「副文案 → 分隔线」的距离；
   分隔线往下到卡片区还有 .svc-block 的 padding-top，
   两者相加（44 + 1 + 58 ≈ 103px）才是视觉上的整段留白。 */
.svc-page .svc-hero {
  border-bottom: 1px solid rgb(var(--gray-500) / 8%);
}

/* ---------- 区块通用 ----------
   间距模型照参考稿：区块之间不靠 margin，而是各自带上下内边距 ——
   相邻两个区块的 padding 相加，就是它们之间的留白。
   （参考稿 .services 是 `padding:58px 0 80px`、.process 是 `padding:64px 0`，
   区块之间没有任何 margin。）
   原先的 `.svc-page > * + * { margin-top: 56px }` 已删除 ——
   区块自带 padding 后再叠一层 margin，间距会翻倍。 */
.svc-page .svc-block {
  padding-block: clamp(8px, 2.2vh, 38px) clamp(0px, 1.2vh, 58px);
}

/* ---------- 区块标题行 ----------
   对应参考稿的 .section-head：标题贴左、右侧一行小字贴右，
   两端分开、基线对齐。两个区块各有一份。 */
.svc-page .svc-section-head {
  display: flex;
  justify-content: space-between;
  align-items: end;
  margin-bottom: clamp(16px, 2vh, 28px);
}

.svc-page .svc-section-head h2 {
  margin: 0;
}

.svc-page .svc-section-head span {
  font-size: 14px;
  color: #8a919c;
}

/* 交付流程区：参考稿是通栏深色块
   （.process{background:#17191d;color:#fff;padding:64px 0}）。
   底色按皇上要求贯穿整个视口宽度 —— 不再受单栏 / 三栏框架的宽度限制。

   做法是「负外边距外扩 + 等量内边距收回」：
   - margin-inline 把区块往两侧撑到视口边界（min 保证窄于内容栏时不外扩）；
   - padding-inline 再把内容收回内容栏宽度，于是内容与上方卡片左右对齐，
     两侧多出来的部分就是纯底色。
   宽度基准用 --vw-actual（JS 实测的 clientWidth）而不是 100vw：
   100vw 含滚动条宽度，直接用会让区块比可视区宽出十几像素、撑出横向滚动条。 */
.svc-page .svc-block--process {
  margin-inline: min(0px, calc(50% - var(--vw-actual, 100vw) / 2));
  padding-block: clamp(24px, 4vh, 64px);
  padding-inline: max(28px, calc(var(--vw-actual, 100vw) / 2 - 50%));
  color: rgb(var(--background-light));
  background-color: #17191d;
}

/* 深色底上的标题行：取参考稿 .process .section-head 的对应值 */
.svc-page .svc-block--process .svc-section-head {
  margin-bottom: clamp(16px, 2.2vh, 34px);
}

.svc-page .svc-block--process .svc-section-head h2 {
  color: #fff;
}

.svc-page .svc-block--process .svc-section-head span {
  color: #8e949d;
}

/* ---------- 服务卡片 ----------
   这一段（含 hover）照抄参考代码，不套用站点的令牌：
   卡片默认态是白底 + 1px 边框，hover 时上浮 3px 并投出柔和的阴影。
   若沿用站点的浅灰底，阴影落在浅灰背景上几乎看不见，上浮也会失去参照。

   3 列是设计稿的原排布。之前排 2 列是因为正文被三栏夹到 776px，
   3 列每列只剩 248px、撑不出竖长比例；服务页的框架放宽到 1128px 后，
   每列回到约 365px，3 列重新成立。 */
.svc-page .svc-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.svc-page .svc-card {
  min-height: clamp(140px, 20vh, 220px);
  padding: clamp(10px, 2.6vh, 28px);
  background-color: #fff;
  border: 1px solid #e7e9ed;
  border-radius: 16px;
  transition: 0.2s;
}

.svc-page .svc-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 35px rgb(20 30 50 / 7%);
}

.svc-page .svc-num {
  margin: 0 0 clamp(4px, 1.8vh, 20px);
  font-size: 13px;
  color: #8a919c;
}

.svc-page .svc-card h3 {
  margin: 0 0 10px;
  color: rgb(var(--gray-900));
}

.svc-page .svc-card-desc {
  margin: 0;
  font-size: 14px;
  line-height: 1.75;
  color: #727985;
}

.svc-page .svc-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin: clamp(4px, 1.8vh, 20px) 0 0;
  padding: 0;
  list-style: none;
}

.svc-page .svc-tags li {
  padding: 5px 9px;
  font-size: 12px;
  color: #555d68;
  background-color: #f3f4f6;
  border-radius: 20px;
}

/* ---------- 交付流程 ----------
   照抄参考稿的深色写法：网格靠 1px 间隙露出底色，单元格与区块同为 #17191d，
   于是网格线呈现为比底色略浅的 #3a3d43。
   （参考稿 .steps{background:#3a3d43;border:1px solid #3a3d43}
          .step{background:#17191d;padding:25px;min-height:150px}） */
.svc-page .svc-steps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow: hidden;
  background-color: #3a3d43;
  border: 1px solid #3a3d43;
}

.svc-page .svc-step {
  padding: clamp(14px, 2.2vh, 25px);
  min-height: clamp(96px, 12vh, 150px);
  background-color: #17191d;
}

/* 颜色不写死：参考稿里 h3 没有自己的颜色，靠 .process 的 color:#fff 继承，
   这里同样交给 .svc-block--process 的 color 继承下来。 */
.svc-page .svc-step h3 {
  margin: 18px 0 8px;
}

.svc-page .svc-step p {
  margin: 0;
  font-size: 13px;
  line-height: 1.7;
  color: #aeb3bb;
}

/* 序号色 #9da3ad 取自参考稿的 .step b。
   选择器要比 `.svc-page .svc-step p` 多带一层 .svc-step：
   序号元素是 <p class="svc-step-num">，会被那条规则命中，
   而它的特异性是「两个类 + 一个元素」= (0,2,1)，
   `.svc-page .svc-step-num` 只有 (0,2,0) —— 差一级，写在后面也翻不过来。
   多一层类变 (0,3,0)，稳定胜出。
   序号没有下外边距，参考稿的间距来自 .step h3 的 margin-top:18px。 */
.svc-page .svc-step .svc-step-num {
  margin: 0;
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  color: #9da3ad;
}

/* ---------- 行动区 ---------- */
/* ---------- 行动区 ----------
   这里是「区块无 margin」模型的一个必要例外：
   行动区是带圆角的浅灰块，紧贴上方通栏深色块的底边会显得挤在一起，
   留一段外边距把两者分开。
   （参考稿里两者都是通栏色块、边界靠颜色区分，不需要这段间距。） */
.svc-page .svc-cta {
  margin-top: clamp(14px, 2.4vh, 32px);
  padding: clamp(28px, 4vh, 56px) 40px;
  text-align: center;
  background-color: rgb(var(--gray-50) / 60%);
  border-radius: var(--rounded-xl);
}

.svc-page .svc-cta h2 {
  margin: 0 0 10px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: rgb(var(--gray-900));
}

.svc-page .svc-cta p {
  margin: 0 0 clamp(14px, 2vh, 24px);
  font-size: 15px;
  line-height: 1.7;
  color: rgb(var(--gray-600));
}

.svc-page .svc-btn {
  display: inline-block;
  padding: 11px 24px;
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;
  color: rgb(var(--background-light));
  text-decoration: none;
  background-color: rgb(var(--primary));
  border-radius: var(--rounded-xl);
  transition: opacity var(--duration) var(--ease);
}

.svc-page .svc-btn:hover {
  opacity: 0.85;
}

/* 3 列卡片需要足够的宽度：1100px 以下每列会掉到 300px 以内，
   先退到 2 列，再由下面的 800px 断点退到 1 列。
   （页面最小宽是 1280px，正常桌面浏览走不到这一档，
   这里是给窄窗口和浏览器缩放留的退路。） */
@media (max-width: 1100px) {
  .svc-page .svc-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 800px) {
  .svc-page .svc-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .svc-page .svc-steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 520px) {
  .svc-page .svc-grid,
  .svc-page .svc-steps {
    grid-template-columns: minmax(0, 1fr);
  }
  /* 窄屏收一收区块留白（参考稿在 520px 是 .services{padding:45px 0 60px}）。
     这里只写 padding-block：若用 `padding` 简写，会把深色区块由
     --vw-actual 算出来的左右内边距一并清零，内容就会贴到视口边上。 */
  .svc-page .svc-block {
    padding-block: clamp(8px, 5vh, 45px) clamp(8px, 7vh, 60px);
  }
  .svc-page .svc-block--process {
    padding-block: clamp(16px, 6vh, 48px);
  }
}

/* ---------- 产品页：下载卡片与开发中状态 ---------- */
.product-download-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin: 24px 0 44px;
}

.product-download-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-height: 156px;
  padding: 20px;
  color: rgb(var(--gray-900));
  text-decoration: none;
  background: rgb(var(--gray-50) / 48%);
  border: 1px solid rgb(var(--gray-500) / 14%);
  border-radius: var(--rounded-xl);
  transition: border-color var(--duration) var(--ease),
              background-color var(--duration) var(--ease),
              transform var(--duration) var(--ease);
}

.product-download-card:not(.is-disabled):hover {
  transform: translateY(-2px);
  background: rgb(var(--gray-50) / 78%);
  border-color: rgb(var(--gray-400));
}

.product-download-card.is-disabled {
  color: rgb(var(--gray-500));
  cursor: not-allowed;
}

.product-download-kicker {
  margin-bottom: 14px;
  font-size: 11px;
  line-height: 16px;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: rgb(var(--gray-400));
}

.product-download-card strong {
  font-size: 17px;
  line-height: 24px;
  font-weight: 600;
}

.product-download-meta {
  margin-top: 5px;
  font-size: 13px;
  line-height: 20px;
  color: rgb(var(--gray-500));
}

.product-download-action {
  margin-top: auto;
  padding-top: 15px;
  font-size: 12px;
  line-height: 18px;
  color: rgb(var(--gray-700));
}

.product-download-card.is-disabled .product-download-action {
  color: rgb(var(--gray-400));
}

.product-coming-soon {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-top: 28px;
  padding: 20px;
  background: rgb(var(--gray-50) / 52%);
  border: 1px solid rgb(var(--gray-500) / 12%);
  border-radius: var(--rounded-xl);
}

.product-coming-soon-mark {
  flex: none;
  font-size: 12px;
  line-height: 20px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: rgb(var(--gray-400));
}

.product-coming-soon p {
  margin: 0;
  font-size: 14px;
  line-height: 24px;
  color: rgb(var(--gray-600));
}

@media (max-width: 700px) {
  .product-download-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ==========================================================================
   联系我们页
   内容只有四项：公司名 / 地址 / 业务联系 / 商户联系。
   信息越少越容易排成一份平铺清单，所以层次全部交给排版：
   标题区独立成块（小标签 → 公司名 → 带图标的地址行），
   两组联系方式拆成并列卡片，卡内再分「电话 / 邮箱」两级。
   卡片语言沿用服务页：白底 + 细边框 + 悬停抬升，两页观感统一。
   ========================================================================== */

/* ---------- 标题区 ---------- */
.ct-page .ct-eyebrow {
  margin: 0 0 12px;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: rgb(var(--gray-500));
}

/* 联系页主标题：公司名退为辅助信息，视觉焦点放在联系动作上。 */
.ct-page .ct-title {
  margin: 0 0 12px;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: rgb(var(--gray-900));
}

.ct-page .ct-lead {
  max-width: 560px;
  margin: 0;
  font-size: 16px;
  line-height: 1.75;
  color: rgb(var(--gray-600));
}

.ct-page .ct-company-row {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-top: 30px;
}

.ct-page .ct-company-label {
  flex: none;
  font-size: 12px;
  line-height: 18px;
  letter-spacing: 0.08em;
  color: rgb(var(--gray-400));
}

.ct-page .ct-company {
  margin: 0;
  font-size: 17px;
  line-height: 1.5;
  font-weight: 600;
  color: rgb(var(--gray-800));
}

.ct-page .ct-icon {
  flex: none;
  color: rgb(var(--gray-400));
}

/* 地址行：图标 + 文字同排。用 flex-start 而非 center ——
   地址在窄屏会折成两行，图标该对齐首行文字，而不是整块的中线。 */
.ct-page .ct-address {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 10px 0 0;
  font-size: 14px;
  line-height: 1.6;
  color: rgb(var(--gray-500));
}

/* 图标与首行文字视觉居中：行高 16 × 1.6 ≈ 25.6px，图标 16px，
   上偏 (25.6 − 16) / 2 ≈ 5px。 */
.ct-page .ct-address .ct-icon {
  margin-top: 5px;
}

/* 标题区与卡片区之间拉一道分隔线，收尾方式与服务页 hero 一致 */
.ct-page .ct-hero {
  padding: 10px 0 34px;
  border-bottom: 1px solid rgb(var(--gray-500) / 8%);
}

/* ---------- 联系卡片 ---------- */
.ct-page .ct-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  margin-top: 30px;
}

.ct-page .ct-card {
  position: relative;
  min-height: 286px;
  padding: 26px 28px 24px;
  background-color: #fff;
  border: 1px solid #e7e9ed;
  border-radius: 16px;
  transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s;
}

.ct-page .ct-card::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 24px;
  width: 34px;
  height: 2px;
  background-color: rgb(var(--gray-900));
}

.ct-page .ct-card:hover {
  transform: translateY(-3px);
  border-color: rgb(var(--gray-300));
  box-shadow: 0 14px 36px rgb(20 30 50 / 8%);
}

.ct-page .ct-card-num {
  margin: 0 0 18px;
  font-size: 12px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: rgb(var(--gray-400));
}

.ct-page .ct-card-title {
  margin: 0 0 8px;
  font-weight: 600;
  color: rgb(var(--gray-900));
}

/* 卡片的一句话用途说明：交代这张卡适合谈什么，
   两张卡的差别因此一眼可辨，不必只靠「业务 / 商务」两个词去猜。 */
.ct-page .ct-card-note {
  margin: 0 0 22px;
  font-size: 14px;
  line-height: 1.7;
  color: #727985;
}

/* ---------- 卡内的两项联系方式 ----------
   电话与邮箱之间补一条淡线：卡里只有两行内容，不隔开就会糊成一整段。 */
.ct-page .ct-ways {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ct-page .ct-way + .ct-way {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid rgb(var(--gray-500) / 8%);
}

.ct-page .ct-way-label {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 7px;
  font-size: 13px;
  line-height: 18px;
  color: rgb(var(--gray-500));
}

/* 号码与邮箱是这一页真正要传达的信息，字号给到 18px 便于一眼扫到；
   写成链接是为了手机端能直接拨号 / 发信，悬停用下划线提示可点。 */
.ct-page .ct-way-value {
  display: inline-block;
  font-size: 18px;
  line-height: 1.4;
  font-weight: 500;
  color: rgb(var(--gray-900));
  text-decoration: none;
  transition: color var(--duration) var(--ease);
}

.ct-page .ct-way-value:hover {
  color: rgb(var(--gray-600));
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ---------- 服务范围 ----------
   一排小标签交代公司能做什么：比再写一段业务介绍省字数，
   也让页面在「怎么联系」之外，顺带回答「能做什么」。
   标签沿用服务页卡片里的 .svc-tags 样式，两页观感一致。 */
.ct-page .ct-scope {
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid rgb(var(--gray-500) / 8%);
}

.ct-page .ct-scope-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
}

.ct-page .ct-scope-label {
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  color: rgb(var(--gray-700));
}

.ct-page .ct-scope-note {
  font-size: 12px;
  line-height: 18px;
  color: rgb(var(--gray-400));
}

.ct-page .ct-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ct-page .ct-tags li {
  padding: 5px 9px;
  font-size: 12px;
  line-height: 18px;
  color: #555d68;
  background-color: #f3f4f6;
  border-radius: 20px;
}

/* ---------- 断点 ----------
   页面最小宽 1280px，所以两列卡片实际很少被压到很窄；
   这里是为将来放开 --page-min-width 留的兜底。 */
@media (max-width: 700px) {
  .ct-page .ct-company-row {
    gap: 10px;
    margin-top: 24px;
  }
  .ct-page .ct-company {
    font-size: 16px;
  }
  .ct-page .ct-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .ct-page .ct-card {
    min-height: 0;
  }
  .ct-page .ct-scope-head {
    align-items: flex-start;
    flex-direction: column;
    gap: 2px;
  }
}

/* ==========================================================================
   案例页
   单栏满宽画廊，3 列卡片（1128px 下每列约 365px）；
   图片统一 4:3，点击后由挂在 body 下的 .case-lightbox 全屏放大。
   ========================================================================== */

/* 顶部留白对齐其他文档页：标题落点与产品 / 服务 / 联系页一致。
   直接写 #page-cases 覆盖 .page-panel 的默认 56px（id 选择器优先级更高，
   不必回头改上面那条规则）。 */
#page-cases {
  padding-top: var(--docs-sticky-gap);
}

.cases-page {
  padding-bottom: 56px;
}

/* ---------- 标题区 ---------- */
.cases-page .cases-hero {
  padding: 10px 0 30px;
  border-bottom: 1px solid rgb(var(--gray-500) / 8%);
}

.cases-page .cases-eyebrow {
  margin: 0 0 10px;
  font-size: 12px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: rgb(var(--gray-400));
}

.cases-page .cases-title {
  margin: 0 0 12px;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: rgb(var(--gray-900));
}

.cases-page .cases-lead {
  max-width: 560px;
  margin: 0;
  font-size: 16px;
  line-height: 1.75;
  color: rgb(var(--gray-600));
}

/* ---------- 网格 ----------
   3 列 → 2 列（≤1024px）→ 1 列（≤700px）。
   页面最小宽 1280px，窄屏断点是为将来放开 --page-min-width 预留的。 */
.cases-page .cases-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px 20px;
  margin: 30px 0 0;
  padding: 0;
  list-style: none;
}

.cases-page .case-card {
  display: flex;
  flex-direction: column;
}

/* ---------- 图片热区 ----------
   用 button 承载：键盘可达（Tab + Enter），不必给 <img> 挂 click。 */
.cases-page .case-figure {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  overflow: hidden;
  cursor: zoom-in;
  background-color: rgb(var(--gray-50) / 60%);
  border: 1px solid rgb(var(--gray-500) / 12%);
  border-radius: 14px;
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}

.cases-page .case-figure img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform 0.45s var(--ease);
}

/* hover / 键盘聚焦：图片轻微放大 + 卡片抬边 */
.cases-page .case-figure:hover,
.cases-page .case-figure:focus-visible {
  border-color: rgb(var(--gray-400));
  box-shadow: 0 14px 34px rgb(20 30 50 / 9%);
}

.cases-page .case-figure:hover img,
.cases-page .case-figure:focus-visible img {
  transform: scale(1.035);
}

.cases-page .case-figure:focus-visible {
  outline: 2px solid rgb(var(--gray-900));
  outline-offset: 2px;
}

/* ---------- 放大提示（hover 浮出）---------- */
.cases-page .case-zoom {
  position: absolute;
  right: 12px;
  bottom: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  color: rgb(var(--gray-900));
  background-color: rgb(255 255 255 / 92%);
  border-radius: 50%;
  box-shadow: 0 4px 14px rgb(20 30 50 / 14%);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
}

.cases-page .case-zoom svg {
  width: 17px;
  height: 17px;
}

.cases-page .case-figure:hover .case-zoom,
.cases-page .case-figure:focus-visible .case-zoom {
  opacity: 1;
  transform: translateY(0);
}

/* ---------- 文案 ---------- */
.cases-page .case-meta {
  padding: 14px 2px 0;
}

.cases-page .case-tag {
  margin: 0 0 6px;
  font-size: 12px;
  line-height: 18px;
  letter-spacing: 0.06em;
  color: rgb(var(--gray-400));
}

.cases-page .case-name {
  margin: 0 0 4px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: rgb(var(--gray-900));
}

.cases-page .case-desc {
  margin: 0;
  font-size: 13px;
  line-height: 21px;
  color: rgb(var(--gray-500));
}

/* ==========================================================================
   案例图片全屏预览（lightbox）
   挂在 <body> 下，position: fixed 的包含块就是视口。
   ========================================================================== */
.case-lightbox {
  position: fixed;
  inset: 0;
  /* 高于 header / footer(30) 与浮层滚动条(60) */
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 56px 48px;
  box-sizing: border-box;
  /* 与 header / footer 同一个坑：fixed 元素不继承 body 的 min-width，
     页面横向滚动时右侧会露出空缺 */
  min-width: var(--page-min-width);
  cursor: zoom-out;
  background-color: rgb(10 10 10 / 84%);
  animation: case-lightbox-in 0.22s var(--ease);
}

/* hidden 属性的优先级最低（0,0,0），必须显式压过上面的 display: flex */
.case-lightbox[hidden] {
  display: none;
}

.case-lightbox.is-closing {
  opacity: 0;
  transition: opacity 0.17s var(--ease);
}

@keyframes case-lightbox-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.case-lightbox-figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  max-width: min(1180px, 92vw);
  margin: 0;
  cursor: default;
  animation: case-lightbox-zoom 0.26s var(--ease);
}

@keyframes case-lightbox-zoom {
  from {
    opacity: 0;
    transform: scale(0.965);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.case-lightbox-img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 72vh;
  background-color: #fff;
  border-radius: 12px;
  box-shadow: 0 30px 80px rgb(0 0 0 / 45%);
}

.case-lightbox-caption {
  display: flex;
  align-items: baseline;
  gap: 12px;
  max-width: 100%;
}

.case-lightbox-title {
  font-size: 14px;
  line-height: 22px;
  color: rgb(255 255 255 / 92%);
}

.case-lightbox-note {
  font-size: 13px;
  line-height: 22px;
  color: rgb(255 255 255 / 58%);
}

.case-lightbox-close {
  position: absolute;
  top: 22px;
  right: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  color: rgb(255 255 255 / 88%);
  cursor: pointer;
  background-color: rgb(255 255 255 / 8%);
  border: 1px solid rgb(255 255 255 / 16%);
  border-radius: 50%;
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}

.case-lightbox-close:hover {
  color: #fff;
  background-color: rgb(255 255 255 / 16%);
}

.case-lightbox-close:focus-visible {
  outline: 2px solid rgb(255 255 255 / 70%);
  outline-offset: 2px;
}

.case-lightbox-close svg {
  width: 18px;
  height: 18px;
}

/* 预览打开时锁住整页滚动（滚动容器是 html，不是 body） */
html.is-case-open {
  overflow: hidden;
}

@media (max-width: 1024px) {
  .cases-page .cases-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 700px) {
  .cases-page .cases-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 20px;
  }
  .case-lightbox {
    padding: 28px 16px;
  }
  .case-lightbox-close {
    top: 12px;
    right: 12px;
  }
}

/* ==========================================================================
   下载页
   左侧是下载目录，中间是正文内容，右侧明确留空。
   复用文档页的三栏网格与 sticky 机制，下载页不创建右侧书签，
   因而右栏只承担版式留白，不参与交互。
   ========================================================================== */
#page-downloads {
  padding-top: var(--docs-sticky-gap);
}

.downloads-layout {
  min-height: 520px;
}

.downloads-layout .downloads-side {
  min-width: 0;
}

.downloads-layout .downloads-main {
  min-width: 0;
}

.downloads-layout .downloads-empty {
  min-height: 1px;
}

.download-article {
  padding-bottom: 56px;
}

/* 眉标：必须带 .download-article 前缀（0,2,0）。
   只写 .downloads-eyebrow 是 (0,1,0)，会被 .docs-article p 的 (0,1,1) 压过 ——
   结果是字号被改成 15px、行高被改成 27px，与这里写的 12px / 18px 不符。
   联系页与案例页的眉标本来就带双类前缀（.ct-page .ct-eyebrow /
   .cases-page .cases-eyebrow），只有下载页这一处漏了。 */
.download-article .downloads-eyebrow {
  margin: 0 0 6px;
  font-size: 12px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: rgb(var(--gray-400));
}

.download-article .downloads-title {
  margin: 0 0 12px;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: rgb(var(--gray-900));
}

.download-article .docs-lead {
  max-width: 560px;
  margin-bottom: 0;
}

/* ---------- 下载页极简版：仅超级收藏夹 ----------
   页面保留左侧产品入口，中间只展示版本号与下载按钮，
   不再渲染概述、其他产品、更新说明或右侧书签。 */
.downloads-simple-article {
  padding-bottom: 56px;
}

/* 页面级标题（h1「下载」）：放在中间列，与联系页同一套做法。
   必须带 .downloads-main 前缀凑到 (0,2,0) —— 只写 .downloads-page-title
   是 (0,1,0)，会被 .content-inner h1 的 (0,1,1) 压过，外边距仍是 8px。
   字号与字重由全局 h1 规则 + 这里决定，不单独覆盖 font-size。 */
.downloads-main .downloads-page-title {
  margin: 0 0 28px;
  font-weight: 700;
  color: rgb(var(--gray-900));
}

/* 产品标识区：图标 + 产品名同行。
   眉标（PRODUCT 01）单独占一行，不参与这一行 —— 它只有 18px 高，
   若和标题一起被居中，图标会跟着往上看齐到「眉标 + 标题」整块的中心，
   比标题的视觉中心高出约 12px，看起来就是图标与标题没对齐。

   图标 PNG 是 256×256 画布，但内容实际只占 256×206 —— 上下各有 23 / 27px
   全透明边，四角也是透明的（图形自带造型，不需要再补圆角）。
   所以用正方形盒子 + object-fit: contain：可见图形约 36×29，等比不拉伸；
   盒子宽高写成 36px 是为了让图形的水平方向正好占满，视觉上更饱满。
   盒子 36 与标题行高 35.1 接近，居中对齐后两者视觉中心重合。

   —— 2026-09-23 标题字号全站统一后，本区标题由 48px 降为 h2 的 26px
      （行高 35.1px），图标盒同步从 56px 收到 36px，
      沿用「盒高 ≈ 标题行高」这条原设计逻辑。窄屏覆盖值同比例下调。 */
.downloads-head {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: 36px;
}

.downloads-head-icon {
  flex: none;
  width: 36px;
  height: 36px;
  object-fit: contain;
}

/* 标题作为 flex 项：允许收缩，长标题不会把图标挤出容器 */
.downloads-head .downloads-title {
  min-width: 0;
  margin: 0;
}

/* 间距上移到 .downloads-head，标题自身不再留底距，
   否则会与标识区的底距叠加成双份空白 */
.downloads-simple-article .downloads-title {
  margin-bottom: 0;
}

/* 版本号与下载按钮同一行：左侧版本信息，右侧操作按钮，
   两者共用一个上下描边构成的横条，不再各自成块。 */
.downloads-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 0;
  border-top: 1px solid rgb(var(--gray-500) / 14%);
  border-bottom: 1px solid rgb(var(--gray-500) / 14%);
}

/* 描边与内边距上移到 .downloads-actions，这里只负责版本信息自身的排版 */
.downloads-version {
  display: flex;
  align-items: baseline;
  gap: 16px;
  min-width: 0;
}

.downloads-version-label {
  font-size: 13px;
  line-height: 20px;
  color: rgb(var(--gray-500));
}

.downloads-version strong {
  font-size: 22px;
  line-height: 28px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: rgb(var(--gray-900));
}

.downloads-download-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 152px;
  min-height: 44px;
  padding: 10px 20px;
  font: inherit;
  font-size: 14px;
  line-height: 22px;
  font-weight: 600;
  color: rgb(var(--gray-900));
  cursor: pointer;
  background-color: rgb(var(--gray-50) / 72%);
  border: 1px solid rgb(var(--gray-500) / 18%);
  border-radius: 10px;
  opacity: 1;
}

/* 禁止光标只属于「禁用态」：按钮有链接时渲染成 <a>，此时必须显示可点光标。
   2026-09-27：链接补齐后按钮由 <button disabled> 改为 <a href>，
   而基础规则里遗留的 cursor: not-allowed 会让鼠标变成禁止符号，故移到这里。 */
.downloads-download-button:disabled {
  cursor: not-allowed;
  color: rgb(var(--gray-500));
}

@media (max-width: 700px) {
  .downloads-head {
    gap: 14px;
    margin-bottom: 28px;
  }

  /* 窄屏图标同比例下调：原 56 → 48（×0.857），新 36 → 32（取整） */
  .downloads-head-icon {
    width: 32px;
    height: 32px;
  }


  /* 窄屏下版本号与按钮允许折成两行，避免横向挤压。
     折行后 space-between 对单个元素等同于靠左，版式仍成立。 */
  .downloads-actions {
    flex-wrap: wrap;
    gap: 16px;
  }
}
