/* ══════════════════════════════════════════════════════════════════════════
   style.css —— 浅白纸面上的液态玻璃
   ──────────────────────────────────────────────────────────────────────────
   两条主线：
     ① 玻璃要「看得出来」—— 磨砂 + 折射亮边 + 分层投影 + 跟随鼠标的高光。
        前提是背景必须有东西可模糊：光雾、网格、鼠标柔光都要看得见。
     ② 背景是一张**浅白、缓慢呼吸**的纸面：光雾 + 细网格 + 一层跟随鼠标的
        柔光拖尾（glow.js 画的 canvas）。鼠标划过去会留下一条由浓到淡、
        自己淡掉的光痕；玻璃卡片把背景一起磨砂掉，所以玻璃背后是活的。
   结构：
     1 变量  2 基础  3 背景（光雾 + 网格 + 柔光）  4 顶栏  5 首屏
     6 零件  7 玻璃材质  8 我的几面  9 数字  10 联系
     11 详情页  12 灯箱 · 页脚  13 响应式
   ══════════════════════════════════════════════════════════════════════════ */

/* ─────────── 1. 设计变量 ─────────── */
:root {
  /* 纸 —— 只在浅白里做层次，不用任何彩色 */
  --bg:         #F1F1EE;
  --bg-top:     #FAFAF8;
  --bg-bottom:  #EDEDEA;

  /* 光雾三调。思路是「在微灰的纸上打白光」，而不是「在白纸上抹灰」：
     前者只会越来越亮，永远不会脏；后者一重就成污渍。
     冷灰是唯一负责「压暗」的，浓度必须够（≥.38），否则整页是平的，动起来也看不出来。 */
  --cloud-lt:   255,255,255;
  --cloud-dk:   122,128,138;
  --cloud-warm: 176,166,150;

  --ink:        #15171B;   /* 标题 */
  --body:       #4B5158;   /* 正文 */
  --ink-2:      #6E757E;   /* 次要 */
  --ink-3:      #8F959D;   /* 提示 */

  --line:       rgba(21,23,27,.10);
  --line-2:     rgba(21,23,27,.19);
  --line-3:     rgba(21,23,27,.30);

  --blur:       30px;
  --sat:        165%;

  --shadow-1:   0 1px 2px rgba(16,18,22,.05);
  --shadow-2:   0 12px 26px -12px rgba(16,18,22,.20);
  --shadow-3:   0 42px 72px -32px rgba(16,18,22,.34);

  --font-sans: Inter, "Noto Sans SC", -apple-system, BlinkMacSystemFont, "PingFang SC",
               "HarmonyOS Sans SC", "Microsoft YaHei", system-ui, sans-serif;
  --font-display: "Noto Serif SC", "Source Han Serif SC", "Noto Serif CJK SC", "Songti SC",
               STZhongsong, "华文中宋", Georgia, serif;
  /* 等宽里必须带中文字体，否则小标签里的中文会掉进宋体 */
  --font-mono: "JetBrains Mono", "Cascadia Mono", ui-monospace, Consolas,
               "Noto Sans SC", "Microsoft YaHei", monospace;

  --ease:       cubic-bezier(.22,.61,.36,1);
  --ease-out:   cubic-bezier(.16,1,.3,1);
  --r:          22px;
  --maxw:       1160px;
}

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  /* 头像那圈光晕故意溢出画框，会撑出几个像素的横向宽度。
     用 clip 而不是 hidden：clip 不创建滚动容器，position: sticky / fixed 都不受影响。 */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--body);
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img { max-width: 100%; display: block; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }

/* 作者样式里的 display 会盖掉浏览器默认的 [hidden]，这里兜底 */
[hidden] { display: none !important; }

::selection { background: rgba(21,23,27,.14); }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 6px; }

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: clamp(18px, 4vw, 40px);
}

.mono {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.eyebrow {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.eyebrow::before {
  content: '';
  display: inline-block;
  width: 22px;
  height: 1px;
  margin-right: 10px;
  vertical-align: middle;
  background: linear-gradient(90deg, transparent, var(--line-3));
}

/* ─────────── 3. 背景：光雾 + 网格 + 柔光 ───────────
   顺序就是层次，从下往上：
     .layer（光雾，远/中/近三层）→ .liquid（两束流动的光斑）→ .grid（细网格）
     → .dust（纸面浮尘，particles.js 那张 canvas）
     → .glow（鼠标柔光拖尾，glow.js 那张 canvas）→ .grain → .vignette
   光雾与网格是「底色」：滚动时按进度位移，制造纵深；
   最上面那层是跟随鼠标的柔光拖尾，停手后自己淡掉、随即停止绘制。
   底色本身一直有极慢的呼吸与漂移，所以它永远不是一张静止的贴图。
   ────────────────────────────────────────────────────────────────── */
.bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(178deg, var(--bg-top) 0%, var(--bg) 46%, var(--bg-bottom) 100%);
}

.layer {
  position: absolute;
  inset: -46% -22%;
  --travel: -8;
  --dx: 0;
  transform: translate3d(calc(var(--p, 0) * var(--dx, 0) * 1vw),
                         calc(var(--p, 0) * var(--travel) * 1vh), 0);
  will-change: transform;
}

/* 光雾为什么「看不出在动」——三个原因，必须一起解决：
   ① 尺寸：要用 vh 定，而且不能太大。雾团大到一百多个 vh，渐变在一屏内就摊平了，
      整页变成一块均匀的灰，位移多少都看不出来。
   ② 形状：必须能被眼睛「定位」。单个径向渐变是完美圆形，又软又匀，动起来等于没动；
      每团用两个偏心渐变叠出不规则轮廓，眼睛才有东西可追。
   ③ 浓淡的走法：三档递减（浓 → 中 → 0），不要「先保持不变、再突然衰减」那种台阶。
      台阶会让雾团看起来像一块有边的污渍；三档递减才是自然的柔和阴影。
      雾团只负责「底色」的层次感 —— 真正的主角是那层跟随鼠标的柔光（.glow），不必抢戏。
   不用 filter: blur() —— 大半径模糊 + 图层提升会把模糊裁在元素边界上，出现硬边。 */
.cloud { position: absolute; border-radius: 50%; }
.cloud.light {
  background:
    radial-gradient(closest-side at 42% 46%, rgba(var(--cloud-lt),.95) 0%, rgba(var(--cloud-lt),.55) 30%, rgba(var(--cloud-lt),0) 100%),
    radial-gradient(46% 42% at 70% 66%, rgba(var(--cloud-lt),.80) 0%, rgba(var(--cloud-lt),.30) 34%, rgba(var(--cloud-lt),0) 100%);
}
.cloud.grey {
  background:
    radial-gradient(closest-side at 46% 44%, rgba(var(--cloud-dk),.36) 0%, rgba(var(--cloud-dk),.20) 32%, rgba(var(--cloud-dk),0) 100%),
    radial-gradient(44% 40% at 68% 62%, rgba(var(--cloud-dk),.24) 0%, rgba(var(--cloud-dk),.12) 34%, rgba(var(--cloud-dk),0) 100%);
}
.cloud.warm {
  background:
    radial-gradient(closest-side at 44% 50%, rgba(var(--cloud-warm),.32) 0%, rgba(var(--cloud-warm),.17) 32%, rgba(var(--cloud-warm),0) 100%),
    radial-gradient(42% 38% at 66% 34%, rgba(var(--cloud-warm),.22) 0%, rgba(var(--cloud-warm),.11) 34%, rgba(var(--cloud-warm),0) 100%);
}

/* 三个速度档：远层几乎不动，近层跑得最凶 —— 速度差够大，纵深才出得来 */
.far  { --travel: -14; --dx: -5; }
.mid  { --travel: -34; --dx: 12; }
.near { --travel: -60; --dx: -20; }

/* 位置写在 144vw × 192vh 的层盒子里；尺寸一律 vh */
.far  .c1  { width: 44vh; height: 44vh; left: 0%;  top: 0%;   animation: driftA 44s ease-in-out infinite; }
.far  .c2  { width: 38vh; height: 38vh; left: 38%; top: 40%;  animation: driftB 50s ease-in-out infinite; }
.far  .c7  { width: 40vh; height: 40vh; left: 60%; top: -4%;  animation: driftC 46s ease-in-out infinite; }
.far  .c10 { width: 34vh; height: 34vh; left: 22%; top: 62%;  animation: driftB 52s ease-in-out infinite; }
.mid  .c3  { width: 34vh; height: 34vh; left: 3%;  top: 46%;  animation: driftB 28s ease-in-out infinite; }
.mid  .c4  { width: 30vh; height: 30vh; left: 44%; top: 6%;   animation: driftC 30s ease-in-out infinite; }
.mid  .c8  { width: 28vh; height: 28vh; left: 62%; top: 52%;  animation: driftA 24s ease-in-out infinite; }
.mid  .c11 { width: 32vh; height: 32vh; left: 26%; top: 22%;  animation: driftC 32s ease-in-out infinite; }
.near .c5  { width: 26vh; height: 26vh; left: 16%; top: 12%;  animation: driftC 18s ease-in-out infinite; }
.near .c6  { width: 22vh; height: 22vh; left: 46%; top: 44%;  animation: driftA 15s ease-in-out infinite; }
.near .c9  { width: 20vh; height: 20vh; left: 8%;  top: 66%;  animation: driftB 20s ease-in-out infinite; }
.near .c12 { width: 24vh; height: 24vh; left: 56%; top: 20%;  animation: driftA 16s ease-in-out infinite; }

/* 雾团自身的慢漂：越近越快（15～20s），远层慢到几乎不动（44～52s）。
   幅度 ±20% 并且带旋转 —— 不只是平移，还在缓慢变形，更接近液体的感觉。
   所以就算不滚动，画面也是活的。 */
@keyframes driftA {
  0%, 100% { transform: translate3d(-14%, 10%, 0) rotate(-8deg) scale(1.00); }
  50%      { transform: translate3d(16%, -13%, 0) rotate(6deg) scale(1.26); }
}
@keyframes driftB {
  0%, 100% { transform: translate3d(18%, -10%, 0) rotate(7deg) scale(1.24); }
  50%      { transform: translate3d(-15%, 14%, 0) rotate(-7deg) scale(.90); }
}
@keyframes driftC {
  0%, 100% { transform: translate3d(3%, 16%, 0) rotate(5deg) scale(1.20); }
  50%      { transform: translate3d(-18%, -14%, 0) rotate(-9deg) scale(.86); }
}

/* 液态层：两束斜掠的柔光带，滚动时各自错位、反向旋转、斜向漂移 ——
   像水底慢慢挪动的光斑。这是水底层里「轮廓最明确」的一组，滚动时纵深靠它。
   mask 让元素自己的边界彻底透明，否则旋转后那条直边会切进画面，变成一道假接缝。 */
.liquid {
  position: absolute;
  inset: -60%;
  -webkit-mask-image: radial-gradient(closest-side, #000 76%, rgba(0,0,0,0) 100%);
          mask-image: radial-gradient(closest-side, #000 76%, rgba(0,0,0,0) 100%);
  transform:
    translate3d(calc(var(--p, 0) * var(--dx, -16) * 1vw),
                 calc(var(--p, 0) * var(--travel, -36) * 1vh), 0)
    rotate(calc(var(--p, 0) * var(--spin, 6) * 1deg));
  will-change: transform;
}
.liquid.a {
  --travel: -36; --dx: -12; --spin: 6;
  background: linear-gradient(104deg,
    rgba(255,255,255,0) 39%, rgba(255,255,255,.62) 46%,
    rgba(255,255,255,1) 50%, rgba(255,255,255,.52) 54%, rgba(255,255,255,0) 61%);
}
/* b 是一道巨大的柔光环（不是实心圆）：环形有明确的轮廓，眼睛能一路跟着它走，
   滚动时它是整个背景里最容易被看见的「在动的东西」。 */
.liquid.b {
  --travel: -46; --dx: 16; --spin: -9;
  background:
    radial-gradient(closest-side,
      rgba(255,255,255,0) 44%, rgba(255,255,255,.72) 56%,
      rgba(255,255,255,.95) 61%, rgba(255,255,255,.20) 70%, rgba(255,255,255,0) 82%),
    radial-gradient(30% 34% at 34% 68%, rgba(var(--cloud-dk),.20), rgba(var(--cloud-dk),0) 76%);
}

/* 细网格：玻璃模糊它、卡片外保留它 —— 一眼看出「玻璃在模糊背景」 */
.grid {
  position: absolute;
  inset: -26%;
  --travel: -24;
  --dx: -14;
  transform: translate3d(calc(var(--p, 0) * var(--dx, 0) * 1vw),
                         calc(var(--p, 0) * var(--travel) * 1vh), 0);
  background-image:
    linear-gradient(rgba(21,23,27,.10) 1px, transparent 1px),
    linear-gradient(90deg, rgba(21,23,27,.10) 1px, transparent 1px);
  background-size: 76px 76px;
  opacity: .9;
}

/* 纸面浮尘（particles.js 画的 #dust）。
   位置很关键：在 .grid 之上、#glow 之下 ——
   · 在 .bg 里，所以会被玻璃卡的 backdrop-filter 一起糊掉，尘在卡后是虚的，
     在空白处是实的，纵深就出来了；
   · 压在 .grid 之上但在 #glow 之下：网格线是底稿、尘浮在纸面前一格，
     鼠标划过去时柔光拖尾又盖过尘 —— 光始终是主体。
   刻意不加 filter: blur() 也不加 mix-blend-mode：整层模糊会把 canvas
   算力翻几倍，而 .grain 那层噪点已经负责「磨砂」了。 */
.dust {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: .9;
}

/* ★ 2026-10-15 改：原 .water（水面）已换成 .glow（鼠标柔光拖尾，glow.js）。
   它仍然是 .bg 里的一层、z-index: 0，内容区是 z-index: 1 ——
   所以光点永远在文字和卡片下面，不可能挡字。 */
.glow {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ★ 2026-10-15 新增：鼠标拖尾的「锐利层」。
   .bg 里那张 #glow 会被玻璃卡的 backdrop-filter 一起糊掉（30px 模糊足以
   抹掉一条 1.8px 的线），所以另起一张固定在视口上的 canvas：
   z-index 35 → 压在正文卡片（main 是 1）之上，但在顶栏 .hud（40）之下。
   35 是刻意选的：顶栏 40、灯箱 90，所以线从顶栏「底下」穿过 ——
   导航文字永远压在线的上方（线不会横穿文字），滚到顶栏吸顶时那条线会被
   那层磨砂轻轻吃掉一点，看起来像真的在玻璃下面；大图查看（90）也永远压住它。
   pointer-events: none → 它不吃任何点击 / 选择 / 悬停。 */
.glow-top {
  position: fixed;
  inset: 0;
  z-index: 35;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* 颗粒：模糊前是噪点，模糊后是细腻的霜面 */
.grain {
  position: absolute;
  inset: 0;
  opacity: .22;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.45'/%3E%3C/svg%3E");
}

.vignette {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(150% 118% at 50% 8%, transparent 52%, rgba(21,23,27,.08) 100%),
    linear-gradient(180deg, rgba(255,255,255,.30) 0%, rgba(255,255,255,.14) 7%, transparent 28%);
}

/* ─────────── 3.9 内容文件坏掉时的兜底页 ───────────
   平时看不见：只有 profile.js 没解析成功、或者渲染中途抛错才替换整页。
   故意做得朴素——这时候要的是「一眼看懂 + 知道去哪查」，不是好看。 */
html.is-broken body { display: block; background: var(--bg); }
.broken {
  max-width: 640px;
  margin: clamp(48px, 14vh, 140px) auto;
  padding: 0 24px;
  font-family: var(--font-sans);
  color: var(--ink);
}
.broken-k { font-family: var(--font-mono); font-size: 11px; letter-spacing: .18em; color: var(--ink-3); }
.broken-h { margin: 14px 0 0; font-family: var(--font-display); font-size: clamp(26px, 4vw, 34px); line-height: 1.25; }
.broken-p { margin: 16px 0 0; font-size: 14px; line-height: 1.85; color: var(--ink-2); }
.broken-p code, .broken-pre { font-family: var(--font-mono); font-size: 12.5px; }
.broken-p code { padding: 1px 5px; border-radius: 5px; background: rgba(21,23,27,.06); }
.broken-p kbd {
  padding: 1px 6px; border-radius: 5px; border: 1px solid var(--line);
  background: rgba(255,255,255,.7); font-family: var(--font-mono); font-size: 12px;
}
.broken-pre {
  margin: 22px 0 0; padding: 14px 16px; border-radius: 12px;
  border: 1px solid var(--line); background: rgba(255,255,255,.66);
  color: var(--ink-2); white-space: pre-wrap; word-break: break-word; overflow-x: auto;
}

/* ─────────── 4. 顶栏 ─────────── */
.hud {
  position: fixed;
  inset: 0 0 auto;
  z-index: 40;
  border: 1px solid transparent;
  border-bottom-color: transparent;
  transition: background .5s var(--ease), backdrop-filter .5s var(--ease), border-color .5s var(--ease),
              top .5s var(--ease-out), left .5s var(--ease-out), right .5s var(--ease-out),
              border-radius .5s var(--ease-out), box-shadow .5s var(--ease-out);
}
.hud.stuck {
  /* 冷灰玻璃：底色是暖白纸（#F1F1EE），玻璃必须往冷的方向偏一点点才分得出
     层次 —— 靠色相微差，不靠 alpha（同色相加 alpha 合成后只差 2 个色阶，等于实心板）。
     .stuck 与 .shrunk 两套都必须写全，后者接在前面那套之上。 */
  background: rgba(240,243,246,.34);
  border-bottom-color: var(--line);
  box-shadow: inset 0 -1px 0 rgba(21,23,27,.04);
  backdrop-filter: blur(28px) saturate(160%) brightness(1.04);
  -webkit-backdrop-filter: blur(28px) saturate(160%) brightness(1.04);
}

/* 收缩态：往下读一点之后，整条顶栏收成一颗居中的胶囊（scrollY > 64 才生效，
   和 .stuck 是两条独立的阈值 —— 贴边时就该是整条毛玻璃，不该一开始就飘着）。
   左右两侧用 max() 让它比内容区每侧各宽 24px：这样里面 .wrap 的内容位置
   一点没动，导航不会横向重排，只是「外壳」缩进来了。
   不用 translateX(-50%) 居中：那会在 .hud 上创建 containing block，
   手机端的 .hud-nav 下拉面板（position:absolute）会跟着跑偏。 */
.hud.shrunk {
  top: 12px;
  left: max(12px, calc((100% - var(--maxw)) / 2 - 24px));
  right: max(12px, calc((100% - var(--maxw)) / 2 - 24px));
  /* 胶囊态用上下渐变做「厚度」：上沿反光、下沿沉一点，免得整块看起来是同一片色。
     同样偏冷灰，且整体比 .stuck 更透一点（它浮在内容上，透一点才不挡字）。 */
  background: linear-gradient(180deg, rgba(255,255,255,.46) 0%, rgba(236,240,244,.26) 100%);
  border: 1px solid rgba(255,255,255,.9);
  border-bottom-color: transparent;
  border-radius: 999px;
  box-shadow: var(--shadow-2), inset 0 1px 0 #fff, inset 0 -1px 0 rgba(21,23,27,.04);
  backdrop-filter: blur(28px) saturate(160%) brightness(1.04);
  -webkit-backdrop-filter: blur(28px) saturate(160%) brightness(1.04);
}

.hud-inner { display: flex; align-items: center; gap: clamp(14px, 3vw, 40px); height: 66px; }

.brand { display: flex; align-items: center; gap: 11px; flex: none; }
.brand-mark {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,.95);
  background: linear-gradient(150deg, rgba(255,255,255,.82), rgba(255,255,255,.42));
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--ink);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-name { font-size: 14px; color: var(--ink); letter-spacing: .01em; }
.brand-sub { font-size: 9px; letter-spacing: .24em; color: var(--ink-3); margin-top: 2px; }

.hud-nav { display: flex; align-items: center; gap: clamp(4px, .8vw, 10px); margin-left: auto; flex-wrap: wrap; }
.hud-nav button {
  position: relative;
  padding: 7px 12px;
  border-radius: 999px;
  font-size: 13px;
  color: var(--ink-2);
  transition: color .35s var(--ease), background .35s var(--ease);
}
.hud-nav button::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 1px;
  width: 0; height: 1px;
  background: var(--ink);
  transform: translateX(-50%);
  transition: width .45s var(--ease-out);
}
.hud-nav button:hover { color: var(--ink); background: rgba(255,255,255,.6); }
.hud-nav button:hover::after { width: 42%; }
.hud-nav button.on { color: var(--ink); }
.hud-nav button.on::after { width: 58%; }

.hud-live {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  padding-left: clamp(10px, 2vw, 20px);
  border-left: 1px solid var(--line);
  font-size: 11px;
  letter-spacing: .12em;
  color: var(--ink-3);
}
.hud-live i { width: 5px; height: 5px; border-radius: 50%; background: var(--ink); animation: blink 3.4s var(--ease) infinite; }
@keyframes blink { 0%, 100% { opacity: .85; } 50% { opacity: .2; } }

/* 「读到哪里」的细进度线：贴在顶栏底沿，从中间往两边长。
   transform: scaleX 由 app.js 写，只动合成层，不重排。 */
.hud-progress {
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: linear-gradient(90deg, rgba(21,23,27,.34), rgba(21,23,27,.16) 70%, transparent);
  transition: transform .18s linear, bottom .5s var(--ease-out),
              left .5s var(--ease-out), right .5s var(--ease-out), height .5s var(--ease-out);
  pointer-events: none;
}
/* 胶囊有圆角，贴底那一根线会被切掉，所以收缩时把它挪进圆角内侧。
   注意：绝对不要给 .hud 加 overflow:hidden —— 那会连带裁掉手机端的下拉面板。 */
.hud.shrunk .hud-progress {
  bottom: 7px;
  left: 28px;
  right: 28px;
  height: 2px;
  border-radius: 2px;
}

/* 手机导航的开关：≥861px 一律不出现 */
.nav-toggle {
  display: none;
  flex: none;
  width: 38px; height: 38px;
  place-items: center;
  gap: 5px;
  border-radius: 12px;
  border: 1px solid rgba(255,255,255,.95);
  background: rgba(255,255,255,.55);
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  transition: background .35s var(--ease), transform .4s var(--ease-out);
}
.nav-toggle { position: relative; }
.nav-toggle span {
  position: absolute;
  left: 50%; top: 50%;
  width: 15px; height: 1.5px;
  margin: -0.75px 0 0 -7.5px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform .4s var(--ease-out);
}
/* 收起：两条平行线（≡）；展开：绕各自的中心转成 ×。
   两条线平时上下错开 4px，转的时候先回到中線 —— 所以不会转出「>」那种斜杠。 */
.nav-toggle span:first-child { transform: translateY(-4px); }
.nav-toggle span:last-child  { transform: translateY(4px); }
.nav-toggle:hover { background: #fff; }
.nav-toggle[aria-expanded="true"] span:first-child { transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:last-child  { transform: rotate(-45deg); }

/* ─────────── 5. 首屏 ─────────── */
main { position: relative; z-index: 1; }
.view[hidden] { display: none; }

.view.enter { animation: viewIn .85s var(--ease-out) both; }
@keyframes viewIn {
  from { opacity: 0; transform: translate3d(0, 16px, 0) scale(.994); }
  to   { opacity: 1; transform: none; }
}

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(28px, 4vw, 60px);
  min-height: min(68vh, 600px);
  padding-block: clamp(88px, 11vh, 118px) clamp(38px, 5vh, 64px);
}

.hero-text { max-width: 40rem; }

.hero-name {
  margin-top: 20px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(46px, 7.4vw, 98px);
  line-height: 1.03;
  letter-spacing: -.015em;
  color: var(--ink);
  /* 一点点压印感：白底黑字看起来像「印出来」的 */
  text-shadow: 0 1px 0 rgba(255,255,255,.65);
}

.hero-role { margin-top: 18px; font-size: 14px; letter-spacing: .16em; color: var(--ink-2); }

.hero-statement {
  margin-top: 26px;
  max-width: 32em;
  font-family: var(--font-display);
  font-size: clamp(19px, 2vw, 25px);
  line-height: 1.78;
  color: var(--ink);
}
/* 强调 = 更黑 + 一道浅记号笔底纹，不用彩色 */
.hero-statement em {
  font-style: normal;
  color: #08090B;
  background-image: linear-gradient(180deg,
    transparent 58%, rgba(21,23,27,.11) 58%, rgba(21,23,27,.11) 93%, transparent 93%);
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 15px 7px 12px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.95);
  background: rgba(255,255,255,.5);
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  font-size: 12.5px;
  letter-spacing: .06em;
  color: var(--ink-2);
}
.status .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ink); animation: pulse 2.8s var(--ease) infinite; }
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(21,23,27,.3); }
  70%  { box-shadow: 0 0 0 8px rgba(21,23,27,0); }
  100% { box-shadow: 0 0 0 0 rgba(21,23,27,0); }
}

/* 头像：一块厚玻璃窗 */
.portrait {
  position: relative;
  flex: none;
  width: clamp(200px, 25vw, 344px);
  aspect-ratio: 4 / 5;
  transform-style: preserve-3d;
  /* 点头像 = 从本机换一张（见 app.js 的 changePortrait）。
     所以这里必须是「可以点」的样子：手型 + 悬停浮起一点。 */
  cursor: pointer;
}
/* 只有真指针才提示：触屏没有悬停态，写上去也永远不会出现。
   提示放在顶部，避开占位图里自带的「换成你的照片」那行字。 */
@media (hover: hover) {
  .portrait::after {
    content: "点一下换成你的照片";
    position: absolute;
    top: 12px;
    left: 50%;
    z-index: 2;
    padding: 6px 12px;
    font-size: 11.5px;
    line-height: 1;
    color: var(--ink-2);
    white-space: nowrap;
    background: rgba(255,255,255,.78);
    border: 1px solid var(--line);
    border-radius: 999px;
    opacity: 0;
    transform: translate(-50%, -4px);
    transition: opacity .3s var(--ease), transform .3s var(--ease);
    pointer-events: none;
  }
  .portrait:hover::after,
  .portrait:focus-visible::after { opacity: 1; transform: translate(-50%, 0); }
}
.portrait-halo {
  position: absolute;
  inset: -26% -22%;
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(255,255,255,.95), rgba(255,255,255,0) 72%);
  animation: breathe 16s ease-in-out infinite;
  /* 反向视差：卡片被指针推向哪边，光晕就往反方向飘一点，玻璃后面就有了纵深。
     --mgx / --mgy 写在 .portrait 上，变量会继承下来。 */
  translate: calc(var(--mgx, 0px) * -1.4) calc(var(--mgy, 0px) * -1.4);
  transition: translate .18s var(--ease-out);
}
@keyframes breathe {
  0%, 100% { transform: scale(1); opacity: .8; }
  50%      { transform: scale(1.1); opacity: 1; }
}
.portrait-frame {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: 28px;
  border: 1px solid rgba(255,255,255,.92);
  background: linear-gradient(150deg, rgba(255,255,255,.44), rgba(255,255,255,.14) 52%, rgba(255,255,255,.30));
  backdrop-filter: blur(34px) saturate(var(--sat)) brightness(1.07);
  -webkit-backdrop-filter: blur(34px) saturate(var(--sat)) brightness(1.07);
  box-shadow:
    var(--shadow-1), var(--shadow-2), var(--shadow-3),
    inset 0 1px 0 #fff, inset 0 -1px 0 rgba(21,23,27,.07);
  font-family: var(--font-display);
  font-size: clamp(56px, 7vw, 86px);
  color: rgba(21,23,27,.16);
}
.portrait-frame img { width: 100%; height: 100%; object-fit: cover; }
.portrait-sheen {
  position: absolute;
  inset: 0;
  border-radius: 28px;
  pointer-events: none;
  background: linear-gradient(118deg,
    transparent 32%, rgba(255,255,255,.8) 46%, rgba(255,255,255,.22) 54%, transparent 68%);
  background-size: 260% 260%;
  animation: sheenSweep 14s ease-in-out infinite;
}
@keyframes sheenSweep {
  0%, 64%, 100% { background-position: 190% 0; }
  84%           { background-position: -60% 0; }
}

/* 首屏文字栏下方那几行小信息（facts + 城市坐标，由 app.js 注入） */
.portrait-facts {
  margin: 22px 0 0;
  max-width: 22em;
  display: flex;
  flex-direction: column;
  gap: 9px;
  font-size: 10.5px;
  letter-spacing: .04em;
}
.portrait-facts > div {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.portrait-facts dt {
  flex: none;
  width: 3.6em;
  color: var(--ink-3);
  opacity: .85;
}
.portrait-facts dd {
  margin: 0;
  color: var(--ink-2);
}

/* ─────────── 6. 零件 ─────────── */
.chips { display: flex; flex-wrap: wrap; gap: 9px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 14px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.9);
  background: rgba(255,255,255,.42);
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(12px) saturate(170%);
  -webkit-backdrop-filter: blur(12px) saturate(170%);
  font-size: 12.5px;
  color: var(--ink-2);
  transition: color .4s var(--ease), background .4s var(--ease), transform .5s var(--ease-out);
}
.chip:hover { color: var(--ink); background: rgba(255,255,255,.9); transform: translateY(-2px); }
.chip em { font-style: normal; font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); }

/* 浅色底上「选中」= 变黑 */
.chip.on {
  color: #fff;
  border-color: rgba(21,23,27,.9);
  background: linear-gradient(180deg, #2A2D33, #15171B);
  box-shadow: 0 6px 18px -10px rgba(21,23,27,.7), inset 0 1px 0 rgba(255,255,255,.16);
}
.chip.on em { color: rgba(255,255,255,.62); }

.sec { padding-block: clamp(38px, 5.5vh, 68px); }

.sec-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-bottom: clamp(24px, 3.4vh, 40px);
}
.sec-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 3.2vw, 40px);
  letter-spacing: -.01em;
  color: var(--ink);
  text-shadow: 0 1px 0 rgba(255,255,255,.6);
}
.sec-hint { margin-left: auto; font-size: 11.5px; letter-spacing: .05em; text-transform: none; color: var(--ink-3); }

/* ─────────── 7. 玻璃材质 ───────────
   一块「看得出来」的玻璃需要五样东西：
   ① 半透白底 —— 注意白不能厚，厚了就成了白卡片：不但看不见背后的光雾，
      更会把**压在柔光拖尾上面的那层白**遮住（表现就是「光痕只在空白处流动」）。
   ② 磨砂模糊（背景必须有东西可模糊 —— 现在流转的光斑与网格正好是那件东西）
   ③ 折射亮边（.glass::after 那圈渐变描边）
   ④ 顶亮底暗的内阴影 = 玻璃厚度
   ⑤ 三层外投影 = 浮起来
   ⑥ 常驻的镜面反光（.glass::before）+ 悬停时掠过的一道高光
   高光用 z-index:-1 + isolation，压在文字下面，免得糊住字。

   玻璃的定位是「浮在背景上的那块板」：光从它下面穿过去，
   板子把光滤成一片流动的亮暗 —— 所以这里的白必须够薄（≤ .36），
   并且给 backdrop-filter 加一点 contrast，把背景微弱的反差重新提出来。
   ────────────────────────────────────────────────────────────────── */
.glass {
  position: relative;
  isolation: isolate;
  background:
    linear-gradient(148deg,
      rgba(255,255,255,.40) 0%, rgba(255,255,255,.08) 42%,
      rgba(255,255,255,.12) 68%, rgba(255,255,255,.26) 100%);
  border-radius: var(--r);
  box-shadow:
    var(--shadow-1), var(--shadow-2), var(--shadow-3),
    /* 玻璃厚度：上缘受光、下缘压暗，内部一圈回光（液体的「鼓」） */
    inset 0 1px 0 rgba(255,255,255,1),
    inset 0 -1.5px 0 rgba(21,23,27,.09),
    inset 0 14px 22px -19px rgba(255,255,255,.85),
    inset 0 -20px 28px -23px rgba(255,255,255,.62),
    inset 1px 0 0 rgba(255,255,255,.72),
    inset -1px 0 0 rgba(21,23,27,.05);
  /* 磨砂：只留 blur，不再挂 SVG 折射滤镜 url(#lgLens)。
     原因：Chrome 拿 SVG 滤镜做 backdrop 时按块光栅化，块边界会留下一道发白的竖印
     —— 就是「我的几面」卡片中间那道接缝。删掉滤镜但保留 0.58 这个系数，
     磨砂程度才和之前看到的一致（--blur 30px → 约 17px）。 */
  -webkit-backdrop-filter: blur(calc(var(--blur) * .58)) saturate(var(--sat)) brightness(1.06) contrast(1.12);
          backdrop-filter: blur(calc(var(--blur) * .58)) saturate(var(--sat)) brightness(1.06) contrast(1.12);
  /* 立体：极轻微地跟着指针倾斜（--tiltX / --tiltY 由 app.js 写），
     --lift 是悬停时抬起的高度。
     注意：静止态**不挂 perspective/rotate**，只有指针真的落在元素上、
     app.js 加上 .tilting 之后才变成 3D。原因见下面 8.7 那段注释
     （常驻 3D 会把每张玻璃提升成独立合成层，低分屏上出现横向接缝）。 */
  transform: translateY(var(--lift, 0px));
  transform-origin: 50% 50%;
  transition: transform .6s var(--ease-out), box-shadow .6s var(--ease-out);
}

/* 折射亮边：一圈 2px 渐变描边（左上最亮、右下压暗），并且极慢地自己流动 ——
   会流动才像液体，不动就只是塑料。 */
.glass::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 2px;
  pointer-events: none;
  background: linear-gradient(145deg,
    rgba(255,255,255,1) 0%, rgba(255,255,255,.52) 16%, rgba(255,255,255,.10) 36%,
    rgba(21,23,27,.15) 56%, rgba(255,255,255,.34) 76%, rgba(255,255,255,.96) 100%);
  background-size: 210% 210%;
  animation: lgDrift 16s var(--ease) infinite;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}
@keyframes lgDrift {
  0%   { background-position: 0% 0%; }
  50%  { background-position: 100% 65%; }
  100% { background-position: 0% 0%; }
}

/* 镜面高光：一颗跟着指针的小亮点 + 一片跟着指针的柔光 + 悬停掠过的一道光 */
.glass::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background:
    radial-gradient(26px circle at var(--mx, 26%) var(--my, -10%),
      rgba(255,255,255,.95), rgba(255,255,255,0) 72%),
    radial-gradient(250px circle at var(--mx, 26%) var(--my, -10%),
      rgba(255,255,255,.90), rgba(255,255,255,.30) 42%, rgba(255,255,255,0) 72%),
    linear-gradient(112deg, transparent 40%, rgba(255,255,255,.82) 49%, transparent 58%);
  background-size: auto, auto, 280% 100%;
  background-repeat: no-repeat, no-repeat, no-repeat;
  background-position: 0 0, 0 0, 210% 0;
  opacity: .62;
  transition: opacity .5s var(--ease);
}
.glass:hover::before { opacity: 1; animation: sheenRun 2.4s var(--ease-out); }
@keyframes sheenRun {
  from { background-position: 0 0, 0 0, 215% 0; }
  to   { background-position: 0 0, 0 0, -75% 0; }
}
/* 减少动画：折射亮边停止流动 */
@media (prefers-reduced-motion: reduce) {
  .glass::after { animation: none; }
}

/* ── 液态玻璃 2.0：立体倾斜也用到照片卡、链接卡上 ───────────────────── */
/* （它们的折射材质写在文件最后，因为要盖住它们自己那条 backdrop-filter） */
.photo, .link-card, .facet, .glass, .work, .skill-card, .portrait {
  /* --mgx / --mgy 是「磁吸」：指针在卡片的哪一边，卡片就往那边偏一点点。
     和 --lift（悬停抬起）、--tiltX/Y（立体倾斜）叠在一起，三者互不干扰。 */
  transform: translate3d(var(--mgx, 0px), calc(var(--lift, 0px) + var(--mgy, 0px)), 0);
  transform-origin: 50% 50%;
}

/* 立体倾斜只在指针真的压在这张卡上时才打开（app.js 的 heat/cool 负责加/去）。
   为什么不在静止态就挂着 perspective + rotate：那会让每一张玻璃在合成器里
   变成一个独立的 3D 层，backdrop-filter 只能按 tile 光栅化，Windows 上
   125% / 150% 缩放时一块卡会裂成上下两块带各自圆角的玻璃（用户看到过）。
   静止态保持 2D 之后，整页只有一张卡在倾斜时才是 3D 层，接缝就没了。 */
.glass.tilting, .photo.tilting, .link-card.tilting, .facet.tilting,
.work.tilting, .skill-card.tilting, .portrait.tilting {
  transform: translate3d(var(--mgx, 0px), calc(var(--lift, 0px) + var(--mgy, 0px)), 0)
             perspective(1400px)
             rotateX(var(--tiltX, 0deg)) rotateY(var(--tiltY, 0deg));
}
/* 倾斜与磁吸要跟手：用 .18s 短过渡，而不是入场动画那条 1.1s 长过渡 */
.facet.glass, .block.glass, .contact.glass, .photo, .link-card, .work, .skill-card, .portrait {
  transition: transform .18s var(--ease-out), translate 1.1s var(--ease-out),
              opacity 1s var(--ease-out), box-shadow .5s var(--ease-out);
}

/* ─────────── 8. 「我的几面」 ─────────── */
.facet-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(14px, 1.6vw, 20px); }

.facet {
  display: flex;
  flex-direction: column;
  gap: 11px;
  min-height: clamp(168px, 14.5vw, 198px);
  padding: clamp(20px, 2.2vw, 26px);
  text-align: left;
  border-radius: var(--r);
  transition: transform .65s var(--ease-out), box-shadow .65s var(--ease-out);
}
.facet:hover {
  --lift: -6px;
  box-shadow:
    0 2px 4px rgba(16,18,22,.06),
    0 18px 34px -14px rgba(16,18,22,.22),
    0 46px 74px -34px rgba(16,18,22,.32),
    inset 0 1px 0 #fff,
    inset 0 -1px 0 rgba(21,23,27,.055);
}

.facet-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.facet-index { font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em; color: var(--ink); opacity: .5; }
.facet-en { font-size: 9.5px; letter-spacing: .2em; color: var(--ink-3); text-align: right; }

.facet-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(21px, 1.9vw, 26px);
  line-height: 1.25;
  color: var(--ink);
}
.facet-summary { flex: 1; font-size: 13.5px; line-height: 1.8; color: var(--ink-2); }
.facet-go {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.facet-go span:first-child {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-3);
  transition: color .45s var(--ease);
}
.facet-go .arrow { font-size: 15px; color: var(--ink-2); transition: transform .55s var(--ease-out), color .45s var(--ease); }
.facet:hover .facet-go span:first-child { color: var(--ink); }
.facet:hover .facet-go .arrow { transform: translateX(7px); color: var(--ink); }

/* ── 小零件（胶囊、社交格、按钮）的磁吸：只挪一两个像素，够「活」但不晃 ──
   这里用 translate 属性而不是 transform：这些元素各自的 transform 里
   已经有悬停抬起 / 缩放，混在一起会互相覆盖。 */
.chip, .social, .hud-nav button, .copy, .gallery-toggle, .back-btn,
.work-link, .icon-btn, .link-card .lc-go {
  translate: var(--mgx, 0px) var(--mgy, 0px);
  transition: translate .35s var(--ease-out), color .35s var(--ease),
              background .4s var(--ease), transform .5s var(--ease-out),
              box-shadow .5s var(--ease-out);
}

/* 屏幕阅读器专用（技能百分比那种「看得见就够」的信息用） */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ═══════════ 8.5 技能栈 / 作品 ═══════════
   两块新板块共用同一套节奏：
     标题行（mono 英文 + 衬线中文 + 右侧一句提示）→ 内容网格。
   卡片的玻璃质感、悬停抬起、指针倾斜全部沿用上面 .glass 那套，
   这里只补各自的骨架与文字层级，不再重复写一遍材质。 */

/* ── 技能栈：三张卡，每条技能一根进度条 ── */
.skill-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 20px);
  align-items: start;
}

.skill-card {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 1.8vw, 22px);
  padding: clamp(22px, 2.2vw, 30px);
  border-radius: var(--r);
}
.skill-card:hover { --lift: -4px; }

.skill-head { display: flex; flex-direction: column; gap: 6px; }
.skill-en { font-size: 9.5px; letter-spacing: .22em; color: var(--ink-3); }
.skill-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(20px, 1.8vw, 24px);
  line-height: 1.3;
  color: var(--ink);
}
.skill-desc { font-size: 12.5px; line-height: 1.8; color: var(--ink-2); }

.skill-list { display: flex; flex-direction: column; gap: 16px; margin: 0; padding: 0; }
.skill-row { position: relative; display: flex; flex-direction: column; gap: 7px; }

.skill-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.skill-k { font-size: 14px; color: var(--ink); letter-spacing: .01em; }
.skill-lv {
  flex: none;
  font-size: 9.5px;
  letter-spacing: .16em;
  text-transform: none;
  color: var(--ink-3);
}
.skill-note { font-size: 11.5px; line-height: 1.6; color: var(--ink-3); }

/* 进度条：轨道是纸面上的一条细线，填充条从左往右长。
   --v 由 app.js 写在 <i> 上（0–100%），宽度在 .in 之后才生效 —— 
   也就是「卡片露头的时候，条从上往下依次长出来」。 */
.skill-bar {
  position: relative;
  display: block;
  height: 2px;
  margin-top: 3px;
  border-radius: 999px;
  background: rgba(21,23,27,.09);
  overflow: hidden;
}
.skill-bar i {
  position: absolute;
  inset: 0 auto 0 0;
  display: block;
  width: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, rgba(21,23,27,.42), var(--ink));
  transition: width 1.1s var(--ease-out);
  transition-delay: var(--d, 0ms);
}
.skill-card.in .skill-bar i { width: var(--v); }
/* 从上往下依次长：第 2、3、4 条各慢一拍 */
.skill-card.in .skill-row:nth-child(1) .skill-bar i { --d: 120ms; }
.skill-card.in .skill-row:nth-child(2) .skill-bar i { --d: 240ms; }
.skill-card.in .skill-row:nth-child(3) .skill-bar i { --d: 360ms; }
.skill-card.in .skill-row:nth-child(4) .skill-bar i { --d: 480ms; }

/* ── 作品：一张卡一个项目 ── */
.work-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: clamp(14px, 1.6vw, 20px);
  align-items: stretch;
}

.work {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: clamp(22px, 2.4vw, 32px);
  border-radius: var(--r);
}
.work:hover { --lift: -5px; }

.work-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.work-en { font-size: 9.5px; letter-spacing: .2em; color: var(--ink-3); }
.work-year { font-size: 10px; letter-spacing: .1em; text-transform: none; color: var(--ink-3); }

.work-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(22px, 2vw, 27px);
  line-height: 1.25;
  letter-spacing: -.01em;
  color: var(--ink);
}
.work-role { font-size: 10px; letter-spacing: .1em; text-transform: none; color: var(--ink-2); }
.work-desc { font-size: 13.5px; line-height: 1.85; color: var(--body); }

/* 亮点：一条一条的小钩，代替一坨段落 */
.work-points { display: flex; flex-direction: column; gap: 8px; margin: 2px 0 0; }
.work-points li {
  position: relative;
  padding-left: 18px;
  font-size: 12.5px;
  line-height: 1.75;
  color: var(--ink-2);
}
.work-points li::before {
  content: '';
  position: absolute;
  left: 1px; top: .68em;
  width: 7px; height: 1px;
  background: var(--ink);
  opacity: .45;
}

.work-tags { gap: 7px; margin-top: 2px; }
.work-tags .chip { padding: 5px 11px; font-size: 11.5px; }

/* 卡底：左边状态标签，右边链接按钮 —— 永远贴在卡片下沿对齐 */
.work-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.work-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .14em;
  color: var(--ink-2);
}
.work-status::before {
  content: '';
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--ink);
  opacity: .6;
}
.work.draft .work-status::before { background: transparent; box-shadow: inset 0 0 0 1px var(--line-3); }

.work-links { display: flex; flex-wrap: wrap; gap: 8px; }
.work-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 13px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.92);
  background: rgba(255,255,255,.55);
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(12px) saturate(170%);
  -webkit-backdrop-filter: blur(12px) saturate(170%);
  font-size: 12px;
  color: var(--ink);
  transition: translate .35s var(--ease-out), background .35s var(--ease),
              box-shadow .4s var(--ease);
}
.work-link:hover { background: #fff; box-shadow: var(--shadow-2), inset 0 1px 0 #fff; }
.work-link .go { color: var(--ink-3); transition: transform .4s var(--ease-out), color .3s var(--ease); }
.work-link:hover .go { color: var(--ink); transform: translate(2px, -2px); }

/* 占位卡：虚线框 + 一点去饱和，一眼就知道「这里还没填」 */
.work.draft {
  border: 1px dashed rgba(21,23,27,.22);
  background: rgba(255,255,255,.22);
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.work.draft::after,
.work.draft::before { display: none; }        /* 虚线框不需要玻璃的折射亮边与镜面高光 */
.work.draft .work-title,
.work.draft .work-desc { color: var(--ink-2); }
.work.draft:hover { --lift: -3px; box-shadow: var(--shadow-2); }

@media (max-width: 1040px) {
  .skill-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .skill-grid { grid-template-columns: 1fr; }
  .work-grid { grid-template-columns: 1fr; }
}

/* ═══════════ 8.7 指针交互：涟漪 / 磁吸 / 照片光斑 ═══════════
   说清楚现状：
     · 涟漪（.ripple）在按下处荡开一圈，直径由目标卡片的大小决定。
     · 磁吸（--mgx / --mgy）与立体倾斜（--tiltX / --tiltY）由 app.js 写在
       元素自己身上，样式这边只负责把它们接进 transform / translate。
     · 「缓动跟随的光标光环」已经删掉 —— 用户反馈像圆球且有延迟；
       跟随式光标必然落后半拍，指针反馈改由卡片自身的磁吸承担。
   整层 pointer-events: none，任何时候都不吃点击。 */

.ripple-layer {
  position: fixed;
  inset: 0;
  z-index: 34;                 /* 正文之上、顶栏(40) 与灯箱(90) 之下 */
  overflow: hidden;
  pointer-events: none;        /* 任何时候都不吃点击 */
}

/* 按下时荡开的一圈：从一个点长到 --d，同时淡到没有 */
.ripple {
  position: absolute;
  left: 0; top: 0;
  width: var(--d, 160px);
  height: var(--d, 160px);
  margin: calc(var(--d, 160px) / -2) 0 0 calc(var(--d, 160px) / -2);
  border-radius: 50%;
  border: 1.5px solid rgba(21,23,27,.42);
  box-shadow: 0 0 0 1px rgba(255,255,255,.55);
  background: radial-gradient(closest-side,
    rgba(255,255,255,.86), rgba(255,255,255,.34) 44%, rgba(255,255,255,0) 74%);
  animation: rippleOut .78s cubic-bezier(.16,1,.3,1) forwards;
}
@keyframes rippleOut {
  from { transform: scale(.12); opacity: 1; }
  55%  { opacity: .62; }
  to   { transform: scale(1); opacity: 0; }
}

/* 照片卡：鼠标扫过时，画面里那道光跟着指针走一点点（--hx / --hy 来自 :root） */
.photo-media::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(60% 60% at 50% 50%,
    rgba(255,255,255,.55), rgba(255,255,255,0) 70%);
  transform: translate3d(calc(var(--hx, 0) * 26px), calc(var(--hy, 0) * 26px), 0) scale(1.35);
  transition: opacity .5s var(--ease);
}
.photo:hover .photo-media::after { opacity: .85; }
/* 图片的放大用上面 .photo:hover 那条（scale 1.05），这里只加一点提亮，
   免得两条规则抢同一个 transform。 */
.photo-media img { transition: transform 1.1s var(--ease-out), filter .6s var(--ease); }
.photo:hover .photo-media img { filter: brightness(1.04) contrast(1.02); }

/* ─────────── 9. 数字 ─────────── */
.stat-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--line-2);
  border-bottom: 1px solid var(--line-2);
}
.stat { padding: clamp(26px, 3.4vw, 44px) clamp(14px, 2vw, 30px); border-left: 1px solid var(--line); }
.stat:first-child { border-left: 0; padding-left: 0; }
.stat .n {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(34px, 4.4vw, 58px);
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--ink);
}
.stat .l { margin-top: 14px; font-size: 12.5px; letter-spacing: .05em; color: var(--ink-2); }

/* ─────────── 10. 联系方式 ─────────── */
.contact {
  display: grid;
  grid-template-columns: minmax(0, .75fr) minmax(0, 1.25fr);
  align-items: center;
  gap: clamp(26px, 4vw, 58px);
  padding: clamp(28px, 3.6vw, 52px);
}
.contact-title {
  margin-top: 16px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(23px, 2.6vw, 34px);
  line-height: 1.35;
  color: var(--ink);
}
.mail-row { display: flex; align-items: center; gap: 12px; margin-top: 22px; flex-wrap: wrap; }
.mail { position: relative; padding-bottom: 3px; font-size: clamp(15px, 1.5vw, 18px); color: var(--ink); }
.mail::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--ink), transparent);
  transform: scaleX(.28);
  transform-origin: left;
  transition: transform .6s var(--ease-out);
}
.mail:hover::after { transform: scaleX(1); }

.copy {
  padding: 7px 14px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.92);
  background: rgba(255,255,255,.5);
  box-shadow: var(--shadow-1);
  font-size: 12px;
  color: var(--ink-2);
  transition: color .35s var(--ease), background .35s var(--ease);
}
.copy:hover, .copy.done { color: var(--ink); background: #fff; }

.social-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; }
.social {
  position: relative;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 13px;
  border-radius: 15px;
  border: 1px solid rgba(255,255,255,.9);
  background: rgba(255,255,255,.38);
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(14px) saturate(175%);
  -webkit-backdrop-filter: blur(14px) saturate(175%);
  transition: background .4s var(--ease), transform .5s var(--ease-out), box-shadow .5s var(--ease-out);
}
.social:hover { background: rgba(255,255,255,.94); transform: translateY(-2px); box-shadow: var(--shadow-2), inset 0 1px 0 #fff; }
.social.locked { cursor: default; }
.social.locked:hover { transform: none; }
.social .badge {
  flex: none;
  width: 29px; height: 29px;
  display: grid; place-items: center;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: rgba(255,255,255,.8);
  box-shadow: inset 0 1px 0 #fff;
  font-size: 12.5px;
  color: var(--ink);
}
.social-meta { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
.social-meta .nm { font-size: 13px; color: var(--ink); }
.social-meta .nt {
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.social .go { margin-left: auto; font-size: 12px; color: var(--ink-3); transition: color .35s var(--ease), transform .5s var(--ease-out); }
.social:hover .go { color: var(--ink); transform: translate(2px, -2px); }

.qr-pop {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 12px);
  z-index: 30;
  padding: 10px;
  border-radius: 16px;
  border: 1px solid rgba(255,255,255,.95);
  background: rgba(255,255,255,.94);
  box-shadow: var(--shadow-2), var(--shadow-3);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  opacity: 0;
  pointer-events: none;
  transform: translateX(-50%) translateY(7px) scale(.96);
  transition: opacity .35s var(--ease), transform .35s var(--ease-out);
}
.qr-pop img { width: 132px; height: 132px; border-radius: 9px; object-fit: cover; }
.qr-pop span { display: block; margin-top: 8px; text-align: center; font-size: 11px; color: var(--ink-2); }
.social:hover .qr-pop { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }

/* ─────────── 11. 详情页 ─────────── */
.detail-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  /* 让出固定顶栏的高度，否则返回按钮会被顶栏压住 */
  padding-top: clamp(86px, 11vh, 108px);
}
.back-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 16px 9px 13px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.92);
  background: rgba(255,255,255,.5);
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  font-size: 13px;
  color: var(--ink-2);
  transition: color .35s var(--ease), background .35s var(--ease), transform .5s var(--ease-out);
}
.back-btn svg { width: 15px; height: 15px; }
.back-btn:hover { color: var(--ink); background: #fff; transform: translateX(-3px); }
.back-bottom { margin: clamp(34px, 6vh, 64px) 0 clamp(48px, 8vh, 92px); }

.crumb { font-size: 11.5px; letter-spacing: .05em; text-transform: none; color: var(--ink-3); }

.detail-head { padding: clamp(30px, 5vh, 58px) 0 clamp(22px, 3.4vh, 38px); }
.detail-en { color: var(--ink-2); }
.detail-name {
  margin-top: 16px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(38px, 5.4vw, 68px);
  line-height: 1.05;
  letter-spacing: -.02em;
  color: var(--ink);
  text-shadow: 0 1px 0 rgba(255,255,255,.6);
}
.detail-summary { margin-top: 18px; max-width: 46em; font-size: 15px; line-height: 1.9; color: var(--ink-2); }
.detail-line {
  display: block;
  height: 1px;
  margin-top: clamp(26px, 4vh, 42px);
  background: linear-gradient(90deg, var(--line-3), var(--line) 55%, transparent);
}

.blocks { display: flex; flex-direction: column; gap: clamp(14px, 1.8vw, 20px); }
.block { padding: clamp(24px, 3vw, 42px); }

.block-title {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 20px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(19px, 1.9vw, 23px);
  letter-spacing: -.005em;
  color: var(--ink);
}
.block-title .mono { font-size: 10px; color: var(--ink); opacity: .45; }

.block-body {
  max-width: 48em;
  font-size: 15px;
  line-height: 2.05;
  color: var(--body);
  white-space: pre-line;
}
.block-body strong { color: var(--ink); font-weight: 600; }

.block-list { display: flex; flex-direction: column; gap: 13px; }
.block-list li { position: relative; padding-left: 24px; font-size: 14.5px; line-height: 1.85; color: var(--body); }
.block-list li::before {
  content: '';
  position: absolute;
  left: 2px; top: .78em;
  width: 11px; height: 1px;
  background: var(--ink);
  opacity: .4;
}

.block-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: clamp(18px, 2.4vw, 32px); }
.block-stats .n {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 3vw, 40px);
  line-height: 1;
  color: var(--ink);
}
.block-stats .l { margin-top: 11px; font-size: 12.5px; color: var(--ink-2); }

.timeline { display: flex; flex-direction: column; }
.timeline li { position: relative; padding: 0 0 30px 30px; border-left: 1px solid var(--line-2); }
.timeline li:last-child { padding-bottom: 0; border-left-color: transparent; }
.timeline li::before {
  content: '';
  position: absolute;
  left: -4px; top: 7px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 0 0 4px rgba(21,23,27,.07);
}
.timeline li:last-child::before {
  background: #fff;
  box-shadow: 0 0 0 4px rgba(21,23,27,.08), inset 0 0 0 1px var(--line-2);
}
.t-time { font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em; color: var(--ink-3); }
.t-title { margin-top: 7px; font-size: 16px; color: var(--ink); }
.t-desc { margin-top: 7px; font-size: 13.5px; line-height: 1.85; color: var(--ink-2); }

.link-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(226px, 1fr)); gap: 10px; }
.link-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 15px;
  border: 1px solid rgba(255,255,255,.9);
  background: rgba(255,255,255,.38);
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(14px) saturate(175%);
  -webkit-backdrop-filter: blur(14px) saturate(175%);
  transition: background .4s var(--ease), transform .18s var(--ease-out), box-shadow .5s var(--ease-out);
}
.link-card:hover { background: #fff; --lift: -2px; box-shadow: var(--shadow-2), inset 0 1px 0 #fff; }
.lc-dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--ink); opacity: .5; }
.lc-name { display: block; font-size: 14px; color: var(--ink); }
.lc-note {
  display: block;
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lc-go { margin-left: auto; font-size: 12px; color: var(--ink-3); }
.link-card:hover .lc-go { color: var(--ink); }

.album-bar { margin-bottom: 18px; }
.album-chips { gap: 8px; }
.album-note { margin-bottom: 18px; font-size: 13.5px; line-height: 1.85; color: var(--ink-2); }

.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(238px, 1fr)); gap: clamp(12px, 1.4vw, 18px); }
.photo-empty { grid-column: 1 / -1; font-size: 14px; color: var(--ink-3); }

.photo {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: 18px;
  border: 1px solid rgba(255,255,255,.9);
  background: rgba(255,255,255,.46);
  box-shadow: var(--shadow-1), var(--shadow-2), inset 0 1px 0 #fff;
  backdrop-filter: blur(10px) saturate(175%);
  -webkit-backdrop-filter: blur(10px) saturate(175%);
  cursor: pointer;
  animation: photoIn .6s var(--ease-out);
  transition: transform .18s var(--ease-out), box-shadow .6s var(--ease-out);
}
.photo:hover {
  --lift: -5px;
  box-shadow: 0 2px 4px rgba(16,18,22,.06), 0 24px 40px -18px rgba(16,18,22,.24), 0 50px 80px -40px rgba(16,18,22,.34), inset 0 1px 0 #fff;
}
/* 新插入的照片淡入（不设 fill-mode，免得压住 hover 的 transform） */
@keyframes photoIn {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to   { opacity: 1; transform: none; }
}
.photo-media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; background: rgba(21,23,27,.03); }
.photo-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease-out); }
.photo:hover .photo-media img { transform: scale(1.05); }
.photo-idx {
  position: absolute;
  left: 11px; top: 11px;
  z-index: 2;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .1em;
  color: rgba(255,255,255,.95);
  text-shadow: 0 1px 4px rgba(0,0,0,.45);
}
.photo-album {
  position: absolute;
  right: 10px; top: 10px;
  z-index: 2;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.75);
  background: rgba(255,255,255,.74);
  backdrop-filter: blur(10px) saturate(180%);
  -webkit-backdrop-filter: blur(10px) saturate(180%);
  font-size: 10.5px;
  color: var(--ink);
}
.photo-body { padding: 14px 16px 16px; }
.photo-body h3 { font-size: 14.5px; font-weight: 500; color: var(--ink); }
.photo-body p { margin-top: 6px; font-size: 12.5px; line-height: 1.75; color: var(--ink-2); }

/* ═══════════ ★ 2026-10-15 新增：相册 / 摄影 ═══════════
   首页那一块照片墙。刻意复用上面 .photo / .photo-grid 的全部规则，
   只给「不同标签名」的标题与说明补几条映射，于是外观和
   分类详情里的照片块一模一样，风格自动统一。 */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(238px, 1fr));
  gap: clamp(12px, 1.4vw, 18px);
}
.gallery-grid[hidden] { display: none; }
.gallery-grid .reveal { transition: opacity .7s var(--ease), translate .7s var(--ease), transform .18s var(--ease-out); }
.gallery-grid .photo-title { display: block; font-size: 14.5px; font-weight: 500; color: var(--ink); }
.gallery-grid .photo-cap { display: block; margin-top: 6px; font-size: 12.5px; line-height: 1.75; color: var(--ink-2); }
/* 张数太少时把卡片摊开，免得右边空一大片 */
.gallery-grid[data-n="1"] { grid-template-columns: minmax(0, 460px); }
.gallery-grid[data-n="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 620px) {
  .gallery-grid[data-n="1"],
  .gallery-grid[data-n="2"] { grid-template-columns: 1fr; }
}

/* 相册的折叠入口：.sec-head 原本是三列（eyebrow / 标题 / 提示），
   这里右边一列换成「一句说明 + 展开按钮」的组合。 */
.sec-actions {
  display: flex;
  align-items: center;
  gap: clamp(10px, 1.6vw, 18px);
  margin-left: auto;
}
.sec-actions .sec-note { margin: 0; }
.gallery-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 14px;
  border: 0;
  border-radius: 999px;
  font: inherit;
  font-size: 12.5px;
  letter-spacing: .02em;
  color: var(--ink);
  cursor: pointer;
  white-space: nowrap;
  transition: transform .3s var(--ease), box-shadow .4s var(--ease);
}
.gallery-toggle:hover { --lift: -1px; }
.gallery-toggle:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.gallery-toggle .go {
  font-size: 10px;
  opacity: .55;
  transition: transform .35s var(--ease);
}
.gallery-toggle[aria-expanded="true"] .go { transform: rotate(180deg); }
@media (max-width: 760px) {
  .sec-actions { flex-direction: column; align-items: flex-end; gap: 8px; }
  .sec-actions .sec-note { display: none; }   /* 窄屏优先保按钮，说明文字让位 */
}

/* ─────────── 12. 灯箱 / 页脚 ─────────── */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: clamp(20px, 5vw, 60px);
  background: rgba(246,246,243,.8);
  backdrop-filter: blur(26px) saturate(170%);
  -webkit-backdrop-filter: blur(26px) saturate(170%);
  animation: fadeIn .4s var(--ease) both;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.lightbox figure {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 100%;
  animation: lbIn .55s var(--ease-out) both;
}
@keyframes lbIn {
  from { opacity: 0; transform: scale(.96) translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.lightbox { overflow-y: auto; }
/* 打开「写简介」时把图压矮一点，表单才不会被挤到屏幕外面去 */
.lightbox.note-open img { max-height: 44vh; }
.lightbox img {
  max-height: 74vh;
  width: auto;
  transition: max-height .45s var(--ease-out);
  margin-inline: auto;
  border-radius: 18px;
  border: 1px solid rgba(255,255,255,.9);
  box-shadow: 0 2px 6px rgba(16,18,22,.06), 0 50px 100px -40px rgba(16,18,22,.4);
}
.lightbox figcaption { text-align: center; }
.lightbox figcaption strong { display: block; font-size: 16px; font-weight: 500; color: var(--ink); }
.lightbox figcaption span { display: block; margin-top: 7px; font-size: 13.5px; color: var(--ink-2); }

/* 大图里的「换成本机照片 / 还原」。
   放在标题下面一行、居中，尺寸跟 .copy 一致 —— 它属于「一句话旁边的小动作」，
   不是主按钮；用玻璃胶囊而不是实心黑，免得抢走图本身。 */
.lb-acts {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 14px;
}
.lb-act {
  padding: 7px 15px;
  font: inherit;
  font-size: 12.5px;
  line-height: 1;
  color: var(--ink);
  background: rgba(255,255,255,.62);
  border: 1px solid var(--line-2);
  border-radius: 999px;
  cursor: pointer;
  transition: background .25s var(--ease), border-color .25s var(--ease),
              translate .25s var(--ease), box-shadow .25s var(--ease);
}
.lb-act:hover {
  background: rgba(255,255,255,.92);
  border-color: var(--line-3);
  translate: 0 -1px;
  box-shadow: var(--shadow-1);
}
.lb-act.ghost { color: var(--ink-2); background: transparent; }
.lb-act.ghost:hover { color: var(--ink); background: rgba(255,255,255,.55); }
.lb-act[hidden] { display: none; }

/* 「写简介」的表单：就在标题下面摊开，不跳页、不弹窗、不遮图。
   注意 .lightbox figcaption span 那条规则会把内部所有 span 变成灰色块，
   所以里面每个 span 都要带上 .lightbox figcaption 前缀才压得住。 */
.lb-note {
  display: flex;
  flex-direction: column;
  gap: 9px;
  width: min(430px, 100%);
  margin: 14px auto 0;
  padding: 14px 16px 13px;
  text-align: left;
  background: rgba(255,255,255,.72);
  border: 1px solid var(--line-2);
  border-radius: 14px;
  box-shadow: var(--shadow-1);
}
.lb-note[hidden] { display: none; }
.lb-field {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
}
.lightbox figcaption .lb-label {
  margin: 0;
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.lb-note input {
  width: 100%;
  min-width: 0;
  padding: 8px 11px;
  font: inherit;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--ink);
  background: rgba(255,255,255,.88);
  border: 1px solid var(--line-2);
  border-radius: 10px;
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease),
              background .25s var(--ease);
}
.lb-note input::placeholder { color: var(--ink-3); }
.lb-note input:focus {
  outline: none;
  background: #fff;
  border-color: var(--line-3);
  box-shadow: 0 0 0 3px rgba(21,23,27,.07);
}
.lightbox figcaption .lb-note-acts {
  display: flex;
  gap: 8px;
  margin-top: 3px;
}
.lightbox figcaption .lb-note-hint {
  margin: 1px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-3);
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.92);
  background: rgba(255,255,255,.7);
  box-shadow: var(--shadow-1), inset 0 1px 0 #fff;
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  font-size: 14px;
  color: var(--ink-2);
  transition: color .3s var(--ease), background .3s var(--ease);
}
.icon-btn:hover { color: var(--ink); background: #fff; }
.lb-close { position: fixed; top: clamp(16px, 3vw, 30px); right: clamp(16px, 3vw, 30px); }

/* 回到顶部：滚过一屏才淡进来，点一下平滑回到顶上 */
.to-top {
  position: fixed;
  right: clamp(16px, 3vw, 30px);
  bottom: clamp(16px, 3vw, 30px);
  z-index: 38;
  display: grid;
  place-items: center;
  width: 42px; height: 42px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.95);
  background: rgba(255,255,255,.62);
  box-shadow: var(--shadow-2), inset 0 1px 0 #fff;
  backdrop-filter: blur(10px) saturate(180%);
  -webkit-backdrop-filter: blur(10px) saturate(180%);
  color: var(--ink-2);
  opacity: 0;
  transform: translateY(10px) scale(.9);
  pointer-events: none;
  transition: opacity .45s var(--ease), transform .5s var(--ease-out),
              color .3s var(--ease), background .3s var(--ease);
}
.to-top svg { width: 16px; height: 16px; }
.to-top.on { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { color: var(--ink); background: #fff; }

.foot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding-block: clamp(26px, 5vh, 46px) clamp(30px, 6vh, 56px);
  border-top: 1px solid var(--line);
  font-size: 12.5px;
  color: var(--ink-3);
}
.foot-mono { font-size: 10.5px; letter-spacing: .16em; text-transform: none; color: var(--ink-3); }

/* ─────────── 13. 动效 · 响应式 · 无障碍 ─────────── */
/* 入场动画用独立的 translate 属性，把 transform 让给立体倾斜（两者可叠加） */
.reveal {
  opacity: 0;
  translate: 0 18px;
  transition: opacity 1s var(--ease-out), translate 1.1s var(--ease-out);
}
.reveal.in { opacity: 1; translate: none; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  border: 3px solid transparent;
  border-radius: 999px;
  background: rgba(21,23,27,.18);
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(21,23,27,.3); background-clip: content-box; }

@media (max-width: 1040px) {
  .facet-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .contact { grid-template-columns: 1fr; }
}

@media (max-width: 860px) {
  .hud-inner { height: 60px; }
  /* 这个宽度下 .hud-nav 是绝对定位的下拉面板，顶栏跟着胶囊收缩会错位，
     所以直接禁用收缩：位置回到整条，边框/阴影也退回 .stuck 那一套。 */
  .hud.shrunk {
    top: 0;
    left: 0;
    right: 0;
    border-radius: 0;
    border-color: transparent;
    border-bottom-color: var(--line);
    box-shadow: none;
  }
  /* 顶栏还没变实的时候整条都是透明的 —— 不拦事件，
     否则那 60px 会盖住下面内容的点击。变实(.stuck)之后才接住指针。 */
  .hud:not(.stuck) { pointer-events: none; }
  .hud:not(.stuck) .hud-inner,
  .hud:not(.stuck) .hud-nav { pointer-events: auto; }

  /* 导航收成「≡」：默认藏起来，点了才展开成一列玻璃按钮。
     不用 display:none —— 那样折叠时按钮也没法过渡；用 opacity + translate。 */
  .nav-toggle { display: grid; margin-left: auto; }
  .hud-live { margin-left: 0; padding-left: 12px; }

  .hud-nav {
    position: absolute;
    left: clamp(18px, 4vw, 40px);
    right: clamp(18px, 4vw, 40px);
    top: calc(100% + 8px);
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin: 0;
    padding: 12px;
    border-radius: 18px;
    border: 1px solid rgba(255,255,255,.95);
    background: rgba(248,248,245,.86);
    box-shadow: var(--shadow-2), var(--shadow-3), inset 0 1px 0 #fff;
    backdrop-filter: blur(22px) saturate(180%);
    -webkit-backdrop-filter: blur(22px) saturate(180%);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px) scale(.98);
    transform-origin: 100% 0;
    transition: opacity .3s var(--ease), transform .35s var(--ease-out),
                visibility .3s var(--ease);
  }
  .hud-nav.open { opacity: 1; visibility: visible; transform: none; }
  .hud-nav button {
    padding: 11px 14px;
    border-radius: 12px;
    font-size: 13.5px;
    text-align: left;
    background: rgba(255,255,255,.6);
  }
  .hud-nav button::after { left: 14px; bottom: 6px; transform: none; }
  .hud-nav button:hover::after,
  .hud-nav button.on::after { width: 26%; }

  /* 展开时把页面按一下，视觉上「面板是浮在内容前面的」 */
  body.nav-open main { opacity: .55; transition: opacity .35s var(--ease); }


  .hero {
    grid-template-columns: 1fr;
    gap: 26px;
    min-height: 0;
    padding-block: clamp(78px, 11vh, 108px) clamp(30px, 4.5vh, 52px);
  }
  .portrait { width: clamp(122px, 34vw, 168px); aspect-ratio: 1 / 1; order: -1; }
  .portrait-frame { border-radius: 24px; font-size: 46px; }
  .portrait-sheen { border-radius: 24px; }
  .hero-name { margin-top: 16px; }
  .hero-statement { margin-top: 20px; }

  .stat-row { grid-template-columns: 1fr; }
  .stat { padding-inline: 0; border-left: 0; border-top: 1px solid var(--line); }
  .stat:first-child { border-top: 0; }

  .lightbox img { max-height: 62vh; }
}

@media (max-width: 620px) {
  .facet-grid { grid-template-columns: 1fr; }
  .sec-hint { margin-left: 0; width: 100%; }
  .social-grid { grid-template-columns: 1fr; }
  .photo-grid { grid-template-columns: 1fr 1fr; }
  .photo-album { display: none; }
}

/* 窄屏的段落标题：eyebrow 和标题挤在一行时，标题会被顶到右边（360px 下实测
   标题起始于 x=184，看着像跑出去了）。这里整行改成竖排，全部左对齐。 */
@media (max-width: 560px) {
  .sec-head { flex-direction: column; align-items: stretch; gap: 7px; }
  .sec-hint { margin-left: 0; width: 100%; }
  .sec-actions { align-items: flex-start; width: 100%; }
}

@media (max-width: 420px) {
  .photo-grid { grid-template-columns: 1fr; }
  .to-top { width: 38px; height: 38px; }
  .work-foot { flex-direction: column; align-items: flex-start; gap: 10px; }
}

/* 手机上减弱玻璃模糊、减少光雾层数，省电 */
@media (max-width: 760px) {
  :root { --blur: 15px; --sat: 150%; }
  .near { display: none; }
  .grid { background-size: 56px 56px; opacity: .45; }
}

/* 系统开了「减少动态效果」。
   现在的主角是「跟随鼠标的柔光拖尾」—— 它只在你划鼠标时才有反应，本来就不喧哗。
   所以这里的策略是：
     · 页面自己发起的动效（入场、扫光、光雾的呼吸与漂移）全部关掉；
     · 用户主动发起的反应（鼠标划过留下的光痕）保留，只是光点更小、更淡、淡得更快，
       见 glow.js 里 reduce 那一行 ——— 这既尊重了无障碍偏好，又不会让人觉得「页面坏了」。
   底色的光雾与视差仍然保留，只把幅度压到 1/3。 */
/* 触屏（没有悬停能力）：没有指针可跟，磁吸一律不参与；
   涟漪保留 —— 手指按下去有反应，本来就该有。 */
@media (hover: none) {
  .chip, .social, .hud-nav button, .copy, .gallery-toggle, .back-btn,
  .work-link, .icon-btn { translate: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .reveal { opacity: 1; translate: none; }
  .grain { display: none; }

  /* 视差与漂移：幅度压到 1/3 */
  .far  { --travel: -5;  --dx: -2; }
  .mid  { --travel: -11; --dx: 4; }
  .near { --travel: -20; --dx: -7; }
  .liquid.a { --travel: -12; --dx: -4; --spin: 2; }
  .liquid.b { --travel: -15; --dx: 5;  --spin: -3; }
  .grid     { --travel: -8;  --dx: -5; }

  /* 水底光雾的循环呼吸：不删，只是放慢（44→52s 放到 90s）。
     慢到「几乎静止」是和没做一样 —— 试过，不行。 */
  .cloud { animation-duration: 90s !important; animation-iteration-count: infinite !important; }

  /* 点击涟漪是用户按下去才有的反馈，保留，只是更快消失；
     粒子层不进动画循环（见 particles.js 的 reduce 分支），只画一帧静止的。 */
  .ripple { animation-duration: .45s !important; }
}

/* ── 液态玻璃 2.0：照片卡 / 链接卡也吃同一块折射材质 ──────────────────
   必须放在文件最后：它们各自的规则里已经写了 backdrop-filter，
   同权重下只有后写的才盖得住。 */
.photo, .link-card {
  -webkit-backdrop-filter: blur(10px) saturate(175%) brightness(1.06) contrast(1.10);
          backdrop-filter: blur(10px) saturate(175%) brightness(1.06) contrast(1.10);
}


/* ══════════════════════════════════════════════════════════════════════════
   语言切换（简 / 繁 / EN）
   三段小胶囊，跟在导航后面：静止是浅白玻璃，选中变成实心墨色。
   放在 .hud-inner 里、跟着顶栏一起收缩，不额外占一行。
   ══════════════════════════════════════════════════════════════════════════ */
.lang {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  flex: 0 0 auto;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(255, 255, 255, .5);
  box-shadow: inset 0 1px 0 #fff;
}

.lang button {
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1;
  letter-spacing: .04em;
  color: var(--ink-3);
  background: transparent;
  border: 0;
  border-radius: 999px;
  padding: 5px 7px;
  cursor: pointer;
  transition: color .28s var(--ease-out), background .28s var(--ease-out),
              box-shadow .28s var(--ease-out);
}

.lang button:hover { color: var(--ink); background: rgba(21, 23, 27, .06); }

.lang button.on {
  color: #fff;
  background: var(--ink);
  box-shadow: 0 1px 2px rgba(16, 18, 22, .22);
}

.lang button:focus-visible { outline: 2px solid rgba(21, 23, 27, .35); outline-offset: 2px; }

/* 繁体：字体排前面，字形才走繁体那一套（「裡」这类字由字体决定骨架） */
html[lang="zh-TW"] {
  --font-sans: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", "Noto Sans SC",
               -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-display: "Noto Serif TC", "Source Han Serif TC", "Songti TC", "Noto Serif SC",
                  "Songti SC", Georgia, serif;
}

/* 英文换西文衬线：Noto Serif SC 的拉丁字母偏窄，Georgia 的层级更稳 */
html[lang="en"] {
  --font-display: Georgia, "Iowan Old Style", "Times New Roman", serif;
}

/* 英文没有汉字那种「字形自带的留白」，字距要收回来一点 */
html[lang="en"] .hero-name,
html[lang="en"] .sec-title,
html[lang="en"] .detail-name,
html[lang="en"] .contact-title { letter-spacing: -.01em; }

/* 英文专用：facts 左边那列的定宽是按两个字的中文标签算的，
   英文标签（SCHOOL / BASED IN）会把它撑成两行，值再被挤着换行。
   改成按内容宽、不折行，并把值的大写解开 —— 全大写加字距读起来太累。 */
html[lang="en"] .portrait-facts { max-width: 34em; }
html[lang="en"] .portrait-facts dt { width: auto; white-space: nowrap; }
html[lang="en"] .portrait-facts dd { text-transform: none; letter-spacing: .015em; }


html[lang="en"] .sec-hint,
html[lang="en"] .sec-note { letter-spacing: 0; }

@media (max-width: 560px) {
  .lang { padding: 2px; gap: 1px; }
  .lang button { padding: 5px 5px; font-size: 10px; letter-spacing: 0; }
}

/* 访客视图里 .lb-acts 是 hidden 的；它会 display:flex，所以要显式压掉 */
.lightbox figcaption .lb-acts[hidden] { display: none; }

/* ═══════════════════════════════════════════════════════════════════════
   8.8 触屏：把「每帧都在重算」的两样关掉

   手机上卡不是错觉，是两个机制叠在一起：
   ① backdrop-filter 是「背后内容一变就重算」的。页面上 12 块玻璃同时跟着滚，
      等于每帧重新模糊 12 次 —— 桌面显卡无感，中低端手机 GPU 直接掉到 30fps 以下。
   ② 背景还有一层 30fps 的全屏 canvas（尘）在动，于是①的重算变成永不停歇。

   处理：尘只画一帧（见 particles.js），玻璃只保留顶栏那一条的实时模糊，
   其余退回半透明实色 —— 描边、内高光、三层投影全部保留，
   静止看仍是玻璃，只是不再实时折射背后的东西。
   ─────────────────────────────────────────────────────────────────────── */
@media (hover: none) and (pointer: coarse) {
  .brand-mark,
  .hud-nav,
  .nav-toggle,
  .status,
  .gallery-toggle,
  .chip,
  .glass,
  .facet,
  .work,
  .skill-card,
  .photo,
  .link-card,
  .social,
  .portrait-frame {
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
  }

  /* 少了磨砂就得自己补回来一点白，否则玻璃会显得「透得太干净」 */
  .glass,
  .facet,
  .work,
  .skill-card,
  .photo,
  .link-card,
  .social {
    background-color: rgba(255, 255, 255, .42);
  }
}
