/* ==========================================================================
   工具箱首页 Demo —— 独立样式表。
   令牌沿用正式项目那套"两个寄存器":纸面(文档)+ 仪器窗(井)。
   两套令牌由 `<html data-theme>` 选择;默认值由 `<head>` 里的内联脚本按
   本机时间给出(6:00–18:00 浅色,其余深色),手动选过则以存储为准。
   ========================================================================== */

@font-face {
  font-family: 'ZCOOL QingKe HuangYou';
  src: url('assets/zcool-qingke-huangyou-subset.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Share Tech Mono';
  src: url('assets/share-tech-mono-latin-400-normal.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Saira Stencil One';
  src: url('assets/saira-stencil-one-latin-400-normal.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

/* ---------- 令牌:纸面(浅) ---------- */
:root {
  color-scheme: light;
  --bg: #f1f3f4;
  --bg-elevated: #ffffff;
  --bg-inset: #e3e8eb;
  --border: #bcc7ce;
  --border-strong: #93a3ae;
  --text: #16202a;
  --text-dim: #55636e;
  --text-faint: #7c8a94;
  --accent: #16181a;
  --accent-dim: #e4e7e9;

  --well: #10161c;
  --well-text: #dce5ec;
  --well-accent: #5ed69b;
  --well-warn: #fbbf24;
  --well-border: #2a3742;

  --radius: 10px;
  --radius-lg: 14px;
  --radius-pill: 999px;
  --max: 1160px;

  --font-sans: system-ui, -apple-system, 'Segoe UI', 'Microsoft YaHei', sans-serif;
  --font-display: 'ZCOOL QingKe HuangYou', var(--font-sans);
  --font-mono: 'Share Tech Mono', ui-monospace, 'Cascadia Mono', monospace;

  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05), 0 4px 12px -6px rgb(0 0 0 / 0.12);
  --shadow-lg: 0 2px 4px rgb(0 0 0 / 0.06), 0 18px 34px -16px rgb(0 0 0 / 0.24);

}

/* ---------- 令牌:纸面(深) ---------- */
:root[data-theme='dark'] {
  color-scheme: dark;
  --bg: #0b0c0e;
  --bg-elevated: #15171a;
  --bg-inset: #0f1113;
  --border: #24282d;
  --border-strong: #363b41;
  --text: #f0f2f4;
  --text-dim: #9ba3ab;
  --text-faint: #6e767e;
  --accent: #f0f2f4;
  --accent-dim: #1f2226;

  --well: #050506;
  --well-border: #2e3034;

  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.5), 0 6px 16px -8px rgb(0 0 0 / 0.7);
  --shadow-lg: 0 2px 6px rgb(0 0 0 / 0.6), 0 22px 40px -18px rgb(0 0 0 / 0.9);

}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

/*
 * 站内锚点要留出吸顶报头的高度。不留的话,点了导航之后目标标题正好被
 * 那条 64px 的报头盖住 —— 看起来像"跳错地方了",而读代码完全看不出来。
 */
[id] { scroll-margin-top: 84px; }

/*
 * 焦点环的兜底。各处组件本来就各自写了 `:focus-visible`,但总有漏网的
 * (品牌链接、页脚链接)。用 `:where()` 把特异度压成 0,所以任何一条
 * 更具体的规则都照样赢 —— 兜底不会盖掉组件自己的长相。
 */
:where(a, button):focus-visible {
  outline: 2px solid color-mix(in srgb, var(--text) 45%, transparent);
  outline-offset: 2px;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
}

/*
 * 主题切换的过渡。**只在按开关时挂约 300ms**(`app.js` 加 `.theme-anim`),
 * 首帧不挂 —— 否则会从浏览器默认色慢慢淡进来。
 *
 * 和正式站 `global.css` 那条同一套写法:`!important` 压过组件自己的
 * `transition`,整页走同一条时间线。减弱动态的读者由文件末尾那条夹子
 * 压成 0.01ms。
 */
:root.theme-anim,
:root.theme-anim *,
:root.theme-anim *::before,
:root.theme-anim *::after {
  transition:
    background-color 280ms ease,
    color 280ms ease,
    border-color 280ms ease,
    fill 280ms ease,
    stroke 280ms ease,
    box-shadow 280ms ease,
    opacity 280ms ease !important;
}

@media (prefers-reduced-motion: reduce) {
  :root.theme-anim,
  :root.theme-anim *,
  :root.theme-anim *::before,
  :root.theme-anim *::after {
    transition-duration: 0.01ms !important;
  }
}

a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
h1, h2, h3 { margin: 0; font-weight: 400; line-height: 1.2; }
p { margin: 0; }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* 星空:只在深色下出现,和正式站同一条判据 */
.aurora {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(900px 420px at 18% -8%, color-mix(in srgb, #38bdf8 12%, transparent), transparent 70%),
    radial-gradient(760px 380px at 92% 4%, color-mix(in srgb, #a78bfa 12%, transparent), transparent 70%);
  opacity: 0;
  transition: opacity 300ms ease;
}
:root[data-theme='dark'] .aurora { opacity: 1; }

/* ---------- 星空:六层,各自的周期与相位不同 ---------- */
/*
 * **这一段是从正式站的 `Backdrop.astro` 直接照搬过来的**(用户的意见:那边已经调好了,
 * 不该在这儿另起一份)。下面每一条数值都是那边量出来、并且被用户打回重调过的,
 * 别凭手感改回去:
 *
 * - **一层里塞四五个点,不是一层一个。** 一层一个点,`background-repeat` 出来的是
 *   规整的方格阵,放大一眼就看出是网格。
 * - **每个点自己带亮度和大小**(alpha 0.34–1.0、直径 1.1–2.5px),所以同一层里
 *   就有亮星、有暗星;所有点给同一档的话,叠出来是一片均匀的白噪点。
 * - **六层的周期(4.6–9.6s)与相位(负延迟)两两不同**,所以任一时刻亮着的
 *   只是其中几片 —— "几个几个地闪"就是从这里来的。两个伪元素做不到:
 *   一个元素只有一个动画,那片里的星只能整片一起明灭。
 * - **色温给了极淡的差**(冷白 / 暖白 / 淡金 / 淡蓝 / 淡玫瑰 / 纯白)。
 *   真实星空的星等不同,色温本来就不一样。**饱和度必须压到极低**,
 *   再高一点就读成"彩色雪花"。
 * - **`50%, transparent 51%` 那一刀是硬的。** 柔边(比如 `transparent 65%`)
 *   在深色下读成"雾",硬边才读成"星点"。
 * - **`--lo` 全是 0**,这是用户定的。原先担心"降到全黑再冒出来是在开关灯、
 *   不是在闪",实测不成立:六层相位两两不同,场永远不会空,而密度随时间
 *   明显起伏 —— 那正是"忽明忽暗"。
 */
.sky {
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  opacity: 0;
  transition: opacity 400ms ease;
}
:root[data-theme='dark'] .sky { opacity: 1; }

/*
 * 星层是 canvas,不是 CSS 渐变(理由见 Backdrop.astro 的同一段注释):
 * 每层几十颗随机星点,画一次;opacity 仍由 CSS 的 twinkle 驱动。
 */
.sky__layer {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* 冷白 */
.sky__layer:nth-child(1) {
  --lo: 0;
  --hi: 1;
  animation: twinkle 5.2s ease-in-out -0.4s infinite;
}

/* 暖白 */
.sky__layer:nth-child(2) {
  --lo: 0;
  --hi: 0.95;
  animation: twinkle 6.8s ease-in-out -2.9s infinite;
}

/* 淡金 */
.sky__layer:nth-child(3) {
  --lo: 0;
  --hi: 0.8;
  animation: twinkle 8.1s ease-in-out -5.5s infinite;
}

/* 淡蓝 */
.sky__layer:nth-child(4) {
  --lo: 0;
  --hi: 0.9;
  animation: twinkle 4.6s ease-in-out -1.7s infinite;
}

/* 淡玫瑰 */
.sky__layer:nth-child(5) {
  --lo: 0;
  --hi: 0.84;
  animation: twinkle 7.4s ease-in-out -3.6s infinite;
}

/* 纯白(最亮的一层,周期最长) */
.sky__layer:nth-child(6) {
  --lo: 0;
  --hi: 1;
  animation: twinkle 9.6s ease-in-out -7.2s infinite;
}

/*
 * **明灭的两端由每层自己给**(`--lo` / `--hi`)。共用一对值的话,暗层的星
 * 到头来也会亮到同一个顶点,一轮闪完亮度差就被抹平。
 */
@keyframes twinkle {
  0%, 100% { opacity: var(--lo, 0.3); }
  50% { opacity: var(--hi, 1); }
}

/* ---------- 流星:偶尔划过一道,纯 CSS ---------- */
/*
 * **这一段同样是从正式站的 `Backdrop.astro` 照搬的。** 原来那版是"一根水平细条
 * 转 -32°、往右平移 420px",毛病是位移短、看着像一条在飘的短线;这一版是
 * 45° 细条 + `vmax` 位移,一道能从屏幕一角划到另一角。
 *
 * ## 为什么位移用 `vmax` 而不是 `vw` / `vh`
 *
 * 条子躺的是**精确的 45°**。位移横向用 `vw`、纵向用 `vh` 的话,在 1440×900 上
 * 两者像素数不一样 —— 于是它**指着一个方向、却往另一个方向飘**,一条斜线在
 * 横着滑,一眼就看得出别扭。两个分量取同一个 `vmax`,位移方向恒等于 45°。
 *
 * ## "一闪而过"是怎么来的
 *
 * 整轮 `--dur` 里**只有开头 10% 在飞**,其余 90% 停在终点(而终点在屏幕外),
 * 所以它不常驻。七条的周期(11–20s)与相位(`--delay` 取负值,所以一进页面
 * 就可能有道正在飞)两两不同,合起来大约**每两秒天上过一道**。
 *
 * ## 基准态必须是 `opacity: 0`
 *
 * 减弱动态时全局那条夹子把 `animation-duration` 压成 0.01ms —— 动画瞬间跑完
 * 就回到基准态,于是整条消失。**别把基准改成 1**,改了减弱动态的用户就会
 * 看到七条白线卡在屏幕上。
 */
.meteors {
  position: fixed;
  inset: 0;
  z-index: -2;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
}
:root[data-theme='dark'] .meteors { opacity: 1; }

.meteors i {
  position: absolute;
  left: 0;
  top: 0;
  width: 2px;
  height: var(--tail, 130px);
  border-radius: 999px;
  /* 亮的那头是头、透明的那头是尾 —— 渐变方向就是运动方向 */
  background: linear-gradient(
    to bottom,
    rgb(255 255 255 / 0) 0%,
    rgb(255 255 255 / 0.16) 55%,
    rgb(255 255 255 / var(--peak, 0.92)) 100%
  );
  opacity: 0;
  animation: shoot var(--dur, 16s) linear var(--delay, 0s) infinite;
}

/*
 * 位移的两个分量取同一个 `--run`(都是 `vmax`),所以方向恒为 45°。
 * `--x` 是横向上从哪儿进场,每条不一样 —— 全从同一个点出来会像一列火车。
 * 亮起来只占 1%:一道流星是"一下子出现"的,不是淡入的。
 */
@keyframes shoot {
  0% {
    opacity: 0;
    transform: translate3d(var(--x, 0px), -18vh, 0) rotate(-45deg);
  }
  1% {
    opacity: var(--peak, 0.92);
  }
  10% {
    opacity: 0;
    transform: translate3d(
        calc(var(--x, 0px) + var(--run, 115vmax)),
        calc(-18vh + var(--run, 115vmax)),
        0
      )
      rotate(-45deg);
  }
  100% {
    opacity: 0;
    transform: translate3d(
        calc(var(--x, 0px) + var(--run, 115vmax)),
        calc(-18vh + var(--run, 115vmax)),
        0
      )
      rotate(-45deg);
  }
}

/*
 * 长 / 亮 / 从哪儿进 / 多久一轮 / 相位错开。
 *
 * **"多久一道"只由 `--dur` 决定**:每条一轮飞一次,平均间隔 ≈ 1 ÷ Σ(1/--dur)。
 * `--tail` 和 `--peak` 只管每一条自己的长短明暗,不影响频率 ——
 * 觉得"不够亮"调 `--peak`,觉得"太稀"调 `--dur`,别把两件事混着调。
 */
.meteors i:nth-child(1) { --x: -6vw;  --tail: 150px; --dur: 13s;   --delay: -2.5s;  --peak: 0.95; }
.meteors i:nth-child(2) { --x: 12vw;  --tail: 100px; --dur: 17s;   --delay: -11s;   --peak: 0.72; }
.meteors i:nth-child(3) { --x: 28vw;  --tail: 130px; --dur: 15s;   --delay: -6.2s;  --peak: 0.85; }
.meteors i:nth-child(4) { --x: 44vw;  --tail: 88px;  --dur: 20s;   --delay: -15.4s; --peak: 0.66; }
.meteors i:nth-child(5) { --x: 60vw;  --tail: 120px; --dur: 11s;   --delay: -1.2s;  --peak: 0.9; }
.meteors i:nth-child(6) { --x: 76vw;  --tail: 108px; --dur: 18s;   --delay: -9.1s;  --peak: 0.78; }
.meteors i:nth-child(7) { --x: 92vw;  --tail: 140px; --dur: 14s;   --delay: -12.8s; --peak: 0.88; }

/* 减弱动态时:星空静止、流星不飞、波形不滚 —— 和正式站那条全站夹子同一个态度 */
@media (prefers-reduced-motion: reduce) {
  .sky__layer { animation: none; opacity: 0.5; }
  .meteors i { animation: none; }
  .scope__trace { animation: none; }
  /*
   * SMIL 不受上面那条夹子管,得单独处理。**不能写 `.scope__sweep animate
   * { display: none }`** —— 动画元素本来就不参与渲染,`display` 对它没有
   * 可靠的效果(试过,Chrome 里照样在动)。直接收掉那条扫描线。
   */
  .scope__sweep { display: none; }
}

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

/* ============================ 报头 ============================ */
.top {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
}
.top__inner {
  display: flex;
  align-items: center;
  gap: 20px;
  height: 64px;
}

.brand { display: flex; align-items: center; gap: 10px; }
.brand__mark {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--bg-elevated);
}
.brand__mark svg { width: 18px; height: 18px; }
.brand__text { display: flex; flex-direction: column; line-height: 1.1; }
.brand__text strong { font-size: 1.02rem; letter-spacing: 0.02em; }
.brand__sub {
  font-family: 'Saira Stencil One', var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  color: var(--text-faint);
}

.nav { display: flex; gap: 22px; margin-left: 18px; }
.nav a {
  position: relative;
  color: var(--text-dim);
  font-size: 0.92rem;
}
.nav a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -5px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 160ms ease;
}
.nav a:hover, .nav a:focus-visible { color: var(--text); }
.nav a:hover::after, .nav a:focus-visible::after { transform: scaleX(1); }

.top__right { display: flex; align-items: center; gap: 10px; margin-left: auto; }

.icon-btn {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: none;
  color: var(--text-dim);
  cursor: pointer;
}
.icon-btn:hover, .icon-btn:focus-visible {
  background: var(--accent-dim);
  border-color: var(--border-strong);
  color: var(--text);
}
.icon-btn svg { width: 17px; height: 17px; }

.seg {
  display: flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.seg__item {
  padding: 3px 10px;
  font-size: 0.78rem;
  color: var(--text-faint);
}
.seg__item.is-on { background: var(--accent-dim); color: var(--text); }

.only-light { display: none; }
:root[data-theme='light'] .only-light { display: block; }
:root[data-theme='light'] .only-dark { display: none; }

/* ============================ Hero ============================ */
.hero {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: 56px;
  align-items: center;
  /*
   * **第一屏占满一屏。** 这样"往下滚"才有一段可以走 —— 云在这段里往上爬,
   * 下一节才露出来。官方参考站那种"滚轮往下、云在动"的效果全靠这一段高度。
   */
  min-height: calc(100vh - 64px);
  padding: 72px 24px 96px;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  margin-bottom: 18px;
}
.eyebrow .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--well-accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--well-accent) 18%, transparent);
}

h1 {
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 4.6vw, 3.5rem);
  line-height: 1.18;
  letter-spacing: 0.01em;
}

.lede {
  margin-top: 18px;
  max-width: 34em;
  color: var(--text-dim);
}
.lede strong { color: var(--text); }

.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 10px 20px;
  border-radius: var(--radius-pill);
  font-size: 0.95rem;
  border: 1px solid var(--border-strong);
  transition: transform 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
.btn svg { width: 16px; height: 16px; }
.btn--solid {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
  font-weight: 600;
}
.btn--solid:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
/*
 * 幽灵按钮悬停**整个反色**(实心墨底 + 纸面字),而不是只把底色加深一档 ——
 * 后者在深色下几乎看不出来。实心是这一站"选中/按下"的长相,借来当悬停,
 * 回馈足够明确。
 */
.btn--ghost:hover, .btn--ghost:focus-visible {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
}
.btn:focus-visible { outline: 2px solid color-mix(in srgb, var(--text) 45%, transparent); outline-offset: 2px; }

.trust {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
  color: var(--text-faint);
  font-size: 0.84rem;
}
.trust li { display: inline-flex; align-items: center; gap: 6px; }
.trust svg { width: 14px; height: 14px; }

/* 仪器窗 */
.scope {
  /*
   * 抬到前景云之上(`z-index: 6`)。前景云是一层淡淡飘过的雾,它**不该盖住读数**。
   */
  position: relative;
  z-index: 6;
  --scope-trace: #5ed69b;
  --scope-ok: #4ade80;
  background: var(--well);
  border: 1px solid var(--well-border);
  border-radius: var(--radius-lg);
  padding: 16px;
  box-shadow: var(--shadow-lg);
  color: var(--well-text);
}

.scope__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  color: #94a3b0;
}

/*
 * 右上那枚状态灯。**待机时是灰的,跑起来才亮** —— 它是这一块唯一的
 * "现在有没有在动"的记号,一直亮着就等于没亮。
 */
/*
 * 状态灯。**常亮** —— 波形一直在动,所以"LIVE"在这块面板上不是一个状态,
 * 而是它的常驻说明:这是一段示意信号,不是一次测量。
 */
.scope__live { display: inline-flex; align-items: center; gap: 6px; color: var(--scope-ok); }
.scope__live i {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 8px var(--scope-ok);
  animation: scope-pulse 1.6s ease-in-out infinite;
}

@keyframes scope-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

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

/*
 * 波形面板**是一个按钮** —— 它真的可以点。所以这里要把按钮的默认长相
 * 全部归零(边框、内边距、字体、背景),再按这块面板自己的样子重画。
 */
.scope__plot {
  position: relative;
  display: block;
  width: 100%;
  height: 150px;
  margin: 14px 0 0;
  border: 1px solid #1b2733;
  border-radius: var(--radius);
  background: #080c10;
  overflow: hidden;
}

.scope__grid-svg,
#scope-wave {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.scope__grid { stroke: #1b2733; stroke-width: 1; fill: none; }

/* 面板下面那句"点了会发生什么"。它同时承担"这是示意,不是实测"的说明 */
.scope__hint {
  margin: 10px 0 0;
  font-size: 0.78rem;
  color: #8b98a6;
}

.scope__readouts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 12px;
}
.scope__readouts div {
  background: #0c1116;
  border: 1px solid #1b2733;
  border-radius: 8px;
  padding: 8px 10px;
}
/*
 * **只把直接子元素里那个 `<span>` 当标签。**
 *
 * 写成 `.scope__readouts span` 会连 `<b>` 里那个数值 `<span>` 一起命中 ——
 * 于是数值变成块级,"ms" 被挤到下一行(第一版就是这样,截图里一眼看得出来)。
 */
.scope__readouts div > span {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  color: #6b7b88;
}
.scope__readouts b {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 1.05rem;
  color: var(--well-text);
}
.scope__readouts b i { font-style: normal; font-size: 0.7rem; color: #6b7b88; }

.scope__cta {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 12px;
  border-radius: var(--radius);
  border: 1px solid #1b2733;
  background: #151b22;
  color: var(--well-text);
  text-align: center;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease;
}
.scope__cta:hover { background: #1b232c; border-color: #2a3a4a; }
.scope__cta:focus-visible { outline: 2px solid color-mix(in srgb, var(--scope-trace) 60%, transparent); outline-offset: 2px; }
.scope__cta:disabled { opacity: 0.6; cursor: default; }

/* 跑出结果之后那一格变绿。**只有这一格会变色** —— 它说的是"这一下被收到了" */
.scope__readouts b.is-ok { color: var(--scope-ok); }

/* ============================ 区块 ============================ */
.band { padding: 52px 24px; }
.band + .band { border-top: 1px solid var(--border); }

.band__head { margin-bottom: 26px; }
.band__head h2 {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.6vw, 2rem);
}
.band__head p { margin-top: 8px; color: var(--text-dim); max-width: 52em; }
.band__head small { display: block; margin-top: 6px; color: var(--text-faint); font-size: 0.82rem; }
.band__head--row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.band__head b { color: var(--text); }

/* 搜索 */
.search {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 300px;
  padding: 9px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg-elevated);
}
.search:focus-within { border-color: var(--text); }
.search svg { width: 16px; height: 16px; color: var(--text-faint); flex: none; }
.search input {
  border: 0;
  outline: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 0.9rem;
  width: 100%;
}
.search input::placeholder { color: var(--text-faint); }

/* ============================ 分类卡 ============================ */
.cats {
  display: grid;
  /*
   * **固定 3 列,不用 `auto-fit`。** 六张卡在 1112px 下 `auto-fit` 会给出 4 列,
   * 于是第二行只剩两张、右边空两个洞 —— 而"洞"会被读成"这里少了一张卡"。
   * 3 列正好 3+3。窄档另给 2 列 / 1 列。
   */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

.cat {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 20px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: transform 140ms ease, box-shadow 140ms ease, border-color 140ms ease;
}
/*
 * **悬停要一眼看得出来。** 用户看完 Demo 说「鼠标碰到可以点击的地方,样式不够明显」——
 * 原来只有 `translateY(-1px)` + 边框深一档,在一张 250px 的卡上几乎不可见。
 * 现在给三样一起:色相边框、一圈 2px 的外环、抬起 3px。
 * 外环用 `outline`(不占布局、跟着圆角),和正式项目那圈悬停环是同一个思路。
 */
.cat:hover, .cat:focus-visible {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  border-color: color-mix(in srgb, var(--hue) 70%, var(--border));
  outline: 2px solid color-mix(in srgb, var(--hue) 42%, transparent);
  outline-offset: 2px;
}
.cat--soon { border-style: dashed; }

.cat__icon {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  margin-bottom: 6px;
  border-radius: 10px;
  color: var(--hue);
  background: color-mix(in srgb, var(--hue) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--hue) 30%, transparent);
}
.cat--soon .cat__icon { color: var(--text-dim); background: var(--bg-inset); border-color: var(--border); }
.cat__icon svg { width: 20px; height: 20px; }

.cat__count {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  text-transform: uppercase;
}
.cat strong { font-size: 1.05rem; font-weight: 600; }
.cat__desc { color: var(--text-dim); font-size: 0.88rem; line-height: 1.65; }
.cat__tags {
  margin-top: auto;
  padding-top: 10px;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  color: var(--text-faint);
}

/* ============================ 工具目录 ============================ */
.empty { padding: 20px 0; color: var(--text-faint); }

.group { margin-top: 34px; }
.group[hidden] { display: none; }

.group__head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}
.group__icon {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  color: var(--hue);
  background: color-mix(in srgb, var(--hue) 14%, transparent);
}
.group__icon svg { width: 15px; height: 15px; }
.group__head h3 { font-size: 1rem; font-weight: 600; }
.group__n {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--text-faint);
  letter-spacing: 0.06em;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(245px, 1fr));
  gap: 10px;
}

/*
 * **`.tool[hidden]` 这条不是多余的。** `.tool` 自己写了 `display: flex`,而
 * **作者样式无条件压过浏览器默认的 `[hidden] { display: none }`** —— 少了这条,
 * 搜索框敲什么都没用:卡片一张都不会消失(实测:给一张卡加 `hidden`,
 * 它照样完整渲染)。这是"不报错、只是不起作用"的那一类。
 *
 * `.group[hidden]` 早就单独写过了,原因一模一样。
 */
.tool[hidden] { display: none; }

.tool {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 14px 16px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 140ms ease, transform 140ms ease, box-shadow 140ms ease;
}
/*
 * 工具卡小,只提边框色不够。加一条**内侧的色相竖条** + 抬起 2px:
 * 竖条是"这一行被选中了"的长相,扫一眼就知道鼠标停在哪儿。
 */
.tool:hover, .tool:focus-visible {
  border-color: color-mix(in srgb, var(--hue) 55%, var(--border));
  transform: translateY(-2px);
  box-shadow: inset 3px 0 0 0 var(--hue), var(--shadow-sm);
}
.tool:focus-visible { outline: 2px solid color-mix(in srgb, var(--hue) 42%, transparent); outline-offset: 2px; }
.tool strong { font-size: 0.95rem; font-weight: 600; }
.tool > span:not(.badge) { color: var(--text-dim); font-size: 0.83rem; line-height: 1.6; }
.tool--soon { border-style: dashed; }
.tool--soon strong { color: var(--text-dim); }

.badge {
  align-self: flex-start;
  margin-top: 6px;
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  font-size: 0.7rem;
  border: 1px solid var(--border);
  color: var(--text-faint);
}
.badge--exact { color: var(--text); border-color: var(--border-strong); }
.badge--estimate { color: var(--text-dim); border-color: var(--border-strong); }
.badge--shape,
.badge--direction { color: var(--text-dim); }
.badge--none { color: var(--text-faint); }
.badge--soon { color: var(--text-faint); border-style: dashed; }

/* ============================ 先试这几个 ============================ */
.picks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}
.pick {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform 140ms ease, box-shadow 140ms ease, border-color 140ms ease;
}
.pick:hover, .pick:focus-visible {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
}
/* 图片本身也跟着放大一点 —— 缩略图上这是最强的"这里有东西"信号 */
.pick__img img { transition: transform 320ms ease; }
.pick:hover .pick__img img, .pick:focus-visible .pick__img img { transform: scale(1.045); }
.pick:focus-visible { outline: 2px solid color-mix(in srgb, var(--text) 35%, transparent); outline-offset: 2px; }
.pick__img {
  position: relative;
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--bg-inset);
}
/*
 * 占位图必须**自己说出来**。这些是 Picsum 的随机风景,不标的话读者会以为
 * 「鼠标按键」那张卡配的就是一张森林照片 —— 那比没有图更糟。
 */
.pick__img::after {
  content: '占位图';
  position: absolute;
  left: 8px;
  bottom: 8px;
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  background: rgb(0 0 0 / 0.55);
  color: #fff;
  font-size: 0.66rem;
  letter-spacing: 0.04em;
}
.pick__img img { width: 100%; height: 100%; object-fit: cover; }
.pick strong,
.pick span:not(.pick__img) { padding: 0 14px; }
.pick strong { padding-top: 12px; font-size: 0.96rem; font-weight: 600; }
.pick span:not(.pick__img) { padding-bottom: 14px; color: var(--text-dim); font-size: 0.83rem; }

/* ============================ 使用场景 ============================ */
.shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 12px;
}
.shot {
  margin: 0;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.shot img { aspect-ratio: 3 / 2; width: 100%; object-fit: cover; }
.shot figcaption { display: flex; flex-direction: column; gap: 2px; padding: 12px 14px 14px; }
.shot figcaption b { font-size: 0.94rem; font-weight: 600; }
.shot figcaption span { color: var(--text-dim); font-size: 0.84rem; }

/* ============================ 说明条 ============================ */
.about {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: 44px;
  align-items: center;
  padding: 28px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.about h2 { font-family: var(--font-display); font-size: clamp(1.35rem, 2.2vw, 1.8rem); }
.about p { margin-top: 12px; color: var(--text-dim); max-width: 46em; }
.about p strong { color: var(--text); }
.about__note {
  font-family: var(--font-mono);
  font-size: 0.82rem !important;
  color: var(--text-faint) !important;
  letter-spacing: 0.02em;
}
.about__art svg { width: 100%; height: auto; opacity: 0.92; }

/* ============================ 页脚 ============================ */
.foot {
  border-top: 1px solid var(--border);
  padding: 26px 0 40px;
}
.foot__inner { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.foot p { color: var(--text-faint); font-size: 0.84rem; }
.foot strong { color: var(--text-dim); }
.foot__meta { font-family: var(--font-mono); font-size: 0.74rem !important; }

/* ============================ 窄屏 ============================ */
@media (max-width: 860px) {
  .hero { grid-template-columns: 1fr; gap: 32px; padding-top: 48px; }
  .about { grid-template-columns: 1fr; gap: 24px; }
  .nav { display: none; }
  .cats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .wrap { padding: 0 16px; }
  .band { padding: 38px 16px; }
  .hero { padding: 36px 16px 24px; }
  .search { min-width: 0; width: 100%; }
  .cats { grid-template-columns: 1fr; }
  /* 三个读数**不堆叠** —— 420 下面板还有 356px,三列各约 113px 放得下。
     堆成三行会把面板从 ~260px 撑到 ~450px,而那三个数本来就是并排看的。 */
  .scope__plot { height: 120px; }
  .brand__sub { display: none; }
}


/* ==========================================================================
   云海背景 —— 参考站那套 WebGL 云层的**免 WebGL 近似**。
   ==========================================================================

   它是怎么做的:五团极大的径向渐变,各自以不同周期缓慢漂移;
   底下铺一层天空的竖向渐变,最上面压一层 28px 的细网格。

   **为什么不上真的 WebGL**:参考站那版是每帧在 GPU 上算的,而这一页上还有
   装置和采样器(首页那些卡片一旦被碰就会跑)。一个铺满视口的 shader 会和它们
   抢同一帧 —— 而"不和测量抢帧"是这个站写在文档第一条的纪律。
   这五团东西是合成器动画,主线程一个帧都不占。

   代价说清楚:**它没有体积感和光照,只有"柔软的大块明暗在动"**。近看是一回事,
   远看是另一回事 —— 这一点得你自己看截图判断值不值。
   ========================================================================== */

.clouds {
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  overflow: hidden;
  opacity: 0;
  transition: opacity 500ms ease;
}
/*
 * **云只在浅色下出现。** 这不是没做完:云海是白天的场景,夜里再怎么调都只会
 * 变成一层灰。深色那边用的是星空 —— 两套跟着主题走,不需要第二个开关。
 */
:root[data-theme='light'] .clouds { opacity: 1; }

/* 切到云海时,星空与流星让位 */


/*
 * **云是画在 canvas 上的**,不是几团渐变。原因很实在:柔光团没有形状,
 * 远看是一层脏雾;而参考站那种效果的核心是**有轮廓的云,而且随滚动分层移动**。
 * 那要的是噪声纹理 + 视差,纯 CSS 给不了。
 *
 * 画布按半分辨率渲染再拉伸 —— 云本来就不该有锐边,顺带省一半像素。
 * 四层各自的视差速度不同:越"近"的层随滚动移动越多,于是滚动时云会互相错开。
 */
.clouds__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

}

/*
 * 原来这里还有一层 28px 的"测绘纸"细网格。**2026-10-18 用户要求去掉**
 * (测试页上看着像"格子")。网格和 `--grid-color` 一起删了,
 * `CloudBackdrop.astro` 那边也不再画它。
 */

/* 减弱动态时,云不动 —— 但还在,只是静止的一层 */
@media (prefers-reduced-motion: reduce) {
  /* 云不动:脚本会渲染一张静态的 */
  .clouds__canvas { opacity: 1; }
}



/* ==========================================================================
   滚动进场的淡入(参考站那种"内容缓缓显现")
   ==========================================================================

   三样一起给才读得出"浮现":透明度、位移、**模糊**。
   只给透明度像幽灵,只给位移像抽风,加了模糊才像"从远处对焦过来"。
   用 IntersectionObserver 触发(`app.js`),不进 rAF 循环。
   ========================================================================== */
.js .reveal {
  opacity: 0;
  transform: translateY(28px);
  filter: blur(7px);
  transition:
    opacity 720ms cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 720ms cubic-bezier(0.22, 0.61, 0.36, 1),
    filter 720ms cubic-bezier(0.22, 0.61, 0.36, 1);
  /* 进场之后再也不用管它 —— 别让 transform 一直挂着占一个图层 */
  will-change: opacity, transform, filter;
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
  filter: none;
  will-change: auto;
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }
}

/* 首屏底部那个"向下滚动"的提示 */
.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  color: var(--text-faint);
}
.hero__scroll {
  transition: opacity 300ms ease;
}
.hero__scroll.is-gone {
  opacity: 0;
}
.hero__scroll svg {
  width: 13px;
  height: 13px;
  /* 箭头本来指右,转成指下 */
  transform: rotate(90deg);
}


/* ==========================================================================
   前景云:浮在内容之上的一层
   ==========================================================================
   `z-index: 5` —— 在正文之上、在吸顶报头(20)之下,所以滚过报头时不会糊住菜单。
   `pointer-events: none` 是硬要求:这一层铺满视口,接事件的话整页按钮全废。
   ========================================================================== */
.clouds-front {
  position: fixed;
  inset: 0;
  z-index: 5;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 500ms ease;
}
:root[data-theme='light'] .clouds-front { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  /* 减弱动态:前景那层直接不出现 —— 它挡字,而它带来的信息是零 */
  .clouds-front { display: none; }
}
